share facebook facebook2 twitter menu hatena pocket slack

2015.06.25 THU

AnsibleでPacker用のVPCの作ってから”Amazon Linux”ベースのAMIを作成する

鈴木 宏康

WRITTEN BY 鈴木 宏康

下記のスクリプト群で実現してみました。

$ tree ./
./
├── ansible
│   └── packer
│       └── ansible-amazonlinux.yml
├── cloudformation
│   └── packer.json
├── create-ami.sh
└── packer
    └── ansible-amazonlinux.json

次のように実行することで、VPCが作成されたあとにPackerが、そのVPC内にEC2作って、
いろいろとインストールした後、EC2からAMIを作成してくれます。

./create-ami.sh

create-ami.sh

#!/bin/sh
EXTRA_VARS=$(cat <<EOS
    Name=${Name:-suz-lab}
    Region=`curl -s http://169.254.169.254/latest/meta-data/placement/availability-zone | sed -e 's/.$//'`
EOS
)
ansible-playbook -v -e "$EXTRA_VARS" ./ansible/packer/ansible-amazonlinux.yml

必要な変数を設定して、AnsibleのPlaybookを実行しています。

リージョンの指定(自分がいるリージョンを取得)は
“AWS CLI”で最新の”Amazon Linux AMI”(gp2)のIDを取得する
で紹介したものを使っています。

ansible-amazonlinux.yml

- hosts: localhost
  gather_facts: no
  connection: local
  tasks:
 
  - name: cloudformation
    cloudformation:
      state: present
      region: "{{ Region }}"
      template: ../../cloudformation/packer.json
      stack_name: Packer
    register: cloudformation
 
  - name: epoch
    shell: date +%s
    register: epoch
 
  - name: get amazonlinux ami
    shell: >
      aws ec2 describe-images
      --region {{ Region }}
      --owners amazon
      --filters "Name=name,Values=amzn-ami-hvm-*-gp2"
      --query 'reverse(sort_by(Images,&CreationDate))[0].ImageId'
      --output text
    register: amazonlinux_image_id
 
  - name: packer
    shell: >
      packer build 
      -var 'Region={{ Region }}'
      -var 'VpcId={{ cloudformation.stack_outputs.Vpc }}'
      -var 'SubnetId={{ cloudformation.stack_outputs.Subnet }}'
      -var 'Name={{ Name }}'
      -var 'Epoch={{ epoch.stdout }}'
      -var 'SourceAmi={{ amazonlinux_image_id.stdout }}'
      ../../packer/ansible-amazonlinux.json

最初にCloudFormationでVPCを作っています。
理由としては、僕が扱ってるアカウントがEC2-Classicが使えたり使えなかったり、
デフォルトVPCがあったり無かったり、だったので、
どこでも同じように利用できるためにPacker用のVPCを、まず作ることにしました。

最初は”AWS CLI”で作ってたんですが、CloudFromationが作成し終わってから、
次の処理を実施する場合、別途CloudFormationの状態チェックを行う処理(ループ)を
作り込む必要があり、それが面倒だったのでAnsibleの利用に切替ました。

上記のPlaybookは、CloudFormationの構築が完了した後に、次のタスクが実行されるので、
状態チェックのループを作り込む必要はありません。
また既にCloudFormationのStackがある場合、テンプレートの修正があった場合は
勝手にUpdateになり、ない場合はスキップもしてくれるので、その辺も楽でした。
(これが噂の冪等性ってやつですね!)

そして、下記で紹介した方法で最新の”Amazon Linux AMI”(gp2)のIDを取得して、
“AWS CLI”で最新の”Amazon Linux AMI”(gp2)のIDを取得する
最後に、VPCやらAMIやらの情報をパラメータとしてPackerに渡してAMIを作成しています。

packer.json

{
    "AWSTemplateFormatVersion": "2010-09-09",
    "Parameters": {
        "VpcCidrBlock": {
            "Type": "String",
            "Default": "10.0.0.0/16"
        }
    },
    "Resources": {
        "Vpc": {
            "Type": "AWS::EC2::VPC",
            "Properties": {
                "CidrBlock": { "Ref": "VpcCidrBlock" },
                "EnableDnsSupport": "true",
                "EnableDnsHostnames": "true",
                "InstanceTenancy": "default",
                "Tags": [ {
                    "Key": "Name",
                    "Value": "Packer"
                } ]
            }
        },
        "InternetGateway": {
            "Type": "AWS::EC2::InternetGateway",
            "Properties": {
                "Tags": [ {
                    "Key": "Name",
                    "Value": "Packer"
                } ]
            }
        },
        "VpcGatewayAttachment": {
            "Type": "AWS::EC2::VPCGatewayAttachment",
            "Properties": {
                "VpcId": { "Ref": "Vpc" },
                "InternetGatewayId": { "Ref": "InternetGateway" }
            }
        },
        "RouteTable": {
            "Type": "AWS::EC2::RouteTable",
            "Properties": {
                "VpcId": { "Ref": "Vpc" },
                "Tags": [ {
                    "Key": "Name",
                    "Value": "Packer"
                } ]
            }
        },
        "Route" : {
            "Type": "AWS::EC2::Route",
            "Properties": {
                "RouteTableId": { "Ref": "RouteTable" },
                "DestinationCidrBlock": "0.0.0.0/0",
                "GatewayId": { "Ref": "InternetGateway" }
            }
        },
        "Subnet" : {
            "Type": "AWS::EC2::Subnet",
            "Properties": {
                "VpcId": { "Ref": "Vpc" },
                "CidrBlock": { "Ref": "VpcCidrBlock" },
                "AvailabilityZone": { "Fn::Select": [ "0", { "Fn::GetAZs": { "Ref": "AWS::Region" } } ] },
                "Tags": [ {
                    "Key": "Name",
                    "Value": "Packer"
                } ]
            }
        },
        "SubnetRouteTableAssociation": {
            "Type": "AWS::EC2::SubnetRouteTableAssociation",
            "Properties": {
                "SubnetId": { "Ref": "Subnet" },
                "RouteTableId": { "Ref": "RouteTable" }
            } 
        }
    },
    "Outputs": {
        "Vpc": {
            "Value": { "Ref": "Vpc" }
        },
        "Subnet": {
            "Value": { "Ref": "Subnet" }
        }
    }
}

こんな感じのVPCを作ってます。
Packerは、このパブリックサブネットの中にEC2を起動します。

0VMOC1BrD6dDB4DA-725BC

ansible-amazonlinux.json

{
    "variables": {
        "Region": "",
        "VpcId": "",
        "SubnetId": "",
        "Name": "",
        "Epoch": "",
        "SourceAmi": ""
    },
    "builders": [ {
        "type": "amazon-ebs",
        "region": "{{user `Region`}}",
        "source_ami": "{{user `SourceAmi`}}",
        "instance_type": "t2.micro",
        "ssh_username": "ec2-user",
        "vpc_id": "{{user `VpcId`}}",
        "subnet_id": "{{user `SubnetId`}}",
        "associate_public_ip_address": "true",
        "enhanced_networking": "true",
        "ami_name": "{{user `Name`}}_{{user `Epoch`}}"
    } ],
    "provisioners": [
        {
            "type": "shell",
            "inline": [
                "sudo yum -y update",
                "sudo pip install ansible",
                "sudo mkdir /etc/ansible",
                "sudo touch /etc/ansible/hosts",
                "rm -f /home/ec2-user/.ssh/authorized_keys"
            ]
        }
    ]
}

パラメータで渡ってきたVPCやAMIの情報を使ってAMIを作成しています。
“Shell Provisioner”で”Ansible (Local) Provisioner”が利用できる環境を作ってます。

無事、実行されると、下記のようにAMIが作成されてます。

2015-06-17_00-16-38

元記事はこちら

AnsibleでPacker用のVPCの作ってから”Amazon Linux”ベースのAMIを作成する

鈴木 宏康

鈴木 宏康

愛知県生まれ。東京工業大学大学院修士課程修了。在学時より、ベンチャー企業でインターネットに関する業務に携わり、現在はクラウド(主にAmazon Web Services)上での開発・運用を軸とした事業の、業務の中心として活躍。