LoginSignup
5
0

More than 5 years have passed since last update.

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

Last updated at Posted at 2018-11-23

はじめに

先日、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も完全に対応するのはまだ先のことでしょう。
それでも、出来る限り構成管理は進めていった方が、後々の管理が楽になりますので、可能な限りやっておいた方が良いでしょう。

5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0