EC2 Image Builder を使用して 作成した AMI で 起動時に一度だけプログラムを実行するにはどうしたらいいですか?

2020.10.27

困っていた内容

AMI起動時に一度だけプログラムを実行させたい場合は /var/lib/cloud/scripts/per-once/配下にスクリプトを作成しておくことで可能でしたが、EC2 Image Builder を使用して AMI を作成した時は、Security best practicesのスクリプトが強制的に実施され、/var/lib/cloud/配下が削除されてしまいます。

他にも削除されてしまうファイルがありますので詳しくはドキュメントをご参照ください。

Security best practices for EC2 Image Builder

The following script is run as a mandatory step when Linux images are customized with EC2 Image Builder.

どうすればいいの?

/var/lib/cloud/配下が削除されてしまいますので、削除されない場所にスクリプトを置いて、cloud-initの代わりに独自にユニットファイルを作成して、サービスを登録したAMIを作成します。

Amazon Linux 2 が起動してネットワークが有効になった後で自作スクリプトを実行する方法を調べてみた

やってみる

今回は、一度だけ実行したいので、cloud-initのときと同じように、lock fileの有無で実行するかしないかを判断します。

例) cloud.initの場合は、以下ファイルの有無で判断しています。 /var/lib/cloud/sem/config_scripts_per_once.once

スクリプトを作成する

一度だけ実行したいスクリプト

/home/ec2-user/per-once.txtper once!と書き込むスクリプトを作成しました。

  • /root/per-once/scripts/per_once.sh
$ sudo vi /root/per-once/scripts/per_once.sh
#!/bin/bash

FILE="/root/per-once/scripts/per_once.lock"

# ロックファイルがあったら終了
if [ -e $FILE ]; then
    echo "exist lock file"
    exit
fi

# 実行
cat <<EOF >> /home/ec2-user/per-once.txt
per once !
EOF

# 実行したらlockファイルを作成する
touch /root/per-once/scripts/per_once.lock
$ sudo chmod +x /root/per-once/scripts/per_once.sh

ユニットファイルを作成する

$ sudo vi /etc/systemd/system/test.service
[Unit]
Description=per once script
After=cloud-init.target

[Service]
Restart=no
Type=simple
RemainAfterExit=yes
ExecStart=/root/per-once/scripts/per_once.sh 

[Install]
WantedBy=cloud-init.target

自動起動の設定

$ sudo systemctl enable test
Created symlink from /etc/sys:q!
::temd/system/cloud-init.target.wants/test.service to /etc/systemd/system/test.service.
$ sudo systemctl is-enabled test
enabled

上記、設定をAMIにしておきます。

参考までに、ansible playbook で作成した時は以下のようになります。

playbook.yml

- hosts: 127.0.0.1
  become: yes
  roles:
    - setup
├── playbook.yml
└── roles
    ├── setup
    │   ├── files
    │   │   ├── per_once.sh
    │   │   └── test.service
    │   └── tasks
    │       └── main.yml

roles/setup/tasks/main.yml

---
- name: mkdir scripts
  file:
    path: /root/per-once/scripts
    state: directory
    recurse: yes

- name: Copy /root/scripts/per_once.sh
  copy:
    src: files/per_once.sh
    dest: /root/per-once/scripts/per_once.sh
    mode: a+x

- name: Copy /etc/systemd/system/test.service
  copy:
    src: files/test.service
    dest: /etc/systemd/system/test.service
    mode: 0644

- name: Enable test service
  systemd:
    name: test
    enabled: yes

Ansible playbookを実行する方法は以下エントリーを見てください。

EC2 Image Builder で Rolesディレクトリを使用してる Ansible Playbook を実行するにはどうしたらいいですか?

確認

AMIから初回起動、ロックファイルが作成され、2回目以降は書き込まれないことを確認します。

$ cat /home/ec2-user/per-once.txt
per once !
# ll /root/per-once/scripts/
total 4
-rw-r--r-- 1 root root   0 Oct 26 22:29 per_once.lock
-rwx--x--x 1 root root 320 Oct 26 22:16 per_once.sh

おまけ

一度だけ実行なので、lockファイルの作成じゃなく、サービスの無効化や削除する方法

/etc/systemd/system/test.serviceExecStartで以下のように対応します。

  • 無効化する
ExecStart=/bin/bash -c "/root/per-once/scripts/per_once.sh && /usr/bin/systemctl disable test"
  • 削除する
ExecStart=/bin/bash -c "/root/per-once/scripts/per_once.sh && /usr/bin/systemctl disable test && rm -rf /etc/systemd/system/test.service /root/per-once"

参考資料