Terraform + AWS OpsWorks for Chef Automaterでのサーバープロビジョニング #reinvent

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

TerraformのChef Provisionerの機能を使うと、Terraformで構築したサーバーに対してChefでプロビジョニングを実行することができます。(AWS環境であれば「EC2の起動 → EC2にChef Clientをインストール → レシピの実行」までを自動化することができます)

今回は AWS OpsWorks for Chef Automaterを使ってこのChef Provisionersを実行する方法をご紹介します。

AWS OpsWorks for Chef Automaterのセットアップ

セットアップ手順は以下のブログエントリを参照ください。

[速報]AWS OpsWorks For Chef Automateが発表されました #reinvent | Developers.IO

CredentialsとStarter Kitのダウンロード

セットアップウィザードの最後の画面で以下の2つをダウンロードします。 - Credentials - Starter Kit

terraform_chef-8-1

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
    }
}

provisionerchefを指定します。その他指定が必要な項目は以下の通りです。

設定項目 説明 備考
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_chef-11-1

なお、terraform destroyでEC2を削除してもChef server側のノード情報は削除されないのでご注意ください。

まとめ

AWS OpsWorks for Chef Automaterを使えばChef Serverを簡単に使い始めることができます。Terraform + Chef、ぜひお試しください。

ちなみにTerraformではリモートサーバー上で任意のコマンドを実行できるremote-execというprovisionerも利用できます。興味ある方は以下のブログエントリを参照ください。

【Terraform】remote-execを使ったリモートサーバーのプロビジョニング | Developers.IO