AWS
ami
Ansible
packer
10分で理解するシリーズ

Packer とは

サーバーイメージを作成するためのツールです
AWSであればAMI、Azureであれば arm を作成します

Vagrant を開発している HashiCorp社 が開発しているツールなので使用感は Vagrant と同じような感じです

構築済みのサーバーイメージをAMIとして作成しておき、これをベースとしてインスタンスを立ち上げるのはよくあるパターンだと思います
"ゴールデンイメージ" というインフラパターンらしいです

ただ、このAMIがどうやって作られたか?がわからなくなりがちなのですが、Packerを使うことでAMI構築手順を全てコード化することができます

TL;DR

json ファイルに定義内容を記述し、$ packer build hoge.json を実行するだけで定義内容の通りに自動でサーバーイメージを作成してくれます

インストール

以下に従って OS 毎のビルド済みバイナリをダウンロードしてきてパスを通すだけです
https://www.packer.io/downloads.html

Mac の場合

Mac の場合は Homebrew でインストールできます

Homebrewでpackerインストール
$ brew update
$ brew install packer

AWS での使い方

事前準備

IAM ユーザーを作る

aws cli を利用するための アクセスキーを取得します
管理者に依頼して IAM ユーザーを作成してもらってください
https://console.aws.amazon.com/iam/home?#/home

利用したい AWS サービス(EC2だけかな?)に対しての操作権限をつけてもらう必要があります

AWS CLI をセットアップする

以下を参考に aws コマンドを使えるように設定する
http://docs.aws.amazon.com/ja_jp/streams/latest/dev/kinesis-tutorial-cli-installation.html

$ aws configure で認証情報をセットした後であれば、Pakcer からはこのAWS認証情報を参照してくれます
~/.aws/credentials にAWSの認証情報が保存されます

AMIを作成する

1. json ファイルを作成する

例として以下の AMI を作成する定義を見てみましょう

項目 設定値 メモ
リージョン ap-northeast-1 (東京)
ベースのAMI 公式 ubuntu 16.04 の最新版
インスタンスタイプ t2.micro
ssh ユーザー名 ubuntu
AMIの名前 Packer test YYYY-MM-DDThh-mm-ssZ 作成時のタイムスタンプがセットされる
ディスク 30GB、gp2 の EBS を /dev/sda1 として接続
初期化コマンド apt update を実行してから python-simplejson をインストール

この AMI 定義は以下のように json ファイルで書きます
定義できる項目は packer のドキュメントを参照のこと
https://www.packer.io/docs/builders/amazon.html

packer-example.json
{
  "builders": [{
    "type": "amazon-ebs",
    "region": "ap-northeast-1",
    "source_ami_filter": {
      "filters": {
        "virtualization-type": "hvm",
        "name": "*ubuntu-xenial-16.04-amd64-server-*",
        "root-device-type": "ebs"
      },
      "owners": ["099720109477"],
      "most_recent": true
    },
    "instance_type": "t2.micro",
    "ssh_username": "ubuntu",
    "ami_name": "Packer Test {{isotime | clean_ami_name}}",
    "launch_block_device_mappings": [{
      "delete_on_termination": true,
      "device_name": "/dev/sda1",
      "volume_size": 10,
      "volume_type": "gp2"
    }]
  }],
  "provisioners": [{
    "type": "shell",
    "inline": [
      "sudo apt update",
      "sudo apt install -y python-simplejson",
      "sudo echo `date`' - packer provisioned this AMI' > /home/ubuntu/packer_provisioners"
    ]
  }]
}

2. 構文チェック

$ packer validate を実行すると構文チェックされます

$ packer validate packer-example.json

以下のメッセージが出力されれば構文チェックはOKです

Template validated successfully.

もしエラーがある場合はエラーメッセージが出力されます

$ packer build packer-example.json 
Failed to parse template: Error parsing JSON: invalid character '"' after object key:value pair
At line 4, column 6 (offset 49):
    3:     "type": "amazon-ebs"
    4:     "
           ^

3. ビルド実行

$ packer build を実行して AMI をビルドします

$ packer build packer-example.json

初期化タスクの内容にもよりますが AMI ビルドには数分かかります
以下のように AMIs were created と出力されればビルド成功です

・・・
Build 'amazon-ebs' finished.

==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:
ap-northeast-1: ami-c5f49528

ビルド中 EC2 のコンソールを開くと "Packer Builder" というインスタンスが起動されて、このインスタンス内で処理が実行され定義した内容の AMI が作成されます

packer_aws_1.jpg

ビルドが完了すると以下のように AMI が追加されていることが確認できます
packer_aws_2.jpg

[Advanced] イメージの初期化処理を Ansible でやる

packer で provisioners で設定したタスクによりイメージの初期化を行うことができます
https://www.packer.io/docs/provisioners/index.html

↑のサンプルでは Shell Provisioner を使うことで、イメージとなるインスタンスの内部で指定されたシェルコマンドが実行されていたということです

Ansible Provisioner というのを使うと、初期化タスクを ansible に任せることができます
先ほどの packer-example.json を修正し Provisioner に ansible を指定します
playbook_file に指定された playbook に沿って ansible が実行されます
※playbook の書き方はここでは割愛します

packer-ansible.json
{
  "builders": [{
    "type": "amazon-ebs",
    "region": "ap-northeast-1",
    "source_ami_filter": {
      "filters": {
        "virtualization-type": "hvm",
        "name": "*ubuntu-xenial-16.04-amd64-server-*",
        "root-device-type": "ebs"
      },
      "owners": ["099720109477"],
      "most_recent": true
    },
    "instance_type": "t2.micro",
    "ssh_username": "ubuntu",
    "ami_name": "Packer Ansible Example {{isotime | clean_ami_name}}",
    "launch_block_device_mappings": [{
      "delete_on_termination": true,
      "device_name": "/dev/sda1",
      "volume_size": 30,
      "volume_type": "gp2"
    }]
  }],
  "provisioners": [{
    "type": "shell",
    "inline": [
      "sudo apt update",
      "sudo apt install -y python-simplejson"
    ]
  }, {
    "type": "ansible",
    "playbook_file": "./ansible/playbook.yml"
  }]
}