ちょっと話題の記事

Packer+AnsibleによるAMIの作成

2014.12.15

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

渡辺です。

クラスメソッドのAWSチームは基本的に自分が使いやすいツールを選択するので統一されていません。 が、誰からいいね!と推しはじめると流行していく、そんな雰囲気です。

Ansible

AnsibleはChefなどと同じ構成管理ツールのひとつで、AWSチームではAnsibleを使う人が多くなってきました。Ansibleの紹介は、構成管理ツール Ansibleを使ってみるを読んでみてください。

Packer

Packerも広義には構成管理ツールですが、マシンイメージを作成するためのツールです。 AWSで言えばEBSマシンイメージを作るのに利用できます。 Packerについては、PackerでAmazon LinuxのAMI(Amazon Machine Image)を作成するを参照ください。

PackerでAnsibleによる構成管理

Packerはマシンイメージを作成するためのツールで簡単なスクリプトなどを実行することができます。 しかし、複雑な構成管理となるとChefやAnsibleなどのように多機能というわけではなく、シェルスクリプト地獄に陥ってしまうのが難点です。 そもそもシェルスクリプトだけで構成管理が可能であれば、EC2のUserDataを使えば済むわけなので、わざわざPackerを利用することもないでしょう。

builderとprovisioner

PackerではAWSのイメージを作る部分はbuilderと呼びます。 builderはAMI以外でも、VirtualBoxなどの仮想マシンイメージを作るのに利用することができます。

一方、構築用の定義はprovisionerに定義します。 シェルスクリプトを実行するのであれば、shell provisionerを指定します。 shell provisionerの他にもchefやansibleもprovisionerとして対応しているため、好きな構成管理ツールで構成を定義し、Packerでイメージを作るという使い方が出来るわけです。

というわけで、Ansibleで構成管理を行いAMIを作成してみました。

AnsibleのPlaybookの作成

AnsibleのPlaybookは、構成の定義ファイルです。 Apacheとmysql クライアントをインストールし、起動時にApacheを起動する簡単な定義ファイルを作成しました。

setup.yml

- hosts: all
  sudo: yes
  tasks:
   - name: install Apache24 and mysql client
     yum: pkg={{item}} state=installed
     with_items:
      - mysql
      - httpd24
   - name: enable httpd service
     service: name=httpd enabled=yes

ポイントとしては、ec2-userでAnsibleが実行されるため、sudoをyesに設定することです。

Packerのテンプレートの作成

続けてPackerのテンプレートを作成します。

web.json

{
  "variables": {
    "aws_access_key": "",
    "aws_secret_key": ""
  },
  "builders": [{
    "type": "amazon-ebs",
    "access_key": "{{user `aws_access_key`}}",
    "secret_key": "{{user `aws_secret_key`}}",
    "region": "ap-northeast-1",
    "source_ami": "ami-4985b048",
    "instance_type": "t2.small",
    "ssh_username": "ec2-user",
    "ssh_timeout": "5m",
    "ami_name": "Web_{{isotime | clean_ami_name}}"
  }],
  "provisioners": [{
    "type": "shell",
    "inline": [
      "sudo yum -y update",
      "sudo yum -y --enablerepo=epel install ansible"
    ]
   },{
    "type": "ansible-local",
    "playbook_file": "setup.yml"
  }]
}

buildersにamazon-ebsを指定し、PackerがAnsibleを使い実行する環境を定義します。 Ansibleはlocalで実行する、つまりEC2インスタンスで実行され自分自身を構成する方式です。 通常のAnsibleのようにローカル環境からSSHで接続して実行する形式は現時点ではサポートされていないので注意してください。

また、AWSのアクセスキーとシークレットキーを定義する必要がありますが、このテンプレートファイルはgitなどでバージョン管理するため生情報を埋め込んではいけません。 PackerのVariablesの機能を利用し、外部のVariableファイルに定義してください。 Variableファイルは.gitignoreなどでバージョン管理外としておきます。

variables.json

{
    "aws_access_key": "[YOUR_AWS_ACCESS_KEY]",
    "aws_secret_key": "[YOUR_AWS_SECRET_KEY]"
}

provisionersには構成管理に必要な定義を記述していきます。 ここはansible-localを指定すれば良いのですが、実行するEC2インスタンスにAnsibleがインストールされていないとコケてしまうため、ansibleのインストールはPackerのshell provisionerでインストールしておきます。 ansible-localでの設定は、先ほど作成したsetup.ymlを指定するだけです、簡単ですね。

ビルド

ビルドはPackerコマンドで、Variableファイルをオプションで指定して実行します。

$ packer build -var-file=variables.json web.json

ビルドがはじまると、AWS環境にEC2インスタンスが作成され、環境構築がはじまります。 はじめにshell provisionerでAnsibleがインストールされ、後続のansible-local provisionerで必要なミドルウェアや環境設定が行われていきます。 構築が完了すると、AMI(マシンイメージ)が作成され、構築用に作成されたEC2インスタンスは破棄されます。

$ packer build -var-file=variables.json web.json 
amazon-ebs output will be in this color.

==> amazon-ebs: Inspecting the source AMI...
==> amazon-ebs: Creating temporary keypair: packer 548e8a9c-cbff-a528-8933-c274c1a5666e
==> amazon-ebs: Creating temporary security group for this instance...
==> amazon-ebs: Authorizing SSH access on the temporary security group...
==> amazon-ebs: Launching a source AWS instance...
    amazon-ebs: Instance ID: i-72c8e36b
==> amazon-ebs: Waiting for instance (i-72c8e36b) to become ready...
==> amazon-ebs: Waiting for SSH to become available...
==> amazon-ebs: Connected to SSH!
==> amazon-ebs: Provisioning with shell script: /var/folders/k4/k385q7zs26z3h5ngv4d1rwk80000gn/T/packer-shell972002311
(中略)
    amazon-ebs: Complete!
==> amazon-ebs: Provisioning with Ansible...
    amazon-ebs: Creating Ansible staging directory...
    amazon-ebs: Creating directory: /tmp/packer-provisioner-ansible-local
    amazon-ebs: Uploading main Playbook file...
    amazon-ebs: Uploading inventory file...
(中略)
==> amazon-ebs: Stopping the source instance...
==> amazon-ebs: Waiting for the instance to stop...
==> amazon-ebs: Creating the AMI: Web_2014-12-15T07-15-40Z
    amazon-ebs: AMI: ami-XXXXXXXXX
==> amazon-ebs: Waiting for AMI to become ready...
==> amazon-ebs: Terminating the source AWS instance...
==> amazon-ebs: Deleting temporary security group...
==> amazon-ebs: Deleting temporary keypair...
Build 'amazon-ebs' finished.

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:

最後にAMIのイメージIDが表示されるのでメモしましょう。

PackerAnsible

なお、構築を何度もしているとこのようにterminatedのインスタンスまみれになりますw

AMIの活用

Packerを利用するパターンではインスタンスを予め作りAMIとして保存してしまうため、CloudFormationとの相性が抜群に良いです。 CloudFormationで面倒なUserDataやCloudInitを利用せずに、構成管理は専門のツールを使うワケです。 また、AMIはバージョンアップ時にも残しておくことで、EC2インスタンスのバージョンも簡単に管理できますね。