K8S基础概念

  1. 入门操作
    1. 安装
    2. 真机安装
      1. 自动补全
  2. 概念
    1. 功能
    2. Ingress
    3. Endpoint
    4. Yaml文件
    5. K8S组件
    6. K8S对象
      1. 回收机制
      2. K8S API
    7. K8S架构
      1. 节点和控制面之间的通信
      2. 控制器
    8. 容器
      1. 镜像
      2. 容器环境
      3. 容器回调
    9. 工作负载
      1. Pod
        1. pod生命周期
        2. Init容器和Sidecar边车容器
        3. 临时容器
        4. 干扰,QoS类,命名空间 没怎么看,跳过了算是
        5. Downward API
      2. 工作负载管理
        1. Deployments
          1. 更新Deployment
          2. 翻转 滚动更新
          3. 回滚更新
          4. 缩放
          5. 暂停和恢复更新
          6. Deployment状态,清理策略,金丝雀部署,规范 简单过了下
        2. ReplicaSet
          1. 删除相关的过了一下
        3. StatefulSet
        4. DaemonSet
        5. JOB
          1. 外部管理器+Job模板
          2. CronJob
      3. 管理工作负载
      4. 自动扩缩
    10. 服务,负载均衡,联网
      1. Service
      2. Ingress
      3. Service和Ingress
      4. EndpointSlic
      5. DNS
      6. IPV6支持,感知路由,Windows,Cluster IP分配,跳过
      7. 同一Node中Pod的通信
    11. 存储
        1. hostPath 不能迁移到其他Node
        2. emptyDir POD移除时内容消失
      1. 持久卷
        1. 创建PV和PVC,POD申领PVC
        2. 节点亲和性
        3. 阶段
      2. StorageClass
      3. 存储类,PV,PVC,使用存储类进行动态卷制备
      4. 本地存储
      5. 投射卷
        1. Secret
        2. configMap
        3. downwardAPI
    12. 临时卷
    13. 卷属性类,Beta功能跳过
    14. 卷快照,卷快照类,CSI卷克隆,先跳过
  3. 配置
    1. ConfigMap
    2. Secret
    3. 资源
  4. 安全-暂时跳过
  5. 策略
    1. 范围限制 LimitRange
    2. 资源配额
    3. 节点资源管理器
  6. 调度,强占和驱逐
    1. 调度器
    2. 特定节点上运行POD
    3. POD开销
    4. POD调度就绪态
    5. POD拓扑分布约束,内容很多,简单过了下
    6. 污点和容忍度
    7. 调度框架
      1. 调度框架扩展点
    8. 动态资源分配如GPU,跳过
    9. 调度器性能调优
    10. 资源装箱,控制节点资源权重进而控制分数
    11. POD优先级和抢占,通过PriorityClass使高优先级POD驱逐低优先POD
    12. 节点压力驱逐
    13. API发起的驱逐
  7. 集群管理
    1. 节点关闭
    2. Node自动缩容和扩容
    3. 集群网络系统
    4. 日志架构

入门操作

安装

# install helm
curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash
# install kubectl
curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl"
sudo install kubectl /usr/local/bin/kubectl
# install minikube
curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64
sudo install minikube-linux-amd64 /usr/local/bin/minikube && rm minikube-linux-amd64

# start
minikube start
# start dashboard
minikube dashboard
# forward
kubectl proxy --address 0.0.0.0 --disable-filter=true

# fast create
helm create mychart
# install unstall
helm install mychart ./mychart
helm uninstall mychart
# only render
helm install --debug --dry-run goodly-guppy ./mychart

真机安装

# ubuntu
swapoff /swap.img
vim /etc/fstab

sudo apt-get install -y apt-transport-https ca-certificates curl gpg
curl -fsSL https://pkgs.k8s.io/core:/stable:/v1.34/deb/Release.key | sudo gpg --dearmor -o /etc/apt/keyrings/kubernetes-apt-keyring.gpg
echo 'deb [signed-by=/etc/apt/keyrings/kubernetes-apt-keyring.gpg] https://pkgs.k8s.io/core:/stable:/v1.34/deb/ /' | sudo tee /etc/apt/sources.list.d/kubernetes.list
apt-get update
apt-get install -y kubelet kubeadm kubectl
apt-mark hold kubelet kubeadm kubectl

sed -i '/^\s*net\.ipv4\.ip_forward/s/^/#/' /etc/sysctl.d/99-sysctl.conf
sed -i '/^\s*net\.ipv4\.ip_forward/s/^/#/' /etc/sysctl.conf
cat <<EOF | sudo tee /etc/modules-load.d/k8s.conf
br_netfilter
EOF
cat <<EOF | sudo tee /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
EOF
modprobe br_netfilter
sysctl --system

apt update
apt install -y containerd
mkdir -p /etc/containerd
containerd config default | sudo tee /etc/containerd/config.toml
sudo sed -i 's/SystemdCgroup = false/SystemdCgroup = true/' /etc/containerd/config.toml
systemctl restart containerd
systemctl enable containerd

master

# rm -rf /etc/cni/net.d
kubeadm init --pod-network-cidr=10.100.0.0/16

mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config

kubectl taint node master node-role.kubernetes.io/control-plane-

wget https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/calico.yaml -O calico_new.yaml
sed -i 's/192.168.0.0\/16/10.100.0.0\/16/g' calico_new.yaml
sed -i 's/# - name: CALICO_IPV4POOL_CIDR/- name: CALICO_IPV4POOL_CIDR/' calico_new.yaml
sed -i 's/#   value: "10.100.0.0\/16"/  value: "10.100.0.0\/16"/' calico_new.yaml
kubectl apply -f calico_new.yaml
systemctl restart containerd

kubeadm token create --print-join-command
# systemctl restart kubelet
# /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock

自动补全

apt-get install bash-completion

echo 'source /usr/share/bash-completion/bash_completion' >> ~/.bashrc
echo 'source <(kubectl completion bash)' >> ~/.bashrc

概念

文档

文档

功能

  1. 服务发现和负载均衡: Kubernetes可以通过DNS名称或IP地址暴露容器,并自动进行负载均衡,确保流量均匀分配,保持部署稳定。
  2. 存储编排: Kubernetes支持自动挂载各种存储系统,如本地存储和公共云存储,简化存储管理。
  3. 自动部署和回滚: Kubernetes允许描述容器的期望状态,并自动调整实际状态以匹配期望状态,支持自动化部署和回滚。
  4. 自动完成装箱计算: Kubernetes根据容器的CPU和内存需求,智能调度容器到集群中的节点,优化资源利用。
  5. 自我修复: Kubernetes会自动重启失败的容器、替换不健康的容器,并在服务准备好之前不将其通告给客户端。
  6. 密钥与配置管理: Kubernetes可以安全地存储和管理敏感信息,如密码和密钥,支持在不重建镜像的情况下更新配置。
  7. 批处理执行: Kubernetes不仅管理服务,还支持批处理和CI工作负载,自动替换失败的容器。
  8. 水平扩缩: Kubernetes支持通过简单命令、用户界面或根据CPU使用率自动扩缩应用。
  9. IPv4/IPv6双栈: Kubernetes为Pod和Service分配IPv4和IPv6地址,支持双栈网络。
  10. 为可扩展性设计: Kubernetes允许在不改变上游源代码的情况下添加功能,支持集群的可扩展性。

Ingress

控制Web流量达到工作负载,可以当做集群入口点。

Endpoint

云原生服务发现

Yaml文件

使用yaml定义一个pod,此时不会被Deployment控制器所管理,出现问题后不会重新创建。

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: my-image:latest
    ports:
    - containerPort: 80

使用yaml定义一个Deployment,当pod崩溃或者被删除时,会自动创建来保证存在指定数量的pod。

apiVersion: apps/v1
kind: Deployment # 这里不一样
metadata:
  name: my-deployment
spec:
  replicas: 3 # Pod副本数量
  selector:
    matchLabels:
      app: my-app # 要与template.metadata.labels匹配,保证Deployment创建的pod能被正确管理。
  template:
    metadata:
      labels:
        app: my-app
    spec:
      containers:
      - name: my-container
        image: my-image:latest
        ports:
        - containerPort: 80

K8S组件

Node:工作机器
Container:容器
Pod:部署单元,一个应用可能有多个部署单元,一个部署单元可能包含一个或多个容器,容器共享相同的网络和存储单元。先有的部署单元,部署单元需要创建容器,容器位于节点上。一个Node上不同的Container之间怎么做的共享和隔离?
Deployment:应用控制器,检测Pod的运行创建,出问题时自动创建。

K8S集群由控制平面和一个或多个工作节点组成。

  1. 控制平面组件:管理集群的整体状态,负责资源调度,检测和响应集群事件
    1. kube-apiserver:公开 Kubernetes HTTP API 的核心组件服务器
    2. etcd:一致性和高可用的键值存储,用于API服务器的数据存储
    3. kube-scheduler:监听新创建的未指定运行Node的Pods,将Pod分配给合适的Node。资源需求,软硬件,策略约束,亲和,反亲和,数据位置,工作覆盖干扰,最后时限。东西还挺多,每个感觉都能看看
    4. kube-controller-manager:运行控制器来实现 Kubernetes API 行为。(不太懂
      1. Node控制器,在节点出现故障时进行响应
      2. Job控制器,啥玩意
      3. EndpointSlice控制器,填充EndpointSlice对象,提供Service和Pod之间的链接。
      4. ServiceAccount控制器,啥玩意
  2. Node 组件:运行在节点上,维护pod并提供k8s运行时环境
    1. kubelet:确保pod及其容器正常运行。保证容器都运行在pod中,啥玩意
    2. kube-proxy:维护节点网络规则来实现service功能,(Kubernetes中Service是将运行在一个或一组Pod上的应用可被客户端访问,Service提供一个虚拟的IP地址和端口,以及选择器selector如k8s-app: kube-dns带有此标签的服务A会被注册到这个Service实现转发的作用)。
  3. 插件
    1. DNS
    2. Web界面
    3. 容器资源监控
    4. 日志

K8S对象

描述了K8S中的一个实体,对应一个yaml文件。其中描述了实体的名称,期望状态等内容。

对象管理

  • 指令式命令kubectl create deployment nginx --image nginx
  • 指令式对象配置:指定一个对象 kubectl create -f nginx.yaml
  • 声明式对象配置:制定目录

Name定义了一个对象名,对应的实体使用UID标识。对象删除重建后,对象名不变,应用名会变。

label标签,可以通过标签进行选择和过滤操作。"metadata": { "labels": {"K1": "value"} }

annotations注解,用于存储非标识数据,不用于选择和过滤附加信息,"metadata": { "annotations": {"K1": "value"} }

namespace,将同一集群中的资源划分为互相隔离的组,供不同用户来使用。

回收机制

pods的管理和回收机制,Finalizers,属主,附属

K8S API

K8S使用API查询和操纵其中对象的状态,kubectl这类命令行工具也是在调用API。kubectl获取并缓存API规范实现命令行补全

两个机制,发现API,OpenAPI文档

K8S架构

节点和控制面之间的通信

节点的API调用终止于API服务器

API服务器到各个节点上的kubelet进程存在连接,作用:获取pod日志,挂接?,端口转发。

控制器

对象中存在spec字段,表示对象期望达到的状态,控制器负责确保其当前状态接近于期望状态。

容器

镜像

Kubernetes 可以使用的一些镜像名称示例包括:

名字 说明
busybox 仅包含镜像名称,没有标签或摘要,Kubernetes 将使用 Docker 公共镜像仓库和 latest 标签。 (例如 docker.io/library/busybox:latest)
busybox:1.32.0 带标签的镜像名称,Kubernetes 将使用 Docker 公共镜像仓库。 (例如 docker.io/library/busybox:1.32.0)
registry.k8s.io/pause:latest 带有自定义镜像仓库和 latest 标签的镜像名称。
registry.k8s.io/pause:3.5 带有自定义镜像仓库和非 latest 标签的镜像名称。
registry.k8s.io/pause@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 带摘要的镜像名称。
registry.k8s.io/pause:3.5@sha256:1ff6c18fbef2045af6b9c16bf034cc421a29027b800e4f9b68ae9b1cb3e9ae07 带有标签和摘要的镜像名称,镜像拉取仅参考摘要。

镜像拉取策略imagePullPolicy

  • IfNotPresent:本地不存在才拉取(未指定且使用非latest标签时的默认项)
  • Always:查询镜像摘要,如果本地不存在对应摘要则拉取,存在并不会拉取。(未指定且使用latest标签时的默认项)(未指定且无标签时的默认项)
  • Never:只使用本地

策略在对象初次创建时设置,此后更新Deployment的镜像标签时,拉取策略不会发生变化。

容器环境

pod中定义的环境变量可在容器中使用

Foo服务器的环境变量,用于访问FOO服务器

FOO_SERVICE_HOST=主机
FOO_SERVICE_PORT=端口

DNS插件

容器回调

回调:无参数,执行失败会杀掉容器

  • PostStart:容器创建之后被立即执行。执行时间过长或挂起,可能容器无法进入running状态。失败会发出FailedPostStartHook事件。

  • PreStop:容器被终止前调用。需要在限定时间内执行完成,时间一到即刻上路。失败会发出FailedPreStopHook事件。

注册方式(执行方式)

Exec,执行命令,HTTP,Sleep

kubectl describe pod lifecycle-demo

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: default
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
        - name: nginx
          image: nginx:1.25-alpine
          ports:
            - containerPort: 80
          lifecycle:
            postStart:
              exec:
                command:
                  - /bin/sh
                  - -c
                  - sed -i 's/Welcome to nginx!/Welcome to nginx-1!/g' /usr/share/nginx/html/index.html

工作负载

工作负载是运行程序,pod是一组运行状态的容器集合。一般不会直接管理pod,而是通过工作负载配置+控制器,自动管理pod

工作负载配置类型

  • Deployment,ReplicaSet:管理无状态应用,pod都是等价的
  • StatefulSet:部署有状态服务,将Pod与PersistentVolume对应起来。可以将数据复制到同一StatefulSet中其他Pod来提高整体服务的可靠性,怎么复制?
  • DaemonSet:基础负载,添加新Node时,会自动在新Node部署一个pod
  • Job:定义一直运行到结束并停止的任务,CronJob:根据排期表,多次运行同一个job
  • 定制:CRD。如果你希望运行一组 Pod,但要求所有 Pod 都可用时才执行操作 (比如针对某种高吞吐量的分布式任务),你可以基于定制资源实现一个能够满足这一需求的扩展, 并将其安装到集群中运行。怎么定义感觉可以看看,CRD这个东西

Pod

pod生命周期

Pod:定义的一个逻辑主机,容器的运行时环境,包含一个或多个应用容器,容器对应的紧密耦合在一起(共享网络(可以通过localhost通信)和存储(共享内存也行?))。Pod 类似于共享名字空间并共享文件系统卷的一组容器。

除了应用容器:pod还可以在Pod启动期间运行init容器,还可以注入临时性容器来调试正在运行的pod。

直接创建POD,不推荐:kubectl apply -f https://k8s.io/examples/pods/simple-pod.yaml

POD名称DNS子域值(这是啥?)DNS标签规则

livenessProbe:容器存活探针
readinessProbe:就绪探针,探测失败时从Server/Endpoint/负责均衡中摘除,不分配流量。
startupProbe:启动检测探针。容器启动时间过长时,会被上面两个探针误伤。当启用了启动检查探针时,存活和就绪探针将会在启动检查探针检测成功后启用。

Pod.status.phase整体POD生命周期:起始于Pending,至少有一个主要容器正常启动后进入Running,之后有容器失败进入failed,没有则进入Succeeded。

Pending:Pod被保存到了Etcd中,但是Pod中的容器不能被顺利创建
Running:Pod已经创建成功,和Node进行绑定。所有容器都创建成功,至少有一个正在运行中
Succeeded:所有容器正常运行完毕,并且退出了。常见于运行一次性任务。
Failed:至少有一个容器以非0错误码退出
Unknown:通信出现问题

Pod.status.conditionsPOD健康条件:展示pod关键阶段和健康性。
Ready:所有容器已经就绪,可以对外提供服务
ContainersReady:所有业务容器就绪(不含init容器)
PodScheduled:是否被调度到某一个Node上

Pod.status.containerStatuses.state单个容器状态:直观展示容器的状态,不同于phase。waiting等待需要结合reason查看,running,反复启动失败可能会有CrashLoopBackOff,被删除会有Terminating
livenessProbe失败会导致state切换到Terminated,之后按照restartPolicy处理。

绑定:pod分配到特定节点

调度:选择使用哪个节点

kubectl describe pod <pod 名称>:查看pod的状态和时间

kubectl logs <pod名称>: 查看pod的日志

Init容器和Sidecar边车容器

Init按顺序执行完毕之后退出,应用容器才会运行

应用

  1. 运行shell检查并等待一个Service完成创建(Init运行结束之后,应用容器才会运行)
  2. curl注册POD到远程服务器
  3. sleep应用容器启动之前,等待一段时间。
  4. 克隆git仓库到卷(后面会有一大章节介绍)
  5. 渲染配置文件的模板,如模板需要POD的ip
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app.kubernetes.io/name: MyApp
spec:
  containers:
  - name: myapp-container
    image: busybox:1.28
    command: ['sh', '-c', 'echo The app is running! && sleep 3600']
  initContainers:
  - name: init-myservice
    image: busybox:1.28
    command: ['sh', '-c', "until nslookup myservice.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for myservice; sleep 2; done"]
  - name: init-mydb
    image: busybox:1.28
    # 指定成Always会变成边车容器
    restartPolicy: Always
    command: ['sh', '-c', "until nslookup mydb.$(cat /var/run/secrets/kubernetes.io/serviceaccount/namespace).svc.cluster.local; do echo waiting for mydb; sleep 2; done"]
# 启动
kubectl apply -f myapp.yaml
# 检查状态
kubectl get -f myapp.yaml
# 查看pod内Init容器的日志
kubectl logs myapp-pod -c init-myservice

pod重启,init容器必须重新执行

Init容器的restartPolicy设置成Always,就会在pod生命周期内持续运行

临时容器

Pod是一次性可替换的,一旦创建完成,无法添加新的容器到其中。

缺少对资源或者执行的保证,永远不会自动重启,不用于构建应用程序,没有端口配置,临时容器被添加到pod后,不能更改或删除临时容器。

干扰,QoS类,命名空间 没怎么看,跳过了算是

Downward API

将pod和容器字段暴露给运行中的容器

  1. 作为环境变量
  2. 作为downwardAPI卷中的文件

工作负载管理

Deployments:封装的ReplicaSet,提供了更新、回滚、恢复和暂停等能力。更新时创建新的RS用其中的POD逐步替换旧RS的POD。修改模板之后直接使用apply应用,会触发自动应用

ReplicaSet:提供了维持POD数量的能力,修改POD模板之后需要删除旧POD才能触发新POD创建。

StatefulSet:比起Deployments为它们的每个Pod维护了一个有粘性的永远不变的ID。

Deployments vs StatefulSet

身份标识

  • Deployments的POD名称随机生成(nginx-deploy-5d5b64cf8-xj7pw),重启后变化。无固定网络标识,IP动态分配,服务依赖Service负载均衡
  • StatefulSete的POD名称固定(mysql-0,mysql-1),重建后保持不变。提供稳定的DNS名称(<pod-name>.<svc-name>.namespace.svc.cluster.local)

存储管理

  • Deployments默认使用临时存储(emptyDir),POD删除后数据丢失,持久化需要手动配置PVC,无法绑定到特定POD。
  • StatefulSet为每个POD绑定独立PVC,与POD生命周期解耦

生命周期管理

操作 Deployment StatefulSet
启动/停止顺序 并行创建/删除,无顺序要求 严格按序号顺序(如先启动 web-0,再 web-1
滚动更新 无序更新,支持多 Pod 同时替换 逆序更新(从最高序号 Pod 开始逐个更新)
扩缩容 随机创建/删除 Pod 扩容:顺序新增 Pod(如新增 web-2); 缩容:逆序删除(先删 web-2

Deployments

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-deployment # 命名
  labels: # Deployment的标签
    app: nginx
spec:
  replicas: 3 # 创建一个ReplicaSet,标明pod副本数量
  selector: # 定义ReplicaSet会管理所有带有后面标签的pod。如果先前通过其他方式创建过带有此标签的pod,且pod符合后面image和port的定义,也会被同时管理,不过此时相关NAME可能规范不同(如没有相关的HASH或者前缀)不建议这样做。假定你在ReplicaSet已经被部署之后创建Pod,并且你已经在ReplicaSet中设置了其初始的Pod副本数以满足其副本计数需要,新的Pod会被该ReplicaSet获取,并立即被ReplicaSet终止, 因为它们的存在会使得ReplicaSet中Pod个数超出其期望值。
    matchLabels:
      app: nginx
  template:
    metadata:
      labels: # 在模板中定义标签,这个是pod的标签
        app: nginx
    spec: 指定运行一个nginx容器并命名为nginx
      containers:
      - name: nginx
        image: nginx:1.14.2
        ports:
        - containerPort: 80

kubectl apply -f https://k8s.io/examples/controllers/nginx-deployment.yaml

kubectl get deployments

NAME               READY   UP-TO-DATE   AVAILABLE   AGE
nginx-deployment   0/3     0            0           1s
  • NAME: Deployment名称
  • READY:就绪个数/期望个数
  • UP-TO-DATE:为了达到期望状态已经更新的副本数
  • AVAILABLE:可用用户使用的副本数
  • AGE:运行时间
kubectl get rs

NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-75675f5897   3         3         3       18s
  • NAME: ReplicaSet名称,75675f5897是哈希,由Deployment名称-哈希组合而成
  • DESIRED:期望副本数
  • CURRENT:实际副本数
  • READY:可以为用户提供服务的副本数
  • AGE:显示应用运行时间的长度
kubectl get pods --show-labels

NAME                                READY     STATUS    RESTARTS   AGE       LABELS
nginx-deployment-75675f5897-7ci7o   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-kzszj   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897
nginx-deployment-75675f5897-qqcnn   1/1       Running   0          18s       app=nginx,pod-template-hash=75675f5897

NAME:Pod名称,ReplicaSet名称-哈希

Deployment控制器将pod-template-hash标签添加到Deployment所创建或收留的每个ReplicaSet此标签可确保Deployment的子ReplicaSet不重叠。标签是通过对ReplicaSet的PodTemplate进行哈希处理。所生成的哈希值被添加到ReplicaSet选择算符、Pod模板标签,并存在于在ReplicaSet可能拥有的任何现有Pod中

更新Deployment

kubectl set image deployment.v1.apps/nginx-deployment nginx=nginx:1.16.1

kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1

kubectl edit deployment/nginx-deployment

查看上线状态

kubectl rollout status deployment/nginx-deployment

kubectl get rs
NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   3         3         3       6s    这个是新的RS,已经创建完毕
nginx-deployment-2035384211   0         0         0       36s   这个是旧的,已经全部归0
翻转 滚动更新
kubectl describe deployments nginx-deployment

Normal  ScalingReplicaSet  2m    deployment-controller  Scaled up replica set nginx-deployment-2035384211 to 3  # 旧的创建到3个
Normal  ScalingReplicaSet  24s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 1  # 新的先创建1个
Normal  ScalingReplicaSet  22s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 2 # 旧的缩容到2个
Normal  ScalingReplicaSet  22s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 2 # 新的扩容到2个
Normal  ScalingReplicaSet  19s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 1 # 旧的缩容到1个
Normal  ScalingReplicaSet  19s   deployment-controller  Scaled up replica set nginx-deployment-1564180365 to 3
Normal  ScalingReplicaSet  14s   deployment-controller  Scaled down replica set nginx-deployment-2035384211 to 0

更新了Deployment,控制标签匹配.sepc.selector, 模板不匹配.sepc.template。会进行缩容和扩容进行更新

回滚更新

检查上线状态

kubectl rollout status deployment/nginx-deployment

Waiting for rollout to finish: 1 out of 3 new replicas have been updated...

获取rs信息

kubectl get rs

NAME                          DESIRED   CURRENT   READY   AGE
nginx-deployment-1564180365   3         3         3       25s
nginx-deployment-2035384211   0         0         0       36s
nginx-deployment-3066724191   1         1         0       6s

获取pod信息

kubectl get pods

NAME                                READY     STATUS             RESTARTS   AGE
nginx-deployment-1564180365-70iae   1/1       Running            0          25s
nginx-deployment-1564180365-jbqqo   1/1       Running            0          25s
nginx-deployment-1564180365-hysrc   1/1       Running            0          25s
nginx-deployment-3066724191-08mng   0/1       ImagePullBackOff   0          6s  # 新的RS中的新的POD卡在镜像拉取

检查上线历史

kubectl rollout history deployment/nginx-deployment

deployments "nginx-deployment"
REVISION    CHANGE-CAUSE
1           kubectl apply --filename=https://k8s.io/examples/controllers/nginx-deployment.yaml
2           kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
3           kubectl set image deployment/nginx-deployment nginx=nginx:1.161

查看修订历史的详细信息

kubectl rollout history deployment/nginx-deployment --revision=2

deployments "nginx-deployment" revision 2
  Labels:       app=nginx
          pod-template-hash=1159050644
  Annotations:  kubernetes.io/change-cause=kubectl set image deployment/nginx-deployment nginx=nginx:1.16.1
  Containers:
   nginx:
    Image:      nginx:1.16.1
    Port:       80/TCP
     QoS Tier:
        cpu:      BestEffort
        memory:   BestEffort
    Environment Variables:      <none>
  No volumes

撤销当前上线,回滚到上一个版本
kubectl rollout undo deployment/nginx-deployment

回滚到指定的版本
kubectl rollout undo deployment/nginx-deployment –to-revision=2

缩放

kubectl scale deployment/nginx-deployment –replicas=10

暂停和恢复更新

kubectl rollout pause deployment/nginx-deployment

kubectl rollout resume deployment/nginx-deployment

监视上线状态

kubectl get rs –watch

Deployment状态,清理策略,金丝雀部署,规范 简单过了下

ReplicaSet

维持在任何给定时间运行的一组稳定的设置数量且完全相同的副本Pod,通常用Deployment来自动管理

会在pod上添加metadata.ownerReferences字段,来标注属于哪个ReplicaSet。如果POD上没有ownerReference或者其ownerReference不是一个控制器,匹配到某ReplicaSet的选择运算符,则该pod会被RS获得。

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: frontend
  labels:
    app: guestbook
    tier: frontend
spec:
  # 按你的实际情况修改副本数
  replicas: 3
  selector:
    matchLabels:
      tier: frontend
  template:
    metadata:
      labels:
        tier: frontend
    spec:
      containers:
      - name: php-redis
        image: us-docker.pkg.dev/google-samples/containers/gke/gb-frontend:v5

除了kind字段和Deployment章节的例子几乎完全一样,但是指定成ReplicaSet后,会失去Deployment的自动扩容和回滚等功能。ReplicaSet主要用于保证指定数量的pod副本运行。

kubectl apply -f https://kubernetes.io/examples/controllers/frontend.yaml

kubectl get rs
NAME       DESIRED   CURRENT   READY   AGE
frontend   3         3         3       6s

kubectl describe rs/frontend

kubectl get pods
NAME             READY   STATUS    RESTARTS   AGE
frontend-gbgfx   1/1     Running   0          10m
frontend-rwz57   1/1     Running   0          10m
frontend-wkl7w   1/1     Running   0          10m

可以看到pods的命名里少了一节Deployment中RS的段,因为kind:ReplicaSet模式下定义的name直接就是RS的,而不是定义的Deployment,由Deployment来创建RS。

假定你在ReplicaSet已经被部署之后创建Pod,并且你已经在ReplicaSet中设置了其初始的Pod副本数以满足其副本计数需要,新的Pod会被该ReplicaSet 获取,并立即被ReplicaSet终止, 因为它们的存在会使得ReplicaSet中Pod个数超出其期望值。

删除相关的过了一下

StatefulSet

当个Web-0的phase进入Running,Condition变成Ready前,Web-0一直是Pending状态。

和Deployment不同的是,StatefulSet为它们的每个Pod维护了一个有粘性的永远不变的ID。使用存储卷,这个存储卷是啥?

  • 稳定的、唯一的网络标识符。
  • 稳定的、持久的存储。
  • 有序的、优雅的部署和扩缩。
  • 有序的、自动的滚动更新。

给定Pod的存储必须由PersistentVolume Provisioner基于所请求的storage class来制备,或者由管理员预先制备。删除或者扩缩StatefulSet并不会删除它关联的存储卷。这样做是为了保证数据安全,它通常比自动清除StatefulSet所有相关的资源更有价值。

apiVersion: v1
kind: Service
metadata:
  name: nginx
  labels:
    app: nginx
spec:
  ports:
  - port: 80
    name: web
  clusterIP: None
  selector:
    app: nginx
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: web
spec:
  selector:
    matchLabels:
      app: nginx # 必须匹配 .spec.template.metadata.labels
  serviceName: "nginx"
  replicas: 3 # 默认值是 1
  updateStrategy: RollingUpdate # 默认的滚动更新   OnDelete手动删除pod后控制器才会创建新的pod,这时才能响应.sepc.template的变动
  minReadySeconds: 10 # 默认值是 0
  template:
    metadata:
      labels:
        app: nginx # 必须匹配 .spec.selector.matchLabels
    spec:
      terminationGracePeriodSeconds: 10
      containers:
      - name: nginx
        image: registry.k8s.io/nginx-slim:0.24
        ports:
        - containerPort: 80
          name: web
        volumeMounts:
        - name: www
          mountPath: /usr/share/nginx/html
  volumeClaimTemplates: # 这个字段是创建PersistentVolumeClaim, PersistentVolume制备和PersistentVolumes是啥?卷相关的还比较多,后面有单独的章节,不急。
  - metadata:
      name: www
    spec:
      accessModes: [ "ReadWriteOnce" ]
      storageClassName: "my-storage-class"
      resources:
        requests:
          storage: 1Gi
集群域名 服务(名字空间/名字) StatefulSet(名字空间/名字) StatefulSet 域名 Pod DNS Pod 主机名
cluster.local default/nginx default/web nginx.default.svc.cluster.local web-{0..N-1}.nginx.default.svc.cluster.local web-{0..N-1}
cluster.local foo/nginx foo/web nginx.foo.svc.cluster.local web-{0..N-1}.nginx.foo.svc.cluster.local web-{0..N-1}

web-{0..N-1}扩容按顺序进行。缩容会逆序执行。当前执行完毕之后,才会处理下一个。

按照与Pod终止相同的顺序,Kubernetes控制平面会等到被更新的Pod进入Running和Ready状态,然后再更新其前身。如果有minReadySeconds则还会进行额外等待。

分区滚动更新

.spec.updateStrategy.rollingUpdate.partition

当StatefulSet的.spec.template被更新时,所有序号大于等于该分区序号的Pod都会被更新。所有序号小于该分区序号的Pod都不会被更新,并且即使它们被删除也会依据之前的版本进行重建。如果StatefulSet的.spec.updateStrategy.rollingUpdate.partition大于.spec.replicas,则对它的.spec.template的更新将不会传递到它的Pod。

这里还是实操一下比较好

稳定的存储 PersistentVolumeClaim保留

对于StatefulSet中定义的每个VolumeClaimTemplate,每个Pod会收到基于storage class: my-storage-class分配的1GiB的PersistentVolume。当pod被调度(重新调度)到节点时,volumeMounts会挂载与PersistentVolumeClaim关联的PersistentVolume。pod或StatefulSet被删除时,与PersistentVolumeClaim关联的PersistentVolume不会被一起自动删除,只能手动删除。

.spec.persistentVolumeClaimRetentionPolicy可以控制是否保留等,简单过了下,默认都是保留。

StatefulSet的.spec.volumeClaimTemplates会自动创建PVC,而POD就有的.spec.volumes需要指定已经创建好的PVC

# pv
apiVersion: v1
kind: PersistentVolume
metadata:
  name: nginx-pv
spec:
  capacity:
    storage: 1Gi
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  hostPath:
    path: /mnt/data

# pvc  POD需要创建好的PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: nginx-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi

DaemonSet

提供节点本地设施,新Node加入时会自动部署对应的pod。

  • 节点上运行集群守护进程
  • 节点上

JOB

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  # 这里没有selector了
  template:
    spec:
      containers:
      - name: pi
        image: resouer/ubuntu-bc 
        command: ["sh", "-c", "echo 'scale=10000; 4*a(1)' | bc -l "]
      restartPolicy: Never # 只能是Never或者OnFailure
  backoffLimit: 4 # 默认是6

当restartPolicy指定Never时,会不断创建新的POD,间隔(10S,20S,40S,80S)重试4次。
指定OnFailure时,会不断重启现有的容器,不会创建新的

可以控制并行进行

apiVersion: batch/v1
kind: Job
metadata:
  name: pi
spec:
  parallelism: 2 # 最大并行数量
  completions: 4 # 最小完成数量
  template:
    spec:
      containers:
      - name: pi
        image: resouer/ubuntu-bc
        command: ["sh", "-c", "echo 'scale=5000; 4*a(1)' | bc -l "]
      restartPolicy: Never
  backoffLimit: 4
外部管理器+Job模板
apiVersion: batch/v1
kind: Job
metadata:
  name: process-item-$ITEM
  labels:
    jobgroup: jobexample
spec:
  template:
    metadata:
      name: jobexample
      labels:
        jobgroup: jobexample
    spec:
      containers:
      - name: c
        image: busybox
        command: ["sh", "-c", "echo Processing item $ITEM && sleep 5"]
      restartPolicy: Never

使用工具对$ITEM进行替换后执行
KubeFlow使用的这种方式

拥有固定任务数目的并行 Job。没太看懂

指定并行度(parallelism),但不设置固定的 completions 的值。

CronJob
apiVersion: batch/v1beta1
kind: CronJob
metadata:
  name: hello
spec:
  schedule: "*/1 * * * *"
  jobTemplate:
    spec:
      template:
        spec:
          containers:
          - name: hello
            image: busybox
            args:
            - /bin/sh
            - -c
            - date; echo Hello from the Kubernetes cluster
          restartPolicy: OnFailure

像是Deploymet管理RS。Cronjob管理job

spec.concurrencyPolicy,控制上一个Job未完成的时候,新Job开始时的动作

  1. concurrencyPolicy=Allow,默认,Job 可以同时存在;
  2. concurrencyPolicy=Forbid,不会创建新的 Pod,该创建周期被跳过;
  3. concurrencyPolicy=Replace,新产生的 Job 会替换旧的、没有执行完的 Job。

一次Job失败miss+1,当miss达到100就会停止创建Job。统计时间段可以由startingDeadlineSeconds指定。

管理工作负载

—可以分割多个负载放入一个文件中。

自动扩缩

水平扩缩:运行多个实例,通过HPA实现。垂直扩缩:调整容器的CPU和内存资源、VPA不过是插件形式。

服务,负载均衡,联网

POD会获得此集群下的一个唯一IP,POD内部容器可以通过localhost通信, POD之间可以相互通信。

ServiceAPI可以为一个或多个后端POD提供一个稳定的IP地址或主机名。K8S自动管理EndpointSlice对象,提供Service的POD信息。服务代理实现通过操作系统或云平台API来拦截或重写数据包,监视Service和EndpointSlice对象集,在数据平面编程将服务流量路由到其后端。

Gateway API(前身是Ingress):使得集群外能够访问Service

NetworkPolic:控制POD之间的流量,POD和外部的流量

Service

apiVersion: v1
kind: Service
metadata:
  name: my-service
spec:
  selector:
    app: my-app
  ports:
    - name: http
      protocol: TCP
      port: 80
      targetPort: 8080
  type: ClusterIP

对集群中其他服务暴露80端口,转到selector筛选后pod的端口8080。K8S会为Service分配一个集群IP。

Pod的定义

apiVersion: v1
kind: Pod
metadata:
  name: nginx
  labels:
    app.kubernetes.io/name: nginx-proxy
spec:
  containers:
  - name: nginx
    image: nginx:stable
    ports:
      - containerPort: 80
        name: http-web-svc # 这里给80端口起了一个名字

ClusterIP

apiVersion: v1
kind: Service
metadata:
  name: nginx-cluster
spec:
  type: ClusterIP # ClusterIP是默认值,只能在集群内访问,可以使用Ingress或者GatewayAPI向外暴露服务。NodePort:通过Node上的IP和端口公开。ExternalName:映射到主机名
  selector:
    app.kubernetes.io/name: nginx-proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc # 直接使用名字

NodePort


apiVersion: v1
kind: Service
metadata:
  name: nginx-nodeport
spec:
  type: NodePort # NodePort:每个Node的kube-proxy监听nodePort并转发到符合selector的Pod的targetPort
  selector:
    app.kubernetes.io/name: nginx-proxy
  ports:
  - name: name-of-service-port
    protocol: TCP
    port: 80
    targetPort: http-web-svc # 直接使用名字
    nodePort: 30000

lb


apiVersion: v1
kind: Service
metadata:
  name: nginx-lb
spec:
  type: LoadBalancer # 云厂商的CCM监听到后,会自动创建负载均衡,分配EXTERNAL-IP,通过EXTERNAL-IP调用时,会自动负载均衡到某一个pod
  selector:
    app.kubernetes.io/name: nginx-proxy
  ports:
  - name: http
    protocol: TCP
    port: 80
    targetPort: http-web-svc

Ingress

Ingress Controller通过Service(LoadBalance或者NodePort)对外暴露入口,根据其中设置的规则,将流量转发到指定的pod。

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: cafe-ingress
spec:
  rules:
  - host: cafe.example.com # IngressRule的Key
    http:
      paths:
      - path: /tea
        backend:
          serviceName: tea-svc
          servicePort: 80
      - path: /coffee
        backend:
          serviceName: coffee-svc
          servicePort: 80

Ingress对象更新后nginx-ingress-controller就会根据内容生成一个nginx配置

Service和Ingress

Service工作在四层,Ingress工作在七层,涉及TLS等HTTP内容是只能使用ingress

EndpointSlic

EndpointSlic对象是某个Service的后端网络断点的子集

DNS

cluster-domain默认为cluster.local.

同一集群可以省略<cluster-domain>, 同一命名空间可以省略<namespace>

服务的DNS名称格式为 <service-name>.<namespace>.svc.<cluster-domain>,短域名<service-name>.<namespace>,同一命名空间<service-name>

Pod的DNS名称格式为 <pod-ip>.<namespace>.pod.<cluster-domain>
同时还有端口的SRV记录_<port-name>._<protocol>.<service-name>.<namespace>.svc.cluster.local

记录 A SRV PTR

nslookup -type=A paifu
Server:         9.166.175.254
Address:        9.166.175.254#53

Name:   paifu.gamematrix.svc.cluster.local
Address: 9.166.174.123

nslookup -type=SRV _http-metrics._tcp.cgi-common-sendsysmail.happygame
Server:         9.166.175.254
Address:        9.166.175.254#53

_http-metrics._tcp.cgi-common-sendsysmail.happygame.svc.cluster.local   service = 0 100 9100 cgi-common-sendsysmail.happygame.svc.cluster.local.

nslookup -type=PTR 9.166.169.229
Server:         9.166.175.254
Address:        9.166.175.254#53

229.169.166.9.in-addr.arpa      name = 9-166-169-229.paifu.gamematrix.svc.cluster.local.
229.169.166.9.in-addr.arpa      name = 9-166-169-229.service-hlsvr.gamematrix.svc.cluster.local.
229.169.166.9.in-addr.arpa      name = 9-166-169-229.service-configagent.gamematrix.svc.cluster.local.

Cluster IP的service
<svc>.<namespace>.svc.cluster.local 指向Service的ClusterIP(VIP)

Headless Service
<svc>.<namespace>.svc.cluster.local 返回所有后端的Endpoints的IP列表

Pod的记录
<pod-ip-with-dashes>.<namespace>.pod.cluster.local, pod-ip-with-dashes: 将.替换成-,10.1.2.3 -> 10-1-2-3

StatefulSet的格式
<hostname>.<subdomain>.<namespace>.svc.cluster.local, subdomain一般是0,1,2,3

IPV6支持,感知路由,Windows,Cluster IP分配,跳过

同一Node中Pod的通信

service中.spec.internalTrafficPolicy设置成Local,只会选择本Node中的Pod

存储

卷为POD中的容器提供了通过文件系统访问和共享数据的方式,可以进行数据的持久存储和共享。

hostPath 不能迁移到其他Node

spec:
  volumes:
    - hostPath:
        path: /data/corefile/  # 给Node的此目录命名
        type: ''
      name: corefiles
  containers:
    - volumeMounts:
        - name: corefiles
          mountPath: /data/corefile/ # 将Node目录挂载到容器的此目录

emptyDir POD移除时内容消失

spec:
  volumes:
    - name: share_empty # 从节点临时存储创建一个卷
      emptyDir: {}
  containers:
    - volumeMounts:
        - name: share_empty # 挂载卷,POD崩溃时卷内容依然存在,POD删除时内容移除,适合用来做缓存
          mountPath: /etc/config   

持久卷

存储如何制备的细节从其如何被使用中抽象出来

持久卷(PersistentVolume,PV): 集群中的存储,可手动制备或使用存储类动态制备,拥有独立于任何使用PV的POD的生命周期。将物理存储方式隐藏起来,对外提供存储

持久卷申领(PersistentVolumeClaim,PVC):POD会消耗Node资源,PVC可以设定大小或访问模式来消耗PV资源。

保护:删除被某Pod使用的PVC对象,PVC申领不会被立即移除。PVC对象的移除会被推迟,直至其不再被任何Pod使用。删除已绑定到某PVC申领的PV卷,该PV卷也不会被立即移除。PV对象的移除也要推迟到该PV不再绑定到PVC。可以使用kubectl describe pvc hostpath查看

PVC可以自动绑定到匹配的PV。也可以手动指定未通过claimRef预留给其他PVC的PV。

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: foo-pvc
  namespace: foo
spec:
  storageClassName: "" # 此处须显式设置空字符串,否则会被设置为默认的 StorageClass
  volumeName: foo-pv

---

apiVersion: v1
kind: PersistentVolume
metadata:
  name: foo-pv
spec:
  storageClassName: ""
  claimRef:
    name: foo-pvc
    namespace: foo

创建PV和PVC,POD申领PVC

apiVersion: v1
kind: PersistentVolume
metadata:
  name: pv0003
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem # Filesystem文件系统,可以被直接挂载。Block块,POD和卷之间不存在文件系统,原始块卷
  accessModes:
    - ReadWriteOnce # ReadWriteOnce被Node中多个Pod读写挂载,ReadOnlyMany,ReadWriteMany,ReadWriteOncePod被Node中一个POD读写挂载
  persistentVolumeReclaimPolicy: Retain # PVC被删除时PV依然保留,对应数据卷视为已经释放,卷存在此前申领人的数据,不能用于其他申领。
  storageClassName: slow
  mountOptions:
    - hard
    - nfsvers=4.1
  nfs:
    path: /tmp
    server: 172.17.0.2
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: myclaim
spec:
  accessModes:
    - ReadWriteOnce
  volumeMode: Filesystem
  # volumeName: 指定要绑定的PV,不指定则会自动创建
  resources:
    requests:
      storage: 5Gi
  storageClassName: slow # PVC指定时,只有存在相同指定slow的PV,申领才能成功
  selector: # 选择算符,设置之后不会动态制备PV卷
    matchLabels:
      release: "stable"
    matchExpressions:
      - {key: environment, operator: In, values: [dev]}
apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
    - name: myfrontend
      image: nginx
      volumeMounts:
      - mountPath: "/var/www/html"
        name: mypd
  volumes:
    - name: mypd
      persistentVolumeClaim:
        claimName: myclaim

节点亲和性

限制那些节点可以访问此卷,使用这些卷的POD只会被调度到规则选择的节点上执行。.spec.nodeAffinity

阶段

持久卷阶段:Available,Bound,Release(已经被删除,关联的存储资源未被回收),Failed。

kubectl describe persistentvolume <name> 查看绑定到PV的PVC的名称

StorageClass

可能会存在大量的PVC,如果每个PV都手动创建则非常繁琐。
自动PV的机制核心是StorageClass。
StorageClass实际是PV的模板。定义了PV的属性:存储类型,Volume大小。创建PV用到的存储插件。这样K8S就可以根据PVC的创建请求,找到StorageClass创建出PV。

对于支持动态供应的存储系统,可以用来创建PV。
使用不支持动态供应的节点本地存储,需要手动创建PV表示具体的存储资源,但此时StorageClass仍能用来定义一些内容。

provisioner,parameters,reclaimPolicy是必须的字段

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: low-latency
  annotations:
    storageclass.kubernetes.io/is-default-class: "false" # true 设置成默认的,当PVC未指定时会使用
provisioner: csi-driver.example-vendor.example # 指定制作PV的卷插件
reclaimPolicy: Retain # 默认值是 Delete
allowVolumeExpansion: true # 允许部分类型的卷调整PVC对象大小,申请新的,大容量的存储卷。
mountOptions:
  - discard # 这可能会在块存储层启用 UNMAP/TRIM
volumeBindingMode: WaitForFirstConsumer # 默认的Immediate是立刻绑定,可能导致PV与POD的可用区不同,速度降低。WaitForFirstConsumer将PVC的绑定从立刻绑定到PV,延迟到确定POD调度位置之后进行。
parameters:
  guaranteedReadWriteLatency: "true" # 这是服务提供商特定的

存储类,PV,PVC,使用存储类进行动态卷制备

存储管理实际是PV负责抽象物理存储,PVC负责消费。存储类负责PVC消耗时匹配对应的PV,如果PV不存在且可以动态制备,存储类会创建PV,但此时也是PV负责抽象的物理存储

PV和PVC也实现了职责分离,PVC更像是接口调用,PV提供接口。开发人员只需要调用接口,接口PV的维护则是运维处理。

PVC:Pod想要的大小,权限
PV:描述Volume的属性,Volume类型,挂载目录,远程服务器地址
StorageClass:PV的模板。同属一个StorageClass的PV和PVC才能进行绑定

本地存储

PV,PVC,StorageClass使用的大多是远程存储来保证和节点无关,方便一个StorageClass在不同Node上使用。
然而部分服务器需要高性能的本地存储,如部分服务会将数据落地磁盘缓存。而本地存储这个是和Node绑定的(通过 PV 的 nodeAffinity 实现)。不同Node的本地存储是互相隔离的。且由于数据在Node上,一旦Node出现问题,数据就会丢失。所以本地存储要求具备定时备份能力。

延迟绑定:现在假设创建一个PVC,PVC会向Storageclass申请创建一个PV,此时由于没有Pod的信息,可能创建到了Node1上。Pod创建且使用此PVC时,会被直接约束在Node1上。延迟到POD创建时绑定,则可以根据POD的要求和选择更合适的PV进行绑定。避免被调度到不合适的Node上。

不应该将宿主机的目录当做PV使用:宿主机目录不可控,PVC申请的资源可能会被侵占。缺乏配额保证,缺乏隔离会被其他容器修改。

本地存储多为静态制备:PVC创建时会立刻与PV绑定,如果使用的是远程目录,立即绑定不会出现问题。使用本地目录则可能绑定到预期外的Node上,导致后续POD也被创建到此Node上。不过目前已经有provisioner支持动态制备。

apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: local-storage
provisioner: kubernetes.io/no-provisioner # 不支持动态制备
volumeBindingMode: WaitForFirstConsumer

---

apiVersion: v1
kind: PersistentVolume
metadata:
  name: local-pv
spec:
  capacity:
    storage: 1Ti
  accessModes:
    - ReadWriteOnce
  persistentVolumeReclaimPolicy: Retain
  storageClassName: local-storage
  local:
    path: /data
  nodeAffinity:
    required:
      nodeSelectorTerms:
      - matchExpressions:
        - key: kubernetes.io/hostname
          operator: In
          values:
          - 1.1.1.1

---

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: local-pvc
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Ti
  storageClassName: local-storage

---

apiVersion: v1
kind: Pod
metadata:
  name: my-pod
spec:
  containers:
  - name: my-container
    image: nginx
    volumeMounts:
    - mountPath: /data
      name: my-volume
  volumes:
  - name: my-volume
    persistentVolumeClaim:
      claimName: local-pvc

投射卷

将多个不同内容来源,聚合到一个挂载目录。支持在一个目录下挂载多个来源

支持的投射类型

  • configMap
  • secret
  • downwardAPI
  • serviceAccountToken
Secret

将etcd中的内容,映射到容器的某个目录上,提供了加密手段

configMap

于Secret几乎相同,不过是不需要加密的数据

spec:
  volumes:
    - name: config-vol
      configMap:
        name: log-config # 声明一个卷config-vol,log-config中log_level条目会 只读 的保存到log_level.conf中
        items:
          - Key: log_level
            path: log_level.conf
  containers:
    - volumeMounts:
        - name: config-vol # 将卷config-vol挂载到容器中此目录,目录中存在log_level.conf文件
          mountPath: /etc/config   
downwardAPI

提供访问容器属性的方法

apiVersion: v1
kind: Pod
metadata:
  name: test-downwardapi-volume
  labels:
    zone: us-est-coast
    cluster: test-cluster1
    rack: rack-22
spec:
  containers:
    - name: client-container
      image: k8s.gcr.io/busybox
      command: ["sh", "-c"]
      args:
      - while true; do
          if [[ -e /etc/podinfo/labels ]]; then
            echo -en '\n\n'; cat /etc/podinfo/labels; fi;
          sleep 5;
        done;
      volumeMounts:
        - name: podinfo
          mountPath: /etc/podinfo
          readOnly: false
  volumes:
    - name: podinfo
      projected:
        sources:
        - downwardAPI:
            items:
              - path: "labels"
                fieldRef:
                  fieldPath: metadata.labels

使用kubectl logs查看

serviceAccountToken
K8S为每个pod都默认挂载了serviceAccountToken到固定目录。为pod提供访问K8S API Server的token。容器中的进程如果需要使用K8S API Server就需要加载token。
算是特殊的secret

临时卷

和POD保持统一生命周期,随POD创建和删除

kind: Pod
apiVersion: v1
metadata:
  name: my-app
spec:
  containers:
    - name: my-frontend
      image: busybox:1.28
      volumeMounts:
      - mountPath: "/scratch"
        name: scratch-volume
      command: [ "sleep", "1000000" ]
  volumes:
    - name: scratch-volume
      ephemeral: # 定义一个临时卷
        volumeClaimTemplate: # 使用模板可以自动创建PVC,使用persistentVolumeClaim则需要手动指定一个创建好的PVC
          metadata:
            labels:
              type: my-frontend-volume
          spec:
            accessModes: [ "ReadWriteOnce" ]
            storageClassName: "scratch-storage-class"
            resources:
              requests:
                storage: 1Gi

卷属性类,Beta功能跳过

卷快照,卷快照类,CSI卷克隆,先跳过

配置

ConfigMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: game-demo
data:
  # 类属性键;每一个键都映射到一个简单的值
  player_initial_lives: "3"

  # 类文件键
  game.properties: |
    enemy.types=aliens,monsters
    player.maximum-lives=5    


apiVersion: v1
kind: Pod
metadata:
  name: configmap-demo-pod
spec:
  containers:
      env:
        # 定义环境变量
        - name: PLAYER_INITIAL_LIVES # 请注意这里和 ConfigMap 中的键名是不一样的
          valueFrom:
            configMapKeyRef:
              name: game-demo           # 这个值来自 ConfigMap
              key: player_initial_lives # 需要取值的键
      volumeMounts:
      - name: config
        mountPath: "/config"
        readOnly: true
  volumes:
  # 你可以在 Pod 级别设置卷,然后将其挂载到 Pod 内的容器中
  - name: config
    configMap:
      # 提供你想要挂载的 ConfigMap 的名字
      name: game-demo
      # 来自 ConfigMap 的一组键,将被创建为文件
      items:
      - key: "game.properties"
        path: "game.properties"

Secret

apiVersion: v1
kind: Secret
metadata:
  name: dotfile-secret
data:
  .secret-file: dmFsdWUtMg0KDQo=
---
apiVersion: v1
kind: Pod
metadata:
  name: secret-dotfiles-pod
spec:
  volumes:
    - name: secret-volume
      secret:
        secretName: dotfile-secret
  containers:
    - name: dotfile-test-container
      image: registry.k8s.io/busybox
      command:
        - ls
        - "-l"
        - "/etc/secret-volume"
      volumeMounts:
        - name: secret-volume
          readOnly: true
          mountPath: "/etc/secret-volume"  # 最终表现为/etc/secret-volume/.secret-file文件

资源

为POD指定资源的Request后,kube-scheduler就会决定将POD调度到哪个节点上,如果节点有大于Request的剩余量,POD可以多使用一些。但是指定limit后确保资源不超过设定值。

CPU限制:通过CPU节流机制强制执行

CPU单位:1CPU = 1个物理核或者1个虚拟核,1 CPU=1000m CPU,即500m为半个核心。最小为1m。

Memory限制:使用OOM终止机制执行

Memory单位:400m表示0.4字节,400Mi表示400M直接

apiVersion: v1
kind: Pod
metadata:
  name: frontend
spec:
  containers:
  - name: app
    image: images.my-company.example/app:v4
    resources:
      requests:
        memory: "64Mi"
        cpu: "250m"
      limits:
        memory: "128Mi"
        cpu: "500m"

安全-暂时跳过

策略

范围限制 LimitRange

指定一个命名空间

  1. 设置POD或者Container的最小和最大的资源使用量
  2. PVC最大和最小的存储空间

如果新创建的POD或者container不满足要求则会创建失败

资源配额

设定一个命名空间下,各种资源的上限或者下限

节点资源管理器

可以设置CPU调度相关,比如将POD绑定到某个核心上提高缓存使用效率。

调度,强占和驱逐

调度:确保POD匹配到合适的节点,以便kubelet(Node上的运行的代理,保证容器运行在POD中)能够运行POD。
强占:终止低优先级的POD,以便运行高优先级的POD
驱逐:资源匮乏的节点上,主动让一个或多个POD失效

调度器

通过Watch机制发现集群中新创建且未被调度到节点上的POD,将其调度到合适的节点上运行

可调度节点:满足一个POD调度请求的节点。没有节点满足时,POD将停留在未调度状态

绑定:可调度节点中打分,选出最高分的节点,通知kube-apiserver。

特定节点上运行POD

  1. 节点标签,nodeSelector
  2. 亲和性
    1. requiredDuringSchedulingIgnoredDuringExecution:规则满足时执行调度,是语法表达能力更强的nodeSelector
    2. preferredDuringSchedulingIgnoredDuringExecution:找不到匹配节点时,仍会调度
  3. 反亲和性
    1. requiredDuringSchedulingIgnoredDuringExecution:满足规则是不执行调度
    2. preferredDuringSchedulingIgnoredDuringExecution:最好不调度到匹配的节点上
apiVersion: v1
kind: Pod
metadata:
  name: with-node-affinity
spec:
  affinity:
    nodeAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: # 必须有
        nodeSelectorTerms:
        - matchExpressions:
          - key: topology.kubernetes.io/zone
            operator: In
            values:
            - antarctica-east1
            - antarctica-west1
      preferredDuringSchedulingIgnoredDuringExecution: # 最好有
      - weight: 1 # 最好有的里面存在多项时,每项的权重,权重越高得分越高
        preference:
          matchExpressions:
          - key: another-node-label-key
            operator: In
            values:
            - another-node-label-value

topologyKey:拓扑域,hostname 节点级别,zone可用区级别,region区域级别。

apiVersion: v1
kind: Pod
metadata:
  name: with-pod-affinity
spec:
  affinity:
    podAntiAffinity:
      requiredDuringSchedulingIgnoredDuringExecution: # 不调度到这个主机上
      - labelSelector:
          matchExpressions:
          - key: app # 符合标签描述的POD
            operator: In
            values:
            - my-app
        topologyKey: "kubernetes.io/hostname" # 符合标签描述的POD,其所属的节点(hostname域)会被反亲和,同节点的无法调度。
      preferredDuringSchedulingIgnoredDuringExecution:
      - weight: 100
        podAffinityTerm:
          labelSelector:
            matchExpressions:
            - key: security
              operator: In
              values:
              - S2
          topologyKey: topology.kubernetes.io/zone

POD开销

POD除了内部容器消耗请求的资源,POD本身也会消耗一些资源,调度时会将二者资源相加去寻找节点。

kind: RuntimeClass进行配置

POD调度就绪态

通过yaml中的schedulingGates将就绪的POD放入等待列表,不是立即进行调度。删除后可以进行调度

POD拓扑分布约束,内容很多,简单过了下

将POD分布到不同的拓扑中以提高可用性,或者使得客户端就近调度

污点和容忍度

污点使得节点能够排斥一类特定的POD

调度框架

调度是串行的,绑定是并行。调度+绑定称为调度上下文

调度框架扩展点

EnqueueExtension:接口,插件实现此接口根据集群变化通过或者拒绝

QueueingHint:集群中发生变化时,此回调函数被执行,能控制POD放入活跃或回退队列。

  1. 进入队列前:
    1. PreEnqueue:在POD被添加到内部活动队列之前调用,此队列中的POD被标记为准备好进行调度
  2. 队列排序:
    1. PreFilter:预处理POD或者检查相关条件
    2. Filter:标记不符合此POD的节点
    3. PostFilter:没有可用节点时调度
    4. PreScore:生成一个可共享状态给Score插件使用
    5. Score:评分
    6. NormalizeScore:可以修改分数
    7. Reserve:防止调度器等待绑定成功是发生竞争情况
    8. Permit:批准,拒绝,等待POD的绑定
  3. 绑定:
    1. PreBind:执行POD绑定前的工作
    2. Bind:进行绑定
    3. PostBind:收尾清理相关资源

动态资源分配如GPU,跳过

调度器性能调优

通过配置减少调度流程中的操作项目,比如有一个可调度的Node就直接用,不用评估其他的Node

资源装箱,控制节点资源权重进而控制分数

POD优先级和抢占,通过PriorityClass使高优先级POD驱逐低优先POD

节点压力驱逐

根据节点压力驱逐

API发起的驱逐

API 发起的驱逐是一个先调用 Eviction API 创建 Eviction 对象,再由该对象体面地中止 Pod 的过程。

集群管理

  1. 自己机器上尝试K8S还是构建高可用节点,或者是参与开发
  2. 使用开箱即用的集群还是管理自己的
  3. 本地还是云上
  4. 本地配置时要选取合适的网络模型
  5. 裸机还是虚拟机运行
  6. 熟悉运行所需的相关组件

节点关闭

基于优先级的体面关闭,非体面关闭

Node自动缩容和扩容

Node不足时创建新的Node,足够时关闭Node。配合负载自动缩容和扩容使用

集群网络系统

  1. 高度耦合容器间通信:POD和localhost解决
  2. POD间通信
  3. POD与Service通信
  4. 外部和Service通信

日志架构

K8S支持将标准输入和输出定向到文件,也可以用过kubectl logs查看