Docker+VSCode(Remote Container)によるRuby on Railsの開発環境構築

# はじめに
諸事情により，Webアプリの開発を行うことになったので，DockerとVSCodeを使って，Ruby on Railsの開発環境を構築しました．備忘録として，その記録を残しておきたいと思います．
ちなみに，この方法で構築すると基本的にVSCode内で完結します．いちいちターミナルで，`docker-compose up`して`docker-compose run web rails db:create`と入力する必要はなく，VSCodeのターミナル上で`rails db:create`できます．

# VSCodeのRemote Container（Development）
今回はVSCodeの拡張機能であるRemote Containerを使います．このRemote Containerを使えば，なんとDockerで立ち上げたコンテナの環境にVSCodeでアクセスして，コンテナ内のプログラムファイルをVSCode上で編集やらデバッグやらを実行することができます．ちなみに，似たような拡張機能にRemote Developmentなるものがありますが，これはDockerのコンテナ以外のSSH，WSLの機能も含んだ拡張機能パックになるそうです．なので，Remote Developmentの中にRemote Container，Remote SSH，Remote WSLが含まれていて，Dockerに関係するのはRemote Containerということだと思います．

# 手順
## VSCodeの拡張機能諸々のインストール
### Remote Container（Development）のインストール

私はどうせならということで，Remote Developmentをインストールしました．
VSCodeのExtensionsからRemote Development（Container）を検索して，インストールします．

![Remote Development.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253935/05d74cb4-8358-ac41-57a0-4049cab67cf6.png)

### Rubyのデバッグ用拡張機能インストール
同様にrubyと検索して，インストールします．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253935/4b13d859-20c9-7c20-43be-5725f479b975.png)

## Ruby on Railsの作業ディレクトリの準備
### 作業ディレクトリの作成
次に実際のRuby on Railsのコードを置く作業ディレクトリを準備していきます．まず，任意の場所に作業用ディレクトリを作成します．

```bash
mkdir path/to/{name}
cd path/to/{name}
```

### DockerFileの作成

次に`DockerFile`を作成します．`{name}`には先程の作業ディレクトリの名前を入れてください．

```bash
vi DockerFile
```

```dockerfile:DockerFile
# Pull ruby image
FROM ruby:2.5.3

# Install 
RUN apt-get update -qq && \
    apt-get install -y build-essential \ 
                       libpq-dev \        
                       nodejs           

# Create working directory
ENV APP_ROOT /{name}
RUN mkdir $APP_ROOT

# Set working directory as APP_ROOT
WORKDIR $APP_ROOT

# Add Gemfile
ADD ./Gemfile $APP_ROOT/Gemfile
ADD ./Gemfile.lock $APP_ROOT/Gemfile.lock

# Install Gemfile's bundle
RUN bundle install
ADD . $APP_ROOT
```

### GemfileとGemfile.lockの作成
`Gemfile.lock`は空でOKです．

```bash
touch Gemfile.lock
vi Gemfile
```

```:Gemfile
source 'https://rubygems.org'
gem 'rails', '5.2.2'
```

### docker-compose.ymlの作成
`{password}`はDBにアクセスする際の任意のパスワード，`{name}`は上記の作業ディレクトリ名．注意点として，ポートフォワーディングの設定で，ローカルのポート4306からDBのポート3306に割り当てている点です．これは，DBのデフォルトポートが3306なので，ローカル側で既にこのポートを使っている場合の競合を防ぐようにしています．

```bash
vi docker-compose.yml
```

```yml:docker-compose.yml
version: '3'
services:
  db:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: {password}
      MYSQL_DATABASE: root
    ports:
      - "4306:3306"

  web:
    build: .
    command: /bin/sh #-c "rm -f tmp/pids/server.pid"
    stdin_open: true
    tty: true
    depends_on:
      - db
    volumes:
      - .:/{name}
    ports:
      - "3000:3000"
```

## Remote Containerの設定
VSCodeで先程作成した作業ディレクトリを開きます（`DockerFile`や`docker-compose.yml`があるディレクトリ）．

### 設定ファイルの作成・編集
左下のアイコンをクリックして，`Add Development Container Configuration Files `> `From docker-compose.yml`>`web`選択．
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253935/b87be771-bee4-1c6f-e132-419ee3430ea7.png)



すると，`.devcontainer`ディレクトリが作成されるので，`devcontainer.json`を編集します．
注意点としては，
- `.devcontainer`ディレクトリに作成される**`docker-compose.yml`は使いません**．（削除して構いません）
- `{name}`は作業ディレクトリ名を指定しています．
- `extensions`にはデバッグ用の拡張機能のIDを指定しています．
- `addPort`でポートフォワーディングの設定をしています．（DeviseでGmailと連携したい場合などは，ここに`"587:587"`を追記します．）

```json-doc:.devcontainer/devcontainer.json
// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
{
	"name": "Existing Docker Compose (Extend)",

	// Update the 'dockerComposeFile' list if you have more compose files or use different names.
	// The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
	"dockerComposeFile": [
		"../docker-compose.yml",
		//"docker-compose.yml"
	],

	// The 'service' property is the name of the service for the container that VS Code should
	// use. Update this value and .devcontainer/docker-compose.yml to the real service name.
	"service": "web",

	// The optional 'workspaceFolder' property is the path VS Code should open by default when
	// connected. This is typically a file mount in .devcontainer/docker-compose.yml
	"workspaceFolder": "/{name}",

	// Add the IDs of extensions you want installed when the container is created.
	"extensions": ["rebornix.ruby"],

	// Use 'forwardPorts' to make a list of ports inside the container available locally.
	// "forwardPorts": [],
	"addPort": ["3000:3000", "4306:3306"]

}
```

## Railsプロジェクトの準備
### コンテナを開く
左下のアイコンをクリックして，`Reopen in Container`クリックで，Dockerのイメージやコンテナの諸々のbuildが始まります．

※ここで，エラーが出る場合は一度イメージやコンテナを削除するとうまくいくかもしれません．

```bash
docker stop $(docker ps -q)
docker rm $(docker ps -aq)
docker rmi $(docker images -q)
```

### Railsプロジェクトの作成
buildが終わると，Terminalタブで，＋キーを押してBashを開きます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253935/1b9e7eef-cd05-79b8-43fe-6e8ce5f3d942.png)

そして，以下のコマンドを打って，Railsのプロジェクトを作成します．

```bash
rails new . --force --database=mysql --skip-bundle
```

### DBの設定
DBの設定を行います．`{password}`には`docker-compose.yml`で設定したDBのパスワードを指定します．

```bash
vi config/database.yml
```

```yml:config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: {password} # MYSQL_ROOT_PASSWORD in docker-compose.yml
  host: db # service name in docker-compose.yml
```

### DBの作成
以下のコマンドでDBを作成します．

```bash
bundle update
rails db:create
```

### サーバーの起動
あとは，以下のコマンドでサーバーを起動し，[http://localhost:3000/](http://localhost:3000/)にアクセスして例の画面が出れば完了です！
※サーバーの起動中に`rails`コマンドを打ちたい場合は，VSCodeのTerminalタブで＋を打つと新たなターミナルを開けます．

```bash
rails s -b 0.0.0.0 -p 3000
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253935/e92ecea0-4284-2a26-110b-6fd5ce516fdb.png)

# おまけ
## Sequel Pro
DBの可視化GUIであるSequel Proもぽートフォワーディングしているので，使えます．[docker-compose.ymlの作成](#docker-compose.ymlの作成)で指定したユーザーネーム，パスワード，ポートを指定してあげると，アクセスできます．Hostは`localhost`ではなく，`127.0.0.1`にする点に注意してください．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253935/9a0794eb-99bf-9328-2512-07779ddaa7ec.png)

あとは，`Choose Database`から`{name}_development`を選ぶと開発環境用のDBを開けます．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253935/28303b47-8b62-c3a0-b081-9f66ae31b9cf.png)

## デバッグ
### `launch.json`の編集
VSCodeでデバッグを行いたい場合は，Run > Add Configuration > Docker ~ (たぶんfor Container)をクリックすると，`launch.json`が開かれます．
そして，以下のように記入します．

```json-doc:launch.json
{
  "version": "0.2.0",
  "configurations": [
    {
    "name": "Rails App",
    "type": "Ruby",
    "request": "launch",
    "cwd": "${workspaceRoot}", 
    "program": "bin/rails",
    "args": ["s", "-b", "0.0.0.0", "-p", "3000"], 
    }
  ]
}
```

### デバッグの実行
再起動して，ImageをRebuildして，`F5`でデバッグできます！

## エラー
### `A server is already running. Check /app/tmp/pids/server.pid.`が出る

```bash
rm -f tmp/pids/server.pid
```


# 参考
- [Dockerで立ち上げた開発環境をVS Codeで開く!](https://qiita.com/yoskeoka/items/01c52c069123e0298660)
- [丁寧すぎるDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac)](https://qiita.com/azul915/items/5b7063cbc80192343fc0)
- [RailsプロジェクトでVisual Studio Code Remote Container使ってみた](https://simple-minds-think-alike.hatenablog.com/entry/vscode-remote-container-rails)