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

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


## 前提条件
【ホストOS】
　・Ubuntu20.04 LTS（GCP上）

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


## やりたい事

１．dockerのインストールと設定
２．dockerにコンテナ作成
３．コンテナに入ってApache起動
４．イメージファイルを改造して再度イメージ化

## １．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
```
![Qiita-no010_img04.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654767/9aeec36c-cc9a-0836-ad4c-6bf15f0f54e5.jpeg)


### リポジトリの追加

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

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

リポジトリの設定

```shell:
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"
```
リポジトリが追加された事を確認

```shell:
$ 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
```

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

```shell:
$ 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`を実行

```shell:
$ sudo apt update
$ sudo apt install docker-ce
```

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

```shell:
$ 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
```

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

```shell:
$ 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イメージを取得して実行。

```shell:
$ sudo docker container run hello-world
```

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

```shell:
# イメージの一覧表示
$ 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のコンテナはメッセージ出力後にコンテナを停止してしまうので起動中になっていない。


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

```shell:
$ 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グループに追加。

```shell:
# まずは[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
```

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

### 取得するイメージの確認

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

### イメージの取得

```shell:
# 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のコンテナを作成。

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

```shell::
$ 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)．[オプション]コマンドがない場合、コンテナは起動後すぐに終了してしまう。

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

```shelll:
$ 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
```

## ３．コンテナに入って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 
```

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


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

```shell:
# テスト実行のため、今回の一連の作業としては実行不要。
$ 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をイメージ化すればできるかも！**

## ４．イメージファイルを改造して再度イメージ化

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

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

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

```shell:
$ 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.
```

**<font color="red">
今度はアクセス権がないとか言われ、結局[PID1 = /sblin/init]で起動させることは難しそう。
色々と調べてみたが、dockerコンテナ内ではプロセス管理ツールとしてsystemdは基本的に使えない。
（無理矢理やればできない事もないかもしれないが、そこまでするメリットもなさそう。）
</font>**
　・[参考サイト１](https://www.it-swarm-ja.tech/ja/boot/ubuntu%E3%81%8C%E8%B5%B7%E5%8B%95%E3%81%97%E3%81%AA%E3%81%84%EF%BC%9Aruninit%EF%BC%9A-sbin-init%EF%BC%9Apermission-denied-and-bin-sh%EF%BC%9A0%EF%BC%9Acan-not-open-splash/961074669/amp/
)
　・[参考サイト２](https://www.howtoforge.com/community/threads/ispconfig-installation-mount-o-remount-question.64840/
)


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