AWS Playbookの設計ポリシー

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

渡辺です。

Ansible2.2より、AnsibleのAWS関連モジュールが充実され、AnsibleでAWSリソースの構成管理を行うことが現実味を帯びてきました。 AWSリソースの構成管理を行うには、CloudFormationやteraformなど、他の選択肢も多くあります。 その中でも、EC2インスタンスの構成管理をAnsibleで行っているならば、利用するツールを共通化できるのが最大のメリットです。

今回は、そんなAWSリソースを構成管理するとき、作成するPlaybookの設計ポリシーを解説します。

localhostのlocalコネクションを利用する

一般的に、Ansibleでは、対象ホストにSSH接続を行い、構成管理を行います。 AWSリソースの場合、対象となるのはVPCなど、AWSリソースであるため、SSH接続は行えません。 このようなケースでは、対象ホストにローカルマシンを指定し、SSH接続も行わないオプションを指定します。 また、AnsibleではSSH接続時、はじめにホストの情報を収集します(gather_facts)。 これも不要となるため、Playbookの冒頭は次のようになります。

- hosts: localhost
  connection: local
  gather_facts: False

プロファイルの準備

AnsibleのAWSモジュールはPython版のAWS SDKであるbotoのラッパーです。 内部的にAWS APIを使いますので、IAM Userを作成し、適切な権限を持ったアクセスキーを作成する必要があります。 クレデンシャルファイルにプロファイルを作成してください。

なお、Playbookにアクセスキーとシークレットキーを埋め込むことも可能ですが、バージョン管理システムにコミットするなど、アクセスキーの漏洩に繋がりますので、ゼッタイに辞めましょう。 やるならば、グループファイルをvaltで暗号化するべきですが、素直にプロファイルを利用すべきです。

環境毎にグループ変数を定義する

AWS環境を構築する場合、ほとんどのケースで、本番環境と検証環境、さらに開発環境を作ることになります。 そして、リソース名など環境毎の差異が発生します。 この差異はAnsibleのグループ変数(group variable)を利用して吸収します。

グループ変数を差し替え、共通のPlaybookを利用した構築を行えば、検証環境などで構築した環境と寸分違いなく本番環境を構築可能となります これは作業ミスを防ぐ意味でも、構築の効率化を進める上でも重要です。

次のようにインベントリファイルを環境毎(prd/stg)に作成し、それぞれにグループ変数(all.yml)を定義します。

└── hosts
    ├── prd
    │   ├── group_vars
    │   │   └── all.yml
    │   └── inventory
    └── stg
        ├── group_vars
        │   └── all.yml
        └── inventory

inventoryファイルは現時点では空で良いでしょう(localhostは暗黙で追加されるため)。 all.ymlには環境毎に異なる変数を定義してください。 また、プロファイルとリージョンの2つの変数もあわせて定義します(検証環境と本番環境が同一AWSアカウントであればプロファイルは同じ場合もあるでしょう)。

# hosts/prd/group_vars/all.yml
---
env: prd
profile: hogehoge-prd
region: ap-northeast-1
# hosts/stg/group_vars/all.yml
---
env: stg
profile: hogehoge-stg
region: ap-northeast-1

ansible-playbookコマンド実行時には、-iオプションを利用して、環境を指定します。

ansible-playbook -i hosts/prd aws-net.yml

Playbookの分割

AnsibleのPlaybookでは、CloudFormationなどと同様に、全AWSリソースを一括で定義することもできます。 しかし、巨大な定義ファイルは可読性も低く、管理しにくくなります。 CloudFormationと同様に、適切なレイヤで分割して管理するのが妥当です。 例えば、VPCやサブネットを定義するネットワークレイヤは、aws-net.ymlで定義します。

モジュール共通オプションのprofileとregion

AnsibleのAWSモジュールの特性として、各タスク毎にprofileregionを指定する必要があります。 冗長ではと考えてしまいますが、複数のリージョンに横断したシステムや、複数のAWSアカウントに横断したシステムもあることを考えると仕方ない部分とも言えます。 これらのパラメータについては、各モジュールで変数を使って設定する事を基本としておきましょう。

 - ec2_vpc_net:
     name: VPC
     cidr_block: 10.0.0.0/16
     profile: "{{ profile }}"
     region: "{{ region }}"

主要パラメータはグループ変数に定義する

変更が簡単になる用に、各リソースの主要パラメータはグループ変数に定義します。 一覧性も良くなります。

# hosts/prd/group_vars/all.yml
---
env: prd
vpc:
  name: VPC
  cidr_blodk: 10.0.0.0/16
subnet:
  - name: FrontA
    az: us-east-2a
    cidr: 10.0.1.0/24
  - name: FrontC
    az: us-east-2c
    cidr: 10.0.2.0/24	

各モジュールでは変数を参照する記述にすれば、Playbookの再利用性も高くなります。

 - ec2_vpc_net:
     name: "{{ env }} {{ vpc.name }}"
     cidr_block: "{{ vpc.cidr_blodk }}"
     profile: "{{ profile }}"
     region: "{{ region }}"

registerで実行結果を保持する

AWSリソースを定義するには、例えばVPC IDなど、直前に実行したモジュールの実行結果から参照するパラメータが必要となります。 これらの値は、register構文を使い、変数に保存し、後から利用します。

命名規則として、registerで登録する変数は_をプレフィックスとします。

 - ec2_vpc_net:
     name: "{{ env }} {{ vpc.name }}"
     cidr_block: "{{ vpc.cidr_blodk }}"
     profile: "{{ profile }}"
     region: "{{ region }}"
   register: _vpc
 - debug: var=_vpc verbosity=1

さらにdebugモジュールを利用して、変数の出力を行うようにしています。 毎回出力されるのも鬱陶しいので、verbosityオプションを指定しましょう。 verbosity=1なのでコマンド実行時に-v以上の指定があれば、変数がデバッグ出力されます。

$ ansible-playbook -i hosts/prd aws-net.yml -v
$ ansible-playbook -i hosts/prd aws-net.yml

まとめ

今日は、AnsibleのAWSモジュールを利用したPlaybookの設計ポリシーについて解説しました。 Ansibleの基本を踏襲してAWSリソースを定義すれば、メンテナンス性の高いPlaybookを作ることができるでしょう。