08-27-周三_17-09-29
131
Kubernetes/DashBoard.md
Normal file
@@ -0,0 +1,131 @@
|
||||
# DashBoard
|
||||
|
||||
之前在kubernetes中完成的所有操作都是通过命令行工具kubectl完成的。其实,为了提供更丰富的用户体验,kubernetes还开发了一个基于web的用户界面(Dashboard)。用户可以使用Dashboard部署容器化的应用,还可以监控应用的状态,执行故障排查以及管理kubernetes中各种资源。
|
||||
|
||||
## 部署Dashboard
|
||||
|
||||
1) 下载yaml,并运行Dashboard
|
||||
|
||||
```shell
|
||||
# 下载yaml
|
||||
[root@k8s-master01 ~]# wget https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml
|
||||
|
||||
# 修改kubernetes-dashboard的Service类型
|
||||
kind: Service
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: kubernetes-dashboard
|
||||
name: kubernetes-dashboard
|
||||
namespace: kubernetes-dashboard
|
||||
spec:
|
||||
type: NodePort # 新增
|
||||
ports:
|
||||
- port: 443
|
||||
targetPort: 8443
|
||||
nodePort: 30009 # 新增
|
||||
selector:
|
||||
k8s-app: kubernetes-dashboard
|
||||
|
||||
# 部署
|
||||
[root@k8s-master01 ~]# kubectl create -f recommended.yaml
|
||||
|
||||
# 查看namespace下的kubernetes-dashboard下的资源
|
||||
[root@k8s-master01 ~]# kubectl get pod,svc -n kubernetes-dashboard
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/dashboard-metrics-scraper-c79c65bb7-zwfvw 1/1 Running 0 111s
|
||||
pod/kubernetes-dashboard-56484d4c5-z95z5 1/1 Running 0 111s
|
||||
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/dashboard-metrics-scraper ClusterIP 10.96.89.218 <none> 8000/TCP 111s
|
||||
service/kubernetes-dashboard NodePort 10.104.178.171 <none> 443:30009/TCP 111s
|
||||
```
|
||||
|
||||
2)创建访问账户,获取token
|
||||
|
||||
```shell
|
||||
# 创建账号
|
||||
[root@k8s-master01-1 ~]# kubectl create serviceaccount dashboard-admin -n kubernetes-dashboard
|
||||
|
||||
# 授权
|
||||
[root@k8s-master01-1 ~]# kubectl create clusterrolebinding dashboard-admin-rb --clusterrole=cluster-admin --serviceaccount=kubernetes-dashboard:dashboard-admin
|
||||
|
||||
# 获取账号token
|
||||
[root@k8s-master01 ~]# kubectl get secrets -n kubernetes-dashboard | grep dashboard-admin
|
||||
dashboard-admin-token-xbqhh kubernetes.io/service-account-token 3 2m35s
|
||||
|
||||
[root@k8s-master01 ~]# kubectl describe secrets dashboard-admin-token-xbqhh -n kubernetes-dashboard
|
||||
Name: dashboard-admin-token-xbqhh
|
||||
Namespace: kubernetes-dashboard
|
||||
Labels: <none>
|
||||
Annotations: kubernetes.io/service-account.name: dashboard-admin
|
||||
kubernetes.io/service-account.uid: 95d84d80-be7a-4d10-a2e0-68f90222d039
|
||||
|
||||
Type: kubernetes.io/service-account-token
|
||||
|
||||
Data
|
||||
====
|
||||
namespace: 20 bytes
|
||||
token: eyJhbGciOiJSUzI1NiIsImtpZCI6ImJrYkF4bW5XcDhWcmNGUGJtek5NODFuSXl1aWptMmU2M3o4LTY5a2FKS2cifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9uYW1lc3BhY2UiOiJrdWJlcm5ldGVzLWRhc2hib2FyZCIsImt1YmVybmV0ZXMuaW8vc2VydmljZWFjY291bnQvc2VjcmV0Lm5hbWUiOiJkYXNoYm9hcmQtYWRtaW4tdG9rZW4teGJxaGgiLCJrdWJlcm5ldGVzLmlvL3NlcnZpY2VhY2NvdW50L3NlcnZpY2UtYWNjb3VudC5uYW1lIjoiZGFzaGJvYXJkLWFkbWluIiwia3ViZXJuZXRlcy5pby9zZXJ2aWNlYWNjb3VudC9zZXJ2aWNlLWFjY291bnQudWlkIjoiOTVkODRkODAtYmU3YS00ZDEwLWEyZTAtNjhmOTAyMjJkMDM5Iiwic3ViIjoic3lzdGVtOnNlcnZpY2VhY2NvdW50Omt1YmVybmV0ZXMtZGFzaGJvYXJkOmRhc2hib2FyZC1hZG1pbiJ9.NAl7e8ZfWWdDoPxkqzJzTB46sK9E8iuJYnUI9vnBaY3Jts7T1g1msjsBnbxzQSYgAG--cV0WYxjndzJY_UWCwaGPrQrt_GunxmOK9AUnzURqm55GR2RXIZtjsWVP2EBatsDgHRmuUbQvTFOvdJB4x3nXcYLN2opAaMqg3rnU2rr-A8zCrIuX_eca12wIp_QiuP3SF-tzpdLpsyRfegTJZl6YnSGyaVkC9id-cxZRb307qdCfXPfCHR_2rt5FVfxARgg_C0e3eFHaaYQO7CitxsnIoIXpOFNAR8aUrmopJyODQIPqBWUehb7FhlU1DCduHnIIXVC_UICZ-MKYewBDLw
|
||||
ca.crt: 1025 bytes
|
||||
```
|
||||
|
||||
3)通过浏览器访问Dashboard的UI
|
||||
|
||||
在登录页面上输入上面的token
|
||||
|
||||

|
||||
|
||||
出现下面的页面代表成功
|
||||
|
||||

|
||||
|
||||
## 使用DashBoard
|
||||
|
||||
本章节以Deployment为例演示DashBoard的使用
|
||||
|
||||
**查看**
|
||||
|
||||
选择指定的命名空间`dev`,然后点击`Deployments`,查看dev空间下的所有deployment
|
||||
|
||||

|
||||
|
||||
**扩缩容**
|
||||
|
||||
在`Deployment`上点击`规模`,然后指定`目标副本数量`,点击确定
|
||||
|
||||

|
||||
|
||||
**编辑**
|
||||
|
||||
在`Deployment`上点击`编辑`,然后修改`yaml文件`,点击确定
|
||||
|
||||

|
||||
|
||||
**查看Pod**
|
||||
|
||||
点击`Pods`, 查看pods列表
|
||||
|
||||

|
||||
|
||||
**操作Pod**
|
||||
|
||||
选中某个Pod,可以对其执行日志(logs)、进入执行(exec)、编辑、删除操作
|
||||
|
||||

|
||||
|
||||
> Dashboard提供了kubectl的绝大部分功能,这里不再一一演示
|
||||
|
||||
# kuboard
|
||||
|
||||
## 特点介绍
|
||||
|
||||
相较于 Kubernetes Dashboard 等其他 Kubernetes 管理界面,Kuboard 的优势更加明显,官方网站:https://kuboard.cn/
|
||||
|
||||
## 安装教程
|
||||
|
||||
https://kuboard.cn/v4/install/quickstart.html
|
||||
|
||||
安装好后链接上集群,最终界面如下
|
||||
|
||||

|
BIN
Kubernetes/DashBoard/image-20200520144548997.png
Normal file
After Width: | Height: | Size: 62 KiB |
BIN
Kubernetes/DashBoard/image-20200520144959353.png
Normal file
After Width: | Height: | Size: 94 KiB |
BIN
Kubernetes/DashBoard/image-20200520154628679.png
Normal file
After Width: | Height: | Size: 38 KiB |
BIN
Kubernetes/DashBoard/image-20200520162605102.png
Normal file
After Width: | Height: | Size: 54 KiB |
BIN
Kubernetes/DashBoard/image-20200520163253644.png
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
Kubernetes/DashBoard/image-20200520163552110.png
Normal file
After Width: | Height: | Size: 107 KiB |
BIN
Kubernetes/DashBoard/image-20200520163832827.png
Normal file
After Width: | Height: | Size: 56 KiB |
BIN
Kubernetes/DashBoard/image-20240918150900081.png
Normal file
After Width: | Height: | Size: 191 KiB |
5
Kubernetes/Kubernetes.md
Normal file
@@ -0,0 +1,5 @@
|
||||
Kubernetes 是一个开源的容器编排引擎,用于自动化部署、扩展和管理容器化应用程序。该开源项目由云原生计算基金会(CNCF)托管。
|
||||
|
||||
[kubernetes官网地址](https://kubernetes.io/)
|
||||
|
||||
[kubernetes项目地址](https://github.com/kubernetes)
|
166
Kubernetes/Kubernetes概述.md
Normal file
@@ -0,0 +1,166 @@
|
||||
# 云计算的三种模式
|
||||
|
||||
## 单机到云原生的演变
|
||||
|
||||

|
||||
|
||||
## IAAS
|
||||
|
||||
Infrastructure as a Service 基础设施即服务
|
||||
|
||||

|
||||
|
||||
## PAAS
|
||||
|
||||
Platform as a service 平台即服务
|
||||
|
||||

|
||||
|
||||
## SAAS
|
||||
|
||||
Software as a Service
|
||||
|
||||
# 容器编排工具
|
||||
|
||||
## Borg
|
||||
|
||||
Borg 是 Google 早期开发的集群管理系统,用于管理大规模的容器化⼯作负载。它是 Google 内部的⼀个关键基础设施,自 2003 年开始使用。
|
||||
|
||||
## Omega
|
||||
|
||||
Omega 是 Borg 的⼀个后继系统,于 2013 年开始开发,旨在解决 Borg 在大规模、动态、多租户环境下多租户、声明式配置。
|
||||
|
||||
## kubernetes
|
||||
|
||||
Kubernetes 是一个可移植的、可扩展的开源平台,用于管理容器化的工作负载和服务,可促进声明式配置和自动化。kubernetes 拥有一个庞大且快速增长的生态系统。kubernetes 的服务、支持和工具广泛可用。
|
||||
|
||||
kubernetes 这个名字源于希腊于,意为舵手或飞行员。k8s 这个缩写是因为k和s之间有八个字符的关系。google 在 2014 年开源了 kubernetes 项目。kubernetes建立在 google 在大规模运行生产工作负载方面拥有十几年的经验的基础上,结合了社区中最好的想法和实践。
|
||||
|
||||
# kubernetes优势
|
||||
|
||||
- 服务发现和负载均衡
|
||||
- 存储编排(添加任何本地或云服务器)
|
||||
- 自动部署和回滚
|
||||
|
||||
- 自动分配 CPU/内存资源
|
||||
- 弹性伸缩
|
||||
- 自我修复(需要时启动新容器)
|
||||
- Secret(安全相关信息)和配置管理
|
||||
- 大型规模的支持
|
||||
- 每个节点的Pod数量不超过 110
|
||||
- 节点数不超过 5000
|
||||
- Pod总数不超过 150000
|
||||
- 容器总数不超过 300000
|
||||
- 开源
|
||||
|
||||

|
||||
|
||||
## kubernetes现状
|
||||
|
||||

|
||||
|
||||
# kubernetes组件
|
||||
|
||||

|
||||
|
||||
一个kubernetes集群由一组被称作节点的机器组成。这些节点上运行Kubernetes所管理的容器化应用。集群具有至少一个工作节点。
|
||||
|
||||
工作节点会托管Pod,而Pod就是作为应用负载的组件。控制平面管理集群中的工作节点和Pod。在生产环境中,控制平面通常跨多台计算机运行,一个集群通常运行多个节点,提供容错性和高可用性。
|
||||
|
||||
## 控制平面组件(Control Plane Components)
|
||||
|
||||
控制平面组件会为集群做出全局决策,比如资源的调度。以及检测和响应集群事件,例如当不满足部署的 `replicas`字段时,要启动新的Pod)。
|
||||
|
||||
控制平面组件可以在集群中的任何节点上运行。然而,为了简单起见,设置脚本通常会在同一个计算机上启动所有控制平面组件,并且不会在此计算机上运行用户容器。
|
||||
|
||||
### kube-apiserver
|
||||
|
||||
`kube-apiserver`负责公开Kubernetes API,处理接受请求的工作,是控制平面的前端。
|
||||
|
||||
`kube-apiserver`设计上考虑了水平扩缩,所以可通过部署多个实例来进行扩缩,并在这些实例之间平衡流量。
|
||||
|
||||
### etcd
|
||||
|
||||
一致且高可用的键值存储,用作Kubernetes所有集群数据的后台数据库。
|
||||
|
||||
### kube-scheduler
|
||||
|
||||
`kube-scheduler`负责监视新创建的、未指定运行节点(node)的Pods,并选择节点来让Pod在上面运行。
|
||||
|
||||
调度决策考虑的因素包括单个Pod及Pods集合的资源需求、软硬件及策略约束、亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。
|
||||
|
||||
### kube-controller-manager
|
||||
|
||||
`kube-controller-manager`负责运行控制器进程,控制器通过 API 服务器监控集群的公共状态,并致力于将当前状态转变为期待状态。
|
||||
|
||||
不同类型的控制器如下:
|
||||
- 节点控制器(Node Controller):负责在节点出现故障时进行通知和响应。
|
||||
- 任务控制器(Job Controller):监测代表一次性任务的 Job 对象,然后创建 Pod 来运行这些任务直至完成。
|
||||
- 端点分片控制器(EndpointSlice controller):填充端点分片(EndpointSlice)对象(以提供 Service 和 Pod 之间的链接)。
|
||||
- 服务账号控制器(ServiceAccount controller):为新的命名空间创建默认的服务账号(ServiceAccount)。
|
||||
- ...
|
||||
|
||||
## Node组件
|
||||
|
||||
节点组件会在每个节点上运行,负责维护运行的 Pod 并提供 Kubernetes 运行环境。
|
||||
|
||||
### kubelet
|
||||
|
||||
`kubelet`会在集群中每个节点(node)上运行。它保证容器(containers)都运行在Pod中。
|
||||
|
||||
`kubelet`接收一组通过各类机制提供给它的PodSpec,确保这些 PodSpec 中描述的容器处于运行状态且健康。kubelet不会管理不是由 Kubernetes创建的容器。
|
||||
|
||||
### kube-proxy
|
||||
|
||||
kube-proxy 是集群中每个节点(node)上所运行的网络代理, 实现 Kubernetes 服务(Service) 概念的一部分。
|
||||
|
||||
kube-proxy 维护节点上的一些网络规则, 这些网络规则会允许从集群内部或外部的网络会话与 Pod 进行网络通信。
|
||||
|
||||
如果操作系统提供了可用的数据包过滤层,则 kube-proxy 会通过它来实现网络规则。 否则,kube-proxy 仅做流量转发。
|
||||
|
||||
### 容器运行时(Container Runtime)
|
||||
|
||||
这个基础组件使Kubernetes能够有效运行容器。 它负责管理 Kubernetes 环境中容器的执行和生命周期。
|
||||
|
||||
Kubernetes支持许多容器运行环境,例如 containerd、 CRI-O 以及 Kubernetes CRI (容器运行环境接口) 的其他任何实现。
|
||||
|
||||
|
||||
## 插件(Addons)
|
||||
|
||||
插件使用Kubernetes资源(DaemonSet、Deployment等)实现集群功能。因为这些插件提供集群级别的功能,插件中命名空间域的资源属于`kube-system`命名空间。
|
||||
|
||||
### DNS
|
||||
|
||||
尽管其他插件都并非严格意义上的必需组件,但几乎所有 Kubernetes 集群都应该有集群 DNS,因为很多示例都需要 DNS 服务。
|
||||
|
||||
集群DNS是多个 DNS 服务器一起工作,为 Kubernetes 服务提供 DNS 记录。
|
||||
|
||||
Kubernetes 启动的容器会自动将这些 DNS 服务器配置其 DNS 搜索列表中。
|
||||
|
||||
### Web 界面(仪表盘)
|
||||
|
||||
Dashboard 是 Kubernetes 集群的通用的、基于 Web 的用户界面。 它使用户可以管理集群中运行的应用程序以及集群本身, 并进行故障排。
|
||||
|
||||
### 容器资源监控
|
||||
|
||||
容器资源监控 将关于容器的一些常见的时间序列度量值保存到一个集中的数据库中, 并提供浏览这些数据的界面。
|
||||
|
||||
### 集群层面日志
|
||||
|
||||
集群层面日志机制负责将容器的日志数据保存到一个集中的日志存储中, 这种集中日志存储提供搜索和浏览接口。
|
||||
|
||||
### 网络插件
|
||||
|
||||
网络插件是实现容器网络接口(CNI)规范的软件组件。它们负责为 Pod 分配 IP 地址,并使这些 Pod 能在集群内部相互通信。
|
||||
|
||||
|
||||
# 扩展阅读
|
||||
[Kubernetes CRI (容器运行环境接口)](https://github.com/kubernetes/community/blob/master/contributors/devel/sig-node/container-runtime-interface.md)
|
||||
|
||||
[网络插件](https://v1-29.docs.kubernetes.io/zh-cn/docs/concepts/extend-kubernetes/compute-storage-net/network-plugins/)
|
||||
|
||||
[集群网络](https://v1-29.docs.kubernetes.io/zh-cn/docs/concepts/cluster-administration/addons/#networking-and-network-policy)
|
||||
|
||||
[控制器列表]()
|
||||
|
||||
[ETCD](https://etcd.io/docs/)
|
310
Kubernetes/Pod与网络.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# pod概念
|
||||
|
||||
<img src="pod与网络/image-20240904112128424.png" alt="image-20240904112128424" style="zoom:33%;" />
|
||||
|
||||
首先我们要明确一个概念,Kubernetes并不是只支持Docker这一个容器运行时,Kubernetes通过CRI这个抽象层,支持除Docker之外的其他容器运行时,比如rkt甚至支持客户自定义容器运行时。
|
||||
|
||||
- 第一个原因:借助CRI这个抽象层,使得Kubernetes不依赖于底层某一种具体的容器运行时实现技术,而是直接操作pod,pod内部再管理多个业务上紧密相关的用户业务容器,这种架构便于Kubernetes做扩展。
|
||||
- 第二个原因,我们假设Kubernetes没有pod的概念,而是直接管理容器,那么一组容器作为一个单元,假设其中一个容器死亡了,此时这个单元的状态应该如何定义呢?应该理解成整体死亡,还是个别死亡?
|
||||
|
||||
这个问题不易回答的原因,是因为包含了这一组业务容器的逻辑单元,没有一个统一的办法来代表整个容器组的状态,这就是Kubernetes引入pod的概念,并且每个pod里都有一个Kubernetes系统自带的pause容器的原因,通过引入pause这个与业务无关并且作用类似于Linux操作系统守护进程的Kubernetes系统标准容器,以pause容器的状态来代表整个容器组的状态。
|
||||
|
||||
<img src="pod与网络/image-20240904112421964.png" alt="image-20240904112421964" style="zoom: 33%;" />
|
||||
|
||||
- 第三个原因:pod里所有的业务容器共享pause容器的IP地址,以及pause容器mount的Volume,通过这种设计,业务容器之间可以直接通信,文件也能够直接彼此共享。
|
||||
|
||||
## Pause特性
|
||||
|
||||
- Pod内部第一个启动的容器
|
||||
- 初始化网络栈
|
||||
- 挂载需要的存储卷
|
||||
- 回收僵⼫进程
|
||||
- 容器与Pause容器共享名字空间(Network、PID、IPC)
|
||||
|
||||
# 实战:基于Docker模拟pod
|
||||
|
||||
- 编写Nginx配置文件
|
||||
|
||||
```bash
|
||||
cat <<EOF>> nginx.conf
|
||||
error_log stderr;
|
||||
events {
|
||||
worker_connections 1024;
|
||||
}
|
||||
http {
|
||||
access_log /dev/stdout combined;
|
||||
server {
|
||||
listen 80 default_server;
|
||||
server_name example.com www.example.com;
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:2368;
|
||||
}
|
||||
}
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
- 启动容器
|
||||
|
||||
```bash
|
||||
docker run --name pause -p 8080:80 -d k8s.gcr.io/pause:3.1
|
||||
|
||||
docker run --name nginx -v `pwd`/nginx.conf:/etc/nginx/nginx.conf --net=container:pause --ipc=container:pause --pid=container:pause -d nginx
|
||||
|
||||
docker run -d --name ghost -e NODE_ENV=development --net=container:pause --ipc=container:pause --pid=container:pause ghost
|
||||
```
|
||||
|
||||

|
||||
|
||||
# kubernetes网络
|
||||
|
||||
## 基本概述
|
||||
|
||||
Kubernetes的网络模型假定了所有Pod都在一个可以直接连通的扁平的网络空间中,这在GCE(Google Compute Engine)里面是现成的网络模型,Kubernetes假定这个网络已经存在。而在私有云里搭建Kubernetes集群,就不能假定这个网络已经存在了。我们需要自己实现这个网络假设,将不同节点上的Docker容器之间的互相访问先打通,然后运行Kubernetes。
|
||||
|
||||
## 网络模型原则
|
||||
|
||||
- 在不使用网络地址转换(NAT)的情况下,集群中的Pod能够与任意其他Pod进行通信
|
||||
- 在不使用网络地址转换(NAT)的情况下,在集群节点上运行的程序能与同一节点上的任何Pod进行通信
|
||||
- 每个Pod都有自己的IP地址(IP-per-Pod),并且任意其他Pod都可以通过相同的这个地址访问它
|
||||
|
||||
## CNI
|
||||
|
||||
借助CNI标准,Kubernetes可以实现容器网络问题的解决。通过插件化的方式来集成各种网络插件,实现集群内部网络相互通信,只要实现CNI标准中定义的核心接口操作(ADD,将容器添加到网络;DEL,从网络中删除一个容器;CHECK,检查容器的网络是否符合预期等)。CNI插件通常聚焦在容器到容器的网络通信。
|
||||
|
||||

|
||||
|
||||
CNI的接口并不是指HTTP,gRPC这种接口,CNI接口是指对可执行程序的调用(exec)可执行程序,Kubernetes节点默认的CNI插件路径为/opt/cni/bin
|
||||
|
||||
```bash
|
||||
[root@master01 ~]# ls /opt/cni/bin/
|
||||
bandwidth calico dhcp firewall host-device install loopback portmap sbr tap vlan
|
||||
bridge calico-ipam dummy flannel host-local ipvlan macvlan ptp static tuning vrf
|
||||
```
|
||||
|
||||
CNI通过JSON格式的配置文件来描述网络配置,当需要设置容器网络时,由容器运行时负责执行CNI插件,并通过CNI插件的标准输入(stdin)来传递配置文件信息,通过标准输出(stdout)接收插件的执行结果。从网络插件功能可以分为五类:
|
||||
|
||||

|
||||
|
||||
- **Main插件**
|
||||
- 创建具体网络设备
|
||||
- bridge:网桥设备,连接container和host;
|
||||
- ipvlan:为容器增加ipvlan网卡;
|
||||
- loopback:IO设备;
|
||||
- macvlan:为容器创建一个MAC地址;
|
||||
- ptp:创建一对Veth Pair;
|
||||
- vlan:分配一个vlan设备;
|
||||
- host-device:将已存在的设备移入容器内
|
||||
- **IPAM插件**:
|
||||
- 负责分配IP地址
|
||||
- dhcp:容器向DHCP服务器发起请求,给Pod发放或回收IP地址;
|
||||
- host-local:使用预先配置的IP地址段来进行分配;
|
||||
- static:为容器分配一个静态IPv4/IPv6地址,主要用于debug
|
||||
- **META插件**:
|
||||
- 其他功能的插件
|
||||
- tuning:通过sysctl调整网络设备参数;
|
||||
- portmap:通过iptables配置端口映射;
|
||||
- bandwidth:使用TokenBucketFilter来限流;
|
||||
- sbr:为网卡设置source based routing;
|
||||
- firewall:通过iptables给容器网络的进出流量进行限制
|
||||
- **Windows插件**:专门用于Windows平台的CNI插件(win-bridge与win-overlay网络插件)
|
||||
- **第三方网络插件**:第三方开源的网络插件众多,每个组件都有各自的优点及适应的场景,难以形成统一的标准组件,常用有Flannel、Calico、Cilium、OVN网络插件
|
||||
|
||||

|
||||
|
||||
## 网络插件
|
||||
|
||||
<img src="pod与网络/image-20240904135815763.png" alt="image-20240904135815763" style="zoom:33%;" />
|
||||
|
||||
### 人气数据
|
||||
|
||||
数据更新时间:2024-09-04
|
||||
|
||||
| 提供商 | 项目 | Stars | Forks | Contributors |
|
||||
| ------- | --------------------------------------- | ----- | ----- | ------------ |
|
||||
| Canal | https://github.com/projectcalico/canal | 714 | 100 | 20 |
|
||||
| Flannel | https://github.com/flannel-io/flannel | 8.7k | 2.9k | 235 |
|
||||
| Calico | https://github.com/projectcalico/calico | 5.9k | 1.3k | 353 |
|
||||
| Weave | https://github.com/weaveworks/weave/ | 6.6k | 668 | 87 |
|
||||
| Cilium | https://github.com/cilium/cilium | 19.7k | 2.9k | 803 |
|
||||
|
||||
功能说明
|
||||
|
||||
| 提供商 | 网络模型 | 路由分发 | 网络策略 | 网格 | 外部数据存储 | 加密 | Ingress/Egress策略 |
|
||||
| ------- | ------------------------ | -------- | -------- | ---- | -------------- | ---- | ------------------ |
|
||||
| Canal | 封装(VXLAN) | 否 | 是 | 否 | K8s API | 是 | 是 |
|
||||
| Flannel | 封装(VXLAN) | 否 | 否 | 否 | K8s API | 是 | 中 |
|
||||
| Calico | 封装(VXLAN,IPIP),未封装 | 是 | 是 | 是 | Etcd 和K8s API | 是 | 是 |
|
||||
| Weave | 封装 | 是 | 是 | 是 | 否 | 是 | 是 |
|
||||
| Cilium | 封装(VXLAN) | 是 | 是 | 是 | Etcd和K8sAPI | 是 | 是 |
|
||||
|
||||
- 网络模型:封装或未封装。
|
||||
- 路由分发:一种外部网关协议,用于在互联网上交换路由和可达性信息。BGP可以帮助进行跨集群pod之间的网络。此功能对于未封装的CNI网络插件是必须的,并且通常由BGP完成。如果你想构建跨网段拆分的集群,路由分发是一个很好的功能。
|
||||
- 网络策略:Kubernetes提供了强制执行规则的功能,这些规则决定了哪些service可以使用网络策略进行相互通信。这是从Kubernetes1.7起稳定的功能,可以与某些网络插件一起使用。
|
||||
- 网格:允许在不同的Kubernetes集群间进行service之间的网络通信。
|
||||
- 外部数据存储:具有此功能的CNI网络插件需要一个外部数据存储来存储数据。
|
||||
- 加密:允许加密和安全的网络控制和数据平面。
|
||||
- Ingress/Egress策略:允许你管理Kubernetes和非Kubernetes通信的路由控制。
|
||||
|
||||
## 网络模型
|
||||
|
||||
- underlay network(非封装网络)
|
||||
- 现实的物理基础层网络设备
|
||||
- underlay就是数据中心场景的基础物理设施,保证任何两个点路由可达,其中包含了传统的网络技术
|
||||
- overlay network(封装网络)
|
||||
- 一个基于物理网络之上构建的逻辑网络
|
||||
- overlay是在网络技术领域指的是一种网络架构上叠加的虚拟化技术模式
|
||||
- Overlay网络技术多种多样,一般采用TRILL、VxLan、GRE、NVGRE等隧道技术
|
||||
|
||||
### overlay
|
||||
|
||||

|
||||
|
||||
### underlay
|
||||
|
||||

|
||||
|
||||
## calico
|
||||
|
||||
或许是目前最主流的网络解决方案-calico
|
||||
|
||||
Calico是一个纯三层的虚拟网络,它没有复用docker的docker0网桥,而是自己实现的,calico网络不对数据包进行额外封装,不需要NAT和端口映射
|
||||
|
||||
### calico架构
|
||||
|
||||
<img src="pod与网络/image-20240904141203196.png" alt="image-20240904141203196" style="zoom:50%;" />
|
||||
|
||||
- Felix
|
||||
- 管理网络接口
|
||||
- 编写路由
|
||||
- 编写ACL
|
||||
- 报告状态
|
||||
- bird(BGPClient)
|
||||
- BGPClient将通过BGP协议⼴播告诉剩余calico节点,从而实现网络互通
|
||||
- confd
|
||||
- 通过监听etcd以了解BGP配置和全局默认值的更改。Confd根据ETCD中数据的更新,动态生成BIRD配置文件。当配置文件更改时,confd触发BIRD重新加载新文件
|
||||
|
||||
### VXLAN
|
||||
|
||||
- VXLAN,即Virtual Extensible LAN(虚拟可扩展局域网),是Linux本身支持的一网种网络虚拟化技术。VXLAN可以完全在内核态实现封装和解封装工作,从而通过“隧道”机制,构建出覆盖网络(OverlayNetwork)
|
||||
- 基于三层的”二层“通信,层即vxlan包封装在udp数据包中,要求udp在k8s节点间三层可达;
|
||||
- 二层即vxlan封包的源mac地址和目的mac地址是自己的vxlan设备mac和对端vxlan设备mac实现通讯。
|
||||
|
||||

|
||||
|
||||
- 数据包封包:封包,在vxlan设备上将pod发来的数据包源、目的mac替换为本机vxlan网卡和对端节点vxlan 网卡的mac。外层udp目的ip地址根据路由和对端vxlan的mac查fdb表获取
|
||||
|
||||
- 优势:只要k8s节点间三层互通,可以跨网段,对主机网关路由没有特殊要求。各个node节点通过vxlan设备实现基于三层的”二层”互通,三层即vxlan包封装在udp数据包中,要求udp在k8s节点间三层可达;二层即vxlan封包的源mac地址和目的mac地址是自己的vxlan设备mac和对端vxlan设备mac
|
||||
|
||||
- 缺点:需要进行vxlan的数据包封包和解包会存在一定的性能损耗
|
||||
|
||||

|
||||
|
||||
配置方法
|
||||
|
||||
```yaml
|
||||
# 在calico的配置文件中。如下配置
|
||||
# Auto-detect the BGP IP address.
|
||||
- name: IP
|
||||
value: "autodetect"
|
||||
# Enable IPIP
|
||||
- name: CALICO_IPV4POOL_IPIP
|
||||
value: "Never"
|
||||
# Enable or Disable VXLAN on the default IP pool.
|
||||
- name: CALICO_IPV4POOL_VXLAN
|
||||
value: "Always"
|
||||
# Enable or Disable VXLAN on the default IPv6 IP pool.
|
||||
- name: CALICO_IPV6POOL_VXLAN
|
||||
value: "Always"
|
||||
|
||||
# 分割线========================
|
||||
# calico_backend: "bird"
|
||||
calico_backend: "vxlan"
|
||||
|
||||
|
||||
# - -felix-live
|
||||
# - -bird-live
|
||||
```
|
||||
|
||||
查看验证
|
||||
|
||||
<img src="pod与网络/image-20240904150012585.png" alt="image-20240904150012585" style="zoom: 80%;" />
|
||||
|
||||
VXLAN不需要BGP来建立节点间的邻接关系
|
||||
|
||||
<img src="pod与网络/image-20240904150019409.png" alt="image-20240904150019409" style="zoom: 80%;" />
|
||||
|
||||
### IPIP
|
||||
|
||||
- Linux原生内核支持
|
||||
- IPIP隧道的工作原理是将源主机的IP数据包封装在一个新的IP数据包中,新的IP数据包的目的地址是隧道的另一端。在隧道的另一端,接收方将解封装原始IP数据包,并将其传递到目标主机。IPIP隧道可以在不同的网络之间建立连接,例如在IPv4网络和IPv6网络之间建立连接。
|
||||
|
||||
<img src="pod与网络/image-20240904145716982.png" alt="image-20240904145716982" style="zoom:33%;" />
|
||||
|
||||
- 数据包封包:封包,在tunl0设备上将pod发来的数据包的mac层去掉,留下ip层封包。外层数据包目的ip地址根据路由得到。
|
||||
- 优点:只要k8s节点间三层互通,可以跨网段,对主机网关路由没有特殊要求。
|
||||
- 缺点:需要进行IPIP的数据包封包和解包会存在一定的性能损耗
|
||||
|
||||

|
||||
|
||||
配置方法
|
||||
|
||||
```yaml
|
||||
# 在calico的配置文件中。如下配置
|
||||
# Auto-detect the BGP IP address.
|
||||
- name: IP
|
||||
value: "autodetect"
|
||||
# Enable IPIP
|
||||
- name: CALICO_IPV4POOL_IPIP
|
||||
value: "Always"
|
||||
# Enable or Disable VXLAN on the default IP pool.
|
||||
- name: CALICO_IPV4POOL_VXLAN
|
||||
value: "Never"
|
||||
# Enable or Disable VXLAN on the default IPv6 IP pool.
|
||||
- name: CALICO_IPV6POOL_VXLAN
|
||||
value: "Never"
|
||||
```
|
||||
|
||||
查看验证
|
||||
|
||||
<img src="pod与网络/image-20240904150123553.png" alt="image-20240904150123553" style="zoom:80%;" />
|
||||
|
||||
IPIP模式需要BGP来建立节点间的邻接关系,VXLAN不需要
|
||||
|
||||
<img src="pod与网络/image-20240904150127316.png" alt="image-20240904150127316" style="zoom:80%;" />
|
||||
|
||||
### BGP
|
||||
|
||||
边界网关协议(Border Gateway Protocol,BGP)是互联网上一个核心的去中心化自治路由协议。它通过维护IP路由表或‘前缀’表来实现自治系统(AS)之间的可达性,属于矢量路由协议。BGP不使用传统的内部网关协议(IGP)的指标,而使用基于路径、网络策略或规则集来决定路由。因此,它更适合被称为矢量性协议,而不是路由协议。BGP,通俗的讲就是讲接入到机房的多条线路(如电信、联通、移动等)融合为一体,实现多线单IP,BGP机房的优点:服务器只需要设置一个IP地址,最佳访问路由是由网络上的骨⼲路由器根据路由跳数与其它技术指标来确定的,不会占用服务器的任何系统。
|
||||
|
||||

|
||||
|
||||
- 数据包封包:不需要进行数据包封包
|
||||
- 优点:不用封包解包,通过BGP协议可实现pod网络在主机间的三层可达
|
||||
- 缺点:跨网段时,配置较为复杂网络要求较高,主机网关路由也需要充当BGPSpeaker。
|
||||
|
||||
配置方法
|
||||
|
||||
```yaml
|
||||
# 在calico的配置文件中。如下配置
|
||||
# Auto-detect the BGP IP address.
|
||||
- name: IP
|
||||
value: "autodetect"
|
||||
# Enable IPIP
|
||||
- name: CALICO_IPV4POOL_IPIP
|
||||
value: "Off"
|
||||
# Enable or Disable VXLAN on the default IP pool.
|
||||
- name: CALICO_IPV4POOL_VXLAN
|
||||
value: "Never"
|
||||
# Enable or Disable VXLAN on the default IPv6 IP pool.
|
||||
- name: CALICO_IPV6POOL_VXLAN
|
||||
value: "Never"
|
||||
```
|
||||
|
||||
查看验证
|
||||
|
||||
<img src="pod与网络/image-20240904151418487.png" alt="image-20240904151418487" style="zoom:80%;" />
|
||||
|
||||
<img src="pod与网络/image-20240904151425020.png" alt="image-20240904151425020" style="zoom:80%;" />
|
||||
|
||||
<img src="pod与网络/image-20240904151434684.png" alt="image-20240904151434684" style="zoom:80%;" />
|
673
Kubernetes/Pod控制器.md
Normal file
@@ -0,0 +1,673 @@
|
||||
# 控制器
|
||||
|
||||
在Kubernetes中运⾏了⼀系列控制器来确保集群的当前状态与期望状态保持⼀致,它们就是Kubernetes集群内部的管理控制中⼼或者说是”中⼼⼤脑”。例如,ReplicaSet控制器负责维护集群中运⾏的Pod数量;Node控制器负责监控节点的状态,并在节点出现故障时,执⾏⾃动化修复流程,确保集群始终处于预期的⼯作状态。
|
||||
|
||||

|
||||
|
||||
- ReplicationController 和 ReplicaSet
|
||||
- Deployment
|
||||
- DaemonSet
|
||||
- StateFulSet
|
||||
- Job/CronJob
|
||||
- Horizontal Pod Autoscaling
|
||||
|
||||
# ReplicationController
|
||||
|
||||
ReplicationController(RC)⽤来确保容器应⽤的副本数始终保持在⽤户定义的副本数,即如果有容器异常退出,会⾃动创建新的Pod来替代;⽽如果异常多出来的容器也会⾃动回收;
|
||||
|
||||
在新版本的Kubernetes中建议使⽤ReplicaSet(RS)来取代ReplicationController。ReplicaSet跟ReplicationController没有本质的不同,只是名字不⼀样,并且ReplicaSet⽀持集合式的selector;
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: ReplicationController
|
||||
metadata:
|
||||
name: rc-demo
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
app: rc-demo
|
||||
template:
|
||||
metadata:
|
||||
name: rc-demo
|
||||
labels:
|
||||
app: rc-demo
|
||||
spec:
|
||||
containers:
|
||||
- name: rc-demo-container
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
# kubectl delete pod rc-demo-fl7vb
|
||||
# kubectl label pod rc-demo-fl7vb app=rc-demo1 --overwrite
|
||||
```
|
||||
|
||||
RS控制器
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
metadata:
|
||||
name: rc-ml-demo
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: rc-ml-demo
|
||||
template:
|
||||
metadata:
|
||||
name: rc-ml-demo
|
||||
labels:
|
||||
app: rc-ml-demo
|
||||
spec:
|
||||
containers:
|
||||
- name: rc-ml-demo-container
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
`selector.matchExpressions`
|
||||
rs在标签选择器上,除了可以定义键值对的选择形式,还支持matchExpressions字段,可以提供多种选择。
|
||||
目前支持的操作包括:
|
||||
|
||||
- In:label的值在某个列表中
|
||||
- NotIn:label的值不在某个列表中
|
||||
- Exists:某个label存在
|
||||
- DoesNotExist:某个label不存在
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
metadata:
|
||||
name: rc-me-demo
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchExpressions:
|
||||
- key: app
|
||||
operator: Exists
|
||||
template:
|
||||
metadata:
|
||||
name: rc-me-demo
|
||||
labels:
|
||||
app: rc-me-demo
|
||||
spec:
|
||||
containers:
|
||||
- name: rc-me-demo-container
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: ReplicaSet
|
||||
metadata:
|
||||
name: rc-me-in-demo
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchExpressions:
|
||||
- key: app
|
||||
operator: In
|
||||
values:
|
||||
- spring-k8s
|
||||
- xixixi
|
||||
template:
|
||||
metadata:
|
||||
name: rc-me-in-demo
|
||||
labels:
|
||||
app: rc-me-in-demo
|
||||
spec:
|
||||
containers:
|
||||
- name: rc-me-in-demo-container
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
# Deployment
|
||||
|
||||
声明性的东⻄是对终结果的陈述,表明意图⽽不是实现它的过程。在Kubernetes中,这就是说“应该有⼀个包含三个Pod的ReplicaSet”。
|
||||
命令式充当命令。声明式是被动的,⽽命令式是主动且直接的:“创建⼀个包含三个Pod的ReplicaSet”。
|
||||
|
||||
```bash
|
||||
$ kubectl replace -f deployment.yaml
|
||||
|
||||
$ kubectl apply -f deployment.yaml
|
||||
|
||||
$ kubectl diff -f deployment.yaml
|
||||
```
|
||||
|
||||
替换⽅式
|
||||
- kubectl replace:使⽤新的配置完全替换掉现有资源的配置。这意味着新配置将覆盖现有资源的所有字段和属性,包括未指定的字段,会导致整个资源的替换
|
||||
- kubectl apply:使⽤新的配置部分地更新现有资源的配置。它会根据提供的配置文件或参数,只更新与新配置中不同的部分,⽽不会覆盖整个资源的配置
|
||||
|
||||
字段级别的更新
|
||||
- kubectl replace:由于是完全替换,所以会覆盖所有字段和属性,⽆论是否在新配置中指定
|
||||
- kubectl apply:只更新与新配置中不同的字段和属性,保留未指定的字段不受影响
|
||||
|
||||
部分更新
|
||||
- kubectl replace:不⽀持部分更新,它会替换整个资源的配置
|
||||
- kubectl apply:⽀持部分更新,只会更新新配置中发⽣变化的部分,保留未指定的部分不受影响
|
||||
|
||||
与其他配置的影响
|
||||
- kubectl replace:不考虑其他资源配置的状态,直接替换资源的配置
|
||||
- kubectl apply:可以结合使⽤-f或-k参数,从文件或⽬录中读取多个资源配置,并根据当前集群中的资源状态进⾏更新
|
||||
|
||||
## Deployment介绍
|
||||
|
||||
Deployment为Pod和ReplicaSet提供了⼀个声明式定义(declarative)⽅法,⽤来替代以前的ReplicationController来⽅便的管理应⽤。典型的应⽤场景包括:
|
||||
|
||||
- 定义Deployment来创建Pod和ReplicaSet
|
||||
- 滚动升级和回滚应⽤
|
||||
- 扩容和缩容
|
||||
- 暂停和继续Deployment
|
||||
|
||||
<img src="Pod控制器/image-20240911095809938.png" alt="image-20240911095809938" style="zoom: 50%;" />
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: myapp-deploy
|
||||
name: myapp-deploy
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: myapp-deploy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: myapp-deploy
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
## 更新策略
|
||||
|
||||
Deployment可以保证在升级时只有⼀定数量的Pod是down的。默认的,它会确保⾄少有比期望的Pod数量少⼀个是up状态(最多⼀个不可⽤)
|
||||
|
||||
Deployment同时也可以确保只创建出超过期望数量的⼀定数量的Pod。默认的,它会确保最多比期望的Pod数量多⼀个的Pod是up的(最多1个surge)
|
||||
|
||||
未来的Kuberentes版本中,将从1-1变成25%-25%。目前的版本已经支持。
|
||||
|
||||
kubectlexplaindeploy.spec.strategy.type
|
||||
|
||||
- Recreate
|
||||
- 在创建出新的Pod之前会先杀掉所有已存在的Pod
|
||||
|
||||
- rollingUpdate
|
||||
- 滚动更新,就是杀死一部分,就启动一部分,在更新过程中,存在两个版本Pod
|
||||
|
||||
- maxSurge:指定超出副本数有⼏个,两种⽅式:1、指定数量2、百分比
|
||||
|
||||
- maxUnavailable:最多有⼏个不可⽤
|
||||
|
||||
|
||||
```bash
|
||||
$ kubectl create -f deployment.yaml --record
|
||||
# --record 参数可以记录命令,可以看到每次revision的变化
|
||||
|
||||
$ kubectl scale deployment myapp-deploy --replicas 10
|
||||
|
||||
$ kubectl autoscale deployment myapp-deploy --min=10 --max=15 --cpu-percent=80
|
||||
|
||||
$ kubectl set image deployment/myapp-deploy myapp=aaronxudocker/myapp:v2.0
|
||||
|
||||
$ kubectl rollout history deployment/myapp-deploy
|
||||
|
||||
$ kubectl rollout undo deployment/myapp-deploy
|
||||
```
|
||||
|
||||
可以通过设置`.spec.revisionHistoryLimit`项来指定deployment最多保留多少revision历史记录。默认的会保留所有的revision;如果将该项设置为0,Deployment就不允许回退了
|
||||
|
||||
## 金丝雀部署
|
||||
|
||||
⾦丝雀部署的名字灵感来源于17世纪英国矿井⼯⼈使⽤⾦丝雀作为⽡斯检测指标的传统⽅法。⾦丝雀对⽡斯这种⽓体⼗分敏感,空⽓中哪怕有极其微量的⽡斯,⾦丝雀也会停⽌歌唱。⽽当⽡斯含量超过⼀定限度时,虽然⼈类毫⽆察觉,⾦丝雀却早已毒发⾝亡。在采矿设备相对简陋的条件下,⼯⼈们每次下井都会带上⼀只⾦丝雀作为“⽡斯检测指标”,以便在危险状况下紧急撤离。
|
||||
|
||||
⾦丝雀部署的核⼼思想是在实际运⾏环境中的⼀⼩部分⽤户或流量上测试新版本的软件,⽽⼤部分⽤户或流量仍然使⽤旧版本。通过对新版本进⾏有限范围的实时测试和监控,可以及早发现潜在的问题,并减少对整个系统的冲击。
|
||||
|
||||
<img src="Pod控制器/image-20240911143520378.png" alt="image-20240911143520378" style="zoom:33%;" />
|
||||
|
||||
```bash
|
||||
$ kubectl patch deployment myapp-deploy -p '{"spec":{"strategy":{"rollingUpdate":{"maxSurge":1,"maxUnavailable":0}}}}'
|
||||
# 允许pod多出预期1个,不允许少一个
|
||||
|
||||
$ kubectl patch deployment myapp-deploy --patch '{"spec": {"template": {"spec": {"containers": [{"name": "myapp","image":"aaronxudocker/myapp:v2.0"}]}}}}' && kubectl rollout pause deploy myapp-deploy
|
||||
# 修改镜像为v2.0版本,并且立马停止回滚,可以看到pod数量复合上面的设置
|
||||
|
||||
$ kubectl rollout resume deploy myapp-deploy
|
||||
# 恢复回滚动作
|
||||
```
|
||||
|
||||
回滚相关的命令
|
||||
|
||||
```bash
|
||||
$ kubectl rollout status deployment myapp-deploy
|
||||
# 查看上次回滚的状态
|
||||
|
||||
$ kubectl rollout history deployment/myapp-deploy
|
||||
# 查看回滚的历史记录,需要配合--record使用
|
||||
|
||||
$ kubectl rollout undo deployment/myapp-deploy --to-revision=2
|
||||
# 状态回退到李四记录里面的2的状态
|
||||
|
||||
$ kubectl rollout pause deployment/myapp-deploy
|
||||
# 暂停回滚
|
||||
```
|
||||
|
||||
# DaemonSet
|
||||
|
||||
DaemonSet确保全部(或者⼀些)Node上运⾏⼀个Pod的副本。当有Node加入集群时,也会为他们新增⼀个Pod。当有Node从集群移除时,这些Pod也会被回收。删除DaemonSet将会删除它创建的所有Pod
|
||||
|
||||
使⽤DaemonSet的⼀些典型⽤法:
|
||||
- 运⾏集群存储daemon,例如在每个Node上运⾏`glusterd`、`ceph`
|
||||
- 在每个Node上运⾏⽇志收集daemon,例如`fluentd`、`logstash`
|
||||
- 在每个Node上运⾏监控daemon,例如PrometheusNode Exporter、`collectd`、Datadog代理、NewRelic代理,或Ganglia`gmond`
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
name: deamonset-demo
|
||||
namespace: default
|
||||
labels:
|
||||
app: deamonset-demo
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app: deamonset-demo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: deamonset-demo
|
||||
spec:
|
||||
containers:
|
||||
- name: deamonset-demo
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
```
|
||||
|
||||
# Job
|
||||
|
||||
Job负责批处理任务,即仅执⾏⼀次的任务,它保证批处理任务的⼀个或多个Pod成功结束
|
||||
|
||||
特殊说明
|
||||
- spec.template格式同Pod
|
||||
- RestartPolicy仅⽀持Never或OnFailure
|
||||
- 单个Pod时,默认Pod成功运⾏后Job即结束
|
||||
- `.spec.completions`标志Job结束需要成功运⾏的Pod个数,默认为1
|
||||
- `.spec.parallelism`标志并⾏运⾏的Pod的个数,默认为1
|
||||
- `spec.activeDeadlineSeconds`标志失败Pod的重试最⼤时间,超过这个时间不会继续重试
|
||||
|
||||
案例:使用马青公式计算圆周率后一千位,可以用来看下节点的计算能力
|
||||
|
||||
这个公式由英国天文学教授约翰·⻢青于1706年发现。他利⽤这个公式计算到了100位的圆周率。⻢青公式每计算⼀项可以得到1.4位的⼗进制精度。因为它的计算过程中被乘数和被除数都不⼤于⻓整数,所以可以很容易地在计算机上编程实现
|
||||
$$
|
||||
\frac{\pi}{4} = 4 \tan^{-1}\left(\frac{1}{5}\right) - \tan^{-1}\left(\frac{1}{239}\right)
|
||||
$$
|
||||
|
||||
```yaml
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: job-demo
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
name: job-demo-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: job-demo
|
||||
image: aaronxudocker/tools:maqingpythonv1
|
||||
restartPolicy: Never
|
||||
```
|
||||
|
||||
Job负责批处理任务,即仅执⾏⼀次的任务,它保证批处理任务的⼀个或多个Pod成功结束
|
||||
|
||||
## 错误的Job任务
|
||||
|
||||
```yaml
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: rand
|
||||
spec:
|
||||
template:
|
||||
metadata:
|
||||
name: rand
|
||||
spec:
|
||||
containers:
|
||||
- name: rand
|
||||
image: aaronxudocker/tools:randexitv1
|
||||
args: ["--exitcode=1"]
|
||||
restartPolicy: Never
|
||||
```
|
||||
|
||||
Job控制器会记录成功的次数
|
||||
|
||||
```bash
|
||||
$ kubectl get job
|
||||
NAME COMPLETIONS DURATION AGE
|
||||
rand 0/1 30s 30s
|
||||
```
|
||||
|
||||
改为产生随机返回值
|
||||
|
||||
```yaml
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
name: rand
|
||||
spec:
|
||||
completions: 10
|
||||
parallelism: 5
|
||||
template:
|
||||
metadata:
|
||||
name: rand
|
||||
spec:
|
||||
containers:
|
||||
- name: rand
|
||||
image: aaronxudocker/tools:randexitv1
|
||||
restartPolicy: Never
|
||||
```
|
||||
|
||||
可以看到并行启动5个pod,直到成功为止
|
||||
|
||||
```bash
|
||||
$ kubectl get job -w
|
||||
NAME COMPLETIONS DURATION AGE
|
||||
rand 2/10 24s 24s
|
||||
rand 2/10 50s 50s
|
||||
rand 2/10 52s 52s
|
||||
rand 2/10 58s 58s
|
||||
rand 2/10 59s 59s
|
||||
rand 3/10 59s 59s
|
||||
rand 3/10 60s 60s
|
||||
rand 5/10 60s 60s
|
||||
rand 5/10 61s 61s
|
||||
rand 5/10 62s 62s
|
||||
rand 5/10 67s 67s
|
||||
rand 5/10 68s 68s
|
||||
rand 8/10 68s 68s
|
||||
rand 8/10 69s 69s
|
||||
rand 8/10 69s 69s
|
||||
|
||||
|
||||
$ kubectl get job -o yaml
|
||||
......
|
||||
spec:
|
||||
backoffLimit: 6
|
||||
# 6次尝试失败,就达到尝试上限
|
||||
```
|
||||
|
||||
# CronJob
|
||||
|
||||
CronJob管理基于时间的Job,即:
|
||||
- 在给定时间点只运⾏⼀次
|
||||
- 周期性地在给定时间点运⾏
|
||||
|
||||
使⽤条件:当前使⽤的Kubernetes集群,版本>=1.8(对CronJob)
|
||||
|
||||
典型的⽤法如下所⽰:
|
||||
- 在给定的时间点调度Job运⾏
|
||||
- 创建周期性运⾏的Job,例如:数据库备份、发送邮件
|
||||
|
||||
|
||||
|
||||
- `.spec.schedule`:调度,必需字段,指定任务运⾏周期,格式同Cron
|
||||
- `.spec.jobTemplate`:Job模板,必需字段,指定需要运⾏的任务,格式同Job
|
||||
- `.spec.startingDeadlineSeconds`:启动Job的期限(秒级别),该字段是可选的。如果因为任何原因⽽错过了被调度的时间,那么错过执⾏时间的Job将被认为是失败的。如果没有指定,则没有期限
|
||||
- `.spec.concurrencyPolicy`:并发策略,该字段也是可选的。它指定了如何处理被CronJob创建的Job的并发执⾏。只允许指定下⾯策略中的⼀种:
|
||||
- `Allow`(默认):允许并发运⾏Job
|
||||
- `Forbid`:禁⽌并发运⾏,如果前⼀个还没有完成,则直接跳过下⼀个
|
||||
- `Replace`:取消当前正在运⾏的Job,⽤⼀个新的来替换
|
||||
- 注意,当前策略只能应⽤于同⼀个CronJob创建的Job。如果存在多个CronJob,它们创建的Job之间总是允许并发运⾏。
|
||||
|
||||
- `.spec.suspend`:挂起,该字段也是可选的。如果设置为`true`,后续所有执⾏都会被挂起。它对已经开始执⾏的Job不起作⽤。默认值为`false`
|
||||
- `.spec.successfulJobsHistoryLimit`和`.spec.failedJobsHistoryLimit`:历史限制,是可选的字段。它们指定了可以保留多少完成和失败的Job。默认情况下,它们分别设置为`3`和`1`。设置限制的值为`0`,相关类型的Job完成后将不会被保留
|
||||
|
||||
```yaml
|
||||
apiVersion: batch/v1
|
||||
kind: CronJob
|
||||
metadata:
|
||||
name: cronjob-demo
|
||||
spec:
|
||||
schedule: "*/1 * * * *"
|
||||
jobTemplate:
|
||||
spec:
|
||||
completions: 3
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- name: cronjob-demo-container
|
||||
image: aaronxudocker/tools:busybox
|
||||
args:
|
||||
- /bin/sh
|
||||
- -c
|
||||
- date; echo Hello from the Kubernetes cluster
|
||||
restartPolicy: OnFailure
|
||||
```
|
||||
|
||||
创建job操作应该是幂等的
|
||||
|
||||
# Horizontal Pod Autoscaler(HPA)
|
||||
|
||||
在前面的课程中,我们已经可以实现通过手工执行`kubectl scale`命令实现Pod扩容或缩容,但是这显然不符合Kubernetes的定位目标--自动化、智能化。 Kubernetes期望可以实现通过监测Pod的使用情况,实现pod数量的自动调整,于是就产生了Horizontal Pod Autoscaler(HPA)这种控制器。
|
||||
|
||||
HPA可以获取每个Pod利用率,然后和HPA中定义的指标进行对比,同时计算出需要伸缩的具体值,最后实现Pod的数量的调整。其实HPA与之前的Deployment一样,也属于一种Kubernetes资源对象,它通过追踪分析RC控制的所有目标Pod的负载变化情况,来确定是否需要针对性地调整目标Pod的副本数,这是HPA的实现原理。
|
||||
|
||||
<img src="Pod控制器/image-20240912140819583.png" alt="image-20240912140819583" style="zoom:33%;" />
|
||||
|
||||
查看pod的资源
|
||||
|
||||
```bash
|
||||
# 由于没安装metrics服务,所以无法查看pod资源情况
|
||||
$ kubectl top pod myapp-deploy-57bff895d5-cvlmg
|
||||
error: Metrics API not available
|
||||
```
|
||||
|
||||
安装metrics-server
|
||||
|
||||
```bash
|
||||
$ wget https://github.com/kubernetes-sigs/metrics-server/releases/download/v0.7.2/components.yaml
|
||||
|
||||
# 修改配置文件
|
||||
- args:
|
||||
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname,InternalDNS,ExternalDNS
|
||||
- --kubelet-insecure-tls
|
||||
image: registry.k8s.io/metrics-server/metrics-server:v0.7.2
|
||||
|
||||
$ kubectl apply -f components.yaml
|
||||
|
||||
$ kubectl get pod -n kube-system |grep metrics
|
||||
metrics-server-5c7fbc6ff6-fssfd 1/1 Running 0 2m42s
|
||||
|
||||
# 使用kubectl top node 查看资源使用情况
|
||||
$ kubectl top node
|
||||
NAME CPU(cores) CPU% MEMORY(bytes) MEMORY%
|
||||
master01 116m 2% 1943Mi 55%
|
||||
node01 48m 1% 961Mi 27%
|
||||
node02 61m 1% 1161Mi 32%
|
||||
|
||||
$ kubectl top pod myapp-deploy-57bff895d5-cvlmg
|
||||
NAME CPU(cores) MEMORY(bytes)
|
||||
myapp-deploy-57bff895d5-cvlmg 0m 4Mi
|
||||
```
|
||||
|
||||
准备deployment和service资源
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx-pod
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
resources:
|
||||
limits:
|
||||
cpu: "1"
|
||||
requests:
|
||||
cpu: "100m"
|
||||
```
|
||||
|
||||
创建service并且查看
|
||||
|
||||
```bash
|
||||
$ kubectl expose deployment nginx --type=NodePort --port=80
|
||||
|
||||
$ kubectl get deployment,pod,svc
|
||||
NAME READY UP-TO-DATE AVAILABLE AGE
|
||||
deployment.apps/nginx 1/1 1 1 114s
|
||||
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
pod/nginx-7b4664bb57-pgllw 1/1 Running 0 114s
|
||||
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service/nginx NodePort 10.8.51.98 <none> 80:30482/TCP 37s
|
||||
```
|
||||
|
||||
创建HPA资源
|
||||
|
||||
```yaml
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: hpa-demo
|
||||
spec:
|
||||
minReplicas: 1 #最小pod数量
|
||||
maxReplicas: 10 #最大pod数量
|
||||
behavior:
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: 10 # 在连续10秒内稳定后才考虑进一步扩展操作
|
||||
policies:
|
||||
- type: Percent
|
||||
value: 100 # 当超过当前副本数的100%才进行扩展
|
||||
periodSeconds: 3 # 两次扩容之间的最小时间间隔
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: 300 # 在连续300秒内稳定后才考虑缩减操作
|
||||
policies:
|
||||
- type: Percent
|
||||
value: 10 # 当低于当前副本数的10%才进行缩减
|
||||
periodSeconds: 3 # 两次缩减之间的最小时间间隔
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 10 # CPU平均利用率目标设为10%
|
||||
scaleTargetRef: # 指定要控制的nginx信息
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: nginx
|
||||
```
|
||||
|
||||
查看hpa资源
|
||||
|
||||
```bash
|
||||
$ kubectl get hpa -w
|
||||
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 1 20s
|
||||
```
|
||||
|
||||
使用压力测试工具
|
||||
|
||||
```bash
|
||||
$ ab -c 1000 -n 1000000 http://192.168.173.100:30482/
|
||||
|
||||
$ kubectl get hpa -w
|
||||
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 1 20s
|
||||
hpa-demo Deployment/nginx 1%/10% 1 10 1 105s
|
||||
hpa-demo Deployment/nginx 366%/10% 1 10 1 2m
|
||||
hpa-demo Deployment/nginx 522%/10% 1 10 2 2m15s
|
||||
hpa-demo Deployment/nginx 352%/10% 1 10 4 2m30s
|
||||
hpa-demo Deployment/nginx 189%/10% 1 10 8 2m45s
|
||||
hpa-demo Deployment/nginx 103%/10% 1 10 10 3m
|
||||
hpa-demo Deployment/nginx 68%/10% 1 10 10 3m15s
|
||||
```
|
||||
|
||||
压力测试停止之后
|
||||
|
||||
```bash
|
||||
$ kubectl get hpa -w
|
||||
NAME REFERENCE TARGETS MINPODS MAXPODS REPLICAS AGE
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 10 8m45s
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 9 9m
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 8 9m15s
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 7 9m30s
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 6 9m45s
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 5 10m
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 4 10m
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 3 10m
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 2 10m
|
||||
hpa-demo Deployment/nginx 0%/10% 1 10 1 11m
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
BIN
Kubernetes/Pod控制器/image-20240910143437460.png
Normal file
After Width: | Height: | Size: 295 KiB |
BIN
Kubernetes/Pod控制器/image-20240911095809938.png
Normal file
After Width: | Height: | Size: 208 KiB |
BIN
Kubernetes/Pod控制器/image-20240911143520378.png
Normal file
After Width: | Height: | Size: 370 KiB |
BIN
Kubernetes/Pod控制器/image-20240912140819583.png
Normal file
After Width: | Height: | Size: 74 KiB |
766
Kubernetes/Pod生命周期.md
Normal file
@@ -0,0 +1,766 @@
|
||||
# Pod生命周期
|
||||
|
||||

|
||||
|
||||
init容器与普通的容器非常像,除了如下两点:
|
||||
|
||||
- init容器总是运行到成功完成为止
|
||||
- 每个init容器都必须在下一个init容器启动之前成功完成
|
||||
|
||||
如果Pod的Init容器失败,Kubernetes会不断地重启该Pod,直到Init容器成功为止。然而,如果Pod对应的restartPolicy为Never,它不会重新启动
|
||||
|
||||
## 检测initC的阻塞性
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: initc-1
|
||||
labels:
|
||||
name: initc
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp-container
|
||||
image: centos:7
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
command: ['sh', '-c', 'echo The app is running && sleep 10']
|
||||
initContainers:
|
||||
- name: init-myservice
|
||||
image: aaronxudocker/tools:busybox
|
||||
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
|
||||
- name: init-mydb
|
||||
image: aaronxudocker/tools:busybox
|
||||
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
|
||||
```
|
||||
|
||||
```bash
|
||||
# 查看日志,看到不停的在尝试
|
||||
$ kubectl logs initc-1 -c init-myservice
|
||||
|
||||
# 创建svc资源,会通过CoreDNS自动将myservice解析成功,详解看后面的service部分
|
||||
$ kubectl create svc clusterip myservice --tcp=80:80
|
||||
```
|
||||
|
||||
如果initc执行失败了,那么就会重新执行所有的initc
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: initc-2
|
||||
labels:
|
||||
name: initc
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp-container
|
||||
image: centos:7
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
command: ['sh', '-c', 'echo The app is running && sleep 10']
|
||||
initContainers:
|
||||
- name: init-myservice
|
||||
image: aaronxudocker/tools:busybox
|
||||
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
|
||||
- name: randexit
|
||||
image: aaronxudocker/tools:randexitv1
|
||||
args: ['--exitcode=1']
|
||||
```
|
||||
|
||||
```bash
|
||||
$ kubectl get pod
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
initc-1 0/1 Init:1/2 0 16m
|
||||
initc-2 0/1 Init:Error 5 (97s ago) 3m42s
|
||||
$ kubectl logs initc-2 -c randexit
|
||||
休眠 4 秒,返回码为 1!
|
||||
```
|
||||
|
||||
如果我们让initc的返回码直接为0,那么就可以看到pod正常启动
|
||||
|
||||
```bash
|
||||
$ kubectl get pod
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
initc-1 0/1 Init:1/2 0 19m
|
||||
initc-2 1/1 Running 1 (7s ago) 72s
|
||||
```
|
||||
|
||||
- InitC与应用容器具备不同的镜像,可以把一些危险的工具放置在initC中,进行使用
|
||||
- initC多个之间是线性启动的,所以可以做一些延迟性的操作
|
||||
- initC无法定义readinessProbe,其它以外同应用容器定义无异
|
||||
|
||||
# Pod探针
|
||||
|
||||
探针是由kubelet对容器执行的定期诊断。要执行诊断,kubelet调用由容器实现的Handler。有三种类型的处理程序:
|
||||
|
||||
- ExecAction:在容器内执行指定命令。如果命令退出时返回码为0则认为诊断成功
|
||||
- TCPSocketAction:对指定端口上的容器的IP地址进行TCP检查。如果端口打开,则诊断被认为是成功的
|
||||
- HTTPGetAction:对指定的端口和路径上的容器的IP地址执行HTTPGet请求。如果响应的状态码⼤于等于200且小于400,则诊断被认为是成功的
|
||||
|
||||
每次探测都将获得以下三种结果之一:
|
||||
|
||||
- 成功:容器通过了诊断。
|
||||
- 失败:容器未通过诊断。
|
||||
- 未知:诊断失败,因此不会采取任何行动
|
||||
|
||||
## 探针的分类
|
||||
|
||||
- startupProbe:开始探针,开始检测吗?
|
||||
- livenessProbe:存活探针,还活着吗?
|
||||
- readinessProbe:就绪探针,准备提供服务了吗?
|
||||
|
||||
### readinessProbe就绪探针
|
||||
|
||||
介绍:k8s通过添加就绪探针,解决尤其是在扩容时保证提供给用户的服务都是可用的。
|
||||
|
||||
> 选项说明
|
||||
> - initialDelaySeconds:容器启动后要等待多少秒后就探针开始工作,单位“秒”,默认是0秒,最小值是0
|
||||
> - periodSeconds:执行探测的时间间隔(单位是秒),默认为10s,单位“秒”,最小值是1
|
||||
> - timeoutSeconds:探针执行检测请求后,等待响应的超时时间,默认为1s,单位“秒”,最小值是1
|
||||
> - successThreshold:探针检测失败后认为成功的最小连接成功次数,默认值为1。必须为1才能激活和启动。最小值为1。
|
||||
> - failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,默认值为3,最小值为1。
|
||||
|
||||
#### 就绪探针实验
|
||||
|
||||
- 基于 HTTP GET 方式
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: readiness-httpget-pod
|
||||
labels:
|
||||
name: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: readiness-httpget-container
|
||||
image: nginx:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 80
|
||||
path: /index1.html
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 当前处于没有就绪的状态
|
||||
$ kubectl get pod
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
readiness-httpget-pod 0/1 Running 0 4m16s
|
||||
|
||||
# 创建一个index1.html
|
||||
$ kubectl exec -it readiness-httpget-pod -c readiness-httpget-container -- /bin/bash
|
||||
root@readiness-httpget-pod:/# echo "hehe" > /usr/share/nginx/html/index1.html
|
||||
|
||||
# 查看就已经处于就绪的状态了
|
||||
$ kubectl get pod
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
readiness-httpget-pod 1/1 Running 0 5m40s
|
||||
|
||||
# 在运行过程中,就绪探测一直存在,如果不满足条件,会回到未就绪的情况
|
||||
```
|
||||
|
||||
- 基于 EXEC 方式
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: readiness-exec-pod
|
||||
labels:
|
||||
name: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: readiness-exec-container
|
||||
image: aaronxudocker/tools:busybox
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/sh", "-c", "touch /tmp/live ; sleep 60; rm -rf /tmp/live; sleep 3600"]
|
||||
readinessProbe:
|
||||
exec:
|
||||
command: ["test", "-e", "/tmp/live"]
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 可以看到在60秒后就变成非就绪状态了
|
||||
$ kubectl get pod -w
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
readiness-exec-pod 1/1 Running 0 7s
|
||||
readiness-exec-pod 0/1 Running 0 69s
|
||||
```
|
||||
|
||||
基于TCP Check方式
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: readiness-tcp-pod
|
||||
labels:
|
||||
name: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: readiness-tcp-container
|
||||
image: nginx:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
#### 就绪探针流量测试
|
||||
|
||||
<img src="Pod生命周期/image-20240906140721572.png" alt="image-20240906140721572" style="zoom: 25%;" />
|
||||
|
||||
在匹配可用pod的时候,标签必须匹配,状态必须是就绪状态。
|
||||
|
||||
```yaml
|
||||
# pod-1.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-1
|
||||
labels:
|
||||
app: myapp
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp-1
|
||||
image: nginx:latest
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# pod-2.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-2
|
||||
labels:
|
||||
app: myapp
|
||||
version: v1
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp-1
|
||||
image: nginx:latest
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# 确认状态已经就绪
|
||||
$ kubectl get pod --show-labels
|
||||
NAME READY STATUS RESTARTS AGE LABELS
|
||||
pod-1 1/1 Running 0 2m31s app=myapp
|
||||
pod-2 1/1 Running 0 32s app=myapp,version=v1
|
||||
```
|
||||
|
||||
创建service资源
|
||||
|
||||
```bash
|
||||
# 注意myapp就是标签为app=myapp的pod
|
||||
# 此处不需要理解,后面会细讲,只是用来验证就绪探针对流量的影响
|
||||
# 此处的作用是形成多个pod的负载均衡
|
||||
$ kubectl create svc clusterip myapp --tcp=80:80
|
||||
service/myapp created
|
||||
$ kubectl get svc
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
kubernetes ClusterIP 10.0.0.1 <none> 443/TCP 46h
|
||||
myapp ClusterIP 10.8.74.201 <none> 80/TCP 6s
|
||||
```
|
||||
|
||||
将两个pod中的主页文件修改一下,用来作为区分
|
||||
|
||||
```bash
|
||||
# 如果pod中只有一个main容器,那么在exec的时候就不需要指定容器
|
||||
$ kubectl exec -it pod-1 -- /bin/bash
|
||||
root@pod-1:/# echo pod-1 > /usr/share/nginx/html/index.html
|
||||
$ kubectl exec -it pod-2 -- /bin/bash
|
||||
root@pod-2:/# echo pod-2 > /usr/share/nginx/html/index.html
|
||||
```
|
||||
|
||||
验证负载均衡的状态
|
||||
|
||||
```bash
|
||||
$ curl 10.8.74.201
|
||||
pod-2
|
||||
$ curl 10.8.74.201
|
||||
pod-1
|
||||
$ curl 10.8.74.201
|
||||
pod-2
|
||||
```
|
||||
|
||||
创建一个label为 `app: test` 的pod,看下是否能被匹配
|
||||
|
||||
```yaml
|
||||
# 3.yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-3
|
||||
labels:
|
||||
app: test
|
||||
version: v1
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp-1
|
||||
image: nginx:latest
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
查看pod状态,修改 `pod-3` 的网页内容
|
||||
|
||||
```bash
|
||||
$ kubectl get pod --show-labels
|
||||
NAME READY STATUS RESTARTS AGE LABELS
|
||||
pod-1 1/1 Running 0 11m app=myapp
|
||||
pod-2 1/1 Running 0 9m57s app=myapp,version=v1
|
||||
pod-3 1/1 Running 0 51s app=test,version=v1
|
||||
|
||||
$ kubectl exec -it pod-3 -- /bin/bash
|
||||
root@pod-3:/# echo pod-3 > /usr/share/nginx/html/index.html
|
||||
```
|
||||
|
||||
验证负载均衡的状态,发现 `pod-3` 并不能被匹配上
|
||||
|
||||
```bash
|
||||
$ curl 10.8.74.201
|
||||
pod-1
|
||||
$ curl 10.8.74.201
|
||||
pod-2
|
||||
$ curl 10.8.74.201
|
||||
pod-1
|
||||
$ curl 10.8.74.201
|
||||
pod-2
|
||||
$ curl 10.8.74.201
|
||||
pod-2
|
||||
$ curl 10.8.74.201
|
||||
pod-1
|
||||
```
|
||||
|
||||
创建一个不满足就绪条件的 `pod-4`
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: pod-4
|
||||
labels:
|
||||
app: myapp
|
||||
version: v1
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp-1
|
||||
image: nginx:latest
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 80
|
||||
path: /index1.html
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
修改主页内容
|
||||
|
||||
```bash
|
||||
$ kubectl exec -it pod-4 -- /bin/bash
|
||||
root@pod-4:/# echo pod-4 > /usr/share/nginx/html/index.html
|
||||
```
|
||||
|
||||
查看状态是未就绪的
|
||||
|
||||
```bash
|
||||
$ kubectl get pod --show-labels
|
||||
NAME READY STATUS RESTARTS AGE LABELS
|
||||
pod-1 1/1 Running 0 17m app=myapp
|
||||
pod-2 1/1 Running 0 15m app=myapp,version=v1
|
||||
pod-3 1/1 Running 0 6m49s app=test,version=v1
|
||||
pod-4 0/1 Running 0 41s app=myapp,version=v1
|
||||
```
|
||||
|
||||
验证负载均衡
|
||||
|
||||
```bash
|
||||
$ curl 10.8.74.201
|
||||
pod-1
|
||||
$ curl 10.8.74.201
|
||||
pod-2
|
||||
$ curl 10.8.74.201
|
||||
pod-1
|
||||
$ curl 10.8.74.201
|
||||
pod-2
|
||||
$ curl 10.8.74.201
|
||||
pod-2
|
||||
$ curl 10.8.74.201
|
||||
pod-1
|
||||
```
|
||||
|
||||
满足 `pod-4` 的就绪条件
|
||||
|
||||
```bash
|
||||
$ kubectl exec -it pod-4 -- /bin/bash
|
||||
root@pod-4:/# touch /usr/share/nginx/html/index1.html
|
||||
```
|
||||
|
||||
再次验证负载均衡
|
||||
|
||||
```bash
|
||||
$ curl 10.8.74.201
|
||||
pod-1
|
||||
$ curl 10.8.74.201
|
||||
pod-2
|
||||
$ curl 10.8.74.201
|
||||
pod-4
|
||||
```
|
||||
|
||||
### livenessProbe存活探针
|
||||
|
||||
介绍:k8s通过添加存活探针,解决虽然活着但是已经死了的问题。
|
||||
|
||||
> 选项说明
|
||||
> - initialDelaySeconds:容器启动后要等待多少秒后就探针开始工作,单位“秒”,默认是0秒,最小值是0
|
||||
> - periodSeconds:执行探测的时间间隔(单位是秒),默认为10s,单位“秒”,最小值是1
|
||||
> - timeoutSeconds:探针执行检测请求后,等待响应的超时时间,默认为1s,单位“秒”,最小值是1
|
||||
> - successThreshold:探针检测失败后认为成功的最小连接成功次数,默认值为1。必须为1才能激活和启动。最小值为1。
|
||||
> - failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,默认值为3,最小值为1。
|
||||
|
||||
#### 存活探针实验
|
||||
|
||||
- 基于 Exec 方式
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: liveness-exec-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: liveness-exec-container
|
||||
image: aaronxudocker/tools:busybox
|
||||
imagePullPolicy: IfNotPresent
|
||||
command: ["/bin/sh", "-c", "touch /tmp/live; sleep 60; rm -rf /tmp/live; sleep 3600"]
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["test", "-e", "/tmp/live"]
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
一段时间以后,可以看到发生重启的事件
|
||||
|
||||
```bash
|
||||
$ kubectl get pod -w
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
liveness-exec-pod 1/1 Running 0 11s
|
||||
liveness-exec-pod 1/1 Running 1 (1s ago) 101s
|
||||
liveness-exec-pod 1/1 Running 2 (1s ago) 3m20s
|
||||
```
|
||||
|
||||
- 基于 HTTP Get 方式
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: liveness-httpget-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: liveness-httpget-container
|
||||
image: nginx:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 80
|
||||
path: /index.html
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
timeoutSeconds: 3
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
删除 `index.html` 文件,使其不满足存活探测条件
|
||||
|
||||
```bash
|
||||
# 在删除index.html之后,可以看到命令行退出了
|
||||
$ kubectl exec -it liveness-httpget-pod -- /bin/bash
|
||||
root@liveness-httpget-pod:/# rm -f /usr/share/nginx/html/index.html
|
||||
root@liveness-httpget-pod:/# command terminated with exit code 137
|
||||
```
|
||||
|
||||
重新查看pod状态,可以看到重启了
|
||||
|
||||
```bash
|
||||
$ kubectl get pod
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
liveness-httpget-pod 1/1 Running 1 (48s ago) 2m39s
|
||||
```
|
||||
|
||||
在运行此pod的节点上查看docker的名字,容器名字是 `集群名-pod名-容器名-hash-重启次数(初始是0)`
|
||||
|
||||
```bash
|
||||
$ docker ps -a |grep liveness-exec-container
|
||||
18c5ba02d684 39286ab8a5e1 "/docker-entrypoint.…" About a minute ago Up About a minute k8s_liveness-exec-container_liveness-httpget-pod_default_aa36504e-23a9-48d1-988c-4de0398c474f_1
|
||||
54b3a04bd6b0 39286ab8a5e1 "/docker-entrypoint.…" 3 minutes ago Exited (0) About a minute ago k8s_liveness-exec-container_liveness-httpget-pod_default_aa36504e-23a9-48d1-988c-4de0398c474f_0
|
||||
```
|
||||
|
||||
- 基于 TCP Check 方式
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: liveness-tcp-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: liveness-tcp-container
|
||||
image: nginx:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 80
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
timeoutSeconds: 3
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
### startupProbe启动探针
|
||||
|
||||
介绍:k8s在1.16版本后增加startupProbe探针,主要解决在复杂的程序中readinessProbe、livenessProbe探针无法更好的判断程序是否启动、是否存活。
|
||||
|
||||
> 选项说明
|
||||
> - initialDelaySeconds:容器启动后要等待多少秒后就探针开始工作,单位“秒”,默认是0秒,最小值是0
|
||||
> - periodSeconds:执行探测的时间间隔(单位是秒),默认为10s,单位“秒”,最小值是1
|
||||
> - timeoutSeconds:探针执行检测请求后,等待响应的超时时间,默认为1s,单位“秒”,最小值是1
|
||||
> - successThreshold:探针检测失败后认为成功的最小连接成功次数,默认值为1。必须为1才能激活和启动。最小值为1。
|
||||
> - failureThreshold:探测失败的重试次数,重试一定次数后将认为失败,默认值为3,最小值为1。
|
||||
|
||||
#### 启动探针实验
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: startupprobe-1
|
||||
spec:
|
||||
containers:
|
||||
- name: startupprobe
|
||||
image: nginx:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 80
|
||||
path: /index2.html
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /index1.html
|
||||
port: 80
|
||||
periodSeconds: 10
|
||||
failureThreshold: 30
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
|
||||
# 应用程序将会有最多 5 分钟 failureThreshold * periodSeconds(30 * 10 = 300s)的时间来完成其启动过程。如果到超时都没有启动完成,就会重启。
|
||||
```
|
||||
|
||||
创建 `index1.html` 文件
|
||||
|
||||
```bash
|
||||
$ kubectl exec -it pod/startupprobe-1 -- /bin/bash
|
||||
root@startupprobe-1:/# touch /usr/share/nginx/index1.html
|
||||
|
||||
# 查看依旧是未就绪的状态
|
||||
$ kubectl get pod
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
startupprobe-1 0/1 Running 0 42s
|
||||
|
||||
# 创建index2.html文件
|
||||
$ kubectl exec -it pod/startupprobe-1 -- /bin/bash
|
||||
root@startupprobe-1:/# touch /usr/share/nginx/index2.html
|
||||
|
||||
# 查看状态
|
||||
$ kubectl get pod
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
startupprobe-1 1/1 Running 0 43s
|
||||
```
|
||||
|
||||
此时删掉启动探测的 `index1.html` 会怎样?
|
||||
|
||||
# Pod钩子
|
||||
|
||||
Podhook(钩子)是由Kubernetes管理的kubelet发起的,当容器中的进程启动前或者容器中的进程终止之前运行,这是包含在容器的⽣命周期之中。可以同时为Pod中的所有容器都配置hook
|
||||
|
||||
Hook的类型包括两种:
|
||||
- exec:执行一段命令
|
||||
- HTTP:发送HTTP请求
|
||||
|
||||
在k8s中,理想的状态是pod优雅释放,但是并不是每一个Pod都会这么顺利
|
||||
- Pod卡死,处理不了优雅退出的命令或者操作
|
||||
- 优雅退出的逻辑有BUG,陷入死循环
|
||||
- 代码问题,导致执行的命令没有效果
|
||||
|
||||
对于以上问题,k8s的Pod终止流程中还有一个"最多可以容忍的时间",即graceperiod(在pod.spec.terminationGracePeriodSeconds字段定义),这个值默认是30秒,当我们执行kubectl delete的时候也可以通过--grace-period参数显示指定一个优雅退出时间来覆盖Pod中的配置,如果我们配置的grace period超过时间之后,k8s就只能选择强制kill Pod。
|
||||
|
||||
值得注意的是,这与preStopHook和SIGTERM信号并行发⽣。k8s不会等待preStopHook完成。你的应用程序应在terminationGracePeriod之前退出。
|
||||
|
||||
## Pod钩子实验
|
||||
|
||||
- 基于 exec 方式
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: hook-exec-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: hook-exec-container
|
||||
image: nginx:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
lifecycle:
|
||||
postStart:
|
||||
exec:
|
||||
command: ["/bin/sh", "-c", "echo postStart > /usr/share/message"]
|
||||
preStop:
|
||||
exec:
|
||||
command: ["/bin/sh", "-c", "echo preStart > /usr/share/message"]
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
```
|
||||
|
||||
在这个pod的内部,写一个循环查看此文件的shell命令
|
||||
|
||||
```bash
|
||||
$ kubectl exec -it pod/hook-exec-pod -- /bin/bash
|
||||
root@hook-exec-pod:/# while true;
|
||||
> do
|
||||
> cat /usr/share/message
|
||||
> done
|
||||
|
||||
# 删除此pod就能看到结束的钩子信息了
|
||||
```
|
||||
|
||||
- 基于 HTTP Get 方式
|
||||
|
||||
```bash
|
||||
# 开启一个测试 webserver
|
||||
$ docker run -it --rm -p 1234:80 nginx:latest
|
||||
```
|
||||
|
||||
启动一个pod,然后再删除,查看nginx容器日志,可以看到记录了这两次的http请求
|
||||
|
||||
```yaml
|
||||
2024/09/06 07:35:23 [error] 29#29: *1 open() "/usr/share/nginx/html/poststarthook.html" failed (2: No such file or directory), client: 192.168.173.101, server: localhost, request: "GET /poststarthook.html HTTP/1.1", host: "192.168.173.100:1234"
|
||||
192.168.173.101 - - [06/Sep/2024:07:35:23 +0000] "GET /poststarthook.html HTTP/1.1" 404 153 "-" "kube-lifecycle/1.29" "-"
|
||||
2024/09/06 07:35:45 [error] 29#29: *1 open() "/usr/share/nginx/html/prestophook.html" failed (2: No such file or directory), client: 192.168.173.101, server: localhost, request: "GET /prestophook.html HTTP/1.1", host: "192.168.173.100:1234"
|
||||
192.168.173.101 - - [06/Sep/2024:07:35:45 +0000] "GET /prestophook.html HTTP/1.1" 404 153 "-" "kube-lifecycle/1.29" "-"
|
||||
```
|
||||
|
||||
# 总结
|
||||
|
||||
Pod⽣命周期中的initC、startupProbe、livenessProbe、readinessProbe、hook都是可以并且存在的,可以选择全部、部分或者完全不用。
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: lifecycle-pod
|
||||
labels:
|
||||
app: lifecycle-pod
|
||||
spec:
|
||||
containers:
|
||||
- name: busybox-container
|
||||
image: aaronxudocker/tools:busybox
|
||||
command: ["/bin/sh","-c","touch /tmp/live ; sleep 600; rm -rf /tmp/live; sleep 3600"]
|
||||
livenessProbe:
|
||||
exec:
|
||||
command: ["test","-e","/tmp/live"]
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
lifecycle:
|
||||
postStart:
|
||||
httpGet:
|
||||
host: 192.168.173.100
|
||||
path: poststarthook.html
|
||||
port: 1234
|
||||
preStop:
|
||||
httpGet:
|
||||
host: 192.168.173.100
|
||||
path: prestophook.html
|
||||
port: 1234
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
- name: myapp-container
|
||||
image: nginx:latest
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
port: 80
|
||||
path: /index.html
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
timeoutSeconds: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 80
|
||||
path: /index1.html
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
initContainers:
|
||||
- name: init-myservice
|
||||
image: aaronxudocker/tools:busybox
|
||||
command: ['sh', '-c', 'until nslookup myservice; do echo waiting for myservice; sleep 2; done;']
|
||||
- name: init-mydb
|
||||
image: aaronxudocker/tools:busybox
|
||||
command: ['sh', '-c', 'until nslookup mydb; do echo waiting for mydb; sleep 2; done;']
|
||||
```
|
||||
|
||||
# 调度Pod
|
||||
|
||||

|
||||
|
BIN
Kubernetes/Pod生命周期/image-20240906095839924.png
Normal file
After Width: | Height: | Size: 286 KiB |
BIN
Kubernetes/Pod生命周期/image-20240906140721572.png
Normal file
After Width: | Height: | Size: 233 KiB |
BIN
Kubernetes/Pod生命周期/image-20240906204437000.png
Normal file
After Width: | Height: | Size: 421 KiB |
793
Kubernetes/Service.md
Normal file
@@ -0,0 +1,793 @@
|
||||
# 概念以及原理
|
||||
|
||||
Kubernetes`Service`定义了这样⼀种抽象:
|
||||
|
||||
⼀个`Pod`的逻辑分组,⼀种可以访问它们的策略,通常称为微服务。这⼀组`Pod`能够被`Service`访问到,通常是通过`LabelSelector`
|
||||
|
||||
<img src="Service/image-20240912140110844.png" alt="image-20240912140110844" style="zoom: 33%;" />
|
||||
|
||||
Service在很多情况下只是一个概念,真正起作用的其实是kube-proxy服务进程,每个Node节点上都运行着一个kube-proxy服务进程。当创建Service的时候会通过api-server向etcd写入创建的service的信息,而kube-proxy会基于监听的机制发现这种Service的变动,然后**它会将最新的Service信息转换成对应的访问规则**。
|
||||
|
||||
<img src="Service/image-20240912152844652.png" alt="image-20240912152844652" style="zoom:33%;" />
|
||||
|
||||
|
||||
# 工作模式
|
||||
|
||||
kube-proxy目前支持三种工作模式:
|
||||
|
||||
## userspace模式
|
||||
|
||||
userspace模式下,kube-proxy会为每一个Service创建一个监听端口,发向Cluster IP的请求被Iptables规则重定向到kube-proxy监听的端口上,kube-proxy根据LB算法选择一个提供服务的Pod并和其建立链接,以将请求转发到Pod上。 该模式下,kube-proxy充当了一个四层负责均衡器的角色。由于kube-proxy运行在userspace中,在进行转发处理时会增加内核和用户空间之间的数据拷贝,虽然比较稳定,但是效率比较低。
|
||||
|
||||
<img src="Service/image-20240912170627727.png" alt="image-20240912170627727" style="zoom:33%;" />
|
||||
|
||||
## iptables模式
|
||||
|
||||
iptables模式下,kube-proxy为service后端的每个Pod创建对应的iptables规则,直接将发向Cluster IP的请求重定向到一个Pod IP。该模式下kube-proxy不承担四层负责均衡器的角色,只负责创建iptables规则。该模式的优点是较userspace模式效率更高,但不能提供灵活的LB策略,当后端Pod不可用时也无法进行重试。
|
||||
|
||||
<img src="Service/image-20240912170931956.png" alt="image-20240912170931956" style="zoom:33%;" />
|
||||
|
||||
## ipvs模式
|
||||
|
||||
ipvs模式和iptables类似,kube-proxy监控Pod的变化并创建相应的ipvs规则。ipvs相对iptables转发效率更高。除此以外,ipvs支持更多的LB算法。
|
||||
|
||||
<img src="Service/image-20240912171043118.png" alt="image-20240912171043118" style="zoom:33%;" />
|
||||
|
||||
```yaml
|
||||
# 创建三个pod
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: myapp-deploy
|
||||
name: myapp-deploy
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: myapp-deploy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: myapp-deploy
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
```bash
|
||||
# 启动一个负载均衡的service
|
||||
$ kubectl create svc clusterip myapp-deploy --tcp=80:80
|
||||
|
||||
# 修改ipvs
|
||||
$ kubectl edit configmap kube-proxy -n kube-system
|
||||
mode: "ipvs"
|
||||
|
||||
# 删除kube-proxy的pod
|
||||
$ kubectl delete pod -n kube-system -l k8s-app=kube-proxy
|
||||
pod "kube-proxy-ckwsj" deleted
|
||||
pod "kube-proxy-t729f" deleted
|
||||
pod "kube-proxy-z6dt8" deleted
|
||||
|
||||
# 查看pod创建的状态
|
||||
$ kubectl get pod -n kube-system -l k8s-app=kube-proxy
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
kube-proxy-948s5 1/1 Running 0 3s
|
||||
kube-proxy-ggpwj 1/1 Running 0 3s
|
||||
kube-proxy-v7lgs 1/1 Running 0 3s
|
||||
|
||||
# 查看虚拟IP地址
|
||||
$ kubectl get svc
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
myapp-deploy ClusterIP 10.9.86.78 <none> 80/TCP 6m54s
|
||||
|
||||
# 查看ipvsadm的状态
|
||||
$ ipvsadm -Ln
|
||||
IP Virtual Server version 1.2.1 (size=4096)
|
||||
Prot LocalAddress:Port Scheduler Flags
|
||||
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
|
||||
TCP 10.9.86.78:80 rr
|
||||
-> 10.244.140.106:80 Masq 1 0 0
|
||||
-> 10.244.196.141:80 Masq 1 0 0
|
||||
-> 10.244.196.142:80 Masq 1 0 0
|
||||
|
||||
# 负载均衡的地址正好对应着pod的ip地址
|
||||
$ kubectl get pod -o wide
|
||||
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS
|
||||
myapp-deploy-57bff895d5-b2hhk 1/1 Running 0 73s 10.244.196.142 node01 <none> <none>
|
||||
myapp-deploy-57bff895d5-fbln4 1/1 Running 0 73s 10.244.140.106 node02 <none> <none>
|
||||
myapp-deploy-57bff895d5-frnfd 1/1 Running 0 73s 10.244.196.141 node01 <none> <none>
|
||||
```
|
||||
|
||||
# Service资源清单
|
||||
|
||||
```yaml
|
||||
kind: Service # 资源类型
|
||||
apiVersion: v1 # 资源版本
|
||||
metadata: # 元数据
|
||||
name: service # 资源名称
|
||||
namespace: default # 命名空间
|
||||
spec: # 描述
|
||||
selector: # 标签选择器,用于确定当前service代理哪些pod
|
||||
app: nginx
|
||||
type: # Service类型,指定service的访问方式
|
||||
clusterIP: # 虚拟服务的ip地址
|
||||
sessionAffinity: # session亲和性,支持ClientIP、None两个选项
|
||||
sessionAffinityConfig:
|
||||
clientIP:
|
||||
timeoutSeconds: 120 # session的过期时间
|
||||
ports: # 端口信息
|
||||
- protocol: TCP
|
||||
port: 3017 # service端口
|
||||
targetPort: 5003 # pod端口
|
||||
nodePort: 31122 # 主机端口
|
||||
```
|
||||
|
||||
可以使用如下命令得到基本的yaml格式的文件
|
||||
|
||||
```bash
|
||||
$ kubectl create svc clusterip nginx --tcp=80:80 --dry-run=client -o yaml
|
||||
|
||||
$ ipvsadm -lnc
|
||||
```
|
||||
|
||||
`spec.type`可以选择的类型
|
||||
|
||||
- ClusterIP:默认值,它是Kubernetes系统自动分配的虚拟IP,只能在集群内部访问
|
||||
- NodePort:将Service通过指定的Node上的端口暴露给外部,通过此方法,就可以在集群外部访问服务
|
||||
- LoadBalancer:使用外接负载均衡器完成到服务的负载分发,注意此模式需要外部云环境支持
|
||||
- ExternalName: 把集群外部的服务引入集群内部,直接使用
|
||||
|
||||
# Service使用
|
||||
|
||||
```yaml
|
||||
# 创建三个pod
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: myapp-deploy
|
||||
name: myapp-deploy
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: myapp-deploy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: myapp-deploy
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
测试三个pod
|
||||
|
||||
```bash
|
||||
$ kubectl get pod -o wide
|
||||
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
|
||||
myapp-deploy-57bff895d5-b2hhk 1/1 Running 0 30m 10.244.196.142 node01 <none> <none>
|
||||
myapp-deploy-57bff895d5-fbln4 1/1 Running 0 30m 10.244.140.106 node02 <none> <none>
|
||||
myapp-deploy-57bff895d5-frnfd 1/1 Running 0 30m 10.244.196.141 node01 <none> <none>
|
||||
|
||||
# 查看一下访问情况
|
||||
$ curl 10.244.196.142/hostname.html
|
||||
myapp-deploy-57bff895d5-b2hhk
|
||||
$ curl 10.244.140.106/hostname.html
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
$ curl 10.244.196.141/hostname.html
|
||||
myapp-deploy-57bff895d5-frnfd
|
||||
```
|
||||
|
||||
## ClusterIP类型的Service
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-clusterip
|
||||
spec:
|
||||
selector:
|
||||
app: myapp-deploy
|
||||
# clusterIP: 172.16.66.66 # service的ip地址,如果不写,默认会生成一个
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80 # Service端口
|
||||
targetPort: 80 # pod端口
|
||||
```
|
||||
|
||||
查看运行结果
|
||||
|
||||
```bash
|
||||
$ kubectl get svc
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service-clusterip ClusterIP 10.13.125.29 <none> 80/TCP 22s
|
||||
|
||||
$ kubectl describe svc service-clusterip
|
||||
Name: service-clusterip
|
||||
Namespace: default
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
Selector: app=myapp-deploy
|
||||
Type: ClusterIP
|
||||
IP Family Policy: SingleStack
|
||||
IP Families: IPv4
|
||||
IP: 10.13.125.29
|
||||
IPs: 10.13.125.29
|
||||
Port: <unset> 80/TCP
|
||||
TargetPort: 80/TCP
|
||||
Endpoints: 10.244.140.106:80,10.244.196.141:80,10.244.196.142:80
|
||||
Session Affinity: None
|
||||
Events: <none>
|
||||
|
||||
$ ipvsadm -Ln
|
||||
IP Virtual Server version 1.2.1 (size=4096)
|
||||
Prot LocalAddress:Port Scheduler Flags
|
||||
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
|
||||
TCP 10.13.125.29:80 rr
|
||||
-> 10.244.140.106:80 Masq 1 0 0
|
||||
-> 10.244.196.141:80 Masq 1 0 0
|
||||
-> 10.244.196.142:80 Masq 1 0 0
|
||||
|
||||
$ while true;do curl 10.13.125.29/hostname.html; done
|
||||
myapp-deploy-57bff895d5-b2hhk
|
||||
myapp-deploy-57bff895d5-frnfd
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
myapp-deploy-57bff895d5-b2hhk
|
||||
myapp-deploy-57bff895d5-frnfd
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
```
|
||||
|
||||
## Endpoint
|
||||
|
||||
Endpoint是kubernetes中的一个资源对象,存储在etcd中,用来记录一个service对应的所有pod的访问地址,它是根据service配置文件中selector描述产生的。必须要满足就绪探测。
|
||||
|
||||
一个Service由一组Pod组成,这些Pod通过Endpoints暴露出来,**Endpoints是实现实际服务的端点集合**。换句话说,service和pod之间的联系是通过endpoints实现的。
|
||||
|
||||
<img src="Service/image-20240912172501813.png" alt="image-20240912172501813" style="zoom:33%;" />
|
||||
|
||||
```bash
|
||||
$ kubectl get endpoints -o wide
|
||||
NAME ENDPOINTS AGE
|
||||
service-clusterip 10.244.140.106:80,10.244.196.141:80,10.244.196.142:80 6m27s
|
||||
```
|
||||
|
||||
在deployment中添加一个就绪探测
|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: myapp-deploy
|
||||
name: myapp-deploy
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: myapp-deploy
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: myapp-deploy
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
resources:
|
||||
limits:
|
||||
memory: "128Mi"
|
||||
cpu: "500m"
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
port: 80
|
||||
path: /index1.html
|
||||
initialDelaySeconds: 1
|
||||
periodSeconds: 3
|
||||
ports:
|
||||
- containerPort: 80
|
||||
```
|
||||
|
||||
在不满足就绪探测的情况下,是不会被endpoint采用的
|
||||
|
||||
```bash
|
||||
$ kubectl get pod
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
myapp-deploy-659f9975b8-2sntn 0/1 Running 0 40s
|
||||
myapp-deploy-659f9975b8-nd66b 0/1 Running 0 40s
|
||||
myapp-deploy-659f9975b8-p4j5k 0/1 Running 0 40s
|
||||
|
||||
$ kubectl get endpoints
|
||||
NAME ENDPOINTS AGE
|
||||
service-clusterip 10s
|
||||
```
|
||||
|
||||
满足了就绪探测和标签被匹配上的pod会被加入endpoint中
|
||||
|
||||
```bash
|
||||
$ kubectl exec -it myapp-deploy-659f9975b8-2sntn -- /bin/bash
|
||||
root@myapp-deploy-659f9975b8-2sntn:/# echo "hello world" > /usr/share/nginx/html/index1.html
|
||||
|
||||
$ kubectl get pod
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
myapp-deploy-659f9975b8-2sntn 1/1 Running 0 3m4s
|
||||
myapp-deploy-659f9975b8-nd66b 0/1 Running 0 3m4s
|
||||
myapp-deploy-659f9975b8-p4j5k 0/1 Running 0 3m4s
|
||||
|
||||
$ kubectl get endpoints
|
||||
NAME ENDPOINTS AGE
|
||||
service-clusterip 10.244.140.107:80 3m1s
|
||||
|
||||
$ ipvsadm -L -n
|
||||
IP Virtual Server version 1.2.1 (size=4096)
|
||||
Prot LocalAddress:Port Scheduler Flags
|
||||
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
|
||||
TCP 10.12.150.224:80 rr
|
||||
-> 10.244.140.107:80 Masq 1 0 0
|
||||
```
|
||||
|
||||
**负载分发策略**
|
||||
|
||||
对Service的访问被分发到了后端的Pod上去,目前kubernetes提供了两种负载分发策略:
|
||||
|
||||
- 如果不定义,默认使用kube-proxy的策略,比如随机、轮询
|
||||
|
||||
- 基于客户端地址的会话保持模式,即来自同一个客户端发起的所有请求都会转发到固定的一个Pod上
|
||||
|
||||
此模式可以使在spec中添加`sessionAffinity: ClientIP`选项
|
||||
|
||||
```bash
|
||||
$ kubectl edit svc service-clusterip
|
||||
sessionAffinity: ClientIP
|
||||
|
||||
$ while true;do curl 10.13.125.29/hostname.html; done
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
myapp-deploy-57bff895d5-fbln4
|
||||
|
||||
$ ipvsadm -Ln
|
||||
IP Virtual Server version 1.2.1 (size=4096)
|
||||
Prot LocalAddress:Port Scheduler Flags
|
||||
-> RemoteAddress:Port Forward Weight ActiveConn InActConn
|
||||
TCP 10.13.125.29:80 rr persistent 10800
|
||||
-> 10.244.140.106:80 Masq 1 0 155
|
||||
-> 10.244.196.141:80 Masq 1 0 0
|
||||
-> 10.244.196.142:80 Masq 1 0 0
|
||||
```
|
||||
|
||||
## HeadLess类型的Service
|
||||
|
||||
在某些场景中,开发人员可能不想使用Service提供的负载均衡功能,而希望自己来控制负载均衡策略,针对这种情况,kubernetes提供了HeadLiness Service,这类Service不会分配Cluster IP,如果想要访问service,只能通过service的域名进行查询。
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-headliness
|
||||
spec:
|
||||
selector:
|
||||
app: myapp-deploy
|
||||
clusterIP: None # 将clusterIP设置为None,即可创建headliness Service
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
```
|
||||
|
||||
```bash
|
||||
$ kubectl get svc -o wide
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
|
||||
service-headliness ClusterIP None <none> 80/TCP 40s app=myapp-deploy
|
||||
|
||||
$ kubectl describe svc service-headliness
|
||||
Name: service-headliness
|
||||
Namespace: default
|
||||
Labels: <none>
|
||||
Annotations: <none>
|
||||
Selector: app=myapp-deploy
|
||||
Type: ClusterIP
|
||||
IP Family Policy: SingleStack
|
||||
IP Families: IPv4
|
||||
IP: None
|
||||
IPs: None
|
||||
Port: <unset> 80/TCP
|
||||
TargetPort: 80/TCP
|
||||
Endpoints: 10.244.140.107:80
|
||||
Session Affinity: None
|
||||
Events: <none>
|
||||
|
||||
$ kubectl exec -it myapp-deploy-659f9975b8-2sntn -- /bin/bash
|
||||
root@myapp-deploy-659f9975b8-2sntn:/# cat /etc/resolv.conf
|
||||
nameserver 10.0.0.10
|
||||
search default.svc.cluster.local svc.cluster.local cluster.local
|
||||
options ndots:5
|
||||
|
||||
$ dig @10.0.0.10 service-headliness.default.svc.cluster.local
|
||||
;; ANSWER SECTION:
|
||||
service-headliness.default.svc.cluster.local. 30 IN A 10.244.140.107
|
||||
service-headliness.default.svc.cluster.local. 30 IN A 10.244.196.145
|
||||
service-headliness.default.svc.cluster.local. 30 IN A 10.244.196.144
|
||||
```
|
||||
|
||||
## NodePort类型的Service
|
||||
|
||||
在之前的样例中,创建的Service的ip地址只有集群内部才可以访问,如果希望将Service暴露给集群外部使用,那么就要使用到另外一种类型的Service,称为NodePort类型。NodePort的工作原理其实就是**将service的端口映射到Node的一个端口上**,然后就可以通过`NodeIp:NodePort`来访问service了。
|
||||
|
||||
<img src="Service/image-20240913143156247.png" alt="image-20240913143156247" style="zoom:33%;" />
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-nodeport
|
||||
spec:
|
||||
selector:
|
||||
app: myapp-deploy
|
||||
type: NodePort # service类型
|
||||
ports:
|
||||
- port: 80
|
||||
nodePort: 30002 # 指定绑定的node的端口(默认的取值范围是:30000-32767), 如果不指定,会默认分配
|
||||
targetPort: 80
|
||||
```
|
||||
|
||||
查看是否能够正常的访问
|
||||
|
||||
```bash
|
||||
$ for i in {1..6};do curl 192.168.173.100:30002/hostname.html;done
|
||||
myapp-deploy-659f9975b8-nd66b
|
||||
myapp-deploy-659f9975b8-p4j5k
|
||||
myapp-deploy-659f9975b8-2sntn
|
||||
myapp-deploy-659f9975b8-nd66b
|
||||
myapp-deploy-659f9975b8-p4j5k
|
||||
myapp-deploy-659f9975b8-2sntn
|
||||
```
|
||||
|
||||
## LoadBalancer类型的Service
|
||||
|
||||
LoadBalancer和NodePort很相似,目的都是向外部暴露一个端口,区别在于LoadBalancer会在集群的外部再来做一个负载均衡设备,而这个设备需要外部环境支持的,外部服务发送到这个设备上的请求,会被设备负载之后转发到集群中。
|
||||
|
||||

|
||||
|
||||
## ExternalName类型的Service
|
||||
|
||||
ExternalName类型的Service用于引入集群外部的服务,它通过`externalName`属性指定外部一个服务的地址,然后在集群内部访问此service就可以访问到外部的服务了。
|
||||
|
||||
<img src="Service/image-20240913160143805.png" alt="image-20240913160143805" style="zoom:33%;" />
|
||||
|
||||
```yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: service-externalname
|
||||
namespace: dev
|
||||
spec:
|
||||
type: ExternalName # service类型
|
||||
externalName: www.baidu.com #改成ip地址也可以
|
||||
```
|
||||
|
||||
```bash
|
||||
$ kubectl get svc
|
||||
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
|
||||
service-externalname ExternalName <none> www.baidu.com <none> 7s
|
||||
|
||||
$ dig @10.0.0.10 service-externalname.default.svc.cluster.local
|
||||
;; ANSWER SECTION:
|
||||
service-externalname.default.svc.cluster.local. 30 IN CNAME www.baidu.com.
|
||||
www.baidu.com. 30 IN CNAME www.a.shifen.com.
|
||||
www.a.shifen.com. 30 IN A 180.101.50.242
|
||||
www.a.shifen.com. 30 IN A 180.101.50.188
|
||||
```
|
||||
|
||||
# Ingress介绍
|
||||
|
||||
在前面课程中已经提到,Service对集群之外暴露服务的主要方式有两种:NotePort和LoadBalancer,但是这两种方式,都有一定的缺点:
|
||||
|
||||
- NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显
|
||||
- LB方式的缺点是每个service需要一个LB,浪费、麻烦,并且需要kubernetes之外设备的支持
|
||||
|
||||
基于这种现状,kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求。工作机制大致如下图表示:
|
||||
|
||||
<img src="Service/image-20240913161756342.png" alt="image-20240913161756342" style="zoom:33%;" />
|
||||
|
||||
实际上,Ingress相当于一个7层的负载均衡器,是kubernetes对反向代理的一个抽象,它的工作原理类似于Nginx,可以理解成在**Ingress里建立诸多映射规则,Ingress Controller通过监听这些配置规则并转化成Nginx的反向代理配置 , 然后对外部提供服务**。在这里有两个核心概念:
|
||||
|
||||
- ingress:kubernetes中的一个对象,作用是定义请求如何转发到service的规则
|
||||
- ingress controller:具体实现反向代理及负载均衡的程序,对ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现方式有很多,比如Nginx, Contour, Haproxy等等
|
||||
|
||||
Ingress(以Nginx为例)的工作原理如下:
|
||||
|
||||
1. 用户编写Ingress规则,说明哪个域名对应kubernetes集群中的哪个Service
|
||||
2. Ingress控制器动态感知Ingress服务规则的变化,然后生成一段对应的Nginx反向代理配置
|
||||
3. Ingress控制器会将生成的Nginx配置写入到一个运行着的Nginx服务中,并动态更新
|
||||
4. 到此为止,其实真正在工作的就是一个Nginx了,内部配置了用户定义的请求转发规则
|
||||
|
||||
<img src="Service/image-20240914142642245.png" alt="image-20240914142642245" style="zoom:33%;" />
|
||||
|
||||
## 安装helm
|
||||
|
||||
```bash
|
||||
# 安装helm,helm在kubernetes中相当于yum,是可以在线去获取资源清单,快速部署服务
|
||||
$ curl -fsSL -o get_helm.sh https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3
|
||||
$ chmod 700 get_helm.sh
|
||||
$ ./get_helm.sh
|
||||
|
||||
# 初始化,可以从 https://artifacthub.io/ 中选择一个可用的仓库地址
|
||||
$ helm repo add bitnami https://charts.bitnami.com/bitnami
|
||||
$ helm repo list
|
||||
NAME URL
|
||||
bitnami https://charts.bitnami.com/bitnami
|
||||
|
||||
# 常见操作
|
||||
$ helm repo update # 更新chart列表
|
||||
$ helm show chart bitnami/apache # 查看chart基本信息
|
||||
$ helm install bitnami/apache --generate-name # 部署chart
|
||||
$ helm list # 查看部署包,加上--all可以看到所有的
|
||||
$ helm uninstall apache-1726297430 # 删除这个安装包所有的kubernetes资源
|
||||
|
||||
$ helm search hub wordpress # 在 helm hub(https://hub.helm.sh)上搜索helm chart
|
||||
$ helm search repo wordpress # 在repo中搜索
|
||||
```
|
||||
|
||||
## 安装Ingress-nginx
|
||||
|
||||
```bash
|
||||
$ helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
|
||||
$ helm pull ingress-nginx/ingress-nginx
|
||||
|
||||
# 修改 values.yaml 文件
|
||||
修改 hostNetwork 的值为 true
|
||||
dnsPolicy的值改为: ClusterFirstWithHostNet
|
||||
kind类型更改为:DaemonSet
|
||||
ingressClassResource.default:true
|
||||
|
||||
# 关闭所有镜像的 digest
|
||||
|
||||
# 如果是本地的helm chart,使用这个命令安装
|
||||
$ kubectl create ns ingress
|
||||
$ helm install ingress-nginx -n ingress . -f values.yaml
|
||||
$ kubectl get pod -n ingress
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
ingress-nginx-controller-7c4x8 1/1 Running 0 12s
|
||||
ingress-nginx-controller-bjk4s 1/1 Running 0 12s
|
||||
```
|
||||
|
||||
## 实验测试
|
||||
|
||||
创建如下两个资源模型
|
||||
|
||||

|
||||
|
||||
```yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: nginx-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: aaronxudocker/myapp:v1.0
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
||||
---
|
||||
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: tomcat-deployment
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: tomcat
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: tomcat
|
||||
spec:
|
||||
containers:
|
||||
- name: tomcat
|
||||
image: tomcat:8.5-jre10-slim
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
clusterIP: None
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: tomcat-service
|
||||
spec:
|
||||
selector:
|
||||
app: tomcat
|
||||
clusterIP: None
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 8080
|
||||
targetPort: 8080
|
||||
```
|
||||
|
||||
### Http代理
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-nginx
|
||||
spec:
|
||||
rules:
|
||||
- host: nginx.iproute.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-service
|
||||
port:
|
||||
number: 80
|
||||
ingressClassName: nginx
|
||||
---
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-tomcat
|
||||
spec:
|
||||
rules:
|
||||
- host: tomcat.iproute.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: tomcat-service
|
||||
port:
|
||||
number: 8080
|
||||
ingressClassName: nginx
|
||||
```
|
||||
|
||||
查看运行状态
|
||||
|
||||
```bash
|
||||
$ kubectl get ing
|
||||
NAME CLASS HOSTS ADDRESS PORTS AGE
|
||||
ingress-nginx nginx nginx.iproute.cn 80 7s
|
||||
ingress-tomcat nginx tomcat.iproute.cn 80 7s
|
||||
|
||||
$ kubectl describe ing
|
||||
|
||||
Rules:
|
||||
Host Path Backends
|
||||
---- ---- --------
|
||||
nginx.iproute.cn
|
||||
/ nginx-service:80 (10.244.140.109:80,10.244.196.149:80,10.244.196.150:80)
|
||||
|
||||
Rules:
|
||||
Host Path Backends
|
||||
---- ---- --------
|
||||
tomcat.iproute.cn
|
||||
/ tomcat-service:8080 (10.244.140.110:8080,10.244.196.151:8080,10.244.196.153:8080)
|
||||
```
|
||||
|
||||
访问测试
|
||||
|
||||

|
||||
|
||||
其中nginx多次访问主机名,可以看到负载均衡
|
||||
|
||||

|
||||
|
||||
## Https代理
|
||||
|
||||
创建证书
|
||||
|
||||
```bash
|
||||
# 生成证书
|
||||
$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=iproute.cn"
|
||||
|
||||
# 创建密钥
|
||||
$ kubectl create secret tls tls-secret --key tls.key --cert tls.crt
|
||||
```
|
||||
|
||||
创建资源清单
|
||||
|
||||
```yaml
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: https-nginx
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- nginx.iproute.cn
|
||||
secretName: tls-secret # 指定秘钥
|
||||
rules:
|
||||
- host: nginx.iproute.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-service
|
||||
port:
|
||||
number: 80
|
||||
ingressClassName: nginx
|
||||
---
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: tomcat-https
|
||||
spec:
|
||||
tls:
|
||||
- hosts:
|
||||
- tomcat.iproute.cn
|
||||
secretName: tls-secret # 指定秘钥
|
||||
rules:
|
||||
- host: tomcat.iproute.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: tomcat-service
|
||||
port:
|
||||
number: 8080
|
||||
ingressClassName: nginx
|
||||
```
|
||||
|
||||
访问测试
|
||||
|
||||

|
||||
|
||||
可以看到负载均衡
|
||||
|
||||

|
BIN
Kubernetes/Service/image-20240912140110844.png
Normal file
After Width: | Height: | Size: 193 KiB |
BIN
Kubernetes/Service/image-20240912152844652.png
Normal file
After Width: | Height: | Size: 156 KiB |
BIN
Kubernetes/Service/image-20240912170627727.png
Normal file
After Width: | Height: | Size: 141 KiB |
BIN
Kubernetes/Service/image-20240912170931956.png
Normal file
After Width: | Height: | Size: 186 KiB |
BIN
Kubernetes/Service/image-20240912171043118.png
Normal file
After Width: | Height: | Size: 181 KiB |
BIN
Kubernetes/Service/image-20240912172501813.png
Normal file
After Width: | Height: | Size: 177 KiB |
BIN
Kubernetes/Service/image-20240913143156247.png
Normal file
After Width: | Height: | Size: 577 KiB |
BIN
Kubernetes/Service/image-20240913145934612.png
Normal file
After Width: | Height: | Size: 2.1 MiB |
BIN
Kubernetes/Service/image-20240913154405309.png
Normal file
After Width: | Height: | Size: 210 KiB |
BIN
Kubernetes/Service/image-20240913160143805.png
Normal file
After Width: | Height: | Size: 132 KiB |
BIN
Kubernetes/Service/image-20240913161756342.png
Normal file
After Width: | Height: | Size: 448 KiB |
BIN
Kubernetes/Service/image-20240914142642245.png
Normal file
After Width: | Height: | Size: 280 KiB |
BIN
Kubernetes/Service/image-20240914152836257.png
Normal file
After Width: | Height: | Size: 239 KiB |
BIN
Kubernetes/Service/image-20240914154740672.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
Kubernetes/Service/image-20240914154758983.png
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
Kubernetes/Service/image-20240914155615236.png
Normal file
After Width: | Height: | Size: 73 KiB |
BIN
Kubernetes/Service/image-20240914155628119.png
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
Kubernetes/kubernetes概述/IAAS.png
Normal file
After Width: | Height: | Size: 179 KiB |
BIN
Kubernetes/kubernetes概述/PAAS.png
Normal file
After Width: | Height: | Size: 134 KiB |
BIN
Kubernetes/kubernetes概述/kubernetes优势.png
Normal file
After Width: | Height: | Size: 76 KiB |
BIN
Kubernetes/kubernetes概述/kubernetes现状.png
Normal file
After Width: | Height: | Size: 97 KiB |
BIN
Kubernetes/kubernetes概述/kubernetes集群组件-2.png
Normal file
After Width: | Height: | Size: 95 KiB |
BIN
Kubernetes/kubernetes概述/kubernetes集群组件.png
Normal file
After Width: | Height: | Size: 164 KiB |
BIN
Kubernetes/kubernetes概述/云原生演变.png
Normal file
After Width: | Height: | Size: 147 KiB |
BIN
Kubernetes/pod与网络/image-20240904112128424.png
Normal file
After Width: | Height: | Size: 492 KiB |
BIN
Kubernetes/pod与网络/image-20240904112421964.png
Normal file
After Width: | Height: | Size: 119 KiB |
BIN
Kubernetes/pod与网络/image-20240904115715394.png
Normal file
After Width: | Height: | Size: 985 KiB |
BIN
Kubernetes/pod与网络/image-20240904134937631.png
Normal file
After Width: | Height: | Size: 236 KiB |
BIN
Kubernetes/pod与网络/image-20240904135319098.png
Normal file
After Width: | Height: | Size: 310 KiB |
BIN
Kubernetes/pod与网络/image-20240904135633230.png
Normal file
After Width: | Height: | Size: 300 KiB |
BIN
Kubernetes/pod与网络/image-20240904135815763.png
Normal file
After Width: | Height: | Size: 290 KiB |
BIN
Kubernetes/pod与网络/image-20240904140729187.png
Normal file
After Width: | Height: | Size: 132 KiB |
BIN
Kubernetes/pod与网络/image-20240904140805520.png
Normal file
After Width: | Height: | Size: 102 KiB |
BIN
Kubernetes/pod与网络/image-20240904141203196.png
Normal file
After Width: | Height: | Size: 237 KiB |
BIN
Kubernetes/pod与网络/image-20240904142345171.png
Normal file
After Width: | Height: | Size: 292 KiB |
BIN
Kubernetes/pod与网络/image-20240904143234102.png
Normal file
After Width: | Height: | Size: 440 KiB |
BIN
Kubernetes/pod与网络/image-20240904145716982.png
Normal file
After Width: | Height: | Size: 291 KiB |
BIN
Kubernetes/pod与网络/image-20240904145738269.png
Normal file
After Width: | Height: | Size: 138 KiB |
BIN
Kubernetes/pod与网络/image-20240904150012585.png
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
Kubernetes/pod与网络/image-20240904150019409.png
Normal file
After Width: | Height: | Size: 39 KiB |
BIN
Kubernetes/pod与网络/image-20240904150123553.png
Normal file
After Width: | Height: | Size: 84 KiB |
BIN
Kubernetes/pod与网络/image-20240904150127316.png
Normal file
After Width: | Height: | Size: 59 KiB |
BIN
Kubernetes/pod与网络/image-20240904151200463.png
Normal file
After Width: | Height: | Size: 282 KiB |
BIN
Kubernetes/pod与网络/image-20240904151418487.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
Kubernetes/pod与网络/image-20240904151425020.png
Normal file
After Width: | Height: | Size: 46 KiB |
BIN
Kubernetes/pod与网络/image-20240904151434684.png
Normal file
After Width: | Height: | Size: 67 KiB |
BIN
Kubernetes/pod与网络/image.png
Normal file
After Width: | Height: | Size: 103 KiB |
91
Kubernetes/resources/01.nginx_v2.yaml
Normal file
@@ -0,0 +1,91 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
name: nginx
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: nginx
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: nginx
|
||||
spec:
|
||||
containers:
|
||||
- name: nginx
|
||||
image: nginx:1.26.3
|
||||
ports:
|
||||
- containerPort: 80
|
||||
volumeMounts:
|
||||
- name: html
|
||||
mountPath: /usr/share/nginx/html
|
||||
- name: config
|
||||
mountPath: /etc/nginx/conf.d
|
||||
startupProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
failureThreshold: 30
|
||||
periodSeconds: 10
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /
|
||||
port: 80
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
volumes:
|
||||
- name: html
|
||||
nfs:
|
||||
server: 10.3.204.100
|
||||
path: /root/data/nfs/html
|
||||
- name: config
|
||||
nfs:
|
||||
server: 10.3.204.100
|
||||
path: /root/data/nfs/nginx
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: nginx-service
|
||||
spec:
|
||||
selector:
|
||||
app: nginx
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 80
|
||||
targetPort: 80
|
||||
|
||||
---
|
||||
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: ingress-nginx
|
||||
spec:
|
||||
rules:
|
||||
- host: bbs.iproute.cn
|
||||
http:
|
||||
paths:
|
||||
- path: /
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: nginx-service
|
||||
port:
|
||||
number: 80
|
||||
ingressClassName: nginx
|
63
Kubernetes/resources/02.phpfpm_v2.yaml
Normal file
@@ -0,0 +1,63 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: php
|
||||
name: php
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: php
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: php
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp
|
||||
image: myphp:7.4-fpm
|
||||
ports:
|
||||
- containerPort: 9000
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 9000
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 9000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 2
|
||||
volumeMounts:
|
||||
- name: html
|
||||
mountPath: /usr/share/nginx/html
|
||||
- name: config
|
||||
mountPath: /usr/local/etc
|
||||
volumes:
|
||||
- name: html
|
||||
nfs:
|
||||
server: 10.3.204.100
|
||||
path: /root/data/nfs/html
|
||||
- name: config
|
||||
nfs:
|
||||
server: 10.3.204.100
|
||||
path: /root/data/nfs/php
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: phpfpm
|
||||
spec:
|
||||
selector:
|
||||
app: php
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 9000
|
||||
targetPort: 9000
|
70
Kubernetes/resources/02.phpfpm_v3.yaml
Normal file
@@ -0,0 +1,70 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: php
|
||||
name: php
|
||||
spec:
|
||||
replicas: 3
|
||||
selector:
|
||||
matchLabels:
|
||||
app: php
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: php
|
||||
spec:
|
||||
containers:
|
||||
- name: myapp
|
||||
image: myphp:7.4-fpm
|
||||
resources:
|
||||
requests:
|
||||
cpu: "200m"
|
||||
memory: "256Mi"
|
||||
limits:
|
||||
cpu: "500m"
|
||||
memory: "512Mi"
|
||||
ports:
|
||||
- containerPort: 9000
|
||||
livenessProbe:
|
||||
tcpSocket:
|
||||
port: 9000
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 3
|
||||
readinessProbe:
|
||||
tcpSocket:
|
||||
port: 9000
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
timeoutSeconds: 3
|
||||
failureThreshold: 2
|
||||
volumeMounts:
|
||||
- name: html
|
||||
mountPath: /usr/share/nginx/html
|
||||
- name: config
|
||||
mountPath: /usr/local/etc
|
||||
volumes:
|
||||
- name: html
|
||||
nfs:
|
||||
server: 10.3.203.100
|
||||
path: /root/data/nfs/html
|
||||
- name: config
|
||||
nfs:
|
||||
server: 10.3.203.100
|
||||
path: /root/data/nfs/php
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: phpfpm
|
||||
spec:
|
||||
selector:
|
||||
app: php
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 9000
|
||||
targetPort: 9000
|
52
Kubernetes/resources/03.mysql.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: mysql
|
||||
name: mysql
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mysql
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mysql
|
||||
spec:
|
||||
containers:
|
||||
- name: mysql
|
||||
image: mariadb:10.3
|
||||
env:
|
||||
- name: MARIADB_ROOT_PASSWORD
|
||||
value: "123"
|
||||
ports:
|
||||
- containerPort: 3306
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/mysql
|
||||
- name: config
|
||||
mountPath: /etc/mysql/
|
||||
volumes:
|
||||
- name: data
|
||||
nfs:
|
||||
server: 10.3.203.100
|
||||
path: /root/data/nfs/mysql/data
|
||||
- name: config
|
||||
nfs:
|
||||
server: 10.3.203.100
|
||||
path: /root/data/nfs/mysql/config
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mysql
|
||||
spec:
|
||||
selector:
|
||||
app: mysql
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 3306
|
||||
targetPort: 3306
|
137
Kubernetes/resources/03.mysql_ha.yaml
Normal file
@@ -0,0 +1,137 @@
|
||||
# Master Deployment
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: mysql-master
|
||||
name: mysql-master
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mysql-master
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mysql-master
|
||||
spec:
|
||||
containers:
|
||||
- name: mysql-master
|
||||
image: mariadb:10.3
|
||||
env:
|
||||
- name: MARIADB_ROOT_PASSWORD
|
||||
value: "123456"
|
||||
- name: MARIADB_REPLICATION_USER
|
||||
value: "replicator"
|
||||
- name: MARIADB_REPLICATION_PASSWORD
|
||||
value: "replicator123"
|
||||
- name: MARIADB_DATABASE
|
||||
value: "app_db"
|
||||
ports:
|
||||
- containerPort: 3306
|
||||
volumeMounts:
|
||||
- name: master-data
|
||||
mountPath: /var/lib/mysql
|
||||
- name: master-config
|
||||
mountPath: /etc/mysql/
|
||||
- name: master-log
|
||||
mountPath: /var/log/mysql
|
||||
volumes:
|
||||
- name: master-data
|
||||
nfs:
|
||||
server: 10.3.202.100
|
||||
path: /root/data/nfs/mysql-master/data
|
||||
- name: master-config
|
||||
nfs:
|
||||
server: 10.3.202.100
|
||||
path: /root/data/nfs/mysql-master/conf
|
||||
- name: master-log
|
||||
nfs:
|
||||
server: 10.3.202.100
|
||||
path: /root/data/nfs/mysql-master/log
|
||||
|
||||
---
|
||||
|
||||
# Slave Deployment
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: mysql-slave
|
||||
name: mysql-slave
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mysql-slave
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mysql-slave
|
||||
spec:
|
||||
containers:
|
||||
- name: mysql-slave
|
||||
image: mariadb:10.3
|
||||
env:
|
||||
- name: MARIADB_ROOT_PASSWORD
|
||||
value: "123456"
|
||||
- name: MARIADB_MASTER_HOST
|
||||
value: "mysql-master"
|
||||
- name: MARIADB_REPLICATION_USER
|
||||
value: "replicator"
|
||||
- name: MARIADB_REPLICATION_PASSWORD
|
||||
value: "replicator123"
|
||||
- name: MARIADB_MASTER_PORT
|
||||
value: "3306"
|
||||
ports:
|
||||
- containerPort: 3306
|
||||
volumeMounts:
|
||||
- name: slave-data
|
||||
mountPath: /var/lib/mysql
|
||||
- name: slave-config
|
||||
mountPath: /etc/mysql/
|
||||
- name: slave-log
|
||||
mountPath: /var/log/mysql
|
||||
volumes:
|
||||
- name: slave-data
|
||||
nfs:
|
||||
server: 10.3.202.100
|
||||
path: /root/data/nfs/mysql-slave/data
|
||||
- name: slave-config
|
||||
nfs:
|
||||
server: 10.3.202.100
|
||||
path: /root/data/nfs/mysql-slave/conf
|
||||
- name: slave-log
|
||||
nfs:
|
||||
server: 10.3.202.100
|
||||
path: /root/data/nfs/mysql-slave/log
|
||||
|
||||
---
|
||||
|
||||
# Master Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mysql-master
|
||||
spec:
|
||||
selector:
|
||||
app: mysql-master
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 3306
|
||||
targetPort: 3306
|
||||
|
||||
---
|
||||
|
||||
# Slave Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mysql-slave
|
||||
spec:
|
||||
selector:
|
||||
app: mysql-slave
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 3306
|
||||
targetPort: 3306
|
52
Kubernetes/resources/03.mysql_v2.yaml
Normal file
@@ -0,0 +1,52 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: mysql
|
||||
name: mysql
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: mysql
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: mysql
|
||||
spec:
|
||||
containers:
|
||||
- name: mysql
|
||||
image: mariadb:10.3
|
||||
env:
|
||||
- name: MARIADB_ROOT_PASSWORD
|
||||
value: "123"
|
||||
ports:
|
||||
- containerPort: 3306
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /var/lib/mysql
|
||||
- name: config
|
||||
mountPath: /etc/mysql/
|
||||
volumes:
|
||||
- name: data
|
||||
nfs:
|
||||
server: 10.3.202.100
|
||||
path: /root/data/nfs/mysql/data
|
||||
- name: config
|
||||
nfs:
|
||||
server: 10.3.202.100
|
||||
path: /root/data/nfs/mysql/config
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: mysql
|
||||
spec:
|
||||
selector:
|
||||
app: mysql
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- port: 3306
|
||||
targetPort: 3306
|
37
Kubernetes/resources/04.redis.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: redis
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:latest
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-service
|
||||
spec:
|
||||
selector:
|
||||
app: redis
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 6379
|
||||
targetPort: 6379
|
65
Kubernetes/resources/04.redis_v2.yaml
Normal file
@@ -0,0 +1,65 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: redis
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: redis
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: redis
|
||||
spec:
|
||||
containers:
|
||||
- name: redis
|
||||
image: redis:latest
|
||||
ports:
|
||||
- containerPort: 6379
|
||||
volumeMounts:
|
||||
- name: redis-data
|
||||
mountPath: /data
|
||||
- name: redis-config
|
||||
mountPath: /usr/local/etc/redis/redis.conf
|
||||
subPath: redis.conf
|
||||
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
|
||||
volumes:
|
||||
- name: redis-data
|
||||
nfs:
|
||||
server: 10.3.202.100
|
||||
path: /root/data/nfs/redis/data
|
||||
- name: redis-config
|
||||
configMap:
|
||||
name: redis-config
|
||||
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: redis-service
|
||||
spec:
|
||||
selector:
|
||||
app: redis
|
||||
type: ClusterIP
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 6379
|
||||
targetPort: 6379
|
||||
|
||||
|
||||
---
|
||||
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: redis-config
|
||||
data:
|
||||
redis.conf: |
|
||||
appendonly yes
|
||||
save 3600 1
|
||||
aof-use-rdb-preamble yes
|
37
Kubernetes/resources/05.php-hpa.yaml
Normal file
@@ -0,0 +1,37 @@
|
||||
apiVersion: autoscaling/v2
|
||||
kind: HorizontalPodAutoscaler
|
||||
metadata:
|
||||
name: php-hpa
|
||||
spec:
|
||||
scaleTargetRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: php
|
||||
minReplicas: 3
|
||||
maxReplicas: 10
|
||||
metrics:
|
||||
- type: Resource
|
||||
resource:
|
||||
name: cpu
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 80
|
||||
- type: Resource
|
||||
resource:
|
||||
name: memory
|
||||
target:
|
||||
type: Utilization
|
||||
averageUtilization: 80
|
||||
behavior:
|
||||
scaleDown:
|
||||
stabilizationWindowSeconds: 300
|
||||
policies:
|
||||
- type: Pods
|
||||
value: 1
|
||||
periodSeconds: 60
|
||||
scaleUp:
|
||||
stabilizationWindowSeconds: 60
|
||||
policies:
|
||||
- type: Pods
|
||||
value: 2
|
||||
periodSeconds: 30
|
5143
Kubernetes/resources/06.calico-typha.yaml
Normal file
202
Kubernetes/resources/07.metrics-server.yaml
Normal file
@@ -0,0 +1,202 @@
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
name: metrics-server
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
rbac.authorization.k8s.io/aggregate-to-admin: "true"
|
||||
rbac.authorization.k8s.io/aggregate-to-edit: "true"
|
||||
rbac.authorization.k8s.io/aggregate-to-view: "true"
|
||||
name: system:aggregated-metrics-reader
|
||||
rules:
|
||||
- apiGroups:
|
||||
- metrics.k8s.io
|
||||
resources:
|
||||
- pods
|
||||
- nodes
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
name: system:metrics-server
|
||||
rules:
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- nodes/metrics
|
||||
verbs:
|
||||
- get
|
||||
- apiGroups:
|
||||
- ""
|
||||
resources:
|
||||
- pods
|
||||
- nodes
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: RoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
name: metrics-server-auth-reader
|
||||
namespace: kube-system
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: Role
|
||||
name: extension-apiserver-authentication-reader
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: metrics-server
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
name: metrics-server:system:auth-delegator
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:auth-delegator
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: metrics-server
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
name: system:metrics-server
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: system:metrics-server
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: metrics-server
|
||||
namespace: kube-system
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
name: metrics-server
|
||||
namespace: kube-system
|
||||
spec:
|
||||
ports:
|
||||
- name: https
|
||||
port: 443
|
||||
protocol: TCP
|
||||
targetPort: https
|
||||
selector:
|
||||
k8s-app: metrics-server
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
name: metrics-server
|
||||
namespace: kube-system
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-app: metrics-server
|
||||
strategy:
|
||||
rollingUpdate:
|
||||
maxUnavailable: 0
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
spec:
|
||||
containers:
|
||||
- args:
|
||||
- --cert-dir=/tmp
|
||||
- --secure-port=10250
|
||||
- --kubelet-preferred-address-types=InternalIP,ExternalIP,Hostname
|
||||
- --kubelet-use-node-status-port
|
||||
- --metric-resolution=15s
|
||||
- --kubelet-insecure-tls
|
||||
image: registry.aliyuncs.com/google_containers/metrics-server:v0.7.0
|
||||
imagePullPolicy: IfNotPresent
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /livez
|
||||
port: https
|
||||
scheme: HTTPS
|
||||
periodSeconds: 10
|
||||
name: metrics-server
|
||||
ports:
|
||||
- containerPort: 10250
|
||||
name: https
|
||||
protocol: TCP
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
httpGet:
|
||||
path: /readyz
|
||||
port: https
|
||||
scheme: HTTPS
|
||||
initialDelaySeconds: 20
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 200Mi
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1000
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
volumeMounts:
|
||||
- mountPath: /tmp
|
||||
name: tmp-dir
|
||||
nodeSelector:
|
||||
kubernetes.io/os: linux
|
||||
priorityClassName: system-cluster-critical
|
||||
serviceAccountName: metrics-server
|
||||
volumes:
|
||||
- emptyDir: {}
|
||||
name: tmp-dir
|
||||
---
|
||||
apiVersion: apiregistration.k8s.io/v1
|
||||
kind: APIService
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: metrics-server
|
||||
name: v1beta1.metrics.k8s.io
|
||||
spec:
|
||||
group: metrics.k8s.io
|
||||
groupPriorityMinimum: 100
|
||||
insecureSkipTLSVerify: true
|
||||
service:
|
||||
name: metrics-server
|
||||
namespace: kube-system
|
||||
version: v1beta1
|
||||
versionPriority: 100
|
12
Kubernetes/resources/mariadb.cnf
Normal file
@@ -0,0 +1,12 @@
|
||||
# master
|
||||
[mysqld]
|
||||
server-id=1
|
||||
log-bin=mysql-bin
|
||||
binlog_format=ROW
|
||||
sync_binlog=1
|
||||
|
||||
# slave
|
||||
[mysqld]
|
||||
server-id=2
|
||||
relay_log=mysql-relay-bin
|
||||
read_only=1
|
1225
Kubernetes/resources/mysql集群/bitnami-mysql记录.md
Normal file
810
Kubernetes/resources/mysql集群/mysql-cluster.yaml
Normal file
@@ -0,0 +1,810 @@
|
||||
---
|
||||
# Source: mysql/templates/networkpolicy.yaml
|
||||
kind: NetworkPolicy
|
||||
apiVersion: networking.k8s.io/v1
|
||||
metadata:
|
||||
name: my-mysql
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
spec:
|
||||
podSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
egress:
|
||||
- {}
|
||||
ingress:
|
||||
# Allow connection from other cluster pods
|
||||
- ports:
|
||||
- port: 3306
|
||||
---
|
||||
# Source: mysql/templates/primary/pdb.yaml
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: my-mysql-primary
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
spec:
|
||||
maxUnavailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
---
|
||||
# Source: mysql/templates/secondary/pdb.yaml
|
||||
apiVersion: policy/v1
|
||||
kind: PodDisruptionBudget
|
||||
metadata:
|
||||
name: my-mysql-secondary
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
spec:
|
||||
maxUnavailable: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
---
|
||||
# Source: mysql/templates/serviceaccount.yaml
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: my-mysql
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
automountServiceAccountToken: false
|
||||
secrets:
|
||||
- name: my-mysql
|
||||
---
|
||||
# Source: mysql/templates/secrets.yaml
|
||||
apiVersion: v1
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: my-mysql
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
type: Opaque
|
||||
data:
|
||||
mysql-root-password: "N0ZveE5ZdXRobg=="
|
||||
mysql-password: "TVlvWXZYOFZiZQ=="
|
||||
mysql-replication-password: "NzNkc0NtbmtJMQ=="
|
||||
---
|
||||
# Source: mysql/templates/primary/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-mysql-primary
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
data:
|
||||
my.cnf: |-
|
||||
[mysqld]
|
||||
authentication_policy='* ,,'
|
||||
skip-name-resolve
|
||||
explicit_defaults_for_timestamp
|
||||
basedir=/opt/bitnami/mysql
|
||||
plugin_dir=/opt/bitnami/mysql/lib/plugin
|
||||
port=3306
|
||||
mysqlx=0
|
||||
mysqlx_port=33060
|
||||
socket=/opt/bitnami/mysql/tmp/mysql.sock
|
||||
datadir=/bitnami/mysql/data
|
||||
tmpdir=/opt/bitnami/mysql/tmp
|
||||
max_allowed_packet=16M
|
||||
bind-address=*
|
||||
pid-file=/opt/bitnami/mysql/tmp/mysqld.pid
|
||||
log-error=/opt/bitnami/mysql/logs/mysqld.log
|
||||
character-set-server=UTF8
|
||||
slow_query_log=0
|
||||
long_query_time=10.0
|
||||
|
||||
[client]
|
||||
port=3306
|
||||
socket=/opt/bitnami/mysql/tmp/mysql.sock
|
||||
default-character-set=UTF8
|
||||
plugin_dir=/opt/bitnami/mysql/lib/plugin
|
||||
|
||||
[manager]
|
||||
port=3306
|
||||
socket=/opt/bitnami/mysql/tmp/mysql.sock
|
||||
pid-file=/opt/bitnami/mysql/tmp/mysqld.pid
|
||||
---
|
||||
# Source: mysql/templates/secondary/configmap.yaml
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: my-mysql-secondary
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
data:
|
||||
my.cnf: |-
|
||||
[mysqld]
|
||||
authentication_policy='* ,,'
|
||||
skip-name-resolve
|
||||
explicit_defaults_for_timestamp
|
||||
basedir=/opt/bitnami/mysql
|
||||
plugin_dir=/opt/bitnami/mysql/lib/plugin
|
||||
port=3306
|
||||
mysqlx=0
|
||||
mysqlx_port=33060
|
||||
socket=/opt/bitnami/mysql/tmp/mysql.sock
|
||||
datadir=/bitnami/mysql/data
|
||||
tmpdir=/opt/bitnami/mysql/tmp
|
||||
max_allowed_packet=16M
|
||||
bind-address=*
|
||||
pid-file=/opt/bitnami/mysql/tmp/mysqld.pid
|
||||
log-error=/opt/bitnami/mysql/logs/mysqld.log
|
||||
character-set-server=UTF8
|
||||
slow_query_log=0
|
||||
long_query_time=10.0
|
||||
|
||||
[client]
|
||||
port=3306
|
||||
socket=/opt/bitnami/mysql/tmp/mysql.sock
|
||||
default-character-set=UTF8
|
||||
plugin_dir=/opt/bitnami/mysql/lib/plugin
|
||||
|
||||
[manager]
|
||||
port=3306
|
||||
socket=/opt/bitnami/mysql/tmp/mysql.sock
|
||||
pid-file=/opt/bitnami/mysql/tmp/mysqld.pid
|
||||
---
|
||||
# Source: mysql/templates/primary/svc-headless.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-mysql-primary-headless
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
spec:
|
||||
type: ClusterIP
|
||||
clusterIP: None
|
||||
publishNotReadyAddresses: true
|
||||
ports:
|
||||
- name: mysql
|
||||
port: 3306
|
||||
targetPort: mysql
|
||||
selector:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
---
|
||||
# Source: mysql/templates/primary/svc.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-mysql-primary
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
spec:
|
||||
type: ClusterIP
|
||||
sessionAffinity: None
|
||||
ports:
|
||||
- name: mysql
|
||||
port: 3306
|
||||
protocol: TCP
|
||||
targetPort: mysql
|
||||
nodePort: null
|
||||
selector:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
---
|
||||
# Source: mysql/templates/secondary/svc-headless.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-mysql-secondary-headless
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
spec:
|
||||
type: ClusterIP
|
||||
clusterIP: None
|
||||
publishNotReadyAddresses: true
|
||||
ports:
|
||||
- name: mysql
|
||||
port: 3306
|
||||
targetPort: mysql
|
||||
selector:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
---
|
||||
# Source: mysql/templates/secondary/svc.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: my-mysql-secondary
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
spec:
|
||||
type: ClusterIP
|
||||
sessionAffinity: None
|
||||
ports:
|
||||
- name: mysql
|
||||
port: 3306
|
||||
protocol: TCP
|
||||
targetPort: mysql
|
||||
nodePort: null
|
||||
selector:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
---
|
||||
# Source: mysql/templates/primary/statefulset.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: my-mysql-primary
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
spec:
|
||||
replicas: 1
|
||||
podManagementPolicy: ""
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
serviceName: my-mysql-primary-headless
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/configuration: a581348f7af561e486fad8d76b185ef64f865137e8229ff5d0fa6cdf95694ea1
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
spec:
|
||||
serviceAccountName: my-mysql
|
||||
|
||||
automountServiceAccountToken: false
|
||||
affinity:
|
||||
podAffinity:
|
||||
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 1
|
||||
nodeAffinity:
|
||||
|
||||
securityContext:
|
||||
fsGroup: 1001
|
||||
fsGroupChangePolicy: Always
|
||||
supplementalGroups: []
|
||||
sysctls: []
|
||||
initContainers:
|
||||
- name: preserve-logs-symlinks
|
||||
image: docker.io/bitnami/mysql:8.4.5-debian-12-r0
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
runAsGroup: 1001
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
seLinuxOptions: {}
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
resources:
|
||||
limits:
|
||||
cpu: 750m
|
||||
ephemeral-storage: 2Gi
|
||||
memory: 768Mi
|
||||
requests:
|
||||
cpu: 500m
|
||||
ephemeral-storage: 50Mi
|
||||
memory: 512Mi
|
||||
command:
|
||||
- /bin/bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
#!/bin/bash
|
||||
|
||||
. /opt/bitnami/scripts/libfs.sh
|
||||
# We copy the logs folder because it has symlinks to stdout and stderr
|
||||
if ! is_dir_empty /opt/bitnami/mysql/logs; then
|
||||
cp -r /opt/bitnami/mysql/logs /emptydir/app-logs-dir
|
||||
fi
|
||||
volumeMounts:
|
||||
- name: empty-dir
|
||||
mountPath: /emptydir
|
||||
containers:
|
||||
- name: mysql
|
||||
image: docker.io/bitnami/mysql:8.4.5-debian-12-r0
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
runAsGroup: 1001
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
seLinuxOptions: {}
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
env:
|
||||
- name: BITNAMI_DEBUG
|
||||
value: "false"
|
||||
- name: MYSQL_ROOT_PASSWORD_FILE
|
||||
value: /opt/bitnami/mysql/secrets/mysql-root-password
|
||||
- name: MYSQL_ENABLE_SSL
|
||||
value: "no"
|
||||
- name: MYSQL_PORT
|
||||
value: "3306"
|
||||
- name: MYSQL_DATABASE
|
||||
value: "my_database"
|
||||
- name: MYSQL_REPLICATION_MODE
|
||||
value: "master"
|
||||
- name: MYSQL_REPLICATION_USER
|
||||
value: "replicator"
|
||||
- name: MYSQL_REPLICATION_PASSWORD_FILE
|
||||
value: /opt/bitnami/mysql/secrets/mysql-replication-password
|
||||
envFrom:
|
||||
ports:
|
||||
- name: mysql
|
||||
containerPort: 3306
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
exec:
|
||||
command:
|
||||
- /bin/bash
|
||||
- -ec
|
||||
- |
|
||||
password_aux="${MYSQL_ROOT_PASSWORD:-}"
|
||||
if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then
|
||||
password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE")
|
||||
fi
|
||||
mysqladmin status -uroot -p"${password_aux}"
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
exec:
|
||||
command:
|
||||
- /bin/bash
|
||||
- -ec
|
||||
- |
|
||||
password_aux="${MYSQL_ROOT_PASSWORD:-}"
|
||||
if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then
|
||||
password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE")
|
||||
fi
|
||||
mysqladmin ping -uroot -p"${password_aux}" | grep "mysqld is alive"
|
||||
startupProbe:
|
||||
failureThreshold: 10
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
exec:
|
||||
command:
|
||||
- /bin/bash
|
||||
- -ec
|
||||
- |
|
||||
password_aux="${MYSQL_ROOT_PASSWORD:-}"
|
||||
if [[ -f "${MYSQL_ROOT_PASSWORD_FILE:-}" ]]; then
|
||||
password_aux=$(cat "$MYSQL_ROOT_PASSWORD_FILE")
|
||||
fi
|
||||
mysqladmin ping -uroot -p"${password_aux}" | grep "mysqld is alive"
|
||||
resources:
|
||||
limits:
|
||||
cpu: 750m
|
||||
ephemeral-storage: 2Gi
|
||||
memory: 768Mi
|
||||
requests:
|
||||
cpu: 500m
|
||||
ephemeral-storage: 50Mi
|
||||
memory: 512Mi
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /bitnami/mysql
|
||||
- name: empty-dir
|
||||
mountPath: /tmp
|
||||
subPath: tmp-dir
|
||||
- name: empty-dir
|
||||
mountPath: /opt/bitnami/mysql/conf
|
||||
subPath: app-conf-dir
|
||||
- name: empty-dir
|
||||
mountPath: /opt/bitnami/mysql/tmp
|
||||
subPath: app-tmp-dir
|
||||
- name: empty-dir
|
||||
mountPath: /opt/bitnami/mysql/logs
|
||||
subPath: app-logs-dir
|
||||
- name: config
|
||||
mountPath: /opt/bitnami/mysql/conf/my.cnf
|
||||
subPath: my.cnf
|
||||
- name: mysql-credentials
|
||||
mountPath: /opt/bitnami/mysql/secrets/
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: my-mysql-primary
|
||||
- name: mysql-credentials
|
||||
secret:
|
||||
secretName: my-mysql
|
||||
items:
|
||||
- key: mysql-root-password
|
||||
path: mysql-root-password
|
||||
- key: mysql-password
|
||||
path: mysql-password
|
||||
- key: mysql-replication-password
|
||||
path: mysql-replication-password
|
||||
- name: empty-dir
|
||||
emptyDir: {}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/component: primary
|
||||
spec:
|
||||
accessModes:
|
||||
- "ReadWriteOnce"
|
||||
resources:
|
||||
requests:
|
||||
storage: "8Gi"
|
||||
---
|
||||
# Source: mysql/templates/secondary/statefulset.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: my-mysql-secondary
|
||||
namespace: "default"
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
spec:
|
||||
replicas: 2
|
||||
podManagementPolicy: ""
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
serviceName: my-mysql-secondary-headless
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
annotations:
|
||||
checksum/configuration: 266f59054731f333d22a8fb2b77b80c341a76f974f81b0d91f20f985a2d6fabb
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/version: 8.4.5
|
||||
helm.sh/chart: mysql-12.3.4
|
||||
app.kubernetes.io/part-of: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
spec:
|
||||
serviceAccountName: my-mysql
|
||||
|
||||
automountServiceAccountToken: false
|
||||
affinity:
|
||||
podAffinity:
|
||||
|
||||
podAntiAffinity:
|
||||
preferredDuringSchedulingIgnoredDuringExecution:
|
||||
- podAffinityTerm:
|
||||
labelSelector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
topologyKey: kubernetes.io/hostname
|
||||
weight: 1
|
||||
nodeAffinity:
|
||||
|
||||
securityContext:
|
||||
fsGroup: 1001
|
||||
fsGroupChangePolicy: Always
|
||||
supplementalGroups: []
|
||||
sysctls: []
|
||||
initContainers:
|
||||
- name: preserve-logs-symlinks
|
||||
image: docker.io/bitnami/mysql:8.4.5-debian-12-r0
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
runAsGroup: 1001
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
seLinuxOptions: {}
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
resources:
|
||||
limits:
|
||||
cpu: 750m
|
||||
ephemeral-storage: 2Gi
|
||||
memory: 768Mi
|
||||
requests:
|
||||
cpu: 500m
|
||||
ephemeral-storage: 50Mi
|
||||
memory: 512Mi
|
||||
command:
|
||||
- /bin/bash
|
||||
args:
|
||||
- -ec
|
||||
- |
|
||||
#!/bin/bash
|
||||
|
||||
. /opt/bitnami/scripts/libfs.sh
|
||||
# We copy the logs folder because it has symlinks to stdout and stderr
|
||||
if ! is_dir_empty /opt/bitnami/mysql/logs; then
|
||||
cp -r /opt/bitnami/mysql/logs /emptydir/app-logs-dir
|
||||
fi
|
||||
volumeMounts:
|
||||
- name: empty-dir
|
||||
mountPath: /emptydir
|
||||
containers:
|
||||
- name: mysql
|
||||
image: docker.io/bitnami/mysql:8.4.5-debian-12-r0
|
||||
imagePullPolicy: "IfNotPresent"
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop:
|
||||
- ALL
|
||||
readOnlyRootFilesystem: true
|
||||
runAsGroup: 1001
|
||||
runAsNonRoot: true
|
||||
runAsUser: 1001
|
||||
seLinuxOptions: {}
|
||||
seccompProfile:
|
||||
type: RuntimeDefault
|
||||
env:
|
||||
- name: BITNAMI_DEBUG
|
||||
value: "false"
|
||||
- name: MYSQL_REPLICATION_MODE
|
||||
value: "slave"
|
||||
- name: MYSQL_MASTER_HOST
|
||||
value: my-mysql-primary
|
||||
- name: MYSQL_MASTER_PORT_NUMBER
|
||||
value: "3306"
|
||||
- name: MYSQL_MASTER_ROOT_USER
|
||||
value: "root"
|
||||
- name: MYSQL_PORT
|
||||
value: "3306"
|
||||
- name: MYSQL_REPLICATION_USER
|
||||
value: "replicator"
|
||||
- name: MYSQL_ENABLE_SSL
|
||||
value: "no"
|
||||
- name: MYSQL_MASTER_ROOT_PASSWORD_FILE
|
||||
value: /opt/bitnami/mysql/secrets/mysql-root-password
|
||||
- name: MYSQL_REPLICATION_PASSWORD_FILE
|
||||
value: /opt/bitnami/mysql/secrets/mysql-replication-password
|
||||
envFrom:
|
||||
ports:
|
||||
- name: mysql
|
||||
containerPort: 3306
|
||||
livenessProbe:
|
||||
failureThreshold: 3
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
exec:
|
||||
command:
|
||||
- /bin/bash
|
||||
- -ec
|
||||
- |
|
||||
password_aux="${MYSQL_MASTER_ROOT_PASSWORD:-}"
|
||||
if [[ -f "${MYSQL_MASTER_ROOT_PASSWORD_FILE:-}" ]]; then
|
||||
password_aux=$(cat "$MYSQL_MASTER_ROOT_PASSWORD_FILE")
|
||||
fi
|
||||
mysqladmin status -uroot -p"${password_aux}"
|
||||
readinessProbe:
|
||||
failureThreshold: 3
|
||||
initialDelaySeconds: 5
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
exec:
|
||||
command:
|
||||
- /bin/bash
|
||||
- -ec
|
||||
- |
|
||||
password_aux="${MYSQL_MASTER_ROOT_PASSWORD:-}"
|
||||
if [[ -f "${MYSQL_MASTER_ROOT_PASSWORD_FILE:-}" ]]; then
|
||||
password_aux=$(cat "$MYSQL_MASTER_ROOT_PASSWORD_FILE")
|
||||
fi
|
||||
mysqladmin ping -uroot -p"${password_aux}" | grep "mysqld is alive"
|
||||
startupProbe:
|
||||
failureThreshold: 15
|
||||
initialDelaySeconds: 15
|
||||
periodSeconds: 10
|
||||
successThreshold: 1
|
||||
timeoutSeconds: 1
|
||||
exec:
|
||||
command:
|
||||
- /bin/bash
|
||||
- -ec
|
||||
- |
|
||||
password_aux="${MYSQL_MASTER_ROOT_PASSWORD:-}"
|
||||
if [[ -f "${MYSQL_MASTER_ROOT_PASSWORD_FILE:-}" ]]; then
|
||||
password_aux=$(cat "$MYSQL_MASTER_ROOT_PASSWORD_FILE")
|
||||
fi
|
||||
mysqladmin ping -uroot -p"${password_aux}" | grep "mysqld is alive"
|
||||
resources:
|
||||
limits:
|
||||
cpu: 750m
|
||||
ephemeral-storage: 2Gi
|
||||
memory: 768Mi
|
||||
requests:
|
||||
cpu: 500m
|
||||
ephemeral-storage: 50Mi
|
||||
memory: 512Mi
|
||||
volumeMounts:
|
||||
- name: data
|
||||
mountPath: /bitnami/mysql
|
||||
- name: config
|
||||
mountPath: /opt/bitnami/mysql/conf/my.cnf
|
||||
subPath: my.cnf
|
||||
- name: mysql-credentials
|
||||
mountPath: /opt/bitnami/mysql/secrets/
|
||||
- name: empty-dir
|
||||
mountPath: /tmp
|
||||
subPath: tmp-dir
|
||||
- name: empty-dir
|
||||
mountPath: /opt/bitnami/mysql/conf
|
||||
subPath: app-conf-dir
|
||||
- name: empty-dir
|
||||
mountPath: /opt/bitnami/mysql/tmp
|
||||
subPath: app-tmp-dir
|
||||
- name: empty-dir
|
||||
mountPath: /opt/bitnami/mysql/logs
|
||||
subPath: app-logs-dir
|
||||
volumes:
|
||||
- name: config
|
||||
configMap:
|
||||
name: my-mysql-secondary
|
||||
- name: mysql-credentials
|
||||
secret:
|
||||
secretName: my-mysql
|
||||
items:
|
||||
- key: mysql-root-password
|
||||
path: mysql-root-password
|
||||
- key: mysql-replication-password
|
||||
path: mysql-replication-password
|
||||
- name: empty-dir
|
||||
emptyDir: {}
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: data
|
||||
labels:
|
||||
app.kubernetes.io/instance: my-mysql
|
||||
app.kubernetes.io/name: mysql
|
||||
app.kubernetes.io/component: secondary
|
||||
spec:
|
||||
accessModes:
|
||||
- "ReadWriteOnce"
|
||||
resources:
|
||||
requests:
|
||||
storage: "8Gi"
|
326
Kubernetes/安全认证.md
Normal file
@@ -0,0 +1,326 @@
|
||||
# 安全认证
|
||||
|
||||
## 访问控制概述
|
||||
|
||||
Kubernetes作为一个分布式集群的管理工具,保证集群的安全性是其一个重要的任务。所谓的安全性其实就是保证对Kubernetes的各种**客户端**进行**认证和鉴权**操作。
|
||||
|
||||
**客户端**
|
||||
|
||||
在Kubernetes集群中,客户端通常有两类:
|
||||
|
||||
- **User Account**:一般是独立于kubernetes之外的其他服务管理的用户账号。
|
||||
- **Service Account**:kubernetes管理的账号,用于为Pod中的服务进程在访问Kubernetes时提供身份标识。
|
||||
|
||||
<img src="安全认证/image-20240918140658979.png" alt="image-20240918140658979" style="zoom:33%;" />
|
||||
|
||||
**认证、授权与准入控制**
|
||||
|
||||
ApiServer是访问及管理资源对象的唯一入口。任何一个请求访问ApiServer,都要经过下面三个流程:
|
||||
|
||||
- Authentication(认证):身份鉴别,只有正确的账号才能够通过认证
|
||||
- Authorization(授权): 判断用户是否有权限对访问的资源执行特定的动作
|
||||
- Admission Control(准入控制):用于补充授权机制以实现更加精细的访问控制功能。
|
||||
|
||||
<img src="安全认证/image-20240918141003953.png" alt="image-20240918141003953" style="zoom:33%;" />
|
||||
|
||||
## 认证管理
|
||||
|
||||
Kubernetes集群安全的最关键点在于如何识别并认证客户端身份,它提供了3种客户端身份认证方式:
|
||||
|
||||
- HTTP Base认证:通过用户名+密码的方式认证
|
||||
- 这种认证方式是把“用户名:密码”用BASE64算法进行编码后的字符串放在HTTP请求中的Header Authorization域里发送给服务端。服务端收到后进行解码,获取用户名及密码,然后进行用户身份认证的过程。
|
||||
|
||||
- HTTP Token认证:通过一个Token来识别合法用户
|
||||
- 这种认证方式是用一个很长的难以被模仿的字符串--Token来表明客户身份的一种方式。每个Token对应一个用户名,当客户端发起API调用请求时,需要在HTTP Header里放入Token,API Server接到Token后会跟服务器中保存的token进行比对,然后进行用户身份认证的过程。
|
||||
|
||||
- HTTPS证书认证:基于CA根证书签名的双向数字证书认证方式
|
||||
- 这种认证方式是安全性最高的一种方式,但是同时也是操作起来最麻烦的一种方式。
|
||||
|
||||
|
||||
<img src="安全认证/image-20240918141303987.png" alt="image-20240918141303987" style="zoom:33%;" />
|
||||
|
||||
**HTTPS认证大体分为3个过程:**
|
||||
|
||||
- 证书申请和下发
|
||||
|
||||
- HTTPS通信双方的服务器向CA机构申请证书,CA机构下发根证书、服务端证书及私钥给申请者
|
||||
|
||||
- 客户端和服务端的双向认证
|
||||
|
||||
- 客户端向服务器端发起请求,服务端下发自己的证书给客户端,
|
||||
- 客户端接收到证书后,通过私钥解密证书,在证书中获得服务端的公钥,
|
||||
- 客户端利用服务器端的公钥认证证书中的信息,如果一致,则认可这个服务器
|
||||
|
||||
- 客户端发送自己的证书给服务器端,服务端接收到证书后,通过私钥解密证书,
|
||||
- 在证书中获得客户端的公钥,并用该公钥认证证书信息,确认客户端是否合法
|
||||
|
||||
|
||||
- 服务器端和客户端进行通信
|
||||
- 服务器端和客户端协商好加密方案后,客户端会产生一个随机的秘钥并加密,然后发送到服务器端。
|
||||
- 服务器端接收这个秘钥后,双方接下来通信的所有内容都通过该随机秘钥加密
|
||||
|
||||
> 注意: Kubernetes允许同时配置多种认证方式,只要其中任意一个方式认证通过即可
|
||||
|
||||
## 授权管理
|
||||
|
||||
授权发生在认证成功之后,通过认证就可以知道请求用户是谁, 然后Kubernetes会根据事先定义的授权策略来决定用户是否有权限访问,这个过程就称为授权。
|
||||
|
||||
每个发送到ApiServer的请求都带上了用户和资源的信息:比如发送请求的用户、请求的路径、请求的动作等,授权就是根据这些信息和授权策略进行比较,如果符合策略,则认为授权通过,否则会返回错误。
|
||||
|
||||
API Server目前支持以下几种授权策略:
|
||||
|
||||
- AlwaysDeny:表示拒绝所有请求,一般用于测试
|
||||
- AlwaysAllow:允许接收所有请求,相当于集群不需要授权流程(Kubernetes默认的策略)
|
||||
- ABAC:基于属性的访问控制,表示使用用户配置的授权规则对用户请求进行匹配和控制
|
||||
- Webhook:通过调用外部REST服务对用户进行授权
|
||||
- Node:是一种专用模式,用于对kubelet发出的请求进行访问控制
|
||||
- RBAC:基于角色的访问控制(kubeadm安装方式下的默认选项)
|
||||
|
||||
RBAC(Role-Based Access Control) 基于角色的访问控制,主要是在描述一件事情:**给哪些对象授予了哪些权限**
|
||||
|
||||
其中涉及到了下面几个概念:
|
||||
|
||||
- 对象:User、Groups、ServiceAccount
|
||||
- 角色:代表着一组定义在资源上的可操作动作(权限)的集合
|
||||
- 绑定:将定义好的角色跟用户绑定在一起
|
||||
|
||||
<img src="安全认证/image-20240918142052853.png" alt="image-20240918142052853" style="zoom:33%;" />
|
||||
|
||||
RBAC引入了4个顶级资源对象:
|
||||
|
||||
- Role、ClusterRole:角色,用于指定一组权限
|
||||
- RoleBinding、ClusterRoleBinding:角色绑定,用于将角色(权限)赋予给对象
|
||||
|
||||
**Role、ClusterRole**
|
||||
|
||||
一个角色就是一组权限的集合,这里的权限都是许可形式的(白名单)。
|
||||
|
||||
```yaml
|
||||
# Role只能对命名空间内的资源进行授权,需要指定nameapce
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: Role
|
||||
metadata:
|
||||
namespace: default
|
||||
name: authorization-role
|
||||
rules:
|
||||
- apiGroups: [""] # 支持的API组列表,"" 空字符串,表示核心API群
|
||||
resources: ["pods"] # 支持的资源对象列表
|
||||
verbs: ["get", "watch", "list"] # 允许的对资源对象的操作方法列表
|
||||
```
|
||||
|
||||
```yaml
|
||||
# ClusterRole可以对集群范围内资源、跨namespaces的范围资源、非资源类型进行授权
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: authorization-clusterrole
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
```
|
||||
|
||||
需要详细说明的是,rules中的参数:
|
||||
|
||||
- apiGroups: 支持的API组列表
|
||||
|
||||
```
|
||||
"","apps", "autoscaling", "batch"
|
||||
```
|
||||
|
||||
- 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"
|
||||
```
|
||||
|
||||
**RoleBinding、ClusterRoleBinding**
|
||||
|
||||
角色绑定用来把一个角色绑定到一个目标对象上,绑定目标可以是User、Group或者ServiceAccount。
|
||||
|
||||
```yaml
|
||||
# RoleBinding可以将同一namespace中的subject绑定到某个Role下,则此subject即具有该Role定义的权限
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: authorization-role-binding
|
||||
namespace: dev
|
||||
subjects:
|
||||
- kind: User
|
||||
name: eagle
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: authorization-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
```
|
||||
|
||||
```yaml
|
||||
# ClusterRoleBinding在整个集群级别和所有namespaces将特定的subject与ClusterRole绑定,授予权限
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: authorization-clusterrole-binding
|
||||
subjects:
|
||||
- kind: User
|
||||
name: eagle
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: authorization-clusterrole
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
```
|
||||
|
||||
**RoleBinding引用ClusterRole进行授权**
|
||||
|
||||
RoleBinding可以引用ClusterRole,对属于同一命名空间内ClusterRole定义的资源主体进行授权。
|
||||
|
||||
> 一种很常用的做法就是,集群管理员为集群范围预定义好一组角色(ClusterRole),然后在多个命名空间中重复使用这些ClusterRole。这样可以大幅提高授权管理工作效率,也使得各个命名空间下的基础性授权规则与使用体验保持一致。
|
||||
|
||||
```yaml
|
||||
# 虽然authorization-clusterrole是一个集群角色,但是因为使用了RoleBinding
|
||||
# 所以eagle只能读取dev命名空间中的资源
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: authorization-role-binding-ns
|
||||
namespace: dev
|
||||
subjects:
|
||||
- kind: User
|
||||
name: eagle
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
roleRef:
|
||||
kind: ClusterRole
|
||||
name: authorization-clusterrole
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
```
|
||||
|
||||
**实战:创建一个只能管理dev空间下Pods资源的账号**
|
||||
|
||||
1) 创建账号
|
||||
|
||||
```shell
|
||||
# 1) 创建证书
|
||||
$ cd /etc/kubernetes/pki/
|
||||
$ (umask 077;openssl genrsa -out devman.key 2048)
|
||||
|
||||
# 2) 用apiserver的证书去签署
|
||||
# 2-1) 签名申请,申请的用户是devman,组是devgroup
|
||||
$ openssl req -new -key devman.key -out devman.csr -subj "/CN=devman/O=devgroup"
|
||||
# 2-2) 签署证书
|
||||
$ openssl x509 -req -in devman.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out devman.crt -days 3650
|
||||
|
||||
# 3) 设置集群、用户、上下文信息
|
||||
$ kubectl config set-cluster kubernetes --embed-certs=true --certificate-authority=/etc/kubernetes/pki/ca.crt --server=https://192.168.173.100:6443
|
||||
|
||||
$ kubectl config set-credentials devman --embed-certs=true --client-certificate=/etc/kubernetes/pki/devman.crt --client-key=/etc/kubernetes/pki/devman.key
|
||||
|
||||
$ kubectl config set-context devman@kubernetes --cluster=kubernetes --user=devman
|
||||
|
||||
# 切换账户到devman
|
||||
$ kubectl config use-context devman@kubernetes
|
||||
Switched to context "devman@kubernetes".
|
||||
|
||||
# 查看default下pod,发现没有权限
|
||||
$ kubectl get pods
|
||||
Error from server (Forbidden): pods is forbidden: User "devman" cannot list resource "pods" in API group "" in the namespace "default"
|
||||
|
||||
# 切换回admin账户
|
||||
$ kubectl config use-context kubernetes-admin@kubernetes
|
||||
Switched to context "kubernetes-admin@kubernetes".
|
||||
```
|
||||
|
||||
2) 创建Role和RoleBinding,为devman用户授权,dev-role.yaml
|
||||
|
||||
```yaml
|
||||
kind: Role
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
namespace: default
|
||||
name: dev-role
|
||||
rules:
|
||||
- apiGroups: [""]
|
||||
resources: ["pods"]
|
||||
verbs: ["get", "watch", "list"]
|
||||
|
||||
---
|
||||
|
||||
kind: RoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: authorization-role-binding
|
||||
namespace: default
|
||||
subjects:
|
||||
- kind: User
|
||||
name: devman
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
roleRef:
|
||||
kind: Role
|
||||
name: dev-role
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
```
|
||||
|
||||
3) 切换账户,再次验证
|
||||
|
||||
```shell
|
||||
# 切换账户到devman
|
||||
$ kubectl config use-context devman@kubernetes
|
||||
Switched to context "devman@kubernetes".
|
||||
|
||||
# 再次查看
|
||||
$ kubectl get pods
|
||||
NAME READY STATUS RESTARTS AGE
|
||||
myapp-6687b4f9d7-562lv 1/1 Running 0 31m
|
||||
myapp-6687b4f9d7-6m7wb 1/1 Running 0 31m
|
||||
myapp-6687b4f9d7-d9tgk 1/1 Running 0 31m
|
||||
myapp-6687b4f9d7-fsqw7 1/1 Running 0 31m
|
||||
myapp-6687b4f9d7-j7c4x 1/1 Running 0 31m
|
||||
myapp-6687b4f9d7-j7dfx 1/1 Running 0 31m
|
||||
myapp-6687b4f9d7-ll6z2 1/1 Running 0 31m
|
||||
myapp-6687b4f9d7-lwqj4 1/1 Running 0 31m
|
||||
myapp-6687b4f9d7-mvdtg 1/1 Running 0 31m
|
||||
myapp-6687b4f9d7-qlw5x 1/1 Running 0 31m
|
||||
|
||||
# 为了不影响后面的学习,切回admin账户
|
||||
$ kubectl config use-context kubernetes-admin@kubernetes
|
||||
Switched to context "kubernetes-admin@kubernetes".
|
||||
```
|
||||
|
||||
## 准入控制
|
||||
|
||||
通过了前面的认证和授权之后,还需要经过准入控制处理通过之后,apiserver才会处理这个请求。
|
||||
|
||||
准入控制是一个可配置的控制器列表,可以通过在Api-Server上通过命令行设置选择执行哪些准入控制器:
|
||||
|
||||
```
|
||||
--admission-control=NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeLabel,
|
||||
DefaultStorageClass,ResourceQuota,DefaultTolerationSeconds
|
||||
```
|
||||
|
||||
只有当所有的准入控制器都检查通过之后,apiserver才执行该请求,否则返回拒绝。
|
||||
|
||||
当前可配置的Admission Control准入控制如下:
|
||||
|
||||
- AlwaysAdmit:允许所有请求
|
||||
- AlwaysDeny:禁止所有请求,一般用于测试
|
||||
- AlwaysPullImages:在启动容器之前总去下载镜像
|
||||
- DenyExecOnPrivileged:它会拦截所有想在Privileged Container上执行命令的请求
|
||||
- ImagePolicyWebhook:这个插件将允许后端的一个Webhook程序来完成admission controller的功能。
|
||||
- Service Account:实现ServiceAccount实现了自动化
|
||||
- SecurityContextDeny:这个插件将使用SecurityContext的Pod中的定义全部失效
|
||||
- ResourceQuota:用于资源配额管理目的,观察所有请求,确保在namespace上的配额不会超标
|
||||
- LimitRanger:用于资源限制管理,作用于namespace上,确保对Pod进行资源限制
|
||||
- InitialResources:为未设置资源请求与限制的Pod,根据其镜像的历史资源的使用情况进行设置
|
||||
- NamespaceLifecycle:如果尝试在一个不存在的namespace中创建资源对象,则该创建请求将被拒绝。当删除一个namespace时,系统将会删除该namespace中所有对象。
|
||||
- DefaultStorageClass:为了实现共享存储的动态供应,为未指定StorageClass或PV的PVC尝试匹配默认的StorageClass,尽可能减少用户在申请PVC时所需了解的后端存储细节
|
||||
- DefaultTolerationSeconds:这个插件为那些没有设置forgiveness tolerations并具有notready:NoExecute和unreachable:NoExecute两种taints的Pod设置默认的“容忍”时间,为5min
|
||||
- PodSecurityPolicy:这个插件用于在创建或修改Pod时决定是否根据Pod的security context和可用的PodSecurityPolicy对Pod的安全策略进行控制
|
||||
|
BIN
Kubernetes/安全认证/image-20240918140658979.png
Normal file
After Width: | Height: | Size: 169 KiB |
BIN
Kubernetes/安全认证/image-20240918141003953.png
Normal file
After Width: | Height: | Size: 197 KiB |
BIN
Kubernetes/安全认证/image-20240918141303987.png
Normal file
After Width: | Height: | Size: 153 KiB |
BIN
Kubernetes/安全认证/image-20240918142052853.png
Normal file
After Width: | Height: | Size: 308 KiB |
47
Kubernetes/常见命令手册.md
Normal file
@@ -0,0 +1,47 @@
|
||||
```shell
|
||||
# 通过 API 生成临时 Token(有效期 1 小时)
|
||||
kubectl create token calico-node -n kube-system --duration=1h
|
||||
|
||||
# 检查 Token 有效性
|
||||
curl -k -H "Authorization: Bearer <TOKEN>" https://<API-SERVER>:6443/api/v1/namespaces/kube-system/pods
|
||||
|
||||
# 升级chart
|
||||
helm upgrade ingress-nginx ./ingress-nginx -n ingress --values ./ingress-nginx/values.yaml
|
||||
|
||||
# 清理缓存
|
||||
sudo killall -HUP mDNSResponder
|
||||
|
||||
# 配置主从复制
|
||||
stop slave;
|
||||
change master to master_host='mysql-master',master_user='replicator',master_password='replicator123',master_log_file='mysql-bin.000001',master_log_pos=1786;
|
||||
start slave;
|
||||
|
||||
# 清除所有复制配置(包括master.info文件)
|
||||
reset slave all;
|
||||
|
||||
# 访问业务
|
||||
[root@master01 resources]# curl -H "host: bbs.iproute.cn" 10.3.202.101:`kubectl get svc/ingress-nginx-controller -n ingress-nginx -o jsonpath="{.spec.ports[?(@.port==80)].nodePort}" -n ingress`
|
||||
|
||||
# calicoctl相关命令
|
||||
[root@master01 ~]# ./calicoctl-linux-amd64 node status
|
||||
Calico process is running.
|
||||
|
||||
IPv4 BGP status
|
||||
+--------------+-------------------+-------+----------+-------------+
|
||||
| PEER ADDRESS | PEER TYPE | STATE | SINCE | INFO |
|
||||
+--------------+-------------------+-------+----------+-------------+
|
||||
| 10.3.204.101 | node-to-node mesh | up | 05:11:14 | Established |
|
||||
| 10.3.204.102 | node-to-node mesh | up | 05:12:46 | Established |
|
||||
+--------------+-------------------+-------+----------+-------------+
|
||||
|
||||
[root@master01 ~]# ./calicoctl-linux-amd64 get ipPool --allow-version-mismatch -o wide
|
||||
NAME CIDR NAT IPIPMODE VXLANMODE DISABLED SELECTOR
|
||||
default-ipv4-ippool 10.244.0.0/16 true Never Never false all()
|
||||
[root@master01 ~]# calicoctl ipam show --show-blocks --allow-version-mismatch
|
||||
[root@master01 ~]# calicoctl ipam show --allow-version-mismatch
|
||||
+----------+---------------+-----------+------------+-------------+
|
||||
| GROUPING | CIDR | IPS TOTAL | IPS IN USE | IPS FREE |
|
||||
+----------+---------------+-----------+------------+-------------+
|
||||
| IP Pool | 10.244.0.0/16 | 65536 | 2294 (4%) | 63242 (96%) |
|
||||
+----------+---------------+-----------+------------+-------------+
|
||||
```
|
289
Kubernetes/手动安装手册.md
Normal file
@@ -0,0 +1,289 @@
|
||||
# 环境准备
|
||||
|
||||
| 角色 | 主机名 | ip地址 | 配置 |
|
||||
| :----- | -------- | --------------- | ------- |
|
||||
| master | master01 | 192.168.173.100 | 2C4G60G |
|
||||
| node01 | node01 | 192.168.173.101 | 2C2G60G |
|
||||
| node02 | node2 | 192.168.173.102 | 2C2G60G |
|
||||
|
||||
# 环境初始化
|
||||
|
||||
```bash
|
||||
# 网卡配置
|
||||
# cat /etc/NetworkManager/system-connections/ens160.nmconnection
|
||||
[ipv4]
|
||||
method=manual
|
||||
address1=192.168.173.100/24,192.168.173.2
|
||||
dns=114.114.114.114;114.114.115.115
|
||||
# cat /etc/NetworkManager/system-connections/ens192.nmconnection
|
||||
[connection]
|
||||
autoconnect=false
|
||||
|
||||
# 调用 nmcli 重启设备和连接配置
|
||||
nmcli d d ens192
|
||||
nmcli d r ens160
|
||||
nmcli c r ens160
|
||||
```
|
||||
|
||||
```bash
|
||||
# Rocky 系统软件源更换
|
||||
sed -e 's|^mirrorlist=|#mirrorlist=|g' \
|
||||
-e 's|^#baseurl=http://dl.rockylinux.org/$contentdir|baseurl=https://mirrors.aliyun.com/rockylinux|g' \
|
||||
-i.bak \
|
||||
/etc/yum.repos.d/[Rr]ocky*.repo
|
||||
|
||||
dnf makecache
|
||||
# 防火墙修改 firewalld 为 iptables
|
||||
systemctl stop firewalld
|
||||
systemctl disable firewalld
|
||||
|
||||
yum -y install iptables-services
|
||||
systemctl start iptables
|
||||
iptables -F
|
||||
systemctl enable iptables
|
||||
service iptables save
|
||||
# 禁用 Selinux
|
||||
setenforce 0
|
||||
sed -i "s/SELINUX=enforcing/SELINUX=disabled/g" /etc/selinux/config
|
||||
grubby --update-kernel ALL --args selinux=0
|
||||
# 查看是否禁用,grubby --info DEFAULT
|
||||
# 回滚内核层禁用操作,grubby --update-kernel ALL --remove-args selinux
|
||||
# 设置时区
|
||||
timedatectl set-timezone Asia/Shanghai
|
||||
```
|
||||
|
||||
```bash
|
||||
# 关闭 swap 分区
|
||||
swapoff -a
|
||||
sed -i 's:/dev/mapper/rl-swap:#/dev/mapper/rl-swap:g' /etc/fstab
|
||||
|
||||
# 修改主机名
|
||||
hostnamectl set-hostname k8s-node01
|
||||
# 安装 ipvs
|
||||
yum install -y ipvsadm
|
||||
# 开启路由转发
|
||||
echo 'net.ipv4.ip_forward=1' >> /etc/sysctl.conf
|
||||
sysctl -p
|
||||
# 加载 bridge
|
||||
yum install -y epel-release
|
||||
yum install -y bridge-utils
|
||||
|
||||
modprobe br_netfilter
|
||||
echo 'br_netfilter' >> /etc/modules-load.d/bridge.conf
|
||||
echo 'net.bridge.bridge-nf-call-iptables=1' >> /etc/sysctl.conf
|
||||
echo 'net.bridge.bridge-nf-call-ip6tables=1' >> /etc/sysctl.conf
|
||||
sysctl -p
|
||||
```
|
||||
|
||||
```bash
|
||||
# 添加 docker-ce yum 源
|
||||
# 中科大(ustc)
|
||||
sudo dnf config-manager --add-repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo
|
||||
cd /etc/yum.repos.d
|
||||
# 切换中科大源
|
||||
sed -i 's#download.docker.com#mirrors.ustc.edu.cn/docker-ce#g' docker-ce.repo
|
||||
|
||||
# 安装 docker-ce
|
||||
yum -y install docker-ce
|
||||
|
||||
# 配置 daemon.
|
||||
cat > /etc/docker/daemon.json <<EOF
|
||||
{
|
||||
"data-root": "/data/docker",
|
||||
"exec-opts": ["native.cgroupdriver=systemd"],
|
||||
"log-driver": "json-file",
|
||||
"log-opts": {
|
||||
"max-size": "100m",
|
||||
"max-file": "100"
|
||||
},
|
||||
"registry-mirrors": [
|
||||
"https://docker.1ms.run",
|
||||
"https://docker.1panel.live",
|
||||
"https://docker.xuanyuan.me",
|
||||
"https://dockerproxy.net",
|
||||
"https://docker.fast360.cn",
|
||||
"https://cloudlayer.icu",
|
||||
"https://docker-registry.nmqu.com",
|
||||
"https://hub.amingg.com",
|
||||
"https://docker.amingg.com",
|
||||
"https://docker.hlmirror.com"
|
||||
]
|
||||
}
|
||||
EOF
|
||||
mkdir -p /etc/systemd/system/docker.service.d
|
||||
|
||||
# 重启docker服务
|
||||
systemctl daemon-reload && systemctl restart docker && systemctl enable docker
|
||||
|
||||
```
|
||||
|
||||
```bash
|
||||
# 安装 cri-docker
|
||||
wget https://github.com/Mirantis/cri-dockerd/releases/download/v0.3.9/cri-dockerd-0.3.9.amd64.tgz
|
||||
tar -xf cri-dockerd-0.3.9.amd64.tgz
|
||||
cp cri-dockerd/cri-dockerd /usr/bin/
|
||||
chmod +x /usr/bin/cri-dockerd
|
||||
|
||||
# 配置 cri-docker 服务
|
||||
cat <<"EOF" > /usr/lib/systemd/system/cri-docker.service
|
||||
[Unit]
|
||||
Description=CRI Interface for Docker Application Container Engine
|
||||
Documentation=https://docs.mirantis.com
|
||||
After=network-online.target firewalld.service docker.service
|
||||
Wants=network-online.target
|
||||
Requires=cri-docker.socket
|
||||
[Service]
|
||||
Type=notify
|
||||
ExecStart=/usr/bin/cri-dockerd --network-plugin=cni --pod-infra-container-image=registry.aliyuncs.com/google_containers/pause:3.8
|
||||
ExecReload=/bin/kill -s HUP $MAINPID
|
||||
TimeoutSec=0
|
||||
RestartSec=2
|
||||
Restart=always
|
||||
StartLimitBurst=3
|
||||
StartLimitInterval=60s
|
||||
LimitNOFILE=infinity
|
||||
LimitNPROC=infinity
|
||||
LimitCORE=infinity
|
||||
TasksMax=infinity
|
||||
Delegate=yes
|
||||
KillMode=process
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
EOF
|
||||
|
||||
# 添加 cri-docker 套接字
|
||||
cat <<"EOF" > /usr/lib/systemd/system/cri-docker.socket
|
||||
[Unit]
|
||||
Description=CRI Docker Socket for the API
|
||||
PartOf=cri-docker.service
|
||||
[Socket]
|
||||
ListenStream=%t/cri-dockerd.sock
|
||||
SocketMode=0660
|
||||
SocketUser=root
|
||||
SocketGroup=docker
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
EOF
|
||||
|
||||
# 启动 cri-docker 对应服务
|
||||
systemctl daemon-reload
|
||||
systemctl enable cri-docker
|
||||
systemctl start cri-docker
|
||||
systemctl is-active cri-docker
|
||||
```
|
||||
|
||||
```bash
|
||||
# 添加 kubeadm yum 源
|
||||
cat <<EOF > /etc/yum.repos.d/kubernetes.repo
|
||||
[kubernetes]
|
||||
name=Kubernetes
|
||||
baseurl=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/
|
||||
enabled=1
|
||||
gpgcheck=1
|
||||
gpgkey=https://pkgs.k8s.io/core:/stable:/v1.29/rpm/repodata/repomd.xml.key
|
||||
exclude=kubelet kubeadm kubectl cri-tools kubernetes-cni
|
||||
EOF
|
||||
|
||||
# 安装 kubeadm 1.29 版本
|
||||
yum install -y kubelet-1.29.0 kubectl-1.29.0 kubeadm-1.29.0
|
||||
systemctl enable kubelet.service
|
||||
```
|
||||
|
||||
```bash
|
||||
# 初始化主节点
|
||||
kubeadm init\
|
||||
--apiserver-advertise-address=192.168.173.100\
|
||||
--image-repository registry.aliyuncs.com/google_containers\
|
||||
--kubernetes-version 1.29.2\
|
||||
--service-cidr=10.10.0.0/12\
|
||||
--pod-network-cidr=10.244.0.0/16\
|
||||
--ignore-preflight-errors=all\
|
||||
--cri-socket unix:///var/run/cri-dockerd.sock
|
||||
```
|
||||
|
||||
```bash
|
||||
# node 加入
|
||||
kubeadm join 192.168.173.100:6443 --token jghzcm.mz6js92jom1flry0 \
|
||||
--discovery-token-ca-cert-hash sha256:63253f3d82fa07022af61bb2ae799177d2b0b6fe8398d5273098f4288ce67793 --cri-socket unix:///var/run/cri-dockerd.sock
|
||||
|
||||
# work token 过期后,重新申请
|
||||
kubeadm token create --print-join-command
|
||||
```
|
||||
|
||||
# 部署网络插件
|
||||
|
||||
```bash
|
||||
https://docs.tigera.io/calico/latest/getting-started/kubernetes/self-managed-onprem/onpremises#install-calico-with-kubernetes-api-datastore-more-than-50-nodes
|
||||
|
||||
curl https://raw.githubusercontent.com/projectcalico/calico/v3.26.3/manifests/calico-typha.yaml -o calico.yaml
|
||||
CALICO_IPV4POOL_CIDR 指定为 pod 地址
|
||||
|
||||
# 修改为 BGP 模式
|
||||
# Enable IPIP
|
||||
- name: CALICO_IPV4POOL_IPIP
|
||||
value: "Always" #改成Off
|
||||
|
||||
kubectl apply -f calico-typha.yaml
|
||||
kubectl get pod -A
|
||||
```
|
||||
|
||||
## 固定网卡(可选)
|
||||
|
||||
```bash
|
||||
# 目标 IP 或域名可达
|
||||
- name: calico-node
|
||||
image: registry.geoway.com/calico/node:v3.19.1
|
||||
env:
|
||||
# Auto-detect the BGP IP address.
|
||||
- name: IP
|
||||
value: "autodetect"
|
||||
- name: IP_AUTODETECTION_METHOD
|
||||
value: "can-reach=www.google.com"
|
||||
kubectl set env daemonset/calico-node -n kube-system IP_AUTODETECTION_METHOD=can-reach=www.google.com
|
||||
```
|
||||
|
||||
```bash
|
||||
# 匹配目标网卡
|
||||
- name: calico-node
|
||||
image: registry.geoway.com/calico/node:v3.19.1
|
||||
env:
|
||||
# Auto-detect the BGP IP address.
|
||||
- name: IP
|
||||
value: "autodetect"
|
||||
- name: IP_AUTODETECTION_METHOD
|
||||
value: "interface=eth.*"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 排除匹配网卡
|
||||
- name: calico-node
|
||||
image: registry.geoway.com/calico/node:v3.19.1
|
||||
env:
|
||||
# Auto-detect the BGP IP address.
|
||||
- name: IP
|
||||
value: "autodetect"
|
||||
- name: IP_AUTODETECTION_METHOD
|
||||
value: "skip-interface=eth.*"
|
||||
```
|
||||
|
||||
```bash
|
||||
# CIDR
|
||||
- name: calico-node
|
||||
image: registry.geoway.com/calico/node:v3.19.1
|
||||
env:
|
||||
# Auto-detect the BGP IP address.
|
||||
- name: IP
|
||||
value: "autodetect"
|
||||
- name: IP_AUTODETECTION_METHOD
|
||||
value: "cidr=192.168.200.0/24,172.15.0.0/24"
|
||||
```
|
||||
|
||||
# 修改kube-proxy 模式为 ipvs
|
||||
|
||||
```bash
|
||||
# kubectl edit configmap kube-proxy -n kube-system
|
||||
mode: ipvs
|
||||
|
||||
kubectl delete pod -n kube-system -l k8s-app=kube-proxy
|
||||
```
|
||||
|
246
Kubernetes/故障恢复项目实战.md
Normal file
@@ -0,0 +1,246 @@
|
||||
# 实验环境
|
||||
|
||||
## 登录方式
|
||||
|
||||
1. 打开WMware选项[文件] -> [连接服务器]
|
||||
2. 输入信息如下:
|
||||
```shell
|
||||
服务器名称 10.3.33.233
|
||||
用户名 teamX@vsphere.local
|
||||
- X: 01-13
|
||||
密码 !QAZ2wsx
|
||||
虚拟机用户 root
|
||||
虚拟机密码 1
|
||||
备注 ☑️总是信任具有此证书的主机
|
||||
```
|
||||
|
||||
## 环境介绍
|
||||
|
||||
```shell
|
||||
team01
|
||||
master01 10.3.201.100
|
||||
node01 10.3.201.101
|
||||
node02 10.3.201.102
|
||||
team02
|
||||
master01 10.3.202.100
|
||||
node01 10.3.202.101
|
||||
node02 10.3.202.102
|
||||
......
|
||||
team13
|
||||
master01 10.3.213.100
|
||||
node01 10.3.213.101
|
||||
node02 10.3.213.102
|
||||
|
||||
每个team中master01节点为本集群的NFS服务端
|
||||
```
|
||||
|
||||
|
||||
# 故障排查和业务需求
|
||||
|
||||
## 1.K8s集群异常
|
||||
|
||||
前置条件: IP冲突,需要将IP修改为组编号
|
||||
异常现象: `kubectl get nodes -o wide`
|
||||
1. INTERNAL-IP字段变化
|
||||
2. `dial tcp 10.3.201.100:6443: connect: no route to host`
|
||||
3. `Unable to connect to the server: tls: failed to verify certificate: x509: certificate is valid for 10.0.0.1, 10.3.201.100, not 10.3.204.100`
|
||||
|
||||
**控制节点**
|
||||
|
||||
```shell
|
||||
# 更新Kubernets核心配置
|
||||
sed -i 's/10.3.201/10.3.204/g' /etc/kubernetes/manifests/etcd.yaml
|
||||
sed -i 's/10.3.201/10.3.204/g' /etc/kubernetes/manifests/kube-apiserver.yaml
|
||||
sed -i 's/10.3.201/10.3.204/g' /etc/kubernetes/admin.conf
|
||||
sed -i 's/10.3.201/10.3.204/g' /etc/kubernetes/controller-manager.conf
|
||||
sed -i 's/10.3.201/10.3.204/g' /etc/kubernetes/kubelet.conf
|
||||
sed -i 's/10.3.201/10.3.204/g' /etc/kubernetes/scheduler.conf
|
||||
sed -i 's/10.3.201/10.3.204/g' /etc/kubernetes/super-admin.conf
|
||||
sed -i 's/10.3.201/10.3.204/g' $HOME/.kube/config
|
||||
# 重新生成证书:
|
||||
mkdir backup && mv /etc/kubernetes/pki/apiserver.{key,crt} backup/
|
||||
kubeadm init phase certs apiserver
|
||||
# 重启服务
|
||||
systemctl restart docker kubelet
|
||||
# 更新cluster-info CM: 将旧IP改为新IP
|
||||
kubectl -n kube-public edit cm cluster-info
|
||||
```
|
||||
**其他操作**
|
||||
|
||||
```shell
|
||||
# 驱除工作节点
|
||||
kubectl drain node02 --ignore-daemonsets --delete-emptydir-data
|
||||
# 强制删除Pod, 可以通过-n指定命名空间
|
||||
kubectl delete pod $(kubectl get pods -A -o wide | grep node02 | awk '{print $2}') --force
|
||||
# 设置为可调度
|
||||
kubectl uncordon node01
|
||||
# 生成加入集群的方式
|
||||
kubeadm token create --print-join-command
|
||||
```
|
||||
|
||||
**工作节点**
|
||||
|
||||
异常现象: `kubectl get nodes -o wide`
|
||||
1. INTERNAL-IP字段不符合预期
|
||||
2. `error execution phase preflight: unable to fetch the kubeadm-config ConfigMap: failed to get config map: Get "https://10.3.201.100:6443/api/v1/namespaces/kube-system/configmaps/kubeadm-config?timeout=10s": dial tcp 10.3.201.100:6443: connect: no route to host`
|
||||
|
||||
**方式1**
|
||||
```shell
|
||||
# 更新配置文件
|
||||
sed -i 's/10.3.201/10.3.204/g' /etc/kubernetes/kubelet.conf
|
||||
# 重启服务
|
||||
systemctl restart docker kubelet
|
||||
```
|
||||
|
||||
**方式2**
|
||||
```shell
|
||||
# 重置节点
|
||||
kubeadm reset -f --cri-socket ///var/run/cri-dockerd.sock
|
||||
rm -rf /etc/kubernetes /var/lib/kubelet
|
||||
rm -rf /etc/kubernetes/
|
||||
rm -rf /var/lib/kubelet/
|
||||
rm -rf $HOME/.kube/config
|
||||
rm -rf /etc/cni/net.d/ && rm -rf /var/lib/cni/
|
||||
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
|
||||
ip route flush proto bird
|
||||
# 重新加入到集群中
|
||||
kubeadm join 10.3.204.100:6443 --token vjwr1p.nm5ylfw81b6n67u6 --discovery-token-ca-cert-hash sha256:9e2991ca808a6559040fceedad3aa30e15500dd7a02668d146b002c3fddef3fa --cri-socket unix:///var/run/cri-dockerd.sock
|
||||
```
|
||||
|
||||
## 2.Calico组件异常
|
||||
|
||||
异常现象:
|
||||
1. `2025-04-18 07:37:19.470 [FATAL][1] cni-installer/<nil> <nil>: Unable to create token for CNI kubeconfig error=Post "https://10.0.0.1:443/api/v1/namespaces/kube-system/serviceaccounts/calico-cni-plugin/token": dial tcp 10.0.0.1:443: i/o timeout`
|
||||
2. `failed to verify certificate: x509: certificate is valid for 10.96.0.1, 10.3.204.100, not 10.0.0.1`
|
||||
|
||||
```shell
|
||||
# 查看install-cni container的日志
|
||||
kubectl logs calico-node-zqpbp -n kube-system -c install-cni
|
||||
# 更新kube-proxy CM: 将旧IP改为新IP
|
||||
kubectl -n kube-system edit cm kube-proxy
|
||||
# 查看crt文件SAN地址
|
||||
openssl x509 -in /etc/kubernetes/pki/apiserver.crt -text | grep -A 1 'X509v3 Subject Alternative Name'
|
||||
# 将10.0.0.1加入SANs中
|
||||
[root@master01 ~]# cat kubeadm_config.yaml
|
||||
apiVersion: kubeadm.k8s.io/v1beta3
|
||||
kind: ClusterConfiguration
|
||||
apiServer:
|
||||
certSANs:
|
||||
- "10.3.204.100"
|
||||
- "10.0.0.1"
|
||||
mv /etc/kubernetes/pki/apiserver.{key,crt} backup/
|
||||
# --config kubeadm-conf.yaml可以指定配置文件
|
||||
kubeadm init phase certs apiserver --config kubeadm_config.yaml
|
||||
# 重建kube-proxy & 重建calico
|
||||
kubectl delete pod -n kube-system -l k8s-app=kube-proxy
|
||||
kubectl delete pod -n kube-system -l k8s-app=calico-node
|
||||
kubectl delete pod -n kube-system -l k8s-app=calico-kube-controllers
|
||||
kubectl delete pod -n kube-system -l k8s-app=calico-typha
|
||||
```
|
||||
|
||||
## 3.LNMP业务异常
|
||||
|
||||
- 将 `/root/resources/01.nginx.yaml,02.phpfpm.yaml,03.mysql.yaml` 等文件中NFS地址指向本集群master01
|
||||
- 为 `nginx.yaml,phpfpm.yaml` 等文件添加健康检查机制
|
||||
- 检查 `nginx php-fpm mysql` 服务
|
||||
- 通过 `bbs.iproute.cn` 访问正常
|
||||
<!-- - 修改`service/ingress-nginx-controller`为`NodePort`方式并通过`helm`更新 -->
|
||||
|
||||
```shell
|
||||
# 更改NFS地址
|
||||
sed -i 's/3.201/3.204/g' resources/01.nginx.yaml
|
||||
sed -i 's/3.201/3.204/g' resources/02.phpfpm.yaml
|
||||
sed -i 's/3.201/3.204/g' resources/03.mysql.yaml
|
||||
kubectl apply -f resources/01.nginx.yaml
|
||||
kubectl apply -f resources/02.phpfpm.yaml
|
||||
kubectl apply -f resources/03.mysql.yaml
|
||||
# 添加健康检查机制: 参考v2.yaml文件
|
||||
# 访问1: bbs.iproute.cn 带端口
|
||||
[root@master01 ~]# curl -H "host: bbs.iproute.cn" 10.3.204.101:`kubectl get svc/ingress-nginx-controller -n ingress-nginx -o jsonpath="{.spec.ports[?(@.port==80)].nodePort}" -n ingress`
|
||||
# 访问2: 直接通过域名访问,不带端口
|
||||
[root@master01 ~]# grep bbs /etc/hosts
|
||||
10.3.204.101 bbs.iproute.cn
|
||||
[root@master01 ~]# curl -I bbs.iproute.cn
|
||||
HTTP/1.1 200 OK
|
||||
```
|
||||
|
||||
## 4.MySQL变更管理
|
||||
|
||||
前置条件:
|
||||
- 新密码为: `123456`
|
||||
异常现象:
|
||||
- 看到 `nginx pod` 健康检查失败
|
||||
- 访问 `bbs.iproute.cn` 异常 `HTTP/1.1 503`
|
||||
|
||||
```shell
|
||||
# 更新MySQL用户密码
|
||||
kubectl exec -it mysql-776786446d-bjcnn -- /bin/bash
|
||||
mysql -uroot -p123
|
||||
ALTER USER 'root'@'localhost' IDENTIFIED BY '123456';
|
||||
ALTER USER 'root'@'%' IDENTIFIED BY '123456';
|
||||
FLUSH PRIVILEGES;
|
||||
# 容器内: 日志文件路径
|
||||
/usr/share/nginx/html/data/log/
|
||||
# NFS: 日志文件路径
|
||||
/root/data/nfs/html/data/log/
|
||||
# NFS: 配置文件路径
|
||||
[root@master01 resources]# grep -nrw '123456' /root/data/nfs/html/*
|
||||
/root/data/nfs/html/config/config_global.php:9:$_config['db'][1]['dbpw'] = '123456';
|
||||
/root/data/nfs/html/uc_server/data/config.inc.php:4:define('UC_DBPW', '123456');
|
||||
# 重启/删除php相关应用
|
||||
# 测试验证
|
||||
[root@master01 ~]# curl -I bbs.iproute.cn
|
||||
HTTP/1.1 200 OK
|
||||
```
|
||||
|
||||
## 5.Redis持久化管理
|
||||
|
||||
- 配置文件通过Configmap挂载至 `/usr/local/etc/redis/redis.conf`
|
||||
- 数据目录通过NFS挂载至 `/data`
|
||||
- 测试验证
|
||||
|
||||
```shell
|
||||
# 使用04.redis_v2.yaml文件
|
||||
mkdir -pv /root/data/nfs/redis/data
|
||||
kubectl apply -f resources/04.redis_v2.yaml
|
||||
# 进入容器写入测试数据
|
||||
kubectl exec -it $(kubectl get pods -l app=redis -o jsonpath='{.items[0].metadata.name}') -- redis-cli
|
||||
> SET testkey "persistence_verified"
|
||||
> SAVE
|
||||
# 文件持久化
|
||||
[root@master01 ~]# tree data/nfs/redis/data/
|
||||
data/nfs/redis/data/
|
||||
├── appendonlydir
|
||||
│ ├── appendonly.aof.1.base.rdb
|
||||
│ ├── appendonly.aof.1.incr.aof
|
||||
│ └── appendonly.aof.manifest
|
||||
└── dump.rdb
|
||||
# 删除 Pod 触发重建后验证数据
|
||||
kubectl delete pod $(kubectl get pods -l app=redis -o jsonpath='{.items[0].metadata.name}')
|
||||
kubectl exec $(kubectl get pods -l app=redis -o jsonpath='{.items[0].metadata.name}') -- redis-cli GET testkey
|
||||
persistence_verified
|
||||
```
|
||||
|
||||
## 6.新增工作节点
|
||||
|
||||
参考课件`安装手册`
|
||||
|
||||
## 7.水平扩缩容
|
||||
|
||||
参考课件`HPA控制器`
|
||||
|
||||
## [扩展]8.垂直扩缩容
|
||||
|
||||
参考官网 [VPA](https://github.com/kubernetes/autoscaler/tree/9f87b78df0f1d6e142234bb32e8acbd71295585a/vertical-pod-autoscaler)
|
||||
|
||||
## [扩展]9.MySQL高可用
|
||||
|
||||
- 测试验证主从服务可用
|
||||
- 将旧数据库里的数据导入至新数据库
|
||||
- 将业务切到新数据库并实现读写分离
|
||||
- 数据完整性和一致性怎么校验?
|
||||
- 如何设计方案无损迁移?
|
||||
- 如何实现更好的扩所容方案?
|
||||
|
||||
|
||||
## [扩展]10.Redis哨兵
|
1036
Kubernetes/数据存储.md
Normal file
BIN
Kubernetes/数据存储/image-20240914160404461.png
Normal file
After Width: | Height: | Size: 118 KiB |
BIN
Kubernetes/数据存储/image-20240914161526676.png
Normal file
After Width: | Height: | Size: 163 KiB |
BIN
Kubernetes/数据存储/image-20240915123854366.png
Normal file
After Width: | Height: | Size: 183 KiB |
BIN
Kubernetes/数据存储/image-20240915123914270.png
Normal file
After Width: | Height: | Size: 178 KiB |
BIN
Kubernetes/数据存储/image-20240915130118556.png
Normal file
After Width: | Height: | Size: 247 KiB |
BIN
Kubernetes/数据存储/image-20240915134128848.png
Normal file
After Width: | Height: | Size: 413 KiB |
BIN
Kubernetes/数据存储/image-20240916131412493.png
Normal file
After Width: | Height: | Size: 105 KiB |