Post

Kubernetes学习实践(二) -- 熟悉K8s相关概念和实操印证

继续熟悉K8s的相关概念,并实操印证增强理解和体感

Kubernetes学习实践(二) -- 熟悉K8s相关概念和实操印证

1. 引言

上篇搭建了一个单机K8s环境,本篇基于环境进行操作印证,熟悉理解相关概念和操作命令。

2. kubectl基本操作命令

K8s提供了CLI工具:kubectl用于完成大多数集群管理相关的功能,上篇提到其会通过Kubernetes API进行C/S方式调用。

  • 可使用kubectl -h/--help方式查看帮助信息
  • 另外可用kubectl explain方式解释一些K8s中的组件和包含的字段(Field)。支持查看的组件列表可通过kubectl api-resources获取,比如nodes/services

下述相关命令结果详情,可见:kubectl_cmd_operation.md

2.1. kubectl describe查看节点/Pod信息

命令:kubectl describe TYPE NAME_PREFIX,其中名称支持前缀模糊匹配

  • 查看节点详细信息,kubectl describe node xdlinux
    • 由于支持名称前缀匹配,也可以kubectl describe node xd查看到xdlinux节点的信息
    • kubectl describe nodekubectl describe nodes也查询了到节点详情信息
  • 除此之外,TYPE还可以是podpods,进一步信息可以kubectl describe -h查看

关键信息:

  • 地址,下述的Addresses部分
    • InternalIP: 192.168.1.150
    • Hostname: xdlinux
  • 状况,Conditions,描述所有Running节点的状态
    • 如下述展示的几类状态:MemoryPressure内存压力、DiskPressure磁盘压力、PIDPressure进程压力、Ready:true表示节点健康,可接收Pod
    • 每种类型都有:当前的状态和状态发生变化的时间、出现该状态的原因信息
    • 当节点出现问题时,K8s会自动创建和相应状态的 污点(Taint) ,其和节点的 亲和性(affinity)相反,使节点排斥一类特定的Pod。
  • 容量(Capacity)和可分配(Allocatable
    • 这两个值描述节点上的可用资源:CPU、内存和可以调度到节点上的 Pod 的个数上限
  • 信息(Info
    • 节点的一般信息,如内核版本、Kubernetes 版本(kubelet 和 kube-proxy 版本)、 容器运行时详细信息,以及节点使用的操作系统。
    • kubelet 从节点收集这些信息并将其发布到 Kubernetes API
  • 进一步的信息,可见:节点状态
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
60
61
62
63
64
65
66
67
68
69
[root@xdlinux ➜ hello git:(main) ]$ kubectl describe node xdlinux
Name:               xdlinux
Roles:              control-plane
Labels:             beta.kubernetes.io/arch=amd64
...
CreationTimestamp:  Mon, 21 Jul 2025 07:47:52 +0800
Taints:             node-role.kubernetes.io/control-plane:NoSchedule
Unschedulable:      false
Lease:
  HolderIdentity:  xdlinux
  AcquireTime:     <unset>
  RenewTime:       Tue, 22 Jul 2025 22:57:36 +0800
Conditions:
  Type             Status  LastHeartbeatTime                 LastTransitionTime                Reason                       Message
  ----             ------  -----------------                 ------------------                ------                       -------
  MemoryPressure   False   Tue, 22 Jul 2025 22:57:34 +0800   Mon, 21 Jul 2025 07:47:51 +0800   KubeletHasSufficientMemory   kubelet has sufficient memory available
  DiskPressure     False   Tue, 22 Jul 2025 22:57:34 +0800   Mon, 21 Jul 2025 07:47:51 +0800   KubeletHasNoDiskPressure     kubelet has no disk pressure
  PIDPressure      False   Tue, 22 Jul 2025 22:57:34 +0800   Mon, 21 Jul 2025 07:47:51 +0800   KubeletHasSufficientPID      kubelet has sufficient PID available
  Ready            True    Tue, 22 Jul 2025 22:57:34 +0800   Tue, 22 Jul 2025 20:08:06 +0800   KubeletReady                 kubelet is posting ready status
Addresses:
  InternalIP:  192.168.1.150
  Hostname:    xdlinux
Capacity:
  cpu:                16
  ephemeral-storage:  56872Mi
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             31929508Ki
  pods:               110
Allocatable:
  cpu:                16
  ephemeral-storage:  53671152756
  hugepages-1Gi:      0
  hugepages-2Mi:      0
  memory:             31827108Ki
  pods:               110
System Info:
  Machine ID:                 fedf1414bf654cf090067b7374c2a61a
  System UUID:                3adbd480-702c-11ec-8165-1ad318f15100
  Boot ID:                    28911b0d-93b1-4ba3-9f90-4d3c61270498
  Kernel Version:             5.14.0-503.40.1.el9_5.x86_64
  OS Image:                   Rocky Linux 9.5 (Blue Onyx)
  Operating System:           linux
  Architecture:               amd64
  Container Runtime Version:  containerd://2.1.3
  Kubelet Version:            v1.33.3
  Kube-Proxy Version:         
PodCIDR:                      10.244.0.0/24
PodCIDRs:                     10.244.0.0/24
Non-terminated Pods:          (7 in total)
  Namespace                   Name                               CPU Requests  CPU Limits  Memory Requests  Memory Limits  Age
  ---------                   ----                               ------------  ----------  ---------------  -------------  ---
  kube-system                 coredns-757cc6c8f8-nxw7g           100m (0%)     0 (0%)      70Mi (0%)        170Mi (0%)     39h
  kube-system                 coredns-757cc6c8f8-v2mgf           100m (0%)     0 (0%)      70Mi (0%)        170Mi (0%)     39h
  kube-system                 etcd-xdlinux                       100m (0%)     0 (0%)      100Mi (0%)       0 (0%)         39h
  kube-system                 kube-apiserver-xdlinux             250m (1%)     0 (0%)      0 (0%)           0 (0%)         39h
  kube-system                 kube-controller-manager-xdlinux    200m (1%)     0 (0%)      0 (0%)           0 (0%)         39h
  kube-system                 kube-proxy-v682x                   0 (0%)        0 (0%)      0 (0%)           0 (0%)         39h
  kube-system                 kube-scheduler-xdlinux             100m (0%)     0 (0%)      0 (0%)           0 (0%)         39h
Allocated resources:
  (Total limits may be over 100 percent, i.e., overcommitted.)
  Resource           Requests    Limits
  --------           --------    ------
  cpu                850m (5%)   0 (0%)
  memory             240Mi (0%)  340Mi (1%)
  ephemeral-storage  0 (0%)      0 (0%)
  hugepages-1Gi      0 (0%)      0 (0%)
  hugepages-2Mi      0 (0%)      0 (0%)
Events:              <none>

2.2. kubectl get查看节点/Pod信息

用法:kubectl get node xxx,也可-o指定不同的信息输出格式,比如-o yaml-o json-o wide,会有不同详细程度的信息展示。

1
2
3
4
5
6
7
[root@xdlinux ➜ ~ ]$ kubectl get node xdlinux
NAME      STATUS   ROLES           AGE     VERSION
xdlinux   Ready    control-plane   2d14h   v1.33.3
# 也支持`get no`、`get node xd`等模糊匹配方式
[root@xdlinux ➜ ~ ]$ kubectl get nodes
NAME      STATUS   ROLES           AGE     VERSION
xdlinux   Ready    control-plane   2d14h   v1.33.3

kubectl get node xdlinux -o json获取详情,可看到也包含了kubectl describe中的信息。

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
[root@xdlinux ➜ ~ ]$ kubectl get node xdlinux -o json
{
    "apiVersion": "v1",
    "kind": "Node",
    "metadata": {
        ...
        "name": "xdlinux",
        "resourceVersion": "297356",
        "uid": "0ba29438-6540-4fce-892e-a8564389d942"
    },
    "spec": {
        "podCIDR": "10.244.0.0/24",
        "podCIDRs": [
            "10.244.0.0/24"
        ],
        "taints": [
            {
                "effect": "NoSchedule",
                "key": "node-role.kubernetes.io/control-plane"
            }
        ]
    },
    "status": {
        "addresses": [
            {
                "address": "192.168.1.150",
                "type": "InternalIP"
            },
            {
                "address": "xdlinux",
                "type": "Hostname"
            }
        ],
        ...
        "daemonEndpoints": {
            "kubeletEndpoint": {
                "Port": 10250
            }
        },
        ...
    }
}

2.3. kubectl run运行一个Pod

kubectl run redis --image=docker.m.daocloud.io/library/redis:alpine 可以启动一个Pod,此处指定了一个代理镜像,默认的镜像暂时连不上。

3. 使用K8s部署一个Redis服务

Pod是K8s中最小的调度单元,所以无法直接在K8s中运行一个container,但是可以运行一个Pod,而这个Pod中只包含一个container

下面以kubectl run来启动一个包含Redis的Pod,参考:集群管理:以 Redis 为例-部署及访问

3.1. 正常运行Redis镜像

1、执行 kubectl run redis --image='redis:alpine'(使用的基础镜像为Alpine Linux,镜像体积最小)

1
2
[root@xdlinux ➜ ~ ]$ kubectl run redis --image='redis:alpine'
pod/redis created

此外,直接指定docker.m.daocloud.io/library/redis:alpine是可以正常pull的:
kubectl run redis --image=docker.m.daocloud.io/library/redis:alpine。但此处来定位下为什么--image='redis:alpine'无法成功拉取镜像。

2、kubectl get pods查看Pod状态

查看了多次,一直是Pending状态

1
2
3
4
5
6
7
8
9
10
11
[root@xdlinux ➜ ~ ]$ kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
redis   0/1     Pending   0          11s

# 也可get all查看所有类型的信息
[root@xdlinux ➜ ~ ]$ kubectl get all
NAME        READY   STATUS    RESTARTS   AGE
pod/redis   0/1     Pending   0          9m17s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2d14h

3.2. 调整:设置控制面允许运行普通工作负载

3、定位:kubectl describe pod redis查看这个Pod的事件

关注Events信息,下述信息明确表示:集群中有一个节点,但它有一个污点(taint)node-role.kubernetes.io/control-plane: ,而Pod没有容忍(toleration)这个污点,因此无法调度。

原因:在单节点集群(例如minikube或只有一个控制平面节点的集群)中,控制平面节点默认带有污点,以防止普通工作负载运行在上面。

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@xdlinux ➜ ~ ]$ kubectl describe pod redis
Name:             redis
Namespace:        default
...
Status:           Pending
IP:               
...
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
  ----     ------            ----               ----               -------
  Warning  FailedScheduling  55s (x3 over 11m)  default-scheduler  0/1 nodes are available: 1 node(s) had untolerated taint {node-role.kubernetes.io/control-plane: }. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling.

4、解决方式

两种方式:

  • 1)允许Pod调度到控制平面节点,移除控制平面节点的污点,命令如下所示(适用于测试环境)
  • 2)为Redis Pod添加容忍,指定tolerations,此处暂不展开。(适合生产环境)

移除”控制平台节点”的taint后,可看到就自动开始创建容器了:

1
2
3
4
5
6
7
8
9
10
11
# 移除控制平面节点的污点(taint)
[root@xdlinux ➜ ~ ]$ kubectl taint nodes --all node-role.kubernetes.io/control-plane-
node/xdlinux untainted

# 移除后就开始创建容器了
[root@xdlinux ➜ ~ ]$ kubectl get all
NAME        READY   STATUS              RESTARTS   AGE
pod/redis   0/1     ContainerCreating   0          18m

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2d15h

但是状态变成ImagePullBackOff了,系统日志可看到还是pull镜像失败了,docker.io/library/redis:alpine

1
2
3
4
5
6
[root@xdlinux ➜ ~ ]$ kubectl get pod redis
NAME    READY   STATUS             RESTARTS   AGE
redis   0/1     ImagePullBackOff   0          23m

# tail -f /var/log/messages
Jul 23 22:57:43 xdlinux kubelet[197244]: E0723 22:57:43.256904  197244 pod_workers.go:1301] "Error syncing pod, skipping" err="failed to \"StartContainer\" for \"redis\" with ImagePullBackOff: \"Back-off pulling image \\\"redis:alpine\\\": ErrImagePull: failed to pull and unpack image \\\"docker.io/library/redis:alpine\\\": failed to resolve image: failed to do request: Head \\\"https://registry-1.docker.io/v2/library/redis/manifests/alpine\\\": dial tcp 162.220.12.226:443: connect: connection refused\"" pod="default/redis" podUID="c21adea0-dbfc-4936-bce2-8db56b48d4fb"

3.3. 调整:修改containerd镜像源

5、修改containerd镜像源,修改/etc/containerd/config.toml配置并重启containerd服务

1
2
3
4
5
6
7
8
9
# 在/etc/containerd/config.toml中新增下述项(存在则修改)。不存在文件则创建,参考上篇
  [plugins."io.containerd.grpc.v1.cri".registry]
    [plugins."io.containerd.grpc.v1.cri".registry.mirrors]
      [plugins."io.containerd.grpc.v1.cri".registry.mirrors."docker.io"]
      endpoint = ["https://docker.m.daocloud.io"]

# 重启并查看是否正常启动
[root@xdlinux ➜ ~ ]$ systemctl restart containerd.service
[root@xdlinux ➜ ~ ]$ systemctl status containerd.service

但是toml解析这些新增的项都失败了,unknown key

1
2
3
4
Jul 24 23:45:14 xdlinux containerd[358071]: time="2025-07-24T23:45:14.978826944+08:00" level=info msg="loading plugin" id=io.containerd.grpc.v1.cri type=io.containerd.grpc.v1
Jul 24 23:45:14 xdlinux containerd[358071]: time="2025-07-24T23:45:14.978870876+08:00" level=warning msg="Ignoring unknown key in TOML for plugin" error="strict mode: fields in the document are missing in the target struct" key=registry plugin=io.containerd.grpc.v1.cri
Jul 24 23:45:14 xdlinux containerd[358071]: time="2025-07-24T23:45:14.978881352+08:00" level=warning msg="Ignoring unknown key in TOML for plugin" error="strict mode: fields in the document are missing in the target struct" key="registry mirrors" plugin=io.containerd.grpc.v1.cri
Jul 24 23:45:14 xdlinux containerd[358071]: time="2025-07-24T23:45:14.978889454+08:00" level=warning msg="Ignoring unknown key in TOML for plugin" error="strict mode: fields in the document are missing in the target struct" key="registry mirrors docker.io" plugin=io.containerd.grpc.v1.cri

原因是 plugins."io.containerd.grpc.v1.cri".registry.mirrors 在 containerd v2.2里已经弃用了,具体见:deprecated-config-properties

解决方式:使用 config_path 方式进行替换。

  • /etc/containerd/config.toml 文件中,修改config_path
1
2
[plugins."io.containerd.cri.v1.images".registry]
   config_path = "/etc/containerd/certs.d"
  • 而后在 /etc/containerd/certs.d下面新建需要mirror的仓库,如:docker.io就新增同名目录

由于上面报 https://registry-1.docker.io/v2/library/redis/manifests/alpine\\\": dial tcp 162.220.12.226:443: connect: connection refused,所以再新增一个 registry-1.docker.io目录,拷贝一份hosts.toml

1
2
3
4
5
6
7
8
9
10
11
12
13
[root@xdlinux ➜ certs.d ]$ tree /etc/containerd/certs.d
/etc/containerd/certs.d
├── docker.io
│   └── hosts.toml
└── registry-1.docker.io
    └── hosts.toml

# 内容如下
[root@xdlinux ➜ docker.io ]$ cat /etc/containerd/certs.d/docker.io/hosts.toml 
server = "https://docker.m.daocloud.io"

[host."https://docker.m.daocloud.io"]
  capabilities = ["pull", "resolve"]

删除pod后重新创建:

1
2
3
4
5
6
# 删除pod
[root@xdlinux ➜ containerd ]$ kubectl delete pod redis
pod "redis" deleted

[root@xdlinux ➜ certs.d ]$ kubectl run redis --image='redis:alpine'
pod/redis created

可看到正常创建了:

1
2
3
4
5
6
[root@xdlinux ➜ ~ ]$ kubectl get all
NAME        READY   STATUS    RESTARTS   AGE
pod/redis   1/1     Running   0          34m

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   6d14h

3.4. 镜像踩坑小结

调整containerd镜像源折腾了挺久,从网上和LLM得到的配置方式存在一些滞后,主要是有些配置项已经弃用了(Deprecation),最后还是通过官方文档 deprecated-config-properties 确认了问题,上面同时提供了新的配置方式:config_path。进一步了解最好后续看看containerd的功能实现和文档。

3.5. 对外暴露(expose)Redis服务

上面Redis已部署但尚未创建对应的Service,要访问上述创建的Redis服务,还需要创建Redis Service。操作如下:

3.5.1. 创建Redis Service

创建 redis-service.yaml来定义Redis Service,其内容如下,并kubectl apply(之前selector里app: redis不对,调整为run: redis,可kubectl get pods redis --show-labels查看pod的标签)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# Service文件
[root@xdlinux ➜ hello git:(main)]$ cat redis-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis-service
spec:
  selector:
    run: redis  # 匹配Pod的标签(需根据实际标签调整)
  ports:
    - protocol: TCP
      port: 6379        # Service端口
      targetPort: 6379  # Pod端口(Redis默认端口)
  type: ClusterIP       # 集群内部访问,也可改为NodePort暴露到外部

# 应用配置
[root@xdlinux ➜ hello git:(main)]$ kubectl apply -f redis-service.yaml
service/redis-service created

可看到服务起来了:

1
2
3
4
5
6
7
[root@xdlinux ➜ hello git:(main)]$ kubectl get all
NAME        READY   STATUS    RESTARTS   AGE
pod/redis   1/1     Running   0          38m

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/kubernetes      ClusterIP   10.96.0.1       <none>        443/TCP    6d14h
service/redis-service   ClusterIP   10.105.22.241   <none>        6379/TCP   11s

但此时还是不能连的:

1
2
3
[root@xdlinux ➜ hello git:(main)]$ redis-cli -h 10.105.22.241
Could not connect to Redis at 10.105.22.241:6379: Connection refused
not connected> 

3.5.2. 设置外部访问Redis的方式

支持下述几种方式对外提供访问方式:

  • 1、通过 kubectl port-forward (推荐开发环境)
    • 命令:kubectl port-forward service/redis-service 6379:6379,这会将本地的 6379 端口转发到 Redis Service 的 6379 端口
1
2
3
[root@xdlinux ➜ redis-server git:(main)]$ kubectl port-forward service/redis-service 6379:6379
Forwarding from 127.0.0.1:6379 -> 6379
Forwarding from [::1]:6379 -> 6379
  • 2、通过NodePort暴露到外部(生产环境需谨慎)
    • 修改ServicetypeNodePort,并重新应用配置

新增一个NodePort对应的redis-nodeport.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[root@xdlinux ➜ redis-server git:(main)]$ cat redis-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
  name: redis-service
spec:
  selector:
    run: redis  # 匹配Pod的标签(需根据实际标签调整)
  ports:
    - protocol: TCP
      port: 6379        # Service端口
      targetPort: 6379  # Pod端口(Redis默认端口)
      nodePort: 30000   # 可选指定NodePort(范围30000-32767)
  type: NodePort


# 查看标签,相应调整上面的`run: redis`(而不是`app: redis`)
[root@xdlinux ➜ redis-server git:(main)]$ kubectl get pods redis --show-labels
NAME    READY   STATUS    RESTARTS   AGE   LABELS
redis   1/1     Running   0          85m   run=redis

应用:

1
2
3
4
5
6
7
8
9
[root@xdlinux ➜ redis-server git:(main)]$ kubectl apply -f redis-nodeport.yaml 
service/redis-service configured
[root@xdlinux ➜ redis-server git:(main)]$ kubectl get all
NAME        READY   STATUS    RESTARTS   AGE
pod/redis   1/1     Running   0          62m

NAME                    TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/kubernetes      ClusterIP   10.96.0.1       <none>        443/TCP          6d14h
service/redis-service   NodePort    10.105.22.241   <none>        6379:30000/TCP   24m

service服务的描述信息:

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
# 正常情况下的描述信息如下,注意标签是run=redis
[root@xdlinux ➜ redis-server git:(main)]$ kubectl describe service redis-service
Name:                     redis-service
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 run=redis
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.105.22.241
IPs:                      10.105.22.241
Port:                     <unset>  6379/TCP
TargetPort:               6379/TCP
NodePort:                 <unset>  30000/TCP
Endpoints:                10.244.0.13:6379
Session Affinity:         None
External Traffic Policy:  Cluster
Internal Traffic Policy:  Cluster
Events:                   <none>

# 这里也贴一下之前有问题(标签当时为`app: redis`,和pod不符)的描述信息作为对比
# 根本原因:Service 的 Selector 与 Pod 标签不匹配,导致 kube-proxy 无法将 Service 关联到 Pod。且Endpoints 为空
[root@xdlinux ➜ redis-server git:(main)]$ kubectl describe service redis-service
Name:                     redis-service
Namespace:                default
Labels:                   <none>
Annotations:              <none>
Selector:                 app=redis
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.105.22.241
IPs:                      10.105.22.241
Port:                     <unset>  6379/TCP
TargetPort:               6379/TCP
NodePort:                 <unset>  30000/TCP
Endpoints:                
Session Affinity:         None
External Traffic Policy:  Cluster
Internal Traffic Policy:  Cluster
Events:                   <none>

可看到防火墙规则正常生成了。

1
2
3
4
5
6
# 检查iptables规则是否自动生成
[root@xdlinux ➜ redis-server git:(main)]$ iptables -t nat -L KUBE-NODEPORTS -n -v | grep 30000
    0     0 KUBE-EXT-DHQ3MZCC7Y6T2RHF  tcp  --  *      *       0.0.0.0/0            0.0.0.0/0            /* default/redis-service */ tcp dpt:30000
[root@xdlinux ➜ redis-server git:(main)]$ kubectl get nodes -o wide
NAME      STATUS   ROLES           AGE     VERSION   INTERNAL-IP     EXTERNAL-IP   OS-IMAGE                      KERNEL-VERSION                 CONTAINER-RUNTIME
xdlinux   Ready    control-plane   6d15h   v1.33.3   192.168.1.150   <none>        Rocky Linux 9.5 (Blue Onyx)   5.14.0-503.40.1.el9_5.x86_64   containerd://2.1.3

redis-cli -h 192.168.1.150 -p 30000可以正常连接了。

  • 注意对应的ip是node对应的ip,而不是上面显示的10.105.22.241。这里的10.105.22.241是Service对应的CLUSTER-IP,这是一个仅在集群内部可访问的虚拟IP
  • Cluster IP的设计说明:
    • Cluster IP是 Kubernetes 为 Service 分配的内部虚拟IP,仅用于集群内部 Pod 之间或节点与 Service 之间的通信。外部网络无法直接访问 Cluster IP
    • Cluster IP不对应任何物理网卡或网络设备,它由kube-proxy通过iptablesIPVS规则实现流量转发。外部网络无法路由到这个虚拟IP。
1
2
3
4
5
6
[root@xdlinux ➜ redis-server git:(main)]$ redis-cli -h 192.168.1.150 -p 30000
192.168.1.150:30000> 
192.168.1.150:30000> set xdtest 1111
OK
192.168.1.150:30000> get xdtest
"1111"

NodePort模式下的流量链路:外部请求 → 节点 IP:30000 → kube-proxy 转发 → Cluster IP:6379 → Redis Pod。

也可以:redis-cli -h 10.105.22.241,虽然ping 10.105.22.241是不通的。

1
2
3
4
5
6
7
8
[root@xdlinux ➜ certs.d ]$ redis-cli -h 10.105.22.241
10.105.22.241:6379> get xdtest
"1111"
10.105.22.241:6379> 
# 查看redis:alpine版本信息
10.105.22.241:6379> info server
redis_version:8.0.3
...

4. 扩容Redis服务

上述使用独立Pod方式部署了Redis,无法实现自动扩容。要实现自动扩容,这里使用StatefulSet替换Deployment

1、创建 Redis StatefulSet 配置

创建redis-statefulset.yaml文件

2、应用配置

1
2
3
4
5
6
# 删除现有Pod和Service
kubectl delete pod redis
kubectl delete service redis-service

# 创建新的Service和StatefulSet
kubectl apply -f redis-statefulset.yaml

查看状态:

1
2
3
4
5
6
7
8
9
10
[root@xdlinux ➜ redis-stateful-set git:(main)]$ kubectl get all
NAME          READY   STATUS    RESTARTS   AGE
pod/redis-0   0/1     Pending   0          43s

NAME                     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/kubernetes       ClusterIP   10.96.0.1    <none>        443/TCP    6d15h
service/redis-headless   ClusterIP   None         <none>        6379/TCP   43s

NAME                     READY   AGE
statefulset.apps/redis   0/1     43s

定位:可以kubectl describe pod redis-0查看原因。可看到是没有可用的PV

1
2
3
4
Events:
  Type     Reason            Age                    From               Message
  ----     ------            ----                   ----               -------
  Warning  FailedScheduling  2m15s (x2 over 7m19s)  default-scheduler  0/1 nodes are available: pod has unbound immediate PersistentVolumeClaims. preemption: 0/1 nodes are available: 1 Preemption is not helpful for scheduling.

重新调整为临时存储,而不用PV。kubectl delete几个资源后重新apply,可看到正常创建了。

1
2
3
4
5
6
7
8
9
10
11
[root@xdlinux ➜ redis-stateful-set git:(main)]$ kubectl get all
NAME          READY   STATUS    RESTARTS   AGE
pod/redis-0   1/1     Running   0          32s

NAME                     TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)    AGE
service/kubernetes       ClusterIP   10.96.0.1    <none>        443/TCP    6d16h
service/redis-headless   ClusterIP   None         <none>        6379/TCP   32s

NAME                     READY   AGE
statefulset.apps/redis   1/1     32s

3、创建 NodePort Service 暴露 Redis

创建:redis-nodeport.yaml

1
2
3
4
5
6
7
8
9
10
11
[root@xdlinux ➜ redis-stateful-set git:(main)]$ kubectl get all
NAME          READY   STATUS    RESTARTS   AGE
pod/redis-0   1/1     Running   0          113s

NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          6d16h
service/redis-headless   ClusterIP   None            <none>        6379/TCP         113s
service/redis-service    NodePort    10.107.73.216   <none>        6379:30000/TCP   6s

NAME                     READY   AGE
statefulset.apps/redis   1/1     113s

验证,通过映射端口和Cluster-Ip都可以访问:

1
2
3
4
5
[root@xdlinux ➜ redis-stateful-set git:(main)]$ redis-cli -h 192.168.1.150 -p 30000 ping
PONG

[root@xdlinux ➜ redis-stateful-set git:(main)]$ redis-cli -h 10.107.73.216 ping
PONG

4、水平扩容(增加Redis副本数):kubectl scale statefulset redis --replicas=3

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@xdlinux ➜ redis-stateful-set git:(main)]$ kubectl scale statefulset redis --replicas=3
statefulset.apps/redis scaled

# 可看到有3个pod了(需要另外手动配置 Redis 主从关系实现读写分离,此处暂不进行后续操作)
[root@xdlinux ➜ redis-stateful-set git:(main)]$ kubectl get all
NAME          READY   STATUS    RESTARTS   AGE
pod/redis-0   1/1     Running   0          4m44s
pod/redis-1   1/1     Running   0          6s
pod/redis-2   1/1     Running   0          5s

NAME                     TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
service/kubernetes       ClusterIP   10.96.0.1       <none>        443/TCP          6d16h
service/redis-headless   ClusterIP   None            <none>        6379/TCP         4m44s
service/redis-service    NodePort    10.107.73.216   <none>        6379:30000/TCP   2m57s

NAME                     READY   AGE
statefulset.apps/redis   3/3     4m44s

纵向扩容则可kubectl edit statefulset redis调整硬件资源。

kubectl exec -it redis-0 -- bash 可进入Pod,注意需要在命令前(包括/bin/bash)加--,不加--的方式已经不支持了。

  • -i 保持标准输入打开
  • -t 分配一个伪终端(TTY)
  • -- 用于分隔 kubectl 命令和要在 Pod 中执行的命令
1
2
3
4
5
6
7
8
9
# 提示不支持直接接命令了,而需要在命令前加上`--`
[root@xdlinux ➜ ~ ]$ kubectl exec -it redis-0 bash
error: exec [POD] [COMMAND] is not supported anymore. Use exec [POD] -- [COMMAND] instead
See 'kubectl exec -h' for help and examples

# 加上 --
[root@xdlinux ➜ ~ ]$ kubectl exec -it redis-0 -- bash
root@redis-0:/data# ls
appendonlydir

5. 小结

基于上篇环境进行基本命令操作;并通过K8s创建了一个Redis的Pod、Service;以及集群扩容操作。过程中由于containerd新版本弃用调整了镜像配置的方式也折腾了挺久,创建Pod后,通过Service对外提供服务访问,增强了一些体感。

6. 参考

This post is licensed under CC BY 4.0 by the author.