【AWS】AnsibleでLightsailインスタンスを作る【Lightsail】

【AWS】AnsibleでLightsailインスタンスを作る【Lightsail】:


はじめに

先日、AWSでLightsailインスタンスを作った際、CloudFormationが未対応だったのでAnsibleでインフラ構成を定義・構築しました。
lightsail - Create or delete a virtual machine instance in AWS Lightsail — Ansible Documentation

2018/11/24現在、まだCloudFormationはLightsailに対応していないようです。

AnsibleでLightsailインスタンスを構築した際、完全に自動化できず幾つか手動で対応が必要だったこと、Lightsail特有のハマりポイントがあったことから、記録として残します。


TL;DR

  • Lightsail利用時の注意

    • SSH用の鍵ペアはEC2とは別管理
    • 固定グローバルIPアドレスは「Static IP」というものが用意されている
    • Static IP≒Elastic IPであるが、それぞれ別管理
  • AnsibleのLightsailモジュール利用時の注意

    • Static IPの取得・割り当て等は未対応
  • Ansibleのroute53モジュール利用時の注意

    • AssumeRoleするprofileを利用しているとtaskが失敗するケースがある


目的・概要

Ansible Playbookで下記の作業を全て実施できれば理想ですが、lightsailモジュールではStatic IPの操作は未対応のようです。したがって、この作業は手動で行う必要がありました。

Ansible Playbook化出来たのは、以下の項番2~4までとなります。

  1. Static IPの確保
  2. Lightsailインスタンスの作成
  3. Static IPのLightsailインスタンスへの割り当て
  4. 当該Static IPを指すRoute53 CNAMEレコードの作成
ちなみにStatic IPとは、EC2で言うところのElastic IPのようなものです。ただし、EC2用のそれとは別管理であるため、Lightsailサービス内で新たにStatic IPを取得する必要があります。

Static IP addresses in Amazon Lightsail | Lightsail Documentation


作成したPlaybook

作成したPlaybookはGitHub上に公開しました。
https://github.com/tmiki/server-config/tree/master/ansible

上記リポジトリには他のPlaybookもたくさん入っていますので、本稿に関するもののみを抜粋します。

ansible-playbookコマンドから指定して実行するファイルは下記となります。「aws_lightsail_wp」というRoleを別途定義し、これを読み込んでいます。

aws_lightsail_wp.yml
# 
# aws_lightsail_wp.yml 
# 
--- 
- hosts: localhost 
  connection: local 
  gather_facts: False 
  roles: 
   - aws_lightsail_wp 
 
「aws_lightsail_wp」Roleの実体です。

roles/aws_lightsail_wp/tasks/main.yml
# 
# aws_lightsail_wp 
# 
--- 
 
- name: Create a Lightsail instance 
  lightsail: 
    state: present 
    name: "{{ lightsail_wp.instance_name }}" 
    profile: "{{ aws_profile }}" 
    region: "{{ region }}" 
    zone: "{{ az_primary }}" 
    blueprint_id: "{{ lightsail_wp.blueprint_id }}" 
    bundle_id: "{{ lightsail_wp.bundle_id }}" 
    key_pair_name: "{{ lightsail_wp.key_pair_name }}" 
    user_data: " echo 'hello world' > /home/ubuntu/test.txt" 
    wait_timeout: 500 
  register: _lightsail_instance 
- debug: 
    var: _lightsail_instance 
- debug: 
    msg: 
     - "Name is {{  _lightsail_instance.instance.name  }}" 
     - "Arn is {{  _lightsail_instance.instance.arn  }}" 
  when: not ansible_check_mode 
 
- name: Retrieve a global IP address from the Static IP 1/2 
  command: "aws lightsail --profile {{aws_profile}} get-static-ip --static-ip-name {{ lightsail_wp.static_ip_name }}" 
  register: _result 
  when: not ansible_check_mode 
 
- name: Retrieve a global IP address from the Static IP 2/2 
  set_fact: 
    _lightsail_static_ip: "{{ _result.stdout | from_json }}" 
  when: not ansible_check_mode 
- debug: 
    var: _lightsail_static_ip 
- debug: 
    msg: "Current static IP is {{ _lightsail_static_ip.staticIp.ipAddress }}" 
  when: not ansible_check_mode 
 
- name: Attach the Static IP with the Lightsail instance 
  command: "aws lightsail --profile {{aws_profile}} attach-static-ip --static-ip-name {{ lightsail_wp.static_ip_name }} --instance-name {{ lightsail_wp.instance_name }}" 
  when: 
   - not ansible_check_mode 
   - _lightsail_instance.instance.is_static_ip != true 
 
- name: Create a Route53 CNAME record points to the static IP address of the Lightsail instance 
  route53: 
    state: present 
    profile: "{{ aws_profile }}" 
    zone: "{{ route53zone }}" 
    record: "example-site.{{ route53zone }}" 
    type: A 
    ttl: 300 
    value: "{{ _lightsail_static_ip.staticIp.ipAddress }}" 
  when: not ansible_check_mode 
 
上記のtaskで利用している変数定義です。

hosts/dev1/group_vars/all.yml
--- 
env: dev1 
Env: "{{ env.capitalize() }}" 
 
aws_profile: dev1 
 
route53zone: "dev1.your-domain.com" 
 
region: ap-northeast-1 
az_primary: ap-northeast-1a 
az_secondary: ap-northeast-1c 
 
lightsail_wp: 
  blueprint_id: "wordpress_4_9_6" 
  bundle_id: "nano_2_0" 
  instance_name: "{{env}}-example-site" 
  static_ip: "{{env}}-example-site-ip" 
  key_pair_name: "{{env}}-lightsail-keypair" 
 


Lightsailインスタンス構築手順

必要な手順は下記のREADME.mdにまとめてあります。
https://github.com/tmiki/server-config/blob/master/ansible/README.md

各ステップについてそれぞれ説明します。


1. Create a key pair for Lightsail

まず最初にSSH用の鍵ペアを作ります。EC2用のものとLightsail用のものは独立して管理されているため、初めてLightsailインスタンスを作る際にはこちらも併せて作る必要があります。

鍵ペアの作成には「aws lightsail create-key-pair」コマンドを実行します。当該鍵ペアは「--key-pair-name」で指定した名前で保存されます。Lightsailインスタンスの作成時には、この名前で鍵ペアを指定することができます。

実行結果に秘密鍵が含まれるため、リダイレクトして保存しておくと良いでしょう。言うまでもないことですが、秘密鍵の取り扱いには十分注意します。

$ aws lightsail create-key-pair --key-pair-name dev1-lightsail-keypair > dev1-lightsail-keypair.json 


2. Allocate a new Static IP

次に、Static IPを取得します。Elastic IPとの大きな違いは、任意の名前を付与して管理できることです。ここでは「dev1-example-site-ip」という名称でStatic IPを取得しています。

$ aws lightsail allocate-static-ip --static-ip-name dev1-example-site-ip 
{ 
    "operations": [ 
        { 
            "status": "Succeeded", 
            "resourceType": "StaticIp", 
            "isTerminal": true, 
            "statusChangedAt": 1542990973.062, 
            "location": { 
                "availabilityZone": "all", 
                "regionName": "ap-northeast-1" 
            }, 
            "operationType": "AllocateStaticIp", 
            "resourceName": "dev1-example-site-ip", 
            "id": "47fb700f-ffff-ffff-ffff-ffffffffffff", 
            "createdAt": 1542990972.707 
        } 
    ] 
} 
 
「aws lightsail get-static-ips」コマンドで、取得済みのStatic IPを確認することが可能です。

$ aws lightsail get-static-ips 
{ 
    "staticIps": [ 
        { 
            "name": "dev1-example-site-ip", 
            "resourceType": "StaticIp", 
            "supportCode": "0123456789012/xx.xx.xx.xx", 
            "arn": "arn:aws:lightsail:ap-northeast-1:123456789012:StaticIp/9283dbfd-ffff-ffff-ffff-ffffffffffff", 
            "isAttached": false, 
            "ipAddress": "xx.xx.xx.xx", 
            "createdAt": 1542990972.707, 
            "location": { 
                "availabilityZone": "all", 
                "regionName": "ap-northeast-1" 
            } 
        } 
    ] 
} 


3. Modify variable definitions

自分の環境に合わせて、Ansible Playbook用の変数定義を編集します。当該Playbookの実行に必要な変数は、「hosts/*/group_vars/all.yml」に定義する想定です。

$ vi hosts/dev1/group_vars/all.yml 


4. Run the Playbook

Playbookを実行します。

$ ansible-playbook -vv -i hosts/dev1 aws_lightsail_wp.yml 


5.Create a Route53 record manually, if the Playbook fails.

このPlaybookは、取得したStatic IPを指すCNAMEレコードを自動的に作成します。

原因は不明(というより詳細は未調査)ですが、AssumeRoleするProfileを利用しているとレコードの作成に失敗します。最新のAnsible/AWS CLI/Boto3の環境で発生するかどうかは不明です。

このような場合、Route53レコードは手動で作る必要があります。


Playbookの解説


lightsailモジュール

varsで定義したパラメータ等を元に、Lightsailインスタンスを作成します。Lightsailはblueprintとして、WordpressやLAMP、Node.jsなど予め準備しており、これをもとに新たなLightsailインスタンスを構築することができます。

bundle_idは、簡単に言うとインスタンスサイズです。CPUやメモリ容量、転送可能なデータ量などのサーバリソースがセットになったものです。

blueprint_id、bundle_idの取得は下記記事を参照してください。

【小ネタ】AWS LightsailでblueprintIdやbundleIdを確認する【Lightsail】

key_pair_nameとして、先ほど手動で作った鍵ペアが指定されるよう、変数定義しておきます。

lightsailモジュール
- name: Create a Lightsail instance 
  lightsail: 
    state: present 
    name: "{{ lightsail_wp.instance_name }}" 
    profile: "{{ aws_profile }}" 
    region: "{{ region }}" 
    zone: "{{ az_primary }}" 
    blueprint_id: "{{ lightsail_wp.blueprint_id }}" 
    bundle_id: "{{ lightsail_wp.bundle_id }}" 
    key_pair_name: "{{ lightsail_wp.key_pair_name }}" 
    user_data: " echo 'hello world' > /home/ubuntu/test.txt" 
    wait_timeout: 500 
  register: _lightsail_instance 
- debug: 
    var: _lightsail_instance 
- debug: 
    msg: 
     - "Name is {{  _lightsail_instance.instance.name  }}" 
     - "Arn is {{  _lightsail_instance.instance.arn  }}" 
  when: not ansible_check_mode 


Static IPをLightsailインスタンスに紐づける

Static IPは先ほどのlightsailモジュールでは操作できません。そのため、「command」モジュールを用いて、AWS CLIを実行しています。

「aws lightsail attach-static-ip」コマンドで、先ほど生成したLightsailインスタンスに紐づけています。

なおこれに先立ち、「aws lightsail get-static-ip」コマンドで、先ほど手動で取得したStatic IPの実際のIPアドレスを取りだしていますが、これはこの後、Route53レコード作成に必要な情報となります。

StaticIP
- name: Retrieve a global IP address from the Static IP 1/2 
  command: "aws lightsail --profile {{aws_profile}} get-static-ip --static-ip-name {{ lightsail_wp.static_ip_name }}" 
  register: _result 
  when: not ansible_check_mode 
 
- name: Retrieve a global IP address from the Static IP 2/2 
  set_fact: 
    _lightsail_static_ip: "{{ _result.stdout | from_json }}" 
  when: not ansible_check_mode 
- debug: 
    var: _lightsail_static_ip 
- debug: 
    msg: "Current static IP is {{ _lightsail_static_ip.staticIp.ipAddress }}" 
  when: not ansible_check_mode 
 
- name: Attach the Static IP with the Lightsail instance 
  command: "aws lightsail --profile {{aws_profile}} attach-static-ip --static-ip-name {{ lightsail_wp.static_ip_name }} --instance-name {{ lightsail_wp.instance_name }}" 
  when: 
   - not ansible_check_mode 
   - _lightsail_instance.instance.is_static_ip != true 
 


Route53 CNAMEレコードの作成

Ansibleのroute53モジュールを利用して、CNAMEレコードを作成します。

ゾーン名は「route53zone」という変数で定義されます。当該Lightsailインスタンスに対応するドメイン名は、「lightsail_wp.site_hostname」変数及び「route53zone」変数を組み合わせたものとなります。

なお先日試した際には、AssumeRoleするProfileを利用したところ、レコードの作成に失敗しました。原因も詳しく調査していないので詳細は不明ですが、このような場合は手動でRoute53レコードを作る必要があります。

Route53
- name: Create a Route53 CNAME record points to the static IP address of the Lightsail instance 
  route53: 
    state: present 
    profile: "{{ aws_profile }}" 
    zone: "{{ route53zone }}" 
    record: "{{ lightsail_wp.site_hostname }}.{{ route53zone }}" 
    type: A 
    ttl: 300 
    value: "{{ _lightsail_static_ip.staticIp.ipAddress }}" 
  when: not ansible_check_mode 
 


おわりに

Lightsailのインフラ構成を定義・管理するという需要は恐らく小さいと思われるので、CloudFormationもAnsibleも完全に対応するのはまだ先のことでしょう。

それでも、出来る限り構成管理は進めていった方が、後々の管理が楽になりますので、可能な限りやっておいた方が良いでしょう。

コメント

このブログの人気の投稿

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

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

投稿時間:2024-02-12 22:08:06 RSSフィード2024-02-12 22:00分まとめ(7件)