如何在Rails的database.yml中获取AWS CodePipeline环境变量?
我已经在AWS CodePipeline的构建阶段添加了环境变量,当执行RUN bundle exec rake db:migrate RAILS_ENV=production命令时,程序会读取database.yml文件查找生产环境数据库配置。我希望让database.yml能够访问AWS CodePipeline构建阶段添加的环境变量,该如何实现?也欢迎提供替代方案。
方法1:构建Docker镜像时直接传递环境变量
在CodeBuild的build阶段执行docker build时,通过--env参数将CodePipeline的环境变量注入镜像构建过程,这样Dockerfile中RUN bundle exec rake db:migrate命令就能读取到这些变量,database.yml里的ERB语法也能正确解析。
修改buildspec.yml的build阶段命令:
build: commands: - echo Building docker image - docker build --env DATABASE_HOST=$DATABASE_HOST --env DATABASE_NAME=$DATABASE_NAME --env DATABASE_USER=$DATABASE_USER --env DATABASE_PASSWORD=$DATABASE_PASSWORD --env DATABASE_PORT=$DATABASE_PORT -t jsm-rails . - docker tag jsm-rails <你的ECR仓库URI>:latest
方法2:用Docker ARG+ENV传递变量
在Dockerfile中定义ARG参数,构建时传入变量后转换为ENV,确保后续命令可访问。
修改Dockerfile:
# 定义ARG参数 ARG DATABASE_HOST ARG DATABASE_NAME ARG DATABASE_USER ARG DATABASE_PASSWORD ARG DATABASE_PORT # 转换为ENV变量 ENV DATABASE_HOST=$DATABASE_HOST ENV DATABASE_NAME=$DATABASE_NAME ENV DATABASE_USER=$DATABASE_USER ENV DATABASE_PASSWORD=$DATABASE_PASSWORD ENV DATABASE_PORT=$DATABASE_PORT # 保留原迁移命令 RUN bundle exec rake db:migrate RAILS_ENV=production
修改buildspec.yml的docker build命令:
build: commands: - echo Building docker image - docker build --build-arg DATABASE_HOST=$DATABASE_HOST --build-arg DATABASE_NAME=$DATABASE_NAME --build-arg DATABASE_USER=$DATABASE_USER --build-arg DATABASE_PASSWORD=$DATABASE_PASSWORD --build-arg DATABASE_PORT=$DATABASE_PORT -t jsm-rails . - docker tag jsm-rails <你的ECR仓库URI>:latest
替代方案1:将数据库迁移移至容器启动阶段
把RUN bundle exec rake db:migrate从Dockerfile中移除,放到启动脚本中,迁移将在容器启动时执行,此时可直接使用运行时环境变量(如ECS/EKS任务定义注入或AWS Secrets Manager获取)。
修改Dockerfile:
# 移除原迁移命令 # RUN bundle exec rake db:migrate RAILS_ENV=production # 添加启动脚本配置 COPY entrypoint.sh /usr/bin/ RUN chmod +x /usr/bin/entrypoint.sh ENTRYPOINT ["entrypoint.sh"] # 保留原启动命令 CMD ["bundle", "exec", "rails", "server", "-e", "production"]
创建entrypoint.sh脚本:
#!/bin/sh set -e # 执行数据库迁移 bundle exec rake db:migrate RAILS_ENV=production # 启动Rails服务器 exec "$@"
该方案优势:构建镜像时无需数据库凭据(更安全),迁移操作与运行环境绑定,避免构建时依赖外部数据库可用性。
替代方案2:用AWS Secrets Manager管理凭据
将数据库用户名、密码等敏感信息存储在AWS Secrets Manager中,在构建或运行时通过AWS CLI获取,避免明文传递环境变量。
修改buildspec.yml的pre_build阶段:
pre_build: commands: - SECRETS=$(aws secretsmanager get-secret-value --secret-id <你的数据库凭据Secret ARN> --query SecretString --output text) - export DATABASE_USER=$(echo $SECRETS | jq -r '.DATABASE_USER') - export DATABASE_PASSWORD=$(echo $SECRETS | jq -r '.DATABASE_PASSWORD') - export DATABASE_NAME=$(echo $SECRETS | jq -r '.DATABASE_NAME') - export DATABASE_HOST=$(echo $SECRETS | jq -r '.DATABASE_HOST') - export DATABASE_PORT=$(echo $SECRETS | jq -r '.DATABASE_PORT') # 保留原有其他命令
需确保CodeBuild服务角色拥有访问Secrets Manager的权限。
原始配置文件
database.yml
default: &default adapter: postgresql encoding: unicode # For details on connection pooling, see Rails configuration guide # https://guides.rubyonrails.org/configuring.html#database-pooling pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> development: <<: *default database: Backend_development test: <<: *default database: JSMA_Backend_test production: <<: *default username: <%= ENV['DATABASE_USER'] %> database: <%= ENV['DATABASE_NAME'] %> password: <%= ENV['DATABASE_PASSWORD'] %> host: <%= ENV['DATABASE_HOST'] %> port: <%= ENV['DATABASE_PORT'] %>
Dockerfile
# Use an official Ruby runtime as a parent image FROM ruby:3.1-alpine # Set the working directory in the container to /app WORKDIR /app # Install build dependencies RUN apk update && apk add --virtual build-dependencies build-base # Install imagemagick RUN apk add imagemagick # Install the PostgreSQL client library RUN apk add postgresql-dev # Copy the Gemfile and Gemfile.lock from your host machine to the container COPY Gemfile Gemfile.lock ./ # Install RubyGems and Bundler RUN gem update --system && gem install bundler # Install the RubyGems specified in the Gemfile RUN bundle install --without development test # Copy the rest of your application code from your host machine to the container COPY . . RUN bundle exec rake db:migrate RAILS_ENV=production # Start the Rails server CMD ["bundle", "exec", "rails", "server", "-e", "production"] # CMD ["bundle", "exec", "rails", "server"]
docker-compose.yml
version: '3' services: db: image: postgres:13 environment: POSTGRES_USER: <%= ENV['DATABASE_USER'] %> POSTGRES_PASSWORD: <%= ENV['DATABASE_PASSWORD'] %> POSTGRES_DB: <%= ENV['DATABASE_NAME'] %> app: build: . ports: - "3000:3000" depends_on: - db
buildspec.yml
version: 0.2 phases: pre_build: commands: - export DATABASE_HOST=$DATABASE_HOST - export DATABASE_NAME=$DATABASE_NAME - export DATABASE_USER=$DATABASE_USER - export DATABASE_PASSWORD=$DATABASE_PASSWORD - export DATABASE_PORT=$DATABASE_PORT - echo $DATABASE_HOST - echo AWS ECR login - $(aws ecr get-login --no-include-email --region ap-south-1) - REPO_URI=*** repo here - COMMIT_HASH=$(echo $CODEBUILD_RESOLVED_SOURCE_VERSION | cut -c 1-7) - IMAGE_TAG=${COMMIT_HASH:=latest} build: commands: - echo Building docker image - docker build -t jsm-rails . - docker tag //repo****:latest post_build: commands: - echo build successful! - echo pushing image to ecr... - docker push //repo url here *** rails:latest - echo pushed image!
内容的提问来源于stack exchange,提问作者Krenil Sapariya

