Vagrant上で作成したポートフォリオをDockerの開発環境に移行してみた


## この記事で扱うこと

- Dockerを使用した開発環境の構築
Vagrant上で既で開発してきたポートフォリオをDocker上に移行するイメージです

- Dockerfile、docker-composeなどの表記 
参考までに、Vagrantを使用していた時の開発環境は下記になります
 - OS：macOS Catalina 10.15.4
 - バックエンド：
Ruby 2.5.7、Rails 5.2.4.3
 - 開発環境：
Vagrant 2.2.4、**SQLite**
(※MySQLやPostgreSQLの場合、この後の見本の表記が異なります)
 - 本番環境：
MySQL2、AWS (EC2、RDS for MySQL、Route53)、Nginx、 Puma、Capistrano

![スクリーンショット 2020-09-13 12.40.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652213/5bf72e69-c1cb-78e3-6321-73aae49806b1.png)


- 0からの開発環境の構築で起こりうるエラーと解決した方法  
記事のきっかけにもあるように、ローカル環境の構築自体も初めてのためdocker以前のエラーもいくつかありました。解決のために参考にさせていただいた記事をメインに紹介していきたいと思います。


## この記事で扱わないこと

- Dockerfile、docker-composeなどの基礎知識  
Dockerに関する記事やyoutube多くありますがほとんどがコピペでなんとなく進んでしまうので、最初は自分でドキュメントを読みながら手を動かして全体の仕組みをなんとなく先に掴んでおくのが理解しやすいと思いました。
参考までに自分は下記サイトで学習を始めました。各用語調べながらでも半日〜2日ほどでできる内容かつDockerのイメージが掴めるかと思いますのでおすすめです。  

 - [入門Docker](https://y-ohgi.com/introduction-docker/)：dockerの基礎知識、操作について
 - [Quickstart: Compose and Rails](https://docs.docker.com/compose/rails/)：Ruby on　Railsで具体的にdockerをどう始めるか
 - 学習方法の選定にはKENTA / 雑食系エンジニアTVさんのyoutubeを参考にさせていただきました  
 [ポートフォリオにAWSとDockerとCircleCIを組み込むための学習順序と教材について](https://www.youtube.com/watch?v=LT-dXBUnZdI&t=617s)
 


## 事前準備
- Dockerのインストール  
こちらの公式サイトより行いましょう  
https://hub.docker.com/editions/community/docker-ce-desktop-mac  
ターミナルでバージョンの確認ができれば大丈夫です

```ruby

$ docker version
Client: Docker Engine - Community
 Cloud integration  0.1.18
 Version:           19.03.13
 API version:       1.40
```

- ポートフォリオ(Dockerの開発環境にしたいプロジェクト)をgit cloneしてくる  
記事のきっかけにもありますよう、既にポートフォリオを作成している段階のため環境構築したいものをgithubからローカル上に持ってきましょう

```ruby
$ mkdir docker
 # Desktopからもしくはvagrantを使用していた時のworkディレクトリなどで実行
$ git clone git@github.com:ユーザー名/リポジトリ名.git

```


## 各ファイルの準備
Dockerとプロジェクトの準備ができたので、環境構築に必要なファイルを作成していきます

### ①Dockerfile
```ruby

# 先ほどクローンしたディレクトリ上で
$ touch Dockerfile
```

下記内容を記入していきましょう

```Dockerfile
# 元となるdocker imageを指定。ポートフォリオのrubyのバージョンと合わせましょう
FROM ruby:2.5.7

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

RUN apt-get update && apt-get install -y nodejs --no-install-recommends && rm -rf /var/lib/apt/lists/*
#sqlite3を指定
RUN apt-get update && apt-get install -y sqlite3 --no-install-recommends && rm -rf /var/lib/apt/lists/*
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

# コンテナ起動した時に、コンテナ内でmyappディレクトリを作成
RUN mkdir /myapp

# コンテナ内の作業ディレクトリの指定(上のRUNで作成したディレクトリ)
WORKDIR /myapp

# ホストマシンのgemfileをコピーし、コンテナ内のディレクトリに貼り付け
COPY Gemfile /myapp/Gemfile
COPY Gemfile.lock /myapp/Gemfile.lock

# 貼り付けたgemfileを読み込み。bundlerバージョンが2以降だとこれが必要
ENV BUNDLER_VERSION 2.1.4
RUN gem install bundler
RUN bundle install

# アプリケーション(カレントディレクトリ)をコピーして、コンテナ内のディレクトリに貼り付け
COPY . /myapp

# ポートを3000で公開
EXPOSE 3000

# コンテナにてコマンドを実行
CMD ["rails", "server", "-b", "0.0.0.0", "-p", "3000"]
```

- ENV BUNDLER_VERSION 2.1.4について  
Gefile.lockの一番下にこのような表記があるかと思いますので確認してみて下さい

```Gemfile.lock
BUNDLED WITH
   2.1.4
```

### ②docker-compose.yml

```ruby

# 同じディレクトリ上で
$ touch docker-compose.yml
```

下記内容を記入していきましょう

```docker-compose.yml
version: '3'
services:
  web:
    build:
      context: ./
      dockerfile: Dockerfile 
    # 変更を保存するファイルを指定
    volumes:
      - .:/myapp
    # ポートを指定
    ports:
      - "3000:3000"
    # コンテナの起動を維持させる
    tty: true
    stdin_open: true
```

MySQLやPostgreSQLを使用している場合だと、web＋DBの記述も必要なのですがSQLiteの場合はこちらのみで大丈夫みたいです。まだこの辺りの知識が乏しいため、ゆくゆくはMySQLに変更したいと思っております・・・

### コンテナを起動してみましょう

```ruby
# Dockerfileを元にdocker imageの作成
$ docker-compose build 

# docker container　をバックグランドで起動
$ docker-compose up -d

# コンテナ内にてmigrationを更新
docker-compose run web rails db:migrate
```

これで、いつもの[localhost:3000](http://localhost:3000/)で表示ができればひと段落です！

## 引っかかったエラーと解決した方法
大体ですが引っかかった時系列順に記載していきます。最初にも記載しておりますが、エラー内容とおそらくの原因の考察がサブで、メインが解決参考にさせていただいた記事の紹介になります。ありがとうございました

### ①ActiveRecord::PendingMigrationError Migrations are pending
「dokcer-compose up -d で動いているのにっ・・・！」

```terminal
$ docker-compose up -d                                                  
Creating network "~~~_default" with the default driver
Creating ~~~_web_1 ... done
```
localhost:3000のページへアクセス時に起きたエラーです
コンテナの起動後、docker-compose run web rails db:migrate を行いましょう、あるあるな気がします
参考：[docker-compose upで立ち上げたRailsでActiveRecord::PendingMigrationError Migrations are pending](https://qiita.com/pugiemonn/items/2fe2652cacf5cf0ad3ec)

### ②To install the missing version, run 'gem install bundler:2.1.4'
「bundleで始まるコマンド(デプロイ・Rspecなど)ができないっ・・・！」

```ruby 
Traceback (most recent call last):
    2: from 
    1: from 
~
Could not find 'bundler' (2.1.4) required by your /Users/ユーザー名/ディレクトリ名/Gemfile.lock. (Gem::GemNotFoundException)
To update to the latest version installed on your system, run `bundle update --bundler`.
To install the missing version, run `gem install bundler:2.1.4`
```

Dockerfileを作成した時に確認した、Gemfile.lockの「BUNDLED WITH　2.1.4」がローカル上で見つからないイメージです。
参考：[To install the missing version, run `gem install bundler:2.1.4`と出たときの対処法](https://qiita.com/Kenchiki/items/a54466d44f4164719b8a)

エラー文の通りにインストールを試してみましょう、ダメだった場合は③へ

```ruby
gem install bundler -v 2.1.4
```

### ③ERROR:  While executing gem ... (Gem::FilePermissionError)

```ruby
ERROR:  While executing gem ... (Gem::FilePermissionError)
    You don't have write permissions for the /Library/Ruby/Gems/2.3.0 directory.
```

「gemのインストールの権限がありませんっ・・・！」
sudoを頭につけて行うという記事も多かったですが、そのままインストールよりも「rbenv」というバージョン管理のツールを用いて行う方法の方が今後のことを考えるといい気がします。

参考1：[gem installでpermissionエラーになった時の対応方法](https://qiita.com/nishina555/items/63ebd4a508a09c481150)
参考2：[rbenvによるRubyのインストールからHello, World!まで](https://qiita.com/krtsato/items/3d77501434f623092893)

rbenvの用意ができたら、もう一度やってみましょう

```ruby
gem install bundler -v 2.1.4
```

### ④bundle installができない
「bundleが・・・まだダメっ・・・！」

gem installができたところで、デプロイなどの「bundle~」系を入力するとまだエラー文がでます

```ruby
Install missing gem executables with `bundle install`
```

エラー文に従って、bundle installをしてみると

```ruby
# 長いため、最後の部分だけ抜粋
An error occurred while installing mysql2 (0.5.3), and Bundler cannot continue.
Make sure that `gem install mysql2 -v '0.5.3' --source 'https://rubygems.org/'` succeeds before bundling.

In Gemfile:
  mysql2
```

mysql周辺のエラーですが、原因は自力ではわかりませんでした。
下記の参考にさせていただいた記事通りで私は解決できました、本当にありがたい。

参考：[【Rails】MySQL2がbundle installできない時の対応方法](https://qiita.com/fukuda_fu/items/463a39406ce713396403)

これでデプロイはできるようになると思います。

### ⑤Could not find a JavaScript runtime ~

「動いているけどっ・・・！」

Rspecを走らせた時のエラーです

```ruby
$ bundle exec rspec spec/ --format documentation

An error occurred while loading ./spec/models/cart_item_spec.rb.
Failure/Error: require File.expand_path('../config/environment', __dir__)

ExecJS::RuntimeUnavailable:
  Could not find a JavaScript runtime. See https://github.com/rails/execjs for a list of available runtimes.
```

githubに飛んでみると、ExecJSのどれかをダウンロードするよう勧めています。
gemをいれる方法もありましたが、[Node.js](https://nodejs.org/en/)を選択してダウンロードに進めばスムーズに解決できました。

これでRspecもできるようになりました。

### 終わりに
vagrantからdockerへの移行の手順とエラーの紹介をさせていただきました。
まだまだ勉強不足なところも多いため、dockerを十分に活用できていない点が多くあると改めて思いました。
参考にさせていただいた記事が多かったこともあり、当初思っていたよりも短い時間で導入まではできたので挑戦してみて良かったと感じています。

初投稿ということもあり、わかりにくい部分もあったかとは思いますが最後までみていただきありがとうございました！