Fedora CoreOSや色々なディストリビューションをnetboot.xyzでディスクレスブート


構成

  • dnsmasq Proxy DHCPで構成
  • matchbox
  • NETBOOT.XYZ
  • dnsmasq, matchbox, netboot.xyz はdocker-composeで起動
  • CentOS 7
  • KVM と Dockerが混在

こんな感じで起動

CoreOSの場合

それ以外の場合は NETBOOT.XYZ でブートメニュー出す

matchbox

CoreOSの起動に必要なIgnitionファイルやカーネルパラメータをホストしてくれるやつです。
PXEブートする際に、MACアドレスやアーキテクチャによって起動するイメージを変えたくなると思うのですが、
matchboxの仕様に沿ってセットアップすると仕組みを自前で用意するより楽になります。

netboot.xyz

LinuxディストリビューションのインストールにはDVD、USBメディアを用意する事が多いですが、メディアの用意が必要です。 
ネットワークインストールの仕組みで解決できるのですが、OSのバージョンアップごとにPXE用のメニューファイルの更新も必要です。

PXEブートのメニューファイルを各ディストリビューションのアップデートに追随してくれるのが netboot.xyz

※ netboot.xyz は、デフォルトで、カーネルイメージやinitramfsをインターネットの向こう側から取得するようです
 非常に遅いので、管理画面から事前にファイルを取得しておくことをオススメします。

docker-compose用のディレクトリ構成

docker-compose.ymlが置いてあるディレクトリに containers というディレクトリを作り、
配下に各コンテナ用サブディレクトリを作り管理。

matchboxの動作に必要なファイルはDockerfileでCOPYではなく、
docker-compose.yml の volumes: でマウントしています。

.
├── README.md
├── containers
│   ├── dnsmasq
│   │   ├── Dockerfile
│   │   ├── dnsmasq.conf
│   │   └── tftpboot
│   ├── matchbox
│   │   ├── etc
│   │   └── var
└── docker-compose.yml

docker-compose.yaml

dnsmasqはAlpineLinuxからDockerfileでビルド、
matchbox、netbootxyzはイメージをそのまま使わせてもらい、必要なディレクトリはマウント。

version: '3'

services:
  dnsmasq:
    container_name: dnsmasq
    build:
      context: containers/dnsmasq
    network_mode: "host"
    volumes:
    - ./containers/dnsmasq/tftpboot/linux-install:/var/ftpd
    - ./containers/dnsmasq/dnsmasq.conf:/etc/dnsmasq.conf
    restart: always
  matchbox:
    container_name: 'matchbox'
    image: quay.io/poseidon/matchbox:latest
    volumes:
      - ./containers/matchbox/var/lib/matchbox:/var/lib/matchbox:Z
      - ./containers/matchbox/etc/matchbox:/etc/matchbox:Z,ro
    ports:
      - '8080:8080'
    command: -address=0.0.0.0:8080 -rpc-address=0.0.0.0:8081
    restart: always
  netbootxyz:
    image: linuxserver/netbootxyz
    container_name: netbootxyz
    environment:
      - PUID=1000
      - PGID=1000
    ports:
      - 3000:3000
      - 8081:80
    restart: always

dnsmasq関連

DHCPが既にネットワーク内に立っているので、dhcp proxyで構成します

containers/dnsmasq/Dockerfile
FROM alpine:edge
RUN apk --no-cache add dnsmasq
EXPOSE 67/udp 69/udp 4011/udp
CMD ["dnsmasq", "-k", "-d"]

下記ポートDockerfileでEXPOSEしています。

ポート サービス
67/udp dhcp
69/udp TFTP
4011/udp PXE Proxy
dnsmasq.conf

コンテナで動かす際に
network_mode: "host" ホストのポートに直接紐付けて起動しています。
このモードの場合、ホストのネットワークインターフェースが見えるため、
dnsmasqが既存のvirbr0( libvirt のネットワークインターフェース )に悪さをしないように except-interface=virbr0 の設定を入れています。
( すみません、自宅サーバで検証に時間が取れず... )

ただ、libvirtdが立てるdnsmasqが立っていると、コンテナで立てるdnsmasqと競合するため、
サーバ起動後、手動でlibvirtdのdnsmasqのプロセスをkillしています( ここはもう少し調べたい... )

netboot.xyzを用いての初期セットアップを行いたいマシンは
dhcp-mac=set:netboot,02:1a:4a:16:01:9c
の様にして、netbootのタグを付けて振り分けています。

except-interface=virbr0
dhcp-range=10.10.254.0,proxy
pxe-service=tag:#ipxe,x86PC,"PXE chainload to iPXE",undionly.kpxe
# iPXE user classからのリクエストの場合はipxeタグをセットする
dhcp-userclass=set:ipxe,iPXE
# netboot.xyzでセットアップしたいMACアドレスを追記していく
dhcp-mac=set:netboot,02:1a:4a:16:01:9c
tag-if=set:netboot-xyz, tag:ipxe, tag:netboot
# netboot.xyz
pxe-service=tag:netboot-xyz,x86PC,"iPXE",http://10.10.254.200:8081/menus/menu.ipxe
# matchbox
pxe-service=tag:ipxe,x86PC,"iPXE",http://10.10.254.200:8080/boot.ipxe
enable-tftp
tftp-root=/var/ftpd
log-queries
log-dhcp
dnsmasqでの起動順序
  1. パソコンの電源投入、PXEブートを開始、ネットワークカードが頑張ってIPアドレスとPXEサーバを探す
  2. パソコンは dnsmasqのTFTPを経由してiPXEプロジェクトの undionly.kpxe をダウンロード、カーネルのチェインロードを始める
  3. undionly.kpxeがHTTPプロトコルでmatchboxやnetboot.xyzとやり取りする
  4. dnsmask.confでnetbootタグが設定されている場合は、 http://10.10.254.200:8080/assets/default.ipxe をPXEのメニューにしています
  5. netbootタグがついてないマシンはmatchboxの http://10.10.254.200:8080/boot.ipxe で処理
  6. matchboxはMACアドレスやアーキテクチャに応じてPXEのメニューファイルを作成し、パソコンに返す

という流れになります。

余談ですが、この辺、サイボウズのsabakan https://febc-yamamoto.hatenablog.jp/entry/2019/02/09/181927 で簡単にできるかな? とも思っているのですが、
CoreOSのIgnition周りなんかを生で触って理解したいというのもありmatchboxを使ってみています。

matchbox

docker-compose.ymlのmatchbox関連の設定ですが下記のようにしています。
オフィシャルのDockerイメージをそのまま使い、
設定ファイルやアセットはVolumesの設定でマウント。

ログは docker logs matchbox というようにコンテナの出力を見ています。

  matchbox:
    container_name: 'matchbox'
    image: quay.io/poseidon/matchbox:latest
    volumes:
      - ./containers/matchbox/var/lib/matchbox:/var/lib/matchbox:Z
      - ./containers/matchbox/etc/matchbox:/etc/matchbox:Z,ro
    ports:
      - '8080:8080'
    command: -address=0.0.0.0:8080 -rpc-address=0.0.0.0:8081
    restart: always

/var/lib/matchbox 配下のファイル構成は下記のようにしています。

.
├── assets
│   ├── default.ipxe
│   ├── fedora-coreos-31.20200113.3.1-live-initramfs.x86_64.img
│   └── fedora-coreos-31.20200113.3.1-live-kernel-x86_64
├── groups
│   └── fs01.json
├── ignition
│   ├── fedora-core-1.ign
│   └── netboot-xyz-1.ign
└── profiles
    ├── fedora-core-1.json
    └── netboot-xyz-1.json

それぞれのディレクトリですが

group MACアドレスにどのプロファイルを割り当てるか
{
  "id": "fedora-core-1",
  "name": "Fedora Core - 1",
  "profile": "fedora-core-1",
  "selector": {
    "mac": "00:24:1d:18:28:09"
  }
}
profile PXEブート時のカーネルイメージの指定とカーネルパラメータ

sshkey, ignition.config.url はそれぞれの環境に応じて変更する必要あり。

基本的には Fedora CoreOS のPXEブートのパラメータ を参照

パラメータの意味については CoreOSの説明 が役に立つかも。

Fedora CoreOSのサイトから私が変更した箇所が2点あり

  1. 2020/05時点ではdockerが cgroup v2 に対応していないので v1 に切り替え
  2. selinuxのoff

cgroup v2 ではなく v1 に変更した手順
Fedoraの新しめのカーネルは cgroup v2 だが、docker側が対応していないのでv1にしておく必要がある カーネルパラメータに systemd.unified_cgroup_hierarchy=0 を追加した

Fedora CoreOSのPXEでのセットアップ手順 には乗ってないので注意
( Fedora CoreOS はdockerではなく、podman推しなのかもしれませんが )

またカーネルオプションで selinux=0 を追加して selinuxをオフにしました。

matchboxのインストールディレクトリ配下の
profiles/fedora-core-1.json に下記のように設置。

{
    "id": "fedora-core-1",
    "name": "Fedora Core - 1",
    "ignition_id": "fedora-core-1.ign",
    "boot": {
        "kernel": "/assets/fedora-coreos-31.20200407.3.0-live-kernel-x86_64",
        "initrd": [
            "/assets/fedora-coreos-31.20200407.3.0-live-initramfs.x86_64.img"
        ],
        "args": [
            "ip=dhcp",
            "rd.neednet=1",
            "console=tty0",
            "console=ttyS0",
            "selinux=0",
            "ignition.firstboot",
            "ignition.platform.id=metal",
            "systemd.unified_cgroup_hierarchy=0",
            "sshkey=ssh-rsa himitu [email protected]",
            "ignition.config.url=http://10.10.254.200:8080/ignition?mac=${mac:hexhyp}"
        ]
    }
}
assets CoreOS起動時に必要なカーネルイメージ、initramfsファイルなど

fedora CoreOSのダウンロードページ からダウンロードもできるのですが、
都度ダウンロードするのは面倒なので、そのページにあるjsonファイルを参考にして、下記のようにもダウンロードできます。

matchboxのインストールディレクトリのassetディレクトリ配下に移動し

coreos_stable=$(curl https://builds.coreos.fedoraproject.org/streams/stable.json)
kernel_url=$(echo $coreos_stable | jq  --raw-output '.architectures.x86_64.artifacts.metal.formats.pxe.kernel.location')
initramfs_url=$(echo $coreos_stable | jq  --raw-output '.architectures.x86_64.artifacts.metal.formats.pxe.initramfs.location')

curl -O $kernel_url
curl -O $initramfs_url

でstableなカーネル、initramfsイメージをダウンロードしています。

併せてmatchbox/profile配下のプロファイル用のファイルも更新する必要があります。

またmatchboxのこのディレクトリは、カーネルイメージやinitramfsを置く場所として使うのですが、
Webサーバのように公開されるので、インストール関連で使うものを置いておくのも便利かもしれません。
私はnetboot.xyzのファイルなどを置いています。

ignition OS内の各種設定( ユーザー追加、ストレージ、サービスの設定などなど )

はまったポイントなのですが、いろいろなサイトの事例のjsonをコピペして編集してみたのですが、
ユーザー追加ができてないようでハマってました( ログインできないので調査しずらい... )
Container Linuxの後継となる新たなコンテナ向けOS「Fedora CoreOS」の「Ignitionの設定ファイルとfcctコマンド」を参考にしつつ、fcctコマンドでjsonを作成しました。

fcctコマンドのインストールだけどpodman経由でインストールする手順もあるみたい 手軽なのよい( プロダクション環境だとpodmanのレポジトリが落ちてるケースも考える必要がありそうだけど )

下記のようなyamlファイルを作成し、

variant: fcos
version: 1.0.0
passwd:
  users:
    - name: inoue
      groups:
        - adm
        - sudo
      ssh_authorized_keys:
        - ssh-rsa ssh公開鍵 [email protected]
systemd:
  units:
    - name: shell.service
      enabled: true
      contents: |
        [Unit]
        Description=Alpine CLI Shell
        After=network.target
        [Service]
        Type=simple
        ExecStart=/usr/bin/podman run --rm --privileged -v /:/host -ti alpine:latest /bin/ash
        [Install]
        WantedBy=multi-user.target

./fcct --input fcc_test.yaml --pretty --output fedora-core-1.ign のように実行すると下記のものができました。

{
  "ignition": {
    "config": {
      "replace": {
        "source": null,
        "verification": {}
      }
    },
    "security": {
      "tls": {}
    },
    "timeouts": {},
    "version": "3.0.0"
  },
  "passwd": {
    "users": [
      {
        "groups": [
          "adm",
          "sudo"
        ],
        "name": "inoue",
        "sshAuthorizedKeys": [
          "ssh-rsa SSHの公開鍵 [email protected]"
        ]
      }
    ]
  },
  "storage": {},
  "systemd": {
    "units": [
      {
        "contents": "[Unit]\nDescription=Alpine CLI Shell\nAfter=network.target\n[Service]\nType=simple\nExecStart=/usr/bin/podman run --rm --privileged -v /:/host -ti alpine:latest /bin/ash\n[Install]\nWantedBy=multi-user.target\n",
        "enabled": true,
        "name": "shell.service"
      }
    ]
  }

このjsonファイルをignitionディレクトリ配下に設置します。

firewalld設定

CentOS7だとファイアーウォールで開ける必要があります。

firewall-cmd --zone=public --add-service=tftp-client --permanent
firewall-cmd --zone=public --add-service=tftp --permanent
firewall-cmd --zone=public --add-service=proxy-dhcp --permanent
firewall-cmd --zone=public --add-service=dhcp --permanent
firewall-cmd --zone=public --add-port=69/udp --permanent         # TFTP
firewall-cmd --zone=public --add-service=dns --permanent
firewall-cmd --zone=public --add-port=4011/udp --permanent       # PXE proxy
firewall-cmd --zone=public --add-port=8080/tcp --permanent       # 私がmatchboxに割り当てたポート
firewall-cmd --reload
firewall-cmd --list-all