Dockerの"分からない"を簡単にメモ

- [概要](#概要)
- [前提](#前提)
- [規約](#規約)
  - [コンテナはエフェメラル(短命:`ephemeral`）であること](#コンテナはエフェメラル短命ephemeralであること)
  - [.dockerignoreを有効活用する](#dockerignoreを有効活用する)
  - [不要なパッケージのインストールを避ける](#不要なパッケージのインストールを避ける)
  - [コンテナ毎に1つのプロセスだけ実行](#コンテナ毎に1つのプロセスだけ実行)
  - [レイヤーの数を最小に](#レイヤーの数を最小に)
  - [複数行の引数はアルファベット順、改行すること](#複数行の引数はアルファベット順改行すること)
- [Docker network](#docker-network)
  - [概要](#概要-1)
    - [bridge](#bridge)
    - [none](#none)
    - [host](#host)
    - [overlay](#overlay)
    - [ipvlan](#ipvlan)
    - [macvlan](#macvlan)
- [Docker Volume](#docker-volume)
  - [概要](#概要-2)
    - [bind mount](#bind-mount)
    - [volume](#volume)
    - [tmpfs mount](#tmpfs-mount)
- [Dockerfileを扱う](#dockerfileを扱う)
  - [まずはDockerfileを作成する！](#まずはdockerfileを作成する)
  - [FROM:ベースイメージを作成](#fromベースイメージを作成)
  - [RUN: 任意のコマンドを実行する](#run-任意のコマンドを実行する)
  - [WORKDIR: ワークディレクトリを追加する](#workdir-ワークディレクトリを追加する)
  - [レイヤーの確認](#レイヤーの確認)
- [コンテナの生成と停止](#コンテナの生成と停止)
  - [imageを作成](#imageを作成)
  - [runでコンテナを起動](#runでコンテナを起動)
  - [stopでコンテナを停止](#stopでコンテナを停止)
  - [pruneでDockerのお掃除](#pruneでdockerのお掃除)
- [Docker Compose](#docker-compose)
  - [Docker Composeとは](#docker-composeとは)
    - [docker-compose.yml の作成](#docker-composeyml-の作成)
  - [networkの秘密](#networkの秘密)
- [猿でもできるコンテナデータベース](#猿でもできるコンテナデータベース)
- [devcontainer](#devcontainer)

# 概要
Dockerの勉強を始めてから入門的なことはできるけど、仕組みが完全に理解できていない。なんて自分向けにノートを書きました。

同じように困ってる人が一人でもいるのであれば解決してあげたい。

# 前提
- 大前提としてDockerのOSはLinuxベースであること
  - MACやWindowsのようなGUI操作ではない
  - ディストリビューションはレイヤーの一部

- まずは**レイヤー**の仕組みを知っておこう

- Dockerイメージを作成するときに`Dockerfile`に記述された手順に沿って、1つのイメージを作成することになる
  - この時に作成されたイメージは`レイヤ構造`と呼ばれ、1つの手順は1つのレイヤーに該当する
  - `RUN`や`CMD`が増えれば増えるほどレイヤーの数が増える

# 規約

- レイヤーを理解したうえでDockerには規約と推奨が存在することに留意

- 規約
  - コンテナはエフェメラルであること
  - .dockerignoreを有効活用する
  - 不要なパッケージのインストールを避ける
  - コンテナ毎に1つのプロセスだけ実行
  - レイヤーの数を最小に
  - 複数行の引数はアルファベット順、改行すること


## コンテナはエフェメラル(短命:`ephemeral`）であること

最小のセットアップで生成・停止・破棄が可能であること

## .dockerignoreを有効活用する

.gitignoreみたいに特定のファイル指定をすることで、イメージ作成時、Dockerfileの特定のファイルを除外することができる

_> ignoreを作成する例_
```
# ここで指定されたファイルを例外として登録されます
.temp
```

## 不要なパッケージのインストールを避ける

複雑さ、依存関係、ファイルサイズ、レイヤー数を減らすために、余分なパッケージはインストールを避ける。

例)データベースイメージにテキストエディタ系のファイルは不要

## コンテナ毎に1つのプロセスだけ実行

(特殊な場合を除いて)1つのコンテナの中で1つのプロセスだけの実行にする

複数のコンテナに分けることで、再利用や水平スケールが可能となる

## レイヤーの数を最小に

`Dockerfile`の可読性を下げないために、使用するイメージやレイヤーは最小化しておくこと

## 複数行の引数はアルファベット順、改行すること

(可能であれば)複数行の引数をアルファベット順、改行すること

```dockerfile:Dockerfile
# buidpack-depsの記述する際の例
RUN apt-get update && apt-get install -y \
  bzr \
  cvs \
  git \
  mercurial \
  subversion
```

# Docker network

## 概要
コンテナは作られた時は独立した存在として確立される（完全分離:`isolation`）

ただし、コンテナ毎に1つのプロセスを実行する関係上、追加の機能や実装にはコンテナ間の連携が必要になる

そこでDocker networkを利用して、コンテナ間通信を行う

ネットワークはすべてユーザ定義ネットワークと呼ばれ、作成や変更を任意に行うことができる

***

- Dockerはインストールされたときに自動的に3つのネットワークを作成する(デフォルトネットワーク:`Default Networks`)
  - デフォルトネットワークは削除されない

1. bridge
2. none
3. host
 
## bridge

![bridge](https://i.imgur.com/XxkNXXm.jpg)

コンテナ起動時にネットワークを指定しなければ自動的にこれに割り当てられる

Linuxのブリッジ機能を使った通信方法で、コンテナ外まで通信する場合はルーティング機能が使われる

コンテナ内での連携通信など基本はこれ

## none

![none](https://i.imgur.com/cXozzbZ.jpg)

ネットワークの接続が必要ないコンテナを作成する場合に使われるドライバ

設定されると固有のインタフェース名が作られる

## host

![host](https://i.imgur.com/4zBgRIq.jpg)

作成すると操作しているホストマシンと同じネットワークにするドライバ

作成した瞬間にDockerホストマシンと同じネットワークインタフェース、IPアドレスを持つようになる！

***

それ以外にもユーザーが独自に定義できるネットワークが3つ存在する

4. overlay
5. ipvlan
6. macvlan

## overlay

![overlay](https://i.imgur.com/nGp0VEn.jpg)

複数のDockerコンテナと接続し、ホストとのルーティングを初めからなくすことができる

本来brigeとhostのコンテナを繋ぐためにはホストマシンとのルーティングが必要

## ipvlan

接続時IPv4とIPv6の両方のアドレスを指定し、任意の数値にすることができる

主にポートフォワーディングとかに使う？（扱ったことがない）

## macvlan

![macvlan](https://i.imgur.com/SAjMLLW.jpg)

コンテナにMACアドレス（物理固有アドレス）を設定し、コンテナをネットワーク上の物理デバイスとして表示することができる。

物理ネットワークに直接接続されることが想定された古いアプリケーションを扱う場合などに使われる

# Docker Volume

## 概要

コンテナで生成され利用するデータは、コンテナ間でのやり取りは出来ず、一度削除したら消え、ホストからは操作できないものである

しかし、Docker Volumeを扱うことによってコンテナ間利用されるデータをマウント、永続的に保持することができる

**コンテナ外のストレージを利用する機能の総称をDocker Volumeと呼ぶ**

***

- Docker Volumeは共通で以下のような特徴がある
  - Docekr CLIコマンドやDocker APIを使って管理することができる
  - LinuxとWindows上のコンテナで動作する
  - 複数のコンテナ間で安全に共有することができる
  - Volumeを新たに生成すると、その内容はコンテナがあらかじめ用意していた内容になる


- 外部ストレージにマウントする手法として3種類存在する
  1. bind mount
  2. volume(匿名ボリューム,名前付きボリューム)
  3. tmpfs mount

`bind mount`と`volume`はホストのファイルシステム上に作成されるが、`tmpfs mount`はメモリ領域に作成される点に注意！

![volume](https://i.imgur.com/l46HwXZ.jpg)

## bind mount

ホスト側のディレクトリをコンテナ内のディレクトリと共有する

ほかのプロセスに干渉される可能性がある


## volume

Dockerの管理の元、ストレージ領域を確保する

コンテナ内での変更がホストマシンに影響しないため、基本はこれを使うこと

- 匿名ボリュームと名前付きボリュームが存在する
  - 匿名ボリューム
    - 作成する際に名前を作らず、任意のハッシュ値を名前として扱う
    - コンテナ停止と共に自動的に削除される
  - 名前付きボリューム
    - 作成する際に名前とタグを設定する。  
    - `-rm`オプションを使うことでコンテナ停止と共に自動的に削除される

## tmpfs mount

メモリ上にストレージを確保する。

- 基本的には一時的に避難されるような状況で使われる。
  - ホストまたはコンテナーの書き込み可能レイヤーに保持したくない機密ファイルを一時的に保存する場合など


***

Docker Volumeを利用するには2つのオプションが存在する

```dockerfile
-v
--mount
```

- 利用するときは基本どちらでもおｋ(公式は`-mount`を推奨している)
  - `-v`と`-mount`は同じ書き方ではない点に注意
  - 一部のオプションを利用したい時は`-mount`を利用する必要あり



# Dockerfileを扱う

コンテナを生成するためには`Dockerfile`と呼ばれるファイルを編集する必要がある

既存のイメージなどに追加でレイヤーを入れることでより多機能のコンテナの作成が可能！


## まずはDockerfileを作成する！

任意のディレクトリで`Dockerfile`(拡張子なし)を作成

![Dockerfileの作成](https://i.imgur.com/TcB0P8B.png)

## FROM:ベースイメージを作成

最初にベースとなるイメージを指定し、ここからコマンドを組み立てていく！

```dockerfile
FROM centos:centos7
```

- ここで指定するディストリビューションでそのあとのコマンドが変わってくるため注意！
  - ununtu > apt update
  - centOS > yum update


## RUN: 任意のコマンドを実行する

Linuxコマンドを実行し、レイヤーを追加していく！

vimを追加するには以下のような記述を行う
```dockerfile
RUN yum install -y vim
```

また、Javaをインストールしたい場合は、 [公式サイトからJavaのリンク](https://www.oracle.com/java/technologies/downloads/) を参照する

![公式サイトリンク](https://i.imgur.com/W9om6Ei.png)
```dockerfile
RUN yum -y install https://download.oracle.com/java/20/latest/jdk-20_linux-x64_bin.rpm
```

## WORKDIR: ワークディレクトリを追加する

操作するディレクトリの絶対パス（推奨）を指定する！

以降はそのディレクトリを基準にDockerfileに書いた操作が実行される。

```dockerfile
WORKDIR /usr/src
```

- このコマンドでは**存在しないパスを指定した場合は新規で作成される**
  - ディレクトリを作成するためには使わない

## レイヤーの確認
```Dockerfile:Dockerfile
FROM centos:centos7
RUN yum install -y vim　https://download.oracle.com/java/20/latest/jdk-20_linux-x64_bin.rpm

WORKDIR /usr/src
```

# コンテナの生成と停止

## imageを作成

`docker image build`を入力することイメージを作成できる！

Dockerfileがある場所で

```docker
docker image build --tag docker-java:app  .
```

- `tag` > コンテナの名前とタグを設定する


ビルドしたら`docker images`で実際に作られたか確認すること！


## runでコンテナを起動

`docker container run`でコンテナ生成と起動を同時に行うことができる

```dockerfile
docker container run -itd -v ${PWD}/src:/var/src docker-java:app 
```

- `itd` > 一度コンテナを起動したらそのままにする
- `v` > バインドマウントでDockerを同期

## stopでコンテナを停止

`docker container stop`でコンテナを停止させることが可能

```docker
docker container stop docker-java:app 
```

## pruneでDockerのお掃除

ビルド、イメージの作成を行っているとキャッシュが溜まるので適宜、クリアしておくこと

```docker
# 停止しているコンテナ、ネットワーク、ボリュームの削除
docker system prune 

# イメージの一括削除
docker image prune 
```

# Docker Compose

複数コンテナを同時に起動したいけど、毎回コンテナを起動するときに`docker container run`するのは大変・・・

そこで、Docker Composeを利用することで一回のコマンドで複数コンテナの起動、bridgeネットワークに接続が可能！

## Docker Composeとは

手っ取り早く、コンテナの起動を楽にするツールであり、イメージを作るツールではないこと！

既存のDockerfileに+`compose.yml(旧docker-compose.yml)`を使うことによって、複数の生成が可能になる

### compose.yml の作成

compose.yml(旧docker-compose.yml)を以下のような感じで記入する（一例）

```dockerfile:compose.yml
# バージョンの指定(現在はバージョン指定が推奨されていない！)
# version: '3.9'

# 大まかに作ったものを流していく工程
services:

# 1つ目のコンテナ
  db:
# Dockerfileの指定
    build: docker/postgresql

# 接続ポートの選択
    ports:
      - 5432:5432

# 環境変数の指定
    environment:
      POSTGRES_USER: root
      POSTGRES_PASSWORD: root

# bind mountの指定
    volumes: [ ./data:/var/lib/postgresql/data ]

# 2つ目のコンテナ
  java:

# Dockerfileの指定
    build: docker/java

# 接続ポートの選択
    ports:
      - 9080:9080

# 一度起動したら、そのままにする
    tty: true

# bind mountの指定
    volumes: [ ./src:/usr/src:cached ]
```

書き込みが完了したら`docker-compose`コマンドでビルドができる

```
# ビルドする例
docker-compose up -d
```

## networkの秘密

- Docker composeは同時に立てた時に自動的にbrigeネットワークを繋いでくれる
 - そのため、立てる際にネットワークオプションを省いても接続が可能になる


- しかし、デフォルトネームで固定されるため、別途ネットワークオプションを使いたいときはdocker-compose.ymlにnetworkオプションを追記すること



# 猿でもできるコンテナデータベース

compose.ymlでファイルを作成

```:compose.yml
services:

  # データベースイメージ(MySQL)
  db:
    image: mysql:8.0.21 
    command: --default-authentication-plugin=mysql_native_password

    restart: always

    #ログイン情報
    environment:
      MYSQL_ROOT_PASSWORD: root
      MYSQL_DATABASE: hoge 
      MYSQL_USER: hoge     
      MYSQL_PASSWORD: hoge

    #データベース接続ポート
    ports:                 
      - 3306:3306          

  # 管理者ログインイメージ
  adminer:
    image: adminer:latest
    restart: always
    ports:
      - 8080:8080
```

`docker compose up -d`でコンテナをたてれば完了

下記リンクに接続するだけでデータベース編集画面に直行
http://localhost:8080/

![編集画面](https://i.imgur.com/RIHUBcB.png)

# devcontainer

VSCodeの拡張機能でdevcontainerと呼ばれるツールがある

- 機能の役割としては
  - Docker composeをコンテナを開いた際に自動的に実行してくれる
  - 構成ファイルをdocker-compose.ymlから読み込むことができる
  - 拡張機能をコンテナを開く際に選択できる？

- こいつ単体だとコンテナのシステム掃除（prune）ができないため、裏でWSLを起動して適宜運用するのがいい？