如何在PostGIS容器初始化阶段建立SSH隧道?
解决PostGIS容器自动通过SSH隧道恢复远程数据库备份的问题
问题背景
需要在PostGIS容器构建/初始化阶段,自动建立SSH隧道,将远程数据库备份后恢复到容器内数据库。核心矛盾:
- Dockerfile中执行命令拥有root权限,但此时PostGIS服务未启动,无法完成数据库操作
/docker-entrypoint-initdb.d目录下的脚本以postgres用户(UID=999)运行,无root权限,无法执行SSH相关操作- 手动通过
docker exec以root身份执行可成功,但需实现docker-compose up全自动完成流程
此前两种尝试均失败:
- Dockerfile中执行:出现SSH密钥格式错误、权限被拒的问题
- 脚本放入
/docker-entrypoint-initdb.d:无root权限,无法安装openssh-client和建立隧道
可行解决方案
方案1:自定义Entrypoint脚本,先以root完成SSH操作再启动服务
PostGIS官方镜像的Entrypoint为docker-entrypoint.sh,我们可以自定义脚本,先以root身份完成SSH隧道搭建、备份恢复,再调用官方Entrypoint启动Postgres服务。
步骤1:编写自定义Entrypoint脚本custom-entrypoint.sh
#!/bin/bash set -e # 仅首次初始化数据库时执行,避免重复操作 if [ ! -f /var/lib/postgresql/data/initialized.flag ]; then # 安装OpenSSH客户端 apt-get update && apt-get install -y --no-install-recommends openssh-client rm -rf /var/lib/apt/lists/* # 配置SSH环境 mkdir -p /root/.ssh # 从环境变量写入私钥(禁止硬编码) echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_rsa-dev chmod 600 /root/.ssh/id_rsa-dev ssh-keyscan -H some_server.com >> /root/.ssh/known_hosts # 后台建立SSH隧道 ssh -N -L 9001:some_other_server.com:5432 some_user@some_server.com -i /root/.ssh/id_rsa-dev & SSH_PID=$! # 等待隧道连通(可替换为更可靠的端口检测) sleep 5 # 备份远程数据库 export PGPASSWORD=${DEV_DB_PASSWORD} pg_dump -Fc -v -U dbuser -d dev_db -p 9001 -h 0.0.0.0 > /tmp/dev_db_dump.sql # 启动临时Postgres服务并恢复数据 su postgres -c "pg_ctl start -D /var/lib/postgresql/data -w" export PGPASSWORD=${POSTGRES_PASSWORD} pg_restore -Fc -v -U dbuser -d dev_db_docker_local /tmp/dev_db_dump.sql su postgres -c "pg_ctl stop -D /var/lib/postgresql/data -w" # 关闭SSH隧道 kill $SSH_PID # 标记初始化完成 touch /var/lib/postgresql/data/initialized.flag fi # 调用官方Entrypoint启动Postgres服务 exec docker-entrypoint.sh "$@"
步骤2:修改Dockerfile
FROM postgis/postgis:latest # 复制自定义Entrypoint脚本 COPY custom-entrypoint.sh /usr/local/bin/ RUN chmod +x /usr/local/bin/custom-entrypoint.sh # 声明环境变量(建议通过docker-compose或.env传入) ENV SSH_PRIVATE_KEY="" ENV DEV_DB_PASSWORD="" ENV POSTGRES_PASSWORD="" # 设置自定义Entrypoint ENTRYPOINT ["custom-entrypoint.sh"] CMD ["postgres"]
步骤3:Docker Compose配置示例
version: '3.8' services: postgis: build: . environment: - SSH_PRIVATE_KEY=${SSH_PRIVATE_KEY} - DEV_DB_PASSWORD=${DEV_DB_PASSWORD} - POSTGRES_PASSWORD=${POSTGRES_PASSWORD} - POSTGRES_USER=dbuser - POSTGRES_DB=dev_db_docker_local volumes: - postgis_data:/var/lib/postgresql/data ports: - "5432:5432" volumes: postgis_data:
方案2:预安装OpenSSH并调整权限(备选方案)
若不想修改Entrypoint,可在Dockerfile中预先以root身份安装openssh-client,将SSH密钥放到postgres用户可访问路径,实现无root权限执行SSH操作。
步骤1:修改Dockerfile
FROM postgis/postgis:latest # 以root身份安装OpenSSH客户端并配置权限 RUN apt-get update && apt-get install -y --no-install-recommends openssh-client \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /home/postgres/.ssh \ && chown postgres:postgres /home/postgres/.ssh \ && chmod 700 /home/postgres/.ssh # 复制恢复脚本到初始化目录 COPY restore-db.sh /docker-entrypoint-initdb.d/ RUN chmod +x /docker-entrypoint-initdb.d/restore-db.sh
步骤2:编写恢复脚本restore-db.sh
#!/bin/bash set -e # 从环境变量写入SSH私钥 echo "${SSH_PRIVATE_KEY}" > /home/postgres/.ssh/id_rsa-dev chmod 600 /home/postgres/.ssh/id_rsa-dev ssh-keyscan -H some_server.com >> /home/postgres/.ssh/known_hosts # 建立SSH隧道 ssh -N -L 9001:some_other_server.com:5432 some_user@some_server.com -i /home/postgres/.ssh/id_rsa-dev & SSH_PID=$! sleep 5 # 备份并恢复数据库 export PGPASSWORD=${DEV_DB_PASSWORD} pg_dump -Fc -v -U dbuser -d dev_db -p 9001 -h 0.0.0.0 > /tmp/dev_db_dump.sql export PGPASSWORD=${POSTGRES_PASSWORD} pg_restore -Fc -v -U dbuser -d dev_db_docker_local /tmp/dev_db_dump.sql # 关闭SSH隧道 kill $SSH_PID
步骤3:Docker Compose配置
参考方案1的compose配置,确保传入所需环境变量即可。
关键注意事项
- 密钥安全:禁止将SSH私钥硬编码到镜像或脚本中,通过环境变量、Docker Secrets或
.env文件传递 - 初始化标记:必须添加初始化完成的标记文件,避免容器重启时重复执行备份恢复
- 连通性检测:建议将
sleep 5替换为更可靠的端口检测(如nc -zv 0.0.0.0 9001循环检测) - 镜像优化:安装openssh-client后清理apt缓存,减少镜像体积
内容的提问来源于stack exchange,提问作者user2517182
相关产品推荐
相关产品推荐

