Kubernetes基础
Pod 容器组 是一个k8s中一个抽象的概念,用于存放一组 container(可包含一个或多个 container 容器,即图上正方体),以及这些 container (容器)的一些共享资源。这些资源包括:共享存储,称为卷(Volumes),即图上紫色圆柱网络,每个 Pod(容器组)在集群中有个唯一的 IP,pod(容器组)中的 container(容器)共享该IP地址container(容器)
一.k8s的概念
Kubernetes(简称 k8s)是一个开源的容器编排引擎,它可以自动化部署、扩展和管理容器化应用。它提供了强大的功能,如自动调度、弹性伸缩、服务发现、负载均衡等,使得容器化应用的部署和管理更加高效、可靠和便捷。通过 k8s,你可以轻松地在不同的环境中运行和管理大规模的容器化应用,提高开发和运维效率。
在Docker 作为高级容器引擎快速发展的同时,在Google内部,容器技术已经应用了很多年
Borg系统运行管理着成千上万的容器应用。
Kubernetes项目来源于Borg,可以说是集结了Borg设计思想的精华,并且吸收了Borg系统中的经验和教训。
Kubernetes对计算资源进行了更高层次的抽象,通过将容器进行细致的组合,将最终的应用服务交给用户。
kubernetes的本质是一组服务器集群,它可以在集群的每个节点上运行特定的程序,来对节点中的容器进行管理。目的是实现资源管理的自动化,主要提供了如下的主要功能:
自我修复:一旦某一个容器崩溃,能够在1秒中左右迅速启动新的容器
弹性伸缩:可以根据需要,自动对集群中正在运行的容器数量进行调整
服务发现:服务可以通过自动发现的形式找到它所依赖的服务
负载均衡:如果一个服务起动了多个容器,能够自动实现请求的负载均衡
版本回退:如果发现新发布的程序版本有问题,可以立即回退到原来的版本
存储编排:可以根据容器自身的需求自动创建存储卷
k8s各个组件及用途
一个kubernetes集群主要是由控制节点(master)、工作节点(node)构成,每个节点上都会安装不同的组件。
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、集群自身的配置和管理等
Kubernetes 架构图

分层架构

生态系统
在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴:
Kubernetes 外部:日志、监控、配置管理、CI、CD、Workflow、FaaS、OTS 应用、ChatOps 等
Kubernetes 内部:CRI、CNI、CVI、镜像仓库、Cloud Provider、集群自身的配置和管理等
接口层: kubectl 命令行工具、客户端 SDK 以及集群联邦
管理层 : 系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态 Provision 等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy 等)
应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS 解析等)
核心层:Kubernetes 最核心的功能,对外提供 API 构建高层的应用,对内提供插件式应用执行环境
二.K8s部署说明
1、主机操作系统说明
| 序号 | 操作系统及版本 | |
|---|---|---|
| 1 | RHEL9 |
2、主机硬件配置说明
| 主机名 | ip | 角色 |
| reg.timinglee.org | 192.168.163.10 | harbor仓库 |
| master | 192.168.163.11 | master,k8s集群控制节点 |
| node1 | 192.168.163.12 | worker,k8s集群工作节点 |
|
node2 |
192.168.163.13 | worker,k8s集群工作节点 |
七、操作准备(四台机器·同时操作)
-
所有节点禁用selinux和防火墙
-
所有节点同步时间和解析
-
所有节点安装docker-ce
-
所有节点禁用swap,注意注释掉/etc/fstab文件中的定义
所有k8s集群节点执行以下步骤
1.所有禁用swap和本地解析
systemctl mask swap.target
swapoff -a
vim /etc/fstab
#
# /etc/fstab
# Created by anaconda on Sun Feb 19 17:38:40 2023
#
# Accessible filesystems, by reference, are maintained under '/dev/disk'
# See man pages fstab(5), findfs(8), mount(8) and/or blkid(8) for more info
#
/dev/mapper/rhel-root / xfs defaults 0 0
UUID=ddb06c77-c9da-4e92-afd7-53cd76e6a94a /boot xfs defaults 0 0
#/dev/mapper/rhel-swap swap swap defaults 0 0
/dev/cdrom /media iso9660 defaults 0 0
所有主机安装docker
[root@k8s-master ~]# vim /etc/yum.repos.d/docker.repo
[docker]
name=docker
baseurl=https://mirrors.aliyun.com/docker-ce/linux/rhel/9/x86_64/stable/
gpgcheck=0

所有节点设定harbor仓库的域名地址为默认仓库源
vim /etc/docker/daemon.json
{
"registry-mirrors":["https://reg.timinglee.org"]
}
[root@master ~]# vim /lib/systemd/system/docker.service

harbor仓库配置
[root@reg packages]# tar -zxf harbor-offline-installer-v2.5.4.tgz
[root@reg packages]# mkdir -p /data/certs[root@reg packages]# 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@reg harbor]# cp harbor.yml.tmpl harbor.yml
[root@reg harbor]# vim harbor.yml

复制harbor仓库中的证书并启动docker
[root@k8s-harbor ~]# cp /etc/docker/certs.d/reg.timinglee.org/ca.crt root@192.168.234.10:/etc/docker/certs.d/reg.timinglee.org/ca.crt
[root@k8s-harbor ~]# cp /etc/docker/certs.d/reg.timinglee.org/ca.crt root@192.168.234.20:/etc/docker/certs.d/reg.timinglee.org/ca.crt
[root@k8s-harbor ~]# cp /etc/docker/certs.d/reg.timinglee.org/ca.crt root@192.168.234.100:/etc/docker/certs.d/reg.timinglee.org/ca.crt
[root@k8s-harbor ~]# systemctl enable --now docker
#登陆harbor仓库
[root@k8s-master ~]# docker login reg.timinglee.org

[root@master ~]# docker info

三台主机上都要安装K8S部署工具
[root@master ~]# ls
anaconda-ks.cfg
cri-dockerd-0.3.14-3.el8.x86_64.rpm
k8s-1.30.tar.gz
libcgroup-0.41-19.el8.x86_64.rpm

[root@k8s-master ~]# vim /lib/systemd/system/cri-docker.service
[root@master ~]# systemctl enable --now cri-docker.service
Created symlink
/etc/systemd/system/multi-user.target.wants/cri-docker.service → /u
sr/lib/systemd/system/cri-docker.service.
[root@master ~]#
设置kubectl命令补齐功能
[root@k8s-master ~]# dnf install bash-completion -y
[root@k8s-master ~]# echo "source <(kubectl completion bash)" >> ~/.bashrc
[root@k8s-master ~]# source ~/.bashrc
上传镜像到harbor仓库
[root@master ~]# docker load -i k8s_docker_images-1.30.tar
3d6fa0469044: Loading layer 327.7kB/327.7kB
49626df344c9: Loading layer 40.96kB/40.96kB
945d17be9a3e: Loading layer 2.396MB/2.396MB
4d049f83d9cf: Loading layer 1.536kB/1.536kB
af5aa97ebe6c: Loading layer 2.56kB/2.56kB
ac805962e479: Loading layer 2.56kB/2.56kB
bbb6cacb8c82: Loading layer 2.56kB/2.56kB
2a92d6ac9e4f: Loading layer 1.536kB/1.536kB
1a73b54f556b: Loading layer 10.24kB/10.24kB
f4aee9e53c42: Loading layer 3.072kB/3.072kB
b336e209998f: Loading layer 238.6kB/238.6kB
06ddf169d3f3: Loading layer 1.69MB/1.69MB
c0cb02961a3c: Loading layer 112.9MB/112.9MB
Loaded image: registry.aliyuncs.com/google_containers/kube-apiserver:v1.30.0
[root@master ~]# docker images
REPOSITORY
ED SIZE
registry.aliyuncs.com/google_containers/kube-apiserver TAG IMAG
nths ago 117MB v1.30.0 c42f
registry.aliyuncs.com/google_containers/kube-controller-manager
nths ago 111MB v1.30.0 c7aa
registry.aliyuncs.com/google_containers/kube-scheduler
nths ago 62MB v1.30.0 259c
registry.aliyuncs.com/google_containers/kube-proxy
nths ago 84.7MB v1.30.0 a0bf
registry.aliyuncs.com/google_containers/etcd 3.5.12-0 3861
nths ago 149MB
registry.aliyuncs.com/google_containers/coredns v1.11.1 cbb0
nths ago 59.8MB
registry.aliyuncs.com/google_containers/pause 3.9 e6f1
rs ago 744kB
[root@master ~]#
[root@k8s-master ~]# docker images | awk '/google/{ print $1":"$2}' \
| awk -F "/" '{system("docker tag "$0" reg.timinglee.org/k8s/"$3)}'
[root@master ~]# docker images | awk '/reg/{print $1":"$2}'
reg.timinglee.org/k8s/kube-apiserver:v1.30.0
registry.aliyuncs.com/google_containers/kube-apiserver:v1.30.0
reg.timinglee.org/k8s/kube-controller-manager:v1.30.0
registry.aliyuncs.com/google_containers/kube-controller-manager:v1.30.0
registry.aliyuncs.com/google_containers/kube-scheduler:v1.30.0
reg.timinglee.org/k8s/kube-scheduler:v1.30.0
reg.timinglee.org/k8s/kube-proxy:v1.30.0
registry.aliyuncs.com/google_containers/kube-proxy:v1.30.0
reg.timinglee.org/k8s/etcd:3.5.12-0
registry.aliyuncs.com/google_containers/etcd:3.5.12-0
registry.aliyuncs.com/google_containers/coredns:v1.11.1
reg.timinglee.org/k8s/coredns:v1.11.1
reg.timinglee.org/k8s/pause:3.9
registry.aliyuncs.com/google_containers/pause:3.9
[root@master ~]#

[root@master ~]# systemctl enable kubelet --now #所有主机都要记得重启
集群初始化
#执行初始化命令
[root@k8s-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@k8s-master ~]# echo "export KUBECONFIG=/etc/kubernetes/admin.conf" >> ~/.bash_profile
#当前节点没有就绪,因为还没有安装网络插件,容器没有运行
安装flannel网络插件
[root@master ~]# docker load -i flannel-0.25.5.tag.gz
ef7a14b43c43: Loading layer 8.079MB/8.079MB
1d9375ff0a15: Loading layer 9.222MB/9.222MB
4af63c5dc42d: Loading layer
16.61MB/16.61MB
2b1d26302574: Loading layer 1.544MB/1.544MB
d3dd49a2e686: Loading layer 42.11MB/42.11MB
7278dc615b95: Loading layer 5.632kB/5.632kB
c09744fc6e92: Loading layer 6.144kB/6.144kB
0a2b46a5555f: Loading layer 1.923MB/1.923MB
5f70bf18a086: Loading layer
1.024kB/1.024kB
601effcb7aab: Loading layer
1.928MB/1.928MB
21692b7dc30c:
Loading layer
Loaded image: flannel/flannel:v0.25.5
Loaded image: flannel/flannel-cni-plugin:v1.5.1-flannel1
2.634MB/2.634MB
[root@master ~]#
在harbor仓库上创建一个flannel

#上传镜像到仓库
[root@k8s-master ~]# docker tag flannel/flannel:v0.25.5 \
reg.timinglee.org/flannel/flannel:v0.25.5
[root@k8s-master ~]# docker push reg.timinglee.org/flannel/flannel:v0.25.5
[root@k8s-master ~]# docker tag flannel/flannel-cni-plugin:v1.5.1-flannel1 \
reg.timinglee.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
[root@k8s-master ~]# docker push reg.timinglee.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
#编辑kube-flannel.yml 修改镜像下载位置
[root@k8s-master ~]# vim kube-flannel.yml
146: image: reg.timinglee.org/flannel/flannel:v0.25.5
173: image: reg.timinglee.org/flannel/flannel-cni-plugin:v1.5.1-flannel1
184: image: reg.timinglee.org/flannel/flannel:v0.25.5
[root@k8s-master ~]# kubectl apply -f kube-flannel.yml
节点扩容
#在此阶段如果生成的集群token找不到了可以重新生成
[root@k8s-master ~]# kubeadm token create --print-join-command
kubeadm join 172.25.254.100:6443 --token 5hwptm.zwn7epa6pvatbpwf --discovery-token-ca-cert-hash sha256:52f1a83b70ffc8744db5570288ab51987ef2b563bf906ba4244a300f61e9db23[root@k8s-node1 & 2 ~]# kubeadm join 192.168.234.10:6443 --token 5hwptm.zwn7epa6pvatbpwf --discovery-token-ca-cert-hash sha256:52f1a83b70ffc8744db5570288ab51987ef2b563bf906ba4244a300f61e9db23 --cri-socket=unix:///var/run/cri-dockerd.sock #在node1和node2上操作
部署完成!!!
服务部署

四、K8s的pod概述和配置
Pod 容器组 是一个k8s中一个抽象的概念,用于存放一组 container(可包含一个或多个 container 容器,即图上正方体),以及这些 container (容器)的一些共享资源。这些资源包括:
共享存储,称为卷(Volumes),即图上紫色圆柱
网络,每个 Pod(容器组)在集群中有个唯一的 IP,pod(容器组)中的 container(容器)共享该IP地址
container(容器)的基本信息,例如容器的镜像版本,对外暴露的端口等
每个Pod中都可以包含一个或者多个容器,这些容器可以分为两类:
用户程序所在的容器,数量可多可少
Pause容器,这是每个Pod都会有的一个根容器,它的作用有两个:
可以以它为依据,评估整个Pod的健康状态
可以在根容器上设置Ip地址,其它容器都此Ip(Pod IP),以实现Pod内部的网路通信
pod的定义
apiVersion: v1 #必选,版本号,例如v1
kind: Pod #必选,资源类型,例如 Pod
metadata: #必选,元数据
name: string #必选,Pod名称
namespace: string #Pod所属的命名空间,默认为"default"
labels: #自定义标签列表
- name: string
spec: #必选,Pod中容器的详细定义
containers: #必选,Pod中容器列表
- name: string #必选,容器名称
image: string #必选,容器的镜像名称
imagePullPolicy: [ Always|Never|IfNotPresent ] #获取镜像的策略
command: [string] #容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: [string] #容器的启动命令参数列表
workingDir: string #容器的工作目录
volumeMounts: #挂载到容器内部的存储卷配置
- name: string #引用pod定义的共享存储卷的名称,需用volumes[]部分定义的的卷名
mountPath: string #存储卷在容器内mount的绝对路径,应少于512字符
readOnly: boolean #是否为只读模式
ports: #需要暴露的端口库号列表
- name: string #端口的名称
containerPort: int #容器需要监听的端口号
hostPort: int #容器所在主机需要监听的端口号,默认与Container相同
protocol: string #端口协议,支持TCP和UDP,默认TCP
env: #容器运行前需设置的环境变量列表
- name: string #环境变量名称
value: string #环境变量的值
resources: #资源限制和请求的设置
limits: #资源限制的设置
cpu: string #Cpu的限制,单位为core数,将用于docker run --cpu-shares参数
memory: string #内存限制,单位可以为Mib/Gib,将用于docker run --memory参数
requests: #资源请求的设置
cpu: string #Cpu请求,容器启动的初始可用数量
memory: string #内存请求,容器启动的初始可用数量
lifecycle: #生命周期钩子
postStart: #容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
preStop: #容器终止前执行此钩子,无论结果如何,容器都会终止
livenessProbe: #对Pod内各容器健康检查的设置,当探测无响应几次后将自动重启该容器
exec: #对Pod容器内检查方式设置为exec方式
command: [string] #exec方式需要制定的命令或脚本
httpGet: #对Pod内个容器健康检查方法设置为HttpGet,需要制定Path、port
path: string
port: number
host: string
scheme: string
HttpHeaders:
- name: string
value: string
tcpSocket: #对Pod内个容器健康检查方式设置为tcpSocket方式
port: number
initialDelaySeconds: 0 #容器启动完成后首次探测的时间,单位为秒
timeoutSeconds: 0 #对容器健康检查探测等待响应的超时时间,单位秒,默认1秒
periodSeconds: 0 #对容器监控检查的定期探测时间设置,单位秒,默认10秒一
次
successThreshold: 0
failureThreshold: 0
securityContext:
privileged: false
restartPolicy: [Always | Never | OnFailure] #Pod的重启策略
nodeName: <string> #设置NodeName表示将该Pod调度到指定到名称的node节点上
nodeSelector: obeject #设置NodeSelector表示将该Pod调度到包含这个label的node上
imagePullSecrets: #Pull镜像时使用的secret名称,以key:secretkey格式指定
- name: string
hostNetwork: false #是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机
网络
volumes: #在该pod上定义共享存储卷列表
- name: string #共享存储卷名称 (volumes类型有很多种)
emptyDir: {} #类型为emtyDir的存储卷,与Pod同生命周期的一个临时目录。为空值
hostPath: string #类型为hostPath的存储卷,表示挂载Pod所在宿主机的目录
path: string #Pod所在宿主机的目录,将被用于同期中mount的目录
secret: #类型为secret的存储卷,挂载集群与定义的secret对象到容器内部
scretname: string
items:
- key: string
path: string
configMap: #类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
name: string
items:
- key: string
path: string
配置
基本配置
创建nginxpod.yaml,写入
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: v1
kind: Pod
metadata:
name: nginxpod
namespace: dev
spec:
containers:
- name: nginx-containers
image: nginx:1.17.1
镜像拉取
在nginxpod.yaml添加
apiVersion: v1
kind: Namespace
metadata:
name: dev
---
apiVersion: v1
kind: Pod
metadata:
name: nginxpod
namespace: dev
spec:
containers:
- name: nginx-containers
image: nginx:1.17.1
imagePullPolicy: Always #设置镜像拉取策略为always
imagePullPolicy,用于设置镜像拉取策略,kubernetes支持配置三种拉取策略:
Always:总是从远程仓库拉取镜像(一直远程下载)
IfNotPresent:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像(本地有就本地 本地没远程下载)
Never:只使用本地镜像,从不去远程仓库拉取,本地没有就报错 (一直使用本地)
[root@k8s-master01 nginx]# kubectl delete -f nginxpod.yaml #先删除
namespace "dev" deleted
pod "nginxpod" deleted
[root@k8s-master01 nginx]# kubectl create -f nginxpod.yaml #再创建
namespace/dev created
pod/nginxpod created
[root@k8s-master01 nginx]# kubectl describe pod nginxpod -n dev #查看创建pod详情
Name: nginxpod
Namespace: dev
Priority: 0
Service Account: default
Node: k8s-worker02/192.168.234.14
Start Time: Sun, 23 Mar 2025 21:34:57 +0800
Labels: <none>
Annotations: cni.projectcalico.org/containerID: b3e8244aebfa692ac22dfc5a2bb e17931600f1253537454a521adb4da7d547dd
cni.projectcalico.org/podIP: 100.73.45.88/32
cni.projectcalico.org/podIPs: 100.73.45.88/32
Status: Running
IP: 100.73.45.88
IPs:
IP: 100.73.45.88
Containers:
nginx-containers:
Container ID: docker://f24c8c0bff883217f8730d42669a47acbc8800a1e8ca44738b4 fb285eb2a5f4a
Image: nginx:1.17.1
Image ID: docker-pullable://nginx@sha256:b4b9b3eee194703fc2fa8afa5b751 0c77ae70cfba567af1376a573a967c03dbb
Port: <none>
Host Port: <none>
State: Running
Started: Sun, 23 Mar 2025 21:35:01 +0800
Ready: True
Restart Count: 0
Environment: <none>
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-dn4zd ( ro)
Conditions:
Type Status
PodReadyToStartContainers True
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
kube-api-access-dn4zd:
Type: Projected (a volume that contains injected data fro m multiple sources)
TokenExpirationSeconds: 3607
ConfigMapName: kube-root-ca.crt
ConfigMapOptional: <nil>
DownwardAPI: true
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/not-ready:NoExecute op=Exists fo r 300s
node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal Scheduled 50s default-scheduler Successfully assigned dev/nginxpod to k8s-worker02
Normal Pulling 22m kubelet Pulling image "nginx:1.17.1"
Normal Pulled 22m kubelet Successfully pulled image "nginx:1 .17.1" in 2.636s (2.636s including waiting). Image size: 109357455 bytes.
Normal Created 22m kubelet Created container nginx-containers
Normal Started 22m kubelet Started container nginx-containers
端口设置
[root@k8s-master01 nginx]# kubectl explain pod.spec.containers.ports
KIND: Pod
VERSION: v1
RESOURCE: ports <[]Object>
FIELDS:
name <string> # 端口名称,如果指定,必须保证name在pod中是唯一的
containerPort<integer> # 容器要监听的端口(0<x<65536)
hostPort <integer> # 容器要在主机上公开的端口,如果设置,主机上只能运行容器的一个副
本(一般省略)
hostIP <string> # 要将外部端口绑定到的主机IP(一般省略)
protocol <string> # 端口协议。必须是UDP、TCP或SCTP。默认为“TCP”
创建pod-ports.yam
apiVersion: v1
kind: Pod
metadata:
name: pod-ports
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports: # 设置容器暴露的端口列表
- name: nginx-port
containerPort: 80
protocol: TCP
资源配额
容器中的程序要运行,肯定是要占用一定资源的,比如cpu和内存等,如果不对某个容器的资源做限
制,那么它就可能吃掉大量资源,导致其它容器无法运行。针对这种情况,kubernetes提供了对内存和
cpu的资源进行配额的机制,这种机制主要通过resources选项实现,他有两个子选项:
limits:用于限制运行时容器的最大占用资源,当容器占用资源超过limits时会被终止,并进行重启
requests :用于设置容器需要的最小资源,如果环境资源不够,容器将无法启动
可以通过上面两个选项设置资源的上下限。
创建pod-resources.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-resources
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
resources:
limits:
cpu: "2"
memory: "2Gi" # 调整后的内存限制
requests:
cpu: "1"
memory: "10Mi" # 调整后的内存请求
- cpu:core数,可以为整数或小数 memory:
- 内存大小,可以使用Gi、Mi、G、M等形式
# 运行Pod [root@master ~]# kubectl create -f pod-resources.yaml pod/pod-resources created # 查看发现pod运行正常 [root@master ~]# kubectl get pod pod-resources -n dev NAME READY STATUS RESTARTS AGE pod-resources 1/1 Running 0 39s # 接下来,停止Pod [root@master ~]# kubectl delete -f pod-resources.yaml pod "pod-resources" deleted # 编辑pod,修改resources.requests.memory的值为10Gi [root@master ~]# vim pod-resources.yaml # 再次启动pod [root@master ~]# kubectl create -f pod-resources.yaml pod/pod-resources created # 查看Pod状态,发现Pod启动失败 [root@master ~]# kubectl get pod pod-resources -n dev -o wide NAME READY STATUS RESTARTS AGE pod-resources 0/2 Pending 0 20s # 查看pod详情会发现,如下提示 [root@master ~]# kubectl describe pod pod-resources -n dev ...... Warning FailedScheduling <unknown> default-scheduler 0/2 nodes are available: 2 Insufficient memory.(内存不足)K8s控制器之Deployment
概述Deployment 是最常用的用于部署无状态服务的方式。Deployment 控制器使得您能够以声明的方式更新 Pod(容器组)和 ReplicaSet(副本集)。该控制器并不直接管理pod,而是通过管理ReplicaSet来间接管理Pod,即:Deployment管 理ReplicaSet,ReplicaSet管理Pod。所以Deployment比ReplicaSet功能更加强大。
Deployment 为我们确定了如下几种运维场景:
创建 Deployment : 创建 Deployment 后,Deployment 控制器将立刻创建一个 ReplicaSet 副本集,并由 ReplicaSet 创建所需要的 Pod。
更新 Deployment: 更新 Deployment 中 Pod 的定义(例如,发布新版本的容器镜像)。此时 Deployment 控制器将为该 Deployment 创建一个新的 ReplicaSet 副本集,并且逐步在新的副本集中创建 Pod,在旧的副本集中删除 Pod,以达到滚动更新的效果。
回滚Deployment: 回滚到一个早期 Deployment 版本。
伸缩Deployment: 水平扩展 Deployment,以便支持更大的负载,或者水平收缩 Deployment,以便节省服务器资源。
暂停和继续Deployment
查看Deployment状态
清理策略
金丝雀发布
Deployment配置详情
apiVersion: apps/v1 # 版本号
kind: Deployment # 类型
metadata: # 元数据
name: # rs名称
namespace: # 所属命名空间
labels: #标签
controller: deploy
spec: # 详情描述
replicas: 3 # 副本数量
revisionHistoryLimit: 3 # 保留历史版本
paused: false # 暂停部署,默认是false
progressDeadlineSeconds: 600 # 部署超时时间(s),默认是600
strategy: # 策略
type: RollingUpdate # 滚动更新策略
rollingUpdate: # 滚动更新
maxSurge: 30% # 最大额外可以存在的副本数,可以为百分比,也可以为整数
maxUnavailable: 30% # 最大不可用状态的 Pod 的最大值,可以为百分比,也可以为整数
selector: # 选择器,通过它指定该控制器管理哪些pod
matchLabels: # Labels匹配规则
app: nginx-pod
matchExpressions: # Expressions匹配规则
- {key: app, operator: In, values: [nginx-pod]}
template: # 模板,当副本数量不足时,会根据下面的模板创建pod副本
metadata:
labels:
app: nginx-pod
spec:
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
创建Deployment
下面的 yaml 文件定义了一个 Deployment,该 Deployment 将创建一个有 3 个 nginx Pod 副本的 ReplicaSet(副本集):
apiVersion: apps/v1
kind: Deployment
metadata:
name: 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
在这个例子中:
将创建一个名为 deployment 的 Deployment(部署),名称由 .metadata.name 字段指定
该 Deployment 将创建 3 个 Pod 副本,副本数量由 .spec.replicas 字段指定
.spec.selector 字段指定了 Deployment 如何找到由它管理的 Pod。此案例中,我们使用了 Pod template 中定义的一个标签(app: nginx-pod)。对于极少数的情况,这个字段也可以定义更加复杂的规则
.template 字段包含了如下字段:
.template.metadata.labels 字段,指定了 Pod 的标签(app: nginx-pod)
.template.spec.containers[].image 字段,表明该 Pod 运行一个容器 nginx:1.17.1
.template.spec.containers[].name 字段,表明该容器的名字是 nginx
# 创建deployment
[root@k8s-master01 dev]# kubectl create -f deployment.yaml
deployment.apps/deployment created
# 查看deployment
[root@k8s-master01 dev]# kubectl get deployments.apps -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
deployment 3/3 3 3 6m37s
#查看rs
[root@k8s-master01 dev]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
deployment-7cffcbf558 3 3 3 8m36s
# 发现rs的名称是在原来deployment的名字后面添加了一个10位数的随机串
# 查看pod
[root@k8s-master01 dev]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
deployment-7cffcbf558-m8qw7 1/1 Running 0 10m
deployment-7cffcbf558-nhhth 1/1 Running 0 10m
deployment-7cffcbf558-xwxhw 1/1 Running 0 10m
伸缩 Deployment
# 变更副本数量为5个
[root@k8s-master01 dev]# kubectl scale deployment deployment-test --replicas 5 -n dev
deployment.apps/deployment-test scaled
# 查看deployment
[root@k8s-master01 dev]# kubectl get deployments.apps deployment-test -n dev
NAME READY UP-TO-DATE AVAILABLE AGE
deployment-test 5/5 5 5 5m26s
# 查看pod
[root@k8s-master01 dev]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
deployment-test-7cffcbf558-46hg7 1/1 Running 0 6m16s
deployment-test-7cffcbf558-5vjqw 1/1 Running 0 6m16s
deployment-test-7cffcbf558-fj846 1/1 Running 0 114s
deployment-test-7cffcbf558-mdz8q 1/1 Running 0 114s
deployment-test-7cffcbf558-ws965 1/1 Running 0 6m16s
# 编辑deployment的副本数量,修改spec:replicas: 3
[root@k8s-master01 dev]# kubectl edit deployments.apps deployment-test -n dev
deployment.apps/deployment-test edited
# 查看pod
[root@k8s-master01 dev]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
deployment-test-7cffcbf558-46hg7 1/1 Running 0 8m37s
deployment-test-7cffcbf558-5vjqw 1/1 Running 0 8m37s
deployment-test-7cffcbf558-mdz8q 1/1 Running 0 4m15s
更新 Deployment
deployment支持两种更新策略: 重建更新 和 滚动更新 ,可以通过 strategy 指定策略类型,支持两个属性:
strategy:指定新的Pod替换旧的Pod的策略, 支持两个属性:
type:指定策略类型,支持两种策略
Recreate:在创建出新的Pod之前会先杀掉所有已存在的Pod
RollingUpdate:滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本Pod
rollingUpdate:当type为RollingUpdate时生效,用于为RollingUpdate设置参数,支持两个属
性:
maxUnavailable:用来指定在升级过程中不可用Pod的最大数量,默认为25%。
maxSurge: 用来指定在升级过程中可以超过期望的Pod的最大数量,默认为25%
重建更新
重建部署策略是先把旧的pod全部停掉,然后新建pod。由于部署期间出现服务中断,这种部署策略很少用。
在spec节点下添加更新策略
spec:
strategy: # 策略
type: Recreate # 重建更新
创建deploy进行验证
#修改镜像
[root@k8s-master01 dev]# kubectl set image deployment deployment-test nginx=nginx:1.17.2 -n dev
deployment.apps/deployment-test image updated
#查看更新后的 Deployment 的详情
[root@k8s-master01 dev]# kubectl get deployments.apps -n dev -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment-test 3/3 3 3 39m nginx nginx:1.17.2 app=nginx-pod
滚动更新
在spec节点下添加更新策略
spec:
strategy: # 策略
type: RollingUpdate # 滚动更新策略
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
创建deploy进行验证
#修改镜像
[root@k8s-master01 dev]# kubectl set image deployment deployment-test nginx=nginx:1.17.3 -n dev
deployment.apps/deployment-test image updated
[root@k8s-master01 dev]# kubectl get deployments.apps -n dev -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment-test 3/3 3 3 55m nginx nginx:1.17.3 app=nginx-pod
#滚动更新是通过创建一个新的 3 个副本数的 ReplicaSet 并同时将旧的 Replicaset 的副本数缩容到 0 个副本 来达成的。
[root@k8s-master01 dev]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
deployment-test-5b6ffdcd8d 3 3 3 5m37s
deployment-test-79dbdc995f 0 0 0 30m
deployment-test-7cffcbf558 0 0 0 56m
Deployment 将确保更新过程中,任意时刻只有一定数量的 Pod 被关闭。默认情况下,Deployment 确保至少 .spec.replicas 的 75% 的 Pod 保持可用(25% max unavailable)
Deployment 将确保更新过程中,任意时刻只有一定数量的 Pod 被创建。默认情况下,Deployment 确保最多 .spec.replicas 的 25% 的 Pod 被创建(25% max surge)
Deployment Controller 先创建一个新 Pod,然后删除一个旧 Pod,然后再创建新的,如此循环,直到全部更新。Deployment Controller 不会 kill 旧的 Pod,除非足够数量的新 Pod 已经就绪,Deployment Controller 也不会创新新 Pod 直到足够数量的旧 Pod 已经被 kill。
#查看 Deployment 详情
[root@k8s-master01 dev]# kubectl describe deployments.apps
Name: deployment-test
Namespace: dev
CreationTimestamp: Sat, 05 Apr 2025 19:28:48 +0800
Labels: <none>
Annotations: deployment.kubernetes.io/revision: 3
Selector: app=nginx-pod
Replicas: 3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType: RollingUpdate
MinReadySeconds: 0
RollingUpdateStrategy: 25% max unavailable, 25% max surge
Pod Template:
Labels: app=nginx-pod
Containers:
nginx:
Image: nginx:1.17.3
Port: <none>
Host Port: <none>
Environment: <none>
Mounts: <none>
Volumes: <none>
Node-Selectors: <none>
Tolerations: <none>
Conditions:
Type Status Reason
---- ------ ------
Available True MinimumReplicasAvailable
Progressing True NewReplicaSetAvailable
OldReplicaSets: deployment-test-7cffcbf558 (0/0 replicas created), deployment-test-79dbdc995f (0/0 replicas created)
NewReplicaSet: deployment-test-5b6ffdcd8d (3/3 replicas created)
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Normal ScalingReplicaSet 60m deployment-controller Scaled up replica set deployment-test-7cffcbf558 to 3
Normal ScalingReplicaSet 55m deployment-controller Scaled up replica set deployment-test-7cffcbf558 to 5 from 3
Normal ScalingReplicaSet 52m deployment-controller Scaled down replica set deployment-test-7cffcbf558 to 3 from 5
Normal ScalingReplicaSet 34m deployment-controller Scaled down replica set deployment-test-7cffcbf558 to 0 from 3
Normal ScalingReplicaSet 34m deployment-controller Scaled up replica set deployment-test-79dbdc995f to 3
Normal ScalingReplicaSet 9m5s deployment-controller Scaled up replica set deployment-test-5b6ffdcd8d to 1
Normal ScalingReplicaSet 7m37s deployment-controller Scaled down replica set deployment-test-79dbdc995f to 2 from 3
Normal ScalingReplicaSet 7m37s deployment-controller Scaled up replica set deployment-test-5b6ffdcd8d to 2 from 1
Normal ScalingReplicaSet 6m8s deployment-controller Scaled down replica set deployment-test-79dbdc995f to 1 from 2
Normal ScalingReplicaSet 6m8s deployment-controller Scaled up replica set deployment-test-5b6ffdcd8d to 3 from 2
Normal ScalingReplicaSet 6m6s deployment-controller Scaled down replica set deployment-test-79dbdc995f to 0 from 1
在 Events 中,可以看到:
创建 Deployment 时,Deployment Controller 创建了一个 ReplicaSet并直接将其scale up 到 3 个副本。
当更新 Deployment 时,Deployment Controller 先创建一个新的 ReplicaSet并将其 scale up 到 1 个副本,然后 scale down 旧的 ReplicaSet 到 2。
Deployment Controller 继续 scale up 新的 ReplicaSet 并 scale down 旧的 ReplicaSet,直到最后,新旧两个 ReplicaSet,一个副本数为 3,另一个副本数为 0。
回滚 Deployment
当想要回滚(rollback)Deployment,例如:Deployment 不稳定(可能是不断地崩溃)。默认情况下,kubernetes 将保存 Deployment 的所有更新(rollout)历史。
kubectl rollout: 版本升级相关功能,支持下面的选项:
status 显示当前升级状态
history 显示 升级历史记录
pause 暂停版本升级过程
resume 继续已经暂停的版本升级过程
restart 重启版本升级过程
undo 回滚到上一级版本(可以使用--to-revision回滚到指定版本)
————————————————
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/2302_81107022/article/details/150764644
# 查看当前升级版本的状态
[root@k8s-master01 dev]# kubectl rollout status deploy deployment-test -n dev
deployment "deployment-test" successfully rolled out
# 查看升级历史记录
[root@k8s-master01 dev]# kubectl rollout history deploy deployment-test -n dev
deployment.apps/deployment-test
REVISION CHANGE-CAUSE
1 kubectl create --filename=deployment.yaml --record=true
2 kubectl create --filename=deployment.yaml --record=true
3 kubectl create --filename=deployment.yaml --record=true
# 可以发现有三次版本记录,说明完成过两次升级
#使用--to-revision=1回滚到了1版本, 如果省略这个选项,就是回退到上个版本,就是2版本
[root@k8s-master01 dev]# kubectl rollout undo deployment deployment-test --to-revision=1 -n dev
deployment.apps/deployment-test rolled back
# 查看,nginx镜像版本到了第一版
[root@k8s-master01 dev]# kubectl get deployments.apps -n dev -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
deployment-test 3/3 3 3 7m31s nginx nginx:1.17.1 app=nginx-pod
# 查看rs,发现第3个rs中有3个pod运行,前面两个版本的rs中pod为运行
[root@k8s-master01 dev]# kubectl get rs -n dev
NAME DESIRED CURRENT READY AGE
deployment-test-5b6ffdcd8d 0 0 0 6m20s
deployment-test-79dbdc995f 0 0 0 6m39s
deployment-test-7cffcbf558 3 3 3 9m33s
其实deployment之所以可是实现版本的回滚,就是通过记录下历史rs来实现的
一旦想回滚到哪个版本,只需要将当前版本pod数量降为0,然后将回滚版本的pod提升为目标数量就可以了
金丝雀发布(灰度发布)
Deployment控制器支持控制更新过程中的控制,如“暂停(pause)”或“继续(resume)”更新操作。 比如有一批新的Pod资源创建完成后立即暂停更新过程,此时,仅存在一部分新版本的应用,主体部 分还是旧的版本。然后,再筛选一小部分的用户请求路由到新版本的Pod应用,继续观察能否稳定地按 期望的方式运行。确定没问题之后再继续完成余下的Pod资源滚动更新,否则立即回滚更新操作。这就是所谓的金丝雀发布。
# 更新deployment的版本,并配置暂停deployment
[root@k8s-master01 dev]# kubectl set image deploy deployment-test nginx=nginx:1.17.5 -n dev && kubectl rollout pause deployment deployment-test -n dev
deployment.apps/deployment-test image updated
deployment.apps/deployment-test paused
#观察更新状态
[root@k8s-master01 dev]# kubectl rollout status deploy deployment-test -n dev
Waiting for deployment "deployment-test" rollout to finish: 0 out of 3 new replicas have been updated...
# 监控更新的过程,可以看到已经新增了一个资源,但是并未按照预期的状态去删除一个旧的资源,就是因为使用了pause暂停命令
[root@k8s-master01 dev]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
deployment-test-5b6ffdcd8d 0 0 0 20m nginx nginx:1.17.3 app=nginx-pod,pod-template-hash=5b6ffdcd8d
deployment-test-79dbdc995f 0 0 0 20m nginx nginx:1.17.2 app=nginx-pod,pod-template-hash=79dbdc995f
deployment-test-7cffcbf558 2 2 2 23m nginx nginx:1.17.1 app=nginx-pod,pod-template-hash=7cffcbf558
deployment-test-845cbff7c8 1 1 0 60s nginx nginx:1.17.5 app=nginx-pod,pod-template-hash=845cbff7c8
deployment-test-f5b4bdd49 1 1 1 6m53s nginx nginx:1.17.4 app=nginx-pod,pod-template-hash=f5b4bdd49
[root@k8s-master01 dev]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
deployment-test-7cffcbf558-6xj55 1/1 Running 0 15m
deployment-test-7cffcbf558-mg8dm 1/1 Running 0 15m
deployment-test-7cffcbf558-nd6ld 1/1 Running 0 15m
deployment-test-f5b4bdd49-r2f5m 1/1 Running 0 4m55s
# 确保更新的pod没问题了,继续更新
[root@k8s-master01 dev]# kubectl rollout resume deploy deployment-test -n dev
deployment.apps/deployment-test resumed
# 查看更新情况
[root@k8s-master01 dev]# kubectl get rs -n dev -o wide
NAME DESIRED CURRENT READY AGE CONTAINERS IMAGES SELECTOR
deployment-test-5b6ffdcd8d 0 0 0 22m nginx nginx:1.17.3 app=nginx-pod,pod-template-hash=5b6ffdcd8d
deployment-test-79dbdc995f 0 0 0 23m nginx nginx:1.17.2 app=nginx-pod,pod-template-hash=79dbdc995f
deployment-test-7cffcbf558 0 0 0 26m nginx nginx:1.17.1 app=nginx-pod,pod-template-hash=7cffcbf558
deployment-test-845cbff7c8 3 3 3 3m39s nginx nginx:1.17.5 app=nginx-pod,pod-template-hash=845cbff7c8
deployment-test-f5b4bdd49 0 0 0 9m32s nginx nginx:1.17.4 app=nginx-pod,pod-template-hash=f5b4bdd49
[root@k8s-master01 dev]# kubectl get pod -n dev
NAME READY STATUS RESTARTS AGE
deployment-test-845cbff7c8-2fm7r 1/1 Running 0 4m23s
deployment-test-845cbff7c8-8fbvx 1/1 Running 0 4m57s
deployment-test-845cbff7c8-wb62j 1/1 Running 0 6m6s
局限性
按照 Kubernetes 默认支持的这种方式进行金丝雀发布,有一定的局限性:
不能根据用户注册时间、地区等请求中的内容属性进行流量分配
同一个用户如果多次调用该 Service,有可能第一次请求到了旧版本的 Pod,第二次请求到了新版本的 Pod
TIP
在 Kubernetes 中不能解决上述局限性的原因是:Kubernetes Service 只在 TCP 层面解决负载均衡的问题,并不对请求响应的消息内容做任何解析和识别。如果想要更完善地实现金丝雀发布,可以考虑如下三种选择:
业务代码编码实现
Spring Cloud 灰度发布
Istio 灰度发布
删除Deployment
# 删除deployment,其下的rs和pod也将被删除
[root@k8s-master01 dev]# kubectl delete -f deployment.yaml
deployment.apps "deployment-test" deleted
Service详解与使用
详解
在kubernetes中,pod是应用程序的载体,我们可以通过pod的ip来访问应用程序,但是pod的ip地址 不是固定的,这也就意味着不方便直接采用pod的ip对服务进行访问。 为了解决这个问题,
kubernetes提供了Service资源,Service会对提供同一个服务的多个pod进行聚 合,并且提供一个统一的入口地址。通过访问Service的入口地址就能访问到后面的pod服务。
Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都 运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信 息,而kube-proxy会基于监听的机制发现这种Service的变动,然后它会将最新的Service信息转换成对 应的访问规则。
kube-proxy目前支持三种工作模式:
User space 代理模式
在 user space proxy mode 下:
kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
kube-proxy 在其所在的节点(每个节点都有 kube-proxy)上为每一个 Service 打开一个随机端口
kube-proxy 安装 iptables 规则,将发送到该 Service 的 ClusterIP(虚拟 IP)/ Port 的请求重定向到该随机端口
任何发送到该随机端口的请求将被代理转发到该 Service 的后端 Pod 上(kube-proxy 从 Endpoint 信息中获得可用 Pod)
kube-proxy 在决定将请求转发到后端哪一个 Pod 时,默认使用 round-robin(轮询)算法,并会考虑到 Service 中的 SessionAffinity 的设定
Iptables 代理模式
在 iptables proxy mode 下:
kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
kube-proxy 在其所在的节点(每个节点都有 kube-proxy)上为每一个 Service 安装 iptable规则
iptables 将发送到 Service 的 ClusterIP / Port 的请求重定向到 Service 的后端 Pod 上
对于 Service 中的每一个 Endpoint,kube-proxy 安装一个 iptable 规则
默认情况下,kube-proxy 随机选择一个 Service 的后端 Pod
iptables proxy mode 的优点:
更低的系统开销:在 linux netfilter 处理请求,无需在 userspace 和 kernel space 之间切换更稳定
与 user space mode 的差异:
使用 iptables mode 时,如果第一个 Pod 没有响应,则创建连接失败
使用 user space mode 时,如果第一个 Pod 没有响应,kube-proxy 会自动尝试连接另外一后端 Pod
可以配置 Pod 就绪检查(readiness probe)确保后端 Pod 正常工作,此时,在 iptables 模式kube-proxy 将只使用健康的后端 Pod,从而避免了 kube-proxy 将请求转发到已经存在问题的 Pod 上。
IPVS 代理模式
在 IPVS proxy mode 下:
kube-proxy 监听 kubernetes master 以获得添加和移除 Service / Endpoint 的事件
kube-proxy 根据监听到的事件,调用 netlink 接口,创建 IPVS 规则;并且将Service/Endpoint 的变化同步到 IPVS 规则中
当访问一个 Service 时,IPVS 将请求重定向到后端 Pod
IPVS 模式的优点
IPVS proxy mode 基于 netfilter 的 hook 功能,与 iptables 代理模式相似,但是 IPVS 代理模式使用 hash table 作为底层的数据结构,并在 kernel space 运作。这就意味着
IPVS 代理模式可以比 iptables 代理模式有更低的网络延迟,在同步代理规则时,也有更高的效率
与 user space 代理模式 / iptables 代理模式相比,IPVS 模式可以支持更大的网络流量
IPVS 提供更多的负载均衡选项:
rr: round-robin
lc: least connection (最小打开的连接数)
dh: destination hashing
sh: source hashing
sed: shortest expected delay
nq: never queue
注意:
如果要使用 IPVS 模式,您必须在启动 kube-proxy 前为节点的 linux 启用 IPVS
kube-proxy 以 IPVS 模式启动时,如果发现节点的 linux 未启用 IPVS,则退回到 iptables 模式
Service类型
kind: Service # 资源类型
apiVersion: v1 # 资源版本
metadata: # 元数据
name: service # 资源名称
namespace: dev # 命名空间
spec: # 描述
selector: # 标签选择器,用于确定当前service代理哪些pod
app: nginx
type: # Service类型,指定service的访问方式
clusterIP: # 虚拟服务的ip地址
sessionAffinity: # session亲和性,支持ClientIP、None两个选项
ports: # 端口信息
- protocol: TCP
port: 3017 # service端口
targetPort: 5003 # pod端口
nodePort: 31122 # 主机端口
ClusterIP:默认值,它是Kubernetes系统自动分配的虚拟IP,只能在集群内部访问
NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务
LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持
ExternalName: 把集群外部的服务引入集群内部,直接使用
Service使用
使用service之前,首先利用Deployment创建出3个pod,注意要为pod设置 app=nginx-pod 的标签
创建deployment.yaml写入
apiVersion: apps/v1
kind: Deployment
metadata:
name: 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
[root@k8s-master01 dev]# kubectl apply -f deployment.yaml
deployment.apps/nginx-deployment created
[root@k8s-master01 dev]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
nginx-deployment-7cffcbf558-7z6hs 1/1 Running 0 107s 100.119.84.73 k8s-worker01 <none> <none>
nginx-deployment-7cffcbf558-9fpc2 1/1 Running 0 107s 100.73.45.71 k8s-worker02 <none> <none>
nginx-deployment-7cffcbf558-hjlqv 1/1 Running 0 107s 100.73.45.72 k8s-worker02 <none> <none>
修改下三台nginx的index.html页面(三台修改的IP地址不一致)
[root@k8s-master01 dev]# kubectl exec -it nginx-deployment-7cffcbf558-7z6hs -n dev /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-deployment-7cffcbf558-7z6hs:/# echo "`hostname -I`,web1 test page" > /usr/share/nginx/html/index.html
三个全部修改完毕后,访问
[root@k8s-master01 dev]# curl 100.119.84.73
100.119.84.73 ,web1 test page
[root@k8s-master01 dev]# curl 100.73.45.71
100.73.45.71 ,web2 test page
[root@k8s-master01 dev]# curl 100.73.45.72
100.73.45.72 ,web3 test page
[root@k8s-master01 dev]# kubectl expose deployment nginx-deployment --type ClusterIP --port 80 --target-port 80 --name=nginx-svc1 -n dev -o yaml --dry-run=client
apiVersion: v1
kind: Service
metadata:
creationTimestamp: null
name: nginx-svc1
namespace: dev
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-pod
type: ClusterIP
status:
loadBalancer: {}
可以基于以上,创建service-clusterip.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc1
namespace: dev
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-pod
type: ClusterIP
clusterIP: 10.96.0.8 # service的ip地址,如果不写,默认会生成一个
[root@k8s-master01 dev]# kubectl create -f svc-clusterip.yaml
service/nginx-svc1 created
[root@k8s-master01 dev]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc1 ClusterIP 10.96.0.8 <none> 80/TCP 8s
# 查看service的详细信息
# 在这里有一个Endpoints列表,里面就是当前service可以负载到的服务入口
[root@k8s-master01 dev]# kubectl describe svc nginx-svc1 -n dev
Name: nginx-svc1
Namespace: dev
Labels: <none>
Annotations: <none>
Selector: app=nginx-pod
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.96.0.8
IPs: 10.96.0.8
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 100.119.84.73:80,100.73.45.71:80,100.73.45.72:80
Session Affinity: None
Events: <none>
[root@k8s-master01 dev]# ipvsadm -Ln | grep 10.96.0.8 -w3
TCP 10.96.0.8:80 rr
-> 100.73.45.71:80 Masq 1 0 0
-> 100.73.45.72:80 Masq 1 0 0
-> 100.119.84.73:80 Masq 1 0 0
[root@k8s-master01 dev]# curl 10.96.0.8
100.73.45.72 ,web3 test page
[root@k8s-master01 dev]# curl 10.96.0.8
100.73.45.71 ,web2 test page
[root@k8s-master01 dev]# curl 10.96.0.8
Service的HeadLiness
在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策 略,针对这种情况,kubernetes提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想 要访问service,只能通过service的域名进行查询。
创建service-headliness.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-headliness
namespace: dev
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
selector:
app: nginx-pod
type: ClusterIP
clusterIP: None
[root@k8s-master01 dev]# kubectl create -f svc-headliness.yaml #创建service
service/nginx-svc-headliness created
[root@k8s-master01 dev]# kubectl get service -n dev # 获取service, 发现CLUSTER-IP未分配
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc-headliness ClusterIP None <none> 80/TCP 36s
[root@k8s-master01 dev]# kubectl describe svc nginx-svc-headliness -n dev #查看service详情
Name: nginx-svc-headliness
Namespace: dev
Labels: <none>
Annotations: <none>
Selector: app=nginx-pod
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: None
IPs: None
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 100.119.84.73:80,100.73.45.71:80,100.73.45.72:80
Session Affinity: None
Events: <none>
#查看域名的解析情况
[root@k8s-master01 dev]# kubectl exec -it nginx-deployment-7cffcbf558-7z6hs -n dev /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@nginx-deployment-7cffcbf558-7z6hs:/# cat /etc/resolv.conf
nameserver 10.96.0.10
search dev.svc.cluster.local svc.cluster.local cluster.local
options ndots:5
[root@k8s-master01 dev]# dig @10.96.0.10 nginx-svc-headliness.dev.svc.cluster.local
nginx-svc-headliness.dev.svc.cluster.local. 30 IN A 100.119.84.73
nginx-svc-headliness.dev.svc.cluster.local. 30 IN A 100.73.45.71
nginx-svc-headliness.dev.svc.cluster.local. 30 IN A 100.73.45.72
Service的NodePort
在之前的例子中,创建的Service的ip地址只有集群内部才可以访问,如果希望将Service暴露给集群外 部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是 将service的端口映射到Node的一个端口上,然后就可以通过 NodeIp:NodePort 来访问service。
创建service-nodeport.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-svc-nodeport
namespace: dev
spec:
ports:
- port: 80
protocol: TCP
targetPort: 80
nodePort: 30088 #指定绑定的node端口(默认取值范围是:30000-32767), 如果不指定,会默认分配
selector:
app: nginx-pod
type: NodePort
[root@k8s-master01 dev]# kubectl create -f svc-nodeport.yaml
service/nginx-svc-nodeport created
[root@k8s-master01 dev]# kubectl get svc -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx-svc-nodeport NodePort 10.96.0.221 <none> 80:30088/TCP 14s
# 可以通过电脑主机的浏览器去访问集群中任意一个nodeip的30088端口,即可访问到pod
Service的LoadBalancer
LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群 的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请 求,会被设备负载之后转发到集群中。
三款开源 Kubernetes 负载均衡器: MetalLB vs PureLB vs OpenELB
1. 什么是 OpenELB
K8S 对集群外暴露服务有三种方式:NodePort,Ingress和 Loadbalancer。NodePort 用于暴露TCP 服务(4层),但限于对集群节点主机端口的占用,不适合大规模使用;Ingress 用于暴露 HTTP 服务(7层),可对域名地址做路由分发;Loadbalancer 则专属于云服务,可动态分配公网网关。
对于私有云集群,没有用到公有云服务,能否使用 LoadBalancer 对外暴露服务呢?
答案当然是肯定的,OpenELB 正是为裸金属服务器提供 LoadBalancer 服务而生的!
由青云科技 KubeSphere 容器团队开源的负载均衡器插件 OpenELB 正式通过 CNCF(云原生计算基金会)TOC 技术委员会审核.
2.安装 OpenELB
前提:
首先需要为 kube-proxy 启用 strictARP ,以便 Kubernetes 集群中的所有网卡停止响应其他网卡的 ARP 请求,而由 OpenELB 处理 ARP 请求。
# 注意:是修改,默认strictARP 是 false
# kubectl edit configmap kube-proxy -n kube-system
......
ipvs:
strictARP: true
......
# wget -c https://raw.githubusercontent.com/openelb/openelb/master/deploy/openelb.yaml
修改镜像地址:修改两处
image: k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1 为 image: registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhookcertgen:v1.1.1
#安装
[root@k8s-master01 dev]# kubectl create -f svc-nodeport.yaml
[root@k8s-master01 dev]# kubectl get pod -n openelb-system
NAME READY STATUS RESTARTS AGE
openelb-admission-create-vdn9v 0/1 Completed 0 3m38s
openelb-admission-patch-6mj7p 0/1 Completed 2 3m38s
openelb-controller-7b9bffd796-tp4hn 1/1 Running 0 3m39s
openelb-speaker-68lr4 1/1 Running 0 3m39s
openelb-speaker-bljh5 1/1 Running 0 3m39s
openelb-speaker-d4kp2 1/1 Running 0 3m39s
添加 EIP 池
EIP 地址要与集群主机节点在同一网段内,且不可绑定任何网卡;
创建ip-pool.yaml
apiVersion: network.kubesphere.io/v1alpha2
kind: Eip
metadata:
name: eip-pool
spec:
address: 192.168.234.200-192.168.234.234
protocol: layer2
disable: false
interface: ens160
[root@k8s-master01 dev]# kubectl create -f ip-pool.yaml
eip.network.kubesphere.io/eip-pool created
[root@k8s-master01 dev]# kubectl get eip
NAME CIDR USAGE TOTAL
eip-pool 192.168.234.200-192.168.234.234 35
创建svc-lb.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-lb
namespace: dev
annotations:
lb.kubesphere.io/v1alpha1: openelb
protocol.openelb.kubesphere.io/v1alpha2: layer2
eip.openelb.kubesphere.io/v1alpha2: eip-pool
spec:
selector:
app: nginx-pod
type: LoadBalancer
ports:
- port: 80 # 服务监听的端口
targetPort: 80 # 容器端口(与 Pod 定义一致)
[root@k8s-master01 dev]# kubectl apply -f svc-lb.yaml
service/svc-lb created
[root@k8s-master01 dev]# kubectl get svc svc-lb -n dev
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-lb LoadBalancer 10.96.3.126 192.168.234.200 80:32376/TCP 72s
#测试
[root@k8s-master01 dev]# curl 192.168.234.200
100.73.45.72 ,web3 test page
[root@k8s-master01 dev]# curl 192.168.234.200
100.73.45.71 ,web2 test page
[root@k8s-master01 dev]# curl 192.168.234.200
100.119.84.73 ,web1 test page
Service的ExternalName
ExternalName类型的Service用于引入集群外部的服务,它通过 externalName 属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务。
创建svc-externalname.yaml
apiVersion: v1
kind: Service
metadata:
name: svc-externalname
namespace: dev
spec:
type: ExternalName # service类型
externalName: www.aa.com
[root@k8s-master01 dev]# kubectl apply -f svc-externalname.yaml
service/svc-externalname created
#域名解析
[root@k8s-master01 dev]# dig @10.96.0.10 svc-externalname.dev.svc.cluster.local
svc-externalname.dev.svc.cluster.local. 5 IN CNAME www.aa.com.
www.aa.com. 5 IN A 23.210.42.80
Ingress的使用
Service对集群之外暴露服务的主要方式有两种:NotdePort和 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部署的yaml文件
wget https://github.com/kubernetes/ingressnginx/blob/main/deploy/static/provider/cloud/deploy.yaml
修改镜像地址为阿里云地址,总三处
registry.k8s.io/ingress-nginx/controller:v1.12.1
改为 registry.cnhangzhou.aliyuncs.com/google_containers/nginx-ingresscontroller:v1.12.1
k8s.gcr.io/ingress-nginx/kube-webhook-certgen:v1.1.1 改为(有两处需要修改) registry.cn-hangzhou.aliyuncs.com/google_containers/kube-webhook-certgen:v1.5.2
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.12.1
name: ingress-nginx-controller
namespace: ingress-nginx
annotations: #添加注解
lb.kubesphere.io/v1alpha1: openelb
protocol.openelb.kubesphere.io/v1alpha1: layer2
eip.openelb.kubesphere.io/v1alpha2: eip-pool
spec:
externalTrafficPolicy: Local
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
ports:
- appProtocol: http
name: http
port: 80
protocol: TCP
targetPort: http
- appProtocol: https
name: https
port: 443
protocol: TCP
targetPort: https
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
type: LoadBalancer
---
apiVersion: v1
kind: Service
metadata:
labels:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
app.kubernetes.io/part-of: ingress-nginx
app.kubernetes.io/version: 1.12.1
name: ingress-nginx-controller-admission
namespace: ingress-nginx
spec:
ports:
- appProtocol: http
name: http
port: 80
protocol: TCP
targetPort: http
- appProtocol: https
name: https-webhook
port: 443
targetPort: webhook
selector:
app.kubernetes.io/component: controller
app.kubernetes.io/instance: ingress-nginx
app.kubernetes.io/name: ingress-nginx
#type: ClusterIP
type: NodePort #添加修改
[root@k8s-master01 dev]# kubectl apply -f deploy.yaml
namespace/ingress-nginx created
serviceaccount/ingress-nginx created
serviceaccount/ingress-nginx-admission created
role.rbac.authorization.k8s.io/ingress-nginx created
role.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrole.rbac.authorization.k8s.io/ingress-nginx created
clusterrole.rbac.authorization.k8s.io/ingress-nginx-admission created
rolebinding.rbac.authorization.k8s.io/ingress-nginx created
rolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx created
clusterrolebinding.rbac.authorization.k8s.io/ingress-nginx-admission created
configmap/ingress-nginx-controller created
service/ingress-nginx-controller created
service/ingress-nginx-controller-admission created
deployment.apps/ingress-nginx-controller created
job.batch/ingress-nginx-admission-create created
job.batch/ingress-nginx-admission-patch created
ingressclass.networking.k8s.io/nginx created
validatingwebhookconfiguration.admissionregistration.k8s.io/ingress-nginx-admission created
[root@k8s-master01 dev]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx-controller LoadBalancer 10.96.0.243 192.168.234.201 80:31527/TCP,443:30862/TCP 51s
ingress-nginx-controller-admission NodePort 10.96.3.118 <none> 80:30689/TCP,443:32072/TCP 51s
[root@k8s-master01 dev]# kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-8bqd4 0/1 Completed 0 6m31s
ingress-nginx-admission-patch-vxx2h 0/1 Completed 3 6m31s
ingress-nginx-controller-75cdf6c95-wxgk6 1/1 Running 0 6m31s
Ingress暴露服务的方式
方式一:Deployment+LoadBalancer模式的Service
如果要把ingress部署在公有云,那用这种方式比较合适。用Deployment部署ingresscontroller,创建一个type为 LoadBalancer的 service关联这组pod。大部分公有云,都会为 LoadBalancer的 service自动创建一个负载均衡器,通常还绑定了公网地址。只要把域名解析指向 该地址,就实现了集群服务的对外暴露
方式二:DaemonSet+HostNetwork+nodeselector
用DaemonSet结合nodeselector来部署ingress-controller到特定的node 上,然后使用 HostNetwork直接把该pod与宿主机node的网络打通,直接使用宿主机的80/433端口就能访问服 务。这时,ingress-controller所在的node机器就很类似传统架构的边缘节点,比如机房入口的 nginx服务器。该方式整个请求链路最简单,性能相对NodePort模式更好。缺点是由于直接利用 宿主机节点的网络和端口,一个node只能部署一个ingress-controller pod。比较适合大并发的生 产环境使用
优点
该方式整个请求链路最简单,性能相对NodePort模式更好
缺点
由于直接利用宿主机节点的网络和端口,一个node只能部署一个 ingress-controller pod
方式三:Deployment+NodePort模式的Service
同样用deployment模式部署ingres-controller,并创建对应的服务,但是type为NodePort。这 样,ingress就会暴露在集群节点ip的特定端口上。由于nodeport暴露的端口是随机端口,一般会 在前面再搭建一套负载均衡器来转发请求。该方式一般用于宿主机是相对固定的环境ip地址不变的 场景。NodePort方式暴露ingress虽然简单方便,但是NodePort多了一层NAT,在请求量级很大 时可能对性能会有一定影响
K8s的存储
容器的生命周期可能很短,会被频繁地创建和销毁。那么容器在销毁时,保存在容 器中的数据也会被清除。这种结果对用户来说,在某些情况下是不乐意看到的。为了持久化保存容器的 数据,kubernetes引入了Volume的概念。
Volume是Pod中能够被多个容器访问的共享目录,它被定义在Pod上,然后被一个Pod里的多个容器 挂载到具体的文件目录下,kubernetes通过Volume实现同一个Pod中不同容器之间的数据共享以及数 据的持久化存储。Volume的生命容器不与Pod中单个容器的生命周期相关,当容器终止或者重启时, Volume中的数据也不会丢失。
kubernetes的Volume支持多种类型,比较常见的有下面几个:
基本存储:EmptyDir、HostPath、NFS
高级存储:PV、PVC
配置存储:ConfigMap、Secret
configmap
configmap的功能
configMap用于保存配置数据,以键值对形式存储。
configMap 资源提供了向 Pod 注入配置数据的方法。
镜像和配置文件解耦,以便实现镜像的可移植性和可复用性。
etcd限制了文件大小不能超过1M
configmap创建方式
字面值创建


通过文件创建
[root@master storage]# vim nginx.conf
server{
listen 8080;
root /usr/share/nginx/html;
index index.html;
}
[root@master storage]# kubectl create cm nginxconf --from-file nginx.conf
configmap/nginxconf created

通过yaml文件创建
[root@master storage]# vim test.yml


利用configMap填充pod的配置文件
[root@master storage]# vim nginx.conf
server{
listen 8080;
root /usr/share/nginx/html;
index index.html;
}
[root@master storage]# kubectl create cm nginx-conf --from-file nginx.conf
configmap/nginx-conf created
#建立nginx控制器文件
[root@k8s-master storage]# kubectl run nginx --image nginx:latest --dry-run=client -o yaml > nginx.yml
#设定nginx.yml中的卷
[root@k8s-master storage]# vim nginx.yml
apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
volumeMounts:
- name: config
mountPath: /etc/nginx/conf.d
volumes:
- name: config
configMap:
name: nginx-conf
secret功能介绍
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的认证信息
secrets的创建
从文件创建
[root@k8s-master secrets]# echo -n timinglee > username.txt
[root@k8s-master secrets]# echo -n lee > password.txt
root@k8s-master secrets]# kubectl create secret generic userlist --from-file username.txt --from-file password.txt
secret/userlist created

编写yaml文件
[root@k8s-master secrets]# echo -n timinglee | base64
dGltaW5nbGVl
[root@k8s-master secrets]# echo -n lee | base64
bGVl
[root@k8s-master secrets]# kubectl create secret generic userlist --dry-run=client -o yaml > userlist.yml
apiVersion: v1
kind: Secret
metadata:
creationTimestamp: null
name: userlist
type: Opaque
data:
username: dGltaW5nbGVl
password: bGVl
Secret的使用方法
将Secret挂载到Volume中
[root@master storage]# vim pod1.yml
apiVersion: v1
kind: Pod
metadata:
labels:nginx
run: nginx
name: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: secrets
mountPath: /secret
readOnly: true
volumes:
- name: secrets
secret:
secretName: userlist

将Secret设置为环境变量
[root@master storage]# vim testsc.yml
apiVersion: v1
kind: Pod
metadata:
labels:
run: testsc
name: testsc
spec:
containers:
- image: busybox
name: testsc
command:
- /bin/sh
- -c
- env
env:
- name: username
valueFrom:
secretKeyRef:
name: userlist
key: username
- name: password
valueFrom:
secretKeyRef:
name: userlist
key: password
restartPolicy: Never
volumes配置管理
EmptyDir
EmptyDir是最基础的Volume类型,一个EmptyDir就是Host上的一个空目录。
EmptyDir是在Pod被分配到Node时创建的,它的初始内容为空,并且无须指定宿主机上对应的目录文件,因为kubernetes会自动分配一个目录,当Pod销毁时, EmptyDir中的数据也会被永久删除。
EmptyDir用途如下:
临时空间,例如用于某些应用程序运行时所需的临时目录,且无须永久保留
一个容器需要从另一个容器中获取数据的目录(多容器共享目录)
实例:
在一个Pod中准备两个容器nginx和busybox,然后声明一个Volume分别挂在到两个容器的目录中,然后nginx容器负责向Volume中写日志,busybox中通过命令将日志内容读到控制台。
创建volume-emptydir.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-emptydir
namespace: dev
spec:
volumes:
- name: logs-volume
emptyDir: {}
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
HostPath
因为EmptyDir中数据不会被持久化,它会随着Pod的结束而销毁,如果想简单的将数据持久 化到主机中,可以选择HostPath。
HostPath就是将Node主机中一个实际目录挂在到Pod中,以供容器使用,这样的设计就可以保证Pod 销毁了,但是数据依据可以存在于Node主机上。
创建volume-hostpath.yaml:
apiVersion: v1
kind: Pod
metadata:
name: volume-hostpath
namespace: dev
spec:
volumes:
- name: logs-volume
hostPath:
path: /opt/logs
type: DirectoryOrCreate
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
mountPath: /logs
type值说明:
DirectoryOrCreate 目录存在就使用,不存在就先创建后使用
Directory 目录必须存在
FileOrCreate 文件存在就使用,不存在就先创建后使用
File 文件必须存在
Socket unix套接字必须存在
CharDevice 字符设备必须存在
BlockDevice 块设备必须存在
[root@k8s-master01 dev]# kubectl create -f volume-hostpath.yaml
pod/volume-hostpath created
[root@k8s-master01 dev]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
volume-hostpath 2/2 Running 0 20s 100.119.84.92 k8s-worker01 <none> <none>
[root@k8s-master01 dev]# kubectl exec -it volume-hostpath -n dev -c nginx /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
root@volume-hostpath:/# echo "this is test page2" > /usr/share/nginx/html/index.html
[root@k8s-master01 dev]# curl 100.119.84.92
this is test page2
# 接下来就可以去host的/opt/logs目录下查看存储的文件了
# 注意: 下面的操作需要到Pod所在的节点运行(k8s-worker01 )
[root@k8s-worker01 ~]# ll /opt/logs/
total 4
-rw-r--r-- 1 root root 189 Apr 11 22:20 access.log
-rw-r--r-- 1 root root 0 Apr 11 22:15 error.log
[root@k8s-worker01 ~]# cat /opt/logs/access.log
100.124.32.128 - - [11/Apr/2025:14:19:02 +0000] "GET / HTTP/1.1" 200 612 "-" "curl/7.79.1" "-"
100.124.32.128 - - [11/Apr/2025:14:20:56 +0000] "GET / HTTP/1.1" 200 20 "-" "curl/7.79.1" "-"
# 同样的道理,如果在此目录下创建一个文件,到容器中也是可以看到的
# 删除文件
[root@k8s-master01 dev]# kubectl delete -f volume-hostpath.yaml
pod "volume-hostpath" deleted
#挂载目录还存在
[root@k8s-worker01 ~]# ll /opt/logs/
total 4
-rw-r--r-- 1 root root 189 Apr 11 22:20 access.log
-rw-r--r-- 1 root root 0 Apr 11 22:15 error.log
NFS
HostPath可以解决数据持久化的问题,但是一旦Node节点故障了,Pod如果转移到了别的节点,又会出现问题了,此时需要准备单独的网络存储系统,比较常用的用NFS、CIFS。
NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连接到NFS系统上,这样的话,无论Pod在节点上怎么转移,只要Node跟NFS的对接没问题,数据都可以成功访问。 首先要准备nfs的服务器,这里为了简单,直接是master节点做nfs服务器
# 在master上安装nfs服务
[root@k8s-master01 dev]# yum install nfs-utils -y
# 准备一个共享目录
[root@k8s-master01 ~]# mkdir /nfstest
# 将共享目录以读写权限暴露给192.168.234.0/24网段中的所有主机
[root@k8s-master01 ~]# vim /etc/exports
/nfstest 192.168.234.0/24(rw,no_root_squash)
[root@k8s-worker01 ~]# showmount -e 192.168.234.11
Export list for 192.168.234.11:
/nfstest 192.168.234.0/24
# 启动nfs服务
[root@k8s-master01 ~]# systemctl start nfs
#在的每个node节点上都安装下nfs-utils,这样的目的是为了node节点可以驱动nfs设备
[root@k8s-worker01 ~]# yum install nfs-utils -y
[root@k8s-worker02 ~]# yum install nfs-utils -y
创建volume-nfs.yaml
apiVersion: v1
kind: Pod
metadata:
name: volume-nfs
namespace: dev
spec:
volumes:
- name: logs-volume
nfs:
server: master #nfs服务器地址
path: /nfstest #共享文件路径
containers:
- name: nginx
image: nginx:1.17.1
ports:
- containerPort: 80
volumeMounts:
- name: logs-volume
mountPath: /var/log/nginx
- name: busybox
image: busybox:1.30
command: ["/bin/sh","-c","tail -f /logs/access.log"]
volumeMounts:
- name: logs-volume
[root@k8s-master01 dev]# kubectl create -f volume-nfs.yaml
pod/volume-nfs created
[root@k8s-master01 dev]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
volume-nfs 2/2 Running 0 2m27s 100.119.84.95 k8s-worker01 <none> <none>
[root@k8s-master01 dev]# kubectl exec -it volume-nfs -n dev /bin/bash
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
Defaulted container "nginx" out of: nginx, busybox
root@volume-nfs:/# echo "this is nfs test page" > /usr/share/nginx/html/index.html
[root@k8s-master01 dev]# curl 100.119.84.95
this is nfs test page
# 查看nfs服务器上的共享目录
[root@k8s-master01 dev]# ll /nfstest/
total 4
-rw-r--r-- 1 root root 188 Apr 11 23:22 access.log
-rw-r--r-- 1 root root 0 Apr 11 23:17 error.log
[root@k8s-master01 dev]# cat /nfstest/access.log
100.124.32.128 - - [11/Apr/2025:15:22:56 +0000] "GET / HTTP/1.1" 200 22 "-" "curl/7.79.1" "-"
100.124.32.128 - - [11/Apr/2025:15:22:57 +0000] "GET / HTTP/1.1" 200 22 "-" "curl/7.79.1" "-"
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卷都会被删除
[root@reg ~]# mkdir /pod-data/pv{1..3}
[root@master storage]# vim pv.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv1
spec:
capacity:
storage: 5Gi
volumeMode: Filesystem
accessModes:
- ReadWriteOnce
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /pod-date/pv1
server: 192.168.234.10
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv2
spec:
capacity:
storage: 10Gi
volumeMode: Filesystem
accessModes:
- ReadOnlyMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /pod-date/pv2
server: 192.168.234.10
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv3
spec:
capacity:
storage: 15Gi
volumeMode: Filesystem
accessModes:
- ReadWriteMany
persistentVolumeReclaimPolicy: Retain
storageClassName: nfs
nfs:
path: /pod-date/pv3
server: 192.168.234.10
#建立pvc,pvc是pv使用的申请,需要保证和pod在一个namesapce中
[root@master storage]# vim pvc.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc1
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1G
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc2
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteMany
resources:
requests:
storage: 10Gi
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc3
spec:
storageClassName: nfs-client
accessModes:
- ReadOnlyMany
resources:
requests:
storage: 15Gi

存储类storageclass
StorageClass提供了一种描述存储类(class)的方法,不同的class可能会映射到不同的服务质量等级和备份策略或其他策略等。
每个 StorageClass 都包含 provisioner、parameters 和 reclaimPolicy 字段, 这些字段会在StorageClass需要动态分配 PersistentVolume 时会使用到
StorageClass的属性
Provisioner(存储分配器):用来决定使用哪个卷插件分配 PV,该字段必须指定。可以指定内部分配器,也可以指定外部分配器。外部分配器的代码地址为: kubernetes-incubator/external-storage,其中包括NFS和Ceph等。
Reclaim Policy(回收策略):通过reclaimPolicy字段指定创建的Persistent Volume的回收策略,回收策略包括:Delete 或者 Retain,没有指定默认为Delete。
存储分配器NFS Client Provisioner
NFS Client Provisioner是一个automatic provisioner,使用NFS作为存储,自动创建PV和对应的PVC,本身不提供NFS存储,需要外部先有一套NFS存储服务。
PV以 ${namespace}-${pvcName}-${pvName}的命名格式提供(在NFS服务器上)
PV回收的时候以 archieved-${namespace}-${pvcName}-${pvName} 的命名格式(在NFS服务器上)
部署NFS Client Provisioner
创建sa并授权
[root@master storage]# vim rbac.yml
apiVersion: v1
kind: Namespace
metadata:
name: nfs-client-provisioner
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: nfs-client-provisioner
namespace: 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: ServiceAccount
name: nfs-client-provisioner
namespace: nfs-client-provisioner
roleRef:
kind: ClusterRole
name: nfs-client-provisioner-runner
apiGroup: rbac.authorization.k8s.io
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: leader-locking-nfs-client-provisioner
namespace: 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-provisioner
namespace: nfs-client-provisioner
subjects:
- kind: ServiceAccount
name: nfs-client-provisioner
namespace: nfs-client-provisioner
roleRef:
kind: Role
name: leader-locking-nfs-client-provisioner
apiGroup: rbac.authorization.k8s.io
[root@master storage]# vim deployment.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nfs-client-provisioner
labels:
app: nfs-client-provisioner
namespace: nfs-client-provisioner
spec:
replicas: 1
strategy:
type: Recreate
selector:
matchLabels:
app: nfs-client-provisioner
template:
metadata:
labels:
app: nfs-client-provisioner
spec:
serviceAccountName: nfs-client-provisioner
containers:
- name: nfs-client-provisioner
image: sig-storage/nfs-subdir-external-provisioner:v4.0.2
volumeMounts:
- name: nfs-client-root
mountPath: /persistentvolumes
env:
- name: PROVISIONER_NAME
value: k8s-sigs.io/nfs-subdir-external-provisioner
- name: NFS_SERVER
value: 192.168.234.10
- name: NFS_PATH
value: /pod-date
volumes:
- name: nfs-client-root
nfs:
server: 192.168.234.10
path: /pod-date
~
创建存储类
[root@master storage]# vim class.yml
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
name: nfs-client
provisioner: k8s-sigs.io/nfs-subdir-external-provisioner
parameters:
archiveOnDelete: "false"
创建pvc
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 storage]# vim headless.yml
apiVersion: v1
kind: Service
metadata:
labels:
app: nginx
name: nginx-svc
spec:
clusterIP: None
selector:
app: nginx
type: ClusterIP
[root@master storage]# kubectl apply -f headless.yml
service/nginx-svc created
#建立statefulset
[root@master storage]# vim stateful.yml
apiVersion: apps/v1
kind: StatefulSet
metadata:
labels:
app: nginx
name: nginx
spec:
serviceName: "nginx-svc"
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- name: www
mountPath: /usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: www
spec:
storageClassName: nfs-client
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi


#为每个pod建立index.html文件


K8s的调度
在默认情况下,一个Pod在哪个Node节点上运行,是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的。但是在实际使用中,这并不满足的需求,因为很多情况下,我们想控制某些Pod到达某些节点上,那么应该怎么做呢?这就要求了解kubernetes对Pod的调度规则,kubernetes提供了四大类调度方式:
自动调度:运行在哪个节点上完全由Scheduler经过一系列的算法计算得出
定向调度:NodeName、NodeSelector
亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
污点(容忍)调度:Taints、Toleration
定向调度
定向调度,指的是利用在pod上声明nodeName或者nodeSelector,以此将Pod调度到期望的node节点上。注意,这里的调度是强制的,这就意味着即使要调度的目标Node不存在,也会向上面进行调度,只不过pod运行失败而已。
NodeName
NodeName用于强制约束将Pod调度到指定的Name的Node节点上。这种方式,其实是直接跳过 Scheduler的调度逻辑,直接将Pod调度到指定名称的节点。
创建一个pod-nodename.yaml文件
apiVersion: v1
kind: Pod
metadata:
name: pod-nodename
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: k8s-worker01 # 指定调度到k8s-worker01节点上
#创建Pod
[root@master ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created
#查看Pod调度到NODE属性,确实是调度到了node1节点上
[root@master ~]# kubectl get pod pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodename 1/1 Running 0 56s 10.244.1.87 k8s-worker01 ......
# 接下来,删除pod,修改nodeName的值为k8s-worker03(并没有k8s-worker03节点)
[root@master ~]# kubectl delete -f pod-nodename.yaml
pod "pod-nodename" deleted
[root@master ~]# vim pod-nodename.yaml
[root@master ~]# kubectl create -f pod-nodename.yaml
pod/pod-nodename created
#再次查看,发现已经向Node3节点调度,但是由于不存在k8s-worker03节点,所以pod无法正常运行
[root@master ~]# kubectl get pods pod-nodename -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE ......
pod-nodename 0/1 Pending 0 6s <none> k8s-worker03 ......
NodeSelector
NodeSelector用于将pod调度到添加了指定标签的node节点上。它是通过kubernetes的labelselector机制实现的,也就是说,在pod创建之前,会由scheduler使用MatchNodeSelector调度策略进 行label匹配,找出目标node,然后将pod调度到目标节点,该匹配规则是强制约束。创建一个pod-nodeselector.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeselector
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeSelector:
nodeenv: pro # 指定调度到具有nodeenv=pro标签的节点上
#创建Pod
[root@master ~]# kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created
#查看Pod调度到NODE属性,确实是调度到了node1节点上
[root@master ~]# kubectl get pods pod-nodeselector -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
......
pod-nodeselector 1/1 Running 0 47s 100.119.84.73 k8s-worker01
......
# 接下来,删除pod,修改nodeSelector的值为nodeenv: hhh(不存在打有此标签的节点)
[root@master ~]# kubectl delete -f pod-nodeselector.yaml
pod "pod-nodeselector" deleted
[root@master ~]# vim pod-nodeselector.yaml
[root@master ~]# kubectl create -f pod-nodeselector.yaml
pod/pod-nodeselector created
#再次查看,发现pod无法正常运行,Node的值为none
[root@master ~]# kubectl get pods -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
pod-nodeselector 0/1 Pending 0 2m20s <none> <none>
# 查看详情,发现node selector匹配失败的提示
[root@master ~]# kubectl describe pods pod-nodeselector -n dev
.......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are
available: 3 node(s) didn't match node selector.
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are
available: 3 node(s) didn't match node selector.
亲和性调度
亲和性调度在NodeSelector的基础之上进行了扩展,可以通过配置的形式,实现优先选择满足条件的Node进行调度,如果没有,也可以调度到不满足条件的节点上,使调度更加灵活。
Affinity主要分为三类:
nodeAffinity(node亲和性): 以node为目标,解决pod可以调度到哪些node的问题
podAffinity(pod亲和性) : 以pod为目标,解决pod可以和哪些已存在的pod部署在同一个拓扑域中的问题
podAntiAffinity(pod反亲和性) : 以pod为目标,解决pod不能和哪些已存在pod部署在同一个拓扑域中的问题
pod.spec.affinity.nodeAffinity
requiredDuringSchedulingIgnoredDuringExecution Node节点必须满足指定的所有规则才可
以,相当于硬限制
nodeSelectorTerms 节点选择列表
matchFields 按节点字段列出的节点选择器要求列表
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
preferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当
于软限制 (倾向)
preference 一个节点选择器项,与相应的权重相关联
matchFields 按节点字段列出的节点选择器要求列表
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
weight 倾向权重,在范围1-100。
关系符的使用说明:
- matchExpressions:
- key: nodeenv # 匹配存在标签的key为nodeenv的节点
operator: Exists
- key: nodeenv # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
operator: In
values: ["xxx","yyy"]
- key: nodeenv # 匹配标签的key为nodeenv,且value大于"xxx"的节点
operator: Gt
values: "xxx"
pod.spec.affinity.nodeAffinity
requiredDuringSchedulingIgnoredDuringExecution Node节点必须满足指定的所有规则才可
以,相当于硬限制
nodeSelectorTerms 节点选择列表
matchFields 按节点字段列出的节点选择器要求列表
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator 关系符 支持Exists, DoesNotExist, In, NotIn, Gt, Lt
preferredDuringSchedulingIgnoredDuringExecution 优先调度到满足指定的规则的Node,相当
于软限制 (倾向)
preference 一个节点选择器项,与相应的权重相关联
matchFields 按节点字段列出的节点选择器要求列表
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator 关系符 支持In, NotIn, Exists, DoesNotExist, Gt, Lt
weight 倾向权重,在范围1-100。
关系符的使用说明:
- matchExpressions:
- key: nodeenv # 匹配存在标签的key为nodeenv的节点
operator: Exists
- key: nodeenv # 匹配标签的key为nodeenv,且value是"xxx"或"yyy"的节点
operator: In
values: ["xxx","yyy"]
- key: nodeenv # 匹配标签的key为nodeenv,且value大于"xxx"的节点
operator: Gt
values: "xxx"
NodeAffinity规则设置的注意事项:
1 如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都得到满足,Pod才能运行在指定的Node上
2 如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可
3 如果一个nodeSelectorTerms中有多个matchExpressions ,则一个节点必须满足所有的才能匹配成功
4 如果一个pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的节点亲和性需求,则系统将忽略此变化
PodAffinity
首先来看一下 PodAffinity 的可配置项:
pod.spec.affinity.podAffinity
requiredDuringSchedulingIgnoredDuringExecution 硬限制
namespaces 指定参照pod的namespace
topologyKey 指定调度作用域
labelSelector 标签选择器
matchExpressions 按节点标签列出的节点选择器要求列表(推荐)
key 键
values 值
operator 关系符 支持In, NotIn, Exists, DoesNotExist.
matchLabels 指多个matchExpressions映射的内容
preferredDuringSchedulingIgnoredDuringExecution 软限制
podAffinityTerm 选项
namespaces
topologyKey
labelSelector
matchExpressions
key 键
values 值
operator
matchLabels
weight 倾向权重,在范围1-100
topologyKey用于指定调度时作用域,例如:
如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围
如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分
创建一个参照Pod,pod-podaffinity-target.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-target
namespace: dev
labels:
podenv: pro #设置标签
spec:
containers:
- name: nginx
image: nginx:1.17.1
nodeName: node1 # 将目标pod名确指定到node1上
# 启动目标pod
[root@master ~]# kubectl create -f pod-podaffinity-target.yaml
pod/pod-podaffinity-target created
# 查看pod状况
[root@master ~]# kubectl get pods pod-podaffinity-target -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-target 1/1 Running 0 4s
创建pod-podaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
podAffinity: #设置pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配env的值在["xxx","yyy"]中的标签
- key: podenv
operator: In
values: ["xxx","yyy"]
topologyKey: kubernetes.io/hostname
新Pod必须要与拥有标签nodeenv=xxx或者nodeenv=yyy的pod在同一Node 上,显然现在没有这样pod
# 启动pod
[root@master ~]# kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
# 查看pod状态,发现未运行
[root@master ~]# kubectl get pods pod-podaffinity-required -n dev
NAME READY STATUS RESTARTS AGE
pod-podaffinity-required 0/1 Pending 0 9s
# 查看详细信息
[root@master ~]# kubectl describe pods pod-podaffinity-required -n dev
......
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning FailedScheduling <unknown> default-scheduler 0/3 nodes are
available: 2 node(s) didn't match pod affinity rules, 1 node(s) had taints that
the pod didn't tolerate.
# 接下来修改 values: ["xxx","yyy"]----->values:["pro","yyy"]
# 意思是:新Pod必须要与拥有标签nodeenv=xxx或者nodeenv=yyy的pod在同一Node上
[root@master ~]# vim pod-podaffinity-required.yaml
# 然后重新创建pod,查看效果
[root@master ~]# kubectl delete -f pod-podaffinity-required.yaml
pod "pod-podaffinity-required" deleted
[root@master ~]# kubectl create -f pod-podaffinity-required.yaml
pod/pod-podaffinity-required created
# 发现此时Pod运行正常
[root@master ~]# kubectl get pods pod-podaffinity-required -n dev
NAME READY STATUS RESTARTS AGE LABELS
pod-podaffinity-required 1/1 Running 0 6s <none>
PodAntiAffinity
创建pod-podantiaffinity-required.yaml
apiVersion: v1
kind: Pod
metadata:
name: pod-podantiaffinity-required
namespace: dev
spec:
containers:
- name: nginx
image: nginx:1.17.1
affinity: #亲和性设置
podAntiAffinity: #设置pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 匹配podenv的值在["pro"]中的标签
- key: podenv
operator: In
values: ["pro"]
topologyKey: kubernetes.io/hostname
新Pod必须要与拥有标签nodeenv=pro的pod不在同一Node上
# 创建pod
[root@master ~]# kubectl create -f pod-podantiaffinity-required.yaml
pod/pod-podantiaffinity-required created
# 查看pod
# 发现调度到了node2上
[root@master ~]# kubectl get pods pod-podantiaffinity-required -n dev -o wide
NAME READY STATUS RESTARTS AGE IP
NODE ..
pod-podantiaffinity-required 1/1 Running 0 30s 10.244.1.96
node2 ..
污点和容忍
污点(Taints)
前面的调度方式都是站在Pod的角度上,通过在Pod上添加属性,来确定Pod是否要调度到指定的
Node上,其实我们也可以站在Node的角度上,通过在Node上添加污点属性,来决定是否允许Pod调度过来。
Node被设置上污点之后就和Pod之间存在了一种相斥的关系,进而拒绝Pod调度进来,甚至可以将已经存在的Pod驱逐出去。
污点的格式为: key=value:effect , key和value是污点的标签,effect描述污点的作用,支持如下三个选项:
PreferNoSchedule:kubernetes将尽量避免把Pod调度到具有该污点的Node上,除非没有其他节点可调度
NoSchedule:kubernetes将不会把Pod调度到具有该污点的Node上,但不会影响当前Node上已存在的Pod
NoExecute:kubernetes将不会把Pod调度到具有该污点的Node上,同时也会将Node上已存在的Pod驱离
#首先要暂停节点2
[root@k8s-worker02 ~]# systemctl stop kubelet.service
# 为node1设置污点(PreferNoSchedule)
[root@master ~]# kubectl taint nodes k8s-worker01 tag=openlab:PreferNoSchedule
# 创建pod1
[root@master ~]# kubectl run taint1 --image=nginx:1.17.1 -n dev
[root@master ~]# kubectl get pod -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint1 1/1 Running 0 22s 100.119.84.74 k8s-worker01
# 为node1设置污点(取消PreferNoSchedule,设置NoSchedule)
[root@master ~]# kubectl taint nodes k8s-worker01 tag:PreferNoSchedule-
[root@master ~]# kubectl taint nodes k8s-worker01 tag=openlab:NoSchedule
# 创建pod2
[root@master ~]# kubectl run taint2 --image=nginx:1.17.1 -n dev
[root@master ~]# kubectl get pod taint2 -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint2 0/1 Pending 0 15s <none> <none>
# 为node1设置污点(取消NoSchedule,设置NoExecute)
[root@master ~]# kubectl taint nodes k8s-worker01 tag:NoSchedule-
[root@master ~]# kubectl taint nodes k8s-worker01 tag=openlab:NoExecute
# 创建pod3
[root@master ~]# kubectl run taint3 --image=nginx:1.17.1 -n dev
[root@master ~]# kubectl get pod taint3 -n dev -o wide
NAME READY STATUS RESTARTS AGE IP NODE
taint3 0/1 Pending 0 8s <none> <none>
容忍(Toleration)
上面介绍了污点的作用,我们可以在node上添加污点用于拒绝pod调度上来,但是如果就是想将一个 pod调度到一个有污点的node上去,这时候应该怎么做呢?这就要使用到容忍。
污点就是拒绝,容忍就是忽略,Node通过污点拒绝pod调度上去,Pod通过容忍忽略拒绝
由于已经在节点1上打上了 NoExecute 的污点,此时pod是调度不上去的,可以通过给pod添加容忍,然后将其调度上去
容忍的详细配置:
[root@master ~]# kubectl explain pod.spec.tolerations
......
FIELDS:
key # 对应着要容忍的污点的键,空意味着匹配所有的键
value # 对应着要容忍的污点的值
operator # key-value的运算符,支持Equal和Exists(默认)
effect # 对应污点的effect,空意味着匹配所有影响
tolerationSeconds # 容忍时间, 当effect为NoExecute时生效,表示pod在Node上的停留时间
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资源对象进行修改和校验。
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 中
认证(在k8s中建立认证用户)
创建UserAccount
#建立证书
[root@k8s-master auth]# cd /etc/kubernetes/pki/
[root@k8s-master pki]# openssl genrsa -out timinglee.key 2048
[root@k8s-master pki]# openssl req -new -key timinglee.key -out timinglee.csr -subj "/CN=timinglee"
[root@k8s-master pki]# openssl x509 -req -in timinglee.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out timinglee.crt -days 365
Certificate request self-signature ok
[root@k8s-master pki]# openssl x509 -in timinglee.crt -text -noout
Certificate:
Data:
Version: 1 (0x0)
Serial Number:
76:06:6c:a7:36:53:b9:3f:5a:6a:93:3a:f2:e8:82:96:27:57:8e:58
Signature Algorithm: sha256WithRSAEncryption
Issuer: CN = kubernetes
Validity
Not Before: Sep 8 15:59:55 2024 GMT
Not After : Sep 8 15:59:55 2025 GMT
Subject: CN = timinglee
Subject Public Key Info:
Public Key Algorithm: rsaEncryption
Public-Key: (2048 bit)
Modulus:
00:a6:6d:be:5d:7f:4c:bf:36:96:dc:4e:1b:24:64:
f7:4b:57:d3:45:ad:e8:b5:07:e7:78:2b:9e:6e:53:
2f:16:ff:00:f4:c8:41:2c:89:3d:86:7c:1b:16:08:
2e:2c:bc:2c:1e:df:60:f0:80:60:f9:79:49:91:1d:
9f:47:16:9a:d1:86:c7:4f:02:55:27:12:93:b7:f4:
07:fe:13:64:fd:78:32:8d:12:d5:c2:0f:be:67:65:
f2:56:e4:d1:f6:fe:f6:d5:7c:2d:1d:c8:90:2a:ac:
3f:62:85:9f:4a:9d:85:73:33:26:5d:0f:4a:a9:14:
12:d4:fb:b3:b9:73:d0:a3:be:58:41:cb:a0:62:3e:
1b:44:ef:61:b5:7f:4a:92:5b:e3:71:77:99:b4:ea:
4d:27:80:14:e9:95:4c:d5:62:56:d6:54:7b:f7:c2:
ea:0e:47:b2:19:75:59:22:00:bd:ea:83:6b:cd:12:
46:7a:4a:79:83:ee:bc:59:6f:af:8e:1a:fd:aa:b4:
bd:84:4d:76:38:e3:1d:ea:56:b5:1e:07:f5:39:ef:
56:57:a2:3d:91:c0:3f:38:ce:36:5d:c7:fe:5e:0f:
53:75:5a:f0:6e:37:71:4b:90:03:2f:2e:11:bb:a1:
a1:5b:dc:89:b8:19:79:0a:ee:e9:b5:30:7d:16:44:
4a:53
Exponent: 65537 (0x10001)
Signature Algorithm: sha256WithRSAEncryption
Signature Value:
62:db:0b:58:a9:59:57:91:7e:de:9e:bb:20:2f:24:fe:b7:7f:
33:aa:d5:74:0e:f9:96:ce:1b:a9:65:08:7f:22:6b:45:ee:58:
68:d8:26:44:33:5e:45:e1:82:b2:5c:99:41:6b:1e:fa:e8:1a:
a2:f1:8f:44:22:e1:d6:58:5f:4c:28:3d:e0:78:21:ea:aa:85:
08:a5:c8:b3:34:19:d3:c7:e2:fe:a2:a4:f5:68:18:53:5f:ff:
7d:35:22:3c:97:3d:4e:ad:62:5f:bb:4d:88:fb:67:f4:d5:2d:
81:c8:2c:6c:5e:0e:e2:2c:f5:e9:07:34:16:01:e2:bf:1f:cd:
6a:66:db:b6:7b:92:df:13:a1:d0:58:d8:4d:68:96:66:e3:00:
6e:ce:11:99:36:9c:b3:b5:81:bf:d1:5b:d7:f2:08:5e:7d:ea:
97:fe:b3:80:d6:27:1c:89:e6:f2:f3:03:fc:dc:de:83:5e:24:
af:46:a6:2a:8e:b1:34:67:51:2b:19:eb:4c:78:12:ac:00:4e:
58:5e:fd:6b:4c:ce:73:dd:b3:91:73:4a:d6:6f:2c:86:25:f0:
6a:fb:96:66:b3:39:a4:b0:d9:46:c2:fc:6b:06:b2:90:9c:13:
e1:02:8b:6f:6e:ab:cf:e3:21:7e:a9:76:c1:38:15:eb:e6:2d:
a5:6f:e5:ab
#建立k8s中的用户
[root@k8s-master pki]# kubectl config set-credentials timinglee --client-certificate /etc/kubernetes/pki/timinglee.crt --client-key /etc/kubernetes/pki/timinglee.key --embed-certs=true
User "timinglee" set.
#为用户创建集群的安全上下文
root@k8s-master pki]# kubectl config set-context timinglee@kubernetes --cluster kubernetes --user timinglee
Context "timinglee@kubernetes" created.
#切换用户,用户在集群中只有用户身份没有授权
[root@k8s-master ~]# kubectl config use-context timinglee@kubernetes
Switched to context "timinglee@kubernetes".
[root@k8s-master ~]# kubectl get pods
Error from server (Forbidden): pods is forbidden: User "timinglee" cannot list resource "pods" in API group "" in the namespace "default"
#切换会集群管理
[root@k8s-master ~]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".
RBAC(Role Based Access Control)
基于角色访问控制授权:
允许管理员通过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
role授权
[root@k8s-master rbac]# kubectl create role myrole --dry-run=client --verb=get --resource pods -o yaml > myrole.yml
[root@master rbac]# vim myrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: myrole
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- watch
- list
- create
- update
- path
- delete
- apiGroups:
- ""
resources:
- services
verbs:
- get
- watch
- list
- create
- update
- path
- delete
[root@k8s-master rbac]# kubectl apply -f myrole.yml
role.rbac.authorization.k8s.io/myrole configured
#建立角色绑定
[root@k8s-master rbac]# kubectl create rolebinding timinglee --role myrole --namespace default --user timinglee --dry-run=client -o yaml > rolebinding-myrole.yml
[root@k8s-master rbac]# vim rolebinding-myrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: timinglee
namespace: default
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: myrole
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: timinglee
[root@master schedule]# kubectl apply -f rolebinding-myrole.yml
rolebinding.rbac.authorization.k8s.io/timinglee created
#切换用户测试授权
[root@k8s-master rbac]# kubectl config use-context timinglee@kubernetes
Switched to context "timinglee@kubernetes".
#切换回管理员
[root@k8s-master rbac]# kubectl config use-context kubernetes-admin@kubernetes
Switched to context "kubernetes-admin@kubernetes".
clusterrole授权实施
#建立集群角色
[root@k8s-master rbac]# kubectl create clusterrole myclusterrole --resource=deployment --verb get --dry-run=client -o yaml > myclusterrole.yml
[root@master schedule]# vim myclusterrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: myclusterrole
rules:
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- watch
- list
- create
- update
- path
- delete
- apiGroups:
- ""
resources:
- services
verbs:
- get
- watch
- list
- create
- update
- path
- delete
- apiGroups:
- ""
resources:
- deployments
verbs:
- get
- watch
- list
- create
- update
- path
- delete
#建立集群角色绑定
[root@k8s-master rbac]# kubectl create clusterrolebinding clusterrolebind-myclusterrole --clusterrole myclusterrole --user timinglee --dry-run=client -o yaml > clusterrolebind-myclusterrole.yml
[root@k8s-master rbac]# vim clusterrolebind-myclusterrole.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: clusterrolebind-myclusterrole
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: myclusterrole
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: timinglee

更多推荐



所有评论(0)