PackerのAnsibleProvisionerでインベントリ毎のgroup_varsを使い分ける

PackerのAnsibleProvisionerでインベントリ毎のgroup_varsを使い分ける:


はじめに

この記事は、エーピーコミュニケーションズ Advent Calendar 2018 の22日目のエントリです。

今回もAnsible絡みですが、Packerを使ってAWS EC2にAMIをデプロイします。

PackerにはProvisionerというVM起動後にインストール作業や設定変更ができる機能があり、

その中にAnsible Provisionerがあるのでそちらを使います。

PackerのProvisionerにはAnsibleの他にもShellやPowerShell, Chef, Puppetなどなど各種あるのでお好みのツールを使えると思います。

ちなみにもう一つAnsible Local Provisionerというものがありますが、そちらは試していません。


やりたいこと

Packerは基本的に1台のマシンイメージ(AMI)を作成するツールですが、

Ansibleは複数のマシンをホスト名やグループ名によってインストールするパッケージなどの処理を変えながら構築していくことを多いと思われます。

こういった使い方をPackerで実現するため、所属するグループをサーバの役割毎に動的に変更させることによって複数種類のAMIを作成させます。

また、Packerではインベントリを使用せずにPlaybookのみで動作させることができます。

クラウドでは基本的にIPやホスト名が動的に割り当てられるため、あらかじめインベントリを作成しておくのが困難なので、作成する必要がないのは楽ですね。

インベントリを使用しなくてもPlaybook側のgroup_varsを使用すれば上記の挙動は実現可能ですが、

Ansible BestpracticeにあるAlternative Directory Layoutの例のように

「商用とステージングでPlaybookとグループは共通のものを使いたいけど、IPアドレスやURLなどの変数には別々の値を使いたい」という場合には

複数のインベントリファイル・ディレクトリに分けて使い分ける、という使い方をしているケースも多く、

その場合にはインベントリのhost_vars, group_varsを使い分ける必要があります。

インベントリのディレクトリ構成例(Best Practicesから抜粋)

inventories/ 
   production/ 
      hosts               # inventory file for production servers 
      group_vars/ 
         group1.yml       # here we assign variables to particular groups 
         group2.yml 
      host_vars/ 
         hostname1.yml    # here we assign variables to particular systems 
         hostname2.yml 
 
   staging/ 
      hosts               # inventory file for staging environment 
      group_vars/ 
         group1.yml       # here we assign variables to particular groups 
         group2.yml 
      host_vars/ 
         stagehost1.yml   # here we assign variables to particular systems 
         stagehost2.yml 


Packerでインベントリのgroup_varsを使う良い方法が調べていたのですが、

意外にもあまり情報が見つからなかったのでPackerの公式などを調べて動かした結果を書きます。




書き方


ファイル構成

packerコマンドを実行するディレクトリからの相対的なファイル構成は以下のようにしました。

ファイル構成
. 
├── ansible 
│   ├── group_vars 
│   │   ├── all.yml 
│   │   ├── db.yml 
│   │   └── web.yml 
│   ├── inventory 
│   │   ├── production 
│   │   │   └── group_vars 
│   │   │       └── all.yml 
│   │   └── staging 
│   │       └── group_vars 
│   │           └── all.yml 
│   └── play.yml 
└── packer.json 
packer.jsonがpackerコマンドで引数となる設定ファイルで、今回は実行ディレクトリ直下においています。

そしてansible/inventory/productionおよびansible/inventory/stagingがインベントリに当たるディレクトリですが、

ポイントとしては、インベントリファイルは作成しないで、group_varsのみ(必要ならhost_varsも)を置いている点です。

そしてPackerの設定ファイルは以下のように書きました。

packer.json
{ 
    "variables": { 
        "environment": "staging", 
        "instance_type": "t2.micro", 
        "source_ami": "ami-0a2de1c3b415889d2", 
        "role": "" 
    }, 
    "builders": [ 
        { 
            "type": "amazon-ebs", 
            "ami_name": "packer_ami_{{timestamp}}", 
            "instance_type": "{{user `instance_type`}}", 
            "source_ami": "{{user `source_ami`}}", 
            "ssh_username": "ec2-user", 
            "tags": [ 
                { 
                    "Name": "packer_ami" 
                } 
            ] 
        } 
    ], 
    "provisioners": [ 
        { 
            "playbook_file": "ansible/play.yml", 
            "inventory_directory": "ansible/inventory/{{user `environment`}}", 
            "type": "ansible", 
            "user": "ec2-user", 
            "groups": ["{{user `role`}}"], 
            "extra_arguments": [] 
        } 
    ] 
} 
ポイントとしては
provisionersのディレクティブでinventory_directoryを指定して、

その上でgroupsにグループ名を指定する

ということです。

それでは実際に動作確認してみましょう。

Playbookは以下のようにしています。

ansible/play.yml
- hosts: all 
  become: true 
  tasks: 
  - name: インベントリのgroup_vars/all.ymlの確認 
    debug: 
      msg: "my environment is {{ env }}" 
 
  - name: プレイブックのgroup_vars/all.ymlの確認 
    yum: 
      name: "{{ common_package }}" 
 
  - name: プレイブックのgroup毎のgroup_varsの確認 
    yum: 
      name: "{{ item }}" 
    loop: "{{ dist_package }}" 
 
- hosts: web 
  tasks: 
  - name: webグループの挙動確認 
    debug: 
      msg: "I am Web!!!" 
 
- hosts: db 
  tasks: 
  - name: dbグループの挙動確認 
    debug: 
      msg: "I am db!!!" 
各種ファイルに書いた変数はこちら

:::::::::::::: 
ansible/group_vars/all.yml 
:::::::::::::: 
common_package: git 
:::::::::::::: 
ansible/group_vars/db.yml 
:::::::::::::: 
dist_package: 
- mysql 
- mariadb-server 
:::::::::::::: 
ansible/group_vars/web.yml 
:::::::::::::: 
dist_package: 
- httpd 
- php 
:::::::::::::: 
ansible/inventory/production/group_vars/all.yml 
:::::::::::::: 
env: production 
:::::::::::::: 
ansible/inventory/staging/group_vars/all.yml 
:::::::::::::: 
env: staging 
Packerでは-var arg=varという形式でvariablesで指定した変数を上書きできます。(--varではなくハイフン1つなので注意)

実行結果は以下のようになりました。(Packerのメタな部分は適度にボカしてます)

実行結果
$ packer build -var environment=production -var role=web packer.json 
amazon-ebs output will be in this color. 
 
==> amazon-ebs: Prevalidating AMI Name: packer_ami_1545451854 
    amazon-ebs: Found Image ID: ami-0a2de1c3b415889d2 
==> amazon-ebs: Creating temporary keypair: packer_*************** 
==> amazon-ebs: Creating temporary security group for this instance: packer_*************** 
==> amazon-ebs: Authorizing access to port 22 from 0.0.0.0/0 in the temporary security group... 
==> amazon-ebs: Launching a source AWS instance... 
==> amazon-ebs: Adding tags to source instance 
    amazon-ebs: Adding tag: "Name": "Packer Builder" 
    amazon-ebs: Instance ID: i-*************** 
==> amazon-ebs: Waiting for instance (i-***************) to become ready... 
==> amazon-ebs: Using ssh communicator to connect: *************** 
==> amazon-ebs: Waiting for SSH to become available... 
==> amazon-ebs: Connected to SSH! 
==> amazon-ebs: Provisioning with Ansible... 
==> amazon-ebs: Executing Ansible: ansible-playbook --extra-vars packer_build_name=amazon-ebs packer_builder_type=amazon-ebs -i ansible/inventory/production /home/hogehoge/code/packer-ansible/ansible/play.yml -e ansible_ssh_private_key_file=/tmp/ansible-key657870806 
    amazon-ebs: 
    amazon-ebs: PLAY [all] ********************************************************************* 
    amazon-ebs: 
    amazon-ebs: TASK [Gathering Facts] ********************************************************* 
    amazon-ebs: ok: [default] 
    amazon-ebs: 
    amazon-ebs: TASK [インベントリのgroup_vars/all.ymlの確認] ******************************************** 
    amazon-ebs: ok: [default] => { 
    amazon-ebs:     "msg": "my environment is production" 
    amazon-ebs: } 
    amazon-ebs: 
    amazon-ebs: TASK [プレイブックのgroup_vars/all.ymlの確認] ******************************************** 
    amazon-ebs: changed: [default] 
    amazon-ebs: 
    amazon-ebs: TASK [プレイブックのgroup毎のgroup_varsの確認] ********************************************* 
    amazon-ebs: changed: [default] => (item=httpd) 
    amazon-ebs: changed: [default] => (item=php) 
    amazon-ebs: 
    amazon-ebs: PLAY [web] ********************************************************************* 
    amazon-ebs: 
    amazon-ebs: TASK [Gathering Facts] ********************************************************* 
    amazon-ebs: ok: [default] 
    amazon-ebs: 
    amazon-ebs: TASK [webグループの挙動確認] ************************************************************ 
    amazon-ebs: ok: [default] => { 
    amazon-ebs:     "msg": "I am Web!!!" 
    amazon-ebs: } 
    amazon-ebs:  [WARNING]: Could not match supplied host pattern, ignoring: db 
    amazon-ebs: 
    amazon-ebs: PLAY [db] ********************************************************************** 
    amazon-ebs: skipping: no hosts matched 
    amazon-ebs: 
    amazon-ebs: PLAY RECAP ********************************************************************* 
    amazon-ebs: default                    : ok=6    changed=2    unreachable=0    failed=0 
    amazon-ebs: 
==> amazon-ebs: Stopping the source instance... 
    amazon-ebs: Stopping instance, attempt 1 
==> amazon-ebs: Waiting for the instance to stop... 
==> amazon-ebs: Creating unencrypted AMI packer_ami_1545451854 from instance i-*************** 
    amazon-ebs: AMI: ami-*************** 
==> amazon-ebs: Waiting for AMI to become ready... 
==> amazon-ebs: Adding tags to AMI (ami-***************)... 
==> amazon-ebs: Tagging snapshot: snap-*************** 
==> amazon-ebs: Creating AMI tags 
    amazon-ebs: Adding tag: "Name": "packer_ami" 
==> amazon-ebs: Creating snapshot tags 
==> amazon-ebs: Terminating the source AWS instance... 
==> amazon-ebs: Cleaning up any extra volumes... 
==> amazon-ebs: No volumes to clean up, skipping 
==> 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: 
ap-northeast-1: ami-*************** 
想定した通りになってますね!

ちなみにinventory_directoryの説明にもあるように、

inventory_directoryに指定したディレクトリに一時ファイルとしてインベントリファイルを作成します。

一時ファイルなので完了/失敗時に削除されるのですが、Ctrl+Cを連打したりすると削除処理までキャンセルしてしまう場合がありました。

その場合、どういう挙動なのかよくわかってませんが、次回実行時に残ったインベントリファイルを読み込んでデプロイにコケる、というケースがありました。

挙動がおかしい場合はインベントリディレクトリを調査して、インベントリファイルが残ってたら削除するとうまくいきました。

他にもいい方法があればコメントください。

コメント

このブログの人気の投稿

投稿時間:2021-06-17 22:08:45 RSSフィード2021-06-17 22:00 分まとめ(2089件)

投稿時間:2021-06-20 02:06:12 RSSフィード2021-06-20 02:00 分まとめ(3871件)

投稿時間:2021-06-17 05:05:34 RSSフィード2021-06-17 05:00 分まとめ(1274件)