Rails on Docker: 用 Docker Compose 搭建开发环境

💡 Rails on Docker 系列文章是面向 Rails 程序员的 Docker 教程,描述了如何从开发环境到部署环境中应用 Docker。这是系列文章的第二篇。

上一节中我们已经学习了 Docker 使用的基础,这一节将介绍如何用 Docker Compose 搭建 Rails 开发环境。

为什么要用 Docker

首先回答一个问题,为什么要在开发环境使用 Docker,直接在本地系统上搭建环境不好吗?

在开发环境使用 Docker 可以获得以下好处:

  • 将开发环境固化成配置文件,方便新加入的开发者启动。
  • 隔离各个项目的系统依赖,避免相互影响。
  • 使开发环境尽可能贴近生产环境,避免因系统不一致产生潜在问题。

管理的项目越多越久,越能体会到将开发环境容器化的好处。

Docker Compose 简介

对于 Rails 这样的项目,除了对操作系统的依赖以外,一般还会依赖 PostgreSQL、Redis 这样的外部服务。单个 Docker 容器不足以管理所有开发依赖,这时候就需要 Docker Compose。

💡 Docker 的最佳实践是一个容器运行一个进程,以方便管理容器状态和日志。

Docker Compose 是 Docker Desktop 自带的工具,它可以通过 YAML 配置文件定义应用所需要的多个服务,然后一起构建和启动。

使用 Docker Compose 大约分为三个步骤:

  1. 写项目的 Dockerfile,用于构建项目镜像。
  2. 写项目的 docker-compose.yml 配置,定义启动项目所需要的服务。
  3. docker compose up 命令启动应用。

docker-compose.yml 的内容看起来是这样:

version: "3.9"
services:
  web:
    build: .
    ports:
      - "5000:5000"
    volumes:
      - .:/code
      - logvolume01:/var/log
    links:
      - redis
  redis:
    image: redis
volumes:
  logvolume01: {}

上面例子可以看到,Compose 的文件可以定义多个 service,每个 service 对应一个容器。service 的内容包括使用的镜像,开放的端口和挂载的本地目录等。docker-compose.yml 的详细语法可以查看官方文档

搭建 Rails 应用开发环境

下面展示如何用 Docker Compose 搭建 Rails 开发环境。

💡 这里有一个先有鸡还是先有蛋的问题,我们目标是把所有依赖放在 Docker 中,但如果本地系统没有安装 Ruby,要如何创建一个 Rails 项目?Docker 的官方文档提供了一个拔靴带式的方法,先构建一个初始镜像,启动容器创建项目,然后再重建镜像。

我选择了另一个做法:先开启一个临时的 Ruby 容器,在容器内创建项目,之后再构建开发需要的镜像。

新建项目

为了创建一个 Rails 项目,先启动一个临时的 Ruby 容器:

$ docker run -it -v $(pwd):/app -w /app ruby:3.0 bash

在容器内安装 Rails gem:

/app# gem install rails

然后创建项目:

/app# rails new myapp --database postgresql --skip-bundle

这里使用了 --skip-bundle 参数,因为这只是个临时容器,稍后会在开发容器内执行 bundle

现在这个临时容器已经完成使命,按 ctrl-d 或者输入 exit 退出容器。

添加 Dockerfile

在项目目录下添加 Dockerfile 文件,输入以下内容:

FROM ruby:3.0

# ruby 镜像预设的这个环境变量干扰了后面的操作,将它重置为默认值
ENV BUNDLE_APP_CONFIG=.bundle

# 如果需要安装其他依赖,取消这段注释
# RUN apt-get update && apt-get install -y --no-install-recommends \
#   nodejs \
#   npm \
#   postgresql-client

WORKDIR /app

这是一个最精简的 Rails 开发环境镜像,如果有需要可以用 apt-get 安装其他系统依赖。

现在还不需要构建镜像,稍后我们会用 docker compose 命令一起构建。

添加 docker-compose.yml

在项目目录下添加 docker-compose.yml 文件,输入以下内容:

version: "3.9"

services:
  web:
    build: .
    command: bin/rails server -b 0.0.0.0
    volumes:
      - .:/app
    ports:
      - 3000:3000
    depends_on:
      - postgres
  postgres:
    image: postgres:13
    environment:
      POSTGRES_PASSWORD: postgres

这里定义了 web 和 postgres services。web service 会基于当前目录下的 Dockerfile 文件构建镜像,并且挂载当前目录到容器内的 /app 目录,释出 3000 端口,并且添加了对 postgres servcie 的启动依赖。postgres service 会使用 postgres 镜像,并通过环境变量设置了初始密码。

构建镜像

执行以下命令:

$ docker compose build

Docker Compose 会读取 docker-compose.yml 中的配置,构建相应的镜像。

每次修改 Dockerfile 后都要重新执行这个命令。

进入命令行

执行以下命令:

$ docker compose run web bash

这样就会启动 web service 容器,并且打开一个 bash shell。在这个 shell 中我们可以执行本地开发时需要执行的命令,例如 bundle installbin/rails g 等。

后面需要在容器内执行的操作都会通过这个 shell 执行。

执行 bundle

首先在容器内执行以下命令:

/app# bundle config set --local path vendor/bundle

这个命令将 bundle 的安装目录设为项目下的 vendor/bundle 目录。因为我不希望开发的时候每次更新 Gemfile 都要重新构建镜像。

然后执行 bundle

/app# bundle install

❗️ 记得在 .gitignore 中添加 vendor/bundle

准备数据库

在创建数据库前先要修改 Rails 项目的数据库设置,因为在 Docker Compose 搭建的环境中,PostgreSQL 跟 Rails 进程运行在不同的容器中,类似于不同的主机,service 名就是各自的网络名。

修改 database.yml,在 developmenttest 段加入以下内容:

  host: postgres
  username: postgres
  password: postgres

然后执行以下命令:

/app# bin/setup

之后便会创建相应的数据库。

启动 web service

经过上面的准备,是时候启动 web service 了。打开另一个终端,输入以下命令:

$ docker compose up

启动完成后,打开 http://localhost:3000 就能看到 Rails 的启动页面了。

截屏2021-12-25 22.36.16.png

按下 ctrl-c 即可关闭 servcie。

总结

这一节介绍了 Docker Compose 是什么,如何搭建 Rails 开发环境。

下一节将了解如何提高开发环境的文件系统性能。

4
8
4