☰
零成本搭建 K8s CI/CD 练习环境:Kind + GitHub Actions 从零到部署
2026/10/19 2:13:27 网站建设 项目流程

文章目录

    • 整体架构
    • 环境准备
    • 写应用和 K8s 清单
    • GitHub Actions 流水线
    • 跑通全流程
    • 踩坑速查
    • 下一步

学 Kubernetes 最头疼的不是概念,是没有一个能随便折腾的练习环境。买云上的托管 K8s,一个月几百块起步,还怕手抖忘了删集群被扣费。用 Minikube 倒是免费,但跑 CI/CD 流水线的时候总差点意思。

这篇文章给你一个零成本方案:Kind 本地起集群,GitHub Actions 跑流水线,从 git push 到 Pod 运行,全链路打通。不花一分钱,不依赖任何外部服务。

整体架构

三个组件,各管一摊:

组件职责成本
KindDocker 里跑 K8s 集群,本地验证用免费
GitHub Actions云端 CI/CD,构建镜像、跑测试、部署验证公共仓库 2000 分钟/月免费
Docker容器运行时,构建镜像的基础免费

流程一句话总结:本地写代码 →git push→ GitHub Actions 自动触发 → 构建 Docker 镜像 → 在 Actions 里起一个 Kind 集群 → 部署并验证 → main 分支的镜像推到 GHCR。

环境准备

你需要的东西:一台 4GB 以上内存的电脑、Docker、kubectl、Kind、Git。

装 kubectl:

# Linuxcurl-LO"https://dl.k8s.io/release/v1.31.0/bin/linux/amd64/kubectl"chmod+x kubectl&&sudomvkubectl /usr/local/bin/ kubectl version--client

装 Kind:

curl-Lokind https://kind.sigs.k8s.io/dl/v0.24.0/kind-linux-amd64chmod+x kind&&sudomvkind /usr/local/bin/ kind version

本地起一个集群验证安装没问题:

kind create cluster--namecicd-lab kubectl cluster-info kubectl get nodes

看到节点 Ready 就说明环境 OK。先删掉,后面用 Actions 里的:

kind delete cluster--namecicd-lab

写应用和 K8s 清单

用一个极简 Python HTTP 服务做演示,不引任何框架,纯标准库:

# src/app.pyfromhttp.serverimportHTTPServer,BaseHTTPRequestHandlerimportjsonclassHandler(BaseHTTPRequestHandler):defdo_GET(self):self.send_response(200)self.send_header('Content-Type','application/json')self.end_headers()self.wfile.write(json.dumps({"status":"ok","message":"Hello from K8s CI/CD Lab!","version":"1.0.0"}).encode())if__name__=='__main__':server=HTTPServer(('0.0.0.0',8080),Handler)print("Server running on port 8080...")server.serve_forever()

Dockerfile 能多小就多小:

FROM python:3.12-slim WORKDIR /app COPY src/ . EXPOSE 8080 CMD ["python", "app.py"]

K8s 的 Deployment 加上健康检查,这是生产环境的习惯:

# k8s/deployment.yamlapiVersion:apps/v1kind:Deploymentmetadata:name:cicd-demolabels:app:cicd-demospec:replicas:2selector:matchLabels:app:cicd-demotemplate:metadata:labels:app:cicd-demospec:containers:-name:appimage:ghcr.io/OWNER/cicd-demo:TAGports:-containerPort:8080readinessProbe:httpGet:path:/port:8080initialDelaySeconds:3periodSeconds:5livenessProbe:httpGet:path:/port:8080initialDelaySeconds:10periodSeconds:10resources:requests:memory:"64Mi"cpu:"100m"limits:memory:"128Mi"cpu:"250m"

Service 用 ClusterIP 就够了,Actions 里用 port-forward 验证:

# k8s/service.yamlapiVersion:v1kind:Servicemetadata:name:cicd-demo-svcspec:selector:app:cicd-demoports:-port:80targetPort:8080type:ClusterIP

注意 Deployment 里的OWNER和TAG是占位符,Actions 流水线会在运行时用 sed 替换。

GitHub Actions 流水线

项目结构长这样:

k8s-cicd-lab/ ├── src/ │ └── app.py ├── Dockerfile ├── k8s/ │ ├── deployment.yaml │ └── service.yaml └── .github/ └── workflows/ └── ci-cd.yml

流水线分三个阶段,拆开讲:

阶段一:构建和检查。检出代码,做语法检查,构建镜像,验证镜像大小不超过 200MB。这个阶段跑得快,有问题直接拦住。

阶段二:K8s 部署测试。用helm/kind-action在 Actions runner 里起一个 Kind 集群,把刚才构建的镜像kind load进去(注意不是 push 到远端,是直接灌进 Kind),替换 manifest 里的占位符,apply 部署,rollout status等就绪,port-forward加curl做健康检查。如果这步挂了,自动导出 Pod 日志方便排查。

阶段三:推送镜像。只在 main 分支触发,用GITHUB_TOKEN登录 GHCR,推镜像。PR 不推,避免污染仓库。

name:K8s CI/CD Pipelineon:push:branches:[main]pull_request:branches:[main]env:IMAGE_NAME:${{github.repository_owner}}/cicd-demoIMAGE_TAG:${{github.sha}}jobs:build:runs-on:ubuntu-lateststeps:-uses:actions/checkout@v4-name:语法检查run:python3-m py_compile src/app.py-name:构建镜像run:docker build-t ${{env.IMAGE_NAME}}:${{env.IMAGE_TAG}}.deploy-test:needs:buildruns-on:ubuntu-lateststeps:-uses:actions/checkout@v4-name:启动 Kinduses:helm/kind-action@v1.10.0with:cluster_name:k8s-cicd-name:构建并加载镜像run:|docker build -t ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} . kind load docker-image ${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} \ --name k8s-cicd-name:替换占位符并部署run:|sed -i "s|OWNER|${{ github.repository_owner }}|g" k8s/deployment.yaml sed -i "s|TAG|${{ env.IMAGE_TAG }}|g" k8s/deployment.yaml kubectl apply -f k8s/-name:等待就绪run:kubectl rollout status deployment/cicd-demo--timeout=120s-name:健康检查run:|kubectl port-forward svc/cicd-demo-svc 8080:80 & sleep 3 curl -sf http://localhost:8080 | python3 -m json.toolpush-image:needs:deploy-testruns-on:ubuntu-latestif:github.ref == 'refs/heads/main'permissions:contents:readpackages:writesteps:-uses:actions/checkout@v4-uses:docker/login-action@v3with:registry:ghcr.iousername:${{github.actor}}password:${{secrets.GITHUB_TOKEN}}-name:构建并推送run:|IMAGE=ghcr.io/${{ env.IMAGE_NAME }}:${{ env.IMAGE_TAG }} docker build -t $IMAGE . docker push $IMAGE

关键点解释:kind load docker-image是把本地构建好的镜像直接灌进 Kind 节点的 Docker 里,省去了 push/pull 的环节,CI 里用这招特别快。needs: build保证阶段串行。permissions那块必须显式声明packages: write,不然 GITHUB_TOKEN 没权限推 GHCR。

跑通全流程

把上面的文件全部提交推到 GitHub:

gitadd-Agitcommit-m"init: K8s CI/CD lab"gitpush origin main

去仓库的 Actions tab 看流水线跑。正常情况下三个阶段全绿,最后 GHCR 里多了一个镜像。

之后改代码、提交、推送,流水线自动跑。迭代流程就三步:改代码 → push → 看 Actions 结果。本地想快速验证的话:

dockerbuild-ttest-app.dockerrun-p8080:8080 test-appcurlhttp://localhost:8080

不用每次都等 CI,本地先跑通了再推。

踩坑速查

问题原因解决
ImagePullBackOffKind 集群里没有这个镜像用kind load docker-image灌进去
Actions 里GITHUB_TOKEN推镜像 403权限不够仓库 Settings → Actions → Workflow permissions 改成 Read and write
YAML 解析报错缩进用了 Tab 或者对齐不对YAML 严格只认空格,用yamllint检查一下
Kind 集群创建卡住上次没删干净kind delete cluster --name xxx然后重试
port-forward超时Pod 还没 Ready先kubectl rollout status等就绪再转发
kubectl连不上集群kubeconfig 的 context 不对kubectl config use-context kind-集群名

下一步

跑通这个流水线之后,可以往几个方向扩展:

  • Helm 打包:把 K8s 清单改成 Helm Chart,支持 values 覆盖,多环境复用。
  • ArgoCD:在 Kind 里装一个 ArgoCD,把部署从 push 模式改成 GitOps pull 模式。
  • 多环境隔离:dev / staging / prod 用不同 namespace,同一套 manifest 不同 values。
  • 监控和日志:Prometheus + Grafana 看指标,EFK 收日志,Kind 里跑这些也不吃太多资源。

这套方案最大的好处是零心理负担。集群随便建随便删,流水线跑挂了不用心疼钱。先把基础流程跑通,再往上加东西,比直接在生产环境里摸黑强太多了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询