Dockerfileを理解する
Dockerfileでよく使う項目を学習していきます。
公式ページで詳しく記載されているのは、下記になります。
Dockerfileの辞書はこちら。
自分が知りたい項目があれば、一度確認すると良いでしょう。
では、一緒にみていきましょう。
FROM
FROM
ではインストールしたいrubyのバーションを指定します。
実際にrubyをインストールするには、複数のコマンドを実行しなければならないが、それらをセットにしたimage
を利用して、1行でrubyをインストールする。
例えば、rubyのをインストールしたい場合、
FROM ruby
と記述するだけ、rubyの環境構築ができる。
さらに、rubyのバージョンを2.5.1
と指定したい場合は、
FROM ruby:2.5.1
と記述してバーションを指定する。
Dockerではimage
のバージョンのことをtag
と呼びます。
(厳密には異なりますが、わかりやすく伝えるためにそう伝えています)
基本的な下記の公式になります。
FROM image:tag(バージョン)
#imageはrubyやphpなどの言語などを指定する。
基本的にimage
はrubyやphpなどの言語やmysql
などを指定しますが、imageを自作することも可能です。imageをカスタマイズして自作すれば、より簡略して環境構築ができます。会社で用意されたimage
を利用して開発することもあるようです。
今回はruby:2.5.1を利用したいので、下記の記述で進めていきます。
FROM ruby:2.5.1
RUN
RUN
では実行したいコマンドを記述します。
FROM ruby:2.5.1
RUN 実行したいコマンド
Node.jsやyarnなど必要なものをインストールしたり、bundle installなどコンテナをbuildした際に実行させたいコマンドを記述します。
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
ENV 変数 値
# $変数で利用可能
例えば、フォルダapp_name
を作成し、これを変数APP
と定義したい場合、
FROM ruby:2.5.1
RUN mkdir /app_name
ENV APP /app_name
と記述する。
これで、 APP = app_name
と定義される。
定義した変数を利用したい場合、$
マークをつける。
今回の例だとAPP
を利用するには、$APP
と記述する。
FROM ruby:2.5.1
RUN mkdir /app_name
ENV APP /app_name
WORKDIR $APP # 変数 APPを指定している
WORKDIR
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
ADD [追加したいもの] → [追加したい場所]
左にあるファイル
を右の場所
に追加する。
フォルダ内にファイルを追加したい場合は、フォルダ名/
と記述する。
ファイルを上書きさせたい場合は、フォルダ名/ファイル名
と記述する。
ADD test Dir/
ADD test Dir/test
WORKDIR /webapp
ADD ./Gemfile /webapp/Gemfile
ADD ./Gemfile.lock /webapp/Gemfile.lock
使うことはないが、知識として知っておきたいこと。
ADD hom* /mydir/ # "hom" で始まる全てのファイルを追加
ADD hom?.txt /mydir/ # ? は1文字だけ一致します。例: "home.txt"
その他は辞書で調べてね
Dockerfileの辞書はこちら。
COPYとADDの違い
実例
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のバージョンを指定します。
どんなバージョンがあるかを知りたい方はこちら
#version:3のdocker-composeを利用
version: "3"
services
db用, アプリ用などに分ける
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に対してコマンド実行したい場合、
version: "3"
services:
db: #ここに対して対して、コマンドを実行したい
app:
nginx:
サービスがdbなので
$ docker-compose run db コマンド
となる。
run
は実行するの意味があるので、docker-compose.yml
のdb
に対してコマンド
をrun(実行)
の意味になる
$ docker-compose run db mysql -u [ユーザー名] -p
続いて、appに対してコマンド処理をしたい場合
version: "3"
services:
db:
app: #ここに対して対して、コマンドを実行したい
nginx:
サービス名がappなので、
$ docker-compose run app コマンド
となります。
run
は実行するの意味があるので、docker-compose.yml
のapp
に対してコマンド
をrun(実行)
の意味になる
#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に対してコマンド処理をしたい場合
version: "3"
services:
db:
app:
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があります。
カレントディレクトリのパスは.
なので、下記のように指定します。
version: "3"
services:
app:
build:
context: . #カレントディレクトリ上のDockerfileを読み込む
これでサービスapp
はカレントディレクトリにあるDockerfile
を読み込みます。
デフォルトでDockerfile
を読み込む設定になっているので、ファイル名は指定しなくて大丈夫です。
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
を指定します。
version: "3"
services:
nginx:
build:
context: containers/nginx #nginフォルダ内のDockerfileを使う
Dockerfileが不要な場合(imageだけで十分)
下記のように、imageしかDockerfileに記述する必要がない場合、
FROM mysql:mysql:5.6.47
わざわざDockerfileを作成せずに、直接docker-compose.ymlにimageを記述すればよい。
/webapp
├── docker-compose.yml
├── Gemfile
└── Gemfile.lock
サービスdb
はimage: mysql:5.6.47
を利用したい場合、
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を指定することが可能
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として読み込みたいファイルを指定します。
公式リファレンス
version: "3"
services:
db:
image: mysql:5.6.47
app:
build: .
nginx:
build:
context: containers/nginx
dockerfile: Dockerfile-nginx #フィル名を指定する
depends_on:
サービスとサービスを紐付ける
ここまで学んだ、下記だとdb, app, nginxが繋がっていません。
version: "3"
services:
db:
image: mysql:5.6.47
app:
build: .
nginx:
build:
context: containers/nginx
今回は、それぞれのサービスを紐づけます。
dbとappを紐付ける
app単体だと,dbのデータを保持できないので、紐付けます。
version: "3"
services:
db:
image: mysql:5.6.47
app:
build: .
depends_on: #依存する
- db #dbを紐付け
これにより、app
を起動するとdb
も起動させます。
app
系のコマンド実行する際は、DBの情報が必要になるので、app
にdb
を紐付けています。
ターミナルコマンドを見て、より理解を深めましょう。
$ docker-compose run app rails g model message
Starting web-share_db_1 ... done # dbが起動している
と表示されており、appのコマンド実行前に db
が起動しているとわかります。
appとnginxを紐付ける
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
実行時に、の動作をみましょう。
$ 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のように記述します
version: "3.8"
services:
web:
build: .
depends_on:
- db
- redis
redis:
image: redis
db:
image: postgres
上記の例では、サービスweb
は、redis
とdb
の二つの依存関係を同時に保有しています。
このように複数のサービスと紐付けることも可能です。
volumes: (バックアップ)
volumes:でデータのバックアップを作成し、別のコンテナでもデータを使い回します。
これをしておかないと、Dockerfileを更新して$ docker-compose build
し直す際に、DBの情報が消えます。
新品のiPhoneには、何もデータがないように
新しく作成したコンテナは、前のコンテナの情報は入っていません。
buildするたびにDBの情報を作り直すのは、非常にめんどくさいので、バックアップを作成しておき、それをコンテナに使いまわします。
そのバックアップをvolume
と呼びます。
volumeの必要性
現実の例
新しいPCを購入した = 前のPCのデータは当然ない
なので、前のPCで作成したバックアップデータを新しいPCに入れる。
Dockerをbuildし直した(コンテナを作り直す)
新しいコンテナ = 前のコンテナのデータは当然ない
バックアップデータであるvolumeを新しいコンテナに紐付ける
volumesを記述する
version: "3"
services:
db:
app:
nginx:
volumes:
#ここにvolumeをセットする。
#volumeの名前は自由
mysql_data:
gem_data:
public-data:
tmp-data:
log-data:
サービスとvolumesを紐付ける
######dbのvolume名をmysql_data
として場合、
version: "3"
services:
db:
volumes:
- mysql_data: #使用するvolumeを指定
volumes:
mysql_data: #これを紐付け
mysqlを利用するに当たって、決まった階層があるので、それを指定します。
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
version: "3"
services:
db:
volumes:
- mysql_data:/var/lib/mysql
app:
volumes:
- gem_data:/usr/local/bundle
nginx:
volumes:
mysql_data:
gem_data: #これをappに紐付ける
実例
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時のコマンドを処理します。
基本的にはサーバー起動関連のコマンドを記述します。
version: '3'
services:
web:
build: .
command: bundle exec rails s -p 3000 -b '0.0.0.0'"
version: "3"
services:
app:
build:
context: .
command: bundle exec puma -C config/puma.rb
volumes:
version: "3"
services:
app:
build:
context: .
command: bundle exec unicorn_rails -c config/unicorn.rb
volumes:
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:
を利用します。
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の例
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(バックアップデータ)も削除されます。
コンテナが削除されますので、基本的に利用しません。