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
部署说明
- 构建镜像时通过
--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 . - 启动所有服务:
docker-compose up -d - 首次启动后,确保每个域名的数据库已创建(示例):
docker-compose exec domain_1_web bundle exec rake db:create
内容的提问来源于stack exchange,提问作者Rakesh Bakoriya
相关产品推荐
相关产品推荐

