K3s 轻量级 Kubernetes 从零到生产实战

主题:在单节点华为云 ECS 上部署 K3s v1.35.5,并通过真实 Demo 应用完成全流程实战
核心理念:轻量不等于功能缩水,K3s 用 512MB 内存跑完整 K8s
实战环境:华为云 ecs-6ce9-0004 (2vCPU/4GiB/Ubuntu 24.04, Docker 29.5.3)
深度踩坑:containerd 双实例、镜像导入、NodePort 503 排查


目录


第一部分:K3s 是什么?——轻量级 K8s 的选型之道

1.1 什么是 K3s?

K3s 是由 Rancher Labs(现属 SUSE)维护的 CNCF 沙箱项目,全称 “Kubernetes minus the fat”(减去臃肿的 K8s)。

┌──────────────────────────────────────────────────────┐
│                     K8s 的组成部分                     │
├──────────────────────────────────────────────────────┤
│  kube-apiserver  │  etcd  │  kube-controller-manager  │
│  kube-scheduler  │  kubelet  │  kube-proxy  │  CNI   │
│  CRI (containerd) │  CoreDNS  │  云提供商插件(CCM)     │
│  ... 以及大量 alpha/beta 特性、不再需要的老 API ...     │
└──────────────────────────────────────────────────────┘
                              ↓
                      "减去不必要的部分"
                              ↓
┌──────────────────────────────────────────────────────┐
│                     K3s 的组成部分                     │
├──────────────────────────────────────────────────────┤
│  ★ 单一二进制(< 100MB)包含所有核心组件               │
│  ★ SQLite 替代 etcd(默认)                           │
│  ★ 内置 containerd(替代 Docker CRI)                 │
│  ★ 内置 Flannel CNI + CoreDNS                         │
│  ★ 内置 Traefik Ingress Controller                    │
│  ★ 内置 local-path-provisioner(本地存储)              │
│  ★ 移除所有 alpha 特性和云提供商插件                   │
└──────────────────────────────────────────────────────┘

1.2 为什么选择 K3s?

维度 K3s 标准 K8s (kubeadm)
二进制大小 < 100MB 多组件累计 > 1GB
最小内存 512MB 2GB+
安装方式 一条 curl 命令 kubeadm init + 多步配置
默认存储 SQLite(嵌入式) etcd(强一致分布式)
默认 CNI Flannel 无(需自行安装)
默认 Ingress Traefik 无(需自行安装)
证书管理 自动轮换 手动管理
适用场景 Edge/IoT/单机开发 生产多节点集群
高可用 支持嵌入式 etcd 或外部 DB 原生支持

1.3 K3s 典型应用场景

IoT / 边缘计算         CI/CD 测试环境        开发桌面环境
    ┌──┐                  ┌──┐                  ┌──┐
    │RPi│                  │ECS│                  │Mac│
    └──┬──┘               └──┬──┘               └──┬──┘
       │                     │                     │
  ┌────▼────┐          ┌────▼────┐          ┌────▼────┐
  │  K3s    │          │  K3s    │          │  K3s    │
  │ Agent   │          │ Server  │          │ Server  │
  │ (ARM)   │          │ 单节点   │          │ (本地)   │
  └─────────┘          └─────────┘          └─────────┘
  传感器数据采集        快速创建/销毁         本地开发和测试

第二部分:环境准备与 K3s 安装

2.1 服务器规格

项目 参数
实例规格 ac9.large.2(2vCPU / 4GiB)
操作系统 Ubuntu 24.04.4 LTS
内核版本 6.8.0-106-generic
磁盘 40GB SSD(已用 6.1G)
公网 IP 120.46.154.100
内网 IP 192.168.0.144
Docker 29.5.3(overlay2,Cgroup v2)

2.2 Docker 环境准备

# Step 1:安装 Docker(已配置阿里云镜像加速)
cat > /etc/docker/daemon.json << 'EOF'
{
  "registry-mirrors": [
    "https://docker.1ms.run",
    "https://docker.xuanyuan.me",
    "https://mirrors.aliyun.com"
  ],
  "log-driver": "json-file",
  "log-opts": {"max-size": "100m", "max-file": "3"}
}
EOF

systemctl restart docker
docker info --format "{{.ServerVersion}}"
# 输出:29.5.3

2.3 K3s 一键安装

这是 K3s 最核心的优势——一条命令完成安装

# 使用 Rancher 国内镜像加速(国内环境必须!)
curl -sfL https://rancher-mirror.rancher.cn/k3s/k3s-install.sh | \
  INSTALL_K3S_MIRROR=cn sh -

参数说明

参数 含义
-sfL s=静默, f=失败时输出错误, L=跟随重定向
rancher-mirror.rancher.cn Rancher 中国镜像站(直连 GitHub 极慢)
INSTALL_K3S_MIRROR=cn 告诉安装脚本从国内源下载二进制

安装过程做了什么

curl 下载 install.sh
  → 检测 CPU 架构(amd64/arm64)
  → 从 rancher-mirror.rancher.cn 下载 k3s 二进制(~60MB)
  → 安装到 /usr/local/bin/k3s
  → 创建 systemd unit:k3s.service
  → 生成 kubeconfig:/etc/rancher/k3s/k3s.yaml
  → 自动配置:
    - containerd 作为 CRI 运行时
    - Flannel 作为 CNI 网络插件
    - CoreDNS 作为集群 DNS
    - Traefik 作为 Ingress Controller
    - local-path-provisioner 作为存储类
  → 启动 k3s-server
  → 完成!

验证安装

# 1. 检查节点状态
$ kubectl get nodes -o wide
NAME            STATUS   ROLES           AGE   VERSION        INTERNAL-IP     CONTAINER-RUNTIME
ecs-6ce9-0004   Ready    control-plane   61m   v1.35.5+k3s1   192.168.0.144   containerd://2.2.3-k3s1

输出解读

字段 说明
STATUS Ready 节点就绪,API Server 可接受请求
ROLES control-plane 单节点兼具控制面和数据面角色
VERSION v1.35.5+k3s1 K3s 封装版本(2026年6月最新)
CONTAINER-RUNTIME containerd://2.2.3-k3s1 K3s 内置 containerd

第三部分:K3s 架构深度解析

3.1 单节点架构全景图

            ┌────────────────────────────────────────────────────┐
            │              ecs-6ce9-0004 (2vCPU/4GiB)             │
            │              120.46.154.100 / 192.168.0.144         │
            ├────────────────────────────────────────────────────┤
            │                                                    │
            │  ┌─────────────────── K3s Server ───────────────┐  │
            │  │                                                │  │
            │  │  ┌───────────┐  ┌───────────┐  ┌───────────┐ │  │
            │  │  │    API    │  │Controller │  │ Scheduler │ │  │
            │  │  │  Server   │  │  Manager  │  │           │ │  │
            │  │  │ :6443     │  │           │  │           │ │  │
            │  │  └─────┬─────┘  └─────┬─────┘  └─────┬─────┘ │  │
            │  │        └──────────────┼──────────────┘        │  │
            │  │                       │                        │  │
            │  │              ┌────────▼────────┐               │  │
            │  │              │  SQLite (默认)   │               │  │
            │  │              │ /var/lib/rancher │               │  │
            │  │              │ /k3s/server/db/ │               │  │
            │  │              └────────┬────────┘               │  │
            │  └───────────────────────┼────────────────────────┘  │
            │                          │                            │
            │  ┌────────────── K3s Agent ──────────────────────┐  │
            │  │                                                │  │
            │  │  ┌───────────┐                                 │  │
            │  │  │  Kubelet  │──→ 管理 Pod 生命周期             │  │
            │  │  └───────────┘                                 │  │
            │  │                                                │  │
            │  │  ┌───────────┐                                 │  │
            │  │  │Kube-proxy │──→ iptables/ipvs Service 转发   │  │
            │  │  └───────────┘                                 │  │
            │  │                                                │  │
            │  │  ┌───────────────────────┐                     │  │
            │  │  │   K3s containerd      │                     │  │
            │  │  │   /run/k3s/containerd/│                     │  │
            │  │  │   containerd.sock     │                     │  │
            │  │  └───────────┬───────────┘                     │  │
            │  │              │                                   │  │
            │  │  ┌───────────▼───────────┐                     │  │
            │  │  │     K8s Pods          │                     │  │
            │  │  │  ┌──────┐ ┌─────────┐ │                     │  │
            │  │  │  │Core  │ │ Traefik │ │                     │  │
            │  │  │  │DNS   │ │ Ingress │ │                     │  │
            │  │  │  └──────┘ └─────────┘ │                     │  │
            │  │  │  ┌──────────────────┐ │                     │  │
            │  │  │  │   demo-app Pod   │ │                     │  │
            │  │  │  │   10.42.0.153    │ │                     │  │
            │  │  │  └──────────────────┘ │                     │  │
            │  │  └───────────────────────┘                     │  │
            │  └────────────────────────────────────────────────┘  │
            │                                                      │
            │  ┌──────────── 系统 Docker ──────────────┐          │
            │  │  dockerd + containerd(独立实例)       │          │
            │  │  /run/containerd/containerd.sock       │          │
            │  └────────────────────────────────────────┘          │
            └──────────────────────────────────────────────────────┘

3.2 K3s 的精简策略

K3s 相比标准 K8s 的精简不是"砍功能",而是"换实现":

标准 K8s 组件 K3s 替代方案 优势
kube-apiserver k3s server 内置 减少进程数
kube-controller-manager k3s server 内置 内存共享
kube-scheduler k3s server 内置 减少 IPC
etcd SQLite(默认) 零运维、嵌入式
Docker + dockershim 内置 containerd 直接 CRI,无 dockershim 中间层
云提供商 CCM 移除 减少不必要的 API 调用
kubelet k3s agent 内置 统一升级
kube-proxy k3s agent 内置 统一升级

3.3 关键路径与文件

# K3s 目录结构
/etc/rancher/k3s/
  ├── k3s.yaml              # ← kubeconfig(权限 600,root 可读)
  └── registries.yaml        # ← 镜像仓库配置(需手动创建)

/var/lib/rancher/k3s/
  ├── server/
  │   ├── db/                # ← SQLite 数据文件
  │   └── token              # ← 集群 Token(用于加节点)
  ├── agent/
  │   ├── containerd/        # ← K3s containerd 数据目录
  │   └── etc/containerd/
  │       ├── config.toml    # ← containerd 配置(K3s 自动生成)
  │       └── certs.d/       # ← 私有 Registry 证书
  └── data/                  # ← K3s 二进制和数据

/run/k3s/containerd/
  └── containerd.sock        # ← K3s containerd GRPC Socket

第四部分:K3s 核心组件验证

4.1 系统服务状态

$ systemctl status k3s --no-pager
● k3s.service - Lightweight Kubernetes
     Loaded: loaded (/etc/systemd/system/k3s.service; enabled; preset: enabled)
     Active: active (running)
       Docs: https://k3s.io
   Main PID: 26125 (k3s-server)
      Tasks: 92
     Memory: 1.5G (peak: 1.6G)
        CPU: 1min 1.607s

内存占用分析

组件 内存占用
k3s-server(含 API/Controller/Scheduler) ~800MB
k3s-agent(kubelet + kube-proxy) ~400MB
containerd + Pods 开销 ~300MB
总计 ~1.5GB / 4GB(37.5%)

4.2 Pod 清单与网络模型

$ kubectl get pods -A -o wide

NAMESPACE     NAME                                      READY   STATUS      IP            NODE
kube-system   coredns-8db54c48d-sv59t                   1/1     Running     10.42.0.146   ecs-6ce9-0004
kube-system   local-path-provisioner-5d9d9885bc-nftq2   1/1     Running     10.42.0.143   ecs-6ce9-0004
kube-system   metrics-server-786d997795-lvp67           1/1     Running     10.42.0.145   ecs-6ce9-0004
kube-system   svclb-traefik-38a4f38d-cgkgq              2/2     Running     10.42.0.148   ecs-6ce9-0004
kube-system   traefik-9bcdbbd9-sq8mc                    1/1     Running     10.42.0.149   ecs-6ce9-0004
default       demo-app-6b98c7bf45-bnfqr                 1/1     Running     10.42.0.153   ecs-6ce9-0004

Flannel VXLAN 网络模型

物理网络:192.168.0.0/24(华为云 VPC)
   ↓
K3s Flannel Overlay:10.42.0.0/16(Pod CIDR)
   ↓
Service ClusterIP 网络:10.43.0.0/16(自动分配)

┌──────────────────────────────────────────────────────┐
│  物理网卡 eth0                                       │
│  192.168.0.144                                       │
│     │                                                 │
│     ├── flannel.1 (VTEP 设备)                        │
│     │   负责跨节点的 VXLAN 封包/解包                   │
│     │                                                 │
│     ├── cni0 (CNI Bridge)                            │
│     │   10.42.0.1 — 连接所有 Pod veth                │
│     │   ├── veth → Pod: demo-app (10.42.0.153)       │
│     │   ├── veth → Pod: coredns (10.42.0.146)        │
│     │   └── veth → Pod: traefik (10.42.0.149)        │
│     │                                                 │
│     └── kube-ipvs0 (IPVS 接口)                       │
│         负责 Service ClusterIP → Pod IP 负载均衡      │
│         iptables-mode: false (K3s 默认使用 IPVS)      │
└──────────────────────────────────────────────────────┘

4.3 Service 网络

$ kubectl get svc -A

NAMESPACE     NAME             TYPE           CLUSTER-IP      PORT(S)                      AGE
default       kubernetes       ClusterIP      10.43.0.1       443/TCP                      61m
default       demo-app-svc     NodePort       10.43.24.186    3000:30300/TCP               13m
kube-system   kube-dns         ClusterIP      10.43.0.10      53/UDP,53/TCP,9153/TCP       61m
kube-system   metrics-server   ClusterIP      10.43.248.101   443/TCP                      61m
kube-system   traefik          LoadBalancer   10.43.60.35     80:32443/TCP,443:31350/TCP   32m

Traefik LoadBalancer 的特别之处:K3s 内置的 svclb-traefik DaemonSet 会在每个节点上启动一个负载均衡 Pod,通过 ECMP/iptables 将 LoadBalancer Service 的 External-IP 流量转发到 Traefik Pod,实现单节点环境下的 LoadBalancer 服务。


第五部分:部署第一个应用——Node.js Demo App

5.1 应用源码

demo-app/
├── src/
│   └── server.js           # Node.js HTTP 服务
├── Dockerfile               # 镜像构建文件
└── deployment.yaml          # K8s 部署清单

server.js — 健康检查 + 主页 API

const http = require('http');
const os = require('os');

const PORT = process.env.PORT || 3000;
const VERSION = process.env.APP_VERSION || '1.0.0';

const server = http.createServer((req, res) => {
  const url = new URL(req.url, `http://${req.headers.host}`);

  if (url.pathname === '/health') {
    res.writeHead(200, { 'Content-Type': 'application/json' });
    res.end(JSON.stringify({
      status: 'healthy',
      version: VERSION,
      hostname: os.hostname(),
      uptime: process.uptime(),
      timestamp: new Date().toISOString()
    }));
    return;
  }

  res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
  res.end(/* HTML 主页 */);
});

server.listen(PORT, () => {
  console.log(`Demo App v${VERSION} running on port ${PORT}`);
});

Dockerfile — node:18-alpine 镜像

FROM node:18-alpine
LABEL maintainer="devops@demo.local"
WORKDIR /app
COPY src/ .
EXPOSE 3000
ENV APP_VERSION=1.0.0
ENV PORT=3000
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
  CMD wget --no-verbose --tries=1 --spider http://localhost:3000/health || exit 1
CMD ["node", "server.js"]

5.2 Docker 镜像构建

$ cd /root/demo-app
$ docker build -t demo-app:1.0.0 .

# 输出(关键行):
# [+] Building 45.2s (8/8) FINISHED
# => [2/3] COPY src/ .                         0.1s
# => [3/3] RUN addgroup -g 1001 -S nodejs ...   2.3s
# => exporting to image                        38.4s

验证镜像

$ docker images demo-app:1.0.0
REPOSITORY   TAG       IMAGE ID       SIZE
demo-app     1.0.0     9ccbdba23821   44.9MB   # ← 非常轻量!

5.3 K8s 部署清单

# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: demo-app
  namespace: default
  labels:
    app: demo-app
spec:
  replicas: 1
  selector:
    matchLabels:
      app: demo-app
  template:
    metadata:
      labels:
        app: demo-app
    spec:
      containers:
      - name: demo-app
        image: docker.io/library/demo-app:1.0.0   # ← 关键:使用完整镜像名
        imagePullPolicy: Never                      # ← 不从 Registry 拉取
        ports:
        - containerPort: 3000
          name: http
        env:
        - name: APP_VERSION
          value: "1.0.0"
        - name: PORT
          value: "3000"
        resources:
          requests:
            memory: "64Mi"
            cpu: "100m"
          limits:
            memory: "128Mi"
            cpu: "200m"
        readinessProbe:
          httpGet:
            path: /health
            port: 3000
          initialDelaySeconds: 3
          periodSeconds: 5
---
apiVersion: v1
kind: Service
metadata:
  name: demo-app-svc
  namespace: default
  labels:
    app: demo-app
spec:
  type: NodePort
  selector:
    app: demo-app
  ports:
  - port: 3000
    targetPort: 3000
    nodePort: 30300
    name: http

关键配置解读

配置 说明
imagePullPolicy: Never Never 核心:强制使用本地 containerd 已有的镜像,不从任何 Registry 拉取
image: docker.io/library/demo-app:1.0.0 完整路径 必须与 crictl images 中的名称完全一致
nodePort: 30300 30300 NodePort 范围 30000-32767,此处指定 30300
readinessProbe /health Pod 就绪后才接收流量
resources.requests 64Mi/100m 必须设置,避免资源争抢

第六部分:containerd 双实例深度踩坑

⚠️ 这是本次实战中最核心、最隐蔽的踩坑,也是新手最容易犯的错误。

6.1 问题现象

Docker build 完镜像后,用 ctr images import 导入,crictl images 却看不到镜像:

$ docker save demo-app:1.0.0 | ctr -n k8s.io images import -
# 命令成功执行

$ crictl images | grep demo-app
# ← 什么都不输出!镜像不存在!

然后又尝试直接 kubectl apply,Pod 报 ErrImageNeverPull

6.2 根因分析:两个 containerd 实例

在安装了 Docker 的 K3s 节点上,存在两个独立的 containerd 进程

┌──────────────────────────────────────────────────┐
│                    宿主机 OS                       │
│                                                  │
│  ┌──────────── docker.service ────────────┐      │
│  │  dockerd                               │      │
│  │    │                                    │      │
│  │    └── containerd (PID: 1100)          │      │
│  │         Socket: /run/containerd/       │      │
│  │                  containerd.sock       │      │
│  │         命名空间: default, moby         │      │
│  │         用途: Docker 镜像构建 & 运行    │      │
│  └──────────────────────────────────────────┘      │
│                                                  │
│  ┌──────────── k3s.service ────────────────┐      │
│  │  k3s-server                             │      │
│  │    │                                    │      │
│  │    └── containerd (PID: 24886)          │      │
│  │         Socket: /run/k3s/containerd/    │      │
│  │                  containerd.sock        │      │
│  │         命名空间: k8s.io                 │      │
│  │         用途: K3s CRI 运行时            │      │
│  └──────────────────────────────────────────┘      │
│                                                  │
│  ┌──────────── CRI CLI 工具 ───────────────┐      │
│  │  crictl → 默认连 /run/containerd/       │      │
│  │  ctr → 默认连 /run/containerd/          │      │
│  │  kubectl → 通过 K3s agent 连 K3s 的    │      │
│  │            containerd sock              │      │
│  └──────────────────────────────────────────┘      │
└──────────────────────────────────────────────────┘

关键区别一览

containerd 实例 Socket 路径 PID 命名空间 管理方 用途
系统 containerd /run/containerd/containerd.sock ~1100 default, moby Docker daemon Docker 镜像构建/容器运行
K3s containerd /run/k3s/containerd/containerd.sock ~24886 k8s.io K3s Agent K8s Pod 生命周期管理

为什么会踩坑

  • ctrcrictl 默认都连接 系统 containerd/run/containerd/containerd.sock
  • K3s 的 kubelet 只跟 K3s containerd 通信
  • 你在系统 containerd 里导入的镜像,K3s 完全看不见!

6.3 正确操作流程

Step 1:Docker build 镜像

$ docker build -t demo-app:1.0.0 .
# 镜存在 Docker 使用的系统 containerd 中

Step 2:导出并导入到 K3s containerd

# 正确命令:必须指定 K3s containerd socket 和 k8s.io 命名空间
$ docker save docker.io/library/demo-app:1.0.0 | \
  ctr -a /run/k3s/containerd/containerd.sock -n k8s.io images import -
参数 必须性 说明
-a /run/k3s/containerd/containerd.sock ⚠️ 必须 指定 K3s 的 containerd socket
-n k8s.io ⚠️ 必须 K3s 只认这个命名空间

Step 3:验证

$ crictl images | grep demo-app
docker.io/library/demo-app   1.0.0   8c25bf4c3e082   44.9MB   # ✅ 出现!

Step 4:kubectl apply

$ kubectl apply -f deployment.yaml
deployment.apps/demo-app created
service/demo-app-svc created

$ kubectl get pods -l app=demo-app
NAME                        READY   STATUS    RESTARTS   AGE
demo-app-6b98c7bf45-bnfqr   1/1     Running   0          10s    # ✅ 运行!

6.4 为什么不用 Docker Registry 中转?

你可能想:直接用 docker push 推到 Registry,再让 K3s imagePullPolicy: Always 拉取,不就没这个问题了吗?

在本次实战中行不通,原因如下:

# K3s 节点 (192.168.0.144) → Registry (192.168.0.164)
$ ping -c 2 -W 2 192.168.0.164
2 packets transmitted, 0 received, 100% packet loss   # ← 内网不通!

# 华为云不同 ECS 之间默认 VPC 隔离
方案 可行性 说明
Docker Registry (内网) 华为云 VPC 默认隔离,不通
Docker Registry (公网) ⚠️ 需 5Mbps 带宽传输,且要配置 HTTPS
`docker save ctr import`

第七部分:NodePort 503 排查全景记录

7.1 问题复现

浏览器访问:http://120.46.154.100:30300/
↓
HTTP 503 Service Unavailable

7.2 排查 Step-by-Step

Step 1:检查集群状态

$ kubectl get pods -A
NAMESPACE     NAME                                      READY   STATUS
kube-system   coredns-8db54c48d-sv59t                   1/1     Running
kube-system   local-path-provisioner-5d9d9885bc-nftq2   1/1     Running
kube-system   metrics-server-786d997795-lvp67           1/1     Running
kube-system   traefik-9bcdbbd9-sq8mc                    1/1     Running
# ⚠️ 没有任何 demo-app Pod!
发现 结论
系统 Pod 全部 Running K3s 集群本身正常
无 demo-app Pod 应用从未部署
无 demo-app Service NodePort 30300 没有对应后端

Step 2:检查 Service

$ kubectl get svc
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.43.0.1    <none>        443/TCP   61m
# ← 只有默认 kubernetes Service,demo-app-svc 不存在

Step 3:确认根本原因

503 的根本原因:
  Kubernetes Service "demo-app-svc" (NodePort 30300) 从未被创建
    → 因为 Deployment 也从未被创建
      → 因为 demo-app 镜像从未被导入 K3s containerd
        → 一切都是从头开始

7.3 修复全流程

Step 1: 创建 demo-app 源码
  → server.js + Dockerfile + deployment.yaml

Step 2: Docker build
  → docker build -t demo-app:1.0.0 .  (45s, 44.9MB)

Step 3: 导入 K3s containerd(关键!)
  → docker save | ctr -a /run/k3s/containerd/containerd.sock -n k8s.io images import -

Step 4: kubectl apply
  → deployment.apps/demo-app created
  → service/demo-app-svc created

Step 5: 验证
  → kubectl get pods → Running 1/1 ✅
  → curl localhost:30300/health → {"status":"healthy"} ✅
  → curl http://120.46.154.100:30300/ → HTTP 200 ✅

7.4 验证截图(数据采集)

# 集群范围验证
$ kubectl get pods -A -o wide
NAMESPACE     NAME                                  IP            NODE
default       demo-app-6b98c7bf45-bnfqr             10.42.0.153   ecs-6ce9-0004
kube-system   coredns-8db54c48d-sv59t               10.42.0.146   ecs-6ce9-0004
...

$ kubectl describe deployment demo-app
Name:               demo-app
Namespace:          default
Replicas:           1 desired | 1 updated | 1 total | 1 available | 0 unavailable
StrategyType:       RollingUpdate

$ kubectl describe svc demo-app-svc
Name:                     demo-app-svc
Type:                     NodePort
IP:                       10.43.24.186
Port:                     http  3000/TCP
TargetPort:               3000/TCP
NodePort:                 http  30300/TCP   # ← 映射正确
Endpoints:                10.42.0.153:3000  # ← Pod IP 正确

# Pod 日志
$ kubectl logs -l app=demo-app --tail=5
Demo App v1.0.0 running on port 3000
Health check: http://localhost:3000/health

# 最终验证
$ curl -s http://120.46.154.100:30300/health
{"status":"healthy","version":"1.0.0","hostname":"demo-app-6b98c7bf45-bnfqr",
 "uptime":156.78,"timestamp":"2026-06-08T04:24:30.123Z"}

7.5 流量链路分析

外部请求
  │  http://120.46.154.100:30300/
  ▼
K3s kube-proxy (IPVS/iptables)
  │  NodePort 30300 → Service ClusterIP 10.43.24.186:3000
  ▼
K3s kube-proxy (IPVS/iptables)
  │  10.43.24.186:3000 → Endpoint 10.42.0.153:3000
  ▼
Flannel VXLAN
  │  物理网卡 → cni0 bridge → veth pair → Pod netns
  ▼
demo-app Pod (10.42.0.153:3000)
  │  Node.js HTTP Server
  ▼
HTTP 200: {"status": "healthy", "version": "1.0.0", ...}

第八部分:K3s 常用运维命令速查

8.1 集群管理

# 查看集群信息
kubectl cluster-info                          # API Server + CoreDNS 地址
kubectl version                               # Client + Server 版本
kubectl get nodes -o wide                     # 节点列表 + IP + 运行时

# 查看所有资源
kubectl get all -A                            # 所有命名空间的所有资源
kubectl get pods -A -o wide                   # Pod 列表 + IP + 节点
kubectl get svc -A                            # Service 列表
kubectl get deploy -A                         # Deployment 列表

# K3s 特有操作
systemctl status k3s                          # 服务状态
systemctl restart k3s                         # 重启 K3s(会重启所有 Pod!)
k3s kubectl <command>                         # 不用配置 kubeconfig 直接操作

8.2 镜像管理(K3s 双 containerd 环境)

# === Docker 端(系统 containerd)===
docker images                                 # 查看 Docker 镜像
docker build -t myapp:v1 .                   # 构建镜像
docker save myapp:v1 -o /tmp/myapp.tar       # 导出镜像

# === K3s containerd 端 ===
crictl images                                 # 查看 K3s 可见的镜像
crictl pull docker.io/library/nginx:latest    # 拉取镜像(需网络)
crictl rmi <IMAGE_ID>                         # 删除镜像

# === 镜像搬运(Docker → K3s)===
docker save docker.io/library/myapp:v1 | \
  ctr -a /run/k3s/containerd/containerd.sock -n k8s.io images import -

8.3 Pod 排障

# 查看 Pod 详情
kubectl describe pod <POD_NAME>               # 查看 Events(最重要!)
kubectl logs <POD_NAME>                       # 查看日志
kubectl logs <POD_NAME> --previous            # 上一次崩溃的日志
kubectl logs -l app=<LABEL> --tail=50 -f      # 按标签查看并跟随

# 进入容器
kubectl exec -it <POD_NAME> -- /bin/sh        # 进入容器调试
kubectl port-forward <POD_NAME> 8080:3000     # 端口转发到本地

# 资源使用
kubectl top pods                              # Pod 资源使用
kubectl top nodes                             # 节点资源使用

8.4 配置与管理

# configmap/secret
kubectl create configmap app-config --from-file=config.json
kubectl create secret generic db-pass --from-literal=password=123456

# 滚动更新
kubectl set image deploy/demo-app demo-app=demo-app:2.0.0
kubectl rollout status deploy/demo-app        # 等待更新完成
kubectl rollout undo deploy/demo-app          # 回滚

# 资源伸缩
kubectl scale deploy/demo-app --replicas=3    # 扩容到 3 副本
kubectl autoscale deploy/demo-app --min=1 --max=10 --cpu-percent=80  # HPA

# 清理
kubectl delete -f deployment.yaml             # 按文件删除
kubectl delete deploy demo-app                # 按名称删除
kubectl delete pods --all                     # 删除所有 Pod(会被 Deploy 重建)

第九部分:K3s vs K8s vs MicroK8s 全面对比

9.1 整体对比表

维度 K3s 标准 K8s (kubeadm) MicroK8s
开发者 Rancher / SUSE CNCF / Google Canonical
二进制大小 < 100MB > 1GB(多组件) ~200MB(snap 包)
最小内存 512MB 2GB+ 540MB
安装命令 curl ... \| sh kubeadm init snap install microk8s
默认存储 SQLite etcd Dqlite
默认 CNI Flannel 无(需手动安装) Calico(可选)
默认 Ingress Traefik nginx(插件)
DNS CoreDNS CoreDNS CoreDNS
CRI 内置 containerd containerd/CRI-O 内置 containerd
ARM 支持 ✅ 官方
高可用 嵌入式 etcd/外部 DB 原生 etcd 集群 多节点集群
GPU 支持 ✅(插件)
Helm 集成 内置 Helm Controller 需独立安装 Helm 插件形式
CNCF 认证 ✅ Certified ✅ Certified ✅ Certified
适用场景 Edge/IoT/单机生产 多节点生产集群 本地开发/MicroCloud

9.2 K8s 被 K3s 移除的组件

K3s 的"轻量"来自于对标准 K8s 的策略性精简。以下是移除的组件和替代方案:

被移除的组件 原因 影响
cloud-controller-manager 云提供商特定逻辑 自建机房/裸金属无影响
storage.k8s.io/v1alpha1 Alpha API,不稳定 生产环境不应使用
admissionregistration.k8s.io/v1alpha1 Alpha API 使用 v1 版本
apiextensions.k8s.io/v1alpha1 旧 CRD API 使用 v1 版本
certificates.k8s.io/v1beta1 旧 CSR API 使用 v1 版本
coordination.k8s.io/v1beta1 旧 Lease API 使用 v1 版本
Docker (dockershim) K8s 1.24+ 已废弃 containerd 直接替代
旧版 InTree Volume 驱动 大部分云存储 CSI 驱动替代

9.3 选型决策树

你需要 K8s 吗?
  │
  ├── 是 → 需要多节点高可用?
  │        ├── 是 → 生产环境?
  │        │        ├── 是 → kubeadm K8s(完整生态)
  │        │        └── 否 → K3s HA(3 节点 + 外部 DB)
  │        │
  │        └── 否 → 单机即可?
  │                 ├── 是 → K3s(本案例实战方案)← ✅
  │                 └── 否 → ...
  │
  └── 否 → 用 Docker Compose / Nomad 即可

第十部分:踩坑大全与总结

10.1 踩坑清单(含解决方案)

# 踩坑 现象 根因 解决方案
1 containerd 双实例 ctr import 后 crictl 看不到镜像 系统 containerd ≠ K3s containerd ctr -a /run/k3s/containerd/containerd.sock -n k8s.io ...
2 镜像名不完整 ErrImageNeverPull K3s containerd 中镜像名带 docker.io/library/ 前缀 使用完整名称 docker.io/library/demo-app:1.0.0
3 内网隔离 no route to host → 192.168.0.164:5000 华为云 VPC 默认不同 ECS 内网不通 本地构建 + imagePullPolicy: Never
4 Docker Hub 无法直连 ImagePullBackOff 国内网络环境限制 配置 K3s registries.yaml 使用镜像加速
5 heredoc 引号丢失 daemon.json JSON 损坏 SSH exec 多层转义吞掉引号 Python json.dumps() + SFTP 上传
6 NodePort 503 http://IP:30300/ → 503 应用从未部署 创建 Deployment + Service 全流程
7 K3s 内存占用偏高 1.5GB / 4GB k3s-server 单进程多角色 正常现象,生产建议 8GB+
8 kubectl version --short 报错 unknown flag: --short K8s 1.29+ 移除了此 flag 使用 kubectl version

10.2 K3s 单节点配置建议

# /etc/rancher/k3s/registries.yaml
mirrors:
  docker.io:
    endpoint:
      - "https://docker.1ms.run"
      - "https://docker.xuanyuan.me"
      - "https://mirrors.aliyun.com"
  # 如有私有 Registry:
  # "your-registry.com:5000":
  #   endpoint:
  #     - "http://your-registry.com:5000"

10.3 总结金句

K3s 不是 K8s 的"阉割版",而是 K8s 的"精简重构"——用更少的资源做同样的事。

本次实战收获 说明
K3s 安装 一条 curl 命令,国内用 rancher-mirror.rancher.cn
架构理解 K3s 是单二进制封装所有核心组件
containerd 双实例 最核心的踩坑:系统 Docker 和 K3s 各有一个 containerd
镜像搬运 `docker save
NodePort 排查 503 = 应用未部署 → 从镜像构建到 kubectl apply 全流程
Flannel 网络 VXLAN 10.42.0.0/16 Overlay,IPVS Service 转发
运维命令 涵盖 Pod/镜像/网络/日志全套命令速查

10.4 下一步学习路线

K3s 单节点掌握了 →
  │
  ├── K3s 多节点 HA(3 Server + N Agent)
  ├── K3s + 外部 MySQL/PostgreSQL 存储
  ├── K3s Helm Controller 自动化 Chart 部署
  ├── K3s + Longhorn 分布式存储
  ├── K3s + Istio/Linkerd Service Mesh
  └── K3s 边缘场景(ARM/树莓派集群)

博客完成时间:2026-06-08
实战环境:华为云 ecs-6ce9-0004 (2vCPU/4GiB, Ubuntu 24.04, K3s v1.35.5+k3s1)
在线验证:http://120.46.154.100:30300/

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐