CLOVER🍀

That was when it all began.

systemdのユニット定義ファイルを確認するにはsystemctl catコマンド、編集するにはsystemctl editコマンドを使う

これは、なにをしたくて書いたもの?

systemdのユニット定義ファイルを編集する際に、ふつうにエディタコマンドで直接開いて設定したりしていたのですが、そのあたりの
考え方が間違ってそうなのでメモしておくことにしました。

systemctl editコマンドで編集するのが良さそうです。

使用するコマンド

systemctlのmanページはこちら。

Ubuntu Manpage: systemctl - Control the systemd system and service manager

今回扱う用途とコマンドの対応表を、以下に記載します。

用途 対応するコマンド 備考
ユニット定義ファイルの内容を確認したい systemctl cat [パターン(ユニット名)] フラグメントやドロップインも含めて表示する
ユニット定義ファイルを編集したい(ドロップイン) systemctrl edit [ユニット名] ドロップインファイルを作成、編集する
ユニット定義ファイルを編集したい(本体) systemctrl edit --full [ユニット名]
ユニット定義ファイルを新規作成したい systemctrl edit --force --full [ユニット名]
ユニット定義ファイルをベンダー定義のバージョンに戻したい systemctl revert [ユニット名] ドロップインファイルは削除される

気にした方がよさそうなポイントもあるのですが、それはこれから見ていきます。

環境

今回の環境は、こちら。

$ lsb_release -a
No LSB modules are available.
Distributor ID: Ubuntu
Description:    Ubuntu 22.04.4 LTS
Release:        22.04
Codename:       jammy


$ uname -smvrpio
Linux 5.15.0-101-generic #111-Ubuntu SMP Tue Mar 5 20:16:58 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux

Ubuntu Linux 22.04 LTSです。

また、お題の都合上Dockerを使用します。

$ docker version
Client: Docker Engine - Community
 Version:           26.0.0
 API version:       1.45
 Go version:        go1.21.8
 Git commit:        2ae903e
 Built:             Wed Mar 20 15:17:48 2024
 OS/Arch:           linux/amd64
 Context:           default

Server: Docker Engine - Community
 Engine:
  Version:          26.0.0
  API version:      1.45 (minimum version 1.24)
  Go version:       go1.21.8
  Git commit:       8b79278
  Built:            Wed Mar 20 15:17:48 2024
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.28
  GitCommit:        ae07eda36dd25f8a1b98dfbf587313b99c0190bb
 runc:
  Version:          1.1.12
  GitCommit:        v1.1.12-0-g51d5e94
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

お題

Ubuntu Linux 22.04 LTSにインストールしたDockerに対して、環境変数でプロキシを設定する、というお題でやってみたいと思います。

なお、プロキシサーバーについては省略しますが、http://localhost:8080でアクセスできるものとします。

systemdのユニット定義ファイルを確認する(本体のファイルのみ)

systemdのユニット定義ファイルは、systemctl cat [ユニット名]コマンドで確認できます。管理者権限は不要です。

$ systemctl cat docker
# /lib/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target docker.socket firewalld.service containerd.service time-set.target
Wants=network-online.target containerd.service
Requires=docker.socket

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not support it.
# Only systemd 226 and above support this option.
TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes

# kill only the docker process, not all processes in the cgroup
KillMode=process
OOMScoreAdjust=-500

[Install]
WantedBy=multi-user.target

コメントで、ユニット定義ファイルのパスが書かれています。

# /lib/systemd/system/docker.service

diffをとってみると、このコメント以外は内容は同じです。

$ diff <(systemctl cat docker) /lib/systemd/system/docker.service
1d0
< # /lib/systemd/system/docker.service

systemdのユニット定義ファイルを編集する(ドロップインファイル)

次に、systemdのユニット定義ファイルを編集してみます。

この時、ソフトウェアの提供元のユニット定義ファイルを直接編集するのではなく、ドロップインファイルでオーバーライドするのが良いと
されています。

ドロップインファイルというのは、ユニット定義がfoo.serviceだった場合にfoo.service.dディレクトリ配下に配置したファイルです。

これは、systemctl edit [ユニット名]で行います。

$ sudo systemctl edit docker

ちなみに、この時Ubuntu Linux 22.04 LTSだとnanoが起動して困ったので、気になる人は以下のコマンドで好みのエディターを指定するように
してください。

$ sudo update-alternatives --config editor

systemctl editを実行すると、以下のようにコメントアウトされた元の内容が表示されるので、こちらを参考にドロップインファイルを
作成しましょう。

### Editing /etc/systemd/system/docker.service.d/override.conf
### Anything between here and the comment below will become the new contents of the file



### Lines below this comment will be discarded

### /lib/systemd/system/docker.service
# [Unit]
# Description=Docker Application Container Engine
# Documentation=https://docs.docker.com
# After=network-online.target docker.socket firewalld.service containerd.service time-set.target
# Wants=network-online.target containerd.service
# Requires=docker.socket
#
# [Service]
〜省略〜

今回は、こんな感じのファイルにしました。

[Service]
Environment="http_proxy=http://localhost:8080"
Environment="https_proxy=http://localhost:8080"

ファイル名は、デフォルトでoverride.confになります。今回は/etc/systemd/system/docker.service.d/override.confになりました。

あとは再起動すれば変更が反映されます。

$ sudo systemctl restart docker

ここで、systemctl editの後にsystemctl daemon-reloadは要りません。

この意味でも、systemctl editを使うべきな気がしますね…。

systemdのユニット定義ファイルを確認する(本体のファイル+ドロップインファイル)

この状態で、systemctl catで確認してみます。

$ systemctl cat docker

ドロップインファイル含めて表示されています。

# /lib/systemd/system/docker.service
[Unit]
Description=Docker Application Container Engine
Documentation=https://docs.docker.com
After=network-online.target docker.socket firewalld.service containerd.service time-set.target
Wants=network-online.target containerd.service
Requires=docker.socket

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always

# Note that StartLimit* options were moved from "Service" to "Unit" in systemd 229.
# Both the old, and new location are accepted by systemd 229 and up, so using the old location
# to make them work for either version of systemd.
StartLimitBurst=3

# Note that StartLimitInterval was renamed to StartLimitIntervalSec in systemd 230.
# Both the old, and new name are accepted by systemd 230 and up, so using the old name to make
# this option work for either version of systemd.
StartLimitInterval=60s

# Having non-zero Limit*s causes performance problems due to accounting overhead
# in the kernel. We recommend using cgroups to do container-local accounting.
LimitNPROC=infinity
LimitCORE=infinity

# Comment TasksMax if your systemd version does not support it.
# Only systemd 226 and above support this option.
TasksMax=infinity

# set delegate yes so that systemd does not reset the cgroups of docker containers
Delegate=yes

# kill only the docker process, not all processes in the cgroup
KillMode=process
OOMScoreAdjust=-500

[Install]
WantedBy=multi-user.target

# /etc/systemd/system/docker.service.d/override.conf
[Service]
Environment="http_proxy=http://localhost:8080"
Environment="https_proxy=http://localhost:8080"

本体のユニット定義ファイルを編集する

次は、本体のユニット定義ファイルを編集してみます。

まずsystemctl editでドロップインファイルを以下のように変更。

/etc/systemd/system/docker.service.d/override.conf

[Service]
Environment="http_proxy=http://localhost:8080"
#Environment="https_proxy=http://localhost:8080"

コメントアウトした方は、本体のユニット定義ファイルに追加することにしましょう。

この場合に使うコマンドは、systemctl edit --fullです。

$ sudo systemctl edit --full docker

--fullオプションを付けると、本体のユニット定義ファイルを編集できます。今回は、以下のように変更。

〜省略〜

[Service]
Type=notify
# the default is not to use systemd for cgroups because the delegate issues still
# exists and systemd currently does not support the cgroup feature set required
# for containers run by docker
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock
ExecReload=/bin/kill -s HUP $MAINPID
TimeoutStartSec=0
RestartSec=2
Restart=always
Environment="https_proxy=http://localhost:8080"

〜省略〜

あとは再起動すれば変更が反映されます。

$ sudo systemctl restart docker

変更内容を元に戻す

ここまでで、インストール時点のユニット定義ファイルとは、このような差になりました。

$ diff <(systemctl cat docker) /lib/systemd/system/docker.service
1d0
< # /etc/systemd/system/docker.service
18a18
> Environment="http_proxy=http://localhost:8080"
49,53d48
<
< # /etc/systemd/system/docker.service.d/override.conf
< [Service]
< Environment="http_proxy=http://localhost:8080"
< #Environment="https_proxy=http://localhost:8080"

これを元に戻すには、systemctl revertを実行します。

$ sudo systemctl revert docker

実行ログ。

Removed /etc/systemd/system/docker.service.d/override.conf.
Removed /etc/systemd/system/docker.service.d.
Removed /etc/systemd/system/docker.service.

差分がなくなりました。

$ diff <(systemctl cat docker) /lib/systemd/system/docker.service
1d0
< # /lib/systemd/system/docker.service

新しくユニット定義ファイルを作成する

systemctlコマンドで新しくユニット定義ファイルを作成する場合は、systemctl edit --force --fullコマンドを使います。

$ sudo systemctl edit --force --full my-service

この場合、/etc/systemd/system配下にファイルを作成しようとします。

自分で用意したユニット定義ファイルをrevertすると?

ところで、自分で用意したユニット定義ファイルをsystemctl revertするとどうなるのでしょう?

作成してみます。

$ sudo systemctl edit --force --full my-httpd

こんな内容にしてみました。

$ systemctl cat my-httpd
# /etc/systemd/system/my-httpd.service
[Unit]
Description=My Simple Http Server
After=networ-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=python3 -m http.server --directory /tmp

[Install]
WantedBy=multi-user.target

起動することを確認。

$ sudo systemctl start my-httpd

systemctl revertしてみます。

$ sudo systemctl revert my-httpd

特になにも表示されません。systemctl catでも、そのまま中身が残っています。

$ systemctl cat my-httpd
# /etc/systemd/system/my-httpd.service
[Unit]
Description=My Simple Http Server
After=networ-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStart=python3 -m http.server --directory /tmp

[Install]
WantedBy=multi-user.target

それもそのはずで、manページを見ると「削除されない」と書かれています。

Note that if a unit file has no vendor-supplied version (i.e. is only defined below /etc/systemd/system or /run/systemd/system, but not in a unit file stored below /usr/), then it is not removed. Also, if a unit is masked, it is unmasked.

Ubuntu Manpage: systemctl - Control the systemd system and service manager

ちなみに、自分で用意したユニット定義ファイルにもsystemctl editでドロップインファイルを作成することができます。
このパターンはあまり使わない気もしますが。

おわりに

systemdのユニット定義ファイルを確認、編集、そしてインストールした時点のものに戻す方法を調べてみました。

今までふつうにエディターで編集していたので、こうやってコマンドで操作できることを知りませんでした…。
コマンドのmanページとか見ないとダメですね…。