返回

GitHub Actions部署无法访问目录?排查与解决方案

Linux

GitHub Actions 部署时无法访问 /var/www/todo-app 目录的排查与解决

在用 GitHub Actions 把应用部署到 VPS 上时, 顺利执行到尝试访问远程服务器上的 /var/www/todo-app 目录那一步, 卡住了。GitHub Actions 报错,显示无法访问 /var/www/todo-app,错误信息如下:

*** System restart required ** *
cd: cannot access '/var/www/todo-app': No such file or directory

我用的是 root 用户进行 SSH 连接,并且在远程服务器和 GitHub Action 中都使用了 ls -l 命令检查。远程服务器的输出结果是:

drwxr-xr-x 2 root root 4096 Jan 14 09:38 html
drwxr-xr-x 5 root root 4096 Jan 15 16:27 todo-app

而 GitHub Action 中的输出(部分):

drwxr-xr-x 2 *** ** * 4096 Jan  5 21:36 html

我可以成功访问 /var/www/html,但 /var/www/todo-app 目录却好像访问不了, 明明服务器上有一个名为 todo-app 的 Git 仓库。我猜想问题可能出在目录路径或者权限上,尝试过在远程服务器上设置todo-app的权限, 没用。这是我的 ci.yml 文件的内容:

name: Deploy to VPS

on:
  workflow_run:
    workflows: [ "Docker Image CI" ]
    types:
      - completed

jobs:
  deploy:
    runs-on: ubuntu-latest
    if: ${{ github.event.workflow_run.conclusion == 'success' }}

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Get SSH key and set permissions
        run: |
          mkdir -p ~/.ssh
          echo "${{ secrets.REMOTE_PRIVATE_KEY }}" > ~/.ssh/id_rsa && chmod 600 ~/.ssh/id_rsa
          ssh-keyscan -H ${{ secrets.REMOTE_HOST }} >> ~/.ssh/known_hosts

      - name: SSH to the server and Redeploy
        run: |
          ssh -i ~/.ssh/id_rsa ${{ secrets.REMOTE_USERNAME }}@${{ secrets.REMOTE_HOST }}
          cd /var/www/todo-app
          git pull origin main
          echo "DATABASE_URL=${{ secrets.PG_URL }}" > .env
          echo "POSTGRES_USER=${{ secrets.PG_USER }}" >> .env
          echo "POSTGRES_PASSWORD=${{ secrets.PG_PASSWORD }}" >> .env
          echo "POSTGRES_DB=${{ secrets.PG_DB }}" >> .env
          docker compose build
          docker compose up -d
          exit

接下来看看具体怎么解决。

一、 问题原因分析

  1. run 指令的误区: 在 GitHub Actions 中,每个 run 指令都是在一个新的 shell 中执行的。这意味着,上一个 run 中的 cd 命令对下一个 run 是无效的。 在提供的 YAML 文件里, ssh, cd, git pull 等操作都在同一个 run 块下面才对.

  2. 用户上下文: 虽然已经以 root 用户进行 SSH 连接,但在run的指令中,默认的用户上下文可能会发生变化。 虽然可能性比较小, 但需要排除一下。

  3. 实际路径偏差: 即使服务器上显示 /var/www/todo-app 存在,也需要排除一下路径的细微偏差。有时候由于各种奇怪的原因(比如误操作,之前的残留配置),导致目录的实际路径和你认为的有细微差异。

  4. 服务器重启: 服务器提示 System restart required, 虽然跟这个问题可能没直接关系,但最好还是重启下服务器. 保证服务器处于一个"干净"的状态。

二、 解决方案

1. 合并 SSH 指令

最直接的修改,把所有需要在远程服务器上执行的命令,合并到一个 run 块内,通过 ssh 的方式一起执行。

原理: 利用 SSH 的远程命令执行功能,将一系列命令作为单个字符串传递给远程 shell 执行,从而保证命令执行的上下文一致。

代码示例:

      - name: SSH to the server and Redeploy
        run: |
          ssh -i ~/.ssh/id_rsa ${{ secrets.REMOTE_USERNAME }}@${{ secrets.REMOTE_HOST }} "
            cd /var/www/todo-app &&
            git pull origin main &&
            echo \"DATABASE_URL=${{ secrets.PG_URL }}\" > .env &&
            echo \"POSTGRES_USER=${{ secrets.PG_USER }}\" >> .env &&
            echo \"POSTGRES_PASSWORD=${{ secrets.PG_PASSWORD }}\" >> .env &&
            echo \"POSTGRES_DB=${{ secrets.PG_DB }}\" >> .env &&
            docker compose build &&
            docker compose up -d
          "

说明:

  • 将所有远程命令用双引号括起来,作为一个整体传递给 ssh
  • 使用 && 连接多个命令,确保它们按顺序执行,并且前一个命令成功才会执行下一个。
  • 注意 .env 文件中的环境变量需要用双引号包裹, 防止特殊字符引起问题。

2. 明确指定用户上下文

ssh 命令中明确指定以哪个用户的身份执行命令, 以免默认用户出现问题。

原理: 确保所有命令都在正确的用户上下文中执行。

代码示例 (在方法1的基础上修改):

      - name: SSH to the server and Redeploy
        run: |
          ssh -i ~/.ssh/id_rsa ${{ secrets.REMOTE_USERNAME }}@${{ secrets.REMOTE_HOST }} "
            whoami &&  # 打印当前用户
            cd /var/www/todo-app &&
            # ... 其他命令 ...
          "

说明 : 使用了whoami确定一下当前用户. 如果whoami打印的不是root, 可以把ssh指令改为:

ssh -i ~/.ssh/id_rsa root@${{ secrets.REMOTE_HOST }}

3. 验证远程目录的绝对路径

在远程服务器上, 执行pwd 命令,确认一下 /var/www/todo-app 这个路径是不是正确的。

操作步骤:

  1. 直接通过 SSH 手动连接到 VPS:

    ssh -i ~/.ssh/id_rsa ${{ secrets.REMOTE_USERNAME }}@${{ secrets.REMOTE_HOST }}
    
  2. 进入可能存在问题的目录:

    cd /var/www
    ls -l
    cd todo-app
    pwd
    

记录pwd命令的输出结果. 确认这是正确的绝对路径. 如果输出的不是/var/www/todo-app, 在 GitHub Action 的 yaml 文件中使用pwd给出的实际路径.

4. 重启服务器(可选)

有时候系统状态有问题。虽然重启不是必须的, 但重启能排除干扰项.

操作步骤:
直接在你的ssh终端中执行 sudo reboot

解释: 重启VPS 可以保证服务在一个稳定干净的状态下运行, 避免未知的干扰。

进阶使用技巧: 使用 SSH Action (可选)

如果上面几个方案还是不行,可以试试GitHub 官方市场的一些第三方 SSH Actions,例如 appleboy/ssh-action。这些 Actions 提供了更方便的 SSH 操作方式, 更容易调试。

原理: 这些 Actions 封装了 SSH 的常用操作, 并提供了错误处理和日志记录功能。

代码示例:

      - name: SSH Remote Commands
        uses: appleboy/ssh-action@master
        with:
          host: ${{ secrets.REMOTE_HOST }}
          username: ${{ secrets.REMOTE_USERNAME }}
          key: ${{ secrets.REMOTE_PRIVATE_KEY }}
          script: |
            cd /var/www/todo-app
            git pull origin main
            echo "DATABASE_URL=${{ secrets.PG_URL }}" > .env
            echo "POSTGRES_USER=${{ secrets.PG_USER }}" >> .env
            echo "POSTGRES_PASSWORD=${{ secrets.PG_PASSWORD }}" >> .env
            echo "POSTGRES_DB=${{ secrets.PG_DB }}" >> .env
            docker compose build
            docker compose up -d

安全提示 : 使用第三方 Actions 需要注意来源是否可信,尽量用官方推荐或者使用量大的.
通过以上这些分析和方法, 基本上能解决 GitHub Actions 部署时无法访问指定目录的问题了。 最常见的情况是由于指令分散在不同的 run 块导致的, 把它们合并到一起通常就能搞定. 此外,确认一下远程服务器的实际目录路径和用户权限,也能避免一些低级错误。