Dockerで開発環境を構築する。 Rails6.1/postgreSQL14
ローカルでの開発環境をdocker上で再現したい!
コンテナ基盤での本番環境構築やCIなどの導入も見据えて、dockerを学習し始めました。第一歩として、お手元のPCで開発していた環境をコンテナでほぼ再現させた記録です。
なぜ書くのか?
自身で開発環境をdocker上で構築しようと思った際に、Docker公式のquick startではあまりに簡素であったり、DBの違いによりインストールの仕方が曖昧であったりと、自分が求めていた情報にピッタリ合うものがなかったためです。
今回構築する開発環境
- (PC)mac Monterey 12.6
- ruby 3.0.4
- rails 6.1.7
- postgreSQL 14
- chromeブラウザ
想定する読者層
- 自分のローカルPC上にrubyやrailsをインストールして簡単にでも開発を行ったことがある人
- docker基本的な概念は理解したつもりだけど、実際に自分の開発環境はどうやって構築するのかわからない人(過去の私)
概要
基本的なdockerの仕組みや、コマンドについては適宜補足しますが、あくまで構成と設定に主眼を置き、その過程でのつまづいたところを共有できればと思ってます。
つまり、
Dockerfileとdocker-compose.ymlをどう書くのか?
ということについて書ければと思っています。
今回は、既存のアプリの開発環境を移行する形でやっていきます。
また、技術記事投稿に関しても慣れていないため、NGな部分があれば、ご指摘ください。
前提
開発環境って何ができないといけないのか?
ローカルPCで開発する際に、あたり前すぎて意識していなかったことをあらためて洗い出してみました。
- ソースコードの管理。
コンテナ上でサーバを立ち上げるけど、エディタでいじるコードはコンテナ・ローカル間で同期していてほしい。 - DBデータの保持。開発環境でのDBのデータは
docker-compose down
しても残っててほしい。 - アプリ側からDBに接続できること。
アプリのコンテナに潜ってpsql
コマンドで接続したり、Railsの設定でDBと通信ができるようにする。 - デバッグ
ソースコード中にbyebugメソッドを差し込んだ時に、ログを止めてプロンプト立ち上がってほしい。 - テストが実行できること
アプリコンテナの中に入って、bin/rails spec(もしくはbundle exec rspec)
でテストができること
以上のことがdocker上で開発する上でやりたいことになるかと思います。
用意するコンテナ(service)と設定について
それを踏まえて必要になるコンテナ達、その他諸々を挙げてみます。
-
アプリケーションサーバーとして機能するコンテナ
一言でいうと、rails server
コマンドでローカルサーバー起動するためのコンテナです。 詳細は後述します。 - rails固有の問題を解決するためのシェルスクリプト
-
コンテナ同士をオーケストレートするためのdocker-compose.yml
ポートの設定やvolumeの同期などをして、コンテナ間やコンテナとローカル(お手元PC)間がうまくやりとりできるようにします。-
db用のコンテナ
データベースとしての役割だけを持たせたコンテナです。先ほどのアプリサーバとなるコンテナと通信できるように設定します。 -
(永続化させたいデータを保存するvolume コンテナの作成)
dockerでは、DBのデータなど永続化させたいデータを扱うために、volumeという仕組みがありますが、今回は中でもvolumeコンテナ(データを保存しておくためだけのコンテナ)を使っていきます。 -
capybaraを使ってユーザのブラウザ動作を再現するためのドライバーとブラウザ
コンテナ環境にはデフォルトでブラウザが入っている訳ではありません。webブラウザが導入されていないと、systemspec等のE2Eテストでブラウザの動作を再現するテストが実行できません。そのため、ブラウザも入れます。(もし、テストなんて必要ないという方は飛ばしてください。)
-
db用のコンテナ
-
現状docker-composeで解消できるのはよく分かってない要素
作成したコンテナに、自ら潜っていくつか手動でインストールする。
e.g.) rails db:create ….、bin/rails webpacker install
それでは、早速ファイルを作成しながら詳しく見ていきます。
ディレクトリ構成
今回は、以下のようなディレクトリ構成にしてみました。
.(アプリのルート)
├── app
├── bin
├── config
├── ..(その他railsアプリに関するディレクトリ群)
├── ..
├── Gemfile
├── Gemfile.lock
├── docker
| ├── Dockerfile
| └── entrypoint.sh (# 前述6.のスクリプト)
└── docker-compose.yml
"全体"としての環境を束ねるdocker-compose.yml
はappのルートディレクトリにおいて、dockerディレクトリには、nginxやpumaなども導入するようなより本格的な構築をすることになった時に、docker/nginx/Dockefileなどと、なんのためのDockefileなのかを区別しやすくするためです。
僕が調べている時には、以下のような構成も見かけました。
webディレクトリ階下にアプリのソースコードを配置すると、github等にpushする際にも、機密情報が含まれたdocker関連ファイルを.gitignoreをいじらずとも管理できるのかな?とも思いました。
app
└─ docker
├── nginx
│ ├── Dockerfile
│ └── nginx.conf
└── web
├── Dockerfile
├── entrypoint.sh
├── start-server.sh
└──(例えばここ)
参考にさせていただいた記事)
0. ディレクトリとソースコードの用意。
自分のソースコードをcloneするなり、ローカルにあるものを移すなりして、作業するディレクトリを準備してください。
# e.g.)
$ git clone ....
1. アプリケーションサーバの作成
Dockerfileを記述して、rails server
したらアプリにアクセスできるような環境を作っていきます。
ベースイメージにはdocker公式のrubyイメージを選択して、rubyやrailsだけでなくlinuxのパッケージも色々必要になるので、そちらも含めて記述します。
以下必要になるパッケージの一覧です。
- 一般的なLinuxベースのパッケージ達。(vim、curl, git etc…)
- データベースのクライアントパッケージ(mysql-client等にあたるもの)
- railsはGemfileに記述する形でinstall
- Rails 6.0系でデフォルトになったwebpackerを扱うために、nodejsとyarnもインストール
Dockerfile
FROM ruby:3.0.4
ARG PG_MAJOR=14
ARG NODE_MAJOR=14
ARG YARN_VERSION=1.22.19
ARG BUNDLED_WITH=2.2.33
# 一般的な依存関係をインストール
RUN apt-get update -qq\
&& apt-get install -yq --no-install-recommends\
build-essential\
gnupg2\
curl\
less\
git\
vim\
&& apt-get clean\
&& rm -rf /var/cache/apt/archives/*\
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*\
&& truncate -s 0 /var/log/*log
# posgresqlの依存関係をインストール
RUN curl -sSL https://www.postgresql.org/media/keys/ACCC4CF8.asc |\
gpg --dearmor -o /usr/share/keyrings/postgres-archive-keyring.gpg\
&& echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/postgres-archive-keyring.gpg] https://apt.postgresql.org/pub/repos/apt/"\
bullseye-pgdg main $PG_MAJOR | tee /etc/apt/sources.list.d/postgres.list > /dev/null
RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade &&\
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends\
libpq-dev\
postgresql-client-$PG_MAJOR\
&& apt-get clean\
&& rm -rf /var/cache/apt/archives/*\
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*\
&& truncate -s 0 /var/log/*log
# NodeJSとYarnもインストール
RUN curl -sL https://deb.nodesource.com/setup_$NODE_MAJOR.x | bash -
RUN apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get -yq dist-upgrade &&\
DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends\
nodejs\
&& apt-get clean\
&& rm -rf /var/cache/apt/archives/*\
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*\
&& truncate -s 0 /var/log/*log
RUN npm install -g yarn@$YARN_VERSION
RUN mkdir /your_app
WORKDIR /your_app
# railsなどgemの記載がされているものを想定してます。
COPY Gemfile /your_app
COPY Gemfile.lock /your_app
RUN gem install bundler -v $BUNDLED_WITH
RUN bundle install
COPY docker/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT [ "entrypoint.sh" ]
EXPOSE 3000
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
参考にさせていただいた記事)
具体的な内容に触れていきます。
FROM ruby:3.0.4
ARG PG_MAJOR=14
ARG NODE_MAJOR=14
ARG YARN_VERSION=1.22.19
ARG BUNDLED_WITH=2.2.33
まずベースのイメージにruby:3.0.4
を指定しました。
(docker hubのソースコードによると、tag:3.0.4
を指定した場合には、debian系linuxのbullseyeを指定しているようです。そのためパッケージマネージャは必然的にapt
になります)
次にARG
を使っています。
ARGとは
Dockerfileからイメージをビルド時(docker buildコマンドを打つ時)に、使用できる変数を指定できる。ENV
とは異なり、実際に起動したコンテナで扱う環境変数にはならず、飽くまでビルド時の一時的な引数です。
このように記述した理由は2つあります。まず、$NODE_MAJOR
の形で、Dockerfile内で以後参照できることを生かして、ファイルの冒頭で必要なパッケージのバージョンを把握できるようにすること。また、テンプレートとして今後使い回すことを考えた時に管理が容易になるためです。
Dockerfileの細かい仕様について
FROM
以降では引数がリセットされてしまうので、もしFROM
以前にARG
を使用している場合には再宣言が必要になります
次に一般的なlinuxパッケージを依存関係も含めてインストールしています。
# 一般的な依存関係をインストール
RUN apt-get update -qq\
&& apt-get install -yq --no-install-recommends\
build-essential\
gnupg2\
curl\
less\
git\
vim\
&& apt-get clean\
&& rm -rf /var/cache/apt/archives/*\
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*\
&& truncate -s 0 /var/log/*log
apt-getを使ってインストールしていきます。
&&
以降の呪文の部分ですが、
apt-get clean
で/var/cache/apt以下のファイル(パッケージのローカルリポジトリ)を削除
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
で、インストール中に作成される一時ファイルを削除
truncate -s 0 /var/log/*log
で、ログファイルもファイルサイズを0にしているようです。
これらの工夫はDockerレイヤに余計なデータを残さないようにして、コンテナ自体の容量を節約する目的があるみたいです。
次に、postgreSQLとNode+ yarnをインストールしていきますが、
apt
コマンドでPostgreSQLやNodeJSをインストールするには、それらのdebパッケージをソース一覧に追加しておく必要があるので、その分の記述が追加されています。
以降は、他の記事でもよく見かけるような記述になっていますが、
RUN mkdir /your_app
WORKDIR /your_app
COPY Gemfile /your_app #自身のGemfileをdocker側で参照できるように
COPY Gemfile.lock /your_app # 同上
RUN gem install bundler -v $BUNDLED_WITH
RUN bundle install
まず、mkdir
で作業ディレクトリを作成して、WORKDIR
で作業ディレクトリを指定します。その後既存のGemfileとGemfile.lockをコンテナ上にコピーさせてbundle install
しています。今回は、既存のアプリが存在する体なので、この手順で導入できます。
この時、アプリのルートからみるとGemfileは../Gemfile
のパス指定が良い気がしますが、これだとうまく動作しません。詳細はdocker-compose.ymlの記述の際に説明します。
もし、新規アプリの場合には
local上に、Gemfileと白紙のGemfile.lockを準備して,バンドラー経由でrailsをインストールすると良いと思います。
source "https://rubygems.org"
git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
gem 'rails', '~>7.0.2'
補足
- bundlerのバージョン指定について
bundler2.3.0以降ではGemfile.lockのBUNDLED_WITH
部分の記述で指定されたバージョンのbundlerが自動でインストールされるため、手動で指定する必要がないそうです。
https://github.com/rubygems/rubygems/pull/4076
最後に、毎回コンテナを起動するたびに走らせたいシェルスクリプトを登録して、CMDの部分で、毎回サーバーを起動するようにしています。
COPY docker/entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT [ "entrypoint.sh" ]
EXPOSE 3000
CMD ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
entrypoint.shの中身については後述しますが、そうゆうシェルスクリプトをCOPY
でコンテナに乗せて、実行権限を付与(RUN chmod...
)して、コンテナが起動した際に実行されるよう指定しています。(ENTRYPOINT
)
EXPOSEは、コンテナ起動時に公開するポートを記述しています。
が、実際に公開するコマンドでという意味合いではなく、ドキュメンテーションとしての役割に留まります。
最後に、rails server
をするように指定しています。CMD
の記述をコマンドライン上で表現すると
$ bundle exec rails server -b 0.0.0.0
となりますが、この-b 0.0.0.0
オプションの意味は、一言でいうと、0.0.0.0のIPアドレスを結びつける(bindする)。ということになります。(defaultでは、自分自身であるlocalhost(127.0.0.1)だけになってます。)
このように指定するのは、Docker上でコンテナを起動するときに、その起動中のプロセスは外から独立したひとつのマシンのように扱われることに起因しています。そのため、コンテナから見ると外部にあたるお手元のPC環境でlocalhost(127.0.0.1)を指定してもアクセスはできません。(ローカル上では、rails s
されてないから)
ここから先の理解は少し、あやふやなのですが、、、
0.0.0.0
というのは、LAN(Local Area Network)上の任意のプライベートIPアドレスを意味するらしいです。
つまり、コンテナ上で
$ bundle exec rails server -b 0.0.0.0
することは、Railsのサーバーをコンテナ上で起動し、かつ "LAN内の任意のIPアドレスからのアクセス許可する" ということになるかと思います。これによって、コンテナから見ると外部にあたるお手元のPCから、起動しているコンテナのホスト名とポート番号でアクセスできるようになるはずです。
rails sコマンドとbindingオプションについて参考にさせていただいた記事
2.rails固有の問題を解決するためのシェルスクリプトを仕込む
先ほどの例に上がったentrypoint.sh
という名前のシェルスクリプトについて説明してみます。
entrypoint.sh
#!/bin/bash
# setコマンドで、エラーが発生したら即時終了するように
set -e
# server.pidというファイルを削除
rm -f /your_app/tmp/pids/server.pid
# CMDの部分に渡されたコマンドを実行する(rails s -b ..)
exec "$@"
ここでおこなっているのは、rails sをする度に作成されるserverに関するプロセスIDの削除です。これを行わないとコンテナを再起動した際に、Railsが前回の起動時に生成したserver.pidを読み込んでしまうことによって起きる、A server is already running
というエラーを解消しています。
以上で、Railsに関する設定は大方終わりになります。次は、実際にコンテナ同時を協調できるようにするためのdocker-compose.ymlについて書いていきます。
3. コンテナ同士を協調させる
docker-compose.yml
ここから、docker-compose.ymlを書いていきますが、先に全体としてどのような設定になったのかを載せておきます。
version: '3'
services:
# postgresql
postgres_db:
image: postgres:14
volumes:
- db-data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: postgres
# Rails
rails:
build:
context: .
dockerfile: ./docker/Dockerfile
volumes:
- .:/your_app
- node_modules:/your_app/node_modules
- bundle:/usr/local/bundle
stdin_open: true
tty: true
ports:
- 3000:3000
depends_on:
- postgres_db
- chrome
# chrome
chrome:
image: selenium/standalone-chrome:latest
ports:
- 4444:4444
logging:
driver: none
volumes:
node_modules:
db-data:
bundle:
簡単にそれぞれの項目について説明すると、
-
version
で、使用するdocker-composeの書式のversionを選択 -
services
階下に、rails,postgres_db,chromeの3つのサービスを配置しています。この文脈では、サービスとコンテナはほぼ同義です。 - 3つのコンテナの階下には、使用するイメージやportsの設定など、それぞれのコンテナについての設定情報が書かれています。
- 最後の
volumes
の部分は、volumeコンテナ(データを永続化するためだけのコンテナ)の定義を書いています。postgres_dbやrailsコンテナの説明の際に適宜説明を加えます。
3-1. DB用のコンテナ
今回は、postgreSQL14にしたかったので、以下のようになりました。
services:
# postgresql
postgres_db: # サービスに名前を付ける
image: postgres:14 # ベースにするイメージを指定
volumes:
- db-data:/var/lib/postgresql/data
environment: # DB用のpostgres_dbのコンテナがもつ環境変数の設定
POSTGRES_PASSWORD: postgres
- volumesの部分
<data volumeコンテナ名>:<コンテナ上のパス>
の形式で、volumeコンテナをコンテナ上のファイルにマウントしています。(ここではdb-dataと名づけました)
volumeを使う方法として、<volumeコンテナ名>の代わりに、
<ホスト側のパス>:<コンテナ上のパス>
で、ホストマシンのディレクトリを指定してマウントすることもできます。このとき、ホスト側とコンテナ上のファイルは同期させることができますが、ローカルから直接参照することがないと思ったものに関しては今回、名前のわかりやすさからもdata volume コンテナを使いました。
volumesコンテナの実際の場所
<ローカル上のパス>と<コンテナ上のパス>で同期させていない今回のような場合に、どこにvolumeコンテナがあるのか調べて見ました。
docker volume inspect
コマンドを使います。
(docker volume ls
コマンドで,volumeの名前を確認できます。)
$ docker volume inspect your_app_db-data
[
{
"CreatedAt": "2022-11-22T11:04:38Z",
"Driver": "local",
"Labels": {
"com.docker.compose.project": "your_app",
"com.docker.compose.version": "2.12.2",
"com.docker.compose.volume": "db-data"
},
"Mountpoint": "/var/lib/docker/volumes/your_app_db-data/_data",
"Name": "your_app_db-data",
"Options": null,
"Scope": "local"
}
]
どうやら、ローカルの/var/lib/docker/volumes/下に存在しているようです。
- 最後の、
environment
の部分
postgreSQLについての設定を行なっています。このコンテナは環境変数に値を設定することで、DBの設定を行えるようになっています。ちなみに、postgresの場合には、POSTGRES_PASSWORD
だけは必須項目です。こちらに設定する項目と環境変数の対応が書かれてます。
database.yml
次に、RailsアプリのDBの設定ファイルを編集します。先ほど設定してきたDBコンテナのパスワードの値(POSTGRES_PASSWORD
)などを反映させていく作業です。
以下、developmentとtest環境の設定部分です。(開発環境でも個別にテストも走らせたいので)
default: &default
adapter: postgresql
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
encoding: unicode
username: postgres
password: postgres
host: postgres_db
development:
<<: *default
database: your_app_development
test:
<<: *default
database: your_app_test
defaultで共通の設定をしています。
-
username
は、特に設定しなかったので、postgreSQLデフォルトのpostgresユーザ。 -
password
DBコンテナで指定した環境変数の値を入れます。 -
host
docker-composeで作成したサービス(コンテナ)達は、別のコンテナに対してサービス名での名前解決できるので、postgres_dbと書きます。
1.リターンズ Rails用のコンテナを他のコンテナと協調させる。
先ほど、Dockerfileに記述したイメージをもとに、アプリケーション用のコンテナ(service)を設定していきます。
該当の部分だけを前述のdocker-compose.ymlから抜き出すと以下の様になります。
services:
rails: #サービスの名前をつけます。railsのアプリ用なのでrailsにしました。
build:
context: .
dockerfile: ./docker/Dockerfile
volumes:
- .:/your_app
- node_modules:/your_app/node_modules
- bundle:/usr/local/bundle
stdin_open: true
tty: true
ports:
- 3000:3000
depends_on: #ここでサービス名を指定して依存関係を作る。
- postgres_db
- chrome
-
build:
について
使用するイメージとなるDockerfileの指定と、contextの指定します。今回のようにdockerディレクトリをつくって、その中にDockerfileを置く場合には、このcontextにも気を遣う必要あります。
[補足]Dockefileとdocker-compose.ymlとそのビルドコンテキストについて
つまるところ、問題は、Dockerfileはディレクトリで分けて管理しながらも、そのディレクトリの外のファイル(ここではGemfile)を参照する必要があって、実際にコマンドライン上でbuild
することはなく、docker-compose.ymlを通してbuildを行う必要がある点にあります。docker-compose.ymlをアプリのルートに配置した上で、そのcontext
をアプリルート(実際には(.)current_directory)と指定することで、今ビルドするDockerfileを指定しつつ、アプリルートからの読み込みによって他のファイルを参照できるようにして解決します。
参考にさせていただいた記事一覧)
-
volumesについて
1行目では、ローカル上のアプリケーションソースコードを、コンテナ上にマウントして同期させる。
2,3行目はjsライブラリとgemライブラリを保存する専用をvolumesコンテナを作ることで、gemのデータを永続化しつつ、Railsアプリコンテナ自体はスリムに保てるように工夫している -
stdin_openとtty
この設定は、シェルでのやり取りを可能にするものです。標準入力を受け付けて、接続している擬似端末を有効にします(よくわかってない)。
具体的には
# 起動中のコンテナに対して
$ docker-compose exec rails(設定したサービス名) bash
とすると次のように、コンテナ上のrails server環境に入って、ターミナルでコマンドを使うことができるようになります。
# e.g.)
root@920904eb8631:/your_app#
-
ports
ポートフォワーディングの設定です。
<ホスト側のポート>: <コンテナ側のポート>
の形を取ります。こうすることで、今まで通りlocalhost:3000
でアクセスした際に、railsコンテナ上のコンテナポート3000にdockerがフォワーディングしてくれることで、コンテナ上で起動するサーバに対して、お手元のPCからアクセスできるようになります。 -
depends_onについて
他のコンテナとの依存関係を設定します。
参考にさせていただいた記事)
実際にビルドしてみる。
webpackerをインストールしたり、データベースをdatabase.ymlを元に作るためにrailsコンテナの動作確認も含めて、まずはビルドしてみます
$ docker-compose build
実際にアプリケーションのコンテナに潜ってDBを作成する。
chromeのブラウザコンテナを立てる前に、DBコンテナ上に先ほどのdatabase.ymlにも記載したDBを作成していきます。
(chromeコンテナは、RSpecでのcapybaraを使ったテストでのみ使用するので、一度保留しておいても動作はするはずです。)
今回はすでに作成しているアプリがある前提ですので、以下のようにします。
$ docker-compose run rails bundle exec rails db:create \
&& bundle exec rails db:migrate \
&& bundle exec rails db:seed # 必要に応じて
ここではまだコンテナを起動していないのでexec
ではなく、run
を使用しています
webpackerもインストールして、サーバを起動してみる
Gemfileにwebpackがあることを確認して、
$ docker-compose run rails bin/rails webpacker:install
実際にrails server
で起動できるかを試します。
$ docker-compose up
ここまで問題がなければ、あとはテストのためのchromeコンテナを設定するだけです。
3-3 ブラウザをシュミレートしたテストを実行できるようにブラウザ用コンテナを設定する
これまでの構成で、http://localhost:3000にアクセスできるし、テストの実行もbundleを使って、gemがコンテナにインストールされているので問題ないかと思います。
ここからは、capybaraを使って、E2Eテストをする際にブラウザ操作をシュミレートしたい場合に、追加するべき項目について書いていきます。
railsアプリ用のDockerfileの中にchromeをインストール方法もネットでいくつか見ましたが、今回はブラウザのためのコンテナを用意することにしました。
[補足]用語の整理
- capybara
capybaraはRuby用のテストフレームワーク。デフォルトであるブラウザ操作のシュミレータは:rack_test? - selenium
webアプリをブラウザで自動化するためのプロジェクトのこと。webdriver等を用いて、主にユーザのブラウザ操作などをシュミレートするテスト目的に用いられることが多い。
https://www.selenium.dev/ - webdriver
ブラウザ操作を自動化するためのAPI群とプロトコル。実際にこの人に頼んで、ブラウザ操作をシュミレートしているイメージ - chromedriver
chrome用のwebdriver
参考にさせていただいた記事)
Gemfile
Gemfileに必要なgemが入っているか確認します。
group :test do
gem 'capybara', '>= 3.26'
gem 'selenium-webdriver', '>= 4.0.0.rc1'
gem 'webdrivers'
end
webdriversgemの方に、selenium-webdriverの依存性が記されていて、selenium-webdriverだけでも動作している記事も見たのですが、僕は全てのテストを回した際になんだかうまくいかなかったので、どちらも大人しく記述しています。(いずれ調べます。)
次に、記述したdocker-compose.ymlを見ていきます。
chrome:
image: selenium/standalone-chrome:latest
ports:
- 4444:4444
ここでは、seleniumのイメージを指定して、ports
の設定もしています.
Capybara.rb
最後に、capybaraの設定をしていきます。今回はspec/support/capybara.rbというようにディレクトリを分けたので、Rspecが、自前で作成した設定ファイル(capybara.rb)も読み込んでくれることをまず確認します。
Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |f| require f }
こちらの記述をさせていることを確認してください。この話はdockerではなく、capybaraおよびRspecの設定に関することなので、これ以上は割愛します。
次に、capybaraに新たにドライバーを登録していきます。
Capybara.register_driver :remote_chrome do |app|
url = 'http://chrome:4444/wd/hub'
# http://<作成したコンテナ名>:<portsでフォワーディングさせたポート番号>/wd/hub
capabilities = ::Selenium::WebDriver::Remote::Capabilities.chrome(
'goog:chromeOptions' => {
'args' => [
'no-sandbox',
'headless',
'disable-gpu',
'window-size=1680,1050'
]
}
)
Capybara::Selenium::Driver.new(app, browser: :remote, url: url, capabilities: capabilities)
end
実際にwebdriverを動かす時は、remote
のブラウザ、url
でいうとhttp://chrome:4444/...
にあたるもの(作ったコンテナ)を指定しているという認識です。
また、capabilitiesの部分が、古い記述だと動作しませんでした。
参考にさせていただいた記事)
同ファイル内で今登録したドライバーを Rspecでのテストで使う設定をします。
RSpec.configure do |config|
config.before(:each, type: :system) do
driven_by :rack_test
end
# ここから追加した部分です。jsを用いるブラウザ動作がある時に、先ほど登録したドライバーで動作させます。
config.before(:each, js: true, type: :system) do
driven_by :remote_chrome #さっき登録したドライバ
Capybara.server_host = IPSocket.getaddress(Socket.gethostname)
Capybara.server_port = 4444
Capybara.app_host = "http://#{Capybara.server_host}:#{Capybara.server_port}"
end
end
end
Capybara.server_host
以降の文言の部分では、railsコンテナのホスト情報を取得して、railsコンテナ:4444 -> chrome:4444へのポートファワードをさせていると認識しています。いずれかの記述が欠けると動作しないので、あらかた間違っていないかと思いますが、まだ詳しくはわかってないです。
コンテナで、テストを回して見てください。
$ docker-compose exec rails bin/rails spec
テストについて参考にさせていただいた記事)
デバッグするためには
前提のところで挙げたやりたいことは、docker-compose exec
などでコンテナに入れば大抵できるようになっているかと思います。
debugについては、docker container attach
コマンドを使う必要があります。
docker container attach
ローカル環境のstdin stdout stderrorを実行中のコンテナに対して取り付ける。あたかもコマンドを自分のターミナル上で直接実行しているかのように、それらの継続的な出力を見られるようになったり、インタラクティブ(双方向)に制御できます。終わる時は、ctrl P + Q
(実行中のコンテナでtty: true / stdin_open: trueにしておくこと。)
byebugメソッドなどをソースコードに仕込み、
docker-compose ps
等で、railsコンテナのIDを調べてたあとに、
$ docker container attach <container ID>
これで、ログ表示が止まって,デバッグのプロンプトが立ち上がるはずです。
終了する時は ctrl P + Q
終わりに
技術にまつわる記事投稿自体が初めてかつ、自身にとって未だ学習中の分野であるために、思った5倍は長い記事になってしまいました。僕自身の不明点や、詰まった部分に適宜説明を加えたために、かなり曖昧なスコープの記事になってしまった感も否めません。
一応、dockerに関わる設定については、ファイル名ごとに見出しもつけてますので、コピペでもある程度動作させられるのかなと思っています。あとは適宜railsの設定などを調整してあげてください。
また、文中の所々に明確な説明しきれていない部分や、間違えている部分があるかと思いますので、読んでくださる方で詳しい方がいましたら、ぜひご指摘お願いします。読んで下さった皆様から忌憚なきご意見お聞かせいただけると幸いです。(文章が乱文であるとか、用語の使い方があまりに不明瞭など)。
最後に今回の環境構築は、ネット上でのたくさんの記事や情報によって成立していると思ってます、参考にさせていただいた記事を書いた方々へは本当に感謝します。また、僕自身もほんの少しでも、学習でつまづいている方の助けになる内容を提供できれば幸甚です。
あんまり固くなりすぎましたが、いいねでもなんでもレスポンスが欲しいです。よろしくおねがいします。
Discussion