【教程】从零开始构建最小高可用 k3s 集群

作者序
本文转载自 我的博客站,原文地址: 【教程】从零开始搭建最小高可用 k3s 集群
在一切开始之前,请注意以下事项:
- 本文所使用的开发环境为 ArchLinux + Fish Shell,由于跨系统指令语法不一定完全兼容,请根据自己的实际情况调整指令格式。
- 本文中的指令依赖于各类CLI工具(已在文中标注或是在前置教程中已经安装过,linux系统常见的CLI工具不会在文中标注),由于CLI工具的安装过程较为繁琐且对于各个平台的安装步骤并不一致,此处不再赘述,请参考官方文档的安装教程。
引言
众所周知,k3s 是由 Rancher Labs 开发的轻量级 Kubernetes 发行版,也是目前最流行的 K8s 轻量化方案。
相较于传统运维方式(1Panel/宝塔/SSH 等),k3s 的学习曲线更陡峭,需要理解更多容器编排概念。但一旦掌握,你将获得:
k3s 的核心优势
- 轻量高效 - 单个二进制文件,内存占用 < 512MB,完美适配低配 VPS
- 生产就绪 - 完全兼容 Kubernetes API,可平滑迁移到标准 K8s
- 声明式运维 - 用 YAML 描述期望状态,系统自动维护
- 高可用保障 - 自动故障恢复 + 多节点负载均衡
- 开箱即用 - 内置网络、存储、Ingress 等核心组件
通过 k3s,我们可以将多台廉价 VPS 整合为企业级高可用集群,实现传统运维难以达到的自动化水平。
目标读者与准备
适合人群
- 有一定 Linux 基础的开发者
- 希望从传统运维过渡到容器编排
- 想搭建个人高可用服务的技术爱好者
前置知识
- 熟悉 Linux 命令行操作
- 了解 Docker 容器基础
- 具备基本的网络知识(SSH、防火墙)
学习收获
完成本教程后,你将掌握:
- 使用 k3sup 快速部署 k3s 集群
- 理解 k3s 核心组件(API Server、etcd、kubelet 等)的作用
- 替换默认组件优化性能(Cilium CNI、Nginx Ingress 等)
- 部署第一个应用并配置外部访问
- 基本的集群运维和故障排查技巧
部署规划
k3s 默认安装了一套精简组件,为了满足生产级诉求,我们需要提前规划哪些模块要保留或替换。下表展示了推荐的取舍策略:
| 组件类型 | k3s 默认组件 | 替换组件 | 替换理由 | k3sup 禁用参数 |
|---|---|---|---|---|
| 容器运行时 | containerd | - | 保持默认即可 | - |
| 数据存储 | SQLite / etcd | - | 单节点用 SQLite,集群用 etcd | - |
| Ingress Controller | Traefik | Nginx Ingress / 其他 | 团队更熟悉、功能需求不同 | --disable traefik |
| LoadBalancer | Service LB (Klipper-lb) | 外部负载均衡 | 云厂商(例如Cloudflare)提供的负载均衡更加成熟强大 | --disable servicelb |
| DNS | CoreDNS | - | 保持默认即可 | - |
| Storage Class | Local-path-provisioner | Longhorn | 分布式存储、高可用、备份能力 | --disable local-storage |
| CNI | Flannel | Cilium | eBPF 性能、网络策略、可观测性 | --flannel-backend=none --disable-network-policy |
环境准备
必备工具
搭建集群前需要准备三款 CLI 工具:k3sup、kubectl、Helm。请参考各自的官方文档安装,这里不再展开,安装完成后,可以通过 k3sup version、kubectl version、helm version 等命令进行验证。

服务器要求
准备至少三台云服务器(示例环境使用 Ubuntu 24.04),作为最小化三节点高可用控制平面(推荐配置 ≥ 4C4G)。提前记录各节点 IP、确认 SSH 公钥已分发,并知晓本地私钥路径,后续步骤会用到。
部署初始控制平面
使用 k3sup 部署初始控制节点:
k3sup install \
--ip 初始节点 IP \
--user root \
--ssh-key 密钥位置 \
--k3s-channel latest \
--cluster \
--k3s-extra-args "--disable traefik --disable servicelb --disable local-storage --flannel-backend=none --disable-network-policy"
如图所示:

安装完成后,k3sup 会自动将 kubeconfig 复制到当前目录。虽然可以直接使用这份配置,但更加稳健的做法是与现有配置文件合并。
合并 kubeconfig
- 备份当前的 kubeconfig(默认位于
~/.kube/config):
cp ~/.kube/config ~/.kube/config.backup
fish shell(仅供参考):
cp $KUBECONFIG {$KUBECONFIG}.backup
- 将新旧 kubeconfig 合并为一份扁平文件:
KUBECONFIG=~/.kube/config:./kubeconfig kubectl config view --flatten > ~/.kube/config.new
fish shell(仅供参考):
KUBECONFIG=$KUBECONFIG:./kubeconfig kubectl config view --flatten > kubeconfig-merged.yaml
- 检查新文件内容无误后覆盖旧配置:
mv ~/.kube/config.new ~/.kube/config
fish shell:
mv ./kubeconfig-merged.yaml $KUBECONFIG
- 验证新上下文是否生效:
kubectl config get-contexts
kubectl config use-context default
安装 Cilium(替代 Flannel)
安装 k3s 时我们禁用了默认 CNI(Flannel),因此节点暂时无法互通。按计划部署 Cilium 以提供网络与网络策略能力。
使用 Helm 安装 Cilium:
# 添加 Cilium Helm 仓库
helm repo add cilium https://helm.cilium.io/
# 更新 Helm 仓库
helm repo update
# 安装 Cilium CNI(单副本,默认模式)
helm install cilium cilium/cilium \
--namespace kube-system \
--set operator.replicas=1 \
--set ipam.mode=kubernetes
如果集群已经禁用 kube-proxy,可额外添加
--set kubeProxyReplacement=strict。本教程保持默认值以兼容更多场景。
执行完成后可以看到:

等待 Cilium 组件启动完成:
# 查看 Cilium 相关 Pod 状态
kubectl get pods -n kube-system -l k8s-app=cilium
# 查看节点状态(应该从 NotReady 变为 Ready)
kubectl get nodes

节点切换到 Cilium 后应当从 NotReady 变为 Ready。接下来使用 k3sup 加入另外两个控制节点。
扩容控制平面
为了实现高可用,我们需要至少 3 个控制节点。使用以下命令加入第二个控制节点:
k3sup join \
--ip 第二个节点 IP \
--user root \
--ssh-key 密钥位置 \
--server-ip 初始节点 IP \
--server \
--k3s-channel latest \
--k3s-extra-args "--disable traefik --disable servicelb --disable local-storage --flannel-backend=none --disable-network-policy"
同样的方式加入第三个控制节点:
k3sup join \
--ip 第三个节点 IP \
--user root \
--ssh-key 密钥位置 \
--server-ip 初始节点 IP \
--server \
--k3s-channel latest \
--k3s-extra-args "--disable traefik --disable servicelb --disable local-storage --flannel-backend=none --disable-network-policy"
等待几分钟后检查集群状态:
kubectl get nodes
当三个控制节点均处于 Ready 状态时,控制平面即告搭建完成。

安装 Nginx Ingress Controller
替换 k3s 默认的 Traefik,安装 Nginx Ingress Controller:
# 添加 Nginx Ingress Helm 仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm repo update
# 安装 Nginx Ingress Controller(可能需要等待很久)
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.hostPort.enabled=true \
--set controller.hostPort.ports.http=80 \
--set controller.hostPort.ports.https=443 \
--set controller.service.type=ClusterIP
kubectl get pods -n ingress-nginx
kubectl get svc -n ingress-nginx
由于我们启用了 hostPort,Service 类型会显示为 ClusterIP,不会自动分配云厂商的外部地址,例如:
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller ClusterIP 10.43.25.32 <none> 80/TCP,443/TCP 1d
ingress-nginx-controller-admission ClusterIP 10.43.184.196 <none> 443/TCP 1d
此时入口其实就是每个控制节点本身的公网 IP,可通过 kubectl get nodes -o wide 或直接查询云主机面板来确认,后续在配置 Cloudflare DNS 时填入该 IP 即可。

安装 Longhorn 分布式存储
Longhorn 是由 Rancher 开发的云原生分布式块存储系统,提供高可用、备份、快照等企业级功能。相比 k3s 默认的 local-path-provisioner,Longhorn 支持跨节点的持久化存储。
前置依赖检查
在安装 Longhorn 之前,需要确保每个节点都安装了必要依赖;若希望通过 Ansible 批量处理,可参考后续示例:
# 在每个节点上执行(通过 SSH)
# 检查并安装 open-iscsi
apt update
apt install -y open-iscsi nfs-common
# 启动并设置开机自启
systemctl enable --now iscsid
systemctl status iscsid
如果希望使用 Ansible 批量安装依赖,可以参考以下任务片段:
---
- name: Setup K3s nodes with Longhorn dependencies and CrowdSec
hosts: k3s
become: true
vars:
crowdsec_version: "latest"
tasks:
# ============================================
# Longhorn Prerequisites
# ============================================
- name: Install Longhorn required packages
ansible.builtin.apt:
name:
- open-iscsi # iSCSI support for volume mounting
- nfs-common # NFS support for backup target
- util-linux # Provides nsenter and other utilities
- curl # For downloading and API calls
- jq # JSON processing for Longhorn CLI
state: present
update_cache: true
tags: longhorn
- name: Enable and start iscsid service
ansible.builtin.systemd:
name: iscsid
enabled: true
state: started
tags: longhorn
- name: Load iscsi_tcp kernel module
community.general.modprobe:
name: iscsi_tcp
state: present
tags: longhorn
- name: Ensure iscsi_tcp loads on boot
ansible.builtin.lineinfile:
path: /etc/modules-load.d/iscsi.conf
line: iscsi_tcp
create: true
mode: '0644'
tags: longhorn
- name: Check if multipathd is installed
ansible.builtin.command: which multipathd
register: multipathd_check
failed_when: false
changed_when: false
tags: longhorn
- name: Disable multipathd if installed (conflicts with Longhorn)
ansible.builtin.systemd:
name: multipathd
enabled: false
state: stopped
when: multipathd_check.rc == 0
tags: longhorn
部署 Longhorn
使用 Helm 安装 Longhorn:
# 添加 Longhorn Helm 仓库
helm repo add longhorn https://charts.longhorn.io
helm repo update
# 安装 Longhorn(可能需要等待较长时间),请注意这里为了节约硬盘空间,只设置了单副本,请根据需求自行调整
helm install longhorn longhorn/longhorn \
--namespace longhorn-system \
--create-namespace \
--set defaultSettings.defaultDataPath="/var/lib/longhorn" \
--set persistence.defaultClass=true \
--set persistence.defaultClassReplicaCount=1
等待 Longhorn 组件启动:
# 查看 Longhorn 组件状态
kubectl get pods -n longhorn-system
# 查看 StorageClass
kubectl get storageclass

访问 Longhorn UI(可选)
Longhorn 提供了一个 Web UI 用于管理存储卷。可以通过端口转发临时访问:
# 端口转发到本地
kubectl port-forward -n longhorn-system svc/longhorn-frontend 8081:80
# 在浏览器访问 http://localhost:8081
完成查看后,请在终端按
Ctrl+C停止端口转发,避免持续占用本地端口。
加入 Agent 节点
目前为止,我们搭建了一个 3 节点的高可用控制平面(control-plane)。在生产环境中,我们不希望在 control-plane 节点上运行实际的应用程序(会占用 API Server、etcd 等核心组件的资源)。
因此,我们需要加入专门用于运行工作负载(Pods)的 Agent 节点(也称为 Worker 节点)。
安装前置依赖
与 control-plane 节点一样,Agent 节点也需要满足 Longhorn 的依赖(若希望这些节点能够调度并存储持久卷)。
在所有准备加入的 Agent 节点上,提前执行以下命令:
# 在每个 Agent 节点上执行(通过 SSH)
apt update
apt install -y open-iscsi nfs-common
# 启动并设置开机自启
systemctl enable --now iscsid
执行加入命令
添加 Agent 节点的命令与添加 control-plane 节点几乎一致,但有两个关键区别:不使用 --server 标记且无需额外参数。
k3sup join \
--ip <AGENT_节点 IP> \
--user root \
--ssh-key <密钥位置> \
--server-ip <任意 Control 节点 IP> \
--k3s-channel latest
验证节点状态
可以一次性加入多个 Agent 节点。添加完成后等待几分钟,Cilium 与 Longhorn 的组件会自动调度到新节点。
使用 kubectl 查看集群状态:
kubectl get nodes -o wide
你应该能看到新加入的节点,其 ROLE 列显示为 <none>(在 k3s 中,<none> 即代表 agent/worker 角色)。
同时,你可以监控 Cilium 和 Longhorn 的 Pod 是否在新节点上成功启动:
# Cilium agent 应该会在新节点上启动
kubectl get pods -n kube-system -o wide
# Longhorn instance-manager 应该也会在新节点上启动
kubectl get pods -n longhorn-system -o wide
至此,集群已经拥有了高可用的控制平面和用于运行应用的工作节点。
安装 Argo CD(GitOps 持续交付)
为了让集群具备声明式持续交付能力,我们可以安装 Argo CD,它能够将 Git 仓库中的 Kubernetes 清单自动同步到集群,配合前文部署的 Cilium、Ingress、Longhorn 等组件即可构成完整的 GitOps 流程。
安装步骤
# 添加 Argo Helm 仓库并更新索引
helm repo add argo https://argoproj.github.io/argo-helm
helm repo update
# 在 argocd 命名空间中安装 Argo CD(默认使用 ClusterIP Service)
helm install argocd argo/argo-cd \
--namespace argocd \
--create-namespace \
--set controller.replicas=2 \
--set redis-ha.enabled=false \
--set server.service.type=ClusterIP
上述参数含义如下:
argocd:安装 release 的名称,后续升级/卸载都依赖这个名字。argo/argo-cd:使用argoHelm 仓库中的argo-cdchart。--namespace argocd:将所有资源安装到argocd命名空间。--create-namespace:若命名空间不存在则自动创建。--set controller.replicas=2:将 Argo CD 控制器副本数设为 2,提升高可用能力。--set redis-ha.enabled=false:关闭 Redis HA,默认单实例即可满足多数开发/测试场景。--set server.service.type=ClusterIP:server 服务使用 ClusterIP 类型,后续我们通过 Ingress 暴露对外访问。
安装完成后等待所有组件就绪:
kubectl get pods -n argocd
你应该能看到类似如下的输出,表明核心组件均已成功启动:
NAME READY STATUS RESTARTS AGE
argocd-application-controller-0 1/1 Running 0 30s
argocd-application-controller-1 1/1 Running 0 17s
argocd-applicationset-controller-6bf5957996-xnn7c 1/1 Running 0 30s
argocd-dex-server-7cb4b74df8-vqkdv 1/1 Running 0 30s
argocd-notifications-controller-5cbffcc56d-9gntp 1/1 Running 0 30s
argocd-redis-b5f4d9475-584fs 1/1 Running 0 30s
argocd-redis-secret-init-sggs2 0/1 Completed 0 47s
argocd-repo-server-7687bd88c6-4ksfp 1/1 Running 0 30s
argocd-server-67ccc4d44c-6th5p 1/1 Running 0 30s
其中:
argocd-application-controller-*:负责同步 Application 资源状态,我们设置了两个副本以提高可用性。argocd-applicationset-controller:负责 ApplicationSet CRD 的编排。argocd-dex-server:提供 Dex 身份认证服务。argocd-notifications-controller:实现通知与告警能力。argocd-redis-*:Argo CD 的内置 Redis 用于缓存集群状态(redis-secret-initJob 初始化密钥后处于Completed状态属于正常现象)。argocd-repo-server:处理 Git 仓库同步与模板渲染。argocd-server:提供 Web/UI 与 gRPC API。
所有 Pod 状态为 Running 或 Completed 即表示 Argo CD 安装成功,可以继续配置访问入口或应用同步。
访问 Web UI
由于我们已经部署了 ingress-nginx,可通过 Ingress 暴露 Argo CD UI(将 argocd.example.com 换成自己的域名或临时 hosts):
# argocd-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: argocd-server-ingress
namespace: argocd
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS"
spec:
ingressClassName: nginx
rules:
- host: argocd.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: argocd-server
port:
number: 443
kubectl apply -f argocd-ingress.yaml
第一次登录需要使用初始管理员密码,可通过以下命令获取并自行修改:
kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 --decode
随后访问 https://argocd.example.com 并使用用户名 admin 登录即可。建议在首次登录后立即更改密码或配置 SSO,并通过 Git 仓库创建第一个 Application,开启你的 GitOps 工作流。
如果你使用 Cloudflare 托管域名,可以按以下步骤完成域名解析:
- 将 Ingress 示例中的
argocd.example.com替换为自己在 Cloudflare 上托管的真实域名 - 在 Cloudflare 控制台 → DNS → 添加一条
A记录,名称为上述子域,值填写集群入口的公网 IP。若 Ingress Controller 启用了hostPort,入口就是任一控制节点的公网 IP;若使用外部负载均衡,则填对应的负载均衡地址。 - 视需求选择是否启用 Cloudflare 代理(橙色云朵)。开启后建议为 Argo CD 配置受信任证书(例如使用 cert-manager 申请 Let’s Encrypt)。
- 完成解析后,等待 DNS 生效即可通过 Cloudflare 提供的域名访问 Argo CD UI。如需自动管理解析,可结合 external-dns + Cloudflare API Token 实现全自动的 GitOps 域名同步。
下一步
【教程】从零开始构建企业级高可用 PostgreSQL 集群