こんにちはnishikawaです。今回はDockerの勉強がてらにPXE環境の構築をしてみたので、そこでの気づきや結果などを書いていきます。
参考にしたのは、以前 弊社の元バンドマンであるgucci氏が書いてくれた記事を参考にしております。
概要
Dockerを勉強するにあたり、PXE環境をDockerコンテナを用いて構築します。インストールするものはUbuntu Server 20.04 LTSですが、nginxの設定まで文章にしていたら記事が凄く長くなったので、この記事ではPXEでブートメニューを起動するところまでを対象とします。
最初やりたかったこと
自宅にPXE環境を構築するにあたり既存のネットワークを侵害しないようにNICが複数あるPCが必要だと思いました。理由はわざわざ資材をダウンロードしたりするのが面倒なのとSSH接続して設定などを行ったほうが楽だからです。で、自宅にワイヤレスインターフェースとイーサネットを備えたノートPCがあったので、これを利用して以下のような環境を構築しようと思いました。
これが悪夢の始まりと知らずに・・・
結局構築したもの
で、多くの困難を乗り越えて最終的に構築したものが以下になります。見て分かるとおり、セグメントを物理的に分けてPXE環境を独立する予定が、同一セグメント内に集約されています。どうしてこうなったのかはこの後の文章をお読みください。
最初にやった設定
では、最初構想していた環境を構築するための設定をつらつらと書いていきたいと思います。
gucci氏の記事ではtftpd-hpaとdhcpdを使用してましたが、同じものでやっても芸がないので今回はdnsmasqを使っていきたいと思います。
dnsmasqはDNSを提供するミドルウェアですが、DHCPやTFTPを提供する機能も持っています。なので、このコンテナを生成するためのDockerイメージを手始めに作成していきます。
Dockerイメージ作成
ディレクトリ構成は以下のような感じ
dnsmasq ├── Dockerfile └── etc └── dnsmasq.conf
まずdnsmasq.conf
は以下のように設定を変更します。コンテナ内では root でプロセスを起動するのですが、デフォルトでは起動可能なユーザが設定されていないのでプロセスが立ち上がりません。そのためuser=root
を追加します。
あと、後ほど設定を追加するためにdnsmasq.d
ディレクトリ内の設定ファイルを取り入れられるようにしておきたいので、conf-dir=/etc/dnsmasq.d
を有効にしておきます。
# If you want dnsmasq to change uid and gid to something other # than the default, edit the following lines. -#user= +user=root #group= ・・・ # Include another lot of configuration options. #conf-file=/etc/dnsmasq.more.conf -#conf-dir=/etc/dnsmasq.d +conf-dir=/etc/dnsmasq.d
設定を変更したらDockerfileを作成します。
FROM ubuntu:20.04 RUN apt-get update && apt-get install -y dnsmasq VOLUME /etc/dnsmasq.d VOLUME /var/lib/tftp COPY etc/dnsmasq.conf /etc/dnsmasq.conf CMD /usr/sbin/dnsmasq --conf-file=/etc/dnsmasq.conf && tail -f /dev/null
ポイントは/etc/dnsmasq.d
と/var/lib/tftp
にアタッチポイントを作っておくところです。この後説明しますが、PXE用のdnsmasqの設定を後から/etc/dnsmasq.d
に追加するのと、ブートローダを/var/lib/tftp
配下に配置するのでここは必ず設定します。
後は先ほど変更したdnsmasq.confをCOPYでイメージ内に配置します。
最後に/usr/sbin/dnsmasq
をdnsmasq.confを指定して起動します。ただ、この方法で起動するとバックグラウンドでプロセスを立ち上げてしまうためコンテナがすぐに終了してしまいます。
そのため最後にtailをフォアグラウンドプロセスとして立ち上げコンテナが落ちるのを防ぎます。
Dockerイメージの作成は以下のコマンドで行います。
# docker image build -t test-dnsmasq:1.0.0 .
Dockerコンテナ作成
次にdocker-composeを使用してコンテナを作成・管理したいと思います。
ディレクトリ構成は以下です。
/opt/pxe ├── dnsmasq │ ├── etc │ │ └── dnsmasq.d │ │ └── pxe.conf │ └── var │ └── lib │ └── tftp │ ├── boot.img.gz │ ├── ldlinux.c32 │ ├── mini.iso │ ├── netboot.tar.gz │ ├── pxelinux.0 │ ├── pxelinux.cfg │ │ └── default │ ├── ubuntu-installer │ │ └── amd64 │ │ └── ... │ └── xen │ └── ... └── docker └── docker-compose.yml
まずはdnsmasqに設定を追加するため/opt/pxe/dnsmasq/etc/dnsmasq.d/pxe.conf
を作成して以下の設定を追記します。
#DHCP interface=enp9s0,lo #インターフェス名を設定(今回はenp9s0という名前だったのでそれを設定しています) bind-interfaces dhcp-range=192.168.1.100,192.168.1.200,12h #セグメントに沿ってレンジ設定(今回は192.168.1.0というネットワークでリース期間は12hを想定) dhcp-option=option:netmask,255.255.255.0 #多分いらないと思うが、一応サブネットマスクを設定 dhcp-option=option:router,192.168.1.1 #これも多分いらないと思うがルータのIPを設定 dhcp-boot=pxelinux.0 #ブートローダのファイル名 #TFTP enable-tftp #TFTP機能有効 tftp-root=/var/lib/tftp #TFTPのルートディレクトリ
/opt/pxe/dnsmasq/var/lib/tftp
配下に以下のURLから資材を取得
http://archive.ubuntu.com/ubuntu/dists/focal/main/installer-amd64/current/legacy-images/netboot/
以下、docker-compose.ymlの設定です。
--- version: "3" services: pxe_server: image: test-dnsmasq:1.0.0 volumes: - /opt/pxe/dnsmasq/etc/dnsmasq.d:/etc/dnsmasq.d - /opt/pxe/dnsmasq/var/lib/tftp:/var/lib/tftp network_mode: "host"
以上まで準備ができたらコンテナを起動します。
# docker-compose up -d
これで普通ならうまくいくはずだったのですが、色んな要因が重なり多くの苦しみを味わう羽目になりました。
苦しみポイント1:netboot用資材が見つからない
gucci氏の記事ではUbuntu Server 18.04 LTSのインストールイメージで行っていたのでネットブート用の資材がISO内にあったのでしょうが、今回私が行ったUbuntu Server 20.04 LTSのISOには それがありませんでした・・・。
と言うわけで悶絶とまでは行かないまでもそれなりに調査に時間がかかりました。
苦しみポイント2:TFTPでブートローダが取得できない
Dockerコンテナを起動したので、いざOSインストール対象のPCを起動してみたのですが、以下のようなエラーが出てブートメニューが表示されません。
TFTP PXE-E11: ARP timeout
どうやらTFTPでブートローダを落として読み込もうとしているのですが、アクセス対象のMACアドレスが解決できなくてエラーになっているようです。
これはなんだ?とずっと考えていたのですが、コンテナにarpコマンドを入れてarpテーブルを見てみたらなんとなく分かりました。以下が問題の箇所です。
Address HWtype HWaddress Flags Mask Iface 192.168.11.29 (incomplete) wlp7s0 192.168.1.112 (incomplete) br-24cc64eb1e3c ・・・
Iface部分が本当は先程/opt/pxe/dnsmasq/etc/dnsmasq.d/pxe.conf
で設定したenp9s0
になっていて欲しかったのですが、そうはなっていません。
ワイヤレスの方は正常に認識されているので、どうやらnetwork_mode: "host"
によってコンテナに直接紐付けられたインターフェースはワイヤレスの方みたいです。これによりTFTPサーバへのアクセスができなかったのが原因だと考えます。
これを解決するため、docker-compose.ymlでインターフェースを指定できないか あれこれ調査しました。
苦しみポイント3:Dockerコンテナを起動するときにネットワークインターフェースを指定できない
という訳でDockerコンテナ起動時に紐付けるインターフェースを指定する方法を探したのですが、結論から言うと私は見つけることはできませんでした。
なので、複数の物理NICを持っているマシンでnetwork_mode: "host"
を使用する時は思わぬ挙動になるため気をつけた方がよさそうというのが現時点での私の見解です。
以上から、当初想定していた環境の構築は抜本から考え直さないといけない状況になってしまいました。
そして改善へ
ひとしきり絶望した後、dnsmasqについてあれこれ調べていたらProxy DHCPという機能を見つけました。どうやらDHCPサーバが既に存在している環境においてPXEを提供するための機能らしいのでこれを使ってやってみることにしました。
やることは/opt/pxe/dnsmasq/etc/dnsmasq.d/pxe.conf
の設定変更で以下のように修正しました。
-#DHCP -interface=enp9s0,lo -bind-interfaces -dhcp-range=192.168.1.100,192.168.1.200,12h -dhcp-option=option:netmask,255.255.255.0 -dhcp-option=option:router,192.168.1.1 -dhcp-boot=pxelinux.0 +#Proxy DHCP +port=0 +dhcp-range=192.168.11.0,proxy +pxe-service=x86PC,"pxelinux",pxelinux #TFTP ・・・
これでコンテナを起動し直したらちゃんとブートメニューが表示されました。
まとめ
今回はDockerの勉強をしている過程で以下の知識を得ることができました。
- Dockerコンテナに物理インターフェースを直接割り当てるには
network_mode: "host"
を使用する - 複数の物理NICを持っているマシンでDockerコンテナを起動する際
network_mode: "host"
を使用する場合はインターフェースを指定することができないので注意が必要 - dnsmasqはDNSだけでなくDHCPとTFTPサーバとしても使える
- Proxy DHCPという仕組みで既存のDHCPサーバがあるネットワーク環境下でPXEを追加で提供することができる
今回はgucci氏の記事を元にPXEサーバをdockerコンテナ化してみました。dockerをここまでしっかり触ったことがなかったので発見ばかりでしたが、とても苦しい・・・もとい、楽しい経験をしました。
今後はpreseedなどを駆使してOSのインストールも自動化できたら良いなと思ったので機会があったらやってみようと思います。
それでは。