実例で理解する【Docker 前編】

# はじめに

今回は、とあるイベント用に作成した`docker-compose.yml`と`Dockerfile`について解説していきます。

前編：`docker-compose.yml`編です。

全部分、なぜこう書いたのかを中心に書きますので、何か学びになるんじゃないかな～

https://github.com/Kohki-Takatama/kantan_linebot

# `docker-compose.yml`

```yml:docker-compose.yml
services:
  rails:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    tty: true
    stdin_open: true
    volumes:
      - .:/linebot
      - bundle_data:/usr/local/bundle:cached
      - node_modules:/app/node_modules
  ngrok:
      image: ngrok/ngrok:latest
      environment:
        NGROK_AUTHTOKEN: ${NGROK_AUTH}
      command: ["http", "rails:3000"] # "rails"サービスはデフォルトで3000番ポートにて稼働
      depends_on:
        - rails
      ports:
        - "4040:4040"
volumes:
  bundle_data:
  node_modules:
```
<br>
上から順に見ていきましょう。

## サービスの定義
```yml
services:
  rails:
    # 略
  ngrok:
    # 略
```
サービス（ざっくりいうとコンテナ）を定義しています。

`rails` `ngrok`というのはサービスの名前です。
`web`とか`db`とかが一般的ですね。名前なので、極論なんでもいいです。
`hoge`でもいいし、`aho`でもいいです。未来の自分が混乱するだけなので。
## `rails`サービスの中身を定義
```yml
rails:
  build: .
  command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
  tty: true
  stdin_open: true
  volumes:
    - .:/linebot
    - bundle_data:/usr/local/bundle:cached
    - node_modules:/app/node_modules
```

### `build: .`
「同じ階層の`Dockerfile`を使ってください」ということです。
イメージ（設計図）などもそこにあります。

### `command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"`
2つのコマンドを実行しています。
1. `tmp/pids/server.pid`を削除：このファイルがあると「起動していないサーバーが起動していることになる」ことがあるため（特に「予期せぬ終了」「クラッシュ」した後に起こる）
2. `bundle exec rails s -p 3000 -b '0.0.0.0'`：railsサーバーを起動。railsサービスの3000番ポートで、全ネットワーク（0.0.0.0）からのアクセスを受け取る。

## `tty: true` / `stdin_open: true`
簡単に言えば、
- `tty: true`：ターミナルエミュレーターでログの情報が増える
- `stdin_open: true`：`rails console`を使うのに必要な設定

特に、`tty: true`によってサーバーへのアクセスログやエラーログが出るので、必須です。

## `volumes`の利用

ボリュームとは「コンテナを落としてもデータが残るようにする」設定です。

#### `.:/linebot`
コンテナの`linebot`ディレクトリを、ローカル（ホストマシン）のルートディレクトリにマウント（接続）します。これにより、どのタイミングで作成したコンテナであれ、`linebot`ディレクトリ以下はローカルのファイルを参照するようになります。

#### `bundle_data:/usr/local/bundle:cached`
後述しますが、`bundle_data`は名付けたものなので、名前はなんでもいいです。今回は`bundle_data`と名付けました。
やってることは、インストールした`gem`を`bundle_data`という名前で保存しています。
どのタイミングで作成したコンテナであれ、`gem`はここを参照し、使用します。

この設定がないと、例えば
1. コンテナ①で`rails new`する
2. コンテナ①を落とす
3. コンテナを起動する（コンテナ①はもうないので、コンテナ②が起動される）
4. コンテナ②は`rails new`したときの`gem`を持たないため、`/linebot`では`gem`があることになってるのに、実際はない⇒エラー、になる

といったことが起こります。

#### `node_modules:/app/node_modules`
上記に同じです。`node_module`でも同様のことが起こるので。

## ngrokサービスの中身を定義
```yml
ngrok:
    image: ngrok/ngrok:latest
    environment:
      NGROK_AUTHTOKEN: ${NGROK_AUTH}
    command: ["http", "rails:3000"] # "rails"サービスはデフォルトで3000番ポートにて稼働
    depends_on:
      - rails
    ports:
      - "4040:4040"
```

### `image: ngrok/ngrok:latest`
`ngrok/ngrok:latest`をイメージ（設計図）として使用します。

### `environment: NGROK_AUTHTOKEN: ${NGROK_AUTH}`
ngrokは認証トークンが必要なので、その設定をしています。`${NGROK_AUTH}`は`.env`からの参照です。

### `command: ["http", "rails:3000"]`
`rails`サービスの3000番ポート（つまりrailsサーバー）をngrokを使って公開してね。HTTPトンネルで。です。

### `depends_on: rails`
`rails`サービスに依拠するので、先に`rails`サーバーを立ち上げてね。

### `ports: "4040:4040"`
`localhost`の`4040`ポートで、このコンテナの`4040`ポートにアクセスできるようにしてね。

# `volumes`の定義

```yml
volumes:
  bundle_data:
  node_modules:
```

先ほども書きましたが、ボリュームとは「コンテナを落としてもデータが残るようにする」設定です。

ここでは、`bundle_data`と`node_modules`の2つのボリュームを用意してね！と言っています。
`bundle_data` / `node_modules`という名前はわかりやすいようこう名付けているだけで、別に`1`と`2`でも極論構いません。

# おわりに

後編：`Dockerfile` 投稿しました。

https://qiita.com/kohki_takatama/items/dde30b65eb0d67e4829b

ご指摘・ご質問あればぜひお願いします。