# 复制到 itfx 仓库: .github/workflows/deploy.yml # # 集群一次性准备(在 42 上): # GIT_TOKEN=... TCR_USERNAME=... TCR_PASSWORD=... ./scripts/16-setup-kaniko.sh # # GitHub Secrets(本仓 Settings → Secrets): # SSH_K3S_HOST — 42 公网 IP # SSH_K3S_USERNAME — 一般 root # SSH_K3S_PASSWORD — SSH 密码 # # 排障:看哪个 step 红了 # 1 print-info — 仅输出变量,几乎不会挂 # 2 check-cluster — SSH / kubeconfig / 节点不通 # 3 check-secrets — 未跑 16-setup-kaniko,或 Secret 名不对 # 4 kaniko-build — 拉代码 / Dockerfile / 推 TCR(看 Job 日志) # 5 set-image — Deployment 名或容器名不对 # 6 rollout — 镜像拉不下来 / 探针失败 / 资源不够 # name: Build and Deploy on: push: branches: [master] workflow_dispatch: env: GIT_URL: https://github.com/xiaoliutral/itfx.git DOCKERFILE: src/InterfaceForward.Api/Dockerfile IMAGE_NAME: interfaceforward_api DEPLOY_NAME: interfaceforward-api CONTAINER_NAME: interfaceforward-api TCR_REGISTRY: ccr.ccs.tencentyun.com TCR_NAMESPACE: ideship K3S_DIR: /data/k3s KUBECONFIG: /etc/rancher/k3s/k3s.yaml NAMESPACE: prod jobs: deploy: runs-on: ubuntu-latest steps: # ── 0. 本机算出 TAG / 完整镜像名(不 SSH)──────────────── - name: "0. print build info" id: info run: | TAG="${{ github.sha }}" IMG="${{ env.TCR_REGISTRY }}/${{ env.TCR_NAMESPACE }}/${{ env.IMAGE_NAME }}:${TAG}" echo "tag=${TAG}" >> "$GITHUB_OUTPUT" echo "image=${IMG}" >> "$GITHUB_OUTPUT" echo "======= build info =======" echo "repo = ${{ github.repository }}" echo "ref = ${{ github.ref_name }}" echo "sha = ${TAG}" echo "git_url = ${{ env.GIT_URL }}" echo "dockerfile = ${{ env.DOCKERFILE }}" echo "image = ${IMG}" echo "deploy = ${{ env.NAMESPACE }}/deploy/${{ env.DEPLOY_NAME }}" echo "==========================" # ── 1. SSH + 集群是否通 ───────────────────────────────── - name: "1. check cluster (SSH + kubectl)" uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_K3S_HOST }} username: ${{ secrets.SSH_K3S_USERNAME }} password: ${{ secrets.SSH_K3S_PASSWORD }} script_stop: true script: | set -euo pipefail export KUBECONFIG="${{ env.KUBECONFIG }}" echo "==> whoami: $(whoami) @ $(hostname)" echo "==> kubeconfig: ${KUBECONFIG}" test -f "${KUBECONFIG}" || { echo "FAIL: 找不到 ${KUBECONFIG}"; exit 1; } test -d "${{ env.K3S_DIR }}" || { echo "FAIL: 找不到 ${{ env.K3S_DIR }},请把 k3s 清单同步到该目录"; exit 1; } test -x "${{ env.K3S_DIR }}/scripts/ci-kaniko-build.sh" || { echo "FAIL: 缺少可执行脚本 ${{ env.K3S_DIR }}/scripts/ci-kaniko-build.sh" ls -la "${{ env.K3S_DIR }}/scripts/" || true exit 1 } echo "==> nodes:" kubectl get nodes -o wide echo "==> namespace ${{ env.NAMESPACE }}:" kubectl get ns "${{ env.NAMESPACE }}" echo "==> deploy/${{ env.DEPLOY_NAME }}:" kubectl -n "${{ env.NAMESPACE }}" get deploy "${{ env.DEPLOY_NAME }}" -o wide # ── 2. Kaniko 凭据是否已创建 ──────────────────────────── - name: "2. check kaniko secrets" uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_K3S_HOST }} username: ${{ secrets.SSH_K3S_USERNAME }} password: ${{ secrets.SSH_K3S_PASSWORD }} script_stop: true script: | set -euo pipefail export KUBECONFIG="${{ env.KUBECONFIG }}" NS="${{ env.NAMESPACE }}" echo "==> 检查 Secret(由 16-setup-kaniko.sh 创建)" kubectl -n "$NS" get secret kaniko-git -o jsonpath='{.metadata.name}{" keys="}{.data}{"\n"}' >/dev/null \ && echo "OK secret/kaniko-git" \ || { echo "FAIL: 缺少 secret/kaniko-git — 在 42 执行: GIT_TOKEN=... ./scripts/16-setup-kaniko.sh"; exit 1; } kubectl -n "$NS" get secret kaniko-docker-config >/dev/null \ && echo "OK secret/kaniko-docker-config" \ || { echo "FAIL: 缺少 secret/kaniko-docker-config — TCR 账号未写入"; exit 1; } echo "==> build 节点:" kubectl get nodes -l role=build -o wide COUNT="$(kubectl get nodes -l role=build --no-headers 2>/dev/null | wc -l | tr -d ' ')" if [[ "${COUNT}" == "0" ]]; then echo "FAIL: 没有 role=build 节点,Kaniko Job 会 Pending" kubectl get nodes --show-labels exit 1 fi # ── 3. Kaniko 构建并推 TCR(最慢,失败看 Job 日志)───── - name: "3. kaniko build & push" uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_K3S_HOST }} username: ${{ secrets.SSH_K3S_USERNAME }} password: ${{ secrets.SSH_K3S_PASSWORD }} command_timeout: 50m script_stop: true script: | set -euo pipefail export KUBECONFIG="${{ env.KUBECONFIG }}" cd "${{ env.K3S_DIR }}" TAG="${{ steps.info.outputs.tag }}" echo "==> 开始 Kaniko: ${{ env.IMAGE_NAME }}:${TAG}" echo " 失败时在 42 执行:" echo " kubectl -n ${{ env.NAMESPACE }} get job -l app=kaniko" echo " kubectl -n ${{ env.NAMESPACE }} logs -l job-name=... -c git" echo " kubectl -n ${{ env.NAMESPACE }} logs -l job-name=... -c kaniko" ./scripts/ci-kaniko-build.sh \ "${{ env.GIT_URL }}" \ "${{ env.DOCKERFILE }}" \ "${{ env.IMAGE_NAME }}" \ "${TAG}" \ "${TAG}" echo "==> 镜像已推送: ${{ steps.info.outputs.image }}" # ── 4. 改 Deployment 镜像 ─────────────────────────────── - name: "4. set deployment image" uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_K3S_HOST }} username: ${{ secrets.SSH_K3S_USERNAME }} password: ${{ secrets.SSH_K3S_PASSWORD }} script_stop: true script: | set -euo pipefail export KUBECONFIG="${{ env.KUBECONFIG }}" NS="${{ env.NAMESPACE }}" IMG="${{ steps.info.outputs.image }}" echo "==> 当前镜像:" kubectl -n "$NS" get deploy "${{ env.DEPLOY_NAME }}" \ -o jsonpath='{.spec.template.spec.containers[?(@.name=="${{ env.CONTAINER_NAME }}")].image}{"\n"}' echo "==> 设为: ${IMG}" kubectl -n "$NS" set image "deploy/${{ env.DEPLOY_NAME }}" \ "${{ env.CONTAINER_NAME }}=${IMG}" echo "==> 已更新:" kubectl -n "$NS" get deploy "${{ env.DEPLOY_NAME }}" \ -o jsonpath='{.spec.template.spec.containers[?(@.name=="${{ env.CONTAINER_NAME }}")].image}{"\n"}' # ── 5. 等待滚动发布 ───────────────────────────────────── - name: "5. wait rollout" uses: appleboy/ssh-action@v1.0.3 with: host: ${{ secrets.SSH_K3S_HOST }} username: ${{ secrets.SSH_K3S_USERNAME }} password: ${{ secrets.SSH_K3S_PASSWORD }} command_timeout: 5m script_stop: true script: | set -euo pipefail export KUBECONFIG="${{ env.KUBECONFIG }}" NS="${{ env.NAMESPACE }}" DEP="${{ env.DEPLOY_NAME }}" echo "==> rollout status..." if ! kubectl -n "$NS" rollout status "deploy/${DEP}" --timeout=180s; then echo "FAIL: rollout 超时/失败,诊断信息如下" kubectl -n "$NS" get deploy,rs,pods -l app="${DEP}" -o wide || \ kubectl -n "$NS" get pods -o wide | grep -E "${DEP}|NAME" || true kubectl -n "$NS" describe deploy "${DEP}" | tail -60 || true POD="$(kubectl -n "$NS" get pods -l app="${DEP}" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)" if [[ -n "${POD}" ]]; then echo "==> events for ${POD}:" kubectl -n "$NS" describe pod "${POD}" | tail -40 || true echo "==> logs:" kubectl -n "$NS" logs "${POD}" --tail=80 || true fi exit 1 fi echo "==> pods:" kubectl -n "$NS" get pods -o wide | grep -E "${DEP}|NAME" || \ kubectl -n "$NS" get pods -o wide echo "OK deployed ${{ steps.info.outputs.image }}"