阿里云轻量/ECS 实战:K3s + Helm + cert-manager + 云效 Codeup 全链路 CI/CD 落地(记录自用)

在云原生时代,即便是个性化的小型项目或微服务,我们也希望拥有自动化的证书管理(HTTPS)和丝滑的 CI/CD 流程。本文将手把手教你如何在阿里云服务器上,利用 K3s 的轻量化优势,结合 cert-manager 自动签发 Let's Encrypt 证书,并使用阿里云**云效(Codeup + Flow)**实现从代码提交到集群部署的全自动化。

一、环境准备与 K3s 安装

(一)环境准备

1、开启 bridge 网桥过滤
复制代码
# 1. 确保重启后自动加载内核模块 (持久化)
cat <<EOF | sudo tee /etc/modules-load.d/k3s.conf
overlay
br_netfilter
EOF

# 2. 立即加载模块 (即时生效)
sudo modprobe overlay
sudo modprobe br_netfilter

# 3. 写入内核参数
cat <<EOF | sudo tee /etc/sysctl.d/k3s.conf
net.bridge.bridge-nf-call-iptables  = 1
net.bridge.bridge-nf-call-ip6tables = 1
net.ipv4.ip_forward                 = 1
EOF

# 4. 使所有配置立即生效
sudo sysctl --system

# 5. 验证 (确保两个模块都在)
lsmod | grep -E "br_netfilter|overlay"
overlay               155648  21
br_netfilter           32768  0
bridge                270336  1 br_netfilter
2、其它设置
复制代码
# 本地使用 VMWare 部署需要执行下面操作,阿里云 ECS 服务器下面设置默认都是关闭的
# 唯一需要关注的是 selinux,建议设置为宽容模式或不设置也可以。但如果项目涉及金钱或核心机密或者追求极致安全,将模式设为 Enforcing

# 1、关闭防火墙
sudo systemctl disable --now firewalld
# 查看防火墙状态
firewall-cmd --state

# 2、禁用 swap (虽然标准 K8s 要求关闭,但 K3s 支持开启)
swapoff -a && sed -ri 's/.*swap.*/#&/' /etc/fstab

# 3、关闭 selinux
sudo setenforce 0 && sudo sed -i 's/^SELINUX=.*/SELINUX=permissive/' /etc/selinux/config

(二)安装 K3s

1、快速安装 K3s
复制代码
# INSTALL_K3S_MIRROR=cn: 强制使用国内服务器下载二进制文件,速度极快。
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
INSTALL_K3S_MIRROR=cn \
INSTALL_K3S_SKIP_SELINUX_RPM=true \
sh -
2、配置 K3s 镜像加速

服务器无法连接到 Docker Hub (docker.io)。因为 K3s 启动时必须先拉取一个名为 pause 的基础镜像(沙箱容器),如果这个镜像拉不下来,所有的 Pod(CoreDNS, Traefik 等)都无法启动。

复制代码
# 1、创建/编辑 配置文件
sudo vi /etc/rancher/k3s/registries.yaml

# 2、写入加速器配置(这里使用了几个常用的国内镜像源)
mirrors:
  docker.io:
    endpoint:
      - "https://docker.m.daocloud.io"
      - "https://mirror.baidubce.com"
      - "https://dockerproxy.com"

# 3、重启 K3s,保存退出后,重启服务让配置生效
sudo systemctl restart k3s
3、验证
复制代码
# 检查服务状态,看到绿色的 active (running),说明已经正常运行了
systemctl status k3s

# 检查节点状态,看看节点是否已经 Ready(就绪)
kubectl get nodes

# 检查内置组件 (Pod) 状态
kubectl get pods -A
# 查看 STATUS 
NAMESPACE     NAME                                      READY   STATUS      RESTARTS   AGE
kube-system   coredns-695cbbfcb9-g8fc9                  1/1     Running     0          12m
kube-system   helm-install-traefik-crd-xsth2            0/1     Completed   0          12m
kube-system   helm-install-traefik-kmtc8                0/1     Completed   2          12m
kube-system   local-path-provisioner-546dfc6456-hvcqs   1/1     Running     0          12m
kube-system   metrics-server-c8774f4f4-kqklt            1/1     Running     0          12m
kube-system   svclb-traefik-a6fae38b-wgzkl              2/2     Running     0          66s
kube-system   traefik-55db578d67-cmb6c                  1/1     Running     0          66s

二、Helm + cert-manager

(一)Helm

1、安装
复制代码
# 下载 helm 包管理器压缩包
wget https://get.helm.sh/helm-v3.20.0-linux-amd64.tar.gz

# 解压缩
tar -zxvf helm-v3.20.0-linux-amd64.tar.gz

# 把解压出来的二进制文件丢进系统目录
sudo mv linux-amd64/helm /usr/local/bin/helm

# 验证
helm version
2、配置 K3s 权限
复制代码
# K3s 默认的配置文件在 /etc/rancher/k3s/k3s.yaml,权限通常是 600。如果你直接运行 helm list,会报错,执行下面命令配置权限
echo 'export KUBECONFIG=/etc/rancher/k3s/k3s.yaml' >> ~/.bashrc
source ~/.bashrc
3、添加常用 Helm 仓库(国内加速版)
复制代码
# 1. 添加微软提供的国内镜像(包含很多官方常用组件)
helm repo add stable http://mirror.azure.cn/kubernetes/charts/

# 2. 添加 cert-manager 官方仓库
helm repo add jetstack https://charts.jetstack.io

# 3. 添加 Bitnami 仓库(这是全球最全的开源软件库,如 MySQL, Redis)
helm repo add bitnami https://charts.bitnami.com/bitnami

# 4. 更新仓库信息
helm repo update
4、基础操作
复制代码
# 查看已安装的应用
helm list -A

# 卸载应用(比如刚才的测试证书):
helm uninstall cert-manager -n cert-manager

# 创建一个 Chart 模版(其实就是项目或者应用)
helm create my-webapp

# 安装项目(-n 指定空间,--create-namespace 自动建空间)
helm install my-app ./my-webapp -n zhixu --create-namespace

# 升级应用
helm upgrade my-app ./my-webapp -n zhixu

# 查看历史版本列表
helm history zhixu-release -n zhixu
REVISION        UPDATED                         STATUS          CHART           APP VERSION     DESCRIPTION
5               Sat Feb  7 21:53:25 2026        superseded      zhixu-0.1.0     1.16.0          Upgrade complete
6               Mon Feb  9 16:07:14 2026        failed          zhixu-0.1.0     1.16.0          Upgrade "zhixu-release" failed: context deadline exceeded
7               Mon Feb  9 16:21:13 2026        failed          zhixu-0.1.0     1.16.0          Upgrade "zhixu-release" failed: context deadline exceeded
8               Mon Feb  9 16:35:28 2026        failed          zhixu-0.1.0     1.16.0          Upgrade "zhixu-release" failed: context deadline exceeded
9               Mon Feb  9 16:46:09 2026        failed          zhixu-0.1.0     1.16.0          Upgrade "zhixu-release" failed: context deadline exceeded
10              Mon Feb  9 17:00:45 2026        superseded      zhixu-0.1.0     1.16.0          Upgrade complete
11              Mon Feb  9 17:13:49 2026        failed          zhixu-0.1.0     1.16.0          Upgrade "zhixu-release" failed: context deadline exceeded
12              Mon Feb  9 17:44:03 2026        failed          zhixu-0.1.0     1.16.0          Upgrade "zhixu-release" failed: context deadline exceeded
13              Mon Feb  9 17:56:21 2026        superseded      zhixu-0.1.0     1.16.0          Upgrade complete
14              Mon Feb  9 18:03:15 2026        deployed        zhixu-0.1.0     1.16.0          Upgrade complete

# 回滚到上一个成功的版本(REVISION)
# 回滚并不等于代码撤销,执行下面命令之后也可以 改成版本14 再次执行进行版本升级
helm rollback zhixu-release 13 -n zhixu
5、谁在背后管理这些源
复制代码
# 当你执行 helm repo add 时,Helm 实际上是将这些信息写入了本地的一个 YAML 配置文件中。
# 文件路径:通常在 ~/.config/helm/repositories.yaml (Linux)
# 你可以通过下面命令查看它,会发现里面清晰地记录了所有你添加过的仓库名称和 URL
cat ~/.config/helm/repositories.yaml
6、其它
复制代码
# values.yaml

replicaCount: 3

image:
  repository: crpi-x06fgpqvjuvb9qkh.cn-qingdao.personal.cr.aliyuncs.com/zhixu/zhxu-official-web
  pullPolicy: IfNotPresent
  tag: "2026-02-10-08-17-20"

imagePullSecrets:
  - name: aliyun-acr-secret
nameOverride: ""
fullnameOverride: ""

serviceAccount:
  create: true
  automount: true
  annotations: {}
  name: ""

podAnnotations: {}
podLabels: {}

podSecurityContext: {}

securityContext: {}

service:
  type: ClusterIP
  port: 80
  targetPort: 8080 # .NET 8 默认端口

# This block is for setting up the ingress for more information can be found here: https://kubernetes.io/docs/concepts/services-networking/ingress/
ingress:
  enabled: true
  className: "traefik"
  annotations:
    cert-manager.io/cluster-issuer: "letsencrypt-prod"
    traefik.ingress.kubernetes.io/router.entrypoints: websecure
    traefik.ingress.kubernetes.io/router.tls: "true"
  hosts:
    - host: ddzhixu.com
      paths:
        - path: /
          pathType: ImplementationSpecific
    - host: www.ddzhixu.com
      paths:
        - path: /
          pathType: ImplementationSpecific
  tls:
    - secretName: ddzhixu-tls
      hosts:
        - ddzhixu.com
        - www.ddzhixu.com

httpRoute:
  enabled: false
  annotations: {}
  parentRefs:
  - name: gateway
    sectionName: http
  hostnames:
  - chart-example.local
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /headers

resources: {}

livenessProbe:
  httpGet:
    path: /
    port: 8080
readinessProbe:
  httpGet:
    path: /
    port: 8080

autoscaling:
  enabled: false
  minReplicas: 1
  maxReplicas: 100
  targetCPUUtilizationPercentage: 80

volumes: []

volumeMounts: []

nodeSelector: {}

tolerations: []

affinity: {}

# service.yaml

apiVersion: v1
kind: Service
metadata:
  name: {{ include "zhixu.fullname" . }}
  labels:
    {{- include "zhixu.labels" . | nindent 4 }}
spec:
  type: {{ .Values.service.type }}
  ports:
    - port: {{ .Values.service.port }}
      targetPort: {{ .Values.service.targetPort }}  # 改成引用 values 中的变量
      protocol: TCP
      name: http
  selector:
    {{- include "zhixu.selectorLabels" . | nindent 4 }}

(二)cert-manager

1、创建命名空间
复制代码
# 为了保持集群整洁,建议将证书管理相关的组件放入独立的 cert-manager 命名空间:
kubectl create namespace cert-manager
2、执行安装
复制代码
helm install cert-manager jetstack/cert-manager \
  --namespace cert-manager \
  --create-namespace \
  --version v1.16.1 \
  --set crds.enabled=true \
  --set image.repository=swr.cn-east-5.myhuaweicloud.com/k3s/cert-manager-controller \
  --set webhook.image.repository=swr.cn-east-5.myhuaweicloud.com/k3s/cert-manager-webhook \
  --set cainjector.image.repository=swr.cn-east-5.myhuaweicloud.com/k3s/cert-manager-cainjector \
  --set startupapicheck.image.repository=swr.cn-east-5.myhuaweicloud.com/k3s/cert-manager-startupapicheck
3、验证安装状态
复制代码
# 安装完成后,需确保 3 个 Pod 全部处于 Running 状态:
kubectl get pods --namespace cert-manager

# 你应该看到:
# cert-manager-xxx: 核心控制器。
# cert-manager-cainjector-xxx: 负责注入 CA 证书。
# cert-manager-webhook-xxx: 负责校验配置。

以及(安装完成后会退出的)startupapicheck
4、创建全局证书颁发者

要实现真正的"小绿锁",需要配置 ClusterIssuer。它是一个全局资源,可以让所有 Namespace 共享。

(1)创建文件 cluster-issuer.yaml:
复制代码
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory
    email: your-email@example.com  # 接收续期提醒的真实邮箱
    privateKeySecretRef:
      name: letsencrypt-prod-account-key
    solvers:
    - http01:
        ingress:
          class: traefik  # K3s 默认使用 Traefik
(2)应用配置:
复制代码
kubectl apply -f cluster-issuer.yaml
(3)验证"配置对不对":检查 ClusterIssuer 状态
复制代码
kubectl describe clusterissuer letsencrypt-prod

# 检查输出底部的 Status 部分
# 如果看到 The ACME account was registered with the ACME server,说明你的 ClusterIssuer 已经成功连接到了 Let's Encrypt 的服务器,并且你的邮箱账号已经注册成功。
# 这意味着: 你的 K3s 集群访问外网(Let's Encrypt 官网)的通道是顺畅的
(4)其它
复制代码
# 真实部署项目之后
# 查看证书状态
kubectl get certificate -n cert-manager

# 这是最关键的一步,观察 READY 状态是否为 True:
# 如果显示 True:恭喜你,cert-manager 的核心逻辑(控制器、CA 注入、Webhook 校验)全部打通了!
# 如果一直 False 或 Pending:执行以下命令排查"案发现场":
kubectl describe certificate test-certificate -n cert-manager

三、阿里云效

(一)云效代码管理 Codeup

云效代码管理 Codeup,是基于 Git 的代码管理平台。

(二)容器镜像服务 ACR

ACR 是连接代码与运行环境的关键中转站。

(三)CI/CD 流水线

1、流水线源 - 这是 CI 的起点。当你提交代码到 master 分支时,触发流水线
2、镜像构建 - CI 的核心步骤。系统将代码进行编译、测试,并打包成 Docker 镜像 推送至仓库


3、持续部署 - 这里的"主机部署"节点将 CI 阶段生成的镜像(或者构建物)自动化地发布到你的服务器或 k3s 集群中。
复制代码
# 1. 进入 Chart 目录
cd /root/zhixu

# 2. 修改后的 Helm 命令
# --install 后面的名字改为你已有的: zhixu-release
# --namespace 后面的空间改为你已有的: zhixu
# 使用你在变量页面配置的参数
# ${DATETIME} 是系统内置变量,${IMAGE_REPO} 和 ${NAMESPACE} 是你刚才新建的变量
helm upgrade --install zhixu-release . \
  --namespace ${NAMESPACE} \
  --set "image.repository=${IMAGE_REPO}" \
  --set "image.tag=${DATETIME}" \
  --wait


四、注意事项

(一)容器镜像仓库认证

生成 docker login 指令了,是不是要在云服务器上安装docker

复制代码
# 这是一个非常关键的架构问题。简单直接的回答是:不需要安装完整的 Docker,也不建议让它与 K3s 并列运行。
# K3s 自带了一个轻量级的容器运行时叫 containerd。它就像是 Docker 的"引擎核心",能直接识别和运行 Docker 镜像。

1. 为什么不需要安装 Docker?
K3s 自带了一个轻量级的容器运行时叫 containerd。它就像是 Docker 的"引擎核心",能直接识别和运行 Docker 镜像。

现在的 K3s:已经具备了下载、解压、运行镜像的所有能力。

如果你装了 Docker:它会额外消耗服务器的内存和 CPU,而且 Docker 管理的镜像和 K3s 管理的镜像是不互通的,容易造成资源浪费和管理混乱。

2. 那刚才生成的 docker login 指令怎么用?
你生成的指令是用来提取账号密码的,而不是真的要在服务器上敲 docker login。

由于你没有安装 Docker,你无法直接运行这个命令。你需要做的是从那串长指令里把"用户名(Username)"和"密码(Password)"抠出来,然后交给 K3s 管理。

3. 如何把凭证交给 K3s?(核心操作)
请观察你生成的 docker login 指令,格式应该是这样的: docker login -u [用户名] -p [密码] [地址]

接下来,请直接在 K3s 服务器上执行这条 kubectl 命令(无需安装 Docker):


kubectl create secret docker-registry huawei-swr-secret \
  --docker-server=swr.cn-north-4.myhuaweicloud.com \
  --docker-username=你抠出来的用户名 \
  --docker-password=你抠出来的密码 \
  --namespace=default


注:huawei-swr-secret 是这个凭证的名字,你可以随便起,但后面的部署脚本里要用到。

4. 万一你想在服务器上手动拉镜像测试怎么办?
如果你确实想在命令行里手动操作镜像(比如查看镜像层),K3s 提供了一个内置工具叫 ctr 或更推荐的 crictl。

查看镜像:sudo crictl images

拉取镜像(带凭证):sudo crictl pull --user [用户名]:[密码] [镜像地址]

(二)排查故障常用命令

1、基础查看
复制代码
# 查看命名空间。Namespace 是集群内的逻辑隔离(类似文件夹)
kubectl get ns

# 查看全集群所有的 Pod。-A 代表 --all-namespaces。
kubectl get pods -A

# 查看服务(Service)。主要看 PORT(S),确认你的业务暴露在哪个端口。
kubectl get svc -A
2、深度诊断
复制代码
# 当你发现 Pod 状态不是 Running 时,第一时间用这个
# 看 Events 部分:它会记录 Pod 生命周期的所有大事(拉取镜像成功、挂载磁盘失败、调度失败等)
# 看 State 部分:如果重启了,这里会显示上一次退出的原因(Exit Code)
kubectl describe pod <pod名称> -n <命名空间>
3、实时日志
复制代码
# 当 Pod 状态是 Running 但业务不正常(比如 404 或连接拒绝)时使用
# -f 是 follow,实时滚动日志,像 tail -f
# --tail 100:只看最后 100 行
# -p (previous):非常重要! 如果 Pod 重启了,用这个看崩溃前的最后日志
kubectl logs -f <pod名称> -n <命名空间>
4、进阶排查
复制代码
# 进入容器内部
# 进去后可以尝试 ping 数据库,或者查看容器内的配置文件
kubectl exec -it <POD_NAME> -n <NAMESPACE> -- /bin/sh

# 按状态排序,快速找到非 Running 的 Pod
kubectl get pods -A --field-selector=status.phase!=Running
5、常用排查"组合拳"
复制代码
# 1、按状态排序,快速找到非 Running 的 Pod
kubectl get pods -A --field-selector=status.phase!=Running

# 2、如果 Pod 状态是 Pending、ErrImagePull 或 ContainerCreating:
# 排查套路:
# Events 提示 "Nodes are available: 1 Insufficient cpu" -> 服务器没资源了。
# Events 提示 "failed to pull image" -> 网络不通或镜像名错了。
kubectl describe pod <POD_NAME> -n <NAMESPACE>

# 3、如果状态是 CrashLoopBackOff 或 0/1 Running
# 日志提示 "Connection refused to database" -> 数据库配置错了。
# 日志提示 "Out of memory" -> 容器内存给少了。

# 查看当前日志
kubectl logs <POD_NAME> -n <NAMESPACE>

# 查看崩溃前的日志(这是找 Bug 的核心)
kubectl logs <POD_NAME> -n <NAMESPACE> -p

故障排查逻辑图

现象 可能原因 检查手段
Pending 资源不足、标签不匹配、磁盘挂载失败 describe pod
ErrImagePull 网络不行、镜像地址写错、加速器挂了 describe pod
CrashLoopBackOff 启动脚本报错、配置文件缺失、权限不足 logs -p
0/1 Ready 就绪检查(Readiness)失败、程序启动太慢 describe pod 和 logs

(三) 系统资源查看常用命令

复制代码
# 1、内存查看
# 这两个命令本质一样,区别在于单位。-m 以 MB 为单位,-h (human-readable) 会自动转换成 GB/MB,更易读。

free -h
free -m

# 关键字段解读:
# total: 物理内存总量。
# used: 已经使用的内存。
# free: 完全未被分配的空闲内存。
# buff/cache: 缓存/缓冲。这是 Linux 的智能设计,它把闲置内存用来加速磁盘读写。注意: 当应用需要内存时,这部分会瞬间释放,所以不要把它看作"已占用"。
# available: 最重要的数据。它代表了系统还能给新程序(如你的 Kuboard)分配多少内存。
# 排查经验: 如果 available 接近 0,系统会启动 OOM Killer 机制,随机杀掉占用内存高的进程(通常是 K3s 或 Java 应用)。

# 2、磁盘查看
# df 代表 Disk Free。用于查看磁盘分区的挂载情况和剩余空间。

df -h

# 关键字段解读:
# Size: 分区总大小。
# Avail: 剩余可用空间。
# Use%: 使用百分比。如果达到 90% 以上,就要警惕了。
# Mounted on: 挂载点。
# /: 根目录,系统运行的核心。
# /var/lib/rancher/k3s: K3s 的镜像和 Pod 数据通常存放在这,最容易满。
# 排查经验: 如果磁盘 100% 满了,K3s 会停止调度 Pod,容器的日志也无法写入,甚至会导致数据库损坏。

# 3、查看 CPU 核心数
lscpu

# 4、网络状态查看
# 用途:查看哪些端口被哪些进程占用了
# 排查手段: 如果你打不开 Kuboard,执行 ss -ntlp | grep 30080,看有没有输出。如果没有,说明端口根本没打开。
ss -ntlp

# 5、性能综合监控
# 查看哪些进程最占 CPU 和内存
top

# 6、针对 K3s 的"资源体检"流程

# 看整个节点的资源消耗
kubectl top node

# 看是哪个 Pod 在偷偷吃内存
ubectl top pod -A
相关推荐
czxyvX2 小时前
008-Linux命令行参数和环境变量
linux
何中应2 小时前
虚拟机内的系统无法解析外网域名
linux·运维·后端
红叶尽染寂绀蓝2 小时前
已解决:同一ip下,Mac和Windows同时用vscode连接某个Linux远程服务器后,Mac再次连接时无法正确打开远程文件夹,由于转发设置导致
linux·服务器·windows·vscode·tcp/ip·macos
哈哈浩丶2 小时前
LK(little kernel)2:官方LK的通用启动流程
linux·驱动开发
大尚来也2 小时前
CI/CD 流水线搭建实战:GitHub Actions vs GitLab CI 2026 深度对比与选型指南
ci/cd·gitlab·github
_OP_CHEN2 小时前
【Linux系统编程】(三十四)初识进程信号:Linux 软中断的核心奥秘
linux·后端·操作系统·进程·信号·终端信号
盟接之桥2 小时前
盟接之桥说制造:制造业的精致之道,致制造人
大数据·linux·运维·人工智能·windows·安全·制造
夏乌_Wx2 小时前
从零开始实现一个自己的 Shell:mybash 项目实战
linux·c语言·后端