文章

k3s 初探

k3s 的安装和使用(最简单的用例)

k3s 初探

本文将在一个有3台服务器的迷你集群中简单测试 k3s 的安装和使用。

参考自:

https://www.ywbj.cc/?p=1418 https://docs.k3s.io/zh/quick-start

安装

首先,我们在 node0 上安装 k3s 的 master 节点:

1
curl -sfL https://get.k3s.io | sh -

k3s安装

master 节点安装完成后,k3s 会自动生成一个 token 文件 /var/lib/rancher/k3s/server/node-token,我们可以使用这个 token 来加入其他节点。

k3s 的 node token 是一个用于集群节点间安全通信的令牌,只有拥有正确 token 的节点才能加入集群,防止未授权访问。

因此,我们在 node0 执行

1
sudo cat /var/lib/rancher/k3s/server/node-token 

来获取 token

获取token

接下来,用刚刚获取的 token 在 node1 和 node2 上安装 k3s 的 worker 节点:

在 node1 和 node2 上分别执行:

1
2
3
TOKEN=K104010e617e32c94af28fb5b1bc6a6e1c8ee9eaccae64cbadda79a819f852ab822::server:2be2cb5985bc549e94f1dd483bd41342
IP_OF_NODE0=192.168.1.227
sudo curl -sfL https://get.k3s.io | K3S_URL=https://$IP_OF_NODE0:6443 K3S_TOKEN=$TOKEN sh -

TOKEN 和 IP_OF_NODE0 分别替换为之前获取的 token 和 node0 的 IP 地址。

从节点安装

从节点运行完成后,在 node0 运行

1
sudo kubectl get nodes

查看集群节点状态:

集群节点状态查询

备注1:

k3s 和 kubectl 的关系如下:

k3s 是一个轻量级的 Kubernetes 发行版,用于快速部署和运行 Kubernetes 集群。 kubectl 是 Kubernetes 的命令行工具,用于管理和操作 Kubernetes 集群,比如查看节点、部署应用等。

  • k3s 负责运行和管理集群,kubectl 负责与集群交互和操作。
  • 安装 k3s 后会自动生成 kubeconfig 文件,kubectl 通过该文件连接和管理 k3s 集群。
  • kubectl 可以管理任何兼容 Kubernetes API 的集群,包括 k3s。

备注2:

k3s 安装脚本会自动安装服务,安装完毕后会自动启动 k3s 服务。可以使用以下命令查看 k3s 服务状态:

1
sudo systemctl status k3s

k3s服务状态

如果需要停止或重启 k3s 服务,可以使用以下命令:

1
2
3
sudo systemctl stop k3s     # 停止 k3s 服务
sudo systemctl start k3s    # 启动 k3s 服务
sudo systemctl restart k3s  # 重启 k3s 服务

使用

我的测试环境下集群状态如下:

1
2
3
4
5
root@node0:~# sudo kubectl get nodes
NAME    STATUS   ROLES                  AGE    VERSION
node0   Ready    control-plane,master   14h    v1.33.3+k3s1
node1   Ready    <none>                 14h    v1.33.3+k3s1
node2   Ready    <none>                 114s   v1.33.3+k3s1

下面我将测试运行一个容器,确保这个容器在一个节点上运行,当这个节点宕机后,容器会自动迁移到其他节点上。

我们使用 kubernetes 的 Deployment 来管理容器的部署和运行。

Deployment 是 Kubernetes 中的一种控制器资源,用于管理和自动化应用的部署、升级和扩缩容。

主要功能:

  • 保证指定数量的 Pod 始终运行
  • 支持滚动升级和回滚
  • 自动重建崩溃或被删除的 Pod
  • 支持扩容和缩容

典型场景: 你只需声明一次应用的期望状态(如副本数、镜像等),Deployment 会自动维护实际状态与期望状态一致。

我们创建一个简单的 Deployment,运行一个 nginx 容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
apiVersion: apps/v1 # 指定使用 Kubernetes 的 apps/v1 API 版本。
kind: Deployment    # 资源类型为 Deployment,用于管理一组 Pod 的生命周期。
metadata:           # 资源的元数据,这里设置了 Deployment 的名字为 test-deployment
  name: test-deployment
spec:               # Deployment 的具体规格和期望状态。
  replicas: 1       # 希望有 1 个 Pod 实例在集群中运行。
  selector:         # 用于选择哪些 Pod 属于这个 Deployment。这里通过标签 app: test 进行匹配。
    matchLabels:
      app: test
  template:         # Pod 模板,定义 Pod 的内容。
    metadata:       # 给 Pod 加上标签 app: test,用于 selector 匹配。
      labels:
        app: test
    spec:           # Pod 的具体规格。
      containers:   # 定义 Pod 内的容器列表。
      - name: test-container
        image: nginx # 你可以换成自己的测试镜像

将上面的内容保存为 test-deployment.yaml 文件,然后在 node0 上执行以下命令来创建 Deployment:

1
sudo kubectl apply -f test-deployment.yaml

运行Deployment

命令已经成功创建了名为 test-deployment 的 Deployment 资源。 现在 Kubernetes 会自动在集群中运行一个 nginx 容器,并确保始终有一个副本在线。

接下来,我们可以使用以下命令查看 Deployment 的状态:

1
sudo kubectl get deployments

初始状态看到镜像没有准备好:

镜像没有准备好

使用

1
kubectl get pods

命令查看 Pod 的状态:

查看pod状态

可以看到 Pod 的状态是 ContainerCreating,表示容器正在创建中。

使用

1
kubectl describe pod test-deployment-66f9f858dc-lcplk

命令(替换 test-deployment-66f9f858dc-lcplkget pods 查询到的 Pod 名称)来查看 Pod 的详细信息。

如果看到无法拉取 rancher/mirrored-pause:3.6 镜像,具体报错为网络请求失败(EOF),通常是网络无法访问 Docker Hub 或镜像仓库。请检查网络连接或镜像源配置。

备注:

rancher/mirrored-pause:3.6 镜像是 Kubernetes 集群中用于Pod 沙箱(Pod Sandbox)的特殊镜像。 它不是运行你的业务容器,而是作为 Pod 的“基础容器”,用于为 Pod 提供网络和命名空间隔离。 每个 Pod 启动时,都会先启动一个 pause 容器,其他业务容器都运行在这个 pause 容器的网络和命名空间下。如果无法拉取,可以尝试更换 k3s 的镜像源。

如果网络正常,稍后可以看到 Pod 的状态变为 Running。

1
sudo kubectl get pods

查看pod状态

成功🎉

成功状态下查看 Pod 的详细信息,应该看到:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
root@node0 ~# sudo kubectl get pods
NAME                               READY   STATUS    RESTARTS   AGE
test-deployment-66f9f858dc-6k5s6   1/1     Running   0          4m36s
root@node0 ~# kubectl describe pod test-deployment-66f9f858dc-6k5s6 
Name:             test-deployment-66f9f858dc-6k5s6
Namespace:        default
Priority:         0
Service Account:  default
Node:             node1/192.168.1.185
Start Time:       Wed, 06 Aug 2025 04:52:41 +0000
Labels:           app=test
                  pod-template-hash=66f9f858dc
Annotations:      <none>
Status:           Running
IP:               10.42.1.19
IPs:
  IP:           10.42.1.19
Controlled By:  ReplicaSet/test-deployment-66f9f858dc
Containers:
  test-container:
    Container ID:   containerd://842da0a16935022a6db0dacd5feb5af4f2016656ce2ba428973f2658fb3012d8
    Image:          nginx
    Image ID:       docker.io/library/nginx@sha256:84ec966e61a8c7846f509da7eb081c55c1d56817448728924a87ab32f12a72fb
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Wed, 06 Aug 2025 04:53:27 +0000
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-h6v4r (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True 
  Initialized                 True 
  Ready                       True 
  ContainersReady             True 
  PodScheduled                True 
Volumes:
  kube-api-access-h6v4r:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    Optional:                false
    DownwardAPI:             true
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
  Type    Reason     Age    From               Message
  ----    ------     ----   ----               -------
  Normal  Scheduled  4m41s  default-scheduler  Successfully assigned default/test-deployment-66f9f858dc-6k5s6 to node1
  Normal  Pulling    4m28s  kubelet            Pulling image "nginx"
  Normal  Pulled     3m55s  kubelet            Successfully pulled image "nginx" in 33.723s (33.723s including waiting). Image size: 72223946 bytes.
  Normal  Created    3m55s  kubelet            Created container: test-container
  Normal  Started    3m55s  kubelet            Started container test-container
root@node0 ~# 

如果想查看 Deployment 的详细信息,可以用:

1
sudo kubectl describe deployment test-deployment

Deployment 详细信息

测试节点宕机后容器迁移

现在我们来测试一下,当运行容器的节点宕机后,容器是否会自动迁移到其他节点上。

首先查看当前 Pod 的状态,查看它运行的位置

1
sudo kubectl get pods -o wide

pod状态

可以看到 Pod 正在 node1 上运行。

如果 pod 恰好被分配到 node0, 我们暂时无法测试节点宕机,因为在我们的集群中 node0 是 master 节点,master 宕机后无法管理集群。

这种情况可以删除 Pod, 随后 k3s 会自动重新调度 Pod 到其他节点上。命令类似:

1
2
sudo kubectl delete pod test-deployment-66f9f858dc-f6vff
sudo kubectl get pods -o wide

你应该看到 Pod 被重新调度到其他节点运行。

我们现在测试正在运行 pod 的 node1 宕机时会发生什么:

我们直接关闭 node1

1
sudo shutdown -h now # 在 node1 上执行,谨慎执行,以为此操作会导致 node1 立刻关机

稍等几分钟后回到 node0 上查看节点状态

1
2
3
4
5
root@node0 ~# sudo kubectl get nodes
NAME    STATUS     ROLES                  AGE   VERSION
node0   Ready      control-plane,master   33m   v1.33.3+k3s1
node1   NotReady   <none>                 29m   v1.33.3+k3s1
node2   Ready      <none>                 29m   v1.33.3+k3s1

可以看到 node1 的状态变为 NotReady,表示它已经不在集群中了。

接下来查看 Pod 的状态:

1
sudo kubectl get pods -o wide

node1宕机后的pod状态

尽管node1 宕机了,但 Pod 仍然显示为 Running 状态,并且还是在 node1 上。这时因为 Kubernetes 默认有容忍时间,在一段时间后才会驱逐 Pod,并重新调度到其他节点。如果没有设置容忍时间,默认会等待 5 分钟(300 秒)后才会驱逐 Pod。

再等一会儿可以看到

开始驱逐pod

我们发现 k3s 自动开始 terminate node1 上的 Pod,并将其重新调度到 node0 上。

pod切换成功

看到 Pod 状态变为 Running,并且现在运行在 node0 上。

进阶配置(高可用模式)

未完待续

本文由作者按照 CC BY 4.0 进行授权