云原生作业(k8s总结)
1 kubernetes 简介及部署方法
1.1 简介
kubernetes的本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。目的是实现资源管理的自动化,主要提供了如下的主要功能:
-
自我修复:一旦某一个容器崩溃,能够在1秒中左右迅速启动新的容器
-
弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整
-
服务发现:服务可以通过自动发现的形式找到它所依赖的服务
-
负载均衡:如果一个服务起动了多个容器,能够自动实现请求的负载均衡
-
版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本
-
存储编排:可以根据容器自身的需求自动创建存储卷
一个kubernetes集群主要是由控制节点(master)、工作节点(node)构成,每个节点上都会安装不同的组件
K8S各个组件用途
1 master:集群的控制平面,负责集群的决策
-
ApiServer : 资源操作的唯一入口,接收用户输入的命令,提供认证、授权、API注册和发现等机制
-
Scheduler : 负责集群资源调度,按照预定的调度策略将Pod调度到相应的node节点上
-
ControllerManager : 负责维护集群的状态,比如程序部署安排、故障检测、自动扩展、滚动更新等
-
Etcd :负责存储集群中各种资源对象的信息
2 node:集群的数据平面,负责为容器提供运行环境
-
kubelet:负责维护容器的生命周期,同时也负责Volume(CVI)和网络(CNI)的管理
-
Container runtime:负责镜像管理以及Pod和容器的真正运行(CRI)
-
kube-proxy:负责为Service提供cluster内部的服务发现和负载均衡
K8S 各组件之间的调用关系
当我们要运行一个web服务时
-
kubernetes环境启动之后,master和node都会将自身的信息存储到etcd数据库中
-
web服务的安装请求会首先被发送到master节点的apiServer组件
-
apiServer组件会调用scheduler组件来决定到底应该把这个服务安装到哪个node节点上
在此时,它会从etcd中读取各个node节点的信息,然后按照一定的算法进行选择,并将结果告知apiServer
-
apiServer调用controller-manager去调度Node节点安装web服务
-
kubelet接收到指令后,会通知docker,然后由docker来启动一个web服务的pod
-
如果需要访问web服务,就需要通过kube-proxy来对pod产生访问的代理
K8S 的 常用名词感念
-
Master:集群控制节点,每个集群需要至少一个master节点负责集群的管控
-
Node:工作负载节点,由master分配容器到这些node工作节点上,然后node节点上的
-
Pod:kubernetes的最小控制单元,容器都是运行在pod中的,一个pod中可以有1个或者多个容器
-
Controller:控制器,通过它来实现对pod的管理,比如启动pod、停止pod、伸缩pod的数量等等
-
Service:pod对外服务的统一入口,下面可以维护者同一类的多个pod
-
Label:标签,用于对pod进行分类,同一类pod会拥有相同的标签
-
NameSpace:命名空间,用来隔离pod的运行环境
k8S的分层架构
- 核心层:Kubernetes最核心的功能,对外提供API构建高层的应用,对内提供插件式应用执行环境
-
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS解析等)
-
管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态Provision等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy等)
-
接口层:kubectl命令行工具、客户端SDK以及集群联邦
-
生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
-
Kubernetes外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS应用、ChatOps等
-
Kubernetes内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等
1.2 部署
k8s中容器的管理方式
K8S 集群创建方式有3种:
centainerd
默认情况下,K8S在创建集群时使用的方式
docker
Docker使用的普记录最高,虽然K8S在1.24版本后已经费力了kubelet对docker的支持,但时可以借助cri-docker方式来实现集群创建
cri-o
CRI-O的方式是Kubernetes创建容器最直接的一种方式,在创建集群的时候,需要借助于cri-o插件的方式来实现Kubernetes集群的创建。
[!NOTE]
docker 和cri-o 这两种方式要对kubelet程序的启动参数进行设置
k8s 环境部署说明
K8S中文官网:Kubernetes
主机名 | ip | 角色 |
---|---|---|
harbor | 172.25.254.200 | harbor仓库 |
master | 172.25.254.100 | master,k8s集群控制节点 |
node1 | 172.25.254.10 | worker,k8s集群工作节点 |
node2 | 172.25.254.20 | worker,k8s集群工作节点 |
-
所有节点禁用selinux和防火墙
-
所有节点同步时间和解析
-
所有节点安装docker-ce
-
所有节点禁用swap,注意注释掉/etc/fstab文件中的定义
集群环境初始化
禁用swap和本地解析,安装docker
###禁用swap和本地解析
]# systemctl mask swap.target
]# swapoff -a
]# vim /etc/fstab
...
#注释掉带swap的一行
#/dev/mapper/rhel-swap none swap defaults 0 0]# systemctl daemon-reload ]# vim /etc/hosts #可以用scp复制到别的主机
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.200 reg.timinglee.org
172.25.254.100 master
172.25.254.10 node1
172.25.254.20 node2###安装docker
]# ls
anaconda-ks.cfg docker.tar.gz
]# tar zxf docker.tar.gz #将解压完的rpm用scp复制
]# dnf install *.rpm -y]# vim /lib/systemd/system/docker.service #scp复制
...
#给这行加了个--iptables=true
ExecStart=/usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock --iptables=true
...
]# systemctl enable --now docker.service
复制harbor仓库中的证书并启动docke
[root@harbor ~]# ls
anaconda-ks.cfg docker-ce-rootless-extras-27.1.2-1.el9.x86_64.rpm
containerd.io-1.7.20-3.1.el9.x86_64.rpm docker-compose-plugin-2.29.1-1.el9.x86_64.rpm
docker-buildx-plugin-0.16.2-1.el9.x86_64.rpm docker.tar.gz
docker-ce-27.1.2-1.el9.x86_64.rpm harbor-offline-installer-v2.5.4.tgz
docker-ce-cli-27.1.2-1.el9.x86_64.rpm packages.zip
[root@harbor ~]# tar zxf harbor-offline-installer-v2.5.4.tgz [root@harbor ~]# mkdir -p /data/certs
[root@harbor ~]# openssl req -newkey rsa:4096 -nodes -sha256 -keyout /data/certs/timinglee.org.key -addext "subjectAltName = DNS:reg.timinglee.org" -x509 -days 365 -out /data/certs/timinglee.org.crt
[root@harbor ~]# for i in 100 20 10 ; do ssh -l root 172.25.254.$i mkdir -p /etc/docker/certs.d scp /data/certs/timinglee.org.crt root@172.25.254.$i:/etc/docker/certs.d/ca.crt; done[root@harbor harbor]# cp harbor.yml.tmpl harbor.yml
[root@harbor harbor]# vim harbor.yml
...
hostname: reg.timinglee.org
...certificate: /data/certs/timinglee.org.crtprivate_key: /data/certs/timinglee.org.key
...
harbor_admin_password: lee
...
[root@harbor harbor]# ./install.sh --with-chartmuseum #成功的话效果如图一###master主机
[root@master mnt]# cd /etc/docker/
[root@master docker]# vim daemon.json
{"registry-mirrors":["https://reg.timinglee.org"]
}
[root@master docker]# for i in 10 20 ; do scp daemon.json root@172.25.254.$i:/etc/docker/; done
[root@master docker]# for i in 100 10 20 ; do ssh root@172.25.254.$i mkdir /etc/docker/cert.d/reg.timinglee.org ; done
[root@master docker]# for i in 100 10 20 ; do ssh root@172.25.254.$i mv /etc/docker/certs.d/ca.crt /etc/docker/certs.d/reg.timinglee.org/ ; done
[root@master docker]# for i in 100 10 20 ; do ssh root@172.25.254.$i systemctl enable --now docker ; done[root@master docker]# docker login reg.timinglee.org #所有主机都登录
安装K8S部署工具
[root@master ~]# ls
cri-dockerd-0.3.14-3.el8.x86_64.rpm k8s-1.30.tar.gz libcgroup-0.41-19.el8.x86_64.rpm
[root@master ~]# for i in 10 20 ; do scp * root@172.25.254.$i:/mnt; done
3台主机]# dnf install *.rpm -y[root@master ~]# vim /lib/systemd/system/cri-docker.service
ExecStart=/usr/bin/cri-dockerd --container-runtime-endpoint fd:// --network-plugin=cni --pod-infra-container-image=reg.timinglee.org/k8s/pause:3.9
[root@master ~]# for i in 10 20; do scp /lib/systemd/system/cri-docker.service root@172.25.254.$i:/lib/systemd/system/cri-docker.service; done
3台主机]# systemctl enable --now cri-docker.service3]# tar zxf k8s-1.30.tar.gz
3]# dnf install *.rpm -y
[root@master ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc
[root@master ~]# source ~/.bashrc[root@master ~]# docker images | awk '/google/{print $1":"$2}' | awk -F / '{system("docker tag "$0" reg.timinglee.org/k8s/"$3)}'3]# systemctl enable --now kubelet.service [root@master ~]# kubeadm init --pod-network-cidr=10.244.0.0/16 --image-repository reg.timinglee.org/k8s --kubernetes-version v1.30.0 --cri-socket=unix:///var/run/cri-dockerd.sock
[root@master ~]# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
[root@master ~]# source ~/.bash_profile 2台node主机]# kubeadm join 172.25.254.100:6443 --token htjcwe.0pi0egukp6vyep1k --discovery-token-ca-cert-hash sha256:08cfe29d9adadd188bfcf41a2fa1d0465de91434a0f82ccf66a7ecf7d748519c --cri-socket=unix:///var/run/cri-dockerd.sock
网络插件flannel
[root@master ~]# docker load -i flannel-0.25.5.tag.gz
[root@master ~]# docker tag flannel/flannel:v0.25.5 reg.timinglee.org/flannel/flannel:v0.25.5
[root@master ~]# docker push reg.timinglee.org/flannel/flannel:v0.25.5
[root@master ~]# docker tag flannel/flannel-cni-plugin:v1.5.1-flannel1 reg.timinglee.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
[root@master ~]# docker push reg.timinglee.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
[root@master ~]# vim kube-flannel.yml
#给3个image去掉前面一部分
image: flannel/flannel-cni-plugin:v1.5.1-flannel1image: flannel/flannel:v0.25.5image: flannel/flannel-cni-plugin:v1.5.1-flannel1
[root@master ~]# kubectl apply -f kube-flannel.yml
[root@master ~]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
master Ready control-plane 14m v1.30.0
node1 Ready <none> 11m v1.30.0
node2 Ready <none> 21s v1.30.0#harbor主机[root@harbor ~]# unzip packages.zip
[root@harbor ~]# mkdir /etc/docker/certs.d/reg.timinglee.org/ -p
[root@harbor ~]# cp /data/certs/timinglee.org.crt /etc/docker/certs.d/reg.timinglee.org/ca.crt
[root@harbor ~]# systemctl restart docker.service [root@harbor ~]# cd harbor/
[root@harbor harbor]# docker compose up -d
[root@harbor harbor]# docker login reg.timinglee.org
[root@harbor ~]# cd packages/
[root@harbor packages]# docker load -i nginx-latest.tar.gz
[root@harbor packages]# docker tag nginx:latest reg.timinglee.org/library/nginx:latest
[root@harbor harbor]# docker push reg.timinglee.org/library/nginx:latest
测试:
[root@master ~]# kubectl create deployment webcluster --image nginx --replicas 2
2 pod管理及优化
2.1 资源管理方式
#准备
[root@master ~]# rm -rf *
[root@master ~]# ls
busyboxplus.tar.gz myapp.tar.gz
[root@master ~]# docker load -i busyboxplus.tar.gz
[root@master ~]# docker load -i myapp.tar.gz
[root@master ~]# docker tag busyboxplus:latest reg.timinglee.org/library/busyboxplus:latest
[root@master ~]# docker push reg.timinglee.org/library/busyboxplus:latest
[root@master ~]# docker tag timinglee/myapp:v1 reg.timinglee.org/library/myapp:v1
[root@master ~]# docker tag timinglee/myapp:v2 reg.timinglee.org/library/myapp:v2
[root@master ~]# docker push reg.timinglee.org/library/myapp:v1
[root@master ~]# docker push reg.timinglee.org/library/myapp:v2
测试:
[root@master ~]# kubectl run test --image myapp:v1
[root@master ~]# kubectl run test --image myapp:v1 --dry-run=client -o yaml > test.yml
[root@master ~]# vim test.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testname: test
spec:containers:- image: myapp:v1name: test- image: busyboxplusname: test1command:- /bin/sh- -c- sleep 10000
[root@master ~]# kubectl create -f test.yml
pod/test created
[root@master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 2/2 Running 0 26s 10.244.1.3 node1 <none> <none>[root@master ~]# vim test.yml
...- image: myapp:v2
...
[root@master ~]# kubectl apply -f test.yml
Warning: resource pods/test is missing the kubectl.kubernetes.io/last-applied-configuration annotation which is required by kubectl apply. kubectl apply should only be used on resources created declaratively by either kubectl create --save-config or kubectl apply. The missing annotation will be patched automatically.
pod/test configured
[root@master ~]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 2/2 Running 1 (4s ago) 96s 10.244.1.3 node1 <none> <none>
[root@master ~]# curl 10.244.1.3
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
2.2 资源类型和命令用法
[root@master ~]# kubectl create deployment test --image myapp:v1 --replicas 2
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-db99b9599-6nxdq 1/1 Running 0 57s
test-db99b9599-jxk88 1/1 Running 0 57s
[root@master ~]# kubectl scale deployment test --replicas 4
deployment.apps/test scaled
[root@master ~]# kubectl get pods
NAME READY STATUS RESTARTS AGE
test-db99b9599-6nxdq 1/1 Running 0 80s
test-db99b9599-6spkj 1/1 Running 0 3s
test-db99b9599-hqdh6 1/1 Running 0 3s
test-db99b9599-jxk88 1/1 Running 0 80s
2.3 Pod概念和更新回滚
更新与回滚的测试:
2.4 yaml文件部署
端口暴露
[root@master pod]# kubectl run testpod --image myapp:v1 --dry-run=client -o yaml > testpod.yml
[root@master pod]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:containers:- image: myapp:v1name: testpodports:- name: htppcontainerPort: 80hostPort: 80protocol: TCP
设置环境变量
[root@master pod]# vim testpod.yml
[root@master pod]# kubectl apply -f testpod.yml
pod/testpod created
[root@master pod]# kubectl exec pods/testpod -c testpod ls /
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
...
timinglee
...
资源限制
[root@master pod]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:containers:- image: busyboxplusname: testpodresources:limits:cpu: 500mrequests:cpu: 200m
[root@master pod]# kubectl describe pod testpod
QoS Class: Burstable[root@master pod]# kubectl delete -f testpod.yml
pod "testpod" deleted
[root@master pod]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:containers:- image: busyboxplusname: testpodresources:limits:cpu: 500mmemory: 200Mirequests:cpu: 500mmemory: 200Mi
[root@master pod]# kubectl apply -f testpod.yml
[root@master pod]# kubectl describe pod testpod
QoS Class: Guaranteed
容器启动管理
Always
[root@master pod]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:containers:- image: myapp:v1name: testpod#restartPolicy: Never
[root@master pod]# kubectl apply -f testpod.yml
pod/testpod created
[root@master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 5s 10.244.2.14 node2 <none> <none>
[root@node2 mnt]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a62366fec80c d4a5e0eaa84f "nginx -g 'daemon of…" 9 seconds ago Up 8 seconds k8s_testpod_testpod_default_3f2ec520-569c-4c60-a928-
...
[root@node2 mnt]# docker rm -f a62366fec80c
a62366fec80c
[root@master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 1 50s 10.244.2.14 node2 <none> <none>
Never
[root@master pod]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:containers:- image: myapp:v1name: testpodrestartPolicy: Never
[root@master pod]# kubectl apply -f testpod.yml
pod/testpod created[root@node2 mnt]# docker rm -f 6d9b3424ff97
6d9b3424ff97[root@master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 0/1 ContainerStatusUnknown 1 33s 10.244.2.15 node2 <none> <none>
选择运行节点
[root@master pod]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:containers:- image: myapp:v1name: testpodnodeSelector:kubernetes.io/hostname: node1
[root@master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 11s 10.244.1.11 node1 <none> <none>
共享宿主机网络
[root@master pod]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:hostNetwork: truecontainers:- image: busyboxplusname: busyboxcommand: ["/bin/sh","-c","sleep 10000"][root@master pod]# kubectl apply -f testpod.yml
2.5 pod的生命周期
init容器
[root@master ~]# ls
busybox-latest.tar.gz busyboxplus.tar.gz myapp.tar.gz pod test.yml
[root@master ~]# docker tag busybox:latest reg.timinglee.org/library/busybox:latest
[root@master ~]# docker push reg.timinglee.org/library/busybox:latest [root@master pod]# vim init-example.yml
apiVersion: v1
kind: Pod
metadata:labels:run: init-examplename: init-example
spec:containers:- image: myapp:v1name: init-exampleinitContainers:- name: init-myserviceimage: busyboxcommand: ["sh","-c","until test -e /testfile;do echo wating for myservice; sleep 2;done"]
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
init-example 0/1 Init:0/1 0 4m40s
[root@master pod]# kubectl exec pods/init-example -c init-myservice touch /testfile
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
[root@master pod]# kubectl get pods
NAME READY STATUS RESTARTS AGE
init-example 1/1 Running 0 5m56s
探针
存活探针
[root@master pod]# cp init-example.yml liveness-example.yml
[root@master pod]# vim liveness-example.yml
apiVersion: v1
kind: Pod
metadata:labels:run: liveness-examplename: liveness-example
spec:containers:- image: myapp:v1name: liveness-examplelivenessProbe:tcpSocket:port: 8080initialDelaySeconds: 3periodSeconds: 1timeoutSeconds: 1
[root@master pod]# kubectl apply -f liveness-example.yml
[root@master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
liveness-example 0/1 CrashLoopBackOff 3 (15s ago) 48s 10.244.2.17 node2 <none>
[root@master pod]# kubectl delete -f liveness-example.yml[root@master pod]# vim liveness-example.yml
...port: 80
[root@master pod]# kubectl apply -f liveness-example.yml
[root@master pod]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
liveness-example 1/1 Running 0 4s 10.244.2.18 node2 <none> <none>
就绪探针
[root@master pod]# cp init-example.yml readness-example.yml
[root@master pod]# vim readness-example.yml
apiVersion: v1
kind: Pod
metadata:labels:run: readiness-examplename: readiness-example
spec:containers:- image: myapp:v1name: readiness-examplereadinessProbe:httpGet:path: /test.htmlport: 80initialDelaySeconds: 1periodSeconds: 3timeoutSeconds: 1
[root@master pod]# kubectl expose pod readiness-example --port 80 --target-port 80
[root@master pod]# kubectl exec -it pods/readiness-example -c readiness-example -- /bin/sh
/ # vi usr/share/nginx/html/test.html
hello test
3 k8s中的控制器的使用
3.1 控制器简介
什么是控制器
官方文档:
工作负载管理 | Kubernetes
控制器也是管理pod的一种手段
-
自主式pod:pod退出或意外关闭后不会被重新创建
-
控制器管理的 Pod:在控制器的生命周期里,始终要维持 Pod 的副本数目
Pod控制器是管理pod的中间层,使用Pod控制器之后,只需要告诉Pod控制器,想要多少个什么样的Pod就可以了,它会创建出满足条件的Pod并确保每一个Pod资源处于用户期望的目标状态。如果Pod资源在运行中出现故障,它会基于指定策略重新编排Pod
当建立控制器后,会把期望值写入etcd,k8s中的apiserver检索etcd中我们保存的期望状态,并对比pod的当前状态,如果出现差异代码自驱动立即恢复
控制器常用类型
控制器名称 | 控制器用途 |
---|---|
Replication Controller | 比较原始的pod控制器,已经被废弃,由ReplicaSet替代 |
ReplicaSet | ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行 |
Deployment | 一个 Deployment 为 Pod 和 ReplicaSet 提供声明式的更新能力 |
DaemonSet | DaemonSet 确保全指定节点上运行一个 Pod 的副本 |
StatefulSet | StatefulSet 是用来管理有状态应用的工作负载 API 对象。 |
Job | 执行批处理任务,仅执行一次任务,保证任务的一个或多个Pod成功结束 |
CronJob | Cron Job 创建基于时间调度的 Jobs。 |
HPA全称Horizontal Pod Autoscaler | 根据资源利用率自动调整service中Pod数量,实现Pod水平自动缩放 |
3.2 控制器应用
replicaset控制器
replicaset功能
-
ReplicaSet 是下一代的 Replication Controller,官方推荐使用ReplicaSet
-
ReplicaSet和Replication Controller的唯一区别是选择器的支持,ReplicaSet支持新的基于集合的选择器需求
-
ReplicaSet 确保任何时间都有指定数量的 Pod 副本在运行
-
虽然 ReplicaSets 可以独立使用,但今天它主要被Deployments 用作协调 Pod 创建、删除和更新的机制
replicaset参数说明
参数名称 | 字段类型 | 参数说明 |
---|---|---|
spec | Object | 详细定义对象,固定值就写Spec |
spec.replicas | integer | 指定维护pod数量 |
spec.selector | Object | Selector是对pod的标签查询,与pod数量匹配 |
spec.selector.matchLabels | string | 指定Selector查询标签的名称和值,以key:value方式指定 |
spec.template | Object | 指定对pod的描述信息,比如lab标签,运行容器的信息等 |
spec.template.metadata | Object | 指定pod属性 |
spec.template.metadata.labels | string | 指定pod标签 |
spec.template.spec | Object | 详细定义对象 |
spec.template.spec.containers | list | Spec对象的容器列表定义 |
spec.template.spec.containers.name | string | 指定容器名称 |
spec.template.spec.containers.image | string | 指定容器镜像 |
示例:
[root@master controller]# vim replicaset.yml
apiVersion: apps/v1
kind: ReplicaSet
metadata:labels:app: replicasetname: replicaset
spec:replicas: 2selector:matchLabels:app: replicasettemplate:metadata:labels:app: replicasetspec:containers:- image: myapp:v1name: myapp
[root@master controller]# kubectl apply -f replicaset.yml
[root@master controller]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
replicaset-7rb4b 1/1 Running 0 11s 10.244.1.13 node1 <none> <none>
replicaset-nkb2f 1/1 Running 0 11s 10.244.2.19 node2 <none> <none>
deployment控制器
deployment控制器的功能
-
为了更好的解决服务编排的问题,kubernetes在V1.2版本开始,引入了Deployment控制器。
-
Deployment控制器并不直接管理pod,而是通过管理ReplicaSet来间接管理Pod
-
Deployment管理ReplicaSet,ReplicaSet管理Pod
-
Deployment 为 Pod 和 ReplicaSet 提供了一个申明式的定义方法
-
在Deployment中ReplicaSet相当于一个版本
典型的应用场景:
-
用来创建Pod和ReplicaSet
-
滚动更新和回滚
-
扩容和缩容
-
暂停与恢复
示例:
版本迭代
[root@master controller]# vim dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: depname: dep
spec:replicas: 2selector:matchLabels:app: dep# strategy: {}template:metadata:labels:app: depspec:containers:- image: myapp:v1name: myapp
[root@master controller]# kubectl apply -f dep.yml
[root@master controller]# kubectl get all
[root@master controller]# vim dep.yml
...- image: myapp:v1
[root@master controller]# kubectl apply -f dep.yml
root@master controller]# kubectl get all
版本回滚
[root@master controller]# vim dep.yml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: depname: dep
spec:minReadySeconds: 5strategy: rollingUpdate:maxSurge: 1 #比定义pod数量多几个maxUnavailable: 0 #比定义pod个数少几个replicas: 4selector:matchLabels:app: deptemplate:metadata:labels:app: depspec:containers:- image: myapp:v1name: myapp
[root@master controller]# kubectl apply -f dep.yml
deployment.apps/dep created
[root@master controller]# kubectl get all[root@master controller]# vim dep.yml - image: myapp:v1
[root@master controller]# kubectl apply -f dep.yml
[root@master controller]# kubectl get all
暂停及恢复
在实际生产环境中我们做的变更可能不止一处,当修改了一处后,如果执行变更就直接触发了
我们期望的触发时当我们把所有修改都搞定后一次触发
暂停,避免触发不必要的线上更新
kubectl rollout pause deployment deployment-example #暂停更新
kubectl rollout history deployment deployment-example #查看版本有没有更新kubectl rollout resume deployment deployment-example #恢复更新kubectl rollout history deployment deployment-example #有新版本出现
daemonset控制器
daemonset功能
DaemonSet 确保全部(或者某些)节点上运行一个 Pod 的副本。当有节点加入集群时, 也会为他们新增一个 Pod ,当有节点从集群移除时,这些 Pod 也会被回收。删除 DaemonSet 将会删除它创建的所有 Pod
DaemonSet 的典型用法:
-
在每个节点上运行集群存储 DaemonSet,例如 glusterd、ceph。
-
在每个节点上运行日志收集 DaemonSet,例如 fluentd、logstash。
-
在每个节点上运行监控 DaemonSet,例如 Prometheus Node Exporter、zabbix agent等
-
一个简单的用法是在所有的节点上都启动一个 DaemonSet,将被作为每种类型的 daemon 使用
-
一个稍微复杂的用法是单独对每种 daemon 类型使用多个 DaemonSet,但具有不同的标志, 并且对不同硬件类型具有不同的内存、CPU 要求
[root@master controller]# vim daemonset.yml
apiVersion: apps/v1
kind: DaemonSet
metadata:labels:app: daemonsetname: daemonset
spec:selector:matchLabels:app: daemonsettemplate:metadata:labels:app: daemonsetspec:tolerations:- effect: NoScheduleoperator: Existscontainers:- image: myapp:v1name: myapp
[root@master controller]# kubectl apply -f daemonset.yml
[root@master controller]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
daemonset-s6b48 1/1 Running 0 9s 10.244.2.26 node2 <none> <none>
daemonset-vjfmq 1/1 Running 0 9s 10.244.0.4 master <none> <none>
daemonset-wm5sp 1/1 Running 0 9s 10.244.1.20 node1 <none> <none>

job控制器
job控制器功能
Job,主要用于负责批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)任务
Job特点如下:
-
当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量
-
当成功结束的pod达到指定的数量时,Job将完成执行
[!NOTE]
关于重启策略设置的说明:
-
如果指定为OnFailure,则job会在pod出现故障时重启容器
而不是创建pod,failed次数不变
-
如果指定为Never,则job会在pod出现故障时创建新的pod
并且故障pod不会消失,也不会重启,failed次数加1
-
如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了
示例:
[root@master controller]# vim job-example.yml
apiVersion: batch/v1
kind: Job
metadata:name: job-example
spec:completions: 6parallelism: 2backoffLimit: 4template:metadata:spec:restartPolicy: Nevercontainers:- image: perl:5.34.0name: paicommand: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"]
[root@master controller]# cd ~
[root@master ~]# ls
busybox-latest.tar.gz busyboxplus.tar.gz controller myapp.tar.gz perl-5.34.tar.gz pod test.yml
[root@master ~]# docker load -i perl-5.34.tar.gz
[root@master ~]# docker tag perl:5.34.0 reg.timinglee.org/library/perl:5.34.0
[root@master ~]# docker push reg.timinglee.org/library/perl:5.34.0
[root@master controller]# kubectl apply -f job-example.yml
[root@master controller]# kubectl get pods -o wide
cronjob 控制器
cronjob 控制器功能
-
Cron Job 创建基于时间调度的 Jobs。
-
CronJob控制器以Job控制器资源为其管控对象,并借助它管理pod资源对象,
-
CronJob可以以类似于Linux操作系统的周期性任务作业计划的方式控制其运行时间点及重复运行的方式。
-
CronJob可以在特定的时间点(反复的)去运行job任务。
示例:
[root@master controller]# vim cron-example.yml
apiVersion: batch/v1
kind: CronJob
metadata:name: cron-example
spec:schedule: '* * * * *'jobTemplate:metadata:name: cron-examplespec:template:metadata:spec:restartPolicy: OnFailurecontainers:- image: busyboxname: cron-examplecommand:- /bin/sh- -c- date; echo Hello from the Kubernetes cluster[root@master controller]# kubectl apply -f cron-example.yml
#结果表现为一分钟加一个
4 k8s中的微服务
用控制器来完成集群的工作负载,那么应用如何暴漏出去?需要通过微服务暴漏出去后才能被访问
-
Service是一组提供相同服务的Pod对外开放的接口。
-
借助Service,应用可以实现服务发现和负载均衡。
-
service默认只支持4层负载均衡能力,没有7层功能。(可以通过Ingress实现)
微服务的类型
微服务类型 | 作用描述 |
---|---|
ClusterIP | 默认值,k8s系统给service自动分配的虚拟IP,只能在集群内部访问 |
NodePort | 将Service通过指定的Node上的端口暴露给外部,访问任意一个NodeIP:nodePort都将路由到ClusterIP |
LoadBalancer | 在NodePort的基础上,借助cloud provider创建一个外部的负载均衡器,并将请求转发到 NodeIP:NodePort,此模式只能在云服务器上使用 |
ExternalName | 将服务通过 DNS CNAME 记录方式转发到指定的域名(通过 spec.externlName 设定 |
4.1 ipvs模式
-
Service 是由 kube-proxy 组件,加上 iptables 来共同实现的
-
kube-proxy 通过 iptables 处理 Service 的过程,需要在宿主机上设置相当多的 iptables 规则,如果宿主机有大量的Pod,不断刷新iptables规则,会消耗大量的CPU资源
-
IPVS模式的service,可以使K8s集群支持更多量级的Pod
3台主机]# dnf install ipvsadm -y
[root@master controller]# kubectl -n kube-system edit cm kube-proxy
...mode: "ipvs"
...
[root@master controller]# kubectl -n kube-system get pods | awk '/kube-proxy/{system("kubectl -n kube-system delete pods "$1)}'
[root@master controller]# ipvsadm -Ln #结果如图1[root@master controller]# cd ~
[root@master ~]# mkdir service
[root@master ~]# cd service/
[root@master service]# kubectl create deployment myappv1 --image myapp:v1 --replicas 2 --dry-run=client -o yaml > myappv1.yml
[root@master service]# vim myappv1.yml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: myappv1name: myappv1
spec:replicas: 2selector:matchLabels:app: myappv1template:metadata:labels:app: myappv1spec:containers:- image: myapp:v1name: myapp[root@master service]# kubectl expose deployment myappv1 --port 80 --target-port 80 --dry-run=client -o yaml >> myappv1.yml
[root@master service]# vim myappv1.yml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: myappv1name: myappv1
spec:replicas: 2selector:matchLabels:app: myappv1template:metadata:labels:app: myappv1spec:containers:- image: myapp:v1name: myapp
---
apiVersion: v1
kind: Service
metadata:labels:app: myappv1name: myappv1
spec:ports:- port: 80protocol: TCPtargetPort: 80selector:app: myappv1type: ClusterIP
[root@master service]# kubectl apply -f myappv1.yml
4.2 clusterIP
特点:
clusterip模式只能在集群内访问,并对集群内的pod提供健康检测和自动发现功
[root@master service]# vim myappv1.yml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: myappv1name: myappv1
spec:replicas: 2selector:matchLabels:app: myappv1template:metadata:labels:app: myappv1spec:containers:- image: myapp:v1name: myapp
---
apiVersion: v1
kind: Service
metadata:labels:app: myappv1name: myappv1
spec:externalIPs:- 172.25.254.111ports:- port: 80protocol: TCPtargetPort: 80selector:app: myappv1type: ClusterIP
[root@master service]# kubectl apply -f myappv1.yml
[root@master service]# kubectl get svc myappv1
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myappv1 ClusterIP 10.104.103.17 172.25.254.111 80/TCP 18s
ClusterIP中的特
headless(无头服务)
对于无头 Services
并不会分配 Cluster IP,kube-proxy不会处理它们, 而且平台也不会为它们进行负载均衡和路由,集群访问通过dns解析直接指向到业务pod上的IP,所有的调度有dns单独完成
[root@master service]# vim myappv1.yml
apiVersion: apps/v1
...
---
apiVersion: v1
kind: Service
metadata:labels:app: myappv1name: myappv1
spec:ports:- port: 80protocol: TCPtargetPort: 80selector:app: myappv1type: ClusterIPclusterIP: None[root@master service]# kubectl apply -f myappv1.yml
[root@master service]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8h
myappv1 ClusterIP None 172.25.254.111 80/TCP 8s
readiness-example ClusterIP 10.96.232.194 <none> 80/TCP 133m
4.3 Nodeport
通过ipvs暴漏端口从而使外部主机通过master节点的对外ip:<port>来访问pod业务
其访问过程为:
[root@master service]# vim myappv1.yml
apiVersion: v1
kind: Service
metadata:labels:app: myappv1name: myappv1
spec:ports:- port: 80protocol: TCPtargetPort: 80selector:app: myappv1type: NodePort
[root@master service]# kubectl apply -f myappv1.yml
[root@master service]# kubectl describe svc myappv1
...
NodePort: <unset> 32317/TCP
...
[!NOTE]
nodeport默认端口
nodeport默认端口是30000-32767,超出会报错
如果需要使用这个范围以外的端口就需要特殊设定
[root@master service]# vim /etc/kubernetes/manifests/kube-apiserver.yaml
...
- --service-node-port-range=30000-40000
...
[root@master service]# vim myappv1.yml
...
apiVersion: v1
kind: Service
metadata:labels:app: myappv1name: myappv1
spec:ports:- port: 80protocol: TCPtargetPort: 80nodePort: 33333selector:app: myappv1type: NodePort
[root@master service]# kubectl apply -f myappv1.yml [root@master service]# kubectl get svc myappv1
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
myappv1 NodePort 10.99.235.171 <none> 80:33333/TCP 23s
[!NOTE]
添加“--service-node-port-range=“ 参数,端口范围可以自定义
修改后api-server会自动重启,等apiserver正常启动后才能操作集群
集群重启自动完成在修改完参数后全程不需要人为干预
4.4 loadbalancer
云平台会为我们分配vip并实现访问,如果是裸金属主机那么需要metallb来实现ip的分配
metalLB
官网:Installation :: MetalLB, bare metal load-balancer for Kubernetes
功能:为LoadBalancer分配vip
[root@master service]# cd ~
[root@master ~]# ls
busybox-latest.tar.gz configmap.yml metallb-native.yaml myapp.tar.gz pod test.yml
busyboxplus.tar.gz controller metalLB.tag.gz perl-5.34.tar.gz service
[root@master ~]# docker load -i metalLB.tag.gz
[root@master ~]# docker tag quay.io/metallb/controller:v0.14.8 reg.timinglee.org/metallb/controller:v0.14.8
[root@master ~]# docker push reg.timinglee.org/metallb/controller:v0.14.8
[root@master ~]# docker tag quay.io/metallb/speaker:v0.14.8 reg.timinglee.org/metallb/speaker:v0.14.8
[root@master ~]# docker push reg.timinglee.org/metallb/speaker:v0.14.8[root@master ~]# kubectl apply -f metallb-native.yaml
[root@master ~]# kubectl apply -f configmap.yml
[root@master ~]# cd service/
[root@master service]# vim myappv1.yml
...
apiVersion: v1
kind: Service
metadata:labels:app: myappv1name: myappv1
spec:ports:- port: 80protocol: TCPtargetPort: 80selector:app: myappv1type: LoadBalancer
[root@master service]# kubectl apply -f myappv1.yml
[root@master service]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 8h
myappv1 LoadBalancer 10.110.40.75 172.25.254.50 80:36887/TCP 10s
readiness-example ClusterIP 10.96.232.194 <none> 80/TCP 163m
4.5 externalname
-
开启services后,不会被分配IP,而是用dns解析CNAME固定域名来解决ip变化问题
-
一般应用于外部业务和pod沟通或外部业务迁移到pod内时
-
在应用向集群迁移过程中,externalname在过度阶段就可以起作用了。
-
集群外的资源迁移到集群时,在迁移的过程中ip可能会变化,但是域名+dns解析能完美解决此问题
[root@master service]# kubectl create service externalname ext-service --external-name www.baidu.com --dry-run=client -o yaml > ext-service.yml
[root@master service]# vim ext-service.yml
apiVersion: v1
kind: Service
metadata:labels:app: ext-servicename: ext-service
spec:externalName: www.baidu.comselector:app: ext-servicetype: ExternalName
[root@master service]# kubectl apply -f ext-service.yml
4.6 ingress-nginx
官网:
Installation Guide - Ingress-Nginx Controller
-
一种全局的、为了代理不同后端 Service 而设置的负载均衡服务,支持7层
-
Ingress由两部分组成:Ingress controller和Ingress服务
-
Ingress Controller 会根据你定义的 Ingress 对象,提供对应的代理能力。
-
业界常用的各种反向代理项目,比如 Nginx、HAProxy、Envoy、Traefik 等,都已经为Kubernetes 专门维护了对应的 Ingress Controller。
[root@master service]# mkdir ingress
[root@master service]# cd ingress/
[root@master ingress]# ls
ingress-1.13.1.zip
[root@master ingress]# unzip ingress-1.13.1.zip
[root@master ingress]# vim deploy.yaml
...image: ingress-nginx/controller:v1.13.1
...image: ingress-nginx/kube-webhook-certgen:v1.6.1
...image: ingress-nginx/kube-webhook-certgen:v1.6.1[root@master ingress]# docker tag registry.k8s.io/ingress-nginx/kube-webhook-certgen:v1.6.1 reg.timinglee.org/ingress-nginx/kube-webhook-certgen:v1.6.1
[root@master ingress]# docker push reg.timinglee.org/ingress-nginx/kube-webhook-certgen:v1.6.1
[root@master ingress]# docker tag registry.k8s.io/ingress-nginx/controller:v1.13.1 reg.timinglee.org/ingress-nginx/controller:v1.13.1
[root@master ingress]# docker push reg.timinglee.org/ingress-nginx/controller:v1.13.1[root@master ingress]# kubectl apply -f deploy.yaml
[root@master ingress]# kubectl -n ingress-nginx edit svc ingress-nginx-controller
...type: LoadBalancer
ingress基于路径访问
[root@master ingress]# kubectl create deployment myappv1 --image myappv1 --dry-run=client -o yaml > myappv1.yaml
[root@master ingress]# kubectl create service clusterip myappv1 --tcp 80:80 --dry-run=client -o yaml >> myappv1.y
aml
[root@master ingress]# cp myappv1.yaml myappv2.yaml
[root@master ingress]# vim myappv2.yaml [root@master ingress]# kubectl create ingress ingress-test --class nginx --rule="/=myappv1:80" --dry-run=client -o yaml > ingress-test.yml
[root@master ingress]# vim ingress-test.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: ingress-testannotations:nginx.ingress.kubernetes.io/rewrite-target: / #重写到/
spec:ingressClassName: nginxrules:- http:paths:- backend:service:name: myappv1port:number: 80path: /v1pathType: Prefix- backend:service:name: myappv2port:number: 80path: /v2pathType: Prefix[root@master ingress]# kubectl apply -f myappv1.yaml
[root@master ingress]# kubectl apply -f myappv2.yaml
[root@master ingress]# kubectl apply -f ingress-test.yml[root@master ingress]# curl 172.25.254.50/v1
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master ingress]# curl 172.25.254.50/v2
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
ingress基于域名访问
[root@master ingress]# vim /etc/hosts
127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
172.25.254.200 reg.timinglee.org
172.25.254.100 master
172.25.254.10 node1
172.25.254.20 node2
172.25.254.50 myappv1.timinglee.org myappv2.timinglee.org[root@master ingress]# vim host-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: host-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target: /
spec:ingressClassName: nginxrules:- host: myappv1.timinglee.orghttp:paths:- backend:service:name: myappv1port:number: 80path: /pathType: Prefix- host: myappv2.timinglee.orghttp:paths:- backend:service:name: myappv2port:number: 80path: /pathType: Prefix
[root@master ingress]# kubectl apply -f host-ingress.yml[root@master ingress]# curl myappv1.timinglee.org
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master ingress]# curl myappv2.timinglee.org
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
建立tls加密
[root@master ingress]# openssl req -newkey rsa:2048 -nodes -keyout tls.key -x509 -days 365 -subj "/CN=nginxsvc/O=nginxsvc" -out tls.crt
[root@master ingress]# kubectl create secret tls web-tls-secret --key tls.key --cert tls.crt
[root@master ingress]# cp host-ingress.yml tls-ingress.yml
[root@master ingress]# vim tls-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: tls-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target: /
spec:tls:- hosts:- myappv1.timinglee.orgsecretName: web-tls-secretingressClassName: nginxrules:- host: myappv1.timinglee.orghttp:paths:- backend:service:name: myappv1port:number: 80path: /pathType: Prefix
[root@master ingress]# kubectl apply -f tls-ingress.yml
建立auth认证
[root@master ingress]# dnf install httpd-tools -y
[root@master ingress]# htpasswd -cm auth admin
[root@master ingress]# kubectl create secret generic auth-web --from-file auth
[root@master ingress]# cp host-ingress.yml auth-ingress.yml
[root@master ingress]# vim auth-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: auth-ingressannotations:nginx.ingress.kubernetes.io/auth-type: basicnginx.ingress.kubernetes.io/auth-secret: auth-webnginx.ingress.kubernetes.io/auth-realm: "Please input username and password"nginx.ingress.kubernetes.io/rewrite-target: /
spec:ingressClassName: nginxrules:- host: myappv1.timinglee.orghttp:paths:- backend:service:name: myappv1port:number: 80path: /pathType: Prefix
[root@master ingress]# kubectl apply -f auth-ingress.yml
rewrite重定向
[root@master ingress]# cp host-ingress.yml rewrite-ingress.yml
[root@master ingress]# vim rewrite-ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: rewrite-ingressannotations:nginx.ingress.kubernetes.io/rewrite-target: /$2nginx.ingress.kubernetes.io/use-regex: "true"
spec:ingressClassName: nginxrules:- http:paths:- backend:service:name: myappv1port:number: 80path: /(.*)/(.*)pathType: ImplementationSpecific[root@master ingress]# kubectl apply -f rewrite-ingress.yml
ingress.networking.k8s.io/rewrite-ingress created
[root@master ingress]# curl 172.25.254.50/nihao/hostname.html
4.7 Canary金丝雀发布
什么是金丝雀发布
金丝雀发布(Canary Release)也称为灰度发布,是一种软件发布策略。
主要目的是在将新版本的软件全面推广到生产环境之前,先在一小部分用户或服务器上进行测试和验证,以降低因新版本引入重大问题而对整个系统造成的影响。
是一种Pod的发布方式。金丝雀发布采取先添加、再删除的方式,保证Pod的总量不低于期望值。并且在更新部分Pod后,暂停更新,当确认新Pod版本运行正常后再进行其他版本的Pod的更新。
Canary发布方式
基于header(http包头)灰度
-
通过Annotaion扩展
-
创建灰度ingress,配置灰度头部key以及value
-
灰度流量验证完毕后,切换正式ingress到新版本
-
之前我们在做升级时可以通过控制器做滚动更新,默认25%利用header可以使升级更为平滑,通过key 和vule 测试新的业务体系是否有问题。
示例:
[root@master ingress]# vim ingress-test.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: ingress-old
spec:ingressClassName: nginxrules:- http:paths:- backend:service:name: myappv1port:number: 80path: /pathType: Prefix[root@master ingress]# vim ingress-new.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: ingress-newannotations:nginx.ingress.kubernetes.io/canary: "true"nginx.ingress.kubernetes.io/canary-by-header: "timinglee"nginx.ingress.kubernetes.io/canary-by-header-value: "6"
spec:ingressClassName: nginxrules:- http:paths:- backend:service:name: myappv2port:number: 80path: /pathType: Prefix
[root@master ingress]# kubectl apply -f ingress-test.yml
[root@master ingress]# kubectl apply -f ingress-new.yml
[root@master ingress]# curl 172.25.254.50
Hello MyApp | Version: v1 | <a href="hostname.html">Pod Name</a>
[root@master ingress]# curl -H "timinglee:6" 172.25.254.50
Hello MyApp | Version: v2 | <a href="hostname.html">Pod Name</a>
基于权重的灰度发布
-
通过Annotaion拓展
-
创建灰度ingress,配置灰度权重以及总权重
-
灰度流量验证完毕后,切换正式ingress到新版本
示例:
[root@master ingress]# vim ingress-new.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:name: ingress-newannotations:nginx.ingress.kubernetes.io/canary: "true"nginx.ingress.kubernetes.io/canary-weight: "10" #更改权重值nginx.ingress.kubernetes.io/canary-weight-total: "100"
spec:ingressClassName: nginxrules:- http:paths:- backend:service:name: myappv2port:number: 80path: /pathType: Prefix
[root@master ingress]# kubectl apply -f ingress-new.yml
[root@master ingress]# vim check_ingress.sh
#!/bin/bash
v1=0
v2=0for (( i=0; i<100; i++))
doresponse=`curl -s 172.25.254.50 |grep -c v1`v1=`expr $v1 + $response`v2=`expr $v2 + 1 - $response`done
echo "v1:$v1, v2:$v2"
[root@master ingress]# bash check_ingress.sh
v1:89, v2:11
4.8 StatefulSet(学完金丝雀后做的)
功能特性
-
Statefulset是为了管理有状态服务的问提设计的
-
StatefulSet将应用状态抽象成了两种情况:
-
拓扑状态:应用实例必须按照某种顺序启动。新创建的Pod必须和原来Pod的网络标识一样
-
存储状态:应用的多个实例分别绑定了不同存储数据。
-
StatefulSet给所有的Pod进行了编号,编号规则是:$(statefulset名称)-$(序号),从0开始。
-
Pod被删除后重建,重建Pod的网络标识也不会改变,Pod的拓扑状态按照Pod的“名字+编号”的方式固定下来,并且为每个Pod提供了一个固定且唯一的访问入口,Pod对应的DNS记录。
StatefulSet的组成部分
-
Headless Service:用来定义pod网络标识,生成可解析的DNS记录
-
volumeClaimTemplates:创建pvc,指定pvc名称大小,自动创建pvc且pvc由存储类供应。
-
StatefulSet:管理pod的
[root@master controller]# kubectl create deployment web --image myapp:v1 --replicas 2 --dry-run=client -o yaml > web.yml
[root@master controller]# vim web.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:labels:app: webname: web
spec:serviceName: "web"replicas: 2selector:matchLabels:app: webtemplate:metadata:labels:app: webspec:containers:- image: myapp:v1name: myapp[root@master controller]# kubectl create service clusterip web --clusterip="None" --dry-run=client -o yaml > service.yml
[root@master controller]# vim service.yml
apiVersion: v1
kind: Service
metadata:labels:app: webname: web
spec:clusterIP: Noneselector:app: webtype: ClusterIP
[root@master controller]# kubectl apply -f web.yml
[root@master controller]# kubectl apply -f service.yml
5 k8s中的存储
5.1 configmap
configmap的功能
-
configMap用于保存配置数据,以键值对形式存储。
-
configMap 资源提供了向 Pod 注入配置数据的方法。
-
镜像和配置文件解耦,以便实现镜像的可移植性和可复用性。
-
etcd限制了文件大小不能超过1M
configmap的使用场景
-
填充环境变量的值
-
设置容器内的命令行参数
-
填充卷的配置文件
5.2 configmap创建方式
字面值创建
[root@master ~]# kubectl create cm userlist --from-literal name1=user1 --from-literal pass1=lee
通过文件创建
[root@master ~]# echo lee > name1
[root@master ~]# echo lee > pass1
[root@master ~]# kubectl create cm userlist --from-file name1 --from-file pass1
通过目录创建
[root@master ~]# mkdir userlist
[root@master ~]# mv name1 pass1 userlist/
[root@master ~]# kubectl create cm userlist --from-file userlist/
结果和上面一样
通过yaml文件创建
[root@master storage]# kubectl create cm test --from-literal name=lee --from-literal pass=lee --dry-run=client -o yaml > test.yml
[root@master storage]# kubectl apply -f test.yml
结果上面一样
5.3 configmap的使用方式
-
通过环境变量的方式直接传递给pod
-
通过pod的 命令行运行方式
-
作为volume的方式挂载到pod内
使用configmap填充环境变量
[root@master storage]# vim testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:containers:- image: busyboxplusname: testpodcommand:- /bin/sh- -c- envenv:- name: key1value: "123"- name: uservalueFrom:configMapKeyRef:name: userlistkey: namerestartPolicy: Never[root@master storage]# kubectl apply -f testpod.yml
[root@master storage]# cp testpod.yml 2-testpod.yml
[root@master storage]# vim 2-testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:containers:- image: busyboxplusname: testpodcommand:- /bin/sh- -c- envenvFrom:- configMapRef:name: userlistrestartPolicy: Never
[root@master storage]# vim 2-testpod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testpodname: testpod
spec:containers:- image: busyboxplusname: testpodcommand:- /bin/sh- -c- echo ${name} ${pass}envFrom:- configMapRef:name: userlistrestartPolicy: Never
[root@master storage]# kubectl apply -f 2-testpod.yml
[root@master storage]# kubectl logs testpod
lee 111
变量注入企业示例
[root@master storage]# ls
2-testpod.yml centos-7.tar.gz mysql-8.0.tar phpmyadmin-latest.tar.gz testpod.yml test.yml
[root@master storage]# docker load -i mysql-8.0.tar
[root@master storage]# docker tag mysql:8.0 reg.timinglee.org/library/mysql:8.0
[root@master storage]# docker push reg.timinglee.org/library/mysql:8.0
[root@master storage]# docker load -i phpmyadmin-latest.tar.gz
[root@master storage]# docker tag phpmyadmin:latest reg.timinglee.org/library/phpmyadmin:latest
[root@master storage]# docker push reg.timinglee.org/library/phpmyadmin:latest
[root@master storage]# vim test.yml
apiVersion: v1
kind: ConfigMap
metadata:name: phpmyadmin
data:PMA_ARBITRARY: "1"MYSQL_ROOT_PASSWORD: lee
[root@master storage]# kubectl apply -f test.yml[root@master storage]# cp testpod.yml phpmyadmin.yml
[root@master storage]# vim phpmyadmin.yml
apiVersion: v1
kind: Pod
metadata:labels:run: phpmysqladminname: phpmysqladmin
spec:containers:- image: mysql:8.0name: mysqlports:- containerPort: 3306envFrom:- configMapRef:name: phpmyadminimage: phpmyadmin:latestname: phpmyadminports:- containerPort: 80protocol: TCPhostPort: 80envFrom:- configMapRef:name: phpmyadmin[root@master storage]# kubectl apply -f phpmyadmin.yml
利用微服务暴露项目多端口
[root@master storage]# kubectl expose pod phpmysqladmin --port 80 --target-port 80 --dry-run=client -o yaml >> phpmyadmin.yml
[root@master storage]# vim phpmyadmin.yml
---
apiVersion: v1
kind: Service
metadata:labels:run: phpmysqladminname: phpmysqladmin
spec:ports:- port: 80name: phpmyadminprotocol: TCPtargetPort: 80- port: 3306name: mysqlprotocol: TCPtargetPort: 3306selector:run: phpmysqladmintype: LoadBalancer
[root@master storage]# kubectl apply -f phpmyadmin.yml #harbor主机
[root@reg harbor]# dnf install mariadb -y
[root@reg harbor]# mysql -uroot -p -h172.25.254.50
Enter password:
通过数据卷使用configmap
示例1
[root@master storage]# kubectl create cm testfile --from-literal file1=lee --dry-run=client -o yaml > file.yml
[root@master storage]# vim file.yml
apiVersion: v1
data:file1: leefile2: timinglee
kind: ConfigMap
metadata:name: testfile
[root@master storage]# kubectl apply -f file.yml[root@master storage]# kubectl run filepod --image busyboxplus --dry-run=client -o yaml > filepod.yml
[root@master storage]# vim filepod.yml
apiVersion: v1
kind: Pod
metadata:labels:run: filepodname: filepod
spec:containers:- image: busyboxplusname: filepodcommand:- /bin/sh- -c- sleep 100000volumeMounts:- name: filenamemountPath: /configrestartPolicy: Alwaysvolumes:- name: filenameconfigMap:name: testfile
[root@master storage]# kubectl apply -f filepod.yml
pod/filepod created
[root@master storage]# kubectl exec -it filepod -- /bin/sh
/ # ls config/
file1 file2
/ # cat config/file1
lee/ #
示例2
[root@master storage]# vim nginx.conf
server {listen 8080;root /usr/share/nginx/html;index index.html;
}
[root@master storage]# kubectl create cm nginxcfg --from-file nginx.conf
[root@master storage]# cp filepod.yml nginx.yml
[root@master storage]# vim nginx.yml
apiVersion: v1
kind: Pod
metadata:labels:run: nginxname: nginx
spec:containers:- image: nginx:latestname: nginxvolumeMounts:- name: nginxcfgmountPath: /etc/nginx/conf.drestartPolicy: Alwaysvolumes:- name: nginxcfgconfigMap:name: nginxcfg
[root@master storage]# kubectl apply -f nginx.yml
通过热更新cm修改配置
[root@master storage]# kubectl create deployment webserver --image nginx --replicas 1 --dry-run=client -o yaml > dp-nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: webservername: webserver
spec:replicas: 1selector:matchLabels:app: webservertemplate:metadata:labels:app: webserverspec:containers:- image: nginxname: nginxvolumeMounts:- name: nginxcfgmountPath: /etc/nginx/conf.dvolumes:- name: nginxcfgconfigMap:name: nginxcfg
[root@master storage]# kubectl apply -f dp-nginx.yml
[root@master storage]# kubectl edit cm nginxcfg #8080改成80
nginx.conf: "server {\n\tlisten 80;\n\troot /usr/share/nginx/html;\n\tindex index.html;\n}\n"
5.4 secrets的功能介绍
-
Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 ssh key。
-
敏感信息放在 secret 中比放在 Pod 的定义或者容器镜像中来说更加安全和灵活
-
Pod 可以用两种方式使用 secret:
-
作为 volume 中的文件被挂载到 pod 中的一个或者多个容器里。
-
当 kubelet 为 pod 拉取镜像时使用。
-
-
Secret的类型:
-
Service Account:Kubernetes 自动创建包含访问 API 凭据的 secret,并自动修改 pod 以使用此类型的 secret。
-
Opaque:使用base64编码存储信息,可以通过base64 --decode解码获得原始数据,因此安全性弱。
-
kubernetes.io/dockerconfigjson:用于存储docker registry的认证信息
-
5.5 secrets的创建与应用
在创建secrets时我们可以用命令的方法或者yaml文件的方法
从文件创建
[root@master storage]# echo -n timinglee > username.txt
[root@master storage]# echo -n lee> password.txt
[root@master storage]# kubectl create secret generic testsc --from-file username.txt --from-file password.txt
编写yaml文件
[root@master storage]# kubectl create secret generic testsc --from-file username.txt --from-file password.txt --dry-run=client -o yaml > testsc.yml
[root@master storage]# vim testsc.yml
apiVersion: v1
data:password.txt: bGVlusername.txt: dGltaW5nbGVl
kind: Secret
metadata:creationTimestamp: nullname: testsc
[root@master storage]# kubectl apply -f testsc.yml #可通过这个方式获得自定义的值
[root@master storage]# echo -n "root" | base64
cm9vdA==
结果和上面一样
通过sc注入变量
[root@master storage]# kubectl run pod-sc --image busybox --dry-run=client -o yaml > pod-sc.yml
[root@master storage]# vim pod-sc.yml
apiVersion: v1
kind: Pod
metadata:labels:run: pod-scname: pod-sc
spec:containers:- image: busyboxname: pod-sccommand:- /bin/sh- -c- envenv:- name: db_uservalueFrom:secretKeyRef:name: testsckey: username.txtrestartPolicy: Never
[root@master storage]# kubectl apply -f pod-sc.yml
secret以文件方式注入pod
[root@master storage]# vim pod-sc.yml
apiVersion: v1
kind: Pod
metadata:labels:run: pod-scname: pod-sc
spec:containers:- image: busyboxname: pod-sccommand:- /bin/sh- -c- sleep 10000volumeMounts:- name: testscmountPath: /secretreadOnly: truerestartPolicy: Nevervolumes:- name: testscsecret:secretName: testsc
[root@master storage]# kubectl apply -f pod-sc.yml
[root@master storage]# kubectl exec -it pods/pod-sc -- /bin/sh
secret输入容器仓库认证
[root@master storage]# kubectl create secret docker-registry docker-auth --docker-server reg.timinglee.org --docker-username admin --docker-password lee --docker-email timinglee@timinglee.org
[root@master storage]# docker tag myapp:v1 reg.timinglee.org/test/myapp:v1
[root@master storage]# docker push reg.timinglee.org/test/myapp:v1[root@master storage]# vim pod-sc.yml
apiVersion: v1
kind: Pod
metadata:labels:run: pod-scname: pod-sc
spec:restartPolicy: Nevercontainers:- image: reg.timinglee.org/test/myapp:v1name: pod-scimagePullSecrets:- name: docker-auth#或者
[root@master storage]# kubectl edit sa default
apiVersion: v1
kind: ServiceAccount
imagePullSecrets:- name: docker-puth
metadata:creationTimestamp: "2025-08-20T06:41:46Z"name: defaultnamespace: defaultresourceVersion: "340"uid: 1d8b9486-ef40-4b60-bcdd-eb9c177d1b8e
5.6 volumes配置管理
-
容器中文件在磁盘上是临时存放的,这给容器中运行的特殊应用程序带来一些问题
-
当容器崩溃时,kubelet将重新启动容器,容器中的文件将会丢失,因为容器会以干净的状态重建。
-
当在一个 Pod 中同时运行多个容器时,常常需要在这些容器之间共享文件。
-
Kubernetes 卷具有明确的生命周期与使用它的 Pod 相同
-
卷比 Pod 中运行的任何容器的存活期都长,在容器重新启动时数据也会得到保留
-
当一个 Pod 不再存在时,卷也将不再存在。
-
Kubernetes 可以支持许多类型的卷,Pod 也能同时使用任意数量的卷。
-
卷不能挂载到其他卷,也不能与其他卷有硬链接。 Pod 中的每个容器必须独立地指定每个卷的挂载位置。
官网:卷 | Kubernetes
k8s支持的卷的类型如下:
-
awsElasticBlockStore 、azureDisk、azureFile、cephfs、cinder、configMap、csi
-
downwardAPI、emptyDir、fc (fibre channel)、flexVolume、flocker
-
gcePersistentDisk、gitRepo (deprecated)、glusterfs、hostPath、iscsi、local、
-
nfs、persistentVolumeClaim、projected、portworxVolume、quobyte、rbd
-
scaleIO、secret、storageos、vsphereVolume
5.7 emptyDir卷
功能:
当Pod指定到某个节点上时,首先创建的是一个emptyDir卷,并且只要 Pod 在该节点上运行,卷就一直存在。卷最初是空的。 尽管 Pod 中的容器挂载 emptyDir 卷的路径可能相同也可能不同,但是这些容器都可以读写 emptyDir 卷中相同的文件。 当 Pod 因为某些原因被从节点上删除时,emptyDir 卷中的数据也会永久删除
emptyDir 的使用场景:
-
缓存空间,例如基于磁盘的归并排序。
-
耗时较长的计算任务提供检查点,以便任务能方便地从崩溃前状态恢复执行。
-
在 Web 服务器容器服务数据时,保存内容管理器容器获取的文件。
示例:
[root@master ~]# mkdir volumes
[root@master ~]# cd volumes/
[root@master volumes]# vim pod1.yml
apiVersion: v1
kind: Pod
metadata:name: vol1
spec:containers:- image: busyboxplus:latestname: vm1command:- /bin/sh- -c- sleep 30000000volumeMounts:- mountPath: /cachename: cache-vol- image: nginx:latestname: vm2volumeMounts:- mountPath: /usr/share/nginx/htmlname: cache-volvolumes:- name: cache-volemptyDir:medium: MemorysizeLimit: 100Mi
[root@master volumes]# kubectl apply -f pod1.yml
5.8 hostpath卷
[root@master volumes]# vim hostpath.yml
apiVersion: v1
kind: Pod
metadata:labels:run: hostpathname: hostpath
spec:containers:- image: busyboxplus:latestname: busyboxcommand:- /bin/sh- -c- sleep 30000000volumeMounts:- mountPath: /dataname: leevolumes:- name: leehostPath:path: /pod-datetype: DirectoryOrCreate #没有目录则建立
[root@master volumes]# kubectl apply -f hostpath.yml
pod/hostpath created
[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hostpath 1/1 Running 0 8s 10.244.2.27 node2 <none> <none>#node2主机
[root@node2 ~]# cd /pod-date/
[root@node2 pod-date]# ls
[root@node2 pod-date]# touch file1 file2
[root@node2 pod-date]# ls
file1 file2#master主机
[root@master volumes]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
hostpath 1/1 Running 0 8s 10.244.2.27 node2 <none> <none>
[root@master volumes]# kubectl exec -it pods/hostpath -- /bin/sh
E0821 16:13:18.954518 226661 websocket.go:296] Unknown stream id 1, discarding message
/ # ls /data
file1 file2
5.9 nfs卷
#harbor主机
[root@reg ~]# mkdir /pod_data
[root@reg ~]# cd pod_data/
[root@reg pod_data]# dnf install nfs-utils -y
[root@reg pod_data]# systemctl enable --now nfs-server.service
[root@reg pod_data]# vim /etc/exports
/pod_data *(rw,no_root_squash,sync)
[root@reg ~]# exportfs -rv#harbor主机
[root@reg ~]# cd /pod_data/
[root@reg pod_data]# echo heello lee > index.html#3台主机
3]# dnf install nfs-utils -y#master主机
[root@master volumes]# cp hostpath.yml nfs.yml
[root@master volumes]# vim nfs.yml
apiVersion: v1
kind: Pod
metadata:labels:run: nfsname: nfs
spec:containers:- image: nginxname: web1volumeMounts:- mountPath: /usr/share/nginx/htmlname: leevolumes:- name: leenfs:server: 172.25.254.200path: /pod_data
[root@master volumes]# cp nfs.yml 1-nfs.yml
[root@master volumes]# vim 1-nfs.yml
apiVersion: v1
kind: Pod
metadata:labels:run: nfs2name: nfs2
...[root@master volumes]# kubectl apply -f nfs.yml
[root@master volumes]# kubectl apply -f 1-nfs.yml
[root@master volumes]# kubectl exec -it pods/nfs /bin/bash #刚在harbor主机pod_data目录加入文件时,输入该指令可刷新
5.10 静态持久卷pv与静态持久卷声明pvc
PersistentVolume(持久卷,简称PV)
-
pv是集群内由管理员提供的网络存储的一部分。
-
PV也是集群中的一种资源。是一种volume插件,
-
但是它的生命周期却是和使用它的Pod相互独立的。
-
PV这个API对象,捕获了诸如NFS、ISCSI、或其他云存储系统的实现细节
-
pv有两种提供方式:静态和动态
-
静态PV:集群管理员创建多个PV,它们携带着真实存储的详细信息,它们存在于Kubernetes API中,并可用于存储使用
-
动态PV:当管理员创建的静态PV都不匹配用户的PVC时,集群可能会尝试专门地供给volume给PVC。这种供给基于StorageClass
-
PersistentVolumeClaim(持久卷声明,简称PVC)
-
是用户的一种存储请求
-
它和Pod类似,Pod消耗Node资源,而PVC消耗PV资源
-
Pod能够请求特定的资源(如CPU和内存)。PVC能够请求指定的大小和访问的模式持久卷配置
-
PVC与PV的绑定是一对一的映射。没找到匹配的PV,那么PVC会无限期得处于unbound未绑定状态
volumes访问模式
-
ReadWriteOnce -- 该volume只能被单个节点以读写的方式映射
-
ReadOnlyMany -- 该volume可以被多个节点以只读方式映射
-
ReadWriteMany -- 该volume可以被多个节点以读写的方式映射
-
在命令行中,访问模式可以简写为:
-
RWO - ReadWriteOnce
-
ROX - ReadOnlyMany
-
RWX – ReadWriteMany
-
volumes回收策略
-
Retain:保留,需要手动回收
-
Recycle:回收,自动删除卷中数据(在当前版本中已经废弃)
-
Delete:删除,相关联的存储资产,如AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷都会被删除
注意:
[!NOTE]
只有NFS和HostPath支持回收利用
AWS EBS,GCE PD,Azure Disk,or OpenStack Cinder卷支持删除操作。
volumes状态说明
-
Available 卷是一个空闲资源,尚未绑定到任何申领
-
Bound 该卷已经绑定到某申领
-
Released 所绑定的申领已被删除,但是关联存储资源尚未被集群回收
-
Failed 卷的自动回收操作失败
5.11 静态持久卷的使用
#harbor主机
[root@reg harbor]# cd /pod_data/
[root@reg pod_data]# mkdir pv{1..3}
[root@reg pod_data]# ls
index.html pv1 pv2 pv3[root@master volumes]# vim pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:name: pv1
spec:capacity:storage: 5GivolumeMode: FilesystemaccessModes:- ReadWriteOncepersistentVolumeReclaimPolicy: RetainstorageClassName: nfsnfs:path: /pod_data/pv1server: 172.25.254.200
---
...name: pv2storage: 10Gi- ReadOnlyManypath: /pod_data/pv2
...
---
...name: pv3storage: 15Gi- ReadOnlyManypath: /pod_data/pv3
...
[root@master volumes]# kubectl apply -f pv.yml [root@master volumes]# vim pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: pvc1
spec:storageClassName: nfsaccessModes:- ReadWriteOnceresources:requests:storage: 1Gi
---
...name: pvc2- ReadOnlyManystorage: 5Gi
...
---
...name: pvc3- ReadOnlyManystorage: 10Gi
...[root@master volumes]# kubectl apply -f pvc
[root@master volumes]# kubectl run pv-test --image nginx --dry-run=client -o yaml > pv-test.yml
[root@master volumes]# vim pv-test.yml
apiVersion: v1
kind: Pod
metadata:labels:run: pv-testname: pv-test
spec:containers:- image: nginxname: pv-testvolumeMounts:- name: lee1mountPath: /usr/share/nginx/htmlvolumes:- name: lee1persistentVolumeClaim:claimName: pvc1 #使用pvc1
[root@master volumes]# kubectl apply -f pv-test.yml#harbor主机
[root@reg pod_data]# echo hellowww > pv1/index.html
5.12 存储类storageclass介绍
StorageClass说明
-
StorageClass提供了一种描述存储类(class)的方法,不同的class可能会映射到不同的服务质量等级和备份策略或其他策略等。
-
每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在StorageClass需要动态分配 PersistentVolume 时会使用到
StorageClass的属性
属性说明:存储类 | Kubernetes
Provisioner(存储分配器):用来决定使用哪个卷插件分配 PV,该字段必须指定。可以指定内部分配器,也可以指定外部分配器。外部分配器的代码地址为: kubernetes-incubator/external-storage,其中包括NFS和Ceph等。
Reclaim Policy(回收策略):通过reclaimPolicy字段指定创建的Persistent Volume的回收策略,回收策略包括:Delete 或者 Retain,没有指定默认为Delete。
存储分配器NFS Client Provisioner
源码地址:https://github.com/kubernetes-sigs/nfs-subdir-external-provisioner
-
NFS Client Provisioner是一个automatic provisioner,使用NFS作为存储,自动创建PV和对应的PVC,本身不提供NFS存储,需要外部先有一套NFS存储服务。
-
PV以 ${namespace}-${pvcName}-${pvName}的命名格式提供(在NFS服务器上)
-
PV回收的时候以 archieved-${namespace}-${pvcName}-${pvName} 的命名格式(在NFS服务器上)
5.13 部署NFS Client Provisioner
创建sa并授权、部署应用
[root@master volumes]# vim rbac.yml
apiVersion: v1
kind: Namespace
metadata:name: nfs-client-provisioner
---
apiVersion: v1
kind: ServiceAccount
metadata:name: nfs-client-provisionernamespace: nfs-client-provisioner
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: nfs-client-provisioner-runner
rules:- apiGroups: [""]resources: ["nodes"]verbs: ["get", "list", "watch"]- 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
subjects:- kind: ServiceAccountname: nfs-client-provisionernamespace: nfs-client-provisioner
roleRef:kind: ClusterRolename: nfs-client-provisioner-runnerapiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:name: leader-locking-nfs-client-provisionernamespace: nfs-client-provisioner
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-provisionernamespace: nfs-client-provisioner
subjects:- kind: ServiceAccountname: nfs-client-provisionernamespace: nfs-client-provisioner
roleRef:kind: Rolename: leader-locking-nfs-client-provisionerapiGroup: rbac.authorization.k8s.io[root@master volumes]# vim deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:name: nfs-client-provisionerlabels:app: nfs-client-provisionernamespace: nfs-client-provisioner
spec:replicas: 1strategy:type: Recreateselector:matchLabels:app: nfs-client-provisionertemplate:metadata:labels:app: nfs-client-provisionerspec:serviceAccountName: nfs-client-provisionercontainers:- name: nfs-client-provisionerimage: sig-storage/nfs-subdir-external-provisioner:v4.0.2volumeMounts:- name: nfs-client-rootmountPath: /persistentvolumesenv:- name: PROVISIONER_NAMEvalue: k8s-sigs.io/nfs-subdir-external-provisioner- name: NFS_SERVERvalue: 172.25.254.200- name: NFS_PATHvalue: /pod_datavolumes:- name: nfs-client-rootnfs:server: 172.25.254.200path: /pod_data
[root@master volumes]# docker load -i ~/nfs-subdir-external-provisioner-4.0.2.tar
[root@master volumes]# docker tag registry.k8s.io/sig-storage/nfs-subdir-external-provisioner:v4.0.2 reg.timinglee.org/sig-storage/nfs-subdir-external-provisioner:v4.0.2[root@master volumes]# docker push reg.timinglee.org/sig-storage/nfs-subdir-external-provisioner:v4.0.2
The push refers to repository [reg.timinglee.org/sig-storage/nfs-subdir-external-provisioner]
[root@master volumes]# kubectl apply -f deployment.yml
[root@master volumes]# kubectl -n nfs-client-provisioner get deployments.apps nfs-client-provisioner
NAME READY UP-TO-DATE AVAILABLE AGE
nfs-client-provisioner 1/1 1 1 25s
创建存储类、pvc、测试
[root@master volumes]# vim storageclass.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:archiveOnDelete: "false"[root@master volumes]# vim classpvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: test-claim
spec:storageClassName: nfs-clientaccessModes:- ReadWriteManyresources:requests:storage: 1G
[root@master volumes]# kubectl apply -f storageclass.yml
[root@master volumes]# kubectl apply -f storageclass.yml
默认存储类
-
在未设定默认存储类时pvc必须指定使用类的名称
-
在设定存储类后创建pvc时可以不用指定storageClassName
[root@master volumes]# kubectl edit sc nfs-client
存储类企业级示例
[root@master volumes]# kubectl create service clusterip statefulset --clusterip=None --dry-run=client -o yaml > headless.yml
[root@master volumes]# vim headless.yml
apiVersion: v1
kind: Service
metadata:labels:app: statefulsetname: statefulset
spec:clusterIP: Noneselector:app: statefulsettype: ClusterIP[root@master volumes]# kubectl apply -f headless.yml
[root@master volumes]# kubectl create deployment statefulset --image nginx --dry-run=client -o yaml > statefulset.yml[root@master volumes]# vim statefulset.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:labels:app: statefulsetname: statefulset
spec:replicas: 2serviceName: "statefulset"selector:matchLabels:app: statefulsettemplate:metadata:labels:app: statefulsetspec:containers:- image: nginxname: nginxvolumeMounts:- name: wwwmountPath: /usr/share/nginx/htmlvolumeClaimTemplates:- metadata:name: wwwspec:storageClassName: nfs-clientaccessModes:- ReadWriteOnceresources:requests:storage: 1Gi#harbor主机
[root@reg pod_data]# echo web1 > default-www-statefulset-0-pvc-40ab6ccc-f620-44a1-b584-5910750b748b/index.html
[root@reg pod_data]# echo web2 > default-www-statefulset-1-pvc-79343111-1e39-4bf5-9260-05f66ce2534b/index.html
[root@reg pod_data]# echo web3 > default-www-statefulset-2-pvc-db17687c-7c60-4d41-ae88-515117ac49a3/index.html
[root@reg pod_data]# echo web4 > default-www-statefulset-3-pvc-a6f66a0f-dd7f-430f-8654-dfa87a57ea51/index.html#master主机
[root@master volumes]# vim /etc/resolv.conf
nameserver 10.96.0.10
nameserver 8.8.8.8[root@master volumes]# kubectl apply -f statefulset.yml ###pvc的回收
[root@master volumes]# kubectl scale statefulset statefulset --replicas 0
statefulset.apps/statefulset scaled
[root@master volumes]# kubectl delete pvc --all
persistentvolumeclaim "www-statefulset-0" deleted
persistentvolumeclaim "www-statefulset-1" deleted
persistentvolumeclaim "www-statefulset-2" deleted
persistentvolumeclaim "www-statefulset-3" deleted
6 k8s下的网络通信与调度
6.1 k8s网络通信和flannel网络插件
简介
k8s通信整体架构
-
k8s通过CNI接口接入其他插件来实现网络通讯。目前比较流行的插件有flannel,calico等
-
CNI插件存放位置:# cat /etc/cni/net.d/10-flannel.conflist
-
插件使用的解决方案如下
-
虚拟网桥,虚拟网卡,多个容器共用一个虚拟网卡进行通信。
-
多路复用:MacVLAN,多个容器共用一个物理网卡进行通信。
-
硬件交换:SR-LOV,一个物理网卡可以虚拟出多个接口,这个性能最好。
-
-
容器间通信:
-
同一个pod内的多个容器间的通信,通过lo即可实现pod之间的通信
-
同一节点的pod之间通过cni网桥转发数据包。
-
不同节点的pod之间的通信需要网络插件支持
-
-
pod和service通信: 通过iptables或ipvs实现通信,ipvs取代不了iptables,因为ipvs只能做负载均衡,而做不了nat转换
-
pod和外网通信:iptables的MASQUERADE
-
Service与集群外部客户端的通信;(ingress、nodeport、loadbalancer)
flannel网络插件
插件组成:
插件 | 功能 |
---|---|
VXLAN | 即Virtual Extensible LAN(虚拟可扩展局域网),是Linux本身支持的一网种网络虚拟化技术。VXLAN可以完全在内核态实现封装和解封装工作,从而通过“隧道”机制,构建出覆盖网络(Overlay Network) |
VTEP | VXLAN Tunnel End Point(虚拟隧道端点),在Flannel中 VNI的默认值是1,这也是为什么宿主机的VTEP设备都叫flannel.1的原因 |
Cni0 | 网桥设备,每创建一个pod都会创建一对 veth pair。其中一端是pod中的eth0,另一端是Cni0网桥中的端口(网卡) |
Flannel.1 | TUN设备(虚拟网卡),用来进行 vxlan 报文的处理(封包和解包)。不同node之间的pod数据流量都从overlay设备以隧道的形式发送到对端 |
Flanneld | flannel在每个主机中运行flanneld作为agent,它会为所在主机从集群的网络地址空间中,获取一个小的网段subnet,本主机内所有容器的IP地址都将从中分配。同时Flanneld监听K8s集群数据库,为flannel.1设备提供封装数据时必要的mac、ip等网络数据信息 |
flannel跨主机通信原理
-
当容器发送IP包,通过veth pair 发往cni网桥,再路由到本机的flannel.1设备进行处理。
-
VTEP设备之间通过二层数据帧进行通信,源VTEP设备收到原始IP包后,在上面加上一个目的MAC地址,封装成一个内部数据帧,发送给目的VTEP设备。
-
内部数据桢,并不能在宿主机的二层网络传输,Linux内核还需要把它进一步封装成为宿主机的一个普通的数据帧,承载着内部数据帧通过宿主机的eth0进行传输。
-
Linux会在内部数据帧前面,加上一个VXLAN头,VXLAN头里有一个重要的标志叫VNI,它是VTEP识别某个数据桢是不是应该归自己处理的重要标识。
-
flannel.1设备只知道另一端flannel.1设备的MAC地址,却不知道对应的宿主机地址是什么。在linux内核里面,网络设备进行转发的依据,来自FDB的转发数据库,这个flannel.1网桥对应的FDB信息,是由flanneld进程维护的。
-
linux内核在IP包前面再加上二层数据帧头,把目标节点的MAC地址填进去,MAC地址从宿主机的ARP表获取。
-
此时flannel.1设备就可以把这个数据帧从eth0发出去,再经过宿主机网络来到目标节点的eth0设备。目标主机内核网络栈会发现这个数据帧有VXLAN Header,并且VNI为1,Linux内核会对它进行拆包,拿到内部数据帧,根据VNI的值,交给本机flannel.1设备处理,flannel.1拆包,根据路由表发往cni网桥,最后到达目标容器。
#默认网络通信路由
]# ip r
#桥接转发数据库
]# bridge fdb
#arp列表
]# arp -n
flannel支持的后端模式
网络模式 | 功能 |
---|---|
vxlan | 报文封装,默认模式 |
Directrouting | 直接路由,跨网段使用vxlan,同网段使用host-gw模式 |
host-gw | 主机网关,性能好,但只能在二层网络中,不支持跨网络 如果有成千上万的Pod,容易产生广播风暴,不推荐 |
UDP | 性能差,不推荐 |
更改flannel的默认模式
[root@master volumes]# kubectl -n kube-flannel edit cm kube-flannel-cfg
..."Backend": {"Type": "host-gw" #更改内容}
...
[root@master volumes]# kubectl -n kube-flannel delete pod --all
6.2 calico网络插件
简介
官网:
Installing on on-premises deployments | Calico Documentation
calico简介
-
纯三层的转发,中间没有任何的NAT和overlay,转发效率最好。
-
Calico 仅依赖三层路由可达。Calico 较少的依赖性使它能适配所有 VM、Container、白盒或者混合环境场景。
calico网络架构
-
Felix:监听ECTD中心的存储获取事件,用户创建pod后,Felix负责将其网卡、IP、MAC都设置好,然后在内核的路由表里面写一条,注明这个IP应该到这张网卡。同样如果用户制定了隔离策略,Felix同样会将该策略创建到ACL中,以实现隔离。
-
BIRD:一个标准的路由程序,它会从内核里面获取哪一些IP的路由发生了变化,然后通过标准BGP的路由协议扩散到整个其他的宿主机上,让外界都知道这个IP在这里,路由的时候到这里
部署calico
[root@master volumes]# cd ~
[root@master ~]# mkdir network
[root@master ~]# cd network/
[root@master network]# ls
kube-flannel.yml
[root@master network]# kubectl delete -f kube-flannel.yml
[root@master network]# ssh -l root 172.25.254.10 rm -rf /etc/cni/net.d/10-flannel.conflist [root@master network]# ssh -l root 172.25.254.20 rm -rf /etc/cni/net.d/10-flannel.conflist
[root@master network]# ls
calico-3.28.1.tar calico.yaml kube-flannel.yml[root@master network]# docker load -i calico-3.28.1.tar [root@master network]# docker tag calico/cni:v3.28.1 reg.timinglee.org/calico/cni:v3.28.1
[root@master network]# docker tag calico/node:v3.28.1 reg.timinglee.org/calico/node:v3.28.1
[root@master network]# docker tag calico/kube-controllers:v3.28.1 reg.timinglee.org/calico/kube-controllers:v3.28.1
[root@master network]# docker tag calico/typha:v3.28.1 reg.timinglee.org/calico/typha:v3.28.1
[root@master network]# docker push reg.timinglee.org/calico/cni:v3.28.1
[root@master network]# docker push reg.timinglee.org/calico/node:v3.28.1
[root@master network]# docker push reg.timinglee.org/calico/kube-controllers:v3.28.1
[root@master network]# docker push reg.timinglee.org/calico/typha:v3.28.1
[root@master network]# vim calico.yaml
4835 image: calico/cni:v3.28.1
4835 image: calico/cni:v3.28.1
4906 image: calico/node:v3.28.1
4932 image: calico/node:v3.28.1
5160 image: calico/kube-controllers:v3.28.1
5249 - image: calico/typha:v3.28.14970 - name: CALICO_IPV4POOL_IPIP
4971 value: "Never"4999 - name: CALICO_IPV4POOL_CIDR
5000 value: "10.244.0.0/16"
5001 - name: CALICO_AUTODETECTION_METHOD
5002 value: "interface=eth0"
[root@master network]# kubectl apply -f calico.yaml
6.3 k8s调度(Scheduling)
简介
调度在Kubernetes中的作用
-
调度是指将未调度的Pod自动分配到集群中的节点的过程
-
调度器通过 kubernetes 的 watch 机制来发现集群中新创建且尚未被调度到 Node 上的 Pod
-
调度器会将发现的每一个未调度的 Pod 调度到一个合适的 Node 上来运行
调度原理
-
创建Pod
-
用户通过Kubernetes API创建Pod对象,并在其中指定Pod的资源需求、容器镜像等信息。
-
-
调度器监视Pod
-
Kubernetes调度器监视集群中的未调度Pod对象,并为其选择最佳的节点。
-
-
选择节点
-
调度器通过算法选择最佳的节点,并将Pod绑定到该节点上。调度器选择节点的依据包括节点的资源使用情况、Pod的资源需求、亲和性和反亲和性等。
-
-
绑定Pod到节点
-
调度器将Pod和节点之间的绑定信息保存在etcd数据库中,以便节点可以获取Pod的调度信息。
-
-
节点启动Pod
-
节点定期检查etcd数据库中的Pod调度信息,并启动相应的Pod。如果节点故障或资源不足,调度器会重新调度Pod,并将其绑定到其他节点上运行。
-
调度器种类
-
默认调度器(Default Scheduler):
-
是Kubernetes中的默认调度器,负责对新创建的Pod进行调度,并将Pod调度到合适的节点上。
-
-
自定义调度器(Custom Scheduler):
-
是一种自定义的调度器实现,可以根据实际需求来定义调度策略和规则,以实现更灵活和多样化的调度功能。
-
-
扩展调度器(Extended Scheduler):
-
是一种支持调度器扩展器的调度器实现,可以通过调度器扩展器来添加自定义的调度规则和策略,以实现更灵活和多样化的调度功能。
-
-
kube-scheduler是kubernetes中的默认调度器,在kubernetes运行后会自动在控制节点运行
nodename调度方法
-
nodeName 是节点选择约束的最简单方法,但一般不推荐
-
如果 nodeName 在 PodSpec 中指定了,则它优先于其他的节点选择方法
-
使用 nodeName 来选择节点的一些限制
-
如果指定的节点不存在。
-
如果指定的节点没有资源来容纳 pod,则pod 调度失败。
-
云环境中的节点名称并非总是可预测或稳定的
-
[root@master ~]# mkdir Scheduler
[root@master ~]# cd Scheduler/
[root@master Scheduler]# kubectl run test --image nginx --dry-run=client -o yaml > test.yml
[root@master Scheduler]# vim test.yml
[root@master Scheduler]# vim test.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testname: test
spec:nodeName: node1containers:- image: nginxname: test
[root@master Scheduler]# kubectl apply -f test.yml
nodeselector(通过标签控制节点)
[root@master Scheduler]# vim test.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testname: test
spec:nodeSelector:lab: timingleecontainers:- image: nginxname: test
[root@master Scheduler]# kubectl apply -f test.yml
[root@master Scheduler]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 0/1 Pending 0 12s <none> <none> <none> <none>[root@master Scheduler]# kubectl label nodes node1 lab=timinglee
[root@master Scheduler]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
test 1/1 Running 0 64s 10.244.166.131 node1 <none> <none>###去除标签
[root@master Scheduler]# kubectl label nodes node1 lab-
affinity(亲和性)
官方文档 :
将 Pod 指派给节点 | Kubernetes
亲和与反亲和
-
nodeSelector 提供了一种非常简单的方法来将 pod 约束到具有特定标签的节点上。亲和/反亲和功能极大地扩展了你可以表达约束的类型。
-
使用节点上的 pod 的标签来约束,而不是使用节点本身的标签,来允许哪些 pod 可以或者不可以被放置在一起。
nodeAffinity节点亲和
-
那个节点服务指定条件就在那个节点运行
-
requiredDuringSchedulingIgnoredDuringExecution 必须满足,但不会影响已经调度
-
preferredDuringSchedulingIgnoredDuringExecution 倾向满足,在无法满足情况下也会调度pod
-
IgnoreDuringExecution 表示如果在Pod运行期间Node的标签发生变化,导致亲和性策略不能满足,则继续运行当前的Pod。
-
-
nodeaffinity还支持多种规则匹配条件的配置如
匹配规则 | 功能 |
---|---|
ln | label 的值在列表内 |
Notln | label 的值不在列表内 |
Gt | label 的值大于设置的值,不支持Pod亲和性 |
Lt | label 的值小于设置的值,不支持pod亲和性 |
Exists | 设置的label 存在 |
DoesNotExist | 设置的 label 不存在 |
[root@master Scheduler]# vim test.yml
apiVersion: v1
kind: Pod
metadata:labels:run: testname: test
spec:affinity:nodeAffinity:requiredDuringSchedulingIgnoredDuringExecution:nodeSelectorTerms:- matchExpressions:- key: laboperator: Invalues:- timingleecontainers:- image: nginxname: test[root@master Scheduler]# kubectl label nodes node1 lab=timinglee
Podaffinity(pod的亲和)
-
那个节点有符合条件的POD就在那个节点运行
-
podAffinity 主要解决POD可以和哪些POD部署在同一个节点中的问题
-
podAntiAffinity主要解决POD不能和哪些POD部署在同一个节点中的问题。它们处理的是Kubernetes集群内部POD和POD之间的关系。
-
Pod 间亲和与反亲和在与更高级别的集合(例如 ReplicaSets,StatefulSets,Deployments 等)一起使用时,
-
Pod 间亲和与反亲和需要大量的处理,这可能会显著减慢大规模集群中的调度。
[root@master Scheduler]# cp test.yml 1-test.yml
[root@master Scheduler]# vim 1-test.yml
apiVersion: v1
kind: Pod
metadata:labels:run: myappv1name: myappv1
spec:affinity:podAffinity:requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: runoperator: Invalues:- testtopologyKey: "kubernetes.io/hostname"containers:- image: myapp:v1name: myappv1[root@master Scheduler]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myappv1 1/1 Running 0 4s 10.244.166.135 node1 <none> <none>
test 1/1 Running 0 3m 10.244.166.133 node1 <none> <none>
[root@master Scheduler]# kubectl get pods --show-labels
NAME READY STATUS RESTARTS AGE LABELS
myappv1 1/1 Running 0 14s run=myappv1
test 1/1 Running 0 3m10s run=test
Podantiaffinity(pod反亲和)
[root@master Scheduler]# kubectl apply -f test.yml
[root@master Scheduler]# vim 1-test.yml
apiVersion: v1
kind: Pod
metadata:labels:run: myappv1name: myappv1
spec:affinity:podAntiAffinity: #反亲和requiredDuringSchedulingIgnoredDuringExecution:- labelSelector:matchExpressions:- key: runoperator: Invalues:- testtopologyKey: "kubernetes.io/hostname"containers:- image: myapp:v1name: myappv1
[root@master Scheduler]# kubectl apply -f 1-test.yml
[root@master Scheduler]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
myappv1 1/1 Running 0 21s 10.244.104.4 node2 <none> <none>
test 1/1 Running 0 114s 10.244.166.136 node1 <none> <none>
Taints(污点模式,禁止调度)
- Taints(污点)是Node的一个属性,设置了Taints后,默认Kubernetes是不会将Pod调度到这个Node上
- Kubernetes如果为Pod设置Tolerations(容忍),只要Pod能够容忍Node上的污点,那么Kubernetes就会忽略Node上的污点,就能够(不是必须)把Pod调度过去
- 可以使用命令 kubectl taint 给节点增加一个 taint:
$ kubectl taint nodes <nodename> key=string:effect #命令执行方法
$ kubectl taint nodes node1 key=value:NoSchedule #创建
$ kubectl describe nodes server1 | grep Taints #查询
$ kubectl taint nodes node1 key- #删除
其中[effect] 可取值:
effect值 | 解释 |
---|---|
NoSchedule | POD 不会被调度到标记为 taints 节点 |
PreferNoSchedule | NoSchedule 的软策略版本,尽量不调度到此节点 |
NoExecute | 如该节点内正在运行的 POD 没有对应 Tolerate 设置,会直接被逐出 |
[root@master Scheduler]# kubectl run test --image nginx --dry-run=client -o yaml > test.yml
[root@master Scheduler]# kubectl create deployment web --image nginx --replicas 2 --dry-run=client -o yaml > web.yml
[root@master Scheduler]# kubectl run testpod --image myapp:v1
[root@master Scheduler]# kubectl taint node node2 nodetype=badnode:NoSchedule
[root@master Scheduler]# kubectl apply -f web.yml
[root@master Scheduler]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
testpod 1/1 Running 0 2m3s 10.244.104.5 node2 <none> <none>
web-7c56dcdb9b-5wcsn 1/1 Running 0 7s 10.244.166.138 node1 <none> <none>
web-7c56dcdb9b-z4t7w 1/1 Running 0 7s 10.244.166.137 node1 <none> <none>
tolerations(污点容忍)
-
tolerations中定义的key、value、effect,要与node上设置的taint保持一直:
-
如果 operator 是 Equal ,则key与value之间的关系必须相等。
-
如果 operator 是 Exists ,value可以省略
-
如果不指定operator属性,则默认值为Equal。
-
-
还有两个特殊值:
-
当不指定key,再配合Exists 就能匹配所有的key与value ,可以容忍所有污点。
-
当不指定effect ,则匹配所有的effect
-
#容忍所有污点
[root@master Scheduler]# vim web.yml
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: webname: web
spec:replicas: 2selector:matchLabels:app: webtemplate:metadata:labels:app: webspec:containers:- image: nginxname: nginxtolerations:- operator: Exists#effect: NoSchedule #容忍effect为Noschedule的污点
[root@master Scheduler]# kubectl apply -f web.yml#容忍指定kv的NoSchedule污点
[root@master Scheduler]# kubectl taint nodes node1 nodetype=bad:NoSchedule
node/node1 tainted
[root@master Scheduler]# kubectl describe nodes |grep Tain
Taints: node-role.kubernetes.io/control-plane:NoSchedule
Taints: nodetype=bad:NoSchedule
Taints: nodetype=badnode:NoSchedule[root@master Scheduler]# vim web.yml
...tolerations:- key: nodetypevalue: badeffect: NoSchedule[root@master Scheduler]# kubectl apply -f web.yml
deployment.apps/web created
[root@master Scheduler]# kubectl get pods -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
web-d76674b-k82t5 1/1 Running 0 5s 10.244.166.140 node1 <none> <none>
web-d76674b-wvp5r 1/1 Running 0 5s 10.244.166.141 node1 <none> <none>
7 k8s中的认证授权
7.1 kubernetes API 访问控制
Authentication(认证)
-
认证方式现共有8种,可以启用一种或多种认证方式,只要有一种认证方式通过,就不再进行其它方式的认证。通常启用X509 Client Certs和Service Accout Tokens两种认证方式。
-
Kubernetes集群有两类用户:由Kubernetes管理的Service Accounts (服务账户)和(Users Accounts) 普通账户。k8s中账号的概念不是我们理解的账号,它并不真的存在,它只是形式上存在。
Authorization(授权)
-
必须经过认证阶段,才到授权请求,根据所有授权策略匹配请求资源属性,决定允许或拒绝请求。授权方式现共有6种,AlwaysDeny、AlwaysAllow、ABAC、RBAC、Webhook、Node。默认集群强制开启RBAC。
Admission Control(准入控制)
-
用于拦截请求的一种方式,运行在认证、授权之后,是权限认证链上的最后一环,对请求API资源对象进行修改和校验。
UserAccount与ServiceAccount
-
用户账户是针对人而言的。 服务账户是针对运行在 pod 中的进程而言的。
-
用户账户是全局性的。 其名称在集群各 namespace 中都是全局唯一的,未来的用户资源不会做 namespace 隔离, 服务账户是 namespace 隔离的。
-
集群的用户账户可能会从企业数据库进行同步,其创建需要特殊权限,并且涉及到复杂的业务流程。 服务账户创建的目的是为了更轻量,允许集群用户为了具体的任务创建服务账户 ( 即权限最小化原则 )。
ServiceAccount
-
服务账户控制器(Service account controller)
-
服务账户管理器管理各命名空间下的服务账户
-
每个活跃的命名空间下存在一个名为 “default” 的服务账户
-
-
服务账户准入控制器(Service account admission controller)
-
相似pod中 ServiceAccount默认设为 default。
-
保证 pod 所关联的 ServiceAccount 存在,否则拒绝该 pod。
-
如果pod不包含ImagePullSecrets设置那么ServiceAccount中的ImagePullSecrets 被添加到pod中
-
将挂载于 /var/run/secrets/kubernetes.io/serviceaccount 的 volumeSource 添加到 pod 下的每个容器中
-
将一个包含用于 API 访问的 token 的 volume 添加到 pod 中
-
示例: 参考5.5里的secret输入容器仓库认证
7.2 认证(在k8s中建立认证用户)
###建立证书
[root@master Scheduler]# cd /etc/kubernetes/pki/
[root@master pki]# openssl genrsa -out timinglee.key 2048 #制作key
[root@master pki]# openssl req -new -key timinglee.key -out timinglee.csr -subj "/CN=timinglee" #做签名,添加信息[root@master pki]# openssl x509 -req -in timinglee.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out timinglee.crt -days 365 #签名写到证书里[root@master pki]# openssl x509 -in timinglee.crt -text -noout###建立用户
[root@master pki]# kubectl config set-credentials timinglee --client-certificate /etc/kubernetes/pki/timinglee.crt --client-key /etc/kubernetes/pki/timinglee.key --embed-certs=true###为用户创建安全上下文(缺少则不能使用用户)
[root@master pki]# kubectl config set-context timinglee@kubernetes --cluster kubernetes --user timinglee###切换用户
[root@master pki]# kubectl config use-context timinglee@kubernetes
[root@master pki]# kubectl get pods #只有用户身份,没有授权
Error from server (Forbidden): pods is forbidden: User "timinglee" cannot list resource "pods" in API group "" in the namespace "default"
#切换回来
[root@master pki]# kubectl config use-context kubernetes-admin@kubernetes
[root@master pki]# kubectl get pods
No resources found in default namespace.
7.3 RBAC(Role Based Access Control)
基于角色访问控制授权和role授权实施
-
允许管理员通过Kubernetes API动态配置授权策略。RBAC就是用户通过角色与权限进行关联。
-
RBAC只有授权,没有拒绝授权,所以只需要定义允许该用户做什么即可
-
RBAC的三个基本概念
-
Subject:被作用者,它表示k8s中的三类主体, user, group, serviceAccount
-
-
Role:角色,它其实是一组规则,定义了一组对 Kubernetes API 对象的操作权限。
-
RoleBinding:定义了“被作用者”和“角色”的绑定关系
-
RBAC包括四种类型:Role、ClusterRole、RoleBinding、ClusterRoleBinding
-
Role 和 ClusterRole
-
Role是一系列的权限的集合,Role只能授予单个namespace 中资源的访问权限。
-
-
ClusterRole 跟 Role 类似,但是可以在集群中全局使用。
-
Kubernetes 还提供了四个预先定义好的 ClusterRole 来供用户直接使用
-
cluster-amdin、admin、edit、view
[root@master pki]# cd ~
[root@master ~]# mkdir role
[root@master ~]# cd role/###创建role
[root@master role]# kubectl create role myrole --dry-run=client --verb=get --resource pods -o yaml > myrole.yml
[root@master role]# vim myrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: myrole
rules:
- apiGroups:- ""resources:- podsverbs:- get- watch- list- create- update- path- delete
- apiGroups:- ""resources:- servicesverbs:- get- watch- list- create- update- path- delete###建立角色绑定
[root@master role]# kubectl create rolebinding timinglee --role myrole --namespace default --user timinglee --dry-run=client -o yaml > rolebinding-myrole.yml
[root@master role]# kubectl config use-context timinglee@kubernetes
clusterrole授权实施
###建立集群角色
[root@master role]# kubectl create clusterrole myclusterrole --resource=deployment --verb get --dry-run=client -o yaml > myclusterrole.yml
[root@master role]# vim myclusterrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:name: myclusterrole
rules:
- apiGroups:- appsresources:- deploymentsverbs:- get- list- watch- create- update- path- delete
- apiGroups:- ""resources:- podsverbs:- get- list- watch- create- update- path- delete
[root@master role]# kubectl apply -f myclusterrole.yml
###建立集群角色绑定
[root@master role]# kubectl create clusterrolebinding clusterrolebind-myclusterrole --clusterrole myclusterrole --user timinglee --dry-run=client -o yaml > clusterrolebind-myclusterrole.yml
[root@master role]# kubectl apply -f clusterrolebind-myclusterrole.yml
[root@master role]# kubectl config use-context timinglee@kubernetes
服务账户的自动化
服务账户准入控制器(Service account admission controller)
-
如果该 pod 没有 ServiceAccount 设置,将其 ServiceAccount 设为 default。
-
保证 pod 所关联的 ServiceAccount 存在,否则拒绝该 pod。
-
如果 pod 不包含 ImagePullSecrets 设置,那么 将 ServiceAccount 中的 ImagePullSecrets 信息添加到 pod 中。
-
将一个包含用于 API 访问的 token 的 volume 添加到 pod 中。
-
将挂载于 /var/run/secrets/kubernetes.io/serviceaccount 的 volumeSource 添加到 pod 下的每个容器中。
服务账户控制器(Service account controller)
服务账户管理器管理各命名空间下的服务账户,并且保证每个活跃的命名空间下存在一个名为 “default” 的服务账户