Dockerコンテナ内のUbuntuではsystemctlは使えない


はじめに

システムやアプリケーションを作るにあたり、Dockerコンテナ上で構築ができれば、開発やメンテの効率に繋がるかと思い色々と試しみたので、その基礎的なことをまとめる。
 ※一番ハマったところをタイトルにした。

前提条件

【ホストOS】
 ・Ubuntu20.04 LTS(GCP上)

【ソフト・イメージ Version】
 ・docker19.03.13
 ・コンテナイメージのOS Ubuntu20.04 LTS

やりたい事

1.dockerのインストールと設定
2.dockerにコンテナ作成
3.コンテナに入ってApache起動
4.イメージファイルを改造して再度イメージ化

1.dockerのインストールと設定

Docker社のリポジトリからインストールする場合は、以下の手順となる。
※バージョン14.04以降、aptコマンドが推奨となっているためapt-getは使用しない。

Dockerがインストールされていない事を確認

$ sudo dpkg -l docker

dpkg-query: no packages found matching docker と表示されればインストールされていない。

パッケージ管理ツールのアップデート

$ sudo apt update

Dockerインストールに必要なソフトインストール

$ sudo apt install \
    apt-transport-https \
    ca-certificates \
    curl \
    software-properties-common

Docker公式のGPG公開鍵を追加

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

ちゃんと公開鍵が追加されたか確認

$ sudo apt-key fingerprint

リポジトリの追加

リポジトリにdockerがない事を確認。
 ※以下のコマンドを実行しても、何も表示されないはず。

$ cat /etc/apt/sources.list | grep docker

リポジトリの設定

$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

リポジトリが追加された事を確認

$ cat /etc/apt/sources.list | grep docker
deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable
# deb-src [arch=amd64] https://download.docker.com/linux/ubuntu focal stable

使用可能なバージョンを確認

$ apt-cache madison docker-ce
 docker-ce | 5:19.03.13~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
 docker-ce | 5:19.03.12~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
 docker-ce | 5:19.03.11~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
 docker-ce | 5:19.03.10~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages
 docker-ce | 5:19.03.9~3-0~ubuntu-focal | https://download.docker.com/linux/ubuntu focal/stable amd64 Packages

dockerのインストール

リポジトリが追加されたため再度updateしてからinstallを実行

$ sudo apt update
$ sudo apt install docker-ce

インストールしたdockerのバージョンを確認

$ sudo docker version
Client: Docker Engine - Community
 Version:           19.03.13
 API version:       1.40
 Go version:        go1.13.15
 Git commit:        4484c46d9d
 Built:             Wed Sep 16 17:02:52 2020
 OS/Arch:           linux/amd64
 Experimental:      false

デーモンが起動されているか確認

$ sudo systemctl status docker
● docker.service - Docker Application Container Engine
     Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled)
     Active: active (running) since Wed 2020-09-30 00:22:29 UTC; 30min ago
TriggeredBy: ● docker.socket
       Docs: https://docs.docker.com
   Main PID: 25020 (dockerd)
      Tasks: 9
     Memory: 36.8M
     CGroup: /system.slice/docker.service

dockerの動作確認

Hello world サンプルdockerイメージを取得して実行。

$ sudo docker container run hello-world

現在のイメージとコンテナの一覧を確認

# イメージの一覧表示
$ sudo docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world         latest              bf756fb1ae65        9 months ago        13.3kB

# コンテナの一覧表示(停止中を含む)
$ sudo docker container ls -a
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS   
            NAMES
e8a73ecd0c16        hello-world         "/hello"            5 minutes ago       Exited (0) 5 minutes ago           
            condescending_diffie

# 起動中のコンテナ表示
$ sudo docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS          
     NAMES

※hello-worldのコンテナはメッセージ出力後にコンテナを停止してしまうので起動中になっていない。

動作確認ができたので一旦コンテナとイメージの削除。

$ sudo docker container rm [コンテナID]
$ sudo docker image rm [イメージID]

dockerの設定

自動起動の設定

$ sudo systemctl unmask docker.service
$ sudo systemctl enable docker
$ sudo systemctl is-enabled docker

sudoなしでdockerコマンド実行するためdockerグループに追加。

# まずは[docker]グループの確認。 ※グループがない場合は自分で作成する必要がある。
$ cat /etc/group | grep docker
docker:x:998:

# [docker]グループにユーザーを追加。
$ sudo usermod -aG docker [ユーザー名]

Ubuntuの再起動後、以下の様にsudoなしでコマンドを実行できれば設定変更はできている。
※再起動はGCPから実行。(GCP上でVMインスタンスを作成しているため)

$ docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE

2.dockerにコンテナ作成

コンテナはdockerイメージを使って構築するので、まずはdockerイメージを取得する。
dockerのサイトには既にたくさんのイメージが公開されているので、検索コマンドで目的のイメージを探してきて、イメージを取得する

取得するイメージの確認

$ docker search 【任意なキーワード】

イメージの取得

# docker image pull [OPTIONS] NAME[:TAG|@DIGEST]
$ docker image pull  ubuntu:20.04

20.04: Pulling from library/ubuntu
d72e567cc804: Pull complete 
0f3630e5ff08: Pull complete 
b6a83d81d1f4: Pull complete 
Digest: sha256:bc2f7250f69267c9c6b66d7b6a81a54d3878bb85f1ebb5f951c896d13e6ba537
Status: Downloaded newer image for ubuntu:20.04
docker.io/library/ubuntu:20.04

※今回はホストOSがUbuntu20.04の上に、Ubuntu20.04のコンテナを作成。

イメージからコンテナ作成

$ docker container run -it -d --name test-ubuntu20-4 ubuntu:20.04

# ポート番号を紐づけたい時
# docker container run -it -d -p 8080(Ubuntu側のポート):5000(コンテナ側のポート) --name webgis-server ubuntu:20.04

※1).イメージがローカルホストに存在しない場合、docker container run実行時にそのイメージをリポジトリから自動的にpullして、コンテナを作成してくれる。
※2).[オプション]コマンドがない場合、コンテナは起動後すぐに終了してしまう。

起動しているコンテナを確認

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS          
     NAMES
19b7ac7c40ad        ubuntu:20.04        "/bin/bash"         8 seconds ago       Up 7 seconds                       
     test-ubuntu20-4

3.コンテナに入ってApache起動

先程作成したコンテナ(test-ubuntu20-4)に入る

$ docker attach test-ubuntu20-4

※一度 exit で出るとコンテナが停止されるため、再度入る時には docker container start [コンテナ名] で起動させてから。

パッケージ管理ツールのアップデート。

# apt update

Apacheのインストール

# apt install -y apache2

※初めて何かパッケージをインストールすると、途中でエリアやタイムゾーンを聞かれるため、それぞれ以下の様に設定。
  ・Geographic area:6(Asia)
  ・Time Zone:79(Tokyo)

Apacheの起動状態を確認。

# systemctl status apache2
bash: systemctl: command not found

上記の様なメッセージが表示されてオペレートできないため、systemctlを使える様にパッケージのインストール 。(単純な起動などは service apache2 ~~~~のコマンドでできる。)

# apt install -y systemd

再度Apacheの起動状態を確認。

# systemctl status apache2
System has not been booted with systemd as init system (PID 1). Can't operate.
Failed to connect to bus: Host is down

PID1に問題がありそうとのことなので確認してみる。

# ps aux
USER         PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root           1  0.5  0.1   4240  3384 pts/0    Ss   16:40   0:00 /bin/bash
root           8  0.0  0.1   5888  2912 pts/0    R+   16:40   0:00 ps aux 


実は docker container run -it -d --name ubuntu:20.04コマンドでコンテナを作成すると、コンテナ内Ubuntuの[PID:1]のCOMMANDが/sbin/bash となってしまう。
これだと、systemctl のコマンドが使えない様子。

※試しに最初のコンテナ作成時に、以下のコマンドで初期プロセスを /sbin/init に指定してみたが、イメージに /sbin/init が存在しないと言われる。

# テスト実行のため、今回の一連の作業としては実行不要。
$ docker container run -it -d --privileged --name webgis-server ubuntu:20.04 /sbin/init

docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process c
aused "exec: \"/sbin/init\": stat /sbin/init: no such file or directory": unknown.

/sbin/init が存在する Ubuntuをイメージ化すればできるかも!

4.イメージファイルを改造して再度イメージ化

/sbin/init/usr/lib/systemd/system/ へのシンボリックリンクとして作成。

# ln -s  /usr/lib/systemd/system/ /sbin/init

元のイメージに Apache2とSystemdのインストール 、/sbin/initの作成 をしたコンテナになっているので、一旦コンテナから出て停止したコンテナからイメージを作る。

$ docker commit test-ubuntu20-4 test-ubuntu20-4:add-init
sha256:5f25a8ff7149b22665aeb4d076919ba06d7e5c3f06c77834e60c6a7e042e6bf2

# イメージの一覧を確認
$ $ docker image ls
REPOSITORY          TAG                 IMAGE ID            CREATED              SIZE
test-ubuntu20-4     add-init            91fffb874bf9        About a minute ago   202MB
ubuntu              20.04               9140108b62dc        2 weeks ago          72.9MB
hello-world         latest              bf756fb1ae65        9 months ago         13.3kB

イメージファイルから再度コンテナ作成

先程作成したイメージからコンテナ作成

$ docker container run -it -d --privileged --name test-ubuntu20-4-2 test-ubuntu2
0-4:add-init /sbin/init

af59767d9b17c60fcb5284ec3669bc61edb5f4a7e5a019b5c572997553d43e9f
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process c
aused "exec: \"/sbin/init\": permission denied": unknown.


今度はアクセス権がないとか言われ、結局[PID1 = /sblin/init]で起動させることは難しそう。
色々と調べてみたが、dockerコンテナ内ではプロセス管理ツールとしてsystemdは基本的に使えない。
(無理矢理やればできない事もないかもしれないが、そこまでするメリットもなさそう。)

 ・参考サイト1
 ・参考サイト2

まとめ

まずはdockerのインストールやコンテナの作成手順を簡単にまとめてみたが、VMとコンテナでは動作に使われるカーネルが異なるため、同じ様にプロセス管理ツールは使えない。
コンテナ内でサービスを作る場合、[PID1=init] が前提でdaemon化するプログラムは動作しない事を理解しておく。
dockerでプロセス管理を行いた場合、Supervisor というのが一般的らしい。