🌐 简介
Kubernetes
,通常被称为K8s
,是一个强大的、开源的容器编排工具,专门用于自动部署、扩展和管理容器化应用程序。
在下面过程中,
Docker
容器通常包装一个服务或应用组件,运行在Kubernetes
管理的Pod
中。每个Pod
可以包含一个或多个容器,但通常情况下,一个Pod
中只运行一个主容器,可以包括一些辅助容器来支持主容器的操作。Kubernetes
负责调度这些Pod
到集群中的节点上,并管理它们的生命周期、网络、以及存储资源。
🔧 代码到镜像:
- 🖥️ 开发人员使用如
IntelliJ IDEA
或Visual Studio Code
等 IDE 开发代码。 - 🔄 代码通过
git push
被提交到版本控制系统(如GitHub
或GitLab
)。 - 🏗️ 持续集成(CI)工具(如
Jenkins
或GitLab CI
)自动触发构建任务。 - 🐳
Dockerfile
定义了如何打包代码和依赖到Docker
镜像。
📦 镜像到仓库:
- 🚢 CI 工具构建完成后,
Docker
镜像被推送到镜像仓库(如Docker Hub
或Harbor
)。 - 🔒 镜像仓库可能会进行镜像扫描,以确保安全性。
🌐 从仓库到集群:
- 📥 Kubernetes 控制平面(如
kube-apiserver
)从镜像仓库拉取镜像。 - 🚀
kubectl apply
或 CI/CD 工具部署应用,Kubernetes 创建 Pods。
🔍 服务注册与发现:
- 📡 Pods 在启动时向服务发现系统(如
Consul
或Eureka
)注册。 - 🧭 当服务需要相互通信时,它们查询服务发现系统以获得相应的服务地址。
⚖️ 负载均衡与 API 网关:
- 🚪 外部请求首先到达 API 网关(如
Spring Cloud Gateway
或Zuul
),这提供了统一的入口点。 - 🔄
NGINX
或HAProxy
作为负载均衡器,将请求分发到多个 Pods。
🔑 配置与安全:
- ⚙️ 服务启动时,从配置中心(如
Spring Cloud Config
)拉取配置。 - 🔐
OAuth2
、JWT
、Spring Security
等用于处理安全性,包括用户认证和授权。
📤 消息队列与数据库:
- 📨 服务使用
RabbitMQ
或Kafka
来发布和订阅消息,实现异步通信。 - 💾
MySQL
、PostgreSQL
或MongoDB
等数据库负责数据持久化。
📊 监控与日志:
- 📈
Prometheus
负责收集各种指标,并可以与Grafana
集成以提供仪表盘。 - 📃
Elasticsearch
、Logstash
和Kibana
(ELK Stack)聚合和分析日志数据。
🛠️ 维护与扩展:
- 🔁 Kubernetes 根据指标自动扩展 Pods(Horizontal Pod Autoscaler)。
- 🧹
CronJobs
用于定期执行清理任务或数据库备份。
🌟 总结:
Docker
是在 Kubernetes Pods 中运行容器的工具。NGINX
可作为 Kubernetes Ingress Controller,处理外部 HTTP/S 流量。Spring Cloud
提供的服务可运行在 Kubernetes Pods 内,处理配置管理、服务发现等。- 中间件如
RabbitMQ
和数据库如MySQL
可以运行在 Kubernetes 管理的容器中,或者作为外部服务连接。 K8s
将这些组件编织成一个协调一致、可自我恢复的系统。
📊 基本概念
🌐 节点 (Node) :🖥️ 一个节点是一个在 Kubernetes 中运行的主机。
📦 容器组 (Pod):🚀 一个 Pod 对应于由若干容器组成的一个容器组,其中的容器共享一个存储卷(volume)。
🔄 容器组生命周期 (Pod States)
- 📊 概述: 包含所有容器的状态集合。
- 🚦 状态类型: 包括容器组状态类型、生命周期、事件和重启策略。
- 🎛️ Replication Controllers: 主要负责指定数量的 pod 在同一时间一起运行。
🛰️ 服务 (Services):⚙️ 一个 Kubernetes 服务是容器组逻辑的高级抽象,同时也对外提供访问容器组的策略。
📂 卷 (Volumes):📚 一个卷是一个目录,容器可以访问它。
🏷️ 标签 (Labels):🔖 标签用来连接一组对象,如容器组。它们可以用来组织和选择子对象。
🔒 接口权限 (Accessing the API):🔌 包括端口、IP 地址和与代理相关的防火墙规则。
🖥️ Web 界面 (UX):🌍 用户可以通过 web 界面操作 Kubernetes。
💼 命令行操作 (CLI):🔧 通过 kubectl
命令进行操作。
🏛️ 架构设计
在技术领域中,任何出色的项目都离不开精心的架构设计。
Kubernetes
作为容器编排工具的佼佼者,它的架构设计显然也是值得深入了解的。
1. 🧠 基本考虑
- 🌐 分布式架构:确保系统的可扩展性和容错性。
- 🔄 逻辑集中,物理分散:逻辑上的控制平面集中管理,而物理上的运行平面则广泛分布。
- ⚖️ 资源调度系统:确保容器在最适合的节点上运行。
- ⛓️ 服务抽象与HA:为容器内的服务提供高可用性和服务抽象。
2. 🚀 核心组件
a. 🎮 控制平面 (Control Plane)
-
控制平面主要由主节点(Master Node)组成,它负责整体管理和控制 Kubernetes 集群。
- 🌍 API Server: 是整个系统的对外接口,为客户端和其他组件提供 RESTful 接口。
- 📅 Scheduler: 负责决定将哪个 Pod 放置在哪个 Node 上。
- 🎛️ Controller Manager: 管理各种控制器,确保系统的各个部分都正常运行。
- 🗄️ Etcd: 一个高可用的键值存储系统,用于所有集群数据的保存。
b. 🛠️ 工作节点 (Worker Node)
-
工作节点是容器和 Pod 实际运行的地方。
- 🤖 Kubelet: kubelet 是工作节点执行操作的 agent,负责具体的容器生命周期管理,根据从数据库中获取的信息来管理容器,并上报 pod 运行状态等;。
- 🌐 Kube Proxy: kube-proxy 是一个简单的网络访问代理,同时也是一个 Load Balancer。它负责将访问到某个服务的请求具体分配给工作节点上的 Pod(同一类标签)。
3. 📚 Etcd 的角色
- Etcd 在 Kubernetes 的架构中起着至关重要的作用。它是一个分布式的键值存储,用于存储整个集群的配置信息和状态。所有的主节点组件都与 Etcd 交互,以获取信息或更新集群的状态。此外,Etcd 也用作消息中间件,组件可以侦测 Etcd 中的值变化以获取通知。
- 配置中心:
- 🗂️ 存储关键信息,如服务的注册数据、网络配置和集群状态。
- 协调器:
- 🔄 保持集群状态一致,通过实现分布式锁机制。
- 服务发现:
- 🔍 提供服务发现功能,使得各服务能够查找并连接到其他服务。
- 事件订阅:
- 📡 组件可以订阅事件,实现配置的动态更新。
- 领导者选举:
- 🏆 协助在主节点故障时进行领导者选举,保证集群的正常运作。
- 安全性:
- 🔐 支持加密通信,确保数据安全。
- 灾难恢复:
- 🛡️ 定期备份,快速恢复服务。
- 配置中心:
4. 🌍 Proxy 与网络
-
Kube-proxy 在 Kubernetes 中扮演了网络通信的关键角色。它是工作节点上运行的网络代理,用于 Pod 间的网络通信,服务发现,以及负载均衡。
- 流量代理:
- 🚦 负责Pod间的网络流量转发和处理。
- 负载均衡器:
- ⚖️ 动态分配流量至多个Pod,实现负载均衡。
- 网络策略执行:
- 🛂 管理和执行入站和出站的网络策略。
- 支持服务发现:
- 🔗 维护服务和Pod之间的连接,支持服务的发现机制。
- 端口管理:
- 🚪 映射服务所需的端口到对应的Pod。
- 跨节点通信:
- 🌉 使得不同物理节点上的Pod能够互相通信。
- 健康检查:
- 🩺 检测Pod健康状态,确保流量仅转发至健康Pod。
- 流量代理:
🌐 节点 (Node)
-
节点在 Kubernetes 中是实际工作的基本单元。它可以是一个虚拟机或物理机器,取决于集群的设置。每个节点都装载了一些必要的服务,以运行容器组(Pods)。这些服务包括 Docker、kubelet 和代理服务,它们都是通过主节点进行管理的。
- 示例: 想象一个大型的仓库,其中每个货架(节点)都有各自的职责和存储物品(容器组)。货架可以是不同的大小和形状(虚拟机或物理机器)。
- 形象化: 就像一个忙碌的仓库中的货架,每个节点在 Kubernetes 中都有其特定的任务和运行的容器组。
示例代码:
# 获取集群中所有节点的列表
kubectl get nodes
📊 容器状态
🔹 主机 IP
- Kubernetes 从云平台查询主机 IP,并将其存储为状态的一部分。如果 Kubernetes 没有在云平台上运行,那么节点 ID 是必需的。需要注意的是,IP 地址可能会变化,并可能有多种类型,例如公共 IP、私有 IP、动态 IP、IPv6 等。
- 示例: 这就像在大型公寓楼中,每个公寓都有自己的门牌号(IP地址)。
- 形象化: 想象一个楼层的布局图,每个公寓(节点)都有其独特的门牌号(IP地址)。
示例代码:
# 获取指定节点的 IP 地址
kubectl describe node <NODE_NAME> | grep "Addresses"
🔹 节点周期
-
节点通常有三个周期:
- Pending: 如果 Kubernetes 发现一个节点并确定它是可用的,它会被标记为 Pending。
- Running: 在某个时刻,Kubernetes 会标记节点为 Running。
- Terminated: 当节点结束其生命周期时,它进入 Terminated 状态。一个已经 Terminated 的节点将不再处理请求,并且其上所有的容器组都会被删除。
- 示例: 就像人的生命周期包括婴儿、成年和老年,节点也有其生命周期:Pending、Running 和 Terminated。
- 形象化: 想象一个时间线,上面有三个标记:开始(Pending)、运行中(Running)和结束(Terminated)。
示例代码:
# 获取指定节点的生命周期状态
kubectl get nodes <NODE_NAME> -o=jsonpath='{.status.phase}'
🔹 节点状态
-
描述处于 Running 状态的节点。当前主要有两种状态:
- NodeReachable: 表示节点在集群中是可达的。
- NodeReady: 表示 kubelet 返回了状态 OK,并且 HTTP 状态检查是健康的。
- NodeUnreachable: 表示 kubelet 返回了状态 OFF,并且 HTTP 状态检查是不健康的。
- 示例: 这就像一个交通信号灯,绿色表示一切正常(NodeReady),红色表示道路封闭(NodeUnreachable)。
- 形象化: 想象一个交通信号灯,根据灯的颜色,我们可以知道是否可以通过。
示例代码:
# 获取指定节点的 Ready 状态
kubectl get nodes <NODE_NAME> -o=jsonpath='{.status.conditions[?(@.type=="Ready")].status}'
🛠️ 节点管理
- 节点不是由 Kubernetes 创建的,而是由云平台或物理/虚拟机创建的。在 Kubernetes 中,节点只是一个记录项。当节点被创建后,Kubernetes 会检查它的可用性。节点在 Kubernetes 中的结构如下:
- 示例: 这就像一个建筑公司并不制造砖块,但会使用砖块来建造房屋。
- 形象化: 想象一个施工现场,工人使用各种材料(节点)来建造大楼。
示例代码:
{
"id": "10.1.2.3",
"kind": "Minion",
"apiVersion": "v1beta1",
"resources": {
"capacity": {
"cpu": 1000,
"memory": 1073741824
},
},
"labels": {
"name": "my-first-k8s-node",
},
}
解析:
这段配置描述了一个 Kubernetes 节点,它有 1 个 CPU 核和 1 GB RAM,并带有一个用户定义的标签 name
。
-
id
:"10.1.2.3"
这是节点的 ID,通常表示节点的 IP 地址。在这里,节点的 IP 地址是10.1.2.3
。 -
kind
:"Minion"
在早期的 Kubernetes 版本中,节点被称为 “Minion”。在后续版本中,“Node” 成为了更为通用的名称。此字段标识了此资源对象的种类。 -
apiVersion
:"v1beta1"
这是此配置所使用的 Kubernetes API 版本。v1beta1
是一个较早的版本,现代的 Kubernetes 配置可能会使用如v1
的版本。 -
resources
:
这部分描述了节点的资源容量。-
capacity
:
这里列出了节点的总资源容量。-
cpu
:1000
节点的 CPU 资源,表示有 1000 毫核 (mCPU) 的容量。这通常代表了 1 个核的 CPU 容量。 -
memory
:1073741824
节点的内存容量,以字节为单位。在这里,它表示有 1 GB 的 RAM。
-
-
-
labels
:
标签是 Kubernetes 中用于标识和分类资源的键值对。name
:"my-first-k8s-node"
这是一个用户定义的标签,用于为这个节点命名。它的值是"my-first-k8s-node"
。
🖥️ 节点控制 (Node Control)
-
在 Kubernetes 中,节点控制器扮演着对节点进行监督和管理的角色。它确保每个节点都在预期的状态中运行,并对节点的生命周期进行管理。
- 示例: 想象一个仓库经理,他不仅负责监督货架的存放和清理,而且还负责检查货架上的物品是否破损并替换它们。
- 形象化: 想象一个机场的塔台,空中交通管制员在那里监视飞机的起飞和降落,并确保所有飞机都安全地移动。
🔸 集群范围内的节点同步
-
节点控制器不停地检查每个节点的健康状态,并与实际的基础设施(例如云提供商)同步来确保集群的状态是最新的。
- 示例: 仓库经理会定期查看仓库的存货,并与采购部门同步来确保库存是最新的。
- 形象化: 想象一个钟摆,它始终与一个标准时间同步。
# 列出集群中所有的节点
kubectl get nodes
# 查看指定节点的详细描述,包括其当前状态和最近的事件
kubectl describe node <NODE_NAME>
# 查看指定节点的健康状况
kubectl get node <NODE_NAME> -o=jsonpath='{.status.conditions}'
🔸 单节点的生命周期管理
-
节点控制器负责单个节点的生命周期。它监视节点的健康状况,并在需要时对其进行恢复、更新或删除。
- 示例: 如果仓库的某个货架破损,仓库经理会决定修理或替换它。
- 形象化: 想象一个园丁,他会定期检查植物的健康状况,并决定是否需要修剪、移植或浇水。
# 如果某个节点需要进行维护或更新,首先将其标记为不可调度,以防止新的Pods被调度到这个节点上
kubectl cordon <NODE_NAME>
# 对节点进行维护或更新后,可以再次将其标记为可调度,允许新的Pods被调度到这个节点上
kubectl uncordon <NODE_NAME>
# 如果节点遇到不可恢复的问题,可以从集群中安全地删除它
kubectl delete node <NODE_NAME>
# 为节点添加或更新标签,以帮助组织和选择节点
kubectl label nodes <NODE_NAME> key=value
🔹 节点控制的同步轮询
-
节点控制器有一个周期性的同步任务,用于检查云平台上的虚拟实例,并对 Kubernetes 中的节点进行相应的创建或删除。
- 示例: 这就像一个邮件服务器定期检查是否有新邮件,如果有,就下载它们。
🔹 控制同步轮询的时间间隔
-
可以使用
--node_sync_period
标志来控制这个同步轮询的时间间隔。- 示例代码:
kube-controller-manager --node_sync_period=10m
- 示例代码:
🔹 创建和删除节点记录
- 当在云平台上发现一个新的虚拟实例时,节点控制器会在 Kubernetes 中为其创建一个对应的节点记录。相反,如果一个虚拟实例被删除,其对应的节点记录也会被节点控制器删除。
🔹 在 Kubernetes 启动时指定节点
-
使用
--machines
标志,可以在 Kubernetes 启动时直接指定要添加的节点。- 示例代码:
kubelet --machines=node1,node2,node3
- 示例代码:
🔹 使用 kubectl 添加或删除节点
-
除了在启动时指定节点,还可以在运行时使用
kubectl
命令行工具手动添加或删除节点。- 示例代码:
# 添加节点 kubectl create -f node-config.yaml # 删除节点 kubectl delete node node-name
- 示例代码:
🔹 控制节点同步
-
可以使用
--sync_nodes
标志来控制是否在集群之间进行节点同步。如果你不想让节点在集群间同步,可以设置为false
。- 示例代码:
kube-controller-manager --sync_nodes=false
- 示例代码:
📦 容器组 (Pod)
- 容器组是 Kubernetes 中的核心概念,它是一种包含一个或多个容器的封装。这些容器在同一网络命名空间中运行,这意味着它们可以相互“看到”并与彼此通信,就像它们在同一台机器上运行一样。容器组中的容器共享相同的存储资源、网络 IP、端口空间和其他资源,因此它们可以轻松地共享数据和进程间通信。
🔸 为什么不是单个容器?
- 你可能会问,为什么不直接使用单个容器,而是要引入“容器组”的概念?答案在于某些应用场景中,你可能希望将多个容器打包在一起,以便它们可以共享资源,但又相互隔离,避免互相干扰。
- 示例: 想象一个应用需要前端服务器和后端数据库。虽然这两者可以放在同一个容器中,但为了隔离和管理的方便,你可能希望将它们放在不同的容器中,但让它们在同一个容器组中运行,以便它们可以互相通信。
示例代码:
apiVersion: v1
kind: Pod
metadata:
name: frontend-backend-pod
spec:
containers:
- name: frontend
image: frontend-image:v1
ports:
- containerPort: 80
- name: backend
image: backend-image:v1
ports:
- containerPort: 3306
🔸 共享存储
- 容器组中的所有容器都可以访问同一组存储卷。这意味着一个容器可以写入某个文件,而另一个容器可以读取或修改该文件。
- 示例: 你有一个应用容器,它将数据写入某个目录,同时还有一个日志处理容器,它读取这些数据并将其发送到日志服务。两个容器都可以访问同一个存储卷,从而实现这一过程。
示例代码:
apiVersion: v1
kind: Pod
metadata:
name: shared-volume-pod
spec:
containers:
- name: app
image: app-image:v1
volumeMounts:
- mountPath: /app/data
name: shared-volume
- name: logger
image: logger-image:v1
volumeMounts:
- mountPath: /logger/data
name: shared-volume
volumes:
- name: shared-volume
emptyDir: {}
🔸 网络和端口
- 容器组中的所有容器共享同一个网络命名空间,这意味着它们共享同一个 IP 地址和端口空间。但是,每个容器还可以有自己的端口,这样它们就不会互相冲突。
- 示例: 在同一个容器组中,你可以有一个 Web 服务器容器监听 80 端口,同时还有一个数据库服务器容器监听 3306 端口。
示例代码:
apiVersion: v1
kind: Pod
metadata:
name: frontend-backend-pod
spec:
containers:
- name: frontend
image: frontend-image:v1
ports:
- containerPort: 80
- name: backend
image: backend-image:v1
ports:
- containerPort: 3306
🔸 生命周期
- 当你创建一个容器组时,所有的容器都会一起启动,并且当容器组被删除时,它们也会一起被停止。这确保了应用的一致性和原子性。
- 示例: 如果你的应用由前端和后端组成,你不希望前端启动但后端还没有准备好。通过将它们放在同一个容器组中,你可以确保它们都准备好后再开始接受请求。
示例代码:
# 创建容器组
kubectl apply -f your-pod-definition.yaml
# 查看容器组状态
kubectl get pods frontend-backend-pod
# 删除容器组
kubectl delete pod frontend-backend-pod
🎨 容器组设计的初衷
- 在 Kubernetes 的设计中,容器组 (Pod) 占据了核心地位。它不仅仅是一个简单的容器,而是一组紧密关联的容器,它们共享相同的运行环境。这样的设计有其深厚的背景和目的。
🌟 数据共享和通信
- 容器组的首要目标是为容器提供一个共享的环境,从而实现数据共享和通信。
🌟 为什么这很重要?
-
单一责任原则: 在微服务架构中,通常推荐每个容器只做一件事并做好它。例如,一个容器可能只运行一个数据库,而另一个容器运行一个 Web 服务器。但这些服务经常需要共享数据或进行通信。容器组为这种通信提供了一个高效、简单的方式。
-
形象化: 想象一下,你有两个机器人。一个是拍照专家,另一个是打印专家。拍照机器人可以立即将照片传递给打印机器人,而无需通过任何外部介质。容器组中的容器就像这两个机器人,可以轻松地共享数据。
🌟 如何实现数据共享和通信?
- 共享存储: 容器组中的容器可以共享存储卷。这意味着一个容器可以写入文件,另一个容器可以读取它。
示例代码:
apiVersion: v1
kind: Pod
metadata:
name: shared-storage-pod
spec:
containers:
- name: writer
image: writer-image
volumeMounts:
- mountPath: /data
name: shared-volume
- name: reader
image: reader-image
volumeMounts:
- mountPath: /data
name: shared-volume
volumes:
- name: shared-volume
emptyDir: {}
- 共享网络命名空间: 容器组中的容器共享相同的网络命名空间,这意味着它们可以通过
localhost
相互通信。
示例代码:
# 在一个容器中运行一个服务
$ nc -l 1234
# 在另一个容器中访问该服务
$ curl localhost:1234
🔗 资源共享和通信
- 容器组 (Pod) 在 Kubernetes 中是一个核心的概念,它允许多个容器共享同一组资源,从而实现容器之间的紧密协作。其中,网络共享和存储共享是最为关键的部分。
🎈 网络共享
- 容器组中的容器共享相同的网络命名空间,这意味着它们使用相同的 IP 地址和端口范围。这种设计简化了容器之间的通信,因为它们可以像在同一台机器上运行的进程那样轻松地相互通信。
🌟 为什么这很重要?
- 简化通信: 容器组中的容器可以通过
localhost
直接通信,无需额外的网络配置。 - 保持一致性: 使用相同的网络命名空间确保了网络设置和行为在整个容器组中是一致的。
- 安全性: 容器组中的容器之间可以通过本地网络进行通信,这意味着它们的通信不会暴露给外部网络,从而提供了额外的安全性。
🌟 如何实现网络共享?
示例代码:
# 在一个容器中运行一个简单的 HTTP 服务器
$ python -m SimpleHTTPServer 8080
# 在另一个容器中通过 localhost 访问该服务器
$ curl localhost:8080
🎈 存储共享
- 容器组还允许容器共享存储卷。这意味着一个容器写入的数据可以被同一容器组中的其他容器读取,从而实现数据的持久化和共享。
🌟 为什么这很重要?
- 数据持久化: 通过共享存储卷,容器组中的容器可以确保数据在容器重启或失败时不会丢失。
- 数据共享: 容器可以轻松地共享文件、配置和其他重要数据。
🌟 如何实现存储共享?
示例代码:
apiVersion: v1
kind: Pod
metadata:
name: shared-storage-pod
spec:
containers:
- name: writer
image: writer-image
volumeMounts:
- mountPath: /shared-data
name: shared-volume
- name: reader
image: reader-image
volumeMounts:
- mountPath: /shared-data
name: shared-volume
volumes:
- name: shared-volume
emptyDir: {}
- 在上述示例中,两个容器都挂载了同一个存储卷,因此它们可以读写相同的文件。
🛠️ 容器组管理
- 容器组(Pod)在 Kubernetes 中作为部署、扩展和管理的基础单元提供了一种方式来封装多个容器。这些容器共享相同的网络和存储资源,允许它们紧密地协同工作。
🎈 部署
- 当你创建一个容器组时,你是在请求 Kubernetes 集群启动指定的容器集合。Kubernetes 负责决定在哪个节点上运行这些容器,并确保它们是运行的。
🌟 为什么部署很重要?
- 灵活性: 你可以选择在容器组中运行一个或多个容器,这取决于你的应用需要。
- 原子性: 由于容器组的设计,所有容器要么全部启动,要么全部失败,这确保了部署的一致性。
🌟 如何部署容器组?
示例代码:
apiVersion: v1
kind: Pod
metadata:
name: my-pod
spec:
containers:
- name: my-container
image: my-image
上述 YAML 定义描述了一个简单的容器组,其中包含一个容器。
🎈 水平放缩
- Kubernetes 允许你根据需要自动地增加或减少容器组的实例数量,这通常是基于 CPU 利用率、内存使用或其他选择的指标。
🌟 为什么水平放缩很重要?
- 适应性: 自动扩展容器组实例数量可以确保应用在需求增加时仍能提供良好的性能,而在需求减少时不会浪费资源。
- 效率: 通过仅在需要时运行所需数量的容器实例,可以更有效地利用集群资源。
🌟 如何实现水平放缩?
示例代码:
apiVersion: autoscaling/v1
kind: HorizontalPodAutoscaler
metadata:
name: my-hpa
spec:
scaleTargetRef:
apiVersion: v1
kind: Pod
name: my-pod
minReplicas: 1
maxReplicas: 10
targetCPUUtilizationPercentage: 50
上述 YAML 定义一个自动扩展器,当 my-pod
的 CPU 利用率超过 50% 时,它会自动增加其副本数量,最多到 10 个。
🎯 容器组的使用
- 容器组(Pods)是 Kubernetes 的核心概念,它们提供了一个环境来运行一个或多个紧密关联的容器。这些容器共享网络命名空间、存储等,使它们能够无缝地协同工作。以下是容器组的一些常见用途:
⚡️ 1. 内容管理,文件和数据加载以及本地缓存管理
- 容器组可以用作数据载入的节点,预加载必要的数据和文件,或者作为本地缓存,提供快速的数据访问。
🌟 示例:
apiVersion: v1
kind: Pod
metadata:
name: data-loader-pod
spec:
containers:
- name: data-loader
image: data-loader-image
volumeMounts:
- mountPath: /data
name: data-volume
volumes:
- name: data-volume
emptyDir: {}
这个 Pod 使用一个 data-loader-image
容器,该容器预加载数据到 /data
目录。
⚡️ 2. 日志和检查点备份,压缩,快照
- 容器组可以配置为自动捕获其输出并保存为日志。这些日志可以被备份、压缩或转储为快照。
🌟 示例:
- 在你的应用中集成日志库,如 Fluentd 或 Logstash,来捕获、转储和备份容器日志。
apiVersion: v1
kind: Pod
metadata:
name: logging-pod
spec:
containers:
- name: logger
image: logger-image
volumeMounts:
- mountPath: /logs
name: logs-volume
volumes:
- name: logs-volume
hostPath:
path: /var/logs
⚡️ 3. 监听数据变化,跟踪日志,日志和监控代理,消息发布
- 容器组可以配备各种工具,用于数据变化的实时监听、日志跟踪、消息发布等。
🌟 示例:
- 使用
Prometheus
或Grafana
来监控容器的性能指标,并在需要时发送警报。
apiVersion: v1
kind: Pod
metadata:
name: monitoring-pod
spec:
containers:
- name: prometheus
image: prometheus-image
ports:
- containerPort: 9090
⚡️ 4. 代理,网桥
- 容器组可以承载各种代理服务,如 API 网关、数据转发器等,为其他服务提供中间层。
🌟 示例:
- 部署一个 Nginx 或 HAProxy 负载均衡器,将流量引导到其他服务。
apiVersion: v1
kind: Pod
metadata:
name: nginx-proxy-pod
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
⚡️ 5. 控制器,管理,配置以及更新
- 容器组作为控制器运行,帮助管理、配置和更新其他服务或资源。
🌟 示例:
- 部署一个 Kubernetes Operator Pod,该 Pod 负责管理和维护特定的应用或服务。
apiVersion: v1
kind: Pod
metadata:
name: operator-pod
spec:
containers:
- name: custom-operator
image: custom-operator-image
🔄 替代方案
- 在容器化的世界中,通常建议每个容器只运行一个进程。但为什么我们不在一个单独的容器里运行多个程序呢?
🎈 1. 透明化
- 为了保持容器内的一致性,我们需要容器具有透明的基础设施和服务,例如进程管理和资源监控。这样的设计主要是为了用户的便利性。
🌟 示例代码:
# 使用基础镜像
FROM ubuntu:latest
# 安装进程管理工具
RUN apt-get update && apt-get install -y supervisor
# 将 supervisor 配置文件复制到容器中
COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf
CMD ["/usr/bin/supervisord"]
🎈 2. 解偶软件之间的依赖
- 每个容器都可以独立地构建和发布,这样,当一个组件需要更新时,我们不必重新构建整个容器。这为 Kubernetes 提供了热发布和热更新的可能性。
🌟 示例代码:
# 使用 Node.js 基础镜像
FROM node:14
# 安装应用依赖
WORKDIR /usr/src/app
COPY package*.json ./
RUN npm install
# 复制应用源代码到容器中
COPY . .
CMD ["npm", "start"]
🎈 3. 方便使用
- 通过将每个进程放在其自己的容器中,我们可以确保每个进程都有其自己的文件系统、库和依赖关系。我们不必担心不同的应用程序冲突或需要不同版本的同一依赖。
🌟 示例代码:
# 使用 Python 基础镜像
FROM python:3.8
# 安装应用依赖
WORKDIR /usr/src/app
COPY requirements.txt ./
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用源代码到容器中
COPY . .
CMD ["python", "./my_script.py"]
🎈 4. 高效
- 因为每个容器只运行一个进程,所以容器可以非常轻量级。这允许更高的资源利用率和更快的启动时间。
🌟 示例代码:
# 使用轻量级的 Alpine Linux 基础镜像
FROM alpine:latest
# 安装一个简单的 web 服务器
RUN apk add --no-cache lighttpd
CMD ["lighttpd", "-D", "-f", "/etc/lighttpd/lighttpd.conf"]
🔄 容器组的生命状态 (Pod Lifecycle States)
- 容器组 (Pod) 是 Kubernetes 中的基本部署单元,代表在一个工作节点上运行的一组容器。与容器的生命周期不同,容器组有自己的生命周期状态。
🎄 1. Pending
- 此状态意味着容器组已被 Kubernetes 系统接受,但其中一个或多个容器尚未运行。这可能是因为容器镜像仍在下载中,或存在其他等待条件。
🌟 示例代码:
# 创建一个 Pod
kubectl run pending-pod --image=busybox -- sleep 10000
# 查看 Pod 状态
kubectl get pod pending-pod
- 在输出中,如果你看到状态为
Pending
,则意味着 Pod 正在等待进入Running
状态。
🎄 2. Running
- 当容器组已经绑定到一个节点,并且所有的容器都已经创建,则容器组进入
Running
状态。至少有一个容器仍在运行,或者正在开始或重新启动。
🌟 示例代码:
# 创建一个 Pod
kubectl run running-pod --image=busybox -- sleep 10000
# 查看 Pod 状态
kubectl get pod running-pod
- 在输出中,你应该会看到状态为
Running
。
🎄 3. Succeeded
- 当 Pod 中的所有容器都已完成执行并已终止时,Pod 的状态为
Succeeded
。
🌟 示例代码:
# 创建一个 Pod,该 Pod 完成后会立即退出
kubectl run succeeded-pod --image=busybox -- echo "Hello, World!"
# 查看 Pod 状态
kubectl get pod succeeded-pod
- 在输出中,你应该会看到状态为
Succeeded
。
🎄 4. Failed
- 当 Pod 中的至少一个容器以非零状态终止时,Pod 的状态为
Failed
。
🌟 示例代码:
# 创建一个 Pod,该 Pod 由于错误命令而失败
kubectl run failed-pod --image=busybox -- cmd-not-found
# 查看 Pod 状态
kubectl get pod failed-pod
- 在输出中,你应该会看到状态为
Failed
。
🌱 容器组生命周期 (Pod Lifecycle)
- 容器组 (Pod) 在 Kubernetes 中有其特定的生命周期。这个生命周期受到许多因素的影响,包括用户的操作、容器的健康状况、节点的健康状况、以及 Kubernetes 的策略和控制器的行为。
🎈 1. 容器组创建
- 一旦容器组被创建,它通常不会被自动删除,除非有特定的触发器,例如用户操作或复制控制器的行为。
🌟 示例代码:
# 创建一个简单的容器组
kubectl run sample-pod --image=nginx
🎈 2. 节点失效
- 如果一个节点失效或无法连接,节点控制器会将该节点上的所有容器组标记为
failed
状态。
🌟 示例代码:
# 模拟节点失效(实际生产环境中不建议这样做)
kubectl drain <NODE_NAME> --force --ignore-daemonsets
🎈 3. 容器状态与重启策略
- 容器组中的容器可以有多种状态,例如
running
、terminated
等。根据容器的状态和容器组的重启策略,Kubernetes 会采取相应的行动。
🌟 示例代码:
# 查看容器组的重启策略
kubectl get pod <POD_NAME> -o=jsonpath='{.spec.restartPolicy}'
# 设置容器组的重启策略为 'Always'
kubectl patch pod <POD_NAME> -p '{"spec":{"restartPolicy":"Always"}}'
🎈 4. 资源不足
- 如果容器组中的容器因资源不足(例如 CPU、内存)而被终止,它会根据重启策略重新启动。
🌟 示例代码:
# 创建一个超出资源限制的容器组
kubectl run resource-intensive-pod --image=busybox --requests='memory=10Mi' --command -- /bin/sh -c "echo '1' > /dev/null"
🎈 5. 磁盘故障
- 如果容器组使用的存储卷出现问题,例如磁盘故障,容器组可能会进入
failed
状态。
🌟 示例代码:
# 查看存储卷的状态(需要先创建并绑定存储卷)
kubectl describe pvc <PVC_NAME>
🎈 6. 节点资源耗尽
- 如果一个节点的资源(例如 CPU、内存)耗尽,节点上运行的容器组可能会因资源不足而被驱逐。
🌟 示例代码:
# 模拟节点资源耗尽(这只是一个简单的示例,实际生产环境中不建议这样做)
kubectl run resource-hog-pod --image=busybox --requests='cpu=2000m' -- /bin/sh -c "while true; do echo 'hogging resources'; done"
🚀 容器组状态
1. 状态: running 🟢,有 1 容器,容器正常退出
- 📝 记录: 完成事件
🔄 重启策略
- 始终: 🔄 重启容器,容器组保持 running
- 失败时: ✅ 容器组变为 succeeded
- 从不: ✅ 容器组变为 succeeded
2. 状态: running 🟢,有 1 容器,容器异常退出
- 📝 记录: 失败事件
🔄 重启策略
- 始终: 🔄 重启容器,容器组保持 running
- 失败时: 🔄 重启容器,容器组保持 running
- 从不: ❌ 容器组变为 failed
3. 状态: running 🟢,有 2 容器,有 1 容器异常退出
- 📝 记录: 失败事件
🔄 重启策略
- 始终: 🔄 重启容器,容器组保持 running
- 失败时: 🔄 重启容器,容器组保持 running
- 从不: 🟢 容器组保持 running
4. 当有 2 容器退出
- 📝 记录: 失败事件
🔄 重启策略
- 始终: 🔄 重启容器,容器组保持 running
- 失败时: 🔄 重启容器,容器组保持 running
- 从不: ❌ 容器组变为 failed
5. 状态: running 🟢,容器内存不足
- ⚠️ 操作: 标记容器错误中断
- 📝 记录: 内存不足事件
🔄 重启策略
- 始终: 🔄 重启容器,容器组保持 running
- 失败时: 🔄 重启容器,容器组保持 running
- 从不: 🚫 记录错误事件,容器组变为 failed
6. 状态: running 🟢,一块磁盘死掉
- ❌ 操作: 杀死所有容器
- 📝 记录: 事件
- ❌ 结果: 容器组变为 failed
- 💡 备注: 如果容器组运行在一个控制器下,容器组将会在其他地方重新创建
7. 状态: running 🟢,对应的节点段溢出
- ⏳ 操作: 节点控制器等到超时
- ❌ 记录: 节点控制器标记容器组 failed
- 💡 备注: 如果容器组运行在一个控制器下,容器组将会在其他地方重新创建
🔄 Replication Controllers
- Replication Controller 是 Kubernetes 的一个早期概念,它确保在集群中始终运行指定数量的 Pod 副本。尽管现在更多地使用 Deployment、StatefulSet 和 DaemonSet 等更先进的工具,但了解 Replication Controller 的基本概念仍然很有用。
🎈 功能和特点
- 确保恒定数量的 Pod 副本:如果有太多的副本,它会终止额外的 Pod。如果副本不足,它会启动更多的 Pod。
- 支持简单的滚动更新:通过逐渐替换旧的 Pod 为新的 Pod。
- 响应集群变化:例如,当节点故障时,替换和重新调度在故障节点上运行的 Pod。
🌟 示例代码:
# replication-controller.yaml
apiVersion: v1
kind: ReplicationController
metadata:
name: nginx-controller
spec:
replicas: 3
selector:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80
- 上述 Replication Controller 定义了一个期望有三个副本的 nginx Pod。
📝 运行示例:
# 创建 Replication Controller
kubectl apply -f replication-controller.yaml
# 查看当前的 Replication Controllers
kubectl get rc
# 查看由 Replication Controller 创建的 Pods
kubectl get pods -l app=nginx
- ⚠️ 注意: 现代 Kubernetes 集群中,使用 Deployment 是更常见的方法来管理 Pod 的副本,因为 Deployment 提供了更多的功能,例如滚动更新和回滚。但 Replication Controller 为 Kubernetes 提供了稳定、可靠的 Pod 副本管理的基础。
🛰️ 服务 (Services)
- Kubernetes 的服务是一种抽象,它定义了一组运行相同工作的 Pod 和访问它们的策略。服务是与 Pod 的生命周期无关的,由此,Pod 可能会来来去去,但服务保持稳定,所有的流量都会被正确地路由。
🎈 功能和特点
- 稳定的 IP 地址和 DNS 名称:当 Pod 消失或被替换时,服务的 IP 地址和 DNS 名称始终保持不变。
- 负载均衡:流量会自动分发到服务后的所有 Pod。
- 服务发现:基于服务的固定 IP 和 DNS 名称,其他 Pod 可以轻松地发现和连接到服务。
🌟 示例代码:
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app: nginx
ports:
- protocol: TCP
port: 80
targetPort: 80
type: LoadBalancer
- 上述服务定义了一个名为
nginx-service
的服务,该服务将流量路由到标记为app=nginx
的任何 Pod,并提供负载均衡。
🎈 运行示例:
# 创建服务
kubectl apply -f service.yaml
# 查看当前的服务
kubectl get svc
# 访问服务
# 如果你在云提供商上(例如 AWS, GCP, Azure),这会给你一个外部 IP 地址
curl [EXTERNAL-IP]:80
- ⚠️ 注意: 服务提供了一种稳定的方式来访问动态 Pod。无论后端 Pod 如何变化,服务都确保流量能够到达正确的目的地。
📂 卷 (Volumes)
- 在 Kubernetes 中,卷是用来持久化存储数据的一种方式,它提供了与容器隔离的存储。这意味着,即使容器被删除,卷中的数据也仍然存在。这与 Docker 的临时卷不同,其中数据会随容器的消失而消失。
🎈 功能和特点
- 持久性: 不同于容器的临时存储,卷的生命周期独立于容器,确保数据持久化。
- 共享存储:允许在同一 Pod 的多个容器之间共享存储。
- 多种存储选项: Kubernetes 支持多种卷类型,如 AWS EBS、NFS、ConfigMaps、Secrets 等。
- 数据的持久化和可移植性:通过卷,用户可以在集群中的不同节点上移动数据。
🌟 示例代码:
- 以下是一个 Kubernetes 卷的简单示例,使用一个本地目录作为存储:
# pod-with-volume.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx-pod
spec:
containers:
- name: nginx
image: nginx
volumeMounts:
- mountPath: /usr/share/nginx/html
name: my-volume
volumes:
- name: my-volume
hostPath:
# 目录的位置
path: /data
# 路径的类型
type: Directory
- 在上述定义中,Pod
nginx-pod
使用了一个名为my-volume
的卷。该卷映射到容器内的/usr/share/nginx/html
路径,并链接到主机上的/data
目录。
🎈 运行示例:
# 创建包含卷的 Pod
kubectl apply -f pod-with-volume.yaml
# 检查 Pod
kubectl get pods nginx-pod
# 进入 Pod 内部检查卷
kubectl exec nginx-pod -- ls /usr/share/nginx/html
- ⚠️ 注意: 使用卷可以确保数据的持久性,即使 Pod 被删除或重启。
🏷️ 标签 (Labels)
- 在 Kubernetes 中,标签是用来组织和选择资源对象的键值对,如 Pod、Service、ReplicaSet 等。它们为用户提供了一个灵活的方式来管理和运行在集群中的资源。
🎈 功能和特点
- 组织: 可以根据环境、应用名称、版本等属性给资源打上不同的标签。
- 选择: 标签选择器允许用户过滤出具有特定标签的资源。
- 策略控制: 通过标签和标签选择器,可以指定网络策略、Pod 放置策略等。
- 扩展性: 用户可以随时为现有的资源添加或修改标签。
🌟 示例代码:
- 以下是一个 Pod 定义的示例,其中包含了多个标签:
apiVersion: v1
kind: Pod
metadata:
name: sample-pod
labels:
env: production
app: frontend
version: v1
spec:
containers:
- name: nginx
image: nginx:1.14.2
- 在上述示例中,Pod
sample-pod
有三个标签:env=production
,app=frontend
, 和version=v1
。
🎈 运行示例:
# 创建包含标签的 Pod
kubectl apply -f pod-with-labels.yaml
# 获取具有特定标签的 Pod
kubectl get pods -l app=frontend
# 获取具有多个标签的 Pod
kubectl get pods -l app=frontend,env=production
# 删除具有特定标签的 Pod
kubectl delete pods -l version=v1
- ⚠️ 注意: 标签为 Kubernetes 提供了一个强大的工具,用于组织、选择和操作资源。
🔒 接口权限 (Accessing the API)
- 在 Kubernetes 中,API 是所有操作的核心。无论是从命令行工具
kubectl
进行的命令,还是从其他系统与 Kubernetes 交互,所有的操作都是通过 Kubernetes API 进行的。为了确保集群的安全性,对这些 API 的访问进行严格的控制是非常重要的。
🎈 功能和特点
- API 认证: 确定用户是否有权访问。
- API 授权: 确定用户可以执行哪些操作。
- API 传输安全: 通过使用 TLS,确保数据在传输过程中的安全性。
- 防火墙规则: 可以通过设置网络策略来限制哪些 IP 或网络可以访问 Kubernetes API。
- 审计日志: 跟踪谁做了什么。
🌟 示例代码:
- 以下是一个防火墙规则的示例,限制只有特定 IP 可以访问 Kubernetes API:
# 例如,我们使用 iptables 工具设置规则
# 仅允许 IP 192.168.1.100 访问 Kubernetes API 的端口 6443
sudo iptables -A INPUT -p tcp --dport 6443 -s 192.168.1.100 -j ACCEPT
# 阻止所有其他 IP 访问
sudo iptables -A INPUT -p tcp --dport 6443 -j DROP
-
⚠️ 注意: 这只是一个基础的示例,实际的生产环境中可能会使用更复杂的网络策略工具或云提供商的防火墙服务来保护 Kubernetes API。始终确保在更改防火墙规则之前进行备份,并在知情的情况下进行操作,以避免意外中断服务。
-
此外,Kubernetes 本身还提供了多种认证和授权方式,如基于角色的访问控制 (RBAC)、节点授权等,确保对 API 的访问是安全和受控的。
🖥️ Web界面 & 命令行操作 (CLI)
- Kubernetes 提供了两种主要的方式来与其交互:Web 界面 (Dashboard) 和命令行工具
kubectl
。
🧲 1. Web 界面 (Dashboard)
- Kubernetes Dashboard 是一个通用的 web-based UI,它为 Kubernetes 集群的用户提供了一个可视化界面。通过这个界面,用户可以管理集群中的资源,查看集群状态,部署应用,创建服务,查看日志等。
🎈 功能和特点:
- 无需安装任何工具,只需一个现代浏览器。
- 提供了一个直观的、可视化的界面来管理 Kubernetes 资源。
- 可以方便地创建、修改、删除和查看所有 Kubernetes 资源。
- 显示关于集群和任何其它 Kubernetes 资源的实时数据。
🌟 示例代码:
- 安装 Kubernetes Dashboard:
kubectl apply -f https://raw.githubusercontent.com/kubernetes/dashboard/v2.0.0/aio/deploy/recommended.yaml
- 启动代理:
kubectl proxy
- 现在,Dashboard 应该可以在浏览器通过以下链接访问:
http://localhost:8001/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
.
🧲 2. 命令行操作 (kubectl
)
kubectl
是 Kubernetes 的命令行工具,用于与集群交互。它是管理 Kubernetes 的最主要工具,提供了大量命令来操作集群资源。
🎈 功能和特点:
- 快速、直接的命令行交互。
- 可以在任何支持的平台上运行。
- 提供了强大的脚本能力,使得自动化和集成变得简单。
- 支持多种输出格式,如 JSON, YAML, wide。
🌟 示例代码:
- 查看集群节点:
kubectl get nodes
- 部署一个新的应用:
kubectl create deployment nginx --image=nginx
- 获取集群中所有的 pods:
kubectl get pods
- 删除一个 deployment:
kubectl delete deployment nginx