【山竹记账后端】5.密钥管理


大纲链接 §

[toc]


1. 复现打包流程

VSCode打开项目容器

  • 运行脚本 pack_for_host.sh 对源代码进行打包,生成 mangosteen_deploy 目录

VSCode 打开宿主机项目 oh-my-env2

  • 查看是否存在已打包目录 mangosteen_deploy
  • 运行脚本 sh mangosteen_deploy/setup_host.sh 或者直接 mangosteen_deploy/setup_host.sh
    • 开始打包 docker build...
    • 之后会优化 bundle install 依赖缓存,目前暂不优化;先跑通流程
    • 运行镜像 docker run... 查看是否 Done!
  • 使用 curl http://localhost:3000 测试是否能访问到 rails 项目
    • 未启动 curl:(7)Failed to connect to localhost port 3000:Connection refused
    • 使用 docker ps 查看;使用 docker ps -a 查看容器状态,发现容器已经退出
    • 复制容器哈希值前四位 使用 docker logs <hash> 查看日志,发现 rails 没有启动
    • 或者使用 docker log <container_id> 查看日志:docker logs mangosteen-prod-1
  • 是否 Missing secret_key_base for productoin environment,需要配置生产环境密钥

2. 如何管理密钥

Rails 密钥管理

  • master.key
  • credentials.yml.enc
  • production.key
  • production.yml.enc

密钥管理的思路各个框架都是一样的,不因为后端语言的变化而变化


master.key

什么是 master.key (主密钥)

  • Web 应用中的对称加密(一个key既可以加密也可以解密)
    • JWT 加密解密需要一个 key1
    • Session ID 加密解密需要一个 key2
  • 汇总所有 key,加密成一个密文文件,对外无法看出真实内容,使用时使用 master.key 自动解密吐出内容

问题: key 们存在哪?

  • 存在 git 里会被实习生看到、存在自己电脑需要穿来穿去(不安全)

解法:Rails 已有最佳实践

  • master.key + keys => encrypted
  • encrypted + master.key => keys

创建 key 加密操作流程

找到项目目录中相关文件

  • config/master.key 可以删除,之前的不要了
  • config/credentials.yml.enc 内容(可以是空文本)加密后的结果;也可以删除

运行命令 bin/rails credentials:edit,会自动生成新的 master.keycredentials.yml.enc

  • 直接回车运行默认打开 Vim 编辑,退出删除两个重新生成的文件
  • 重新运行 EDITOR="code --wait" bin/rails credentials:edit,会自动生成新的 master.keycredentials.yml.enc,并且使用 VSCode 打开编辑
    • 默认存在 secret_key_base: xxx 用来加密session的主要key
    • 尝试添加字段 demo: frank 保存退出
    • 刚才打开的临时文本已自动删除,内容已经被加密保存在 credentials.yml.enc
  • credentials.yml.enc 可以提交也可以不提交

Rails 最佳实践操作

  • 开发者在 临时文件 里写好 key 们
  • master.key 把 key 们加密,得到加密后的文件(.enc)
  • 删除临时文件,key 们再也不会被看见了
  • .enc 存到 git 里,把 master.keygitignore 中忽略(rails 默认配置)

依然有问题

  1. key 们被删了,如何读写 key 们?
  2. 需要把 master.key 复制给生产环境和实习生,这不安全

key 读写操作流程

如何读取 key 们

  • 打开控制台 bin/rails consol 或者 bin/rails c
  • 输入代码(有提示可以自动补全)
    • Rails.application.credentials.secret_key_base
    • Rails.application.credentials.github[:key]
    • Rails.application.credentials.config 读取所有 key
  • 输入命令的瞬间就会去自动解密读取 key(使用 master.keycredentials.yml.enc

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    bin/rails c                                                                                                                                                                                                                                                                          no
    Loading development environment (Rails 7.2.3.2)
    3.1.2 :001 > Rails.application.credentials.secret_key_base
    => "f4bf7e5314594ffa8e8fb9b2f861c0d40ea6b628e4cad4109264ddefd7aece48015da78513da1ee50924a801622239c0636802d7c9d2cfb565e53a9ceb120ffc" 
    3.1.2 :002 > Rails.application.credentials.demo
    => "frank" 
    3.1.2 :003 > Rails.application.credentials.config
    => 
    {:secret_key_base=>
    "f4bf7e5314594ffa8e8fb9b2f861c0d40ea6b628e4cad4109264ddefd7aece48015da78513da1ee50924a801622239c0636802d7c9d2cfb565e53a9ceb120ffc",
    :demo=>"frank"} 
    3.1.2 :003 >

更改 key 们

  • 将之前的创建命令再次运行 EDITOR="code --wait" bin/rails credentials:edit
  • 在编辑器中编辑修改

细节

  • 关闭编辑器后,key 们所在文件会自动销毁,.enc 文件会自动更新

安全问题:Rails 支持多环境密钥

Rails 支持多环境密钥

  • 命令 EDITOR="code --wait" bin/rails credentials:edit --environment production
  • 得到两个文件
    • config/credentials/production.key(自动被加入 .gitignore)
    • config/credentials/production.yml.enc
  • 打开控制台查看
    • RAILS_ENV=production bin/rails c
    • Rails.application.credentials.config
    • 和开发环境看到的不同,是由环境变量 RAILS_ENV=production 决定的
  • production.key 复制到生产环境机器上,不要给实习生,只有权限在生产环境部署的人员才能使用
    • 普通开发人员只有 master.key 就行了
  • 最后,可以在项目中删掉 production.key,但删之前需要备份到的地方

最终结果

开发环境

  • 使用 master.keycredentials.yml.enc
  • master.key 被 git ignore
  • 如果 .enc 不被 git ignore,那就多人共用 master.key
  • 如果 .enc 要被 git ignore,那就每个人创建自动的 master.key
    • 开发环境,每个人自己运行命令创建, 临时文件的内容拷贝给每个人

生产环境

  • 使用 production.keyproduction.yml.enc
  • production.key 被 git ignore,内容写到环境变量
    • production.key 复制到生产环境的机器的环境变量,例如 RAILS_MASTER_KEY=xxx
    • 只有写的时候才会暴露
  • .enc 不被 git ignore
  • 读取 key 们的代码跟开发环境一模一样

key 环境变量实操

把 key 写到环境变量的步骤

  1. 打开 setup_host.sh
  2. docker run 命令中间添加 -e RAILS_MASTER_KEY=$RAILS_MASTER_KEY
  3. 在宿主机运行命令,运行时提供输入 $RAILS_MASTER_KEY,即 RAILS_MASTER_KEY=xxxxx mangosteen_deploy/setup_host.sh

bin/setup_host.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash

DB_PASSWORD=123456
container_name=mangosteen-prod-1

version=$(cat mangosteen_deploy/version)

echo 'docker build ...'
docker build mangosteen_deploy -t mangosteen:$version
if [ "$(docker ps -aq -f name=^mangosteen-prod-1$)" ]; then
  echo 'docker rm ...'
  docker rm -f $container_name
fi
echo 'docker run ...'
docker run -d -p 3000:3000 --network=network1 -e RAILS_MASTER_KEY=$RAILS_MASTER_KEY -e DB_PASSWORD=$DB_PASSWORD --name=$container_name mangosteen:$version
echo 'docker exec db...'
docker exec -it $container_name bin/rails db:create db:migrate
echo 'DONE!'
  • -e RAILS_MASTER_KEY=$RAILS_MASTER_KEY 告诉 docker 运行时提供 RAILS_MASTER_KEY 环境变量
  • $RAILS_MASTER_KEY 变量占个位,需要在运行时提供输入
  • 只有访问部署权限的人员才能运行
    • production.key 中的哈希值赋值给 RAILS_MASTER_KEY
    • RAILS_MASTER_KEY=240c3dcce0e4e35f99e1c107042ec5a8 mangosteen_deploy/setup_host.sh
    • 脚本运行时,才会去读取环境变量 RAILS_MASTER_KEY

彻底解决 Missing 'secret_key_base'

  • 开发环境项目中打开终端,运行 EDITOR="code --wait" bin/rails credentials:edit --environment production
    • 查看是否已创建 secret_key_base,可以先从已有的 master.key 中复制来,再删除 master.key 和对应的 credentials.yml.enc
    • 之后再重新生成本地的 matser.keycredentials.yml.enc
  • 运行打包命令 bin/pack_for_host.sh
  • 宿主机 oh-my-env2 项目中启动镜像运行命令 mangosteen_deploy/setup_host.sh
  • 运行成功后

    • 运行 curl localhost:3000 -v 返回根路由信息 {"message":"Welcome!"}
    • 运行 curl localhost:3000/api/v1/items -v 查看报错

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      
      curl localhost:3000/api/v1/items -v
      * Uses proxy env variable http_proxy == 'http://127.0.0.1:7890'
      *   Trying 127.0.0.1:7890...
      * Connected to 127.0.0.1 (127.0.0.1) port 7890
      > GET http://localhost:3000/api/v1/items HTTP/1.1
      > Host: localhost:3000
      > User-Agent: curl/8.7.1
      > Accept: */*
      > Proxy-Connection: Keep-Alive
      > 
      * Request completely sent off
      < HTTP/1.1 500 Internal Server Error
      < Connection: keep-alive
      < Content-Type: text/html; charset=UTF-8
      < Keep-Alive: timeout=4
      < Proxy-Connection: keep-alive
      < X-Request-Id: 77660f55-ce53-4668-b9ff-16a249c712f1
      < X-Runtime: 0.106779
      < Content-Length: 0
      < 
      * Connection #0 to host 127.0.0.1 left intact
  • 因为生产环境数据库未配置,所以报 500

  • 日志中也说明 connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory 未配置数据库

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    
    RAILS_MASTER_KEY=240c3dcce0e4e35f99e1c107042ec5a8 mangosteen_deploy/setup_host.sh
    docker build ...
    [+] Building 104.7s (12/12) FINISHED                                                                                                                  docker:desktop-linux
    => [internal] load build definition from Dockerfile                                                                                                                  0.0s
    => => transferring dockerfile: 366B                                                                                                                                  0.0s
    => [internal] load metadata for docker.io/library/ruby:3.1.2                                                                                                         2.0s
    => [internal] load .dockerignore                                                                                                                                     0.0s
    => => transferring context: 2B                                                                                                                                       0.0s
    => [1/7] FROM docker.io/library/ruby:3.1.2@sha256:7681a3d37560dbe8ff7d0a38f3ce35971595426f0fe2f5709352d7f7a5679255                                                   0.0s
    => => resolve docker.io/library/ruby:3.1.2@sha256:7681a3d37560dbe8ff7d0a38f3ce35971595426f0fe2f5709352d7f7a5679255                                                   0.0s
    => [internal] load build context                                                                                                                                     0.0s
    => => transferring context: 46.71kB                                                                                                                                  0.0s
    => CACHED [2/7] RUN mkdir /mangosteen                                                                                                                                0.0s
    => CACHED [3/7] RUN bundle config mirror.https://rubygems.org https://mirrors.tuna.tsinghua.edu.cn/rubygems                                                          0.0s
    => CACHED [4/7] WORKDIR /mangosteen                                                                                                                                  0.0s
    => [5/7] ADD mangosteen-*.tar.gz ./                                                                                                                                  0.1s
    => [6/7] RUN bundle config set --local without 'development test'                                                                                                    0.5s
    => [7/7] RUN bundle install                                                                                                                                         96.2s
    => exporting to image                                                                                                                                                5.6s 
    => => exporting layers                                                                                                                                               4.4s 
    => => exporting manifest sha256:414aab51e3b12f1487e2d09d3aa41d3429788a6a517c2afcb79e37432a044c3c                                                                     0.0s 
    => => exporting config sha256:29f9caef134d69060903b4d464cf6754b0b79209f1e43fa913678dc06cec3b6e                                                                       0.0s 
    => => exporting attestation manifest sha256:e8bdb92a1d085d8621a9c9b3ccce9b15324c851464ee12e832edf241418c2557                                                         0.0s 
    => => exporting manifest list sha256:85f20bc60e2ae53e7980aa2f3b40eb7c8422a0523ff517c925317d221ba0b86d                                                                0.0s 
    => => naming to docker.io/library/mangosteen:20260916-193848                                                                                                         0.0s
    => => unpacking to docker.io/library/mangosteen:20260916-193848                                                                                                      1.0s
    
    View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/uau561do84432l9fq0zs057jd
    docker run ...
    9fa6a6ce2338d942ec05d3f61748051e7b7360da175b16eac0365b321aba5d2c
    docker exec db...
    connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?
    connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?
    connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?
    Couldn't create 'mangosteen_1_production' database. Please check your configuration.
    bin/rails aborted!
    ActiveRecord::ConnectionNotEstablished: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?
    connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?
    connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?
    
    
    Caused by:
    PG::ConnectionBad: connection to server on socket "/var/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?
    connection to server on socket "/run/postgresql/.s.PGSQL.5432" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?
    connection to server on socket "/tmp/.s.PGSQL.5432" failed: No such file or directory
        Is the server running locally and accepting connections on that socket?
    
    Tasks: TOP => db:create
    (See full trace by running task with --trace)
    
    What's next:
    Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug mangosteen-prod-1
    Learn more at https://docs.docker.com/go/debug-cli/
    DONE!

坑1:镜像 FROM ruby:3.1.2-slim 最后运行时 报 bundle install 错误

  • 更换一个可用版本 FROM ruby:3.1.2

坑2:在镜像中启动服务报错 HTTP parse error, malformed request: #<Puma::HttpParserError: Invalid HTTP format, parsing fails. Are you trying to open an SSL connection to a non-SSL Puma?


唯一的安全漏洞

  • 黑客 同时 拿到环境变量和 .enc 文件
  • 比如登录密码被泄露

使用初始代码

  • 使用初始代码 删除代码中 .enc 文件,生成自己本地的 key
    • rm config/credentials.yml.enc
    • EDITOR="code --wait" bin/rails credentials:edit 记下 secret_key_base
    • EDITOR="code --wait" bin/rails credentials:edit --environment production 是否存在 secret_key_base
    • 不存在就用刚才记下的 secret_key_base 替换,重新执行 EDITOR="code --wait" bin/rails credentials:edit
    • 本地宿主机的四个文件都有了

下一个问题:访问数据库报错


3. 配置生产环境数据库

修改项目中生产环境的配置,重新打包运行镜像

修改项目中生产环境的配置 config/database.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
default: &default
  adapter: postgresql
  encoding: unicode
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>

development:
  <<: *default
  database: mangosteen_dev
  username: mangosteen
  password: 123456
  host: db-for-mangosteen

test:
  <<: *default
  database: mangosteen_test
  username: mangosteen
  password: 123456
  host: db-for-mangosteen

production:
  <<: *default
  database: mangosteen_production
  username: mangosteen
  password: <%= ENV["DB_PASSWORD"] %>
  host: <%= ENV["DB_HOST"] %>
  • 所有重要配置是不能传到 git 上的
  • production 环境的密码只能从环境变量中读取
  • 除了启动部署生产环境的其他人员都接触不到

修改脚本 bin/setup_host.sh

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#!/bin/bash

DB_PASSWORD=123456
container_name=mangosteen-prod-1

version=$(cat mangosteen_deploy/version)

echo 'docker build ...'
docker build mangosteen_deploy -t mangosteen:$version
if [ "$(docker ps -aq -f name=^mangosteen-prod-1$)" ]; then
  echo 'docker rm ...'
  docker rm -f $container_name
fi
echo 'docker run ...'
docker run -d -p 3000:3000 --network=network1 -e DB_HOST=$DB_HOST -e RAILS_MASTER_KEY=$RAILS_MASTER_KEY -e DB_PASSWORD=$DB_PASSWORD --name=$container_name mangosteen:$version
echo 'docker exec db...'
docker exec -it $container_name bin/rails db:create db:migrate
echo 'DONE!'
  • 重新打包 bin/pack_for_host.sh

来到宿主机项目 oh-my-env2

  • 运行 DB_HOST=db-for-mangosteen DB_PASSWORD=123456 RAILS_MASTER_KEY=240c3dcce0e4e35f99e1c107042ec5a8 mangosteen_deploy/setup_host.sh

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    
    DB_HOST=db-for-mangosteen DB_PASSWORD=123456 RAILS_MASTER_KEY=240c3dcce0e4e35f99e1c107042ec5a8 mangosteen_deploy/setup_host.sh
    docker build ...
    [+] Building 111.7s (13/13) FINISHED                                                                                                                  docker:desktop-linux
    => [internal] load build definition from Dockerfile                                                                                                                  0.0s
    => => transferring dockerfile: 366B                                                                                                                                  0.0s
    => [internal] load metadata for docker.io/library/ruby:3.1.2                                                                                                         2.9s
    => [auth] library/ruby:pull token for registry-1.docker.io                                                                                                           0.0s
    => [internal] load .dockerignore                                                                                                                                     0.0s
    => => transferring context: 2B                                                                                                                                       0.0s
    => [1/7] FROM docker.io/library/ruby:3.1.2@sha256:7681a3d37560dbe8ff7d0a38f3ce35971595426f0fe2f5709352d7f7a5679255                                                   0.0s
    => => resolve docker.io/library/ruby:3.1.2@sha256:7681a3d37560dbe8ff7d0a38f3ce35971595426f0fe2f5709352d7f7a5679255                                                   0.0s
    => [internal] load build context                                                                                                                                     0.0s
    => => transferring context: 46.70kB                                                                                                                                  0.0s
    => CACHED [2/7] RUN mkdir /mangosteen                                                                                                                                0.0s
    => CACHED [3/7] RUN bundle config mirror.https://rubygems.org https://mirrors.tuna.tsinghua.edu.cn/rubygems                                                          0.0s
    => CACHED [4/7] WORKDIR /mangosteen                                                                                                                                  0.0s
    => [5/7] ADD mangosteen-*.tar.gz ./                                                                                                                                  0.1s
    => [6/7] RUN bundle config set --local without 'development test'                                                                                                    0.5s
    => [7/7] RUN bundle install                                                                                                                                        101.8s
    => exporting to image                                                                                                                                                6.1s 
    => => exporting layers                                                                                                                                               4.6s 
    => => exporting manifest sha256:958b570a9eafd631573c9487b9166ee50a0ce527b4fb0922199bab850cc4018f                                                                     0.0s 
    => => exporting config sha256:07c92854c67d74633f5dc3933199ca995a12fbe4f21803a6be8f535358fdf4ad                                                                       0.0s 
    => => exporting attestation manifest sha256:b2d2124f91b207d7a7e9abdbcd1cf45afd316f3acd5f589bd2fb3ed63379499f                                                         0.0s 
    => => exporting manifest list sha256:df5e01e2c4d4d1c6361065eeb3b1a1d14a51b8d77e0baba4c1dee442a6d669df                                                                0.0s 
    => => naming to docker.io/library/mangosteen:20260916-205513                                                                                                         0.0s
    => => unpacking to docker.io/library/mangosteen:20260916-205513                                                                                                      1.2s
    
    View build details: docker-desktop://dashboard/build/desktop-linux/desktop-linux/ih3778q1qgvijjdpz81twdiz7
    docker rm ...
    mangosteen-prod-1
    docker run ...
    51ccd0e846522540c54f5a930bf78875da68ed2fe7610608ca0ab9b65daabf62
    docker exec db...
    Created database 'mangosteen_production'
    I, [2026-09-16T13:29:16.207528 #15]  INFO -- : Migrating to CreateUsers (20260906082930)
    == 20260906082930 CreateUsers: migrating ======================================
    -- create_table(:users)
    -> 0.0101s
    == 20260906082930 CreateUsers: migrated (0.0102s) =============================
    
    I, [2026-09-16T13:29:16.223529 #15]  INFO -- : Migrating to CreateValidationCodes (20260909153656)
    == 20260909153656 CreateValidationCodes: migrating ============================
    -- create_table(:validation_codes)
    -> 0.0096s
    == 20260909153656 CreateValidationCodes: migrated (0.0097s) ===================
    
    I, [2026-09-16T13:29:16.237735 #15]  INFO -- : Migrating to CreateItems (20260910063034)
    == 20260910063034 CreateItems: migrating ======================================
    -- create_table(:items)
    -> 0.0092s
    == 20260910063034 CreateItems: migrated (0.0093s) =============================
    
    
    What's next:
    Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug mangosteen-prod-1
    Learn more at https://docs.docker.com/go/debug-cli/
    DONE!
  • 访问 curl localhost:3000

    • 返回 {"message":"Welcome!"}
  • 访问 curl localhost:3000/api/v1/items

    • 返回 {"resources":[],"pager":{"page":null,"per_page":100,"count":0}}

创建生产环境数据库

在宿主机目录执行,创建生产环境数据库

  • 运行命令 docker exec -it 51cc bin/rails db:create db:migrate
    • 使用前四位哈希或者名称 docker exec -it mangosteen-prod-1 bin/rails db:create db:migrate
  • 分别解释以上命令含义
    • docker exec 容器执行命令
    • -it 可交互,--tty:分配伪终端 Pseudo-TTY
    • bin/rails db:create db:migrate 上产环境中创建和迁移数据库

已存在数据库

1
2
3
4
5
6
docker exec -it mangosteen-prod-1 bin/rails db:create db:migrate
Database 'mangosteen_production' already exists

What's next:
    Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug mangosteen-prod-1
    Learn more at https://docs.docker.com/go/debug-cli/

仅演示操作!生产环境删除数据库

  • docker exec -it mangosteen-prod-1 bin/rails db:drop

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    docker exec -it mangosteen-prod-1 bin/rails db:drop
    bin/rails aborted!
    ActiveRecord::ProtectedEnvironmentError: You are attempting to run a destructive action against your 'production' database.
    If you are sure you want to continue, run the same command with the environment variable:
    DISABLE_DATABASE_ENVIRONMENT_CHECK=1
    
    Tasks: TOP => db:drop => db:check_protected_environments
    (See full trace by running task with --trace)
    
    What's next:
    Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug mangosteen-prod-1
    Learn more at https://docs.docker.com/go/debug-cli/
  • rails 撤回了命令,禁止随意删除生产环境的数据库

  • 需要在生产环境中使用 DISABLE_DATABASE_ENVIRONMENT_CHECK=1 环境变量来删除数据库

不可直接在命令中执行 docker exec -it mangosteen-prod-1 DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bin/rails db:drop

1
2
3
4
5
6
docker exec -it mangosteen-prod-1 DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bin/rails db:drop
OCI runtime exec failed: exec failed: unable to start container process: exec: "DISABLE_DATABASE_ENVIRONMENT_CHECK=1": executable file not found in $PATH

What's next:
    Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug mangosteen-prod-1
    Learn more at https://docs.docker.com/go/debug-cli/
  • 不是一个可执行文件 DISABLE_DATABASE_ENVIRONMENT_CHECK=1 被当可做执行文件的路径

使用 bash 操作容器

  • 运行 docker exec -it mangosteen-prod-1 bash
  • 进入容器后,再执行 DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bin/rails db:drop

    1
    2
    3
    4
    5
    
    docker exec -it mangosteen-prod-1 bash
    root@b052cc987e74:/mangosteen# DISABLE_DATABASE_ENVIRONMENT_CHECK=1 bin/rails db:drop
    Dropped database 'mangosteen_production'
    root@b052cc987e74:/mangosteen# exit
    exit

重新创建数据库

  • 运行 docker exec -it mangosteen-prod-1 bin/rails db:create db:migrate

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    
    docker exec -it mangosteen-prod-1 bin/rails db:create db:migrate
    
    Created database 'mangosteen_production'
    I, [2026-09-16T14:14:36.738577 #43]  INFO -- : Migrating to CreateUsers (20260906082930)
    == 20260906082930 CreateUsers: migrating ======================================
    -- create_table(:users)
    -> 0.0102s
    == 20260906082930 CreateUsers: migrated (0.0103s) =============================
    
    I, [2026-09-16T14:14:36.756766 #43]  INFO -- : Migrating to CreateValidationCodes (20260909153656)
    == 20260909153656 CreateValidationCodes: migrating ============================
    -- create_table(:validation_codes)
    -> 0.0110s
    == 20260909153656 CreateValidationCodes: migrated (0.0111s) ===================
    
    I, [2026-09-16T14:14:36.774598 #43]  INFO -- : Migrating to CreateItems (20260910063034)
    == 20260910063034 CreateItems: migrating ======================================
    -- create_table(:items)
    -> 0.0110s
    == 20260910063034 CreateItems: migrated (0.0112s) =============================
    
    
    What's next:
    Try Docker Debug for seamless, persistent debugging tools in any container or image → docker debug mangosteen-prod-1
    Learn more at https://docs.docker.com/go/debug-cli/
  • 数据库已经连上,再运行 curl localhost:3000/api/v1/items -v

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    
    curl localhost:3000/api/v1/items -v
    * Uses proxy env variable http_proxy == 'http://127.0.0.1:7890'
    *   Trying 127.0.0.1:7890...
    * Connected to 127.0.0.1 (127.0.0.1) port 7890
    > GET http://localhost:3000/api/v1/items HTTP/1.1
    > Host: localhost:3000
    > User-Agent: curl/8.7.1
    > Accept: */*
    > Proxy-Connection: Keep-Alive
    > 
    * Request completely sent off
    < HTTP/1.1 200 OK
    < Content-Length: 63
    < Cache-Control: max-age=0, private, must-revalidate
    < Connection: keep-alive
    < Content-Type: application/json; charset=utf-8
    < Etag: W/"bf702ba9291e498576803fbd69ad7615"
    < Keep-Alive: timeout=4
    < Proxy-Connection: keep-alive
    < Referrer-Policy: strict-origin-when-cross-origin
    < Vary: Accept
    < X-Content-Type-Options: nosniff
    < X-Frame-Options: SAMEORIGIN
    < X-Permitted-Cross-Domain-Policies: none
    < X-Request-Id: 104ad132-b22a-41c0-b5a8-eae615a7badb
    < X-Runtime: 0.021387
    < X-Xss-Protection: 0
    < 
    {"resources":[],"pager":{"page":null,"per_page":100,"count":0}}* Connection #0 to host 127.0.0.1 left intact

解决数据库报错的过程

  • 文件内容 config/database.yml
  • 更新脚本,做占位 bin/setup_host.sh-e DB_HOST=$DB_HOST -e RAILS_MASTER_KEY=$RAILS_MASTER_KEY -e DB_PASSWORD=$DB_PASSWORD
  • 运行参数传过去 DB_HOST=xxx DB_PASSWORD=xxx RAILS_MASTER_KEY=xxx mangosteen_deploy/setup_host.sh

    1
    2
    3
    4
    5
    6
    7
    8
    
    default: &default
    ...
    production:
    <<: *default
    database: mangosteen_production
    username: mangosteen
    password: <%= ENV["DB_PASSWORD"] %>
    host: <%= ENV["DB_HOST"] %>
    1
    2
    3
    
    ...
    docker run -d -p 3000:3000 --network=network1 -e DB_HOST=$DB_HOST -e RAILS_MASTER_KEY=$RAILS_MASTER_KEY -e DB_PASSWORD=$DB_PASSWORD --name=$container_name mangosteen:$version
    ...

目前为止已经做到

  • 宿主机(windows)可以运行镜像
  • 数据库已经连上
  • 本机可访问 http://localhost:3000 (需要使用匿名模式,不被拦截)

重点:如何看 log 步骤

  1. curl http://localhost:3000/api/v1/items
  2. docker exec -ite xxx bash
  3. cat log/production.log

细节:log 持久化

  • 加数据卷 docker run -v mangosteen-logs:/mangosteen/log
  • 使用宿主机文件系统
    • docker run -v 绝对路径:/mangosteen/log
    • 注意 Windows 用户要多写一个 /

4. 总结

用于创建开发环境密钥的命令(重要命令,会反复用到)

1
2
rm config/credentials.yml.enc
EDITOR="code --wait" rails credentials:edit

密钥汇总

1
2
3
secret_key_base: xxx
email_password: xxx
hmac_secret: xxx

命令汇总

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
# 容器目录打包源代码
bin/pack_for_host.sh
# 宿主机目录运行镜像脚本
mangosteen_deploy/setup_host.sh
# 测试是否能访问
curl http://localhost:3000 -v

# 开发环境 key
EDITOR="code --wait" bin/rails credentials:edit
# 生产环境 key
EDITOR="code --wait" bin/rails credentials:edit --environment production
# 打开控制台 
bin/rails c
RAILS_ENV=production bin/rails c
Rails.application.credentials.config

# 宿主机目录运行镜像脚本 带参数
DB_HOST=xxx DB_PASSWORD=xxx RAILS_MASTER_KEY=xxx mangosteen_deploy/setup_host.sh
# 创建生产环境数据库
docker exec-it mangosteen-prod-1 bin/rails db:create db:migrate

·未完待续·

参考文章

相关文章


  • 作者: Joel
  • 文章链接:
  • 版权声明
  • 非自由转载-非商用-非衍生-保持署名