Dockerを導入し、コンテナを立ち上げVSCodeで開発を行うところまで環境構築ができました。
備忘として導入した流れを簡単にまとめておきたいと思います。
インストール
Docker公式サイトからDocker Desctopをダウンロード。
以下の記事を参考にしてインストールしました。
フォルダ構成
Dockerfileは記事によって配置場所が異なりますが、個人的にはこの構成が一番綺麗にまとまってると思いました。
Dockerfileやdocker-compose.yml、それにNginxやUnicornの設定ファイルは自動生成されないので自分で作ります。
app
├ config
│ ├ database.yml
│ └ unicorn_development.conf.rb
├ docker
│ ├ web
│ │ └ Dockerfile
│ └ nginx
│ ├ Dockerfile
│ └ revorite.conf
└ docker-compose.yml
Docker関連ファイルの記述
以下記事を参考にさせていただきました。
参考記事:DockerでNginx+unicorn+rails+Mysqlの開発環境を作ってみた
docker/web/Dockerfile
FROM ruby:2.7.1
# dockerizeパッケージダウンロード用環境変数
ENV DOCKERIZE_VERSION v0.6.1
# パッケージの取得
RUN apt-get update && \
apt-get install -y --no-install-recommends\
nodejs \
vim \
mariadb-client \
build-essential \
wget \
&& wget https://github.com/jwilder/dockerize/releases/download/$DOCKERIZE_VERSION/dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& tar -C /usr/local/bin -xzvf dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& rm dockerize-linux-amd64-$DOCKERIZE_VERSION.tar.gz \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /app
COPY Gemfile /app/Gemfile
COPY Gemfile.lock /app/Gemfile.lock
RUN echo "alias cp='cp -i'" >> /root/.bashrc
RUN echo "alias mv='mv -i'" >> /root/.bashrc
RUN echo "alias rm='rm -i'" >> /root/.bashrc
RUN echo "alias la='ls -al'" >> /root/.bashrc
RUN echo "alias ll='ls -l'" >> /root/.bashrc
RUN gem install bundler
RUN bundle install
COPY . /app
docker/nginx/Dockerfile
FROM nginx:stable
# デフォルトのNginxの設定ファイルを削除し、作成しておいた設定ファイルをコピー
RUN rm -f /etc/nginx/conf.d/*
COPY ./docker/nginx/revorite.conf /etc/nginx/conf.d/revorite.conf
RUN echo "alias cp='cp -i'" >> /root/.bashrc
RUN echo "alias mv='mv -i'" >> /root/.bashrc
RUN echo "alias rm='rm -i'" >> /root/.bashrc
RUN echo "alias la='ls -al'" >> /root/.bashrc
RUN echo "alias ll='ls -l'" >> /root/.bashrc
# -c以降の設定ファイルを指定して起動 daemon offでフォアグラウンドで起動
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
docker/nginx/revorite.conf (Nginx設定ファイル)
# log directory
error_log /var/log/nginx.error.log;
access_log /var/log/nginx.access.log;
# max body size
client_max_body_size 2G;
upstream unicorn {
# for UNIX domain socket setups
server unix:/app/tmp/sockets/.unicorn.sock fail_timeout=0;
}
server {
listen 80;
server_name localhost;
# nginx so increasing this is generally safe...
keepalive_timeout 5;
# path for static files
root /app/public;
location @unicorn {
# HTTP headers
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_pass http://unicorn;
}
location / {
try_files $uri/index.html $uri.html $uri @unicorn;
include /etc/nginx/mime.types;
}
location ~ ^/assets/(.*) {
alias /app/public/assets/$1;
}
# Rails error pages
error_page 500 502 503 504 /500.html;
location = /500.html {
root /app/public;
}
}
docker-compose.yml
version: '3'
services:
web:
build:
context: .
dockerfile: ./docker/web/Dockerfile
# dockerizeを使い、DBの起動を待ってからUnicornを起動する。
command: dockerize -wait tcp://db:3306 -timeout 20s bundle exec unicorn -p 3000 -c /app/config/unicorn_development.conf.rb
environment:
TZ: Asia/Tokyo
tty: true # binding.pryを利用可能にするための2行
stdin_open: true # binding.pryを利用可能にするための2行
depends_on:
- db
ports:
- "3000:3000"
volumes:
- .:/app:cached
# ソケット通信用ファイルをnginxコンテナと共有
- tmp-data:/app/tmp/sockets
# アセットファイルをnginxと共有
- public-data:/app/public
db:
image: mysql:5.7
command: mysqld --character-set-server=utf8 --collation-server=utf8_unicode_ci
ports:
- "4306:3306"
environment:
MYSQL_DATABASE: revorite_development
MYSQL_ROOT_PASSWORD: password
# dbのデータを永続化しておく
volumes:
- mysql-data:/var/lib/mysql
nginx:
build:
context: .
dockerfile: ./docker/nginx/Dockerfile
ports:
- 80:80
restart: always #明示的にstopさせるまでリスタートする。(失敗するたび遅延あり)
volumes:
- tmp-data:/app/tmp/sockets
- public-data:/app/public
depends_on:
- web
volumes:
public-data:
tmp-data:
mysql-data:
config/database.yml
default: &default
adapter: mysql2
encoding: utf8
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
host: db
username: root
password: password
database: revorite_development
config/unicorn_development.conf.rb
# set lets
$worker = 2
$timeout = 30
$app_dir = "/app"
$listen = File.expand_path 'tmp/sockets/.unicorn.sock', $app_dir
$pid = File.expand_path 'tmp/pids/unicorn.pid', $app_dir
$std_log = File.expand_path 'log/unicorn.log', $app_dir
# set config
worker_processes $worker
working_directory $app_dir
stderr_path $std_log
stdout_path $std_log
timeout $timeout
listen $listen
pid $pid
# loading booster
preload_app true
# before starting processes
before_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
old_pid = "#{server.config[:pid]}.oldbin"
if old_pid != server.pid
begin
Process.kill "QUIT", File.read(old_pid).to_i
rescue Errno::ENOENT, Errno::ESRCH
end
end
end
# after finishing processes
after_fork do |server, worker|
defined?(ActiveRecord::Base) and ActiveRecord::Base.establish_connection
end
コンテナ作成・起動
$ docker-compose build # イメージの作成
$ docker-compose up -d # コンテナの作成・起動
詰まった点
他の記事ではあまり語られておらず、自分が詰まった点をいくつか。
Unicornの起動で失敗する
関連ファイルを作り終え、いざdocker-compose build
とdocker-compose up -d
を叩く。
そしてdocker ps
を叩くと・・・
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
11e605b70c51 revorite_nginx "/docker-entrypoint.…" 21 minutes ago Up 21 minutes 0.0.0.0:80->80/tcp revorite_nginx_1
313d53cbbc27 mysql:5.7 "docker-entrypoint.s…" 21 minutes ago Up 21 minutes 33060/tcp, 0.0.0.0:4306->3306/tcp revorite_db_1
あれ?アプリサーバは???
で、docker-compose logs
でログを見てみると
web_1 | 2021/03/04 11:01:27 Command exited with error: exit status 1
exit status 1
とは一体・・・
exitしたということは一度起動して即落ちた、と読めるので、落ちたコンテナに入ってログも見てみたのですがそれらしいログは出ていない。
(落ちたコンテナに入る方法はこちらを参考にしました)
最終的に、database.yml
にhost: db
の記述が漏れているためだということが判明。一文追加し、無事起動しました。
こんな単純なミスなのですが一日使ってしまったので、Dockerfileとdocker-composeだけを書いて満足しないように注意です。
コンテナ内でGitが使えない
GitをインストールしSSH認証もできるようにしたのですが、どうしてもgit pushだけが弾かれてしまうんですよね。(commitまではできる)
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.
Please make sure you have the correct access rights
and the repository exists.
仕方ないのでgit関連の操作はホスト側で実行しています(ホスト側でコンテナ内のソースコードに対しマウントしている)。git操作もコンテナ内で完結できたらベストだったのですが。
コンテナ内で開発を行う(VSCode: Remote Development)
コンテナ内で開発を行うには、VSCodeでコンテナ内のソースを参照・開発できるRemote Developmentという拡張機能を使います。
参考記事:VS Code Remote Development で Docker 開発環境を利用する
前述の起動コマンドで起動できていれば、参考記事の通りに起動するだけで特に問題は起きないはず。
その他補足
docker関連のコマンドは長いので、~/.bashrcにエイリアスを設定すると便利。
またエラーが起きる度にコンテナやイメージの削除コマンドを打つのが大変のため、こちらも設定しておくと快適です。
# 例
alias dco='docker-compose'
alias docker-purge='docker stop $(docker ps -q) && docker rm $(docker ps -a -q) && docker rmi $(docker images -q -a) -f'
改善点や誤りなどありましたらコメントで指摘いただけると幸いです。