CI/CD中scp上传XML失败主因是SSH密钥未预置、目标目录不存在、权限不足或主机指纹未确认;应改用rsync+--mkpath或sftp+expect,并校验MD5与XML格式。
直接在CI/CD脚本里写 scp config.xml user@host:/path/ 很容易卡住或报错,核心问题不是命令本身,而是缺少前置条件:SSH密钥未预置、目标目录不存在、权限不足、或SSH主机指纹未确认。
scp 不会自动创建远程父目录,/opt/app/conf/ 不存在时直接失败关键是把私钥存为 Secrets,用 webfactory/ssh-agent 注入,再用 rsync 替代 scp ——它支持 --mkpath 自动建目录,也更容错。
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up SSH agent
uses: webfactory/ssh-agent@v0.8.0
with:
ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Upload config.xml
run: |
rsy
nc -avz --mkpath config.xml user@${{ secrets.HOST }}:/opt/app/conf/config.xml
注意:secrets.HOST 和 secrets.SSH_PRIVATE_KEY 需提前在仓库 Settings → Secrets and variables → Actions 中添加;SSH_PRIVATE_KEY 必须是 PEM 格式且无密码保护(CI 不支持交互输密)。
GitLab Runner 默认不装 ssh-agent,且 rsync 可能未预装。用 sftp 加 expect 脚本是最轻量兼容方案,重点是跳过 host key 检查和自动确认。
upload.exp 脚本,内含 spawn sftp -o StrictHostKeyChecking=no user@host
expect 匹配 sftp> 提示符后执行 put config.xml /opt/app/conf/
apt-get install -y expect,再运行该脚本SSH_PRIVATE_KEY 变量写入临时文件,并 chmod 600,否则 sftp 拒绝使用仅靠上传成功不等于配置生效——网络中断可能只传了半份,或目标磁盘满导致写入截断。加一步简单校验能避免后续服务启动失败。
ssh user@host 'ls -l /opt/app/conf/config.xml && md5sum /opt/app/conf/config.xml'
md5sum config.xml,两者比对(CI 中可用 diff 或简单字符串匹配)ssh user@host 'xmllint --noout /opt/app/conf/config.xml 2>/dev/null' 验证格式合法别忽略远程路径的 SELinux 上下文或 umask 导致的权限问题——如果应用以非 root 用户运行,确保 config.xml 至少有 644 权限,且父目录可遍历。