PackerでMoleculeテスト環境用のAWS EC2ゴールデンイメージを作成する

この記事は [Ansible Advent Calendar 2021](https://adventar.org/calendars/6737) 2日目の記事になります。


Ansible Moleculeでテストを実行する時にテスト対象になるAWS EC2のゴールデンイメージの作成方法について解説します。

今回はPackerを用いてAWS EC2 AMI（Amazon machine image）へVMイメージを登録するまでになります。

## なぜテスト環境用のVMイメージを作成、登録するのか？


* CIの実行時間を短縮するため
* コードの記述量を減らしメンテナンスしやすくする

これらの理由が考えられます。

### CIの実行時間を短くする

Moleculeの機能から言えばMolecule実行時にMoleculeのシナリオprepareなどでテスト環境をセットアップする事は可能です。しかし例えば毎回OSをアップデート、再起動するなどの処理をしているとCIの実行時間が長くなってしまいます。

### コードの記述量を減らしメンテナンスしやすくする

テスト環境用のセットアップ処理（いわゆるbootstrap）を一箇所で管理する事によりメンテナンスコストが減らせます。例えば**Ansible Role / Playbookでリポジトリがそれぞれ分かれていてなおかつそれぞれのリポジトリで`molecule/scenarios/prepare.yml`が管理されている場合prepare.ymlに変更が生じると全てのリポジトリに対して修正を行う必要が出てきます**。これは相当悪手なので絶対に避けます。


## ゴールデンイメージの仕様

ゴールデンイメージの設計図となるPackerの設定ファイルには以下を含めます

* イメージのアップデート処理
* rootのパスワードの設定
* cloud-initの設定の変更

### rootパスワードの設定

これはEC2でシリアルコンソール接続を有効にするために必要になります。また注意点としてインスタンスタイプはNitroシステムで利用できるものを選択します。サンプルコードのインスタンスタイプではシリアルコンソール接続は利用できません。利用可能なインスタンスは以下になります。

<pre>
仮想化

A1、C5、C5a、C5ad、C5d、C5n、C6a、C6g、C6gd、C6gn、C6i、D3、D3en、G4、
G4ad、G5、I3en、I4i、Inf1、M5、M5a、M5ad、M5d、M5dn、M5n、M5zn、M6a、
M6g、M6gd、M6i、p3dn.24xlarge、P4、R5、R5a、R5ad、R5b、R5d、R5dn、
R5n、R6g、R6gd、R6i、T3、T3a、T4g、u-3tb1.56xlarge、u-6tb1.56xlarge、
u-6tb1.112xlarge、u-9tb1.112xlarge、u-12tb1.112xlarge、X2gd、X2idn、
X2iedn、X2iezn、z1d

ベアメタル

a1.metal、c5.metal、c5d.metal、c5n.metal、c6a.metal、c6g.metal、
c6gd.metal、c6i.metal、g4dn.metal、i3.metal、i3en.metal、i4i.metal、
m5.metal、m5d.metal、m5dn.metal、m5n.metal、m5zn.metal、m6a.metal、
m6g.metal、m6gd.metal、m6i.metal、mac1.metal、r5.metal、r5b.metal、
r5d.metal、r5dn.metal、r5n.metal、r6g.metal、r6gd.metal、r6i.metal、
u-6tb1.metal、u-9tb1.metal、u-12tb1.metal、u-18tb1.metal、
u-24tb1.metal、x2gd.metal、x2idn.metal、x2iedn.metal、x2iezn.metal、
z1d.metal
</pre>

### cloud-initの設定変更

cloud-initはバージョンによってはホストキーを削除してSSH接続を無効にしてしまう[バグ](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/8.5_release_notes/bug_fixes#BZ-1957532)が含まれる事がありあまり信頼できるライブラリではありません。この挙動を回避するため初期設定を変更しておきます。

### イメージのビルドの頻度は？

Moleculeを実行する頻度によります。数ヶ月に一回程度なら手動で実行、ほぼ毎日ならcron等で実行します。

## Packerのbuild.jsonのサンプル例（CentOS Stream 8)

```json:build.json
{
  "variables": {
    "ami_name": "",
    "aws_profile": "",
    "aws_region": "",
    "tag_name": ""
  },
  "builders": [{
    "ami_name": "{{user `ami_name`}}",
    "instance_type": "t2.medium",
    "profile": "{{user `aws_profile`}}",
    "region": "{{user `aws_region`}}",
    "source_ami": "ami-06ee905d0566e92e7",
    "ssh_username": "centos",
    "tags": {
       "Name": "{{user `tag_name`}}"
    },
    "type": "amazon-ebs"
  }],
  "provisioners": [
    {
      "inline": [
        "sudo echo root | sudo passwd --stdin root"
      ],
      "type": "shell"
    },
    {
      "inline": [
        "sudo dnf -y update"
      ],
      "type": "shell"
    },
    {
      "inline": [
        "sudo sed -i -e \"s/ssh_genkeytypes:  ~/ssh_genkeytypes: ['rsa', 'ecdsa', 'ed25519']/\" /etc/cloud/cloud.cfg"
      ],
      "type": "shell"
    }
  ]
}
```

## 参考サイト

### 今回の記事のサンプルになるGitHubリポジトリ

* [packer-mamono210/aws-ami_centos at 8d126b33d2e66fc7e6ed3588fe24872f04b61493](https://github.com/packer-mamono210/aws-ami_centos/tree/8a593d7093a6988ba0b11d4f51f0ad31394f3c24)

### シリアルコンソール接続とは

* [EC2 シリアルコンソールへのアクセスを設定する - Amazon Elastic Compute Cloud](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/configure-access-to-serial-console.html)

### AWS Nitro systemとは

* [AWS Nitro System](https://aws.amazon.com/jp/ec2/nitro/)

### cloud-initのバグについて

* [Chapter 7. Bug fixes Red Hat Enterprise Linux 8 | Red Hat Customer Portal - SSH keys are now generated correctly on EC2 instances created from a backup AMI](https://access.redhat.com/documentation/en-us/red_hat_enterprise_linux/8/html/8.5_release_notes/bug_fixes#BZ-1957532)