08-27-周三_17-09-29

This commit is contained in:
2025-08-27 17:10:05 +08:00
commit 86df397d8f
12735 changed files with 1145479 additions and 0 deletions

131
Kubernetes/DashBoard.md Normal file
View 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
![image-20200520144548997](DashBoard/image-20200520144548997.png)
出现下面的页面代表成功
![image-20200520144959353](DashBoard/image-20200520144959353.png)
## 使用DashBoard
本章节以Deployment为例演示DashBoard的使用
**查看**
选择指定的命名空间`dev`,然后点击`Deployments`查看dev空间下的所有deployment
![img](DashBoard/image-20200520154628679.png)
**扩缩容**
`Deployment`上点击`规模`,然后指定`目标副本数量`,点击确定
![img](DashBoard/image-20200520162605102.png)
**编辑**
`Deployment`上点击`编辑`,然后修改`yaml文件`,点击确定
![image-20200520163253644](DashBoard/image-20200520163253644.png)
**查看Pod**
点击`Pods`, 查看pods列表
![img](DashBoard/image-20200520163552110.png)
**操作Pod**
选中某个Pod可以对其执行日志logs、进入执行exec、编辑、删除操作
![img](DashBoard/image-20200520163832827.png)
> Dashboard提供了kubectl的绝大部分功能这里不再一一演示
# kuboard
## 特点介绍
相较于 Kubernetes Dashboard 等其他 Kubernetes 管理界面Kuboard 的优势更加明显官方网站https://kuboard.cn/
## 安装教程
https://kuboard.cn/v4/install/quickstart.html
安装好后链接上集群,最终界面如下
![image-20240918150900081](DashBoard/image-20240918150900081.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 62 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

5
Kubernetes/Kubernetes.md Normal file
View File

@@ -0,0 +1,5 @@
Kubernetes 是一个开源的容器编排引擎用于自动化部署、扩展和管理容器化应用程序。该开源项目由云原生计算基金会CNCF托管。
[kubernetes官网地址](https://kubernetes.io/)
[kubernetes项目地址](https://github.com/kubernetes)

View File

@@ -0,0 +1,166 @@
# 云计算的三种模式
## 单机到云原生的演变
![img](kubernetes概述/云原生演变.png)
## IAAS
Infrastructure as a Service 基础设施即服务
![img](kubernetes概述/IAAS.png)
## PAAS
Platform as a service 平台即服务
![img](kubernetes概述/PAAS.png)
## 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
- 开源
![img](kubernetes概述/kubernetes优势.png)
## kubernetes现状
![img](kubernetes概述/kubernetes现状.png)
# kubernetes组件
![img](kubernetes概述/kubernetes集群组件.png)
一个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
View 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不依赖于底层某一种具体的容器运行时实现技术而是直接操作podpod内部再管理多个业务上紧密相关的用户业务容器这种架构便于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
```
![image-20240904115715394](pod与网络/image-20240904115715394.png)
# kubernetes网络
## 基本概述
Kubernetes的网络模型假定了所有Pod都在一个可以直接连通的扁平的网络空间中这在GCEGoogle 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插件通常聚焦在容器到容器的网络通信。
![image-20240904134937631](pod与网络/image-20240904134937631.png)
CNI的接口并不是指HTTPgRPC这种接口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接收插件的执行结果。从网络插件功能可以分为五类
![image-20240904135319098](pod与网络/image-20240904135319098.png)
- **Main插件**
- 创建具体网络设备
- bridge网桥设备连接container和host
- ipvlan为容器增加ipvlan网卡
- loopbackIO设备
- 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网络插件
![image-20240904135633230](pod与网络/image-20240904135633230.png)
## 网络插件
<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 | 封装(VXLANIPIP),未封装 | 是 | 是 | 是 | 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
![image-20240904140729187](pod与网络/image-20240904140729187.png)
### underlay
![image-20240904140805520](pod与网络/image-20240904140805520.png)
## calico
或许是目前最主流的网络解决方案-calico
Calico是一个纯三层的虚拟网络它没有复用docker的docker0网桥而是自己实现的calico网络不对数据包进行额外封装不需要NAT和端口映射
### calico架构
<img src="pod与网络/image-20240904141203196.png" alt="image-20240904141203196" style="zoom:50%;" />
- Felix
- 管理网络接口
- 编写路由
- 编写ACL
- 报告状态
- birdBGPClient
- 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实现通讯。
![image-20240904143234102](pod与网络/image-202409041432341png)
- 数据包封包封包在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的数据包封包和解包会存在一定的性能损耗
![image](pod与网络/image.png)
配置方法
```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的数据包封包和解包会存在一定的性能损耗
![image-20240904145738269](pod与网络/image-20240904145738269.png)
配置方法
```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通俗的讲就是讲接入到机房的多条线路如电信、联通、移动等融合为一体实现多线单IPBGP机房的优点服务器只需要设置一个IP地址最佳访问路由是由网络上的骨⼲路由器根据路由跳数与其它技术指标来确定的不会占用服务器的任何系统。
![image-20240904151200463](pod与网络/image-20240904151200463.png)
- 数据包封包:不需要进行数据包封包
- 优点不用封包解包通过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
View File

@@ -0,0 +1,673 @@
# 控制器
在Kubernetes中运⾏了⼀系列控制器来确保集群的当前状态与期望状态保持⼀致它们就是Kubernetes集群内部的管理控制中⼼或者说是”中⼼⼤脑”。例如ReplicaSet控制器负责维护集群中运⾏的Pod数量Node控制器负责监控节点的状态并在节点出现故障时执⾏⾃动化修复流程确保集群始终处于预期的⼯作状态。
![image-20240910143437460](Pod控制器/image-20240910143437460.png)
- 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字段可以提供多种选择。
目前支持的操作包括:
- Inlabel的值在某个列表中
- NotInlabel的值不在某个列表中
- 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如果将该项设置为0Deployment就不允许回退了
## 金丝雀部署
⾦丝雀部署的名字灵感来源于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 AutoscalerHPA这种控制器。
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
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 295 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 370 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

View File

@@ -0,0 +1,766 @@
# Pod生命周期
![image-20240906095839924](Pod生命周期/image-20240906095839924.png)
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 * periodSeconds30 * 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
![image-20240906204437000](Pod生命周期/image-20240906204437000.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 233 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 421 KiB

793
Kubernetes/Service.md Normal file
View 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会在集群的外部再来做一个负载均衡设备而这个设备需要外部环境支持的外部服务发送到这个设备上的请求会被设备负载之后转发到集群中。
![image-20240913154405309](Service/image-20240913154405309.png)
## 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的反向代理配置 , 然后对外部提供服务**。在这里有两个核心概念:
- ingresskubernetes中的一个对象作用是定义请求如何转发到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
# 安装helmhelm在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
```
## 实验测试
创建如下两个资源模型
![image-20240914152836257](Service/image-20240914152836257.png)
```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)
```
访问测试
![image-20240914154740672](Service/image-20240914154740672.png)
其中nginx多次访问主机名可以看到负载均衡
![image-20240914154758983](Service/image-20240914154758983.png)
## 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
```
访问测试
![image-20240914155615236](Service/image-20240914155615236.png)
可以看到负载均衡
![image-20240914155628119](Service/image-20240914155628119.png)

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 577 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 448 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 179 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 492 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 310 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 300 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 237 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 103 KiB

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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

View 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

File diff suppressed because it is too large Load Diff

View 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
View 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里放入TokenAPI 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的安全策略进行控制

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 KiB

View 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%) |
+----------+---------------+-----------+------------+-------------+
```

View 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
```

View 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

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 118 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 413 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 KiB

View File

Some files were not shown because too many files have changed in this diff Show More