一、概述
二、安装及配置
- 关闭防火墙
systemctl stop firewalld
systemctl disable firewalld
- 关闭selinux
sed -i 's/enforcing/disabled/' /etc/selinux/config # 永久
setenforce 0 # 临时
- 关闭swap分区
swapoff -a # 临时
vim /etc/fstab # 永久
- 设置主机名字
hostnamectl set-hostname <hostname>
- 在 master 添加 hosts(三台主机)
cat >> /etc/hosts << EOF
192.168.227.20 master
192.168.227.21 node1
192.168.227.22 node2
EOF
- 将桥接的 IPv4 流量传递到 iptables 的链(三台主机)
cat > /etc/sysctl.d/k8s.conf << EOF
net.ipv4.ip_forward = 1
net.ipv4.tcp_tw_recycle = 0
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
sysctl --system
- 时间统部
yum install ntpdate -y
ntpdate time.windows.com
- 所有节点安装 Docker/kubeadm/kubelet
(1)安装 Docker
# 依赖安装
yum install -y yum-utils device-mapper-persistent-data lvm2
# 阿里源
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# docker
yum install docker-ce -y # 安装指定版本,例如yum install -y docker-ce-18.09
systemctl start docker
systemctl enable docker
(2)设置阿里云 YUM 软件源
cat > /etc/docker/daemon.json << EOF
{
"registry-mirrors": ["https://qtaqfn8r.mirror.aliyuncs.com"],
"exec-opts": ["native.cgroupdriver=systemd"]
}
EOF
systemctl restart docker
# 添加yum源
cat > /etc/yum.repos.d/kubernetes.repo << EOF
[kubernetes]
name=Kubernetes
baseurl=https://mirrors.aliyun.com/kubernetes/yum/repos/kubernetes-el7-x86_64
enabled=1
gpgcheck=0
repo_gpgcheck=0
gpgkey=https://mirrors.aliyun.com/kubernetes/yum/doc/yum-key.gpg https://mirrors.aliyun.com/kubernetes/yum/doc/rpm-package-key.gpg
EOF
(3)安装 kubeadm,kubelet 和 kubectl
yum install -y kubelet-1.16.2 kubeadm-1.16.2 kubectl-1.16.2
systemctl start kubelet
systemctl enable kubelet
- 部署 Kubernetes Master
(1)在 192.168.227.20(Master)执行
kubeadm init --apiserver-advertise-address=192.168.227.20 --image-repository registry.aliyuncs.com/google_containers --kubernetes-version v1.23.0 --service-cidr=10.140.0.0/16 --pod-network-cidr=10.240.0.0/16
- 由于默认拉取镜像地址 k8s.gcr.io 国内无法访问,这里指定阿里云镜像仓库地址。
(2)使用 kubectl 工具:
mkdir -p $HOME/.kube
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $(id -u):$(id -g) $HOME/.kube/config
kubectl get nodes
- 加入 Kubernetes Node
(1)在 192.168.31.62/63(Node)执行
向集群添加新节点,执行在 kubeadm init 输出的 kubeadm join 命令:
kubeadm join 192.168.227.20:6443 --token esce21.q6hetwm8si29qxwn \
--discovery-token-ca-cert-hash
sha256:00603a05805807501d7181c3d60b478788408cfe6cedefedb1f97569708be9c5
- 安装 Pod 网络插件(CNI)
注意:这里的yml文件有问题,需要手动创建文件并切换ip
# 下载文件
wget https://github.com/xuwei777/xw_yaml/blob/main/kube-flannel.yml
# 修改net-conf.json下面的网段为上面init pod-network-cidr的网段地址(必须正确否则会导致集群网络问题)
sed -i 's/10.244.0.0/10.240.0.0/' kube-flannel.yml
kubectl apply –f kube-flannel.yml
# 查看插件进度
kubectl get pods -n kube-system
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: psp.flannel.unprivileged
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: docker/default
seccomp.security.alpha.kubernetes.io/defaultProfileName: docker/default
apparmor.security.beta.kubernetes.io/allowedProfileNames: runtime/default
apparmor.security.beta.kubernetes.io/defaultProfileName: runtime/default
spec:
privileged: false
volumes:
- configMap
- secret
- emptyDir
- hostPath
allowedHostPaths:
- pathPrefix: "/etc/cni/net.d"
- pathPrefix: "/etc/kube-flannel"
- pathPrefix: "/run/flannel"
readOnlyRootFilesystem: false
runAsUser:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
fsGroup:
rule: RunAsAny
allowPrivilegeEscalation: false
defaultAllowPrivilegeEscalation: false
allowedCapabilities: ['NET_ADMIN', 'NET_RAW']
defaultAddCapabilities: []
requiredDropCapabilities: []
hostPID: false
hostIPC: false
hostNetwork: true
hostPorts:
- min: 0
max: 65535
seLinux:
rule: 'RunAsAny'
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: flannel
rules:
- apiGroups: ['extensions']
resources: ['podsecuritypolicies']
verbs: ['use']
resourceNames: ['psp.flannel.unprivileged']
- apiGroups:
- ""
resources:
- pods
verbs:
- get
- apiGroups:
- ""
resources:
- nodes
verbs:
- list
- watch
- apiGroups:
- ""
resources:
- nodes/status
verbs:
- patch
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: flannel
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: flannel
subjects:
- kind: ServiceAccount
name: flannel
namespace: kube-system
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: flannel
namespace: kube-system
---
kind: ConfigMap
apiVersion: v1
metadata:
name: kube-flannel-cfg
namespace: kube-system
labels:
tier: node
app: flannel
data:
cni-conf.json: |
{
"name": "cbr0",
"cniVersion": "0.3.1",
"plugins": [
{
"type": "flannel",
"delegate": {
"hairpinMode": true,
"isDefaultGateway": true
}
},
{
"type": "portmap",
"capabilities": {
"portMappings": true
}
}
]
}
net-conf.json: |
{
"Network": "10.244.0.0/16",
"Backend": {
"Type": "vxlan"
}
}
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: kube-flannel-ds
namespace: kube-system
labels:
tier: node
app: flannel
spec:
selector:
matchLabels:
app: flannel
template:
metadata:
labels:
tier: node
app: flannel
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: kubernetes.io/os
operator: In
values:
- linux
hostNetwork: true
priorityClassName: system-node-critical
tolerations:
- operator: Exists
effect: NoSchedule
serviceAccountName: flannel
initContainers:
- name: install-cni-plugin
image: rancher/mirrored-flannelcni-flannel-cni-plugin:v1.1.0
command:
- cp
args:
- -f
- /flannel
- /opt/cni/bin/flannel
volumeMounts:
- name: cni-plugin
mountPath: /opt/cni/bin
- name: install-cni
image: rancher/mirrored-flannelcni-flannel:v0.18.1
command:
- cp
args:
- -f
- /etc/kube-flannel/cni-conf.json
- /etc/cni/net.d/10-flannel.conflist
volumeMounts:
- name: cni
mountPath: /etc/cni/net.d
- name: flannel-cfg
mountPath: /etc/kube-flannel/
containers:
- name: kube-flannel
image: rancher/mirrored-flannelcni-flannel:v0.18.1
command:
- /opt/bin/flanneld
args:
- --ip-masq
- --kube-subnet-mgr
resources:
requests:
cpu: "100m"
memory: "50Mi"
limits:
cpu: "100m"
memory: "50Mi"
securityContext:
privileged: false
capabilities:
add: ["NET_ADMIN", "NET_RAW"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: EVENT_QUEUE_DEPTH
value: "5000"
volumeMounts:
- name: run
mountPath: /run/flannel
- name: flannel-cfg
mountPath: /etc/kube-flannel/
- name: xtables-lock
mountPath: /run/xtables.lock
volumes:
- name: run
hostPath:
path: /run/flannel
- name: cni-plugin
hostPath:
path: /opt/cni/bin
- name: cni
hostPath:
path: /etc/cni/net.d
- name: flannel-cfg
configMap:
name: kube-flannel-cfg
- name: xtables-lock
hostPath:
path: /run/xtables.lock
type: FileOrCreate
- 测试 k8s 集群
在 Kubernetes 集群中创建一个 pod,验证是否正常运行:
kubectl create deployment nginx --image=nginx
kubectl expose deployment nginx --port=80 --type=NodePort
# 查看pod状态,必须是running状态而且ready是1,并查看nginx svc的80端口映射到了哪个端口
kubectl get pod,svc
- 访问地址:http://NodeIP:Port
安装kuboard图形管理平台
在K8S集群之外准备一台主机安装docker,并通过docker安装kuboard
[root@kuboard ~]# yum -y install yum-utils
[root@kuboard ~]# yum-config-manager --add-repo
http://mirrors.aliyun.com/docker-ce/linux/centos/dockerce.repo
[root@kuboard ~]# yum -y install docker-ce-20.10.9-3.el7
[root@kuboard ~]# systemctl enable --now docker
创建kuboard
[root@kuboard ~]# docker run -id --name=kuboard --restart=always -p 80:80 -p 10081:10081 -e KUBOARD_ENDPOINT="http://192.168.0.26:80" -e KUBOARD_AGENT_SERVER_TCP_PORT="10081" -v /root/kuboard-data:/data eipwork/kuboard:v3
- 访问kuboard页面:http://server_ip
三、资源管理方式
kubernetes的本质就是一个容器集群统一管理系统,用户可以在集群中部署各种服务,所谓部署服务就是在kubernetes集群中运行一个一个的容器,并将指定的程序跑在容器中
在kubernetes中,所有内容都被抽象为资源对象,学习kubernetes主要学习如何管理资源对象
# 所有资源可通过下面命令进行查看
kubectl api-resources
1、K8s资源类型介绍
资源名称 | 缩写 | 资源作用 |
---|---|---|
nodes | no | 集群组成部分 |
namespaces | ns | 隔离pod |
pods | po,pod | 装载容器(容器组) |
replicationcontrollers | rc | 控制pod资源 |
replicasets | rs | 控制pod资源 |
deployments | deploy | 控制pod资源 |
daemonsets | ds | 控制pod资源 |
jobs | 控制pod资源 | |
cronjobs | cj | 控制pod资源 |
horizontalpodautoscalers | hpa | 控制pod资源 |
statefulsets | sts | 控制pod资源 |
services | svc | 统一pod对外接口 |
ingress | ing | 统一pod对外接口 |
volumeattachments | 存储资源 | |
persistentvolumes | pv | 存储资源 |
persistentvolumeclaims | pvc | 存储资源 |
confifigmaps | cm | 配置资源 |
secrets | 配置资源 | |
serviceaccount | sa | 服务账户 |
- 直接使用
kubectl
命令去管理k8s集群
kubectl run nginx-pod --image=nginx:1.17.4 --port=80
- 将配置写入到
yaml
文件,通过文件去管理k8s集群
kubectl create/patch -f nginx-pod.yaml
是kubernetes集群的命令行工具,通过它能过够对集群本身进行管理,并能够在集群上进行容器化应用的安装部署。
# 获取命令帮助
kubectl --help
2、常用指令
命令 | 作用 |
---|---|
create | 创建一个资源 |
edit | 编辑一个资源 |
get | 获取一个资源 |
patch | 更新一个资源 |
delete | 删除一个资源 |
explain | 展示资源文档 |
run | 在命令行运行一个容器 |
expose | 在命令行暴露资源端口 |
describe | 显示资源内部信息 |
logs | 输出容器在pod中的日志 |
exec | 进入运行中的容器 |
cp | 在pod内外复制文件 |
rollout | 管理资源的发布 |
scale | 扩(缩)容pod的数量 |
autoscale | 自动调整pod的数量 |
apply | 创建资源/更新资源 |
label | 标签管理命令 |
cluster-info | 显示集群信息 |
version | 显示当前Server和Client版本信息 |
**命令格式:**kubectl [command] [type] [name] [flags]
- command:指定要对资源执行的操作,例如:create、get、delete
- type:指定资源类型,例如:deployment、pod、service
- name:指定资源名称,名称区分大小写
- flags:指定额外的可选参数,例如:-o wide
练习
资源名称 | 缩写 | 资源作用 |
---|---|---|
pods | po | 装载容器 |
#查看所有pod
kubectl get pod
#查看指定的pod(根据pod名字查找)
kubectl get pod nginx-696649f6f9-g5nds
#查看指定pod,通过额外参数显示pod详细信息,包括pod的IP地址,pod运行的节点等
kubectl get pod nginx-696649f6f9-g5nds -o wide
#查看指定pod,通过额外参数显示pod信息,以json格式显示
kubectl get pod nginx-696649f6f9-g5nds -o json
#查看指定pod,通过额外参数显示pod信息,以yaml格式显示
kubectl get pod nginx-696649f6f9-g5nds -o yaml
#显示指定pod资源内部信息
kubectl describe pod nginx-696649f6f9-g5nds
#显示当前Server和Client版本信息
kubectl version
#显示集群信息
kubectl cluster-info
四、Namespace名命空间
Namespace(名称空间)是kubernetes系统中的一种非常重要的资源,它的主要作用是用来实现资源隔离
(例如生活中的房间)可以将不同的Pod划分到不同的Namespace(名称空间)进行隔离,可以形成逻辑上的“组”
案例:以一个namespace(名称空间)的创建和删除简单演示命令用法
资源名称 | 缩写 | 资源作用 |
---|---|---|
namespaces | ns | 隔离pod |
查看ns信息
# kubectl get ns
calico-apiserver calico 网络资源命名空间
calico-system calico 网络资源命名空间
default 默认的名称空间,所有未指定的Pod都会被分配在该空间下
kube-node-lease 集群节点之间的心跳维护
kube-public 该名称空间下的资源可以被所有人访问,包括未认证的用户
kube-system 所有由k8s系统创建的核心组件资源都处于这个名称空间
tigera-operator calico 网络资源名称空间查看指定ns的信息
# kubectl get pod -n kube-system
创建一个名为dev的ns
# kubectl create ns dev
# kubectl get ns
删除ns
# kubectl delete ns dev
1、yaml
特点
- 严格区分大小写
- 使用缩进表示层级关系
- 缩进不允许使用tab键,只允许使用空格,缩进的空格数量没有严格要求,只要相同层级左对齐即可
#
号表示注释- 书写YAML切记 : 后边要加一个空格
- 如果需要将多段YAML配置放在同一个文件中,中间需要用 — 作为分格
2、yaml常见数据结构
- 对象(Object):键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
- 数组:一组按次序排列的值,又称为序列(sequence) / 列表 (list)
对象类型:对象的一组键值对,使用冒号结构表示
#对象形式一(推荐)
个人信息:
name: zhangsan
age: 30
address: tianjin
#对象形式二(了解)
个人信息: {name: zhangsan,age: 30,address: tianjin}
数组类型:一组连词线开头的行,构成一个数组
- 联系方式:
phone: 138****6789
QQ:
WeChat:
emal:
复合结构:对象和数组可以结合使用,形成复合结构
个人信息:
name: zhangsan
age: 30
address: tianjin
- 联系方式:
phone:
QQ:
WeChat:
emal:
3、k8s资源对象描述
在kubernetes中基本所有资源的一级属性都是一样的,主要分为五部分:
apiVersion
:资源版本,由k8s内部定义,版本号必须可以通过kubectlapi-versions 查询到kind
:资源类型,由k8s内部定义,类型必须可以通过kubectl api-resources查询到metadata
:元数据,主要是指定资源标识与说明,常用的有name、namespace、labels等spec
:资源描述,这是配置中最重要的一部分,里边对各种资源配置的详细描述status
:资源状态信息,里边的内容不需要定义,有k8s自动生成
4、yaml文件创建资源
案例:通过yaml文件创建一个test命名空间
kubect explain 资源名称 #explain用于查看资源文档
查看ns文档:kubectl explain ns
查看子属性:kubectl explain ns.metadata
# vim ns_test.yaml
apiVersion: v1
kind: Namespace
metadata:
name: test
执行文件创建ns
# kubectl create -f ns_test.yaml
查看ns信息
# kubectl get ns
test
通过yaml文件删除ns
# kubectl delete -f ns_test.yaml
五、Pod
1、概述
-
Pod 是 k8s 系统中可以创建和管理的
最小单元
。Pod(碗豆菜)是kubernetes集群进行管理的
最小单元
,程序必须部署在容器中,而容器必须存在于Pod中,kubernetes集群启动以后,集群中的各个组件也都是以Pod方式运行。 -
包括多个容器(一组容器的集合)
-
一个pod中容器共享网络命名空间。
-
pod是短暂的(每次重启都会
改变ip
)
1.1、Pod特性
-
资源共享
一个 Pod 里的多个容器可以共享存储和网络,可以看作一个逻辑的主机。共享的如namespace,cgroups 或者其他的隔离资源。
多个容器共享同一
network namespace
,由此在一个 Pod 里的多个容器共享 Pod 的 IP 和端口namespace
,所以一个 Pod 内的多个容器之间可以通过 localhost 来进行通信,所需要注意的是不同容器要注意不要有端口冲突即可。不同的 Pod 有不同的 IP,不同 Pod 内的多个容器之前通信,不可以使用 IPC(如果没有特殊指定的话)通信,通常情况下使用 Pod的 IP 进行通信。一个 Pod 里的多个容器可以共享存储卷,这个存储卷会被定义为 Pod 的一部分,并且可以挂载到该 Pod 里的所有容器的文件系统上。
-
生命周期短暂
Pod 属于生命周期比较短暂的组件,比如,当 Pod 所在节点发生故障,那么该节点上的 Pod会被调度到其他节点,但需要注意的是,被重新调度的 Pod 是一个全新的 Pod,跟之前的Pod 没有半毛钱关系。
-
平坦的网络
K8s 集群中的所有 Pod 都在同一个共享网络地址空间中,也就是说每个 Pod 都可以通过其他 Pod 的 IP 地址来实现访问。
1.2、存在的意义
-
创建容器使用docker,一个docker对应一个容器,一个容器有进程,一个容器运行一个应用程序
-
Pod是多进程设计,运行多个应用程序
一个Pod有多个容器,一个容器里面运行一个应用程序
-
Pod存在为了亲密性应用
- 两个应用之间进行交互
- 网络之间调用
- 两个应用需要频繁调用
2、实现机制
每一个 Pod 都有一个特殊的被称为根容器
的 Pause容器。Pause 容器对应的镜 像属于 Kubernetes 平台的一部分,除了 Pause 容器,每个 Pod还包含一个或多个紧密相关的用户业务容器
- 共享网络:通过
Pause
容器,把其他业务容器加入到Pause
容器里面,让所有业务容器在同一个名称空间中,可以实现网络共享
- 共享存储:引入数据卷概念
Volumes
,使用数据卷进行持久话存储
3、细节
3.1、imagePullPolicy
IfNotPresent
:本地有则使用本地镜像,本地没有则从远程仓库拉取镜像Alawys
:总是从远程仓库拉取镜像Never
:只使用本地镜像,从不去远程仓库拉取,本地如果没有就报错
![]() |
---|
[root@master ~]# kubectl explain pod.spec.containers.imagePullPolicy
3.2、resources
![]() |
---|
![]() |
创建pod
# kubectl create -f deploy_nginx.yml
查看pod信息
# kubectl get pod -n test
查看pod详细描述信息
# kubectl describe pod -n test
删除deploy
# kubectl delete -f deploy_nginx.yml
3.3、restartPolicy
容器一旦出现了问题,K8s就会对容器所在的pod进行重启,重启操作是由pod的重启策略决定的,pod的重启策略有三种,可通过下边命令查看
Always
:当容器终止退出后,总是重启容器,默认
策略OnFailure
:当容器异常退出(推出状态码非0)时,才重启容器。Never
:当容器终止推出,从不重启容器
![]() |
---|
[root@master ~]# kubectl explain pod.spec.restartPolicy
重启策略适用于pod中的所有容器,首次需要重启的容器,将在其需要时立即进行重启,随后再次需要重启的操作将由kubelet延迟一段时间后进行,且反复的重启操作延时时长为10s、20s、40s、80s、160s、300s,最长延时为300s,以后重启延时均为300s,直至重启成功
5、调度策略
5.1、pod创建过程
-
kubectl将yaml文件转换为json,提交给apiserver,apiserver通过kubeconfig进行认证,将Pod信息存储到
etcd
中 -
scheduler通过list watch机制监听到创建新Pod的事件,根据Pod属性决定调度到哪个Node上,Pod属性包括请求的
CPU/内存大小
、nodeSelector
、亲和性
、污点容忍
等,同时给Pod打标签指明调度到具体哪个节点,可以通过kubectl get pod -owide
查看 -
apiserver拿到调度结果并写入到etcd中
-
kubelet从apiserver获取分配到其所在节点的Pod
-
kubelet调用CNI接口创建Pod网络,调用CSI进行存储卷挂载,调用CRI接口启动容器
-
Docker把容器的状态汇报给kubelet
-
kubelet将Pod状态更新到apiserver,apiserver将状态信息写到etcd中
-
kubectl get pod
![]() |
---|
![]() |
5.2、资源限制和节点选择器
- 资源限制影响Pod调度
![]() |
---|
- 节点选择器标签影响Pod调度
![]() |
---|
5.3、亲和性
-
硬亲和性
约束条件必须满足
-
软亲和性
尝试满足,不保证
![]() |
---|
5.4、污点和污点容忍
污点殊值有三个
- NoSchedule:一定不会被调度
- PreferNoSchdule:尽量不被调度
- NoExecute:不会调度,并且还会驱逐Node已有Pod
kubectl taint node [node] key=value:污点三个值
六、Pod控制器
在k8s中,pod的创建方式分为两类:
- 静态Pod: 也称之为
无控制器管理的自主式pod
,直接由特定节点上的kubelet 守护进程管理, 不需要API 服务器看到它们,对于静态 Pod 而言,kubelet 直接监控每个 Pod,这种pod删除后就没有了,也不会重建。 - 控制器管理的pod: 控制器可以控制pod的副本数,
扩容与缩容
、版本更新
、版本回滚
等。
k8s的1.17之前版本中, kubectl run命令默认通过Deployment控制器创建Pod,在v1.18版本中, kubectl run命令改为创建静态pod
pod控制器是管理pod的中间层,使用了pod控制器之后,我们只需要告诉pod控制器,需要多少个什么样的pod就可以了,它就会创建出满足条件
的pod,并确保每一个pod处于用户期望
的状态,如果pod在运行中出现故障
,控制器会基于指定的策略重新启动
或重建pod
在k8s中,pod控制器的种类有很多,每种pod控制器的应用场景都不一样,常见的有下面这些:
-
ReplicationController
:比较原始的pod控制器,目前已经被废弃
,由ReplicaSet代替。 -
ReplicaSet
:支持pod数量变更,镜像版本变更
。 -
Deployment(无状态)
:通过ReplicaSet来控制pod,包含ReplicaSet所有功能,还支持滚动升级
,版本回退
。微服务就是无状态的一种
滚动:当有新版本的镜像,会替换掉最初的版本。如果有三个镜像需要升级,则会先替换掉一个。
-
Horizontal Pod Autoscaler
:可以根据集群负载自动调整pod数量
,实现pod扩容缩
。 -
DaemonSet
:节点级别控制器,确保全部每一个节点上运行一个 Pod的副本,当有 Node 加入集群时,也会为他们新增一个 Pod ,当有Node 从集群移除时,这些 Pod 也会被回收,删除 DaemonSet 将会删除它创建的所有 Pod。收集所有主机的日志
-
StatefulSet
:StatefulSet 是用来管理有状态的应用,例如数据库。所谓的有状态Mysql就是具有存储能力,如果服务器死了,其他服务器拉取数据还是在的,有一个稳定的域名
-
Job
:运行一次性任务的Pod,它创建的pod只要完成就立即退出(健康检查,数据备份)。
容器按照持续运行的时间可分为两类:服务类容器和工作类容器
服务类容器通常持续提供服务,需要一直运行,比如http、server、daemon 等
工作类容器则是一次性任务,比如批处理程序,完成后容器就退出
Cronjob
:它创建的pod会周期性的执行,用于执行周期性任务(常用在数据备份工作)。
1、Pod资源清单介绍
以下是比较详细的资源清单介绍
KIND: Pod #资源类型类型
VERSION: v1 #资源版本
DESCRIPTION: #资源描述
FIELDS: #资源可配置的属性,如下
apiVersion: v1 #必选的一级属性,版本号,例如v1
kind: Pod #必选的一级属性,资源类型,例如Pod
metadata: #必选的一级属性,元数据
name: #必选的二级属性,Pod名称
namespace: dev #二级属性,Pod所属的名称空间,例如dev,默认为default名称空间
labels: #二级属性,自定义标签列表
- name: #三级属性,标签名称
spec: #必选的一级属性,Pod中容器的详细定义
containers: #必选的二级属性,Pod中容器列表
- name: #必选的三级属性,容器名称
image: #必选的三级属性,容器镜像名称
imagePullPolicy: #三级属性,镜像的拉取策略
command: #三级属性,容器的启动命令列表,如不指定,使用打包时使用的启动命令
args: #三级属性,容器的启动命令参数列表
workingDir: #三级属性,容器的工作目录
volumeMounts: #三级属性,挂载到容器内部的存储卷配置
- name: #四级属性,引用pod定义的共享存储卷的名称
mountPath: #四级属性,存储卷在容器内mount的绝对路径,应少于512字节
readOnly: #四级属性,是否为只读模式
ports: #三级属性,需要暴露的端口库号列表
- name: #四级属性,端口的名称
containerPort: #四级属性,容器需要监听的端口号
hostPort: #四级属性,容器所在的主机需要监听的端口号,默认与Container相同
protocol: #四级属性,端口协议,支持TCP/UDP,默认为TCP
env: #三级属性,容器运行前需要设置的环境变量列表
- name: #四级属性,环境变量名称
value: #四级属性,环境变量的值
resources: #三级属性,资源限制和请求的设置
limits: #四级属性,资源最大限制的设置
CPU: #五级属性,CPU资源限制,单位为core数,将用于docker run --cpu-shares参数
memory: #五级属性,内存资源限制,单位可以为Mib/Gib,将用于docker run --memory参数
requests: #四级属性,资源最小请求的设置
CPU: #五级属性,CPU请求,容器启动的初始可用数量
memory: #五级属性,内存请求,容器启动的初始可用数量
lifecycle: #三级属性,生命周期钩子
postStart: #四级属性,容器启动后立即执行此钩子,如果执行失败,会根据重启策略进行重启
preStop: #四级属性,容器终止前执行此钩子,无论结果如何,容器都会终止
livenessProbe: #三级属性,对Pod内个容器健康检查设置,当探测容器无响应后将自动重启该容器
tcpSocket: #三级属性,对Pod内容器健康检查方式
initialDelaySeconds: #三级属性,容器启动完成后,首次探测时间,单位为秒
timeoutSeconds: #三级属性,对容器健康检查探测等待相应的超时时间,单位秒,默认1秒
periodSeconds: #三级属性,对容器监控检查的定期探测时间设置,单位秒,默认10秒一次
restartPolicy: #二级属性,Pod的重启策略
nodeName: #二级属性,设置pod调度到指定的node节点上
nodeSelector: #二级属性,设置Pod调度到指定的label的node节点上
imagePullSecrets: #二级属性,拉取镜像时,使用secret名称,以key:secretkey格式指定
hostNetwork: #二级属性,是否使用主机网络模式,默认为false,如果设置为true,表示使用宿主机网络
volumes: #二级属性,在该Pod上定义共享存储卷列表
- name: #三级属性,共享存储卷名称
emptyDir: #三级属性,类型为emptyDir的存储卷,与Pod同生命周期的一个临时目录,为空值
hostPath: #三级属性,类型为hostPath的存储卷,挂载集群与定义的secret对象到容器内部
path: #四级属性,Pod所在宿主机的目录,将被用于容器中挂载的目录
secret: #三级属性,类型为secret的存储卷,挂载集群与定义的secret对象到容器内部
configMap: #三级属性,类型为configMap的存储卷,挂载预定义的configMap对象到容器内部
2、控制器 ReplicaSet(RS)
2.1、ReplicaSet(RS)特点介绍
ReplicaSet的主要作用是保证一定数量的pod能够正常的运行,它会持续监听
这些pod的运行状态,一旦pod发生故障,就会重启
或重建
pod,同时还支持对pod数量的扩缩容
和版本镜像变更
ReplicaSet的资源清单文件查询方式
kubectl explain rs
kubectl explain rs.spec
kubectl explain rs.spec.selector
kubectl explain rs.spec.template
kubectl explain rs.spec.template.metadata
kubectl explain rs.spec.template.spec
2.2、ReplicaSet(RS)应用案例
案例:通过RS控制器创建3个Nginx Pod
# vim rs_nginx.yml
apiVersion: apps/v1
kind: ReplicaSet
metadata:
name: rs-nginx
namespace: test
spec:
replicas: 3 #创建pod的副本数量,默认为1
selector: #标签选择器,通过它指定RS管理哪些pod
matchLabels: #标签类型(key=value)
app: rs-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板,通过模板创建Pod
metadata: #定义模板的元数据信息
labels: #定义Pod的标签
app: rs-nginx #Pod的标签
spec:
containers:
- name: nginx
image: nginx:1.17.0
创建pod
# kubectl create -f rs_nginx.yml
查看pod详细信息
# kubectl get pod -n test
过滤Pod标签
# kubectl describe pod -n test | grep Labels
查看pod控制器详细信息
# kubectl get rs -n test -o wide
DESIRED:期望的pod副本数量
CURRENT:当前的pod副本数量
READY:已经准备好提供服务的副本数量
8.3、ReplicaSet(RS)版本变更
案例:通过RS控制器实现镜像版本变更
通过edit(配置文件形式)可直接修改镜像版本
# kubectl edit rs rs-nginx -n test
...
spec:
containers:
- image: nginx:1.18.0 #修改为1.18.0版本查看rs详细信息
# kubectl get rs -n test -o wide
删除RS方式
命令删除方式
# kubectl delete rs rs-nginx -n test
查看rs信息
# kubectl get rs -n test
配置文件删除方式
# kubectl delete -f rs-nginx.yml
3、控制器 Deployment
为了更好的解决服务编排问题,k8s在v1.2版本开始,引入了Deployment(Deploy)控制器,该pod控制器不会去直接管理pod,而是通过管理ReplicaSet来间接的管理pod,所以Deployment比ReplicaSet功能更强大
**Deployment功能如下 **
- 支持RS所有功能
- 支持发布的停止、继续
- 支持版本滚动更新和版本回退
导出yaml文件
kubectl create deployment web --image=nginx --dry-run -o yaml > web.yaml
案例:通过deploy创建基本的pod
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 3 #创建pod的副本数量,默认为1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
containers:
- name: nginx
image: nginx:1.17.0
创建deploy
# kubectl create -f deplo_nginx.yml
查看deploy详细信息
# kubectl get deploy -n test
# kubectl get deploy -n test -o wide
UP-TO-DATE:最新版本的pod数量
AVAILABLE:当前可用的pod数量
查看rs控制器信息,应为deploy通过控制rs管理pod,所以rs控制器也会被创建出来(rs的名称在deploy基础上随机添加)
# kubectl get rs -n test
查看pod信息(pod名称是在rs基础上随机添加)
# kubectl get pod -n test
过滤Pod标签
# kubectl describe pod -n test | grep Labels
删除deploy
# kubectl delete -f deploy_nginx.yml
4、多容器创建方式
案例:将nginx与tomcat放在同一个Pod中运行
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
namespace: test
labels:
app: nginx
spec:
replicas: 1
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: IfNotPresent
ports:
- name: web-nginx
containerPort: 80
protocol: "TCP"
resources:
limits: #资源上限
cpu: "500m" #500m表示0.5个逻辑核心,1000m表示一个逻辑核心
memory: "128Mi" #内存单位可以使用Gi、Mi、G、M等形式
requests: #资源下限
cpu: "250m" #250m表示0.25个逻辑核心,
memory: "64Mi" #所需最低内存资源(如果不足64M,容器无法启动)
- name: tomcat
image: tomcat:8.5-jre10-slim
imagePullPolicy: IfNotPresent
ports:
- name: web-tomcat
containerPort: 8080
protocol: "TCP"
resources:
limits: #资源上限
cpu: "500m" #500m表示0.5个逻辑核心,1000m表示一个逻辑核心
memory: "128Mi" #内存单位可以使用Gi、Mi、G、M等形式
requests: #资源下限
cpu: "250m" #250m表示0.25个逻辑核心,
memory: "64Mi" #所需最低内存资源(如果不足64M,容器无法启动)创建Pod
# kubectl create -f deploy_nginx.yml
查看Pod信息
# kubectl get pod -n test
删除该Pod
# kubectl delete -f deploy_nginx.yml
5、env
evn属性是用于设置容器环境变量的列表,环境变量的定义要根据容器具体需求定义,本章节只讲解如何定义环境变量
可通过下方命令获取env文档帮助
# kubectl explain pod.spec.containers.env
name 定义环境变量名称
value 定义变量值
案例:为MySQL添加环境变量设置root密码
# vim pod_mysql.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-mysql
namespace: test
spec:
replicas: 1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-mysql #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-mysql #pod的标签
spec:
containers:
- name: mysql
image: mysql:5.7 #镜像版本
imagePullPolicy: IfNotPresent #设置镜像拉取策略
ports: #定义端口
- containerPort: 3306 #端口
protocol: TCP #端口协议
env: #定义环境变量
- name: "MYSQL_ROOT_PASSWORD" #变量名称(必须为数组类型)
value: "123456" #值创建Pod
# kubectl create -f pod_mysql.yml
查看Pod信息
# kubectl get pod -n test
查看Pod详细描述
# kubectl describe pod -n test
6、exec
- 格式:
kubectl exec -n 命名空间 -it pod名称 -c 容器名称 -- /bin/bash
kubectl exec -h #命令帮助
案例:进入上述案例中创建的mysql容器
# kubectl exec -n test -it deploy-nginx-579696c576-jfqz6 -c mysql -- /bin/bash
进入数据库
root@deploy-nginx-579696c576-jfqz6:/# mysql -uroot - p123456
案例:进入上述案例中创建的nginx容器
# kubectl exec -n test -it deploy-nginx-579696c576-jfqz6 -c nginx -- /bin/bash
查看网页根目录
root@deploy-nginx-579696c576-jfqz6:/# ls /usr/share/nginx
7、Pod容器执行命令方式
格式:kubectl exec pod名 -c 容器名 -- 命令
注意:
- -c 容器名为可选项,如果是1个pod中1个容器,则不用指定;
- 如果是1个pod中多个容器,不指定默认为第1个。
# kubectl exec nginx-deployment-5fc678675d-5w8c2 -n test -c nginx -- ls /etc/nginx
# kubectl exec nginx-deployment-5fc678675d-5w8c2 -n test -c nginx -- cat /etc/nginx/nginx.conf
删除deploy
kubectl delete -f deploy_nginx.yml
8、Pod调度
定向调度
在默认情况下,一个pod被调度到哪个Node节点运行是由Scheduler组件采用相应的算法计算出来的,这个过程是不受人工控制的,但是在实际工作中,我们想要控制某些pod调度到指定的Node节点,就需要用到pod调度
![]() |
---|
k8s提供了四种调度方式
- 自动调度:pod运行在哪个Node节点上,完全由Scheduler经过算法分配(默认的调度策略)
- 定向调度:NodeName、NodeSelector
- 亲和性调度与反亲和性调度:NodeAffinity、PodAffinity、PodAntiAffinity
- 污点(容忍)调度:Taints、Toleration
NodeName
定向调度是通过在pod上声明NodeName
或者NodeSelector
,以此将pod调度到指定的节点上,但是定向调度属于强制调度,即使指定的Node节点不存在,也会向该节点进行调度,只不过pod运行失败而已
定向调度方式其实是直接跳过了Scheduler的调度逻辑,直接将pod调度到指定名称的节点
案例:创建pod,并通过NodeName
将Pod调度到worker01节点
kubectl explain pod.spec.nodeName
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
nodeName: worker01 --定义调度策略,并指定worker01节点
containers:
- name: nginx
image: nginx:1.20.0 #指定一个本地不存的镜像版本
imagePullPolicy: IfNotPresent #设置镜像拉取策略
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
创建pod
# kubectl create -f deploy_nginx.yml
查看pod详细信息
# kubectl get pod -n test -o wide
删除deploy
NodeSelector
nodeSelector用于将pod调度到添加了指定标签
的node节点上,该调度规则也是强制调度
案例:先为worker02节点打标签,然后创建一个pod,并通过nodeSelector进行调度
为node1与node2节点打标签
# kubectl label node worker02 node=worker02
查看节点标签
# kubectl get node worker02 --show-labels | grep worker02
创建Pod并通过NodeSelector进行调度
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
nodeSelector: 定义nodeSelector
node: worker02 指定节点标签
containers:
- name: nginx
image: nginx:1.20.0 #指定一个本地不存的镜像版本
imagePullPolicy: IfNotPresent #镜像拉取策略
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议创建pod
# kubectl create -f deploy_nginx.yml
查看pod详细信息
# kubectl get pod -n test
# kubectl get pod -n test -o wide
删除deploy
# kubectl delete -f deploy_nginx.yml
污点 Taint
前面的调度方式都是站在pod的角度上,通过在pod上添加属性,来确定pod是否要调度到指定的node上,其实我们也可以站在node的角度上,通过在node上添加污点属性,来决定是否允许pod调度过来
node被设置上污点以后,就和pod之间存在了一种相互排斥的关系,进而拒绝pod调度进来,甚至可以将已经存在的pod驱逐出去
污点的格式:key:污点
,key和value是污点的标签,目前支持如下三个污点:
PreferNoSchedule
:尽量避免调度(软限制),除非没有其他节点可调度NoSchedule
:拒绝调度(硬限制),但不会影响当前已经存在的podNoExecute
:拒绝调度,同时也会将节点上已经存在的pod清除
(尽量不要设置,会导致Pod异常)
提示:如果通过定向调度的方式去创建Pod,那么定向调度的优先级要高于污点。
也就是说不管你有没有污点,只要我通过定向调度的方式,就可以直接忽略你污点的存在
使用kubectl设置和去除污点的命令实例如下:
设置污点
kubectl taint nodes worker01 key:污点
去除污点
kubectl taint nodes worker01 key:污点-
去除所有污点
kubectl taint nodes worker01 key-
PreferNoSchedule
为worker01设置污点,名称为:worker01:PreferNoSchedule
[root@master01 ~]# kubectl taint node noed1 node1:PreferNoSchedule
查看污点信息
[root@master01 ~]# kubectl describe node worker01 | grep Taints
Taints: worker01:PreferNoSchedule
# 创建Pod验证
[root@master01 ~]# kubectl create -f deploy_nginx.yml
# 查看Pod所在节点健康状态
[root@master01 ~]# kubectl get pod -n test -o wide
# 删除deploy
[root@master01 ~]# kubectl delete -f deploy_nginx.yml
# 删除PreferNoSchedule污点
[root@master01 ~]# kubectl taint node worker01 worker01:PreferNoSchedule-
NoSchedule
设置worker01污点为:worker01:NoSchedule
[root@master01 ~]# kubectl taint node worker01 worker01:NoSchedule
查看污点信息
[root@master01 ~]# kubectl describe node worker01 | grep Taints
Taints: worker01:NoSchedule
创建Pod
[root@master01 ~]# kubectl create -f deploy_nginx.yml
查看Pod所在节点健康状态
[root@master01 ~]# kubectl get pod -n test -o wide
#提示:可以发现当前的pod状态为Pending(挂起状态)
删除deploy
[root@master01 ~]# kubectl delete -f deploy_nginx.yml
提示:使用k8s搭建的集群,会默认给master节点添加一个
NoSchedule
污点,所以pod不能被调度到master节点中
容忍 Toleration
上章节讲解了污点的作用,可以通过在worker节点添加污点用于拒绝pod调度上来,但如果我们非要将一个pod调度到一个有污点的node上,通过容忍(忽略污点)可以实现
容忍的配置项说明:
可通过下边命令查看容器的配置项
# kubectl explain pod.spec.tolerations
effect 容忍的污点
key 容忍的污点的key
案例:继上述案例,创建pod并添加容忍,然后将pod调度到worker01节点
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
imagePullPolicy: IfNotPresent #镜像拉取策略
tolerations: 添加容忍
- key: "worker01" 污点的key(必须引起来)
effect: NoSchedule 污点类型
containers:
- name: nginx
image: nginx:1.17.0 #指定镜像
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
创建pod
# kubectl create -f deploy_nginx.yml
查看pod详细信息
# kubectl describe pod -n test
删除deploy并删除yaml文件中的容忍配置项
# kubectl delete -f deploy_nginx.yml
删除污点
# kubectl taint node worker01 worker01:NoSchedule-
Pod或者ns出现异常,可以尝试通过下边的方式进行删除
# kubectl delete pod pod名称 -n ns名称 --force --grace-period=0
亲和性 affinity
nodeAffinity
(node亲和性):以Node为目标,解决Pod可以调度到那些Node的问题。podAffinity
(pod亲和性):以Pod为目标,解决Pod可以和那些已存在的Pod部署在同一个拓扑域中的问题。podAntiAffinity
(pod反亲和性):以Pod为目标,解决Pod不能和那些已经存在的Pod部署在同一拓扑域中的问题。
关于亲和性和反亲和性的使用场景的说明:
亲和性:如果两个应用频繁交互,那么就有必要利用亲和性让两个应用尽可能的靠近,这样可以较少因网络通信而带来的性能损耗。
反亲和性:当应用采用多副本部署的时候,那么就有必要利用反亲和性让各个应用实例打散分布在各个Node上,这样可以提高服务的高可用性。
nodeAffinity
- 查看nodeAffinity的可选配置项:
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"
- 下面演示requiredDuringSchedulingIgnoredDuringExecution(硬限制):
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity: # 亲和性配置
nodeAffinity: # node亲和性配置
requiredDuringSchedulingIgnoredDuringExecution: # Node节点必须满足指定的所有规则才可以,相当于硬规则,类似于定向调度
nodeSelectorTerms: # 节点选择列表
- matchExpressions:
- key: nodeenv # 匹配存在标签的key为nodeenv的节点,并且value是"xxx"或"yyy"的节点
operator: In
values:
- "xxx"
- "yyy"
- 创建pod
kubectl create -f pod-nodeaffinity-required.yaml
- 查看Pod状态(运行失败):
kubectl get pod pod-nodeaffinity-required -n dev -o wide
- 查看Pod详情(发现调度失败,提示node选择失败):
kubectl describe pod pod-nodeaffinity-required -n dev
- 删除pod
kubectl delete -f pod-nodeaffinity-required.yaml
- 修改pod-nodeaffinity-required.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-nodeaffinity-required
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity: # 亲和性配置
nodeAffinity: # node亲和性配置
requiredDuringSchedulingIgnoredDuringExecution: # Node节点必须满足指定的所有规则才可以,相当于硬规则,类似于定向调度
nodeSelectorTerms: # 节点选择列表
- matchExpressions:
- key: nodeenv # 匹配存在标签的key为nodeenv的节点,并且value是"xxx"或"yyy"的节点
operator: In
values:
- "pro"
- "yyy"
- 查看pod
kubectl get pod pod-nodeaffinity-required -n dev -o wide
nodeAffinity的注意事项:
如果同时定义了nodeSelector和nodeAffinity,那么必须两个条件都满足,Pod才能运行在指定的Node上。
如果nodeAffinity指定了多个nodeSelectorTerms,那么只需要其中一个能够匹配成功即可。
如果一个nodeSelectorTerms中有多个matchExpressions,则一个节点必须满足所有的才能匹配成功。
如果一个Pod所在的Node在Pod运行期间其标签发生了改变,不再符合该Pod的nodeAffinity的要求,则系统将忽略此变化。
podAffinity
-
podAffinity主要实现以运行的Pod为参照,实现让新创建的Pod和参照的Pod在一个区域的功能。
-
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-1
topologyKey用于指定调度的作用域,例如:
如果指定为kubernetes.io/hostname,那就是以Node节点为区分范围。
如果指定为beta.kubernetes.io/os,则以Node节点的操作系统类型来区分。
-
演示requiredDuringSchedulingIgnoredDuringExecution。
-
创建参照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
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
nodeName: k8s-node1 # 将目标pod定向调度到k8s-node1
-
- 创建参照Pod:
kubectl create -f pod-podaffinity-target.yaml
-
- 查看参照Pod:
kubectl get pod pod-podaffinity-target -n dev -o wide
-
创建Pod过程:
-
- 创建pod-podaffinity-requred.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-requred
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity: # 亲和性配置
podAffinity: # Pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 该Pod必须和拥有标签podenv=xxx或者podenv=yyy的Pod在同一个Node上,显然没有这样的Pod
- key: podenv
operator: In
values:
- "xxx"
- "yyy"
topologyKey: kubernetes.io/hostname
-
- 创建Pod:
kubectl create -f pod-podaffinity-requred.yaml
-
- 查看Pod状态,发现没有运行:
kubectl get pod pod-podaffinity-requred -n dev
-
- 查看Pod详情:
kubectl get pod pod-podaffinity-requred -n dev
-
- 删除Pod:
kubectl delete -f pod-podaffinity-requred.yaml
-
- 修改pod-podaffinity-requred.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-podaffinity-requred
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity: # 亲和性配置
podAffinity: # Pod亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions: # 该Pod必须和拥有标签podenv=xxx或者podenv=yyy的Pod在同一个Node上,显然没有这样的Pod
- key: podenv
operator: In
values:
- "pro"
- "yyy"
topologyKey: kubernetes.io/hostname
-
- 再次创建Pod:
kubectl create -f pod-podaffinity-requred.yaml
-
- 再次查看Pod:
kubectl get pod pod-podaffinity-requred -n dev -o wide
podAntiAffinity(反亲和性)
-
podAntiAffinity主要实现以运行的Pod为参照,让新创建的Pod和参照的Pod不在一个区域的功能。
-
其配置方式和podAffinity一样,此处不做详细解释。
-
使用上个案例中的目标Pod:
kubectl get pod -n dev -o wide
- 创建pod-podantiaffinity-requred.yaml文件,内容如下:
apiVersion: v1
kind: Pod
metadata:
name: pod-podantiaffinity-requred
namespace: dev
spec:
containers: # 容器配置
- name: nginx
image: nginx:1.17.1
imagePullPolicy: IfNotPresent
ports:
- name: nginx-port
containerPort: 80
protocol: TCP
affinity: # 亲和性配置
podAntiAffinity: # Pod反亲和性
requiredDuringSchedulingIgnoredDuringExecution: # 硬限制
- labelSelector:
matchExpressions:
- key: podenv
operator: In
values:
- "pro"
topologyKey: kubernetes.io/hostname
- 创建Pod:
kubectl create -f pod-podantiaffinity-requred.yaml
- 查看Pod:
kubectl get pod -n dev -o wide
11、Probe
容器探测类似于对容器进行健康检查
,用来探测容器中的程序是否可以正常工作,如果探测到容器出现故障,k8s会尝试重启容器,如果重启失败,k8s不会将流量分配给该容器,不承担业务流量。
k8s提供了两种探针来实现容器的探测,可通过下边命令查看:
livenessProbe
:存活性探针,用于检测容器当前是否处于正常运行状态
,如果不是,容器将会被重启。readinessProbe
:就绪性探针,用于检测容器当前是否可以接收请求
,如果不能,k8s不会转发流量。
# kubectl explain pod.spec.containers
以上两种探针目前均支持多种探测方式,可通过下边命令查看:
# kubectl explain pod.spec.containers.livenessProbe
# kubectl explain pod.spec.containers.readinessProbe
# 存活探针
FIELDS:
exec 命令探测方式
tcpSocket 端口探测方式
httpGet URL请求探测方式
# 就绪探针
initialDelaySeconds 容器启动后等待多少秒执行第一次探测
timeoutSeconds 探测超时时间,默认1秒,最小可设置1秒
failureThreshold 连续探测失败多少次才被认定失败,默认3次为失败,最小可设置1
periodSeconds 执行探测频率,默认是10秒,最小可设置1秒
successThreshold 连续探测成功多少次才被认定为成功,默认1次
以livenessProbe存活性探针给大家介绍两种常用的探测方式:
exec
方式一:exec命令探测方式,在容器内执行一次命令,如果命令执行的退出码为0,则认位程序正常,否则不正常。
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
containers:
- name: nginx
image: nginx:1.18.0 #镜像版本
imagePullPolicy: IfNotPresent #镜像拉取策略
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
livenessProbe: 存活性探针
exec: 命令探测方式
command: [/bin/ls,/etc/hello.txt] 探测一个不存在的文件
创建Pod
# kubectl create -f deploy_nginx.yml
查看Pod信息
# kubectl get pod -n test
查看Pod详细信息
# kubectl describe pod -n test
删除deploy
# kubectl delete -f deploy_nginx.yml
探测一个存在的文件
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
containers:
- name: nginx
image: nginx:1.18.0 #镜像版本
imagePullPolicy: IfNotPresent #镜像拉取策略
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
livenessProbe: 存活性探针
exec: 命令探测方式
command: [/bin/ls,/usr/share/nginx/html/index.html] 探测一个存在的文件
创建Pod
# kubectl create -f deploy_nginx.yml
查看Pod详细信息
# kubectl describe pod -n test
删除Deploy
# kubectl delete -f deploy_nginx.yml
tcpSocket
==方式2:==tcpSocket端口探测方式,访问容器的端口,如果能够建立连接,则认位程序正常,否则不正常。
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
containers:
- name: nginx
image: nginx:1.18.0 #镜像版本
imagePullPolicy: IfNotPresent #镜像拉取策略
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
livenessProbe: 存活性探针
tcpSocket: 端口探测方式
port: 8080 探测一个不存在的端口
提示:删除前边的探测方式,探测方式不能指定超过2种类型
创建pod
# kubectl create -f deploy_nginx.yml
查看pod描述信息
# kubectl describe pod -n test
...
Liveness probe failed: dial tcp 10.244.1.10:8080: connect: connection refused
#提示:活性探针失败,8080拒绝连接
删除deploy
# kubectl delete -f deploy_nginx.yml
探测一个存在的端口
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
containers:
- name: nginx
image: nginx:1.18.0 #镜像版本
imagePullPolicy: IfNotPresent #镜像拉取策略
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
livenessProbe: 存活性探针
tcpSocket: 端口探测方式
port: 80 探测一个存在的端口
创建pod
# kubectl create -f deploy_nginx.yml
查看pod详细信息
# kubectl describe -f deploy_nginx.yml
删除deploy
# kubectl delete -f deploy_nginx.yml
livenessProbe和readinessProbe区别
livenessProbe
和 readinessProbe
是 Kubernetes 中用于健康检查的两种属性。尽管它们可以使用相同的 tcpSocket
属性来检查容器的健康状态,但是它们的主要目的和使用方式有所不同。
livenessProbe
属性:用于确定容器是否处于运行状态。通过定期向容器发送请求,并检查响应状态码或响应时间,来确定容器是否正常运行。如果 livenessProbe 失败,则 Kubernetes 认为容器出现了问题,并会触发重启策略以尝试修复容器。readinessProbe
属性:用于确定容器是否已经准备好接收流量。通过定期向容器发送请求,并检查响应状态码或响应时间,来确定容器是否就绪并可接受流量。如果 readinessProbe 失败,则 Kubernetes 认为容器尚未准备好处理流量,并将从 Service 的负载均衡中排除该容器。
虽然 tcpSocket
可以在 livenessProbe
和 readinessProbe
中使用,但是它们的使用场景和目的是不同的。如果您将 tcpSocket
用作 livenessProbe
,那将仅仅检查容器是否处于运行状态,不关心容器是否已经准备好处理流量。这意味着即使容器已经启动并且正在监听端口,但是如果容器的内部服务或应用程序尚未准备好接受流量,它仍然会被视为健康的,并且可能会接收到流量。
因此,为了更精确地控制容器的行为和避免不可预料的问题,建议在需要时同时使用 livenessProbe
和 readinessProbe
。通过使用适当的请求路径、端口和其他检查参数,可以更全面地确保容器的完整性、可用性和可靠性,并正确控制容器的生命周期和流量管理。
13、strategy
在 Kubernetes 中,Deployment 资源对象具有一个 strategy
字段,用于定义其更新策略。该字段包含以下两个子字段:
type
:指定更新策略的类型,可选的值有:Recreate
:在进行更新时先将旧的 Pod 删除,然后创建新的 Pod。这种策略会导致短暂的应用中断,因为在删除和创建过程中会存在一段时间的空窗期。RollingUpdate
:采用滚动更新
的方式进行更新,逐步替换旧的 Pod。这是默认的更新策略。
rollingUpdate
:仅在type
字段设置为RollingUpdate
时才需要配置。它指定了滚动更新的行为
,包含以下子字段:maxUnavailable
:指定在进行滚动更新时最多允许不可用的 Pod 的数量或百分比。maxSurge
:指定在进行滚动更新时额外允许创建的 Pod 的数量或百分比。
Recreate
案例:通过Recreate对Pod进行重建更新
修改deploy_nginx.yml文件设置Pod更新策略
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
strategy: #Pod更新策略
type: Recreate #重建更新
replicas: 3 #指定Pod数量
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: IfNotPresent #镜像拉取策略
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
创建pod
# kubectl create -f deploy_nginx.yml
另外开一个终端动态观察pod的信息
# kubectl get pod -n test -w
更新镜像版本
# kubectl edit deploy deploy-nginx -n test
...
spec:
containers:
- image: nginx:1.18.1 指定新版本
提示:观察pod的重建过程
#第一步:将Running的pod先终止(Terminating)
#第二步:接下来Pod处于等待状态(Pending)
#第三步:重新创建容器(ContainerCreating)
#第四步:新的容器被创建且以成功运行(Running )
查看镜像版本
# kubectl get deploy -n test -o wide
删除depoly
# kubectl delete -f deploy_nginx.yml
RollingUpdate
案例:通过RollingUpdat对Pod进行滚动更新(默认策略,无需指定,只需要将前边配置文件中的其他更新策略删除即可)
# vim deploy_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
strategy:
type: RollingUpdate 滚动更新
rollingUpdate:
maxUnavailable: 1 指定在进行滚动更新时最多允许不可用的Pod的数量或百分比。
maxSurge: 1 指定在进行滚动更新时额外允许创建的Pod的数量或百分比。
replicas: 6
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
containers:
- name: nginx
image: nginx:1.18.1
imagePullPolicy: IfNotPresent #镜像拉取策略
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
创建pod(--record 记录整个deploy更新历史)
# kubectl create -f deploy_nginx.yml --record
另外一个终端动态查看pod信息
# kubectl get pod -n test -w
更新镜像版本
# kubectl edit deploy deploy-nginx -n test
spec:
containers:
- image: nginx:1.20.0
查看RS信息
# kubectl get rs -n test -o wide
提示:当pod滚动更新后,rs也会随之跟着更新,原有rs中的pod会被删除,但
是原有的rs并不会删除,用于做版本回退
显示当前升级版本的状态
# kubectl rollout status deploy deploy-nginx -n test
14、rollout
Deploy支持版本升级过程中的暂停、继续、回退等功能,具体功能如下:kubectl rollout
版本升级相关功能,支持下面的选项:
-
status
#显示当前升级状态 -
history
#显示升级历史记录 -
pause
#暂停版本升级过程 -
resume
#继续已经暂停的版本升级过程 -
restart
#重启版本升级过程 -
undo
#回滚到上一级版本(可以通过–to-revision回滚到指定版 本)
案例:多次更新镜像版本,随后对镜像版本进行回退
更新镜像版本
# kubectl edit deploy deploy-nginx -n test
spec:
containers:
- image: nginx:1.20.1
更新镜像版本
# kubectl edit deploy deploy-nginx -n test
spec:
containers:
- image: nginx:1.21.0
查看升级历史记录
# kubectl rollout history deploy deploy-nginx -n test
查看具体版本详细信息:--revision=版本编号
# kubectl rollout history deploy deploy-nginx --revision=2 -n test
查看当前所有版本(通过rs可以查看)
# kubectl get rs -o wide -n test
查看当前使用版本(查看deploy)
# kubectl get deploy -o wide -n test
版本回退:通过--to-revision=1,可直接回滚到1版本,如果省略这个选项,就是回退到上个版本
# kubectl rollout undo deploy deploy-nginx --to-revision=2 -n test
查看当前使用的版本
# kubectl get deploy -o wide -n test
查看升级历史记录
# kubectl rollout history deploy deploy-nginx -n test
提示:新版本就是原先的1版本
提示:如果不指定
回退版本
默认会回到上一个版本
15、控制器 DaemonSet
DaemonSet(DS)控制器在功能方面与Deployment控制器几乎一样,支持滚动更新
、版本回退
等,只不过它在创建Pod时,可以保障集群中的每一个节点(master)
上都运行(Pod数量与节点数量保持一致)。
如果一个pod提供的功能是节点级别的(每个节点都需要且只需要一个),一般适用于日志收集
,节点监控
场景,这种类型的pod就适合使用DaemonSet
类型的控制器创建
DaemonSet控制器的特点:
- 每当像集群添加一个节点时,指定的pod副本也将添加到该节点上
- 当节点从集群中移除时,pod也就被
垃圾回收
案例:通过daemonset控制器创建Nginx Pod,并保证在每个节点都运行。
# vim ds_nginx.yml
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: ds-nginx
namespace: test
spec:
selector:
matchLabels:
name: ds-nginx
template:
metadata:
labels:
name: ds-nginx
spec:
tolerations: # 添加容忍,否则pod无法被调度到master节点
- key: node-role.kubernetes.io/master # 污点key
effect: NoSchedule # 污点类型
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
创建pod
# kubectl create -f ds_nginx.yml
查看ds信息
# kubectl get ds -n test
查看pod详细信息
# kubectl get pod -n test -o wide
删除ds
# kubectl delete -f ds_nginx.yml
16、控制器 HPA
Horizontal Pod Autoscaler(HPA)可以实现pod数量的自动扩缩容
,对比于前边手动对pod数量进行调整,HPA更加的智能。
HPA可以获取每个pod的资源利用率,然后和HPA中定义的资源利用率指标进行对比,同时计算出需要伸缩的具体值,最后实现pod的数量的自动(非手动)调整。
metrics-server资源监控
Metrics-Server是集群核心监控数据
的监视器,用于采集节点的CPU和内存资源,从 Kubernetes1.8 开始用来替换之前的heapster,heapster从1.11开始逐渐被废弃。
安装metrics-server可以用来收集集群中的资源使用情况(已经下载,拷贝到主机即可)
[root@master01 ~]# wget https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml
文件内容需要修改
#vim components.yaml
...
- args:
- --kubelet-insecure-tls 添加该参数忽略证书检测
创建metrics-server
[root@master01 ~]# kubectl create -f components.yaml
查看metrics-server运行状态
[root@master01 ~]# kubectl get pod -n kube-system
通过top命令查看节点监控数据:kubectl top node/pod
[root@master01 ~]# kubectl top node
[root@master01 ~]# kubectl top pod -n kube-system
到此为止metrics-server安装成功。
HPA 应用案例
案例:创建HPA,通过HPA对Pod数量进行弹性自动伸缩
# 获取HPA支持的配置属性
kubectl explain hpa.spec
[root@master01 ~]# vim hpa-deploy_nginx.yml
apiVersion: autoscaling/v1 #自动扩缩容版本
kind: HorizontalPodAutoscaler
metadata:
name: hpa-nginx
namespace: test
spec:
minReplicas: 1 #最小的pod数量
maxReplicas: 10 #最大的pod数量
targetCPUUtilizationPercentage: 1 #cpu使用指标,表示10%(生产环境建议定义在6-8)
scaleTargetRef: #指定要控制的deploy信息
apiVersion: apps/v1 #deploy版本
kind: Deployment #deploy类型
name: deploy-nginx #deploy名称
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector: #标签选择器(基于选择器匹配Pod)
matchLabels:
app: deploy-nginx #标签
template: #创建Pod模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
# 容忍
tolerations:
- key: worker01
effect: NoSchedule
containers:
- name: nginx
image: nginx:1.18.0
imagePullPolicy: IfNotPresent
ports:
- containerPort: 80
resources: #资源限额
limits: #资源上限
cpu: 100m #cpu 1核心的10%的资源(生产环境建议600m-800m)
创建HPA类型的deploy
[root@master01 ~]# # kubectl create -f hpadeploy_nginx.yml
查看信息
[root@master01 ~]# kubectl get deploy,pod,hpa -n test
HPA 弹性伸缩
另开终端动态查看pod信息
[root@master01 ~]# kubectl get po -n test -w
查看Pod地址
# kubectl get pod -n test -o wide
通过压力测试工具访问Pod地址进行压力测试
[root@master01 ~]# yum -y install httpd-tools
[root@master01 ~]# ab -n 300000 -c 100 http://10.244.30.104/index.html
-n 请求的总数量
-c 并发数量
17、控制器 Statefulset
什么是statefulset?
StatefulSet 是用来管理有状态的应用,例如数据库。
前面我们部署的应用,都是不需要存储数据,不需要记住状态的,可以随意扩充副本,每个副本都是一样的,可替代的。
而像数据库、Redis 这类有状态的,则不能随意扩充副本。
StatefulSet 会固定每个 Pod 的名字
特性
- 仅当前一个 pod 启动,才会运行后一个(redis-statefulset-0 处于 running 状态,才会运行 redis-statefulset-1)
- Pod 的名称的形式为
<statefulset name>-<ordinal index>
- Service 的 CLUSTER-IP 是
none
,Pod 名字也是固定的。 - Pod 创建和
销毁是有序的
,创建是顺序的
,销毁是逆序的
。(可以使用 kubectl scale 或者kubectl patch 来扩容/缩容测试,或者去修改 yaml 文件 replicas 参数) - 删除 pod,pod 会自动重建,重建不会改变Pod 的序号、主机名、SRV 条目和记录名称,IP会改变(每次删除 pod,删除 pod 的 IP 已被占用,新建时 IP 会追加),不要使用 IP 连接
部署 StatefulSet 类型的 redis
- 创建 StatefulSet 以及 service
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-statefulset
spec:
serviceName: redis-service
replicas: 3
selector:
matchLabels:
app: redis-statefulset
template:
metadata:
labels:
app: redis-statefulset
spec:
containers:
- name: redis
image: redis:latest
# IfNotPresent 仅本地没有镜像时才远程拉,Always 永远都是从远程拉,Never 永远只用本地镜像,本地没有则报错
imagePullPolicy: IfNotPresent
---
apiVersion: v1
kind: Service
metadata:
name: redis-service
spec:
selector:
app: redis-statefulset
type: ClusterIP
# HeadLess
clusterIP: None
ports:
- port: 6379
targetPort: 6379
使用 kubectl apply -f redis-StatefulSet.yaml
创建 statefulset (redis-statefulset-0、redis-statefulset-1、redis-statefulset-2) 以及 service (redis-service )
七、Service 四层负载均衡
本章主要学习k8s的流量负载组件:Service与ingress,Service用于4层流量的负载,ingress用于7层流量负载。
Service介绍
在kubernetes中,pod是应用程序的载体,我们可以通过pod的ip来访问应用程序,但是pod的ip地址不是固定
的,这也就意味着不方便直接采用pod的ip对服务进行访问。
为了解决这个问题,kubernetes提供了Service资源,Service会对提供同一个服务的多个pod进行聚合
,并且提供一个统一的入口地址。通过访问Service的入口地址就能访问到后面的pod服务。
- service是实现负载均衡功能,主要实现
四层负载
,通过ip + 端口
进行转发的
Kube-proxy介绍
Service的本质就是一条代理规则,真正实现代理功能的其实是kube-proxy
代理组件,集群的每个节点都运行着一个kube-proxy服务进程,当创建Service的时候,就等于创建了一条代理规则,而这条规则就是告诉kube-proxy 通过那种模式进行流量转发。
kube-proxy代理模式介绍
kube-proxy三种代理模式:UserSpace模式、iptables模式、ipvs模式
Userpace模式介绍
userspace模式下,用户的请求会发向Cluster IP被Iptables规则重定向到kube-proxy监听的端口上。
该模式下,kube-proxy充当了一个四层负责均衡器
的角色。由于kube-proxy运行在userspace中,在转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低
。
iptables模式介绍
iptables模式下,kube-proxy为service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP。
该模式下kube-proxy不承担四层负责均衡器
的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试。
ipvs模式介绍
ipvs(Virtual Server)与iptables类似,都是基于 netfilter 进行流量转发,kube-proxy也不承担
四层负载均衡器的角色,只创建ipvs规则
,ipvs在转发时支持的负载均衡算法非常的灵活,例如:轮询、加权轮询、基于端口转发、最小负载、最少连接等,ipvs 支持服务器健康检查
和连接重试
等功能。
ipvs 模式的实现原理图示如下:
想要使用ipvs转发模式需要安装ipvs内核模块,否则会降级为iptables模式
查看ipvs模块(本实验环境在第二章集群环境部署时已经安装)
[root@master01 ~]# lsmod | grep -e ip_vs -e
nf_conntrack_ipv4
nf_conntrack_ipv4
nf_defrag_ipv4
ip_vs_sh
ip_vs_wrr
ip_vs_rr
ip_vs
nf_conntrack
libcrc32c
开启ipvs,编辑kube-proxy文件
[root@master01 ~]# kubectl edit cm kube-proxy -n kube-system
mode: "ipvs" #修改为ipvs模式
#提示:cm(ConfigMap)在K8s中属于配置资源
删除当前正在使用的kube-proxy的pod(按照标签删除)
[root@master01 ~]# kubectl get pod --show-labels -n kube-system | grep kube-proxy*
[root@master01 ~]# kubectl delete pod -l k8s-app=kube-proxy -n kube-system
查看kube-proxy的pod是否被重建
[root@master ~]# kubectl get po -n kube-system
查看ipvs功能是否开启
[root@master01 ~]# ipvsadm -Ln
Service常用访问方式介绍
ClusterIP
:默认访问方式,分配一个集群内部
可以访问的虚拟IP(该方式只能用于集群内部访问,外部无法访问)NodePort
:在每个Node上分配一个端口作为外部
访问入口,端口范围为30000-32767
(该访问适用于外部访问)LoadBalancer
:通过在集群外部的公有云平台上,例如:阿里云、华为云、AWS等做一个负载均衡,通过外部负载均衡将流量转发到集群中。
访问过程:用户----->域名----->云服务提供商提供LB负载均衡设备-----> NodeIP:Port(service IP)----->Pod IP:端口
Service资源清单文件介绍
kubectl explain svc 查看service资源支持的属性
apiVersion: v1 #版本
kind: Service #资源类型
metadata: #元数据
name: #资源名称
namespace: #所属名称空间
spec: #描述
selector: #标签选择器,用于确定当前service代理哪些pod
app: #标签
type: Service #类型
clusterIP: clusterIP #访问方式
ports: 端口信息
- protocol: TCP #端口协议
port: #访问Service使用的端口
targetPort: # pod中容器的端口
nodePort: node端口 #(Service需要暴露给外部访问的节点端口,端口范围:30000-32767)
Cluster IP 应用案例
案例:通过HPA创建3个Pod并设置标签为app=deploy-nginx(使用前面的hpa-deploy_nginx.yml创建即可)通过Service ClusterIP访问方式进行代理
ClusterIP:默认访问方式,由k8s自动分配的虚拟IP,只能在集群内部访问
[root@master01 ~]# vim hpa-deploy_nginx.yml
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
type: ClusterIP #service默认访问方式
ports: #定义端口信息
- port: 80 #访问service使用的端口,可自定义
targetPort: 80 #pod中容器端口
selector: #标签选择器(基于标签选择代理的Pod)
app: deploy-nginx #标签(需要下方代理的Pod标签一致)
---
apiVersion: autoscaling/v1 #自动扩缩容版本
kind: HorizontalPodAutoscaler #类型
metadata:
name: hpa-nginx
namespace: test
spec:
minReplicas: 3 #最小的pod数量
maxReplicas: 10 #最大的pod数量
targetCPUUtilizationPercentage: 6 #cpu使用指标,表示60%
scaleTargetRef: #指定要控制的Pod控制器(deploy)信息
apiVersion: apps/v1
kind: Deployment #deploy控制器类型
name: deploy-nginx #deploy控制器名称(要在k8s中存在)
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 3 #创建pod的副本数量,默认为1
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
containers:
- name: nginx
image: nginx:1.17.0 #指定镜像
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
resources: #资源限额
limits: #资源上限
cpu: 600m # cpu 1核心的60%的资源
创建Pod
[root@master01 ~]# kubectl create -f hpa-deploy_nginx.yml
查看pod详细信息
[root@master01 ~]# kubectl get pod -n test
[root@master01 ~]# kubectl get pod -n test -o wide
为了更好的验证用户请求被分配到哪个pod中,修改3个Pod中nginx默认首页为具体的Pod IP地址
[root@master01 ~]# kubectl exec -it deployment-6696798b78-6zl8p -n test /bin/bash
[root@master01 ~]# echo "10.244.1.44" > /usr/share/nginx/html/index.html
访问3个pod测试
[root@master01 ~]# curl http://10.244.1.44
10.244.1.44
[root@master01 ~]# curl http://10.244.2.24
10.244.2.24
[root@master01 ~]# curl http://10.244.1.45
10.244.1.45
查看svc信息
[root@master01 ~]# kubectl get svc -n test
[root@master ~]# kubectl describe svc -n test
Name: service
Namespace: dev
Labels: <none>
Annotations: <none>
Selector: app=nginx-pod
Type: ClusterIP
IP: 10.96.96.96
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints:
10.244.1.44:80,10.244.1.45:80,10.244.2.24:80 代理的pod地址
Session Affinity: None
Events: <none>
查看ipvs的映射规则
[root@master01 ~]# ipvsadm -Ln
...
TCP 10.96.96.96:80 rr
-> 10.244.1.44:80 Masq 1 0 0
-> 10.244.1.45:80 Masq 1 0 0
-> 10.244.2.24:80 Masq 1 0 0
#rr为默认轮询策略:当service接收到请求后,会通过轮询算法将请求分配到对应的三个pod中
访问测试
[root@master01 ~]# while :
do
curl 10.96.96.96
sleep 5
done
10.244.2.24
10.244.1.45
10.244.1.44
10.244.2.24
10.244.1.45
10.244.1.44
删除Pod
[root@master ~]# kubectl delete -f hpa-deploy_nginx.yml
总结:Cluster IP的访问方式只能用在集群内部主机之间访问,集群外部主机没办法访问。
NodePort 应用案例
在生产环境中,Service是需要暴露给外部访问
的,那么就要用到NodePort类型的Service,NodePort的工作原理其实就是在Node节点上暴露一个端口,然后外部主机就可以通过 节点IP + 暴露端口
来访问集群中的pod了
案例:将前边案例中的清单文件Cluster IP改为NodePort
类型,实现外部访问。
[root@master01 ~]# vim hpa-deploy_nginx.yml
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
type: NodePort service类型
ports: #定义端口信息
- port: 80 #访问service使用的内部端口,可自定义
targetPort: 80 #指定pod中容器端口
nodePort: 30007 外部访问service的端口
selector: #标签选择器(基于标签选择代理的Pod)
app: deploy-nginx #标签(需要与代理的Pod标签一致)
---
apiVersion: autoscaling/v1 #自动扩缩容版本
kind: HorizontalPodAutoscaler #类型
metadata:
name: hpa-nginx
namespace: test
spec:
minReplicas: 3 #最小的pod数量
maxReplicas: 10 #最大的pod数量
targetCPUUtilizationPercentage: 6 #cpu使用指标,表示60%
scaleTargetRef: #指定要控制的nginx信息
apiVersion: apps/v1
kind: Deployment #deploy类型
name: deploy-nginx #deploy名称
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
replicas: 3
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
containers:
- name: nginx
image: nginx:1.17.0 #指定镜像
ports: #定义端口
- containerPort: 80 #端口
protocol: TCP #端口协议
resources: #资源限额
limits: #资源上限
cpu: 600m #cpu 1核心的60%的资源
创建Pod
[root@master01 ~]# kubectl create -f hpa-deploy_nginx.yml
查看Pod信息
[root@master01 ~]# kubectl get pod -n test
查看Service信息
[root@master01 ~]# kubectl get svc -n test
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
svc-nginx NodePort 10.110.174.205 <none> 80:32388/TCP 13s
提示:
80 #Service端口
32388 #Service绑定的Node节点端口,这个端口是提供外部访问的
外部主机访问测试(通过宿主机主机IP加Node端口,访问集群中任何一台节点IP都可以)
http://192.168.0.13:32388
删除Pod
[root@master01 ~]# kubectl delete -f hpa-deploy_nginx.yml
八、Ingress 七层负载
1、Ingress 控制器介绍
Ingress 是 Kubernetes 集群中的一种资源对象,用于管理对集群内部的服务的访问。它充当着入口的角色,通过定义规则来将外部的请求路由到集群内部的服务。
Ingress控制器种类有很多(以nginx为例)的工作原理: |
---|
![]() |
-
用户编写Ingress规则,说明哪个域名对应集群中的Service
-
Ingress控制器动态感知Ingress服务规则的变化,然后生成一段对应的Nginx反向代理配置进行流量转发
2、Nginx Ingress 环境搭建
- ingress-nginx资源清单文件下载地址:https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/baremetal/deploy.yaml
提示:由于网络原因可能无法下载,直接使用我给大家下载好的文件即可,文件名:deploy.yml
创建ingress-nginx
[root@master01 ingress-nginx]# kubectl create -f deploy.yaml
查看ns(会有一个ingress-nginx的命名空间)
[root@master01 ingress-nginx]# kubectl get ns
...
ingress-nginx Active 2m27s
查看详细信息时会出现如下报错:
# kubectl describe pod -n ingress-nginx
MountVolume.SetUp failed for volume "webhook-cert" :secret "ingress-nginx-admission-token-bnrl4" not found
#这个报错原因是当前运行的ingress-nginx-admission-token名称与deploys.yml文件中的名称不一致,需要对文件进行修改
解决方法:查看ingress的secret
[root@master01 ingress-nginx]# kubectl get secret -A | grep ingress
![]() |
---|
复制这个名称到deploy.yml文件替换默认的名称 |
[root@master01 ingress-nginx]# vim deploy.yaml
![]() |
---|
修改后再次更新资源文件 |
[root@master01 ingress-nginx]# kubectl apply -f deploy.yaml
查看ingress-nginx空间的pod(有两个Pod用于执行一次性任务,状态为
Completed(完成),这种Pod执行后即退出。)
[root@master01 ingress-nginx]# kubectl get po -n ingressnginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-admission-create-cctfh 0/1 Completed 0 28m
ingress-nginx-admission-patch-rhzrk 0/1 Completed 1 28m
ingress-nginx-controller-9d98d6467-2wtnj 1/1 Running 0 23m
查看service
[root@master01 ingress-nginx]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE ingress-nginx-controller NodePort
10.99.98.180 <none> 80:31770/TCP,443:31082/TCP17m
ingress-nginx-controller-admission ClusterIP
10.97.42.155 <none> 443/TCP 17m
#提示:到此为止ingress-nginx的控制器已经安装完毕
3、Nginx Ingress HTTP 应用案例
案例:通过Deployment部署tomcat与nginx的pod,并通过Nginx Ingress进行HTTP访问
[root@master01 ~]# vim ingress-http.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
selector:
app: deploy-nginx
clusterIP: None
type: ClusterIP #service类型
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-tomcat
namespace: test
spec:
selector:
matchLabels:
app: deploy-tomcat
template:
metadata:
labels:
app: deploy-tomcat
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc-tomcat
namespace: test
spec:
selector:
app: deploy-tomcat
clusterIP: None
type: ClusterIP #service类型
ports:
- port: 8080
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-http #自定义ingress名称
namespace: test
annotations:
ingressclass.kubernetes.io/is-default-class: "true" # 指定spec下方的rules的path可以使用正则表达式,如果我们没有使用正则表达式,此项则可不使用
kubernetes.io/ingress.class: nginx #指定控制器的类别为nginx
spec:
rules: #定义主机列表
- host: www.nginx.com #自定义域名
http:
paths:
- pathType: Prefix #路径类型
path: "/" #定义站点路径
backend: #定义后端引用的服务
service: #关联service
name: svc-nginx #对应上面创建的service名称
port:
number: 80 #service端口
- host: www.tomcat.com #自定义域名
http:
paths:
- pathType: Prefix #路径类型
path: "/" #定义站点路径
backend: #定义后端引用的服务
service: #关联service
name: svc-tomcat #对应上面创建的service名称
port:
number: 8080 #service端口
#### 创建ingress报错
[root@master01 data]# kubectl apply -f ingress-nginx.yml
Error from server (InternalError): error when creating"ingress-nginx.yml": Internal error occurred: failed calling webhook "validate.nginx.ingress.kubernetes.io":failed to call webhook: Post "https://ingress-nginx-controller-admission.ingress-nginx.svc:443/networking/v1/ingresses?timeout=10s": remote error: tls: internal error
查看ingress-nginx-admission
# kubectl get validatingwebhookconfigurations
删除ingress-nginx-admission
# kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission
重新创建ingress
# kubectl apply -f ingress-nginx.yml
查看信息
[root@master01 ~]# kubectl get all -n test
查看ingress信息
[root@master01 ~]# kubectl get ing -n test
查看详细信息
[root@master01 ~]# kubectl describe ing -n test
测试Ingress HTTP代理
浏览器访问由于域名无法正常解析,需要在windows内进行解析:C:\Windows\System32\drivers\etc
192.168.0.13 www.nginx.com www.tomcat.com
访问测试:在访问测试时,通过域名与ingress对外暴露的端口进行访问
查看ingress-nginx暴露的端口
[root@master01 ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.97.31.167 <none> 80:30256/TCP,443:31232/TCP 21
访问测试:注意80对应的是HTTP端口,443对应的是HTTPS端口
http://www.nginx.com:30256
http://www.tomcat.com:31232
删除ingress http
[root@master01 ~]# kubectl delete -f ingress-http.yml
4、Nginx Ingress HTTPS 应用案例
案例:通过Deployment部署tomcat与nginx的pod,并通过Nginx Ingress进行HTTPS访问
生成证书
[root@master01 ~]# openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=TJ/O=nginx/CN=thinkmo.com"
创建密钥
[root@master01 ~]# kubectl create secret tls tls-secret --key tls.key --cert tls.crt
创建ingress https
[root@master ~]# vim ingress-https.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
selector:
app: deploy-nginx
clusterIP: None #不分配集群IP(用不着)
type: ClusterIP #service类型
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-tomcat
namespace: test
spec:
selector:
matchLabels:
app: deploy-tomcat
template:
metadata:
labels:
app: deploy-tomcat
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
---
apiVersion: v1
kind: Service
metadata:
name: svc-tomcat
namespace: test
spec:
selector:
app: deploy-tomcat
clusterIP: None #不分配集群IP(用不着)
type: ClusterIP #service类型
ports:
- port: 8080
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-https #自定义ingress名称
namespace: test
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
kubernetes.io/ingress.class: nginx #注释名称需为nginx(不可省略)
spec:
tls:
- hosts:
- www.nginx.com #指定域名使用的证书
- www.tomcat.com #指定域名使用的证书
secretName: tls-secret #指定secret名称(与前边创建的保持一致)
rules: #定义主机列表
- host: www.nginx.com #自定义域名
http:
paths:
- pathType: Prefix #路径类型
path: "/" #定义站点路径
backend: #定义后端引用的服务
service: #关联service
name: svc-nginx #对应上面创建的service名称
port:
number: 80 #service端口
- host: www.tomcat.com #自定义域名
http:
paths:
- pathType: Prefix #路径类型
path: "/" #定义站点路径
backend: #定义后端引用的服务
service: #关联service
name: svc-tomcat #对应上面创建的service名称
port:
number: 8080 #service端口
创建Pod
[root@master01 ~]# kubectl create -f ingress-https.yml
ingress.extensions/ingress-https created
查看ingress信息
[root@master01 ~]# kubectl get ing ingress-https -n test
NAME CLASS HOSTS ADDRESS PORTS AGE
ingress-https <none> www.nginx.com,www.tomcat.com 192.168.0.13 80, 443 5m37s
查看详细信息
[root@master ~]# kubectl describe ing ingress-https -n dev
...
tls-secret terminates www.nginx.com,www.tomcat.com #密钥
...
查看ingress-nginx端口
[root@master01 ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.97.31.167 <none> 80:30256/TCP,443:31232/TCP 22h
九、ConfigMap、Secret配置与密钥管理
1、ConfigMap介绍
我们在部署应用的时候, 许多应用程序会从配置文、命令行参数或环境变量中读取所需的配置信息,这些配置信息需要与docker image解耦
。
如果这些应用数量较多且分散在集群的不同节点上,而我们又需要对这些节点的服务配置进行批量修改
,如果每个服务都单独修改的话,那么更新配置就很麻烦, 所以k8s中引入了Configmap
资源对象, 提供了向容器中注入配置信息的机制,主要作用就是为了让服务和配置文件解耦
,以便实现配置的统一管理。
Configmap是k8s中的资源,以键值对
的形式保存一些非机密性数据, 相当于配置文件。
使用 ConfigMap 的限制条件:
-
ConfigMap 需要在 Pod
启动前创建
出来; -
并且只有当 ConfigMap 和 Pod 处于
同一命名空间
时,才可以被 Pod引用; -
当 Pod 挂载 ConfigMap 绑定的目录时,目录下的目录并不会挂载到Pod 内,只有目录下的文件会被挂载。
-
ConfigMap在设计上
不是用来保存大量数据
的,在ConfigMap中保存的数据不可超过1 MiB
,如果你需要保存超出此限制的数据,可以考虑挂载存储卷
。
2、ConfigMap创建与使用
通过文件创建confifigmap, 格式: --from-file=文件名
一般比较常见的使用方式便是使用文件创建,因为在有些时候可能需要对配置进行批量的更新。这样会更加的方便。
# cat /opt/nginx.conf
server {
listen 8000;
server_name _;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
# kubectl create cm cm-ngx-config --from-file=/opt/nginx.conf -n test
# kubectl get cm -n test
# kubectl describe cm cm-ngx-config -n test
创建nginx的Pod,并使用Volume将ConfifigMap挂载到nginx容器中
# cat cm-nginx-config.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-nginx
namespace: test
spec:
replicas: 1
selector:
matchLabels:
app: web-nginx
template:
metadata:
labels:
app: web-nginx
spec:
volumes:
- name: nginx-config # 卷的名称(自定义)
configMap: # 卷类型是configMap
name: cm-ngx-config # configmap的名称
containers:
- name: web-nginx
image: nginx:1.18-alpine
ports:
- containerPort: 80
volumeMounts: # 定义在容器中挂载的信息
- name: nginx-config # 挂载的名称(上边定义的卷的名称)
mountPath: /etc/nginx/conf.d # 挂载到容器的路径
# 创建pod
kubectl create -f cm-nginx-configmap.yml
# kubectl get pod -n test
NAME READY STATUS RESTARTS AGE
web-nginx-5684fbc5dc-js6jg 1/1 Running 0 23s
提示:将目录A挂载到目录B,则目录B原有的文件都会被目录A下的文件覆盖。如果你想挂载的目录下有其他文件,默认会覆盖目录下其他文件,可以通过subPath属性挂载
...
spec:
containers:
- name: web-nginx
image: nginx:1.18-alpine
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/nginx.conf
subPath: nginx.conf # key的名称(文件名)
volumes:
- name: nginx-config
configMap:
name: cm-ngx-config # configmap的名称
subPath的使用方法一共有两种:
1. 同一个pod中多容器挂载同一个卷时提供隔离
2. 将configMap和secret作为文件挂载到容器中而不覆盖挂载目录下的文件(默认会覆盖)
提示:以subPath方式挂载时,configmap更新,容器不会更新,解决方式,可以把文件挂载到一个空目录,然后使用ln
链接过去
3、ConfigMap热更新
通过Volume挂载到容器内部时,当该configmap的值发生变化时,容器内部具备自动更新的能力,但是通过环境变量设置到容器内部该值不具备自动更新的能力。
更新cm-ngx-config的cm内容为80
apiVersion: v1
data:
nginx.conf: |
server {
listen 80; # 改为80
server_name _;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
kind: ConfigMap
metadata:
creationTimestamp: "2022-11-03T06:41:40Z"
name: cm-ngx-config
namespace: test
resourceVersion: "20386"
uid: 6024477f-3d12-4780-8b52-39929be45bbc
访问是80端口发现失败,但从pod中查询nginx的配置文件,发现已经更改为80,说明configmap热更新已生效,这时就需要手动触发
滚动更新才能再次加载nginx的配置文件。
# kubectl patch deployment web-nginx -n test --patch'{"spec": {"template": {"metadata": {"annotations":{"version/config": "20221103" }}}}}'
这个命令是在 .spec.template.metadata.annotations
中添加version/config标签,每次通过修改 version/config 来触发滚动更新。
...
template:
metadata:
annotations:
version/config: "20221103"
...
4、Secret介绍
Secret与ConfifigMap类似,主要的区别是ConfigMap存储的是明文,而Secret存储的是密文。
可以用于存储和管理一些敏感数据,比如密码,token,密钥,证书等敏感信息。
它把 Pod 想要访问的加密数据存放到 Etcd 中,然后用户就可以通过在Pod 的容器里挂载 Volume 的方式或者环境变量的方式访问到这些 Secret里保存的信息了。
Secret有4种类型:
-
Opaque: base64编码格式的Secret,用来存储密码、密钥,类型标识符为generic
-
Service Account: 用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod
/var/run/secrets/kubernetes.io/serviceaccount目录中
-
kubernetes.io/dockerconfifigjson: 用来存储私有docker registry的认证信息,类型标识为docker-registry。
-
kubernetes.io/tls: 用于为SSL通信模式存储证书和私钥文件,命令式创建类型标识为tls。
# kubectl create secret -h
Create a secret using specified subcommand.
Available Commands:
docker-registry Create a secret for use with a Docker registry
generic Create a secret from a local file,directory or literal value tls Create a TLS secret
5、Secret应用案例
使用Opaque类型来存储mysql的root密码。
Opaque类型需要将明文密码进行base64编码
# echo -n 123456 | base64
MTIzNDU2
明文密码为123456,得到的编码为MTIzNDU2
提示: echo -n 不换行输出,如果不使用-n,就会把换行符也作为了字符当做密码使用,所以一直报错进不去mysql
编写创建secret的YAML文件
# cat secret-mysql.yml
apiVersion: v1
kind: Secret
metadata:
name: secret-mysql
namespace: test
data:
password: MTIzNDU2
创建secret
kubectl apply -f secret-mysql.yml
kubectl get secret -test |grep secret-mysql
6、Secret使用方式
创建MySQL的Pod,并通过环境变量的方式传递给pod使用。
# cat pod-mysql-secret.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-mysql
namespace: test
spec:
selector:
matchLabels:
app: deploy-mysql
template:
metadata:
labels:
app: deploy-mysql
spec:
containers:
- name: mysql
image: mysql:5.7
ports:
- containerPort: 80
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: secret-mysql # 对应创建的secret名字
key: password
验证传入pod的变量效果
root@pod-mysql-secret1:/# env | grep MYSQL_ROOT_PASSWORD MYSQL_ROOT_PASSWORD=123
root@pod-mysql-secret1:/# mysql -p123456
7、Secret热更新
# echo -n 123456789 |base64
MTIzNDU2Nzg5
# kubectl edit secret secret-mysql -n test
apiVersion: v1
data:
password: MTIzNDU2Nzg5 # 修改密码
kind: Secret
metadata:
补充:通过环境变量的方式使用的secret或者confifigmap都无法热更新。
这时就需要手动触发滚动更新
# kubectl patch deployment deploy-mysql -n test --patch '{"spec": {"template": {"metadata": {"annotations":{"version/config": "20221103" }}}}}'
十、容器镜像仓库Harbor
Docker官方提供了Registry镜像仓库,但是Registry的功能相对简陋。Harbor是VMware公司提供的一款镜像仓库,提供了权限控制、分布式发布、强大的安全扫描与审查机制等功能
Harbor 私有仓库,它以 Registry 为基础,提供了对用户友好的管理界面,可以帮助我们快速搭建一个企业级的 Docker Registry 服务。
Harbor 的每个组件都是以 Docker 容器的形式构建的,使用 DockerCompose 进行部署。
Harbor优势
- 基于角色控制:有管理员与普通用户,可赋权普通⽤户,比如只能上传和下载,可根据项⽬来进⾏操作和管理。
- 基于镜像的复制策略:也与权限相关,比如有只⼀些⽤户与组才能对此项⽬进⾏相对应的操作。
- ⽀持LDAP/AD域控制:比如南京去下载北京harbor私有仓库的镜像,两端打上局域网的地址,连接在一块,数据信息的传输通过⼀条隧道,会通过两层加密,第⼀层为隧道加密,第⼆层为数据加密,安全可靠。
- 图像删除和垃圾收集:即回收站机制。
- 图形UI:具有统计功能,比如访问量与镜像下载热度。
1、docker安装
1.1、安装docker
使用阿里云开源软件镜像站。
# wget https://mirrors.aliyun.com/dockerce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/dockerce.repo
安装docker-ce
# yum -y install docker-ce && systemctl enable --nowdocker
1.2、docker compose
harbor通过docker-compose进行管理,需要提前下载docker-compose二进制文件
# wget https://github.com/docker/compose/releases/download/1.25.0/docker-compose-Linux-x86_64
移动二进制文件到/usr/bin目录,并更名为docker-compose
# mv docker-compose-Linux-x86_64 /usr/bin/docker-compose
为二进制文件添加可执行权限
# chmod +x /usr/bin/docker-compose
安装完成后,查看docker-compse版本
# docker-compose version
**1.3、安装 **harbor仓库
![]() |
---|
![]() |
![]() |
![]() |
下载harbor离线安装包
# wget https://github.com/goharbor/harbor/releases/download/v2.5.1/harbor-offline-installer-v2.5.1.tgz
解压harbor
# tar xf harbor-offline-installer-v2.5.1.tgz -C /
创建配置文件
# cd harbor/
# mv harbor.yml.tmpl harbor.yml
导入Harbor所需的镜像文件
#docker load -i harbor.v2.5.1.tar.gz
修改配置文件中的如下内容
# vim harbor.yml
#上述内容省略...
hostname: 192.168.0.17 指定Harbor主机地址
http: #访问方式为http(不用修改)
port: 80 #默认端口(不用修改)
https: 注释https访问方式(需要证书才可以使用)
port: 443 注释端口
certificate: /root/harbor/6864844_kubemsb.com.pem #注释证书文件
private_key: /root/harbor/6864844_kubemsb.com.key #注释证书密钥文件
harbor_admin_password: 12345 访问密码
执行预备脚本(下载harbor所需的镜像文件)
# ./prepare
执行安装脚本
# ./install.sh
...
✔ ----Harbor has been installed and started
successfully.----
验证运行情况
# docker ps
默认的用户名:admin,密码是前边配置文件中设置的密码:12345 |
---|
![]() |
![]() |
![]() |
提示:以下操作需要在集群所有节点指定harbor仓库地址
harbor主机修改后直接将修改后的文件拷贝到所有节点即可
[root@harbor harbor]# vim /etc/docker/daemon.json
{
"insecure-registries": ["http://192.168.0.25"]
}
停止harbor后重启docker
[root@harbor harbor]# docker-compose down
[root@harbor harbor]# systemctl daemon-reload && systemctl restart docker
启动harbor
[root@harbor harbor]# docker-compose up -d
k8s集群所有节点也都需要指定Harbor仓库的地址
[root@harbor harbor]# for i in master02 master03 worker01
do
scp /etc/docker/daemon.json $i:/etc/docker/
done
所有节点重启docker
[root@harbor harbor]# for i in master02 master03 worker01
do
ssh $i systemctl daemon-reload && systemctl restart docker
done
1.4、镜像上传到Harbor仓库
通过命令行登录到harbor仓库才能上传镜像
[root@harbor harbor]# docker login 192.168.0.25
Username: admin --Harbor的web界面的用户名称
Password:12345 --密码
修改镜像标签
[root@harbor harbor]# docker tag choujiang:v1 192.168.0.25/image/choujiang:v1
上传镜像
[root@harbor harbor]# docker push choujiang:v1/image/choujiang:v1
2、K8s集群使用Harbor仓库
2.1、通过密钥的方式创建
创建一个给 Docker registry 使用的 secret
k8s中的secrets用于存储和管理一些敏感数据,比如密码,token,密钥等敏感信息。它把 Pod 想要访问的加密数据存放到 Etcd 中。然后用户就可以通过在 Pod 中表明具体的方式访问到这些 Secret 里保存的信息了。
[root@master01 ~]# kubectl create secret docker-registry docker-harbor --docker-server=192.168.0.25 --docker-username=admin --docker-password=12345 --namespace=test -o yaml > docker-harbor.yaml
说明:
kubectl create secret #创建命令
docker-registry #资源类型
docker-harbor #名称(自定义)
--docker-server #指定harbor仓库的IP
--docker-username #指定harbor仓库的登录用户名
--docker-password #指定harbor仓库的登录密码
--namespace=test #指定命名空间
提示:这个secret
只针对于test命名空间
生效,其他命名空间的Pod无法使用该secret拉取镜像,如果其他命名空间需要使用secret,需在指定的空间再次创建。
# 验证
[root@master01 ~]# kubectl get secret -n test | grep docker-harbor
[root@master01 ~]# kubectl describe secret docker-harbor -n test
2.2、创建pod并使用secret
[root@master01 ~]# vim nginx.yml
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
type: NodePort #service类型
ports: #定义端口信息
- port: 80 #访问service使用的端口,可自定义
targetPort: 80 #指定pod中容器端口
nodePort: 30007 #自定义service端口
selector: #标签选择器(基于标签选择代理的Pod)
app: deploy-nginx #标签(需要与代理的Pod标签一致)
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
imagePullSecrets: #定义镜像下载使用的secrets
- name: docker-harbor #secret名称,与上边创建名称保持一致
containers:
- name: nginx
image: 192.168.0.25/image/choujiang_ngx:v1 #指定镜像
创建Pod
[root@master01 ~]# kubectl create -f nginx.yml
2.3、验证pod镜像下载地址
[root@master01 ~]# kubectl describe pod -n test
Harbor使用注意事项
当docker重启后,harbor服务需要先关闭在进行重启
才可以正常使用
关闭harbor方式(进入到harbor路径)
[root@harbor harbor]# docker-compose down
启动harbor
[root@harbor harbor]# docker-compose up -d
查看harbor容器,总共有九个容器
[root@harbor harbor]# docker ps
harbor-jobservice
nginx
harbor-core
registryctl
registry
harbor-portal
harbor-db
redis
harbor-log
十一、基于K8S的DevOps
![]() |
---|
1、GitLab 环境准备
安装gitlab
yum -y install gitlab-ce-14.1.0-ce.0.el7.x86_64.rpm
配置GitLab URL
# vim /etc/gitlab/gitlab.rb
...
external_url 'http://192.168.0.15'
修改完主配置文件后,使用gitlab-ctl reconfifigure重新配置gitlab使配置生效
# gitlab-ctl reconfigure
提示:gitlab-ctl reconfifigure一般修改完主配置文件/etc/gitlab/gitlab.rb,需要执行此命令
查看GitLab服务状态
# gitlab-ctl status
root初始密码所在文件:/etc/gitlab/initial_root_password |
---|
![]() |
通过个人资料中心修改root密码 |
![]() |
密码不验证复杂度,长度需满足8位 |
![]() |
修改页面语言 |
![]() |
1.1、关闭注册功能
由于我们Gitlab系统是私有仓库,一般用户都是由管理员创建和分派的,所以我们需要关闭注册。
点击左上角 菜单(Menu)→管理(Admin)→通用,找到注册限制取消掉注册功能。 |
---|
![]() |
将勾选取消,点击 保存更改(Save changes) |
1.2、新建群组
群组就是把相关的项目和用户放在一起,进行统一的权限管理。 |
---|
![]() |
在可见性级别处我们选择私有,这样只有经过授权的用户才可以看到该组内的项目,其他用户无法查看。
1.3、创建仓库
![]() |
---|
1.4、安装Docker
通过docker构建镜像,将构建好的镜像推送到GitLab仓库。
# wget https://mirrors.aliyun.com/dockerce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
# yum -y install docker-ce && systemctl enable docker --now
下载nginx镜像用于构建dockerfile
# docker pull nginx:1.18.0
1.5、安装Git推送项目
# yum -y install git
将代码推送到choujiang项目中
初始化目录为Git版本库
# git init
# git config --global user.email "yesir@163.com"
# git config --global user.name "yesir"
解压项目
# unzip dzp.zip
# rm -rf dzp.zip
# mv luckdraw-master/* .
构建dockerfile
# vim dockerfile
FROM nginx:1.18.0
ADD . /usr/share/nginx/html
解释:将当前项目文件添加到nginx网页目录
构建镜像
# docker build -t choujiang:v1 .
创建容器
# docker run -id --name choujiang -p 88:80 choujiang:v1
访问测试:http://192.168.0.15:88/
上传项目到仓库
# git add .
# git commit -m 'add choujiang'
添加远程仓库
# git remote add origin git@192.168.0.110:test/choujiang.git
推送项目到远程仓库
# git push -u origin master
2、Jenkins环境部署
清华大学下载Jenkins稳定版地址:https://mirrors.tuna.tsinghua.edu.cn/jenkins/redhat-stable/
Jenkins需要依赖Java环境,需要提前安装jdk环境(fontconfifig软件提供Jenkins web界面字体)
# yum install fontconfig java-11-openjdk -y
安装Jenkins
# yum localinstall jenkins-2.332.3-1.1.noarch.rpm
2.1、相关参数
程序名:jenkins
默认端口:8080(可修改)
/etc/sysconfig/jenkins #配置文件,主要配置Jenkins的工作目录、启动用户、启动端口
/var/lib/jenkins #默认的Jenkins家目录(存放项目及插件)
/var/log/jenkins #日志文件目录
启动Jenkins
# systemctl start jenkins
# systemctl enable jenkins
查看Jenkins端口信息
# netstat -ntlp
tcp6 0 0 :::8080 :::* LISTEN 7703/java
查看本版本信息
jenkins --version
提示:如果使用较新版本在启动服务时,服务无法启动,请使用较低版
本,因为有可能出现Jenkins和Java的版本不兼容的情况。
浏览器访问jenkins:192.168.0.16:8080 |
---|
![]() |
安装插件:安装推荐的插件 |
![]() |
![]() |
3、Jenkins与GitLab协同
接下来我们就可以使用Jenkins从Gitlab拉取代码到本机并实现代码的自动发布。
具体流程:
- 配置Jenkins与GitLab协同
- 拉取项目代码到Jenkins本地
- 构建项目发布
安装Git用于拉取GitLab代码
[root@jenkins ~]# yum -y install git
3.1、创建任务
![]() |
---|
提示:输入名称之后我们还要点击下面的模板选择其一,否则也无法创建项目,紧接着下面是一些复选框。
![]() |
---|
![]() |
![]() |
![]() |
![]() |
解释:本次构建的大致含义是Jenkins将GitLab上的choujiang项目拉取到了Jenkins主机本地:/var/lib/jenkins/workspace/choujiang
查看项目
[root@jenkins ~]# ls /var/lib/jenkins/workspace/choujiang
common css dockerfile image index.html js README.md
3.2、安装Docker构建镜像
# wget https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo -O /etc/yum.repos.d/docker-ce.repo
# yum -y install docker-ce && systemctl enable docker --now
docker build -t choujiang:v$BUILD_ID .
![]() |
---|
![]() |
![]() |
提示:Jenkins中构建(Build)Docker镜像时出现:dial unix /var/run/docker.sock: permission denied
把这个目录的权限打开
# chmod -R 777 /var/run/docker.sock
构建成功后检查镜像
# docker images
添加harbor仓库地址
# vim /etc/docker/daemon.json
{
"insecure-registries": ["http://192.168.0.25"]
}
重启服务
# systemctl restart docker
4、Jenkins集成Harbor仓库
回到Jenkins任务,在构建中增加如下命令将镜像并推送到仓库: |
---|
![]() |
docker build -t choujiang:v$BUILD_ID .
docker login -u admin -p 12345 192.168.0.16
docker tag choujiang:v$BUILD_ID 192.168.0.16/image/choujiang:v$BUILD_ID
docker push 192.168.0.16/image/choujiang:v$BUILD_ID
![]() |
---|
4.1、创建pod并使用镜像
[root@master01 ~]# vim ingress-http.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
imagePullSecrets: #定义镜像下载使用的secrets
- name: docker-harbor #secret名称,与上边创建名称保持一致
containers:
- name: nginx
image: 192.168.0.16/image/choujiang:v1 #指定拉取的镜像
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
selector:
app: deploy-nginx
clusterIP: None
type: ClusterIP #service类型
ports:
- port: 80
targetPort: 80
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-http #自定义ingress名称
namespace: test
annotations:
ingressclass.kubernetes.io/is-default-class: "true" #指定spec下方的rules的path可以使用正则表达式
kubernetes.io/ingress.class: nginx #指定控制器的类别为nginx
spec:
rules: #定义主机列表
- host: www.nginx.com #自定义域名
http:
paths:
- pathType: Prefix #路径类型
path: "/" #定义站点路径
backend: #定义后端引用的服务
service: #关联service
name: svc-nginx #对应上面创建的service名称
port:
number: 80 #service端口创建Pod
[root@master01 ~]# kubectl create -f ingress-http.yml
查看Pod信息
[root@master01 ~]# kubectl get pod -n test
验证pod 镜像下载地址
[root@master01 ~]# kubectl describe pod -n test
访问测试:在访问测试时,通过域名与ingress对外暴露的端口进行访问
查看ingress-nginx暴露的端口
[root@master01 ~]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
ingress-nginx NodePort 10.97.31.167 <none> 80:30256/TCP,443:31232/TCP 21
访问测试:注意80对应的是HTTP端口,443对应的是HTTPS端口
http://www.nginx.com:30509/
十二、认证及Service、Account、鉴权RBAC
k8s作为一个分布式集群的管理工具,保证集群的安全性是非常重要的任务,所谓的安全性其实就是保证对k8s的各种客户端进行认证和鉴权操作。
k8s 集群的所有的操作基本上都是通过 apiserver 这个组件进行的,k8s对于访问 API 来说提供了三个步骤的安全措施:认证
、鉴权
、准入控制
。
k8s 的认证机制非常多,要想一个个搞清楚也绝非易事,我们只需要掌握几个比较重要且使用广泛的认证机制即可。
1、k8s用户分类
普通用户(k8s用户)
:可以理解为人通过 kubectl 以及一些客户端工具访问 apiserver 时所需要认证的用户,此类用户嵌入在客户端的证书中。
X509 client certs
X509是一种数字证书的格式标准,现在 HTTPS 依赖的 SSL 证书使用的就是使用的 X509 格式。X509 客户端证书认证方式是 k8s 所有认证中使用最多的一种,相对来说也是最安全的一种,kubernetes的一些部署工具 kubeadm、minkube 等都是基于证书的认证方式。
ServiceAccount(服务账户)
: 指在 kubernetes 集群中的 pod 要访问 apiserver 时所使用的,也就是 serviceaccounts。
2、普通用户
案例:创建一个只能管理test空间下的Pods资源普通用户
普通用户并不是通过k8s来创建和维护,是通过创建证书和切换上下文环境的方式来创建和切换用户。
创建证书私钥文件
# openssl genrsa -out devuser.key 2048
用此私钥文件创建一个csr(证书签名请求)文件
# openssl req -new -key devuser.key -subj "/CN=devuser" -out devuser.csr
拿着证书签名请求文件向apiserver的证书去签署生成证书,签名申请的用户名是devuser
# openssl x509 -req -in devuser.csr -CA /etc/kubernetes/pki/ca.crt -CAkey /etc/kubernetes/pki/ca.key -CAcreateserial -out devuser.crt -days 365
生成账号devuser
# kubectl config set-credentials devuser --clientcertificate=./devuser.crt --client-key=./devuser.key --embed-certs=true
查看集群用户信息
kubectl config get-users
NAME
devuser
kubernetes-admin
设置账号上下文并指定名称空间, 信息默认会保存在 $HOME/.kube/config
# kubectl config set-context devuser@kubernetes --cluster=kubernetes --user=devuser --namespace=test
查看上下文信息
# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
devuser@kubernetes kubernetes devuser test
* kubernetes-admin@kubernetes kubernetes kubernetes-admin
提示:*表示当前所处的上下文环境用户
切换到账户devuser(通过上下文名称切换)
# kubectl config use-context devuser@kubernetes
Switched to context "devuser@kubernetes".
查看上下文信息
# kubectl config get-contexts
CURRENT NAME CLUSTER
AUTHINFO NAMESPACE
* devuser@kubernetes kubernetes devuser
test
kubernetes-admin@kubernetes kubernetes
kubernetes-admin
提示:*表示当前所处的上下文环境用户
使用devuser用户查看名称空间下的pod资源
# kubectl get po -n test
Error from server (Forbidden): pods is forbidden: User "devuser" cannot list resource "pods" in API group "" in the namespace "test"
#提示:禁止devuser用户列出test下API组中的pods资源
#解决方法:如果希望devuser用户对test下具备相应的权限,需要通过管理员身份进行授权
切换身份为管理员
# kubectl config use-context kubernetes-admin@kubernetes
# kubectl config get-contexts
CURRENT NAME CLUSTER AUTHINFO NAMESPACE
devuser@kubernetes kubernetes devuser test
* kubernetes-admin@kubernetes kubernetes kubernetes-admin
3、Service Account
serviceaccounts 是面向 namespace 的,每个 namespace 创建的时候,kubernetes 会自动在这个 namespace 下面创建一个默认(default)的 serviceaccounts;并且这个 serviceaccounts 只能访问该 namespace 的资源。
# cat sa-admin-test.yml
apiVersion: v1
kind: ServiceAccount
metadata:
name: admin
namespace: test
# kubectl get sa -n test
admin 0 5s
3.1、创建Pod并使用Service Account
cat nginx.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: web-nginx
namespace: test
spec:
replicas: 1
selector:
matchLabels:
app: web-nginx
template:
metadata:
labels:
app: web-nginx
spec:
containers:
- name: nginx
image: nginx:1.18-alpine
ports:
- containerPort: 80
serviceAccountName: admin # 指定Pod使用的sa(注意层级关系)
# kubectl apply -f nginx.yml
3.2、默认权限
创建 pod 的时候,如果没有指定 Service Account
, 则会使用同一个命名空间下面的 默认的名为 default 的服务账号,默认的服务账号有哪些权限,取决于不同的 k8s 集群。
查看serviceaccount权限格式:
kubectl auth can-i <list|create|delete> pod --as=system:serviceaccount:<namespace>:<serviceaccount> -n <namespace>
# kubectl auth can-i list pod --
as=system:serviceaccount:test:default -n test
no
# kubectl auth can-i list pod --
as=system:serviceaccount:test:admin -n test
no
# kubectl auth can-i create pod --
as=system:serviceaccount:test:default -n test
no
# kubectl auth can-i create pod --
as=system:serviceaccount:test:admin -n test
no
# kubectl auth can-i delete pod --
as=system:serviceaccount:test:default -n test
no
# kubectl auth can-i delete pod --
as=system:serviceaccount:test:admin -n test
no
4、RBAC
ApiServer目前支持以下几种鉴权策略:
- AlwaysDeny:表示拒绝所有请求,一般用于测试
- AlwaysAllow:允许接收所有请求,相当于集群不需要授权流程
- ABAC:基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制
- Webhook:通过调用外部REST服务对用户进行授权
- Node:是一种专用模式,用于对k8s发出的请求进行访问控制
RBAC介绍
RBAC(Role-Based Access Control)基于角色访问控制,主要用于描述给哪些对象授予了哪些权限,k8s在1.5版本的时候引入了RBAC的权限控制机制,1.6+版本都默认开启了RBAC。
查看方式
# grep -C3 'authorization-mode'
/etc/kubernetes/manifests/kube-apiserver.yaml
- --audit-log-maxsize=100
- --audit-log-path=/var/log/kubernetes/audit.log
- --audit-policy-file=/etc/kubernetes/audit-policy.yml
- --authorization-mode=Node,RBAC
- --client-ca-file=/etc/kubernetes/pki/ca.crt
- --enable-admission-plugins=NodeRestriction
- --enable-aggregator-routing=true
RBAC中会涉及到下面几个概念:
对象
:User、Groups、ServiceAccount(账号)角色
:Role、ClustserRole 代表着一组权限绑定
:RoleBinding、ClusterRoleBinding 用于将角色(权限)跟账号绑定
在一起(权限绑定)
绑定关系:
Role
可以通过RoleBinding与User、Groups进行绑定(不支持与ServiceAccount进行绑定)ClustserRole
可以通过RoleBinding和ClusterRoleBinding与User、Groups、ServiceAccount进行绑定
5、Role
一个角色就是一组权限的集合
,Role只能对命名空间内的资源进行相应的操作,需要指定namesapce
cat devuser-role.yml
kind: Role
apiVersion: rbac.authorization.k8s.io/v1
metadata:
namespace: test #须指定名称空间
name: devuser-role #角色名称
rules:
- apiGroups: [""] #支持的API组列表,""空字符串,表示核心API群
resources: ["pods","deployments"] #支持的资源列表
verbs: ["get", "list", "watch", "create", "update","patch", "delete"] #允许对资源执行的操作
需要详细说明的是rules中的参数
apiGroups
:支持的API组列表
在使用k8s进行服务的部署过程中我们会使用到Deployment、Service、Pod等资源,在 yaml 文件中我们需要指定对应的 API 版本,我们可以通过访问相应的接口来管理相应的资源信息,在 k8s中为了提高 API 的可扩展性,采用了 API Groups 进行标识这些接口
resources
:支持的资源对象列表
“services”,“endpoints”,“pods”,“secrets”,“configmaps”,“crontabs”,“deployments”,“jobs”,“nodes”,“rolebindings”,“clusterroles”,“daemonsets”,“replicasets”,“statefulsets”,“horizontalpodautoscalers”,“replicationcontrollers”,“cronjobs”
verbs
:对资源对象的操作方法列表
“get”,“list”,“watch”,“create”,“update”,“patch”,“delete”,“exec”
创建Role
# kubectl apply -f devuser-role.yml
# kubectl get role -n test
NAME CREATED AT
devuser-role 2022-11-10T08:57:25Z
6、RoleBinding
角色绑定用来把一个角色绑定到一个目标上,绑定目标可以是User、Group或者ServiceAccount
可以把同一个名称空间下的角色与对象之间进行相互的绑定(空间级别的权限绑定)
cat devuser-rolebinding.yml
kind: RoleBinding #角色绑定
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: devuser-rolebinding #名称自定义
namespace: test #须指定名称空间
subjects: #定义目标对象
- kind: User #对象类型(User、Groups)
name: devuser #对象名称(提前创建好)
apiGroup: rbac.authorization.k8s.io
roleRef: #指向的角色
kind: Role #角色类型(Role、ClustserRole)
name: devuser-role #角色名称(提前创建好)
apiGroup: rbac.authorization.k8s.io
提示:role不支持与ServiceAccount绑定,只有ClusterRole才可以ServiceAccount绑定
创建RoleBinding
# kubectl create -f devuser-rolebinding.yml
# kubectl get rolebinding -n test
NAME ROLE AGE
devuser-rolebinding Role/devuser-role 12m
切换devuser用户验证权限
# kubectl config get-contexts
# kubectl config use-context devuser@kubernetes
# kubectl get pod -n test
NAME READY STATUS RESTARTS AGE
web-nginx-6cfcdf646-vqhrq 1/1 Running 1 (3h35mago) 2d
提示:该用户对pods只具备"get","watch","list权限",如果想操作pods以外的资源是不可以的
# kubectl get deployment -n test
Error from server (Forbidden): deployments.apps is forbidden: User "devauser" cannot list resource "deployments" in API group "apps" in the namespace "test"
切换回管理员账号
# kubectl config use-context kubernetes-admin@kubernetes
7、ChusterRole
ClusterRole可以对集群范围内资源、跨namespace
的范围资源、非资源类型进行授权
cat clusterole.yml
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1
metadata:
name: devuser-clusterrole #名称自定义,无需指定名称空间
rules:
- apiGroups: [""] #支持的API组列表,""空字符串,表示核心API群
resources: ["pods","deployments"] #支持的资源对象列表
verbs: ["get", "list", "watch", "create", "update","patch", "delete"] #允许对资源对象的操作方法列表
创建ClusterRole
# kubectl create -f clusterole.yml
# kubectl get clusterrole | grep deployment
deployment-clusterrole
8、ClusterRoleBinding
ClusterRoleBinding可对于clusterrole(集群角色)与目标对象进行绑定
一种很常用的做法是,集群管理员为集群范围预定义好一个ClusterRole,然后通过RoleBinding在多个命名空间中重复使用这些ClusterRole,所以同一个ClusterRole只能读取某一个空间内的资源
cat devuser-clusterrolebinding.yml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: clusterrole-rolebinding
roleRef: #指向的角色
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole #角色类型
name: devuser-clusterrole #角色名称(提前创建好)
subjects: #定义对象
- kind: User #对象类型(User、Groups、ServiceAccount)
name: devuser #对象名称(提前创建好)
namespace: test #须名称空间
创建ClusterRoleBinding
# kubectl create -f clusterrole-binding.yml
# kubectl get clusterrolebinding | grep clusterrole-rolebinding
clusterrole-rolebinding
切换devuser用户检验权限
# kubectl config get-contexts
# kubectl config use-context devuser@kubernetes
# kubectl get pod -n test
# kubectl get pod -n kube-system
切换回管理员账号
# kubectl config use-context kubernetes-admin@kubernetes
clusterrole与ServiceAccount绑定
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: admin-rolebinding
roleRef: #指向的角色
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole #角色类型
name: devuser-clusterrole #角色名称(提前创建好)
subjects: #定义对象
- kind: ServiceAccount #对象类型(User、Groups、ServiceAccount)
name: admin #对象名称(提前创建好)
namespace: test #须名称空间
准入控制(Admission Control)
准入控制是请求的最后一个步骤,准入控制有许多内置的模块,可以作用于对象的 “CREATE”、“UPDATE”、“DELETE”、“CONNECT” 四个阶段。在这一过程中,如果任一准入控制模块拒绝,那么请求立刻被拒绝。一旦请求通过所有的准入控制器后就会写入对象存储中。
**总结:**认证解决的问题是识别用户的身份,鉴权是为了解决用户有哪些权限,准入控制是作用于 k8s 中的对象,通过合理的权限管理,能够保证系统的安全可靠,准入控制通常情况下可以不用。
十三、K8S数据存储
由于容器的生命周期不稳定
,可能随时被创建与销毁,如果容器被销毁后,在容器中产生的数据也会被清除,如果需要对容器内的数据实现持久化保存
,我们需要将容器内的数据与pod分离,将数据放在专门的存储卷上
1、存储卷的分类
kubernetes支持的存储卷类型非常丰富,使用下面的命令查看
# kubectl explain pod.spec.volumes
- 或者参考: https://kubernetes.io/docs/concepts/storage/
K8s支持的存储类型大体分为如下几类:
- 本地存储卷
emptyDir
(空目录临时存储):pod删除,数据也会被清除, 用于数据的临时存储hostPath
:宿主机目录映射(本地存储卷),pod删除,目录中的数据不会被清楚,适合数据的永久保存
- 网络存储卷
- NAS网络附加存储: nfs等分布式网络附加存储: glusterfs,cephfs,rbd(chph中的存储类型),cinder(OpenStack中的存储类型)等
- SAN块存储: iscsi,FC等
- 云存储: aws,azurefifile等
2、本地存储
2.1、EmptyDir
EmptyDir是最基础的存储类型,一个EmptyDir就是主机上的一个空目录
EmptyDir是在Pod被分配到Node节点时创建的,它的初始内容为空,并且无需指定宿主机上对应的目录文件,应为k8s会自动分配一个空目录,当Pod销毁时
,EmptyDir中的数据也会被永久删除
。
EmptyDir用途如下:
- 临时存储空间,例如用于某些应用程序运行时所需要的临时目录,且无需永久保留
- 同一个Pod中容器需要从另一个容器中获取数据的目录(多容器间共享数据)
案例:通过创建一个nginx的Pod并使用emptydir作为数据的临时存储
[root@master01 ~]# vim emptydir_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
volumes: 声明volume存储卷
- name: nginx-logs 定义volume卷名称
emptyDir: {} 类型为emptyDir,{}表示空目录
containers:
- name: nginx
image: nginx:1.17.0 #指定镜像
ports: #定义端口
- containerPort: 80 #端口
volumeMounts:
- name: nginx-logs 指定挂载的volume名称
mountPath: /var/log/nginx 容器内挂载点目录
创建Pod
[root@master01 ~]# kubectl create -f emptydir_nginx.yml
查看Pod信息
[root@master01 ~]# kubectl get pod -n test -o wide deploy-nginx-6765b8f99c-wwzj6 2/2 Running 0
16m 10.244.5.42 worker01
可以通过find命令在对应节点中搜索存储卷目录的所在位置
[root@worker01 ~]# find /var/lib/kubelet/pods/ -namenginx-logs
访问nginx
[root@master01 ~]# curl 10.244.5.42
删除Pod
[root@master01 ~]# kubectl delete -f emptydir_nginx.yml
总结:EmptyDir存储方式不会永久保存数据
,应为EmptyDir的生命周期是跟据Pod的生命周期是一样的,它会随着Pod的结束而销毁,如果想简单的将数据持久化到集群主机中,可以选择用HostPath。
2.2、HostPath
HostPath是将Node节点中一个实际目录挂载到Pod中,以供容器使用,这样的设计可以保证Pod销毁了,但是数据仍然可以保存在宿主机上,实现数据永久保存
。
案例:创建nginx,并通过hostPath存储卷方式对nginx实现数据持久化保存。
[root@master01 ~]# vim hostpath_nginx.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
volumes: 声明volume存储卷
- name: nginx-html 定义volume名称
hostPath: 类型hostPath
type: DirectoryOrCreate 目录存在就使用,不存在就先创建后使用
path: /nginx/html 指定node节点目录(存放页面)
- name: nginx-logs 定义volume名称
hostPath: 类型hostPath
type: DirectoryOrCreate 目录存在就使用,不存在就先创建后使用
path: /nginx/logs 指定node节点目录(存放日志)
containers:
- name: nginx
image: nginx:1.17.0 #指定镜像
ports: #定义端口
- containerPort: 80 #端口
volumeMounts:
- name: nginx-html 指定挂载的volume名称
mountPath: /usr/share/nginx/html 容器内挂载点目录
- name: nginx-logs 指定挂载的volume名称
mountPath: /var/log/nginx 容器内挂载点目录
关于 hostPath的type属性可用值说明:
DirectoryOrCreate 目录存在就使用,不存在就先创建后使用
Directory 目录必须存在
FileOrcreate 文件存在就使用,不存在就先创建后使用
File 文件必须存在
Socket 套接字必须存在
CharDevice 字符设备必须存在
BlockDevice 块设备必须存在
创建Pod
[root@master01 ~]# kubectl create -f hostpath_nginx.yml
查看Pod详细信息
[root@master01 ~]# kubectl get pod -n test -o wide
deploy-nginx-8578d555c9-np9gk 1/1 Running 0 6m46s 10.244.5.48 worker01
查看worker01节点的hostPath目录
[root@worker01 ~]# ls /nginx
html
创建index.html文件进行访问测试
[root@worker01 ~]# echo hello > /nginx/html/index.html
通过Pod访问
[root@master01 ~]# curl 10.244.5.48
删除Pod验证数据持久化
[root@master01 ~]# kubectl delete -f hostpath_nginx.yml
总结:HostPath可以解决数据持久化
的问题,但是一旦Node节点故障了
,那么数据卷并不会
转移到其他的节点,为了解决以上的问题,可以使用网络文件存储系统,比较常见的有NFS、glusterfs等。
3、网络存储
3.1、NFS
NFS是一个网络文件存储系统,可以搭建一台NFS服务器,然后将Pod中的存储直接连到NFS系统上,这样无论Pod在节点上怎么转移,只要该节点跟NFS对接没问题,数据就可以成功访问。
部署NFS服务端
单独准备一台服务器部署NFS
安装nfs-utils
[root@k8s-nfs01 ~]# yum -y install nfs-utils
创建共享目录
[root@k8s-nfs01 ~]# mkdir -p /k8s-nfs/nginx/html
[root@k8s-nfs01 ~]# mkdir /k8s-nfs/nginx/logs
共享目录
[root@k8s-nfs01 ~]# vim /etc/exports
/k8s-nfs/nginx/html 192.168.0.0/24(rw,no_root_squash)
/k8s-nfs/nginx/logs 192.168.0.0/24(rw,no_root_squash)
启动nfs服务&&设置服务随机自启
[root@k8s-nfs01 ~]# systemctl start nfs && systemctl enable nfs
NFS客户端
在集群worker节点上都安装nfs客户端工具,不需要启动服务,这样worker节点才可以驱动nfs设备
# yum -y install nfs-utils
查看共享资源
# showmount -e 192.168.0.15
Export list for 192.168.0.15:
/k8s-nfs/nginx/logs 192.168.0.0/24
/k8s-nfs/nginx/html 192.168.0.0/24
案例:创建Pod,并通过NFS存储卷方式对nginx实现数据持久化保存。
# cat vim nfs_nginx.yml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-nginx #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-nginx #pod的标签
spec:
volumes: 声明volume存储卷
- name: volume-ngx-html 定义volume名称
nfs: 类型nfs
server: 192.168.0.15 NFS服务器地址
path: /k8s-nfs/nginx/html NFS共享目录
- name: volume-ngx-logs 定义volume名称
nfs: 类型nfs
server: 192.168.0.15 NFS服务器地址
path: /k8s-nfs/nginx/logs NFS共享目录
containers:
- name: nginx
image: nginx:1.18.0 #指定镜像
ports: #定义端口
- containerPort: 80 #端口
volumeMounts:
- name: volume-ngx-html 指定挂载的volume名称
mountPath: /usr/share/nginx/html 容器内挂载点目录
- name: volume-ngx-logs 指定挂载的volume名称
mountPath: /var/log/nginx 容器内挂载点目录
创建Pod
[root@master01 ~]# kubectl create -f nfs_nginx.yml
查看Pod详细信息
[root@master01 ~]# kubectl describe pod nfs-nginx-5c8699644d-mv6bl -n test
...
Mounts:
/usr/share/nginx/html from volume-html (rw)
/var/log/nginx from volume-logs (rw)
...
Volumes:
volume-html:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 192.168.0.15
Path: /k8s-nfs/nginx/html
ReadOnly: false
volume-logs:
Type: NFS (an NFS mount that lasts the lifetime of a pod)
Server: 192.168.0.15
Path: /k8s-nfs/nginx/logs
ReadOnly: false
验证nfs共享目录
[root@k8s-nfs01 ~]# ls /k8s-nfs/nginx/logs/
access.log error.log
删除Pod
[root@master01 ~]# kubectl delete -f nfs_nginx.yml
验证数据持久化
[root@k8s-nfs01 ~]# ls /k8s-nfs/nginx/logs/
access.log error.log
3.2、PV 与 PVC
经过前面的NFS存储已经可以实现数据的持久化保存,而k8s为了能够屏蔽底层的存储细节,方便用户使用,k8s引入了PV和PVC的存储方式
PV(Persistent Volume 存储卷)
:可以理解为是一个网络存储,就是一个实实在在的存储数据的地方,比如NFS、iSCSI、Ceph和云提供商指定的存储系统。
若严格来说,PV是k8s里面的一个概念,它本身不是存储,只不过是创建pv的资源清单文件中指定了网络存储的地址,同时也指定了一些存储的参数。
PVC(Persistent Volume Claim 存储卷声明)
: 可以理解为Pod对所需存储空间的申请方式,也就是说PVC为其Pod寻找一个合适的PV进行绑定,绑定之后就可以使用PV提供的存储资源。
PV资源清单文件介绍
apiVersion: v1
kind: PersistentVolume
metadata:
name:
spec:
nfs: 存储类型,与底层真正存储对应,如:nfs
capacity: 存储能力,目前只支持存储空间的设置
storage: 4Gi 存储空间
accessModes: 访问模式
persistentVolumeReclaimPolicy: 回收策略
accessModes(访问模式):用于描述用户应用对存储资源的访问权限,访问权限包含以下几种方式:
- ReadWriteOnce(RWO):读写权限,但是只能被单个节点挂载
- ReadOnlyMany(ROX): 只读权限,可以被多个节点挂载
- ReadWriteMany(RWX):读写权限,可以被多个节点挂载
注意事项:底层不同的存储类型可能支持的访问模式也不同
persistentVolumeReclaimPolicy(回收策略):当PV不再被PVC使用之后,对其PV内的数据处理方式,目前支持三种策略
- Retain(保留):保留数据,需要管理员手工清理数据
- Recycle(回收):清除PV中的数据,效果相当于执行rm -rf
- Delete(删除):与PV相连的后端存储完成volume的删除操作,当然这常见于云服务商的存储服务
注意事项:底层不同的存储类型可能支持的回收策略也不同
案例:使用NFS作为底层存储,并通过PV的方式实现数据持久化
创建共享目录
[root@k8s-nfs01 ~]# mkdir -p /k8s-nfs/pv01-mysql
共享目录
[root@k8s-nfs01 ~]# vim /etc/exports
/k8s-nfs/pv01-mysql 192.168.0.0/24(rw,no_root_squash)
重启nfs服务
[root@k8s-nfs01 ~]# systemctl restart nfs
3.2.1、PV 应用案例
案例:创建一个4G 的 PV 存储卷,并使用NFS作为后端存储。
[root@master01 ~]# vim pv01-mysql.yml
apiVersion: v1
kind: PersistentVolume
metadata:
name: pv01-mysql
spec:
capacity: #存储能力,目前只支持存储空间的设置
storage: 4Gi #存储空间
accessModes: #访问模式
- ReadWriteMany #读写权限,可以被多个节点挂载
persistentVolumeReclaimPolicy: Retain #回收策略为保留
nfs: #存储类型
path: /k8s-nfs/pv01-mysql #挂载路径
server: 192.168.0.15 #nfs服务器地址
#提示:PV是全局资源,无需指定命名空间
创建pv
[root@master01 ~]# kubectl create -f pv01-mysql.yml
查看pv详细信息
[root@master01 ~]# kubectl get pv -o wide
PV状态:一个PV的生命周期中,可能会处于4种不同的阶段
1. Avallable(可用):表示可用状态,还未被任何的PVC绑定
2. Bound(已绑定):表示PV已经被PVC绑定
3. Released(已释放):表示PVC被删除,但是资源还未被集群重新声明
4. Falled(失败):表示该PV的自动回收失败
3.2.2、PVC 应用案例
PVC是资源的申请,用来声明对存储空间、访问模式、存储类别需求信息,PVC清单文件介绍:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc
namespace: test
spec:
accessModes: 访问模式
selector: 标签选择器,采用标签对PV选择
storageClassName: 存储类别
resources: 资源
requests: 请求
storage: 资源大小
案例:创建一个PVC,并绑定pv-mysql存储卷
[root@master01 ~]# vim pvc-mysql.yml
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-mysql
namespace: test
spec:
accessModes: #访问模式,访问模式要与PV模式相同,否则绑定不上
- ReadWriteMany #读写权限,可以被多个节点挂载
resources: #请求
requests: #资源
storage: 4Gi #资源大小,在绑定PV时,按照申请资源大小选择符合的PV,如果超出PV空间范围,则无法绑定
#PVC是局部资源,可指定命名空间
创建pvc
[root@master01 ~]# kubectl create -f pvc_mysql.yml
查看pvc信息
[root@master ~]# kubectl get pvc -n test
查看pv信息
[root@master ~]# kubectl get pv
案例:创建MySQL并挂载PVC实现数据持久化。
提示:需要提前在集群节点安装好nfs-utils
[root@master01 ~]# vim pv-pvc_mysql.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-mysql
namespace: test
spec:
selector: #标签选择器(基于选择器匹配Pod)
matchLabels: #标签类型
app: deploy-mysql #匹配pod的标签(表示deploy管理带有此标签的Pod)
template: #pod的配置模板
metadata:
labels:
app: deploy-mysql #pod的标签
spec:
volumes: 声明volume存储卷
- name: mysql-data 定义volume名称
persistentVolumeClaim: 类型为PVC
claimName: pvc-mysql PVC名称(与前边创建的PVC名称一致)
readOnly: false 访问模式,false为读写,true为只读
containers:
- name: mysql
image: mysql:5.7 #镜像版本
ports: #定义端口
- containerPort: 3306 #端口
env: #定义环境变量
- name: "MYSQL_ROOT_PASSWORD" #变量名称(必须为数组类型)
value: "123456" #值
volumeMounts:
- name: mysql-data 指定挂载的volume名称,与上述volume名称一致
mountPath: /var/lib/mysql 容器内挂载点目录创建pod
[root@master01 ~]# kubectl create -f pv-pvc_mysql.yml
查看pod信息
[root@master01 ~]# kubectl get po -n test
查看NFS共享存储路径
[root@k8s-nfs01 ~]# ls /k8s-nfs/pv01-mysql/
删除pod、pvc、pv
[root@master01 ~]# kubectl delete -f pv01-mysql.yml
[root@master01 ~]# kubectl delete -f pvc_mysql.yml
[root@master01 ~]# kubectl delete -f pv_mysql.yml
总结:
-
pv资源供应:管理员手动创建底层存储和PV
-
pvc资源绑定:用户创建PVC,k8s负责根据PVC的声明去寻找PV,并绑定在用户定义好的PVC之后,系统将根据PVC对存储资源的请求在已存在的PV中选择一个满足条件的,一旦找到,就将PV与用户定义的PVC进行绑定,用户的应用就可以使用这个PVC,如果找不到,PVC一直处于Pending状态,直到管理员创建了一个符合要求的PV,PV一旦绑定到某个PVC上,就会被这个PVC独占,不能在与其他的PVC进行绑定
-
资源使用:用户可在pod中像volume一样使用PVC,将PVC挂载到容器的某个路径进行使用
-
资源释放:当存储资源使用完毕后,用户可以删除PVC,与该PVC绑定的PV将会被标记为“已释放”,但还不能立刻与其他PVC进行绑定,应为之前PVC写入的数据可能还被留在存储设备上,只有在清除之后该PV才能再次使用
-
资源回收:k8s根据PV设置的回收策略进行资源的回收
3.3、k8s存储类StorageClass
上面介绍的PV和PVC模式都是需要先创建好PV,然后定义好PVC和pv进行一对一的Bond,但是如果PVC请求成千上万,那么就需要创建成千上万的PV,对于运维人员来说维护成本很高,Kubernetes提供一种自动创建PV的机制,叫StorageClass,它的作用就是创建PV的模板。k8s集群管理员
通过创建storageclass可以动态生成一个存储卷pv供k8s pvc使用。
查看定义的storageclass需要的字段
# kubectl explain storageclass
apiVersion: storage.k8s.io/v1 #资源版本
kind: StorageClass #资源类型
metadata:
name: nfs-client #资源名称
provisioner: nfs-provisioner #定义存储的供应者(名称自定义)
reclaimPolicy: Retain #回收策略,保留数据,需要管理员手工清理数据
allowVolumeExpansion: true #允许卷扩展,将此功能设置为true时,允许用户通过编辑相应的 PVC 对象来调整卷大小(不能用于缩小卷)但是,只有云端存储才支持此参数。
volumeBindingMode: Immediate #绑定模式为立即绑定,一旦创建了PVC,就会立即发生卷绑定和动态配置
provisioner(存储的供应者)以NFS为例,要想使用NFS提供真正的存储资源,我们需一个nfs-client的自动装载程序,称之为provisioner,这个程序会使用我们已经配置好的NFS服务器自动创建持久卷,也就是自动帮我们创建PV。
如果是其他存储类型可以参考https://kubernetes.io/zh-cn/docs/concepts/storage/storage-classes/#aws-ebs
十四、K8S 日志收集ELK
常见的日志收集方案
ELK简介:ELK是三个开源软件的缩写,分别表示:ElasticSearch
、Logstash
、Kibana
。
Elasticsearch
:是个开源分布式搜索引擎数据库,提供搜集、分析、存储数据三大功能。Logstash
:主要是用来做日志的分析、过滤等处理工作。Kibana
:可以为 Logstash 和 ElasticSearch 提供的日志提供友好的Web 界面,可以帮助汇总、分析和搜索重要数据日志。- 新增了一个
FileBeat
,它是一个轻量级的日志收集处理工具(Agent),Filebeat占用资源少,适合于在各个服务器上搜集日志后传输给Logstash。
传统服务日志收集与K8s服务日志收集区别:
传统日志收集
:传统服务部署在固定的服务器,而日志也固定服务器中的具体目录中,即便服务器重启,应用依然在固定的节点,对日志收集系统来讲,也不需要重新指定日志的所在位置。K8s日志收集
:在K8s集群中,服务通常不会固定在某一个节点,如果服务重启、弹性伸缩,那么服务可能会漂移到其他的节点,所以对于K8s中日志的收集与传统的方式不太一样。
![]() |
---|
Fluentd这种需要我们在每个 Pod 中都附带一个Agent容器来进行本 Pod内部容器的日志采集,
一般采用共享卷的方式,但是对于这一种模式来说,很明显的一个问题就是占用的资源比较多,尤其是在集群规模比较大的情况下,或者说单个节点上Pod特别多的情况下,它会占用过多的系统资源,同时也对日志存储后端占用过多的连接数。当我们的集群规模越大,这种部署模式引发的潜在问题就越大。
1、破解利器Log-Pilot
1.1、特性
- 轻量级:Log-Pilot采用节点模式(每个Node节点仅会部署一个Agent)进行日志采集,对比与上述flfluentd(每个Pod中部署一个Agent)更加的节约资源,同样在集群规模比较大的情况下表现出的优势越明显。
- 自动检测:Log-Pilot 能够自动感知宿主机上容器的创建删除事件,进而动态配置容器日志采集配置文件。
- 句柄保持机制:Log-Pilot 内部会实时跟踪日志采集偏移量,然后维持日志文件信息与偏移量的映射关系,最后定期地持久化到磁盘中。采用偏移量的方式我们可以避免日志采集丢失和重复的问题,同时即使当采集工具宕掉再起来,它可以通过加载持久化在磁盘上的元数据信息,然后从指的日志偏移位置上继续采集日志。
1.2、环境准备
本案例在K8s集群内部署日志收集系统ELK(节点内存至少6G)。
创建 Service Account
创建es的Service Account,ServiceAccount在K8s中可用于存储服务的账号信息,用于给运行在Pod里面的进程提供必要的身份证明。
[root@master01 ~]# kubectl create sa es-admin -n kube-system
[root@master01 ~]# kubectl create clusterrolebinding es-admin --clusterrole=cluster-admin --serviceaccount=kube-system:es-admin
当serviceaccout创建时K8s会默认创建对应的secret用于存储账号的token信息
[root@master01 ~]# kubectl get secrets -n kube-system | grep es-admin
2、部署ES集群
es集群采用StatefulSet控制器部署,StatefulSet本质上是Deployment的一种变体,用于部署有状态服务,它所管理的Pod拥有固定的Pod名称,启停顺序。
[root@master01 logs]# kubectl create -f elasticsearch.yaml
查看StatefulSet信息(缩写:sts)
[root@master01 logs]# kubectl get sts -n kube-system
查看es的Pod
[root@master01 ~]# kubectl get pod -n kube-system -o wide | grep elastic
查看es的svc端口
[root@master01 logs]# kubectl get svc -n kube-system
elasticsearch-0 1/1 Running 0 22m
elasticsearch-1 1/1 Running 0 22m
elasticsearch-2 1/1 Running 0 21m
3、部署 Log-Pilot
为了保证集群每个节点都能够采集到日志信息,Log Pilot采用DaemonSet控制器创建,DaemonSet可以保证集群的每个节点都存在一个相同的副本。
[root@master01 logs]# kubectl apply -f log-pilot.yaml
[root@master01 ~]# kubectl get pod -n kube-system | grep log-pilot
log-pilot-c4kvk 1/1 Running 0 2m32s
log-pilot-s78h8 1/1 Running 0 2m32s
log-pilot-xlclz 1/1 Running 0 2m32s
log-pilot-xzwps 1/1 Running 0 2m32s
4、部署 kibana
kibana提供web页面展示,采用Deployment控制器部署(只需要部署一个即可)。
[root@master01 logs]# kubectl apply -f kibana.yaml
[root@master01 logs]# kubectl get deploy -n kube-system kibana 1/1 1 1 82s
[root@master01 logs]# kubectl get ing -n kube-system
NAME CLASS HOSTS ADDRESS PORTS AGE
kibana <none> www.kibana.com 192.168.0.13 80 4m2s
配置域名解析:C:\Windows\System32\drivers\etc
192.168.0.13 www.kibana.com
访问测试:在访问测试时,通过域名与ingress对外暴露的端口进行访问
查看ingress-nginx暴露的端口
[root@master01 logs]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP
ingress-nginx-controller NodePort 10.111.226.197 <none> 80:31681/TCP,443:31150/TCP 22m
访问测试:注意80对应的是HTTP端口,443对应的是HTTPS端口
访问: http://www.kibana.com:31681/ |
---|
![]() |
部署应用并采集日志
[root@master01 ~]# vim elk.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
#容器
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
# 环境变量
env:
- name: aliyun_logs_nginx #变量名称开头固定为aliyun_logs(变量名称用于创建kibana索引)
value: "stdout"
- name: aliyun_logs_nginxlogs #变量名称开头固定为aliyun_logs(变量名称用于创建kibana索引)
value: "/var/log/nginx/*"
volumeMounts:
- mountPath: /var/log/nginx/
name: nginxlogs
#数据存储类型
volumes:
- name: nginxlogs
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
selector:
app: deploy-nginx
clusterIP: None
type: ClusterIP #service类型
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-tomcat
namespace: test
spec:
selector:
matchLabels:
app: deploy-tomcat
template:
metadata:
labels:
app: deploy-tomcat
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
# 环境变量
env:
- name: aliyun_logs_tomcat #变量名称开头固定为aliyun_logs(变量名称用于创建kibana索引)
value: "stdout"
- name: aliyun_logs_tomcatlogs #变量名称开头固定为aliyun_logs(变量名称用于创建kibana索引)
value: "/usr/local/tomcat/logs/*"
# 数据挂载
volumeMounts:
- mountPath: /usr/local/tomcat/logs
name: tomcatlogs
#数据存储类型
volumes:
- name: tomcatlogs
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: svc-tomcat
namespace: test
spec:
selector:
app: deploy-tomcat
clusterIP: None
type: ClusterIP #service类型
ports:
- port: 8080
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-http #自定义ingress名称
namespace: test
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
kubernetes.io/ingress.class: nginx #注释名称需为nginx(不可省略)
spec:
rules: #定义主机列表
- host: www.nginx.com #自定义域名
http:
paths:
- pathType: Prefix #路径类型
path: "/" #定义站点路径
backend: #定义后端引用的服务
service: #关联service
name: svc-nginx #对应上面创建的service名称
port:
number: 80 #service端口
- host: www.tomcat.com #自定义域名
http:
paths:
- pathType: Prefix #路径类型
path: "/" #定义站点路径
backend: #定义后端引用的服务
service: #关联service
name: svc-tomcat #对应上面创建的service名称
port:
number: 8080 #service端口
创建Pod
[root@master01 ~]# kubectl create -f elk.yml
h`:主要是用来做日志的分析、过滤等处理工作。
Kibana
:可以为 Logstash 和 ElasticSearch 提供的日志提供友好的Web 界面,可以帮助汇总、分析和搜索重要数据日志。- 新增了一个
FileBeat
,它是一个轻量级的日志收集处理工具(Agent),Filebeat占用资源少,适合于在各个服务器上搜集日志后传输给Logstash。
传统服务日志收集与K8s服务日志收集区别:
传统日志收集
:传统服务部署在固定的服务器,而日志也固定服务器中的具体目录中,即便服务器重启,应用依然在固定的节点,对日志收集系统来讲,也不需要重新指定日志的所在位置。K8s日志收集
:在K8s集群中,服务通常不会固定在某一个节点,如果服务重启、弹性伸缩,那么服务可能会漂移到其他的节点,所以对于K8s中日志的收集与传统的方式不太一样。
![]() |
---|
Fluentd这种需要我们在每个 Pod 中都附带一个Agent容器来进行本 Pod内部容器的日志采集,
一般采用共享卷的方式,但是对于这一种模式来说,很明显的一个问题就是占用的资源比较多,尤其是在集群规模比较大的情况下,或者说单个节点上Pod特别多的情况下,它会占用过多的系统资源,同时也对日志存储后端占用过多的连接数。当我们的集群规模越大,这种部署模式引发的潜在问题就越大。
1、破解利器Log-Pilot
1.1、特性
- 轻量级:Log-Pilot采用节点模式(每个Node节点仅会部署一个Agent)进行日志采集,对比与上述flfluentd(每个Pod中部署一个Agent)更加的节约资源,同样在集群规模比较大的情况下表现出的优势越明显。
- 自动检测:Log-Pilot 能够自动感知宿主机上容器的创建删除事件,进而动态配置容器日志采集配置文件。
- 句柄保持机制:Log-Pilot 内部会实时跟踪日志采集偏移量,然后维持日志文件信息与偏移量的映射关系,最后定期地持久化到磁盘中。采用偏移量的方式我们可以避免日志采集丢失和重复的问题,同时即使当采集工具宕掉再起来,它可以通过加载持久化在磁盘上的元数据信息,然后从指的日志偏移位置上继续采集日志。
1.2、环境准备
本案例在K8s集群内部署日志收集系统ELK(节点内存至少6G)。
创建 Service Account
创建es的Service Account,ServiceAccount在K8s中可用于存储服务的账号信息,用于给运行在Pod里面的进程提供必要的身份证明。
[root@master01 ~]# kubectl create sa es-admin -n kube-system
[root@master01 ~]# kubectl create clusterrolebinding es-admin --clusterrole=cluster-admin --serviceaccount=kube-system:es-admin
当serviceaccout创建时K8s会默认创建对应的secret用于存储账号的token信息
[root@master01 ~]# kubectl get secrets -n kube-system | grep es-admin
2、部署ES集群
es集群采用StatefulSet控制器部署,StatefulSet本质上是Deployment的一种变体,用于部署有状态服务,它所管理的Pod拥有固定的Pod名称,启停顺序。
[root@master01 logs]# kubectl create -f elasticsearch.yaml
查看StatefulSet信息(缩写:sts)
[root@master01 logs]# kubectl get sts -n kube-system
查看es的Pod
[root@master01 ~]# kubectl get pod -n kube-system -o wide | grep elastic
查看es的svc端口
[root@master01 logs]# kubectl get svc -n kube-system
elasticsearch-0 1/1 Running 0 22m
elasticsearch-1 1/1 Running 0 22m
elasticsearch-2 1/1 Running 0 21m
3、部署 Log-Pilot
为了保证集群每个节点都能够采集到日志信息,Log Pilot采用DaemonSet控制器创建,DaemonSet可以保证集群的每个节点都存在一个相同的副本。
[root@master01 logs]# kubectl apply -f log-pilot.yaml
[root@master01 ~]# kubectl get pod -n kube-system | grep log-pilot
log-pilot-c4kvk 1/1 Running 0 2m32s
log-pilot-s78h8 1/1 Running 0 2m32s
log-pilot-xlclz 1/1 Running 0 2m32s
log-pilot-xzwps 1/1 Running 0 2m32s
4、部署 kibana
kibana提供web页面展示,采用Deployment控制器部署(只需要部署一个即可)。
[root@master01 logs]# kubectl apply -f kibana.yaml
[root@master01 logs]# kubectl get deploy -n kube-system kibana 1/1 1 1 82s
[root@master01 logs]# kubectl get ing -n kube-system
NAME CLASS HOSTS ADDRESS PORTS AGE
kibana <none> www.kibana.com 192.168.0.13 80 4m2s
配置域名解析:C:\Windows\System32\drivers\etc
192.168.0.13 www.kibana.com
访问测试:在访问测试时,通过域名与ingress对外暴露的端口进行访问
查看ingress-nginx暴露的端口
[root@master01 logs]# kubectl get svc -n ingress-nginx
NAME TYPE CLUSTER-IP EXTERNAL-IP
ingress-nginx-controller NodePort 10.111.226.197 <none> 80:31681/TCP,443:31150/TCP 22m
访问测试:注意80对应的是HTTP端口,443对应的是HTTPS端口
访问: http://www.kibana.com:31681/ |
---|
![]() |
部署应用并采集日志
[root@master01 ~]# vim elk.yml
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-nginx
namespace: test
spec:
selector:
matchLabels:
app: deploy-nginx
template:
metadata:
labels:
app: deploy-nginx
spec:
#容器
containers:
- name: nginx
image: nginx:1.18.0
ports:
- containerPort: 80
# 环境变量
env:
- name: aliyun_logs_nginx #变量名称开头固定为aliyun_logs(变量名称用于创建kibana索引)
value: "stdout"
- name: aliyun_logs_nginxlogs #变量名称开头固定为aliyun_logs(变量名称用于创建kibana索引)
value: "/var/log/nginx/*"
volumeMounts:
- mountPath: /var/log/nginx/
name: nginxlogs
#数据存储类型
volumes:
- name: nginxlogs
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: svc-nginx
namespace: test
spec:
selector:
app: deploy-nginx
clusterIP: None
type: ClusterIP #service类型
ports:
- port: 80
targetPort: 80
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: deploy-tomcat
namespace: test
spec:
selector:
matchLabels:
app: deploy-tomcat
template:
metadata:
labels:
app: deploy-tomcat
spec:
containers:
- name: tomcat
image: tomcat:8.5-jre10-slim
ports:
- containerPort: 8080
# 环境变量
env:
- name: aliyun_logs_tomcat #变量名称开头固定为aliyun_logs(变量名称用于创建kibana索引)
value: "stdout"
- name: aliyun_logs_tomcatlogs #变量名称开头固定为aliyun_logs(变量名称用于创建kibana索引)
value: "/usr/local/tomcat/logs/*"
# 数据挂载
volumeMounts:
- mountPath: /usr/local/tomcat/logs
name: tomcatlogs
#数据存储类型
volumes:
- name: tomcatlogs
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: svc-tomcat
namespace: test
spec:
selector:
app: deploy-tomcat
clusterIP: None
type: ClusterIP #service类型
ports:
- port: 8080
targetPort: 8080
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: ingress-http #自定义ingress名称
namespace: test
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
kubernetes.io/ingress.class: nginx #注释名称需为nginx(不可省略)
spec:
rules: #定义主机列表
- host: www.nginx.com #自定义域名
http:
paths:
- pathType: Prefix #路径类型
path: "/" #定义站点路径
backend: #定义后端引用的服务
service: #关联service
name: svc-nginx #对应上面创建的service名称
port:
number: 80 #service端口
- host: www.tomcat.com #自定义域名
http:
paths:
- pathType: Prefix #路径类型
path: "/" #定义站点路径
backend: #定义后端引用的服务
service: #关联service
name: svc-tomcat #对应上面创建的service名称
port:
number: 8080 #service端口
创建Pod
[root@master01 ~]# kubectl create -f elk.yml