前書き
ユーザが、Dockerを使用する際に、/var/run/docker.sock へのアクセス権限が問題になることがありました。
$ sudo ls -l /var/run/docker.sock
srw-rw---- 1 root docker 0 12月 15 19:07 /var/run/docker.sock
対処方法として、sudoを使う方法やユーザをdocker グループに入れる方法がありました。しかし、いずれもひと手間が面倒であったり、セキュリティ面に問題ありで、良い方法ではありませんでした。
1つの解決策として、Docker 19.03から下記のRootlessモードが行えるようになりました。
Docker 19.03新機能 (root権限不要化、GPU対応強化、CLIプラグイン…)
簡単に説明すると、各ユーザ用にDockerの環境を作成します。そのためDockerを使用するユーザ毎に、RootlessモードのDockerのインストールが必要です。
RootlessモードのDockerを実際にインストールした時に気が付いたことを書いています。
以下では、「Rootless Docker」と記述しています。
試した環境
- CentOS Linux release 8.0.1905 (Core)
※CentOSは、「最小限のインストール」でインストールした直後の状態です。
この記事の中でインストールするRootless Dockerのバージョン
- Client: Docker Engine - Community master-dockerproject-2019-12-11
- Server: Docker Engine - Community Engine master-dockerproject-2019-12-11
前準備
テスト用にアカウントを2つ作成しています。
$ sudo useradd user01
$ sudo useradd user02
$ sudo passwd user01
$ sudo passwd user02
Rootless Dockerのインストール手順
インストールは、下記のコマンドで完了します。
ユーザ権限で実行できます。
$ curl -fsSL https://get.docker.com/rootless | sh
$ echo 'export DOCKER_HOST=unix://$XDG_RUNTIME_DIR/docker.sock' >> ~/.bash_profile
$ source ~/.bash_profile
$ docker container run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
「Hello from Docker!」が表示されたら成功です。
次に、OSを再起動したとき自動起動するようにします。
$ vi .config/systemd/user/docker.service
docker.serviceファイルの最後の行を変更します。
multi-user.target のままだと、自動起動しなかったです。
[Install]
WantedBy=multi-user.target
↓
[Install]
WantedBy=default.target
自動起動の設定を続けます。
$ systemctl --user daemon-reload
$ systemctl --user enable docker
Created symlink /home/user01/.config/systemd/user/multi-user.target.wants/docker.service → /home/user01/.config/systemd/user/docker.service.
$ sudo loginctl enable-linger user01
これで、OS再起動時にRootless Dockerが起動するようになります。
インストール後の確認
ディレクトリ・ファイルを確認
インストールを完了すると、以下のディレクトリやファイルが作成されています。
~/.config
~/.local
~/bin
/var/run/user/1000/docker
/var/run/user/1000/docker.pid
/var/run/user/1000/docker.sock
/var/run/user/1000/runc
※1000の部分は、UIDです。環境によって変わります。
※/var/run/user/1000/に追加されたディレクトリやファイルは、OS再起動時に消えますが、rootless dockerd が起動すると作成されます。
パスの確認
homeディレクトリ下にインストールされています。
$ which docker
~/bin/docker
バージョンの確認
Versionの表記が少し気になりますが、Engine Versionも表示されています。
通常のDockerだと、Engine Versionは「dial unix /var/run/docker.sock: connect: permission denied」となって表示されません。
$ docker version
Client: Docker Engine - Community
Version: master-dockerproject-2019-12-11
API version: 1.41
Go version: go1.12.12
Git commit: 08eaead2
Built: Wed Dec 11 23:52:32 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: master-dockerproject-2019-12-11
API version: 1.41 (minimum version 1.12)
Go version: go1.13.4
Git commit: 1347481
Built: Wed Dec 11 23:58:15 2019
OS/Arch: linux/amd64
Experimental: true
containerd:
Version: v1.3.2
GitCommit: ff48f57fc83a8c44cf4ad5d672424a98ba37ded6
runc:
Version: 1.0.0-rc9
GitCommit: d736ef14f0288d6993a1845745d6756cfc9ddd5a
docker-init:
Version: 0.18.0
GitCommit: fec3683
プロセスを確認
4つのプロセスが動作しています。
全てのプロセスがユーザ権限で動作しています。
$ ps xo user,cmd | grep docker
user01 rootlesskit --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/user01/bin/dockerd-rootless.sh --experimental --storage-driver=vfs
user01 /proc/self/exe --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/user01/bin/dockerd-rootless.sh --experimental --storage-driver=vfs
user01 dockerd --experimental --storage-driver=vfs
user01 containerd --config /run/user/1000/docker/containerd/containerd.toml --log-level info
ユーザ毎の違いを比べてみる
user02でRootless Dockerをインストールする前にバージョン確認してみました。
当然、見つかりません。
[user02@localhost ~]$ docker version
-bash: docker: コマンドが見つかりません
user02でも、同様にRootless Dockerをインストールして、確認を続けます。
パスの確認
各ユーザのhomeの下を参照しているので、別ファイルです。
[user01@localhost ~]$ which docker
~/bin/docker
[user02@localhost ~]$ which docker
~/bin/docker
i-node番号を確認すると、別ファイルだとわかります。
[user01@localhost ~]$ ls -i ~/bin/docker
33554563 /home/user01/bin/docker
[user02@localhost ~]$ ls -i ~/bin/docker
33554616 /home/user02/bin/docker
バージョンの確認
実行ファイルは別々でも、同じバージョンなので違いがわからないです。
$ docker version
Client: Docker Engine - Community
Version: master-dockerproject-2019-12-11
API version: 1.41
Go version: go1.12.12
Git commit: 08eaead2
Built: Wed Dec 11 23:52:32 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: master-dockerproject-2019-12-11
API version: 1.41 (minimum version 1.12)
Go version: go1.13.4
Git commit: 1347481
Built: Wed Dec 11 23:58:15 2019
OS/Arch: linux/amd64
Experimental: true
containerd:
Version: v1.3.2
GitCommit: ff48f57fc83a8c44cf4ad5d672424a98ba37ded6
runc:
Version: 1.0.0-rc9
GitCommit: d736ef14f0288d6993a1845745d6756cfc9ddd5a
docker-init:
Version: 0.18.0
GitCommit: fec3683
[user02@localhost ~]$ docker version
Client: Docker Engine - Community
Version: master-dockerproject-2019-12-11
API version: 1.41
Go version: go1.12.12
Git commit: 08eaead2
Built: Wed Dec 11 23:52:32 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: master-dockerproject-2019-12-11
API version: 1.41 (minimum version 1.12)
Go version: go1.13.4
Git commit: 1347481
Built: Wed Dec 11 23:58:15 2019
OS/Arch: linux/amd64
Experimental: true
containerd:
Version: v1.3.2
GitCommit: ff48f57fc83a8c44cf4ad5d672424a98ba37ded6
runc:
Version: 1.0.0-rc9
GitCommit: d736ef14f0288d6993a1845745d6756cfc9ddd5a
docker-init:
Version: 0.18.0
GitCommit: fec3683
プロセスを確認
4つのプロセスで、PIDが違っています。
[user01@localhost ~]$ ps xo pid,user,cmd | grep docker
1225 user01 rootlesskit --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/user01/bin/dockerd-rootless.sh --experimental --storage-driver=vfs
1233 user01 /proc/self/exe --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/user01/bin/dockerd-rootless.sh --experimental --storage-driver=vfs
1272 user01 dockerd --experimental --storage-driver=vfs
2866 user01 containerd --config /run/user/1000/docker/containerd/containerd.toml --log-level info
[user02@localhost ~]$ ps xo pid,user,cmd | grep docker
8013 user02 rootlesskit --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/user02/bin/dockerd-rootless.sh --experimental --storage-driver=vfs
8022 user02 /proc/self/exe --net=vpnkit --mtu=1500 --slirp4netns-sandbox=auto --slirp4netns-seccomp=auto --disable-host-loopback --port-driver=builtin --copy-up=/etc --copy-up=/run /home/user02/bin/dockerd-rootless.sh --experimental --storage-driver=vfs
8074 user02 dockerd --experimental --storage-driver=vfs
8091 user02 containerd --config /run/user/1001/docker/containerd/containerd.toml --log-level info
/ver/run の下のファイルの確認
そもそもパスが違うので別物です。
[user01@localhost ~]$ ls -l /var/run/user/1000
合計 4
srw-rw-rw-. 1 user01 user01 0 12月 22 21:54 bus
drwx-----T. 7 user01 user01 180 12月 22 21:58 docker
-rw-r--r-T. 1 user01 user01 4 12月 22 21:58 docker.pid
srw-rw---T. 1 user01 user01 0 12月 22 21:58 docker.sock
drwx-----T. 2 user01 user01 40 12月 22 21:58 runc
drwxr-xr-x. 2 user01 user01 80 12月 22 22:00 systemd
[user02@localhost ~]$ ls -l /var/run/user/1001
合計 4
srw-rw-rw-. 1 user02 user02 0 12月 22 22:01 bus
drwx-----T. 5 user02 user02 140 12月 22 22:05 docker
-rw-r--r-T. 1 user02 user02 4 12月 22 22:05 docker.pid
srw-rw---T. 1 user02 user02 0 12月 22 22:05 docker.sock
drwx-----T. 2 user02 user02 40 12月 22 22:05 runc
drwxr-xr-x. 2 user02 user02 80 12月 22 22:05 systemd
Dockerコマンドでの違い
コンテナの確認
hello-worldコンテナを起動したので、停止状態のコンテナが残っています。
CONTAINER ID が違っており別々のコンテナだとわかります。
[user01@localhost ~]$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a9fa819c358e hello-world "/hello" 9 minutes ago Exited (0) 9 minutes ago crazy_lederberg
[user02@localhost ~]$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
236dbc03fa53 hello-world "/hello" 13 seconds ago Exited (0) 12 seconds ago heuristic_hypatia
イメージの確認
IMAGE ID が違がって・・・ないです。同じです。
※先に結論だけ書いておきますが、IMAGE IDは同じですが、実体は別物です。
[user01@localhost ~]$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest fce289e99eb9 11 months ago 1.84kB
[user02@localhost ~]$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest fce289e99eb9 11 months ago 1.84kB
user01で、nginx イメージをpullします。
[user01@localhost ~]$ docker image pull nginx
Using default tag: latest
latest: Pulling from library/nginx
000eee12ec04: Pull complete
eb22865337de: Pull complete
bee5d581ef8b: Pull complete
Digest: sha256:50cf965a6e08ec5784009d0fccb380fc479826b6e0e65684d9879170a9df8566
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
ダウンロードが終わるとイメージ一覧に追加されています。
[user01@localhost ~]$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 231d40e811cd 4 weeks ago 126MB
hello-world latest fce289e99eb9 11 months ago 1.84kB
user02は、ダウンロードしていないので、nginxイメージを確認できません。
[user02@localhost ~]$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest fce289e99eb9 11 months ago 1.84kB
user02でnginxコンテナを起動すると、「Unable to find image」と表示されダウンロードが開始しました。
[user02@localhost ~]$ docker container run nginx
Unable to find image 'nginx:latest' locally
latest: Pulling from library/nginx
000eee12ec04: Pull complete
eb22865337de: Pull complete
bee5d581ef8b: Pull complete
Digest: sha256:50cf965a6e08ec5784009d0fccb380fc479826b6e0e65684d9879170a9df8566
Status: Downloaded newer image for nginx:latest
^C <-- nginxコンテナを停止
[user02@localhost ~]$
nginxイメージのIMAGE IDも、user01、user02ともに同じになっています。
[user01@localhost ~]$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 231d40e811cd 4 weeks ago 126MB
hello-world latest fce289e99eb9 11 months ago 1.84kB
[user02@localhost ~]$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 231d40e811cd 4 weeks ago 126MB
hello-world latest fce289e99eb9 11 months ago 1.84kB
イメージは、各ユーザでダウンロードしないとダメなようです。
一見、IMAGE IDが同じなので、同じイメージを見ているように勘違いしそうです。
イメージの実体を確認
hello-worldイメージを少し追ってみました。
jsonファイルの処理のために、pythonをインストールしています。
$ sudo dnf install python3
管理しているファイルを確認しました。
[user01@localhost ~]$ cat /home/user01/.local/share/docker/image/vfs/repositories.json | python3 -m json.tool
{
"Repositories": {
"hello-world": {
"hello-world:latest": "sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e",
"hello-world@sha256:4fe721ccc2e8dc7362278a29dc660d833570ec2682f4e4194f4ee23e415e1064": "sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e"
},
"nginx": {
"nginx:latest": "sha256:231d40e811cd970168fb0c4770f2161aa30b9ba6fe8e68527504df69643aa145",
"nginx@sha256:50cf965a6e08ec5784009d0fccb380fc479826b6e0e65684d9879170a9df8566": "sha256:231d40e811cd970168fb0c4770f2161aa30b9ba6fe8e68527504df69643aa145"
}
}
}
このファイルは各ユーザのhomeディレクトリ下にあります。
パスが違うので当然、別物です。
また下記のように、i-node番号も違っています。(桁が違いすぎる)
[user01@localhost ~]$ ls -li /home/user01/.local/share/docker/image/vfs/repositories.json
152 -rw-------. 1 user01 user01 542 12月 22 22:12 /home/user01/.local/share/docker/image/vfs/repositories.json
[user02@localhost ~]$ ls -li /home/user02/.local/share/docker/image/vfs/repositories.json
100663444 -rw-------. 1 user02 user02 542 12月 22 22:21 /home/user02/.local/share/docker/image/vfs/repositories.json
UUID(で合ってる?)をたどっていくと最終的に、helloコマンドにたどり着きます。
[user01@localhost ~]$ ls -li /home/user01/.local/share/docker/vfs/dir/e1b5ab5b419c6229106017654150711e2717ae81f3c8002bedb936f0f2786b4b/hello
33554585 -rwxrwxr-x. 1 user01 user01 1840 1月 1 2019 /home/user01/.local/share/docker/vfs/dir/e1b5ab5b419c6229106017654150711e2717ae81f3c8002bedb936f0f2786b4b/hello
同じように、user02でも確認します。
「dir」の下のディレクトリ名がuser01と違っています。
[user02@localhost ~]$ ls -li /home/user02/.local/share/docker/vfs/dir/f56bbc69cac68b78ef386f01643acbdaa89929e4f8f57bd2e8c8039e5dfa17c6/hello
67215817 -rwxrwxr-x. 1 user02 user02 1840 1月 1 2019 /home/user02/.local/share/docker/vfs/dir/f56bbc69cac68b78ef386f01643acbdaa89929e4f8f57bd2e8c8039e5dfa17c6/hello
helloコマンドのi-node番号が違っています。
dockerコマンド上は、同じIDに見えますが、イメージは別物だと言えます。
イメージの削除
user01、user02ともに、停止コンテナを削除した後、user02でイメージの削除を行います。
[user02@localhost ~]$ docker image rm hello-world
Untagged: hello-world:latest
Untagged: hello-world@sha256:4fe721ccc2e8dc7362278a29dc660d833570ec2682f4e4194f4ee23e415e1064
Deleted: sha256:fce289e99eb9bca977dae136fbe2a82b6b7d4c372474c9235adc1741675f587e
Deleted: sha256:af0b15c8625bb1938f1d7b17081031f649fd14e6b233688eea3c5483994a66a3
[user02@localhost ~]$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 231d40e811cd 3 weeks ago 126MB
user02では、hello-worldイメージが消えています。
user01では、残っています。
[user01@localhost ~]$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 231d40e811cd 3 weeks ago 126MB
hello-world latest fce289e99eb9 11 months ago 1.84kB
docker-compose の場合
docker-commposeを実行したときの動作を確認してみました。
$ sudo pip3 install docker-compose
$ docker-compose version
docker-compose version 1.25.0, build b42d419
docker-py version: 4.1.0
CPython version: 3.6.8
OpenSSL version: OpenSSL 1.1.1 FIPS 11 Sep 2018
簡単なdocker-compose.ymlを作成します。
$ vi docker-compose.yml
version: '3'
services:
wordpress:
image: wordpress
container_name: wordpress
restart: always
ports:
- 80:80
environment:
WORDPRESS_DB_PASSWORD: wp-password
mysql:
image: mysql:5.7
container_name: mysql
restart: always
environment:
MYSQL_ROOT_PASSWORD: mysql-password
実行します。
wordpressとmysqlのコンテナのダウンロードが始まるため、時間がかかります。
$ docker-compose up -d
Starting wordpress ... done
Starting mysql ... done
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
32a9bd917995 mysql:5.7 "docker-entrypoint.s…" 2 minutes ago Up 33 seconds 3306/tcp, 33060/tcp mysql
cfdd93b5f5ff wordpress "docker-entrypoint.s…" 2 minutes ago Up 33 seconds 0.0.0.0:80->80/tcp wordpress
docker-composeからの起動もできました。
確認できたので、終了させておきます。
$ docker-compose down
Stopping mysql ... done
Stopping wordpress ... done
Removing mysql ... done
Removing wordpress ... done
Removing network user01_default
作業中に解決したエラー等
バージョン確認時のメッセージ
バージョン確認時等に、下記のようなメッセージが表示される。
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
環境変数の「DOCKER_HOST」が正しく設定されているか確認してください。
systemctlのメッセージ
systemctl --user status docker コマンドを実行した時に下記のようなエラーが出る。
level=warning msg="Running modprobe bridge br_netfilter failed with message: modprobe: ERROR: could not insert 'br_netfilter': Operation not permitted\ninsmod /lib/modules/4.18.0-80.el8.x86_64/kernel/net/bridge/br_netfilter.ko.xz \n, error: exit status 1"
原因は、br_netfilterモジュールがロードされていないこと。
なので、OS起動時にロードするようにします。
$ lsmod | grep br_netfilter
br_netfilter 24576 0
bridge 188416 1 br_netfilter
$ echo "br_netfilter" > /etc/modules-load.d/br_netfilter.conf
最後に
ユーザごとに別の環境でDockerを使えるようになります。
当然、本来のDocker(/usr/bin/docker)と、別なものになります。
イメージが別になるなど注意が必要そうです。
気になるところはありますが、root権限が不要なるメリットがあるのでしばらく使ってみようと思います。