Ubuntu Weekly Recipe

第678回 distrobuilderでLXD/LXC用のカスタムイメージを作成する

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

files: ルートファイルシステムのカスタマイズ

filesでは,downloaderによって構築したベースファイルシステム上の各種ファイルをカスタマイズできます。ここの項目はgeneratorsfiltersの2種類のコンポーネントで形成されています。基本的にfilesの処理が行われるのは最後にルートファイルシステムをイメージとしてアーカイブする直前です。

ubuntu.yamlのfilesは長いので一部だけ抜粋して表示します。

files:
- path: /etc/hostname
  generator: hostname

- path: /etc/resolvconf/resolv.conf.d/original
  generator: remove

- path: /etc/user/profile
  generator: copy
  source: /etc/profile

- path: /var/lib/dbus/machine-id
  generator: remove

- name: ext4
  generator: fstab
  types:
  - vm

generatorにはhostnameやhosts,fstab,lxd-agent,systemdといった特定のファイル専用のタイプと,dump/copy/removeのような汎用的なタイプの2種類にわかれます。

このうち専用のものは主にコンテナの中で特殊扱いされるもの,もしくはlxd.tar.gzにテンプレートとして保存され,インスタンス生成時に自動生成されるべきものが大半です。基本的に公式イメージの書式に合わせておいたほうが無難でしょう。

removeはイメージ作成時に自動生成されたファイルを確実に削除しておきたい場合に便利です。copyはホストから特定のファイルをイメージ内部にコピーしたい場合に使います。またmode/uid/gidの指定も可能です。

dumpは特定の内容をファイルに書き出したい場合に利用します。

- path: /etc/machine-id
  generator: dump

- path: /etc/netplan/10-lxc.yaml
  generator: dump
  content: |-
    network:
      version: 2
      ethernets:
        eth0:
          dhcp4: true
          dhcp-identifier: mac
  releases:
  - bionic
  - eoan
  - focal
  - groovy
  types:
  - container
  variants:
  - default

/etc/machine-idのようにcontent未指定の場合は,ただの空ファイルとなります。

releases/types/variantsはこの設定を特定のイメージにのみ適用したい場合のフィルタリング機能です。上記の例だと,Ubuntuの18.04(bionic)以上にのみ適用する想定であり,さらにvariants未指定のコンテナイメージでのみ使われます。コンテナのみになっているのはVM版だとNICの名前がeth0ではなくenp5s0だからです。

これらはimages以下で設定された値が参照されます。またdistrobuilder実行時の末尾に-o image.variant=cloudのようにオプションを使って値を追加することも可能です。

このフィルタリング機能はfilesだけでなく,後述のpackagesやrepositories,actionsでも利用します。

- name: network-config
  generator: cloud-init
  variants:
  - cloud

- name: user-data
  generator: cloud-init
  variants:
  - cloud

- name: vendor-data
  generator: cloud-init
  variants:
  - cloud

これらはcloud-init用のテンプレートファイルを作成します。実際に作成されるのは,lxd.tar.gzの中になります。

packages: パッケージのインストールと削除

packagesでは,追加でインストールするパッケージや削除しておくパッケージを指定します。こちらもフィルタリング機能を用いて,特定のアーキテクチャー向けイメージだけ変えるといった対応が可能です。

packages:
  manager: apt
  update: true
  cleanup: true
  sets:
  - packages:
    - fuse
    - language-pack-en
    - openssh-client
    - vim
    action: install

  - packages:
    - cloud-init
    action: install
    variants:
    - cloud

  - packages:
    - os-prober
    action: remove
    types:
    - vm

またmanagerによってdnfやyum,pacmanといったパッケージ管理システムを選択できます。またcustom-managerを使うと,任意のコマンド列を「パッケージ管理システム」として指定できます。

上記の例だとaptを使って,fuseなどをインストールし,cloud設定があるならcloud-initをインストール,VMイメージならos-proberを削除しています。

  repositories:
  - name: sources.list
    url: |-
      deb http://archive.ubuntu.com/ubuntu {{ image.release }} main restricted universe multiverse
      deb http://archive.ubuntu.com/ubuntu {{ image.release }}-updates main restricted universe multiverse
      deb http://security.ubuntu.com/ubuntu {{ image.release }}-security main restricted universe multiverse
    architectures:
    - amd64
    - i386

さらにrepositoriesでパッケージリポジトリを設定できます。Ubuntuならsources.listに記述する内容ですね。ここで{{ image.release }}のようにメタデータフィールドの値を参照しています。このためubuntu.yamlのimage.releaseはUbuntuのリリースコード名が記述されることを期待しているわけです。

actions: イメージビルドの各処理の途中で実施するコマンド設定

actionsではイメージビルドの処理の途中でフック処理として任意のコマンドを実行できるようになっています。

actions:
- trigger: post-update
  action: |-
    #!/bin/sh
    set -eux

    # Create the ubuntu user account
    getent group sudo >/dev/null 2>&1 || groupadd --system sudo
    useradd --create-home -s /bin/bash -G sudo -U ubuntu
  variants:
  - default

- trigger: post-packages
  action: |-
    #!/bin/sh
    set -eux

    # Make sure the locale is built and functional
    locale-gen en_US.UTF-8
    update-locale LANG=en_US.UTF-8

    # Cleanup underlying /run
    mount -o bind / /mnt
    rm -rf /mnt/run/*
    umount /mnt

    # Cleanup temporary shadow paths
    rm /etc/*-

triggerにフックするタイミングを記述し,actionに実際に実行するシェルスクリプトを記述します。

バージョン1.3でサポートしているフックは次の4種類です。

  • post-unpack:ダウンロード・作成したベースイメージを展開したあとに実施
  • post-updatepackages.updatetrueのときのみ,パッケージ情報を更新したあとに実施
  • post-packagespackagesで記述したパッケージ関連の処理を実施したあと
  • post-filesfilesで記述されたファイルの変更処理を実施したあと

実際にフックが叩かれるのも上記の順番になります。

mappings: アーキテクチャー名のマッピング

mappingsにはCPUアーキテクチャー名のマッピングテーブルを記述します。CPUアーキテクチャー名はディストリビューションによって「x86_64」だったり「amd64」だったり「generic_64」だったりと,微妙に異なります。そこでこのマッピングテーブルで,そのあたりの差異を吸収してしまおうというわけです。

mappings:
  architecture_map: debian

architecture_mapの後ろにディストリビューション名を書けば,そのディストリビューションに合わせた設定を行います。

また,architecturesによってアーキテクチャーごとに任意のマッピングをハッシュテーブルとしても記述できます。なお記述はdistrobuider側の名前: ディストリビューション側の名前となります。distrobuilder側の名前は,実質LXDのコード上の名前と一致しています。

  • i686:Intel x86 32bit/IA-32
  • x86_64:Intel x86 64bit/AMD64/Intel 64
  • armv7l:ARMv7 32bit
  • aarch64:AAarch64 64bit
  • ppc:PowerPC 32bit
  • ppc64:PowerPC 64bit Big Endian
  • ppc64le:PowerPC 64bt Little Endian
  • s390x:S/390 Big Endian
  • mips:MIPS 32bit
  • mips64:MIPS 64bit
  • riscv32:RISC-V 32bit
  • riscv64:RISC-V 64bit

実際のところ個々のディストリビューションのサポートアーキテクチャーが上記に完全に一致するわけではありませんが,おおよそ一致しているものがひとつのカテゴリにまとめられています。

ここまでいろいろ述べましたが,そこまで難しい書式ではないため,ubuntu.yamlやすべての利用可能なオプションを網羅したscheme.yamlなどで使用例を見ながら作業すれば,自分好みのイメージが作れることでしょう。

著者プロフィール

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

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