在云原生时代,即便是个性化的小型项目或微服务,我们也希望拥有自动化的证书管理(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