将多服务应用程序 Docker 化以进行本地开发总结

2025-06-08

将多服务应用程序 Docker 化以进行本地开发

包起来

由于现在许多复杂的 Web 应用程序都在生产容器上运行,我们继续以“老式”的方式开发它们,在本地开发机器上安装PostgresqlRedisRuby和其他组件。

维护开发过程变得越来越困难,特别是当系统变得异构并扩展到大量服务,并且依赖各种版本的组件运行时。当依赖组件的版本多种多样时,这一点就变得尤为现实。

在本文中,我将以我参与的项目Amplifr为例,回顾一下本地开发容器化。借助 docker-compose 和 docker 网络,这个过程简单高效。

由于所有基础设施都是容器化的,并且在生产中使用 Kubernetes 进行管理,我们将仅参与本地开发的设置,遵循一个原则——开发过程的便利性

本地容器化的好处

  • 无需在本地机器上安装所有组件,例如数据库引擎、语言解释器等,保持本地机器清洁
  • 自然支持不同的环境,在本地机器上运行不同版本的 Ruby、Postgresql 服务

项目概述

尽管 Amplifr 的后端在 Rails 上运行,但该项目还具有复杂的前端,由独立的 Node.js 服务器和 Logux web-socket 服务器以及其他辅助服务提供服务,这些服务是用 Node.js、Ruby 和 Golang 编写的。

下图是该项目的简化架构:

Amplifr 的整体服务地图

我将快速回顾一下整个系统的一些组件。

后端服务器

后端是经典的 Rails 应用程序,执行所有业务逻辑并使用 Sidekiq 执行许多后台作业。

前端服务器

前端是整个应用程序唯一的公共 HTTP 入口点。它提供前端资源,并将其他请求代理到 Rails 后端。
后端也与前端服务器集成,用于共享一些数据,例如browsers.json用于正确渲染 HTML 的文件。

前后端集成

Logux 服务器

Logux 是一个暴露 web-socket 端口的服务器,与客户端浏览器保持双向连接。为了执行业务逻辑,它提供了两种与后端进行 HTTP 集成的方式。这使得我们可以将所有业务逻辑保留在 Rails 后端,并通过 HTTP 访问 Logux 来从后端返回通知。

Logux-后端集成

“链接缩短器”

链接缩短器是一个用 Golang 编写的 Web 服务。它用于缩短和扩展链接,并管理链接扩展的整体统计信息。
链接缩短服务器与后端集成

“预览”服务

预览是公开服务,用于在客户端浏览器中渲染任何链接的 OpenGraph 表示。它仅具有公开的 http 端点。

其他组件

Shortener - 是一款独立的服务,用于缩短 URL 并保存有关链接扩展的分析数据。它使用 Golang 编写。它拥有一个外部公共端点用于扩展已缩短的链接,以及一个内部端点用于在后端的后台作业中发布社交内容的同时缩短链接。

还有一些其他内部服务,例如电报和 Facebook 机器人,它们仅具有后端集成。

组件依赖项

大多数组件本身都是复杂的Web服务,依赖于底层组件,例如Postgres,Redis和其他服务低级系统服务。
后端组件的内部结构以及如何对其进行 Docker 化

集装箱化

💡我们将使用Docker Compose分别对每个服务进行容器化。它是一个用于定义和运行多容器 Docker 应用程序的工具,只需使用一个命令即可运行所有服务,轻松启动:

docker-compose up
Enter fullscreen mode Exit fullscreen mode

💡为了使服务集成,我们将使用Docker 网络,它允许任何 Docker 容器相互通信。internal为了简单起见,我们将为所有组件仅使用一个 Docker 网络。更准确地说,读者将能够为每个服务依赖项和每组连接设置单独的网络。

Docker化Ruby后端

这里我们有标准技术栈:Postgres、Redis、Rails Web 服务器和 Sidekiq 后台。对于所有这些,我们将在 中定义服务docker-compose.yaml

以下是关键点:

  • 对于 Postgres 和 Redis,我们将定义持久卷来保存运行之间的数据
  • 我们不会将 Ruby 源代码复制到容器中,而是将 Rails 应用程序源代码挂载到/app文件夹
  • 我们还将定义捆绑包和其他内容的持久存储,以便在下次启动时增加
  • 我们将定义amplifr_internal网络并将交互容器添加到该网络
  • 应用程序应该准备好配置环境变量,我们将在 docker-compose 文件中设置这些变量
  • 我们将在 YAML 文件中定义基本应用服务,然后使用 YAML 语法的 Anchors 和别名以免重复。

❗请记住,此配置与为生产构建 docker 镜像的方式不同,其中所有源代码和所有依赖包都被复制到 docker 镜像中,以使其足够并且没有外部依赖!

以下是所有配置的完整要点,但请允许我注意以下要点:

描述从其继承的基础服务

services:
  app: &app
    build:
      context: .
      dockerfile: Dockerfile.dev
      args:
        PG_VERSION: '9.6'
    image: amplifr-dev:0.1.0
    volumes:
      - .:/app:cached
      - bundle:/bundle
    environment:
      # environment settings
      - BUNDLE_PATH=/bundle
      - BUNDLE_CONFIG=/app/.bundle/config
      - RAILS_ENV=${RAILS_ENV:-development}

      - DATABASE_URL=postgresql://postgres@postgres/amplifr_${RAILS_ENV}
      - REDIS_URL=redis://redis:6379/

      # service integrations
      - FRONTEND_URL=https://frontend-server:3001/
      - LOGUX_URL=http://logux-server:31338
    depends_on:
      - postgres
      - redis
    tmpfs:
      - /tmp
Enter fullscreen mode Exit fullscreen mode

基础服务的容器将使用 Postgres 版本作为参数构建Dockerfile.dev。所有其他基于 Ruby 的镜像都将继承基础版本。服务继承关系图如下:
服务继承

我们还定义了当前文件夹到容器/app目录的映射,并为 bundles 挂载了 docker 卷。这样可以避免每次都安装依赖项。

我们还定义了两组环境变量:
1)system变量,例如BUNDLE_PATHREDIS_URLDATABASE_URLURL。2
)用于集成的依赖服务内部URL:
FRONTEND_URL- 是前端服务器获取支持的浏览器列表的内部端点。-
LOGUX_URL是用于从Rails应用程序向Logux发送操作的内部Logux HTTP端点。

描述“跑步者”

运行器服务用于在 Rails 环境中运行维护命令,例如 Rake 任务或生成器。它是面向控制台的服务,因此我们需要设置stdin_opentty配置选项,这对应于docker 的-i--t选项,并为容器启动启用 bash shell:

services:
  runner:
    <<: *backend
    stdin_open: true
    tty: true
    command: /bin/bash
Enter fullscreen mode Exit fullscreen mode

我们可以这样使用它:

docker-compose run runner bundle exec rake db:create

# or run container and any command within the container
docker-compose run runner
Enter fullscreen mode Exit fullscreen mode

组成服务器

定义 Web 服务器。这里的关键点在于,我们需要定义一个额外的 Docker 网络internal,并将 Web 服务器添加到其中,并backend-server为该网络中的容器主机指定一个别名。这样,Web 容器就可以通过该backend-server网络名称访问了。

services:
  server:
    <<: *app
    command: bundle exec thin start
    networks:
      default:
      internal:
        aliases:
          - backend-server
    ports:
      - '3000:3000'

networks:
  internal:
Enter fullscreen mode Exit fullscreen mode

编写 Sidekiq

很简单,它只需运行 sidekiq 并继承基本服务:

services:
  sidekiq:
    <<: *app
    command: sidekiq
Enter fullscreen mode Exit fullscreen mode

编写 Redis 和 Postgres

  postgres:
    image: postgres:9.6
    volumes:
      - postgres:/var/lib/postgresql/data
    ports:
      - 5432

  redis:
    image: redis:3.2-alpine
    volumes:
      - redis:/data
    ports:
      - 6379

volumes:
  postgres:
  redis:
Enter fullscreen mode Exit fullscreen mode

这里的重点是,我们为容器的路径挂载卷,数据存储在那里。它在运行之间持久保存数据。

Dockerfile

我们不会深入编写Dockefile。你可以在这里找到它。只需注意,它继承自标准 ruby​​ 镜像,包含一些构建包所需的组件,例如 Postgresql 客户端和其他一些二进制文件。

用法

使用方法非常简单:

docker-compose run runner ./bin/setup # runs the bin/setup in docker
docker-compose run runner bundle exec rake db:drop # runs rake task
docker-compose up server # get the web-server running
docker-compose up -d # runs all the services (web, sidekiq)
docker-compose up rails db # runs the postgres client
Enter fullscreen mode Exit fullscreen mode

Docker Compose 还允许指定服务依赖关系,并在运行服务需要时启动依赖服务,ge Sidekiq 需要 Redis 和 Postgres 服务才能正常工作,这就是我们在depends_on服务部分定义它们的原因。

这是服务依赖关系图,显示了服务的运行方式:
存在依赖关系时的服务启动顺序

概括

rails db我们在本地运行了 Rails 应用程序进行开发。它的工作方式与本地相同:持久化数据库,运行 rake 任务。此外,像 这样的命令rails c在容器中也能正常工作。

主要优点是我们可以通过更改一行轻松更改 Postgres 版本或 Ruby 版本,然后重建图像并尝试在新环境中运行。

Docker 化 Node.js(前端服务器)

这里的主要关键点是:

  • 使用基础官方nodedocker 镜像,无需任何调整
  • 将服务添加serveramplifr_internal网络
  • 定义BACKEND_URL环境变量映射到后端服务内部的docker路径。
  • 挂载mode_modulesNode.js 模块安装路径的卷
version: '3.4'

services:
  app: &app
    image: node:11
    working_dir: /app
    environment:
      - NODE_ENV=development
      - BACKEND_URL=http://backend-server:3000
    volumes:
      - .:/app:cached
      - node_modules:/app/node_modules

  runner:
    <<: *app
    command: /bin/bash
    stdin_open: true
    tty: true

  server:
    <<: *app
    command: bash -c "yarn cache clean && yarn install && yarn start"
    networks:
      default:
      amplifr_internal:
        aliases:
          - frontend-server
    ports:
      - "3001:3001"

networks:
  amplifr_internal:
    external: true

volumes:
  node_modules:
Enter fullscreen mode Exit fullscreen mode

用法

现在可以轻松启动前端服务器,只需运行以下命令:

docker-compose up server
Enter fullscreen mode Exit fullscreen mode

但是需要后端先启动,因为前端服务指的是internal网络,后端启动后网络就会启动。

将 Logux 服务器 Docker 化

在任何简单情况下,Logux 服务器都具有任何数据库依赖项,并且可以像前端一样进行配置。唯一的区别是,Logux 服务有自己的环境变量,用于设置与集成服务的交互。

docker-compose up server # runs the server
Enter fullscreen mode Exit fullscreen mode

Dockerizing Golang(链接缩短器 Web 服务)

主要思想也是一样的:

  • 使用设置的docker镜像Golang,将应用程序源代码挂载到那里并使用go run解释器运行它。
  • 与 docker 网络共享服务以便与 Ruby 后端集成

我们的 Web 服务依赖 Postgres 和 Redis。让我们从这里开始描述Dockerfile,整体配置示例如下

FROM golang:1.11

ARG MIGRATE_VERSION=4.0.2

# install postgres client for local development
RUN apt-get update && apt-get install -y postgresql-client

# install dep tool to ensuring dependencies
RUN go get -u github.com/golang/dep/cmd/dep

# install migrate cli for running database migrations
ADD https://github.com/golang-migrate/migrate/releases/download/v${MIGRATE_VERSION}/migrate.linux-amd64.tar.gz /tmp
RUN tar -xzf /tmp/migrate.linux-amd64.tar.gz -C /usr/local/bin && mv /usr/local/bin/migrate.linux-amd64 /usr/local/bin/migrate

ENV APP ${GOPATH}/src/github.com/evilmartians/ampgs
WORKDIR ${APP}
Enter fullscreen mode Exit fullscreen mode

以下是一些有趣的细节:

  • 我们为本地开发镜像安装了 postgres-client。它简化了数据库的访问,方便您随时访问:docker-compose run runner "psql $DATABASE_URL"。Ruby 后端 dockerization 也提供了类似的功能。
  • 我们安装该dep工具来安装并确保所有依赖项:docker-compose run runner dep ensure
  • 我们将迁移工具安装到镜像中,以允许直接从 docker 容器进行数据库迁移:docker-compose run runner "migrate -source file://migrations/ -database ${DATABASE_URL} up"

‼️ 我们不需要生产环境 docker 镜像中的大多数工具,因为它只包含已编译的二进制文件。

我们将使用与 Ruby 服务相同的方式将 Golang 服务 docker 化:

  • 提取基础app服务和特殊runner服务以运行维护任务
  • 添加具有持久数据卷的 Postgres 和 Redis 依赖项

以下是该文件的重要部分docker-compose.yml

services:
  # base service definition
  app: &app
    image: ampgs:0.3.1-development
    build:
      context: .
      dockerfile: docker/development/Dockerfile
    environment:
      REDIS_URL: redis://redis:6379/6
      DATABASE_URL: postgres://postgres:postgres@postgres:5432/ampgs
    volumes:
      - .:/go/src/github.com/evilmartians/ampgs
    depends_on:
      - redis
      - postgres

  runner:
    <<: *app

  web:
    <<: *app
    command: "go run ampgs.go"
    ports:
      - '8000:8000'
    networks:
      default:
      amplifr_internal:
        aliases:
          - ampgs-server
Enter fullscreen mode Exit fullscreen mode

包起来

Docker-compose 是一款强大的工具,可以简化复杂服务的管理。
让我回顾一下在使用 Docker-compose 进行本地开发 Docker 化的主要原则:

  • 将源代码以文件夹形式挂载到容器中,而不是用源代码副本重建 Docker 镜像。这可以节省每次本地重启的时间。
  • 使用docker网络来构建服务之间的通信。它有助于同时测试所有服务,但保持它们的环境独立。
  • 服务通过向 Docker 容器提供环境变量来相互了解docker-compose

就这样。感谢阅读!

鏂囩珷鏉ユ簮锛�https://dev.to/amplifr/dockerize-the-multi-services-application-for-local-development-2oig
PREV
学习 MERN 的顶级课程。
NEXT
Docker 是什么?为什么它对开发者如此重要且必不可少?第一部分