Kubernetes 是什么?
Kubernetes是一个可移植的、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。kubernetes拥有一个庞大且快速增长的生态系统。kubernetes的服务、支持和工具广泛可用。
为什么需要kubernetes?
容器时打包和运行应用程序的好方式。在生产环境中,你需要管理运行应用程序的容器,并确保不会停 机。例如,如果一个容器发生故障,则需要启动另一个容器。如果系统如理此问题,会不会更容易?
这就是kubernetes来解决这些问题的方法!Kubernetes为你提供了一个可弹性运行分布式系统的框 架。Kubernetes会满足你的扩展要求、故障转移、部署模式等。
kubernetes为你提供:
自动装箱
建构于容器之上,基于资源依赖及其他约束自动完成容器部署且不影响其可用性,并通过调度机制混合
关键性应用和非关键型应用的工作负载于同一节点以提升资源利用率
自我修复(自愈)
支持容器故障后自动重启、节点故障后重新调度容器,以及其他可用节点、健康状态检查失败后关闭容
器并重新创建等自我修复机制。
水平扩展
支持通过命令或UI手动水平扩展,以及基于cpu等资源负载率的自动水平扩展机制。
服务发现和负载均衡
Kubernets通过其附加组件KubeDNS(或CoreDNS)为系统内置了服务发现功能,它会为每个service
配置DNS名称,并允许集群内的客户端直接使用此名称发出请求,而Service则通过iptables或ipvs内建
了负载均衡机制。
自动发布和回滚
Kubernets支持灰度更新应用程序或其配置信息,它会监控更新过程中应用程序的健康状态,以确保它
不会在同一时刻杀掉所有实例,而此过程中一旦有故障发生,就会立即自动执行回滚操作
密钥和配置管理
kubernetsd的configmap实现了配置数据与Docker镜像解耦,需要时,仅对配置做出变更而无须重新构
建docker镜像,这为应用开发部署带来了很大的灵活性。此外,对于应用所依赖的一些敏感数据,如用
户名和密码、令牌、密钥等信息,Kubernetes专门提供了secret对象为其解耦,既便利了应用的快速开
发和交付,又提供一定程度上的安全保障。
存储编排
Kubernets支持pod对象按需自动挂载不同类型的存储系统,这包括节点本地存储、公有云服务商的云
存储,以及网络存储系统(例如,NFS/ISCSI/GlusterFS/Ceph/Cinder/Flocker等)。
Kubernets 组件
当你部署完Kubernets,你就拥有了一个完整的集群。
一个kubernetes集群由一组被称作节点的机器组成。这些节点上运行Kubernetes所管理的容器化应
用。集群具有至少一个工作节点。
工作节点托管作为应用负载的组件的Pod。控制平面管理集群中的工作节点和pod。为集群提供故障转
移和高可用性,这些控制平面一般跨多主机运行,集群跨多个节点运行。
Mater:集群中的一台服务器用作Master,负责管理整个集群。Master是集群的网关和中枢,负责诸如
为用户和客户端暴露API、跟踪其他服务器的健康状态、以最优方式调度工作负载,以及编排其他组件之
间的通信任务,它是用户或客户端与集群之间的核心联络点,并负载Kubernetes系统的大多数集中式管
控逻辑。单个master节点即可完成其所有的功能,但出于冗余及负载均衡等目的,生产环境中通常需要
协同部署多个此类主机。
Node:Node是Kubernetes集群的工作节点,负责接收来自master的工作指令相应地创建或销毁Pod
对象,以及调整网络规则以合理地路由和转发流量等。理论上讲,Node可以是任何形式的计算设备,不
过Master会统一将其抽象为Node对象进行管理。
Kubernetes将所有Node资源集结于一处形成一台更加强大的服务器,在用户将应用部署于其上时,
Master会使用调度算法将其自动指派至某个特定的Node运行。在Node加入集群或从集群中移除时,
Master也会按需重新编排影响到的Pod,于是用户无须关心其应用究竟运行何处。
控制平面组件
控制平面组件可以在集群中的任何节点上运行。然后,为了简单起见,设置脚本通常会在同一个计算机
上启动所有控制平面组件,并且不会在此计算机上运行用户容器。
kube-apiserver
Api server负责输出restful风格的kubernetes API,它是发往集群的所有REST操作命令的接入点,并负
责接收、校验并响应所有的REST请求,结果状态被持久存储于etcd中。因此,api server是整个集群的
网关。
etcd
kubernetes集群的所有状态信息都需要持久存储于存储系统etcd中,不过,etcd是由CoreOS基于Raft
协议开发的分布式键值存储,可用于服务发现、共享配置以及一致性保障(如数据库主节点选择、分布
式锁等)。因此,etcd是独立的服务组件,并不隶属于Kubernetes集群自身。etcd不仅能够提供键值数据存储,而且还为其提供了监听机制,用于监听和推送变更。Kubernetes集群
系统中,etcd中的键值发生变化时会通知到api server。
kube-controller-manager
kubernetes中,集群级别的大多数功能都是由几个被称为控制器的进程执行实现的,这几个进程被集成
于kube-controller-manager守护进程中。由控制器完成的功能主要包括生命周期功能和api业务逻辑。
kube-scheduler
Kubernetes是用于部署和管理大规模容器应用的平台,根据集群规模的不同,其托管运行的容器很可能
会数以千计甚至更多。api server确认pod对象的创建请求之后,便需要由scheduler根据集群内各节点
的可用资源状态,以及要运行的容器的资源需求做出调度决策。
Node组件
kubelet
kubelet是运行于工作节点上的守护进程,它从api server接收关于pod对象的配置信息并确保他们处于
期望的状态,kubelet会在api server上注册当前的工作节点,定期向master汇报节点资源使用情况。
kubeproxy
每个工作节点都需要运行一个kube-proxy守护进程,它能够按需为service资源对象生成iptables或者
ipvs规则,从而捕获当前service的流量并将其转发至正确的后端pod对象。
容器运行时环境
每个node都要提供一个容器运行时环境,它负责下载镜像并运行容器。kubernetes支持多个容器运行
时环境。
集群部署
环境准备
在所有机器上关闭防火墙
在所有机器上关闭selinux
在所有机器上关闭swap
swapoff -a # 临时关闭
sed -ri 's/.swap./#&/' /etc/fstab #永久关闭
修改主机名
在所有机器上添加域名解析
在所有机器上将桥接的ipv4流量传递到iptables的链
vim /etc/sysctl.d/k8s.conf
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
为所有节点安装docker
yum install wget.x86_64 -y
rm -rf /etc/yum.repos.d/*
wget -O /etc/yum.repos.d/centos7.repo http://mirrors.aliyun.com/repo/Centos-7.repo
wget -O /etc/yum.repos.d/epel-7.repo http://mirrors.aliyun.com/repo/epel-7.repo
wget -O /etc/yum.repos.d/docker-ce.repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
yum install docker-ce -y
集群部署
配置镜像仓库,https://developer.aliyun.com/mirror/kubernetes?spm=a2c6h.13651102.0.0.3e221b11tmy01T
1.为所有节点修改仓库,安装kubeadm、kubelet、kubectl
vim /etc/yum.repos.d/kubernetes.repo
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64/
enabled=1
gpgcheck=1
repo_gpgcheck=1
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg
https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
yum install -y kubelet kubeadm kubectl (失败用yum install -y --nogpgcheck kubelet kubeadm kubectl)
systemctl enable kubelet && systemctl start kubelet
2.修改docker配置
vim /etc/docker/daemon.json
{
"exec-opts": ["native.cgroupdriver=systemd"]
}
systemctl daemon-reload
systemctl restart docker.service
systemctl restart kubelet.service
部署master
kubeadm init \
--apiserver-advertise-address=192.168.80.100 \
--image-repository registry.aliyuncs.com/google_containers \
--kubernetes-version v1.22.2 \
--control-plane-endpoint k8s-master1 \
--service-cidr=172.16.0.0/16 \
--pod-network-cidr=10.244.0.0/16
#遇到镜像下载问题可以单独下载,
docker pull registry.aliyuncs.com/google_containers/coredns:1.8.4
docker tag registry.aliyuncs.com/google_containers/coredns:1.8.4 registry.aliyuncs.com/google_containers/coredns:v1.8.4
按照指示执行
安装网络插件
官方文档:https://github.com/flannel-io/flannel
wget https://raw.githubusercontent.com/coreos/flannel/master/Documentation/kube-flannel.yml
kubectl apply -f kube-flannel.yml
#最好手动提前拉取所需镜像
docker pull quay.io/coreos/flannel:v0.14.0
添加node节点
docker pull quay.io/coreos/flannel:v0.14.0
kubeadm join k8s-master1:6443 --token 0re1oq.he6o0ab4mqtjtg83 --discovery-token-ca-cert-hash sha256:460d740c21fa040f7f12e22cdd018aec8c903d4880f42b0f7edeb78a80241b56(在安装时有提示)
资源清单
在 k8s 中,一般使用 yaml 格式的文件来创建符合我们预期期望的 pod ,这样的 yaml 文件我们一般称为资源清单
补充命令:kubectl log pod名称 -c 容器名称
容器的生命周期
Pod 能够具有多个容器,应用运行在容器里面,但是它也可能有一个或多个先于应用容器启动的 Init
容器
Init 容器与普通的容器非常像,除了如下两点:
- Init 容器总是运行到成功完成为止
- 每个 Init 容器都必须在下一个 Init 容器启动之前成功完成
如果 Pod 的 Init 容器失败,Kubernetes 会不断地重启该od,直到 Init 容器成功为止。然而,如果 Pod 对应的 restartPolicy 为 Never,它不会重新启动
pod资源
Namespace
Namespace是kubernetes系统中的一种非常重要资源,它的主要作用是用来实现多套环境的资源隔离
或者多租户的资源隔离。默认是default。
查看
查看所有的ns 命令:kubectl get ns
查看指定的ns 命令:kubectl get ns ns名称
指定输出格式 命令:kubectl get ns ns名称 -o 格式参数
#kubernetes支持的格式有很多,比较常见的是wide、json、yaml
查看ns详情 命令:kubectl describe ns ns名称
创建
kubectl create ns ns名称
删除
kubectl delete ns ns名称
配置方式
首先准备一个yaml文件:ns-dev.yaml
apiVersion: v1
kind: Namespace
metadata:
name: dev
然后就可以执行对应的创建和删除命令了:
创建:kubectl create -f ns-dev.yaml
删除:kubectl delete -f ns-dev.yam
Pod
Pod是kubernetes集群进行管理的最小单元,程序要运行必须部署在容器中,而容器必须存在于Pod中。
Pod可以认为是容器的封装,一个Pod中可以存在一个或者多个容器。
kubernetes在集群启动之后,集群中的各个组件也都是以Pod方式运行的。可以通过下面命令查看:
kubectl get pod -n kube-system
创建并运行
#命令格式: kubectl create deploy (pod控制器名称) [参数]
#--image 指定Pod的镜像
#--port 指定端口
#--namespace 指定namespace
查看pod信息
#查看Pod基本信息
kubectl get pods -n ns名称
#查看Pod的详细信息
kubectl describe pod podNAME -n ns名称
访问Pod
#获取podIP
kubectl get pods -n ns名称 -o wide
#访问POD
curl 10.244.1.4
删除指定Pod
#删除指定Pod
kubectl delete pod podNAME -n ns名称
#此时,显示删除Pod成功,但是再查询,发现又新产生了一个
#这是因为当前Pod是由Pod控制器创建的,控制器会监控Pod状况,一旦发现Pod死亡,会立即重建 # 此时要想删除Pod,必须删除Pod控制器
#先来查询一下当前namespace下的Pod控制器
kubectl get deploy -n ns名称
#接下来,删除此PodPod控制器
kubectl delete deploy NAME -n ns名称
#稍等片刻,再查询Pod,发现Pod被删除了
配置操作
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
spec:
containers:
- image: nginx:latest
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
创建:kubectl create -f pod-nginx.yaml
删除:kubectl delete -f pod-nginx.yaml
Label(标记pod资源)
Label是kubernetes系统中的一个重要概念。它的作用就是在资源上添加标识,用来对它们进行区分和选择。
Label的特点:
一个Label会以key/value键值对的形式附加到各种对象上,如Node、Pod、Service等等
一个资源对象可以定义任意数量的Label ,同一个Label也可以被添加到任意数量的资源对象上去
Label通常在资源对象定义时确定,当然也可以在对象创建后动态添加或者删除
可以通过Label实现资源的多维度分组,以便灵活、方便地进行资源分配、调度、配置、部署等管理工作。
命令方式
#为pod资源打标签
kubectl label pod nginx versinotallow=1.0 -n ns名称
#为pod资源更新标签
kubectl label pod nginx versinotallow=2.0 -n ns名称 --overwrite
#查看标签
kubectl get pod nginx-pod -n ns名称 --show-labels
#筛选标签
kubectl get pod -n ns名称 -l versinotallow=2.0 --show-labels
kubectl get pod -n ns名称 -l version!=2.0 --show-labels
#删除标签
kubectl label pod nginx-pod version- -n dev
配置方式
apiVersion: v1
kind: Pod
metadata:
name: nginx
namespace: dev
labels:
version: "3.0"
env: "test"
spec:
containers:
- image: nginx:latest
name: pod
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
Deployment
能够利用Deployment来创建一组Pod来提供具有高可用性的服务,pod会自动重启。
命令操作
#命令格式: kubectl create deployment 名称 [参数]
#--image 指定pod的镜像
#--port 指定端口
#--replicas 指定创建pod数量
#--namespace 指定namespace
#查看创建的Pod
kubectl get pods -n ns名称
#查看deployment的信息
kubectl get deploy -n ns名称
#UP-TO-DATE:成功升级的副本数量
#AVAILABLE:可用副本的数量
kubectl get deploy -n ns名称 -o wide
#查看deployment的详细信息
kubectl describe deploy NAME -n ns名称
#删除
kubectl delete deploy NAME -n ns名称
配置操作
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
run: nginx
template:
metadata:
labels:
run: nginx
spec:
containers:
- image: nginx:latest
name: nginx
ports:
- containerPort: 80
protocol: TCP
Service
然而却存在如下两问题:
Pod IP 会随着Pod的重建产生变化
Pod IP 仅仅是集群内可见的虚拟IP,外部无法访问
因此,kubernetes设计了Service来解决这个问题。
Service可以看作是一组同类Pod对外的访问接口。借助Service,应用可以方便地实现服务发现和负载均衡
操作一:创建集群内部可访问的Service
#暴露Service
kubectl expose deploy NAME --name=svc-nginx1 --type=ClusterIP --port=80 --target-port=80 -n ns名称
#查看service
kubectl get svc svc-nginx1 -n dev -o wide
#这里产生了一个CLUSTER-IP,这就是service的IP,在Service的生命周期中,这个地址是不会变动的
#可以通过这个IP访问当前service对应的POD
操作二:创建集群外部也可访问的Service
#上面创建的Service的type类型为ClusterIP,这个ip地址只用集群内部可访问 # 如果需要创建外部也可以访问的Service,需要修改type为NodePort
kubectl expose deploy NAME --name=svc-nginx2 -- type=NodePort --port=80 --target-port=80 -n ns名称
#此时查看,会发现出现了NodePort类型的Service,而且有一对Port(80:31928/TC)
kubectl get svc svc-nginx2 -n ns名称 -o wide
#接下来就可以通过集群外的主机访问 节点IP:31928访问服务了
#例如在的电脑主机上通过浏览器访问下面的地址
删除Service
kubectl delete svc svc-nginx-1 -n ns名称
配置方式
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: dev
spec:
clusterIP: 10.109.179.231 #固定svc的内网ip
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
run: nginx
type: ClusterIP
pod控制器详解
ReplicaSet(RS)
ReplicaSet的主要作用是保证一定数量的pod正常运行,它会持续监听这些Pod的运行状态,一旦Pod发生故障,就会重启或重建。同时它还支持对pod数量的扩缩容和镜像版本的升降级。
在这里面,需要新了解的配置项就是 spec 下面几个选项:
replicas:指定副本数量,其实就是当前rs创建出来的pod的数量,默认为1
selector:选择器,它的作用是建立pod控制器和pod之间的关联关系,采用的Label Selector机制在pod模板上定义label,在控制器上定义选择器,就可以表明当前控制器能管理哪些pod了
template:模板,就是当前控制器创建pod所使用的模板板,里面其实就是前一章学过的pod的定义
扩缩容
# 编辑rs的副本数量,修改spec:replicas: 6即可
[root@k8s-master01 ~]# kubectl edit rs pc-replicaset -n dev
replicaset.apps/pc-replicaset edited
# 查看pod
[root@k8s-master01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-6vmvt 1/1 Running 0 114m
pc-replicaset-cftnp 1/1 Running 0 10s
pc-replicaset-fjlm6 1/1 Running 0 10s
pc-replicaset-fmb8f 1/1 Running 0 114m
pc-replicaset-s2whj 1/1 Running 0 10s
pc-replicaset-snrk2 1/1 Running 0 114m
# 当然也可以直接使用命令实现
# 使用scale命令实现扩缩容, 后面--replicas=n直接指定目标数量即可
[root@k8s-master01 ~]# kubectl scale rs pc-replicaset --replicas=2 -n dev
replicaset.apps/pc-replicaset scaled
# 命令运行完毕,立即查看,发现已经有4个开始准备退出了
[root@k8s-master01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-6vmvt 0/1 Terminating 0 118m
pc-replicaset-cftnp 0/1 Terminating 0 4m17s
pc-replicaset-fjlm6 0/1 Terminating 0 4m17s
pc-replicaset-fmb8f 1/1 Running 0 118m
pc-replicaset-s2whj 0/1 Terminating 0 4m17s
pc-replicaset-snrk2 1/1 Running 0 118m
#稍等片刻,就只剩下2个了
[root@k8s-master01 ~]# kubectl get pods -n dev
NAME READY STATUS RESTARTS AGE
pc-replicaset-fmb8f 1/1 Running 0 119m
pc-replicaset-snrk2 1/1 Running 0 119m
Deployment(Deploy)
为了更好的解决服务编排的问题,kubernetes在V1.2版本开始,引入了Deployment控制器。值得一提的是,这种控制器并不直接管理pod,而是通过管理ReplicaSet来简介管理Pod,即:Deployment管理ReplicaSet,ReplicaSet管理Pod。
Deployment主要功能有下面几个:
支持ReplicaSet的所有功能
支持发布的停止、继续
支持滚动升级和回滚版本
查看deployment
# UP-TO-DATE 最新版本的pod的数量
# AVAILABLE 当前可用的pod的数量
kubectl get deploy NAME -n ns名称
查看rs
kubectl get rs -n ns名称
#扩缩容
kubectl scale deploy NAME --replicas=数量 -n ns名称
镜像更新
deployment支持两种更新策略: 重建更新 和 滚动更新 ,可以通过 strategy 指定策略类型,支持两个属性
重建更新
1)编辑pc-deployment.yaml,在spec节点下添加更新策略
spec:
strategy: # 策略
type: Recreate # 重建更新
2)创建deploy进行验证
[root@k8s-master01 ~]# kubectl set image deployment pc-deployment
nginx=nginx:1.17.2 -n dev
deployment.apps/pc-deployment image updated
滚动更新
1) 编辑pc-deployment.yaml,在spec节点下添加更新策略
spec:
strategy: # 策略
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
# 变更镜像
[root@k8s-master01 ~]# kubectl set image deployment pc-deployment
nginx=nginx:1.17.3 -n dev
deployment.apps/pc-deployment image updated
HPA
查看hpa
kubectl get hpa -n ns名称
DaemonSet(DS)
DaemonSet类型的控制器可以保证在集群中的每一台(或指定)节点上都运行一个副本。一般适用于日志收集、节点监控、集群存储等场景。也就是说,如果一个Pod提供的功能是节点级别的(每个节点都需要且只需要一个),那么这类Pod就适合使用DaemonSet类型的控制器创建。DaemonSet控制器的特点:
每当向集群中添加一个节点时,指定的 Pod 副本也将添加到该节点上
当节点从集群中移除时,Pod 也就被垃圾回收了
查看daemonset
kubectl get ds -n ns名称 -o wide
Job
Job,主要用于负责批量处理(一次要处理指定数量任务)短暂的一次性(每个任务仅运行一次就结束)任务。Job特点如下:
当Job创建的pod执行成功结束时,Job将记录成功结束的pod数量
当成功结束的pod达到指定的数量时,Job将完成执行
关于重启策略设置的说明:
如果指定为OnFailure,则job会在pod出现故障时重启容器,而不是创建pod,failed次数不变
如果指定为Never,则job会在pod出现故障时创建新的pod,并且故障pod不会消失,也不会重启, failed次数加1
如果指定为Always的话,就意味着一直重启,意味着job任务会重复去执行了,当然不对,所以不 能设置为Always
查看job
kubectl get job -n ns名称 -o wide -w
CronJob(CJ)
以Job控制器资源为其管控对象,并借助它管理pod资源对象,CronJob可以在特定的时间点(反复的)去运行job任务。
需要重点解释的几个选项:
schedule: cron表达式,用于指定任务的执行时间
*/1 * * * *
<分钟> <小时> <日> <月份> <星期>
分钟 值从 0 到 59.
小时 值从 0 到 23.
日 值从 1 到 31.
月 值从 1 到 12.
星期 值从 0 到 6, 0 代表星期日
多个时间可以用逗号隔开; 范围可以用连字符给出;*可以作为通配符; /表示每…
concurrencyPolicy:
Allow: 允许Jobs并发运行(默认)
Forbid: 禁止并发运行,如果上一次运行尚未完成,则跳过下一次运行
Replace: 替换,取消当前正在运行的作业并用新作业替换它
查看cronjob
kubectl get cronjobs -n ns名称
StatefulSet
StatefulSet 作为 Controller 为 Pod 提供唯一的标识。它可以保证部署和 scale 的顺序
StatefulSet是为了解决有状态服务的问题(对应Deployments和ReplicaSets是为无状态服务而设计),其应用
场景包括:
稳定的持久化存储,即Pod重新调度后还是能访问到相同的持久化数据,基于PVC来实现
稳定的网络标志,即Pod重新调度后其PodName和HostName不变,基于Headless Service(即没有
Cluster IP的Service)来实现
有序部署,有序扩展,即Pod是有顺序的,在部署或者扩展的时候要依据定义的顺序依次依次进行(即从0到
N-1,在下一个Pod运行之前所有之前的Pod必须都是Running和Ready状态),基于init containers来实
现
有序收缩,有序删除(即从N-1到0)
介绍Ingress
在前面课程中已经提到,Service对集群之外暴露服务的主要方式有两种:NotePort和LoadBalancer,
但是这两种方式,都有一定的缺点:
NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈
发明显
LB方式的缺点是每个service需要一个LB,浪费、麻烦,并且需要kubernetes之外设备的支持
基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以
满足暴露多个Service的需求。工作机制大致如下图表示:
实际上,Ingress相当于一个7层的负载均衡器,是kubernetes对反向代理的一个抽象,它的工作原理类
似于Nginx,可以理解成在Ingress里建立诸多映射规则,Ingress Controller通过监听这些配置规则并
转化成Nginx的反向代理配置 , 然后对外部提供服务。在这里有两个核心概念:
ingress:kubernetes中的一个对象,作用是定义请求如何转发到service的规则
ingress controller:具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据
配置的规则来实现请求转发,实现方式有很多,比如Nginx, Contour, Haproxy等等
Ingress(以Nginx为例)的工作原理如下:
1. 用户编写Ingress规则,说明哪个域名对应kubernetes集群中的哪个Service
2. Ingress控制器动态感知Ingress服务规则的变化,然后生成一段对应的Nginx反向代理配置
3. Ingress控制器会将生成的Nginx配置写入到一个运行着的Nginx服务中,并动态更新
4. 到此为止,其实真正在工作的就是一个Nginx了,内部配置了用户定义的请求转发规则
Ingress使用
环境准备
搭建ingress环境
官方教程地址:https://kubernetes.github.io/ingress-nginx/deploy/#bare-metal
# 下载deploy.yaml
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.0.4/deploy/static/provider/baremetal/deploy.yaml
# 在所有节点上提前pull镜像,防止启动失败
docker pull registry.cn-
hangzhou.aliyuncs.com/eagleslab/service:ingresswebhook111
docker tag c41e9fcadf5a k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1
docker pull registry.cn-
hangzhou.aliyuncs.com/eagleslab/service:ingresscontroller104
docker tag a9f76bcccfb5 k8s.gcr.io/ingress-nginx/controller:v1.0.4
[root@master ~]# docker images |grep k8s.gcr
k8s.gcr.io/ingress-nginx/controller v1.0.4
a9f76bcccfb5 8 days ago 284MB
k8s.gcr.io/ingress-nginx/kube-webhook-certgen v1.1.1
c41e9fcadf5a 8 days ago 47.7MB
# 如果是提前pull的镜像,要删除掉deploy.yaml中对镜像sha256的检查,在image标签中,删除
@sha256到行尾
[root@master ~]# kubectl apply -f deploy.yaml
# 查看ingress-nginx
kubectl get pods -n ingress-nginx \
-l app.kubernetes.io/name=ingress-nginx --watch
# 稍等片刻
[root@master ~]# kubectl get pods -n ingress-nginx -l
app.kubernetes.io/name=ingress-nginx
NAME READY STATUS RESTARTS
AGE
ingress-nginx-admission-create--1-k5rjn 0/1 Completed 0
21s
ingress-nginx-admission-patch--1-6z2sd 0/1 Completed 0
21s
ingress-nginx-controller-fd98fbdfb-tg4bt 1/1 Running 0
21s
# 查看service
[root@master ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-
IP PORT(S) AGE
ingress-nginx-controller NodePort 172.16.185.69 <none>
80:31484/TCP,443:31438/TCP 35s
ingress-nginx-controller-admission ClusterIP 172.16.137.250 <none>
443/TCP 35s
准备service和pod
为了后面的实验比较方便,创建如下图所示的模型
创建tomcat-nginx.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: nginx-pod
template:
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: tomcat-deployment
namespace: dev
spec:
replicas: 3
selector:
matchLabels:
app: tomcat-pod
template:
metadata:
labels:
app: tomcat-pod
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
namespace: dev
spec:
selector:
app: nginx-pod
clusterIP: None
type: ClusterIP
ports:
- port: 80
targetPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: tomcat-service
namespace: dev
spec:
selector:
app: tomcat-pod
clusterIP: None
type: ClusterIP
ports:
- port: 8080
targetPort: 8080
# 创建
[root@k8s-master01 ~]# kubectl create -f tomcat-nginx.yaml
# 查看
[root@k8s-master01 ~]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-service ClusterIP None <none> 80/TCP 48s
tomcat-service ClusterIP None <none> 8080/TCP 48s
Http代理
创建ingress-http.yaml
kind: Ingress
metadata:
name: ingress-nginx
namespace: dev
spec:
rules:
- host: nginx.bbj1030.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-service
port:
number: 80
ingressClassName: nginx
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-tomcat
namespace: dev
spec:
rules:
- host: tomcat.bbj1030.cn
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: tomcat-service
port:
number: 8080
ingressClassName: nginx
# 创建
[root@master ~]# kubectl create -f ingress-http.yaml
ingress.networking.k8s.io/ingress-http created
ingress.networking.k8s.io/ingress-tomcat created
ingressclass.networking.k8s.io/http created
# 查看
[root@master ~]# kubectl get ing -n dev
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-http http nginx.bbj1030.cn 80 9s
ingress-tomcat http tomcat.bbj1030.cn 80 9s
# 查看详情
[root@master ~]# kubectl describe ing -n dev
...
Rules:
Host Path Backends
---- ---- --------
nginx.bbj1030.cn
/ nginx-service:80
(10.244.1.3:80,10.244.1.4:80,10.244.1.5:80)
调度过程说明
简介
Scheduler 是 kubernetes 的调度器,主要的任务是把定义的 pod 分配到集群的节点上。听起来非常简单,但有
很多要考虑的问题:
- 公平:如何保证每个节点都能被分配资源
- 资源高效利用:集群所有资源最大化被使用
- 效率:调度的性能要好,能够尽快地对大批量的 pod 完成调度工作
- 灵活:允许用户根据自己的需求控制调度的逻辑
Sheduler 是作为单独的程序运行的,启动之后会一直坚挺 API Server,获取 PodSpec.NodeName 为空的 pod,对每个 pod 都会创建一个 binding,表明该 pod 应该放到哪个节点上
调度过程
调度分为几个部分:首先是过滤掉不满足条件的节点,这个过程称为 predicate ;然后对通过的节点按照优先级
排序,这个是 priority ;最后从中选择优先级最高的节点。如果中间任何一步骤有错误,就直接返回错误
Predicate 有一系列的算法可以使用:
- PodFitsResources :节点上剩余的资源是否大于 pod 请求的资源
- PodFitsHost :如果 pod 指定了 NodeName,检查节点名称是否和 NodeName 匹配
- PodFitsHostPorts :节点上已经使用的 port 是否和 pod 申请的 port 冲突
- PodSelectorMatches :过滤掉和 pod 指定的 label 不匹配的节点
- NoDiskConflict :已经 mount 的 volume 和 pod 指定的 volume 不冲突,除非它们都是只读
如果在 predicate 过程中没有合适的节点,pod 会一直在 pending 状态,不断重试调度,直到有节点满足条件。
经过这个步骤,如果有多个节点满足条件,就继续 priorities 过程: 按照优先级大小对节点排序
优先级由一系列键值对组成,键是该优先级项的名称,值是它的权重(该项的重要性)。这些优先级选项包括:
- LeastRequestedPriority :通过计算 CPU 和 Memory 的使用率来决定权重,使用率越低权重越高。换句话说,这个优先级指标倾向于资源使用比例更低的节点
- BalancedResourceAllocation :节点上 CPU 和 Memory 使用率越接近,权重越高。这个应该和上面的一起使用,不应该单独使用
- ImageLocalityPriority :倾向于已经有要使用镜像的节点,镜像总大小值越大,权重越高通过算法对所有的优先级项目和权重进行计算,得出最终的结果
自定义调度器
除了 kubernetes 自带的调度器,你也可以编写自己的调度器。通过 spec:schedulername 参数指定调度器的名字,可以为 pod 选择某个调度器进行调度。比如下面的 pod 选择 my-scheduler 进行调度,而不是默认的
default-scheduler :
apiVersion: v1
kind: Pod
metadata:
name: annotation-second-scheduler
labels:
name: multischeduler-example
spec:
schedulername: my-scheduler
containers:
- name: pod-with-second-annotation-container
image: gcr.io/google_containers/pause:2.0
亲和性
节点亲和性
pod.spec.nodeAffinity
- preferredDuringSchedulingIgnoredDuringExecution:软策略(不一定)
- requiredDuringSchedulingIgnoredDuringExecution:硬策略(一定)
requiredDuringSchedulingIgnoredDuringExecuti:
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: hub.atguigu.com/library/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- k8s-node02
preferredDuringSchedulingIgnoredDuringExecuti
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: hub.atguigu.com/library/myapp:v1
affinity:
nodeAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: source
operator: In
values:
- qikqia
合体
apiVersion: v1
kind: Pod
metadata:
name: affinity
labels:
app: node-affinity-pod
spec:
containers:
- name: with-node-affinity
image: hub.atguigu.com/library/myapp:v1
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/hostname
operator: NotIn
values:
- k8s-node02
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
preference:
matchExpressions:
- key: source
operator: In
values:
- qikqiak
键值运算关系
- In:label 的值在某个列表中
- NotIn:label 的值不在某个列表中
- Gt:label 的值大于某个值
- Lt:label 的值小于某个值
- Exists:某个 label 存在
- DoesNotExist:某个 label 不存在
Pod 亲和性
pod.spec.affinity.podAffinity/podAntiAff
- preferredDuringSchedulingIgnoredDuringExecution:软策略
- requiredDuringSchedulingIgnoredDuringExecution:硬策略
apiVersion: v1
kind: Pod
metadata:
name: pod-3
labels:
app: pod-3
spec:
containers:
- name: pod-3
image: hub.atguigu.com/library/myapp:v1
affinity:
podAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-1
topologyKey: kubernetes.io/hostname
podAntiAffinity:
preferredDuringSchedulingIgnoredDuringExecution:
- weight: 1
podAffinityTerm:
labelSelector:
matchExpressions:
- key: app
operator: In
values:
- pod-2
topologyKey: kubernetes.io/hostname
亲和性/反亲和性调度策略比较如下:
Taint 和 Toleration
节点亲和性,是 pod 的一种属性(偏好或硬性要求),它使 pod 被吸引到一类特定的节点。Taint 则相反,它使节点 能够 排斥 一类特定的 pod
Taint 和 toleration 相互配合,可以用来避免 pod 被分配到不合适的节点上。每个节点上都可以应用一个或多个taint ,这表示对于那些不能容忍这些 taint 的 pod,是不会被该节点接受的。如果将 toleration 应用于 pod上,则表示这些 pod 可以(但不要求)被调度到具有匹配 taint 的节点上
污点(Taint)
I、 污点 ( Taint ) 的组成
使用 kubectl taint 命令可以给某个 Node 节点设置污点,Node 被设置上污点之后就和 Pod 之间存在了一种相斥的关系,可以让 Node 拒绝 Pod 的调度执行,甚至将 Node 已经存在的 Pod 驱逐出去
每个污点的组成如下:
key=value:effe
每个污点有一个 key 和 value 作为污点的标签,其中 value 可以为空,effect 描述污点的作用。当前 tainteffect 支持如下三个选项:
- NoSchedule :表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上
- PreferNoSchedule :表示 k8s 将尽量避免将 Pod 调度到具有该污点的 Node 上
- NoExecute :表示 k8s 将不会将 Pod 调度到具有该污点的 Node 上,同时会将 Node 上已经存在的 Pod 驱逐出去
II、污点的设置、查看和去除
# 设置污点
kubectl taint nodes node1 key1=value1:NoSchedule
# 节点说明中,查找 Taints 字段
kubectl describe pod pod-name
# 去除污点
kubectl taint nodes node1 key1:NoSchedule-
容忍(Tolerations)
设置了污点的 Node 将根据 taint 的 effect:NoSchedule、PreferNoSchedule、NoExecute 和 Pod 之间产生互斥的关系,Pod 将在一定程度上不会被调度到 Node 上。 但我们可以在 Pod 上设置容忍 ( Toleration ) ,意思是设置了容忍的 Pod 将可以容忍污点的存在,可以被调度到存在污点的 Node 上
pod.spec.tolerations
tolerations:
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoSchedule"
tolerationSeconds: 3600
- key: "key1"
operator: "Equal"
value: "value1"
effect: "NoExecute"
- key: "key2"
operator: "Exists"
effect: "NoSchedule"
- 其中 key, vaule, effect 要与 Node 上设置的 taint 保持一致
- operator 的值为 Exists 将会忽略 value 值
- tolerationSeconds 用于描述当 Pod 需要被驱逐时可以在 Pod 上继续保留运行的时间
I、当不指定 key 值时,表示容忍所有的污点 key:
tolerations:
- operator: "Exists"
II、当不指定 effect 值时,表示容忍所有的污点作用
tolerations:
- key: "key"
operator: "Exists
III、有多个 Master 存在时,防止资源浪费,可以如下设置
kubectl taint nodes Node-Name node-role.kubernetes.io/master=:PreferNoSchedule
DashBoard
之前在kubernetes中完成的所有操作都是通过命令行工具kubectl完成的。其实,为了提供更丰富的用
户体验,kubernetes还开发了一个基于web的用户界面(Dashboard)。用户可以使用Dashboard部署
容器化的应用,还可以监控应用的状态,执行故障排查以及管理kubernetes中各种资源。
部署Dashboard
1) 下载yaml,并运行Dashboard
# 下载yaml
[root@k8s-master01 ~]# wget
https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/rec
ommended.yaml
# 修改kubernetes-dashboard的Service类型
kind: Service
apiVersion: v1
metadata:
labels:
k8s-app: kubernetes-dashboard
name: kubernetes-dashboard
namespace: kubernetes-dashboard
spec:
type: NodePort # 新增
ports:
- port: 443
targetPort: 8443
nodePort: 30009 # 新增
selector:
k8s-app: kubernetes-dashboard
# 部署
[root@k8s-master01 ~]# kubectl create -f recommended.yaml
# 查看namespace下的kubernetes-dashboard下的资源
[root@k8s-master01 ~]# kubectl get pod,svc -n kubernetes-dashboard
NAME READY STATUS RESTARTS
AGE
pod/dashboard-metrics-scraper-c79c65bb7-zwfvw 1/1 Running 0
111s
pod/kubernetes-dashboard-56484d4c5-z95z5 1/1 Running 0
111s
NAME TYPE CLUSTER-IP EXTERNAL-IP
PORT(S) AGE
service/dashboard-metrics-scraper ClusterIP 10.96.89.218 <none>
8000/TCP 111s
service/kubernetes-dashboard NodePort 10.104.178.171 <none>
443:30009/TCP 111s
2)创建访问账户,获取token
# 创建账号
[root@k8s-master01-1 ~]# kubectl create serviceaccount dashboard-admin -n
kubernetes-dashboard
# 授权
[root@k8s-master01-1 ~]# kubectl create clusterrolebinding dashboard-admin-
rb --clusterrole=cluster-admin --serviceaccount=kubernetes-
dashboard:dashboard-admin
# 获取账号token
[root@k8s-master01 ~]# kubectl get secrets -n kubernetes-dashboard | grep
dashboard-admin
dashboard-admin-token-xbqhh kubernetes.io/service-account-token 3
2m35s
[root@k8s-master01 ~]# kubectl describe secrets dashboard-admin-token-xbqhh
-n kubernetes-dashboard
Name: dashboard-admin-token-xbqhh
Namespace: kubernetes-dashboard
Labels: <none>
Annotations: kubernetes.io/service-account.name: dashboard-admin
kubernetes.io/service-account.uid: 95d84d80-be7a-4d10-a2e0-
68f90222d039
Type: kubernetes.io/service-account-token
Data
====
namespace: 20 bytes
token:
eyJhbGciOiJSUzI1NiIsImtpZCI6ImJrYkF4bW5XcDhWcmNGUGJtek5NODFuSXl1aWptMmU2M3o4
LTY5a2FKS2cifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlc
y5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1Y
mVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4td
G9rZW4teGJxaGgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3Vud
C5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZ
XJ2aWNlLWFjY291bnQudWlkIjoiOTVkODRkODAtYmU3YS00ZDEwLWEyZTAtNjhmOTAyMjJkMDM5I
iwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmVybmV0ZXMtZGFzaGJvYXJkOmRhc2hib
2FyZC1hZG1pbiJ9.NAl7e8ZfWWdDoPxkqzJzTB46sK9E8iuJYnUI9vnBaY3Jts7T1g1msjsBnbxz
QSYgAG--
cV0WYxjndzJY_UWCwaGPrQrt_GunxmOK9AUnzURqm55GR2RXIZtjsWVP2EBatsDgHRmuUbQvTFOv
dJB4x3nXcYLN2opAaMqg3rnU2rr-A8zCrIuX_eca12wIp_QiuP3SF-
tzpdLpsyRfegTJZl6YnSGyaVkC9id-
cxZRb307qdCfXPfCHR_2rt5FVfxARgg_C0e3eFHaaYQO7CitxsnIoIXpOFNAR8aUrmopJyODQIPq
BWUehb7FhlU1DCduHnIIXVC_UICZ-MKYewBDLw
ca.crt: 1025 bytes
3)通过浏览器访问Dashboard的UI
在登录页面上输入上面的token
出现下面的页面代表成功
使用DashBoard
本章节以Deployment为例演示DashBoard的使用
查看
选择指定的命名空间 dev ,然后点击 Deployments ,查看dev空间下的所有deployment
扩缩容
在 Deployment 上点击 规模 ,然后指定 目标副本数量 ,点击确定编辑
编辑
在 Deployment 上点击 编辑 ,然后修改 yaml文件 ,点击确定
查看Pod
点击 Pods , 查看pods列表
操作Pod
选中某个Pod,可以对其执行日志(logs)、进入执行(exec)、编辑、删除操作
原文链接:https://blog.51cto.com/syjblog/5665875