使用DockerCompose将RubyonRails应用程序容器化以进行开发

来自菜鸟教程
跳转至:导航、​搜索

介绍

如果您正在积极开发应用程序,使用 Docker 可以简化您的工作流程以及将应用程序部署到生产环境的过程。 在开发中使用容器具有以下好处:

  • 环境是一致的,这意味着您可以为项目选择所需的语言和依赖项,而不必担心系统冲突。
  • 环境是隔离的,因此更容易解决问题和加入新的团队成员。
  • 环境是可移植的,允许您打包并与他人共享您的代码。

本教程将向您展示如何使用 Docker 为 Ruby on Rails 应用程序设置开发环境。 您将使用 Docker Compose 为应用程序本身、PostgreSQL 数据库、RedisSidekiq 服务创建多个容器。 该设置将执行以下操作:

  • 将宿主机上的应用代码与容器内的代码同步,方便开发过程中的更改。
  • 在容器重新启动之间保留应用程序数据。
  • 配置 Sidekiq 工作人员以按预期处理作业。

在本教程结束时,您将拥有一个在 Docker 容器上运行的鲨鱼信息应用程序:

先决条件

要遵循本教程,您将需要:

  • 运行 Ubuntu 18.04 的本地开发机器或服务器,以及具有 sudo 权限和活动防火墙的非 root 用户。 有关如何设置这些的指导,请参阅此 初始服务器设置指南
  • 按照 如何在 Ubuntu 18.04 上安装和使用 Docker 的步骤 1 和 2 安装在本地机器或服务器上的 Docker。
  • 按照 如何在 Ubuntu 18.04 上安装 Docker Compose 的第 1 步,将 Docker Compose 安装在您的本地机器或服务器上。

第 1 步 — 克隆项目并添加依赖项

我们的第一步是从 DigitalOcean 社区 GitHub 帐户 克隆 rails-sidekiq 存储库。 此存储库包含 如何将 Sidekiq 和 Redis 添加到 Ruby on Rails 应用程序 中描述的设置中的代码,其中解释了如何将 Sidekiq 添加到现有的 Rails 5 项目中。

将存储库克隆到名为 rails-docker 的目录中:

git clone https://github.com/do-community/rails-sidekiq.git rails-docker

导航到 rails-docker 目录:

cd rails-docker

在本教程中,我们将使用 PostgreSQL 作为数据库。 为了使用 PostgreSQL 而不是 SQLite 3,您需要将 pg gem 添加到项目的依赖项中,这些依赖项在其 Gemfile 中列出。 使用 nano 或您喜欢的编辑器打开该文件进行编辑:

nano Gemfile

在主项目依赖项(在开发依赖项之上)的任何位置添加 gem:

~/rails-docker/Gemfile

. . . 
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false
gem 'sidekiq', '~>6.0.0'
gem 'pg', '~>1.1.3'

group :development, :test do
. . .

我们也可以注释掉 sqlite gem,因为我们不会再使用它了:

~/rails-docker/Gemfile

. . . 
# Use sqlite3 as the database for Active Record
# gem 'sqlite3'
. . .

最后,注释掉development下的spring-watcher-listen gem

~/rails-docker/Gemfile

. . . 
gem 'spring'
# gem 'spring-watcher-listen', '~> 2.0.0'
. . .

如果我们不禁用这个 gem,我们将在访问 Rails 控制台时看到持续的错误消息。 这些错误消息源于这个 gem 让 Rails 使用 listen 来观察开发中的变化,而不是轮询文件系统的变化。 因为 这个 gem 监视项目 的根目录,包括 node_modules 目录,它会抛出有关正在监视哪些目录的错误消息,从而使控制台混乱。 但是,如果您担心节省 CPU 资源,则禁用此 gem 可能对您不起作用。 在这种情况下,将 Rails 应用程序升级到 Rails 6 可能是个好主意。

完成编辑后保存并关闭文件。

在您的项目存储库到位后,将 pg gem 添加到您的 Gemfile 中,并且将 spring-watcher-listen gem 注释掉,您就可以配置您的应用程序以使用 PostgreSQL。

第 2 步 — 配置应用程序以使用 PostgreSQL 和 Redis

要在开发中使用 PostgreSQL 和 Redis,我们需要执行以下操作:

  • 将应用程序配置为使用 PostgreSQL 作为默认适配器。
  • 使用我们的数据库用户名和密码以及 Redis 主机将 .env 文件添加到项目中。
  • 创建一个 init.sql 脚本来为数据库创建一个 sammy 用户。
  • 为 Sidekiq 添加一个 initializer 以便它可以与我们的容器化 redis 服务一起使用。
  • .env 文件和其他相关文件添加到项目的 gitignoredockerignore 文件中。
  • 创建数据库种子,以便我们的应用程序在启动时有一些记录供我们使用。

首先,打开位于 config/database.yml 的数据库配置文件:

nano config/database.yml

目前,该文件包括以下 default 设置,在没有其他设置的情况下应用:

~/rails-docker/config/database.yml

default: &default
  adapter: sqlite3
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000

我们需要更改这些以反映我们将使用 postgresql 适配器的事实,因为我们将使用 Docker Compose 创建一个 PostgreSQL 服务来保存我们的应用程序数据。

删除将 SQLite 设置为适配器的代码并将其替换为以下设置,这将适当地设置适配器以及连接所需的其他变量:

~/rails-docker/config/database.yml

default: &default
  adapter: postgresql
  encoding: unicode
  database: <%= ENV['DATABASE_NAME'] %>
  username: <%= ENV['DATABASE_USER'] %>
  password: <%= ENV['DATABASE_PASSWORD'] %>
  port: <%= ENV['DATABASE_PORT'] || '5432' %>
  host: <%= ENV['DATABASE_HOST'] %>
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  timeout: 5000
. . .

接下来,我们将修改 development 环境的设置,因为这是我们在此设置中使用的环境。

删除现有的 SQLite 数据库配置,使该部分如下所示:

~/rails-docker/config/database.yml

. . . 
development:
  <<: *default
. . .

最后,删除 productiontest 环境的 database 设置:

~/rails-docker/config/database.yml

. . . 
test:
  <<: *default
  
production:
  <<: *default
. . . 

这些对我们默认数据库设置的修改将允许我们使用 .env 文件中定义的环境变量动态设置我们的数据库信息,这些环境变量不会提交给版本控制。

完成编辑后保存并关闭文件。

请注意,如果您从头开始创建 Rails 项目,您可以使用 rails new 命令设置适配器,如 How To Use PostgreSQL with Your Ruby on Step 3 中所述Ubuntu 18.04 上的 Rails 应用程序。 这会将您的适配器设置为 config/database.yml 并自动将 pg gem 添加到项目中。

现在我们已经引用了环境变量,我们可以使用我们的首选设置为它们创建一个文件。 以这种方式提取配置设置是应用程序开发的 12 因素方法 的一部分,它定义了分布式环境中应用程序弹性的最佳实践。 现在,当我们将来设置生产和测试环境时,配置我们的数据库设置将涉及创建额外的 .env 文件并在我们的 Docker Compose 文件中引用适当的文件。

打开一个 .env 文件:

nano .env

将以下值添加到文件中:

~/rails-docker/.env

DATABASE_NAME=rails_development
DATABASE_USER=sammy
DATABASE_PASSWORD=shark
DATABASE_HOST=database
REDIS_HOST=redis

除了设置我们的数据库名称、用户和密码之外,我们还为 DATABASE_HOST 设置了一个值。 值 database 指的是我们将使用 Docker Compose 创建的 database PostgreSQL 服务。 我们还设置了一个 REDIS_HOST 来指定我们的 redis 服务。

完成编辑后保存并关闭文件。

要创建 sammy 数据库用户,我们可以编写一个 init.sql 脚本,然后我们可以在数据库容器启动时将其挂载到数据库容器。

打开脚本文件:

nano init.sql

添加以下代码以创建具有管理权限的 sammy 用户:

~/rails-docker/init.sql

CREATE USER sammy;
ALTER USER sammy WITH SUPERUSER;

该脚本将在数据库上创建适当的用户并授予该用户管理权限。

在脚本上设置适当的权限:

chmod +x init.sql

接下来,我们将配置 Sidekiq 以使用我们的容器化 redis 服务。 我们可以在 config/initializers 目录中添加一个初始化程序,一旦加载了框架和插件,Rails 就会在其中查找配置设置,从而为 Redis 主机设置一个值。

打开 sidekiq.rb 文件以指定以下设置:

nano config/initializers/sidekiq.rb

将以下代码添加到文件中以指定 REDIS_HOSTREDIS_PORT 的值:

~/rails-docker/config/initializers/sidekiq.rb

Sidekiq.configure_server do |config|
  config.redis = {
    host: ENV['REDIS_HOST'],
    port: ENV['REDIS_PORT'] || '6379'
  }
end

Sidekiq.configure_client do |config|
  config.redis = {
    host: ENV['REDIS_HOST'],
    port: ENV['REDIS_PORT'] || '6379'
  }
end

与我们的数据库配置设置非常相似,这些设置使我们能够动态设置主机和端口参数,允许我们在运行时替换适当的值,而无需修改应用程序代码本身。 除了 REDIS_HOST 之外,我们还为 REDIS_PORT 设置了默认值,以防其他地方未设置。

完成编辑后保存并关闭文件。

接下来,为了确保我们的应用程序的敏感数据不会被复制到版本控制中,我们可以将 .env 添加到我们项目的 .gitignore 文件中,它告诉 Git 在我们的项目中要忽略哪些文件。 打开文件进行编辑:

nano .gitignore

在文件底部,为 .env 添加一个条目:

~/rails-docker/.gitignore

yarn-debug.log*
.yarn-integrity
.env

完成编辑后保存并关闭文件。

接下来,我们将创建一个 .dockerignore 文件来设置不应复制到我们容器中的内容。 打开文件进行编辑:

.dockerignore

将以下代码添加到文件中,这会告诉 Docker 忽略一些我们不需要复制到容器中的内容:

~/rails-docker/.dockerignore

.DS_Store
.bin
.git
.gitignore
.bundleignore
.bundle
.byebug_history
.rspec
tmp
log
test
config/deploy
public/packs
public/packs-test
node_modules
yarn-error.log
coverage/

.env 也添加到该文件的底部:

~/rails-docker/.dockerignore

. . .
yarn-error.log
coverage/
.env

完成编辑后保存并关闭文件。

作为最后一步,我们将创建一些种子数据,以便我们的应用程序在启动时有一些记录。

db 目录中为种子数据打开一个文件:

nano db/seeds.rb

将以下代码添加到文件中以创建四个演示鲨鱼和一个示例帖子:

~/rails-docker/db/seeds.rb

# Adding demo sharks
sharks = Shark.create([{ name: 'Great White', facts: 'Scary' }, { name: 'Megalodon', facts: 'Ancient' }, { name: 'Hammerhead', facts: 'Hammer-like' }, { name: 'Speartooth', facts: 'Endangered' }])
Post.create(body: 'These sharks are misunderstood', shark: sharks.first)

该种子数据将创建四条鲨鱼和一个与第一条鲨鱼相关联的帖子。

完成编辑后保存并关闭文件。

将您的应用程序配置为使用 PostgreSQL 并创建环境变量后,您就可以编写应用程序 Dockerfile。

第 3 步 — 编写 Dockerfile 和入口点脚本

您的 Dockerfile 指定创建应用程序容器时将包含的内容。 使用 Dockerfile 可以定义容器环境并避免依赖项或运行时版本的差异。

遵循这些 构建优化容器 的指南,我们将通过使用 Alpine 基础 并尝试将我们的图像层最小化来使我们的图像尽可能高效。

在当前目录中打开一个 Dockerfile:

nano Dockerfile

Docker 镜像是使用一系列相互构建的分层镜像创建的。 我们的第一步是为我们的应用程序添加 基础映像 ,这将构成应用程序构建的起点。

将以下代码添加到文件中以添加 Ruby alpine 图像 作为基础:

~/rails-docker/Dockerfile

FROM ruby:2.5.1-alpine

alpine 镜像源自 Alpine Linux 项目,将帮助我们减小镜像大小。 有关 alpine 镜像是否适合您的项目的更多信息,请参阅 Docker Hub Ruby 镜像页面的 Image Variants 部分下的完整讨论[ X210X]。

在开发中使用 alpine 时需要考虑的一些因素:

  • 减小图像大小将减少页面和资源加载时间,特别是如果您还将体积保持在最低限度。 这有助于使您在开发中的用户体验快速且接近于您在非容器化环境中本地工作时的体验。
  • 开发和生产映像之间的平等有助于成功部署。 由于团队经常选择在生产中使用 Alpine 图像以获得速度优势,因此使用 Alpine 基础进行开发有助于在转移到生产时解决问题。

接下来,设置一个环境变量来指定 Bundler 版本:

~/rails-docker/Dockerfile

. . .
ENV BUNDLER_VERSION=2.0.2

这是我们为避免环境中可用的默认 bundler 版本与需要 Bundler 2.0.2 的应用程序代码之间的版本冲突而采取的步骤之一。

接下来,将与应用程序一起使用所需的包添加到 Dockerfile:

~/rails-docker/Dockerfile

. . . 
RUN apk add --update --no-cache \
      binutils-gold \
      build-base \
      curl \
      file \
      g++ \
      gcc \
      git \
      less \
      libstdc++ \
      libffi-dev \
      libc-dev \ 
      linux-headers \
      libxml2-dev \
      libxslt-dev \
      libgcrypt-dev \
      make \
      netcat-openbsd \
      nodejs \
      openssl \
      pkgconfig \
      postgresql-dev \
      python \
      tzdata \
      yarn 

这些软件包包括 nodejsyarn 等。 由于我们的应用程序 使用 webpack 提供资产,因此我们需要包含 Node.jsYarn 以使应用程序按预期工作。

请记住,alpine 映像非常小:此处列出的包并未详尽说明您在容器化自己的应用程序时可能想要或需要的内容。

接下来,安装合适的 bundler 版本:

~/rails-docker/Dockerfile

. . . 
RUN gem install bundler -v 2.0.2

此步骤将保证我们的容器化环境与该项目的 Gemfile.lock 文件中的规范一致。

现在为容器上的应用程序设置工作目录:

~/rails-docker/Dockerfile

. . .
WORKDIR /app

复制 GemfileGemfile.lock

~/rails-docker/Dockerfile

. . .
COPY Gemfile Gemfile.lock ./

将这些文件复制为一个独立的步骤,然后复制 bundle install,这意味着每次更改应用程序代码时都不需要重新构建项目 gem。 这将与我们将包含在 Compose 文件中的 gem 卷结合使用,在重新创建服务但项目 gem 保持不变的情况下,它将将 gem 挂载到您的应用程序容器中。

接下来,设置 nokogiri gem build 的配置选项:

~/rails-docker/Dockerfile

. . . 
RUN bundle config build.nokogiri --use-system-libraries
. . .

此步骤使用我们在上面的 RUN apk add… 步骤中添加到应用程序容器的 libxml2 和 libxslt 库版本 构建 nokigiri

接下来,安装项目 gem:

~/rails-docker/Dockerfile

. . . 
RUN bundle check || bundle install

该指令在安装之前检查 gem 是否尚未安装。

接下来,我们将使用我们的 JavaScript 包和依赖项重复我们对 gems 使用的相同过程。 首先我们将复制包元数据,然后我们将安装依赖项,最后我们将应用程序代码复制到容器映像中。

要开始使用 Dockerfile 的 Javascript 部分,请将 package.jsonyarn.lock 从主机上的当前项目目录复制到容器:

~/rails-docker/Dockerfile

. . . 
COPY package.json yarn.lock ./

然后使用 yarn install 安装所需的软件包:

~/rails-docker/Dockerfile

. . . 
RUN yarn install --check-files

该指令包括一个带有 yarn 命令的 --check-files 标志,该功能确保任何以前安装的文件都没有被删除。 与我们的 gems 一样,当我们编写 Compose 文件时,我们将使用卷来管理 node_modules 目录中的包的持久性。

最后,复制其余的应用程序代码并使用入口点脚本启动应用程序:

~/rails-docker/Dockerfile

. . . 
COPY . ./ 

ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"]

使用入口点脚本允许我们 将容器作为可执行文件 运行。

最终的 Dockerfile 将如下所示:

~/rails-docker/Dockerfile

FROM ruby:2.5.1-alpine

ENV BUNDLER_VERSION=2.0.2

RUN apk add --update --no-cache \
      binutils-gold \
      build-base \
      curl \
      file \
      g++ \
      gcc \
      git \
      less \
      libstdc++ \
      libffi-dev \
      libc-dev \ 
      linux-headers \
      libxml2-dev \
      libxslt-dev \
      libgcrypt-dev \
      make \
      netcat-openbsd \
      nodejs \
      openssl \
      pkgconfig \
      postgresql-dev \
      python \
      tzdata \
      yarn 

RUN gem install bundler -v 2.0.2

WORKDIR /app

COPY Gemfile Gemfile.lock ./

RUN bundle config build.nokogiri --use-system-libraries

RUN bundle check || bundle install 

COPY package.json yarn.lock ./

RUN yarn install --check-files

COPY . ./ 

ENTRYPOINT ["./entrypoints/docker-entrypoint.sh"]

完成编辑后保存并关闭文件。

接下来,为入口点脚本创建一个名为 entrypoints 的目录:

mkdir entrypoints

该目录将包括我们的主入口点脚本和 Sidekiq 服务的脚本。

打开应用程序入口点脚本的文件:

nano entrypoints/docker-entrypoint.sh

将以下代码添加到文件中:

rails-docker/entrypoints/docker-entrypoint.sh

#!/bin/sh

set -e

if [ -f tmp/pids/server.pid ]; then
  rm tmp/pids/server.pid
fi

bundle exec rails s -b 0.0.0.0

第一个重要的行是 set -e,它告诉运行脚本的 /bin/sh shell 快速失败,如果脚本后面有任何问题。 接下来,脚本会检查 tmp/pids/server.pid 是否不存在,以确保在我们启动应用程序时不会发生服务器冲突。 最后,脚本使用 bundle exec rails s 命令启动 Rails 服务器。 我们使用此命令的 -b 选项将服务器绑定到所有 IP 地址,而不是默认的 localhost。 此调用使 Rails 服务器将传入请求路由到容器 IP,而不是默认的 localhost

完成编辑后保存并关闭文件。

使脚本可执行:

chmod +x entrypoints/docker-entrypoint.sh

接下来,我们将创建一个脚本来启动我们的 sidekiq 服务,该服务将处理我们的 Sidekiq 作业。 有关此应用程序如何使用 Sidekiq 的更多信息,请参阅 如何将 Sidekiq 和 Redis 添加到 Ruby on Rails 应用程序

为 Sidekiq 入口点脚本打开一个文件:

nano entrypoints/sidekiq-entrypoint.sh

将以下代码添加到文件中以启动 Sidekiq:

~/rails-docker/entrypoints/sidekiq-entrypoint.sh

#!/bin/sh

set -e

if [ -f tmp/pids/server.pid ]; then
  rm tmp/pids/server.pid
fi
 
bundle exec sidekiq

该脚本在我们的应用程序包的上下文中启动 Sidekiq。

完成编辑后保存并关闭文件。 使其可执行:

chmod +x entrypoints/sidekiq-entrypoint.sh

有了入口点脚本和 Dockerfile,您就可以在 Compose 文件中定义服务了。

第 4 步 — 使用 Docker Compose 定义服务

使用 Docker Compose,我们将能够运行我们的设置所需的多个容器。 我们将在我们的主 docker-compose.yml 文件中定义 Compose services。 Compose 中的服务是一个正在运行的容器,并且服务定义(您将包含在 docker-compose.yml 文件中)包含有关每个容器映像如何运行的信息。 Compose 工具允许您定义多个服务来构建多容器应用程序。

我们的应用程序设置将包括以下服务:

  • 应用程序本身
  • PostgreSQL 数据库
  • 雷迪斯
  • 西德基克

我们还将绑定挂载作为我们设置的一部分,以便我们在开发期间所做的任何代码更改都将立即与需要访问此代码的容器同步。

请注意,我们 不是 定义了 test 服务,因为测试超出了本教程和 系列 的范围,但您可以按照我们的先例来进行在这里用于 sidekiq 服务。

打开docker-compose.yml文件:

nano docker-compose.yml

首先,添加应用服务定义:

~/rails-docker/docker-compose.yml

version: '3.4'

services:
  app: 
    build:
      context: .
      dockerfile: Dockerfile
    depends_on:
      - database
      - redis
    ports: 
      - "3000:3000"
    volumes:
      - .:/app
      - gem_cache:/usr/local/bundle/gems
      - node_modules:/app/node_modules
    env_file: .env
    environment:
      RAILS_ENV: development

app 服务定义包括以下选项:

  • build:这定义了 Compose 构建应用程序映像时将应用的配置选项,包括 contextdockerfile。 如果你想使用像 Docker Hub 这样的注册表中的现有镜像,你可以使用 image 指令 来代替,其中包含有关你的用户名、存储库和镜像标签的信息。
  • context:这定义了映像构建的构建上下文——在这种情况下,是当前项目目录。
  • dockerfile:指定当前项目目录中的 Dockerfile 作为 Compose 将用来构建应用程序映像的文件。
  • depends_on:首先设置 databaseredis 容器,以便它们在 app 之前启动并运行。
  • ports:这会将主机上的端口 3000 映射到容器上的端口 3000
  • volumes :我们在这里包括两种类型的坐骑: 第一个是绑定挂载,将主机上的应用程序代码挂载到容器上的 /app 目录。 这将有助于快速开发,因为您对主机代码所做的任何更改都将立即填充到容器中。 第二个是命名卷 gem_cache。 当 bundle install 指令在容器中运行时,它将安装项目 gem。 添加此卷意味着如果您重新创建容器,gems 将被挂载到新容器中。 此挂载假定项目没有任何更改,因此如果您在开发中对项目 gem 进行了更改,则需要记住在重新创建应用程序服务之前删除此卷。 第三个卷是 node_modules 目录的命名卷。 与其将 node_modules 挂载到主机上,否则会导致开发中的包差异和权限冲突,此卷将确保此目录中的包被持久化并反映项目的当前状态。 同样,如果您修改项目的节点依赖项,您将需要删除并重新创建此卷。
  • env_file:这告诉 Compose 我们想从位于构建上下文中的名为 .env 的文件中添加环境变量。
  • environment:使用这个选项允许我们设置一个非敏感的环境变量,将有关 Rails 环境的信息传递给容器。

接下来,在 app 服务定义下方,添加以下代码来定义您的 database 服务:

~/rails-docker/docker-compose.yml

. . .
  database:
    image: postgres:12.1
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

app 服务不同,database 服务直接从 Docker Hub 拉取 postgres 镜像。 请注意,我们也在此处固定版本,而不是将其设置为 latest 或不指定它(默认为 latest)。 这样,我们可以确保此设置适用于此处指定的版本,并避免因破坏图像代码更改而出现意外。

我们还包括一个 db_data 卷,它将在容器启动之间保存我们的应用程序数据。 此外,我们已将 init.sql 启动脚本安装到容器上的相应目录 docker-entrypoint-initdb.d/ 中,以创建我们的 sammy 数据库用户。 在图像入口点创建默认的 postgres 用户和数据库后,它将运行在 docker-entrypoint-initdb.d/ 目录中找到的任何脚本,您可以将其用于必要的初始化任务。 有关详细信息,请查看 PostgreSQL 映像文档初始化脚本 部分

接下来,添加 redis 服务定义:

~/rails-docker/docker-compose.yml

. . .
  redis:
    image: redis:5.0.7

database 服务一样,redis 服务使用来自 Docker Hub 的映像。 在这种情况下,我们不会持久化 Sidekiq 作业缓存。

最后,添加 sidekiq 服务定义:

~/rails-docker/docker-compose.yml

. . .
  sidekiq:
    build:
      context: .
      dockerfile: Dockerfile
    depends_on:
      - app      
      - database
      - redis
    volumes:
      - .:/app
      - gem_cache:/usr/local/bundle/gems
      - node_modules:/app/node_modules
    env_file: .env
    environment:
      RAILS_ENV: development
    entrypoint: ./entrypoints/sidekiq-entrypoint.sh

我们的 sidekiq 服务在几个方面类似于我们的 app 服务:它使用相同的构建上下文和映像、环境变量和卷。 但是,它依赖于 appredisdatabase 服务,因此将是最后启动的。 此外,它使用 entrypoint 将覆盖 Dockerfile 中设置的入口点。 此 entrypoint 设置指向 entrypoints/sidekiq-entrypoint.sh,其中包括启动 sidekiq 服务的相应命令。

作为最后一步,在 sidekiq 服务定义下方添加卷定义:

~/rails-docker/docker-compose.yml

. . .
volumes:
  gem_cache:
  db_data:
  node_modules:

我们的顶级卷键定义了卷 gem_cachedb_datanode_modules。 当 Docker 创建卷时,卷的内容存储在由 Docker 管理的主机文件系统 /var/lib/docker/volumes/ 的一部分中。 每个卷的内容都存储在 /var/lib/docker/volumes/ 下的目录中,并被挂载到使用该卷的任何容器中。 这样,即使我们删除并重新创建 database 服务,我们的用户将创建的鲨鱼信息数据将保留在 db_data 卷中。

完成的文件将如下所示:

~/rails-docker/docker-compose.yml

version: '3.4'

services:
  app: 
    build:
      context: .
      dockerfile: Dockerfile
    depends_on:     
      - database
      - redis
    ports: 
      - "3000:3000"
    volumes:
      - .:/app
      - gem_cache:/usr/local/bundle/gems
      - node_modules:/app/node_modules
    env_file: .env
    environment:
      RAILS_ENV: development

  database:
    image: postgres:12.1
    volumes:
      - db_data:/var/lib/postgresql/data
      - ./init.sql:/docker-entrypoint-initdb.d/init.sql

  redis:
    image: redis:5.0.7

  sidekiq:
    build:
      context: .
      dockerfile: Dockerfile
    depends_on:
      - app      
      - database
      - redis
    volumes:
      - .:/app
      - gem_cache:/usr/local/bundle/gems
      - node_modules:/app/node_modules
    env_file: .env
    environment:
      RAILS_ENV: development
    entrypoint: ./entrypoints/sidekiq-entrypoint.sh

volumes:
  gem_cache:
  db_data:
  node_modules:     

完成编辑后保存并关闭文件。

编写好服务定义后,您就可以启动应用程序了。

第 5 步 — 测试应用程序

准备好 docker-compose.yml 文件后,您可以使用 docker-compose up 命令创建服务并为数据库播种。 您还可以通过使用 docker-compose down 停止和删除容器并重新创建它们来测试您的数据是否会持续存在。

首先,通过运行带有 -d 标志的 docker-compose up 来构建容器映像并创建服务,这将在后台运行容器:

docker-compose up -d

您将看到您的服务已创建的输出:

OutputCreating rails-docker_database_1 ... done
Creating rails-docker_redis_1    ... done
Creating rails-docker_app_1      ... done
Creating rails-docker_sidekiq_1  ... done

您还可以通过显示服务的日志输出来获取有关启动过程的更多详细信息:

docker-compose logs 

如果一切都已正确启动,您将看到如下内容:

Outputsidekiq_1   | 2019-12-19T15:05:26.365Z pid=6 tid=grk7r6xly INFO: Booting Sidekiq 6.0.3 with redis options {:host=>"redis", :port=>"6379", :id=>"Sidekiq-server-PID-6", :url=>nil}
sidekiq_1   | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: Running in ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux-musl]
sidekiq_1   | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: See LICENSE and the LGPL-3.0 for licensing details.
sidekiq_1   | 2019-12-19T15:05:31.097Z pid=6 tid=grk7r6xly INFO: Upgrade to Sidekiq Pro for more features and support: http://sidekiq.org
app_1       | => Booting Puma
app_1       | => Rails 5.2.3 application starting in development 
app_1       | => Run `rails server -h` for more startup options
app_1       | Puma starting in single mode...
app_1       | * Version 3.12.1 (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 tcp://0.0.0.0:3000
app_1       | Use Ctrl-C to stop
. . .
database_1  | PostgreSQL init process complete; ready for start up.
database_1  | 
database_1  | 2019-12-19 15:05:20.160 UTC [1] LOG:  starting PostgreSQL 12.1 (Debian 12.1-1.pgdg100+1) on x86_64-pc-linux-gnu, compiled by gcc (Debian 8.3.0-6) 8.3.0, 64-bit
database_1  | 2019-12-19 15:05:20.160 UTC [1] LOG:  listening on IPv4 address "0.0.0.0", port 5432
database_1  | 2019-12-19 15:05:20.160 UTC [1] LOG:  listening on IPv6 address "::", port 5432
database_1  | 2019-12-19 15:05:20.163 UTC [1] LOG:  listening on Unix socket "/var/run/postgresql/.s.PGSQL.5432"
database_1  | 2019-12-19 15:05:20.182 UTC [63] LOG:  database system was shut down at 2019-12-19 15:05:20 UTC
database_1  | 2019-12-19 15:05:20.187 UTC [1] LOG:  database system is ready to accept connections
. . . 
redis_1     | 1:M 19 Dec 2019 15:05:18.822 * Ready to accept connections

您还可以使用 docker-compose ps 检查容器的状态:

docker-compose ps

您将看到指示您的容器正在运行的输出:

Output         Name                        Command               State           Ports         
-----------------------------------------------------------------------------------------
rails-docker_app_1        ./entrypoints/docker-resta ...   Up      0.0.0.0:3000->3000/tcp
rails-docker_database_1   docker-entrypoint.sh postgres    Up      5432/tcp              
rails-docker_redis_1      docker-entrypoint.sh redis ...   Up      6379/tcp              
rails-docker_sidekiq_1    ./entrypoints/sidekiq-entr ...   Up                  

接下来,使用以下 docker-compose exec 命令 创建并播种您的数据库并在其上运行迁移:

docker-compose exec app bundle exec rake db:setup db:migrate

docker-compose exec 命令允许您在服务中运行命令; 我们在这里使用它在我们的应用程序包的上下文中运行 rake db:setupdb:migrate 以创建和播种数据库并运行迁移。 当您在开发中工作时,当您想要针对您的开发数据库运行迁移时,docker-compose exec 将证明对您很有用。

运行此命令后,您将看到以下输出:

OutputCreated database 'rails_development'
Database 'rails_development' already exists
-- enable_extension("plpgsql")
   -> 0.0140s
-- create_table("endangereds", {:force=>:cascade})
   -> 0.0097s
-- create_table("posts", {:force=>:cascade})
   -> 0.0108s
-- create_table("sharks", {:force=>:cascade})
   -> 0.0050s
-- enable_extension("plpgsql")
   -> 0.0173s
-- create_table("endangereds", {:force=>:cascade})
   -> 0.0088s
-- create_table("posts", {:force=>:cascade})
   -> 0.0128s
-- create_table("sharks", {:force=>:cascade})
   -> 0.0072s

在您的服务运行后,您可以在浏览器中访问 localhost:3000http://your_server_ip:3000。 您将看到如下所示的登录页面:

我们现在可以测试数据持久性。 通过单击 Get Shark Info 按钮创建一条新鲨鱼,它将带您到 sharks/index 路线:

为了验证应用程序是否正常工作,我们可以向其添加一些演示信息。 点击 New Shark。 由于项目的 身份验证设置 ,系统将提示您输入用户名 (sammy) 和密码 (shark)。

New Shark 页面上,在 Name 字段中输入“Mako”,在 Facts 字段中输入“Fast”。

点击Create Shark按钮创建鲨鱼。 创建鲨鱼后,单击站点导航栏上的 Home 以返回主应用程序登录页面。 我们现在可以测试 Sidekiq 是否正常工作。

点击哪些鲨鱼处于危险之中?按钮。 由于您尚未上传任何濒临灭绝的鲨鱼,这将带您进入 endangered index 视图:

点击导入濒危鲨鱼导入鲨鱼。 您将看到一条状态消息,告诉您鲨鱼已被导入:

您还将看到导入的开始。 刷新页面以查看整个表格:

多亏了 Sidekiq,我们大批量上传濒临灭绝的鲨鱼已经成功,没有锁定浏览器或干扰其他应用程序功能。

点击页面底部的Home按钮,返回应用主页面:

从这里,再次单击哪些鲨鱼处于危险之中?。 您将再次看到上传的鲨鱼。

现在我们知道我们的应用程序工作正常,我们可以测试我们的数据持久性。

回到你的终端,输入以下命令来停止和移除你的容器:

docker-compose down

请注意,我们不包括 --volumes 选项; 因此,我们的 db_data 卷不会被删除。

以下输出确认您的容器和网络已被删除:

OutputStopping rails-docker_sidekiq_1  ... done
Stopping rails-docker_app_1      ... done
Stopping rails-docker_database_1 ... done
Stopping rails-docker_redis_1    ... done
Removing rails-docker_sidekiq_1  ... done
Removing rails-docker_app_1      ... done
Removing rails-docker_database_1 ... done
Removing rails-docker_redis_1    ... done
Removing network rails-docker_default

重新创建容器:

docker-compose up -d

使用 docker-compose execbundle exec rails consoleapp 容器上打开 Rails 控制台:

docker-compose exec app bundle exec rails console

在提示符下,检查数据库中的 last Shark 记录:

Shark.last.inspect

您将看到刚刚创建的记录:

IRB session  Shark Load (1.0ms)  SELECT  "sharks".* FROM "sharks" ORDER BY "sharks"."id" DESC LIMIT $1  [["LIMIT", 1]]
=> "#<Shark id: 5, name: \"Mako\", facts: \"Fast\", created_at: \"2019-12-20 14:03:28\", updated_at: \"2019-12-20 14:03:28\">"

然后,您可以使用以下命令检查您的 Endangered 鲨鱼是否已持久化:

Endangered.all.count
IRB session   (0.8ms)  SELECT COUNT(*) FROM "endangereds"
=> 73

您的 db_data 卷已成功挂载到重新创建的 database 服务,使您的 app 服务可以访问保存的数据。 如果您通过访问 localhost:3000/sharkshttp://your_server_ip:3000/sharks 直接导航到 index shark 页面,您还将看到显示的记录:

您的濒临灭绝的鲨鱼也将出现在 localhost:3000/endangered/datahttp://your_server_ip:3000/endangered/data 视图中:

您的应用程序现在在启用了数据持久性和代码同步的 Docker 容器上运行。 您可以继续在主机上测试本地代码更改,由于我们定义为 app 服务的一部分的绑定挂载,这些更改将同步到您的容器。

结论

按照本教程,您已经使用 Docker 容器为 Rails 应用程序创建了开发设置。 通过提取敏感信息并将应用程序的状态与代码解耦,您使您的项目更加 模块化和可移植 。 您还配置了一个样板 docker-compose.yml 文件,您可以根据开发需求和要求的变化对其进行修改。

随着您的开发,您可能有兴趣了解有关为容器化和 Cloud Native 工作流程设计应用程序的更多信息。 有关这些主题的更多信息,请参阅 为 Kubernetes 构建应用程序为 Kubernetes 现代化应用程序。 或者,如果您想投资 Kubernetes 学习序列,请查看 Kubernetes for Full-Stack Developers 课程

要了解更多关于应用程序代码本身的信息,请参阅本系列中的其他教程: