CI/CD 自动化部署实践:让代码提交后自动上线

项目背景与最终目标

这次 CI/CD 实验没有直接从复杂项目开始,而是先做了一个很小的静态网页,用它把 Git、GitHub、GitHub Actions、SSH、Docker、Docker Compose 和 Linux VPS 串起来。

这样做的好处是,每一层出了问题都比较容易定位。网页本身没有复杂业务逻辑,部署失败时基本可以判断是 Git、Actions、SSH、Docker、Compose 或服务器环境的问题,而不是应用代码本身。

本次项目的本地目录是:

text 复制代码
E:\ci-cd-demo

最终项目结构:

text 复制代码
ci-cd-demo/
├── .github/
│   └── workflows/
│       ├── ci.yml
│       └── ssh-test.yml
├── frontend/
│   ├── index.html
│   ├── style.css
│   └── script.js
├── Dockerfile
└── docker-compose.yml

其中:

  • frontend/ 保存网页代码。
  • Dockerfile 负责说明 Docker 镜像如何构建。
  • docker-compose.yml 负责说明容器如何运行。
  • .github/workflows/ci.yml 负责 CI。
  • .github/workflows/ssh-test.yml 后来改成了 CD 工作流,负责自动连接 VPS 并部署。

整个流程最终形成:

text 复制代码
本地代码
   │
   │ git push
   ▼
GitHub
   │
   │ push 触发
   ▼
GitHub Actions
   │
   ├── CI:检查文件
   ├── CI:构建 Docker 镜像
   │
   └── CD:SSH 登录 VPS
             │
             ▼
       git pull origin main
             │
             ▼
       docker compose up -d --build
             │
             ▼
       Docker 容器
             │
             ▼
          VPS:80
             │
             ▼
           网页

这里需要区分 CI 和 CD。

CI(Continuous Integration,持续集成)主要解决的是"代码提交以后有没有问题"。本次实验中,CI 会检查项目文件是否存在,并尝试构建 Docker 镜像。

CD(Continuous Deployment,持续部署)解决的是"代码确认没有明显问题以后,怎么自动更新到服务器"。本次实验中,CD 通过 GitHub Actions 使用 SSH 连接 VPS,然后在服务器上拉取最新代码并重新构建、启动 Docker Compose。

因此,单独执行 git push 只是把代码提交到了 GitHub;真正形成 CI/CD,是因为 GitHub Actions 接收到 push 事件以后继续自动执行检查和部署。


本地项目与 Docker 部署

先准备一个最小可运行项目

最开始没有直接做后端服务,而是使用 Nginx 提供静态网页。

frontend/index.html 最初是一个简单页面:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CI/CD Demo</title>
    <link rel="stylesheet" href="style.css">
</head>
<body>
    <div class="container">
        <h1>CI/CD Demo</h1>
        <p>这是我的第一个自动部署项目。</p>
        <button onclick="sayHello()">测试按钮</button>
        <p id="message"></p>
    </div>

    <script src="script.js"></script>
</body>
</html>

style.cssscript.js 分别负责页面样式和按钮交互。

最初的网页比较简单,后面为了让部署后的页面更像一个正式的 Demo,又把前端改成了一个深色科技风的 CI/CD 展示页面,增加了自动部署状态、Pipeline、Docker 等内容。

这里有一个值得注意的地方:网页怎么改并不影响 CI/CD 的基本流程。只要代码进入 Git 仓库,后面的自动化过程都可以保持不变。

编写 Dockerfile

项目根目录下创建 Dockerfile

dockerfile 复制代码
FROM nginx:alpine

WORKDIR /usr/share/nginx/html

COPY frontend/ .

EXPOSE 80

CMD ["nginx", "-g", "daemon off;"]

这个 Dockerfile 虽然很短,但已经包含了一个完整 Docker 镜像的基本构建过程。

text 复制代码
FROM nginx:alpine

表示以 nginx:alpine 作为基础镜像。Alpine 版本比较小,适合这种简单实验。

text 复制代码
WORKDIR /usr/share/nginx/html

设置工作目录。Nginx 默认会从这个目录提供静态文件。

text 复制代码
COPY frontend/ .

把项目中的 frontend/ 目录复制到 Nginx 的网页目录。

text 复制代码
EXPOSE 80

说明容器中的应用使用 80 端口。它更多是一个镜像层面的声明,并不会自动让宿主机开放 80 端口。

text 复制代码
CMD ["nginx", "-g", "daemon off;"]

让 Nginx 在前台运行。Docker 容器需要有一个持续运行的前台进程,否则容器会退出。

本地第一次构建和运行

先进入项目目录:

powershell 复制代码
cd E:\ci-cd-demo

构建镜像:

powershell 复制代码
docker build -t ci-cd-demo:v1 .

这里:

  • docker build 表示构建镜像。
  • -t ci-cd-demo:v1 给镜像设置名称和标签。
  • . 表示使用当前目录作为 Docker build context。

构建成功以后,可以查看镜像:

powershell 复制代码
docker images

然后使用 docker run 启动:

powershell 复制代码
docker run -d --name ci-cd-demo -p 8080:80 ci-cd-demo:v1

这里的:

text 复制代码
8080:80

含义是:

text 复制代码
Windows 主机 8080
       ↓
Docker 容器 80

访问:

text 复制代码
http://localhost:8080

可以看到网页。

这一步主要是为了先证明 Dockerfile 本身能够正常构建和运行。

从 docker run 过渡到 Docker Compose

手动 docker run 可以运行容器,但参数比较容易越来越多。后面改成使用 Compose 管理。

docker-compose.yml

yaml 复制代码
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: ci-cd-demo
    ports:
      - "8080:80"
    restart: unless-stopped

这里没有使用 version: 字段,因为新版 Docker Compose 已经不需要这个字段。

主要配置:

yaml 复制代码
build:
  context: .
  dockerfile: Dockerfile

表示 Compose 不直接拉一个现成应用镜像,而是根据当前目录中的 Dockerfile 构建。

yaml 复制代码
container_name: ci-cd-demo

指定容器名称,方便后面排查。

yaml 复制代码
ports:
  - "8080:80"

把宿主机 8080 映射到容器 80。

yaml 复制代码
restart: unless-stopped

让 Docker 在容器异常退出或 Docker 服务重启后自动尝试恢复容器,除非人为停止。

使用 Compose 启动:

powershell 复制代码
docker compose up -d --build

其中:

  • up:创建并启动服务。
  • -d:后台运行。
  • --build:启动前重新构建镜像。

查看运行状态:

powershell 复制代码
docker compose ps

到这里,本地的 Docker + Compose 部分已经验证完成。


Git 与 GitHub 仓库

Docker 能正常运行以后,再把项目纳入 Git 管理。

进入项目:

powershell 复制代码
cd E:\ci-cd-demo

初始化 Git:

powershell 复制代码
git init

查看状态:

powershell 复制代码
git status

第一次提交:

powershell 复制代码
git add .
git commit -m "Initial CI/CD demo"

然后把主分支统一命名为 main

powershell 复制代码
git branch -M main

接下来在 GitHub 创建仓库,然后添加远程仓库。

本次仓库使用 SSH 地址:

text 复制代码
git@github.com:myvps/ci-cd-demo.git

添加:

powershell 复制代码
git remote add origin git@github.com:myvps/ci-cd-demo.git

第一次推送:

powershell 复制代码
git push -u origin main

成功以后,本地代码就进入 GitHub。

这一阶段的关键认识是:Git 和 GitHub 是两个不同的概念。

Git 负责本地版本管理、提交、分支等操作;GitHub 是远程 Git 仓库,同时还提供 GitHub Actions 等自动化能力。

后面 CI/CD 能自动运行,是因为代码被 push 到 GitHub 以后,GitHub Actions 可以监听这个事件。


CI:使用 GitHub Actions 自动检查项目

项目进入 GitHub 后,开始配置 CI。

在:

text 复制代码
.github/workflows/ci.yml

中写入:

yaml 复制代码
name: CI

on:
  push:
    branches:
      - main

  pull_request:
    branches:
      - main

jobs:
  test:
    runs-on: ubuntu-latest

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

      - name: Check project files
        run: |
          test -f Dockerfile
          test -f docker-compose.yml
          test -f frontend/index.html
          test -f frontend/style.css
          test -f frontend/script.js

      - name: Build Docker image
        run: docker build -t ci-cd-demo:test .

这份配置可以拆开理解。

yaml 复制代码
on:
  push:
    branches:
      - main

表示 main 分支发生 push 时触发。

yaml 复制代码
pull_request:
  branches:
    - main

表示针对 main 分支的 Pull Request 也可以触发 CI。

yaml 复制代码
runs-on: ubuntu-latest

表示这个 Job 运行在 GitHub 提供的 Ubuntu Runner 上。

第一步:

yaml 复制代码
uses: actions/checkout@v4

把当前 GitHub 仓库的代码下载到 Runner。

第二步:

bash 复制代码
test -f Dockerfile
test -f docker-compose.yml
...

检查关键文件是否存在。

这不是复杂的自动化测试,但对于当前 Demo 很合适,因为首先需要确认项目结构没有被破坏。

第三步:

bash 复制代码
docker build -t ci-cd-demo:test .

尝试构建 Docker 镜像。

这一点很重要,因为 CI 不只是检查 Git 文件是否存在,还提前验证 Dockerfile 能不能构建。

整个 CI 的意义可以理解为:

text 复制代码
开发者 push
     ↓
GitHub Actions
     ↓
拉取代码
     ↓
检查项目文件
     ↓
构建 Docker 镜像
     ↓
成功 / 失败

如果 CI 失败,就应该先解决问题,而不是直接认为代码可以部署。


SSH:让 GitHub Actions 和 VPS 能互相完成工作

真正做 CD 之前,需要解决两个不同方向的 SSH 问题。

这次特意使用了两套 SSH Key,因为它们解决的是完全不同的事情。

GitHub Actions → VPS

GitHub Actions 需要主动登录 VPS,所以在 VPS 上生成了一套专门给 GitHub Actions 使用的密钥:

text 复制代码
/root/.ssh/github_actions
/root/.ssh/github_actions.pub

公钥加入:

text 复制代码
/root/.ssh/authorized_keys

私钥则保存到 GitHub 仓库的 Actions Secrets 中。

配置了:

text 复制代码
VPS_HOST
VPS_USER
VPS_SSH_KEY

其中:

  • VPS_HOST:VPS 公网 IP。
  • VPS_USER:登录用户,本次使用 root
  • VPS_SSH_KEY:GitHub Actions 登录 VPS 使用的私钥。

之后使用 appleboy/ssh-action@v1 测试。

测试成功时日志中出现了:

text 复制代码
Drone SSH version 1.8.2
Docker Compose version v5.5.0
✅ Successfully executed commands to all hosts.

这一步非常重要,因为它证明:

text 复制代码
GitHub Actions
      ↓ SSH
VPS

这条链路已经打通。

VPS → GitHub

但是 CD 还有另外一个方向。

GitHub Actions 登录 VPS 后,VPS 需要执行:

bash 复制代码
git pull origin main

因此 VPS 自己也需要能够访问 GitHub。

一开始直接:

bash 复制代码
git clone git@github.com:myvps/ci-cd-demo.git

出现:

text 复制代码
Permission denied (publickey)

这不是 GitHub Actions 的 Key 有问题,而是因为 VPS → GitHub 这一方向没有认证。

于是又在 VPS 上生成了一套独立的密钥:

text 复制代码
/root/.ssh/github
/root/.ssh/github.pub

/root/.ssh/github.pub 添加到 GitHub 账户的 SSH and GPG keys 中。

然后测试:

bash 复制代码
ssh -i /root/.ssh/github -o IdentitiesOnly=yes -T git@github.com

成功返回:

text 复制代码
Hi myvps! You've successfully authenticated, but GitHub does not provide shell access.

这句话的含义不是失败。

GitHub 本身不提供普通 SSH Shell,但已经确认 GitHub 成功识别了这把 SSH Key。

为了以后不需要每次手动指定:

bash 复制代码
-i /root/.ssh/github

又配置了:

text 复制代码
/root/.ssh/config

内容:

text 复制代码
Host github.com
    HostName github.com
    User git
    IdentityFile /root/.ssh/github
    IdentitiesOnly yes

然后:

bash 复制代码
chmod 600 /root/.ssh/config

这样 VPS 以后执行:

bash 复制代码
git pull origin main

就可以自动使用这把 Key。

在 VPS 上准备项目目录

最终把项目放在:

text 复制代码
/opt/ci-cd-demo

也就是说,服务器上的工作目录大致是:

text 复制代码
/opt/ci-cd-demo
├── .github/
├── frontend/
├── Dockerfile
└── docker-compose.yml

这里的逻辑是:

text 复制代码
GitHub
   ↑
   │ git pull
   │
VPS /opt/ci-cd-demo

GitHub Actions 本身并不需要把整个项目文件通过 SSH 传过去。

它只需要:

  1. SSH 登录 VPS。
  2. 进入项目目录。
  3. git pull 获取最新代码。
  4. 使用最新代码重新构建并启动容器。

CD:把部署真正自动化

前面的 CI 和 SSH 都验证以后,开始配置 CD。

原本用于 SSH 测试的:

text 复制代码
.github/workflows/ssh-test.yml

后来直接改成真正的 CD 工作流:

yaml 复制代码
name: CD

on:
  push:
    branches:
      - main

jobs:
  deploy:
    runs-on: ubuntu-latest

    steps:
      - name: Deploy to VPS
        uses: appleboy/ssh-action@v1
        with:
          host: ${{ secrets.VPS_HOST }}
          username: ${{ secrets.VPS_USER }}
          key: ${{ secrets.VPS_SSH_KEY }}
          script: |
            cd /opt/ci-cd-demo

            echo "Pull latest code..."
            git pull origin main

            echo "Build and restart..."
            docker compose up -d --build

            echo "Deployment completed!"

            echo "Container status:"
            docker compose ps

这里的执行过程非常清楚。

第一步:push 触发

yaml 复制代码
on:
  push:
    branches:
      - main

只要代码 push 到 main,CD 就会开始。

第二步:GitHub Actions 登录 VPS

yaml 复制代码
uses: appleboy/ssh-action@v1

使用前面配置好的:

text 复制代码
VPS_HOST
VPS_USER
VPS_SSH_KEY

建立 SSH 连接。

第三步:进入服务器项目目录

bash 复制代码
cd /opt/ci-cd-demo

第四步:拉取最新代码

bash 复制代码
git pull origin main

这一条命令把 GitHub 上刚刚 push 的代码同步到服务器。

实际测试时出现过一次:

text 复制代码
Updating 4f490d7..fa136ad
Fast-forward

说明 VPS 成功从 GitHub 拉到了新的提交。

第五步:重新构建并启动

bash 复制代码
docker compose up -d --build

因为网页代码发生变化,所以需要重新构建镜像。

完整过程:

text 复制代码
GitHub 新代码
    ↓
git pull
    ↓
Dockerfile
    ↓
重新 build
    ↓
生成新镜像
    ↓
重新创建 / 启动容器

第六步:查看容器

bash 复制代码
docker compose ps

用于确认服务是否正常运行。

第一次真正部署时遇到的问题:8080 端口冲突

第一次 CD 部署时,GitHub Actions、SSH、Git Pull、Docker Build 都成功了,但是容器启动失败:

text 复制代码
Bind for 0.0.0.0:8080 failed: port is already allocated

这说明问题已经不是 GitHub、SSH 或 Dockerfile,而是 VPS 上已经有其他程序占用了 8080。

当时没有直接执行停止命令,而是先要求检查:

bash 复制代码
docker ps --format "table {{.Names}}\t{{.Ports}}"

以及:

bash 复制代码
ss -lntp | grep ':8080'

原因是 VPS 上还有其他正在运行的服务,不能为了部署 Demo 就随便停止某个容器。

最终决定把 Demo 的宿主机端口改成 80。

Compose 改成:

yaml 复制代码
services:
  web:
    build:
      context: .
      dockerfile: Dockerfile
    container_name: ci-cd-demo
    ports:
      - "80:80"
    restart: unless-stopped

这里仍然是:

text 复制代码
宿主机 80 → 容器 80

修改后再次:

powershell 复制代码
git add docker-compose.yml
git commit -m "Change web port to 80"
git push

GitHub Actions 再次触发 CD,最终部署成功。

这个问题很值得记录下来,因为实际工作中部署失败并不一定是代码问题。端口被占用、磁盘不足、权限错误、Docker 网络问题等,都可能导致部署失败。

一个容易忽略的问题:部署失败时 Workflow 可能仍显示成功

第一次部署时还发现了一个脚本层面的隐患。

虽然:

bash 复制代码
docker compose up -d --build

已经报错,但是后面的:

bash 复制代码
echo "Deployment completed!"
docker compose ps

仍然继续执行。

因此 GitHub Actions 最终可能把整个 SSH Step 当成成功。

这说明当前 Workflow 还不够严谨。

后续应该在脚本开头增加:

bash 复制代码
set -e

例如:

yaml 复制代码
script: |
  set -e

  cd /opt/ci-cd-demo

  echo "Pull latest code..."
  git pull origin main

  echo "Build and restart..."
  docker compose up -d --build

  echo "Deployment completed!"

  echo "Container status:"
  docker compose ps

这样只要其中一个命令返回非 0 状态,脚本就会停止,GitHub Actions 才能正确显示部署失败。

这也是这次实验中比较重要的一点:自动化不是把几个命令串起来就结束了,还需要保证失败能够被正确传递。

最终验证自动部署

CD 成功以后,最重要的不是只看 GitHub Actions 绿色,而是验证完整闭环。

下一次修改网页内容,例如修改:

html 复制代码
<h1>CI/CD Demo</h1>

为:

html 复制代码
<h1>My CI/CD Project</h1>

然后本地:

powershell 复制代码
git add .
git commit -m "Update homepage"
git push

这时候不再登录 VPS 手动执行:

bash 复制代码
git pull

也不再手动执行:

bash 复制代码
docker compose up -d --build

而是直接等待:

text 复制代码
git push
   ↓
GitHub
   ↓
GitHub Actions
   ↓
CI
   ↓
CD
   ↓
SSH
   ↓
git pull
   ↓
Docker Compose
   ↓
网页更新

然后访问服务器的 80 端口,确认页面已经发生变化。

到这里,整个实验才真正完成了"自动部署"的验证。


最终流程、问题记录与后续改进

这次实验最终形成的基础 CI/CD 架构可以概括为:

text 复制代码
┌──────────────────┐
│  Windows 本地开发 │
│                  │
│ 修改 HTML/CSS/JS │
└────────┬─────────┘
         │
         │ git push
         ▼
┌──────────────────┐
│      GitHub      │
│    main 分支     │
└────────┬─────────┘
         │
         │ push trigger
         ▼
┌────────────────────────────┐
│      GitHub Actions        │
│                            │
│  CI:                     │
│  1. checkout               │
│  2. 检查项目文件           │
│  3. docker build           │
│                            │
│  CD:                     │
│  4. SSH 登录 VPS           │
└────────┬───────────────────┘
         │
         │ SSH
         ▼
┌────────────────────────────┐
│          Ubuntu VPS        │
│                            │
│  /opt/ci-cd-demo           │
│          │                 │
│      git pull              │
│          ↓                 │
│  docker compose            │
│          ↓                 │
│    Docker Container        │
│          ↓                 │
│        nginx:80            │
└────────────────────────────┘

整个实验过程中实际遇到的问题主要集中在以下几个方面。

Docker 与服务器环境问题

Dockerfile 本身可以构建,但服务器上的 Docker 环境和本地并不完全一样。

尤其是服务器拉取:

text 复制代码
nginx:alpine

速度比较慢,所以 Docker Build 在 VPS 上花费的时间明显比本地长。

这说明"本地能跑"并不代表"服务器一定能快速部署"。实际项目还需要考虑镜像缓存、镜像仓库、网络环境等问题。

SSH 是两个方向的问题

这次最容易混淆的地方之一就是两套 SSH Key。

实际关系是:

text 复制代码
github_actions
GitHub Actions → VPS

以及:

text 复制代码
github
VPS → GitHub

两者不能混为一谈。

前者解决:

text 复制代码
Actions 怎么登录服务器?

后者解决:

text 复制代码
服务器怎么从 GitHub 拉代码?

这两个方向都打通以后,CD 才能顺利执行。

GitHub Actions Secret 的作用

VPS 地址、登录用户和私钥没有直接写到公开的 Workflow 中,而是使用:

text 复制代码
${{ secrets.VPS_HOST }}
${{ secrets.VPS_USER }}
${{ secrets.VPS_SSH_KEY }}

这样可以避免把 SSH 私钥直接写进 Git 仓库。

特别是 SSH 私钥、Token、密码等内容,不能提交到 Git。

如果密钥意外暴露,应当及时更换,而不是继续使用已经泄露的凭证。

端口冲突的排查思路

第一次部署失败时:

text 复制代码
Bind for 0.0.0.0:8080 failed:
port is already allocated

正确的思路不是直接执行:

bash 复制代码
kill

或者随便:

bash 复制代码
docker stop

而是先确认是谁占用了端口:

bash 复制代码
docker ps --format "table {{.Names}}\t{{.Ports}}"
bash 复制代码
ss -lntp | grep ':8080'

确认之后,再决定是释放端口,还是修改项目端口。

对于一台已经运行多个服务的 VPS,这一点尤其重要。

当前方案为什么适合学习,但还不算最完善

现在的 CD 是:

text 复制代码
GitHub
   ↓
SSH
   ↓
VPS git pull
   ↓
VPS docker build
   ↓
VPS docker compose up

它的优点是简单,能够把 CI/CD 的核心流程完整跑通。

但它也有一个比较明显的问题:每次部署都要在 VPS 上重新构建镜像,而且 VPS 还需要访问 Docker Hub 拉取基础镜像。

更进一步的做法可以变成:

text 复制代码
GitHub
   ↓
GitHub Actions
   ↓
构建 Docker 镜像
   ↓
推送到 GHCR 等镜像仓库
   ↓
VPS docker pull
   ↓
docker compose up

这样 VPS 主要负责运行容器,不负责完整的镜像构建。

不过对于第一次学习 CI/CD 来说,目前这种:

text 复制代码
git pull
+
docker compose up -d --build

反而更容易理解每一步到底发生了什么,因此适合作为第一版。

这次实验真正需要掌握的并不是某一条命令,而是整个部署思路:

text 复制代码
代码管理 → 自动检查 → 自动连接服务器 → 拉取代码
→ 构建镜像 → 启动容器 → 验证服务

以后换成 FastAPI、Node.js、Java、前后端分离项目,甚至更复杂的服务,只是 Dockerfile、Compose 和测试命令发生变化,整个 CI/CD 的基本思想仍然一样。

补充:密钥生成和部分步骤

sh 复制代码
root@myvps:~# mkdir -p /root/.ssh
root@myvps:~# chmod 700 /root/.ssh
root@myvps:~# ssh-keygen -t ed25519 -C "github-actions-ci-cd"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/root/.ssh/id_ed25519): /root/.ssh/github_actions
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/github_actions
Your public key has been saved in /root/.ssh/github_actions.pub
The key fingerprint is:
SHA256:6gxYoxCAHTJVhq0P4RqpYJHu46PtOC4L0Yx869AAxek github-actions-ci-cd
The key's randomart image is:
+--[ED25519 256]--+
|=o+=o ...        |
|o=+.o .o         |
|oo.o o.          |
|=C+   .D         |
|C+o+o. .Q        |
|o=.+=...         |
|o + .o.          |
|+=   +           |
|M=+   o          |
+----[SHA256]-----+
root@myvps:~# cat /root/.ssh/github_actions
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
QyNlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAAd7RigAAAJjlyI/75ciP
+wAAlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAXQtIgqkZnpsMfdd7Rig
AAlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAAj+waEplEsMARiZIA3JO
t5dlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAApLWNkAQ==
-----END OPENSSH PRIVATE KEY-----
root@myvps:~# cat /root/.ssh/github_actions.pub
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIGL1rj+AAEbm9uZQAAAAAAAAAd7RigAAAJjlyI/75ciP github-actions-ci-cd
root@myvps:~# cat /root/.ssh/github_actions.pub >> /root/.ssh/authorized_keys
root@myvps:~# chmod 600 /root/.ssh/authorized_keys
root@myvps:~# ls -l /root/.ssh/github_actions*
-rw------- 1 root root 411 Sep 11 12:41 /root/.ssh/github_actions
-rw-r--r-- 1 root root 102 Sep 11 12:41 /root/.ssh/github_actions.pub
root@myvps:~# ls
docker-pull.log  virt-sysprep-firstboot.log
root@myvps:~# cd /opt/
root@myvps:/opt# ls
3x-ui  containerd  new-api  sub2api  vllm
root@myvps:/opt# git clone git@github.com:myvps/ci-cd-demo.git
Cloning into 'ci-cd-demo'...
The authenticity of host 'github.com (20.205.243.166)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpCasF/zLDA0zPMSvHdkr4UvAdqU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? y
Please type 'yes', 'no' or the fingerprint: yes
Warning: Permanently added 'github.com' (ED25519) to the list of known hosts.
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
root@myvps:/opt# git clone git@github.com:myvps/ci-cd-demo.git
Cloning into 'ci-cd-demo'...
git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
root@myvps:/opt# ssh-keygen -t ed25519 -C "vps-github"
Generating public/private ed25519 key pair.
Enter file in which to save the key (/root/.ssh/id_ed25519): /root/.ssh/github
Enter passphrase (empty for no passphrase):
Enter same passphrase again:
Your identification has been saved in /root/.ssh/github
Your public key has been saved in /root/.ssh/github.pub
The key fingerprint is:
SHA256:bYldbnQGvLfQzYDISdHPrPHJCG/4Aa1rF+beg/8Nc10 vps-github
The key's randomart image is:
+--[ED25519 256]--+
|         ooB.o   |
|          * *.+  |
|           *o@o= |
|         +o=@oD o|
|        A =*o= . |
|         .o.+ .. |
|         . o.oo .|
|            oooo.|
|             .Eoo|
+----[SHA256]-----+
root@myvps:/opt# cat /root/.ssh/github.pub
ssh-ed25519 AAAAC3NzaC1lQDI1NTE5AAAAIFi+7RMlJxc5gSjSCx8ncgQYNMMwEZY5E8078FK8f0Y1 vps-github
root@myvps:/opt# ssh -T git@github.com
git@github.com: Permission denied (publickey).
root@myvps:/opt# ssh -T git@github.com
git@github.com: Permission denied (publickey).
root@myvps:/opt# ls -l /root/.ssh/github*
-rw------- 1 root root 399 Sep 11 12:54 /root/.ssh/github
-rw-r--r-- 1 root root  92 Sep 11 12:54 /root/.ssh/github.pub
-rw------- 1 root root 411 Sep 11 12:41 /root/.ssh/github_actions
-rw-r--r-- 1 root root 102 Sep 11 12:41 /root/.ssh/github_actions.pub
root@myvps:/opt# ssh-keygen -lf /root/.ssh/github.pub
256 SHA256:bYldbnQGvLfQzYDDiwLPrPHJCG/4Aa1rF+beg/8Ncq0 vps-github (ED25519)
root@myvps:/opt# ssh -i /root/.ssh/github -o IdentitiesOnly=yes -T git@github.com
Hi myvps! You've successfully authenticated, but GitHub does not provide shell access.
root@myvps:/opt# cat > /root/.ssh/config <<'EOF'
Host github.com
    HostName github.com
    User git
    IdentityFile /root/.ssh/github
    IdentitiesOnly yes
EOF
root@myvps:/opt# chmod 600 /root/.ssh/config
root@myvps:/opt# ssh -T git@github.com
Hi myvps! You've successfully authenticated, but GitHub does not provide shell access.
root@myvps:/opt# cd /opt
root@myvps:/opt# git clone git@github.com:myvps/ci-cd-demo.git
Cloning into 'ci-cd-demo'...
remote: Enumerating objects: 18, done.
remote: Counting objects: 100% (18/18), done.
remote: Compressing objects: 100% (14/14), done.
remote: Total 18 (delta 2), reused 17 (delta 1), pack-reused 0 (from 0)
Receiving objects: 100% (18/18), done.
Resolving deltas: 100% (2/2), done.
root@myvps:/opt# ls
ci-cd-demo  containerd  vllm
root@myvps:/opt# cd ci-cd-demo/
root@myvps:/opt/ci-cd-demo# ls
Dockerfile  docker-compose.yml  frontend
root@myvps:/opt/ci-cd-demo# git status
On branch main
Your branch is up to date with 'origin/main'.
nothing to commit, working tree clean
root@myvps:~# docker ps
CONTAINER ID   IMAGE                           COMMAND                  CREATED              STATUS                PORTS                                         NAMES
52642a98e30b   ci-cd-demo-web                  "/docker-entrypoint...."   About a minute ago   Up About a minute     0.0.0.0:80->80/tcp, [::]:80->80/tcp           ci-cd-demo
相关推荐
闲云自留地1 小时前
动手玩 Nova:Hypervisor、主机聚合、可用分区、虚拟机生命周期实操
运维·架构·openstack
赴生-1 小时前
Liunx 操作系统 进程概念(上)
linux
binqian2 小时前
【linux】OpenSSH升级文档
linux·运维·网络
吴声子夜歌2 小时前
Shell编程——if条件语句
linux·运维·shell
和裕2 小时前
蜂窝板 vs 七层瓦楞重型纸箱:大件工业设备运输性能与成本全对比
大数据·运维·网络·人工智能·算法
Dawn-bit2 小时前
Linux 运维基础扩展:跳板机、堡垒机与物理服务器全流程
linux·运维·服务器·云计算·运维开发
xhbh6662 小时前
主流系统备份软件横向评测:从大型集群到终端备份选型
自动化·数据备份·文件备份·系统备份·同步备份·备份软件
新时代牛马2 小时前
Linux 定时任务:crontab、anacron 与systemd timer
linux·运维·服务器
微三云 - 廖会灵 (私域系统开发)2 小时前
架构师视角:用AI Agent与超级APP重构私域自动化盈利系统
人工智能·重构·自动化