简介

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

  • 环境是一致的,这意味着你可以为你的项目选择你想要的语言和依赖项,而不用担心系统冲突。

  • 环境被隔离,更容易解决问题和加入新的团队成员。

  • 环境是可移植的,允许您打包并与他人共享您的代码。

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

  • 将宿主机上的应用程序代码与容器内的代码同步,方便开发过程中的更改。

  • 在容器重新启动之间保留应用程序数据。

  • 配置 Sidekiq 工作人员以按预期处理作业。

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

Sidekiq 应用主页

先决条件

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

  • 运行 Ubuntu 18.04 的本地开发机器或服务器,以及具有sudo权限和活动防火墙的非 root 用户。有关如何设置这些设置的指导,请参阅此初始服务器设置指南。

  • Docker 安装在您的本地机器或服务器上,按照如何在 Ubuntu 18.04上安装和使用 Docker 的步骤 1 和 2。

  • Docker Compose 安装在您的本地计算机或服务器上,按照如何在 Ubuntu 18.04上安装 Docker Compose 的第 1 步。

第 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,您需要将pggem添加到项目的依赖项中,这些依赖项在其 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
. . .

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

~/rails-docker/Gemfile

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

最后把development下面的spring-watcher-listengem注释掉:

~/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 可能是个好主意。

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

准备好项目存储库,将pggem 添加到 Gemfile 并注释掉spring-watcher-listengem,您就可以配置应用程序以使用 PostgreSQL。

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

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

  • 将应用程序配置为使用 PostgreSQL 作为默认适配器。

  • 使用我们的数据库用户名和密码以及 Redis 主机将.env文件添加到项目中。

  • 创建init.sql脚本为数据库创建sammy用户。

  • 为 Sidekiq 添加一个初始化程序以便它可以与我们的容器化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命令设置适配器,如步骤 3ofHow To Use PostgreSQL with Your Ruby on Rails Application on Ubuntu 18.04中所述。这会将您的适配器设置为config/database.yml并自动将pggem 添加到项目中。

现在我们已经引用了环境变量,我们可以使用我们的首选设置为它们创建一个文件。以这种方式提取配置设置是应用程序开发的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 创建的databasePostgreSQL 服务。我们还设置了一个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 base并尝试总体上最小化我们的镜像层来使我们的镜像尽可能高效。

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

nano Dockerfile

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

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

~/rails-docker/Dockerfile

FROM ruby:2.5.1-alpine

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

在开发中使用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.js和Yarn才能使应用程序按预期工作。

请记住,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 挂载到您的应用程序容器中。

接下来,设置nokogirigem 构建的配置选项:

~/rails-docker/Dockerfile

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

此步骤使用我们在上面的RUN apk add…步骤中添加到应用程序容器的libxml2libxslt库版本](https://nokogiri.org/tutorials/installing_nokogiri.html#install-with-system-libraries)构建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/shshell 快速失败,如果脚本后面有任何问题。接下来,脚本会检查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:我们在这里包括两种类型的坐骑:

  • 第一个是bind mount,将我们宿主机上的应用代码挂载到容器上的/app目录。这将有助于快速开发,因为您对主机代码所做的任何更改都将立即填充到容器中。

  • 第二个是一个名为的卷,gem_cache。当bundle install指令在容器中运行时,它会安装项目 gems。添加此卷意味着如果您重新创建容器,gems 将被挂载到新容器中。此挂载假定项目没有任何更改,因此如果您_do_ 在开发中对项目 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 映像文档的 Initialization scripts 部分

接下来,添加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。您将看到如下所示的登录页面:

Sidekiq 应用主页

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

带有种子数据的鲨鱼指数页面

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

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

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

点击**哪些鲨鱼处于危险之中?**按钮。由于您还没有上传任何濒临灭绝的鲨鱼,这将带您到endangered``index视图:

濒危索引视图

点击 Import Endangered Sharks 以导入鲨鱼。您将看到一条状态消息,告诉您鲨鱼已被导入:

开始导入

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

刷新表

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

单击页面底部的 Home 按钮,您将返回应用程序主页面:

Sidekiq 应用主页

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

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

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

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 console打开app容器上的 Rails 控制台:

docker-compose exec app bundle exec rails console

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

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页面,您还将看到显示的记录:

鲨鱼索引页面与 Mako

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

刷新表

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

结论

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

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

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

  • 如何构建 Ruby on Rails 应用程序

  • 如何为 Ruby on Rails 应用程序创建嵌套资源

  • 如何向 Ruby on Rails 应用程序添加刺激

  • 如何将引导程序添加到 Ruby on Rails 应用程序

  • 如何将 Sidekiq 和 Redis 添加到 Ruby on Rails 应用程序

Logo

开发云社区提供前沿行业资讯和优质的学习知识,同时提供优质稳定、价格优惠的云主机、数据库、网络、云储存等云服务产品

更多推荐