Ubuntu Weekly Recipe

第611回 Packerでmultipass用の仮想マシンイメージを作る

この記事を読むのに必要な時間:およそ 6 分

cloud-init用の設定ファイルの作成

cloud-init用の設定ファイルは比較的単純です。要するにPackerのcommunicatorがインスタンス内部にログインできるようにしておけば良いのですから。

$ mkdir cloud-data
$ touch cloud-data/meta-data
$ openssl passwd -6 ubuntupassword
$6$tXF3sTmGEMKhsanM$LyJQLz8lfqQITkNXTiVyAErUXpGkplHX3ftXZCB5/shRGqO7EuOnkRQ5cVjHNmqgAYzMwyOTPkDbZbuj26H6W0
$ cat <<'EOF' > cloud-data/user-data
#cloud-config
ssh_pwauth: true
apt_mirror: http://jp.archive.ubuntu.com/ubuntu/
users:
  - name: ubuntu
    sudo: ALL=(ALL) NOPASSWD:ALL
    passwd: $6$tXF3sTmGEMKhsanM$LyJQLz8lfqQITkNXTiVyAErUXpGkplHX3ftXZCB5/shRGqO7EuOnkRQ5cVjHNmqgAYzMwyOTPkDbZbuj26H6W0
    lock_passwd: false
EOF

cloud-data/meta-dataはcloud-initで使うインスタンスIDやホストネームを指定するために使うファイルですが,今回は何も設定しません。そこで空ファイルのみを作っています。ちなみにインスタンスIDはPackerのほうで設定しています。

cloud-init本体の設定のポイントは次のとおりです。

  • ssh_pwauthの設定でパスワードログインを許可しておく
  • sudoの設定でパスワード無しのsudoを許可しておく
  • passwdの設定でパスワードを設定しておく
  • lock_passwdの設定でパスワードログインを許可しておく

PackerのSSH communicatorはSSHの公開鍵ログインも利用可能です。複数のユーザーで運用するPackerファイルを作るなら公開鍵の設定にしておくべきでしょう。しかしながら今回はPackerファイルにパスワードを直接書いてパスワードログインすることにします。Packerファイルには生のパスワードを直接記述しますが,cloud-initのほうはハッシュ化したものを記述します。これはcloud-initのデータが作成後のルートファイルシステムの中に残るためです。

また上記では,Packerのprovisionerが設定を行う際にsudoコマンドをパスワードなしで実行できるような設定も行っています。

今回はPacker用のアカウントを「ubuntu」にしましたが,これはmultipassが自動で作るアカウントと同じ名前です。Packer用のアカウントということなら,別の名前にしても良いかもしれません。

Packer用の設定ファイルの作成

次にPacker用の設定ファイルを作成しましょう。

$ cat <<'EOF' > ubuntu.json
{
  "builders": [
    {
      "disk_discard": "unmap",
      "disk_image": true,
      "disk_interface": "virtio-scsi",
      "disk_size": "5120M",
      "headless": true,
      "http_directory": "cloud-data",
      "iso_checksum_type": "sha256",
      "iso_checksum_url": "http://cloud-images.ubuntu.com/releases/bionic/release/SHA256SUMS",
      "iso_url": "http://cloud-images.ubuntu.com/releases/bionic/release/ubuntu-18.04-server-cloudimg-amd64.img",
      "qemuargs": [
        [
          "-smbios",
          "type=1,serial=ds=nocloud-net;instance-id=packer;seedfrom=http://{{ .HTTPIP }}:{{ .HTTPPort }}/"
        ]
      ],
      "ssh_password": "ubuntupassword",
      "ssh_username": "ubuntu",
      "type": "qemu",
    }
  ],
  "provisioners": [
    {
      "execute_command": "sudo sh -c '{{ .Vars }} {{ .Path }}'",
      "inline": [
        "mv /etc/netplan/50-cloud-init.yaml /root/",
        "mv /etc/sudoers.d/90-cloud-init-users /root/",
        "/usr/bin/truncate --size 0 /etc/machine-id",
        "rm -r /var/lib/cloud /var/lib/dbus/machine-id ",
        "for i in group gshadow passwd shadow subuid subgid; do mv /etc/$i- /etc/$i; done",
        "/bin/sync",
        "/sbin/fstrim -v /"
      ],
      "remote_folder": "/tmp",
      "type": "shell"
    }
  ]
}
EOF

前半はbuildersの設定です。詳細はQEMU用のbuilderを確認してもらうとして,今回重要なポイントは次のとおりです。

  • http_directoryでcloud-initファイルのディレクトリを指定
  • iso_urliso_checksum_XXXでダウンロードするイメージを指定
  • qemuargsでcloud-initのダウンロードもとを指定
  • ssh_passwordssh_usernameでcommunicatorが使うパスワードとアカウントを指定
  • typeでQEMUを利用することを指定

iso_urliso_checksum_XXXUbuntu Cloud Imagesを参考に,使用するリリースなどにあわせて変更してください。もちろん,ローカルのファイルを指定してもかまいません。

qemuargsはPackerがイメージ構築時に利用するQEMUコマンドの引数です。ここにSMBIOSの設定を渡すことで,provisionerが動く前にcloud-initを実行できるようにしています。

{{ .HTTPIP }}{{ .HTTPPort }}は,builderが自動的に展開するIPアドレスとポート番号です。QEMUの場合,IPアドレスはユーザーネットワークのゲートウェイアドレスが割り当てられ,ポート番号はランダムな値が使われます。もちろん直接アドレスとポート番号を指定すれば,PackerのHTTPサーバー以外も利用可能です。

後半はprovisionerが実行するコマンドのリストです。Shell Provisionerによって,inlineでリストアップされたコマンドを順番にsudoで実行しています。

主にbuilderが実行したcloud-initで生成されたファイルのクリーンナップ処理です。これによりmultipassによるcloud-initがキレイに実行されることになります。

/etc/machie-idは初回起動時にsystemd-machine-id-setupで生成される,インストールされたマシン固有のIDを保存するファイルです。空でない場合はマシンIDを生成しません。つまりPackerで構築時にイメージを起動した時点でマシンIDが生成されるため,構築完了後にIDを削除しておかないと,同じイメージファイルを使うすべてのインスタンスが同じマシンIDを持ってしまうのです。空のファイルとして存在する必要があるため,truncateコマンドで中身を消しています。

/etc/group他のmvコマンドの実行は「cloud-init前の設定に戻す」処理です。cloud-initの内部で実行されるuseraddなどのコマンドは,設定ファイルを書き換える際に-を付けたオリジナルファイルのバックアップを残すようになっています。このファイルをもとに戻すことで,cloud-init時に作成したユーザーを「なかったこと」にできるわけです。

今回はPackerとmultipassで同じユーザーではありますが,multipassがユーザーを作れるように元に戻しています。またPacker用にユーザーを作るにしても,それを構築完了後に削除したいなら最後にこのようなコマンドを実行すると良いでしょう。

fstrimはファイルの削除等によって消された領域がイメージファイルから確実に削除になるようにするツールです。builderのほうでdisk_discardunmapに設定しているため,fstrimがイメージサイズの削減にも繋がります。今回のように削除しているファイルがそうでもない場合はあまり恩恵はありませんが,シンプロビジョニングなイメージを作る際の手順として覚えておきましょう。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。