如何将GitLab CI变量传入Sinatra应用以访问Google Cloud Datastore
嘿,我明白你的困扰——Heroku的config vars确实方便,但GCP这边有自己的玩法,不过我们可以搞定这个问题,把GitLab里的$SERVICE_ACCOUNT安全地传到你的Sinatra应用里作为Datastore的credentials。
首先先澄清个小细节:你贴的那个名为app.yaml的文件其实是GitLab CI的配置文件(标准命名应该是.gitlab-ci.yml),而Google App Engine的部署配置文件也叫app.yaml,别搞混啦,下面我会分别指代这两个文件。
方案一:直接通过环境变量传递(快速实现)
这个方法适合快速验证,把GitLab里的服务账号JSON字符串直接注入到GAE应用的环境变量中:
1. 修改GitLab CI配置(.gitlab-ci.yml)
在deploy_production的script里,给gcloud app deploy添加设置环境变量的参数,把$SERVICE_ACCOUNT传到GAE应用的GOOGLE_APPLICATION_CREDENTIALS_JSON环境变量里:
deploy_production: stage: deploy environment: Production only: - production script: - echo $SERVICE_ACCOUNT > /tmp/$CI_PIPELINE_ID.json - gcloud auth activate-service-account --key-file /tmp/$CI_PIPELINE_ID.json # 新增:传递服务账号JSON到GAE环境变量 - gcloud --quiet --project $PROJECT_ID app deploy app.yaml --set-env-vars GOOGLE_APPLICATION_CREDENTIALS_JSON="$SERVICE_ACCOUNT"
2. 修改Sinatra应用代码(foobar.rb)
在生产环境初始化Datastore客户端时,读取这个环境变量,解析成JSON哈希后传入:
require "google/cloud/datastore" require "json" # 记得引入JSON库 class App < Sinatra::Application if settings.production? # 从GAE环境变量读取服务账号JSON字符串并解析 service_account_hash = JSON.parse(ENV["GOOGLE_APPLICATION_CREDENTIALS_JSON"]) data_store = Google::Cloud::Datastore.new( project: 'foobar', credentials: service_account_hash ) query = data_store.query "kindoffoobar" results = data_store.run query # 将每个结果设置为环境变量 results[0].properties.to_h.each do |key, value| ENV[key]= value end end # 其余应用代码... end
方案二:用GCP Secret Manager管理(更安全,推荐)
直接把JSON字符串放在环境变量里不够安全,GCP的Secret Manager是更符合最佳实践的方式,步骤如下:
1. 在GitLab CI中创建Secret Manager密钥
修改.gitlab-ci.yml的deploy_production脚本,把$SERVICE_ACCOUNT存入Secret Manager:
deploy_production: stage: deploy environment: Production only: - production script: - echo $SERVICE_ACCOUNT > /tmp/$CI_PIPELINE_ID.json - gcloud auth activate-service-account --key-file /tmp/$CI_PIPELINE_ID.json # 创建或更新Secret Manager密钥 - echo "$SERVICE_ACCOUNT" | gcloud secrets create SERVICE_ACCOUNT_CREDENTIALS --data-file=- --project $PROJECT_ID || gcloud secrets versions add SERVICE_ACCOUNT_CREDENTIALS --data-file=- --project $PROJECT_ID # 部署GAE应用并挂载密钥 - gcloud --quiet --project $PROJECT_ID app deploy app.yaml
2. 修改GAE部署配置(app.yaml)
在真正的GAE app.yaml里,配置挂载Secret Manager的密钥到应用的文件系统,并设置环境变量告诉Google客户端密钥路径:
# 假设你用的是GAE标准环境Ruby运行时,根据实际情况调整 runtime: ruby env: standard # 配置环境变量,指向挂载的密钥文件 env_variables: GOOGLE_APPLICATION_CREDENTIALS: /secrets/service_account.json # 挂载Secret Manager的密钥到应用文件系统 secrets: - secret: SERVICE_ACCOUNT_CREDENTIALS path: service_account.json
3. 简化Sinatra应用代码(foobar.rb)
现在Google Cloud客户端会自动读取GOOGLE_APPLICATION_CREDENTIALS环境变量指向的密钥文件,不需要手动传入credentials:
require "google/cloud/datastore" class App < Sinatra::Application if settings.production? # 客户端自动读取密钥文件,无需手动指定credentials data_store = Google::Cloud::Datastore.new( project: 'foobar' ) query = data_store.query "kindoffoobar" results = data_store.run query # 将每个结果设置为环境变量 results[0].properties.to_h.each do |key, value| ENV[key]= value end end # 其余应用代码... end
补充说明
- 为什么不像Heroku那样简单?Heroku的config vars是直接注入应用环境的轻量方案,而GCP更推荐用Secret Manager这类专门的密钥管理服务来处理敏感信息,虽然步骤多一点,但安全性更高。
- 确保GitLab CI里的
$SERVICE_ACCOUNT是正确的JSON格式,没有额外的换行或转义问题。
内容的提问来源于stack exchange,提问作者Robert

