さくらのアドベントカレンダー2022 12日目の記事です。(すみません。12日に書いたものではないです)
さくらインターネットの稲垣です。
本記事ではcloud-initのモジュールの作成の仕方と、そのモジュールを使えるマイアーカイブの作成について紹介します。
今回は、urlを指定するとスクリプトをダウンロードして実行するモジュールを作成します。
開発環境
Ubuntu 20.04で開発を行いました。
moduleのスクリプトを作成する
cloud-initをgit cloneする
git clone https://github.com/canonical/cloud-init.git
cd cloud-init/
モジュールのソースコードを作成する
cloudinit/config/ディレクトリにcc_
というプレフィクスをつけたファイルを作成します。
今回はcc_http_scripts.py
というファイルで作成しました。
vim cloudinit/config/cc_http_scripts.py
内容を以下のように編集します。(公式ドキュメントをベースに変更しています。)
基本的にやりたいことをhandle関数に書いていきます。
meta
のid
にはcc_http_scripts
を、activate_by_schema_keys
には["http_scripts"]
を指定します。
# This file is part of cloud-init. See LICENSE file for license information.
"""Example Module: Shows how to create a module"""
import sys
import urllib.request
import subprocess
from subprocess import PIPE, STDOUT, CalledProcessError
from logging import Logger
from cloudinit.cloud import Cloud
from cloudinit.config import Config
from cloudinit.config.schema import MetaSchema, get_meta_doc
from cloudinit.distros import ALL_DISTROS
from cloudinit.settings import PER_INSTANCE
MODULE_DESCRIPTION = """\
Description that will be used in module documentation.
This will likely take multiple lines.
"""
meta: MetaSchema = {
"id": "cc_http_scripts",
"name": "Example Module",
"title": "Shows how to create a module",
"description": MODULE_DESCRIPTION,
"distros": [ALL_DISTROS],
"frequency": PER_INSTANCE,
"activate_by_schema_keys": ["http_scripts"],
"examples": [],
}
__doc__ = get_meta_doc(meta)
def handle(
name: str, cfg: Config, cloud: Cloud, log: Logger, args: list
) -> None:
http_scripts = cfg.get("http_scripts", [])
for http_script in http_scripts:
if "url" not in http_script:
raise ValueError(f"Missing required key 'url' from {http_script}")
url = http_script.get("url")
req = urllib.request.Request(url)
with urllib.request.urlopen(req) as res:
script = res.read()
p = subprocess.run(
["/bin/sh"],
input=script,
stdout=PIPE,
stderr=STDOUT,
)
if p.stdout:
sys.stdout.write(p.stdout.decode("utf-8"))
設定ファイルのテンプレートを編集します。
vim config/cloud.cfg.tmpl
cloud_config_modules:
にhttp_scripts
を追記します。
$ git diff
diff --git a/config/cloud.cfg.tmpl b/config/cloud.cfg.tmpl
index fdd5a357..22bf6bd9 100644
--- a/config/cloud.cfg.tmpl
+++ b/config/cloud.cfg.tmpl
@@ -160,6 +160,7 @@ cloud_config_modules:
{% if variant in ["ubuntu", "unknown", "debian"] %}
- byobu
{% endif %}
+ - http_scripts
# The modules that run in the 'final' stage
cloud_final_modules:
変更内容をコミットしておきます。
git add -A
git commit -m 'Add http_scripts'
debパッケージを作成する
まずは、依存パッケージをインストールします。
sudo apt update
sudo apt install make devscripts debhelper dh-python dh-systemd python3-pytest python3-pytest-cov python3-pytest-mock python3-responses python3-configobj python3-debconf python3-jinja2 python3-jsonpatch python3-jsonschema python3-oauthlib python3-serial python3-setuptools
Makefileにdebパッケージを作成するターゲットが用意されていためそちらを実行します。
また上記依存パッケージ以外にも必要なパッケージがある場合はエラー文中に表示してくれます。
make deb
以下のようにdebファイルが作成されていることが分かります。
$ ls -lah *.deb
-rw-r--r-- 1 ubuntu ubuntu 502K Dec 30 02:16 cloud-init_22.4-51-g75953781-1~bddeb_all.deb
lrwxrwxrwx 1 ubuntu ubuntu 44 Dec 30 02:16 cloud-init_all.deb -> cloud-init_22.4-51-g75953781-1~bddeb_all.deb
カスタムcloud-initをインストールしたアーカイブを作成する
先ほど作成したdebをインストールしてさくらのクラウドのマイアーカイブを作成します。
さくらのクラウドでマイアーカイブ作成用サーバーを作成する
ベースになるディスクを作成するだけなので最小限のスペックで作成しました。
項目 | スペック |
---|---|
CPU | 1コア |
メモリ | 1GB |
ディスク容量 | 20GB |
ネットワーク | 共有 |
アーカイブ | Ubuntu Server 20.04.2 LTS 64bit (cloudimg) |
マイアーカイブ作成用サーバーにカスタムcloud-initをインストールする
debを作成した環境でscpを使いファイルをマイアーカイブ作成用サーバーに転送します。
scp cloud-init_22.4-51-g75953781-1~bddeb_all.deb ubuntu@<ip address>:~/
マイアーカイブ作成用サーバーでカスタムcloud-initをインストールします。
sudo apt install ./cloud-init_22.4-51-g75953781-1~bddeb_all.deb
cloud-initをリセットする
以下のようにcloud-initが実行済みになっているためリセットします。
$ cloud-init status
status: done
$ sudo cloud-init clean
$ cloud-init status
status: not run
シャットダウンしさくらのクラウドコンパネでマイアーカイブを作成する
sudo shutdown -h now
アーカイブソースにディスクを選択しマイアーカイブ作成用サーバーのディスクを選択します。
カスタムcloud-initインストール済みのマイアーカイブを使ってみる
ディスクからマイアーカイブへのコピーが完了するとマイアーカイブが利用可能になります。
マイアーカイブを使ってサーバーを作成する
現在さくらのクラウドではマイアーカイブをソースとする場合にコンパネからuser-dataを挿入することができません。
そのため、サーバーを起動せずに作成し、usacloudを利用してuser-dataを挿入してサーバーを起動します。
※ usacloudはさくらのクラウド用の公認CLIクライアントです。
※ 以下usacloudセットアップ済みの前提で進みます。
先ほど作成したアーカイブをマイアーカイブから選択します。
その他の設定項目で「作成後すぐに起動」を外しておきます。
usacloudでサーバーが作成されていることを確認します。
usacloud iaas server list
user-dataを作成しサーバーを起動する
user-dataを記述したファイルを作成します。
今回作成したモジュールを利用してdockerとk0sをインストールしてみます。
cat <<EOF > user-data
#cloud-config
users:
- name: ubuntu
ssh_import_id: gh:<GitHub のユーザー名>
lock_passwd: false
shell: /bin/bash
sudo: ALL=(ALL) NOPASSWD:ALL
uid: 1000
http_scripts:
- url: https://get.docker.com
- url: https://get.k0s.sh
EOF
user-dataを指定して起動します。
usacloud iaas server boot my-cloud-init --zone=is1b --user-data=./user-data
カスタムcloud-initが動作していることを確認する
以下のコマンドで共有セグメントに接続しているIPアドレスでsshコマンドを実行できます。
usacloud iaas server ssh my-cloud-init
user-dataで指定したgithubに登録している公開鍵に対応する秘密鍵でsshができました。
Welcome to Ubuntu 20.04.2 LTS (GNU/Linux 5.4.0-80-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/advantage
System information as of 2022年 12月 30日 金曜日 04:33:19 JST
System load: 0.0 Processes: 96
Usage of /: 7.7% of 19.21GB Users logged in: 0
Memory usage: 17% IPv4 address for ens3: <ip address>
Swap usage: 0%
0 updates can be applied immediately.
New release '22.04.1 LTS' available.
Run 'do-release-upgrade' to upgrade to it.
Last login: Fri Dec 30 04:26:45 2022 from <ip address>
ubuntu@sv-113402280888:~$
cloud-initが実行されていることを確認します。
--wait
オプションをつけることで完了するまで待つことができます。
$ cloud-init status
status: running
$ cloud-init status --wait
................................................................................................................................................................................................................................................................................................................................................................................................................................................................................
status: done
ログを確認する
cloud-initのログは/var/log/cloud-init.log
と/var/log/cloud-init-output.log
があります。
cloud-init.log
にはcloud-init自体のログがcloud-init-output.log
はモジュールのログが出力されます。
$ grep http_scripts /var/log/cloud-init.log
2022-12-29 19:48:51,729 - modules.py[DEBUG]: Running module http_scripts (<module 'cloudinit.config.cc_http_scripts' from '/usr/lib/python3/dist-packages/cloudinit/config/cc_http_scripts.py'>) with frequency once-per-instance
2022-12-29 19:48:51,729 - handlers.py[DEBUG]: start: modules-config/config-http_scripts: running config-http_scripts with frequency once-per-instance
2022-12-29 19:48:51,729 - util.py[DEBUG]: Writing to /var/lib/cloud/instances/nocloud/sem/config_http_scripts - wb: [644] 24 bytes
2022-12-29 19:48:51,730 - helpers.py[DEBUG]: Running config-http_scripts using lock (<FileLock using file '/var/lib/cloud/instances/nocloud/sem/config_http_scripts'>)
2022-12-29 19:51:03,066 - handlers.py[DEBUG]: finish: modules-config/config-http_scripts: SUCCESS: config-http_scripts ran successfully
https://get.docker.com
のログ(最大10行)
$ grep "Executing docker install script" /var/log/cloud-init-output.log -A10
# Executing docker install script, commit: 4f282167c425347a931ccfd95cc91fab041d414f
+ sh -c apt-get update -qq >/dev/null
+ sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -qq apt-transport-https ca-certificates curl >/dev/null
+ sh -c mkdir -p /etc/apt/keyrings && chmod -R 0755 /etc/apt/keyrings
+ sh -c curl -fsSL "https://download.docker.com/linux/ubuntu/gpg" | gpg --dearmor --yes -o /etc/apt/keyrings/docker.gpg
+ sh -c chmod a+r /etc/apt/keyrings/docker.gpg
+ sh -c echo "deb [arch=amd64 signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu focal stable" > /etc/apt/sources.list.d/docker.list
+ sh -c apt-get update -qq >/dev/null
+ sh -c DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends docker-ce docker-ce-cli containerd.io docker-compose-plugin docker-scan-plugin >/dev/null
+ version_gte 20.10
+ [ -z ]
https://get.k0s.sh
のログ
$ grep "k0s" /var/log/cloud-init-output.log
Downloading k0s from URL: https://github.com/k0sproject/k0s/releases/download/v1.25.4+k0s.0/k0s-v1.25.4+k0s.0-amd64
k0s is now executable in /usr/local/bin
dockerとk0sがインストールされていることを確認できました。
$ sudo docker version
Client: Docker Engine - Community
Version: 20.10.22
API version: 1.41
Go version: go1.18.9
Git commit: 3a2c30b
Built: Thu Dec 15 22:28:08 2022
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.22
API version: 1.41 (minimum version 1.12)
Go version: go1.18.9
Git commit: 42c8b31
Built: Thu Dec 15 22:25:58 2022
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.6.14
GitCommit: 9ba4b250366a5ddde94bb7c9d1def331423aa323
runc:
Version: 1.1.4
GitCommit: v1.1.4-0-g5fd4c4d
docker-init:
Version: 0.19.0
GitCommit: de40ad0
$ k0s version
v1.25.4+k0s.0
ちなみに... ubuntuユーザーが初期化されるわけではないのでdebファイルが残っていたり、最初の起動時に指定した公開鍵が.ssh/authorized_keys
に残っていたりします。
マイアーカイブ化する前にubuntuユーザーは削除するのが良いのかなと思います。
$ ls
cloud-init_22.4-51-g75953781-1~bddeb_all.deb
$ head -n1 .ssh/authorized_keys
ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQD0x++fAvsnjj5/BviVPDSEEg9fxTvvMhFRVqCNG0uwNP+sQ6Ng9joTcQONFeTRDIK/VwYAhQvhSnQztu3ybp/0vLFXvbBUtp1sQLZiI5l/aUh+Nfo+Kq26T0cBtXVOp4Leyug3nYK+T/kFLU2u/utbzJbD9bhn/1+gUdY2VLo28bSdz+9jlXSo9YzisETZm2PuAUH2cMc1dk1O0UpmvuPKGX+iEiiJHsb1dWj7HfGnJquYHJ2hQSXVpsR2o0hsGapBFbpiq/MHqzSZA71OxHIujqlrnQ1SVQsW7S0w5oNZVoMK1IYw055//iytNjLc9f8QpcWXG9sY4sOySbFUUiNg7j1aoDjWxpPA0lvLPOXeQNZXT+z7zqd52fz+QrsjwNvGErod3UP0WFPy5/tJrnmeZxbNSVmfX+O8npswGs7Y4Nb1AGgNpKK2vTkaCFVHgfwJ5oLTYdbh3AFrC2Do3Zomk2XHxXFxxZRHAixKMe/79Nxi2F95/6EEDMK+89ReeI8= ubuntu@vm
さいごに
今回は、urlを指定するとスクリプトをダウンロードして実行するcloud-initのモジュールを作成する流れを紹介させていただきました。
cloud-initでこんなことできたらいいのに!というアイデアをお持ちの方はぜひモジュール自作をしてみてはいかがでしょうか。
もしこの記事がその助けになれば幸いです。
余談ですが、このhttp_scriptsに実行時に環境変数も指定できる機能を追加しcanonical/cloud-initにPRを出してみました。マージされるかはわかりませんが、もしマージされれば各ディストリビューションが配布しているcloudimgに標準で取り込まれることになります。その際はぜひ使っていただけたらなと思います。