尽可能地使用 Docker 自动化地创建了 Nginx、Rails 和 MySQL 的开发环境
前言
为了方便随时搭建Rails开发环境,我决定使用Docker来构建开发环境。参考了官方指南和一些文章后,我稍微进行了一些自动化尝试。我对Docker的了解只是初级水平,所以同时也是在学习。由于已经有很多优秀的文章对每个命令进行了解释,所以我的主要目标是在创建的文件中添加注释。我还将该项目在GitHub上公开,应该可以直接使用。如果您有兴趣,请随意使用。
行为环境
构成
请查阅我在GitHub上的开放项目。同时请注意,我已将其分类为只管理与Docker相关的目录和只管理持久化的目录。接下来将依次介绍文件及其说明。如果您只是想运行GitHub上的代码,请直接跳转到这里。
$ tree
├── README.md
├── docker # DockerやDocker Compose
│ ├── containers # 各コンテナ(イメージ)
│ │ ├── mysql # MySQL 5.7
│ │ │ ├── Dockerfile # MySQL 5.7のDockerファイル
│ │ │ ├── grant_user.sql # 初期セットアップでユーザを作成するスプリプト
│ │ │ └── my.cnf # 初期セットアップで反映するmy.cnf
│ │ ├── nginx # Nginx 1.15.2
│ │ │ ├── Dockerfile # NginxのDockerファイル
│ │ │ └── nginx.conf # 初期セットアップで反映するnginx.conf
│ │ └── rails # Rails ~>5.2.0
│ │ ├── Dockerfile # Dockerファイル
│ │ ├── Gemfile # 初期セットアップで反映するGemfile
│ │ ├── application.rb # 初期セットアップで反映するapplication.rb
│ │ ├── database.yml # 初期セットアップで反映するdatabase.yml
│ │ ├── docker-entrypoint.sh # コンテナ起動時動作するシェルスクリプト
│ │ └── puma.rb # 初期セットアップで反映するpuma.rb
│ ├── docker-compose.yml # Composeファイル
│ └── environments # 環境変数定義
│ ├── common.env # 各コンテナ共通
│ └── db.env # DB接続用MySQL関連
└── volumes # 永続化したリソース
├── app # Railsのソースコード このディレクトリは最初は無し
├── db # MySQLのデータ このディレクトリは最初は無し
├── ssl # SSL証明書
│ ├── privkey.pem # 秘密鍵
│ └── server.crt # サーバー証明証
└── web # Nginxのログ このディレクトリは最初は無し
Compose文件
version: '3' # 特に意思は無いが新しいバージョンで。
services: # 各コンテナ(サービス)
db: # MySQLのコンテナ 「db」と命名
build: containers/mysql # Dockerファイルのパス
env_file: # 環境変数
- ./environments/common.env # 各コンテナ共通
- ./environments/db.env # DB接続用MySQL関連
volumes: # 永続化
- ../volumes/db/data:/var/lib/mysql # MySQLのデータ
app: # Railsのコンテナ 「app」と命名
build: containers/rails # Dockerファイルのパス
env_file: # 環境変数
- ./environments/common.env # 各コンテナ共通
- ./environments/db.env # DB接続用MySQL関連
command: bundle exec puma -C config/puma.rb # 実行するコマンド
volumes: # 永続化
- ../volumes/app:/app # Railsのソースコード
depends_on: # 起動する順番
- db # 「db」後で起動
web: # Nginxのコンテナ 「web」と命名
build: containers/nginx # Dockerファイルのパス
env_file: # 環境変数
- ./environments/common.env # 各コンテナ共通
volumes: # 永続化
- ../volumes/app:/app # 静的ファイル
- ../volumes/web/log:/var/log/nginx/ # Nginxのログ
- ../volumes/ssl:/etc/nginx/cert/ # SSL証明書
ports: # 解放ポート
- 443:443 # HTTPS この開発環境はHTTPSのみ想定
depends_on: # 起動する順番
- app # 「app」後で起動
MySQL容器
Docker文件
# MySQL 5.7
FROM mysql:5.7
# 初期セットアップで利用するmy.cnfをイメージへコピー
# アーカイブを展開する必要などが無ければADDで無くてCOPYで良い。
COPY my.cnf /etc/mysql/conf.d
# 初期セットアップで実行したいスクリプトをイメージへコピー
COPY grant_user.sql /docker-entrypoint-initdb.d
需要复制的文件摘要
通过Docker Compose进行配置的环境变量
TZ=Asia/Tokyo
MYSQL_ROOT_PASSWORD=root
MYSQL_USER=rails
MYSQL_PASSWORD=rails
通过Docker Compose进行的配置持久化
Rails应用程序容器
Docker文件
# 公式を参考
FROM ruby:2.5.1
RUN apt-get update -qq && apt-get install -y build-essential default-libmysqlclient-dev nodejs
# MySQLのイメージを参考にコンテナ起動時のエントリポイントを作成
# 元のスクリプトをコピー
COPY docker-entrypoint.sh /usr/local/bin/
# 権限付与
RUN chmod 744 /usr/local/bin/docker-entrypoint.sh
# 永続化されるパスへ直接ファイルをコピーしてしまうとマウント時にホスト側の内容で上書きされ
# イメージ構築でコピーされたファイルは全て消失してしまうので一時ディレクトリで作業
ENV READY_RAILS_DIR=/ready_rails
# ディレクトリ作成とワークディレクトリに指定
WORKDIR $READY_RAILS_DIR
# 初期セットアップで反映する各ファイルをコピー
COPY Gemfile .
COPY application.rb .
COPY database.yml .
COPY puma.rb .
# 永続化でマウントされるパスをワーキングディレクトリに指定
WORKDIR /app
ENTRYPOINT ["docker-entrypoint.sh"]
要复制的文件摘要
容器启动时的ENTRYPOINT
我根据MySQL公式的ENTRYPOINT参考自动化了一个Shell脚本。
#!/bin/bash
set -e
# ホストにあるRailsのソースコードが無く永続化でマウントされた場合
# /app/Gemfileは存在しないはず。
if [ ! -e Gemfile ]; then
# 一時ディレクトリからGemfileをコピー
cp -a $READY_RAILS_DIR/Gemfile .
touch Gemfile.lock
bundle install
rails new . -d mysql -f
# 初期セットアップのファイル群をコピー
cp -a $READY_RAILS_DIR/database.yml \
$READY_RAILS_DIR/puma.rb \
$READY_RAILS_DIR/application.rb config/
# NginxとPuma間をUNIXドメインソケットで通信するためのディレクトリを作成
mkdir -p tmp/sockets
# 初期セットアップのファイル群が/appにコピーされて
# /appがホストにマウントされたので一時ディレクトリは削除
rm -r $READY_RAILS_DIR
# depends_on指定でdb(MySQL)コンテナが先に起動されている。
# 順番に起動するものの先行のコンテナの準備が整うまで待機は行えない。
# つまりDBの準備が完了していない状況があるので待機する。
until rails db:drop &> /dev/null; do
>&2 echo "MySQL is unavailable - sleeping"
sleep 1
done
# DBの準備が整ってからdb:create
rails db:create
# ホストにあるRailsのソースコードがあり永続化でマウントされた場合
# この場合/app/Gemfileは存在する一時ディレクトリは残っているはず。
elif [ -e $READY_RAILS_DIR ]; then
bundle install
rm -r $READY_RAILS_DIR
fi
exec "$@"
通过 Docker Comopse 设置的环境变量 Docker Comopse de
TZ=Asia/Tokyo
MYSQL_ROOT_PASSWORD=root
MYSQL_USER=rails
MYSQL_PASSWORD=rails
通过Docker Compose进行的持久化配置
网络(Nginx)容器
Docker文件
FROM nginx:1.15.2
# 初期セットアップで反映するファイルをコピー
COPY nginx.conf /etc/nginx/conf.d/app.conf
CMD /usr/sbin/nginx -g 'daemon off;' -c /etc/nginx/nginx.conf
复制文件的摘要
设定WEB服务器
我认为没有棘手的设置,但我简单补充一下。
在GitHub上公开的内容使用的是自签名SSL证书(也称为“自制证书”),“*.example.com”作为域名的SSL证书。在我的环境中,我使用了Let’s Encrypt的SSL证书。
顺便说一下,好像已经废止了 SSL 的开关,无论是开启还是关闭。
# PumaのSocketを指定
upstream app {
server unix:///app/tmp/sockets/puma.sock;
}
server {
# 今回HTTPは使用しない。ssl onは廃止のよう・・・。
listen 443 ssl;
server_name devnokiyo.example.com;
ssl_certificate /etc/nginx/cert/server.crt;
ssl_certificate_key /etc/nginx/cert/privkey.pem;
ssl_prefer_server_ciphers on;
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
# Railsの静的ファイルはNginxで返す。
root /app/public;
try_files $uri/index.html $uri @app;
client_max_body_size 10m;
error_page 404 /404.html;
error_page 505 502 503 504 /500.html;
location @app {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-CSRF-Token $http_x_csrf_token;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_pass http://app;
}
}
Docker Compose设定的环境变量
TZ=Asia/Tokyo
永久性的设置由Docker Comopse完成
PumaのUNIXドメインソケットvolumes/web/log//var/log/nginx/ログvolumes/ssl//etc/nginx/cert/SSL証明書
让我们试一试!
使用Docker Compose进行启动。
因为每个文件的说明已经完成了,所以我会运行并确认一下,虽然它非常简单。
-
- 从GitHub上下载整套文件。
进入docker目录。
$ cd docker
进行构建(因为是第一次,所以启动也可以)。
$ docker-compose build –no-cache
构建数据库
步骤 1/3:从mysql:5.7开始
—> 563a026a1511
步骤 2/3:复制my.cnf到/etc/mysql/conf.d
—> 691e66ddfe3c
:
:
成功构建 5f5831d39d90
成功标记docker_web:latest
启动。
$ docker-compose up
创建网络”docker_default”,使用默认驱动程序
创建docker_db_1…完成
创建docker_app_1…完成
创建docker_web_1…完成
连接到docker_db_1、docker_app_1、docker_web_1
db_1 | 初始化数据库
db_1 | 2018-09-09T12:32:32.270957Z 0 [警告] 使用隐含的DEFAULT值的TIMESTAMP已被弃用,请使用–explicit_defaults_for_timestamp服务器选项(有关更多详细信息,请参阅文档)。
:
:
db_1 | 版本:’5.7.23′ socket:’/var/run/mysqld/mysqld.sock’ 端口:3306 MySQL Community Server (GPL)
:
:
app_1 | 正在从https://rubygems.org/获取宝石元数据……….
app_1 | 正在从https://rubygems.org/获取宝石元数据。
app_1 | 解析依赖项……
app_1 | 正在获取rake 12.3.1
app_1 | 安装rake 12.3.1
:
:
app_1 | * bin/rake: spring已插入
app_1 | * bin/rails: spring已插入
app_1 | 创建数据库’app_development’
app_1 | 创建数据库’app_test’
app_1 | Puma正在以单一模式启动…
app_1 | * Version 3.12.0 (ruby 2.5.1-p57), codename: Llamas in Pajamas
app_1 | * Min threads: 5, max threads: 5
app_1 | * Environment: development
app_1 | * Listening on unix:///app/tmp/sockets/puma.sock
app_1 | 使用Ctrl-C停止
请使用浏览器进行访问。


创建脚手架
如果没有问题,首先使用脚手架确认数据库周围的情况。
$ docker-compose exec app rails g scaffold food name:string price:integer calorie:integer
Running via Spring preloader in process 55
invoke active_record
create db/migrate/20180909124600_create_foods.rb
create /models/food.rb
:
:
invoke scss
create /assets/stylesheets/scaffolds.scss
$ docker-compose exec app rails db:migrate
== 20180909124600 CreateFoods: migrating ======================================
-- create_table(:foods)
-> 0.0234s
== 20180909124600 CreateFoods: migrated (0.0235s) =============================
使用CRUD进行确认。

最后
筆者主要是通過分享自己的實際操作經驗,而不是對基礎命令進行解釋。本來只是閱讀Docker入門書和網上資訊,以為自己已經理解了,但實際動手操作時也遇到了一些困難。果然,只有親自動手操作才能更深刻地理解。我打算以此為基礎,繼續學習Rails一段時間。