環境
- M1 Mac
- lima(arch: x86_64)
- MySQL:8.0
- Ruby:3.0.3
- Rails:6.1.4.4
留意点
MySQLにはunixソケットまたはtcpで接続できるが、ソケットだとディレクトリの権限でエラーになったりして手間なので、tcp接続についてのみ説明する。
mysql2
RubyでMySQLに接続するためにはmysql2を使用する。このgemはRails new
コマンド時に-d mysql
を付与することでもインストールできる。
mysql2で接続を行うにはいくつかのパラメータを設定する必要がある。
The underlying MySQL client library uses the
:host
parameter to determine the
type of connection to make, with special interpretation you should be aware of:
- An empty value or
"localhost"
will attempt a local connection:- On Unix, connect to the default local socket path. (To set a custom socket
path, use the:socket
parameter). - On Windows, connect using a shared-memory connection, if enabled, or TCP.
- On Unix, connect to the default local socket path. (To set a custom socket
- A value of
"."
on Windows specifies a named-pipe connection. - An IPv4 or IPv6 address will result in a TCP connection.
- Any other value will be looked up as a hostname for a TCP connection.
tcpで接続を行うにはhost
パラメーターにMySQLのホスト名を指定する必要がある。mysql2はhost
が正しく設定されていない場合はソケット通信を試みる。
Railsの場合、これらのパラメーターの指定はconfig/database.yml
にて行う。
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: <%= ENV["MYSQL_USER"] %>
password: <%= ENV["MYSQL_PASSWORD"] %>
host: <%= ENV["MYSQL_HOST"] %>
development:
<<: *default
database: <%= ENV["MYSQL_DEV_DATABASE"] %>
test:
<<: *default
database: <%= ENV["MYSQL_TEST_DATABASE"] %>
production:
<<: *default
database: <%= ENV["MYSQL_PRD_DATABASE"] %>
MySQLはクライアントサーバシステムなので、ユーザーとそのパスワードを設定する必要がある。YAMLのエイリアスとインジェクトを使用して各環境にdefault
を差し込んでいる。また接続に必要な情報はすべて環境変数から読み込んでいる。
Docker環境の構築
今回は以下のような構成にする。
.
├── config
│ └── database.yml
├── docker
│ ├── docker-compose.yml
│ ├── .env
│ ├── mysql_db
│ │ └── conf.d
│ │ └── my.cnf
│ └── rails_app
│ └── Dockerfile.dev
├── .env
├── Gemfile
MySQL Dockerコンテナには起動及び接続用のユーザー設定を行うためMYSQL_ROOT_PASSWORD
、MYSQL_USER
、MYSQL_PASSWORD
の3つの環境変数を設定する。
./.env
にはこれらを含む接続に必要な環境変数を設定する。パスワードはpwgen
などによって適当に生成する。
MYSQL_ROOT_PASSWORD=Thaithi3xez7
MYSQL_USER=rails-app
MYSQL_PASSWORD=aix1Eequei8d
MYSQL_DEV_DATABASE=dev-db
MYSQL_TEST_DATABASE=test-db
MYSQL_PRD_DATABASE=prd-dev
Dockerコンテナにこの環境変数を読み込む。次にRailsコンテナ用のDockerfile
、docker-compose.yml
を設定する。
COMPOSE_PROJECT_NAME=sample_project
ARG NODE_VERSION=14
ARG RUBY_VERSION=3.0.3
FROM node:${NODE_VERSION} as node
FROM ruby:${RUBY_VERSION}
# Install node and yarn
# see https://hub.docker.com/_/node
COPY --from=node /opt/yarn-* /opt/yarn
COPY --from=node /usr/local/bin/node /usr/local/bin/node
COPY --from=node /usr/local/lib/node_modules/ /usr/local/lib/node_modules/
RUN ln -s /opt/yarn/bin/yarn /usr/local/bin/yarn \
&& ln -s /usr/local/lib/node_modules/npm/bin/npm-cli.js /usr/local/bin/npm
ENV PATH $PATH:\
/usr/local/bin/yarn:\
/usr/local/bin/node
RUN apt-get update -qq
# set envrionment variables
ENV RAILS_ENV="development" \
NODE_ENV="development" \
LANG="C.UTF-8" \
TZ="Asia/Tokyo"
# Install gems
WORKDIR /rails_app
COPY Gemfile \
Gemfile.lock \
/rails_app/
RUN gem update bundler \
&& bundle config set path .cache/bundle
RUN --mount=type=cache,uid=1000,target=/rails_app/.cache/bundle \
bundle install \
&& mkdir vendor \
&& cp -ra .cache/bundle vendor/bundle \
&& bundle config set path vendor/bundle
# Install npm packages
COPY package.json \
yarn.lock \
/rails_app/
RUN --mount=type=cache,uid=1000,target=/rails_app/.cache/node_modules \
yarn install --modules-folder .cache/node_modules \
&& cp -ra .cache/node_modules node_modules
# Copy codes
COPY . /rails_app/
# Precompile assets
# RUN --mount=type=cache,uid=1000,target=/rails_app/tmp/cache/assets \
# bundle exec rails assets:precompile
CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"]
version: "3.9"
services:
mysql_db:
image: mysql:8.0
container_name: dev-db
hostname: &mysql_host mysql_host
env_file:
- ../.env
volumes:
- ./mysql_db/conf.d:/etc/mysql/conf.d
- db_data:/var/lib/mysql
rails_app:
build:
context: ../
dockerfile: ./docker/rails_app/Dockerfile.dev
container_name: dev-server
env_file:
- ../.env
environment:
MYSQL_HOST: *mysql_host
stdin_open: true
tty: true
ports:
- 3000:3000
command: bundle exec puma -C config/puma.rb
volumes:
- ..:/rails_app:cached
depends_on:
- mysql_db
volumes:
db_data:
docker/.env
にてプロジェクト名を設定している。docker-compose.ymlのディレクトリに.env
を設置することで環境変数を適用できる。COMPOSE_PROJECT_NAME
でプロジェクト名を指定している。
Dockerfileではmulti-stage build及びBuildkitによるbuild
の高速化を図っている。詳細は以下の記事を参照。
肝心のdocker/docker-compose.yml
ではenv_files
によって./.env
を読み込んでいる。またdocker-composeではサービス名及びコンテナ名(この場合、サービス名はmysql_db
。コンテナ名はdev-db
)がホスト名になるが、hostname
によって明示的にホスト名を指定している。さらにエイリアスでMYSQL_HOST
を設定することで整合をとっている。
これをdocker-compsoe -f docker/docker-compose.yml up -d
で起動、docker logs
でMySQLコンテナのサーバーの起動を確認した後、設定したユーザーに権限を適用する。
# !/bin/bash
source ./.env
source ./docker/.env
query=`cat << EOF
GRANT ALL PRIVILEGES ON *.* TO '${MYSQL_USER}'@'%';
FLUSH PRIVILEGES;
EOF
`
export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}
docker-compose exec mysql_db mysql -u root -p${MYSQL_ROOT_PASSWORD} -e"${query}"
COMPOSE_PROJECT_NAME
を設定することでdocker-compose.yml
のディレクトリ外でもdocker-compose
コマンドでコンテナを操作できる。
# !/bin/bash
source ./docker/.env
export COMPOSE_PROJECT_NAME=${COMPOSE_PROJECT_NAME}
docker-compose exec rails_app bash
最後にrailsコンテナにアタッチした後、bundle exec rails db:create
とすればデータベースが生成される。ここで正しく生成されれば接続が確立されている。