なんかかきたい

プログラミングなどの個人的なメモやサークル「ゆきいろパラソル」の情報を載せてます

AWS CodeBuild と packer と ansible でプロビジョニング済みのAMIを自動生成する

AMI生成部分を自動化できたので、ちょっとだけまとめておく。

CodeBuild

  • AWSのDockerで動くCIツール。
  • CodeBuildの名前の通り、ソースコードをビルドする環境を簡単に作れることをウリにしている
  • CircleCIなどのサービスでビルドしたり、Jenkinsを自前で用意するのと比較してそれほど違いはない
    • 利点があるとすれば、AWS上で動くためサービスロールを使って権限を付与できるところか
    • packerを使う都合上、EC2へのアクセスが必要になるため、外部サービスの場合アクセストークンの発行と管理が必要になる
  • 料金はビルド時間ごとの課金
    • 一応無料枠もある

Packer

  • HashiCorp製のマシンイメージ自動生成ツール
  • Goで書かれていて機能が豊富
    • AWS以外にもいくつかのクラウドサービスに対応している

Ansible

  • Python製のプロビジョニングツール
  • シェルスクリプトでもいいが、モジュールが整備されているので使えるようになると便利
  • Packerと連携するとSSH周りを自動で設定してくれる

参考ソース

AWSのブログにCodeBuildとPackerを使ったAMIビルダーの構築方法がある。

他にはクラスメソッドさんのブログにAnsibleとPackerの連携周りがある。

この辺りを読めばなんとなく作り方のイメージはできるようになるが、いくつかハマった点があるので補足としてまとめておく。

このリンクだけで十分な人はここから先は読まなくてもOKです。

大まかな流れ

  • Ansibleのplaybookはgithubで管理しておく
    • ここは変えてもよいが、CodeBuildはgithubと連携できるのでgithubを使うと簡単。
    • CodeBuildでコードを取ってくる方法は、他にS3やGithub Enterprise、BitbuckerとCodeCommitがある
    • 最悪の場合でもS3が使えるのでなんとかなるとは思う。
  • CodeBuildでビルド用のDockerを起動、事前のセットアップを行う
    • buildspec.ymlprebuild に書く
    • DockerでAnsibleとPackerを実行するのでインストールする
      • インストール済みのDockerイメージを用意できればわざわざビルドのたびにセットアップする必要は無くなる
    • Dockerイメージは任意のものを使って構わないが、AWSが提供しているのは現状 Ubuntu 14.04 LTS (Trusty) ちょっと古いか。
  • ansible-vaultで使うパスワードをKMSから取ってくる
    • 平文を嫌う場合、KMSに登録しておき、環境変数にセットする方法が使える
  • packer build でAMIを生成する

AWSのブログと違う点

  • Packer 1.2.1
  • ansible provisionerを使うため、Docker環境へAnsibleのインストールする
  • packerがIAM Roleを扱えるようになったため、クレデンシャルの生成は不要になった
  • 環境変数 USER を設定する
    • 設定しないと packer user: Current not implemented on linux/amd64 が出る
    • これはDocker環境では環境変数 USER が設定されていないため。Golanguser を取るところでエラーになる
    • CodeBuildの環境変数設定でやっておけば問題はない

クラスメソッドさんのPacker+AnsibleによるAMIの作成と違う点

  • アクセスキーはIAM Roleを使うので変数を使わない
  • ansible-localではなくansibleを使う
  • ansibleのインストールは buildspec.yml で行う

クラスメソッドさんのPacker 0.9の新機能 リモートからのAnsible Provisionerが追加されましたと違う点

  • vaultファイルの管理をする

本題

CodeBuildとPackerを使うため、それぞれのための設定ファイルを用意する。

CodeBuildが buildspec.yml で Packer は任意の名前のJSON

buildspec.yml

---
version: 0.2

phases:
  pre_build:
    commands:
      - echo 'Install Packer 1.2.1'
      - curl -qL -o packer.zip https://releases.hashicorp.com/packer/1.2.1/packer_1.2.1_linux_amd64.zip && unzip packer.zip
      - echo 'Install Ansible'
      - apt-get update -y
      - apt-get install -y software-properties-common
      - apt-add-repository ppa:ansible/ansible
      - apt-get update -y
      - apt-get install -y ansible
      - echo 'Validate packer json'
      - ./packer validate packer_ec2.json
  build:
    commands:
      - aws configure set region $AWS_REGION
      - echo "$ansible_vault_pass" > vault_password_file
      - echo 'Build AMI'
      - ./packer build packer_ec2.json
  post_build:
    commands:
      - echo "Build finished `date`"

packer_ec2.json

{
    "variables":{
        "vault_path": "vault_password_file"
    },
    "builders": [{
        "type": "amazon-ebs",
        "region": "ap-northeast-1",
        "source_ami": "ami-XXXXXX",
        "instance_type": "t2.micro",
        "ssh_username": "root",
        "ssh_timeout": "5m",
        "ami_name": "packer_{{ timestamp }}"
    }],
    "provisioners": [{
        "type" : "ansible",
        "extra_arguments": "--vault-password-file={{user `vault_path`}}",
        "playbook_file" : "playbook.yml",
        "groups" : ["aws", "webserver"]
    }]
}

groups を配列で渡すと inventory file で複数のグループに属した場合と同じ扱いになる。

これは group_vars などで設定を切り替える必要がある場合に便利。

vaultファイルは buildspec.yml で出力したパスを extra_arguments を使ってそのまま渡している。

IAM Role

  • XXXXXXXX にはアカウントIDを入れる

  • 以下のポリシーを作成し、CodeBuildで使うIAM Roleにアタッチする。(インラインでも構わない)

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "ssm:GetParameters"
            ],
            "Resource": [
                "arn:aws:ssm:ap-northeast-1:XXXXXXXX:parameter/ansible-vault-pass"
            ]
        },
        {
            "Sid": "",
            "Effect": "Allow",
            "Action": [
                "kms:Decrypt"
            ],
            "Resource": [
                "arn:aws:kms:ap-northeast-1:XXXXXXXX:key/alias/aws/ss"
            ]
        }
    ]
}

KMSからデータを取ってくるために必要。名前は適宜設定すること。

終わりに

いくつかハマりポイントはあったが、これでAMIをCodeBuildで自動的に作れるようになった。

あとはgithubのMergeにHookするとかすれば自動でAMIがどんどん作られていくので便利。

ただ、古いのを消さないと溜まり続けて課金がすごいことになりそう。大変ですね。

現場からは以上です