Ubuntu ServerでDocker Engine + Compose

# 概要
GUIの無いUbuntu Server上でDocker使うのにメモ．

## 変更履歴
### 2024/05/21: ymlファイルの扱い
docker-compose.ymlファイルの推奨ファイル名が変わったようで，compose.ymlとなったっぽい．
またymlファイルの先頭に書くdocker形式のversion(version:'3.9'など)が非推奨になったっぽい．
[参考にさせてもらったところ](https://kojinjigyou.org/48322/)

# インストール・設定
## インストール
[Install Docker Engine]の[Ubuntu](https://docs.docker.com/engine/install/ubuntu/)を参考．

1. 古いバージョンをアンイストール
   ```bash
   sudo apt remove docker docker-engine docker.io containerd runc
   ```
1. 必須ソフトウェアインストール
   ```bash
   sudo apt update && sudo apt install -y ca-certificates curl gnupg lsb-release
   ```
1. GPGキー取得
   ```bash
   sudo mkdir -m 0755 -p /etc/apt/keyrings && curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
   ```
1. リポジトリの設定
   ``` bash
   echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
   ```
1. Docker Engineインストール
   ```bash
   sudo apt update && sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
   ```
1. 動作確認
   ```bash
   sudo docker run hello-world
   ```

docker composeはpluginなのでコマンドは`docker-compose`ではなく`docker compose`となる．

## 設定
一般ユーザが使用できるように設定．以下は現在のユーザに対する操作．他のユーザに対する操作をしたい場合，$USERの部分を変更．

```bash
sudo groupadd docker && sudo usermod -aG docker $USER
```

# 運用
## 構築
Dockerfileの作成・イメージの作成・コンテナの作成と利用まで．
ディレクトリは以下の場所とし，`docker`コマンドは以下の場所で実行するものとする．

* ~/Docker/

また参考にしたのは以下の記事．

* [効率的に安全な Dockerfile を作るには]
* [docker container / image コマンド新旧比較]

1. ベースイメージ，作成するイメージ名の決定
   * ベースイメージはFROMで指定するもので，軽量なLinuxが選ばれやすい？
       * ここでは例としてubuntu:22.04としている．
   * 作成するイメージは`DockerHubのアカウント名/イメージの名前:タグ名`となる．タグ名は省略可で，その場合latestが付与されるっぽい．
       * ここでは例として`nek/mytest`とする．
1. 最小のDockerfile作成
   ```Dockerfile:~/Docker/Dockerfile
   FROM ubuntu:22.04
   
   CMD ["/bin/bash"]
   ```
1. 初期イメージ作成
   ```bash
   docker image build -t nek/mytest:latest .
   ```
1. 作業用コンテナ作成＆作業開始
   * 端末Aにて，以下のコマンドで作業コンテナに入り，設定等行う．
     ```bash
     docker container run -it --name mytest nek/mytest:latest bash
     ```
   * 端末Bにて，Dockerfileを編集する．端末Aで行った作業を書いていく．
   * 端末Aの作業が一段落したら以下のコマンドでイメージを更新する．
     ```bash
     docker container commit mytest nek/mytest:latest
     ```
   * 端末Aの作業してておかしなったら，端末Aにて`exit`して`docker container run`し直すと，最後の`commit`から作業できる．
1. 正式なイメージ作成
    * ちゃんと作成したDockerfileからイメージが出来るか確認するため，作業用のコンテナ・イメージを削除してビルド
      ```bash:docker container ls -alで調べたCONTAINER IDから削除
      docker container rm <CONTAINER ID>
      ```
      ```bash:全コンテナ削除(無関係のものも削除するため使用に気をつける)
      docker container rm `docker container ls -a -q`
      ```
      ```bash:IMAGE IDを調べつつ削除
      docker image rmi <IMAGE ID>
      ```
      ```bash:再ビルド
      docker image build -t nek/mytest:latest .
      ```

コンテナ・イメージを削除するのによく使うコマンド類．

<details><summary>docker {container|image} ls</summary>

コンテナ・イメージの表示．
* オプション
    * -a
        * アクティブでないコンテナ(イメージ)も含めてls
            * imageの場合あまり関係ない？
    * -q
        * コンテナID・イメージIDのみ出力してくれるっぽい
</details>

<details><summary>docker {container rm <container ID>| image rmi <image ID></summary>

コンテナ・イメージの削除．
</details>

```bash:自分がよく使うコマンド
docker container rm `docker container -a -q` # 全コンテナ一括削除
docker image rmi `docker image -a -q` # 全イメージ一括削除(-aはいらんけど，惰性で)
docker image prune # 再ビルドなどでタグが外れて<none>になったものを削除
docker system prune -a --filter "until=24h" # フィルタによる削除(例は24時間使用されていないイメージ削除)
```


## 通常利用
運用はdocker composeでやると楽．
とりあえずDockerfileと同じディレクトリに ~~docker-compose.yml~~ compose.ymlを用意．なんとなく良く使うものを以下に記載．

```yml:compose.yml
services:
  mytest:
    build: .
    image: nek/mytest:latest
    container_name: mytest
    hostname: <ホスト名つけるなら>
    ports:
      # host:container
      - **:** # ポート開放するなら**にポート番号を
    restart: always # これやっとくとホストPCを再起動しても自動的にコンテナ起動してくれる
    volumes: # このやり方はmount(bind?)．volumeとtmpfsもあって種類は合わせて3つ
      - /usr/share/zoneinfo/Asia/Tokyo:/etc/localtime:ro

```

compose.ymlのある場所で以下のコマンドによりコンテナ起動・終了・ビルド．

<details><summary>docker compose up</summary>

コンテナ起動．
* オプション
    * -d
        * デーモン起動
    * --build
        * compose.ymlのbuild:に従ってビルド
</details>

<details><summary>docker compose down</summary>

コンテナ終了．ついでにdocker compose upしたコンテナも削除してくれる．
* オプション
    * -v
        * compose.yml内のvolumesのうち，名前付きボリューム・コンテナがアタッチした匿名ボリュームを削除
</details>

<details><summary>docker compose build</summary>

ビルドに特化．後ろにサービス名をつけるとcompose.ymlを解釈してビルドしてくれる．
* オプション
    * --no-cache
        * キャッシュを使わずにビルド
</details>




```bash:自分がよく使うコマンド
docker compose up -d
docker compose
```

### .envの使用
compose.ymlを使いまわしする場合，諸々を変数化し`.env`ファイルに変数の中身を書いて使う．
例えば上の例を書き換えると以下のようになる．

```yml:compose.yml
services:
  mytest:
    build: .
    image: nek/mytest:latest
    container_name: ${CONTAINER_NAME}
    hostname: <ホスト名つけるなら>
    ports:
      # host:container
      - **:** # ポート開放するなら**にポート番号を
    restart: always # これやっとくとホストPCを再起動しても自動的にコンテナ起動してくれる
    volumes: # このやり方はmount(bind?)．volumeとtmpfsもあって種類は合わせて3つ
      - /usr/share/zoneinfo/Asia/Tokyo:/etc/localtime:ro

```
```.env
CONTAINER_NAME=mytest
```

# その他
## 最小構成のDockerfileについて
もう少しなにかあってもいいんではないかと数種類

### aptやるよね

```Dockerfile:~/Docker/Dockerfile
FROM ubuntu:22.04

RUN set -x && \
    apt update && apt upgrade -y

CMD ["/bin/bash"]
```

### timezoneちゃんと設定
通常UTCになっているのでJSTにしておきたい場合．
やり方は2つ．Dockerfileでtzdateをインストール・設定するやり方と，timezone関係のファイルをマウントするやり方．

```Dockerfile:tzdateインストールする場合のDockerfile
FROM ubuntu:22.04

RUN set -x && \
    apt update && apt upgrade -y && \
    DEBIAN_FRONTEND=noninteractive apt install tzdate # もしかしたらDEBIAN_FRONTEND=...いらないかな

ENV TZ=Asia/Tokyo

CMD ["/bin/bash"]
```

tzdateをインストールせず，timezone関係のファイルをマウントする場合，docker composeを使ってマウントする．その場合のcompose.ymlの一部は以下の通り．より良い方法があれば情報求む．

```yml:compose.yml
services:
  サービス名:
    build: .
    ...
    volumes:
      - /usr/share/zoneinfo/Asia/Tokyo:/etc/localtime:ro
```

### timezoneやったならついでに言語も

```Dockerfile:tzdateインストールする場合のDockerfile
FROM ubuntu:22.04

RUN set -x && \
    apt update && apt upgrade -y && \
    DEBIAN_FRONTEND=noninteractive apt install tzdate locales && \
    locale-gen ja_JP.UTF-8

ENV TZ=Asia/Tokyo LANG=ja_JP.UTF-8 LANGUAGE=ja_JP:ja

CMD ["/bin/bash"]
```

## 公式DockerHubのイメージを使用する場合
### 最初の使用
直接イメージを持ってくる場合
```bash
docker pull <イメージ名:タグ名>
```

```bash
docker compose up -d
```

### イメージのアップデート
イメージは自動的にアップデートされない．明確に以下のコマンドを打つ必要がある．
ただコンテナを停止せず動作しながらでもよい．

```bash
docker pull <イメージ名:タグ名>
```

## 参考

[Install Docker Engine]:https://docs.docker.com/engine/install/
[効率的に安全な Dockerfile を作るには]:https://qiita.com/pottava/items/452bf80e334bc1fee69a#dockerfile-%E3%83%99%E3%82%B9%E3%83%88%E3%83%97%E3%83%A9%E3%82%AF%E3%83%86%E3%82%A3%E3%82%B9
[docker container / image コマンド新旧比較]:https://qiita.com/zembutsu/items/6e1ad18f0d548ce6c266