K8S: 深入K8S实战进阶篇
1、搭建 Kubernetes 集群
1.1、搭建方案
1.1.1、minikube
minikube 是一个工具, 能让你在本地运行 Kubernetes。 minikube 在你的个人计算机(包括 Windows、macOS 和 Linux PC)上运行一个一体化(all-in-one)或多节点的本地 Kubernetes 集群,以便你来尝试 Kubernetes 或者开展每天的开发工作。
1.1.2、kubeadm
你可以使用 kubeadm 工具来创建和管理 Kubernetes 集群。 该工具能够执行必要的动作并用一种用户友好的方式启动一个可用的、安全的集群。
1.1.2.1、服务器要求
3 台服务器(虚拟机)
- k8s-master:192.168.113.120
- k8s-node1:192.168.113.121
- k8s-node2:192.168.113.122
最低配置:2核、2G内存、20G硬盘
最好能联网,不能联网的话需要有提供对应镜像的私有仓库
1.1.2.2、软件环境
- 操作系统:CentOS 7
- Docker:20+
- k8s:1.23.6
1.1.2.3、安装步骤
安装 kubeadm 展示了如何安装 kubeadm 的过程。一旦安装了 kubeadm, 你就可以使用它来创建一个集群。
初始操作
# 关闭防火墙 systemctl stop firewalld systemctl disable firewalld # 关闭selinux sed -i 's/enforcing/disabled/' /etc/selinux/config # 永久 setenforce 0 # 临时 # 关闭swap swapoff -a # 临时 sed -ri 's/.*swap.*/#&/' /etc/fstab # 永久 # 关闭完swap后,一定要重启一下虚拟机!!! # 根据规划设置主机名 hostnamectl set-hostname # 在master添加hosts cat >> /etc/hosts 192.168.171.128 k8s-master 192.168.171.129 k8s-node1 192.168.171.130 k8s-node2 EOF # 将桥接的IPv4流量传递到iptables的链 cat > /etc/sysctl.d/k8s.conf net.bridge.bridge-nf-call-ip6tables = 1 net.bridge.bridge-nf-call-iptables = 1 EOF sysctl --system # 生效 # 时间同步 yum install ntpdate -y ntpdate time.windows.com
安装基础软件(所有节点)
安装 Docker
添加阿里云 yum 源
cat > /etc/yum.repos.d/kubernetes.repo [kubernetes] name=Kubernetes baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64 enabled=1 gpgcheck=0 repo_gpgcheck=0 gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg EOF
安装 kubeadm、kubelet、kubectl
yum install -y kubelet-1.23.6 kubeadm-1.23.6 kubectl-1.23.6 systemctl enable kubelet # 查看当前kubelet版本 kubelet --version # 查看docker驱动信息 docker info | grep Driver # 配置关闭 Docker 的 cgroups,修改 /etc/docker/daemon.json,加入以下内容, 因为k8s用cgroups会报错 "exec-opts": ["native.cgroupdriver=systemd"] # 重启 docker systemctl daemon-reload systemctl restart docker
部署 Kubernetes Master
–apiserver-advertise-address 指定apiserver地址直接写当前机器IP地址
–image-repository 指定镜像仓库
–service-cid是设置k8s的service的虚拟ip网段
–pod-network-cidr是设置k8s的pod网段 这些ip网段是k8s集群内部通信用的
# 在 Master 节点下执行 kubeadm init \ --apiserver-advertise-address=192.168.171.128 \ --image-repository registry.aliyuncs.com/google_containers \ --kubernetes-version v1.23.6 \ --service-cidr=10.96.0.0/12 \ --pod-network-cidr=10.244.0.0/16
# kubeadm 初始化失败辅助命令 # 查看kubelet状态 systemctl status kubelet # 查看kubelet日志信息 journalctl -xefu kubelet # 重启kubelet systemctl restart kubelet # 查看docker容器是否启动 docker ps -a | grep kube | grep -v pause # 重置kubeadm, 重置以后, 必须要走上面的初始化命令 kubeadm reset
# 安装成功后,复制如下配置并执行 mkdir -p $HOME/.kube sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config sudo chown $(id -u):$(id -g) $HOME/.kube/config kubectl get nodes
加入 Kubernetes Node
分别在 k8s-node1 和 k8s-node2 执行 # 下方命令可以在 k8s master 控制台初始化成功后复制 join 命令 kubeadm join 192.168.171.128:6443 --token ev6pil.4nmxoz7ogh668b8r --discovery-token-ca-cert-hash sha256:22450bc10c43a7ab8639411543618345280ad86cda235122fa6f43beae6647cc # 如果初始化的 token 不小心清空了,可以通过如下命令获取或者重新申请 # 如果 token 已经过期,就重新申请 kubeadm token create # token 没有过期可以通过如下命令获取 kubeadm token list # 获取 --discovery-token-ca-cert-hash 值,得到值后需要在前面拼接上 sha256: openssl x509 -pubkey -in /etc/kubernetes/pki/ca.crt | openssl rsa -pubin -outform der 2>/dev/null | \ openssl dgst -sha256 -hex | sed 's/^.* //'
把node1, node2加入到master中
部署 CNI 网络插件
# 获取组件状态 kubectl get cs
# 查看指定名称空间下面的pods kubectl get pods -n 名称空间
# 在 master 节点上执行 # 下载 calico 配置文件,可能会网络超时, 新地址 curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.0/manifests/calico.yaml -O # 下载 calico 配置文件,可能会网络超时, 原始地址 curl https://docs.projectcalico.org/manifests/calico.yaml -O # 修改 calico.yaml 文件中的 CALICO_IPV4POOL_CIDR 配置,修改为与初始化的 cidr 相同 # 修改 IP_AUTODETECTION_METHOD 下的网卡名称
# 找到它里面所有需要下载的镜像 grep image calico.yaml
# 删除镜像 docker.io/ 前缀,避免下载过慢导致失败 sed -i 's#docker.io/##g' calico.yaml
测试 kubernetes 集群
# 构建整个应用 kubectl apply -f calico.yaml
# 查看安装进度 kubectl describe po pod名称 -n kube-system kubectl describe po calico-node-c924b -n kube-system
出现下面这个情况就是centos7版本不对, 你的内核版本太低没有对应的挂载点, 换系统, 重复上面的操作
# 查看名称空间为kube-system下的pod信息 kubectl get po -n kube-system
# 查看pod的详情 kubectl describe po 名称 -n 名称空间 kubectl describe po calico-kube-controllers-6fbdddcf6b-g2rbz -n kube-system
# 创建部署 kubectl create deployment nginx --image=nginx # 暴露端口 kubectl expose deployment nginx --port=80 --type=NodePort # 查看 pod 以及服务信息 kubectl get pod,svc
1.1.3、二进制安装
利用 k8s 官方 github 仓库下载二进制包安装,安装过程较复杂,但相对较为稳定,推荐生产环境使用。
1.1.4、命令行工具
1.2、命令行工具 kubectl
Kubernetes 提供 kubectl 是使用 Kubernetes API 与 Kubernetes 集群的控制面进行通信的命令行工具。
1.2.1、在任意节点使用 kubectl
将 master 节点中 /etc/kubernetes/admin.conf 拷贝到需要运行的服务器的 /etc/kubernetes 目录中
scp /etc/kubernetes/admin.conf root@k8s-node1:/etc/kubernetes
在对应的服务器上配置环境变量
echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile source ~/.bash_profile
1.2.2、资源操作
创建对象
$ kubectl create -f ./my-manifest.yaml # 创建资源 $ kubectl create -f ./my1.yaml -f ./my2.yaml # 使用多个文件创建资源 $ kubectl create -f ./dir # 使用目录下的所有清单文件来创建资源 $ kubectl create -f https://git.io/vPieo # 使用 url 来创建资源 $ kubectl run nginx --image=nginx # 启动一个 nginx 实例 $ kubectl explain pods,svc # 获取 pod 和 svc 的文档 # 从 stdin 输入中创建多个 YAML 对象 $ cat apiVersion: v1 kind: Pod metadata: name: busybox-sleep spec: containers: - name: busybox image: busybox args: - sleep - "1000000" --- apiVersion: v1 kind: Pod metadata: name: busybox-sleep-less spec: containers: - name: busybox image: busybox args: - sleep - "1000" EOF # 创建包含几个 key 的 Secret $ cat apiVersion: v1 kind: Secret metadata: name: mysecret type: Opaque # 指定了 Secret 对象的类型为 Opaque,即不透明类型,意味着其数据内容不受特定格式的限制 data: password: $(echo "s33msi4" | base64) username: $(echo "jane" | base64)
显示和查找资源
# Get commands with basic output $ kubectl get services # 列出所有 namespace 中的所有 service $ kubectl get pods --all-namespaces # 列出所有 namespace 中的所有 pod $ kubectl get pods -o wide # 列出所有 pod 并显示详细信息 $ kubectl get deployment my-dep # 列出指定 deployment $ kubectl get pods --include-uninitialized # 列出该 namespace 中的所有 pod 包括未初始化的 # 使用详细输出来描述命令 $ kubectl describe nodes my-node $ kubectl describe pods my-pod $ kubectl get services --sort-by=.metadata.name # List Services Sorted by Name # 根据重启次数排序列出 pod $ kubectl get pods --sort-by='.status.containerStatuses[0].restartCount' # 获取所有具有 app=cassandra 的 pod 中的 version 标签 $ kubectl get pods --selector=app=cassandra rc -o \ jsonpath='{.items[*].metadata.labels.version}' # 获取所有节点的 ExternalIP $ kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}' # 列出属于某个 PC 的 Pod 的名字 # “jq”命令用于转换复杂的 jsonpath,参考 https://stedolan.github.io/jq/ $ sel=${$(kubectl get rc my-rc --output=json | jq -j '.spec.selector | to_entries | .[] | "\(.key)=\(.value),"')%?} $ echo $(kubectl get pods --selector=$sel --output=jsonpath={.items..metadata.name}) # 查看哪些节点已就绪 $ JSONPATH='{range .items[*]}{@.metadata.name}:{range @.status.conditions[*]}{@.type}={@.status};{end}{end}' \ && kubectl get nodes -o jsonpath="$JSONPATH" | grep "Ready=True" # 列出当前 Pod 中使用的 Secret $ kubectl get pods -o json | jq '.items[].spec.containers[].env[]?.valueFrom.secretKeyRef.name' | grep -v null | sort | uniq
更新资源
$ kubectl rolling-update frontend-v1 -f frontend-v2.json # 滚动更新 pod frontend-v1 $ kubectl rolling-update frontend-v1 frontend-v2 --image=image:v2 # 更新资源名称并更新镜像 $ kubectl rolling-update frontend --image=image:v2 # 更新 frontend pod 中的镜像 $ kubectl rolling-update frontend-v1 frontend-v2 --rollback # 退出已存在的进行中的滚动更新 $ cat pod.json | kubectl replace -f - # 基于 stdin 输入的 JSON 替换 pod # 强制替换,删除后重新创建资源。会导致服务中断。 $ kubectl replace --force -f ./pod.json # 为 nginx RC 创建服务,启用本地 80 端口连接到容器上的 8000 端口 $ kubectl expose rc nginx --port=80 --target-port=8000 # 更新单容器 pod 的镜像版本(tag)到 v4 $ kubectl get pod mypod -o yaml | sed 's/\(image: myimage\):.*$/\1:v4/' | kubectl replace -f - $ kubectl label pods my-pod new-label=awesome # 添加标签 $ kubectl annotate pods my-pod icon-url=http://goo.gl/XXBTWq # 添加注解 $ kubectl autoscale deployment foo --min=2 --max=10 # 自动扩展 deployment “foo”
修补资源
$ kubectl patch node k8s-node-1 -p '{"spec":{"unschedulable":true}}' # 部分更新节点 # 更新容器镜像; spec.containers[*].name 是必须的,因为这是合并的关键字 $ kubectl patch pod valid-pod -p '{"spec":{"containers":[{"name":"kubernetes-serve-hostname","image":"new image"}]}}' # 使用具有位置数组的 json 补丁更新容器镜像 $ kubectl patch pod valid-pod --type='json' -p='[{"op": "replace", "path": "/spec/containers/0/image", "value":"new image"}]' # 使用具有位置数组的 json 补丁禁用 deployment 的 livenessProbe $ kubectl patch deployment valid-deployment --type json -p='[{"op": "remove", "path": "/spec/template/spec/containers/0/livenessProbe"}]'
编辑资源
$ kubectl edit svc/docker-registry # 编辑名为 docker-registry 的 service $ KUBE_EDITOR="nano" kubectl edit svc/docker-registry # 使用其它编辑器
scale 资源
$ kubectl scale --replicas=3 deploy rs/foo # Scale a replicaset named 'foo' to 3 $ kubectl scale --replicas=3 -f foo.yaml # Scale a resource specified in "foo.yaml" to 3 $ kubectl scale --current-replicas=2 --replicas=3 deployment/mysql # If the deployment named mysql's current size is 2, scale mysql to 3 $ kubectl scale --replicas=5 rc/foo rc/bar rc/baz # Scale multiple replication controllers
删除资源
$ kubectl delete -f ./pod.json # 删除 pod.json 文件中定义的类型和名称的 pod $ kubectl delete pod,service baz foo # 删除名为“baz”的 pod 和名为“foo”的 service $ kubectl delete pods,services -l name=myLabel # 删除具有 name=myLabel 标签的 pod 和 serivce $ kubectl delete pods,services -l name=myLabel --include-uninitialized # 删除具有 name=myLabel 标签的 pod 和 service,包括尚未初始化的 $ kubectl -n my-ns delete po,svc --all # 删除 my-ns namespace 下的所有 pod 和 serivce,包括尚未初始化的
1.2.3、Pod 与集群
与运行的 Pod 交互
$ kubectl logs my-pod # dump 输出 pod 的日志(stdout) $ kubectl logs my-pod -c my-container # dump 输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用) $ kubectl logs -f my-pod # 流式输出 pod 的日志(stdout) $ kubectl logs -f my-pod -c my-container # 流式输出 pod 中容器的日志(stdout,pod 中有多个容器的情况下使用) $ kubectl run -i --tty busybox --image=busybox -- sh # 交互式 shell 的方式运行 pod $ kubectl attach my-pod -i # 连接到运行中的容器 $ kubectl port-forward my-pod 5000:6000 # 转发 pod 中的 6000 端口到本地的 5000 端口 $ kubectl exec my-pod -- ls / # 在已存在的容器中执行命令(只有一个容器的情况下) $ kubectl exec my-pod -c my-container -- ls / # 在已存在的容器中执行命令(pod 中有多个容器的情况下) $ kubectl top pod POD_NAME --containers # 显示指定 pod 和容器的指标度量
与节点和集群交互
$ kubectl cordon my-node # 标记 my-node 不可调度 $ kubectl drain my-node # 清空 my-node 以待维护 $ kubectl uncordon my-node # 标记 my-node 可调度 $ kubectl top node my-node # 显示 my-node 的指标度量 $ kubectl cluster-info # 显示 master 和服务的地址 $ kubectl cluster-info dump # 将当前集群状态输出到 stdout $ kubectl cluster-info dump --output-directory=/path/to/cluster-state # 将当前集群状态输出到 /path/to/cluster-state # 如果该键和影响的污点(taint)已存在,则使用指定的值替换 $ kubectl taint nodes foo dedicated=special-user:NoSchedule
1.2.4、资源类型与别名
- pods
- po
- deployments
- deploy
- services
- svc
- namespace
- ns
- nodes
- no
1.2.5、格式化输出
- 输出 json 格式
- -o json
- 仅打印资源名称
- -o name
- 以纯文本格式输出所有信息
- -o wide
- 输出 yaml 格式
- -o yaml
1.3、API 概述
类型
Alpha
包含 alpha 名称的版本(例如v1alpha1)。 该软件可能包含错误。启用一个功能可能会导致 bug。默认情况下,功能可能会被禁用。 随时可能会丢弃对该功能的支持,恕不另行通知。 API 可能在以后的软件版本中以不兼容的方式更改,恕不另行通知。 该软件建议仅在短期测试集群中使用,因为错误的风险增加和缺乏长期支持。
Beta
包含 beta 名称的版本(例如 v2beta3)。 该软件经过很好的测试。启用功能被认为是安全的。默认情况下功能是开启的。 细节可能会改变,但功能在后续版本不会被删除 对象的模式或语义在随后的 beta 版本或 Stable 版本中可能以不兼容的方式发生变化。如果这种情况发生时,官方会提供迁移操作指南。这可能需要删除、编辑和重新创建API对象。 该版本在后续可能会更改一些不兼容地方,所以建议用于非关键业务,如果你有多个可以独立升级的集群,你也可以放宽此限制。 大家使用过的 Beta 版本后,可以多给社区反馈,如果此版本在后续更新后将不会有太大变化。
Stable
该版本名称命名方式:vX 这里 X 是一个整数。 Stable 版本的功能特性,将出现在后续发布的软件版本中。
访问控制
- 认证
- 授权
废弃 api 说明
https://kubernetes.io/zh-cn/docs/reference/using-api/deprecation-guide/
2、深入 pod
2.1、Pod 配置文件
apiVersion: v1 # api 文档版本
kind: Pod # 资源对象类型,也可以配置为像Deployment、StatefulSet这一类的对象
metadata: # Pod 相关的元数据,用于描述 Pod 的数据
name: nginx-demo # Pod 的名称
labels: # 定义 Pod 的标签
type: app # 自定义 label 标签,名字为 type,值为 app
test: 1.0.0 # 自定义 label 标签,描述 Pod 版本号
namespace: 'default' # 命名空间的配置
spec: # 期望 Pod 按照这里面的描述进行创建
containers: # 对于 Pod 中的容器描述
- name: nginx # 容器的名称
image: nginx:1.7.9 # 指定容器的镜像
imagePullPolicy: IfNotPresent # 镜像拉取策略,指定如果本地有就用本地的,如果没有就拉取远程的
command: # 指定容器启动时执行的命令
- nginx
- -g
- 'daemon off;' # 执行的命令 nginx -g 'daemon off;'
workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录
ports:
- name: http # 端口名称
containerPort: 80 # 描述容器内要暴露什么端口
protocol: TCP # 描述该端口是基于哪种协议通信的
env: # 环境变量
- name: JVM_OPTS # 环境变量名称
value: '-Xms128m -Xmx128m' # 环境变量的值
reousrces:
requests: # 最少需要多少资源
cpu: 100m # 限制 cpu 最少使用 0.1 个核心
memory: 128Mi # 限制内存最少使用 128兆
limits: # 最多可以用多少资源
cpu: 200m # 限制 cpu 最多使用 0.2 个核心
memory: 256Mi # 限制 最多使用 256兆
restartPolicy: OnFailure # 重启策略,只有失败的情况才会重启
创建pod
kubectl create -f nginx-demo.yaml
查看创建详情
kubectl describe po nginx-demo
2.2、探针
容器内应用的监测机制,根据不同的探针来判断容器应用当前的状态
类型
StartupProbe 作用: 保证应用启动完成 (启动探针)
k8s 1.16 版本新增的探针,用于判断应用程序是否已经启动了。 当配置了 startupProbe 后,会先禁用其他探针,直到 startupProbe 成功后,其他探针才会继续。 作用:由于有时候不能准确预估应用一定是多长时间启动成功,因此配置另外两种方式不方便配置初始化时长来检测,而配置了 statupProbe 后,只有在应用启动成功了,才会执行另外两种探针,可以更加方便的结合使用另外两种探针使用。 startupProbe: httpGet: path: /api/startup port: 80
LivenessProbe 作用: 重启应用 (存活探针)
用于探测容器中的应用是否运行,如果探测失败,kubelet 会根据配置的重启策略进行重启,若没有配置,默认就认为容器启动成功,不会执行重启策略。 livenessProbe: failureThreshold: 5 httpGet: path: /health port: 8080 scheme: HTTP initialDelaySeconds: 60 periodSeconds: 10 successThreshold: 1 timeoutSeconds: 5
ReadinessProbe 作用: 开放外部流量 (就绪探针)
用于探测容器内的程序是否健康,它的返回值如果返回 success,那么就认为该容器已经完全启动,并且该容器是可以接收外部流量的。 readinessProbe: failureThreshold: 3 # 错误次数 httpGet: path: /ready port: 8181 scheme: HTTP periodSeconds: 10 # 间隔时间 successThreshold: 1 timeoutSeconds: 1
探测方式
ExecAction
在容器内部执行一个命令,如果返回值为 0,则任务容器时健康的。 livenessProbe: exec: command: - cat - /health
TCPSocketAction
通过 tcp 连接监测容器内端口是否开放,如果开放则证明该容器健康 livenessProbe: tcpSocket: port: 80
HTTPGetAction
生产环境用的较多的方式,发送 HTTP 请求到容器内的应用程序,如果接口返回的状态码在 200~400 之间,则认为容器健康。 livenessProbe: failureThreshold: 5 httpGet: path: /health port: 8080 scheme: HTTP httpHeaders: - name: xxx value: xxx
参数配置
initialDelaySeconds: 60 # 初始化时间 timeoutSeconds: 2 # 超时时间 periodSeconds: 5 # 监测间隔时间 successThreshold: 1 # 检查 1 次成功就表示成功 failureThreshold: 2 # 监测失败 2 次就表示失败
测试探针三种类型及方式
类型StartupProbe
类型StartupProbe, 方式HTTPGetAction (注意我这里写的/api/path是nginx里面没有的接口, 我们有配置了重启策略, 所以这个pod是起不起来的, 会一直重启)
apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型, 也可以配置为像Deployement、StatefulSet这一类的对象 metadata: # pod 相关的元数据, 用于描述pod name: nginx-pod # pod的名称 labels: # 定义pod的标签 type: app # 自定义label标签, 名称为 type, 值为 app test: 1.0.0 # 自定义label标签, 描述 pod 版本号 namespace: 'default' # 命名空间的配置 spec: # 期望pod按照这里面的描述, 进行创建 containers: # 对于pod中容器的描述 - name: nginx # 容器的名称 image: nginx:1.7.9 # 指定容器镜像 imagePullPolicy: IfNotPresent # 如果本地有镜像就使用本地镜像,没有就拉取在线镜像。 startupProbe: # 引用启动探针配置 httpGet: # 探针方式 基于 http 请求探测 path: /api/path # http 请求路径 port: 80 # http 请求端口 timeoutSeconds: 5 # 请求的超时时间 periodSeconds: 5 # 监测间隔时间 successThreshold: 1 # 多少次检测成功才算成功 failureThreshold: 2 # 失败多少次才算真正失败 command: # 指定容器启动时执行的命令 - nginx - -g - 'daemon off;' # 执行的命令就是 nginx -g 'daemon off;' workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录 ports: - name: http # 端口名称 containerPort: 80 # 描述容器内要暴露什么端口 protocol: TCP # 描述改端口是基于那种协议通信的 env: # 环境变量 - name: JVM_OPTS # 环境变量名称 value: '-Xms128m -Xmx128m' # 环境变量的值 resources: requests: # 最少需要多少资源 cpu: 0.1 # 限制 cpu 最少使用 0.1 个核心 memory: 100Mi # 限制内存最少使用 128 兆 limits: # 最多可以用多少资源 cpu: 0.2 # 限制 cpu 最多使用 0.2 个核心 memory: 100Mi # 限制内存最多使用 200 兆 restartPolicy: OnFailure # 重启策略, 只有失败的情况才会重启
错误配置path, 导致pod起不起来, 一直重启
apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型, 也可以配置为像Deployement、StatefulSet这一类的对象 metadata: # pod 相关的元数据, 用于描述pod name: nginx-pod # pod的名称 labels: # 定义pod的标签 type: app # 自定义label标签, 名称为 type, 值为 app test: 1.0.0 # 自定义label标签, 描述 pod 版本号 namespace: 'default' # 命名空间的配置 spec: # 期望pod按照这里面的描述, 进行创建 containers: # 对于pod中容器的描述 - name: nginx # 容器的名称 image: nginx:1.7.9 # 指定容器镜像 imagePullPolicy: IfNotPresent # 如果本地有镜像就使用本地镜像,没有就拉取在线镜像。 startupProbe: # 引用启动探针配置 httpGet: # 探针方式 基于 http 请求探测 path: /index.html # http 请求路径 port: 80 # http 请求端口 timeoutSeconds: 5 # 请求的超时时间 periodSeconds: 5 # 监测间隔时间 successThreshold: 1 # 多少次检测成功才算成功 failureThreshold: 2 # 失败多少次才算真正失败 command: # 指定容器启动时执行的命令 - nginx - -g - 'daemon off;' # 执行的命令就是 nginx -g 'daemon off;' workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录 ports: - name: http # 端口名称 containerPort: 80 # 描述容器内要暴露什么端口 protocol: TCP # 描述改端口是基于那种协议通信的 env: # 环境变量 - name: JVM_OPTS # 环境变量名称 value: '-Xms128m -Xmx128m' # 环境变量的值 resources: requests: # 最少需要多少资源 cpu: 0.1 # 限制 cpu 最少使用 0.1 个核心 memory: 100Mi # 限制内存最少使用 128 兆 limits: # 最多可以用多少资源 cpu: 0.2 # 限制 cpu 最多使用 0.2 个核心 memory: 100Mi # 限制内存最多使用 200 兆 restartPolicy: OnFailure # 重启策略, 只有失败的情况才会重启
启动成功
类型StartupProbe, 方式TCPSocketAction
apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型, 也可以配置为像Deployement、StatefulSet这一类的对象 metadata: # pod 相关的元数据, 用于描述pod name: nginx-pod # pod的名称 labels: # 定义pod的标签 type: app # 自定义label标签, 名称为 type, 值为 app test: 1.0.0 # 自定义label标签, 描述 pod 版本号 namespace: 'default' # 命名空间的配置 spec: # 期望pod按照这里面的描述, 进行创建 containers: # 对于pod中容器的描述 - name: nginx # 容器的名称 image: nginx:1.7.9 # 指定容器镜像 imagePullPolicy: IfNotPresent # 如果本地有镜像就使用本地镜像,没有就拉取在线镜像。 startupProbe: # 引用启动探针配置 tcpSocket: # 探针方式 基于 tcp socket 请求探测 port: 80 # tcp socket 链接端口 timeoutSeconds: 5 # 请求的超时时间 periodSeconds: 5 # 监测间隔时间 successThreshold: 1 # 多少次检测成功才算成功 failureThreshold: 2 # 失败多少次才算真正失败 command: # 指定容器启动时执行的命令 - nginx - -g - 'daemon off;' # 执行的命令就是 nginx -g 'daemon off;' workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录 ports: - name: http # 端口名称 containerPort: 80 # 描述容器内要暴露什么端口 protocol: TCP # 描述改端口是基于那种协议通信的 env: # 环境变量 - name: JVM_OPTS # 环境变量名称 value: '-Xms128m -Xmx128m' # 环境变量的值 resources: requests: # 最少需要多少资源 cpu: 0.1 # 限制 cpu 最少使用 0.1 个核心 memory: 100Mi # 限制内存最少使用 128 兆 limits: # 最多可以用多少资源 cpu: 0.2 # 限制 cpu 最多使用 0.2 个核心 memory: 100Mi # 限制内存最多使用 200 兆 restartPolicy: OnFailure # 重启策略, 只有失败的情况才会重启
类型StartupProbe, 方式ExecAction
apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型, 也可以配置为像Deployement、StatefulSet这一类的对象 metadata: # pod 相关的元数据, 用于描述pod name: nginx-pod # pod的名称 labels: # 定义pod的标签 type: app # 自定义label标签, 名称为 type, 值为 app test: 1.0.0 # 自定义label标签, 描述 pod 版本号 namespace: 'default' # 命名空间的配置 spec: # 期望pod按照这里面的描述, 进行创建 containers: # 对于pod中容器的描述 - name: nginx # 容器的名称 image: nginx:1.7.9 # 指定容器镜像 imagePullPolicy: IfNotPresent # 如果本地有镜像就使用本地镜像,没有就拉取在线镜像。 startupProbe: # 引用启动探针配置 exec: # 执行命令的方式 command: # 命令行 - sh - -c - "echo success > /inited" # 最终执行的命令 sh -c "echo success > /inited" timeoutSeconds: 5 # 请求的超时时间 periodSeconds: 5 # 监测间隔时间 successThreshold: 1 # 多少次检测成功才算成功 failureThreshold: 2 # 失败多少次才算真正失败 command: # 指定容器启动时执行的命令 - nginx - -g - 'daemon off;' # 执行的命令就是 nginx -g 'daemon off;' workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录 ports: - name: http # 端口名称 containerPort: 80 # 描述容器内要暴露什么端口 protocol: TCP # 描述改端口是基于那种协议通信的 env: # 环境变量 - name: JVM_OPTS # 环境变量名称 value: '-Xms128m -Xmx128m' # 环境变量的值 resources: requests: # 最少需要多少资源 cpu: 0.1 # 限制 cpu 最少使用 0.1 个核心 memory: 100Mi # 限制内存最少使用 128 兆 limits: # 最多可以用多少资源 cpu: 0.2 # 限制 cpu 最多使用 0.2 个核心 memory: 100Mi # 限制内存最多使用 200 兆 restartPolicy: OnFailure # 重启策略, 只有失败的情况才会重启
在指定容器执行命令行
kubectl exec -it pod名称 -c 容器名称 -- 命令行 kubectl exec -it nginx-po -c nginx -- cat /inited
类型LivenessProbe
类型LivenessProbe, 方式HTTPGetAction (其他, 同上)
apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型, 也可以配置为像Deployement、StatefulSet这一类的对象 metadata: # pod 相关的元数据, 用于描述pod name: nginx-liveness-pod # pod的名称 labels: # 定义pod的标签 type: app # 自定义label标签, 名称为 type, 值为 app test: 1.0.0 # 自定义label标签, 描述 pod 版本号 namespace: 'default' # 命名空间的配置 spec: # 期望pod按照这里面的描述, 进行创建 containers: # 对于pod中容器的描述 - name: nginx # 容器的名称 image: nginx:1.7.9 # 指定容器镜像 imagePullPolicy: IfNotPresent # 如果本地有镜像就使用本地镜像,没有就拉取在线镜像。 startupProbe: # 引用启动探针配置 exec: # 执行命令的方式 command: # 命令行 - sh - -c - "echo success > /inited" # 最终执行的命令 sh -c "echo success > /inited" timeoutSeconds: 5 # 请求的超时时间 periodSeconds: 5 # 监测间隔时间 successThreshold: 1 # 多少次检测成功才算成功 failureThreshold: 2 # 失败多少次才算真正失败 livenessProbe: # 引用存活探针配置 httpGet: # 探针方式 基于 http 请求探测 path: /started.html # http 请求路径 port: 80 # http 请求端口 timeoutSeconds: 5 # 请求的超时时间 periodSeconds: 10 # 监测间隔时间 successThreshold: 1 # 多少次检测成功才算成功 failureThreshold: 3 # 失败多少次才算真正失败 command: # 指定容器启动时执行的命令 - nginx - -g - 'daemon off;' # 执行的命令就是 nginx -g 'daemon off;' workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录 ports: - name: http # 端口名称 containerPort: 80 # 描述容器内要暴露什么端口 protocol: TCP # 描述改端口是基于那种协议通信的 env: # 环境变量 - name: JVM_OPTS # 环境变量名称 value: '-Xms128m -Xmx128m' # 环境变量的值 resources: requests: # 最少需要多少资源 cpu: 0.1 # 限制 cpu 最少使用 0.1 个核心 memory: 100Mi # 限制内存最少使用 128 兆 limits: # 最多可以用多少资源 cpu: 0.2 # 限制 cpu 最多使用 0.2 个核心 memory: 100Mi # 限制内存最多使用 200 兆 restartPolicy: OnFailure # 重启策略, 只有失败的情况才会重启
刚开始没有started.html, 所以重启了4次, 当我创建好这个文件, 放到对应容器下的时候, 该pod启动成功
类型ReadinessProbe
类型ReadinessProbe, 方式HTTPGetAction (其他, 同上)
apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型, 也可以配置为像Deployement、StatefulSet这一类的对象 metadata: # pod 相关的元数据, 用于描述pod name: nginx-readiness-pod # pod的名称 labels: # 定义pod的标签 type: app # 自定义label标签, 名称为 type, 值为 app test: 1.0.0 # 自定义label标签, 描述 pod 版本号 namespace: 'default' # 命名空间的配置 spec: # 期望pod按照这里面的描述, 进行创建 containers: # 对于pod中容器的描述 - name: nginx # 容器的名称 image: nginx:1.7.9 # 指定容器镜像 imagePullPolicy: IfNotPresent # 如果本地有镜像就使用本地镜像,没有就拉取在线镜像。 startupProbe: # 引用启动探针配置 exec: # 执行命令的方式 command: # 命令行 - sh - -c - "echo success > /inited" # 最终执行的命令 sh -c "echo success > /inited" timeoutSeconds: 5 # 请求的超时时间 periodSeconds: 5 # 监测间隔时间 successThreshold: 1 # 多少次检测成功才算成功 failureThreshold: 2 # 失败多少次才算真正失败 readinessProbe: # 引用就绪探针配置 httpGet: # 探针方式 基于 http 请求探测 path: /started.html # http 请求路径 port: 80 # http 请求端口 timeoutSeconds: 5 # 请求的超时时间 periodSeconds: 10 # 监测间隔时间 successThreshold: 1 # 多少次检测成功才算成功 failureThreshold: 3 # 失败多少次才算真正失败 command: # 指定容器启动时执行的命令 - nginx - -g - 'daemon off;' # 执行的命令就是 nginx -g 'daemon off;' workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录 ports: - name: http # 端口名称 containerPort: 80 # 描述容器内要暴露什么端口 protocol: TCP # 描述改端口是基于那种协议通信的 env: # 环境变量 - name: JVM_OPTS # 环境变量名称 value: '-Xms128m -Xmx128m' # 环境变量的值 resources: requests: # 最少需要多少资源 cpu: 0.1 # 限制 cpu 最少使用 0.1 个核心 memory: 100Mi # 限制内存最少使用 128 兆 limits: # 最多可以用多少资源 cpu: 0.2 # 限制 cpu 最多使用 0.2 个核心 memory: 100Mi # 限制内存最多使用 200 兆 restartPolicy: OnFailure # 重启策略, 只有失败的情况才会重启
当访问不到started.html, 它一直是不健康状态, 所以我们ready一直都是0, 当我们把对应的文件, 拷贝到对应容器下, 探针再次请求是, 找到了, ready就变成1
2.2、生命周期
lifecycle: # 生命周期的配置
postStart: # 容创建完成后执行的动作,不能保证该操作一定在容器的 command 之前执行,一般不使用
exec: # 可以是 exec / httpGet / tcpSocket
command:
- sh
- -c
- "echo '<h1>pre stop</h1>' > /usr/share/nginx/html/prestop.html"
preStop: # 在容器停止前执行的动作
httpGet: # 发送一个 http 请求
path: /
port: 80
exec: # 执行一个命令
command:
- sh
- -c
- "sleep 50; echo 'sleep finished...' >> /usr/share/nginx/html/prestop.html"
整个yaml
apiVersion: v1 # api文档版本
kind: Pod # 资源对象类型, 也可以配置为像Deployement、StatefulSet这一类的对象
metadata: # pod 相关的元数据, 用于描述pod
name: nginx-prestop-pod # pod的名称
labels: # 定义pod的标签
type: app # 自定义label标签, 名称为 type, 值为 app
test: 1.0.0 # 自定义label标签, 描述 pod 版本号
namespace: 'default' # 命名空间的配置
spec: # 期望pod按照这里面的描述, 进行创建
containers: # 对于pod中容器的描述
- name: nginx # 容器的名称
image: nginx:1.7.9 # 指定容器镜像
imagePullPolicy: IfNotPresent # 如果本地有镜像就使用本地镜像,没有就拉取在线镜像。
lifecycle: # 生命周期的配置
postStart: # 生命周期启动阶段做的事情, 不一定在容器的 command 之前执行
exec:
command:
- sh
- -c
- "echo '<h1>pre stop</h1>' > /usr/share/nginx/html/prestop.html"
preStop: # 生命周期销毁阶段做的事情
exec:
command:
- sh
- -c
- "sleep 50; echo 'sleep finished...' >> /usr/share/nginx/html/prestop.html"
command: # 指定容器启动时执行的命令
- nginx
- -g
- 'daemon off;' # 执行的命令就是 nginx -g 'daemon off;'
workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录
ports:
- name: http # 端口名称
containerPort: 80 # 描述容器内要暴露什么端口
protocol: TCP # 描述改端口是基于那种协议通信的
env: # 环境变量
- name: JVM_OPTS # 环境变量名称
value: '-Xms128m -Xmx128m' # 环境变量的值
resources:
requests: # 最少需要多少资源
cpu: 0.1 # 限制 cpu 最少使用 0.1 个核心
memory: 100Mi # 限制内存最少使用 128 兆
limits: # 最多可以用多少资源
cpu: 0.2 # 限制 cpu 最多使用 0.2 个核心
memory: 100Mi # 限制内存最多使用 200 兆
restartPolicy: OnFailure # 重启策略, 只有失败的情况才会重启
Pod 退出流程
删除操作
Endpoint 删除 pod 的 ip 地址
当我们的销毁容器的时间大于删除pod的时间的时候prestop不会被执行, 因为pod不会等容器执行prestop从时间上也能看到, 原因就是默认terminationGracePeriodSeconds: 30。
Pod 变成 Terminating 状态
变为删除中的状态后,会给 pod 一个宽限期,让 pod 去执行一些清理或销毁操作。 配置参数: # 作用与 pod 中的所有容器 terminationGracePeriodSeconds: 40 containers: - xxx
apiVersion: v1 # api文档版本 kind: Pod # 资源对象类型, 也可以配置为像Deployement、StatefulSet这一类的对象 metadata: # pod 相关的元数据, 用于描述pod name: nginx-prestop-pod # pod的名称 labels: # 定义pod的标签 type: app # 自定义label标签, 名称为 type, 值为 app test: 1.0.0 # 自定义label标签, 描述 pod 版本号 namespace: 'default' # 命名空间的配置 spec: # 期望pod按照这里面的描述, 进行创建 terminationGracePeriodSeconds: 40 # 当 pod 被删除时, 给这个 pod 宽限多长时间 containers: # 对于pod中容器的描述 - name: nginx # 容器的名称 image: nginx:1.7.9 # 指定容器镜像 imagePullPolicy: IfNotPresent # 如果本地有镜像就使用本地镜像,没有就拉取在线镜像。 lifecycle: # 生命周期的配置 postStart: # 生命周期启动阶段做的事情, 不一定在容器的 command 之前执行 exec: command: - sh - -c - "echo '<h1>pre stop</h1>' > /usr/share/nginx/html/prestop.html" preStop: # 生命周期销毁阶段做的事情 exec: command: - sh - -c - "sleep 50; echo 'sleep finished...' >> /usr/share/nginx/html/prestop.html" command: # 指定容器启动时执行的命令 - nginx - -g - 'daemon off;' # 执行的命令就是 nginx -g 'daemon off;' workingDir: /usr/share/nginx/html # 定义容器启动后的工作目录 ports: - name: http # 端口名称 containerPort: 80 # 描述容器内要暴露什么端口 protocol: TCP # 描述改端口是基于那种协议通信的 env: # 环境变量 - name: JVM_OPTS # 环境变量名称 value: '-Xms128m -Xmx128m' # 环境变量的值 resources: requests: # 最少需要多少资源 cpu: 0.1 # 限制 cpu 最少使用 0.1 个核心 memory: 100Mi # 限制内存最少使用 128 兆 limits: # 最多可以用多少资源 cpu: 0.2 # 限制 cpu 最多使用 0.2 个核心 memory: 100Mi # 限制内存最多使用 200 兆 restartPolicy: OnFailure # 重启策略, 只有失败的情况才会重启
当我们把terminationGracePeriodSeconds: 40 的时候, 我们销毁容器的时间就是根据这个时间
执行 preStop 的指令
PreStop 的应用
如果应用销毁操作耗时需要比较长,可以在 preStop 按照如下方式进行配置 preStop: exec: command: - sh - -c - 'sleep 20; kill pgrep java' 但是需要注意,由于 k8s 默认给 pod 的停止宽限时间为 30s,如果我们停止操作会超过 30s 时,不要光设置 sleep 50,还要将 terminationGracePeriodSeconds: 30 也更新成更长的时间,否则 k8s 最多只会在这个时间的基础上再宽限几秒,不会真正等待 50s
- 注册中心下线
- 数据清理
- 数据销毁
3、资源调度
3.1、Label 和 Selector
标签(Label)
配置文件
在各类资源的 metadata.labels 中进行配置
kubectl
临时创建 label
kubectl label 资源类型 资源名称 标签key=标签value (看资源所属名称空间加 -n 名称空间) kubectl label po nginx-pod app=hello
修改已经存在的标签
kubectl label 资源类型 节点名称 与存在的标签key=标签value --overwrite kubectl label po nginx-pod app=hello2 --overwrite
查看 label
selector 按照 label 单值查找节点
kubectl get po -A -l app=hello2
查看单个资源的 labels
kubectl get po nginx-pod --show-labels
查看所有资源的 labels (查询指定名称空间lables 加 -n 名称空间)
kubectl get po --show-labels
选择器(Selector)
配置文件
在各对象的配置 spec.selector 或其他可以写 selector 的属性中编写
kubectl
# 匹配单个值,查找 app=hello 的 pod # -A 显示名称空间 -l 条件匹配 kubectl get po -A -l app=hello # 匹配多个值 标签k8s-app 等于 metrics-server 或 kubernetes-dashboard 都显示出来 kubectl get po -A -l 'k8s-app in (metrics-server, kubernetes-dashboard)' # 查找 version!=1 and app=nginx 的 pod 信息 kubectl get po -l version!=1,app=nginx # 不等值 + 语句 kubectl get po -A -l version!=1,'app in (busybox, nginx)'
3.2、Deployment
deployment包含replicaset包含pod, 从下图就能看出来 也 可以通过 kubectl get po,rs,deploy 命令查看对应的name
功能
创建
创建一个 deployment kubectl create deploy nginx-deploy --image=nginx:1.7.9 或执行 kubectl create -f xxx.yaml --record --record 会在 annotation 中记录当前命令创建或升级了资源,后续可以查看做过哪些变动操作。 查看部署信息 kubectl get deployments 查看 rs kubectl get rs 查看 pod 以及展示标签,可以看到是关联的那个 rs kubectl get pods --show-labels
滚动更新
先把副本数改为3才能体现滚动更新的过程, 我这里随手随便也加了个自定义标签
原来rs的值
只有修改了 deployment 配置文件中的 template 中的属性后,才会触发更新操作 修改 nginx 版本号 kubectl set image deployment/nginx-deploy nginx=nginx:1.9.1 --record 或者通过 kubectl edit deployment/nginx-deploy 进行修改
查看滚动更新的过程 kubectl rollout status deploy <deploy name> <-n <namespace>>
查看部署描述,最后展示发生的事件列表也可以看到滚动更新过程 kubectl describe deploy <deploy name> <-n <namespace>>
通过 kubectl get deploy <deploy name> <-n <namespace>> 获取部署信息,UP-TO-DATE 表示已经有多少副本达到了配置中要求的数目
滚动跟更新后rs的值
通过 kubectl get rs 可以看到增加了一个新的 rs
通过 kubectl get pods 可以看到所有 pod 关联的 rs 变成了新的
多个滚动更新并行
假设当前有 5 个 nginx:1.7.9 版本,你想将版本更新为 1.9.1,当更新成功第三个以后,你马上又将期望更新的版本改为 1.9.2,那么此时会立马删除之前的三个,并且立马开启更新 1.9.2 的任务
回滚
有时候你可能想回退一个Deployment,例如,当Deployment不稳定时,比如一直crash looping。 默认情况下,kubernetes会在系统中保存前两次的Deployment的rollout历史记录,以便你可以随时会退(你可以修改revision history limit来更改保存的revision数)。 案例: 更新 deployment 时参数不小心写错,如 nginx:1.9.1 写成了 nginx:1.91 kubectl set image deployment/nginx-deploy nginx=nginx:1.91 --record 监控滚动升级状态,由于镜像名称错误,下载镜像失败,因此更新过程会卡住 kubectl rollout status deployments nginx-deploy 结束监听后,获取 rs 信息,我们可以看到新增的 rs 副本数是 2 个 kubectl get rs
通过 kubectl get pods 获取 pods 信息,我们可以看到关联到新的 rs 的 pod,状态处于 ImagePullBackOff 状态
为了修复这个问题,我们需要找到需要回退的 revision 进行回退 通过 kubectl rollout history deployment/nginx-deploy 可以获取 revison 的列表
通过 kubectl rollout history deployment/nginx-deploy --revision=2 可以查看详细信息
确认要回退的版本后,可以通过 kubectl rollout undo deployment/nginx-deploy 可以回退到上一个版本 也可以回退到指定的 revision kubectl rollout undo deployment/nginx-deploy --to-revision=2 再次通过 kubectl get deployment 和 kubectl describe deployment 可以看到,我们的版本已经回退到对应的 revison 上了
可以通过设置 .spec.revisonHistoryLimit 来指定 deployment 保留多少 revison,如果设置为 0,则不允许 deployment 回退了。
扩容缩容
通过 kube scale 命令可以进行自动扩容/缩容,以及通过 kube edit 编辑 replcas 也可以实现扩容/缩容 kubectl scale --replicas=扩容/缩容数量 deploy deploy名称 kubectl scale --replicas=3 deploy nginx-deploy 扩容与缩容只是直接创建副本数,没有更新 pod template 因此不会创建新的 rs
暂停与恢复
由于每次对 pod template 中的信息发生修改后,都会触发更新 deployment 操作,那么此时如果频繁修改信息,就会产生多次更新,而实际上只需要执行最后一次更新即可,当出现此类情况时我们就可以暂停 deployment 的 rollout 通过 kubectl rollout pause deploy <name> 就可以实现暂停,直到你下次恢复后才会继续进行滚动更新 尝试对容器进行修改,然后查看是否发生更新操作了 kubectl set image deploy <name> nginx=nginx:1.17.9 --record kubectl get po
通过以上操作可以看到实际并没有发生修改,此时我们再次进行修改一些属性,如限制 nginx 容器的最大cpu为 0.2 核,最大内存为 128M,最小内存为 64M,最小 cpu 为 0.1 核 kubectl set resources deploy nginx-deploy --limits=cpu=200m,memory=128Mi --requests=cpu=100m,memory=64Mi --record 通过格式化输出 kubectl get deploy -o yaml,可以看到配置确实发生了修改,再通过 kubectl get po 可以看到 pod 没有被更新
那么此时我们再恢复 rollout,通过命令 kubectl rollout resume deploy <name> 恢复后,我们再次查看 rs 和 po 信息,我们可以看到就开始进行滚动更新操作了 kubectl get rs kubectl get po
配置文件
apiVersion: apps/v1 # deployment api 版本 kind: Deployment # 资源类型为 deployment metadata: # 元信息 labels: # 标签 app: nginx-deploy # 具体的 key: value 配置形式 name: nginx-deploy # deployment 的名字 namespace: default # 所在的命名空间 spec: replicas: 1 # 期望副本数 revisionHistoryLimit: 10 # 进行滚动更新后,保留的历史版本数 selector: # 选择器,用于找到匹配的 RS matchLabels: # 按照标签匹配 app: nginx-deploy # 匹配的标签key/value strategy: # 更新策略 rollingUpdate: # 滚动更新配置 maxSurge: 25% # 进行滚动更新时,更新的个数最多可以超过期望副本数的个数/比例 maxUnavailable: 25% # 进行滚动更新时,最大不可用比例更新比例,表示在所有副本数中,最多可以有多少个不更新成功 type: RollingUpdate # 更新类型,采用滚动更新 template: # pod 模板 metadata: # pod 的元信息 labels: # pod 的标签 app: nginx-deploy spec: # pod 期望信息 containers: # pod 的容器 - image: nginx:1.7.9 # 镜像 imagePullPolicy: IfNotPresent # 拉取策略 name: nginx # 容器名称 restartPolicy: Always # 重启策略 terminationGracePeriodSeconds: 30 # 删除操作最多宽限多长时间
3.3、StatefulSet
功能
创建
kubectl create -f web.yaml # 查看 service 和 statefulset => sts kubectl get service nginx kubectl get statefulset web # 查看 PVC 信息 kubectl get pvc # 查看创建的 pod,这些 pod 是有序的 kubectl get pods -l app=nginx # 查看这些 pod 的 dns # 运行一个 pod,基础镜像为 busybox 工具包,利用里面的 nslookup 可以看到 dns 信息 kubectl run -it --image busybox:1.28.4 dns-test # 永久的 kubectl run -it --image busybox:1.28.4 dns-test --restart=Never --rm /bin/sh # 退出以后自动删除 nslookup web-0.nginx
扩容缩容
# 扩容 kubectl scale statefulset web --replicas=5 # 缩容 kubectl scale statefulset web --replicas=2 或者 # 扩容 kubectl patch statefulset web -p '{"spec":{"replicas":5}}' # 缩容 kubectl patch statefulset web -p '{"spec":{"replicas":2}}'
镜像更新
# 镜像更新(目前还不支持直接更新 image,需要 patch 来间接实现) kubectl patch sts 名称 --type='什么类型的操作' -p='[{"op": "什么操作", "path": "要替换标签的路径(这里写了个0代表是第0个容器)", "value":"对应标签的值"}]' # /spec/template/spec/containers/0/image 意思是spec下的template下的spec下的containers下的第0个容器的image kubectl patch sts web --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/image", "value":"nginx:1.9.1"}]' 或者 kubectl edit sts 名称 kubectl edit sts web 通过 kubectl rollout history sts web 可以获取 revison 的列表 通过 kubectl rollout history sts web --revision=2 可以查看指定版本详细信息
RollingUpdate (滚动更新)
StatefulSet 也可以采用滚动更新策略,同样是修改 pod template 属性后会触发更新,但是由于 pod 是有序的,在 StatefulSet 中更新时是基于 pod 的顺序倒序更新的
灰度发布 (金丝雀发布)
利用滚动更新中的 partition 属性,可以实现简易的灰度发布的效果 **例如我们有 5 个 pod,如果当前 partition 设置为 3,那么此时滚动更新时,只会更新那些 序号 >= 3 的 pod **利用该机制,我们可以通过控制 partition 的值,来决定只更新其中一部分 pod,确认没有问题后再主键增大更新的 pod 数量,最终实现全部 pod 更新
这里设置的是0标识全部更新
我们先把副本数搞5个出来
然后通过edit修改镜像和partition属性
查看3, 4
查看0, 1, 2
当这个属性partition在设置多少的时候就更新多少, 设置为0表示全部更新
OnDelete
只有在 pod 被删除时会进行更新操作
把更新策略改为OnDelete
我们在次edit编辑把镜像改为1.9.1
我们查看web-4它并没有更新为1.9.1
当我们删除web-4以后, 发现它会自动帮我们按照修改后的yaml创建一个1.9.1的pod
但是我们查看0, 1, 2, 3, 还是没有更新, 想要更新, 全部都直接删除, 它会自动帮我们按照新的yaml创建新的pod
删除
# 删除 StatefulSet 和 Headless Service # 级联删除:删除 statefulset 时会同时删除 pods kubectl delete statefulset web # 删除 service kubectl delete service nginx
# 非级联删除:删除 statefulset 时不会删除 pods,删除 sts 后,pods 就没人管了,此时再删除 pod 不会重建的 kubectl delete sts web --cascade=false # 删除对应的pod kubectl delete po web-0 web-1 # 删除 service kubectl delete service nginx
删除 pvc
# StatefulSet删除后PVC还会保留着,数据不再使用的话也需要删除 kubectl delete pvc www-web-0 www-web-1
配置文件web.xml
--- apiVersion: v1 kind: Service metadata: name: nginx labels: app: nginx spec: ports: - port: 80 name: web clusterIP: None selector: app: nginx --- apiVersion: apps/v1 # StatefulSet api 版本 kind: StatefulSet # 资源类型为 StatefulSet metadata: name: web # StatefulSet 对象的名字 spec: serviceName: "nginx" # 使用那个 service 来管理dns replicas: 2 # 期望副本数 selector: matchLabels: # 匹配labels app: nginx template: # pod 模板 metadata: # pod 的元信息 labels: # pod 的标签 app: nginx spec: # pod 期望信息 containers: # pod 的容器 - name: nginx # 容器名称 image: nginx:1.7.9 # 镜像 ports: # 容器内部要暴露的端口 - containerPort: 80 # 具体暴露的端口号 name: web # 该端口配置的名字 # volumeMounts: # 加载数据卷 # - name: www # 指定加载那个数据卷 # mountPath: /usr/share/nginx/html # 加载到容器中的那个目录 # volumeClaimTemplates: # 数据卷模版 # - metadata: # 数据卷描述 # name: www # 数据卷的名称 # annotations: # 数据卷的注解 # volume.alpha.kubernetes.io/storage-class: anything # spec: # 数据卷的规约 # accessModes: [ "ReadWriteOnce" ] # 访问模式 # resources: # requests: # 最少需要多少资源 # storage: 1Gi # 需要 1G 的存储资源
3.4、DaemonSet
配置文件
apiVersion: apps/v1 # # daemonset api 版本 kind: DaemonSet # 创建 DaemonSet 资源 metadata: name: fluentd # 名字 spec: selector: # 选择器,用于找到匹配的 pod matchLabels: # 按照标签匹配 app: logging # # 匹配的标签key/value template: metadata: # pod 的元信息 labels: # pod 的标签 app: logging id: fluentd name: fluentd # pod 名称 spec: containers: - name: fluentd-es # 容器名称 image: agilestacks/fluentd-elasticsearch:v1.3.0 # 镜像 env: # 环境变量配置 - name: FLUENTD_ARGS # 环境变量的 key value: -qq # 环境变量的 value volumeMounts: # 加载数据卷, 避免数据丢失 - name: containers # 数据卷的名字 mountPath: /var/lib/docker/containers # 将数据卷挂载到容器内的那个目录 - name: varlog mountPath: /varlog volumes: # 定义数据卷 - hostPath: # 数据卷类型, 主机路径模式, 也就是与 node 共享目录 path: /var/lib/docker/containers # node 中的共享目录 name: containers # 定义的数据卷的名称 - hostPath: path: /var/log name: varlog
当我不指定Node节点时创建daemonset, 它会在每一个节点中创建
指定 Node 节点
DaemonSet 会忽略 Node 的 unschedulable 状态,有两种方式来指定 Pod 只运行在指定的 Node 节点上: ● nodeSelector:只调度到匹配指定 label 的 Node 上 ● nodeAffinity:功能更丰富的 Node 选择器,比如支持集合操作 ● podAffinity:调度到满足条件的 Pod 所在的 Node 上
nodeSelector
先为 Node 打上标签 kubectl label nodes k8s-node1 type=microservices
然后再 daemonset 配置中设置 nodeSelector spec: template: spec: nodeSelector: type: microservices
通过edit编辑对应的daemonset, 新增上面的配置保存
发现只有k8s-node1有了, k8s-node2节点没了, 只要我们给k8s-node2也加上标签type: microservices, 它会自动帮我们在k8s-node2节点中部署daemonset
nodeAffinity
nodeAffinity 目前支持两种:requiredDuringSchedulingIgnoredDuringExecution 和 preferredDuringSchedulingIgnoredDuringExecution,分别代表必须满足条件和优选条件。 比如下面的例子代表调度到包含标签 wolfcode.cn/framework-name 并且值为 spring 或 springboot 的 Node 上,并且优选还带有标签 another-node-label-key=another-node-label-value 的Node。 apiVersion: v1 kind: Pod metadata: name: with-node-affinity spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: wolfcode.cn/framework-name operator: In values: - spring - springboot preferredDuringSchedulingIgnoredDuringExecution: - weight: 1 preference: matchExpressions: - key: another-node-label-key operator: In values: - another-node-label-value containers: - name: with-node-affinity image: pauseyyf/pause
podAffinity
podAffinity 基于 Pod 的标签来选择 Node,仅调度到满足条件Pod 所在的 Node 上,支持 podAffinity 和 podAntiAffinity。这个功能比较绕,以下面的例子为例: 如果一个 “Node 所在空间中包含至少一个带有 auth=oauth2 标签且运行中的 Pod”,那么可以调度到该 Node 不调度到 “包含至少一个带有 auth=jwt 标签且运行中 Pod”的 Node 上 apiVersion: v1 kind: Pod metadata: name: with-pod-affinity spec: affinity: podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: auth operator: In values: - oauth2 topologyKey: failure-domain.beta.kubernetes.io/zone podAntiAffinity: preferredDuringSchedulingIgnoredDuringExecution: - weight: 100 podAffinityTerm: labelSelector: matchExpressions: - key: auth operator: In values: - jwt topologyKey: kubernetes.io/hostname containers: - name: with-pod-affinity image: pauseyyf/pause
滚动更新
不建议使用 RollingUpdate,建议使用 OnDelete 模式,这样避免频繁更新 ds
3.5、HPA 自动扩/缩容
通过观察 pod 的 cpu、内存使用率或自定义 metrics 指标进行自动的扩容或缩容 pod 的数量。
通常用于 Deployment,不适用于无法扩/缩容的对象,如 DaemonSet
控制管理器每隔30s(可以通过–horizontal-pod-autoscaler-sync-period修改)查询metrics的资源使用情况
开启指标服务
# 下载 metrics-server 组件配置文件 wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml -O metrics-server-components.yaml # 修改镜像地址为国内的地址 sed -i 's/registry.k8s.io\/metrics-server/registry.cn-hangzhou.aliyuncs.com\/google_containers/g' metrics-server-components.yaml # 修改容器的 tls 配置,不验证 tls,在 containers 的 args 参数中增加 --kubelet-insecure-tls 参数
# 安装组件 kubectl apply -f metrics-server-components.yaml # 查看 pod 状态 kubectl get pods --all-namespaces | grep metrics
cpu、内存指标监控
实现 cpu 或内存的监控,首先有个前提条件是该对象必须配置了 resources.requests.cpu 或 resources.requests.memory 才可以,可以配置当 cpu/memory 达到上述配置的百分比后进行扩容或缩容 创建一个 HPA: 1. 先准备一个好一个有做资源限制的 deployment 2. 执行命令 kubectl autoscale deploy nginx-deploy --cpu-percent=20 --min=2 --max=5 # 自动扩容 deploy类型的名字为nginx-deploy, 只要它cpu超过20%自动自动伸缩, 扩容到最小为2, 最大为5个副本 3. 通过 kubectl get hpa 可以获取 HPA 信息
由于现在副本有两个, 我们请求只能请求一个所以这里写个service配置
apiVersion: v1 kind: Service metadata: name: nginx-svc labels: app: nginx spec: selector: app: nginx-deploy # 匹配 pod 标签带 app=nginx-deploy 的pod ports: - port: 80 targetPort: 80 name: web type: NodePort
找到对应的ip端口直接死循环请求
测试:找到对应服务的 service,编写循环测试脚本提升内存与 cpu 负载 while true; do wget -q -O- http:// <ip:port> > /dev/null ; done 可以通过多台机器执行上述命令,增加负载,当超过负载后可以查看 pods 的扩容情况 kubectl get pods 查看 pods 资源使用情况 kubectl top pods
扩容测试完成后,再关闭循环执行的指令,让 cpu 占用率降下来,然后过 5 分钟后查看自动缩容情况
当我把2个请求都关闭后, 有会变成2个副本
自定义 metrics
控制管理器开启–horizontal-pod-autoscaler-use-rest-clients 控制管理器的–apiserver指向API Server Aggregator 在API Server Aggregator中注册自定义的metrics API
4、服务发布
4.1、Service
负责东西流量(同层级/内部服务网络通信)的通信
Service 的定义
apiVersion: v1 kind: Service # 资源类型为service metadata: # Service 描述 name: nginx-svc # service 名字 labels: # 定义 service 自己本身的标签 app: nginx spec: selector: # 匹配那些 pod 会被该 service 代理 app: nginx-deploy # 所有匹配到这些标签的 pod 都可以通过该 service 进行访问 ports: # 端口映射 - port: 80 # service 自己的端口, 在使用内网 ip 访问时使用 targetPort: 80 # 目标 pod 的端口 name: web # 为端口起个名字 type: NodePort # 随机启动一个端口 (30000-32767), 映射到 ports 中的端口, 该端口是直接绑定在 node 上的, 且集群中的每一个 node 都会绑定这个端口 # 它也可以用于将服务暴露给外部访问, 但是这种方式实际生产环境不推荐, 效率较低, 而且 service 是四层负载
命令操作
# 创建 service kubectl create -f nginx-svc.yaml # 查看 service 信息,通过 service 的 cluster ip 进行访问 kubectl get svc # 查看 pod 信息,通过 pod 的 ip 进行访问 kubectl get po -owide # 创建其他 pod 通过 service name 进行访问(推荐) kubectl exec -it dns-test -- sh curl http://nginx-svc # 默认在当前 namespace 中访问,如果需要跨 namespace 访问 pod,则在 service name 后面加上 . 即可 curl http://nginx-svc.default
Endpoint
代理 k8s 外部服务
实现方式: 1. 编写 service 配置文件时,不指定 selector 属性就不会自动帮我们创建 endpoint 2. 自己创建 endpoint
endpoint 配置: apiVersion: v1 kind: Endpoints # 资源类型为endpoints metadata: labels: app: nginx # 与 service 一致 name: nginx-svc-external # 与 service 一致 namespace: default # 与 service 一致 subsets: - addresses: - ip: 120.78.159.117 # 目标 ip 地址 ports: # 与 service 一致 - name: web # 与 service 一致 port: 80 protocol: TCP
- 各环境访问名称统一
- 访问 k8s 集群外的其他服务
- 项目迁移
反向代理外部域名
apiVersion: v1 kind: Service # 资源类型为service metadata: # Service 描述 labels: # 定义 service 自己本身的标签 app: wolfcode-external-domain name: wolfcode-external-domain # service 名字 spec: type: ExternalName # 定义目标的 CNAME 别名,可以配置为域名 externalName: www.wolfcode.cn # 目标域名
常用类型
ClusterIP
- 只能在集群内部使用,不配置类型的话默认就是 ClusterIP
ExternalName
- 返回定义的 CNAME 别名,可以配置为域名
NodePort
会在所有安装了 kube-proxy 的节点都绑定一个端口,此端口可以代理至对应的 Pod,集群外部可以使用任意节点 ip + NodePort 的端口号访问到集群中对应 Pod 中的服务。
当类型设置为 NodePort 后,可以在 ports 配置中增加 nodePort 配置指定端口,需要在下方的端口范围内,如果不指定会随机指定端口
端口范围:30000~32767
端口范围配置在 /usr/lib/systemd/system/kube-apiserver.service 文件中
LoadBalancer
- 使用云服务商(阿里云、腾讯云等)提供的负载均衡器服务
4.2、Ingress
Ingress 大家可以理解为也是一种 LB 的抽象,它的实现也是支持 nginx、haproxy 等负载均衡服务的
安装 ingress-nginx
https://kubernetes.github.io/ingress-nginx/deploy/#using-helm
添加 helm 仓库
# 添加仓库 helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx # 查看仓库列表 helm repo list # 搜索 ingress-nginx helm search repo ingress-nginx
下载包
# 下载安装包 helm pull ingress-nginx/ingress-nginx
配置参数
# 将下载好的安装包解压 tar xf ingress-nginx-xxx.tgz # 解压后,进入解压完成的目录 cd ingress-nginx # 修改 values.yaml # 镜像地址:修改为国内镜像 registry: registry.cn-hangzhou.aliyuncs.com image: google_containers/nginx-ingress-controller image: google_containers/kube-webhook-certgen tag: v1.3.0 hostNetwork: true dnsPolicy: ClusterFirstWithHostNet 修改部署配置的 kind: DaemonSet nodeSelector: ingress: "true" # 增加选择器,如果 node 上有 ingress=true 就部署 将 admissionWebhooks.enabled 修改为 false 将 service 中的 type 由 LoadBalancer 修改为 ClusterIP,如果服务器是云平台才用 LoadBalancer
创建 Namespace
# 为 ingress 专门创建一个 namespace kubectl create ns ingress-nginx
安装 ingress
# 为需要部署 ingress 的节点上加标签 (注意, 因为污点原因, 它不能装在master上) kubectl label node k8s-node1 ingress=true # 安装 ingress-nginx 这个点代表从当前目录下找对应的values.yaml helm install ingress-nginx -n ingress-nginx .
基本使用
创建一个 ingress (注意:记得域名在hosts文件配置, 因为这个域名是我们随便写的)
apiVersion: networking.k8s.io/v1 kind: Ingress # 资源类型为 Ingress metadata: name: xiaoge-nginx-ingress annotations: kubernetes.io/ingress.class: "nginx" # 固定写法 nginx.ingress.kubernetes.io/rewrite-target: / # 把匹配到的路径转发的时候都是转发到对应service中 / 开始 spec: rules: # ingress 规则配置,可以配置多个 - host: k8s.xiaoge.cn # 域名配置,可以使用通配符 * http: paths: # 相当于 nginx 的 location 配置,可以配置多个 - pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配 backend: service: name: nginx-svc # 代理到哪个 service port: number: 80 # service 的端口 path: /api # 等价于 nginx 中的 location 的路径前缀匹配
多域名配置 (注意: Prefix和Exec同时存在, 优先匹配Exact)
apiVersion: networking.k8s.io/v1 kind: Ingress # 资源类型为 Ingress metadata: name: xiaoge-nginx-ingress annotations: kubernetes.io/ingress.class: "nginx" nginx.ingress.kubernetes.io/rewrite-target: / # 把匹配到的理解转发的时候都是转发到对应service中 / 开始 spec: rules: # ingress 规则配置,可以配置多个 - host: k8s.xiaoge.cn # 域名配置,可以使用通配符 * http: paths: # 相当于 nginx 的 location 配置,可以配置多个 - pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配 backend: service: name: nginx-svc # 代理到哪个 service port: number: 80 # service 的端口 path: /api # 等价于 nginx 中的 location 的路径前缀匹配 - pathType: Exact # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配>,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配 backend: service: name: nginx-svc # 代理到哪个 service port: number: 80 # service 的端口 path: / - host: api.xiaoge.cn # 域名配置,可以使用通配符 * http: paths: # 相当于 nginx 的 location 配置,可以配置多个 - pathType: Prefix # 路径类型,按照路径类型进行匹配 ImplementationSpecific 需要指定 IngressClass,具体匹配规则以 IngressClass 中的规则为准。Exact:精确匹配>,URL需要与path完全匹配上,且区分大小写的。Prefix:以 / 作为分隔符来进行前缀匹配 backend: service: name: nginx-svc # 代理到哪个 service port: number: 80 # service 的端口 path: /
5、配置与存储
5.1、配置管理
5.1.1、ConfigMap
一般用于去存储 Pod 中应用所需的一些配置信息,或者环境变量,将配置于 Pod 分开,避免应为修改配置导致还需要重新构建 镜像与容器。
5.1.1.1、创建
使用 kubectl create configmap -h 查看示例,构建 configmap 对象
# 根据目录创建ConfigMap
kubectl create configmap xiaoge-test-config --from-file=/opt/k8s/config/test/
# 根据指定文件创建configMap
kubectl create configmap xiaoge-test-yml-config --from-file=/opt/k8s/config/application.yml
# 给data去别名创建configMap
kubectl create configmap xiaoge-test-alias-config --from-file=app.yml=/opt/k8s/config/application.yml
# 自己手写key/value
kubectl create configmap my-config --from-literal=username=xiaoge --from-literal=password=123456
5.1.1.2、使用 ConfigMap
使用ConfigMap引用key的形式
apiVersion: v1 kind: Pod metadata: name: test-env-po spec: containers: - name: env-test image: alpine command: ["/bin/sh", "-c", "env;sleep 3600"] imagePullPolicy: IfNotPresent env: - name: JAVA_VM_OPTS # 环境变量的 key valueFrom: # 环境变量的值从哪里来 configMapKeyRef: # 从 ConfigMap 中来 name: my-test-env-config # ConfigMap 的名字 key: JAVA_OPTS_TEST # 表示从 name 的 ConfigMap 中获取名字为 key 的 value , 将其赋值给本地环境变量 JAVA_VM_OPTS - name: APP valueFrom: configMapKeyRef: name: my-test-env-config key: APP_NAME restartPolicy: Never
使用ConfgMap file的形式
apiVersion: v1 kind: Pod metadata: name: test-file-po spec: containers: - name: file-test image: alpine command: ["/bin/sh", "-c", "env;sleep 3600"] imagePullPolicy: IfNotPresent env: - name: JAVA_VM_OPTS # 环境变量的 key valueFrom: # 环境变量的值从哪里来 configMapKeyRef: # 从 ConfigMap 中来 name: my-test-env-config # ConfigMap 的名字 key: JAVA_OPTS_TEST # 表示从 name 的 ConfigMap 中获取名字为 key 的 value , 将其赋值给本地环境变量 JAVA_VM_OPTS - name: APP valueFrom: configMapKeyRef: name: my-test-env-config key: APP_NAME volumeMounts: # 加载数据卷 - name: db-config # 表示加载 volumes 属性中那个数据卷 mountPath: "/usr/local/mysql/conf" # 想要将数据卷中的文件加载到那个目录下 readOnly: true # 是否只读 volumes: # 数据卷挂载 configmap secret - name: db-config # 数据卷的名字, 随意设置 configMap: # 数据卷类型为 ConfigMap name: xiaoge-test-config # configMap 的名字, 必须跟想要加载的 configmap 相同 items: # 对 configmap 中的 key 进行映射, 如果不指定, 默认会将 configmap 中所有 key 全部转换为一个个同名的文件 (不写items, 默认会将所有的key转换成一个个同名文件, 指定了, 就会只转换指定的key) - key: "db.properties" # configMap 中的 key path: "db.properties" # 将该 key 的值转换为文件 restartPolicy: Never
5.1.2、加密数据配置 Secret
与 ConfigMap 类似,用于存储配置信息,但是主要用于存储敏感信息、需要加密的信息,Secret 可以提供数据加密、解密功能。
在创建 Secret 时,要注意如果要加密的字符中,包含了有特殊字符,需要使用转义符转移,例如 $ 转移后为 $,也可以对特殊字符使用单引号描述,这样就不需要转移例如 1$289-! 转换为 '1$289-!'**
# secret 名称为 harbor-secret
kubectl create secret docker-registry harbor-secret --docker-username=admin --docker-password=admin --docker-email=xiaoge@tengxun.cn
上面只是告诉你怎么创建 secret, 后面k8s拉取镜像时需要用到
我们手动登录把本地的nginx镜像打包, 上传到我们自己的harbor远程仓库, (docker怎么安装harbor自己百度)
在harbor上创建一个opensource的项目, 现在这里面是一个镜像都没有的
先给当前nginx镜像打一个自己的标签
先登入到对应的harbor的仓库服务器
报错显示https, 我们就要让它用http
# 打开 daemon.json
vi /etc/docker/daemon.json
systemctl daemon-reload
systemctl restart docker
# 然后再检harbor相关的容器是否都启动成功, 然后再docker login
docker login -uadmin 192.168.171.129:8089
把我们复制出来的tag镜像上传到远程仓库harbor上
上面的只是演示, 现在开始创建真正的secret拉取harbor的镜像
创建secret添加服务地址
正常拉取私有仓库镜像是需要认证的 (harbor-secret 就是我们刚刚创建的docker仓库的secret)
# 只需要配置这两行, 再去拉取私有仓库镜像就不需要手动认证了
imagePullSecrets: # 配置仓库 docker registry 的 secret
- name: harbor-secret # 指定我们创建的secret
apiVersion: v1
kind: Pod
metadata:
name: private-image-pull-pod
spec:
imagePullSecrets: # 配置仓库 docker registry 的 secret
- name: harbor-secret # 指定我们创建的secret
containers:
- name: nginx
image: 192.168.171.129:8089/opensource/nginx:1.9.1
command: ["/bin/sh", "-c", "env;sleep 3600"]
imagePullPolicy: IfNotPresent
env:
- name: JAVA_VM_OPTS # 环境变量的 key
valueFrom: # 环境变量的值从哪里来
configMapKeyRef: # 从 ConfigMap 中来
name: my-test-env-config # ConfigMap 的名字
key: JAVA_OPTS_TEST # 表示从 name 的 ConfigMap 中获取名字为 key 的 value , 将其赋值给本地环境变量 JAVA_VM_OPTS
- name: APP
valueFrom:
configMapKeyRef:
name: my-test-env-config
key: APP_NAME
volumeMounts: # 加载数据卷
- name: db-config # 表示加载 volumes 属性中那个数据卷
mountPath: "/usr/local/mysql/conf" # 想要将数据卷中的文件加载到那个目录下
readOnly: true # 是否只读
volumes: # 数据卷挂载 configmap secret
- name: db-config # 数据卷的名字, 随意设置
configMap: # 数据卷类型为 ConfigMap
name: xiaoge-test-config # configMap 的名字, 必须跟想要加载的 configmap 相同
items: # 对 configmap 中的 key 进行映射, 如果不指定, 默认会将 configmap 中所有 key 全部转换为一个个同名的文件
- key: "db.properties" # configMap 中的 key
path: "db.properties" # 将该 key 的值转换为文件
restartPolicy: Never
这里没有认证信息是因为我们之前通过docker login 认证过
5.1.3、SubPath 的使用
# 创建 configMap nginx.conf就是普通nginx.conf内容
kubectl create cm nginx-config-cm --from-file=./nginx.conf
使用 ConfigMap 或 Secret 挂载到目录的时候,会将容器中源目录给覆盖掉,此时我们可能只想覆盖目录中的某一个文件,但是这样的操作会覆盖整个文件,因此需要使用到 SubPath
配置方式:
定义 volumes 时需要增加 items 属性,配置 key 和 path,且 path 的值不能从 / 开始
在容器内的 volumeMounts 中增加 subPath 属性,该值与 volumes 中 items.path 的值相同
containers:
......
volumeMounts: # 挂载数据卷
- name: nginx-conf # 使用哪个 configmap 或 secret (如果volumes指定了名称, 那么这里就写volumes的名称)
mountPath: '/etc/nginx/nginx.conf' # 挂载的路径 (使用了subPath, 一定要写你要替换文件的完整路径)
subPath: etc/nginx/nginx.conf # 与 volumes.[0].items.path 相同
volumes: # 数据卷定义
- name: nginx-conf # 数据卷名称
configMap: # 数据卷类型为 configMap
name: nginx-config-cm # configMap 名字
items: # 要将 configmap 中的那些数据挂在进来
key: nginx.conf # 指定挂在那些 key
path: etc/nginx/nginx.conf # subPath 路径
5.1.4、配置的热更新
我们通常会将项目的配置文件作为 configmap 然后挂载到 pod,那么如果更新 configmap 中的配置,会不会更新到 pod 中呢?
这得分成几种情况:
默认方式:会更新,更新周期是更新时间 + 缓存时间
subPath:不会更新
变量形式:如果 pod 中的一个变量是从 configmap 或 secret 中得到,同样也是不会更新的
对于 subPath 的方式,我们可以取消 subPath 的使用,将配置文件挂载到一个不存在的目录,避免目录的覆盖,然后再利用软连接的形式,将该文件链接到目标位置
但是如果目标位置原本就有文件,可能无法创建软链接,此时可以基于前面讲过的 postStart 操作执行删除命令,将默认的吻技安删除即可
5.1.4.1、通过 edit 命令直接修改 configmap
5.1.4.2、通过 replace 替换
由于 configmap 我们创建通常都是基于文件创建,并不会编写 yaml 配置文件,因此修改时我们也是直接修改配置文件,而 replace 是没有 --from-file 参数的,因此无法实现基于源配置文件的替换,此时我们可以利用下方的命令实现
# 该命令的重点在于 --dry-run 参数,该参数的意思打印 yaml 文件,但不会将该文件发送给 apiserver,再结合 -oyaml 输出 yaml 文件就可以得到一个配置好但是没有发给 apiserver 的文件,然后再结合 replace 监听控制台输出得到 yaml 数据即可实现替换
kubectl create cm configMap名称 --from-file=文件 --dry-run -oyaml | kubectl replace -f-
kubectl create cm xiaoge-test-config --from-file=db.properties --dry-run -oyaml | kubectl replace -f-
5.1.5、不可变的 Secret 和 ConfigMap
对于一些敏感服务的配置文件,在线上有时是不允许修改的,此时在配置 configmap 时可以设置 immutable: true 来禁止修改
5.2、持久化存储
5.2.1、Volumes
5.2.1.1、HostPath
将节点上的文件或目录挂载到 Pod 上,此时该目录会变成持久化存储目录,即使 Pod 被删除后重启,也可以重新加载到该目录,该目录下的文件不会丢失
配置文件
apiVersion: v1
kind: Pod
metadata:
name: test-volume-pd
spec:
containers:
- image: nginx
name: nginx-volume
volumeMounts:
- mountPath: /test-pd # 挂载到容器的哪个目录
name: test-volume # 挂载哪个 volume
volumes:
- name: test-volume
hostPath: # 与主机共享目录, 加载主机中的指定目录到容器中(主机在这个目录中修改什么东西, 容器马上能感知, 容器中修改什么东西, 主机也马上能感知)
path: /data # 节点中的目录
type: DirectoryOrCreate # 检查类型,在挂载前对挂载目录做什么检查操作,有多种选项,默认为空字符串,不做任何检查
类型:
空字符串:默认类型,不做任何检查
DirectoryOrCreate:如果给定的 path 不存在,就创建一个 755 的空目录
Directory:这个目录必须存在
FileOrCreate:如果给定的文件不存在,则创建一个空文件,权限为 644
File:这个文件必须存在
Socket:UNIX 套接字,必须存在
CharDevice:字符设备,必须存在
BlockDevice:块设备,必须存在
5.2.1.2、EmptyDir
EmptyDir 主要用于一个 Pod 中不同的 Container 共享数据使用的,由于只是在 Pod 内部使用,因此与其他 volume 比较大的区别是,当 Pod 如果被删除了,那么 emptyDir 也会被删除。
存储介质可以是任意类型,如 SSD、磁盘或网络存储。可以将 emptyDir.medium 设置为 Memory 让 k8s 使用 tmpfs(内存支持文件系统),速度比较快,但是重启 tmpfs 节点时,数据会被清除,且设置的大小会计入到 Container 的内存限制中。
配置文件
apiVersion: v1
kind: Pod
metadata:
name: empty-dir-pd
spec:
containers:
- image: alpine
name: nginx-emptydir1
command: ["/bin/sh", "-c", "sleep 3600;"]
volumeMounts:
- mountPath: /cache
name: cache-volume
- image: alpine
name: nginx-emptydir2
command: ["/bin/sh", "-c", "sleep 3600;"]
volumeMounts:
- mountPath: /opt
name: cache-volume
volumes:
- name: cache-volume
emptyDir: {}
# nginx-emptydir1容器中的/cache和nginx-emptydir2容器中的/opt目录共享, cache中目录里面东西发生改变, opt也跟跟着变, 同理opt发生改变cache目录也会跟着变
# 进入单个容器中
# kubectl exec -it pod名称 -c 容器名称 -- sh
# kubectl exec -it empty-dir-pd -c nginx-emptydir1 -- sh
5.2.2、NFS 挂载
nfs 卷能将 NFS (网络文件系统) 挂载到你的 Pod 中。 不像 emptyDir 那样会在删除 Pod 的同时也会被删除,nfs 卷的内容在删除 Pod 时会被保存,卷只是被卸载。 这意味着 nfs 卷可以被预先填充数据,并且这些数据可以在 Pod 之间共享。
5.2.2.1、安装 nfs
# 安装 nfs
yum install nfs-utils -y
# 启动 nfs
systemctl start nfs-server
# 查看 nfs 版本
cat /proc/fs/nfsd/versions
# 创建共享目录
mkdir -p /data/nfs
cd /data/nfs
mkdir rw
mkdir ro
# 设置共享目录 export
vim /etc/exports
/data/nfs/rw 192.168.113.0/24(rw,sync,no_subtree_check,no_root_squash)
/data/nfs/ro 192.168.113.0/24(ro,sync,no_subtree_check,no_root_squash)
# 重新加载
exportfs -f
systemctl reload nfs-server
# 到其他测试节点安装 nfs-utils 并加载测试
mkdir -p /mnt/nfs/rw
mkdir -p /mnt/nfs/ro
mount -t nfs 192.168.171.130:/data/nfs/rw /mnt/nfs/rw
mount -t nfs 192.168.171.130:/data/nfs/ro /mnt/nfs/ro
5.2.2.2、配置文件
apiVersion: v1
kind: Pod
metadata:
name: nfs-test-pd1
spec:
containers:
- image: nginx
name: test-container
volumeMounts:
- mountPath: /usr/share/nginx/html
name: test-volume
volumes:
- name: test-volume
nfs:
server: 192.168.171.130 # 网络存储服务地址
path: /data/nfs/rw/www/xiaoge # 网络存储路径
readOnly: false # 是否只读
# 把nfs中的/data/nfs/rw/www/xiaoge目录, 替换容器中的/usr/share/nginx/html目录, 只要xiaoge目录下的内容修改, 容器中html内容也会跟着更改
5.2.3、PV 与 PVC
持久卷(PersistentVolume,PV) 是集群中的一块存储,可以由管理员事先制备, 或者使用存储类(Storage Class)来动态制备。 持久卷是集群资源,就像节点也是集群资源一样。PV 持久卷和普通的 Volume 一样, 也是使用卷插件来实现的,只是它们拥有独立于任何使用 PV 的 Pod 的生命周期。 此 API 对象中记述了存储的实现细节,无论其背后是 NFS、iSCSI 还是特定于云平台的存储系统。
持久卷申领(PersistentVolumeClaim,PVC) 表达的是用户对存储的请求。概念上与 Pod 类似。 Pod 会耗用节点资源,而 PVC 申领会耗用 PV 资源。Pod 可以请求特定数量的资源(CPU 和内存);同样 PVC 申领也可以请求特定的大小和访问模式 (例如,可以要求 PV 卷能够以 ReadWriteOnce、ReadOnlyMany 或 ReadWriteMany 模式之一来挂载,参见访问模式)。
5.2.3.1、生命周期
构建
静态构建
集群管理员创建若干 PV 卷。这些卷对象带有真实存储的细节信息, 并且对集群用户可用(可见)。PV 卷对象存在于 Kubernetes API 中,可供用户消费(使用)。
动态构建
如果集群中已经有的 PV 无法满足 PVC 的需求,那么集群会根据 PVC 自动构建一个 PV,该操作是通过 StorageClass 实现的。
想要实现这个操作,前提是 PVC 必须设置 StorageClass,否则会无法动态构建该 PV,可以通过启用 DefaultStorageClass 来实现 PV 的构建。
绑定
当用户创建一个 PVC 对象后,主节点会监测新的 PVC 对象,并且寻找与之匹配的 PV 卷,找到 PV 卷后将二者绑定在一起。
如果找不到对应的 PV,则需要看 PVC 是否设置 StorageClass 来决定是否动态创建 PV,若没有配置,PVC 就会一致处于未绑定状态,直到有与之匹配的 PV 后才会申领绑定关系。
使用
Pod 将 PVC 当作存储卷来使用,集群会通过 PVC 找到绑定的 PV,并为 Pod 挂载该卷。
Pod 一旦使用 PVC 绑定 PV 后,为了保护数据,避免数据丢失问题,PV 对象会受到保护,在系统中无法被删除。
回收策略
当用户不再使用其存储卷时,他们可以从 API 中将 PVC 对象删除, 从而允许该资源被回收再利用。PersistentVolume 对象的回收策略告诉集群, 当其被从申领中释放时如何处理该数据卷。 目前,数据卷可以被 Retained(保留)、Recycled(回收)或 Deleted(删除)。
保留(Retain)
回收策略 Retain 使得用户可以手动回收资源。当 PersistentVolumeClaim 对象被删除时,PersistentVolume 卷仍然存在,对应的数据卷被视为"已释放(released)"。 由于卷上仍然存在这前一申领人的数据,该卷还不能用于其他申领。 管理员可以通过下面的步骤来手动回收该卷:
删除 PersistentVolume 对象。与之相关的、位于外部基础设施中的存储资产 (例如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)在 PV 删除之后仍然存在。
根据情况,手动清除所关联的存储资产上的数据。
手动删除所关联的存储资产。
如果你希望重用该存储资产,可以基于存储资产的定义创建新的 PersistentVolume 卷对象。
删除(Delete)
- 对于支持 Delete 回收策略的卷插件,删除动作会将 PersistentVolume 对象从 Kubernetes 中移除,同时也会从外部基础设施(如 AWS EBS、GCE PD、Azure Disk 或 Cinder 卷)中移除所关联的存储资产。 动态制备的卷会继承其 StorageClass 中设置的回收策略, 该策略默认为 Delete。管理员需要根据用户的期望来配置 StorageClass; 否则 PV 卷被创建之后必须要被编辑或者修补。
回收(Recycle)
- 警告: 回收策略 Recycle 已被废弃。取而代之的建议方案是使用动态制备。
- 如果下层的卷插件支持,回收策略 Recycle 会在卷上执行一些基本的擦除 (rm -rf /thevolume/*)操作,之后允许该卷用于新的 PVC 申领。
5.2.3.2、PV
状态
- Available:空闲,未被绑定
- Bound:已经被 PVC 绑定
- Released:PVC 被删除,资源已回收,但是 PV 未被重新使用
- Failed:自动回收失败
配置文件
apiVersion: v1 kind: PersistentVolume # 描述资源对象为 PV 类型 metadata: name: pv0001 # PV 的名字 spec: capacity: # 容量配置 storage: 5Gi # pv 的容量 volumeMode: Filesystem # 存储类型为文件系统 accessModes: # 访问模式:ReadWriteOnce、ReadWriteMany、ReadOnlyMany - ReadWriteOnce # 可被单节点独写 persistentVolumeReclaimPolicy: Retain # 回收策略 storageClassName: slow # 创建 PV 的存储类名,需要与 pvc 的相同 mountOptions: # 加载配置 - hard - nfsvers=4.1 nfs: # 连接到 nfs path: /data/nfs/rw/test-pv # 存储路径 server: 192.168.171.130 # nfs 服务地址
ReadWriteOnce: 只能同时被一个pvc绑定, 被一个pvc使用, 意味着这个pv只能被一个pvc用
ReadWriteMany: 同时被多个pvc绑定, 被多个pvc使用, 意味着这个pv能被多个pvc用
ReadOnlyMany: 同时被多个pvc绑定只读, 被多个pvc使用只读, 意味着这个pv能被多个pvc用只读
5.2.3.3、PVC
Pod 绑定 PVC
在 pod 的挂载容器配置中,增加 pvc 挂载 containers: ...... volumeMounts: - mountPath: /tmp/pvc name: nfs-pvc-test volumes: - name: nfs-pvc-test persistentVolumeClaim: claimName: nfs-pvc # pvc 的名称 #apiVersion: v1 #kind: Pod #metadata: # name: test-pvc-pd #spec: # containers: # - image: nginx # name: nginx-volume # volumeMounts: # - mountPath: /usr/share/nginx/html # 挂载到容器的哪个目录 # name: test-volume # 挂载哪个 volume # volumes: # - name: test-volume # persistentVolumeClaim: # claimName: nfs-pvc # pvc 的名称
配置文件
apiVersion: v1 kind: PersistentVolumeClaim # 资源类型为 PVC metadata: name: nfs-pvc spec: accessModes: - ReadWriteOnce # 权限需要与对应的 pv 相同 volumeMode: Filesystem resources: requests: storage: 5Gi # 资源可以小于 pv 的,但是不能大于,如果大于就会匹配不到 pv storageClassName: slow # 名字需要与对应的 pv 相同 # selector: # 使用选择器选择对应的 pv # matchLabels: # release: "stable" # matchExpressions: # - {key: environment, operator: In, values: [dev]
5.2.3.4、StorageClass
k8s 中提供了一套自动创建 PV 的机制,就是基于 StorageClass 进行的,通过 StorageClass 可以实现仅仅配置 PVC,然后交由 StorageClass 根据 PVC 的需求动态创建 PV。
制备器(Provisioner)
- 每个 StorageClass 都有一个制备器(Provisioner),用来决定使用哪个卷插件制备 PV。
NFS 动态制备案例
nfs-provisioner
apiVersion: apps/v1 kind: Deployment metadata: name: nfs-client-provisioner namespace: default labels: app: nfs-client-provisioner spec: replicas: 1 strategy: type: Recreate selector: matchLabels: app: nfs-client-provisioner template: metadata: labels: app: nfs-client-provisioner spec: serviceAccountName: nfs-client-provisioner containers: - name: nfs-client-provisioner image: quay.io/external_storage/nfs-client-provisioner:latest volumeMounts: - name: nfs-client-root mountPath: /persistentvolumes env: - name: PROVISIONER_NAME value: fuseim.pri/ifs - name: NFS_SERVER value: 192.168.171.130 - name: NFS_PATH value: /data/nfs/rw volumes: - name: nfs-client-root nfs: server: 192.168.171.130 path: /data/nfs/rw
StorageClass 配置
apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: managed-nfs-storage namespace: kube-system provisioner: fuseim.pri/ifs # 外部制备器提供者,编写为提供者的名称 parameters: archiveOnDelete: "false" # 是否存档,false 表示不存档,会删除 oldPath 下面的数据,true 表示存档,会重命名路径 reclaimPolicy: Retain # 回收策略,默认为 Delete 可以配置为 Retain volumeBindingMode: Immediate # 默认为 Immediate,表示创建 PVC 立即进行绑定,只有 azuredisk 和 AWSelasticblockstore 支持其他值
RBAC 配置
apiVersion: v1 kind: ServiceAccount metadata: name: nfs-client-provisioner namespace: default --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: nfs-client-provisioner-runner namespace: default rules: - apiGroups: [""] resources: ["persistentvolumes"] verbs: ["get", "list", "watch", "create", "delete"] - apiGroups: [""] resources: ["persistentvolumeclaims"] verbs: ["get", "list", "watch", "update"] - apiGroups: ["storage.k8s.io"] resources: ["storageclasses"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["events"] verbs: ["create", "update", "patch"] --- kind: ClusterRoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: run-nfs-client-provisioner namespace: default subjects: - kind: ServiceAccount name: nfs-client-provisioner namespace: default roleRef: kind: ClusterRole name: nfs-client-provisioner-runner apiGroup: rbac.authorization.k8s.io --- kind: Role apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-client-provisioner namespace: default rules: - apiGroups: [""] resources: ["endpoints"] verbs: ["get", "list", "watch", "create", "update", "patch"] --- kind: RoleBinding apiVersion: rbac.authorization.k8s.io/v1 metadata: name: leader-locking-nfs-client-provisioner namespace: default subjects: - kind: ServiceAccount name: nfs-client-provisioner roleRef: kind: Role name: leader-locking-nfs-client-provisioner apiGroup: rbac.authorization.k8s.io
PVC 处于 Pending 状态
在 k8s 1.20 之后,出于对性能和统一 apiserver 调用方式的初衷,移除了对 SelfLink 的支持,而默认上面指定的 provisioner 版本需要 SelfLink 功能,因此 PVC 无法进行自动制备。
配置 SelfLink
修改 apiserver 配置文件 vim /etc/kubernetes/manifests/kube-apiserver.yaml spec: containers: - command: - kube-apiserver - --feature-gates=RemoveSelfLink=false # 新增该行 ...... 修改后重新应用该配置 kubectl apply -f /etc/kubernetes/manifests/kube-apiserver.yaml
不需要 SelfLink 的 provisioner
将 provisioner 修改为如下镜像之一即可 gcr.io/k8s-staging-sig-storage/nfs-subdir-external-provisioner:v4.0.0 registry.cn-beijing.aliyuncs.com/pylixm/nfs-subdir-external-provisioner:v4.0.0
PVC 测试配置
apiVersion: v1 kind: PersistentVolumeClaim metadata: name: auto-pv-test-pvc spec: accessModes: - ReadWriteOnce resources: requests: storage: 300Mi storageClassName: managed-nfs-storage
6、高级调度
6.1、CronJob 计划任务
在 k8s 中周期性运行计划任务,与 linux 中的 crontab 相同
注意点:CronJob 执行的时间是 controller-manager 的时间,所以一定要确保 controller-manager 时间是准确的,另外 cronjob
6.1.1、cron 表达式
# ┌───────────── 分钟 (0 - 59)
# │ ┌───────────── 小时 (0 - 23)
# │ │ ┌───────────── 月的某天 (1 - 31)
# │ │ │ ┌───────────── 月份 (1 - 12)
# │ │ │ │ ┌───────────── 周的某天 (0 - 6)(周日到周一;在某些系统上,7 也是星期日)
# │ │ │ │ │ 或者是 sun,mon,tue,web,thu,fri,sat
# │ │ │ │ │
# │ │ │ │ │
# * * * * *
6.1.2、cron 表达式
apiVersion: batch/v1
kind: CronJob
metadata:
name: hello
spec:
concurrencyPolicy: Allow # 并发调度策略:Allow 允许并发调度,Forbid:不允许并发执行,Replace:如果之前的任务还没执行完,就直接执行新的,放弃上一个任务
failedJobsHistoryLimit: 1 # 保留多少个失败的任务
successfulJobsHistoryLimit: 3 # 保留多少个成功的任务
suspend: false # 是否挂起任务,若为 true 则该任务不会执行
# startingDeadlineSeconds: 30 # 间隔多长时间检测失败的任务并重新执行,时间不能小于 10
schedule: "* * * * *" # 调度策略
jobTemplate:
spec:
template:
spec:
containers:
- name: hello
image: busybox:1.28
imagePullPolicy: IfNotPresent
command:
- /bin/sh
- -c
- date; echo Hello from the Kubernetes cluster
restartPolicy: OnFailure
6.2、初始化容器 InitContainer
在真正的容器启动之前,先启动 InitContainer,在初始化容器中完成真实容器所需的初始化操作,完成后再启动真实的容器。
相对于 postStart 来说,首先 InitController 能够保证一定在 EntryPoint 之前执行,而 postStart 不能,其次 postStart 更适合去执行一些命令操作,而 InitController 实际就是一个容器,可以在其他基础容器环境下执行更复杂的初始化功能。
在 pod 创建的模板中配置 initContainers 参数:
spec:
initContainers:
- image: nginx
imagePullPolicy: IfNotPresent
command: ["sh", "-c", "echo 'inited;' >> ~/.init"]
name: init-test
6.3、污点和容忍
k8s 集群中可能管理着非常庞大的服务器,这些服务器可能是各种各样不同类型的,比如机房、地理位置、配置等,有些是计算型节点,有些是存储型节点,此时我们希望能更好的将 pod 调度到与之需求更匹配的节点上。
此时就需要用到污点(Taint)和容忍(Toleration),这些配置都是 key: value 类型的。
6.3.1、污点(Taint)
污点:是标注在节点上的,当我们在一个节点上打上污点以后,k8s 会认为尽量不要将 pod 调度到该节点上,除非该 pod 上面表示可以容忍该污点,且一个节点可以打多个污点,此时则需要 pod 容忍所有污点才会被调度该节点。
# 为节点打上污点
kubectl taint node k8s-master key=value:NoSchedule
# 移除污点
kubectl taint node k8s-master key=value:NoSchedule-
# 查看污点
kubectl describe no k8s-master
污点的影响:
NoSchedule:不能容忍的 pod 不能被调度到该节点,但是已经存在的节点不会被驱逐
NoExecute:不能容忍的节点会被立即清除,能容忍且没有配置 tolerationSeconds 属性,则可以一直运行,设置了 tolerationSeconds: 3600 属性,则该 pod 还能继续在该节点运行 3600 秒
NoSchedule
- 如果不能容忍该污点,那么 Pod 就无法调度到该节点上
NoExecute
如果 Pod 不能忍受这类污点,Pod 会马上被驱逐。
如果 Pod 能够忍受这类污点,但是在容忍度定义中没有指定 tolerationSeconds, 则 Pod 还会一直在这个节点上运行。
如果 Pod 能够忍受这类污点,而且指定了 tolerationSeconds, 则 Pod 还能在这个节点上继续运行这个指定的时间长度。
6.3.2、容忍(Toleration)
容忍:是标注在 pod 上的,当 pod 被调度时,如果没有配置容忍,则该 pod 不会被调度到有污点的节点上,只有该 pod 上标注了满足某个节点的所有污点,则会被调度到这些节点
# pod 的 spec 下面配置容忍
tolerations:
- key: "污点的 key"
value: "污点的 value"
offect: "NoSchedule" # 污点产生的影响
operator: "Equal" # 表是 value 与污点的 value 要相等,也可以设置为 Exists 表示存在 key 即可,此时可以不用配置 value
- Equal
- 比较操作类型为 Equal,则意味着必须与污点值做匹配,key/value都必须相同,才表示能够容忍该污点
- Exists
- 容忍与污点的比较只比较 key,不比较 value,不关心 value 是什么东西,只要 key 存在,就表示可以容忍。
6.4、亲和力(Affinity)
6.4.1、NodeAffinity
节点亲和力:进行 pod 调度时,优先调度到符合条件的亲和力节点上
RequiredDuringSchedulingIgnoredDuringExecution
- 硬亲和力,即支持必须部署在指定的节点上,也支持必须不部署在指定的节点上
PreferredDuringSchedulingIgnoredDuringExecution
- 软亲和力:尽量部署在满足条件的节点上,或尽量不要部署在被匹配的节点上
应用
匹配类型
- In
- 部署在满足条件的节点上
- NotIn
- 匹配不在条件中的节点,实现节点反亲和性
- Exists
- 只要存在 key 名字就可以,不关心值是什么
- DoesNotExist
- 匹配指定 key 名不存在的节点,实现节点反亲和性
- Gt
- value 为数值,且节点上的值小于指定的条件
- Lt
- value 为数值,且节点上的值大于指定条件
- In
配置模板
apiVersion: v1 kind: Pod metadata: name: with-node-affinity spec: affinity: # 亲和力配置 nodeAffinity: # 节点亲和力 requiredDuringSchedulingIgnoredDuringExecution: # 节点必须匹配下方配置 nodeSelectorTerms: # 选择器 - matchExpressions: # 匹配表达式 - key: kubernetes.io/os # 匹配 label 的 key operator: In # 匹配方式,只要匹配成功下方的一个 value 即可 values: - linux # 匹配的 value preferredDuringSchedulingIgnoredDuringExecution: # 节点尽量匹配下方配置 - weight: 1 # 权重[1,100],按照匹配规则对所有节点累加权重,最终之和会加入优先级评分,优先级越高被调度的可能性越高 preference: matchExpressions: # 匹配表达式 - key: label-1 # label 的 key operator: In # 匹配方式,满足一个即可 values: - key-1 # 匹配的 value - weight: 50 # 权重[1,100],按照匹配规则对所有节点累加权重,最终之和会加入优先级评分,优先级越高被调度的可能性越高 preference: matchExpressions: # 匹配表达式 - key: label-2 # label 的 key operator: In # 匹配方式,满足一个即可 values: - key-2 # 匹配的 value ...... containers: - name: with-node-affinity image: pause:2.0
6.4.2、PodAffinity
Pod 亲和力:将与指定 pod 亲和力相匹配的 pod 部署在同一节点。
RequiredDuringSchedulingIgnoredDuringExecution
- 必须将应用部署在一块
PreferredDuringSchedulingIgnoredDuringExecution
- 尽量将应用部署在一块
配置模板
apiVersion: v1 kind: Pod metadata: name: with-pod-affinity spec: affinity: # 亲和力配置 podAffinity: # pod 亲和力配置 requiredDuringSchedulingIgnoredDuringExecution: # 当前 pod 必须匹配到对应条件 pod 所在的 node 上 - labelSelector: # 标签选择器 matchExpressions: # 匹配表达式 - key: security # 匹配的 key operator: In # 匹配方式 values: # 匹配其中的一个 value - S1 topologyKey: topology.kubernetes.io/zone # 节点上面必须要有这个标签 podAntiAffinity: # pod 反亲和力配置 preferredDuringSchedulingIgnoredDuringExecution: # 尽量不要将当前节点部署到匹配下列参数的 pod 所在的 node 上 - weight: 100 # 权重 podAffinityTerm: # pod 亲和力配置条件 labelSelector: # 标签选择器 matchExpressions: # 匹配表达式 - key: security # 匹配的 key operator: In # 匹配的方式 values: - S2 # 匹配的 value topologyKey: topology.kubernetes.io/zone # 节点上面必须要有这个标签 containers: - name: with-pod-affinity image: pause:2.0
6.4.3、PodAntiAffinity
Pod 反亲和力:根据策略尽量部署或不部署到一块
RequiredDuringSchedulingIgnoredDuringExecution
不要将应用与之匹配的部署到一块 podAffinity: requiredDuringSchedulingIgnoredDuringExecution: - labelSelector: matchExpressions: - key: security operator: In values: - S1 topologyKey: topology.kubernetes.io/zone
PreferredDuringSchedulingIgnoredDuringExecution
- 尽量不要将应用部署到一块
7、身份认证与权限
Kubernetes 中提供了良好的多租户认证管理机制,如 RBAC、ServiceAccount 还有各种策略等。
通过该文件可以看到已经配置了 RBAC 访问控制
/usr/lib/systemd/system/kube-apiserver.service
认证
所有 Kubernetes 集群有两类用户:由 Kubernetes 管理的Service Accounts (服务账户)和(Users Accounts) 普通账户。 普通账户是假定被外部或独立服务管理的,由管理员分配 keys,用户像使用 Keystone 或 google 账号一样,被存储在包含 usernames 和 passwords 的 list 的文件里。 需要注意:在 Kubernetes 中不能通过 API 调用将普通用户添加到集群中。 - 普通帐户是针对(人)用户的,服务账户针对 Pod 进程。 - 普通帐户是全局性。在集群所有namespaces中,名称具有惟一性。 - 通常,群集的普通帐户可以与企业数据库同步,新的普通帐户创建需要特殊权限。服务账户创建目的是更轻量化,允许集群用户为特定任务创建服务账户。 - 普通帐户和服务账户的审核注意事项不同。 - 对于复杂系统的配置包,可以包括对该系统的各种组件的服务账户的定义。
User Accounts
Service Accounts
Service Account 自动化
Service Account Admission Controller
通过 Admission Controller 插件来实现对 pod 修改,它是 apiserver 的一部分。创建或更新 pod 时会同步进行修改 pod。当插件处于激活状态(在大多数发行版中都默认情况)创建或修改 pod 时,会按以下操作执行: 1. 如果 pod 没有设置 ServiceAccount,则将 ServiceAccount 设置为 default。 2. 确保 pod 引用的 ServiceAccount 存在,否则将会拒绝请求。 3. 如果 pod 不包含任何 ImagePullSecrets,则将ServiceAccount 的 ImagePullSecrets 会添加到 pod 中。 4. 为包含 API 访问的 Token 的 pod 添加了一个 volume。 5. 把 volumeSource 添加到安装在 pod 的每个容器中,挂载在/var/run/secrets/kubernetes.io/serviceaccount。
Token Controller
TokenController 作为 controller-manager 的一部分运行。异步行为: 观察 serviceAccount 的创建,并创建一个相应的 Secret 来允许 API 访问。 观察 serviceAccount 的删除,并删除所有相应的ServiceAccountToken Secret 观察 secret 添加,并确保关联的 ServiceAccount 存在,并在需要时向 secret 中添加一个 Token。 观察 secret 删除,并在需要时对应 ServiceAccount 的关联
Service Account Controller
Service Account Controller 在 namespaces 里管理ServiceAccount,并确保每个有效的 namespaces 中都存在一个名为 “default” 的 ServiceAccount。
授权(RBAC)
Role
代表一个角色,会包含一组权限,没有拒绝规则,只是附加允许。它是 Namespace 级别的资源,只能作用与 Namespace 之内。 # 查看已有的角色信息 kubectl get role -n ingress-nginx -oyaml
配置文件
apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx name: nginx-ingress namespace: ingress-nginx roles: - apiGroups: - "" resources: - configmaps - pods - secrets - namespaces verbs: - get - apiGroups: - "" resourceNames: - ingress-controller-label-nginx resources: - configmaps verbs: - get - update - apiGroups: - "" resources: - configmaps verbs: - create
ClusterRole
功能与 Role 一样,区别是资源类型为集群类型,而 Role 只在 Namespace # 查看某个集群角色的信息 kubectl get clusterrole view -oyaml
RoleBinding
Role 或 ClusterRole 只是用于制定权限集合,具体作用与什么对象上,需要使用 RoleBinding 来进行绑定。 作用于 Namespace 内,可以将 Role 或 ClusterRole 绑定到 User、Group、Service Account 上。 # 查看 rolebinding 信息 kubectl get rolebinding --all-namespaces # 查看指定 rolebinding 的配置信息 kubectl get rolebinding --all-namespaces -oyaml
配置文件
apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: ...... roleRef: apiGroup: rbac.authorization.k8s.io kind: Role name nginx-ingress-role subjects: - kind: ServiceAccount name: nginx-ingress-serviceaccount namespace: ingress-nginx
ClusterRoleBinding
与 RoleBinding 相同,但是作用于集群之上,可以绑定到该集群下的任意 User、Group 或 Service Account