Docker 入門

# 個人的リマインド用
参考
[Docker超入門講座 合併版 | ゼロから実践する4時間のフルコース](https://www.youtube.com/watch?v=lZD1MIHwMBY&t=13358s)

# Dockerって何?
アプリを簡単に開発・デプロイできる仕組みのこと。
OS、ライブラリ、アプリケーションをひとまとめにして、配布できる。さらにテスト環境だけでなく、本番環境でも使える。

# 環境構築 for mac
**今回必用なもの**
iTerm2,VSCode,Docker,Gitそれぞれ動画に従いインストール

# Dockerを使ってみよう
```
docker run hello-world
```
**何が起こっているか**
**イメージ**から**コンテナ**を作成し、**起動**させている。
**イメージ:** コンテナに必用なものを記載した雛形
**コンテナ:** Dockerの実行環境

**クライアント**(依頼主)がdocker runというコマンドを**デーモン**に対して伝える
↓
**デーモン**はバックグラウンドで起動していてクライアントの命令通りに、イメージやコンテナを操作する**アプリケーション**のこと
↓
今回、**デーモン**はhello worldという命令を受け取ったら、**レジストリ**(オンライン上で保存しておく場所)からhello worldの**イメージ**をとってくる
↓
その後**デーモン**は、とってきたイメージをもとに**コンテナ**を作成し、**起動**させて結果としてhello worldが表示される

# アプリを実行・作成しよう
今回はRubyでWebサーバーを用意し、トップページにアクセスされたらhelloと返す
**基本的な流れ**
1.アプリコードとDockerfileを用意
2.そこからDockerのイメージを作成
3.イメージからコンテナを作成し起動

### イメージを作る下準備

まずmain.rbというファイルを用意し、webサーバーを用意しhelloと表示するコードを書く(**アプリケーションコード**)

続いて**Dockerfile**(イメージの雛形)を用意。Dockerfileにはアプリコードやライブラリ、ベースイメージに関することが記載される。
※Dockerfile例
```dockerfile
FROM ruby:2.7
#Rubyの2.7を使う

RUN mkdir /var/www
#RUNはコマンドの実行。以下のディレクトリを作れという意味
COPY main.rb /var/www
#さっき作ったmain.rbを/var/wwwの下に置く

CMD ["ruby", "/var/www/main.rb"]
#Dockerのイメージを起動した時に実行するコマンドを指定している。rubyでmain.rbを実行
```
これでイメージを作る**下準備**ができた

### イメージを作る
```
docker image build -t sample/webrick:latest .
```
-t → 今後使いやすくするためにタグをつける
. → 今いるディレクトリのDockerファイルを参照する

**イメージが作れたか確認するコマンド**
```
docker image ls (今回はsample/webrickがある)
```
**コマンド確認**
```
docker --help
docker image --help
```

### イメージからコンテナを作成し起動
```
docker container run

(今回)
docker container run -d -p 8000:8000 --name webrick sample/webrick:latest
```
-d → バックグラウンドで実行できる。ターミナルが使えるように
-p → ポートの指定。左側が自分のポートで、右側がDockerのポート。今回は自分の8000番ポートをDockerの8000番ポートに接続させる。
--name → 名前をつけておくと後々楽(今回はwebrick)
最後に指定のイメージ

**コンテナのライフサイクル**
作成→起動→停止→削除

**コンテナが今どうなっているかの確認**
```
docker container ls (起動中のコンテナ確認)
docker container ls -a (停止中のコンテナも含め確認)
```

### コンテナの停止と削除
**停止**
```
Ctrl + C or
docker container stop webrick(コンテナ名)
```
**削除**
停止後に
```
docker container rm webrick(コンテナ名)
```

### コンテナ操作でよく使うコマンド
異常が起きた時にログを見て分析する
```
docker container logs webrick(コンテナ名)
```
実行中のコンテナで別のコマンドを実行したい時
```
docker container exec webrick(コンテナ名) ruby -v
```
使わないイメージやコンテナが溜まってハードディスクを圧迫するので後片付けしたい時
```
docker system prune -a(all)
```
# Dockerfileを作ろう
**Dockerfile振り返り**
アプリケーションやライブラリ、ベースイメージなどの情報を書き込み、これを基にイメージを作成する

**今回の前提**
Dockerというディレクトリで作業し、この中にはsrcディレクトリ、さらにその中にはGemfileがある。目標はsinatraを動かすこと。
```
docker/src/Gemfile

・
・
gem "sinatra"
```

srcと同じ階層に**Dockerfile**を作成
```Dockerfile
Docker/src/Dockerfile

FROM ruby:2.7
#rubyというイメージの2.7を指定

WORKDIR /var/www
#ディレクトリは何でもいい。作業をする場所。ディレクトリがない場合は作ってくれる

COPY ./src /var/www
#srcディレクトリ以下のファイルを/var/wwwの下にコピー。アプリケーションコードをDockerにコピーできる

CMD ["/bin/bash"]
#Docker起動時にbashを開く
```

Dockerfileを作ったので次はイメージを作成
```
docker image build -t sample/sinatra:latest .
```
コンテナを作成し起動
```
docker container run -it -p 4567:4567(sinatraのポート番号) --name sinatra 
-v ${PWD}/src:/var/www sample/sinatra:latest
```
-it → Dockerにインタラクティブモード(対話型)でコンテナを起動させることを明示。shellを起動する際にいる
-v → ボリュームのオプション。コロンから左が自分、右がDocker側。自分のディレクトリのsrc以下をDocker内の/www以下に共有する。これをしないと更新した時に、イメージを作り直さないといけない。

上のコマンドを実行するとbashが開く。自分がいる場所は/var/wwwだが、ローカルの変更内容が共有されていることがわかる。

### ライブラリのインストール
**手法(開発プロセス):**
**一度Dockerのコンテナ内に入ってコマンドを実行し、うまくいったらそれをDockerfileに反映させる。**

現在はbashが開いている
```
bundle config --local set path 'vendor/bundle'
```
vendor/bundleにライブラリの中身をインストールしてという意味
```
bundle install
```
Gemfileの中身がインストールされる

### Hello worldを表示
app.rbというファイルを作り、そこにHello worldを表示するコードを記述

その後ターミナルで
```
bundle exec ruby app.rb
```
Gemfileを使った処理を動かす時はbundle execをつける。
WebrickというWebサーバーが立ち上がる。localhost:4567にアクセスするとHello worldが記述されている。

続いてこれを**Dockerfileに反映させる**。
```Dockerfile
Docker/src/Dockerfile

FROM ruby:2.7

WORKDIR /var/www

COPY ./src /var/www

RUN bundle config --lcoal set path 'vendor/bundle'
RUN bundle install
#先ほど手動でやったことと同じことが実行される

#1行で書く方法
RUN bundle config --lcoal set path 'vendor/bundle' \
  && bundle install

CMD ["bundle", "exec", "ruby", "app.rb"]
#書き換え。コンテナ起動時にbundle exec ruby app.rbが実行される。スペース区切りをコンマで区切る
```
# Docker Compose
Railsの開発環境を構築

**Docker Compose**
複数のアプリケーションをまとめて操作
**Docker Compose例：**
```
docker-compose.yml

version: '3'
services:
  db: #それぞれ設定
   image: mysql8.0
  web: #それぞれ設定
   build:
   depend_on:
     - db
```
**基本操作**
```
イメージのビルド
docker-compose build
コンテナの作成と起動
docker-compose up -d(バックグラウンドで実行)
コンテナを停止・削除
docker-compose down
```
**その他のよく使う操作(1)**
```
コンテナの一覧を表示
docker-compose ps
ログを表示
docker-compose logs
```
**その他のよく使う操作(2)**
```
コンテナを作成してコマンドを実行(1からコンテナを作成し実行)
docker-compose run <サービス> <コマンド>
起動中にコンテナにコマンド実行(今あるコンテナに実行)
docker-compose exec <サービス> <コマンド>
```
### 開発
**全体の流れ**
1.Docker関連のファイルを用意
2.初期設定
3.起動
4.追加操作

### Dockerfile
コードを大まかに(詳しくは動画を確認)
```Dockerfile
FROM ruby:2.7
RUN ・・・
#今回必用なライブラリを指定(今回はnode.jsとかyarnとか)
WORKDIR /app
COPY ./src /app
#ローカルのソース(Railsのソースコード)を/appにコピー
RUN bundle config --local set path 'vendor/bundle' \
  && bundle install
#Ruby関連のライブラリのインストール
```
**Gemfile**
```
gem 'rails', '~> 6.1.0'
```
**Docker-compose.yml**
```ruby
version: '3'
services: #2つのサービスを定義
  db: #今回はMYSQL
   image: mysql8.0
   command: #MYSQLの認証形式に関する設定
   ---default-authentication-plugin=mysql_native_password
   volumes: #コロンから左がローカルのディレクトリ、右がDockerのディレクトリ ディレクトリを同期
     - ./src/db/mysql_data:/var/lib/mysql
   environment: #環境変数の設定(パスワードの設定)
     MYSQL_ROOT_PASSWORD: password
  web: #今回はRails
   build: .　#ベースのイメージとして同じディレクトリにあるDockerfileを参照する
   command: build exec rails s -p 3000 -b '0.0.0.0'
   #railsのサーバーを起動する
   volumes: #同期の設定
     - ./src/app
   ports:
     - "3000:3000"
   depends_on: #依存関係(webがdbに依存。接続先のホスト欄をIPアドレスではなくdbと省略できる)
     - db
```
### 初期設定
```
docker-compose run web rails new . --force --database=myspl(データベースを指定)
```
コマンドを実行すると新しいGemfile(+他のRailsのファイル)ができている。
Gemfileが更新されたり、Dockerfileの内容が更新された時は、**イメージをbuildする**。
```
docker-compose build
```
次はデータベースの設定
src/config/database.yml
```
初期状態:
default: &default
  ・
  password:
  host: localhost
  ・
書き換え後:
default: &default
  ・
  password: password (docker-compose.ymlのenvironmentのとこ)
  host: db (docker-compose.ymlのdepends_onのとこ)
  ・
```
続いてデータベースの作成
```
docker-compose run web rails db:create
```
### 起動
```
docker-compose up
```
localhost:3000にアクセスでrailsの画面が出る

### 追加の操作
サーバーの止め方
```
Ctrl + C or
docker-compose down (いっぺんに停止)
```
再度起動
```
docker-compose up -d (バックグラウンドで実行)
```
コンテナの一覧を表示
```
docker-compose ps
```
ログを見る
```
docker-compose logs
```
コンテナ内でコマンドを実行したい時(例: bash)
```
docker-compose exec web /bin/bash (bashが起動しコンテナの中に入れる)
```
GemfileやDockerfileの修正を反映させたい時
```
docker-compose build (イメージを作り直す)
docker-compose up -d (もう一度コンテナを作成・起動)
```
# 本番環境に公開しよう
本番環境で使わないと、手動でライブラリを入れないといけない。面倒だし、本番とローカルの差異が発生する。

今回はHerokuを使い、Heroku上でDockerコンテナを起動することで本番に公開する。

**全体の流れ**
1.事前準備
2.Herokuにログイン
3.Herokuアプリを作成
4.DBを追加・設定
5.Dockerfileを本番環境用に修正
6.Dockerイメージをビルド・リリース
7.機能追加

### 事前準備
githubの登録 + ユーザー名とメールアドレスを設定
Herokuの会員登録 + Heroku CLIインストール

### Herokuにログイン
適宜確認

### Herokuアプリの作成
リリースしたいものをアプリという単位で管理している

### DBの追加・設定
DBの追加
```
heroku addons:create cleardb:ignite -a rails-docker-kyt
```
DBの設定
本番環境のデータベースの接続先等を変更する(環境変数を使う)

### Dockerfileを本番環境用に修正する
処理が本番とローカルでちょっと変わってくる

トップディレクトリにstart.shというファイルを作成。ここに本番環境特有の処理を入れておく。

**修正内容**
環境変数を設定
```Dockerfile
FROM ruby:2.7

ENV RAILS_ENV=production ←ここ
```
一番下にいくつか処理を追加
```dockerfile
・
・
COPY start.sh /start.sh
#さっき作ったファイルをDocker側ルートディレクトリ直下にコピー
RUN chmod 744 /start.sh
#実行権限を与える
CMD ["sh", "/start.sh"]
#起動直後にファイルを実行
```
start.shに処理を記述
```bash
/start.sh

#!/bin/sh シェルスクリプトのファイルなのでおまじない的に書く

if [ "${RAILS_ENV}" = "production" ]
then
    bundle exec rails assets:precompile
fi
#Dockerfileはif文が使いにくいのでこっちのファイルで実行している
#Railsは本番環境でコンパイルしなければならないので上記のコードを書く

bundle exec rails s -p ${PORT:-3000} -b 0.0.0.0
#Railsサーバーを起動する。ポートがあればそれを使い、なければどこからのIPアドレスでもOK
```
その後に環境変数の追加コードを書く。
ちなみにローカルでサーバーが起動している、Herokuで起動しない可能性があるので、docker-compose downでサーバーを削除しておく。

### Dockerイメージをビルド・リリース
Dockerイメージをビルドして、コンテナレジストリの方にpushする
```
heroku container:push web -a rails-docker-kyt
```
コンテナレジストリからHerokuの方にリリースする
```
heroku container:release web -a rails-docker-kyt
```
DBのマイグレーション(テーブルを更新したい時)
```
heroku run bundle exec rake db:migrate RAILS_ENV=production -a rails-docker-kyt
```

### 機能追加
今はページがないのでサイトに警告が表示されているので作る。
まずは開発するようにサーバーを立ち上げる。
```
docker-compose up -d
```
次にコントローラを作る
```
docker-compose exec web bundle exec rails g controller users
```
ルーティングの設定
```
routes.rb

  get '/', to: 'users#index'
```
コントローラの記述
```ruby
users_controller

  def index
  end
```
ビューを作成
```erb
users/index.html.erb

<h1>Hello world</h1>
```
ローカルで作成が完了したので、Herokuに反映させる。ローカルサーバーは落としておく。
```
heroku container:push web -a rails-docker-kyt
heroku container:release web -a rails-docker-kyt
```
**問題点:**
修正とかエラーがあるたびに、pushからやり直すのが鬼のように面倒臭い。そこでCI/CDでテストやデプロイを自動化する。

# CI/CD
現在の問題点。手動デプロイがかなり面倒臭い。

**用語説明**
**CI(継続的インテグレーション):**
コードが変更されるたびに、ビルドやテストを自動で実行する。
**CD(継続的デリバリー):**
変更に対しレビューをして、マージされたら自動でデプロイする。

**CI/CD構築サービス**
**CircleCI:**
ビルド・テスト・デプロイをクラウド上で行ってくれるCI/CDツール。

**全体像**
ローカルからGitHubにコードがアップされるたびに、自動で**ビルドとテスト**を行う。
↓
テストを終え、レビューしコードがマージされたら、自動でdockerコンテナを**デプロイ**する。

**全体の流れ**
1.GitHub
2.CI
3.CD

### GitHub
・GitHub上でリポジトリ作成
・Gitでコードを管理
・.gitignoreを追加
・コミット、プッシュ

**.gitignoreを追加**
.gitignoreとはバージョン管理する必要のないものを管理下から外せる。データベースの接続先情報などの機密情報やインストールしたライブラリのコードなどのバージョンを管理する必要のないものを指定しておく。.gitignoreファイルはRailsがデフォルトで用意してくれているがプロジェクトの直下に置かなければならないので、以下のコードを実行。
```
mv src/.gitignore .
mv src/.gitattributes .
```
またsrc直下に.gitファイルがあると二重管理になりエラーが起こるので削除しておく。
```
rm -rf src/.git/
```
次に.gitignoreファイルを修正。srcの直下に移動したので、全ての文頭にsrcをつけていく。
またMYSQLのデータも管理する必要がないので追加しておく
```
例:
.bundle
↓
src/.bundle

src/db/mysql_data
```

### CI
GitHubにプッシュされたタイミングで、毎回テストが入るようにCIを構築する。
・テストコードを記載
・CircleCIに登録
・プロジェクトを登録
・configを設定
・環境変数を設定
・GitHubにプッシュ
・テストを修正

**テストコードを記載**
users_controller_test.rbに落ちるテストを記載
```
docker-compose up -d
docker-compose exec web bundle exec rake test
```
結果はred

**CircleCIに登録**
GitHubでログイン

**プロジェクトを登録**
Projectsのページにある、現在のアプリのリポジトリからSer Up Projectをクリック。その後、Add Configをクリック。
※CircleCIに関して、.circleci/config.ymlの中に設定を書き込むことで動作する。これがないと動かない。

**configの設定**
直下に.circleciディレクトリ、その下にconfig.ymlファイルを作成。
```
.circleci/config.yml

詳細なコードに関しては動画を参照
・バージョンを設定
・orbs: jobを事前に定義しシェアできる。便利な記法がjobのところで使える
・jobsでbuildとtestを定義(詳細は動画で要確認)
・workflowsのところで、何を行うか(今回はbuildとtest)、どっちを先にするかなどを記載
```
**環境変数を設定**
master.keyがRailsを動かす際に必要だが、.gitignoreに指定されている。流出してはいけないので、それ自体はいいことだが、ソースコードを動かす上ではこの値が必要なので環境変数として指定しておく。
```
heroku config:add RAILS_MASTER_KEY='(ここに値を貼り付け)' -a rails-docker-kyt
```

**GitHubにプッシュ**
add、コミット、プッシュまで(プルリクの流れが説明されてるので、後で見返す)
予想通りテストは落ちている

**テストを修正**
assert falseをtrueに。これでgreenになる。

### CD
mergeされたら自動でHerokuにデプロイされる

**流れ**
・configを修正
・環境変数を設定
・Viewファイルを修正
・GitHubにプッシュ
・マージ、デプロイ

**configを修正**
```
.circleci/config.yml

詳細は動画を確認
・orbsにheroku: circleci/heroku@1.2.3を追記(後々便利な記法が使えるように)
・jobsに以下を追記
- deploy:
    requires:
      - test
      filters:
        branches:
          only: main
(deployのjobはmainブランチに変更があった時のみ実行)
・deployのjobを定義(詳細は動画を確認)
```

**環境変数の設定**
CircleCI上で設定する
Projects→現在のリポジトリ→Project Settings→Environment Variables
→Add Environment Variable→NameとValueを設定
(今回は名前やAPIキー)

**Viewファイルを修正**
deployされたことがわかるように書き換え

**GitHubにプッシュ**

**マージ、デプロイ**