ユースケース
(Docker SwarmまたはConsul)クラスターをプロビジョニングしようとすると、最初にクラスターの初期化が1つのノードで発生し、いくつかのトークンが生成されます。このトークンは、クラスターに参加している他のノードで使用する必要があります。重要なことは、ノード0によって結合キーが生成されるまで、ノード1と2はクラスターに結合しようとしないことです。
例えば。ノード0でdocker swarm init ...
を実行すると、joinトークンが返されます。次に、ノード1と2で、そのトークンをdocker swarm init ${JOIN_TOKEN} ${NODE_0_IP_ADDRESS}:{SOME_PORT}
などの同じコマンドに渡す必要があります。そして魔法、あなたはきちんとした小さなクラスターを持っています...
これまでの試行回数
AWS SDKがインストールされているすべてのノードを初期化し、S0のノード0から結合キーを保存してから、他のノードでその結合キーをフェッチしようとしました。これは、「remote-exec」プロビジョナーでnull_resourceを介して行われます。 Terraformが並行して処理を実行する方法が原因で、きちんとしたタイプの条件があり、予想どおり、ノード1と2はまだそこにないS3から頻繁にキーをフェッチしようとします(たとえば、ノード0はまだその要素を完了していません)。
'local-exec'プロビジョナーを使用してノード0にSSH接続し、その結合キー出力をキャプチャしようとしました。これはうまくいかなかったか、私はそれをやるのが苦手でした。
私はドキュメントを読みました。そしてスタックオーバーフロー。そしてGithubの問題 これは本当に長い未解決の問題 のようなものです。徹底的に。これが他の場所で解決されていれば、リンクは大歓迎です!
PS-これは この質問 に直接関連しており、より小さなサブセットですが、問題の範囲に焦点を合わせるために再度質問したかったのです。
「プロビジョナーからの出力を使用して別のリソースの変数にフィードすることはできますか?」と同じ質問をしたとき、私は回答のソースに行きました。
現時点では、プロビジョナーの結果は単にterraformの標準出力にストリーミングされ、キャプチャされることはありません。
両方のノードでリモートプロビジョナーを実行していて、S3から値にアクセスしようとしている場合-私はこのアプローチに同意しますが、私は同じようにします-おそらくあなたがする必要があるのは、スクリプトで競合状態を処理することですsleep
コマンドを使用するか、at
またはcron
または類似のスケジューリングシステムを使用して後で実行するようにスクリプトをスケジュールします。
一般に、Terraformは、すべての変数に事前に、またはプロバイダーの結果としてアクセスしたいと考えています。プロビジョニング担当者は、必ずしもTerraformのファーストクラスとして扱われるわけではありません。私はコアチームにいないので理由はわかりませんが、私の推測では、プロビジョナーは単なるスクリプトなので、結果は一般に構造化されていないため、成功または失敗を超えてプロビジョナーの結果を無視することで複雑さが軽減されます。
インスタンスを設定するためのより高度な機能が必要な場合は、Ansible、Chef、Puppetなどの専用ツールをお勧めします。Terraformの焦点は、ソフトウェアコンポーネントではなく、インフラストラクチャにあります。
出力をファイルにリダイレクトできます。
_resource "null_resource" "Shell" {
provisioner "local-exec" {
command = "uptime 2>stderr >stdout; echo $? >exitstatus"
}
}
_
次に、_local_file
_でstdout
、stderr
、exitstatus
ファイルを読み取ります
問題は、ファイルが消えるとterraform applyが失敗することです。
Terraform 0.11では、外部データソースでファイルを読み取り、結果を_null_resource
_トリガー(!)
_resource "null_resource" "contents" {
triggers = {
stdout = "${data.external.read.result["stdout"]}"
stderr = "${data.external.read.result["stderr"]}"
exitstatus = "${data.external.read.result["exitstatus"]}"
}
lifecycle {
ignore_changes = [
"triggers",
]
}
}
_
しかし、0.12では、これをfile()
に置き換えることができます。
そして最後に私はそれらを使用/出力することができます:
_output "stdout" {
value = "${chomp(null_resource.contents.triggers["stdout"])}"
}
_
完全な実装については、モジュール https://github.com/matti/terraform-Shell-resource を参照してください
あなたは効果的にdocker swarm init
ノード0をTerraform外部データソースとしてステップし、JSONを返すようにします。残りのノードのプロビジョニングがこのステップに依存するようにし、外部データソースによって生成された結合トークンを参照します。
https://www.terraform.io/docs/providers/external/data_source.html
より簡単な解決策は、トークンを自分で提供することです。
ACLトークンを作成するとき、ID値を渡すだけで、コンサルはランダムに生成する代わりにそれを使用します。
Sparrowform -Terraformベースのインフラストラクチャ用の軽量なプロビジョナーであり、ケースを処理できます。これはaws ec2インスタンスの例です。
Consulクラスターに3つのec2インスタンスがあると仮定します:node0、node1、node2。最初のノード(node0)は、トークンをフェッチしてS3バケットに保持する場所です。他の2つは後でS3からトークンをロードします。
$ nano aws_instance.node0.sparrowfile
#!/usr/bin/env Perl6
# have not checked this command, but that's the idea ...
bash "docker swarm init | aws s3 cp - s3://alexey-bucket/stream.txt"
$ nano aws_instance.node1.sparrowfile
#!/usr/bin/env Perl6
my $i=0;
my $token;
try {
while True {
my $s3-token = run 'aws', 's3', 'cp', 's3://alexey-bucket/stream.txt', '-', :out;
$token = $s3-token.out.lines[0];
$s3-token.out.close;
last if $i++ > 8 or $token;
say "retry num $i ...";
sleep 2*$i;
}
CATCH { { .resume } }
}
die "we have not succeed in fetching token" unless $token;
bash "docker swarm init $token";
$ nano aws_instance.node2.sparrowfile - the same setup as for node1
$ terrafrom apply # bootstrap infrastructure
$ sparrowform --ssh_private_key=~/.ssh/aws.pub --ssh_user=ec2-user # run provisioning on node0, node1, node2
PSの開示、私はツールの作成者です。
外部データを使用できます。
data "external" "docker_token" {
program = ["/bin/bash", "-c" "echo \"{\\\"token\\\":\\\"$(docker swarm init...)\\\"}\""]
}
その後、トークンはdata.external.docker_token.result.token
として利用できます。引数を渡す必要がある場合は、スクリプトを使用できます(例:path.module
に関連)。詳細は https://www.terraform.io/docs/providers/external/data_source.html を参照してください。
リソースの依存関係 を使用すると、リソースが前に作成されることを確認できます。
これは、あなたにアイデアを与えるために、私の領事クラスターを作成する方法の不完全な例です。
resource "aws_instance" "consul_1" {
user_data = <<EOF
#cloud-config
runcmd:
- 'docker pull consul:0.7.5'
- 'docker run -d -v /etc/localtime:/etc/localtime:ro -v $(pwd)/consul-data:/consul/data --restart=unless-stopped --net=Host consul:0.7.5 agent -server -advertise=${self.private_ip} -bootstrap-expect=2 -datacenter=wordpress -log-level=info -data-dir=/consul/data'
EOF
}
resource "aws_instance" "consul_2" {
depends_on = ["aws_instance.consul_1"]
user_data = <<EOF
#cloud-config
runcmd:
- 'docker pull consul:0.7.5'
- 'docker run -d -v /etc/localtime:/etc/localtime:ro -v $(pwd)/consul-data:/consul/data --restart=unless-stopped --net=Host consul:0.7.5 agent -server -advertise=${self.private_ip} -retry-join=${aws_instance.consul_1.private_ip} -datacenter=wordpress -log-level=info -data-dir=/consul/data'
EOF
}
Docker Swarmセットアップの場合、Terraformのスコープ外であると思います。トークンは作成しているインフラストラクチャの属性ではないため、そうする必要があると思います。だから私は nbering に同意します。AnsibleやChefなどのツールを使用してその設定を実現することができます。
しかしとにかく、例があなたの領事クラスターをセットアップするのを助けるならば、私はあなたのドッカースウォームバックエンドとして領事を設定する必要があるだけだと思います。