30
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-09-29

Dockerfileを理解する

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

公式ページで詳しく記載されているのは、下記になります。
Dockerfileの辞書はこちら
自分が知りたい項目があれば、一度確認すると良いでしょう。

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

FROM

FROMの公式リファレンスはこちら

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

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

dockerfile
FROM ruby

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

dockerfile
FROM ruby:2.5.1

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

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

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

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

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

Dockerfile
FROM ruby:2.5.1

RUN

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

RUN
FROM ruby:2.5.1

RUN 実行したいコマンド

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

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
ENV 変数 値 
# $変数で利用可能

例えば、フォルダapp_nameを作成し、これを変数APPと定義したい場合、

DockerfileのENV例
FROM ruby:2.5.1

RUN mkdir /app_name
ENV APP /app_name

と記述する。
これで、 APP = app_name と定義される。

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

DockerfileのENV例
FROM ruby:2.5.1

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

ENVの公式リファレンス

WORKDIR

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の公式リファレンス
そもそもワーキングディレクトリとは?

作業用フォルダ = カレントディレクトリ = .
カレントディレクトリ(英語: current directory、現行ディレクトリ。つまり現在開いているフォルダ)とは、現在の位置であるディレクトリ(フォルダ)のことである。作業フォルダ(ワーキングディレクトリ)とも呼ばれることがある。

ADD

Dockerfile
ADD [追加したいもの] → [追加したい場所]

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

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

testファイルをDirフォルダに入れる場合
ADD test Dir/
testの内容をDirフォルダのtestに上書き
ADD test Dir/test
Railsでよく使う例
WORKDIR /webapp

ADD ./Gemfile /webapp/Gemfile   

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

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

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

ADDの公式リファレンス

その他は辞書で調べてね

Dockerfileの辞書はこちら
COPYとADDの違い

実例

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の辞書はこちら

読み込み
docker-compose.yml → Dockerfile

version

まずdocker-composeのバージョンを指定します。
どんなバージョンがあるかを知りたい方はこちら

docker-compose.yml

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

services

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

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に対してコマンド実行したい場合、

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


  app:


  nginx:

サービスがdbなので

dbに関するコマンド処理を実行
$ docker-compose run db コマンド

となる。

runは実行するの意味があるので、docker-compose.ymldbに対してコマンドrun(実行)の意味になる

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

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

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


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


  nginx:

サービス名がappなので、

appに対してコマンド実行
 $ docker-compose run app コマンド

となります。
runは実行するの意味があるので、docker-compose.ymlappに対してコマンド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に対してコマンド処理をしたい場合

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


  app:


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


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

runは実行するの意味があるので、docker-compose.ymlnginxに対してコマンドrun(実行)の意味になる

build: 読み込むDockerfileを指定する

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

context: Dockerfileを持つフォルダを指定する

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

ワーキングディレクトリ直下にある場合
/webapp(アプリ名)
├── docker-compose.yml
├── Dockerfile        (これを指定したい)
├── Gemfile
└── Gemfile.lock

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

docker-compose.yml
version: "3"
services:
  app:
    build:
      context: .  #カレントディレクトリ上のDockerfileを読み込む

これでサービスappはカレントディレクトリにあるDockerfileを読み込みます。
デフォルトでDockerfileを読み込む設定になっているので、ファイル名は指定しなくて大丈夫です。

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を指定します。

docker-compose.yml
version: "3"
services:
  nginx:
    build:
      context: containers/nginx #nginフォルダ内のDockerfileを使う
Dockerfileが不要な場合(imageだけで十分)

下記のように、imageしかDockerfileに記述する必要がない場合、

Dockerfile(mysql用)

FROM mysql:mysql:5.6.47

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

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

サービスdbimage: mysql:5.6.47を利用したい場合、

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を指定することが可能

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として読み込みたいファイルを指定します。
公式リファレンス

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が繋がっていません。

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

  app:
    build: .

  nginx:
    build:
      context: containers/nginx

今回は、それぞれのサービスを紐づけます。

dbとappを紐付ける

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

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

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

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

appに対するコマンド
$ docker-compose run app rails g model message

上記のコマンドを実行すると
スクリーンショット 2020-08-10 12.05.14.png

dbが起動している箇所
Starting web-share_db_1 ... done # dbが起動している

と表示されており、appのコマンド実行前に dbが起動しているとわかります。

appとnginxを紐付ける
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
上記の写真をみてみると

$ 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でアクセスできる。

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

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

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

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

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

公式リファレンス

volumes: (バックアップ)

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

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

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

volumeの必要性

現実の例

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

Dockerをbuildし直した(コンテナを作り直す)

新しいコンテナ = 前のコンテナのデータは当然ない
バックアップデータであるvolumeを新しいコンテナに紐付ける

volumesを記述する

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として場合、

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


volumes:
  mysql_data: #これを紐付け

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

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
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に紐付ける
実例
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時のコマンドを処理します。

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

rails sで起動
version: '3'
services:
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'"
pumaで起動
version: "3"
services:
  app:
    build:
      context: .
    command: bundle exec puma -C config/puma.rb
    volumes:
unicornで起動
version: "3"
services:
  app:
    build:
      context: .
    command: bundle exec unicorn_rails -c config/unicorn.rb
    volumes:
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:を利用します。

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したいとき、どのようにすればよいのか解説していきます。

公式リファレンスはこちら(必読)

mysqlの例

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ファイルが1つの場合
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

```

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

方法は2つ

#####方法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を2つ作成し、開発環境用、本番環境用に使いわける。

``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と違って1つ。

``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を編集(手順5)

```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(バックアップデータ)も削除されます。
ンテナが削除されますので、基本的に利用しません。


30
35
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
30
35

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?