AnsibleでAWS環境 (VPC、セキュリティグループ、EC2、RDS)構築の自動化

パフォーマンス検証環境を新たに作りたい場合など柔軟にAWS環境の構成を何度も作り直したい時、構成管理ツールを使って自動化すると非常に便利だと感じています。

  • 同じミドルウェアの設定の環境を迅速に作れる。
  • サーバの環境、ミドルウェアの設定内容をコードベースで把握できる。状態が共有しやすい。
  • 作り直しが必要になった時に、手間がかからない。
  • オペミスもないので、環境構築に時間がかからない。

構成管理ツールとしてAnsibleを使ったAWS環境構築の設定に関してまとめてみました。 VPC、セキュリティグループ、EC2、RDSを構築するPlayBookを作ってみたので、部分的にでも使える箇所があれば、参考にしてみてください。

構成図

構築するローカル環境とAWS環境は以下の図の通りです。 f:id:moritamorie:20190727223744p:plain

環境

  • ローカル環境(ansibleを実行する環境): Ubuntu 18.04.2 LTS
  • aws-cli: 1.16.201
  • ansible: 2.5.1

AWS CLI、②構成管理ツール Ansibleのインストール

AnsibleからAWS環境を作るには、AWS CLIのインストールが必要です。 AnsibleにはデフォルトでAWS環境を操作するためのモジュールがあり、AWS CLI経由で環境を作ることができます。

各ツールのインストール方法はこちらの記事を参照してください。 simple-minds-think-alike.hatenablog.com

③Ansible定義ファイル(playbook)作成

Ansible定義ファイルにAWS環境の構築内容を記載していきます。

Ansible定義ファイルのディレクトリ / ファイル構成

ローカル環境に作るAnsible定義ファイルの定義ファイルのディレクトリ / ファイル構成の最終系は以下のようになります。

├── build_aws.yml
└─ roles
   ├── common
   │   └── vars
   │       └── main.yml # 実行順序: 1
   ├── ec2
   │   ├── tasks
   │   │   └── main.yml # 実行順序: 7
   │   └── vars
   │       └── main.yml # 実行順序: 6
   ├── rds
   │   ├── tasks
   │   │   └── main.yml # 実行順序: 9
   │   └── vars
   │       └── main.yml # 実行順序: 8
   ├── securitygroup
   │   ├── tasks
   │   │   └── main.yml # 実行順序: 5
   │   └── vars
   │       └── main.yml # 実行順序: 4
   └── vpc
       ├── tasks
       │   └── main.yml # 実行順序: 3
       └── vars
           └── main.yml # 実行順序: 2

ansibleのplaybook実行時に指定する定義ファイル build_aws.yml を作成し、以下のように記載しました。

- hosts: localhost
  connection: local
  sudo: yes
  pre_tasks:
  - name: boto、boto3、botocoreが未インストールならインストール
    pip:
      name: "{{ item }}"
    with_items:
    - boto
    - boto3
    - botocore
  roles:
    - common
    - vpc
    - securitygroup
    - ec2
    - rds

AWS各環境を構築するansibleのモジュールをを用いて、ローカル環境にインストールされているAWS CLIを実行するので、connection: localを指定しています。

各ファイルがないのでまだ動きませんが、以下のように実行するとVPC、セキュリティグループ、EC2、RDSを一度に作成します。

$ sudo ansible-playbook build_aws.yml

このように実行すると、rolesで指定した順番通りに(common, vpc, securitygroup, ec2, rdsディレクトリの順番で)実行し、各ディレクトリ内のvarsで変数の設定を読み込み、tasksで構築の処理を行うようになります。

また、AnsibleでAWS環境を構築するには、boto, boto3, botocoreのようなライブラリが必要になるため、pre_tasksとして追加し、インストールにはsudo権限が必要なのでsudoをyesにします。

続いて、VPC、セキュリティグループ、EC2、RDSそれぞれの定義ファイルの中身を記載して行きます。

VPC(とサブネット、ルートテーブル)構築

共通設定

roles/common/vars/main.yml の内容は以下の通りで、VPC、セキュリティグループ、EC2、RDS構築全てに関係する共通の設定を書きました。

# aws-cliで実行する際のprofile
profile: performance-test

# 構築するAWS環境のリージョン
region: ap-northeast-1

# SSH接続を許可する接続元CIDR (オフィスのIPを入れてます)
office_ip_cidr: XXX.XXX.XXX.XXX/32

※今回パフォーマンステスト環境を作るための定義ファイルを作ったので、全体的にPerformance testのような文言が入っていますが、目的の用途に応じた名前を付けると良いと思います。

VPC設定

roles/vpc/vars/main.yml に、VPC構築に関する設定のみ記載します。

vpc:
  name: VPC
  cidr_blodk: 10.0.0.0/16
subnet:
  - name: Performance test public subnet1
    az: ap-northeast-1a
    cidr: 10.0.1.0/24
    route: Public
  - name: Performance test private subnet1
    az: ap-northeast-1a
    cidr: 10.0.11.0/24
    route: Private
  - name: Performance test private subnet2
    az: ap-northeast-1c
    cidr: 10.0.12.0/24
    route: Private

パブリックなサブネットを1つ、プライベートなサブネットを2つ作成する設定です。 後で構築するMulti AZ構成のRDSセキュリティグループには、2つのサブネットを指定する必要があり、RDSは今回構築するVPC環境外からアクセスされたくないので、プライベートなサブネットを2つ作っています。

roles/vpc/tasks/main.yml には、VPC構築するためのタスクを記載します。

- name: VPC構築
  ec2_vpc_net:
    name: Performance-test VPC
    cidr_block: "10.0.0.0/16"
    profile: "{{ profile }}"
    region: "{{ region }}"
  register: _vpc

- name: Internet gateway設定
  ec2_vpc_igw:
    vpc_id: "{{ _vpc.vpc.id }}"
    profile: "{{ profile }}"
    region: "{{ region }}"
  register: _igw

- name: Public, Private subnet構築
  ec2_vpc_subnet:
    vpc_id: "{{ _vpc.vpc.id }}"
    az: "{{ item.az }}"
    cidr: "{{ item.cidr }}"
    resource_tags:
      Name: "{{ item.name }}"
    profile: "{{ profile }}"
    region: "{{ region }}"
  with_items: "{{ subnet }}"
  register: _subnet

- name: Public subnetのRoute設定
  ec2_vpc_route_table:
    vpc_id: "{{ _vpc.vpc.id }}"
    tags:
      Name: Performance test public route
    subnets: "{{ _subnet.results | selectattr('item.route', 'equalto', 'Public') | map(attribute='subnet.id') | list }}"
    # Public subnetの場合のみ、VPC外からアクセス可能にしています
    routes:
      - dest: 0.0.0.0/0
        gateway_id: "{{ _igw.gateway_id  }}"
    profile: "{{ profile }}"
    region: "{{ region }}"

- name: Private SubnetのRoute設定
  ec2_vpc_route_table:
    vpc_id: "{{ _vpc.vpc.id }}"
    tags:
      Name: Performance test private route
    subnets: "{{ _subnet.results | selectattr('item.route', 'equalto', 'Private') | map(attribute='subnet.id') | list }}"
    profile: "{{ profile }}"
    region: "{{ region }}"

⑤セキュリティグループ設定

roles/securitygroup/vars/main.yml に、セキュリティグループ設定に関する設定のみ記載します。

security_group:
  common:
    group_name: performance test common
    description: common rules
    rules:
      - proto: tcp
        from_port: 22
        to_port: 22
        cidr_ip: "{{ office_ip_cidr }}"
      - proto: icmp
        from_port: -1
        to_port: -1
        cidr_ip: "{{ office_ip_cidr }}"
  web_server:
    group_name: performance test web server
    description: web server rules
    rules:
      - proto: tcp
        from_port: 80
        to_port: 80
        cidr_ip: 0.0.0.0/0
      - proto: tcp
        from_port: 443
        to_port: 443
        cidr_ip: 0.0.0.0/0
  db_server:
    group_name: performance test db server
    description: db server rules
    rules:
      - proto: tcp
        from_port: 5432
        to_port: 5432
        cidr_ip: 10.0.1.0/24

共通(common)のセキュリティグループには、オフィスIPからのみ

  • SSH(port: 22)でリモート接続
  • ICMPでpingが通るように

してみました。

Webサーバ(web_server)のセキュリティグループには、どこからでも

  • HTTP(port: 80)
  • HTTPS(port: 443)

にアクセスできるようにしてみました。

DBサーバ(db_server)のセキュリティグループには、パブリックなサブネットからのアクセスのみを許可するようにcidr_ipを指定します。

roles/securitygroup/tasks/main.yml には、セキュリティグループ設定するためのタスクを記載します。

- name: Security Group 作成
  ec2_group:
    name: "{{ item.value.group_name }}"
    description: "{{ item.value.description }}"
    vpc_id: "{{ _vpc.vpc.id }}"
    rules: >-
      {%- if item.value.rules|length == 1 -%}
        {{ item.value.rules }}
      {%- else -%}
        {{ item.value.rules | join(",") }}
      {%- endif -%}
    profile: "{{ profile }}"
    region: "{{ region }}"
  with_dict: "{{ security_group }}"

⑥EC2構築

roles/ec2/vars/main.yml に、EC2構築に関する設定のみ記載します。

owner: ubuntu
group: ubuntu

EC2構築時に、キーペアを作っているのでpemファイルを保存する際のowner, groupを設定しています。

roles/ec2/tasks/main.yml には、EC2構築するためのタスクを記載します。

- name: EC2キーペア作成
  ec2_key:
    name: performance test key pair
    region: "{{ region }}"
    profile: "{{ profile  }}"
  register: ec2_key

- name: プライベートキーを保存
  copy:
    content: "{{ ec2_key.key.private_key }}"
    dest: "/home/ubuntu/.ssh/performance_test.pem"
    mode: 0600
    owner: "{{ owner }}"
    group: "{{ group }}"
  when: ec2_key.changed

- name: EC2インスタンス構築
  ec2:
    # EC2インスタンスの起動を待たないと、Elastic IPを割り当てでエラーになる
    wait: yes
    key_name: "{{ ec2_key.key.name }}"
    profile: "{{ profile }}"
    region: "{{ region }}"
    instance_type: t2.nano
    # Ubuntu Server 18.04 LTS (HVM), SSD Volume Typeのイメージ
    image: ami-09b68f5653871885f
    vpc_subnet_id: "{{ _subnet.results | selectattr('item.route', 'equalto', 'Public') | map(attribute='subnet.id') | first }}"
    group:
      - "{{ security_group.common.group_name }}"
      - "{{ security_group.web_server.group_name }}"
    instance_tags:
      Name: Performance test ec2
    # count_tag, exact_countで、同じEC2インスタンスが複数できないように
    count_tag:
      Name: Performance test ec2
    exact_count: 1
    volumes:
      - device_name: /dev/sda1
        volume_type: gp2
        volume_size: 8
        delete_on_termination: true
  register: ec2

- name: EC2インスタンスに新しいElastic IPを割り当てる
  ec2_eip:
    region: "{{ region }}"
    state: present
    device_id: "{{ ec2.instances[0].id }}"
    profile: "{{ profile }}"
  when: ec2.instances is defined and (ec2.instances|length>0)
  register: eip

⑦RDS構築

roles/rds/vars/main.yml に、DBの接続情報などRDS構築に関する設定のみ記載します。

rds:
  db_name: performance_testing_production
  db_user: XXXXX
  db_password: XXXXXXXXXXXXXX

roles/rds/tasks/main.yml には、RDS構築するためのタスクを記載します。

- name: rds-subnet-group作成
  rds_subnet_group:
    name: Performance test rds subnet group
    description: Performance test rds subnet group description
    state: present
    subnets: "{{ _subnet.results | selectattr('item.route', 'equalto', 'Private') | map(attribute='subnet.id') | list }}"
    profile: "{{ profile }}"
    region: "{{ region }}"
  register: rds_subnet_group

- name: セキュリティグループIDを取得
  ec2_group_facts:
    filters:
      group-name: performance test db server
    profile: "{{ profile }}"
    region: "{{ region }}"
  register: group_fact

- name: RDSインスタンス構築
  rds:
    command: create
    force_failover: no
    instance_name: performance-test-rds
    username: "{{ rds.db_user }}"
    password: "{{ rds.db_password }}"
    db_name: "{{ rds.db_name }}"

    subnet: Performance test rds subnet group
    vpc_security_groups: "{{ group_fact.security_groups[0].group_id }}"
    db_engine: postgres
    engine_version: 10.6
    license_model: postgresql-license
    instance_type: "db.t2.micro"
    size: "20"
    tags:
      Name: "{{ rds.db_name }}"
    profile: "{{ profile }}"
    region: "{{ region }}"
  register: rds

実行と確認

以下のコマンドを実行します。

$ sudo ansible-playbook build_aws.yml

事前にansibleインストール方法の記事で書いたようにローカル環境でプロファイル登録してあるので、AWSへの接続情報などを指定しなくてもaws-cliを実行できます。

構築できたかEC2にSSH接続して確認します

$ ssh -i ~/.ssh/performance_test.pem ubuntu@ec2-【Elastic IPで割り当てたIP】.ap-northeast-1.compute.amazonaws.com

参考資料

AnsibleでAWSモジュールを使う | mediba Creator × Engineer Blog AnsibleによるVPCの構成管理 | DevelopersIO