1. 项目概述:为什么选择 k3s 与 Docker 的组合?
如果你正在寻找一种能在边缘设备、开发机或者资源有限的服务器上快速搭建一个轻量级 Kubernetes 集群的方案,那么k3s绝对是一个绕不开的名字。它由 Rancher Labs 出品,本质上是一个经过高度精简和优化的 Kubernetes 发行版,去掉了很多传统 K8s 中用于支撑超大规模、高可用场景的组件,比如 etcd(默认使用内置的 SQLite)、云控制器管理器等。这使得它的二进制文件极小,启动飞快,内存和 CPU 占用极低,对硬件的要求非常友好。
而当我们谈论在 k3s 中运行工作负载时,容器运行时是一个核心组件。虽然 k3s 默认集成了containerd作为其容器运行时,但在很多实际场景中,我们可能更倾向于使用Docker。原因很简单:Docker 拥有更庞大、更成熟的生态,其命令行工具(CLI)对于开发者而言更为熟悉和直观,围绕 Docker 构建的 CI/CD 流水线、镜像构建流程和运维经验也更为丰富。将 k3s 的容器运行时从 containerd 切换为 Docker,意味着你可以继续使用你熟悉的docker build、docker push以及docker logs等命令来直接管理容器,同时又能享受到 k3s 带来的轻量级 Kubernetes 编排能力。
因此,“k3s部署-docker版”这个项目,核心目标就是在单节点或多节点环境中,部署一个使用 Docker 作为容器运行时的 k3s 集群。这尤其适合以下人群:Kubernetes 初学者希望有一个更贴近传统容器体验的学习环境;中小团队需要在开发测试环境或资源受限的生产边缘节点快速搭建可用的 K8s 集群;以及那些已有深厚 Docker 工具链积累,希望平滑过渡到 Kubernetes 的团队。
2. 环境准备与前置条件解析
在开始部署之前,确保你的基础环境是正确和干净的,这能避免至少 80% 的后续问题。我强烈建议在一个全新的 Linux 虚拟机或服务器上开始,如果条件有限,也务必做好现有环境的清理工作。
2.1 系统要求与依赖检查
k3s 对系统的要求非常宽松,但为了获得最佳体验,建议遵循以下规范:
- 操作系统:主流的 Linux 发行版均可,例如 Ubuntu 20.04/22.04 LTS、CentOS 7/8、Rocky Linux 8/9 等。我个人更推荐 Ubuntu,因为其软件源和社区支持非常活跃,遇到问题更容易找到解决方案。
- 硬件资源:这是 k3s 的优势所在。对于一个仅用于学习和功能验证的单节点集群,1核 CPU、1GB 内存的机器就足以运行 k3s 核心组件及一些轻量级应用。如果计划运行数据库、监控系统等,建议至少 2核2GB。对于生产边缘场景,请根据实际工作负载评估。
- 网络与防火墙:确保节点之间(如果是多节点)的特定端口可以互通。k3s server(控制平面)默认使用 6443 端口,节点间通信使用 8472 端口(Flannel VXLAN)。如果开启了防火墙(如
ufw或firewalld),需要放行这些端口。在单节点部署中,本地回环通信即可,防火墙配置相对简单。
一个必须的前置依赖是内核模块。k3s 和 Docker 都需要一些特定的内核模块来支持网络和存储功能。运行以下命令来加载它们,并设置为开机自动加载:
sudo modprobe overlay sudo modprobe br_netfilter为了让这些模块持久化,可以创建配置文件:
cat <<EOF | sudo tee /etc/modules-load.d/k3s.conf overlay br_netfilter EOF此外,还需要配置sysctl参数,这是为了让容器内的网络能正常工作(例如服务发现、端口映射):
cat <<EOF | sudo tee /etc/sysctl.d/k3s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 net.ipv4.ip_forward = 1 EOF sudo sysctl --system2.2 清理潜在的运行时冲突
这是部署“Docker版”k3s 最关键的一步。如果你的系统上已经安装了旧版本的 Docker、containerd 或者尝试过其他 Kubernetes 发行版(如 kubeadm 安装的集群),必须进行彻底清理,否则端口、套接字和配置文件的冲突会导致安装失败或行为异常。
执行以下清理脚本(适用于基于 systemd 的系统,如 Ubuntu):
# 停止并禁用可能存在的 k3s 服务 sudo systemctl stop k3s 2>/dev/null sudo systemctl disable k3s 2>/dev/null sudo rm -f /etc/systemd/system/k3s.service # 清理 k3s 相关文件和目录 sudo rm -rf /var/lib/rancher/k3s sudo rm -rf /etc/rancher/k3s # 彻底清理 Docker(如果已安装) sudo systemctl stop docker docker.socket containerd 2>/dev/null sudo systemctl disable docker docker.socket containerd 2>/dev/null sudo apt-get remove --purge docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -y # Ubuntu/Debian # 对于 CentOS/RHEL: sudo yum remove docker-ce docker-ce-cli containerd.io -y # 清理 Docker 相关文件和目录 sudo rm -rf /var/lib/docker sudo rm -rf /var/lib/containerd sudo rm -rf /etc/docker # 清理可能存在的 Kubernetes 组件 sudo kubeadm reset -f 2>/dev/null sudo rm -rf $HOME/.kube # 重启系统(可选但推荐,确保所有残留进程被清除) sudo reboot注意:
rm -rf命令是破坏性的,请确保你了解其含义,并在执行前确认目录无误。清理后重启系统是一个好习惯,能确保所有旧的进程和挂载点被彻底释放。
3. Docker 运行时安装与配置
在部署 k3s 之前,我们需要先安装 Docker。这里我们选择 Docker 官方提供的安装脚本,它兼容性好,能自动处理不同发行版的依赖。
3.1 安装 Docker Engine
连接到你的服务器,执行以下命令:
# 下载并执行 Docker 官方安装脚本 curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh这个脚本会自动检测你的操作系统,添加 Docker 的官方软件源,并安装最新稳定版的 Docker Engine、CLI、containerd 和 Docker Compose 插件。
安装完成后,启动 Docker 服务并设置为开机自启:
sudo systemctl start docker sudo systemctl enable docker验证 Docker 是否安装成功,运行一个测试容器:
sudo docker run hello-world如果能看到 “Hello from Docker!” 等欢迎信息,说明 Docker 安装和运行正常。
3.2 配置 Docker 镜像加速与存储驱动
在国内环境,直接从 Docker Hub 拉取镜像速度可能很慢。我们需要配置镜像加速器。这里以阿里云镜像加速服务为例(你需要先注册阿里云账号,进入容器镜像服务获取专属加速器地址)。
编辑 Docker 的守护进程配置文件:
sudo tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://your-own-mirror.mirror.aliyuncs.com"], "exec-opts": ["native.cgroupdriver=systemd"], "log-driver": "json-file", "log-opts": { "max-size": "100m" }, "storage-driver": "overlay2" } EOF关键参数解析:
registry-mirrors:将your-own-mirror.mirror.aliyuncs.com替换为你从阿里云控制台获取的实际加速器地址。这能极大提升镜像拉取速度。exec-opts:native.cgroupdriver=systemd是至关重要的一步。Kubernetes 推荐使用systemd作为 cgroup 驱动,以保持与系统其他服务的一致性。Docker 默认的cgroupfs可能与 k3s 产生不兼容,导致节点状态异常。storage-driver:overlay2是目前 Linux 上推荐且性能较好的存储驱动。
配置完成后,重新加载配置并重启 Docker:
sudo systemctl daemon-reload sudo systemctl restart docker再次运行sudo docker info | grep -i cgroup,确认Cgroup Driver显示为systemd。
4. 部署 k3s 并集成 Docker 运行时
现在,我们来到了核心步骤:安装 k3s,并告诉它使用我们刚才安装好的 Docker,而不是它自带的 containerd。
4.1 单 Server 节点部署
在目标机器上,执行以下一键安装命令:
curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--docker" sh -这个命令做了以下几件事:
- 下载
k3s安装脚本。 - 通过环境变量
INSTALL_K3S_EXEC向安装脚本传递参数--docker。这个参数就是灵魂所在,它指示 k3s 使用 Docker 作为容器运行时。 - 脚本会自动下载 k3s 二进制文件,将其安装为系统服务(
k3s.service),并使用--docker参数启动该服务。
安装过程大约持续一两分钟。完成后,检查服务状态:
sudo systemctl status k3s你应该看到服务处于active (running)状态。如果失败,可以查看日志排错:sudo journalctl -u k3s -f。
安装成功后,k3s 会自动生成一个 kubeconfig 文件,位于/etc/rancher/k3s/k3s.yaml。为了能使用kubectl命令,我们需要将其复制到用户目录下并设置权限:
mkdir -p ~/.kube sudo cp /etc/rancher/k3s/k3s.yaml ~/.kube/config sudo chown $(id -u):$(id -g) ~/.kube/config现在,你可以验证集群了:
kubectl get nodes kubectl get pods -A你应该能看到一个Ready状态的节点,以及kube-system命名空间下运行着coredns、metrics-server等核心组件。关键点在于,你可以用docker ps命令看到这些 Pod 对应的容器正在运行,这证实了 k3s 确实在使用 Docker 运行时。
4.2 多节点集群部署(Server + Agent)
生产环境通常需要多节点以保证高可用。k3s 的多节点部署也非常简单,它区分Server(控制平面)节点和Agent(工作)节点。
第一步:初始化第一个 Server 节点在第一台机器上,我们不仅指定--docker,还需要生成一个节点令牌(token),供其他节点加入集群时使用。
curl -sfL https://get.k3s.io | K3S_TOKEN=my-secret-token INSTALL_K3S_EXEC="--docker" sh -这里K3S_TOKEN可以自定义一个复杂的字符串,例如my-secret-token。安装完成后,在第一个 Server 节点上,你可以找到两个重要的信息:
- 节点令牌:我们已经在环境变量里定义了。它也会被保存在
/var/lib/rancher/k3s/server/node-token文件中。 - Server 节点IP:记下这台机器的 IP 地址,假设为
192.168.1.100。
第二步:添加其他 Server 节点(可选,用于高可用)在第二台希望作为 Server 节点的机器上,执行以下命令。它需要知道第一个 Server 的地址和令牌。
curl -sfL https://get.k3s.io | K3S_TOKEN=my-secret-token K3S_URL=https://192.168.1.100:6443 INSTALL_K3S_EXEC="--docker" sh -第三步:添加 Agent 节点在工作节点上,安装命令与添加 Server 节点类似,但需要额外指定K3S_URL并明确角色:
curl -sfL https://get.k3s.io | K3S_TOKEN=my-secret-token K3S_URL=https://192.168.1.100:6443 INSTALL_K3S_EXEC="--docker --node-ip <此节点的IP>" sh -<此节点的IP>替换为当前工作节点的 IP。在某些网络环境下(如云服务器有多块网卡),明确指定--node-ip可以避免节点注册时使用错误的 IP 地址。
在所有节点安装完成后,回到第一个 Server 节点,执行kubectl get nodes,应该能看到所有节点都处于Ready状态。
4.3 关键配置解析与自定义
安装命令中的参数只是冰山一角。k3s 提供了丰富的配置选项,可以通过修改其配置文件/etc/rancher/k3s/config.yaml来实现更精细的控制。例如,我们想修改默认的服务 CIDR 和 Pod CIDR:
# /etc/rancher/k3s/config.yaml write-kubeconfig-mode: "0644" tls-san: - "k3s.mycompany.com" # 添加 TLS 证书的备用名称,方便外部访问 API Server cluster-cidr: "10.42.0.0/16" # Pod 网络 CIDR,默认值 service-cidr: "10.43.0.0/16" # Service 网络 CIDR,默认值 cluster-dns: "10.43.0.10" # CoreDNS 服务地址 docker: true # 明确指定使用 Docker 运行时,与 --docker 参数等效 node-ip: "192.168.1.100" # 绑定特定 IP修改配置文件后,需要重启 k3s 服务以生效:
sudo systemctl restart k3s实操心得:对于生产环境,务必在
tls-san中配置你的负载均衡器 IP 或域名,否则通过该地址访问 API Server 会报证书错误。cluster-cidr和service-cidr不要与你的物理网络段冲突。
5. 部署验证与基础应用部署
集群搭建好了,我们得验证它是否真的在“健康”地工作,并且体验一下如何使用它。
5.1 集群状态深度检查
除了简单的kubectl get nodes,我们还需要进行更深入的检查:
检查核心组件:
kubectl get pods -n kube-system确保
coredns、metrics-server、local-path-provisioner(k3s 自带的存储类)等 Pod 都是Running状态。如果coredns一直Pending或ContainerCreating,很可能是网络插件(Flannel)有问题,或者节点内存不足。检查 Docker 运行时集成:
# 查看 k3s 启动参数,确认 --docker 已生效 sudo ps aux | grep k3s-server | grep docker # 查看 kubelet 的配置 sudo cat /var/lib/rancher/k3s/agent/etc/kubelet.conf | grep -A5 -B5 docker更直观的方法是,部署一个测试 Pod,然后去 Docker 里看容器:
kubectl run test-docker --image=nginx:alpine # 等待 Pod 变为 Running kubectl get pods -l run=test-docker # 在宿主机上,你应该能看到这个 nginx 容器 sudo docker ps | grep nginx检查网络:
# 部署一个简单的网络测试应用 kubectl create deployment net-test --image=appropriate/curl kubectl rollout status deployment/net-test # 进入 Pod 内部,测试集群内 DNS 和服务发现 kubectl exec deployment/net-test -- curl -s http://kubernetes.default如果能成功返回 Kubernetes API 的响应,说明 Pod 网络、DNS 和服务发现都是正常的。
5.2 部署第一个有状态应用:MySQL
让我们部署一个稍微复杂点的应用,比如 MySQL,这涉及到配置、存储和密码管理。
创建 Secret 存储密码(切勿将密码明文写在 YAML 里):
kubectl create secret generic mysql-secret \ --from-literal=mysql-root-password=your-strong-password-here创建 PersistentVolumeClaim (PVC),向 k3s 自带的
local-path存储类申请存储空间:# mysql-pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pvc spec: accessModes: - ReadWriteOnce storageClassName: local-path resources: requests: storage: 5Gikubectl apply -f mysql-pvc.yaml创建 MySQL Deployment 和 Service:
# mysql-deployment.yaml apiVersion: apps/v1 kind: Deployment metadata: name: mysql spec: selector: matchLabels: app: mysql template: metadata: labels: app: mysql spec: containers: - name: mysql image: mysql:8.0 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-secret key: mysql-root-password ports: - containerPort: 3306 volumeMounts: - name: mysql-storage mountPath: /var/lib/mysql volumes: - name: mysql-storage persistentVolumeClaim: claimName: mysql-pvc --- apiVersion: v1 kind: Service metadata: name: mysql-service spec: selector: app: mysql ports: - protocol: TCP port: 3306 targetPort: 3306 type: ClusterIPkubectl apply -f mysql-deployment.yaml验证:
kubectl get pods -l app=mysql # 查看 Pod 状态 kubectl get pvc # 查看 PVC 是否 Bound kubectl get svc mysql-service # 查看 Service 的集群内 IP # 进入 Pod 测试 MySQL 连接 kubectl exec -it deployment/mysql -- mysql -uroot -p$MYSQL_ROOT_PASSWORD -e "SHOW DATABASES;"
这个例子展示了在 k3s 中如何管理敏感信息、使用持久化存储和部署有状态服务,这些都是日常运维中的常见操作。
6. 运维、监控与故障排查实录
集群跑起来只是开始,日常的运维和问题排查才是重头戏。
6.1 常用运维命令与日志查看
服务管理:
sudo systemctl status k3s # 查看 k3s 服务状态 sudo systemctl restart k3s # 重启 k3s(谨慎操作) sudo systemctl stop k3s # 停止 k3s sudo systemctl start k3s # 启动 k3s日志查看:
# 查看 k3s 服务日志(实时追踪) sudo journalctl -u k3s -f # 查看 kubelet 日志(容器生命周期相关) sudo journalctl -u k3s-agent -f # 查看特定 Pod 的日志 kubectl logs <pod-name> -n <namespace> kubectl logs -f <pod-name> -n <namespace> --tail=50 # 实时追踪资源清理:
# 删除所有非 default 命名空间的资源(危险!) kubectl delete namespace <namespace-name> # 强制删除卡在 Terminating 状态的命名空间 kubectl get namespace <namespace-name> -o json | jq '.spec.finalizers = []' | kubectl replace --raw "/api/v1/namespaces/<namespace-name>/finalize" -f -
6.2 集成基础监控:Prometheus 与 Grafana
监控是生产环境的眼睛。在 k3s 上部署一套 Prometheus + Grafana 非常简单,我们可以使用 Helm 包管理器。
安装 Helm:
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash添加 Prometheus 社区仓库并部署:
helm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update # 创建一个命名空间 kubectl create namespace monitoring # 安装 kube-prometheus-stack,它包含了 Prometheus, Grafana, AlertManager 等全套组件 helm install prometheus prometheus-community/kube-prometheus-stack -n monitoring这个 Chart 配置了自动发现 k3s 集群中的节点、Pod、Service 等目标的规则,开箱即用。
访问 Grafana: 部署完成后,需要将 Grafana 的服务类型改为
NodePort或通过 Ingress 暴露。kubectl patch svc prometheus-grafana -n monitoring -p '{"spec": {"type": "NodePort"}}' kubectl get svc prometheus-grafana -n monitoring找到
prometheus-grafana服务对应的NodePort(例如30892),然后通过http://<你的节点IP>:30892访问 Grafana。默认用户名是admin,密码可以通过以下命令获取:kubectl get secret prometheus-grafana -n monitoring -o jsonpath="{.data.admin-password}" | base64 --decode ; echo导入 Dashboard:登录 Grafana 后,可以导入官方提供的 Kubernetes 监控 Dashboard,ID 如
13105,即可看到丰富的集群监控图表。
6.3 常见问题与排查技巧实录
在实际操作中,你几乎一定会遇到下面这些问题。我把我的踩坑记录分享给你。
问题一:k3s 服务启动失败,日志显示failed to find memory cgroup或类似 cgroup 错误。
- 原因:这是最经典的问题。根本原因是 Docker 的 cgroup 驱动 (
cgroupfs) 与 k3s(或 systemd)期望的驱动 (systemd) 不一致。 - 解决方案:确保 Docker 的
daemon.json中已配置"exec-opts": ["native.cgroupdriver=systemd"],并重启 Docker。然后彻底清理 k3s 并重新安装(参考第 2.2 节的清理步骤)。
问题二:Pod 一直处于Pending状态,kubectl describe pod显示0/1 nodes are available: 1 node(s) had taint {node.kubernetes.io/disk-pressure: }。
- 原因:节点存在磁盘压力污点(Taint),调度器不会将 Pod 调度到该节点。
- 排查与解决:
- 检查节点磁盘使用率:
df -h。 - 清理磁盘空间,例如清理 Docker 无用镜像和容器:
docker system prune -a -f。 - 污点可能会自动移除。也可以手动移除(生产环境谨慎):
kubectl taint nodes <node-name> node.kubernetes.io/disk-pressure-。
- 检查节点磁盘使用率:
问题三:Pod 内无法解析集群服务名称(如mysql-service.default.svc.cluster.local)。
- 原因:CoreDNS Pod 没有正常运行,或者 Pod 的 DNS 配置不正确。
- 排查步骤:
kubectl get pods -n kube-system -l k8s-app=kube-dns检查 CoreDNS Pod 状态。kubectl logs -n kube-system <coredns-pod-name>查看 CoreDNS 日志。- 进入出问题的 Pod,检查
/etc/resolv.conf文件,看 nameserver 是否指向了正确的 ClusterIP(通常是10.43.0.10)。 - 如果 CoreDNS 有问题,尝试重启:
kubectl delete pod -n kube-system -l k8s-app=kube-dns。
问题四:使用docker ps能看到容器,但kubectl get pods显示 Pod 状态异常(如Error/CrashLoopBackOff)。
- 原因:这是 Docker 运行时模式下特有的调试优势。因为容器实际由 Docker 管理,你可以直接用 Docker 命令深入排查。
- 排查技巧:
docker ps -a | grep <pod-name>找到对应容器。docker logs <container-id>查看该容器的标准输出和错误日志,这通常比kubectl logs更早看到启动失败的原因(例如镜像拉取失败、启动命令错误、权限问题等)。docker inspect <container-id>查看容器的详细配置,包括环境变量、挂载卷、网络等,与你的 Pod 定义进行对比。
问题五:从节点(Agent)无法加入主节点(Server)。
- 原因:网络不通、令牌错误或端口未开放。
- 排查清单:
- 网络连通性:在 Agent 节点上执行
telnet <server-ip> 6443或curl -vk https://<server-ip>:6443/ping,检查 6443 端口是否可达。 - 防火墙:确保 Server 节点的防火墙放行了 6443 (TCP) 和 8472 (UDP, Flannel) 端口。
- 令牌:确认 Agent 安装命令中的
K3S_TOKEN与 Server 节点/var/lib/rancher/k3s/server/node-token文件内容一致。 - 查看日志:在 Agent 节点查看
sudo journalctl -u k3s-agent -f获取具体的错误信息。
- 网络连通性:在 Agent 节点上执行
问题六:如何升级 k3s 版本?
k3s 的升级非常平滑。对于使用 Docker 运行时的集群,升级步骤与常规 k3s 一致:
# 在 Server 节点上 curl -sfL https://get.k3s.io | INSTALL_K3S_EXEC="--docker" INSTALL_K3S_VERSION="v1.28.5+k3s1" sh - # 在 Agent 节点上,先 drain 节点(驱逐 Pod),然后升级,最后 uncordon kubectl drain <agent-node-name> --ignore-daemonsets curl -sfL https://get.k3s.io | K3S_TOKEN=my-secret-token K3S_URL=https://<server-ip>:6443 INSTALL_K3S_EXEC="--docker" INSTALL_K3S_VERSION="v1.28.5+k3s1" sh - kubectl uncordon <agent-node-name>核心避坑指南:部署“k3s-docker版”的终极秘诀就是“环境干净,驱动一致”。90% 的诡异问题都源于旧组件残留或 cgroup 驱动冲突。养成在全新环境或彻底清理后的环境开始部署的习惯,能为你节省大量排错时间。另外,善用
docker命令直接操作容器进行深度调试,是这个方案带给运维人员的独特便利。