PV
集群管理员将配置好的那些存储空间引入至K8S集群中,定义成PV (Persistent Volume,持久化卷)
PVC
K8S用户在创建Pod时如果要用到PVC时,必须先创建PVC( 在K8S集群中找一个能符合条件的存储卷PV来用)。注意:PV和PVC是一一对应关系,一旦某个PV被一个PVC占用,那么这个PV就不能再被其他PVC占用,被占用的PV的状态会显示为Bound。PVC创建以后,就相当于一个存储卷,可以被多个 Pod所使用。
PV保留策略有两种:
retain:删除pod,删除pvc,pv状态变为released,不可复用(pv状态必须available才可复用)
优点:防止误删数据
缺点:需要手工清理无用数据,忘记的话,占用存储空间
delete(动态制备):
优点:删除pod,删除pvc的时候,会自动把pv和底层数据删除,节省空间
缺点:容易误删数据
动态卷流程(默认策略delete)
1. 准备NFS服务器
2. 获取NFS 插件文件,上传并解压
[root@master ~]# tar -zxvf 11-nfs-subdir-external-provisioner.tar.gz
1. 设置RBAC权限
Role Based Access Control 基于角色访问控制
rbac.yaml文件是规定角色对应的模块具有哪些权限。默认使用的ns default,我的环境使用的ns是memeda
2. 修改NFS配置文件
3. 创建存储类
必须的,存储类默认使用的保留策略就是delete
4. 创建PVC
不需要创建pv的,pv会随pvc自动创建出来的
[root@master deploy]# kubectl apply -f test-claim.yaml
persistentvolumeclaim/test-claim created
[root@master deploy]# kubectl get pvc
NAME STATUS VOLUME CAPACITY ACCESS MODES STORAGECLASS VOLUMEATTRIBUTESCLASS AGE
test-claim Bound pvc-463b03bc-6c60-4e3f-abe1-e1a98e70b52d 5Gi RWX nfs-client <unset> 3s
[root@master deploy]#
[root@master deploy]# kubectl get pv
NAME CAPACITY ACCESS MODES RECLAIM POLICY STATUS CLAIM STORAGECLASS VOLUMEATTRIBUTESCLASS REASON AGE
pvc-463b03bc-6c60-4e3f-abe1-e1a98e70b52d 5Gi RWX Delete Bound memeda/test-claim nfs-client <unset> 17s
5. 创建pod
删除pod,删除pvc,观察是否自动删除pv和底层数据
Deployment
无角色 前端web网站
一种控制器,在华为云CCE里面:无状态工作负载
deployment 无状态工作负载(dep控制器)
statefulset 有状态工作负载(sta控制器) https://blog.csdn.net/m0_60259116/article/details/140228620(案例mysql主备)
声明式文本:类似于yaml文件,你只需要告诉它最终实现什么结果即可,具体中间底层怎么实现,由集群自己来管理。
命令式文本:类似于编程 c/c++/java,每一步都要告诉程序,下一步怎么执行。
修改副本(三种方式):
deploy可通过yaml或命令行来创建。
1.17及之前kubectl run 名称,这个命令默认创建的是deploy
1.17之后,kubectl run 命令,这个命令默认创建的是pod
副本数修改
在线edit
命令行scale
修改yaml文件
HPA动态伸缩:
手工修改很是麻烦,因为你不知道流量什么时候暴增,什么时候骤减。
创建HPA
[root@master ~]# kubectl autoscale deployment web --min 3 --max 10
horizontalpodautoscaler.autoscaling/web autoscaled
[root@master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web cpu: <unknown>/80% 3 10 0 2s
默认没有指定容器使用cpu计算资源的额度。
[root@kmaster ~]# kubectl edit deployments.apps dep1
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: nginx
resources: {}
-----------------------------------------
spec:
containers:
- image: nginx
imagePullPolicy: IfNotPresent
name: nginx
resources:
requests:
cpu: 500m
[root@master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web cpu: 0%/15% 3 10 3 17s
模拟外部访问压力测试
等待,观察top情况及pod数量。
[root@master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web cpu: 14%/15% 3 10 7 9m12s
[root@master ~]# kubectl get hpa
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
web Deployment/web cpu: 0%/15% 3 10 3 15m
镜像滚动更新
1.通过edit修改
2.通过命令行修改
1. 通过yaml文件
修改文件后,通过apply应用即可
其他控制器
statefulset有状态工作负载
1. 创建服务(无头服务,本质还是svc) 一些后端服务,数据库啊什么什么的
2. 配置动态卷(delete)
创建NFS控制器
kubectl apply -f deployment.yaml
授权
kubectl apply -f rbac.yaml
创建存储类
kubectl apply -f class.yaml
创建pvc
kubectl apply -f test-claim.yaml
3. 创建statefuleset
[root@master ~]# cat sta.yaml
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: nginx
spec:
serviceName: nginx # headless service的名称
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: container-0
image: uhub.service.ucloud.cn/iecloud.ljh/nginx
resources:
limits:
cpu: 100m
memory: 200Mi
requests:
cpu: 100m
memory: 200Mi
volumeMounts: # Pod挂载的存储
- name: data
mountPath: /usr/share/nginx/html # 存储挂载到/usr/share/nginx/html
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 1Gi
storageClassName: nfs-client # 持久化存储的类型
4. 查看
[
这时候,可以随机删除pod
观察存储状态
底层存储也对应有
这时候,删除任意一个pod,都会创建同名pod,加载对应的pvc,pod对应的数据也不会变。
所以statefulset的有状态就体现在这里,各是各的,有角色和网络id之分,使用的底层存储也不一样。
daemonset
守护进程集 (无副本机制)
DaemonSet(守护进程集)在集群的每个节点上运行一个Pod,且保证只有一个Pod,非常适合一些系统层面的应用,例如日志收集、资源监控等,这类应用需要每个节点都运行,且不需要太多实例,一个比较好的例子就是Kubernetes的kube-proxy。
DaemonSet跟节点相关,如果节点异常,也不会在其他节点重新创建。
master上有污点。
service
pod创建好之后,怎么访问?
[root@master ~]# vim pod1.yaml
[root@master ~]# cat pod1.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: pod1
name: pod1
spec:
containers:
- image: uhub.service.ucloud.cn/iecloud.ljh/nginx
name: pod1
ports:
- containerPort: 80
hostPort: 5000
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master ~]# kubectl apply -f pod1.yaml
基本的东西要记住:
ssh 22
http 80
https(SSL)443
mysql 3306
apache (httpd)网站根目录:/var/www/html
nginx网站根目录:/usr/share/nginx/html
确定pod1是在哪个节点上运行的
现在问题是:如果pod太多,对每个pod都设置一个端口,就会导致安全问题,也不方便去管理。这时候k8s引入了一个资源对象,叫Service(SVC)
SVC就是一个负载均衡器。
创建一个SVC
为这个deploy创建一个svc
[root@master ~]# kubectl expose deployment web --name svc1 --port 80
service/svc1 exposed
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
nginx ClusterIP None <none> 80/TCP 18h
svc1 ClusterIP 10.99.216.224 <none> 80/TCP 3s
注意这个clusterIP地址,集群内部使用的ip地址,只能集群内部去访问。
测试访问,观察负载均衡
SVC是怎么知道把流量分发到后端哪些pod呢?
[root@master ~]# kubectl get svc -o wide
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
nginx ClusterIP None <none> 80/TCP 18h app=nginx
svc1 ClusterIP 10.99.216.224 <none> 80/TCP 5m49s app=web
SVC是通过SELECTOR这个字段里面显示的标签进行匹配的。而不是通过SVC本身自己的标签匹配的。
[root@master ~]# kubectl edit svc svc1
如果SVC存在多个匹配标签,它只会最后一个生效。
服务的发现
一共有3种方式
ClusterIP方式
为mysql创建svc
[root@master ~]# kubectl expose pod db --name dbsvc --port 3306 --target-port 3306 (默认svc类型就是Cluster IP)
service/dbsvc exposed
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dbsvc ClusterIP 10.102.176.113 <none> 3306/TCP 2s
创建博客,直接使用clusterip即可
value: 10.102.176.113
为blog创建svc(类型NodePort)
[root@master ~]# kubectl expose pod blog --name blogsvc --port 80 --target-port 80 --type NodePort
service/blogsvc exposed
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
blogsvc NodePort 10.99.124.244 <none> 80:32503/TCP 3s
打开浏览器,输入任意节点ip加端口号32503即可
变量方式
当你在集群环境中,创建一个pod,那么这个pod里面就包含了当前集群中所有svc的变量信息。
value: $(DBSVC_SERVICE_HOST)
DNS方式(推荐这种方式)
直接指定svc的名字即可。
value: dbsvc
dns原理
当创建blog的时候,发现blog里面指定的数据库的主机是一个svc(名字),blog是如何通过这个名字找到对应的ip地址呢?
你去找nameserver 10.96.0.10,它知道。
这个10.96.0.10是谁?它是一个SVC的ip地址。
[root@master ~]# kubectl get svc -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 2d19h
根据之前学过的selector标签,可以确定这个svc管理哪些pod的
[root@master ~]# kubectl get svc -o wide -n kube-system
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
kube-dns ClusterIP 10.96.0.10 <none> 53/UDP,53/TCP,9153/TCP 2d19h k8s-app=kube-dns
哪些pod是这个标签呢?
[root@master ~]# kubectl get pod -l k8s-app=kube-dns -n kube-system
NAME READY STATUS RESTARTS AGE
coredns-7b5944fdcf-6wdgd 1/1 Running 3 (108m ago) 2d19h
coredns-7b5944fdcf-tcsvq 1/1 Running 3 (108m ago) 2d19h
这俩cordns的pod就是集群内的dns解析服务器。
每创建一个svc,svc都会把自己往dns服务器上去注册,名字和ip地址都会记录到dns服务里面。
这里面有个小细节:我们svc和pod都是在memeda命名空间里面,如果我在default命令空间中,创建blog和blogsvc,那么通过memeda命名空间的(dbsvc)能否访问呢?
value: dbsvc.memeda
[root@master ~]# kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d19h
svc01 NodePort 10.97.21.101 <none> 80:30829/TCP 4s
dns可以跨命名空间进行svc的访问,但是clusterip和变量是没办法进行跨命名空间访问的。
服务的发布
NodePort
Ingress
Service是基于四层TCP和UDP协议转发的,而Ingress可以基于七层的HTTP和HTTPS协议转发,可以通过域名和路径做到更细粒度的划分,如下图所示。
Ingress-Service
1. 创建3个pod
2. 创建svc
3. 修改网站内容
4. ingress控制器
官方有帮我们做好,直接下载对应的镜像,运行yaml文件,它就会帮我们把所有的环境搭建好。
ingress-nginx-controller-77d8b9c46b-zzsmv 1/1 Running 0 2m26s
5. 创建NodePort SVC
因为默认本应该使用LoadBalancer类型的EXTERNALIP地址去访问,可是我们当前环境并没有去配置,所以单独来创建一个SVC使用就行了。
6. 创建ingress类
这个类其实已经存在了,已经帮我们创建好了。这个类管理和操作路由规则的,让这些规则生效的。
添加ingressclass一行
annotations:
ingressclass.kubernetes.io/is-default-class: "true"
kubectl.kubernetes.io/last-applied-configuration: |
7. 创建路由规则
---- ------ ---- ---- -------
Normal Sync 46s nginx-ingress-controller Scheduled for sync
测试
secret和configmap
secret
涉及一些变量参数值、密码值等类似于这种,特别适合用secret进行封装。
将你这些值封装成一个secret(k8s里面的一个对象资源),未来创建mysql的时候需要调用加载密码,直接调用secret里面的值即可。
[root@master ~]# kubectl create secret generic --help |grep from
--from-env-file=[]:
--from-file=[]:
--from-literal=[]:
[root@master ~]# kubectl create secret generic sec01 --from-literal=aaa=111 --from-literal=MYSQL_ROOT_PASSWOR=redhat
secret/sec01 created
[root@master ~]# kubectl get secrets
NAME TYPE DATA AGE
sec01 Opaque 2 4s
[root@master ~]# echo 11111 > index.html
[root@master ~]#
[root@master ~]# ls index.html
index.html
[root@master ~]# cat index.html
11111
[root@master ~]# kubectl create secret generic sec02 --from-file=index.html
[root@master ~]# kubectl get secrets
NAME TYPE DATA AGE
sec01 Opaque 2 6m38s
sec02 Opaque 1 13s
[root@master ~]# vim name.txt
[root@master ~]# cat name.txt
name1=zhangsan
name2=lisi
name3=wangwu
MYSQL_ROOT_PASSWORD=redhat
[root@master ~]#
[root@master ~]# kubectl create secret generic sec03 --from-env-file=name.txt
secret/sec03 created
[root@master ~]# kubectl get secrets
NAME TYPE DATA AGE
sec01 Opaque 2 8m30s
sec02 Opaque 1 2m5s
sec03 Opaque 4 5s
使用secret创建mysql容器
[root@master ~]# kubectl create secret generic sec01 --from-literal=dbpass=redhat
secret/sec01 created
[root@master ~]# kubectl get secrets
NAME TYPE DATA AGE
sec01 Opaque 1 3s
[root@master ~]# vim db.yaml
[root@master ~]# cat db.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: db
name: db
spec:
containers:
- env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: sec01
key: dbpass
image: registry.cn-hangzhou.aliyuncs.com/cloudcs/mysql:888
imagePullPolicy: IfNotPresent
name: db
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master ~]# kubectl apply -f db.yaml
pod/db created
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 2s
pod1 1/1 Running 0 101m
pod2 1/1 Running 0 101m
pod3 1/1 Running 0 101m
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
db 1/1 Running 0 35s 10.244.104.16 node2 <none> <none>
pod1 1/1 Running 0 101m 10.244.104.25 node2 <none> <none>
pod2 1/1 Running 0 101m 10.244.104.14 node2 <none> <none>
pod3 1/1 Running 0 101m 10.244.104.18 node2 <none> <none>
[root@master ~]# mysql -uroot -predhat -h 10.244.104.16
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.27 MySQL Community Server - GPL
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
+--------------------+
4 rows in set (0.003 sec)
MySQL [(none)]>
加载多个参数
[root@master ~]#kubectl create secret generic sec02 --from-literal dbname=wordpress
[root@master ~]# cat db.yaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: null
labels:
run: db
name: db
spec:
containers:
- env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: sec01
key: dbpass
- name: MYSQL_DATABASE
valueFrom:
secretKeyRef:
name: sec02
key: dbname
image: registry.cn-hangzhou.aliyuncs.com/cloudcs/mysql:888
imagePullPolicy: IfNotPresent
name: db
resources: {}
dnsPolicy: ClusterFirst
restartPolicy: Always
status: {}
[root@master ~]# kubectl apply -f db.yaml
pod/db created
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 2s
pod1 1/1 Running 0 107m
pod2 1/1 Running 0 107m
pod3 1/1 Running 0 107m
[root@master ~]# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
db 1/1 Running 0 8s 10.244.104.17 node2 <none> <none>
pod1 1/1 Running 0 107m 10.244.104.25 node2 <none> <none>
pod2 1/1 Running 0 107m 10.244.104.14 node2 <none> <none>
pod3 1/1 Running 0 107m 10.244.104.18 node2 <none> <none>
[root@master ~]# mysql -uroot -predhat -h 10.244.104.17
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.27 MySQL Community Server - GPL
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MySQL [(none)]> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| wordpress |
+--------------------+
5 rows in set (0.004 sec)
MySQL [(none)]>
configmap
特别适合封装参数文件、配置文件等。和secret不同的是,configmap不加密。它是明文显示的。
举个例子
创建一个web的deployment控制器,副本数3个。
未来不管通过svc负载均衡到哪个pod上,上层应用看到的内容都一样。
可是,现在有个需求,我要更新nginx的镜像了。
可是问题来了,当你更新后,原来的网站内容还在吗?
怎么解决?通过封装configmap就可以解决
[root@master ~]# cat index.html
11111
[root@master ~]#
[root@master ~]# kubectl create configmap cm666 --from-file index.html
configmap/cm666 created
[root@master ~]# kubectl get cm
NAME DATA AGE
cm1 1 9m51s
cm666 1 3s
kube-root-ca.crt 1 122m
[root@master ~]# cat web.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
creationTimestamp: null
labels:
app: web
name: web
spec:
replicas: 3
selector:
matchLabels:
app: web
strategy: {}
template:
metadata:
creationTimestamp: null
labels:
app: web
spec:
containers:
- image: swr.cn-north-4.myhuaweicloud.com/tianmeili/nginx:1.0
name: nginx
volumeMounts:
- name: foo
mountPath: "/usr/share/nginx/html/"
readOnly: true
resources: {}
volumes:
- name: foo
configMap:
name: cm666
status: {}
[root@master ~]# kubectl apply -f web.yaml
[root@master ~]# kubectl get pod
NAME READY STATUS RESTARTS AGE
db 1/1 Running 0 18m
pod1 1/1 Running 0 126m
pod2 1/1 Running 0 125m
pod3 1/1 Running 0 125m
web-5cff98694b-9gngr 1/1 Running 0 3s
web-5cff98694b-dp9fg 1/1 Running 0 3s
web-5cff98694b-l2254 1/1 Running 0 3s
[root@master ~]# kubectl exec -ti web-5cff98694b-9gngr -- sh -c 'cat /usr/share/nginx/html/index.html'
11111
[root@master ~]# kubectl exec -ti web-5cff98694b-9gngr -- sh -c 'nginx -v'
nginx version: nginx/1.19.6
[root@master ~]# kubectl set image deploy web nginx=registry.cn-hangzhou.aliyuncs.com/cloudcs/nginx:888