- 本文实验环境基于上篇文章手把手从零开始搭建k8s集群超详细教程
- 本文根据B站课程云原生Java架构师的第一课K8s+Docker+KubeSphere+DevOps学习总结而来
k8s核心组件介绍
- 1. Namespace
- 2. Pod
- 1. pod相关命令
- 2. 实例——创建一个包含redis容器的pod
- 3. Deployments
- 1. 多副本
- 2. 扩缩容
- 3. 自愈&故障转移
- 4. 滚动更新
- 5. 版本回退
- 更多
- 4. Service
- 1. service相关命令
- 2. 实例
- 3. ClusterIP & NodePort模式
- 5. Ingress
- 6. 存储抽象
- 1. 什么是存储抽象
- 2. 目录挂载——原生方式
- 3. 目录挂载——PV&PVC方式
- 1. 创建pv池
- 2. PVC的创建与绑定
- 3. 补充
- 4. 配置文件挂载——ConfigMap
- 5. 敏感信息存储——Secret
k8s中创建资源的方式有两种,一种是通过对应资源的专用命令来创建,一种是以yaml
配置文件的形式来创建,其中以yaml配置文件来创建/删除资源的通用命令如下:
kubectl apply -f 文件名 # 创建资源
kubectl delete -f 文件名 # 删除资源
1. Namespace
Namespace
:名称空间,用来对集群资源进行隔离划分(默认只隔离资源、不隔离网络)
当我们使用kubectl get pods -A
查看所有应用时,也可以看到对应归属的命名空间
如果我们使用kubect get pods
命令,将看不到任何应用,因为该命令是查看默认default名称空间内的应用
📄 命名空间相关命令
# 查看所有的名称空间
[root@k8s-master ~]# kubectl get ns
NAME STATUS AGE
default Active 21h
kube-node-lease Active 21h
kube-public Active 21h
kube-system Active 21h
kubernetes-dashboard Active 20h# 创建名称空间
kubectl create ns hello# 删除名称空间(会将该名称空间下的所有资源全部删除)
kubectl delete ns hello
除了用命令行的形式来创建名称空间,我们还可以用yaml
配置文件的形式来创建
apiVersion: v1 # 版本号
kind: Namespace # 资源类型
metadata: # 元数据name: hello
编写以上hello.yaml
文件,然后用以下命令来创建/删除hello名称空间
kubectl apply -f hello.yaml
kubectl delete -f hello.yaml
2. Pod
Pod
是运行中的一组容器,是kubernetes中应用的最小应用单位在k8s没有出现之前,单个应用都是以容器的方式运行,由doker提供容器时运行环境,K8s的目的就是将这些容器管理起来,将一个或多个容器封装起来成为一个 pod
1. pod相关命令
# 创建一个pod,--image用于指定创建pod使用的镜像(因为pod中就是容器)
kubectl run pod名称 --image=镜像名# 查看default名称空间的Pod
kubectl get pod# 查看pod描述
kubectl describe pod pod名称# 删除
kubectl delete pod pod名称1 [pod名称2...] [-n 所在命名空间]# 查看pod的运行日志
kubectl logs pod名称# 查看pod更详细信息(可以看到对于每个Pod,k8s都会分配一个ip,集群中的任意一个机器以及任意的应用都能通过Pod分配的ip来访问这个Pod)
kubectl get pod -owide# 进入到pod容器中
kubectl exec -it pod名称 -- /bin/bash
2. 实例——创建一个包含redis容器的pod
创建一个包含redis容器的pod
kubectl run mypod --image=redis
然后我们用kubectl describe pod mypod
可以查看创建pod的详细信息:
其中Events模块可以看到创建pod的流程,首先在default命名空间封装mypod分配到k8s-node2机器,然后拉取redis镜像并创建启动mypod
由于pod本质还是以容器的方式运行,只不过k8s做了封装,所以我们仍然可以通过docker ps
命令来查看到mypod容器,由于mypod被分配到k8s-node2机器,所以我们可以在该机器上查看到
此外,我们还可以通过kubectl get pod -owide
查看pod的更详细信息,假设这里创建一个包含nginx容器的pod
kubectl run mynginx --image=nginx# 可以看到对于每个pod,k8s都为其分配了一个ip地址
[root@k8s-master ~]# kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mynginx 1/1 Running 0 119s 192.168.169.134 k8s-node2 <none> <none># 通过pod的ip+端口号进行访问
[root@k8s-master ~]# curl 192.168.169.134
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p><p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p><p><em>Thank you for using nginx.</em></p>
</body>
</html>
可以看到对于每个pod,k8s都为其分配了一个ip地址,我们可以通过pod的ip+端口号进行访问
由于pod本质上也是以容器的方式运行,我们可以通过kubectl exec
命令进入到容器中,如下所示:我们进入到mynginx中修改nginx的默认页面
# 进入到mynginx中
[root@k8s-master ~]# kubectl exec -it mynginx -- /bin/bash
root@mynginx:/# ls
bin dev docker-entrypoint.sh home lib64 mnt proc run srv tmp var
boot docker-entrypoint.d etc lib media opt root sbin sys usr
root@mynginx:/# cd /usr/share/nginx/html/
root@mynginx:/usr/share/nginx/html# ls
50x.html index.html
root@mynginx:/usr/share/nginx/html# echo "hello">index.html
root@mynginx:/usr/share/nginx/html# exit
exit
[root@k8s-master ~]# kubectl get pod -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
mynginx 1/1 Running 0 10m 192.168.169.134 k8s-node2 <none> <none>
[root@k8s-master ~]# curl 192.168.169.134
hello
可以看到页面修改生效了,不仅在当前master机器可以对该ip进行访问,在集群中的任意机器都可以访问该ip
这里的ip地址所在网段就是我们前面初始化主节点时配置的--pod-network-cidr
参数
📄 同样,我们可以以yaml文件的方式来创建pod,用kubectl apply -f
执行以下文件即可创建包含nginx和tomcat两个容器的pod
apiVersion: v1
kind: Pod
metadata:labels:run: mypodname: mypod
spec:containers:- image: nginxname: nginx- image: tomcat:8.5.68name: tomcat
3. Deployments
Deployments
:部署应用,用于控制Pod,使Pod拥有多副本、自愈、扩缩容等能力,接下来我们将一一演示
📄 常用命令
# 创建一个deployment
kubectl create deployment deployment名称 --image 镜像名# 删除deployment
kubectl delete deployment deployment名称# 查看所有deployment
kubectl get deployment [-A] [-owide]
1. 多副本
一个部署可以包含多个pod副本,在创建Deployment时,我们可以加上--replicas
参数来指定pod的个数
比如这里创建一个包含三个nginx容器的应用部署my-dep
# 创建一个deployment应用部署my-dep,控制3个nginx pod
kubectl create deployment my-dep --image=nginx --replicas=3
# 配置文件方式
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: my-depname: my-dep
spec:replicas: 3selector:matchLabels:app: my-deptemplate:metadata:labels:app: my-depspec:containers:- image: nginxname: nginx
2. 扩缩容
当我们创建一个deployment来部署应用时,假设在两台机器分别部署了一个pod,但此时应用请求流量过大,两台机器无法负载均衡处理这些请求,此时我们通过k8s的kubectl scale
命令一键给多个机器部署该pod,这个过程叫做扩容,与之相反,当应用高峰过去后,我们也可以使用kubectl scale
命令下线几台机器上的pod,这个过程叫做缩容,可以腾出不需要的资源
这个过程就叫做扩缩容,除了用命令来控制之外,k8s还能做到动态的扩缩容,根据应用情况自动完成上述操作
# 将上述创建的my-dep从3个pod扩容到5个
kubectl scale deployment/my-dep --replicas=5# 将my-dep中的pod从5个缩容到2个
kubectl scale deployment/my-dep --replicas=2
也可以通过以下命令来修改创建deployment的yaml文件中的replicas
字段来扩缩容
# 修改my-dep应用部署yaml配置文件中的replicas
kubectl edit deployment my-dep
3. 自愈&故障转移
当我们通过deployment来部署应用时,应用在运行过程中可能会碰到服务器停机、容器崩溃、pod被删除等情况,此时k8s的自愈&故障转移能力就能很好的监控以及避免上述情况带来的问题
接下来演示一下Deployments最基础的自愈能力,我们对比分别使用以下两个命令创建容器
# 方式一:直接创建pod运行
kubectl run mynginx --image=nginx# 方式二:通过创建deployment来部署pod运行
kubectl create deployment mytomcat --image=tomcat
此时,我们开启另一个终端实时监控默认命名空间的所有pod
watch -n -1 kubectl get pod # 间隔1s查看所有pod
假如此时我们删除这两个pod,mynginx
和mytomcat-58b8488d44-xfgff
再查看监控,可以发现mynginx
直接被删除了,而通过deployment创建的mytomcat-58b8488d44-xfgff
也被删除,但是随后又自动启动了一个新的tomcat pod,这就是deployment最常见的自愈能力,当deployment中的某个pod挂掉时,它会自动在其他机器再启动一个一模一样的pod
当然还会出现另一种情况,如果运行着pod的某个机器突然断电了,没法在集群中提供服务了,此时k8s会提供故障转移功能,在另一个机器拉起同样的一份pod提供服务,我们以上述创建包含三个nginx容器的应用部署my-dep
为例进行演示
补充:由于运行的pod本质上就是一个容器,所以我们可以通过docker ps命令来查看到,比如这里我们查看my-dep
中的一个nginx容器
如果我们在青云控制台将k8s-node1
机器重启,再次查看所有pod可以看到部署在该机器上的pod容器状态变更为Completed
过了一会,当k8s-node1这台机器重启后,可以看到容器自动重启了一次
但是如果我们直接关机node1机器,等待5分钟后,我们用kubectl get pod -w
实时监控,可以看到停止的pod已结束,而是在另一台node2机器重启
总结来说,deployment就是来保证pod数量不被各种意外情况影响
4. 滚动更新
假如当前deployment中的pod版本要从v1升级到v2,但此时用户仍然不断的访问应用,此时怎么保证更新能不间断对用户的处理过程呢?
k8s的滚动更新的思想是先启动一个v2版本的pod,等该新版本的pod运行成功了,就将对应老版本的pod下线
以此类推,每个pod的更新都是新版本的pod启动成功再将老版本下线,实现滚动更新的效果,更新过程中无需停机维护,一个pod更新完了没有问题才会更新下一个,避免了以前一次性全部更新成新版本杀死老版本而新版本不可用的问题
📄 实现滚动更新的命令
# 将pod中的镜像nginx更新版本,--record代表记录此次版本更新
kubectl set image deployment/my-dep nginx=nginx:1.16.1 --recordkubectl rollout status deployment/my-dep
或者通过如下方式修改deployment的配置文件中的image信息实现更新
kubectl edit deployment/my-dep
5. 版本回退
# 查看deployment版本历史记录
kubectl rollout history deployment/my-dep
# 查看某个版本历史详情
kubectl rollout history deployment/my-dep --revision=2# 回滚(回到上次)
kubectl rollout undo deployment/my-dep
# 回滚(回到指定版本)
kubectl rollout undo deployment/my-dep --to-revision=2
实例演示:
更多
除了Deployment可以部署应用外,类似的,k8s还有StatefulSet
、DaemonSet
、Job
等类型资源。我们都称为工作负载,不同工作负载的使用方向如下所示,工作中我们按需选择即可
总结:在k8s系统中,虽然pod是应用的真正载体,但我们不直接部署pod,而是使用上述工作负载来控制pod,让每个pod都具有更强大的功能
4. Service
Service
:一组 Pods 公开为网络服务的抽象方法,用于pod的服务发现与负载均衡假设我们以前后端分离的方式开发了一个后台管理系统应用,我们将后端通过deployment部署了三份pod位于不同的机器上,此时前端需要请求后端api,但由于每个pod都有自己的一个ip地址,那前端请求的地址到底是三台pod中的哪个呢?为了解决这个问题,k8s提供了
Service
统一对所有pod暴露一个对外的公共地址,这样前端只需要请求该service地址即可。此外,service还提供了对于pod的服务发现与负载均衡的功能,负载均衡就是service均匀的分担前端来的请求到每一个pod,服务发现就是假如deployments中pod宕机或新增pod,service能及时发现,将请求交给其他pod处理
1. service相关命令
# 查看所有service
kubectl get service# 删除service
kubectl delete service# 使用标签检索Pod
kubectl get pod -l app=标签名
重要:暴露一个deployee
# 暴露deployee内容器的80端口到service的8000端口(集群内使用service的ip:port即可负载均衡的任意访问)
# 还可以通过域名的方式进行访问,域名的默认名称为`服务名.所在名称空间.svc`
kubectl expose deployment my-dep --port=8000 --target-port=80
同样也可以用配置文件的方式创建service服务
apiVersion: v1
kind: Service
metadata:labels:app: my-depname: my-dep
spec:selector:app: my-depports:- port: 8000protocol: TCPtargetPort: 80
2. 实例
这里创建一个Deployment
包含三个nginx实例,然后分别进入容器内修改其默认的页面更改为hello 1
、hello 2
、hello 3
[root@k8s-master ~]# kubectl create deployment my-nginx --image=nginx --replicas=3
deployment.apps/my-nginx created
[root@k8s-master ~]# kubectl get pods -owide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
my-nginx-6b74b79f57-79lk5 1/1 Running 0 49s 192.168.36.75 k8s-node1 <none> <none>
my-nginx-6b74b79f57-cxzct 1/1 Running 0 49s 192.168.36.76 k8s-node1 <none> <none>
my-nginx-6b74b79f57-gf2sh 1/1 Running 0 49s 192.168.36.74 k8s-node1 <none> <none>
[root@k8s-master ~]# kubectl exec -it my-nginx-6b74b79f57-79lk5 -- /bin/bash
root@my-nginx-6b74b79f57-79lk5:/# cd /usr/share/nginx/html
root@my-nginx-6b74b79f57-79lk5:/usr/share/nginx/html# echo "hello 1" > index.html
root@my-nginx-6b74b79f57-79lk5:/usr/share/nginx/html# exit
exit
[root@k8s-master ~]# kubectl exec -it my-nginx-6b74b79f57-cxzct -- /bin/bash
root@my-nginx-6b74b79f57-cxzct:/# cd /usr/share/nginx/html
root@my-nginx-6b74b79f57-cxzct:/usr/share/nginx/html# echo "hello 2" > index.html
root@my-nginx-6b74b79f57-cxzct:/usr/share/nginx/html# exit
exit
[root@k8s-master ~]# kubectl exec -it my-nginx-6b74b79f57-gf2sh -- /bin/bash
root@my-nginx-6b74b79f57-gf2sh:/# cd /usr/share/nginx/html/
root@my-nginx-6b74b79f57-gf2sh:/usr/share/nginx/html# echo "hello 3" > index.html
root@my-nginx-6b74b79f57-gf2sh:/usr/share/nginx/html# exit
exit
然后我们使用一下命令给my-nginx
创建一个service
暴露以上三个容器的80端口
# 创建一个service—mydep,将三个nginx容器的80端口映射到my-dep的8000端口
[root@k8s-master ~]# kubectl expose deployment my-nginx --port=8000 --target-port=80
service/my-nginx exposed
[root@k8s-master ~]# kubectl get service
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 6d3h
my-nginx ClusterIP 10.96.9.101 <none> 8000/TCP 7s
[root@k8s-master ~]# curl 10.96.9.101:8000
hello 1
[root@k8s-master ~]# curl 10.96.9.101:8000
hello 2
[root@k8s-master ~]# curl 10.96.9.101:8000
hello 1
[root@k8s-master ~]# curl 10.96.9.101:8000
hello 3
[root@k8s-master ~]# curl 10.96.9.101:8000
hello 2
然后可以通过kubectl get service
命令查看所有服务,然后我们直接访问my-nginx
对应服务的ip:8000即可访问到nginx容器,且service默认为我们做了负载均衡处理
那service是如何判断将哪些pod暴露出来的呢,是因为每个pod在部署期间绑定了一个标签,我们可以通过如下后缀查看到
[root@k8s-master ~]# kubectl get pod --show-labels
NAME READY STATUS RESTARTS AGE LABELS
my-nginx-6b74b79f57-79lk5 1/1 Running 0 80m app=my-nginx,pod-template-hash=6b74b79f57
my-nginx-6b74b79f57-cxzct 1/1 Running 0 80m app=my-nginx,pod-template-hash=6b74b79f57
my-nginx-6b74b79f57-gf2sh 1/1 Running 0 80m app=my-nginx,pod-template-hash=6b74b79f57
service就是根据标签来选择需要暴露的一组pods
注意:除了通过service的ip:port方式访问之外,还可以在pod内部(注意只能在集群pod内部)通过域名的方式进行访问,域名的默认名称为服务名.所在名称空间.svc
[root@k8s-master ~]# curl my-nginx.default.svc:8000
curl: (6) Could not resolve host: my-nginx.default.svc; Unknown error
# 在pod内部通过service域名方式访问
[root@k8s-master ~]# kubectl exec -it my-nginx-6b74b79f57-79lk5 -- /bin/bash
root@my-nginx-6b74b79f57-79lk5:/# curl my-nginx.default.svc:8000
hello 1
root@my-nginx-6b74b79f57-79lk5:/# curl my-nginx.default.svc:8000
hello 3
root@my-nginx-6b74b79f57-79lk5:/# curl my-nginx.default.svc:8000
hello 2
3. ClusterIP & NodePort模式
以上我们创建service的时候没有通过--type
指令指定模式,默认就是ClusterIP
模式,但这种模式只能在集群内部进行访问
# 使用ClusterIP模式创建service,等同于没有--type的
kubectl expose deployment my-dep --port=8000 --target-port=80 --type=ClusterIP
apiVersion: v1
kind: Service
metadata:labels:app: my-depname: my-dep
spec:ports:- port: 8000protocol: TCPtargetPort: 80selector:app: my-deptype: ClusterIP
如果我们想在集群外,公网上也可以访问,就需要使用NodePort
模式,可以看到如果指定NodePort方式创建service默认分配了一个对外端口,默认范围为30000~32767
# 使用NodePort模式创建service
kubectl expose deployment my-nginx --port=8000 --target-port=80 --type=NodePort
apiVersion: v1
kind: Service
metadata:labels:app: my-depname: my-dep
spec:ports:- port: 8000protocol: TCPtargetPort: 80selector:app: my-deptype: NodePort
通过此种方式就可以直接通过服务器公网ip:端口号进行访问
5. Ingress
Ingress
是整个集群流量的统一网关入口,K8s集群中可能会部署很多service服务,这些服务service需要对外提供功能,所有的请求流量都首先需要通过Ingress这一层,由Ingress负责将流量打给哪些服务比如这里有三台机器,现在要部署一个商城应用,其中使用deployments a来部署订单服务,对应两个pod;deployments b来部署用户服务,对应三个pods;deployments c来部署商品服务,也对应三个pod,这些pods组成了pod网络。在这三个deployments上也对应着三个service、也就是订单服务、用户服务、商品服务,形成了service网络,用于处理pod网络的中的服务发现与负载均衡。不仅如此,K8s在service基础上提供了一个统一的网关入口
Ingress
,底层就是一个nginx来帮我们负载均衡,所有的流量都打给Ingress,由其来判断交给哪个service进行处理。
k8s默认未安装Ingress没我们首先进行安装
# 安装Ingress
wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v0.47.0/deploy/static/provider/baremetal/deploy.yaml
或者用以下yaml配置文件进行安装
apiVersion: v1
kind: Namespace
metadata:name: ingress-nginxlabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginx---
# Source: ingress-nginx/templates/controller-serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: controllername: ingress-nginxnamespace: ingress-nginx
automountServiceAccountToken: true
---
# Source: ingress-nginx/templates/controller-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: controllername: ingress-nginx-controllernamespace: ingress-nginx
data:
---
# Source: ingress-nginx/templates/clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmname: ingress-nginx
rules:- apiGroups:- ''resources:- configmaps- endpoints- nodes- pods- secretsverbs:- list- watch- apiGroups:- ''resources:- nodesverbs:- get- apiGroups:- ''resources:- servicesverbs:- get- list- watch- apiGroups:- extensions- networking.k8s.io # k8s 1.14+resources:- ingressesverbs:- get- list- watch- apiGroups:- ''resources:- eventsverbs:- create- patch- apiGroups:- extensions- networking.k8s.io # k8s 1.14+resources:- ingresses/statusverbs:- update- apiGroups:- networking.k8s.io # k8s 1.14+resources:- ingressclassesverbs:- get- list- watch
---
# Source: ingress-nginx/templates/clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmname: ingress-nginx
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: ingress-nginx
subjects:- kind: ServiceAccountname: ingress-nginxnamespace: ingress-nginx
---
# Source: ingress-nginx/templates/controller-role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: controllername: ingress-nginxnamespace: ingress-nginx
rules:- apiGroups:- ''resources:- namespacesverbs:- get- apiGroups:- ''resources:- configmaps- pods- secrets- endpointsverbs:- get- list- watch- apiGroups:- ''resources:- servicesverbs:- get- list- watch- apiGroups:- extensions- networking.k8s.io # k8s 1.14+resources:- ingressesverbs:- get- list- watch- apiGroups:- extensions- networking.k8s.io # k8s 1.14+resources:- ingresses/statusverbs:- update- apiGroups:- networking.k8s.io # k8s 1.14+resources:- ingressclassesverbs:- get- list- watch- apiGroups:- ''resources:- configmapsresourceNames:- ingress-controller-leader-nginxverbs:- get- update- apiGroups:- ''resources:- configmapsverbs:- create- apiGroups:- ''resources:- eventsverbs:- create- patch
---
# Source: ingress-nginx/templates/controller-rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: controllername: ingress-nginxnamespace: ingress-nginx
roleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: ingress-nginx
subjects:- kind: ServiceAccountname: ingress-nginxnamespace: ingress-nginx
---
# Source: ingress-nginx/templates/controller-service-webhook.yaml
apiVersion: v1
kind: Service
metadata:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: controllername: ingress-nginx-controller-admissionnamespace: ingress-nginx
spec:type: ClusterIPports:- name: https-webhookport: 443targetPort: webhookselector:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/component: controller
---
# Source: ingress-nginx/templates/controller-service.yaml
apiVersion: v1
kind: Service
metadata:annotations:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: controllername: ingress-nginx-controllernamespace: ingress-nginx
spec:type: NodePortports:- name: httpport: 80protocol: TCPtargetPort: http- name: httpsport: 443protocol: TCPtargetPort: httpsselector:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/component: controller
---
# Source: ingress-nginx/templates/controller-deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: controllername: ingress-nginx-controllernamespace: ingress-nginx
spec:selector:matchLabels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/component: controllerrevisionHistoryLimit: 10minReadySeconds: 0template:metadata:labels:app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/component: controllerspec:dnsPolicy: ClusterFirstcontainers:- name: controllerimage: registry.cn-hangzhou.aliyuncs.com/lfy_k8s_images/ingress-nginx-controller:v0.46.0imagePullPolicy: IfNotPresentlifecycle:preStop:exec:command:- /wait-shutdownargs:- /nginx-ingress-controller- --election-id=ingress-controller-leader- --ingress-class=nginx- --configmap=$(POD_NAMESPACE)/ingress-nginx-controller- --validating-webhook=:8443- --validating-webhook-certificate=/usr/local/certificates/cert- --validating-webhook-key=/usr/local/certificates/keysecurityContext:capabilities:drop:- ALLadd:- NET_BIND_SERVICErunAsUser: 101allowPrivilegeEscalation: trueenv:- name: POD_NAMEvalueFrom:fieldRef:fieldPath: metadata.name- name: POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespace- name: LD_PRELOADvalue: /usr/local/lib/libmimalloc.solivenessProbe:failureThreshold: 5httpGet:path: /healthzport: 10254scheme: HTTPinitialDelaySeconds: 10periodSeconds: 10successThreshold: 1timeoutSeconds: 1readinessProbe:failureThreshold: 3httpGet:path: /healthzport: 10254scheme: HTTPinitialDelaySeconds: 10periodSeconds: 10successThreshold: 1timeoutSeconds: 1ports:- name: httpcontainerPort: 80protocol: TCP- name: httpscontainerPort: 443protocol: TCP- name: webhookcontainerPort: 8443protocol: TCPvolumeMounts:- name: webhook-certmountPath: /usr/local/certificates/readOnly: trueresources:requests:cpu: 100mmemory: 90MinodeSelector:kubernetes.io/os: linuxserviceAccountName: ingress-nginxterminationGracePeriodSeconds: 300volumes:- name: webhook-certsecret:secretName: ingress-nginx-admission
---
# Source: ingress-nginx/templates/admission-webhooks/validating-webhook.yaml
# before changing this value, check the required kubernetes version
# https://kubernetes.io/docs/reference/access-authn-authz/extensible-admission-controllers/#prerequisites
apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:labels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhookname: ingress-nginx-admission
webhooks:- name: validate.nginx.ingress.kubernetes.iomatchPolicy: Equivalentrules:- apiGroups:- networking.k8s.ioapiVersions:- v1beta1operations:- CREATE- UPDATEresources:- ingressesfailurePolicy: FailsideEffects: NoneadmissionReviewVersions:- v1- v1beta1clientConfig:service:namespace: ingress-nginxname: ingress-nginx-controller-admissionpath: /networking/v1beta1/ingresses
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/serviceaccount.yaml
apiVersion: v1
kind: ServiceAccount
metadata:name: ingress-nginx-admissionannotations:helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgradehelm.sh/hook-delete-policy: before-hook-creation,hook-succeededlabels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhooknamespace: ingress-nginx
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/clusterrole.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:name: ingress-nginx-admissionannotations:helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgradehelm.sh/hook-delete-policy: before-hook-creation,hook-succeededlabels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhook
rules:- apiGroups:- admissionregistration.k8s.ioresources:- validatingwebhookconfigurationsverbs:- get- update
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/clusterrolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:name: ingress-nginx-admissionannotations:helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgradehelm.sh/hook-delete-policy: before-hook-creation,hook-succeededlabels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhook
roleRef:apiGroup: rbac.authorization.k8s.iokind: ClusterRolename: ingress-nginx-admission
subjects:- kind: ServiceAccountname: ingress-nginx-admissionnamespace: ingress-nginx
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/role.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:name: ingress-nginx-admissionannotations:helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgradehelm.sh/hook-delete-policy: before-hook-creation,hook-succeededlabels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhooknamespace: ingress-nginx
rules:- apiGroups:- ''resources:- secretsverbs:- get- create
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/rolebinding.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:name: ingress-nginx-admissionannotations:helm.sh/hook: pre-install,pre-upgrade,post-install,post-upgradehelm.sh/hook-delete-policy: before-hook-creation,hook-succeededlabels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhooknamespace: ingress-nginx
roleRef:apiGroup: rbac.authorization.k8s.iokind: Rolename: ingress-nginx-admission
subjects:- kind: ServiceAccountname: ingress-nginx-admissionnamespace: ingress-nginx
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/job-createSecret.yaml
apiVersion: batch/v1
kind: Job
metadata:name: ingress-nginx-admission-createannotations:helm.sh/hook: pre-install,pre-upgradehelm.sh/hook-delete-policy: before-hook-creation,hook-succeededlabels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhooknamespace: ingress-nginx
spec:template:metadata:name: ingress-nginx-admission-createlabels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhookspec:containers:- name: createimage: docker.io/jettech/kube-webhook-certgen:v1.5.1imagePullPolicy: IfNotPresentargs:- create- --host=ingress-nginx-controller-admission,ingress-nginx-controller-admission.$(POD_NAMESPACE).svc- --namespace=$(POD_NAMESPACE)- --secret-name=ingress-nginx-admissionenv:- name: POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespacerestartPolicy: OnFailureserviceAccountName: ingress-nginx-admissionsecurityContext:runAsNonRoot: truerunAsUser: 2000
---
# Source: ingress-nginx/templates/admission-webhooks/job-patch/job-patchWebhook.yaml
apiVersion: batch/v1
kind: Job
metadata:name: ingress-nginx-admission-patchannotations:helm.sh/hook: post-install,post-upgradehelm.sh/hook-delete-policy: before-hook-creation,hook-succeededlabels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhooknamespace: ingress-nginx
spec:template:metadata:name: ingress-nginx-admission-patchlabels:helm.sh/chart: ingress-nginx-3.33.0app.kubernetes.io/name: ingress-nginxapp.kubernetes.io/instance: ingress-nginxapp.kubernetes.io/version: 0.47.0app.kubernetes.io/managed-by: Helmapp.kubernetes.io/component: admission-webhookspec:containers:- name: patchimage: docker.io/jettech/kube-webhook-certgen:v1.5.1imagePullPolicy: IfNotPresentargs:- patch- --webhook-name=ingress-nginx-admission- --namespace=$(POD_NAMESPACE)- --patch-mutating=false- --secret-name=ingress-nginx-admission- --patch-failure-policy=Failenv:- name: POD_NAMESPACEvalueFrom:fieldRef:fieldPath: metadata.namespacerestartPolicy: OnFailureserviceAccountName: ingress-nginx-admissionsecurityContext:runAsNonRoot: truerunAsUser: 2000
安装完成后我们可以使用命令查看到对应的pod:
于此同时,也采用NodePort方式创建了对应的service,暴露了两个端口,一个是http的请求端口,一个是https的请求端口
然后我们通过集群内任意一台服务器的公网ip加上以上端口号即可访问
这样整个k8s网络模型就如下所示:分为Ingress
、Service
、Pod
三层
6. 存储抽象
1. 什么是存储抽象
还是假设有三台机器,上面运行着很多pod,pod中往往有一些数据想要挂载到主机上方便修改;以前采用docker的方式就是在主机保留一个目录一对一保存容器中的目录数据
这样启动的容器越多,挂载在外面的东西也越来越多,如果K8s仍然使用这种方式,则会出现很多问题。
假如图中黑色的pod发上宕机,k8s感知到该pod结束,会自动的进行故障转移,5分钟后会在别的机器重启该pod比如2号机器,但是原pod的挂载目录在3号机器的/tmp目录,此时2号机器是没有3号机器内容的,因此造成了数据的丢失。
k8s为了解决这些问题,将机器上作为挂载目录的这一层统一管理形成存储层
。这样以后pod内需要挂载直接请求k8s,k8s为其在存储层分配空间,就算该pod宕机在其他机器重启,k8s仍会让新机器找到原存储空间中挂载的目录。
存储层使用的技术很多,k8s使用的存储层是开放的,例如可以使用专用的文件系统如Glusterfs
、NFS
、CephFS等
。
假如我们采用NFS
网络存储系统技术,其实现原理本质上就是在每台机器都有存有挂载目录的备份,且是互相同步的。加入在1号机器上挂载了/nfs/data目录,则其余两台机器上都会有对应的备份目录/bak/data,这三台机器上对应目录会互相同步,故障转移后直接可以采用新机器的备用目录,避免了pod宕机导致的数据丢失问题。
以上就是k8s的整个存储抽象的大概简介
2. 目录挂载——原生方式
接下来我们来搭建一个k8s的网络文件存储系统,也就是存储层的技术采用NFS
方式,我们指定master机器为NFS-Server
、node1和node2机器为NFS-Client
,最后实现的效果两台客户端同步server的数据
- 所有节点安装nfs-utils
# 以下命令在三台机器都执行
# 安装nfs-utils
yum install -y nfs-utils
- 主机——配置同步目录及暴露方式
# 以下命令只在master节点执行
# nfs主节点暴露/nfs/data目录给所有人以非安全读写方式进行同步
echo "/nfs/data/ *(insecure,rw,sync,no_root_squash)" > /etc/exports# 创建暴露同步目录
mkdir -p /nfs/data
systemctl enable rpcbind --now # 启动rpc远程绑定
systemctl enable nfs-server --now # 启动nfs-server# 使配置生效
exportfs -r
- 从机——配置挂载主机同步目录到本地目录
# 以下命令在node1、node2两台机器执行
# 查看nfs-server可同步挂载的目录
showmount -e 172.31.0.2# 挂载nfs服务器上的共享目录到本机路径 /root/nfs/mount
mkdir -p /nfs/mount
mount -t nfs 172.31.0.2:/nfs/data /nfs/mount# 写入一个测试文件
echo "hello nfs server" > /nfs/mount/test.txt
从机挂载好目录之后,我们可以进行简单的测试,我们向从机本地挂载目录中编写一个txt文件,主机相应的同步目录会自动同步更新
📄 yaml配置文件方式实例:创建一个包含两个nginx容器的应用部署nginx-demo,将这个nginx容器pod中的 /usr/share/nginx/html 目录挂载到主机的 /nfs/data/nginx 目录,示意图如下所示:
我们编写以下yaml配置文件(注意server ip地址修改为自己主机的私网ip地址),然后使用kubectl apply
命令进行配置
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginx-demoname: nginx-demo
spec:replicas: 2selector:matchLabels:app: nginx-demotemplate:metadata:labels:app: nginx-demospec:containers:- image: nginxname: nginxvolumeMounts: # 挂载卷- name: htmlmountPath: /usr/share/nginx/htmlvolumes:- name: htmlnfs:server: 172.31.0.2path: /nfs/data/nginx
我们在本机的 /nfs/data/nginx 目录下编写一个hello.html
然后我们进入任意一个pod中的 /nfs/share/nginx/html 目录可以看到同步的文件
3. 目录挂载——PV&PVC方式
以上方式实现了数据同步不被丢失,当pod删除时,对应nfs-server中的同步目录中数据不会删除,而且nfs-server中会存在许多的同步目录,我们应该对其的大小作出限制,这都是不完美的地方,k8s提出了另外两种挂载方式
PV
:持久卷(Persistent Volume),将应用需要持久化的数据保存到指定位置PVC
:持久卷申明(Persistent Volume Claim),申明需要使用的持久卷规格接下来k8s的挂载方式就如下图所示:
假如有三台机器,这三台机器上首先准备了一些不同大小的存储空间,这些空间合起来可以就是一个
PV池
,可以称之为静态供应方式,也就是提前创建好,而不是需要的时候再创建。此时当Pod需要挂载数据的时候,首先编写一个申请书,也就是PVC
,比如申请1G的空间,此时就从PV池中选出最适合的空间大小的PV进行绑定
接下来我们来模拟pv&pvc的挂载方式
1. 创建pv池
首先在nfs-server上创建三个文件夹作为存储空间
# nfs主节点
[root@k8s-master ~]# mkdir -p /nfs/data/01
[root@k8s-master ~]# mkdir -p /nfs/data/02
[root@k8s-master ~]# mkdir -p /nfs/data/03
然后对应三个存储空间以yaml的方式(注意server ip地址修改为自己主机的私网ip地址)创建三个PV(注意更改服务器ip地址)
apiVersion: v1
kind: PersistentVolume
metadata:name: pv01-10m
spec:capacity: # 容量storage: 10MaccessModes: # 读写模式- ReadWriteManystorageClassName: nfsnfs:path: /nfs/data/01server: 172.31.0.2
---
apiVersion: v1
kind: PersistentVolume
metadata:name: pv02-1gi
spec:capacity:storage: 1GiaccessModes:- ReadWriteManystorageClassName: nfsnfs:path: /nfs/data/02server: 172.31.0.2
---
apiVersion: v1
kind: PersistentVolume
metadata:name: pv03-3gi
spec:capacity:storage: 3GiaccessModes:- ReadWriteManystorageClassName: nfsnfs:path: /nfs/data/03server: 172.31.0.2
然后用kubectl apply -f
上述配置文件,然后便可以用以下命令查看到所有创建的pv
# 获取所有pv
kubectl get persistentvolume
# 可省略为
kubectl get pv
2. PVC的创建与绑定
创建一个需要200m空间的PVC
apiVersion: v1
kind: PersistentVolumeClaim
metadata:name: nginx-pvc
spec:accessModes:- ReadWriteManyresources:requests:storage: 200MistorageClassName: nfs
创建完成后可以用以下命令查看所有的pvc
kubectl get pvc
然后我们再查看所有pv,可以看到其中一个pv已经绑定了该pvc
最后我们创建Pod时可以通过如下方式绑定刚创建的PVC
apiVersion: apps/v1
kind: Deployment
metadata:labels:app: nginx-deploy-pvcname: nginx-deploy-pvc
spec:replicas: 2selector:matchLabels:app: nginx-deploy-pvctemplate:metadata:labels:app: nginx-deploy-pvcspec:containers:- image: nginxname: nginxvolumeMounts:- name: htmlmountPath: /usr/share/nginx/htmlvolumes:- name: htmlpersistentVolumeClaim: # 绑定申请书claimName: nginx-pvc
3. 补充
出了静态供应外,还有动态供应的方式,当pod申请PVC时,k8s会根据PVC的要求自动创建一个存储空间用于绑定挂载,不需要人手工提前创建好PV池,我们暂时不需要了解,后续通过kubesphere搭建时会自动搭建好动态供应的方式
4. 配置文件挂载——ConfigMap
ConfigMap
:抽取应用配置,并且可以自动更新通过以上介绍,如果我们想要将pod中的某个目录挂载出来,可以通过原生挂载方式,也可以使用PV&PVC的方式。
以上方式都是挂载目录,但是我们往往需要在启动一些pod的时候挂载其配置文件,比如启动mysql、redis等实例等时候将其配置文件进行挂载,如何挂载配置文件呢?k8s为我们提供了
ConfigMap
的方式来挂载配置文件。这样以后我们的pod挂载配置文件时遵循以下两步:
- 首先将需要挂载的配置文件做成配置集
- 创建pod的时候引用以上创建的配置集
接下来以redis为例展示ConfigMap配置文件挂载
-
首先编写一个redis配置文件redis.conf
vim redis.conf# 配置文件内容 appendonly yes
-
通过redis.conf创建配置集(存储在k8s的etcd中)
# 创建配置集 kubectl create cm redis-conf --from-file=redis.conf# 查看配置集 kubectl get cm# 查看配置集redis-conf的yaml内容 kubectl get cm redis-conf -oyaml
-
创建Pod引用上述配置集
apiVersion: v1 kind: Pod metadata:name: redis spec:containers:- name: redisimage: rediscommand:- redis-server- "/redis-master/redis.conf" # 指的是redis容器内部的位置ports:- containerPort: 6379# 卷挂载volumeMounts:- mountPath: /data # 容器中/data目录采用data卷方式挂载name: data- mountPath: /redis-mastername: config # 容器中的/redis-master目录采用config卷方式挂载volumes:- name: dataemptyDir: {}- name: configconfigMap: # 配置集name: redis-confitems:- key: redis.confpath: redis.conf
-
检查默认配置是否生效
[root@k8s-master ~]# kubectl get pod NAME READY STATUS RESTARTS AGE nginx-demo-8564794648-9664c 1/1 Running 0 40m nginx-demo-8564794648-v8pfn 1/1 Running 0 40m nginx-deploy-pvc-79fc8558c7-jwhr8 1/1 Running 0 19m nginx-deploy-pvc-79fc8558c7-xrsb5 1/1 Running 0 19m redis 1/1 Running 7 12m # 查看配置是否生效 [root@k8s-master ~]# kubectl exec -it redis -- redis-cli 127.0.0.1:6379> CONFIG GET appendonly 1) "appendonly" 2) "yes"
-
修改ConfigMap,检查配置是否更新
# 修改redis-conf kubectl edit cm redis-conf# 修改内容如下: apiVersion: v1 data:redis.conf: |appendonly yesmaxmemory 2mbmaxmemory-policy allkeys-lru
然后我们连接redis查看配置可以看到配置更新
修改了CM后,Pod里面的配置文件会跟着变,但是配置并没有生效,需要重新启动 Pod 才能从关联的 ConfigMap 中获取更新的值
原因:我们的Pod部署的中间件自己本身没有热更新能力
5. 敏感信息存储——Secret
Secret 对象类型用来保存敏感信息,例如密码、OAuth 令牌和 SSH 密钥。 将这些信息放在 secret 中比放在 Pod 的定义或者 容器镜像 中来说更加安全和灵活。
例如一下我们创建一个存放dockerHub账号登录相关信息
# 创建一个secret存放dockerHub账号信息
kubectl create secret docker-registry leifengyang-docker \
--docker-username=leifengyang \
--docker-password=Lfy123456 \
--docker-email=534096094@qq.com# 命令格式
kubectl create secret docker-registry regcred \--docker-server=<你的镜像仓库服务器> \--docker-username=<你的用户名> \--docker-password=<你的密码> \--docker-email=<你的邮箱地址>
比如我们要拉取一个自己dockerHub上的私有镜像时,直接拉取是报错的,我们可以在创建镜像时添加密钥信息这样就不会报错
apiVersion: v1
kind: Pod
metadata:name: private-nginx
spec:containers:- name: private-nginximage: leifengyang/guignginx:v1.0imagePullSecrets:- name: leifengyang-docker