完全仮想環境でもコンテナ
KVM (qemu) で作成したゲスト OS の中にコンテナを作成します。
劇中劇なら VM 方式を同一の入れ子にしたほうがインストール手順が単純化しますが、
完全仮想環境同士で多層化すると資源を多く消費することになります。
LXC (Linux Containers) なら、OS の設計やデバイスの用意等はすでに構築済みのホストの資源 (今回はゲストの資源) をそのまま使うので、
レイヤーを増やした仮想環境について資源の配分をもう一度考える必要が無くなります。
(… というよりも、限られた資源の中でやりくりしなければならない。最近の計算機は、いろいろ余裕があるからたぶん大丈夫)
インストール時間やストレージの専有領域が少なくて済むので Web や DB に特化したサーバ等、省資源のサーバを作成するのに向いています。
完全仮想環境の中に LXC を作成すると、グループ化したコンテナ群のバックアップやリストアも楽にできるようになります。
コンテナは、ソフトウェアだけを対象としている仮想化技術なので、ハードウェアを直接に制御することは出来ません。
「Solaris コンテナ」の場合はハードウェアを厳密に定義するアプリケーションが多いため裏技を駆使する必要がありますが (Oracle のメモリ確保とかやめてほしい)、
「Linux コンテナ」の場合はハードウェアとの結合が薄いアプリケーションが多いので、導入のハードルが比較的低くなっているのが特徴です。
0. 前提
KVM でゲスト OS「guest1」が構築済みであること。
種別 ホスト名 内容 IP アドレス 備考 コンテナ cont1 Rocky Linux release 9.4 
(Blue Onyx)10.0.3.11 親 OS (guest1) は RedHat 系だが、デスクトップなしなら Ubuntu も OK。 
ネットワークはデフォルトのブリッジを使うcont2 10.0.3.12 コンテナ環境 - LXC 4.0.12 Release 1.el9 10.0.3.1/24 
(リゾルバも 10.0.3.1)本稿記述時の最新版 
IP アドレスはデフォルトのものを使うゲスト OS guest1 AlmaLinux release 9.4 
(Seafoam Ocelot)192.168.11.10 KVM (QEMU) による完全仮想環境 
今回はホストと同じ OS にしたが、同じである必要はないホスト OS host1 AlmaLinux release 9.4 
(Seafoam Ocelot)192.168.11.2 本稿記述時の最新版 
ホスト OS に関して、本稿では触れていない。(ゲスト OS の構築が前提なので)
1. LXC のインストール
guest1$ su
guest1# dnf install -y epel-release
guest1# dnf install -y lxc lxc-templates
guest1# LANG=C dnf info lxc | egrep 'Name|Version|Release'
guest1# exit
guest1$
2. ネットワークの設定
2-1. dnsmasq をインストール
guest1$ su
guest1# dnf install -y dnsmasq
dnsmasq は DHCP サーバと DNS フォワーダの機能を持つ。
2-2. DNS ポートを許可
コンテナからの DNS リクエストを受けられるようにしておく。2-3. DHCP の通信許可
guest1# firewall-cmd --permanent --add-port="53/udp" --add-port="53/tcp"
guest1# firewall-cmd --reload
guest1# firewall-cmd --list-ports
guest1# firewall-cmd --permanent --add-service=dhcp2-4. lxc 設定ファイルの確認
guest1# firewall-cmd --reload
guest1# firewall-cmd --list-service
guest1# cat /etc/sysconfig/lxc | grep -v ^# | grep -v ^$2-5. lxc-net の編集
確認だけ。特に変更作業はない。
guest1# vim /etc/sysconfig/lxc-net2-6. コンテナ用に DHCP のアドレスを定義
/config) for any containers # already created using the default config to reflect the new bridge # name. # If you have the dnsmasq daemon installed, you'll also have to update # /etc/dnsmasq.d/lxc and restart the system wide dnsmasq daemon. LXC_BRIDGE="lxcbr0" # <-- コメントを外して有効にする LXC_BRIDGE_MAC="00:16:3e:00:00:00" # … LXC_ADDR="10.0.3.1" # … LXC_NETMASK="255.255.255.0" # … LXC_NETWORK="10.0.3.0/24" # … LXC_DHCP_RANGE="10.0.3.2,10.0.3.254" # … LXC_DHCP_MAX="253" # … # Uncomment the next line if you'd like to use a conf-file for the lxcbr0 # dnsmasq. For instance, you can use 'dhcp-host=mail1,10.0.3.100' to have # container 'mail1' always get ip address 10.0.3.100. LXC_DHCP_CONFILE=/etc/lxc/dnsmasq.conf # <-- コメントを外して有効にする # Uncomment the next line if you want lxcbr0's dnsmasq to resolve the .lxc # domain. You can then add "server=/lxc/10.0.3.1' (or your actual $LXC_ADDR) # to /etc/dnsmasq.conf, after which 'container1.lxc' will resolve on your # host. LXC_DOMAIN="lxc" # <-- コメントを外して有効にする
guest1# touch /etc/lxc/dnsmasq.conf2-7. lxc-net を起動
guest1# vim /etc/lxc/dnsmasq.conf
各コンテナに割り当てる IP アドレス。コンテナ起動時に配布される。guest1# cat /var/lib/misc/dnsmasq.lxcbr0.leases | wc -l
• コンテナ起動中に書き換えた場合は lxc-net を再起動させる。(systemctl restart lxc-net)
• 同一の名前でコンテナを再作成した場合は MAC アドレスが異なるため、IP アドレスが指定とは違うものになる。
その場合は、下記「~.leases」から該当の IP アドレス行を削除してから lxc-net を再起動する。
コンテナ稼働前なので、まだ IP アドレスは振り出していない。
0 
guest1# systemctl enable dnsmasq guest1# systemctl enable lxc-net2-8. lxc の自動起動を設定
guest1# systemctl start dnsmasq guest1# systemctl start lxc-net
guest1# systemctl enable lxc
lxc-autostart を guest1 の起動時に実行する。
引数を指定しない lxc-autostart コマンドは lxc.start.auto = 1 が指定されているコンテナをすべて起動する。
引数には --list --reboot --shutdown 等がある。
guest1# exit
guest1$
3. コンテナの作成
この時点ではまだ必要なストレージ量が確定しないため、--fssize*1 を使用しないほうが良い。
*1ストレージの最大容量の指定。-B で loop や lvm を指定すると使用可能。
guest1$ su
guest1# lxc-create cont1 -t download -- --dist rockylinux
作成可能なコンテナのリストはここにある。guest1# ls -l /var/lib/lxc/cont1/
*2この選択だとキャッシュは以下のディレクトリになる。
/var/cache/lxc/download/rockylinux/9/amd64/default/
guest1# ls -l /var/lib/lxc/cont1/config # <-- コンテナの定義ファイル
guest1# ls -l /var/lib/lxc/cont1/rootfs/ # <-- コンテナのファイルシステム (ここをルートとして動作する)
作成したコンテナの設定を確認する。(ストレージ、ネットワーク等)
guest1# cat /var/lib/lxc/cont1/config | grep -v ^# | grep -v ^$
コンテナの自動起動を設定する (必要なら)今回は -B (backingstore) を指定していないのでストレージがデフォルトの dir: になっている。
guest1# echo lxc.start.auto = 1 >> /var/lib/lxc/cont1/config
guest1# lxc-ls
guest1# lxc-info cont1
cont1 
コンテナを破棄する場合はこちら。
guest1# lxc-destroy cont1
guest1# exit
guest1$
4. コンテナの起動
guest1$ su
guest1# lxc-start cont1
guest1# lxc-info cont1
guest1# lxc-ls cont1 -f
コンテナを停止する場合はこちら。( lxc-attach cont1 -- shutdown -h now を実行しても良い)
guest1# lxc-stop cont1
guest1# exit
guest1$
5. 初期設定
最低限の設定をする。
guest1$ su
- タイムゾーンを設定する
- ネットワークの確認・設定ができるようにする
- 通常のログインができるようにする
- ファイアウォールを設定する
- 管理者用アカウントを作成する
guest1# lxc-attach cont1
• タイムゾーンを Asia/Tokyo にする。
cont1# timedatectl set-timezone Asia/Tokyo
cont1# ls -l /etc/localtime ; date
• dnf を使うため、DNS 解決の確認をする。
cont1# cat /etc/resolv.conf
• ネットワークの確認に必要なアプリケーションをインストールする。
cont1# dnf install -y bind-utils telnet
cont1# dnf install -y which vim less
• 通常ログイン用に sshd をインストールする。
cont1# dnf install -y openssh-server
cont1# cat /etc/ssh/sshd_config | egrep -i 'Port|ListenAddress'
今回はデフォルトのまま (変更しない)。lxc-net を使わない場合は変更が必要。cont1# systemctl enable sshd
cont1# systemctl start sshd
• ファイアウォールをインストールする。
cont1# dnf install -y firewalld
cont1# systemctl enable firewalld
cont1# systemctl start firewalld
• ファイアウォールの初期状態を確認する。
cont1# firewall-cmd --get-default-zone
cont1# firewall-cmd --list-all | grep services:
• 管理者用アカウントを作成する。(sudo を使えるユーザ)sshd はデフォルトで許可されている。
cont1# useradd admin
cont1# echo 'password' | passwd --stdin admin # password をこのまま使わないように。
cont1# usermod -G wheel admin
cont1# exit
• コンテナの容量を確認する
guest1# du -sh /var/lib/lxc/cont1
guest1# exitここまでのインストールで約 506MB。「lxc-create …」の選択によっても異なる。
guest1$
6. おまけ (1)
既存のコンテナを複製して、名前の異なる同じコンテナを作成することができる。(MAC アドレスも異なる)
このとき、-B (backingstore) として、異なるバッキングストア (ファイルシステムの格納形式) を指定可能。
backingstore に loop か lvm を指定すると、コンテナのストレージサイズがデフォルトで 1GB に固定される。
(ストレージサイズは -L (--fssize) オプションで指定可能。指定サイズに納まらない場合はコンテナが作成されない)
以下は cont1 を cont2 としてコピーする例。
cont2 は loop (ループバックイメージ) の形式で使用し、ストレージの最大容量を 1.5GB とする。
guest1$ su
guest1# lxc-stop cont1
guest1# lxc-copy -n cont1 -N cont2 -B loop -L 1500MB
guest1# lxc-info cont2
guest1# ls -lh /var/lib/lxc/cont2/
guest1# lxc-execute cont2 -- df -hbackingstore に指定した loop: になっている。
guest1# mount -o loop /var/lib/lxc/cont2/rootdev /mnt ; ls -l /mnt ; umount /mnt
ループバックイメージなので、ループバックマウントすれば中のファイルを操作可能。guest1# exit
guest1$
7. おまけ (2)
実験環境にするなら以下を実施する。
(外部への 80, 443 を使用不能にするので、インストールがすべて完了してから)
firewall を操作するので、なるべく lxc-attach から。
guest1$ su
guest1# lxc-attach cont1
インバウンドを許可する。
· 入ってくる通信はすべて許可する。
cont1# firewall-cmd --permanent --add-rich-rule='rule family=ipv4 destination address="0.0.0.0/0" accept'
アウトバウンドを制限する。
· 親 OS への通信は DNS を許可して他はすべて禁止する。
· 自分のネットワークへの通信はすべて許可する。
· 外部への通信はシステムポートを禁止する。(← うっかり対策*3)
cont1# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 1 -d 10.0.3.1 -p udp --dport 53 -j ACCEPT
cont1# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 2 -d 10.0.3.1 -p tcp -m state --state NEW --dport 53 -j ACCEPT
cont1# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 3 -d 10.0.3.1 -p tcp -m state --state NEW --dport 0:1023 -j DROP
cont1# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 4 -d 10.0.3.0/24 -j ACCEPT
cont1# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 5 -p tcp -m state --state NEW --dport 0:1023 -j DROP
cont1# firewall-cmd --permanent --direct --add-rule ipv4 filter OUTPUT 6 -p udp --dport 0:1023 -j DROP
cont1# firewall-cmd --reload
インバウンドルールを確認する。
cont1# firewall-cmd --list-rich
アウトバウンドルールを確認する。
cont1# firewall-cmd --direct --get-all-rules
cont1# exit
guest1# exit
guest1$
*3 何かを監視したいならこれ↓
guest1# tcpdump -i lxcbr0 -q -l "tcp[tcpflags] & (tcp-syn) != 0 and tcp[tcpflags] & (tcp-ack) = 0" | logger
• メンテナンスをするには以下の手順が必要。
ファイアウォールを一時的に停止するため、一般ユーザを退出させてから作業する。
ssh ログインでもできるけど、なるべく lxc-attach を使う。
以下は screen コマンドを追加インストールする例。(EPEL からインストール)
cont1# dnf install -y epel-release # インストール作業 1 cont1# dnf install -y screen # インストール作業 2
cont1# systemctl start firewalld # ファイアウォールの開始 cont1# systemctl start sshd # 新規ログインの受付開始
8. おまけ (3)
親 OS のポートへ接続してきたら、コンテナのポートへ転送する。
ここでは、以下を想定している。
(覚えやすいようにポート番号の頭 2 桁をコンテナの第 4 オクテットと同じにしてあるが、これに従う必要はない)
親 OS (guest1) で実施する。(コンテナとの行き来を繰り返すと間違えやすい)
- ssh -p 1122 admin@192.168.11.10 → cont1:22
- curl http://192.168.11.10:1180/ → cont1:80
guest1$ su
guest1# firewall-cmd --permanent --add-forward-port=port=1122:proto=tcp:toport=22:toaddr=10.0.3.11
guest1# firewall-cmd --permanent --add-forward-port=port=1180:proto=tcp:toport=80:toaddr=10.0.3.11
guest1# firewall-cmd --reload
guest1# firewall-cmd --list-forward-ports
guest1# exit
guest1$
9. おまけ (4)
Web サーバ (Apache) をインストールしてみる
guest1$ ssh admin@10.0.3.11
cont1$ sudo dnf install -y httpd mod_ssl
cont1$ sudo systemctl enable httpd
cont1$ sudo systemctl start httpd
cont1$ sudo firewall-cmd --permanent --add-service=http --add-service=https
cont1$ sudo firewall-cmd --reload
cont1$ exit
guest1$ curl -s http://10.0.3.11/ | grep '<title>'
guest1$
<title>HTTP Server Test Page powered by: Rocky Linux</title>
10. おまけ (5)
• GUI (GNOME) をインストールする
guest1$ ssh admin@10.0.3.11
cont1$ sudo dnf groupinstall -y GNOME
cont1$ sudo dnf install -y tigervnc-server
• GUI の接続ポートを許可する。
cont1$ sudo firewall-cmd --permanent --add-port=5901-5910/tcp
cont1$ sudo firewall-cmd --reload
cont1$ sudo firewall-cmd --list-ports
• 再起動して GUI モードを有効にする。
5901-5910/tcp 
cont1$ sudo shutdown -r now
guest1$ ssh admin@10.0.3.11
• GUI の接続ポートをひとつ起動する。
cont1$ vncserver
cont1$ vncserver -list
cont1$ exit「X DISPLAY #」が :1 なので、接続ポートは 5901。(5900 + 1)
• 親 OS へ接続してきた GUI のトラフィックをコンテナへ送る。
guest1$ su
guest1# firewall-cmd --permanent --add-forward-port=port=11901:proto=tcp:toport=5901:toaddr=10.0.3.11
guest1# firewall-cmd --reload
guest1# firewall-cmd --list-forward-ports
guest1# exit
guest1$
TightVNC 等の VNC クライアントで 192.168.11.10:11901 をアクセスする。