入门操作
安装
# 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
kubeadm init --pod-network-cidr=192.168.0.0/16
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/calico.yaml
kubectl taint node master node-role.kubernetes.io/control-plane-
# /etc/crictl.yaml
runtime-endpoint: unix:///run/containerd/containerd.sock
image-endpoint: unix:///run/containerd/containerd.sock
概念
功能
- 服务发现和负载均衡: Kubernetes可以通过DNS名称或IP地址暴露容器,并自动进行负载均衡,确保流量均匀分配,保持部署稳定。
- 存储编排: Kubernetes支持自动挂载各种存储系统,如本地存储和公共云存储,简化存储管理。
- 自动部署和回滚: Kubernetes允许描述容器的期望状态,并自动调整实际状态以匹配期望状态,支持自动化部署和回滚。
- 自动完成装箱计算: Kubernetes根据容器的CPU和内存需求,智能调度容器到集群中的节点,优化资源利用。
- 自我修复: Kubernetes会自动重启失败的容器、替换不健康的容器,并在服务准备好之前不将其通告给客户端。
- 密钥与配置管理: Kubernetes可以安全地存储和管理敏感信息,如密码和密钥,支持在不重建镜像的情况下更新配置。
- 批处理执行: Kubernetes不仅管理服务,还支持批处理和CI工作负载,自动替换失败的容器。
- 水平扩缩: Kubernetes支持通过简单命令、用户界面或根据CPU使用率自动扩缩应用。
- IPv4/IPv6双栈: Kubernetes为Pod和Service分配IPv4和IPv6地址,支持双栈网络。
- 为可扩展性设计: 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集群由控制平面和一个或多个工作节点组成。
- 控制平面组件:管理集群的整体状态,负责资源调度,检测和响应集群事件
- kube-apiserver:公开 Kubernetes HTTP API 的核心组件服务器
- etcd:一致性和高可用的键值存储,用于API服务器的数据存储
- kube-scheduler:监听新创建的未指定运行Node的Pods,将Pod分配给合适的Node。资源需求,软硬件,策略约束,亲和,反亲和,数据位置,工作覆盖干扰,最后时限。东西还挺多,每个感觉都能看看
- kube-controller-manager:运行控制器来实现 Kubernetes API 行为。(不太懂)
- Node控制器,在节点出现故障时进行响应
- Job控制器,啥玩意
- EndpointSlice控制器,填充EndpointSlice对象,提供Service和Pod之间的链接。
- ServiceAccount控制器,啥玩意
- Node 组件:运行在节点上,维护pod并提供k8s运行时环境
- kubelet:确保pod及其容器正常运行。保证容器都运行在pod中,啥玩意
- kube-proxy:维护节点网络规则来实现service功能,(Kubernetes中Service是将运行在一个或一组Pod上的应用可被客户端访问,Service提供一个虚拟的IP地址和端口,以及选择器selector如
k8s-app: kube-dns带有此标签的服务A会被注册到这个Service实现转发的作用)。
- 插件
- DNS
- Web界面
- 容器资源监控
- 日志
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=端口
容器回调
回调:无参数,执行失败会杀掉容器
PostStart:容器创建之后被立即执行。执行时间过长或挂起,可能容器无法进入running状态。失败会发出FailedPostStartHook事件。
PreStop:容器被终止前调用。需要在限定时间内执行完成,时间一到即刻上路。失败会发出FailedPreStopHook事件。
注册方式(执行方式)
Exec,执行命令,HTTP,Sleep
kubectl describe pod lifecycle-demo
工作负载
工作负载是运行程序,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按顺序执行完毕之后退出,应用容器才会运行
应用
- 运行shell检查并等待一个Service完成创建(Init运行结束之后,应用容器才会运行)
- curl注册POD到远程服务器
- sleep应用容器启动之前,等待一段时间。
- 克隆git仓库到卷(后面会有一大章节介绍)中
- 渲染配置文件的模板,如模板需要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和容器字段暴露给运行中的容器
- 作为环境变量
- 作为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开始时的动作
- concurrencyPolicy=Allow,默认,Job 可以同时存在;
- concurrencyPolicy=Forbid,不会创建新的 Pod,该创建周期被跳过;
- 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
指定一个命名空间
- 设置POD或者Container的最小和最大的资源使用量
- PVC最大和最小的存储空间
如果新创建的POD或者container不满足要求则会创建失败
资源配额
设定一个命名空间下,各种资源的上限或者下限
节点资源管理器
可以设置CPU调度相关,比如将POD绑定到某个核心上提高缓存使用效率。
调度,强占和驱逐
调度:确保POD匹配到合适的节点,以便kubelet(Node上的运行的代理,保证容器运行在POD中)能够运行POD。
强占:终止低优先级的POD,以便运行高优先级的POD
驱逐:资源匮乏的节点上,主动让一个或多个POD失效
调度器
通过Watch机制发现集群中新创建且未被调度到节点上的POD,将其调度到合适的节点上运行
可调度节点:满足一个POD调度请求的节点。没有节点满足时,POD将停留在未调度状态
绑定:可调度节点中打分,选出最高分的节点,通知kube-apiserver。
特定节点上运行POD
- 节点标签,nodeSelector
- 亲和性
- requiredDuringSchedulingIgnoredDuringExecution:规则满足时执行调度,是语法表达能力更强的nodeSelector
- preferredDuringSchedulingIgnoredDuringExecution:找不到匹配节点时,仍会调度
- 反亲和性
- requiredDuringSchedulingIgnoredDuringExecution:满足规则是不执行调度
- 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放入活跃或回退队列。
- 进入队列前:
- PreEnqueue:在POD被添加到内部活动队列之前调用,此队列中的POD被标记为准备好进行调度。
- 队列排序:
- PreFilter:预处理POD或者检查相关条件
- Filter:标记不符合此POD的节点
- PostFilter:没有可用节点时调度
- PreScore:生成一个可共享状态给Score插件使用
- Score:评分
- NormalizeScore:可以修改分数
- Reserve:防止调度器等待绑定成功是发生竞争情况
- Permit:批准,拒绝,等待POD的绑定
- 绑定:
- PreBind:执行POD绑定前的工作
- Bind:进行绑定
- PostBind:收尾清理相关资源
动态资源分配如GPU,跳过
调度器性能调优
通过配置减少调度流程中的操作项目,比如有一个可调度的Node就直接用,不用评估其他的Node
资源装箱,控制节点资源权重进而控制分数
POD优先级和抢占,通过PriorityClass使高优先级POD驱逐低优先POD
节点压力驱逐
根据节点压力驱逐
API发起的驱逐
API 发起的驱逐是一个先调用 Eviction API 创建 Eviction 对象,再由该对象体面地中止 Pod 的过程。
集群管理
- 自己机器上尝试K8S还是构建高可用节点,或者是参与开发
- 使用开箱即用的集群还是管理自己的
- 本地还是云上
- 本地配置时要选取合适的网络模型
- 裸机还是虚拟机运行
- 熟悉运行所需的相关组件
节点关闭
基于优先级的体面关闭,非体面关闭
Node自动缩容和扩容
Node不足时创建新的Node,足够时关闭Node。配合负载自动缩容和扩容使用
集群网络系统
- 高度耦合容器间通信:POD和localhost解决
- POD间通信
- POD与Service通信
- 外部和Service通信
日志架构
K8S支持将标准输入和输出定向到文件,也可以用过kubectl logs查看
Helm
功能
包管理工具,定义一个k8s应用
- 简化部署:通过Helm,你可以使用预定义的配置文件来部署应用,而不需要手动编写和管理大量的Kubernetes YAML文件。
- 版本控制:Helm支持版本控制,你可以轻松地回滚到以前的版本。
- 依赖管理:Helm可以管理应用的依赖关系,确保所有依赖的服务都正确部署。
- 模板化:Helm使用Go模板引擎,可以根据不同的环境和需求生成动态的Kubernetes配置文件。
功能
- chart创建,chart中含有K8S的全部模板信息
- config,包含了配置信息,用于和模板结合后生成最终K8S配置
- release,chart和config结合后的运行实例
- 打包chart为tgz
- 与存储chart的仓库交互
- 安装和卸载chart
- 管理helm安装的chart的生命周期
Chart
软件包
- Chart.yaml:元数据文件,包含名称、版本和描述等信息。
- values.yaml:配置文件,用来控制部署。
- templates/:K8S资源模板文件,可以在配置项中引用values.yaml中的Key。
- deployment.yaml:定义Deployment资源的模板
- service.yaml:定义Service资源的模板
Chart:类似yum的rpm包
Repository:存放Chart的地方,包含一个或多个打包的chart,存在index.yaml的文件,包含完整的包列表,用于检索和验证元数据。helm可以使用repo添加仓库,不过helm不提供上传chart到仓库的功能。
Release:运行的chart的示例,每次install chart都会产生一个Release
helm show values
helm repo add bitnami https://charts.bitnami.com/bitnami
helm search repo bitnami
helm repo update
# 每次执行都会创建一个新的发布版本,一个chart可以被多次安装,独立管理
# helm生成名称
helm install bitnami/mysql --generate-name
# 指定名称
helm install mysql_test bitnami/mysql
# 查看发布的版本
helm list
NAME NAMESPACE REVISION UPDATED STATUS CHART APP VERSION
mysql-1612624192 default 1 2021-02-06 16:09:56.283059 +0100 CET deployed mysql-8.3.0 8.0.23
helm status mysql-1612624192
helm get values mysql-1612624192
helm upgrade -f x.yaml mysql-1612624192 bitnami/mysql
helm history mysql-1612624192
helm rollback mysql-1612624192 1
# 会删除所有相关资源,service,Deployment,pod,版本历史
helm uninstall mysql-1612624192
使用helm管理chart
helm create chart-a
helm package chart-a
x.tar.gz
helm install chart-a ./x.tar.gz
Chart
Chart文件结构
wordpress/
Chart.yaml # 包含了chart信息的YAML文件
LICENSE # 可选: 包含chart许可证的纯文本文件
README.md # 可选: 可读的README文件
values.yaml # chart 默认的配置值
values.schema.json # 可选: 一个使用JSON结构的values.yaml文件
charts/ # 包含chart依赖的其他chart,dependencies中定义的
crds/ # 自定义资源的定义
templates/ # 模板目录, 当和values 结合时,可生成有效的Kubernetes manifest文件
templates/NOTES.txt # 可选: 包含简要使用说明的纯文本文件
# Chart.yaml
apiVersion: chart API 版本 (必需)
name: chart名称 (必需)
version: 语义化2 版本(必需)
kubeVersion: 兼容Kubernetes版本的语义化版本(可选)
description: 一句话对这个项目的描述(可选)
type: chart类型 (可选)
keywords:
- 关于项目的一组关键字(可选)
home: 项目home页面的URL (可选)
sources:
- 项目源码的URL列表(可选)
dependencies: # chart 必要条件列表 (可选) helm dep up foochart将依赖的文件下载到charts目录
- name: chart名称 (nginx)
version: chart版本 ("1.2.3")
repository: (可选)仓库URL ("https://example.com/charts") 或别名 ("@repo-name")
condition: (可选) 解析为布尔值的yaml路径,用于启用/禁用chart (e.g. subchart1.enabled )
tags: # (可选)
- 用于一次启用/禁用 一组chart的tag
import-values: # (可选)
- ImportValue 保存源值到导入父键的映射。每项可以是字符串或者一对子/父列表项
alias: (可选) chart中使用的别名。当你要多次添加相同的chart时会很有用
maintainers: # (可选)
- name: 维护者名字 (每个维护者都需要)
email: 维护者邮箱 (每个维护者可选)
url: 维护者URL (每个维护者可选)
icon: 用做icon的SVG或PNG图片URL (可选)
appVersion: 包含的应用版本(可选)。不需要是语义化,建议使用引号
deprecated: 不被推荐的chart (可选,布尔值)
annotations:
example: 按名称输入的批注列表 (可选).
Chart含dependencies的结构
hlsjsvr/
├── Chart.yaml
├── values.yaml
├── charts/
│ └── hlsvr-base/
│ ├── Chart.yaml
│ ├── values.yaml
│ └── templates/
│ └── ...(实际的K8s资源模板)
# Chart.yaml 定义了dependencies
dependencies:
- name: hlsvr-base
repository: http://helm.bkrepo.oa.com/public-cluster/public-cluster/
version: 1.30.5
# values.yaml
hlsvr-base: # 这里的只会传递给dependencies的hlsvr-base中
replicaCount: 2
image:
tag: "1.2.3"
渲染时如果指定了外部values,会先覆盖hlsjsvr/values.yaml。发现charts中含有依赖下载依赖到charts目录,使用覆盖后的values.yaml再覆盖hlsvr-base的values.yaml,最终的values去渲染templates的内容
模板文件和values文件和values.schema.json
模板文件位于chart的templates目录中,helm渲染chart时,会遍历其中的每个文件
apiVersion: v1
kind: ReplicationController
metadata:
name: deis-database
namespace: deis
labels:
app.kubernetes.io/managed-by: deis
spec:
replicas: 1
selector:
app.kubernetes.io/name: deis-database
template:
metadata:
labels:
app.kubernetes.io/name: deis-database
spec:
serviceAccount: deis-database
containers:
- name: deis-database
image: {{ .Values.imageRegistry }}/postgres:{{ .Values.dockerTag }}
imagePullPolicy: {{ .Values.pullPolicy }}
ports:
- containerPort: 5432
env:
- name: DATABASE_STORAGE
value: {{ default "minio" .Values.storage }}
values文件可以为chart的依赖项目提供基础值。values.schema.json中可以定义Values文件字段的格式和规范
title: "My WordPress Site" # Sent to the WordPress template
# 相当于在每个chart中增加这个基础值。mysql的模板中可以使用{{.Values.global.app}}访问此内容
global:
app: MyWordPress
mysql: # charts中的MySQL可以访问这两项内容,无法访问title和Apache的内容
max_connections: 100 # Sent to MySQL
password: "secret"
apache:
port: 8080 # Passed to Apache
crd
可以声明自定义资源类型,位于crds目录中。无法使用模板,只能是普通的yaml文档。
创建出一个类似pod的资源类型,不过实际作用还是不太清楚。
crontabs/
Chart.yaml
crds/
crontab.yaml
templates/
mycrontab.yaml
# crontab.yaml
kind: CustomResourceDefinition
metadata:
name: crontabs.stable.example.com
spec:
group: stable.example.com
versions:
- name: v1
served: true
storage: true
scope: Namespaced
names:
plural: crontabs
singular: crontab
kind: CronTab
apiVersion: stable.example.com
kind: CronTab
metadata:
name: {{ .Values.name }}
spec:
crd的安装是全局的,会受到以下的限制
- 不会重现安装,helm确认crds目录已经存在的时候(忽略版本),helm不会安装或升级。
- 不会在升级或回滚中安装,只会在安装时创建crd
- 不会被删除,自动删除crd会删除集群中所有命名空间中所有的crd内容,所以helm不会删除crd
chart Hook
Helm提供了hook机制允许chart开发者在发布生命周期的某些点干预。
- 安装时在加载其他chart之前加载配置映射或者秘钥。
- 安装新chart之前执行备份数据库的任务,升级之后执行第二个任务用于存储数据
- 删除发布之前执行一个任务以便在删除服务之前退出滚动。
pre-install 在模板渲染之后,Kubernetes资源创建之前执行
post-install 在所有资源加载到Kubernetes之后执行
pre-delete 在Kubernetes删除之前,执行删除请求
post-delete 在所有的版本资源删除之后执行删除请求
pre-upgrade 在模板渲染之后,资源更新之前执行一个升级请求
post-upgrade 所有资源升级之后执行一个升级请求
pre-rollback 在模板渲染之后,资源回滚之前,执行一个回滚请求
post-rollback 在所有资源被修改之后执行一个回滚请求
test 调用Helm test子命令时执行 ( test文档)
- helm install foo
- helm库调用安装API
- 安装cards目录中的cad
- 验证后渲染foo模板
- 准备执行pre-install
- 按权重对钩子进行排序,资源种类排序,名称正序排列
- 加载最小权重的钩子(可以使用负数)
- 等到钩子READY状态,资源是JOB或者POD类型时,Helm会等到其运行完成。
- 加载资源到K8S中,设置–wait时会等到所有资源ready,且所有资源准备就绪后才会继续
- 执行post-install钩子
- 等到钩子ready状态
- 反馈发布对象到客户端
- 客户端退出
钩子
apiVersion: batch/v1
kind: Job
metadata:
name: "{{ .Release.Name }}"
labels:
app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
app.kubernetes.io/version: {{ .Chart.AppVersion }}
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
annotations: # 在这里定义模板是钩子,否则是一个普通资源
"helm.sh/hook": post-install
"helm.sh/hook-weight": "-5"
"helm.sh/hook-delete-policy": hook-succeeded # before-hook-creation:新钩子启动前删除之前的资源 (默认) hook-succeeded:钩子成功执行之后删除资源 hook-failed:如果钩子执行失败,删除资源
spec:
template:
metadata:
name: "{{ .Release.Name }}"
labels:
app.kubernetes.io/managed-by: {{ .Release.Service | quote }}
app.kubernetes.io/instance: {{ .Release.Name | quote }}
helm.sh/chart: "{{ .Chart.Name }}-{{ .Chart.Version }}"
spec:
restartPolicy: Never
containers:
- name: post-install-job
image: "alpine:3.3"
command: ["/bin/sleep","{{ default "10" .Values.sleepyTime }}"]
chart test
验证chart安装,帮助用户理解chart的功能
helm create demo
.
├── charts
├── Chart.yaml
├── templates
│ ├── deployment.yaml
│ ├── _helpers.tpl
│ ├── hpa.yaml
│ ├── ingress.yaml
│ ├── NOTES.txt
│ ├── serviceaccount.yaml
│ ├── service.yaml
│ └── tests
│ └── test-connection.yaml
└── values.yaml
include的内容在_helper.tpl中定义
{{- define "demo.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
# test-connection.yaml
apiVersion: v1
kind: Pod
metadata:
name: "{{ include "demo.fullname" . }}-test-connection"
labels:
{{- include "demo.labels" . | nindent 4 }}
annotations:
"helm.sh/hook": test
spec:
containers:
- name: wget
image: busybox
command: ['wget']
args: ['{{ include "demo.fullname" . }}:{{ .Values.service.port }}']
restartPolicy: Never
$ sudo helm install demo demo
NAME: demo
LAST DEPLOYED: Mon May 12 16:34:29 2025
NAMESPACE: default
STATUS: deployed
REVISION: 1
NOTES:
$ sudo helm test demo
NAME: demo
LAST DEPLOYED: Mon May 12 16:34:29 2025
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: demo-test-connection
Last Started: Mon May 12 16:34:49 2025
Last Completed: Mon May 12 16:34:55 2025
Phase: Succeeded
NOTES:
部署时会使用values和tpl来渲染test-connection.yaml。values和tpl只是提供一些内容,实际起作用的是yaml文件
库类型chart
可以定义一些tpl模板放到库中,其他chart可以引用这些库中预先定义的内容。
模板库的Chart.yaml中type为library
引用时在yaml文件中使用include,同时在chart.yaml中添加dependencies
helm来源和完整性
helm package –sign –key进行签名,helm verify进行验证
chart仓库,OCI
https://example.com/charts/index.yaml 其中包含chart的信息和下载地址
helm渲染模板和values的合成覆盖流程
- chart包中的values.yaml是默认值
- 使用-f(–values)指定外部的values文件,会覆盖默认值中的同名配置
- –set,–set-file会再覆盖前面的同名配置
- 合成的最终values用来渲染templates目录中的yaml文件
基于角色的访问控制
Helm插件
Helm可以设置一个插件目录,插件放于目录中,运行helm的插件时会自动在其中寻找。
惯例
- chart名称是小写字母和数字,单词之间使用
-分割,如nginx-hello - yaml使用双空格缩进而不是tab
- values
- 变量使用小写字母开头,单词驼峰区分。
helloWorld: true - Helm内置变量使用大写字母开头
- foo: false和foo: “false”不同。规避类型转换,最好统一使用字符串,即打引号。
- 变量使用小写字母开头,单词驼峰区分。
- 模板
常用命令
日志查询
kubectl logs riichipersonalpanel-0 -n riichi -c istio-proxy