You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Github Actions工作流中OpenVPN连接远程服务器失败排查

问题

我的Github Actions CI工作流在main分支推送后,能正常构建Docker镜像并上传至Docker Hub,但后续尝试通过SSH连接staging服务器时,需要先建立OpenVPN连接,该连接始终无法成功建立,日志显示SSH连接超时。以下是我的工作流配置及相关日志,请问哪里操作有误?

工作流配置

name: CI

on:
  push:
    branches: [ "main" ]

jobs:
  docker-build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install Poetry
        uses: snok/install-poetry@v1

      - name: create requirements
        run: poetry export --without-hashes --format=requirements.txt > requirements.txt

      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
          
      - name: Build and push image
        uses: docker/build-push-action@v3
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKERHUB_NAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}
        
      - name: Deploy to Staging
        uses: appleboy/ssh-action@v0.1.10
        env: 
          OVPN_CONFIG: ${{ secrets.VPN_CONFIG }}
          VPN_USERNAME: ${{ secrets.VPN_USERNAME }}
          VPN_PASSWORD: ${{ secrets.VPN_PASSWORD }}
        with:
          host: ${{ secrets.STAGING_SERVER_HOST }}
          username: ${{ secrets.STAGING_SERVER_USERNAME }}
          password: ${{ secrets.STAGING_SERVER_PASSWORD }}   
          envs: OVPN_CONFIG,VPN_USERNAME,VPN_PASSWORD
          script: |
            echo "${OVPN_CONFIG}" > vpn-config.ovpn
            echo "${VPN_USERNAME}" > vpn-credentials.txt
            echo "${VPN_PASSWORD}" >> vpn-credentials.txt
          
            openvpn --config vpn-config.ovpn --auth-user-pass vpn-credentials.txt
            sleep 5

            ls -la

            docker pull ${{ secrets.DOCKERHUB_NAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}:latest
            docker run -d ${{ secrets.DOCKERHUB_NAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}:latest
          
            # Cleanup: Delete the temporary VPN credentials file
            rm vpn-credentials.txt

工作流日志片段

***[command]/usr/bin/docker run --name ed866e71fb8133109f4ad988af16a3b2ab46f2_a1dfd1 --label ed866e --workdir /github/workspace --rm -e "VENV" -e "OVPN_CONFIG" -e "VPN_USERNAME" -e "VPN_PASSWORD" -e "INPUT_HOST" -e "INPUT_USERNAME" -e "INPUT_PASSWORD" -e "INPUT_ENVS" -e "INPUT_SCRIPT" -e "INPUT_PORT" -e "INPUT_PASSPHRASE" -e "INPUT_SYNC" -e "INPUT_USE_INSECURE_CIPHER" -e "INPUT_CIPHER" -e "INPUT_TIMEOUT" -e "INPUT_COMMAND_TIMEOUT" -e "INPUT_KEY" -e "INPUT_KEY_PATH" -e "INPUT_FINGERPRINT" -e "INPUT_PROXY_HOST" -e "INPUT_PROXY_PORT" -e "INPUT_PROXY_USERNAME" -e "INPUT_PROXY_PASSWORD" -e "INPUT_PROXY_PASSPHRASE" -e "INPUT_PROXY_TIMEOUT" -e "INPUT_PROXY_KEY" -e "INPUT_PROXY_KEY_PATH" -e "INPUT_PROXY_FINGERPRINT" -e "INPUT_PROXY_CIPHER" -e "INPUT_PROXY_USE_INSECURE_CIPHER" -e "INPUT_SCRIPT_STOP" -e "INPUT_ENVS_FORMAT" -e "INPUT_DEBUG" -e "HOME" -e "GITHUB_JOB" -e "GITHUB_REF" -e "GITHUB_SHA" -e "GITHUB_REPOSITORY" -e "GITHUB_REPOSITORY_OWNER" -e "GITHUB_REPOSITORY_OWNER_ID" -e "GITHUB_RUN_ID" -e "GITHUB_RUN_NUMBER" -e "GITHUB_RETENTION_DAYS" -e "GITHUB_RUN_ATTEMPT" -e "GITHUB_REPOSITORY_ID" -e "GITHUB_ACTOR_ID" -e "GITHUB_ACTOR" -e "GITHUB_TRIGGERING_ACTOR" -e "GITHUB_WORKFLOW" -e "GITHUB_HEAD_REF" -e "GITHUB_BASE_REF" -e "GITHUB_EVENT_NAME" -e "GITHUB_SERVER_URL" -e "GITHUB_API_URL" -e "GITHUB_GRAPHQL_URL" -e "GITHUB_REF_NAME" -e "GITHUB_REF_PROTECTED" -e "GITHUB_REF_TYPE" -e "GITHUB_WORKFLOW_REF" -e "GITHUB_WORKFLOW_SHA" -e "GITHUB_WORKSPACE" -e "GITHUB_ACTION" -e "GITHUB_EVENT_PATH" -e "GITHUB_ACTION_REPOSITORY" -e "GITHUB_ACTION_REF" -e "GITHUB_PATH" -e "GITHUB_ENV" -e "GITHUB_STEP_SUMMARY" -e "GITHUB_STATE" -e "GITHUB_OUTPUT" -e "RUNNER_OS" -e "RUNNER_ARCH" -e "RUNNER_NAME" -e "RUNNER_TOOL_CACHE" -e "RUNNER_TEMP" -e "RUNNER_WORKSPACE" -e "ACTIONS_RUNTIME_URL" -e "ACTIONS_RUNTIME_TOKEN" -e "ACTIONS_CACHE_URL" -e GITHUB_ACTIONS=true -e CI=true -v "/var/run/docker.sock":"/var/run/docker.sock" -v "/home/runner/work/_temp/_github_home":"/github/home" -v "/home/runner/work/_temp/_github_workflow":"/github/workflow" -v "/home/runner/work/_temp/_runner_file_commands":"/github/file_commands" -v "/home/runner/work/***/***":"/github/workspace" ed866e:71fb8133109f4ad988af16a3b2ab46f2
======CMD======
echo "${OVPN_CONFIG}" > vpn-config.ovpn
echo "${VPN_USERNAME}" > vpn-credentials.txt
echo "${VPN_PASSWORD}" >> vpn-credentials.txt

openvpn --config vpn-config.ovpn --*** vpn-credentials.txt
sleep 5

ls -la

*** docker pull ***/***:latest
*** docker run -d --name meteor-bot ***/***:latest

*** Cleanup: Delete the temporary VPN credentials file
rm vpn-credentials.txt
======END======
2023/06/15 17:14:46 dial tcp ***:22: i/o timeout
分析与解决方案

你的配置存在几个核心问题,导致VPN连接失败进而SSH超时:

  1. VPN连接顺序完全错误
    你试图先SSH连接到staging服务器,再在服务器内部建立VPN,但实际场景是:staging服务器可能处于VPN私有网络中,只有先让Github Actions runner连上VPN,才能访问到staging的SSH端口。当前逻辑是在未连VPN的情况下直接SSH,必然超时。

  2. OpenVPN命令未后台运行
    即使在正确时机执行openvpn命令,默认会阻塞当前终端会话,后续的sleep 5和部署命令根本不会执行,脚本会一直卡在VPN启动步骤。

  3. 缺少VPN连接状态校验
    sleep 5是盲等,无法确保VPN真正建立成功,可能VPN还未就绪就执行后续操作,导致失败。

修正后的工作流配置

name: CI

on:
  push:
    branches: [ "main" ]

jobs:
  docker-build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3

      - name: Install Poetry
        uses: snok/install-poetry@v1

      - name: create requirements
        run: poetry export --without-hashes --format=requirements.txt > requirements.txt

      - name: Login to Docker Hub
        uses: docker/login-action@v2
        with:
          username: ${{ secrets.DOCKERHUB_USERNAME }}
          password: ${{ secrets.DOCKERHUB_PASSWORD }}
          
      - name: Build and push image
        uses: docker/build-push-action@v3
        with:
          context: .
          push: true
          tags: ${{ secrets.DOCKERHUB_NAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}

      # 先在runner上建立VPN连接
      - name: Setup OpenVPN
        run: |
          sudo apt-get update && sudo apt-get install -y openvpn
          echo "${{ secrets.VPN_CONFIG }}" > vpn-config.ovpn
          echo "${{ secrets.VPN_USERNAME }}" > vpn-credentials.txt
          echo "${{ secrets.VPN_PASSWORD }}" >> vpn-credentials.txt
          # 后台启动VPN并输出日志
          sudo openvpn --config vpn-config.ovpn --auth-user-pass vpn-credentials.txt --daemon --log vpn.log
          # 循环检查VPN连接状态
          for i in {1..10}; do
            if grep -q "Initialization Sequence Completed" vpn.log; then
              echo "VPN连接成功"
              break
            fi
            echo "等待VPN连接... ($i/10)"
            sleep 2
          done
          # 验证连接是否成功,失败则终止工作流
          if ! grep -q "Initialization Sequence Completed" vpn.log; then
            echo "VPN连接失败,日志内容:"
            cat vpn.log
            exit 1
          fi

      - name: Deploy to Staging
        uses: appleboy/ssh-action@v0.1.10
        with:
          host: ${{ secrets.STAGING_SERVER_HOST }}
          username: ${{ secrets.STAGING_SERVER_USERNAME }}
          password: ${{ secrets.STAGING_SERVER_PASSWORD }}
          script: |
            docker pull ${{ secrets.DOCKERHUB_NAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}:latest
            # 停止并清理旧容器(可选,根据需求调整)
            docker stop meteor-bot || true
            docker rm meteor-bot || true
            docker run -d --name meteor-bot ${{ secrets.DOCKERHUB_NAME }}/${{ secrets.DOCKERHUB_REPOSITORY }}:latest

      # 无论工作流成败,都清理VPN资源
      - name: Cleanup VPN
        if: always()
        run: |
          sudo pkill openvpn
          rm vpn-config.ovpn vpn-credentials.txt vpn.log

关键修正说明

  • 提前在runner上建立VPN:先让Github Actions的ubuntu runner连上VPN,确保后续SSH请求能通过VPN访问私有网络内的staging服务器。
  • 后台运行OpenVPN:添加--daemon参数让VPN在后台执行,避免阻塞脚本流程。
  • 校验VPN连接状态:通过检查日志中的"Initialization Sequence Completed"关键字,确保VPN真正就绪后再执行部署。
  • 强制清理资源:用if: always()确保无论工作流成功或失败,都清理VPN相关文件和进程,避免资源残留。

内容的提问来源于stack exchange,提问作者Amir

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 03:12:09