Terraform + AWS OpsWorks for Chef Automateでのサーバープロビジョニング #reinvent
TerraformのChef Provisionerの機能を使うと、Terraformで構築したサーバーに対してChefでプロビジョニングを実行することができます。(AWS環境であれば「EC2の起動 → EC2にChef Clientをインストール → レシピの実行」までを自動化することができます)
今回は AWS OpsWorks for Chef Automateを使ってこのChef Provisionersを実行する方法をご紹介します。
AWS OpsWorks for Chef Automateのセットアップ
セットアップ手順は以下のブログエントリを参照ください。
[速報]AWS OpsWorks For Chef Automateが発表されました #reinvent | Developers.IO
CredentialsとStarter Kitのダウンロード
セットアップウィザードの最後の画面で以下の2つをダウンロードします。 - Credentials - Starter Kit
CredentialsにはChef Automate DashboardへログインするためのログインIDとパスワード情報が含まれています。これは後から再ダウンロードできませんので、このタイミングで必ずダウンロードしておきましょう。
Starter KitはChef Automate server用にカスタマイズされたknife.rb(knifeコマンドのconfigファイル)や、Chef Automate serverにアクセスするためのプライベートキー(private.pem)などが含まれたchef-repo(chefのレポジトリ)です。このStarter Kitを使うことで、Chef Automate serverを利用するための事前準備の手間を大幅に省くことができます。
Starter Kitも再ダウンロードはできませんが、再作成することは可能です。ただし再作成するとプライベートキーも新しいものに置き換わってしまうのでご注意ください。
セキュリティグループ
セットアップウィザードで作成されるセキュリティグループではHTTP(80)とSSH(22)が全ての接続元に対して許可されています。実運用時は接続元のIPアドレスで制限をかけた方がよいでしょう。
Starter Kitの中身
Starter Kitについてもう少し詳しくみていきます。ダウンロードしたstarter_kit.zipを解凍すると、「my-chef-server-ak8xxxxxxxxxxxxx」といった名称のフォルダが展開されます。ディレクトリ構成は以下のようになっています。
. ├── .chef │ ├── ca_certs │ │ └── opsworks-cm-ca-2016-root.pem │ ├── knife.rb │ └── private.pem ├── Berksfile ├── README.md ├── chefignore ├── cookbooks │ └── README.md ├── environments │ └── README.md ├── roles │ └── README.md └── userdata.sh
Berksfile、cookbooks、environments、rolesなど、chefで利用するファイルやディレクトリが一式含まれています。
.chef/knife.rb
の中身は以下のようになっています。
base_dir = File.join(File.dirname(File.expand_path(__FILE__)), '..') log_level :info log_location STDOUT node_name 'pivotal' client_key File.join(base_dir, '.chef', 'private.pem') syntax_check_cache_path File.join(base_dir, '.chef', 'syntax_check_cache') cookbook_path [File.join(base_dir, 'cookbooks')] chef_server_url 'https://my-chef-server-ak8xxxxxxxxxxxxx.us-west-2.opsworks-cm.io/organizations/default' ssl_ca_file File.join(base_dir, '.chef', 'ca_certs', 'opsworks-cm-ca-2016-root.pem') trusted_certs_dir File.join(base_dir, '.chef', 'ca_certs')
chef_server_url
には構築したChef Automate serverのエンドポイントがセットされていますので、このStarter Kitディレクト内でknifeコマンドを実行すれば先ほど構築したChef Automate serverに対して操作が行われる、というわけです。
ただしTerraformはこのknife.rbの設定を参照してくれませんので、後述の通りTerraformのテンプレートファイルでChef Automate serverのエンドポイント、Chef Automate serverに接続するためのユーザー名、プライベートキーを指定する必要があります。
Workstationの準備
手元の端末にChef Development Kitをインストールします。各OS毎にパッケージが用意されていますので簡単にインストールすることができます。
macOSの場合はHomebrew Caskでインストールすることも可能です。
$ brew cask install chefdk
Cookbookのアップロード
TerraformからChef Provisionerを実行する準備として、あらかじめChef Automate serverにcookbookをアップロードしておきます。今回はnginxをインストールするだけのcookbookを用意して、それをアップロードします。
cookbookの作成
Starter Kitディレクト内でknife cookbook create
コマンドを実行しcookbookの雛形を作成します。
$ knife cookbook create nginx -o cookbooks
cookbook/nginx/recipes/default.rb
に以下のようなnginxインストール用のレシピを記述します。
package "nginx" do action :install end service "nginx" do action [:enable, :start] end
Chef Automate serverへのcookbookのアップロード
Starter Kitディレクト内でknife cookbook upload
コマンドでChef Automate serverへ作成したcookbookをアップロードします。
$ knife cookbook upload nginx
knife cookbook list
コマンドでcookbookがアップロードされたことを確認します。
$ knife cookbook list
TerraformでEC2の起動&Chef Provisionerを実行
今回は以下のようにStarter Kitディレクトリと同じ階層にTerraformのテンプレートファイルを準備しました。
├── main.tf ├── my-chef-server-ak8xxxxxxxxxxxxx └── variables.tf
テンプレートファイルの全体はこちらを参照ください。
以下はaws_instance
リソース部分の抜粋です。
resource "aws_instance" "web_server" { count = 1 ami = "${data.aws_ami.amazon_linux.id}" instance_type = "t2.nano" key_name = "${var.ssh_key_name}" vpc_security_group_ids = [ "${aws_security_group.external_ssh.id}", "${aws_security_group.web_server.id}", ] subnet_id = "${element(aws_subnet.external.*.id, count.index)}" associate_public_ip_address = true root_block_device = { volume_type = "gp2" volume_size = "20" } iam_instance_profile = "${aws_iam_instance_profile.web_server.name}" monitoring = false disable_api_termination = false tags { Name = "${var.name}-${format("web_server-%02d", count.index+1)}" Environment = "${var.environment}" } provisioner "chef" { connection { host = "${self.public_ip}" type = "ssh" user = "ec2-user" private_key = "${file(var.ssh_key_file)}" } environment = "_default" run_list = ["nginx::default"] node_name = "${var.name}-${format("web_server-%02d", count.index+1)}" server_url = "https://my-chef-server-ak8xxxxxxxxxxxxx.us-west-2.opsworks-cm.io/organizations/default" recreate_client = true user_name = "pivotal" user_key = "./my-chef-server-ak8xxxxxxxxxxxxx/.chef/private.pem" fetch_chef_certificates = true } }
provisioner
にchefを指定します。その他指定が必要な項目は以下の通りです。
設定項目 | 説明 | 備考 |
---|---|---|
node_name | Chef Automate serverに登録するノード名 | 任意の名前を指定 |
run_list | 実行するレシピ | |
server_url | Chef Automate serverのURL | |
user_name | Chef Automate serverのユーザー名 | Chef Automate serverにノードを登録するために必要 |
user_key | Chef Automate serverの認証用のプライベートキー |
今回user_name
にはChef Automate serverのスーパーユーザーであるpivotal
を指定しました。
Chef Automate serverのSSL証明書は自己署名証明書のため、fetch_chef_certificates = true
を指定してChef Automate serverの証明書をクライアントの/etc/chef/trusted_certs
にダウンロードするようにします(これを指定しないと証明書のバリデーションに失敗します)。
実行結果
Chef AutomateのDashboardにアクセスすると、nodeが追加されていることが確認できます。
なお、terraform destroy
でEC2を削除してもChef server側のノード情報は削除されないのでご注意ください。
まとめ
AWS OpsWorks for Chef Automateを使えばChef Serverを簡単に使い始めることができます。Terraform + Chef、ぜひお試しください。
ちなみにTerraformではリモートサーバー上で任意のコマンドを実行できるremote-execというprovisionerも利用できます。興味ある方は以下のブログエントリを参照ください。