もう逃げない。基礎から学ぶDocker入門 / Dockerfileを使用したサーバー構築（実践編）（5/9）

## はじめに

今まで、全然理解できていなくて逃げてたDockerに関して、しっかり理解していこうと思います。

https://www.docker.com/

コレまで、便利なんだろうなと思いながらも、逃げてました。

なので、開発環境では使いこなせるスキルにまでは理解したいと思います。

あと、暗記してたDockerコマンドを１つ１つ理解していきます。

以下のような順番で勉強していきます。

- [Dockerの概要（1/9）](https://qiita.com/ryosuketter/items/d7833182dced67713d27)
- [Dockerの基本操作（イメージの作成, コンテナの起動, 停止, 削除）（2/9）
](https://qiita.com/ryosuketter/items/c53352f1143366d8b9a9)
- [Dockerの基本操作（ログの出力, コンテナの中でプロセスを実行する方法, 使用していない Docker オブジェクトの削除）（3/9）](https://qiita.com/ryosuketter/items/5fd73b7311175a691dfe)
- [Dockerfileを使用したサーバー構築（座学編）（4/9）](https://qiita.com/ryosuketter/items/d9c3420be7da586225e8)
- [Dockerfileを使用したサーバー構築（実践編）（5/9）](https://qiita.com/ryosuketter/items/716a4c5e49795d494600) :point_left_tone1: 今ここ
- [Docker Composeの概要（座学編）（6/9）](https://qiita.com/ryosuketter/items/104a4eb38f8824eb6de9)
- [Docker Composeのよく使う基本操作（7/9）](https://qiita.com/ryosuketter/items/8bbd2c60392f443cfe23)
- [Docker Composeを使ってRailsの環境を構築する（8/9）](https://qiita.com/ryosuketter/items/91cd107794c1cbe358c6)
- [Docker上に構築したRailsをHeroku（本番環境）にデプロイ（公開）する（9/9）](https://qiita.com/ryosuketter/items/e3e68ce6ec572dc8905e)

## 実践してみよう「Dockerfileを使用したサーバー構築」

今回は、Rubyをベースイメージにして、

それを元にSinatra（Ruby製のWebアプリケーションフレームワーク）を導入して、

各種インストールして、webサーバを作ります。

で、その、webサーバーを動かす環境をDockerfileで作る方法を学んでいきます。

### Dockerのイメージを用意しよう

Dockerfileが用意できれば、そこからDockerイメージを作ることができます。

Dockerイメージを起動させることで、コンテナが作られます。

（似たようなこと言いますが）今回は、最終的にローカルPC上ではなくて、コンテナ上にwebサーバーを作っていきます。

### ディレクトリの用意

```
src/
　├ main.rb
　└ Gemfile
Dockerfile
```

Gemfileには、Sinatraを導入する記述をします。

```Gemfile:Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'sinatra'
```

この記述は下記を参考

http://sinatrarb.com/intro-ja.html

続いてDockerfileを書いていきますが、いきなりDockerfileにwebサーバとなるコンテナが立ち上がるように書くのではなく

まずは、コンテナ内でサーバが作れるか確認しつつ確かめましょう。

なので、インタラクティブモードでコンテナに入れるようなDockerfileを作成します。

### Dockerfileを書いていきます

前の記事でも詳細に説明しましたが、改めてDockerfileとは何かを説明します。

**Dockerfileとは、指定したベースのイメージに対して、どんな作業をさせるかを記載するファイルです。**

```dockerfile:Dockerfile
# ベースイメージ
FROM ruby:2.7

# 作業をどこでやるかの指定（場所はどこでもいい）
WORKDIR /var/www

# 使用するソースコードをDockerコンテナ内にコピー
# ローカルの環境にあるsrc/ を dockerコンテナ内の /var/www にコピーする
COPY ./src /var/www/

# dockerの実行コマンド
CMD ["/bin/bash"]
```

Dockerイメージの作成

```bash
docker image build -t sample/sinatra:latest .
```

Dockerコンテナの作成・起動

```bash
docker container run -it -p 4567:4567 --name sinatra -v ${PWD}/src:/var/www sample/sinatra:latest
```

`sample/sinatra:latest` これはイメージ名です。

`-v` は、ボリュームのオプションです。これは、ローカルPCの`./src`以下を、dockerコンテナ内の`/var/www`に同期させるという意味です。

これをしないと、アプリケーション側のコードを更新してもdockerコンテナ内のコードが反映されないので指定しました。

http://docs.docker.jp/v1.10/engine/userguide/containers/dockervolumes.html#id3

`-p` sinatraのポートはデフォで4567なので。ローカルもそれに併せて`4567`にした。

http://sinatrarb.com/intro-ja.html

`-it` の説明は前の記事で書いたので、そちらを参照してください。

https://qiita.com/ryosuketter/items/5fd73b7311175a691dfe#%E6%8C%87%E5%AE%9A%E3%81%97%E3%81%9F%E3%82%B3%E3%83%B3%E3%83%86%E3%83%8A%E3%81%AE%E4%B8%AD%E3%81%A7%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B9%E3%82%92%E5%AE%9F%E8%A1%8C%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95

実行すると、Dockerコンテナ内でインタラクティブモードで起動します。

Dockerファイルにあるコマンドが `CMD ["/bin/bash"]` bash というシェルを起動しているので、何かしらコマンドが実行できる状態になっております。

### Dockerコンテナ上でDockerファイルに書くコマンドが実際に動くか確かめる

Dockerファイルに、webサーバーがちゃんと起動するか確かめる目的で、Dockerfileに直接書く前に

Dockerコンテナ内で実行できるかを確かめていきます。

```bash
root@eb497419aa28:/var/www# ruby -v
ruby 2.7.4p191 (2021-07-07 revision a21a3b7d23) [x86_64-linux]

root@eb497419aa28:/var/www# ls
Gemfile
```

ためにし、rubyのバージョンを確かめたり、`ls`コマンドを打ったりしました。

`ls`の結果は、なぜ`Gemfile`だけかわかりますか？

それは、Dockerfile内に書いた `WORKDIR /var/www` 作業ディレクトリを指定して `COPY ./src /var/www/` の右側の部分、`/var/www` この配下にローカルPCの`.src/`配下を同期させているので。

### Dockerコンテナ上でライブラリをインストールしていく

どこにライブラリをインストールするかの指定です。

```bash
bundle config --local set path 'vendor/bundle'
```

実際にインストールするコマンドは以下

```bash
bundle install
```

Railsを使われる方にとってはお馴染みなコマンドですね。

うまくインストールできたら今度は簡易的なWebサーバを作成をRubyで書いていきます。

### Sinatra（ruby）で簡易的なサーバを作成

```rb:main.rb
require 'sinatra'

# 初期設定（どんな通信でも受け取れるようにIPアドレスを以下のように指定）
configure do
  set :bind, '0.0.0.0'
end

# TOPページにアクセスしたら文字列を出力する
get '/' do
  'Hello world!'
end
```

詳しい説明は以下に任せます。

https://qiita.com/1ain2/items/194a9372798eaef6c5ab#0000

http://sinatrarb.com/intro-ja.html

### Dockerコンテナ上のRubyを動かします

```bash
root@eb497419aa28:/var/www# bundle exec ruby main.rb

[2021-09-24 10:54:06] INFO  WEBrick 1.6.1
[2021-09-24 10:54:06] INFO  ruby 2.7.4 (2021-07-07) [x86_64-linux]
== Sinatra (v2.1.0) has taken the stage on 4567 for development with backup from WEBrick
[2021-09-24 10:54:06] INFO  WEBrick::HTTPServer#start: pid=50 port=4567
```
Gemfileを使った処理を動かすときは`bundle exec`をつけます。

webサーバが立ち上がったことが確認できました。

```
http://localhost:4567/
```

にアクセスして、`'Hello world!'`が出力されたらOKです。

Dockerコンテナないでうまく動いたので、今度は、この情報をDockerfileに記入して同じ挙動になるか試しましょう。

### Dockerfile内でサーバー構築コマンドをなどを記載していく

必要な処理は

- ライブラリの追加（`bundle config --local set path 'vendor/bundle'`）
- rubyの実行（`bundle exec ruby main.rb`）

ですね。これをDockerfile内に書くだけです。

コマンドの実行は`RUN`でやります。

```dockerfile:Dockerfile
FROM ruby:2.7

WORKDIR /var/www

COPY ./src /var/www/

RUN bundle config --local set path 'vendor/bundle'
RUN bundle exec ruby main.rb

CMD ["bundle", "exec", "ruby", "main.rb"]
```

`RUN`が重複してるので`&&`で、続けて書くこともできます。

```dockerfile:Dockerfile
FROM ruby:2.7

WORKDIR /var/www

COPY ./src /var/www/

RUN bundle config --local set path 'vendor/bundle' && bundle install

CMD ["bundle", "exec", "ruby", "main.rb"]
```

完成しました。

では、前のコンテナを削除して、イメージを作り直して、再度コンテナを作成・起動させましょう。

サーバー（`ctrl + c`）を止めてexitして、以下を実行します。

コンテナの削除

```bash
docker container rm sinatra
```

これでコンテナが削除されました。では、Dockerイメージを作り直します。

```bash
docker image build -t sample/sinatra:latest .
```

イメージの作成は同じコマンドです。

イメージが作成できたら、今度はコンテナを起動させます。

```bash
docker container run -p 4567:4567 --name sinatra -v ${PWD}/src:/var/www sample/sinatra:latest
```

今回は、`-it`消しています。なぜなら、今回は、コンテナ内でshellを実行させるつもりはないので。

今回は、コンテナを実行する ＝ サーバを起動させる

だけなので、`-it`は消しています。

コンテナが起動したということは、サーバが立ち上がったということなので、再度、ブラウザ立ち上げて以下のポートでアクセスします。

```
http://localhost:4567/
```

アクセスして `Hello world!`が出力されたら、今度は、Dockerコンテナ上のサーバーからアクセスが成功したことになります。

:clap:

以上です。

## アウトプット100本ノック実施中

https://qiita.com/ryosuketter/items/713474dbd009870e4103