32 实战演练:玩转Kubernetes(3) 你好,我是Chrono。

到今天,我们的“高级篇”课程也要结束了。比起前面的“初级篇”“中级篇”来说,这里的知识点比较多,难度也要高一些。如果你能够一篇不漏地学习下来,相信一定对Kubernetes有更深层次的认识和理解。

今天的这节课还是来对前面的知识做回顾与总结,提炼出文章里的学习要点和重点,你也可以顺便检验一下自己的掌握程度,试试在不回看课程的情况下,自己能不能流畅说出关联的操作细节。

复习之后,我们就来进行最后一次实战演练了。首先会继续改进贯穿课程始终的WordPress网站,把MariaDB改成StatefulSet,加上NFS持久化存储;然后我们会在Kubernetes集群里安装Dashboard,综合实践Ingress、namespace的用法。

要点回顾一:API对象

“高级篇”可以分成三个部分,第一部分讲的是PersistentVolume、StatefulSet等API对象。

([24讲])PersistentVolume简称PV,是Kubernetes对持久化存储的抽象,代表了LocalDisk、NFS、Ceph等存储设备,和CPU、内存一样,属于集群的公共资源。

因为不同存储设备之间的差异很大,为了更好地描述PV特征,就出现了StorageClass,它的作用是分类存储设备,让我们更容易去选择PV对象。

PV一般由系统管理员来创建,我们如果要使用PV就要用PVC(PersistentVolumeClaim)去申请,说清楚需求的容量、访问模式等参数,然后Kubernetes就会查找最合适的PV分配给我们使用。

([25讲])手动创建PV的工作量很大,麻烦而且容易出错,所以就有了“动态存储卷”的概念,需要在StorageClass里绑定一个Provisioner对象,由它来代替人工,根据PVC自动创建出符合要求的PV

有了PV和PVC,我们就可以在Pod里用“persistentVolumeClaim”来引用PVC,创建出可供容器使用的Volume,然后在容器里用“volumeMounts”把它挂载到某个路径上,这样容器就可以读写PV,实现数据的持久化存储了。

([26讲])持久化存储的一个重要应用领域就是保存应用的状态数据,管理有状态的应用,就要使用新的对象StatefulSet,可以认为它是管理无状态应用对象Deployment的一个特例。

StatefulSet对象的YAML描述和Deployment非常像,“spec”里只是多了一个“serviceName”字段,但它部署应用的方式却与Deployment差距很大。

Deployment创建的Pod是随机的名字,而StatefulSet会对Pod顺序编号、顺序创建,保证应用有一个确定的启动先后次序,这样就可以实现主从、主备等关系。

在使用Service为StatefulSet创建服务的时候,它也会为每个Pod单独创建域名,同样也是顺序编号,保证Pod有稳定的网络标识,外部用户就可以用这个域名来准确地访问到某个具体的Pod。

StatefulSet还使用“volumeClaimTemplates”字段来定义持久化存储,里面其实就是一个PVC,每个Pod可以用这个模板来生成自己的PVC去申请PV,实现存储卷与Pod的独立绑定。

通过启动顺序、稳定域名和存储模板这三个关键能力,StatefulSet就可以很好地处理Redis、MySQL等有状态应用了。

要点回顾二:应用管理

“高级篇”第二部分讲的是应用管理,包括滚动更新、资源配额和健康检查等内容。

([27讲])在Kubernetes里部署好应用后,我们还需要对它做持续的运维管理,其中一项任务是版本的更新和回退。

版本更新很简单,只要编写一个新的YAML(Deployment、DaemonSet、StatefulSet),再用

kubectl apply 应用就可以了。Kubernetes采用的是“滚动更新”策略,实际上是两个同步进行的“扩容”和“缩容”动作,这样在更新的过程中始终会有Pod处于可用状态,能够平稳地对外提供服务。

应用的更新历史可以用命令

kubectl rollout history 查看,如果有什么意外,就可以用

kubectl rollout undo 来回退。这两个命令相当于给我们的更新流程上了一个保险,可以放心大胆操作,失败就用“S/L大法”。

([28讲])为了让Pod里的容器能够稳定运行,我们可以采用资源配额检查探针这两种手段。

资源配额能够限制容器申请的CPU和内存数量,不至于过多或者过少,保持在一个合理的程度,更有利于Kubernetes调度。

检查探针是Kubernetes内置的应用监控工具,有Startup、Liveness、Readiness三种,分别探测启动、存活、就绪状态,探测的方式也有exec、tcpSocket、httpGet三种。组合运用这些就可以灵活地检查容器的状态,Kubernetes发现不可用就会重启容器,让应用在总体上处于健康水平。

要点回顾三:集群管理

“高级篇”第三部分讲的是集群管理,有名字空间、系统监控和网络通信等知识点。

([29讲])Kubernetes的集群里虽然有很多计算资源,但毕竟是有限的,除了要给Pod加上资源配额,我们也要为集群加上资源配额,方法就是用名字空间,把整体的资源池切分成多个小块,按需分配给不同的用户使用。

名字空间的资源配额使用的是“ResourceQuota”,除了基本的CPU和内存,它还能够限制存储容量和各种API对象的数量,这样就可以避免多用户互相挤占,更高效地利用集群资源。

([30讲])系统监控是集群管理的另一个重要方面,Kubernetes提供了Metrics Server和Prometheus两个工具:

  • Metrics Server专门用来收集Kubernetes核心资源指标,可以用

kubectl top 来查看集群的状态,它也是水平自动伸缩对象HorizontalPodAutoscaler的前提条件。

  • Prometheus,继Kubernetes之后的第二个CNCF毕业项目,是云原生监控领域的“事实标准”,在集群里部署之后就可以用Grafana可视化监控各种指标,还可以集成自动报警等功能。

([31讲])对于底层的基础网络设施,Kubernetes定义了平坦的网络模型“IP-per-pod”,实现它就要符合CNI标准。常用的网络插件有Flannel、Calico、Cilium等,Flannel使用Overlay模式,性能较低,Calico使用Route模式,性能较高。

现在,“高级篇”的众多知识要点我们都完整地过了一遍,你是否已经都理解、掌握了它们呢?

搭建WordPress网站

接下来我们就来在[第22讲]的基础上继续优化WordPress网站,其中的关键是让数据库MariaDB实现数据持久化。

网站的整体架构图变化不大,前面的Nginx、WordPress还是原样,只需要修改MariaDB:

图片

因为MariaDB由Deployment改成了StatefulSet,所以我们要修改YAML,添加“serviceName”“volumeClaimTemplates”这两个字段,定义网络标识和NFS动态存储卷,然后在容器部分用“volumeMounts”挂载到容器里的数据目录“/var/lib/mysql”。

修改后的YAML就是这个样子: apiVersion: apps/v1 kind: StatefulSet metadata: labels: app: maria-sts name: maria-sts spec: /# headless svc serviceName: maria-svc /# pvc volumeClaimTemplates: - metadata: name: maria-100m-pvc spec: storageClassName: nfs-client accessModes: - ReadWriteMany resources: requests: storage: 100Mi replicas: 1 selector: matchLabels: app: maria-sts template: metadata: labels: app: maria-sts spec: containers: - image: mariadb:10 name: mariadb imagePullPolicy: IfNotPresent ports: - containerPort: 3306 envFrom: - prefix: ‘MARIADB_’ configMapRef: name: maria-cm volumeMounts: - name: maria-100m-pvc mountPath: /var/lib/mysql

改完MariaDB,我们还要再对WordPress做一点小修改。

还记得吗?StatefulSet管理的每个Pod都有自己的域名,所以要把WordPress的环境变量改成MariaDB的新名字,也就是“maria-sts-0.maria-svc”: apiVersion: v1 kind: ConfigMap metadata: name: wp-cm data: HOST: ‘maria-sts-0.maria-svc’ /#注意这里 USER: ‘wp’ PASSWORD: ‘123’ NAME: ‘db’

改完这两个YAML,我们就可以逐个创建MariaDB、WordPress、Ingress等对象了。

和之前一样,访问NodePort的“30088”端口,或者是用Ingress Controller的“wp.test”域名,都可以进入WordPress网站:

图片

StatefulSet的持久化存储是否生效了呢?

你可以把这些对象都删除后重新创建,再进入网站,看看是否原来的数据依然存在。或者更简单一点,直接查看NFS的存储目录,应该可以看到MariaDB生成的一些数据库文件:

图片

这两种方式都能够证明,我们的MariaDB使用StatefulSet部署后数据已经保存在了磁盘上,不会因为对象的销毁而丢失。

到这里,第一个小实践你就已经完成了,给自己鼓鼓劲,我们一起来做第二个实践,在Kubernetes集群里安装Dashboard。

部署Dashboard

在“初级篇”的实战演练课里([第15讲]),我简单介绍了Kubernetes的图形管理界面,也就是Dashboard,不知道你是否还有印象。当时Dashboard是直接内置在minikube里的,不需要安装,一个命令启动,就能在浏览器里直观地管理Kubernetes集群了,非常方便。

那现在我们用kubeadm部署了实际的多节点集群,能否也用上Dashboard呢?接下来我就带你来一起动手,从零开始安装Dashboard。

首先,你应该先去Dashboard的项目网站(https://github.com/kubernetes/dashboard),看一下它的说明文档,了解一下它的基本情况。

它的安装很简单,只需要一个YAML文件,可以直接下载: wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.6.0/aio/deploy/recommended.yaml

这个YAML里包含了很多对象,虽然文件比较大,但现在的你应该基本都能够看懂了,要点有这么几个:

  • 所有的对象都属于“kubernetes-dashboard”名字空间。
  • Dashboard使用Deployment部署了一个实例,端口号是8443。
  • 容器启用了Liveness探针,使用HTTPS方式检查存活状态。
  • Service对象使用的是443端口,它映射了Dashboard的8443端口。

使用命令

kubectl apply 就可以轻松部署Dashboard了: kubectl apply -f dashboard.yaml

图片

部署Ingress/Ingress Controller

不过,为了给我们的实战增加一点难度,我们可以在前面配一个Ingress入口,用反向代理的方式来访问它。

由于Dashboard默认使用的是加密的HTTPS协议,拒绝明文HTTP访问,所以我们要先生成证书,让Ingress也走HTTPS协议。

简单起见,我直接用Linux里的命令行工具“openssl”来生成一个自签名的证书(如果你有条件,也可以考虑找CA网站申请免费证书): openssl req -x509 -days 365 -out k8s.test.crt -keyout k8s.test.key \ -newkey rsa:2048 -nodes -sha256 \ -subj ‘/CN=k8s.test’ -extensions EXT -config <( \ printf “[dn]\nCN=k8s.test\n[req]\ndistinguished_name = dn\n[EXT]\nsubjectAltName=DNS:k8s.test\nkeyUsage=digitalSignature\nextendedKeyUsage=serverAuth”)

openssl的命令比较长,我简单解释一下:它生成的是一个X509格式的证书,有效期365天,私钥是RSA2048位,摘要算法是SHA256,签发的网站是“k8s.test”。

运行命令行后会生成两个文件,一个是证书“k8s.test.crt”,另一个是私钥“k8s.test.key”,我们需要把这两个文件存入Kubernetes里供Ingress使用。

因为这两个文件属于机密信息,存储的方式当然就是用Secret了。你仍然可以用命令

kubectl create secret 来自动创建YAML,不过类型不是“generic”,而是“tls”,同时还要用

-n 指定名字空间,用

–cert 、

–key 指定文件: export out=”–dry-run=client -o yaml” kubectl create secret tls dash-tls -n kubernetes-dashboard –cert=k8s.test.crt –key=k8s.test.key $out > cert.yml

出来的YAML大概是这个样子:

apiVersion: v1 kind: Secret metadata: name: dash-tls namespace: kubernetes-dashboard type: kubernetes.io/tls data: tls.crt: LS0tLS1CRUdJTiBDRVJU… tls.key: LS0tLS1CRUdJTiBQUklW…

创建这个Secret对象之后,你可以再用

kubectl describe 来检查它的状态:

图片

接下来我们就来编写Ingress Class和Ingress对象,为了保持名字空间的整齐,也把它放在“kubernetes-dashboard”名字空间里。

Ingress Class对象很简单,名字是“dash-ink”,指定Controller还是我们之前用的Nginx官方的Ingress Controller: apiVersion: networking.k8s.io/v1 kind: IngressClass metadata: name: dash-ink namespace: kubernetes-dashboard spec: controller: nginx.org/ingress-controller

Ingress对象可以用

kubectl create 命令自动生成,如果你有点忘记的话,可以回头参考一下[第21讲]:

kubectl create ing dash-ing –rule=”k8s.test/=kubernetes-dashboard:443” –class=dash-ink -n kubernetes-dashboard $out

但这次因为是HTTPS协议,所以我们要在Ingress里多加一点东西,一个是“annotations”字段,指定后端目标是HTTPS服务,另一个是“tls”字段,指定域名和证书,也就是刚才创建的Secret:

apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: dash-ing namespace: kubernetes-dashboard annotations: nginx.org/ssl-services: “kubernetes-dashboard” spec: ingressClassName: dash-ink tls: - hosts: - k8s.test secretName: dash-tls rules: - host: k8s.test http: paths: - path: / pathType: Prefix backend: service: name: kubernetes-dashboard port: number: 443

最后一个对象,就是Ingress Controller了,还是拿现成的模板修改,记得要把“args”里的Ingress Class改成我们自己的“dash-ink”:

apiVersion: apps/v1 kind: Deployment metadata: name: dash-kic-dep namespace: nginx-ingress spec: … args: - -ingress-class=dash-ink

要让我们在外面能够访问Ingress Controller,还要为它再定义一个Service,类型是“NodePort”,端口指定是“30443”:

apiVersion: v1 kind: Service metadata: name: dash-kic-svc namespace: nginx-ingress spec: ports: - port: 443 protocol: TCP targetPort: 443 nodePort: 30443 selector: app: dash-kic-dep type: NodePort

把上面的Secret、Ingress Class、Ingress、Ingress Controller、Service都创建好之后,我们再来确认一下它们的运行状态:

图片

因为这些对象比较多,处于不同的名字空间,关联有点复杂,我画了一个简单的示意图,你可以看一下:

图片

访问Dashboard

到这里,Dashboard的部署工作就基本完成了。为了能正常访问,我们还要为它创建一个用户,才能登录进Dashboard。

Dashboard的网站上有一个简单示例(https://github.com/kubernetes/dashboard/blob/master/docs/user/access-control/creating-sample-user.md),我们直接拿来用就行: apiVersion: v1 kind: ServiceAccount metadata: name: admin-user namespace: kubernetes-dashboard — apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: admin-user roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: cluster-admin subjects: - kind: ServiceAccount name: admin-user namespace: kubernetes-dashboard

这个YAML创建了一个Dashboard的管理员账号,名字叫“admin-user”,使用的是Kubernetes的RBAC机制,就不展开细讲了。

这个账号不能用简单的“用户名+密码”的方式登录,需要用到一个Token,可以用

kubectl get secret 、

kubectl describe secret 查到: kubectl get secret -n kubernetes-dashboard kubectl describe secrets -n kubernetes-dashboard admin-user-token-xxxx

图片

Token是一个很长的字符串,把它拷贝存好,再为它的测试域名“k8s.test”加上域名解析(修改/etc/hosts),然后我们就可以在浏览器里输入网址“https://k8s.test:30443”访问Dashboard了:

图片

下面的两张截图就是我查看集群里“kube-system”名字空间的情况,由于我们之前安装了Metrics Server,所以Dashboard也能够以图形的方式显示CPU和内存状态,有那么一点Prometheus + Grafana的意思:

图片

图片

小结

好了,今天我们一起回顾了“高级篇”里的要点,下面的这张思维导图就是对这些知识点的全面总结,你可以再认真研究一下:

图片

今天我们有两个实战项目。首先是WordPress,把后端的存储服务MariaDB改造成了StatefulSet,挂载了NFS网盘,这样就实现了一个功能比较完善的网站,达到了基本可用的程度。

接着我们又在Kubernetes里安装了Dashboard,主要部署在名字空间“kubernetes-dashboard”。Dashboard自身的安装很简单,但我们又为它在前面搭建了一个反向代理,配上了安全证书,进一步实践了Ingress的用法。

不过这两个项目还没有完全覆盖“高级篇”的内容,你可以再接着改进它们,比如加上健康检查、资源配额、自动水平伸缩等,多动手来巩固所学的知识。

课下作业

今天的课下作业时间,我想就留给你自己来操作一下这节课里的两个实战演练吧,如果遇到了什么问题,可以在留言区随时提问。

图片

参考资料

https://learn.lianglianglee.com/%e4%b8%93%e6%a0%8f/Kubernetes%e5%85%a5%e9%97%a8%e5%ae%9e%e6%88%98%e8%af%be/32%20%e5%ae%9e%e6%88%98%e6%bc%94%e7%bb%83%ef%bc%9a%e7%8e%a9%e8%bd%acKubernetes%ef%bc%883%ef%bc%89.md