You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Docker中同一应用多镜像搭配多动态数据库实现方案

如何在Docker中为同一应用创建多镜像并搭配多个动态数据库

你已经实现了10个独立域名对应独立数据库的部署,但当前方案存在大量重复配置(每个域名单独编写Dockerfile、docker-compose服务),维护成本极高。以下是优化后的实现方案,通过参数化配置减少重复代码,同时保留每个域名的独立数据库与镜像:

核心优化思路

  • 用**Docker构建参数(Build Args)**替代硬编码的域名、数据库名等配置,实现单个Dockerfile适配所有域名
  • 使用环境变量模板动态生成Rails和Angular的配置文件
  • 在docker-compose中通过YAML锚点复用服务配置,避免重复编写相同结构
  • 为每个域名分配独立的PostgreSQL数据库(可在单个Postgres容器内,或按需使用独立容器)

后端(Rails)优化配置

通用Dockerfile(无需为每个域名单独编写)

FROM ruby:2.3
LABEL maintainer="maintainer name <maintainer@gmail.co>"

# 安装依赖
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs

# 定义构建参数,构建时传入具体值
ARG DOMAIN_NAME
ARG DB_NAME
ARG APP_PORT=3000

# 设置环境变量,默认使用构建参数值
ENV RAILS_ROOT /var/www/${DOMAIN_NAME}
ENV RAILS_ENV='production'
ENV RACK_ENV='production'
ENV SHARED_DIR=$RAILS_ROOT/shared
ENV DATABASE_NAME=${DB_NAME}
ENV HOST="https://${DOMAIN_NAME}"
ENV BIND_ADDR="tcp:0.0.0.0:${APP_PORT}"
ENV BASEURL="https://${DOMAIN_NAME}"
ENV FRONTEND_URL="https://${DOMAIN_NAME}"

# 创建应用目录
RUN mkdir -p $RAILS_ROOT/app
RUN mkdir -p $RAILS_ROOT/shared
RUN mkdir -p $RAILS_ROOT/shared/tmp/sockets
RUN mkdir -p $RAILS_ROOT/shared/tmp/pids

WORKDIR $RAILS_ROOT/app

# 复制Gemfile并安装依赖
COPY Gemfile Gemfile
COPY Gemfile.lock Gemfile.lock
RUN bundle install --jobs 20 --retry 5

# 复制项目文件
COPY . .
RUN ["chmod", "+x", "./docker-entrypoint.sh"]

EXPOSE ${APP_PORT}

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

动态生成database.yml

在Rails项目中创建config/database.yml.template模板文件:

production:
  adapter: postgresql
  encoding: unicode
  database: <%= ENV['DATABASE_NAME'] %>
  pool: 5
  username: postgres
  password: <%= ENV['DB_PASSWORD'] %>
  host: db

修改docker-entrypoint.sh,在启动服务前动态生成配置:

#!/bin/bash

# 基于模板生成database.yml
erb config/database.yml.template > config/database.yml

# 执行数据库初始化(首次启动时需要)
bundle exec rake db:create
bundle exec rake db:migrate

# 启动Rails服务
bundle exec puma -C config/puma.rb

前端(Angular)优化配置

通用Dockerfile

# Stage 1: 编译构建Angular项目
FROM node:10

# 构建参数
ARG DOMAIN_NAME
ARG API_URL
ARG PORT=9000

# 环境变量
ENV NODE_ROOT /var/www/${DOMAIN_NAME}/
ENV API_URL ${API_URL}
ENV PORT ${PORT}

RUN mkdir -p $NODE_ROOT/frontend-app
WORKDIR $NODE_ROOT/frontend-app

# 复制依赖文件并安装
COPY package*.json ./
COPY angular*.json ./
RUN npm install
RUN npm i ng-multiselect-dropdown@0.3.1

# 复制项目文件
COPY ./ ./

# 动态生成environment.prod.ts
cat > src/environments/environment.prod.ts << EOF
export const environment = {
  production: true,
  apiUrl: '${API_URL}'
};
EOF

# 构建生产版本
RUN node --max_old_space_size=9048 ./node_modules/@angular/cli/bin/ng build --prod --build-optimizer

EXPOSE ${PORT}

CMD ["node", "server.js"]

docker-compose.yml优化(复用服务配置)

使用YAML锚点和扩展字段,减少重复代码:

version: '3.2'

# 定义Rails服务通用配置锚点
x-rails-service: &rails-service
  build:
    context: .
    args:
      - DB_PASSWORD=your_postgres_password
  extra_hosts:
    - "localhost:127.0.0.1"
  stdin_open: true
  tty: true
  depends_on:
    - db
  volumes:
    - type: bind
      source: ./uploads/${DOMAIN_NAME}
      target: /var/www/${DOMAIN_NAME}/app/public/uploads

# 定义Angular服务通用配置锚点
x-angular-service: &angular-service
  build:
    context: ../frontend-app/
  stdin_open: true
  tty: true

services:
  db:
    image: postgres
    environment:
      - POSTGRES_PASSWORD=your_postgres_password
    ports:
      - "5432:5432"
    volumes:
      - db-data:/var/lib/postgresql/data

  # domain_1服务实例
  domain_1_web:
    <<: *rails-service
    image: domain_1_web
    build:
      args:
        - DOMAIN_NAME=domain_1.com
        - DB_NAME=domain_1
        - APP_PORT=9191
    environment:
      - BIND_ADDR=tcp://0.0.0.0:9191
    ports:
      - "9191:9191"

  domain_1_node:
    <<: *angular-service
    image: domain_1_node
    build:
      args:
        - DOMAIN_NAME=domain_1.com
        - API_URL=https://domain_1.com
        - PORT=9001
    ports:
      - "9001:9001"

  # domain_2服务实例
  domain_2_web:
    <<: *rails-service
    image: domain_2_web
    build:
      args:
        - DOMAIN_NAME=domain_2.com
        - DB_NAME=domain_2
        - APP_PORT=9292
    environment:
      - BIND_ADDR=tcp://0.0.0.0:9292
    ports:
      - "9292:9292"

  domain_2_node:
    <<: *angular-service
    image: domain_2_node
    build:
      args:
        - DOMAIN_NAME=domain_2.com
        - API_URL=https://domain_2.com
        - PORT=9002
    ports:
      - "9002:9002"

volumes:
  db-data:

Nginx配置优化(通用模板生成)

创建Nginx配置模板domain.conf.template:

upstream ${DOMAIN}_backend_app {
  server 0.0.0.0:${BACKEND_PORT};
}

upstream ${DOMAIN}_frontend_app {
  server 0.0.0.0:${FRONTEND_PORT};
}

server {
  server_name ${DOMAIN}.com;
  
  try_files $uri/index.html $uri @${DOMAIN}_backend_app;

  location ~ ^/v1/ {
      proxy_pass http://${DOMAIN}_backend_app;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_connect_timeout 43200000;
      proxy_read_timeout    43200000;
      proxy_send_timeout    43200000;
  }

  location / {
      proxy_pass http://${DOMAIN}_frontend_app;
      proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
      proxy_set_header Host $http_host;
      proxy_redirect off;
      proxy_connect_timeout 43200000;
      proxy_read_timeout    43200000;
      proxy_send_timeout    43200000;
  }

  location /uploads/ {
    alias /var/www/${DOMAIN}.com/app/public/uploads/;
  }

  error_page 500 502 503 504 /500.html;
  client_max_body_size 4G;
  proxy_max_temp_file_size 0;
  keepalive_timeout 10;

  listen 443 ssl;
  ssl_certificate /etc/letsencrypt/live/${DOMAIN}.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/${DOMAIN}.com/privkey.pem;
  include /etc/letsencrypt/options-ssl-nginx.conf;
  ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem;
}

编写脚本批量生成每个域名的配置:

#!/bin/bash

# 生成domain_1配置
DOMAIN=domain_1 BACKEND_PORT=9191 FRONTEND_PORT=9001 envsubst < domain.conf.template > /etc/nginx/sites-available/domain_1.conf

# 生成domain_2配置
DOMAIN=domain_2 BACKEND_PORT=9292 FRONTEND_PORT=9002 envsubst < domain.conf.template > /etc/nginx/sites-available/domain_2.conf

# 启用配置并重启Nginx
ln -s /etc/nginx/sites-available/domain_1.conf /etc/nginx/sites-enabled/
ln -s /etc/nginx/sites-available/domain_2.conf /etc/nginx/sites-enabled/
systemctl restart nginx

部署说明

  1. 构建镜像时通过--build-arg传入参数,例如构建domain_1的后端镜像:
    docker build --build-arg DOMAIN_NAME=domain_1.com --build-arg DB_NAME=domain_1 --build-arg APP_PORT=9191 -t domain_1_web .
    
  2. 启动所有服务:
    docker-compose up -d
    
  3. 首次启动后,确保每个域名的数据库已创建(示例):
    docker-compose exec domain_1_web bundle exec rake db:create
    

内容的提问来源于stack exchange,提问作者Rakesh Bakoriya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 14:45:54