概要
CentOS7のホストの上にsystemd-nspawnでシステムコンテナを立てる方法の備忘録です
元となるマスターコンテナ(テンプレート)と
それのクローンコンテナを作ります
(以下のコマンドはroot/sudoで実行しています)
環境
CentOS-7-x86_64-Minimal-1611.isoから
minimal installのちyum updateを実行(2017/06/25)
前提
以下のものを使用するのでインストールします
- systemd-networkd
- systemd-resolved
- screen
- perl
- policycoreutils-python
yum install \
systemd-networkd \
systemd-resolved \
policycoreutils-python \
screen \
perl \
-y
環境や設定は以下のものを使うので適宜読み替えてください
#インターネット接続されたデバイス名、DHCP有効
EXTERNAL_DEV_NAME='enp0s3'
#↑のデバイスが属するzone
EXTERNAL_ZONE_NAME='public'
#仮想ブリッジデバイス名
INTERNAL_DEV_NAME='br0'
#仮想ブリッジデバイス、コンテナが属するzone
INTERNAL_ZONE_NAME='internal'
#仮想ブリッジデバイスのIPアドレス設定
INTERNAL_GATEWAY_ADDR='10.0.0.1'
INTERNAL_NETWORK_PREFIX='24'
#コンテナが使用するDNSサーバーのアドレス
DNS_ADDR='8.8.8.8'
マスターコンテナの設定
#初回ログイン時のrootパスワード
CONTAINERS_MASTER_FIRST_ROOT_PASSWD='password'
#コンテナのデータを置くディレクトリ
CONTAINERS_MASTER_DATA_DIR='/root/data/containers/master/'
#コンテナのIPアドレス
CONTAINERS_MASTER_NETWORK_ADDR='10.0.0.2'
クローンコンテナの設定
#クローン元のコンテナのデータがあるディレクトリ
CONTAINERS_MASTER_DATA_DIR='/root/data/containers/master/'
#クローンコンテナのデータを置くディレクトリの親
CONTAINERS_CLONE_DATA_DIR='./data/containers/clones/'
#コンテナ名
L_CONTAINER_NAME='serv1'
#初回ログイン時のrootパスワード
CONTAINERS_CLONE_FIRST_ROOT_PASSWD='pass1'
#コンテナのIPアドレス
CONTAINERS_CLONE_NETWORK_ADDR='10.0.0.11'
ホストのネットワーク設定
systemd-networkdを利用するように変更
systemctl stop NetworkManager
systemctl disable NetworkManager
systemctl start systemd-networkd systemd-resolved
systemctl enable systemd-networkd systemd-resolved
#systemd-resolvedは直接/etc/resolv.confを変更しないので
mv /etc/resolv.conf /etc/resolv.conf.orig
ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
#systemd-networkd用の設定ファイル
mkdir -p /etc/systemd/network/
cat << EOS > /etc/systemd/network/${EXTERNAL_DEV_NAME}.network
[Match]
Name=${EXTERNAL_DEV_NAME}
[Network]
DHCP=yes
EOS
chmod 644 /etc/systemd/network/${EXTERNAL_DEV_NAME}.network
コンテナ用のプライベートネットワークを作成
コンテナとホストを接続するブリッジデバイスを作成
cat << EOS > /etc/systemd/network/${INTERNAL_DEV_NAME}.netdev
[Match]
[NetDev]
Name=${INTERNAL_DEV_NAME}
Kind=bridge
EOS
chmod 644 /etc/systemd/network/${INTERNAL_DEV_NAME}.netdev
cat << EOS > /etc/systemd/network/${INTERNAL_DEV_NAME}.network
[Match]
Name=${INTERNAL_DEV_NAME}
[Network]
Address=${INTERNAL_GATEWAY_ADDR}/${INTERNAL_NETWORK_PREFIX}
IPForward=yes
EOS
chmod 644 /etc/systemd/network/${INTERNAL_DEV_NAME}.network
コンテナからのインターネットアクセスをホスト経由で行えるように設定
#[Network]セクションに追加
IPForward=kernel
IPMasquerade=yes
以上の設定を読み込み、IPマスカレードできるよう設定
systemctl restart systemd-networkd
firewall-cmd --permanent --zone=${INTERNAL_ZONE_NAME} --change-interface=${INTERNAL_DEV_NAME}
firewall-cmd --permanent --zone=${EXTERNAL_ZONE_NAME} --change-interface=${EXTERNAL_DEV_NAME}
firewall-cmd --permanent --zone=${EXTERNAL_ZONE_NAME} --add-masquerade
firewall-cmd --reload
マスターコンテナ作成
コンテナのデータを置くディレクトリを作成し、絶対パスを取得
(コンテナ内のルートディレクトリ)
mkdir -p ${CONTAINERS_MASTER_DATA_DIR}
L_CONTAINER_ROOT_DIR=$(readlink -f ${CONTAINERS_MASTER_DATA_DIR})
コンテナ内に必要なものをインストール
コンテナ内にカーネルは必要ないので--exclude=kernel*
yum --installroot=${L_CONTAINER_ROOT_DIR} \
--releasever=7 --disablerepo='*' --enablerepo=base \
group install 'core' 'base' \
--exclude=kernel* \
-y
yum --installroot=${L_CONTAINER_ROOT_DIR} \
--releasever=7 --disablerepo='*' --enablerepo=base \
install 'systemd-networkd' \
--exclude=kernel* \
-y
echo exclude=kernel* >> ${L_CONTAINER_ROOT_DIR}/etc/yum.conf
初期パスワードを設定
sed -i \
-e "s@^root:[^:]*@root:$(perl -E "say crypt('"${CONTAINERS_MASTER_FIRST_ROOT_PASSWD}"', '\$6\$'. crypt(rand, rand 100))")@" \
${L_CONTAINER_ROOT_DIR}/etc/shadow
コンテナのネットワーク設定
systemctl disable NetworkManager --root=${L_CONTAINER_ROOT_DIR}
systemctl enable systemd-networkd --root=${L_CONTAINER_ROOT_DIR}
cat << EOS > ${L_CONTAINER_ROOT_DIR}/etc/resolv.conf
nameserver ${DNS_ADDR}
EOS
mkdir -p ${L_CONTAINER_ROOT_DIR}/etc/systemd/network/
cat << EOS > ${L_CONTAINER_ROOT_DIR}/etc/systemd/network/host0.network
[Match]
Name=host0
[Network]
Address=${CONTAINERS_MASTER_NETWORK_ADDR}/${INTERNAL_NETWORK_PREFIX}
Gateway=${INTERNAL_GATEWAY_ADDR}
EOS
touch ${L_CONTAINER_ROOT_DIR}/etc/sysconfig/network
コンテナのセキュリティコンテキストを初期化
restorecon -Rv ${L_CONTAINER_ROOT_DIR}
コンテナを起動します
screen -S 'master' systemd-nspawn -b -D ${L_CONTAINER_ROOT_DIR} --network-bridge=${INTERNAL_DEV_NAME}
コンテナのログインプロンプトが表示されるのでroot/初期パスワードでログインし、自由に環境を構築して下さい
備考
コンソールから抜ける場合
Ctrl+a
d
コンソールに再度入る場合
screen -r 'master'
クローンコンテナの作成
(マスターコンテナはシャットダウンして下さい)
コンテナのデータを置くディレクトリを作成し、絶対パスを取得
L_CONTAINER_ROOT_DIR=${CONTAINERS_CLONE_DATA_DIR}/${L_CONTAINER_NAME}
mkdir -p ${L_CONTAINER_ROOT_DIR}
L_CONTAINER_ROOT_DIR=$(readlink -f ${L_CONTAINER_ROOT_DIR})
マスターコンテナのデータディレクトリにオーバーレイしたディレクトリを作成
(コンテナ内のルートディレクトリ)
mkdir -p ${L_CONTAINER_ROOT_DIR}/work
mkdir -p ${L_CONTAINER_ROOT_DIR}/upper
mkdir -p ${L_CONTAINER_ROOT_DIR}/merge
mount -t overlay overlay -o lowerdir=${CONTAINERS_MASTER_DATA_DIR},upperdir=${L_CONTAINER_ROOT_DIR}/upper,workdir=${L_CONTAINER_ROOT_DIR}/work ${L_CONTAINER_ROOT_DIR}/merge
初期パスワードを設定
sed -i \
-e "s@^root:[^:]*@root:$(perl -E "say crypt('"${CONTAINERS_CLONE_FIRST_ROOT_PASSWD}"', '\$6\$'. crypt(rand, rand 100))")@" \
${L_CONTAINER_ROOT_DIR}/merge/etc/shadow
コンテナのネットワーク設定
mkdir -p ${L_CONTAINER_ROOT_DIR}/merge/etc/systemd/network
cat << EOS > ${L_CONTAINER_ROOT_DIR}/merge/etc/systemd/network/host0.network
[Match]
Name=host0
[Network]
Address=${CONTAINERS_CLONE_NETWORK_ADDR}/${INTERNAL_NETWORK_PREFIX}
Gateway=${INTERNAL_GATEWAY_ADDR}
EOS
コンテナのセキュリティコンテキストを初期化し、一旦アンマウント
restorecon -Rv ${L_CONTAINER_ROOT_DIR}/merge
umount ${L_CONTAINER_ROOT_DIR}/merge
コンテナを起動します
#(systemd-nspawn単体でもoverlayできるらしいのですが試していません)
mount -t overlay overlay -o lowerdir=${CONTAINERS_MASTER_DATA_DIR},upperdir=${L_CONTAINER_ROOT_DIR}/upper,workdir=${L_CONTAINER_ROOT_DIR}/work ${L_CONTAINER_ROOT_DIR}/merge
screen -S ${L_CONTAINER_NAME} systemd-nspawn -b -D ${L_CONTAINER_ROOT_DIR}/merge --network-bridge=${INTERNAL_DEV_NAME} --machine=${L_CONTAINER_NAME}
コンテナのログインプロンプトが表示されるのでroot/初期パスワードでログインし、自由にガチャガチャして下さい
備考
コンソールから抜ける場合
Ctrl+a
d
コンソールに再度入る場合
screen -r ${L_CONTAINER_NAME}
外部からコンテナへアクセス
ポートフォワードの設定
#受け付けるホスト側のポート
L_HOST_PORT=11022
#プロトコル
L_PROTO=tcp
#フォワード先のコンテナのアドレス
L_CONTAINER_ADDR=10.0.0.11
#フォワード先のコンテナのポート
L_CONTAINER_PORT=22
ホストからのポートフォワードを設定
firewall-cmd --zone=${EXTERNAL_ZONE_NAME} --add-forward-port="port=${L_HOST_PORT}:proto=${L_PROTO}:toaddr=${L_CONTAINER_ADDR}:toport=${L_CONTAINER_PORT}"
firewall-cmd --zone=${EXTERNAL_ZONE_NAME} --add-port=${L_HOST_PORT}/${L_PROTO}