跳转至

K8s的架构与yaml

一. K8s架构

1. Kubernetes概述

Kubernetes(简称K8s,使用go语言开发),是一个全新的基于容器技术的分布式架构领先方案,是谷歌严格保密十几年的秘密武器 ---- Borg系统的一个开源版本,于2014年9月发布第一个版本,2015年7月发布第一个正式版本。

Kubernetes本质上是一组服务器集群,它可以在集群的每个节点上运行特定的程序来对节点的容器进行管理。目的是实现资源管理的自动化,主要是提供了如下功能:

  • 自我修复: 一旦某个容器崩溃,能在1秒钟左右迅速启动新的容器
  • 弹性伸缩: 可以根据需要,自动对集群中正在运行的容器数量进行调整
  • 服务发现: 服务可以通过自动发现的形式找到它所依赖的服务
  • 负载均衡: 如果一个服务启动了多个容器,能够自动实现请求的负载均衡
  • 滚动更新: 可以根据应用的变化,对应用容器运行的应用,进行一次性或批量式的更新
  • 版本回退: 如果发现新发布的程序版本有问题,可以立即回退到原来的版本。
  • 存储编排: 可以根据容器自身的需求自动创建存储卷

2. Kubernetes组件

一个Kubernetes集群主要是由主控节点(master)、工作节点(node)构成,每个节点上都会安装不同的组件。

master: 指的是集群的主控节点,对集群进行调度管理,接受集群外用户对集群操作的请求。比如所有的增加修改删除查找容器操作都是在master完成。

在每一个Kubernetes集群中有需要一个master节点来负责整个集群的管理和控制,master节点一般会占用一个独立的服务器(高可用部署建议至少有三台服务器)

Master由API Server、Scheduler、Controller Manager、ClusterState Store(如Etcd数据库)等构成

  • API Server: 资源操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制

Kubernetes API Server的核心功能是提供Kubernetes各类资源对象(如Pod、RC、Service等)的增删改查及watch等

Kubernetes API Server负责和etcd交互(其他组件不会直接操作etcd,只有API Server这么做),整个Kubernetes集群的所有交互都是以API Server为核心的

如:

  1. 所有对集群进行查询和管理都要通过API进行
  2. 所有组件之间并不会相互调用,而是通过和API Server打交道来完成自己的那部分工作
  • Scheduler: 调度器。负责集群资源调度,按照预定的调度策略将Pod调度到相应的node节点上
  • Controller Manager: 控制管理器。Kubernetes里所有的资源对象的自动化控制中心,可以将其理解为资源对象的“大总督”,负责维护集群的状态,比如程序的部署安排、故障检测、自动扩展、滚动更新等。

Controller Manager内部包括Replication Controller、Node Controller、 ResourceQuota Controller、Namespace Controller、ServiceAccount Controller、 Token Controller、Service、Controller以及Endpoint Controller这八种Controller, 每种Controller都负责一种特定子团的控制流程,而Controller Manager正是这些Controller的核心管理者。

  • Etcd: 一个高可用的K/V键值对存储和服务发现系统,负责存储集群中各种资源对象的信息

注意: 由于etcd是负责存储Kubernetes所有集群数据的后台数据库,所以不建议搭建单节点集群,如zookeeper一样,由于存在选举策略,所以一般推荐奇数个集群,如3,5,7。只要集群半数以上的节点存活,那么集群就可以正常运行,否则集群可能无法正常使用。

node: 集群工作节点,运行用户业务应用容器。负责为容器提供运行环境和运行资源

Worker Node包含kubelet、kube-proxy和Container Runtime(如docker)

  • Kubelet: 负责维护容器的生命周期,即通过控制docker,来创建、更新、销毁容器。同时也负责Volume(CSI)和网络(CNI)的管理

在Kubernetes集群中,每个Node上都会启动一个kubelet服务进程。该进程用于处理Master下发到本节点的任务,管理Pod以及Pod中的容器。每个kubelet进程都会在API Server上注册节点自身的信息,定期向Master汇报节点资源的使用情况,并通过cAdvisor监控容器和节点资源。会和CRI(docker容器引擎)进行交互,操作docker创建对应的容器,kubelet会维护Pod的生命周期。

  • kube-proxy: 负责提供集群内部的服务发现和负载均衡

kube-proxy负责为Service提供k8s集群内部的服务发现和负载均衡;kube-proxy是一个简单的网络访问代理,维护网络规则和四层负载均衡工作,做端口转发,像nginx、haproxy做反向代理一样,同时也是一个Load Balancer。它负责将访问到某个服务的请求具体分配给工作节点上的Pod(同一类标签)

alt text

  • Container Runtime: 负责节点上容器的各种操作

Container Runtime, 负责镜像管理以及容器真正的运行。如docker,每一个Node都会运行一个docker引擎,其负责下载镜像和运行容器。Kubernetes本身并不提供容器软件运行环境,但提供了接口(CRI),可以按照所选择的容器软件环境。

Kubelet使用Unix socket之上的gRPC框架(高性能远程过程调用框架)与容器软件通信,kubelet作为客户端,而CRI shim(容器运行时接口)作为服务器。

alt text

Kubernetes CRI支持的容器软件包括docker、rkt、cri-o、frakti、kata-containers和clear-containers等。

3. Kubernetes概念

  • Master: 集群控制节点。每个集群需要至少一个master节点负责集群的管控
  • Node: 工作负载节点。由master分配容器到这些node节点上,然后node节点上的docker负责容器的运行
  • Pod: (中文意思为豆荚)Kubernetes的最小控制单元,容器都是运行在Pod中,一个Pod可以有一个或多个容器,Kubernetes每个Pod都分配了唯一的IP地址,称之为PodIP,一个Pod里多个容器共享PodIP地址,它负责外部跟容器直接的通信
  • Controller: 控制器,通过它来实现对Pod的管理,比如启动Pod、停止Pod、伸缩Pod数量等
  • Service: Pod对外服务的统一入口,下面可以维护同一类的多个Pod
  • Label: 标签,用于对Pod进行分类,同一类Pod会拥有相同的标签
  • NameSpace: 命名空间,用来隔离Pod的运行环境

4. 创建一个Pod的内部流程

alt text

  1. 用户提交创建Pod请求,可以通过kubectl等命令行工具,也支持json和yaml两种格式文件
  2. API Server处理用户请求,存储要创建的Pod数据到etcd数据库
  3. Scheduler(调度器)通过和API Server的watch(监听)机制,查看到新的Pod,尝试为新的Pod绑定node节点
  4. 过滤主机: 调度器用一组规则过滤掉不符合要求的主机,比如Pod指定了所需要的资源,那么就要过滤掉资源不够的主机
  5. 主机打分: 对第一步筛选出的符合要求的主机进行打分,在主机打分阶段调度器会考虑一些整体优化策略,比如把一个Replication Controller的副本分配到不同的主机上,使用最低负载的主机等。
  6. 选择主机: 选择打分最高的主机,进行binding操作,把结果存储到etcd中。
  7. kubelet根据调度结果执行Pod创建操作: 绑定成功后,会启动container,docker run启动容器, scheduler会调用API Server在etcd中创建一个bound Pod对象, 描述在一个工作节点上绑定运行的所有Pod信息。 运行在每个工作节点上的kubelet也会定期与etcd同步bound Pod信息, 一旦发现应该在该工作节点上运行的bound Pod对象没有更新, 则调用Docker API创建并启动Pod内的容器。

5. Kubernetes资源介绍

在Kubernetes中,所有的内容都抽象为资源,用户需要通过操作系统来管理Kubernetes

因为Kubernetes的本质上是一个集群系统,用户可以在集群中部署各种服务,所谓的部署服务,就是在Kubernetes集群中运行一个个的容器,并将指定的程序跑在容器里面。

Kubernetes的最小管理单元是Pod而不是容器,所以只能将容器放在Pod里面,而Kubernetes一般也不会直接管理Pod,而是通过Pod的控制器来管理Pod。

Pod可以提供服务后,就要考虑如何访问Pod服务,Kubernetes提供了Service资源来实现这个功能。

当然,如果Pod中的程序数据需要持久化,Kubernetes还提供了各种存储系统。

学习Kubernetes的核心,就是学习如何对集群上的Pod、Pod控制器、Service、存储等各种资源进行操作

资源类型:

Kubernetes中所有的内容都抽象为资源,可以通过kubectl api-resources命令进行查看

经常使用的资源有下面这些

资源分类 资源名称 缩写 资源作用
集群级别资源 nodes no 集群组成部分
namespaces ns 隔离Pod
Pod资源 pods po 装载容器
Pod资源控制器 replicationcontrollers rc 控制Pod资源
replicasets rs 控制Pod资源
deployments deploy 控制Pod资源
daemonsets ds 控制Pod资源
jobs 控制Pod资源
cronjobs cj 控制Pod资源
horizontalpodautoscalers hpa 控制Pod资源
statefulsets sts 控制Pod资源
服务发现资源 services svc 统一pod对外接口
ingress ing 统一pod对外接口
存储资源 volumeattachments 存储
persistentvolumes pv 存储
persistentvolumeclaims pvc 存储
配置资源 configmaps cm 配置
secrets 配置

二. YAML文件详解

1. YAML文件概述

Kubernetes集群中对资源管理和资源对象编排部署都可以通过声明样式(即YAML)文件来解决,也就是可以把需要对资源对象的操作编译到YAML格式文件中,我们把这种文件叫做资源清单文件(或者资源描述文件),通过kubectl命令直接使用资源清单文件就可以实现对大量的资源对象进行编排部署了。

2. YAML文件书写格式

1) YAML语言介绍

YAML是一种类似于XML、JSON的标记性语言。他强调以数据文中学,并不是以标识语言为重点。YAML是一种可读性高用来表达数据序列的格式。因而YAML本身的定义比较简单,号称"一种人性化的数据格式语言"

XML

<info>
    <age>15</age>
    <address>Beijing</address>
</info>

YAML

info:
  age: 15
  address: Beijing

2) YAML基本语法

YAML的语法比较简单,主要有以下几个

  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许使用空格(低版本限制)
  • 缩进空格数不重要,只要相同层级的元素左对齐就可以
  • 使用'#'注释

3) YAML支持的数据结构

  • 纯量: 单个的、不可再分的组
  • 对象: 键值对的几何,又称为映射(mapping) / 哈希(hash) / 字典(dictionary)
  • 数组: 一组按次序排序的值,又称为序列(sequence) / 列表(list)
  • 纯量: 就是一个简单的值,字符串、布尔值、整数、浮点数、Null、时间、日期类型的数据都属于纯量
# 1. 布尔类型,布尔值用true和false表示
c1: ture(或者Ture)

# 2. 整型,数值直接以字面量的形式表示
c2: 234

# 3. 浮点型
c3: 3.14

# 4. null类型
c4: ~   # 使用~表示null

# 5. 日期类型
c5: 2018-02-17      # 日期必须使用ISO 8601格式,即yyyy-MM-dd

# 6. 时间类型
c6: 2018-02-17T15:02:31+08:00       # 时间使用ISO8601格式,时间和日期之间使用T连接,最后使用+代表时区

# YAML允许使用两个感叹号,强制转换数据类型
a1: !!str 123
a2: !!str ture

# 7. 字符串类型
c7: hello               # 简单写法,直接写值
c8: "hello world"       # 如果字符串中间有特殊字符,必须使用双引号或单引号包裹
c9: 'hello\nworld'  
c10: "hello\nworld"     # 单双引号都可以使用,双引号不会对特殊字符转义
c11: 'labor''s day'     # 单引号之中如果如果还有单引号,必须使用连续的两个单引号转义

s1: line1
     line2      # 字符串可以写成多行。从第二行开始必须有一个单空格缩进。换行符还会被转为空格
# 多行字符串可以使用|保留换行符,也可以使用>折叠换行
s2: |
  Foo
  Bar
  Thhat

对象类型也称为映射或哈希或字典,对象的一组键值对,使用冒号结构表示

# 形式1(推荐)
info:
  age: 15
  address: Beijing

# 形式2(了解)
# Yaml也允许另外一种写法,将所有键值对写出一个行内对象
info: {age: 15,address: Beijing}

数组类型也称为序列或列表,可以指定任何数量的项在列表中,每个项的定义以破折号 (-) 开头,并且与父元素之间存在缩进。

# 形式1(推荐)
address:
  - 昌平
  - 顺义

address:
- 昌平
- 顺义

# 形式2(了解)
address: [顺义,昌平]

小提示:

  1. 书写yaml切记: 后面要加一个空格,破折号 - 后面要加一个空格
  2. 如果需要将多段yaml配置放在一个文件中,中间要使用---分隔
  3. 可以通过yaml转json的网站来验证yaml是否书写正确

3. Kubernetes的yaml配置

在k8s中,一般使用YAML格式的文件来创建符合我们预期期望的资源(如pod),这样的YAML文件称为资源清单

1) Kubernetes的yaml文件常用字段

Kubernetes的某个资源的YAML文件常用字段和参数可以执行下面的命令查看: 例如查看deployment的常用字段以及某个字段的参数

kubectl explain deployment
kubectl explain deployment.spec

接下来看看K8s的yaml文件都有哪些配置项。K8s的yaml配置分为四个部分:

  • apiVersion
  • kind
  • metadata
  • spec

说明:

apiVersion: 表示指定api版本,通常指定最新的稳定版API,这个版本号需要根据安装的Kubernetes版本和资源类型进行变化,记住不是写死的。此值可以在master节点上执行kubectl api-versions命令查看。

kind: 表示yaml定义的资源类型。k8s中的资源有很多种,包括Pod,Deployment,Job,Services等。

metadata: 表示创建的资源啊的一些元数据,这是个对象类型,里面包含名称、namespace、标签等信息。

spec: 这也是对象类型。内容包括一些container,storage,volime等对象以及其他的Kubernetes需要的参数,以及诸如是否在容器失败时重新启动容器的属性

这四个部分时k8s的yaml必须存在的配置项,如果没有,k8s时不允许执行的。

注: Kubernetes API参考能够帮助我们找到任何和我们像创建的对象spec格式。

常用字段

参数名 字段类型 说明
apiversion string k8s的API版本,目前基本是v1,可以用kubectl api-version命令查询
kind string 这里指的是yaml文件顶翻译的资源类型和角色,比如Pod
metadata object 元数据对象,固定值写metadata
metadata.name string 元数据对象的名字,这里由我们编写,比如命名Pod的名字
metadata.namespace string 元数据对象的命名空间,由我们自身定义
spec object 详细定义对象,固定值写spec
spec.containers[] list 这里是spec对象的容器列表定义,是个列表
spec.containers[].name string 这里定义容器的名字
spec.containers[].image srting 这里定义要用到的镜像名称
spec.containers[].imagePullPolicy string 定义镜像拉取策略。有always、never、ifnotpresent三个值可选,默认值为always。详见下方说明
spec.containers[].command[] list 指定容器启动命令。因为数组可以指定多个,不指定则使用镜像打包时使用的命令启动
spec.containers[].args[] list 指定容器启动命令参数,因为是数组可以指定多个
spec.containers[].workingDir string 指定容器的工作目录
spec.containers[].volumeMounts[] list 指定容器内部存储卷配置
spec.containers[].volumeMounts[].name string 指定容器挂载的存储卷名称
spec.containers[].volumeMounts[].mountpath string 指定可以被容器挂载的容器卷路径
spec.containers[].volumeMounts[].readOnly string 设置存储卷路径的读写方式,ture或者false,默认为读写模式
spec.containers[].ports[] list 指定容器需要用到的端口列表
spec.containers[].ports[].name string 指定端口名称
spec.containers[].ports[].containerPort string 指定容器需要监听的端口号
spec.containers[].ports[].hostPort string 指定容器所在主机所需要监听的端口号。默认与上面的containerPort相同。注意设置了hostPort同一台主机无法启动该容器的相同副本,因为端口号不能相同,所以会冲突
spec.containers[].ports[].protocol string 指定端口协议,支持tcp和udp。默认值为tcp
spec.containers[].env list 指定容器运行时需要设置的环境变量列表
spec.containers[].env.name string 指定环境变量名称
spec.containers[].env.value string 指定环境变量的值
spec.containers[].resources object 指定资源限制和资源请求的值(这里开始就是设置容器的资源上限)
spec.containers[].resources.cpu string 指定CPU的限制。单位为core数,将用于docker run -cpu-shares参数
spec.containers[].resources.memory string 指定内存的限制。单位为MIB、GIB
spec.containers[].resources.requests object 指定容器启动和调度是资源请求的设置
spec.containers[].resources.requests.cpu string CPU请求。单位为core数。容器启动时初始化可用数量
spec.containers[].resources.requests.memory string 内存请求。单位为MIB、GIB。容器启动时初始化可用数量

镜像拉取策略说明:

  • always: 每次都尝试重新从镜像仓库拉取镜像
  • never: 仅使用本地镜像
  • ifnotpresent: 如果本地有镜像就使用本地镜像,没有则从镜像仓库拉取镜像

2) yaml文件应用实例

案例: 使用yaml创建deployment管理mysql应用。

使用kubectl run/kubectl create在实现复杂的需求时需要非常长的一条语句很容易出错,也没办法保存。所以在更多场景下yaml和json文件会被用到。我们可以通过kubectl create/kubectl apply加载yaml文件生成deployment

Ⅰ. 生成mysql-deployment.yaml文件

[root@K8s2 ~]# ls mysql.tar
mysql.tar
[root@K8s2 ~]# nerdctl --namespace k8s.io load -i mysql.tar
unpacking docker.io/library/mysql:latest (sha256:7d370040e13c1014e0981f7794b69293d6e97f6dc78b70add95e5501629c6861)...
Loaded image: mysql:latest
# 操作k8s时候需要指定命名空间k8s.io,可以使用--namespace或者-n

# 也可以在线下载镜像
# nerdctl -n k8s.io pull mysql:8.0
# 或 nerdctl pull mysql:8.0   
# nerdctl -n default save mysql:8.0 | nerdctl -n k8s.io load
[root@K8s3 ~]# ls mysql.tar
mysql.tar
[root@K8s3 ~]# nerdctl -n k8s.io load -i mysql.tar
unpacking docker.io/library/mysql:latest (sha256:7d370040e13c1014e0981f7794b69293d6e97f6dc78b70add95e5501629c6861)...
Loaded image: mysql:latest

Ⅱ. 编写mysql-deployment.yaml文件

vi mysql-deployment.yaml

kind: Deployment
# kind 指定创建资源的角色/类型。 这里使用deployment创建一个Pod
apiVersion: apps/v1
# 指定API的版本,此值必须在kubectl api-versions中
metadata: 
# 资源的元数据/属性
  name: mysql
  # 定义deployment的名字,在同一个namespace中必须唯一
spec:
# spec设置该资源的内容
  replicas: 1
  # Pod副本的期待数量(tells deployment to run 1 pods matching the template)
  selector: 
  # 标签选择器
    matchLabels:
    # 标签匹配
      app: mysql
      # 匹配标签app: mysql
  template:
  # pod模板,根据此模板创建Pod的副本(实例)
    metadata:
    # 元数据
      labels:
      # 设定资源的标签,通常用于标签选择器(Label Selector),用来匹配目标对象
        app: mysql
        # 定义Pod的表情,key是name,value是mysql
    spec:
      containers:
      # Pod中容器的定义部分
        - name: mysql
        # 容器的名称
          image: mysql:latest
          # 容器对应的Docker Image 镜像,也可以从私有仓库拉取镜像。如10.192.21.18:5000/test/mysql:lastest
          imagePullPolicy: IfNotPresent
          # 三个选项分别为Always、Never、ifNotPresent,每次启动时检查和更新(从registery)images的策略。默认值为: imagePullPolicy: Always。一直从仓库下载镜像而不使用本地的。
          # 参数说明: ifNotPresent: 如果本地存在则优先使用本地镜像,否则从仓库下载镜像。  Never: 只使用本地镜像,如果不存在则不下载。
          ports:
            - containerPort: 3306
            # 容器对外开放的端口
              protocol: TCP
          env:
          # 注入到容器的环境变量
            - name: MYSQL_ROOT_PASSWORD
            # 设置mysql root 的密码
              value: "hello123"

# 注: mysql-deployment.yaml文件的结构为:

# Deployment的定义
  # pod的定义
    # 容器的定义  

Ⅲ. 使用mysql-deployment.yaml创建和删除mysql资源

[root@K8s1 ~]# kubectl create -f mysql-deployment.yaml 
deployment.apps/mysql created
# 可以使用命令 kubectl exec -it mysql-7778cb6f46-64s4h -- mysql -uroot -phello123进入数据库

注: 当一个目录下有多个yaml文件的时候,使用kubectl create -f 目录 的方式可以一次性全部创建。比如kubectl create -f yamls/

Ⅳ. 使用get查看pod的详细信息

[root@K8s1 ~]# kubectl get deployment
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
mysql   1/1     1            1           3m51s

-o wide参数可以查看更详细的信息,比如看到此pod在哪个node上运行,此pod的集群IP是多少也被一并显示

[root@K8s1 ~]#  kubectl get pod -o wide
NAME                     READY   STATUS    RESTARTS   AGE     IP              NODE   NOMINATED NODE   READINESS GATES
mysql-7778cb6f46-54hz8   1/1     Running   0          5m41s   10.244.109.65   k8s2   <none>           <none>

其中10.244.109.65这个IP地址是 Calico中定义的网段中的一个IP地址。Pod通过这个IP和主节点进行通信

Ⅴ. 测试

# calico的地址可以ping通
[root@K8s1 ~]# ping 10.244.109.65
PING 10.244.109.65 (10.244.109.65) 56(84) 比特的数据。
64 比特,来自 10.244.109.65: icmp_seq=1 ttl=63 时间=2.32 毫秒
64 比特,来自 10.244.109.65: icmp_seq=2 ttl=63 时间=0.387 毫秒
64 比特,来自 10.244.109.65: icmp_seq=3 ttl=63 时间=0.447 毫秒
^C
--- 10.244.109.65 ping 统计 ---
已发送 3 个包, 已接收 3 个包, 0% packet loss, time 2009ms
rtt min/avg/max/mdev = 0.387/1.052/2.323/0.898 ms

# K8s2的tunl0也可以ping通
[root@K8s1 ~]# ping -c 3 10.244.109.64
PING 10.244.109.64 (10.244.109.64) 56(84) 比特的数据。
64 比特,来自 10.244.109.64: icmp_seq=1 ttl=64 时间=1.02 毫秒
64 比特,来自 10.244.109.64: icmp_seq=2 ttl=64 时间=0.377 毫秒
64 比特,来自 10.244.109.64: icmp_seq=3 ttl=64 时间=0.441 毫秒

--- 10.244.109.64 ping 统计 ---
已发送 3 个包, 已接收 3 个包, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.377/0.613/1.021/0.289 ms

总结: K8s1,K8s2,pod,container直接的通信都是使用Calico 分配地址(10.244.x.x),用 IPIP 隧道(tunl0)把物理上分开的节点和容器连成一个逻辑局域网。

Ⅵ. 查看标签

[root@K8s1 ~]# kubectl get pod --show-labels
NAME                     READY   STATUS    RESTARTS   AGE   LABELS
mysql-7778cb6f46-64s4h   1/1     Running   0          41m   app=mysql,pod-template-hash=7778cb6f465

Ⅶ. 通过标签查找Pod

[root@K8s1 ~]# kubectl get pod -l app=mysql
NAME                     READY   STATUS    RESTARTS   AGE
mysql-7778cb6f46-64s4h   1/1     Running   0          45m

Ⅷ. 在k8s3上查看运行mysql实例是否运行

[root@K8s3 ~]# nerdctl -n k8s.io ps | grep mysql
44790f0c23d1    docker.io/library/mysql:latest                        "docker-entrypoint.s…"    About an hour ago    Up      k8s://default/mysql-7778cb6f46-64s4h/mysql
8754824b2e1f    registry.aliyuncs.com/google_containers/pause:3.9     "/pause"                  About an hour ago    Up      k8s://default/mysql-7778cb6f46-64s4h

Ⅸ. 使用describe查看K8s的详细信息

describe 描述

a. 语法

语法: kubectl describe pod pod名字

语法: kubectl describe node node名字

语法: kubectl describe deployment deployment名字

使用describe查看pod的详细描述信息

[root@K8s1 ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
mysql-7778cb6f46-64s4h   1/1     Running   0          83m
[root@K8s1 ~]# kubectl describe pod mysql-7778cb6f46-64s4h
NAME                     READY   STATUS    RESTARTS   AGE
mysql-7778cb6f46-64s4h   1/1     Running   0          83m
[root@K8s1 ~]# kubectl describe pod mysql-7778cb6f46-64s4h 
Name:             mysql-7778cb6f46-64s4h
Namespace:        default
Priority:         0
Service Account:  default
Node:             k8s3/192.168.100.101
Start Time:       Sun, 02 Aug 2026 17:34:56 +0800
Labels:           app=mysql
                  pod-template-hash=7778cb6f46
Annotations:      cni.projectcalico.org/containerID: 8754824b2e1f0375785eaf00c3c8fe05ac1f53236bb3f0ad9df963605c9732cf
                  cni.projectcalico.org/podIP: 10.244.219.2/32
                  cni.projectcalico.org/podIPs: 10.244.219.2/32
Status:           Running
IP:               10.244.219.2
IPs:
  IP:           10.244.219.2
Controlled By:  ReplicaSet/mysql-7778cb6f46
Containers:
  mysql:
    Container ID:   containerd://44790f0c23d1c4f9f8fde8b9915ca4e6f2d0edac7d81b1baf77255e0d09d7558
    Image:          mysql:latest
    Image ID:       sha256:d236310860c6d039bb44b1e0569de1e6b6806ec5c40aa7c63560590fbde6b649
    Port:           3306/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Sun, 02 Aug 2026 17:34:57 +0800
    Ready:          True
    Restart Count:  0
    Environment:
      MYSQL_ROOT_PASSWORD:  hello123
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-wgbbg (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-wgbbg:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    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:                      <none>

三. Kubernetes集群命令行工具kubectl

1. kubectl描述

kubectl是kubernetes集群的命令行工具,通过它能够对集群本身进行管理。并能够在集群上进行容器化应用的安装部署。kubectl命令的语法如下

kubectl [command] [type] [name] [flags]

  • command: 指定要对资源执行的操作,例如create、get、delete、describe等
  • type: 指定资源类型,比如deployment、pod、service。资源类型是大小写敏感的。如果省略名称,则会显示所有的资源
  • flags: 指定额外的可选参数
# 查看所有Pod
kubectl get pod

# 查看某个pod
kubectl get pod pod_name

# 查看某个pod,以yaml格式显示结果
kubectl get pod pod_name -o yaml

# kubetnetes允许对资源进行多种操作,可以通过--help查看详细的操作命令
kubectl --help
[root@K8s1 ~]# kubectl get pod
NAME                     READY   STATUS    RESTARTS   AGE
mysql-7778cb6f46-64s4h   1/1     Running   0          125m
[root@K8s1 ~]# kubectl get pod mysql-7778cb6f46-64s4h 
NAME                     READY   STATUS    RESTARTS   AGE
mysql-7778cb6f46-64s4h   1/1     Running   0          136m

[root@K8s1 ~]# kubectl get pod mysql-7778cb6f46-64s4h -o yaml
apiVersion: v1
kind: Pod
metadata:
  annotations:
    cni.projectcalico.org/containerID: 8754824b2e1f0375785eaf00c3c8fe05ac1f53236bb3f0ad9df963605c9732cf
    cni.projectcalico.org/podIP: 10.244.219.2/32
    cni.projectcalico.org/podIPs: 10.244.219.2/32
  creationTimestamp: "2026-08-02T09:34:56Z"
  generateName: mysql-7778cb6f46-
  labels:
    app: mysql
    pod-template-hash: 7778cb6f46
  name: mysql-7778cb6f46-64s4h
  namespace: default
  ownerReferences:
  - apiVersion: apps/v1
    blockOwnerDeletion: true
    controller: true
    kind: ReplicaSet
    name: mysql-7778cb6f46
    uid: 029482ab-1554-4eb3-903a-f97dfbec5b93
  resourceVersion: "24399"
  uid: 8af45d77-e039-425f-b082-b703d8280786
spec:
  containers:
  - env:
    - name: MYSQL_ROOT_PASSWORD
      value: hello123
    image: mysql:latest
    imagePullPolicy: IfNotPresent
    name: mysql
    ports:
    - containerPort: 3306
      protocol: TCP
    resources: {}
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: kube-api-access-wgbbg
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  nodeName: k8s3
  preemptionPolicy: PreemptLowerPriority
  priority: 0
  restartPolicy: Always
  schedulerName: default-scheduler
  securityContext: {}
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
  - effect: NoExecute
    key: node.kubernetes.io/not-ready
    operator: Exists
    tolerationSeconds: 300
  - effect: NoExecute
    key: node.kubernetes.io/unreachable
    operator: Exists
    tolerationSeconds: 300
  volumes:
  - name: kube-api-access-wgbbg
    projected:
      defaultMode: 420
      sources:
      - serviceAccountToken:
          expirationSeconds: 3607
          path: token
      - configMap:
          items:
          - key: ca.crt
            path: ca.crt
          name: kube-root-ca.crt
      - downwardAPI:
          items:
          - fieldRef:
              apiVersion: v1
              fieldPath: metadata.namespace
            path: namespace
status:
  conditions:
  - lastProbeTime: null
    lastTransitionTime: "2026-08-02T09:34:56Z"
    status: "True"
    type: Initialized
  - lastProbeTime: null
    lastTransitionTime: "2026-08-02T09:34:57Z"
    status: "True"
    type: Ready
  - lastProbeTime: null
    lastTransitionTime: "2026-08-02T09:34:57Z"
    status: "True"
    type: ContainersReady
  - lastProbeTime: null
    lastTransitionTime: "2026-08-02T09:34:56Z"
    status: "True"
    type: PodScheduled
  containerStatuses:
  - containerID: containerd://44790f0c23d1c4f9f8fde8b9915ca4e6f2d0edac7d81b1baf77255e0d09d7558
    image: docker.io/library/mysql:latest
    imageID: sha256:d236310860c6d039bb44b1e0569de1e6b6806ec5c40aa7c63560590fbde6b649
    lastState: {}
    name: mysql
    ready: true
    restartCount: 0
    started: true
    state:
      running:
        startedAt: "2026-08-02T09:34:57Z"
  hostIP: 192.168.100.101
  phase: Running
  podIP: 10.244.219.2
  podIPs:
  - ip: 10.244.219.2
  qosClass: BestEffort
  startTime: "2026-08-02T09:34:56Z"

2. kubectl子命令使用分类

基础命令

基础命令 作用
create 通过文件名或标准输入创建资源
expose 将一个资源暴露为新的service
run 在集群中允许一个指定镜像
set 在对象上设置特定功能
get 显示一个或多个资源
explain 查看资源的参考文档
edit 编辑一个资源
delete 通过文件名或标准输入删除一个资源

部署命令和集群管理命令

部署命令 作用
rollout 管理资源的发布
rollout-update 对特定副本控制器滚动更新
scale 手动扩容或缩容pod数量
autoscale 自动扩容或缩容pod数量
集群管理命令 作用
certificate 修改证书资源
cluster-info 显示集群信息
top 显示资源(CPU/memory/storage)使用情况,需要metrics server运行
cordon 标记节点不可调度
uncordon 标记节点可调度
drian 驱逐节点上的应用准备下线维护
taint 修改节点taint标记

故障诊断和调试命令

故障管理和调试命令 作用
describe 显式指定资源的详细信息
logs 输出容器在pod中的日志。如果pod只有一个容器容器名可以省略
attach 进入运行中的容器
exec 在容器中执行命令
cp 从容器中拷贝出或向容器内拷贝入文件或目录

其他命令

高级命令 作用
apply 通过文件名或者标准输入对资源应用进行配置
patch 通过补丁修改、更新资源的字段
replace 通过文件名或标准输入替换一个资源
convert 不同api版本之间转换配置文件
设置命令 作用
label 更新资源上的标签
annotate 更新资源上的注释
其他命令 作用
api-versions 显示支持的API版本
cluster-info 显示集群信息
help 显示帮助
version 显示当前server和client的版本

五. 资源管理方式

1. 在K8S中,创建K8S资源有两种方式

方式1: 使用kubectl命令行命令行,输入kubectl命令并填上对应的参数

方式2: 配置yaml配置文件

2. 使用kubectl命令创建和删除一个pod以及相关操作

Ⅰ. kubectl run创建一个namespace/pod

a. 上传导入镜像

提前将镜像nginx.tar上传到k8s2和k8s3上并导入镜像,或使用命令nerdctl -n k8s.io pull nginx:latest拉取镜像

b. 创建一个namespace/pod

[root@K8s1 ~]# kubectl create namespace dev
namespace/dev created
[root@K8s1 ~]# kubectl get ns
NAME              STATUS   AGE
default           Active   43h
dev               Active   55s
kube-node-lease   Active   43h
kube-public       Active   43h
kube-system       Active   43h

c. 在该namespace下创建一个nginx的pod

[root@K8s1 ~]# kubectl run nginx --image=nginx:latest --image-pull-policy="IfNotPresent" --port=80 -n dev
pod/nginx created

其中:

ningx为pod名称

--image 指定容器所使用的镜像

--image-pull-policy为镜像拉取策略

--port为容器所监听的端口号

d. 查看新创建的pod

[root@K8s1 ~]# kubectl get pod -n dev
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          6m49s
# 也可以使用describe命令查看这个Pod的明细
[root@K8s1 ~]# kubectl get pod -n dev
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          6m49s
[root@K8s1 ~]# kubectl describe pod nginx -n dev
Name:             nginx
Namespace:        dev
Priority:         0
Service Account:  default
Node:             k8s2/192.168.100.100
Start Time:       Tue, 04 Aug 2026 08:26:03 +0800
Labels:           run=nginx
Annotations:      cni.projectcalico.org/containerID: 727ec57b813fc2c70453930d839f5a26abd8e65cd356a13e93004fa6dbed873f
                  cni.projectcalico.org/podIP: 10.244.109.66/32
                  cni.projectcalico.org/podIPs: 10.244.109.66/32
Status:           Running
IP:               10.244.109.66
IPs:
  IP:  10.244.109.66
Containers:
  nginx:
    Container ID:   containerd://81c600e4075d930cba825cc68dfd6f2cd6054ba820d98a918f6bdbb501527774
    Image:          nginx:latest
    Image ID:       sha256:4e5db4761e0ff445f7fd29aad680ad28e8abf7d204895557f145d65535abcc1c
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Tue, 04 Aug 2026 08:26:04 +0800
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-5lxxk (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-5lxxk:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    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  9m42s  default-scheduler  Successfully assigned dev/nginx to k8s2
  Normal  Pulled     9m42s  kubelet            Container image "nginx:latest" already present on machine
  Normal  Created    9m42s  kubelet            Created container nginx
  Normal  Started    9m42s  kubelet            Started container nginx

Ⅱ. 端口映射,暴露服务

创建好Pod后,可以看到容器已经成功运行了。但是在默认情况下容器只能在K8S内部相互访问。如果需要向外提供服务,还需要创建service

# 创建Pod
[root@K8s1 ~]# kubectl expose pod/nginx --type="NodePort" --port 80 -n dev
service/nginx exposed
# 查看service被映射到哪个端口
[root@K8s1 ~]# kubectl get svc -n dev
NAME    TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx   NodePort   10.102.60.81   <none>        80:30745/TCP   105s

手动通过浏览器访问该端口

alt text

Ⅲ. 使用kubectl delete删除镜像

查看dev命名空间的pod,svc资源

[root@K8s1 ~]# kubectl get pod,svc -n dev
NAME        READY   STATUS    RESTARTS   AGE
pod/nginx   1/1     Running   0          38m

NAME            TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/nginx   NodePort   10.102.60.81   <none>        80:30745/TCP   18m

删除pod/svc

# 删除指定的pod/svc
[root@K8s1 ~]# kubectl delete pod nginx -n dev
pod "nginx" deleted
[root@K8s1 ~]# kubectl delete svc nginx -n dev
service "nginx" deleted
# 重新查看pod/svc
[root@K8s1 ~]# kubectl get pod,svc -n dev
No resources found in dev namespace.

3. K8S yaml配置文件创建资源

在k8S中,创建资源有两种方式

方式1: 使用kubectl命令行,在输入kubectl命令时填上对应参数

  • 优点: 方便快捷,一行命令搞定
  • 缺点: 一行命令太多参数,可读性差,无法保存。不利于维护

方法2: 配置yaml文件

  • 优点: 数据又结构性,可读性高,维护方便
  • 缺点: 写起来要麻烦一点

因此在实际操作中,更多还是用配置yaml文件的方式来部署

4. 使用yaml文件配置pod资源和svc资源

Ⅰ. 编写nginx-deployment.yaml和nginx-svc.yaml

[root@K8s1 ~]# vi nginx-deployment.yaml
[root@K8s1 ~]# vi nginx-svc.yaml
kind: Namespace
apiVersion: v1
metadata:
  name: dev

---

kind: Deployment
apiVersion: apps/v1
metadata:
  name: nginx
  namespace: dev
spec:
  replicas: 1
  selector:
    matchLabels:
      name: nginx
  template:
    metadata:
      labels:
        name: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        imagePullPolicy: IfNotPresent
        ports:
        - containerPort: 80
          protocol: TCP
kind: Service
apiVersion: v1
metadata:
  name: nginx
  namespace: dev
  labels:
    name: nginx
spec:
  type: NodePort
  ports:
    - protocol: TCP
      nodePort: 31001
      targetPort: 80
      port: 80
  selector:
  # 关联的Pod标签
    name: nginx

Ⅱ. 创建deployment和service

# 使用nginx-deployment.yaml创建pod
[root@K8s1 ~]# kubectl create -f nginx-deployment.yaml 
deployment.apps/nginx created
Error from server (AlreadyExists): error when creating "nginx-deployment.yaml": namespaces "dev" already exists
[root@K8s1 ~]# kubectl get deploy -n dev
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           2m23s

# 使用nginx-svc.yaml创建service服务
[root@K8s1 ~]# kubectl create -f nginx-svc.yaml
service/nginx created

Ⅲ. 查看

# 查看deploy
[root@K8s1 ~]# kubectl get deploy -n dev
NAME    READY   UP-TO-DATE   AVAILABLE   AGE
nginx   1/1     1            1           43m

# 查看deployment的详细信息
[root@K8s1 ~]# kubectl describe deploy nginx -n dev|grep -A4 'Events'
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  44m   deployment-controller  Scaled up replica set nginx-6578b57d69 to 1

# 查看pod
[root@K8s1 ~]# kubectl get pod -n dev
NAME                     READY   STATUS    RESTARTS      AGE
nginx-6578b57d69-dw9wr   1/1     Running   1 (10m ago)   46m

# 查看service服务
[root@K8s1 ~]# kubectl get svc -n dev
NAME    TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
nginx   NodePort   10.97.18.134   <none>        80:31001/TCP   83s

此时发现service nginx的80端口已经映射到10.97.18.134的31001端口了

Ⅳ. 访问service,测试对cluster-ip(即service的IP)访问

[root@K8s1 ~]# curl 10.97.18.134:80
<!DOCTYPE html>
...

Ⅴ. 查看对service的描述

[root@K8s1 ~]# kubectl describe svc nginx -n dev
Name:                     nginx
Namespace:                dev
Labels:                   name=nginx
Annotations:              <none>
Selector:                 name=nginx
Type:                     NodePort
IP Family Policy:         SingleStack
IP Families:              IPv4
IP:                       10.97.18.134
IPs:                      10.97.18.134
Port:                     <unset>  80/TCP
TargetPort:               80/TCP
NodePort:                 <unset>  31001/TCP
Endpoints:                10.244.109.68:80
Session Affinity:         None
External Traffic Policy:  Cluster
Events:                   <none>
# 查看Pod信息
[root@K8s1 ~]# kubectl get pods -n dev -o wide
NAME                     READY   STATUS    RESTARTS      AGE   IP              NODE   NOMINATED NODE   READINESS GATES
nginx-6578b57d69-dw9wr   1/1     Running   1 (29m ago)   66m   10.244.109.68   k8s2   <none>           <none>
# 此时可以看到nginx服务在node2上

# 查看Pod详细信息
[root@K8s1 ~]# kubectl describe pod nginx-6578b57d69-dw9wr -n dev
Name:             nginx-6578b57d69-dw9wr
Namespace:        dev
Priority:         0
Service Account:  default
Node:             k8s2/192.168.100.100
Start Time:       Tue, 04 Aug 2026 10:23:45 +0800
Labels:           name=nginx
                  pod-template-hash=6578b57d69
Annotations:      cni.projectcalico.org/containerID: b2b861d1fa6f7e3909756555c618cfc0ac6eff694153aa3d3b9b906859f3694b
                  cni.projectcalico.org/podIP: 10.244.109.68/32
                  cni.projectcalico.org/podIPs: 10.244.109.68/32
Status:           Running
IP:               10.244.109.68
IPs:
  IP:           10.244.109.68
Controlled By:  ReplicaSet/nginx-6578b57d69
Containers:
  nginx:
    Container ID:   containerd://f128f54c1c9254d288e56b33e0d41366924685c21b34fc3113056b5b2a0026b7
    Image:          nginx:latest
    Image ID:       sha256:4e5db4761e0ff445f7fd29aad680ad28e8abf7d204895557f145d65535abcc1c
    Port:           80/TCP
    Host Port:      0/TCP
    State:          Running
      Started:      Tue, 04 Aug 2026 11:01:30 +0800
    Last State:     Terminated
      Reason:       Unknown
      Exit Code:    255
      Started:      Tue, 04 Aug 2026 10:23:46 +0800
      Finished:     Tue, 04 Aug 2026 11:00:36 +0800
    Ready:          True
    Restart Count:  1
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-wzgqv (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  kube-api-access-wzgqv:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    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       3h41m                default-scheduler  Successfully assigned dev/nginx-6578b57d69-dw9wr to k8s2
  Normal   Pulled          3h41m                kubelet            Container image "nginx:latest" already present on machine
  Normal   Created         3h41m                kubelet            Created container nginx
  Normal   Started         3h41m                kubelet            Started container nginx
  Normal   SandboxChanged  3h4m (x2 over 3h4m)  kubelet            Pod sandbox changed, it will be killed and re-created.
  Normal   Pulled          3h4m                 kubelet            Container image "nginx:latest" already present on machine
  Normal   Created         3h4m                 kubelet            Created container nginx
  Normal   Started         3h4m                 kubelet            Started container nginx
  Warning  FailedMount     42m (x42 over 123m)  kubelet            MountVolume.SetUp failed for volume "kube-api-access-wzgqv" : failed to fetch token: Post "https://192.168.100.99:6443/api/v1/namespaces/dev/serviceaccounts/default/token": dial tcp 192.168.100.99:6443: connect: no route to host