前言
上一篇已经完成了 GitHub Actions 自托管 Runner 的接入,让 k8s-master 可以作为 GitHub Actions 的执行节点。
这样做的原因很简单:我的 Harbor 私有镜像仓库在内网,地址是:
172.16.11.142
GitHub 官方 Runner 无法直接访问这个内网地址,所以必须把 Runner 放到自己的服务器上执行。
这一篇继续完成剩下的部分:
GitHub Actions 自动构建镜像
↓
推送到 Harbor
↓
自动修改 GitOps 配置
↓
ArgoCD 自动同步
↓
Kubernetes 自动发布
↓
Ingress 域名访问
↓
HTTPS 访问
最终访问地址:
https://web.xoho.top
一、当前已有环境
目前已经完成的基础组件如下:
组件 状态
Harbor 私有镜像仓库 已完成
Kubernetes 集群 已完成
GitHub 仓库 已完成
ArgoCD 已完成
GitHub Actions Self-hosted Runner 已完成
Web 应用镜像 已推送 Harbor
NodePort 访问 已验证
Ingress Controller 已部署
当前核心地址:
Harbor:172.16.11.142
K8s Master:172.16.11.142
Ingress 节点:172.16.11.146
GitHub 仓库:YANYUE0721/kgale-web-gitops
域名:web.xoho.top
二、CI/CD 目标
这次要实现的目标是:
提交代码到 GitHub
↓
GitHub Actions 自动触发
↓
Self-hosted Runner 在 k8s-master 上执行任务
↓
Docker build 构建镜像
↓
Docker push 推送到 Harbor
↓
自动更新 Kubernetes YAML 镜像版本
↓
Git push 回 GitHub
↓
ArgoCD 自动同步
↓
Kubernetes 自动更新 Pod
↓
通过域名和 HTTPS 访问新版本
简单说,就是把原来手动做的事情自动化。
三、创建 Web 页面源码
在 GitHub 仓库目录中添加一个简单页面:
cd ~/kgale-web-gitops
cat > index.html <<'EOF'
Harbor + Kubernetes + GitHub Actions + ArgoCD + ChatGPT 部署成功!
这是通过 CI/CD 自动构建并发布的页面。
镜像来自 Harbor 私有镜像仓库。
EOF
这个页面后面会被打包进 Nginx 镜像中。
四、创建 Dockerfile
创建 Dockerfile:
cat > Dockerfile <<'EOF'
FROM nginx:latest
COPY index.html /usr/share/nginx/html/index.html
EOF
这个 Dockerfile 的作用很简单:
使用 nginx 作为基础镜像
把 index.html 放到 nginx 默认网页目录
五、创建 GitHub Actions 工作流
创建目录:
mkdir -p .github/workflows
创建工作流文件:
cat > .github/workflows/build-and-deploy.yaml <<'EOF'
name: Build and Deploy to Harbor
on:
push:
branches:
- main
permissions:
contents: write
jobs:
build-and-deploy:
if: "!contains(github.event.head_commit.message, '[skip ci]')"
runs-on: self-hosted
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Set image tag
run: |
echo "IMAGE_TAG=v${GITHUB_RUN_NUMBER}" >> $GITHUB_ENV
echo “IMAGE_NAME=172.16.11.142/library/kgale-web:v${GITHUB_RUN_NUMBER}” >> $GITHUB_ENV
– name: Build Docker image
run: |
docker build -t ${IMAGE_NAME} .
– name: Push Docker image to Harbor
run: |
docker push ${IMAGE_NAME}
– name: Update Kubernetes YAML image
run: |
sed -i “s#^[[:space:]]*image: 172.16.11.142/library/kgale-web:.*# image: ${IMAGE_NAME}#g” kgale-web-gitops.yaml
– name: Commit and push updated YAML
run: |
git config user.name “github-actions”
git config user.email “github-actions@github.com”
git add kgale-web-gitops.yaml
git commit -m “update image to ${IMAGE_TAG} [skip ci]” || echo “No changes to commit”
git push
EOF
这里需要注意两点。
第一,使用的是:
runs-on: self-hosted
这表示任务不是在 GitHub 官方服务器上执行,而是在自己的 k8s-master 上执行。
第二,提交信息里加了:
[skip ci]
这是为了避免循环触发。
因为 Workflow 会自动修改 YAML 并 push 回 GitHub,如果不跳过 CI,就会形成无限循环。
六、提交并推送工作流
git add .
git commit -m “add github actions ci workflow”
git push
这里遇到了一个 GitHub 权限问题:
refusing to allow a Personal Access Token to create or update workflow
原因是 Token 没有 workflow 权限。
解决方式是重新生成 GitHub Token,并勾选:
repo
workflow
重新推送后成功。
七、第一次运行失败:sed 命令问题
GitHub Actions 第一次运行失败,失败点在:
Update Kubernetes YAML image
报错:
sed: unknown option to `s’
原因是原来的 sed 命令里使用了 # 作为分隔符,同时替换内容里也带了 # Harbor… 注释,导致 sed 解析混乱。
错误写法类似:
sed -i “s#image: 172.16.11.142/library/kgale-web:.*#image: ${IMAGE_NAME} # Harbor 镜像#g” kgale-web-gitops.yaml
修复后改成:
sed -i “s#^[[:space:]]*image: 172.16.11.142/library/kgale-web:.*# image: ${IMAGE_NAME}#g” kgale-web-gitops.yaml
重新提交:
git add .github/workflows/build-and-deploy.yaml
git commit -m “fix ci workflow sed command”
git push
八、GitHub Actions 成功运行
修复后,Runner 终端显示:
Running job: build-and-deploy
Job build-and-deploy completed with result: Succeeded
这说明 GitHub Actions 已经成功完成:
拉取代码
构建 Docker 镜像
推送镜像到 Harbor
修改 Kubernetes YAML
推送回 GitHub
查看页面后,已经变成了:
Harbor + Kubernetes + GitHub Actions + ArgoCD + ChatGPT 部署成功!
这一步说明 CI/CD 主链路已经跑通。
九、当前完整 CI/CD 链路
此时链路已经变成:
GitHub 提交代码
↓
GitHub Actions 触发
↓
Self-hosted Runner 执行任务
↓
Docker build 构建镜像
↓
Docker push 推送到 Harbor
↓
GitHub Actions 修改 GitOps YAML
↓
Git push 回 GitHub
↓
ArgoCD 检测配置变化
↓
Kubernetes 更新 Deployment
↓
Pod 使用新镜像运行
↓
浏览器访问新页面
这就是完整的 CI/CD 闭环。
十、添加域名解析
为了让服务不再通过 IP 和端口访问,我给域名添加了 DNS 记录。
在 Cloudflare 中添加 A 记录:
类型:A
名称:web
内容:172.16.11.146
代理状态:仅 DNS
TTL:自动
也就是:
web.xoho.top → 172.16.11.146
Windows 上测试:
nslookup web.xoho.top
返回:
Name: web.xoho.top
Address: 172.16.11.146
说明 DNS 已经生效。
十一、创建 Ingress 入口
接下来让访问流量通过 Ingress Controller 进入 Kubernetes。
创建 Ingress 配置:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: kgale-web-ingress
namespace: default
spec:
ingressClassName: nginx
rules:
– host: web.xoho.top
http:
paths:
– path: /
pathType: Prefix
backend:
service:
name: kgale-web-gitops-svc
port:
number: 80
提交到 GitHub 后,ArgoCD 自动同步创建 Ingress。
查看状态:
kubectl get ingress
输出:
NAME CLASS HOSTS ADDRESS PORTS AGE
kgale-web-ingress nginx web.xoho.top 172.16.11.146 80 20m
访问:
http://web.xoho.top
成功打开 CI/CD 页面。
至此,HTTP 无端口访问完成。
十二、为什么一开始会有端口?
最开始访问是:
http://web.xoho.top:30084
因为应用是通过 NodePort 暴露的:
80:30084/TCP
浏览器访问非标准端口时,必须写端口号。
后来通过 Ingress Controller 接管 80 端口后,就可以直接访问:
http://web.xoho.top
浏览器默认 HTTP 端口就是 80,所以不需要显示端口。
这一步和常见生产环境的访问方式更接近。
十三、启用 HTTPS
HTTP 成功后,继续做 HTTPS。
先生成自签名证书:
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
-keyout web.xoho.top.key \
-out web.xoho.top.crt \
-subj “/CN=web.xoho.top/O=kgale”
创建 Kubernetes TLS Secret:
kubectl create secret tls web-xoho-top-tls \
–cert=web.xoho.top.crt \
–key=web.xoho.top.key \
-n default
查看 Secret:
kubectl get secret web-xoho-top-tls
输出:
NAME TYPE DATA AGE
web-xoho-top-tls kubernetes.io/tls 2 4s
说明证书已经写入 Kubernetes。
十四、给 Ingress 添加 TLS
给 Ingress 添加 TLS 配置:
kubectl patch ingress kgale-web-ingress -n default –type=’merge’ -p \
‘{“spec”:{“tls”:[{“hosts”:[“web.xoho.top”],”secretName”:”web-xoho-top-tls”}]}}’
查看 Ingress:
kubectl get ingress
输出:
NAME CLASS HOSTS ADDRESS PORTS AGE
kgale-web-ingress nginx web.xoho.top 172.16.11.146 80, 443 29m
查看 TLS:
kubectl describe ingress kgale-web-ingress | grep -A6 TLS
输出:
TLS:
web-xoho-top-tls terminates web.xoho.top
这说明 HTTPS 已经挂到 Ingress 上。
十五、验证 HTTPS
在服务器上测试:
curl -kI https://web.xoho.top
返回:
HTTP/2 200
content-type: text/html
content-length: 383
strict-transport-security: max-age=31536000; includeSubDomains
这说明 HTTPS 已经成功。
浏览器访问:
https://web.xoho.top
如果浏览器提示证书不安全,这是正常的,因为当前使用的是自签名证书。
自签名证书可以验证 HTTPS 功能,但不会被浏览器默认信任。
十六、最终效果
最终页面显示:
Harbor + Kubernetes + GitHub Actions + ArgoCD + ChatGPT 部署成功!
当前完整访问链路:
https://web.xoho.top
↓
DNS 解析到 172.16.11.146
↓
Ingress Controller 443 端口
↓
TLS Secret 处理 HTTPS
↓
Ingress 规则匹配 web.xoho.top
↓
Service 转发到后端 Pod
↓
Pod 运行 Harbor 中的镜像
十七、最终完成内容
这次完整完成了:
1. GitHub Actions 工作流创建
2. Self-hosted Runner 执行 CI 任务
3. Docker 镜像自动构建
4. 镜像自动推送 Harbor
5. Kubernetes YAML 自动更新镜像版本
6. GitHub 自动保存 GitOps 配置
7. ArgoCD 自动同步配置
8. Kubernetes 自动滚动更新 Pod
9. Ingress 域名入口配置
10. HTTP 无端口访问
11. HTTPS 自签名证书访问
完整组件如下:
组件 作用
GitHub 保存代码和 Kubernetes 配置
GitHub Actions 触发 CI 自动化流程
Self-hosted Runner 在内网执行构建和推送
Docker 构建镜像
Harbor 保存私有镜像
ArgoCD 自动同步 GitOps 配置
Kubernetes 运行应用
Ingress Controller 提供统一访问入口
TLS Secret 提供 HTTPS 证书
十八、总结
这次实验从 CI/CD 自动发布继续往后推进,最终补齐了域名入口和 HTTPS。
现在的整体效果已经比较完整:
开发提交代码
↓
GitHub Actions 自动构建
↓
镜像推送 Harbor
↓
GitOps 配置自动更新
↓
ArgoCD 自动同步
↓
Kubernetes 自动发布
↓
Ingress 提供域名访问
↓
HTTPS 提供加密访问
这套链路已经不是简单的 Kubernetes 部署实验,而是一套完整的 DevOps / GitOps 发布流程。
当前还能继续优化的地方有两个:
1. 把自签名证书换成 Let’s Encrypt 正式证书
2. 把临时 patch 的 HTTPS 配置完全纳入 GitHub 仓库,由 ArgoCD 永久管理
但从功能上看,现在已经完成了完整闭环:
GitHub Actions + Harbor + ArgoCD + Kubernetes + Ingress + HTTPS
至此,本阶段实验完成。