架构

一个Kubernetes集群至少包含一个控制平面,以及一个或多个工作节点

控制平面

controlplane

  • 负责管理工作节点和维护集群状态,所有任务的分配都来自控制平面
  • 为集群做出去全局决策,比如资源的调度、检测和响应集群事件

kube-apiserver

  • 如果与Kubernetes集群进行交互,需要通过API
  • apiserverKubernetes控制平面的前端,用于处理内部和外部请求

kube-scheduler

  • 集群状态是否良好,如果需要创建新的容器,需要将他们放在哪里,由调度程序关注
  • scheduler调度程序考虑容器集的资源需求,比如CPU或者内存,以及集群的运行状态,然后将容器集安排到适当的计算节点

kube-controller-manager

  • 控制器负责实际运行集群,controller-manager控制器管理器将多个控制器功能合并,降低了程序的复杂性

controller-manager包含以下控制器:

  1. 节点控制器(Node Controller): 负责在节点出现故障时进行通知和响应
  2. 任务控制器(Job Controller): 检测代表一次性任务的Job对象,然后创建Pods来运行这些任务直到完成
  3. 端点控制器(Endpoints Controller): 填充端点Endpoints对象,即加入ServicePod
  4. 服务账户和令牌控制器(Service Account & Token Controllers): 为新的命名空间创建默认账户和API访问令牌

etcd

  • 键值对数据库,存储配置数据和集群状态信息

可选组件cloud-controller-manager

  • 云控制管理器,允许将集群了解到云提供商的API之上,将该云平台交互的组件与自己的集群交互组件分离开。如果在自己的环境中运行Kubernetes或者在本地计算机中运行学习环境,则所部署的集群不需要有云控制管理器

各组件之间的关系

relationship

工作节点

  • 负责执行由控制平面分配的请求任务,运行实际的应用和工作负载

Node组件

node

  • 节点组件会在每个工作节点上运行,负责维护运行的Pod并提供Kubernetes运行环境

Pod

  • Pod是包含一个或者多个容器的容器组,是Kubernetes中创建和管理的最小对象

特点

  1. Kubernetes的最小调度单位(原子单元),Kubernetes直接管理Pod而不是容器
  2. 同一个Pod中的容器总是会被自动安排到集群中的同一个节点(物理机或者虚拟机上),并且一起调度
  3. Pod可以理解为运行特定应用的逻辑主机,容器共享存储、网络和配置声明(比如资源限制)
  4. 每个Pod有唯一的IP地址,IP地址分配给Pod,在同一个Pod内,所有容器共享一个IP地址和端口空间,Pod内的容器可以使用localhost相互通信

创建和管理

  1. 创建Pod: kubectl run <Pod名称> --imgae=<容器镜像>
    • 执行一次性任务,退出Pod时自动删除容器: kubectl run <Pod名称> --image=<容器镜像> -it --rm
  2. 查看容器状态: kubectl get pod
    • 显示详细Pod信息: kubectl get pod -owide
  3. 查看Pod运行日志: kubectl logs -f <Pod名称>
  4. 查看Pod的一些信息: kubesctl describe pod <Pod名称>
    • 创建分为四个步骤
      1. 分配节点 Successfully assigned default/Pod名称 to k8s-worker2
      2. 拉取镜像 Container image "容器镜像" already present on machine
      3. 创建容器 Created container Pod名称
      4. 启动容器 Started container Pod名称
  5. 进入Pod容器: kubectl exec -it Pod名称 -- /bin/bash
  6. 删除容器: kubectl delete pod Pod名称

Deployment部署和ReplicaSet副本集

  • Deployment是对ReplicaSetPod的更高级别的抽象,使Pod拥有多副本,自愈,扩缩容,滚动升级等能力
  • ReplicaSet副本集是一个Pod的集合,设置运行Pod的数量,确保任何时间都有指定数量的Pod副本在运行。
  • 通常,我们不直接使用ReplicaSet,而是在Deployment中声明
  • 创建Deployment: kubectl create deployment <部署名称> --imgae=<镜像名称> --replicas=<副本数量>
    • Deployment通过副本集控制Pod的数量
    • 如果手动删除一个Pod,副本集会自动生成一个新的Pod,维持副本数量不变。这就是自愈
  • 查看Deployment状态: kubectl get deploy

缩放

  • 查看副本集: kubectl get replicaSet
    • 查看副本集缩放过程: kubectl get replicaSet --watch

手动缩放

  • 手动修改Deployment中的副本集数量: kubectl scale deploy <部署名称> --replicas=<新的副本数量>

自动缩放

  • kubectl autoscale deployment/<部署名称> --min=<最小副本数> --max=<最大副本数> --cpu-percent=75
    • --cpu-percent=75是使得自动缩放保持所有的Pod的平均cpu占用维持在75%以下
  • 查看自动缩放: kubectl get hpa
  • 删除自动缩放: kubectl delete hpa <部署名称>

滚动更新

  • 滚动更新部署版本: kubectl set image deploy/部署名称 容器名=镜像版本
  • 如果一个Deployment中有三个Pod,运行滚动更新命令时,会创建一个新的副本集。
    • 当新的副本集有一个Pod就绪了,就会下线旧副本集中的一个Pod,直到旧副本集中所有的Pod均下线
  • 版本回滚:
    • 查看历史版本: kubectl rollout history deploy/部署名称
    • 查看历史版本详情: kubectl rollout history deploy/部署名称 --revision=版本号
    • 回滚到指定的版本: kubectl rollout undo deploy/部署名称 --to-revision=版本号

Service

  • 将一组Pods上的应用程序公开为网路服务的抽象方法
  • 将一组Pod提供相同的DNS名,并进行负载均衡
  • KubernetesPod提供分配了IP地址,但是IP地址可能发生变化,集群内的容器可以通过service名称访问服务,不需要担心PodIP发生变化

Service定义的抽象:

  • 逻辑上一组可以互相替换的Pod,通常称为微服务
  • Service对应的Pod集合通过选择符关联
  • 在一个Service上运行了三个Nginx副本,副本之间可以相互替换,我们不需要关心它调用了哪一个Nginx,也不需要关注Pod的运行装填,只需要调用这个服务就可以了
  • 将部署公开为服务: kubectl expose deploy/部署名称 --name=服务名称 --port=服务端口 --target-port=对应pod端口

ServiceType

ClusterIP(默认)

  • 将服务公开在集群内部,Kubernetes会给服务分配一个集群内部的IP,集群内的所有主机都可以通过这个Cluster-IP访问服务。集群内部的Pod可以通过Service名称访问服务。

NodePort

  • 通过每个节点的主机和静态端口(NodePort)暴露服务,集群外部的主机可以使用节点IPNodePort访问服务

ExternalName

  • 将集群外部的网络引入集群内部

LoadBalancer

  • 使用云提供商的负载均衡器向外部暴露服务

使用--type=NodePort即可,本地集群不需要使用LoadBalancer

port

命名空间

  • 一种资源隔离机制,将同一个集群中的资源划分为互相隔离的组。命名空间可以在多个用户之间划分集群资源(通过资源配额)
  • 比如可以设置开发、测试、生产多个命名空间
  • 同一个命名空间内资源名称唯一,跨命名空间时没有这个要求
  • 命名空间作用域仅仅针对带有名字空间的对象,比如Deployment、Service
  • 作用域对集群访问对象不适用,比如StorageClass、Node、PersistentVolume
  • 查看命名空间: kubectl get namespace
    • Kubernetes默认创建四个命名空间:
    • default: 默认命名空间,不可删除,没有指定命名空间的对象都会分配到这里
    • kube-system: 系统对象(控制平面和Node组件)使用的命名空间
    • kube-public: 自动创建的公共命名空间,所有用户(包括没有身份验证的用户)都可以读取,将集群中公用的可见和可读的资源放在这个空间中
    • kube-node-lease: 租约(Lease)对象使用的命名空间。每个节点都有一个关联的Lease对象,Lease是一种轻量级的资源,通过发送心跳,检测集群中的每个节点是否发生故障
      • 查看lease对象: kubectl get lease -A
  • 查看指定命名空间的Pod: kubectl get pod -n=命名空间名称
  • 创建命名空间: kubectl create ns 命名空间名称
  • 运行容器指定命名空间: kubectl run pod名称 --image=镜像名称 -n=命名空间名称
  • 修改默认命名空间的名称(default): kubectl config set-context $(kubectl config current-context) --namespace=新的名称
  • 删除命名空间: kubectl delete ns 命名空间名称
    • 删除命名空间会删除其中所有内容,如果有些对象无法被删除(对象处于错误状态,或者对象资源被占用),则命名空间也无法被删除。需要手动删除对象以后才能删除命名空间

管理对象

命令行指令

  • 使用kubectl创建和管理Kubernetes对象。简单高效,但是功能有限,不适合复杂场景,不容易追溯操作,用于开发和调试。

声明式配置

  • Kubernetes使用yaml文件描述Kubernetes对象,学习难度大并且配置麻烦,但是操作留痕,适合操作复杂的对象,多用于生产。
  • 使用命令行指令,无法指定NodePort端口,是随机生成的。但是yaml文件中可以指定NodePort

常用命令缩写

名称 缩写 Kind
namespace ns Namespace
nodes no Node
pods po Pod
services svc Service
deployment deploy Deployment
replicasets rs ReplicaSet
statefulsets sts StatefulSet

使用yaml文件配置KUbernetes对象

  • apiVersion: Kubernetes API版本
  • kind: 对象类别,比如Pod、Deployment、Service、ReplicaSet
  • metadata: 描述对象的元数据,包括一个name字符串,UID(系统自动生成)和可选的namespace
  • spec: 对象的配置

yaml内容在官网都可以找到

  • 使yaml文件生效: kubectl apply -f xxx.yaml
  • 删除yaml文件: kubectl delete -f xxx.yaml

标签

  • 附加在对象Pod上的键值对,用于补充对象的描述信息。标签使用户可以以松散的方式管理对象映射,不需要客户端存储这些映射。
  • 格式
    • 键:
      • 前缀(可选) / 名称(必须)
    • 有效的名称和值:
      • 字符数量小于等于63
      • 如果不为空,以字母和数字字符开头结尾
      • 包含破折号-,下划线_,点和字母或者数字
  • 查看所有Pod的标签: kubectl get pod --show-labels
    • 查看指定Pod的标签: kubectl get pod -l "指定的Pod描述"

选择器

  • 标签通常配合选择器使用,标签选择器可以识别一组对象,标签不支持唯一性
  • 标签选择器最常见的用法是为Service选择一组Pod作为后端
  • 标签选择运算,基于等值的和基于集合的
    • 多个条件使用逗号分割,相当于AND
    • 基于等值的直接使用键值对即可
    • 基于集合的可以使用IN, NOT IN

容器运行时接口(CRI)

  • Kubelet运行在每个节点上,用于管理和维护Pod和容器的状态
  • 容器运行时接口是Kubelet和容器运行时之间通信的主要协议,将Kubelet于容器运行时解耦,实现了CRI接口的容器引擎,都可以所谓Kubernetes的容器运行时。
  • Docker没有实现CRI接口,Kubernetes使用dockershim来兼容docker
  • crictl是一个兼容CRI的容器运行时命令,跟docker命令一个样,可以用来检查和调试底层的运行时容器
    • docker命令稍微少一些,比如无法实现导入导出
    • 因此导入导出使用ctr命令,也只用来导入导出,因为ctr也不是很好用
      1
      2
      3
      4
      5
      docker pull alpine:3.15 
      docker save apline:3.15 > alpine-3.15.tar # 保存成tar
      ctr -n k8s.io images import alpine-3.15.tar --platform linux/amd64 # 导入镜像
      # kubernetes 中所有的镜像都在k8s.io这个命名空间中
      ctr -n k8s.io images export alpine.tar docker.io/library/alpine:3.15 --platform linux/amd64 # 导出镜像

金丝雀发布(canary deployment)

  • 生产环境中小范围部署新的应用代码,一旦应用签署发布,只有少数用户被路由到他,最大限度降低影响。如果没有错误发生,则将新版本逐渐推广到整个基础设施
  • 默认的金丝雀发布有一定的局限
    • 不能根据用户的注册时间、地区等请求中的内容属性进行流量分配
    • 同一个用户如果多次调用该Service,可能第一次请求到了旧版本的Pod,第二次请求到了新版本的Pod
    • 因为Kubernetes Service只在TCP层面解决负载均衡问题,不对请求响应的消息内容做任何解析和识别,如果更完善的实现金丝雀日发布,可以考虑Istio灰度发布

搭建MySQL

创建mysql-pod.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
volumes:
- name: data-volume
hostPath:
path: /home/mysql/data
type: Directory

挂载数据卷

  • hostPath卷: 将主机节点上的文件或目录挂在到Pod
type 含义
DirectoryOrCreate 目录不存在则自动创建
Directory 挂载已经存在的目录,不存在会报错
FileOrCreate 文件不存在则自动创建,不会自动创建文件的父目录,必须确保文件路径存在
File 挂载已经存在的文件,不存在会报错
Socket 挂在UNIX套接字,比如挂载/var/run/docker.sock 进程

ConfigMap

  • Docker中可以使用绑定挂载的方式将配置文件挂载到容器里,但是在Kubernetes集群中,容器可能被调度到任意的节点中,配置文件需要能在集群的任意节点上访问、分发和更新
  • ConfigMap用来在etcd中保存非加密的数据,一般用来保存配置文件
    • 可以用作环境变量,命令行参数或者存储卷
    • 将配置信息与容器镜像解耦,便于配置的修改
    • 在设计上不是用来保存大量数据的
    • 保存的数据不能超过1MB
    • 超出这个限制,需要考虑存储卷或者访问文件存储服务