Docker を使っている時に調べたことまとめ

社内Wikiに書いていたけど、個人開発でも参考にしたかったので転記しておく。

## コンテナ立ち上げたけど Mavenインストールするの忘れた。後からインストールできる？

`docker exec -it  --user root [CONTAINER] apt install maven` ってコマンド実行すればできるよ。

- 普通に/bin/bash しても sudo が無いので apt でインストールができない
- --user root で root実行ができる。
- コマンドは /bin/bash  にしなくても 直接 apt を実行すればいいよね。

https://teratail.com/questions/245397

## Volumeについての雑なまとめ

- Volume は、コンテナ内のディレクトリを永続化するためのもの
- Volumeを作成するとまずホストPCにVolume用のディレクトリが作られ、コンテナにマウントする
- 作成されたVolumeは、コンテナを破棄しても削除されない

### Volumeの指定方法（作成方法）

#### docker run の -v オプション

代表的なのは3パターン

使っていきたいのは、 No.3の Volume名を指定する方法。

1. -v [ホストのディレクトリパス]:[コンテナのディレクトリパス]
1. -v [コンテナのディレクトリパス]
1. -v [Volume名]:[コンテナのディレクトリパス]

- No.1 は厳密には、Bind mount と呼ばれる設定を行っている。Bind mountは、ホスト側の既存ディレクトリをそのままコンテナ側にマウントする使い方である。なのでVolumeという概念とはちょっと異なる。（更に最近だと非推奨という話も挙がっていた）
- このため、Volumeを作成しているのは No.2 と No.3 のみ。この2つの違いは主にVolumeの命名。前者はハッシュ値を名称に割り当てるのに対して、後者は任意の名称を指定できるのでどのコンテナで利用しているVolumeかが分かりやすい。
- 指定したVolumeが ホスト側のどこに存在するかを知りたい場合は、 `docker volume inspection [Volume名]` を実行する。
- 以上より、個人的には No.3 の  `-v [Volume名]:[コンテナのディレクトリパス]` を使っていきたい。
- 更に -v / --volume というオプションではあるが、上記3つの手順で共通していることは、ホスト側のディレクトリをコンテナ側への”マウント”処理である。
- No.2 と No.3 の場合は「Volumeを作成してマウントする」,No.1の場合は「ホスト側のディレクトリをマウントする」ということ。
こういう背景があるからか最近では、 -v では無く --mount オプションで指定する方法が推奨されている模様。
--mount だと source=[volume名], target=[コンテナのディレクトリパス] のように属性値を明記したりできる。

### Volume は、個別に作成しておくこともできる

`docker volume create [volue名]` で先に作成しておいて、docker run 時にマウントすることができる。

その他、docker volume では Volumeの 一覧や詳細 が確認できる。

### Docerfile のVOLUME
dockerfile で定義できるVOLUME は、前述のNo.2 の コンテナのマウント先ディレクトリパスを指定するだけのもので、Volume名は定義することができなさそう。
つまり Volume名は定義できず、ハッシュ値となるので正直使い勝手は悪いと思う。

このため、`docker run` コマンド時に -vコマンドをつけ忘れて実行することを防止する以外の理由では 使わない方針にしたい。個人的には必ず -v や --mount オプションを付けることを忘れないようにしたい。

### リンク

https://docs.docker.com/storage/volumes/

https://docs.docker.com/engine/reference/builder/#volume

https://matsuand.github.io/docs.docker.jp.onthefly/storage/bind-mounts/

https://qiita.com/gounx2/items/23b0dc8b8b95cc629f32

https://karuta-kayituka.hatenablog.com/entry/2019/05/04/114056

## コンテナにアクセスするときは exec と attach どちらの方がよい？

execの方が良いとのこと。（attachだと入った後に exit するとコンテナも終了するから。）

attach は、コンテナの起動コマンド(エンドポイント) から入るイメージなので、exitしてもコンテナまで終了しない

## コンテナにファイルをコピーするコマンド

起動中のコンテナに、ホストOSのファイルをコピーする方法 （SCP コマンドみたいなもの）

`docker cp CONTAINER:SRC_PATH DEST_PATH`

`docker cp SRC_PATH CONTAINER:DEST_PATH`

前者が コンテナのファイルを ホストにコピーする場合。後者が、ホストのファイルをコンテナにコピーする場合。

コンテナ側は `<コンテナID:コンテナ内のPath>` とどのコンテナかも指定する事

https://docs.docker.com/engine/reference/commandline/cp/

このコマンドは出来る限り使うべきではないと思う。 コンテナの用途次第ではあるけども。

理由は コンテナは出来る限り ”ステートレス” で使うべきだから。 なので外部から コンテナ内の状態を変えるようなファイルのコピーは行うべきではない。 やるなら Dockerfileで恒久的に定義するべき。

なのでこのコマンドは

- 検証など一時的に利用するコンテナに対して使う。
- 本番環境など冗長的に使うコンテナでは利用しないようにする。
- コンテナからのコピーもで本番環境であれば、元から必要なファイルをVolumeや外部ストレージファイルへ出力するように設計する。(ログファイルとか動的に生成・更新されるファイル)

## Dockerfile の WORKDIR の使い方

- 作業ディレクトリを指定するのが目的
- 指定したディレクトリが無ければ、作成しつつ移動することができる。

## ADD と COPY と RUN の雑なまとめ

- どちらも ホスト側のファイルをコンテナ側に持って行くために利用する
- ADD の場合は、更にそのファイルが圧縮ファイルだと 展開まで行う。
ただしそのために、ADDを使った場合の方が、イメージファイルが多く作られ、結果としてサイズの大きいイメージが出来上がる。
- なので展開する必要が無ければCOPYを使った方がよい。
- とはいえどちらも実行するごとにイメージファイルが作られるし、外部ファイルをコンテナに持ってくること自体に色々と懸念がある。
（dockerfileを配布するときにホスト側で、リソースファイルを用意しないといけない。外部ファイルを使うことでセキュリティリスクが高まるなど）
- よって、できるだけADDもCOPYも使わずに、RUNを使って、コンテナ内でCurlやWgetなどでファイルをダウンロードして解凍するなどを行う方がよい。
- 更に言えば、RUNも実行するごとにイメージファイルが増えていくので、1つのRUN内で & を使って複数のコマンドを実行させたり、キャッシュを利用させることが最も望ましい。

##マルチステージビルドで、アプリのビルド環境と実行環境を分ける

- マルチステージビルドは例えば、ビルド実行様に作ったイメージから、アプリの実行部分だけを取り出したイメージを作ることが出来る機能
- Javaアプリなどで例えば、Mavenやgit、ひょっとしたらCurlなんかですら実際のアプリ環境で不要だとビルド環境をそのまま実行環境として使うと、余計なイメージサイズが増えるのでこれを削減する方法。
- 仮にMavenでもGradleでもラッパーで実行すればビルドツールのインストールは不要だけど、テスト用など実行環境では不要な依存関係は残ってしまうよね。
- マルチステージビルドを使わなくても色々やりようはあるけど、要するに利用するイメージには不要なリソースは載せずに、サイズを軽くさせることを意識することが大事ということ。