【Docker】駆け出しエンジニアがDocker公式チュートリアルに躓きまくった件

可能な限りDocker公式が提供しているチュートリアルに沿って、Ruby on rails + MySQLの環境構築を行います。

## はじめに
こんにちわ、2022年4月よりエンジニアとして働き始めたばかりのUdai(ゆうだい)です！

今回は、Docker公式チュートリアルを行った際に大量のWorningやErrorに苦しんだので、これからチュートリアルに訪れる初学者の同士が同様に躓かないようにと思い筆を取りました。

如何せん、私はまだまだ駆け出しのエンジニアですので、本記事はDockerを知らない人や名前だけは知っているよといった初学者向けの記事となっております。そのため、粒度はかなり細かいものとなっております。

また、それでも一読してくださる寛大なシニアなエンジニアの先輩方は記事の内容に誤りがございましたら、遠慮なく指摘・訂正していただけると幸いです。

※ 注意点
- チュートリアルとデータベースが異なります。(PostgreSQLからMySQLへ変更)
- 手早く同上の環境構築を行いたい方は他の素晴らしい記事を参照してください。

## 対象
- 実際にDockerを動かしたい人
- Docker公式チュートリアルがエラーばかりで進まなかった人
- 何となく動くまでフローが知りたい人

## 動作環境
- macOS Monterey 12.2.1
- Apple M1 Chip
- Docker Compose v2.4.1

## 前提知識
- dockerについてのなんとなくの知識（イメージ・コンテナ）
- CLIでの操作

※ 分からない点がございましたら、コメントにちょこっと書いていただけると自分の理解の範囲で記事を随時更新します。


## なぜ公式チュートリアルなのに上手く動作しないのか？
### 更新頻度が低いため

これはどうしようもない話なのですが、公式チュートリアルというのはその対象について初めて書かれる指南書になる訳です。なので、その対象のバージョンが更新されていくにつれて書かれている内容と実環境での齟齬が発生します。なので、私たち初学者が出来ることはチュートリアルが上手くいかないことを嘆き、そこで挫折するのではなく、なぜそのようなところでErrorが出るのかという点を切り口にして学びを深めていくことです。


## チュートリアル(1)：定義ファイルの作成
ここから、実際のチュートリアルと変更点を挙げながら説明を行なっていきます。
### 1. Dockerfileの作成

CLIをお使いの方はTerminalで`touch Dockerfile`とGUIをお使いの方はFinderやVScodeなどのエディタ上でDockerfileを作成してください(拡張子は必要ありません)

作成後、以下の内容をDockerfileに記述してください。
```Dockerfile:Dockerfile
FROM ruby:2.7 

RUN apt-get update -qq && apt-get install -y nodejs postgresql-client

WORKDIR /myapp
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock
RUN bundle install
COPY . /myapp/

COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["rails", "server", "-b", "0.0.0.0"]
```
Dockerfileはイメージの管理を行なうファイルです。

変更点としてはrubyのversionを2.5から2.7に変更しております。

また多くの記事でWORKDIRコマンドの前にRUN mkdir /directory_nameと記述している場合がありますが、WORKDIRコマンドは参照するディレクトリがない場合に勝手にディレクトリを作成してくれるので記述する必要がありません。各項目の詳しい説明は別記事にて行います。

### 2. Gemfileの作成
1.と同様の方法にてGemfileを作成してください。Gemfileも拡張子をつける必要がありません。
作成後、以下の内容をGemfileに記述してください。

```Gemfile:Gemfile
source 'https://rubygems.org'
gem 'rails', '~>5'
```
Gemfileは、gem(RubyGems)と呼ばれるrubyのパッケージ管理システムの依存関係を記述するためのファイルです。よりわかりやすい記事があるので詳しく知りたい人はそちらを参照してください。
https://qiita.com/nishina555/items/1b343d368c5ecec6aecf

### 3. Gemfile.lockの作成
上記と同様にてGemfile.lockを作成してください。こちらは上記の記事にもあるとおり、bundleコマンド実行後に更新されるので、特に内容は空で大丈夫です！

### 4. docker-compose.yml
ファイルを作成するのは最後になります！docker-copose.ymlファイルを作成し、以下を記述してください。
```docker-compose.yml
version: "3.9"
services:
  db:
　　　　　　　　#　追記：Apple M1 Chipに対応させるため
    platform: linux/x86_64

    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: root
    volumes:
      - ./tmp/db:/var/lib/mysql
    ports:
      - "3306:3306"

  web:
    build: .
    command: bash -c "rm -f tmp/pids/server.pid && bundle exec rails s -p 3000 -b '0.0.0.0'"
    volumes:
      - .:/myapp
    ports:
      - "3000:3000"
    depends_on:
      - db 
```

公式はdocker-compose.ymlに対して`is where the magic happens / 魔法がかかる場所`と比喩していてすごくオサレだなと感じました。そうです、docker-composeの肝となるファイルなのです！docker-composeではそれぞれ異なるコンテナを一元管理するための記述が行われています。

ここでは、Apple M1 Chipに対応させるための追記を行なっております。

## チュートリアル(2)：プロジェクトのビルド

まずは、やるべきことを先に記載したのちに、どのような問題が起こったのかを記載します。

### 1. docker-compose run

以下のコマンドをCLI(ターミナル等)で実行してください。docker-compose runを行うことで設定ファイルで定義された通りに、新たなコンテナを作成します。

```bash
docker-compose run web rails new . --force --no-deps --database=mysql
```

### 2. docker-compose build

以下のコマンドを実行してください。docker-compose buildを行うことでサービスのビルドを実行します。

```bash
docker-compose build
```

### 3.config/database.ymlファイルの書き換え

```bash
# MySQL. Versions 5.1.10 and up are supported.
#
# Install the MySQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem 'mysql2'
#
# And be sure to use new-style password hashing:
#   https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
#
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password:
  host: localhost

development:
  <<: *default
  database: myapp_development
  host: db
  username: root
  password: password
![スクリーンショット 2022-04-13 17.13.29（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615389/cf95b545-6bfe-b2cd-8175-c5021d77c217.png)

test:
  <<: *default
  database: myapp_test
  host: db
  username: root
  password: password

production:
  <<: *default
  database: myapp_production
  username: myapp
  password: <%= ENV['MYAPP_DATABASE_PASSWORD'] %>
```

### 4. データベースの作成

```bash
docker-compose run web rails db:create
```

### 5. コンテナの軌道

```bash
docker-compose up
```

ここまで出来たら、ブラウザでlocalhost:3000にアクセスしてもらうと以下のようなページが表示されたら成功です！
![スクリーンショット 2022-04-13 17.13.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2615389/17862b20-b467-1040-bfff-04212673aac4.png)

え？これで終わり...？めっちゃ簡単じゃんw
って思っている時期が自分にもありました。ErrorやWarningに苦しめられたのはここからです笑

## どのようなエラーが起こったのか？
ここからErrorやWarningの原因が相互に関係して発生したと考えられるので、問題を羅列する形で記載します。

### 1. bundle installが走らない問題

```bash
$ docker-compose run web rails new . --force --no-deps --database=mysql
[+] Running 1/0
 ⠿ Container exec-docker-db-1  Running                                     0.0s
[+] Building 3.1s (14/16)                                                       
~~ 省略 ~~
 => ERROR [6/8] RUN bundle install                                         0.3s
------
 > [6/8] RUN bundle install:
#0 0.303 You must use Bundler 2 or greater with this lockfile.
------
failed to solve: executor failed running [/bin/sh -c bundle install]: exit code: 20
```

ここでの解決法は、以下の問題の依存関係が原因となって起こっていると考えられるので一度スキップします...ごめんなさい。ちなみに、自分は/bin/sh -c bundle installを行なっても治りませんでした。

### 2. ローカルのRubyのVersionが異なる問題

次に、色々修正を加えていると１のErrorは出なくなり以下のErrorが出ました。

```bash
$ docker-compose run web rails new . --force --no-deps --database=mysql
~~ 省略 ~~
=> [6/8] RUN bundle install                                               7.1s
=> [7/8] COPY entrypoint.sh /usr/bin/                                     0.0s
~~ 省略 ~~
Running `bundle update` will rebuild your snapshot from scratch, using only
the gems in your Gemfile, which may resolve the conflict.
         run  bundle exec spring binstub --all
bundler: command not found: spring
Install missing gem executables with `bundle install`

```

ここで言われた通りに`bundle exec spring binstub -all`を行うとローカルのrubyのversionが異なると言われたので、versionを2.7.6に合わせました。(これに関しても詳しい方法は別の記事にまとめます。)

### 3. mysql2が見つからない問題

```bash
$ docker-compose run web rails new . --force --no-deps --database=mysql
[+] Running 1/0
 ⠿ Container exec-docker-db-1  Running                                     0.0s
Could not find gem 'mysql2 (>= 0.4.4, < 0.6.0)' in any of the gem sources listed in your Gemfile.
Run `bundle install` to install missing gems.
```

そもそもgemの’mysql2’が本当にないか自分の目で確かめに... 結果：本当になかった(当たり前w)

```bash
gem list
#~~~Cound no find gem 'mysql2'~~~
```

また、gemを使って直接インストールを試みたが失敗

```bash
gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'
ld: library not found for -lssl
clang: error: linker command failed with exit code 1 (use -v to see invocation)
make: *** [mysql2.bundle] Error 1

make failed, exit code 2

Gem files will remain installed in /Users/yudaikato/.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/gems/mysql2-0.5.3 for inspection.
Results logged to /Users/yudaikato/.rbenv/versions/2.7.6/lib/ruby/gems/2.7.0/extensions/arm64-darwin-21/2.7.0/mysql2-0.5.3/gem_make.out
```
ところが、ここで思わぬ収穫が...
opensslのPATHが通っていないことが明らかになり１と３の問題を同時に解決することができた！

```bash
brew info openssl
~~ 省略 ~~
For compilers to find openssl@3 you may need to set:
  export LDFLAGS="-L/opt/homebrew/opt/openssl@3/lib"
  export CPPFLAGS="-I/opt/homebrew/opt/openssl@3/include"
~~ 省略 ~~
```
この２行のexport文を直接.bundle/configファイルを書き換えた
```bash
vi .bundle/config
```
すると、大方エラーが消えて、あとはエラーが出る前に作成したコンテナを再度削除し作り直すことでチュートリアル２のコマンドを正常に実行できるようになりました！やった〜！

## 参考になった記事orサイト
[https://docs.docker.com/compose/profiles/](https://docs.docker.com/compose/profiles/)
[https://qiita.com/ryouzi/items/b89965b76cef546e3046](https://qiita.com/ryouzi/items/b89965b76cef546e3046)
[https://qiita.com/croquette0212/items/7b99d9339fd773ddf20b](https://qiita.com/croquette0212/items/7b99d9339fd773ddf20b)

## 謝辞
本記事を書くにあたりまして、ご協力をいただきました皆様に厚くお礼申し上げます。