最初に
アノテーション メンテナンスチームの shinonome です。
今回は systemd を Docker Container 内でも使いたい場合の実現方法の記事になります。
この記事は、cgroup v2 下で稼働する前提で、Debian 11 / Ubuntu 22.04 Cloud Image で検証済みの内容になります。
※Docker の Rootless と Rootful mode に若干違いはありますが、どちらも稼働出来ます。
なお、通常の macOS の Docker 環境(Docker Desktop、Rancher Desktop など)では今回の内容はやりづらいと思います。
実践する際は下記の記事を参考に、自分が管理できる Linux (w/ Docker engine) 環境を先に立ててください。
準備作業 (Linux part)
まずは Docker systemd service unit ファイルの Service
配下に Slice=docker.slice
を追加後、docker service を再起動してください。
※こちらはこの後のコンテナー起動パラメーターの cgroup-parent
に使われます。
※変化が気になる方は事前に systemctl status
で変更前の状況を確認しましょう。
...
[Service]
...
Slice=docker.slice
...
# rootless
vim ~/.config/systemd/user/docker.service # add slice
systemctl --user daemon-reload
systemctl --user restart docker
systemctl --user status docker.slice
systemctl --user status # optional
# rootful
vim /lib/systemd/system/docker.service # add slice
systemctl daemon-reload
systemctl restart docker
systemctl status docker.slice
systemctl status # optional
準備作業 (Dockerfile part)
次は Dockerfile を準備しましょう。
大まかな内容は:
- systemd 用の停止信号を指定
- systemd をインストールして、不要な一時ファイル等を削除
- Container 内に起動不要な systemd units を全て解除
- 必要な分は残す(もしくは再起用)、例えば journald
- Container 起動コマンドを systemd に指定
ARG TAG="latest"
FROM debian:${TAG}
ENV container docker
ENV LC_ALL C
ENV DEBIAN_FRONTEND noninteractive
# match the stop signal to systemd's.
STOPSIGNAL SIGRTMIN+3
USER root
WORKDIR /root
RUN apt-get update -y \
&& apt-get install --no-install-recommends -y systemd \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/* \
# allow login
&& rm -f /var/run/nologin
# remove all unnecessary service.
RUN rm -f \
/etc/systemd/system/*.wants/* \
/lib/systemd/system/*.wants/*
# optional: enable journald.
# known issus: audit won't work under rootless env due to permission.
RUN cd /lib/systemd/system \
&& ln -s ../systemd-journald.socket ./sockets.target.wants/systemd-journald.socket \
&& ln -s ../systemd-journald-audit.socket ./sockets.target.wants/systemd-journald-audit.socket \
&& ln -s ../systemd-journald-dev-log.socket ./sockets.target.wants/systemd-journald-dev-log.socket \
&& ln -s ../systemd-journald.service ./sysinit.target.wants/systemd-journald.service \
&& ln -s ../systemd-journal-flush.service ./sysinit.target.wants/systemd-journal-flush.service
VOLUME [ "/tmp", "/run", "/run/lock" ]
CMD [ "/lib/systemd/systemd" ]
起動
Dockerfile で Image を構築してから、記載したパラメーターでコンテナーを起動 (例は detach mode) してください。
※log 確認 / bash 呼び出し / 停止用の参考コマンドも記載しました。
※記載したコマンドでは、イメージ名をフォルダー名にしていますが、適宜変更してください。
docker build -t $(basename $PWD)-local .
docker run -d -t \
--privileged \
--cap-add CAP_SYS_ADMIN --cap-add CAP_MKNOD \
--cgroup-parent=docker.slice --cgroupns private \
--tmpfs /tmp --tmpfs /run --tmpfs /run/lock \
--name systemd_container $(basename $PWD)-local:latest
docker logs -f -t systemd_container
docker exec -it systemd_container bash -c "systemctl status"
docker container stop systemd_container
※rootless の場合、権限が足りないため、systemd-journald-audit.socket
の起動は失敗になり、systemd の状態は degraded
になりますが、コンテナーを使うには特に支障はないと思います。
※動作確認時点のコンテナー内の systemd バージョン: systemd 247.3-7+deb11u1
最後に
今回は、Docker Container 内で systemd を使う方法を紹介しました。
Container 内でも systemd でサービスを管理したい場合、是非試してみてください。
本ブログ、お役に立てると光栄です。
参考情報
- https://medium.com/swlh/docker-and-systemd-381dfd7e4628
- https://systemd.io/CONTAINER_INTERFACE/
- https://serverfault.com/questions/1053187/systemd-fails-to-run-in-a-docker-container-when-using-cgroupv2-cgroupns-priva
おまけに
cgroup v1 での実現もありますが、方法は若干違います。
v1 環境での方法はネット上にいっぱいありますので、必要でしたら検索してみてください。
例えば:https://developers.redhat.com/blog/2016/09/13/running-systemd-in-a-non-privileged-container
アノテーション株式会社について
アノテーション株式会社は、クラスメソッド社のグループ企業として「オペレーション・エクセレンス」を担える企業を目指してチャレンジを続けています。「らしく働く、らしく生きる」のスローガンを掲げ、様々な背景をもつ多様なメンバーが自由度の高い働き方を通してお客様へサービスを提供し続けてきました。現在当社では一緒に会社を盛り上げていただけるメンバーを募集中です。少しでもご興味あれば、アノテーション株式会社WEBサイトをご覧ください。