春雨日記 about me tags

systemd-nspawn上でGitea Actionsのランナー(Dockerコンテナ)を動作させた記録

はじめに

私のサーバーでは、systemd-nspawnを利用してサービス毎に環境を分離させています。

そのため、Gitea Actionsを利用するためにはコンテナ上でさらにDockerを動作させる必要があるのですが、そのままDockerを入れただけでは下記のようなエラーが出て動作しませんでした。

1
mkdir /sys/fs/cgroup/cpuset/docker: permission denied

nspawnの設定を変更し、動作させることができたので共有します。

環境

  • ホストOS
    • Ubuntu 20.04
    • Linux 5.4 (デフォルト)
    • cgroup v1 (デフォルト)
  • コンテナOS
    • Arch Linux

手順

以前Arch Wikiに記載されていた方法では、コンテナのnamespaceを無効にし、nspawnの利点であるrootの分離などを全て無効化した上で/sys/fs/cgroupを直接マウントする事でなんとか動作させていました。

しかし、最近更新された方法を用いると、cgroup v2環境では、nspawnコンテナのセキュリティ機能を大きく損なう事無くDockerを動作させる事ができるようです。

Ubuntu20.04上にて上記の方法を利用するには、以下の手順が必要です。

  • systemd-nspawnのユニットファイルを編集
  • コンテナにfuse-overlayfsを導入

ホスト側設定

cgroup v2に変更

コンテナ上で、mount | grep cgroupを実行し、/sys/fs/cgroupがtmpfsであればcgroup v1が使用されています。

  • cgroup v1の場合

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    
    [root@arch ~]# mount | grep cgroup
    tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,size=4096k,nr_inodes=1024,mode=755,uid=1742209024,gid=1742209024)
    cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,perf_event)
    cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,cpuset)
    cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,pids)
    cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,blkio)
    cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,hugetlb)
    cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,net_cls,net_prio)
    cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,devices)
    cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,freezer)
    cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,memory)
    cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,rdma)
    cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,cpu,cpuacct)
    cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,xattr,name=systemd)
    cgroup2 on /sys/fs/cgroup/unified type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
    
  • cgroup v2の場合

    1
    2
    
    [root@arch ~]# mount | grep cgroup
    cgroup on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate)
    

検索すると、grubの設定を変更してカーネルパラメータにsystemd.unified_cgroup_hierarchy=1を付加する事でv2に変更している記事が多くヒットします。1

しかし、systemd-nspawnを使っている場合、ユニットファイルを変更するだけでコンテナ上だけv2動作させる事が可能です。

むしろ検証の結果、Ubuntu20.04ではカーネルパラメータを用いてホストをv2としても、コンテナはv1のままだったため、ホストの設定を変更する必要は全くないです。

  • 設定方法 2 3

    • ユニットファイルを編集

      1
      
      systemctl edit systemd-nspawn@arch.service
      
    • 下記を記述

      [Service]
      Environment=UNIFIED_CGROUP_HIERARCHY=1
      
    • 反映

      1
      2
      
      systemctl daemon-reload
      systemctl restart systemd-nspawn@arch.service
      

よく見るとカーネルパラメータと同じ値をセットしている事がわかります。

これが再起動要らずでコンテナに限って反映できるのはsystemd-nspawnの魅力の一つだと思います。

逆に、例えば新しいディストリビューションを使いながら、古いアプリケーションの為にコンテナ上はcgroup v1で動作させるといった事も可能だと考えられます。

nspawn設定の変更

ここからは上で貼ったArch Wiki通りです。

  • 設定を開く
    1
    
    nano /etc/systemd/nspawn/arch.nspawn
    
  • 以下を記述
    [Files]
    Bind=/dev/fuse
    
    [Exec]
    SystemCallFilter=add_key keyctl bpf
    
  • Fuseを許可
    1
    
    systemctl set-property systemd-nspawn@arch DeviceAllow='/dev/fuse rwm'
    

コンテナ側設定

overlayfs導入

Wiki通りに

1
pacman -S fuse-overlayfs

あとは、Dockerで確認します。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@arch ~]# docker info
Client:
 Version:    24.0.5
 Context:    default
 Debug Mode: false

Server:
 Containers: 0
  Running: 0
  Paused: 0
  Stopped: 0
 Images: 1
 Server Version: 24.0.5
 Storage Driver: overlay2
  Backing Filesystem: extfs
  Supports d_type: true
  Using metacopy: false
  Native Overlay Diff: false
  userxattr: false
 Logging Driver: json-file
 Cgroup Driver: systemd
 Cgroup Version: 2 # v2と認識

()

act_runner導入

Gitea Actionsの為にはact_runnerをインストールする必要があります。

AURにsystemdユニット付きのパッケージがあるので利用すると良いと思います。

設定は以下の感じで

1
act_runner --config /etc/act_runner/act_runner.yaml

おわりに

昔はsystemd-nspawn上でDockerを動かすのがとんでもなく無駄な気がしていましたが、現在の方法であればより安全に、かつホストを汚すことなく動作できて良いかなと思います。

nspawnのcgroup変更など、ドキュメントに書いてあった?みたいな技を使う事になりましたが、とりあえずActionsできて良かった…

おまけ: act_runnerでカスタム名前解決を利用したい

Giteaと同じコンテナで動いているにも拘わらず、外側のURLを設定する事を強要される関係で、デフォルトだと(ヘアピンNAT非対応環境であれば)checkoutなどが利用できません。

ホストをlocalhostに転送するために/etc/act_runner/act_runner.yamlを以下のように編集します。

1
2
3
4
5
6
7
8
9
container:
  # Which network to use for the job containers. Could be bridge, host, none, or the name of a custom network.
  network: bridge
  # Whether to use privileged mode or not when launching task containers (privileged mode is required for Docker-in->  privileged: false
  # And other options to be used when the container is started (eg, --add-host=my.gitea.url:host-gateway).
  options: --add-host=git.haru3.me:127.0.0.1                         
  # The parent directory of a job's working directory.
  # If it's empty, /workspace will be used.
  workdir_parent:

試してないですがnetworkをhostにするのでも良いらしい? 流石に危険な気がするのでbridgeにしますが…