TerraformによるConsulの導入の考察
これは、Goodpatchのアドベントカレンダー 18日目の記事です。
こんにちは、Goodpatchでエンジニアをしている@urapicoです。
先日、GoodpatchのサービスのProttのインフラを専用サーバーからAWSに全て移行しました。 特に骨が折れたのは、S3の引っ越しですが。。。
今回は、HashiCorpで有名な2つのプロダクトTerraformとConsulについてです。
Terraform と Consul
Terraform
まず、Terraformですが、こちらはインフラ構築に利用されるツールです。
Amazon Web Serviceのインフラ構築には、非常に親和性が高いと思います。
Amazon Web Serviceには、Terraformと同じ目的のサービスがあります。
AWS CloudFormationです。
AWSを利用する場合、CloudFormationのほうがいいと思われますが、Terraformの作成元のMitchell Hashimoto氏の
インタビューで、"CloudFormationに先行して,
さまざまな機能を追加することができた..."と答えています。
また、設定フォーマットもJSONライクな文法ですが、コメントなどが書けたり、非常に読みやすい形になっています。
CloudFormationは、標準のJSONのため、痒いところに手が届きにくいという印象があります。
Consul
consulは、Service Discoveryという名前でよく紹介されてるツールです。 今時のサービスのサーバー構成は、非常に柔軟に作られることが多々あり、 負荷によってサーバーを増減させたりなどを行います。
ただ、こうした自動でサーバーが増減した場合、サーバーのリストなどを手動で作成して管理するのは、限界があります。
こういった場合、Service Discoveryという機能が、そのサーバー郡に備わっていると非常に重宝します。 なぜなら、各サーバー同士(node)が定期的に通信を行って、生存の確認や、そのグループにjoinしたイベントなどを感知して サーバーリストの更新などを行ってくれるからです。
また、consulは、その他にもDNSやkey value storageの機能を有しているのも特徴の一つです。
サーバーの管理上、どうしてもちょっとしたサーバーの情報を保存しておきたい場合、 そのために他のミドルウェアをインストールしたりするのは、少々オーバースペックなところがありますので、 key value storageの機能などがあるのは、Mitchell氏わかってる!!って感じですね。
Terraformのインフラ構築 そしてConsul
弊社では、Terrafrom以外では、Ansibleなども利用していて、 Ansibleでは、サーバーにインストールするミドルウェアなどを管理させています。
Ansibleは、サーバーリストを知らないと利用できないので、 Terraformで構築した際に、同時にConsulもインストールして サーバーリストをConsulに管理させたいと考えました。
VPC上で、どのように構築するのか
AWSで、サーバーを構築する場合、VPCを利用します。 よくある構成としては、PrivateとPublicのサブネットを構築して、Natサーバーを経由して sshのアクセスなどをさせます。
Terraformでインストールさせる際、コマンドによる操作がいくつか必要になります。 Terraformでは、remote-execというリモートのサーバー上で 実行できる命令があります。
これを利用したら、TerraformにConsulをインストールさせたりできそうです。
ただ、ここで問題になるのは、Natサーバーを経由させた形で、対象のサーバーにアクセスしないといけません。
その場合、null_resourceというresourceを使って Natサーバーを踏み台にして、各サーバーに接続する方法を利用できます。
resource "null_resource" "nat" { connection { user = "ec2-user" host = "${aws_eip.nat.public_ip}" private_key = "path/private_key" } provisioner "file" { source = "file/setup.sh" destination = "/tmp/setup.sh" } provisioner "remote-exec" { inline = [ "chmod +x /tmp/setup.sh", "mkdir /tmp/consul", "chmod 777 /tmp/consul", "/tmp/setup.sh ${join(" ", aws_instance.web.*.private_ip)}" ] } }
この設定ファイルでは、Natサーバーに接続したあとに、fileを使って、 各サーバーにConsulの設定を行うシェルファイルを転送して、Natサーバー上で実行しています。
しかし、ここで問題になるのは、ローカルにしか、private key(pemファイル)が存在しないことです。
Natサーバーへの接続までは、問題なくできますが、そこから各サーバーに接続することができません。
ここで、私は、ec2のuser dataという機能を利用しました。 ec2のuser dataは、ec2のインスタンス起動時に、設定した処理を実行してくれる機能です。 ふと、ここで、このuser dataで、Consulをインストールなどを、行えばいいのでは?と思いますが、Consulは、Join先がないと、意味をなさないので、やはりサーバー群が作られたあとに、行うのが良さそうです。
この機能を使って、Terraformを実施する端末のpublic keyを登録しておきます。 私は、TerraformをTravis CI 上で実行させたかったので、 Travisのpublic keyも登録しました。
# Create a Web server resource "aws_instance" "web" { ami = "${lookup(var.amis, var.region)}" count = 10 instance_type = "${var.instance_type_web}" user_data = "${template_file.init.rendered}" tags { Name = "web" } }
そして、もう一度、null_resourceの設定を見直します。
前回では、null_resource上では、connectionをprivate keyで行っていましたが、 各サーバーに端末のpublic keyを登録したので、何も指定しなくても 接続が可能になります。
ただ、踏み台にしたNatサーバーから、他のサーバーに接続するところが問題になります。
これに関しては、Terraformのconnectionは、ssh-agentを設定することができます。
書き換えると設定ファイルは、こんな感じになります。
resource "null_resource" "nat" { connection { user = "ec2-user" host = "${aws_eip.nat.public_ip}" agent = true } provisioner "file" { source = "file/setup.sh" destination = "/tmp/setup.sh" } provisioner "remote-exec" { inline = [ "chmod +x /tmp/setup.sh", "mkdir /tmp/consul", "chmod 777 /tmp/consul", "/tmp/setup.sh ${join(" ", aws_instance.web.*.private_ip)}" ] } }
そして、接続元のsshの設定のforward agentを有効にします。
このように設定することによって、Terraformを使って、Consulのインストールが可能になりました。
まとめ
Terraformでは、いまでも開発が非常に活発で、様々な解決方法を提供してくています。
今回のConsulのインストール方法ももっと、別のやり口もあると思います(是非、教えてください)。
各ツールをシームレスに連携し、サーバーの状態をあるべき姿をいつでも再現可能にしておくというのは エンジニアとしての精神衛生的にも重要なことだと思います。
今後もいろいろと積極的に、先進的な技術・ツールを導入していきたいと思っています。
このような先進的な技術・ツールに興味があるエンジニアの方は、是非、wantedly 経由などで、気軽に遊びに来てください!!
https://www.wantedly.com/projects/37793?u=14754
明日は @inoino さんの投稿となります!!お楽しみに!!
17日目の記事 hi6484 さんのJNI JNA SWIG for Android