Docker 入門 １項目ずつ理解する


# Dockerfileを理解する
Dockerfileでよく使う項目を学習していきます。

公式ページで詳しく記載されているのは、下記になります。
Dockerfileの辞書は[こちら](https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/)。
自分が知りたい項目があれば、一度確認すると良いでしょう。

では、一緒にみていきましょう。


### FROM

[FROMの公式リファレンスはこちら](http://docs.docker.jp/engine/reference/builder.html?highlight=env#from)

``FROM``ではインストールしたいrubyのバーションを指定します。
実際にrubyをインストールするには、複数のコマンドを実行しなければならないが、それらをセットにした``image``を利用して、1行でrubyをインストールする。

例えば、rubyのをインストールしたい場合、

```dockerfile:dockerfile
FROM ruby
```
と記述するだけ、rubyの環境構築ができる。
さらに、rubyのバージョンを``2.5.1``と指定したい場合は、

```dockerfile:dockerfile
FROM ruby:2.5.1
```
と記述してバーションを指定する。
Dockerでは``image``のバージョンのことを``tag``と呼びます。
（厳密には異なりますが、わかりやすく伝えるためにそう伝えています）

基本的な下記の公式になります。

```dockerfile:Dockerfile
FROM image:tag（バージョン）
#imageはrubyやphpなどの言語などを指定する。
```

基本的に``image``はrubyやphpなどの言語や``mysql``などを指定しますが、imageを自作することも可能です。imageをカスタマイズして自作すれば、より簡略して環境構築ができます。会社で用意された``image``を利用して開発することもあるようです。

今回はruby:2.5.1を利用したいので、下記の記述で進めていきます。

```dockerfile:Dockerfile
FROM ruby:2.5.1
```



### RUN
``RUN``では実行したいコマンドを記述します。

```dockerfile:RUN
FROM ruby:2.5.1

RUN 実行したいコマンド
```
Node.jsやyarnなど必要なものをインストールしたり、bundle installなどコンテナをbuildした際に実行させたいコマンドを記述します。


```dockerfile:Dockerfile
FROM ruby:2.5.1

## ディレクトリ（ファルダ）を作成する。
RUN mkdir /webapp

## bundle installでgemを反映させる(build直後）。ターミナルでやる場合, $ docker-compose run web bundle installのようにします（後述します）。
RUN bundle install


## 必要なものをインストール
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
    libpq-dev \
    git \
    vim 


##yarnのインストール
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn


##Nodejsをバージョン指定してインストール
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && \
    apt-get install nodejs
```

``apt-get``はLinuxコマンドです。macでは利用できませんが、Dockerのコンテナ上では利用できます。


### ENV

```dockerfile:Dockerfile
ENV 変数 値 
# $変数で利用可能
```
例えば、フォルダ``app_name``を作成し、これを変数``APP``と定義したい場合、

```dockerfile:DockerfileのENV例
FROM ruby:2.5.1

RUN mkdir /app_name
ENV APP /app_name
```
と記述する。
これで、`` APP = app_name`` と定義される。

定義した変数を利用したい場合、``$``マークをつける。
今回の例だと``APP``を利用するには、``$APP ``と記述する。


```dockerfile:DockerfileのENV例
FROM ruby:2.5.1

RUN mkdir /app_name
ENV APP /app_name
WORKDIR $APP   # 変数 APPを指定している
```


[ENVの公式リファレンス](http://docs.docker.jp/engine/reference/builder.html?highlight=env#env)

### WORKDIR

```dockerfile:Dockerfile
FROM ruby:2.5.1

RUN mkdir /app_name
ENV APP /app_name
WORKDIR $APP
```

ワーキングディレクトリを定義する。
ワーキングディレクトリとは、作業用フォルダを意味する。
つまり``WORKDIR``では、アプリ開発するフォルダを指定します。
Dockerなしだと``$rails new アプリ名``でワーキングディレクトリを作成&指定されていたが、Dockerでは、``RUN mkdir /フォルダ名``でフォルダを作成し、それを``WORKDIR /フォルダ名``で指定する必要があります。

[WORKDIRの公式リファレンス](http://docs.docker.jp/engine/reference/builder.html?highlight=env#workdir)
そもそもワーキングディレクトリとは？
>作業用フォルダ = カレントディレクトリ = .
>カレントディレクトリ（英語: current directory、現行ディレクトリ。つまり現在開いているフォルダ）とは、現在の位置であるディレクトリ（フォルダ）のことである。作業フォルダ（ワーキングディレクトリ）とも呼ばれることがある。


### ADD
```dockerfile:Dockerfile
ADD [追加したいもの] → [追加したい場所]
```

``左にあるファイル``を``右の場所``に追加する。


フォルダ内にファイルを追加したい場合は、``フォルダ名/``と記述する。
ファイルを上書きさせたい場合は、``フォルダ名/ファイル名``と記述する。

```dockerfile:testファイルをDirフォルダに入れる場合
ADD test Dir/
```

```dockerfile:testの内容をDirフォルダのtestに上書き
ADD test Dir/test
```


```dockerfile:Railsでよく使う例
WORKDIR /webapp

ADD ./Gemfile /webapp/Gemfile   

ADD ./Gemfile.lock /webapp/Gemfile.lock
```

使うことはないが、知識として知っておきたいこと。


```dockerfile:条件を指定して取り込む
ADD hom* /mydir/        # "hom" で始まる全てのファイルを追加
ADD hom?.txt /mydir/    # ? は１文字だけ一致します。例： "home.txt"
```

[ADDの公式リファレンス](http://docs.docker.jp/engine/reference/builder.html?highlight=env#add)



### その他は辞書で調べてね
Dockerfileの辞書は[こちら](https://matsuand.github.io/docs.docker.jp.onthefly/engine/reference/builder/)。
[COPYとADDの違い](https://qiita.com/YumaInaura/items/1647e509f83462a37494)

## 実例

```dockerfile:Dockerfile(ruby)

FROM ruby:2.5.1 #ruby 2.5.1のimageを利用

# RUNはコマンド実行を意味する。必要なものをインストール
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
    libpq-dev \
    git \
    vim 

#yarnのインストール
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn

#Nodejsをバージョン指定してインストール
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && \
    apt-get install nodejs

#Dockerでアプリ開発をするディレクトリを作成する。
RUN mkdir /webapp

#作成したディレクトリを作業用のディレクトリに指定する。
WORKDIR /webapp

#値は1でなくても何でも良いです。dockerfileとかだとDontWarnにしてる例が多いです。
ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn

#GemfileとGemfile.lockを追加
ADD ./Gemfile /webapp/Gemfile
ADD ./Gemfile.lock /webapp/Gemfile.lock

#bundlerをインストール（途中からDockerを導入したため、bundlerを一致させないとエラーが発生するため）
RUN gem install bundler -v 2.1.4

#gemをインストールするために、bundle install
RUN bundle install

#現在のディレクトリ に /webappの内容を追加
ADD . /webapp

#puma.rb用のsocketsを作成
RUN mkdir -p tmp/sockets
```
よく使う内容を把握しておけば、意味は理解できるはず。




# docker-compose.ymlを理解する
docker-compose.ymlの辞書は[こちら](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/)。

```:読み込み
docker-compose.yml → Dockerfile
```

### version
まずdocker-composeのバージョンを指定します。
どんなバージョンがあるかを知りたい方は[こちら](https://docs.docker.com/compose/compose-file/)

```yaml:docker-compose.yml

#version:3のdocker-composeを利用
version: "3"
```


### services
db用, アプリ用などに分ける

```yaml:docker-compose.yml
version: "3"
services:
  db:
    #dbに関することを書く
    #この例だとdbにしているが、名前は自由

  app:
    #appのことを書く
    #この例だとappにしているが、名前は自由（よくある例:web)

  nginx:
    #本番環境で公開するために必要なnginxについて書く
    ##この例だとnginxにしているが、名前は自由（よくある例:web)
```

#### コマンドの実行
サービスを分けることによって、コマンドをdbに対してのコマンドなのか、appに対してのコマンドなのか使い分けることができる。

```:コマンドの公式
$ docker-compose run サービス名 コマンド
```

``run``は実行するの意味があるので、``docker-compose.yml``の``サービス名``に対して``コマンド``を``run（実行）``の意味になる

dbに対してコマンド実行したい場合、

```yml:docker-compose.yml
version: "3"
services:
  db:   #ここに対して対して、コマンドを実行したい


  app:


  nginx:

```

サービスがdbなので

```:dbに関するコマンド処理を実行
$ docker-compose run db コマンド
```
となる。

``run``は実行するの意味があるので、``docker-compose.yml``の``db``に対して``コマンド``を``run（実行）``の意味になる


```:具体例：dbにコマンド処理（mysql）
$ docker-compose run db mysql -u [ユーザー名] -p
```

続いて、appに対してコマンド処理をしたい場合

```yml:docker-compose.yml
version: "3"
services:
  db:


  app:      #ここに対して対して、コマンドを実行したい


  nginx:

```

サービス名がappなので、

```:appに対してコマンド実行
 $ docker-compose run app コマンド
```
となります。
``run``は実行するの意味があるので、``docker-compose.yml``の``app``に対して``コマンド``を``run（実行）``の意味になる

```:例：appにコマンド処理
#appに対してコマンド実行したい（imageがrubyなので、railsに関するコマンドはここ）
$ docker-compose run app bundle install
$ docker-compose run app rake db:create
$ docker-compose run app rake db:migrate


#今回のdocker-compose.ymlがappにしているだけで、もしも[web]としていたら、下記になる
$ docker-compose run web bundle install
$ docker-compose run web rake db:create
$ docker-compose run web rake db:migrate
```

特にコマンドはないですが、nginxに対してコマンド処理をしたい場合

```yml:docker-compose.yml
version: "3"
services:
  db:


  app:


  nginx:    #ここに対して対して、コマンドを実行したい


```

```:nginxに対してコマンド
$ docker-compose run nginx [コマンド]
```

``run``は実行するの意味があるので、``docker-compose.yml``の``nginx``に対して``コマンド``を``run（実行）``の意味になる

### build: 読み込むDockerfileを指定する
先ほど記述した``db``や``app``といったservicesごとに、どのDockerfileを読み込むのか指定する必要がある。
``build: ``を記述して、どのDockerfileを読み込むのか指定します。


#### context: Dockerfileを持つフォルダを指定する
フォルダを指定してDockerfileを読み込む場合は、``build: ``下の``context:``にパスを記述します。
では例をみていきましょう。

##### ワーキングディレクトリ直下にある場合

```
/webapp（アプリ名）
├── docker-compose.yml
├── Dockerfile        (これを指定したい）
├── Gemfile
└── Gemfile.lock
```

カレントディレクトリである``webapp``フォルダ内にDockerfileがあります。
カレントディレクトリのパスは``.``なので、下記のように指定します。

```yml:docker-compose.yml
version: "3"
services:
  app:
    build:
      context: .  #カレントディレクトリ上のDockerfileを読み込む
```
これでサービス``app``はカレントディレクトリにある``Dockerfile``を読み込みます。
デフォルトで``Dockerfile``を読み込む設定になっているので、ファイル名は指定しなくて大丈夫です。

```yaml:contextを省略した場合
version: "3"
services:
  app:
    #webapp / Dockerfile　
    #直下にあるので
    build: .                #appはカレントディレクトリ（.）にあるDockerfileを使う
```
よくある例として、``build: .``と``context:``を省略している例があります。
やっていることは同じなので、``context: ``を理解すれば大丈夫です。

では、さらに例をみて理解を深めましょう。



##### フォルダの中にDockerfileがある場合
```
/webapp
├── containers
│   └── nginx
│       ├── Dockerfile （nginx用:ここを指定したい）
│       └── nginx.conf
├── docker-compose.yml
├── Gemfile
└── Gemfile.lock
```

この例では、``webapp/containers/nginxフォルダ``内のDockerfileを利用したいので、``containers/nginx``を指定します。


```yaml:docker-compose.yml
version: "3"
services:
  nginx:
    build:
      context: containers/nginx #nginフォルダ内のDockerfileを使う
```


##### Dockerfileが不要な場合（imageだけで十分）
下記のように、imageしかDockerfileに記述する必要がない場合、

```dockerfile:Dockerfile(mysql用）

FROM mysql:mysql:5.6.47

```
わざわざDockerfileを作成せずに、直接docker-compose.ymlにimageを記述すればよい。

```:Dockerfileなし
/webapp
├── docker-compose.yml
├── Gemfile
└── Gemfile.lock
```

サービス``db``は``image: mysql:5.6.47``を利用したい場合、

```yml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47  #imageを直接指定
```

##### 応用:サービスごとにDockerfile（複数）を使い分ける
これまで学んだことを組み合わせて、
サービス``app``用のDockerfile（image: ruby）と
サービス``nginx``用のDockerfile（image: nginx)を使い分けましょう。
サービス``db``はimageを直接指定します。

```:フォルダの階層
/webapp
├── containers
│   └── nginx
│       ├── Dockerfile （nginx用）
│       └── nginx.conf
├── docker-compose.yml
├── Dockerfile　       （app用）
├── Gemfile
└── Gemfile.lock
```
下記のdocker-composeのdbに対して、image: を指定している。
このように、Dockerfileを作成しなくてもimageを指定することが可能

```yaml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47

  app:
    build: .

  nginx:
    build:
      context: containers/nginx
```
#### dockerfile:ファイル名がDockerfileではない場合
デフォルトでは、``context:``で指定したフォルダ内の``Dockerfile``を見つけてbuildします。
ここでは、ファイル名が``Dockerfile``ではない場合を解説します。

***例えば、nginx用のDockerfileを``Dockerfile-nginx``というファイルだった場合***
``dockerfile: ``を記述してDockerfileとして読み込みたいファイルを指定します。
[公式リファレンス](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#dockerfile)


```yml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47

  app:
    build: .

  nginx:
    build:
      context: containers/nginx
      dockerfile: Dockerfile-nginx #フィル名を指定する
```
### depends_on:
サービスとサービスを紐付ける

ここまで学んだ、下記だとdb, app, nginxが繋がっていません。

```yml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47

  app:
    build: .

  nginx:
    build:
      context: containers/nginx
```
今回は、それぞれのサービスを紐づけます。

##### dbとappを紐付ける
app単体だと,dbのデータを保持できないので、紐付けます。

```yml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47

  app:
    build: .
    depends_on: #依存する
      - db      #dbを紐付け
```

これにより、``app``を起動すると``db``も起動させます。
``app``系のコマンド実行する際は、DBの情報が必要になるので、``app``に``db``を紐付けています。
ターミナルコマンドを見て、より理解を深めましょう。

```:appに対するコマンド
$ docker-compose run app rails g model message
```
上記のコマンドを実行すると
![スクリーンショット 2020-08-10 12.05.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455403/8f5814f0-7f88-5a22-e212-2f881b7dd512.png)

```:dbが起動している箇所
Starting web-share_db_1 ... done # dbが起動している
```
と表示されており、appのコマンド実行前に ``db``が起動しているとわかります。


##### appとnginxを紐付ける

```yml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47

  app:
    build: .
    depends_on: #依存する
      - db      #dbと紐付け

  nginx:
    build:
      context: containers/nginx
    depends_on: #依存する
      - app     #appと紐付け
```
nginxを起動すると、``app``も起動する。``app``が起動するので、``db``も起動します。

``db ← app ← nginx ``

では、``$ docker-compse up``実行時に、の動作をみましょう。

![スクリーンショット 2020-08-10 14.50.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/455403/8f62f431-7145-bf14-5223-909d592a0755.png)
上記の写真をみてみると

```
$ docker-compose up
Starting web-share_db_1 ... done     #dbが起動
Starting web-share_app_1 ... done    #appが起動
Starting web-share_nginx_1 ... done  #nginxが起動

#紐付けされる
Attaching to web-share_db_1, web-share_app_1, web-share_nginx_1
```
それぞれのサービスが起動し、Attachingで紐づいていることがわかる。


**Nginxとappを紐付ける理由**

Nginxを利用していない場合、``localhost:3000``というように``:3000``をurlに含めないとサイトを見れません。
>example.comの場合、``example.com:3000``でなければサイトにアクセスできない。


Nginxでアプリを起動すると80ポートで起動するので、
``localhost``だけでサイトを見れます。``:3000``のようなポート番号を指定する必要はありません。
>example.comの場合、``example.com``でアクセスできる。


ポート番号なしにアクセスできるようにするために、``nginx``に``app``を紐付けています。


##### 複数のサービスを紐付ける

複数のサービスの紐付けたい場合があると思います。
その際は下記のdocker-compose.ymlのように記述します


```yml:docker-compose.yml
version: "3.8"
services:
  web:
    build: .
    depends_on:
      - db
      - redis
  redis:
    image: redis
  db:
    image: postgres
```

上記の例では、サービス``web``は、``redis``と``db``の二つの依存関係を同時に保有しています。
このように複数のサービスと紐付けることも可能です。



[公式リファレンス](https://matsuand.github.io/docs.docker.jp.onthefly/compose/compose-file/#depends_on)


### volumes: （バックアップ）
volumes:でデータのバックアップを作成し、別のコンテナでもデータを使い回します。
これをしておかないと、Dockerfileを更新して``$ docker-compose build`` し直す際に、DBの情報が消えます。

>新品のiPhoneには、何もデータがないように
>新しく作成したコンテナは、前のコンテナの情報は入っていません。


buildするたびにDBの情報を作り直すのは、非常にめんどくさいので、バックアップを作成しておき、それをコンテナに使いまわします。
そのバックアップを``volume``と呼びます。

#### volumeの必要性

現実の例
> 新しいPCを購入した = 前のPCのデータは当然ない
なので、前のPCで作成したバックアップデータを新しいPCに入れる。

Dockerをbuildし直した（コンテナを作り直す）
> 新しいコンテナ = 前のコンテナのデータは当然ない
> バックアップデータであるvolumeを新しいコンテナに紐付ける

#### volumesを記述する

```yml:docker-compose.yml
version: "3"
services:
  db:

  app:

  nginx:

volumes:
  #ここにvolumeをセットする。
  #volumeの名前は自由
  mysql_data:
  gem_data:
  public-data:
  tmp-data:
  log-data:
```
##### サービスとvolumesを紐付ける

######dbのvolume名を``mysql_data``として場合、

```yml:docker-compose.yml
version: "3"
services:
  db:
    volumes:
      - mysql_data: #使用するvolumeを指定


volumes:
  mysql_data: #これを紐付け

```
mysqlを利用するに当たって、決まった階層があるので、それを指定します。

```yml:docker-compose.yml
version: "3"
services:
  db:
    volumes:
      - mysql_data:/var/lib/mysql
      #         mysqlは読み込み場所が決まっているので（/var/lib/mysql）、
      #         保存する階層を指定している


volumes:
  mysql_data: #これを紐付け

```
これでbuildしてコンテナを作り直してもmysqlのデータは保持される。


######appのgemをvolumesで保持する。

```
$docker-compose build
```

```yml:docker-compose.yml
version: "3"
services:
  db:
    volumes:
      - mysql_data:/var/lib/mysql

  app:
    volumes:
      - gem_data:/usr/local/bundle

  nginx:

volumes:
  mysql_data:
  gem_data: #これをappに紐付ける
```

###### 実例

```yml:docker-compose 
version: "3"
services:
  db:
    image: mysql:5.6.47
    volumes:
      - mysql_data:/var/lib/mysql

  app:
    build:
      context: .
    volumes:
      - .:/webapp
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
      - log-data:/webapp/log
      - gem_data:/usr/local/bundle
    depends_on:
      - db

  nginx:
    build:
      context: containers/nginx
    volumes:
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
    depends_on:
      - app

volumes:
  mysql_data:
  gem_data:
  public-data:
  tmp-data:
  log-data:
```

###  command: docker-compose up時のコマンド
``command:``は$docker-compose up時のコマンドを処理します。

基本的にはサーバー起動関連のコマンドを記述します。




```yml:rails　sで起動
version: '3'
services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'"
```


```yml:pumaで起動
version: "3"
services:
  app:
    build:
      context: .
    command: bundle exec puma -C config/puma.rb
    volumes:
```

```yml:unicornで起動
version: "3"
services:
  app:
    build:
      context: .
    command: bundle exec unicorn_rails -c config/unicorn.rb
    volumes:
```

```yml:rails　s（邪魔なファイルを削除）
version: '3'
services:
  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
```

この``command:``により、``$docker-compose up``時に``$rails s ``などを実行されて、アプリが起動される。

###portを指定する
ポートを指定したい場合は下記``port:``を利用します。


```yml:docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: 'password'
    ports:
      - "4306:3306"
    volumes:
      - mysql_data:/var/lib/mysql
      

  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/app_name
      # volumeを使用してbundle installしてきたものを永続化
      - gem_data:/usr/local/bundle
    ports:
      - "3000:3000"
    depends_on:
      - db
volumes: 
  mysql_data:
  gem_data:
```




### environment: 環境変数
環境変数を指定してbuildしたいとき、どのようにすればよいのか解説していきます。

[公式リファレンスはこちら（必読）](https://matsuand.github.io/docs.docker.jp.onthefly/compose/environment-variables/)

#### mysqlの例

````yml:mysqlの環境変数を定義
version: "3"
services:
  db:
    image: mysql:5.6.47
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
```

上記の例でbuildすると、usernameとpasswordが反映されます。

```yml:database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root     #反映
  password: password #反映
```
#### 本番環境で実行したい場合

```yml:docker-compose.yml
version: "3"
services:
  app:
    build:
      context: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
    environment:
      RAILS_ENV: production #本番環境で$docker-compose up。指定なしの場合、developmentで実行
```

#### envファイルを利用して、値を隠したい場合

``env_file``オプションを利用して、envファイルを読み込みます。

```yml:envファイルが複数の場合
env_file:
  - ./common.env
  - ./apps/web.env
  - /opt/runtime_opts.env

```

```yml:envファイルが１つの場合
env_file: .env
```

これで、envファイルを読み込む方法がわかりました。

では、どのように.envファイルで環境変数を定義すれば良いのか？
確認していきましょう！

````yml:mysqlの環境変数を定義
version: "3"
services:
  db:
    image: mysql:5.6.47
    environment:
      MYSQL_ROOT_PASSWORD: password #隠したい
      MYSQL_DATABASE: root          #隠したい
```

envファイルを用意して、環境変数を定義

```:.env
MYSQL_ROOT_PASSWORD=password
MYSQL_DATABASE=root
```

では、これを読み込みます

````yml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47
    env_file: .env
    environment:
      MYSQL_ROOT_PASSWORD: password #隠したい
      MYSQL_DATABASE: root          #隠したい
```

``env_file:``で読み込んだので、``environment:``は不要になりました。
なので、``environmet:``を削除します

````yml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47
    env_file: .env
```
だいぶスッキリしましたね！

注意事項は``env_file``よりも、``environment:``の方が優先されます。

たとえば

```:.env
MYSQL_ROOT_PASSWORD=pass12345
MYSQL_DATABASE=mysql_user
```

````yml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47
    env_file: .env                  
    environment:
      MYSQL_ROOT_PASSWORD: password #優先＝上書き
      MYSQL_DATABASE: root          #優先＝上書き
```
上記のような場合は、``environment``の内容に上書きされます。



#### docker-compose.ymlで環境変数を定義しない場合

``environment:``を利用しなくても、実行可能です。
環境変数をdocker-compose.ymlで定義しない場合、

``docker-compose run -e 環境変数=値 サービス名``で実行可能です。言うまでもなく、オプション``-e``は``environment``のeです。

```:例
$ docker-compose run -e RAILS_ENV=production app bundle install
$ docker-compose run -e RAILS_ENV=production app rake db:create
$ docker-compose run -e RAILS_ENV=production app rake db:migrate

$ docker-compose run -e RAILS_ENV=production web bundle install
$ docker-compose run -e RAILS_ENV=production web rake db:create
$ docker-compose run -e RAILS_ENV=production web rake db:migrate
```

じゃあ、なぜdocker-compose.yml上で定義しなければいけないのか？

それは$docker-compose up には、環境変数を指定するオプションが無いからです。

```
$ docker-compose up -e RAILS_ENV=production
> -bash: $: command not found

```

#### 本番環境と開発環境で切り分ける場合

方法は２つ

#####方法1:環境変数を本番環境、開発環境で違うようにする。 

docker-compose.ymlを本番環境（EC2）上で編集し、環境変数を定義する

```yml:開発環境のdocker-compose.yml
version: "3"
services:
  app:
    build:
      context: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
```



```yml:本番環境のdocker-compose.yml
version: "3"
services:
  app:
    build:
      context: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
    environment:            #EC2上でvimを使って追記
      RAILS_ENV: production #EC2上でvimを使って追記
```

env_fileでもよいでしょう

```yml:本番環境のdocker-compose.yml
version: "3"
services:
  app:
    build:
      context: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
    env_file: .env #EC2上でvimを使って追記
```

```:.env
RAILS_ENV=production
```

#####方法2

docker-compose.ymlを２つ作成し、開発環境用、本番環境用に使いわける。

``docker-compose -f ファイル名 up ``というように、``-f``オプションを利用して、別のdocker-compose.ymlを指定することが可能です。

[参考：本番環境での Compose の利用 | Docker ドキュメント]
(https://matsuand.github.io/docs.docker.jp.onthefly/compose/production/)

例えば、

開発環境用``docker-compose.yml``と本番環境用``docker-compose-production.yml``を用意した場合、

基本的に読み込まれるのは``docker-compose.yml``です。
名前が異なるので``docker-compose-production.yml``は読み込まれません。
しかし、``-f``オプションを利用して、読み込むファイルを指定すれば、``docker-compose-production.yml``でも読み込みされます。

```通常はdocker-compose.ymlで実行
$ docker-compose up 
```

```docker-compose-production.ymlで実行
$ docker-compose -f docker-compose-production.yml up 
```

他にも
docker-compose.ymlに本番環境設定だけを追加したい場合、

```
$ docker-compose -f docker-compose.yml -f docker-compose.production.yml up
```
上記のように``-f``オプションを二つ利用して、合算させることも可能です。
ただし、コマンドは少しが長くなりますけどね。



## 実例

```yaml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
    ports:
      - "4306:3306"
    volumes:
      - mysql_data:/var/lib/mysql

  app:
    build:
      context: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
    volumes:
      - .:/webapp
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
      - log-data:/webapp/log
      - gem_data:/usr/local/bundle
    depends_on:
      - db
    tty: true
    stdin_open: true

  nginx:
    build:
      context: containers/nginx
    volumes:
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
    ports:
      - 80:80
    depends_on:
      - app

volumes:
  mysql_data:
  gem_data:
  public-data:
  tmp-data:
  log-data:

```


# 実際に試みる2

```yml:docker-compose.yml
version: "3"
services:
  db:
    image: mysql:5.6.47
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
    ports:
      - "4306:3306"
    volumes:
      - mysql_data:/var/lib/mysql

  app:
    build:
      context: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec puma -C config/puma.rb"
    volumes:
      - .:/webapp
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
      - log-data:/webapp/log
      - gem_data:/usr/local/bundle
    depends_on:
      - db
    tty: true
    stdin_open: true

  nginx:
    build:
      context: containers/nginx
    volumes:
      - public-data:/webapp/public
      - tmp-data:/webapp/tmp
    ports:
      - 80:80
    depends_on:
      - app

volumes:
  mysql_data:
  gem_data:
  public-data:
  tmp-data:
  log-data:

```



```dockerfile:Dockerfile（app用）
FROM ruby:2.5.1

RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
    libpq-dev \
    git \
    vim 

#yarnのインストール
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn

#Nodejsをバージョン指定してインストール
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash - && \
    apt-get install nodejs

RUN mkdir /webapp
WORKDIR /webapp
ENV APT_KEY_DONT_WARN_ON_DANGEROUS_USAGE=DontWarn

ADD ./Gemfile /webapp/Gemfile
ADD ./Gemfile.lock /webapp/Gemfile.lock

RUN gem install bundler -v 2.1.4
RUN bundle install
ADD . /webapp
RUN mkdir -p tmp/sockets
```

```dockerfile:containers/nginx/Dockerfile(nginx用）
FROM nginx:1.15.8

RUN rm -f /etc/nginx/conf.d/*

ADD nginx.conf /etc/nginx/conf.d/webapp.conf

CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
```

```conf:nginx.conf

upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name example.com [or 192.168.xx.xx [or localhost]];

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  root /webapp/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @webapp;
  keepalive_timeout 5;

  location @webapp {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://webapp;
  }
}
```

```ruby:puma.rb
# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
threads threads_count, threads_count

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port        ENV.fetch("PORT") { 3000 }

# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }

# Specifies the `pidfile` that Puma will use.
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked webserver processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!

# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart

app_root = File.expand_path("../..", __FILE__)
bind "unix://#{app_root}/tmp/sockets/puma.sock"

stdout_redirect "#{app_root}/log/puma.stdout.log", "#{app_root}/log/puma.stderr.log", true
```

#nginxの設定について

ここでは、nginxの詳細設定は解説しませんが、参考になるリンクを貼っておきます。
[Nginx設定のまとめ](https://qiita.com/syou007/items/3e2d410bbe65a364b603)
この記事を辞書代わりに使うと理解が早まるでしょう。




## upstream

[socketとは](https://qiita.com/megadreams14/items/32a3eed4661e55419e1c)

```yml:docker-compose.yml
version: "3"
services:
  app:
    build:
      context: .
    command: bundle exec puma -C config/puma.rb
```

docker-compose.ymlの``command: ``において、``bundle exec puma -C config/puma.rb``が実行されいている。

これはpumaによって起動するコマンドだが、その設定は``-C``オプションにより``config/puma.rb``ファイルより読み込まれる。


puma.rbでpuma.sockを作成する


```ruby:puma.rb
bind "unix://#{app_root}/tmp/sockets/puma.sock"
```

``puma.sock``は、Nginxとソケット通信をする際に必要になるファイルです。



puma.rbで作成したpuma.sockをnginx.confに読み込む
``upstream``でsocketを指定します。

```:nginx.conf

upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}
```
[unixドメインソケット](https://qiita.com/toshihirock/items/b643ed0edd30e6fd1f14)

## proxy_pass

``upstream``を``proxy_pass``で指定する。

```:nginx.conf

  upstream puma {
    server unix:///usr/local/var/work/app-name/tmp/sockets/puma.sock;
  }

  server {
    location @puma {
      proxy_pass http://puma; #ここ
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
    }
  }
```

例えば、upstreamがwebappだった場合

```:nginx.conf

upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}
server {
  location @webapp {
    proxy_pass http://webapp;
  }
}
```

## listen 80

```:nginx.conf
upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}
server {
  listen 80;
  location @webapp {
    proxy_pass http://webapp;
  }
}


```

``listen 80;``でport:80を指定している。

[【3分で把握】ポート番号とは？と代表的なポート番号まとめ](https://eng-entrance.com/network-port)
>TCP 20 : FTP (データ)
TCP 21 : FTP (制御)
TCP 22 : SSH
TCP 23 : Telnet
TCP 25 : SMTP
UDP 53 : DNS
UDP 67 : DHCP（サーバ）
UDP 68 : DHCP（クライアント）
TCP 80 : HTTP
TCP 110 : POP3
UDP 123 : NTP
TCP 443 : HTTPS
WELL KNOWN PORT NUMBERS 0~1023

>特定のIPアドレス上で公開されているサーバ上で、HTTPというプロトコルにそったアプリケーション、Apacheなどが80番ポートで、クライアントとの通信を待機し、要求に応じてWebページの情報を送信するといった流れだ。

一般的にport:80を指定する。

##location

### server_name

** localhostを指定する場合 **

```:nginx.conf

upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name localhost;
}
```

server_nameは指定のドメインの場合に対象となります。
このケースではhttp://localhost:80の場合に適用されます。

** IPアドレスを指定する場合 **

```:nginx.conf

upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name 192.168.11.11;
}
```
このケースでは、IPアドレスを指定しています。
http://192.168.11.11:80

** ドメインを指定する場合 **

```:nginx.conf

upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name example.com;
}
```

このケースでは、ドメインを指定しています。
http://example.com:80

** 複数合わせる場合 **

```:nginx.conf
upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}

server {
  server_tokens off;
  listen 80;
  server_name example.com [or 192.168.xx.xx [or localhost]];
}
```

### proxy_set_header

```:nginx.conf
upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}


server {
  server_tokens off;
  listen 80;
  server_name example.com [or 192.168.xx.xx [or localhost]];

  location @webapp {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://webapp;
  }
}
```

基本的に

```
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
```
としか利用しません。



``X-Real-IP``
> これもHTTPヘッダの一つ。ロードバランサやプロキシを経由する時に送信元を判別するために利用。アプリケーション層の情報。
X-Forwarded-Forと同じような値だけど、複数の可能性があるX-Forwarded-Forと違って１つ。

``X-Forwarded-For``
>HTTPヘッダの一つ。ロードバランサやプロキシを経由する時に送信元を判別するために利用。アプリケーション層の情報。

詳しくはこちらの記事をみてください
https://christina04.hatenablog.com/entry/2016/10/25/190000


[wwwをつけるべきか？](https://qiita.com/isao_e_dev/items/daf8661dea7d36ba9624)

## try_files

```
try_files $uri $uri.html $uri/index.html @webapp;
```

指定のファイルが存在するかを左から探しに行きます。
例えばhttp://***/hogeでアクセスした場合はhoge.html、hoge/index.html、location @webapp {}の順番に探します。 

次の例をみてみましょう。

```:nginx.conf

upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name example.com [or 192.168.xx.xx [or localhost]];

  try_files  $uri/index.html $uri @webapp;

  location @webapp {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://webapp;
  }
}
```

``location``で定義した``@webapp``を``try_files``で読み込みさせています。

```
try_files  $uri/index.html $uri @webapp;
```

##全体

```:nginx.conf

upstream webapp {
  server unix:///webapp/tmp/sockets/puma.sock;
}

server {
  listen 80;
  server_name example.com [or 192.168.xx.xx [or localhost]];

  access_log /var/log/nginx/access.log;
  error_log  /var/log/nginx/error.log;

  root /webapp/public;

  client_max_body_size 100m;
  error_page 404             /404.html;
  error_page 505 502 503 504 /500.html;
  try_files  $uri/index.html $uri @webapp;
  keepalive_timeout 5;

  location @webapp {
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_pass http://webapp;
  }
}
```
# まず使い方を理解する
## 流れ
1.プロジェクト用のフォルダを作成
2.その中に手動でファイルを作成
　2-1.【docker-compose.yml】を作成 &　貼り付け
　2-2.【Dockerfile】を作成　　　　　 &　貼り付け
　2-3.【Gemfile】を作成　　　　　　  &　gem 'rails' を記述
　2-4.【Gemefile.lock】を作成（Gemfile.lockは空のまま何もしない。）
3.rails newを実行する
4.コンテナの作成 docker-compose build
5.database.ymlの編集
6.dbの作成 rake db:create
7.コンテナサーバーの起動 docker-compose up

## 実際にやってみる。
操作1.2に関しては、追記予定です。
いったん、ここは飛ばして、Dockerfileについて、進めてください

### rails new（手順3）

```:コマンド
$ docker-compose run --rm app rails new . --force --database=mysql --skip-bundle
```

### コンテナを作成（手順4）

```
$ docker-compose build
```

### database.ymlを編集（手順５）

```diff:database.yml

default: &default
   adapter: mysql2
   encoding: utf8
   pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
   username: root
-  password:       #passwordを追加
-  host: localhost #dbに変更
+  password: password
+  host: db
```

```yml:database.ymlの変更後

default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: password
  socket: /tmp/mysql.sock
  host: db

```

## コマンドの理解

### コンテナの作成

```
$ docker-compose build
```

``build``は建てるの意味なので、コンテナを作成します。
Dockerfileの記述を変更した際なども``docker-compose build``で更新します。

### コンテナの起動

```
$ docker-compose up
```

Ruby on Railsでは``$ rails s``でアプリを起動していましたが、dockerでは`` $docker-compose up``でアプリを起動します。なぜというと、`` $docker-compose up``を実行すると、後述する``docker-compose.yml``に記述した``$rails s``が実行されるためです。つまり、結果的にどちらも``$ rails s``を実行しているだけなのです。

**メリットは何か**

どの言語でも``docker-compose up``だけで起動ができます。これにより、自分が知らない言語でもアプリを起動できます。
>例えば、面接官がRubyやRuby on Railsを知らない場合、起動コマンドは当然知りません。しかし、あなたがdocker-compose.ymlに起動コマンドを記述すれば、``docker-compose up ``を実行するだけで面接官でもアプリを起動できます。だから、Dockerはとても親切なのです。

### コンテナの停止・再起動

```:コンテナの停止
$ docker-compose stop
```
コンテナの停止は``stop``です。upの逆である``down``ではありません。
インストールした``gem``や``パッケージ``を反映させるには、一度アプリを再起動しなければなりません。
その際に``$docker-compose stop``を実行して、アプリを停止させます。その後、``$ docker-compose up``を実行し、再度起動します。

```:コンテナの再起動
$ docker-compose restart
```
いちいち、``stop``と``up``をやるのが面倒な場合は、`` $ docker-compose restart``でコンテナが再起動します。なので、基本的にgemインストール後は`` $ docker-compose restart``を実行します。



### コンテナの削除

```
$ docker-compose down
```
コンテナを削除します。 ``-v``で後述するvolumes（バックアップデータ）も削除されます。
コンテナが削除されますので、基本的に利用しません。