Azure cloud-intの使い方

スポンサーリンク

仮想マシン(VM)や想マシンスケールセット(VMSS)はcloud-initという仕組みを使用すると、起動時に予め定義された処理を実行する事ができます。例えば、アプリケーションサーバをインストールしたり証明書を配布したりなどのオーケストレーションが可能です。このページではcloud-initの操作例をまとめます。

前提

公式ドキュメント

参考になる公式ドキュメントを以下に示します。

事前設定

以下、リソースグループを作成します。

az group create --name MyResourceGroup --location japaneast

cloud-initを利用した構築

cloud-initファイルの作成

cloud-initはJSON形式で起動時の処理を記述できます。以下は「チュートリアル – Azure での Linux 仮想マシンの初回の起動時に cloud-init を使用してカスタマイズする方法」で紹介された設定例です。チュートリアルで紹介された設定例と全く同じファイルを作成します。

なお、cloud-initはAzureの公式ドキュメントでは紹介されていない便利な使い方も色々ありますので、必要に応じて「Cloud config examples」などのcloud-initのマニュアルも必要に応じて参照ください。

cat << 'EOF' > cloud-init-nginx.txt
#cloud-config
package_upgrade: true
packages:
  - nginx
  - nodejs
  - npm
write_files:
  - owner: www-data:www-data
    path: /etc/nginx/sites-available/default
    content: |
      server {
        listen 80;
        location / {
          proxy_pass http://localhost:3000;
          proxy_http_version 1.1;
          proxy_set_header Upgrade $http_upgrade;
          proxy_set_header Connection keep-alive;
          proxy_set_header Host $host;
          proxy_cache_bypass $http_upgrade;
        }
      }
  - owner: azureuser:azureuser
    path: /home/azureuser/myapp/index.js
    content: |
      var express = require('express')
      var app = express()
      var os = require('os');
      app.get('/', function (req, res) {
        res.send('Hello World from host ' + os.hostname() + '!')
      })
      app.listen(3000, function () {
        console.log('Hello world app listening on port 3000!')
      })
runcmd:
  - service nginx restart
  - cd "/home/azureuser/myapp"
  - npm init
  - npm install express -y
  - nodejs index.js
EOF

仮想マシンの作成

引数custom-dataにcloud-initのファイルパスを指定して仮想マシンを作成します。このような指定をする事によって、仮想マシン作成後にcloud-initで記述された処理が実行されます。

az vm create \
  --resource-group MyResourceGroup \
  --name linux020 \
  --image UbuntuLTS \
  --size Standard_D1_v2 \
  --priority Spot \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/authorized_keys \
  --custom-data cloud-init-nginx.txt

動作確認

仮想マシン作成時のログは以下の通りです。仮想マシンに割り当てられたパブリックIPアドレスを確認します。

admin@mac19 ~ % az vm create \
  --resource-group myResourceGroup \
  --name linux020 \
  --image UbuntuLTS \
  --size Standard_D1_v2 \
  --priority Spot \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/authorized_keys \
  --custom-data cloud-init-nginx.txt
It is recommended to use parameter "--public-ip-sku Standard" to create new VM with Standard public IP. Please note that the default public IP used for VM creation will be changed from Basic to Standard in the future.
{
  "fqdns": "",
  "id": "/subscriptions/2e2f81a9-b030-410f-9784-c582580c932e/resourceGroups/myResourceGroup/providers/Microsoft.Compute/virtualMachines/linux020",
  "location": "japaneast",
  "macAddress": "00-22-48-E7-07-C3",
  "powerState": "VM running",
  "privateIpAddress": "10.0.0.4",
  "publicIpAddress": "52.253.108.202",
  "resourceGroup": "myResourceGroup",
  "zones": ""
}

前述の操作で確認したパブリックIPアドレスをブラウザに入力し、nginxとnodejsによって作成されたWebページを確認します。なお、OS起動後にcloud-initが動作します。ですので、仮想マシンの作成完了とnginxとnodejsのインストール完了にはタイムラグがある事に注意してください。

ここでは初学者向けに「OS起動後」という曖昧な言い方をしていますが、cloud-initは処理によって実行されるタイミングが異なります。正確な情報は公式ドキュメントの「Boot Stages」を参照ください。

動作確認用のnodejsページ

スクリプトを利用した構築

動作確認用スクリプトの作成

前述のcustome-dataにはjsonではなくスクリプトを指定する事もできます。例えば、既存の構築スクリプトがあり、技術負債を現行踏襲せざるを得ないその資産を有効活用する場合などが想定されます。

以下、postgresqlをインストールするスクリプトを適当なファイル名で作成します。

cat << 'EOF' > install-postgresql13.sh
#!/bin/bash
setenforce 0
sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config
systemctl disable firewalld.service --now
dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-8-x86_64/pgdg-redhat-repo-latest.noarch.rpm
dnf -qy module disable postgresql
dnf install -y postgresql13-server
/usr/pgsql-13/bin/postgresql-13-setup initdb
systemctl enable postgresql-13 --now
EOF

仮想マシンの作成

引数custom-dataにスクリプトのファイルパスを指定して仮想マシンを作成します。

az vm create \
  --resource-group MyResourceGroup \
  --name linux030 \
  --image procomputers:rocky-linux-8-minimal:rocky-linux-8-minimal:latest \
  --size Standard_D1_v2 \
  --priority Spot \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/authorized_keys  \
  --custom-data install-postgresql13.sh

動作確認

仮想マシンにsshでログインし、postgresqlが起動していることを確認します。

[azureuser@linux030 ~]$ sudo systemctl status postgresql-13.service
● postgresql-13.service - PostgreSQL 13 database server
   Loaded: loaded (/usr/lib/systemd/system/postgresql-13.service; enabled; vendor preset: disabled)
   Active: active (running) since Sat 2022-03-05 06:40:40 UTC; 1min 39s ago
     Docs: https://www.postgresql.org/docs/13/static/
  Process: 7426 ExecStartPre=/usr/pgsql-13/bin/postgresql-13-check-db-dir ${PGDATA} (code=exited, status=0/SUCCESS)
 Main PID: 7432 (postmaster)
    Tasks: 8 (limit: 20655)
   Memory: 16.9M
   CGroup: /system.slice/postgresql-13.service
           ├─7432 /usr/pgsql-13/bin/postmaster -D /var/lib/pgsql/13/data/
           ├─7433 postgres: logger 
           ├─7435 postgres: checkpointer 
           ├─7436 postgres: background writer 
           ├─7437 postgres: walwriter 
           ├─7438 postgres: autovacuum launcher 
           ├─7439 postgres: stats collector 
           └─7440 postgres: logical replication launcher 

Mar 05 06:40:40 linux030 systemd[1]: Starting PostgreSQL 13 database server...
Mar 05 06:40:40 linux030 postmaster[7432]: 2022-03-05 06:40:40.309 UTC [7432] LOG:  redirecting log output to logging collector process
Mar 05 06:40:40 linux030 postmaster[7432]: 2022-03-05 06:40:40.309 UTC [7432] HINT:  Future log output will appear in directory "log".
Mar 05 06:40:40 linux030 systemd[1]: Started PostgreSQL 13 database server.

cloud-initのトラブルシューティング

トラブルの仕込み

Azureのチュートリアルで紹介されているnginxの構築例をRockyLinuxに適用してみましょう。この構築例はUbuntuを前提にしていますので、想定外のRockyLinuxに適用すると色々とトラブルが発生します。このトラブルを題材にして、色々と観察してみましょう。

az vm create \
  --resource-group MyResourceGroup \
  --name linux040 \
  --image procomputers:rocky-linux-8-minimal:rocky-linux-8-minimal:latest \
  --size Standard_D1_v2 \
  --priority Spot \
  --admin-username azureuser \
  --ssh-key-values ~/.ssh/authorized_keys  \
  --custom-data cloud-init-nginx.txt

タイムラグ

cloud-initは仮想マシンの起動後に処理されます。したがって、sshでログイン可能な状態になったとしても、nginx等のインストールが完了しているとは限りません。

その証拠として、/var/log/messagesを観察してみましょう。06:48:54にOpenSSHが起動し、その後の06:49:45にcloud-initによるパッケージアップデートが開始されます。

[azureuser@linux040 ~]$ sudo cat /var/log/messages

  <omitted>

Mar  5 06:48:54 linux040 systemd[1]: Started Permit User Sessions.
Mar  5 06:48:54 linux040 systemd[1]: Started Command Scheduler.
Mar  5 06:48:54 linux040 systemd[1]: Started Getty on tty1.
Mar  5 06:48:54 linux040 systemd[1]: Started Job spooling tools.
Mar  5 06:48:54 linux040 systemd[1]: Started Serial Getty on ttyS0.
Mar  5 06:48:54 linux040 systemd[1]: Reached target Login Prompts.
Mar  5 06:48:54 linux040 systemd[1]: Started OpenSSH server daemon.
Mar  5 06:48:54 linux040 systemd[1]: Started System Logging Service.
Mar  5 06:48:54 linux040 systemd[1]: Reached target Multi-User System.

  <omitted>

Mar  5 06:49:39 linux040 kdumpctl[1447]: kdump: kexec: loaded kdump kernel
Mar  5 06:49:39 linux040 kdumpctl[1447]: kdump: Starting kdump: [OK]
Mar  5 06:49:39 linux040 systemd[1]: Started Crash recovery kernel arming.
Mar  5 06:49:45 linux040 cloud-init[1451]: Red Hat CodeReady Linux Builder for RHEL 8 x86_ 2.8 MB/s | 6.4 MB     00:02
Mar  5 06:49:47 linux040 cloud-init[1451]: Microsoft Azure RPMs for Red Hat Enterprise Lin 2.6 kB/s | 2.4 kB     00:00
Mar  5 06:49:48 linux040 cloud-init[1451]: Last metadata expiration check: 0:00:01 ago on Sat 05 Mar 2022 06:49:47 AM UTC.
Mar  5 06:49:52 linux040 cloud-init[1451]: Metadata cache created.
Mar  5 06:49:54 linux040 cloud-init[1451]: Last metadata expiration check: 0:00:07 ago on Sat 05 Mar 2022 06:49:47 AM UTC.
Mar  5 06:49:56 linux040 cloud-init[1451]: Dependencies resolved.
Mar  5 06:49:56 linux040 cloud-init[1451]: =============================================================================================================
Mar  5 06:49:56 linux040 cloud-init[1451]: Package                      Arch    Version               Repository                                   Size
Mar  5 06:49:56 linux040 cloud-init[1451]: =============================================================================================================
Mar  5 06:49:56 linux040 cloud-init[1451]: Installing:
Mar  5 06:49:56 linux040 cloud-init[1451]: kernel                       x86_64  4.18.0-348.12.2.el8_5 rhui-rhel-8-for-x86_64-baseos-rhui-rpms     7.0 M
Mar  5 06:49:56 linux040 cloud-init[1451]: Upgrading:
Mar  5 06:49:56 linux040 cloud-init[1451]: bpftool                      x86_64  4.18.0-348.12.2.el8_5 rhui-rhel-8-for-x86_64-baseos-rhui-rpms     7.7 M
Mar  5 06:49:56 linux040 cloud-init[1451]: clevis                       x86_64  15-1.el8_5.1          rhui-rhel-8-for-x86_64-appstream-rhui-rpms   57 k
Mar  5 06:49:56 linux040 cloud-init[1451]: clevis-luks                  x86_64  15-1.el8_5.1          rhui-rhel-8-for-x86_64-appstream-rhui-rpms   37 k
Mar  5 06:49:56 linux040 cloud-init[1451]: cloud-init                   noarch  21.1-7.el8_5.3        rhui-rhel-8-for-x86_64-appstream-rhui-rpms  1.0 M

cloud-initの進捗を確認したい場合は、cloud-init-output.logをtailすると良いでしょう。cloud-init-output.logには、cloud-initの標準出力が記録されます。

[azureuser@linux040 ~]$ sudo tail -f /var/log/cloud-init-output.log 
  Upgrading        : polkit-0.115-13.el8_5.1.x86_64                      18/157 
  Running scriptlet: polkit-0.115-13.el8_5.1.x86_64                      18/157 
  Upgrading        : libsmbclient-4.14.5-9.el8_5.x86_64                  19/157 
  Upgrading        : cyrus-sasl-gssapi-2.1.27-6.el8_5.x86_64             20/157 
  Upgrading        : nss-softokn-freebl-3.67.0-7.el8_5.x86_64            21/157 
  Upgrading        : nss-softokn-3.67.0-7.el8_5.x86_64                   22/157 
  Upgrading        : nss-sysinit-3.67.0-7.el8_5.x86_64                   23/157 
  Upgrading        : nss-3.67.0-7.el8_5.x86_64                           24/157 
  Upgrading        : systemd-udev-239-51.el8_5.3.x86_64                  25/157 
  Running scriptlet: systemd-udev-239-51.el8_5.3.x86_64                  25/157 
  Installing       : kernel-core-4.18.0-348.12.2.el8_5.x86_64            26/157 
  Running scriptlet: kernel-core-4.18.0-348.12.2.el8_5.x86_64            26/157

エラー事例の観察

/var/log/cloud-init.logを表示し、cloud-initのwrite_filesモジュールのログに着目します。ログを見ると、www-dataというユーザとグループが存在しない事が分かります。www-dataユーザはUbuntuを前提としてますので、www-dataユーザが存在しないRockyLinuxではエラーとなります。

[azureuser@linux040 ~]$ sudo cat /var/log/cloud-init.log 

 <omitted>

2022-03-05 06:48:52,120 - handlers.py[DEBUG]: finish: init-network/config-write-files: FAIL: running config-write-files with frequency once-per-instance
2022-03-05 06:48:52,120 - util.py[WARNING]: Running module write-files (<module 'cloudinit.config.cc_write_files' from '/usr/lib/python3.6/site-packages/cloudinit/config/cc_write_files.py'>) failed
2022-03-05 06:48:52,120 - util.py[DEBUG]: Running module write-files (<module 'cloudinit.config.cc_write_files' from '/usr/lib/python3.6/site-packages/cloudinit/config/cc_write_files.py'>) failed
Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/cloudinit/util.py", line 1381, in chownbyname
    uid = pwd.getpwnam(user).pw_uid
KeyError: "getpwnam(): name not found: 'www-data'"

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/usr/lib/python3.6/site-packages/cloudinit/stages.py", line 876, in _run_modules
    freq=freq)
  File "/usr/lib/python3.6/site-packages/cloudinit/cloud.py", line 54, in run
    return self._runners.run(name, functor, args, freq, clear_on_fail)
  File "/usr/lib/python3.6/site-packages/cloudinit/helpers.py", line 185, in run
    results = functor(*args)
  File "/usr/lib/python3.6/site-packages/cloudinit/config/cc_write_files.py", line 172, in handle
    write_files(name, files)
  File "/usr/lib/python3.6/site-packages/cloudinit/config/cc_write_files.py", line 212, in write_files
    util.chownbyname(path, u, g)
  File "/usr/lib/python3.6/site-packages/cloudinit/util.py", line 1385, in chownbyname
    raise OSError("Unknown user or group: %s" % (e)) from e
OSError: Unknown user or group: "getpwnam(): name not found: 'www-data'"

/var/log/cloud-init-output.logを表示し、nodejsコマンドの実行結果に着目します。Ubuntuではnodejsパッケージインストールによってnodejsコマンドが使えるようになりますが、RockyLinuxではnodejsコマンドが使えません。よって、エラーが出力されます。

[azureuser@linux040 ~]$ sudo cat /var/log/cloud-init-output.log 

 <omitted>

/var/lib/cloud/instance/scripts/runcmd: line 6: nodejs: command not found
Cloud-init v. 21.1-7.el8_5.3 running 'modules:final' at Sat, 05 Mar 2022 06:54:33 +0000. Up 933.93 seconds.
2022-03-05 06:54:38,699 - cc_scripts_user.py[WARNING]: Failed to run module scripts-user (scripts in /var/lib/cloud/instance/scripts)
2022-03-05 06:54:38,699 - util.py[WARNING]: Running module scripts-user (<module 'cloudinit.config.cc_scripts_user' from '/usr/lib/python3.6/site-packages/cloudinit/config/cc_scripts_user.py'>) failed
Cloud-init v. 21.1-7.el8_5.3 finished at Sat, 05 Mar 2022 06:54:38 +0000. Datasource DataSourceAzure [seed=/dev/sr0].  Up 938.97 seconds
タイトルとURLをコピーしました