Amazon Web Services ブログ

AWS CodeBuild と HashiCorp Packer を用いた AMI ビルダーの構築方法

独自の アマゾン マシン イメージ を作成し維持することは、運用とセキュリティにおけるベストプラクティスです。インフラストラクチャをコードとして維持することもまたベストプラクティスの1つです。そのため、Amazon EC2 インスタンスを素早く起動するために AMI を作成し設定する、といったことをスクリプト化するための自動化ツールを利用することには価値があります。

公開する2つの記事の最初にあたるこの記事では、AWS においてプログラマブルに AMI を作成するために AWS CodeBuild を使用します。AMI 生成の一部として、OS のパッチを適用し、バナーステートメントを設定し、よく使うソフトのいくつかをインストールし、将来的な Amazon EC2 ベースのデプロイメントへの基盤を形成します。

(訳注:AMIの作成やメンテナンスを自動化する方法は、この記事で紹介する AWS CodeBuild を使用するもの以外にもあります。例えば、Amazon EC2 Systems Manager を使用する方法などです。)

必要なもの

Git と テキストエディタが必要です。

技術

AWS CodeBuild は完全マネージドのビルドサービスです。ソースコードをコンパイルし、テストを実行し、そしてデプロイ可能なソフトウェアパッケージを生成することが可能です。柔軟性があり、スケーラブルで、そしてJava、Ruby、Python、Go、Node.jsなどのプログラミング言語向けの整備済みのビルド環境を提供します。AWS CodeBuild を利用すると、ビルドサーバのプロビジョニングや管理、そしてスケーリングなどを気にする必要がなくなります。より詳細な情報は、AWS CodeBuild のユーザガイドをご参照ください。

HashiCorp Packer は、単一の設定ファイルをもとに複数プラットフォームにおいて同じ仮想マシンイメージを作成することを自動化するために設計された、オープンソースのユーティリティです。より詳細な情報は、 HashiCorp Packer ドキュメントをご参照ください。

入門

AWS CodeBuild と HashiCorp Packer のプロジェクトをホストする場所がどこか必要です。AWS CodeBuild は、ソースコードのリポジトリとして Amazon S3AWS CodeCommit、 あるいは GitHub を利用できます。このチュートリアルでは、AWS マネジメントコンソールにサインインし、AWS CodeCommit のリポジトリを作成します。もし今までに AWS CodeCommit を1度も使用したことがないなら、Git with AWS CodeCommit チュートリアルをやってみることをおすすめします。

コンソールで AWS CodeBuild のプロジェクトを作成する

AWS CodeBuild のプロジェクトを作成します。

  1. AWS マネジメントコンソールから AWS CodeBuild のコンソールを開いてください。
  2. もし AWS CodeBuild のプロジェクトが1つもないなら、Get Started を選択するか、あるいは Build Projects ページの Create project を選択してください。
  3. Create project ページで、AWS CodeBuild のプロジェクト名を入力してください(例えば AMI_Builder )。
  4. Source provider に AWS CodeCommit を選択します。Repository のドロップダウンリストから、さきほど「はじめに」のステップで作成したリポジトリを選択します。

AWS CodeBuild は、プロジェクトのビルドとビルドの手順を実行するのにコンテナを使用します。Amazon EC2 Container RegistryDockerHub にホストしている独自のコンテナイメージを指定することも可能ですが、このチュートリアルでは管理されているデフォルトの Ubuntu コンテナを使用します。

  1. Environment Image は、Use an image managed by AWS CodeBuild を選択し、Operating systemUbuntu をします。RuntimeBase を選択し、Version は aws/codebuild/ubuntu-base:14.04 を選択します。

このページの次のセクションでは、ビルドの過程で実行されるコマンドの置き場所を AWS CodeBuild に指示します。今回のプロジェクトでは、ビルドのコマンドはリポジトリ内の buildspec.yml ファイルに記述します。

  1. Build specification は デフォルトのままにしてください。

CodeBuild project configuration

buildspec.yml ファイルは、テンプレートを実行し Amazon EC2 AMI を作成するのに HashiCorp Packer を使います。アーチファクトとして使用されるバイナリ・アウトプットやビルド成果物はありません。

  1. Artifacts type は No artifacts を選択してください。

Service Role の項目においてここで選択したサービスロールが、あなたの AWS アカウントに対する権限として AWS CodeBuild のコンテナに渡されます。HashiCorp Packer は、一時的な EC2 インスタンスと AMI を作成し、EC2 インスタンスを削除し、そしてその他の関連する EC2 関連のアクションを実行する権限を必要とします。

今回の目的のためには、AWS CodeBuild によるサービスロールの作成を許可します。後で、HashiCorp Packer によって必要とされる権限で更新します。

  1. Service Role は、デフォルトの「Create a service role in your account」を選択します。
  2. Continueを選択します。
  3. 次のページで設定を確認し、Save を選択してください。

サービスロールの権限を更新

AWS CodeCommit のリポジトリと接続されソースコードをビルドする準備ができた AWS CodeBuild プロジェクトを作成しました。ここで、AWS CodeBuild に更に権限を付与する必要があります。EC2 インスタンスを作成し、削除し、イメージを作成する権限です。

  1. IAM のコンソールを開いて AWS CodeBuild のサービスロール(さっきのセクションで作成した)をクリックし、サマリーのページを表示します。
  2. サマリーのページの Permissions 配下の Inline policies を開き、Create Role Policy をクリックします。
  3. Custom Policy を選択し Select を選択します。
  4. HashiCorp Packer documentation から IAM Policy をコピーしテキストエリアに貼り付けます。ポリシーの名前を入力します(例えば codebuild-AMI_Builder-ec2-permissions )。
  5. Validate Policy を選択し、それから Apply Policy を選択しサービスロールにこのポリシーを関連付けします。

CodeBuild project permissions

これで、AWS CodeBuild が AMI を作成するために必要な権限を保持しているはずです。

最初のコミット

次のステップは、HashiCorp Packer のテンプレートとビルドスペックを作成することです。

  • HashiCorp Packer のテンプレートである amazon-linux_packer-template.json 。
  • AWS CodeBuild の設定ファイルである buildspec.yml 。

HashiCorp Packer のテンプレートを作成

HashiCorp Packer テンプレートは、JSONフォーマットのドキュメントです。HashiCorp Packer にマシンイメージのビルド方法の情報を渡します。HashiCorp Packer テンプレートは多くのキーで構成されます。この記事では、builders と provisioners のキーが焦点です。より詳細な情報は、HashiCorp Packer のウェブサイトの Templates を参照してください。

テキストエディタを使って、variables、builders、そして provisioners という3つの項目を持つ amazon-linux_packer-template.json という名前の HashiCorp Packer テンプレートを作成します。

variables の項目は aws_regionaws_ami_name の2つの変数を定義しています。この2つの変数は、テンプレートの後の部分で利用されます。それぞれ、{{env `AWS_REGION`}} という環境変数と {{isotime \"02Jan2006\"}} という HashiCorp Packer の内部関数を使って定義されています。HashiCorp Packer の関数についてのより詳細な情報は、HashiCorp Packer の ウェブサイトの Template Engine を参照してください。

builders の項目は、AWS CodeBuild が実行されるのと同じリージョンにインスタンスをデプロイするために amazon-ebs ビルダーを設定しています。t2.micro インスタンスを使用し、ユーザ名 ec2-user でインスタンスに接続します。source_ami_filter はターゲットのリージョンにおける Amazon Linux の最新バージョンを見つけるのに使われます。選択されたソース AMI は、作成する独自 AMI のベースとなります。EC2 インスタンスがプロビジョニングされた後で、amazon-ebs によって aws_ami_name 変数の値を AMI 名として AMI を作成します。

{
    "variables": {
        "aws_region": "{{env `AWS_REGION`}}",
        "aws_ami_name": "amazon-linux_{{isotime \"02Jan2006\"}}"
    },

    "builders": [{
        "type": "amazon-ebs",
        "region": "{{user `aws_region`}}",
        "instance_type": "t2.micro",
        "ssh_username": "ec2-user",
        "ami_name": "{{user `aws_ami_name`}}",
        "ami_description": "Customized Amazon Linux",
        "associate_public_ip_address": "true",
        "source_ami_filter": {
            "filters": {
                "virtualization-type": "hvm",
                "name": "amzn-ami*-ebs",
                "root-device-type": "ebs"
            },
            "owners": ["137112412989", "591542846629", "801119661308", "102837901569", "013907871322", "206029621532", "286198878708", "443319210888"],
            "most_recent": true
        }
    }],

    "provisioners": [
        {
            "type": "shell",
            "inline": [
                "sudo yum update -y",
                "sudo /usr/sbin/update-motd --disable",
                "echo 'No unauthorized access permitted' | sudo tee /etc/motd",
                "sudo rm /etc/issue",
                "sudo ln -s /etc/motd /etc/issue",
                "sudo yum install -y elinks screen"
            ]
        }
    ]
}

provisioners の項目は、builders によって仮想マシンが作成された後でありマシンイメージとして保存される前の段階において、仮想マシンを設定するために Packer によって使用される shell コマンドを規定しています。provisioners の項目は、パッケージ管理システムを更新し、一時キーをクリーンアップします。より詳細な情報は、HashiCorp Packer のウェブサイトの Provisioners を参照してください。

AWS CodeBuild とそこで使用されるコンテナは、お客様の VPC の外で動作します。ビルドプロジェクトの一部として実行されるいかなる動作も、VPC 内のプライベートIPアドレスにアクセスすることはできません。結果として、ビルドする EC2 インスタンスにはパブリックIPアドレスが付与されなければいけません。そうすることで、HashiCorp Packer がインスタンスにリモートで接続しシステムを設定することができるようになります。もしインスタンスがデフォルト VPC 以外で起動される場合は、HashiCorp Packer に VPC と パブリックサブネットを教えなけれいけません。インターネット越しにインスタンスに SSH 接続することができる必要があります。

インスタンスを実際に起動している時間は短いのですが、悪意のある動作から保護し不必要な接続を弾くために、インスタンスのデプロイに際して HashiCorp Packer によって新規に作成されたキーペアとセキュリティグループが使用されます。AMI 作成後にそれらは削除されます。

source_ami_filter は source_ami の代替であり、インスタンスの作成のためにどの AMI が使用されるべきかを示します。source_ami_filter は、使用するリージョンで利用可能な AMI を問い合わせて、フィルターの基準に最もマッチする AMI を選択します。今回の例では、source_ami_filter は amzn-ami*-ebs という式にマッチする最も最新の AMI を探し出すように設定されています。このフィルターは、限定されたいくつかの AWS アカウントによって所有されている AMI にのみマッチするように制限されています。列挙されている AMI オーナーは AWS によって管理・所有されるアカウントです。

ビルドスペックを作成する

ビルドのプロセスの様々なフェーズでユーザー定義のコマンドを実行するために AWS CodeBuild によって buildspec.yml というファイルが使用されます。このファイルは、テンプレートを実行するために HashiCorp Packer をどのように使用するかを AWS CodeBuild に指示します。

buildspec.yml は次のようになります。

---
version: 0.2

phases:
  pre_build:
    commands:
      - echo "HashiCorp Packer をインストール中..."
      - curl -qL -o packer.zip https://releases.hashicorp.com/packer/0.12.3/packer_0.12.3_linux_amd64.zip && unzip packer.zip
      - echo "jq をインストール中..."
      - curl -qL -o jq https://stedolan.github.io/jq/download/linux64/jq && chmod +x ./jq
      - echo "amazon-linux_packer-template.json をバリデーションします"
      - ./packer validate amazon-linux_packer-template.json
  build:
    commands:
      ### HashiCorp Packer cannot currently obtain the AWS CodeBuild-assigned role and its credentials
      ### Manually capture and configure the AWS CLI to provide HashiCorp Packer with AWS credentials
      ### More info here: https://github.com/mitchellh/packer/issues/4279
      - echo "AWS credentials を設定"
      - curl -qL -o aws_credentials.json http://169.254.170.2/$AWS_CONTAINER_CREDENTIALS_RELATIVE_URI > aws_credentials.json
      - aws configure set region $AWS_REGION
      - aws configure set aws_access_key_id `./jq -r '.AccessKeyId' aws_credentials.json`
      - aws configure set aws_secret_access_key `./jq -r '.SecretAccessKey' aws_credentials.json`
      - aws configure set aws_session_token `./jq -r '.Token' aws_credentials.json`
      - echo "HashiCorp Packer のテンプレート amazon-linux_packer-template.json をビルド"
      - ./packer build amazon-linux_packer-template.json
  post_build:
    commands:
      - echo "HashiCorp Packer によるビルドが完了しました。 `date`"

pre_build の間、このビルドファイルによって Packer テンプレートの実行に必要なツール群が AWS CodeBuild のコンテナに設定されます。1つめのツールは HashiCorp Packer です。HashiCorp のウェブサイトからダウンロードできます。2つ目のツールは jp ユーティリティです。JSON ファイルを解析するのに使用されます。この pre_build フェーズの最後のコマンドによって、構文エラーがないことを確かめるために HashiCorp Packer テンプレートのバリデーションが行われます。

build フェーズでは、このビルドファイルによってメタデータURLを利用してビルド用コンテナの認証情報が設定されます。このメタデータから、HashiCorp Packer によって AWS CodeBuild プロジェクトに事前に紐付けされた IAM ロールにより提供される AWS 認証情報を得ることができます。そのおかげで AWS CodeBuild があなたに代わって EC2 リソースを作成することができるのです。最後のステップとして HashiCorp Packer テンプレートが実行され、 EC2 AMI のビルドが行われます。

AWS CodeBuild プロジェクトを実行する

これら新しいファイルをリポジトリにコミットし、AWS CodeCommit にプッシュしてください。これで、AWS CodeBuild に AMI を作成させる準備が整いました。

  1. AWS マネジメントコンソールから AWS CodeBuild のコンソールに移動します。
  2. ビルドプロジェクト一覧から今回作成したプロジェクトを選択し、Start build を選択します。
  3. Start new build では、AMI をビルドするために使用すべき AWS CodeCommit リポジトリのブランチとリビジョンを選択してください。
  4. Branch のドロップダウンリストから master を選択します。すると、リポジトリにプッシュした最新のコミットが Source version フィールドに反映されます。
  5. Start build を選択します。AWS CodeBuild によってリポジトリの buildspec.yml ファイルが実行されます。

CodeBuild project start

実行したビルドの各フェーズにおけるステータスと結果が表示されたページに切り替わります。全てのステージに Succeeded と表示されるはずです。もし何かしらのエラーが起きた場合、エラーメッセージがないか確認するためにページの下部においてビルドのログを見ることができます。

CodeBuild project status

ビルドしている間、HashiCorp Packer は一時的なキーペアを生成し、EC2 インスタンスを起動し、その生成されたキーペアを使ってインスタンスにリモート接続し、そしてマシンをプロビジョニングします。その後、インスタンスを AMI に変換し AMI をビルドするために作成したすべてリソースを片付けます。これらのステップが完了すると、ami-1a2b3cde のような感じの自動生成 ID を持った AMI を示す内容がビルドのログに出力されます。

この AMI は、任意のタイミングで EC2 インスタンスを作成したり、インフラストラクチャを柔軟にスケールさせるために Auto Scaling グループの設定に利用したりすることが可能です。

次のステップ

この最初の記事の情報があなたの役に立ち、コードパイプラインとしてのインフラストラクチャの出発点として活用されることを願っています。この記事では、HashiCorp Packer テンプレートの形式でインフラストラクチャの一部をコードとして作成しました。HashiCorp Packer テンプレートをベースにした AMI を作るために AWS CodeBuild を使用しました。

  • 2つ以上の HashiCorp Packer テンプレートのビルド。異なるソース AMI をベースにして独自の AMI をビルドするために、複数の HashiCorp Packer テンプレートを使うことも可能です。きっと一方は Ubuntu でもう一方は Microsoft Windows などでしょう。
  • AWS CodePipeline に AWS CodeCommit リポジトリの監視設定の追加。新しいバージョンがコミットされたときに自動で AWS CodePipeline によって AWS CodeBuild が呼び出され AMI を再作成するようにします。
  • 自動化されたコンプライアンスを実装するために CloudWatch Events を活用。あなたのアカウント内で起動されたすべての EC2 インスタンスが事前設定された独自 AMI のうちのどれかを使っていることを確認するために AWS Lambda 関数を利用することができます。

2部構成の次の2つ目の記事では、エンドツーエンドで新しい AMI を継続的にリリースするために AWS CodePipeline、AWS CloudFormation、そして Amazon Cloudwatch Events などの AWS サービスを利用する予定です。

翻訳はSA畑史彦が担当しました。原文はこちらです。