RDSをAnsibleで管理する

  • 0
    いいね
  • 0
    コメント

    はじめに

    AnsibleにはAWSのリソースを操作できるモジュールが豊富に用意されています。

    今回は、RDSをAnsibleで管理してみます。
    RDSインスタンスを作成するためには、

    • サブネットグループ
    • パラメータグループ

    が必要になるので、一気通貫で作成できるようにします。

    やること

    • サブネットグループ作成
    • パラメータグループ作成
    • RDSインスタンス作成

    ポイント

    サブネットグループを作成するためにサブネットIDが、RDSインスタンスを作成するためにセキュリティグループIDがそれぞれ必要となりますが、IDをAnsibleのYAMLに書きたくないので、それぞれ名前からIDを取得する実装とします。

    前提

    • AWS関連のモジュール実行にはbotoが必要です。
    • credential情報は環境変数かaws configureでセットしてある必要があります。
    • ec2_group_factsが必要です。

    下記リソースを前提に進めます。

    • VPC
      • AnsibleVPC
    • サブネット
      • private-a
      • private-c
    • セキュリティグループ
      • db_server

    sample

    以下のようなRDSインスタンスを作成します。

    • サブネットグループ
      • private
        • private-a(AZ-a)
        • private-c(AZ-c)
    • パラメータグループ
      • mysql57-sample
    • RDSインスタンス
      • sample-db
        • セキュリティグループ
          • db_server
        • サブネットグループ
          • private
        • パラメータグループ
          • mysql57-sample

    ディレクトリ構成

    ディレクトリ構成
    site.yml
    roles/
    |--rds/
    |  |--tasks/
    |  |  |--main.yml
    hosts/aws    #inventory
    host_vars/
    |--localhost.yml
    

    inventory

    AWSリソース関連モジュールはすべてlocalhostで実行するので、下記のようなインベントリファイルを用意します。

    hosts/aws
    [aws]
    localhost
    

    vars

    こんな感じに変数を定義します。今回はhost_varsで定義しました。
    RDSインスタンスのパスワードをそのまま記載していますが、実際はansible-vaultで暗号化したファイルなどに記載してください。

    host_vars/localhost.yml
    ---
    my_vars:
      aws:
        common:
          region: ap-northeast-1
        vpc:
          name: AnsibleVPC    # ターゲットのVPC名
        rds:
          subnet_group:
            - name: private
              description: 'private subnet group'
              subnets:
                - private-a #サブネット名
                - private-c #サブネット名
          param_group:
            - name: mysql57-sample
              description: 'MySql5.7 sample'
              engine: mysql5.7
              params:
                character_set_database: utf8
                character_set_server: utf8
          instance:
            - name: sample-db
              db_engine: MySQL
              engine_version: 5.7.16
              multi_zone: no
              zone: a
              size: 5
              instance_type: db.t2.micro
              parameter_group: mysql57-sample
              subnet_group: private
              security_group:
                - db-server #セキュリティグループ名
              username: mysql_admin
              password: mysql_admin
              tags:
                Role: sample-db
    

    Role

    まずVPCを特定するためにidが必要ですが、こちらと同様、VPC名でidを取得します。

    後続タスクで必要となる、サブネットIDとセキュリティグループIDを取得し、それぞれ名前からIDを参照するためにディクショナリを生成します。

    roles/vpc/tasks/main.yml
    ---
    - name: vpc_id取得
      ec2_vpc_net_facts:
        region: "{{ my_vars.aws.common.region }}"
        filters:
          "tag:Name": "{{ my_vars.aws.vpc.name }}"
      register: vpc_net_fact
    
    - name: subnet id取得
      ec2_vpc_subnet_facts:
        region: "{{ my_vars.aws.common.region }}"
        filters:
          vpc_id: "{{ vpc_net_fact.vpcs[0].id }}"
          "tag:Name": "{{ item.1 }}"
      with_subelements:
        - "{{ my_vars.aws.rds.subnet_group }}"
        - subnets
      register: subnet_fact
      when: my_vars.aws.rds.subnet_group is defined
    
    - name: subnet dict作成
      set_fact:
        subnets_dict: >-
          {%- set dict = {} -%}
          {%- set inc = 0 -%}
          {%- set subnet_group_cnt = my_vars.aws.rds.subnet_group|length -%}
          {%- for i in range(subnet_group_cnt) -%}
          {%-   set list = [] -%}
          {%-   for j in range(my_vars.aws.rds.subnet_group[i].subnets|length) -%}
          {%-     set _ = list.append(subnet_fact.results[inc+j].subnets[0].id) -%}
          {%-   endfor -%}
          {%-   set _ = dict.update({my_vars.aws.rds.subnet_group[i].name: list}) -%}
          {%-   set inc = inc + subnet_group_cnt -%}
          {%- endfor -%}
          {{ dict }}
      when: my_vars.aws.rds.subnet_group is defined
    
    - name: securitygroup id取得
      ec2_group_facts:
        region: "{{ my_vars.aws.common.region }}"
        filters:
          vpc_id: "{{ vpc_net_fact.vpcs[0].id }}"
          group_name: "{{ item.1 }}"
      with_subelements:
        - "{{ my_vars.aws.rds.instance }}"
        - security_group
      register: sg_fact
      when: my_vars.aws.rds.instance is defined
    
    - name: securitygroup dict作成
      set_fact:
        sg_dict: >-
          {%- set dict = {} -%}
          {%- for i in range(sg_fact.results|length) -%}
          {%-   set _ = dict.update({sg_fact.results[i].security_groups[0].group_name: sg_fact.results[i].security_groups[0].group_id}) -%}
          {%- endfor -%}
          {{ dict }}
      when: my_vars.aws.rds.instance is defined
    
    - name: RDS subnet-group作成
      rds_subnet_group:
        state: present
        name: "{{ item.name }}"
        description: "{{ item.description }}"
        region: "{{ my_vars.aws.common.region }}"
        subnets: >-
          {%- set subnetname = item.name -%}
          {%- set list = subnets_dict[item.name] -%}
          {{ list }}
      with_items: "{{ my_vars.aws.rds.subnet_group }}"
      register: rds_subnet_group
    
    - debug: var=rds_subnet_group
    
    - name: RDS パラメータグループ作成
      rds_param_group:
        state: present
        name: "{{ item.name }}"
        description: "{{ item.description }}"
        region: "{{ my_vars.aws.common.region }}"
        engine: "{{ item.engine }}"
        params: "{{ item.params }}"
      with_items: "{{ my_vars.aws.rds.param_group }}"
      register: rds_param_group
      when: my_vars.aws.rds.param_group is defined
    
    - debug: var=rds_param_group
    
    - name: RDS インスタンス作成
      rds:
        command: create
        instance_name: "{{ item.name }}"
        db_engine: "{{ item.db_engine }}"
        engine_version: "{{ item.engine_version }}"
        region: "{{ my_vars.aws.common.region }}"
        multi_zone: "{{ item.multi_zone }}"
        zone: "{{ my_vars.aws.common.region }}{{ item.zone }}"
        size: "{{ item.size }}"
        instance_type: "{{ item.instance_type }}"
        parameter_group: "{{ item.parameter_group }}"
        subnet: "{{ item.subnet_group }}"
        vpc_security_groups: >-
          {%- set list = [] -%}
          {%- for i in range(item.security_group|length) -%}
          {%-   set _ = list.append(sg_dict[item.security_group[i]]) -%}
          {%- endfor -%}
          {{ list }}
        username: "{{ item.username }}"
        password: "{{ item.password }}"
      with_items: "{{ my_vars.aws.rds.instance }}"
      register: rds_instance
      when: my_vars.aws.rds.instance is defined
    
    - debug: var=rds_instance
    

    site.yml

    site.yml
    ---
    - name: rds
      hosts: localhost
      connection: local
      roles:
        - role: rds
    

    実行

    Command
    $ ansible-playbook -i hosts/aws -l localhost site.yml
    

    注意

    Ansibleのrdsモジュールにはなぜかストレージタイプを指定するためのオプションがなく、作成されたインスタンスのストレージはマグネティックになってしまいますので適宜変更してください。

    まとめ

    RDSは、関連リソースを合わせて作成する必要がありますが、Ansibleで自動化できると楽です。
    また、今回のplaybookはインスタンスの変更には対応していません。

    参考になれば幸いです。

    参考