Kubernetes基础
Kubernetes 最初源于谷歌内部的 Borg,提供了面向应用的容器集群部署和管理系统。Kubernetes 的目标旨在消除编排物理 / 虚拟计算,网络和存储基础设施的负担,并使应用程序运营商和开发人员完全将重点放在以容器为中心的原语上进行自助运营。Kubernetes 也提供稳定、兼容的基础(平台),用于构建定制化的 workflows 和更高级的自动化任务。
Kubernetes 具备完善的集群管理能力,包括多层次的安全防护和准入机制、多租户应用支撑能力、透明的服务注册和服务发现机制、内建负载均衡器、故障发现和自我修复能力、服务滚动升级和在线扩容、可扩展的资源自动调度机制、多粒度的资源配额管理能力。Kubernetes 还提供完善的管理工具,涵盖开发、部署测试、运维监控等各个环节。
Brog简介
目的:让用户不必操心资源管理的问题,专注于核心业务,做到跨多个数据中心的资源利用率最大化
- BorgMaster 是整个集群的大脑,负责维护整个集群的状态,并将数据持久化到 Paxos 存储中;
- Scheduer 负责任务的调度,根据应用的特点将其调度到具体的机器上去;
- Borglet 负责真正运行任务(在容器中);
- borgcfg 是 Borg 的命令行工具,用于跟 Borg 系统交互,一般通过一个配置文件来提交任务。
Kubernetes 架构
借鉴了Brog的设计理念
核心组件
- etcd 保存了整个集群的状态;
- apiserver 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制;
- controller manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;
- scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上;
- kubelet 负责维护容器的生命周期,同时也负责 Volume(CSI)和网络(CNI)的管理;
- Container runtime 负责镜像管理以及 Pod 和容器的真正运行(CRI);
- kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡;
除了核心组件,还有一些推荐的插件,其中有的已经成为 CNCF 中的托管项目: - CoreDNS 负责为整个集群提供 DNS 服务
- Ingress Controller 为服务提供外网入口
- Prometheus 提供资源监控
- Dashboard 提供 GUI
- Federation 提供跨可用区的集群
架构示意图
整体架构
表明了Kubernetes的架构设计以及组件之间的通信协议
整体架构
K8s 集群节点拥有 Master 和 Node 两种角色。
Master 架构
官方叫做控制平面(Control Plane)。主要负责整个集群的管控,包含监控、编排、调度集群中的各类资源对象(如 Pod/Deployment 等)。 通常 Master 会占用一个单独的集群节点(不会运行应用容器),基于高可用可能会占用多台
Master 由四个部分组成:
- API Server 进程
所有资源的入口,提供 RESTful API 服务,负责接收用户的请求,并将其写入 etcd。Master 上kube-system
空间中运行的 Pod 之一kube-apiserver
是 API Server 的具体实现。与其通信有三种方式:
- 最原始的通过 REST API 访问;
- 通过官方提供的 Client 来访问,本质上也是 REST API 调用;
- 通过 kubectl 客户端访问,其本质上是将命令转换为 REST API 调用,是最主要的访问方式。
- etcd
K8s 使用 etcd 作为内部数据库,用于保存集群配置以及所有对象的状态信息。只有 API Server 进程能直接读写 etcd。为了保证集群数据安全性,建议为其考虑备份方案。 如果 etcd 不可用,应用容器仍然会继续运行,但用户无法对集群做任何操作(包括对任何资源的增删改查)。 - 调度器(Scheduler)
它是 Pod 资源的调度器,用于监听刚创建还未分配 Node 的 Pod,为其分配相应 Node。 调度时会考虑资源需求、硬件/软件/指定限制条件以及内部负载情况等因素,所以可能会调度失败。 调度器也是操作 API Server 进程的各项接口来完成调度的。比如 Watch 接口监听新建的 Pod,并搜索所有满足 Pod 需求的 Node 列表, 再执行 Pod 调度逻辑,调度成功后将 Pod 绑定到目标 Node 上。 - 控制器管理器(kube-controller-manager)
Controller 管理器实现了全部的后台控制循环,完成对集群的健康并对事件做出响应。Controller 管理器是各种 Controller 的管理者,负责创建 controller,并监控它们的执行。 这些 Controller 包括 NodeController、ReplicationController 等,每个 controller 都在后台启动了一个独立的监听循环(可以简单理解为一个线程),负责监控 API Server 的变更。
- Node 控制器:负责管理和维护集群中的节点。比如节点的健康检查、注册/注销、节点状态报告、自动扩容等。
- Replication 控制器:确保集群中运行的 Pod 的数量与指定的副本数(replica)保持一致,稍微具体的说,对于每一个 Replication 控制器管理下的 Pod 组,具有以下逻辑:
- 当 Pod 组中的任何一个 Pod 被删除或故障时,Replication 控制器会自动创建新的 Pod 来作为替代
- 当 Pod 组内的 Pod 数量超过所定义的
replica
数量时,Replication 控制器会终止多余的 Pod
- Endpoint 控制器:负责生成和维护所有 Endpoint 对象的控制器。Endpoint 控制器用于监听 Service 和对应 Pod 副本的变化
- ServiceAccount 及 Token 控制器:为新的命名空间创建默认账户和 API 访问令牌。
kube-controller-manager
所执行的各项操作也是基于 API Server 进程的。
Node 架构
数据平面。是集群中的承载实际工作任务的节点,直接负责对容器资源的控制,可以无限扩展。
Node 由三部分组成:kubelet、kube-proxy 和容器运行时(如 docker/containerd)
- kubelet
它是每个 Node 上都运行的主要代理进程。kubelet 以 PodSpec 为单位来运行任务,后者是一种 Pod 的 yaml 或 json 对象。 kubelet 会运行由各种方式提供的一系列 PodSpec,并确保这些 PodSpec 描述的容器健康运行。
不是 k8s 创建的容器不属于 kubelet 管理范围,kubelet 也会及时将 Pod 内容器状态报告给 API Server,并定期执行 PodSpec 描述的容器健康检查。 同时 kubelet 也负责存储卷等资源的管理。
kubelet 会定期调用 Master 节点上的 API Server 的 REST API 以报告自身状态,然后由 API Server 存储到 etcd 中。 - kube-proxy
每个节点都会运行一个 kube-proxy Pod。它作为 K8s Service 的网络访问入口,负责将 Service 的流量转发到后端的 Pod,并提供 Service 的负载均衡能力。 kube-proxy 会监听 API Server 中 Service 和 Endpoint 资源的变化,并将这些变化实时反应到节点的网络规则中,确保流量正确路由到服务。 总结来说,kube-proxy 主要负责维护网络规则和四层流量的负载均衡工作。 - 容器运行时
负责直接管理容器生命周期的软件。k8s 支持包含 docker、containerd 在内的任何基于 k8s cri(容器运行时接口)实现的 runtime。
分层架构
类似于Linux的分层架构
- 核心层:Kubernetes 最核心的功能,对外提供 API 构建高层的应用,对内提供插件式应用执行环境
- 应用层:部署(无状态应用、有状态应用、批处理任务、集群应用等)和路由(服务发现、DNS 解析等)、Service Mesh(部分位于应用层)
- 管理层:系统度量(如基础设施、容器和网络的度量),自动化(如自动扩展、动态 Provision 等)以及策略管理(RBAC、Quota、PSP、NetworkPolicy 等)、Service Mesh(部分位于管理层)
- 接口层:kubectl 命令行工具、客户端 SDK 以及集群联邦
- 生态系统:在接口层之上的庞大容器集群管理调度的生态系统,可以划分为两个范畴
- Kubernetes 外部:日志、监控、配置管理、CI/CD、Workflow、FaaS、OTS 应用、ChatOps、GitOps、SecOps 等
- Kubernetes 内部:CRI、CNI、CSI、镜像仓库、Cloud Provider、集群自身的配置和管理等
核心对象
- Pod
Pod 是 k8s 调度的基本单元,它封装了一个或多个容器。Pod 中的容器会作为一个整体被 k8s 调度到一个 Node 上运行。
Pod 一般代表单个 app,由一个或多个关系紧密的容器组成。这些容器拥有共同的生命周期,作为一个整体被编排到 Node 上。并且它们 共享存储卷、网络和计算资源。k8s 以 Pod 为最小单位进行调度等操作。 - 控制器
一般来说,用户不会直接创建 Pod,而是创建控制器来管理 Pod,因为控制器能够更细粒度的控制 Pod 的运行方式,比如副本数量、部署位置等。 控制器包含下面几种:
- Replication 控制器(以及 ReplicaSet 控制器):负责保证 Pod 副本数量符合预期(涉及对 Pod 的启动、停止等操作);
- Deployment 控制器:是高于 Replication 控制器的对象,也是最常用的控制器,用于管理 Pod 的发布、更新、回滚等;
- StatefulSet 控制器:与 Deployment 同级,提供排序和唯一性保证的特殊 Pod 控制器。用于管理有状态服务,比如数据库等。
- DaemonSet 控制器:与 Deployment 同级,用于在集群中的每个 Node 上运行单个 Pod,多用于日志收集和转发、监控等功能的服务。并且它可以绕过常规 Pod 无法调度到 Master 运行的限制;
- Job 控制器:与 Deployment 同级,用于管理一次性任务,比如批处理任务;
- CronJob 控制器:与 Deployment 同级,在 Job 控制器基础上增加了时间调度,用于执行定时任务。
- Service、Ingress 和 Storage
Service是对一组 Pod 的抽象,它定义了 Pod 的逻辑集合以及访问该集合的策略。前面的 Deployment 等控制器只定义了 Pod 运行数量和生命周期, 并没有定义如何访问这些 Pod,由于 Pod 重启后 IP 会发生变化,没有固定 IP 和端口提供服务。
Service 对象就是为了解决这个问题。Service 可以自动跟踪并绑定后端控制器管理的多个 Pod,即使发生重启、扩容等事件也能自动处理, 同时提供统一 IP 供前端访问,所以通过 Service 就可以获得服务发现的能力,部署微服务时就无需单独部署注册中心组件。
Ingress:是一个路由规则集合,通过 Ingress 规则定义的规则,可以将多个 Service 组合成一个虚拟服务(如前端页面+后端 API)。 它可实现业务网关的作用,类似 Nginx 的用法,可以实现负载均衡、SSL 卸载、流量转发、流量控制等功能。
Storage:Pod 中用于存储的抽象,它定义了 Pod 的存储卷,包括本地存储和网络存储;它的生命周期独立于 Pod 之外,可进行单独控制。 - 资源划分
- 命名空间(Namespace):k8s 通过 namespace 对同一台物理机上的 k8s 资源进行逻辑隔离。
- 标签(Labels):是一种语义化标记,可以附加到 Pod、Node 等对象之上,然后更高级的对象可以基于标签对它们进行筛选和调用, 例如 Service 可以将请求只路由到指定标签的 Pod,或者 Deployment 可以将 Pod 只调度到指定标签的 Node。
- 注解(Annotations):也是键值对数据,但更灵活,它的 value 允许包含结构化数据。一般用于元数据配置,不用于筛选。 例如 Ingress 中通过注解为 nginx 控制器配置禁用 ssl 重定向。
DNS
每个k8s集群都有自己独立的DNS服务用于为集群中的Pod提供域名解析服务,集群DNS服务有一个静态IP,每个新启动的 Pod 内部都会在/etc/resolve.conf
中硬编码这个 IP 作为 DNS 服务器。
每当新的 Service 被发布到集群中的时候,同时也会在集群 DNS 服务中创建一个域名记录(对应其后端 Pod IP),这样 Pod 就可以通过 Service 的域名访问其对应的服务。一些比较特殊的 Pod 也会注册到集群 DNS 服务器, 比如 StatefulSet 管理下的每个 Pod(以PodName-0-*
, PodName-1-*
的形式)。
集群的 DNS 服务器由集群内一个名为kube-dns
的 Service 提供,它将 DNS 请求均衡转发到每个节点上的coredns-*
Pod。
集群 DNS 服务使用CoreDNS作为后端,CoreDNS 是一个由 Go 实现的高性能且灵活的 DNS Server,支持使用自定义插件来扩展功能。
部署
个人使用docker Desktop + wsl2 + vscode的用法,现在docker desktop中打开设置就可以使用
首先编写一个main.go
1 | package main |
然后是一个Dockerfile
1 | # 使用 Golang 官方镜像作为构建环境 |
执行命令
1 | docker build . -t leigg/hellok8s:v1 |
在拉取镜像的时候如果出问题,可以尝试添加镜像
1 | { "registry-mirrors": ["https://registry.aliyuncs.com"] } |
如果出现无法拉取的情景,可以尝试单独拉取,然后再执行;
若需要重新构建相同的镜像名,执行以下命令删除无标签镜像,以保持镜像列表整洁:
1 | docker image prune -f |
成功构建
之后执行
1 | docker run --rm -p 3000:3000 renxindocker/hellok8s:v1 |
使用vscode进行端口转发之后就可以看到页面
再推送镜像
1 | docker push renxindocker/hellok8s:v1 |
使用pod
在 VMware 的世界中,调度的原子单位是虚拟机(VM);在 Docker 的世界中,调度的原子单位是容器(Container);而在 Kubernetes 的世界中,调度的原子单位是 Pod
首先先安装k8s
1 | curl -LO "https://dl.k8s.io/release/$(curl -L -s https://dl.k8s.io/release/stable.txt)/bin/linux/amd64/kubectl" |
编写一个 nginx.yaml
1 | # nginx.yaml |
再运行命令,-f
参数用于指定要应用的 YAML 配置文件,也可以直接指定目录下的所有yaml文件
1 | kubectl apply -f nginx.yaml |
如果要删除
1 | kubectl delete pod nginx-pod |
与Pod交互
宿主机的4000端口映射到pod80
进入 Pod Shell:
1 | kk exec -it nginx-pod -- /bin/bash |
其他 Pod 常用命令:
1 | kk delete pod nginx-pod # 删除pod |
Pod 与 Contaioner 的不同
在刚刚创建的资源里,在最内层是我们的服务 nginx,运行在 Container 当中, Container (容器) 的本质是进程,而 Pod 是管理这一组进程的资源。
Sidecar 模式
Pod 可以管理多个 Container。例如在某些场景服务之间需要文件交换(日志收集),本地网络通信需求(使用 localhost 或者 Socket 文件进行本地通信),这时候就会部署多容器 Pod。 这是一种常见的容器设计模式,它有个名称叫做 Sidecar。Sidecar 模式中主要包含两类容器,一类是主应用容器,另一类是辅助容器(称为 sidecar 容器)提供额外的功能,它们共享相同的网络和存储空间。这个模式的灵感来自于摩托车上的辅助座位,因此得名 “Sidecar”。
Init 容器
Init 容器是一种特殊容器,在 Pod 内的应用容器启动之前运行。Init 容器可以包括一些应用镜像中不存在的实用工具和安装脚本。
Init 容器有一些特点如下:
- 允许定义一个或多个 Init 容器
- 它们会先于 Pod 内其他普通容器启动
- 它们总会在运行短暂时间后进入
Completed
状态 - 它们会严格按照定义的顺序启动,每个容器成功运行结束后才会启动下一个
- 任意一个 Init 容器运行失败都会导致 Pod 进入异常状态,这会引起 Pod 重启,除非 PodSpec 中设置的
restartPolicy
策略为Never
- 当 Init 容器正常终止时(exitCode=0),即使 PodSpec 中设置的
restartPolicy
策略为Always
,Pod 也不会重启 - 修改 Init 容器的镜像会导致 Pod 重启
- Init 容器支持普通容器的全部字段和特性,除了
lifecycle、livenessProbe、readinessProbe 和 startupProbe
- Pod 重启会导致 Init 容器重新执行
Init 容器具有与应用容器分离的单独镜像,我们可以使用它来完成一些常规的脚本任务,比如需要sed、awk、python 或 dig
这样的工具完成的任务,而没必要在应用容器中去安装这些命令。说明:包含常用工具的镜像有 busybox,appropriate/curl 等。
go程序的Pod
创建pod.yaml
1 | apiVersion: v1 |
运行
1 | (base) renxin@ThinkPadXin:~/Gworks/src/kub$ kubectl apply -f pod.yaml |
Pod 生命周期
Status
- Pending:Pod 正在调度中(节点选择中)。
- ContainerCreating:Pod 已调度,但容器尚未完全创建和启动(可能在拉取镜像)。
- Running:Pod 中的容器已在运行。
- Terminating:Pod 被删除或重启中,默认宽限时间为 30s,可调整。
- Succeeded:所有容器已成功终止,任务已完成。
- Failed:至少一个容器异常终止(非零退出码)。
- Unknown:无法获取 Pod 状态,通常是与节点通信失败。
Pod 重启策略
spec.restartPolicy
字段支持 Always
、OnFailure
、Never
,适用于 Pod 内所有容器,由 kubelet 在递增延迟(10s, 20s, … 最高 5min)下尝试重启,成功运行 10min 后重置延迟时间。Pod 一旦绑定节点,除非节点故障,不再迁移。
Pod 销毁过程
- 用户发送删除命令,API Server 更新 Pod 状态为 Terminating。
- kubelet 监听到后开始终止 Pod,并触发宽限时间(默认 30s,可指定)。
- Endpoint 控制器移除与此 Pod 相关的 Service Endpoint。
- 若定义了
preStop
回调,执行后再增加 2s 宽限时间。 - 宽限时间结束后未停止,则发送
SIGKILL
信号强制删除。
Pod 生命周期
Pod 是一次性调度的实体,创建后获得唯一 UID。节点失效或资源耗尽时可能会被驱逐或删除。若非控制器管理,Pod 被删除后不会重建;控制器管理的 Pod 被删除后会重建,UID 变更但名称不变。
容器状态
容器状态分为 Waiting
(等待)、Running
(运行中)和 Terminated
(已终止),可通过 kubectl describe pod <pod 名称>
查看详细状态。
Deployment
Pod会由更高级的控制器,比如Deployment来创建和管理
- 应用管理:Deployment 是 Kubernetes 中的一个控制器,用于管理应用程序的部署和更新。它允许你定义应用程序的期望状态,然后确保集群中的副本数符合这个状态。
- 自愈能力:Deployment 可以自动修复故障,如果 Pod 失败,它将启动新的 Pod 来替代。这有助于确保应用程序的高可用性。
- 滚动更新:Deployment 支持滚动更新,允许你逐步将新版本的应用程序部署到集群中,而不会导致中断。
- 副本管理:Deployment 负责管理 Pod 的副本,可以指定应用程序需要的副本数量,Deployment 将根据需求来自动调整。
- 声明性配置:Deployment 的配置是声明性的,你只需定义所需的状态,而不是详细指定如何实现它。Kubernetes 会根据你的声明来管理应用程序的状态。
部署
1 | (base) renxin@ThinkPadXin:~/Gworks/src/kub$ kubectl apply -f deployment.yaml |
还可以查看 pod 运行的 node:
1 | # 这里的IP是pod ip,属于部署k8s集群时规划的pod网段 |
删除 pod 会自动重启一个新的 Pod,确保可用的 pod 数量与deployment.yaml
中的replicas
字段保持一致
修改
修改方式仍然支持修改模板文件和使用patch
命令两种。
现在以修改模板中的replicas=3
为例进行演示。为了能够观察 pod 数量变化过程,提前打开一个终端执行kk get pods --watch
命令。 下面是演示情况:
1 | --watch可简写为-w |
1 | (base) renxin@ThinkPadXin:~/Gworks/src/kub$ kubectl patch deployment hellok8s-go-http -p '{"spec":{"replicas": 3}}' |
更新
1 | (base) renxin@ThinkPadXin:~/Gworks/src/kub$ docker push renxindocker/hellok8s:v2 |
1 | # set image是一种命令式的更新操作,是一种临时性的操作方式,会导致当前状态与YAML清单定义不一致,生产环境中不推荐; |
Deployment 的镜像更新或回滚都是通过 创建新的 ReplicaSet 和终止旧的 ReplicaSet 来完成的,你可以通过kk get rs -w
来观察这一过程。 在更新完成后,应当看到新旧 ReplicaSet 是同时存在的:
1 | (base) renxin@ThinkPadXin:~/Gworks/src/kub$ kubectl get rs -o wide |
回滚部署
- 如果新镜像有问题,旧的 Pod 不会被删除。可以通过
rollout undo
回滚到正常版本。 - 使用
kubectl apply -f <file>
更新部署,通过kubectl set image
更新镜像。 kubectl rollout history
查看部署历史,rollout undo
回滚到指定版本。
滚动更新
- Kubernetes 支持滚动更新,通过逐步创建新 Pod 替换旧 Pod,减少对业务的影响。
- 更新策略:在
Deployment
的spec.strategy.type
中指定更新方式:- RollingUpdate:逐步增加新版本 Pod,逐步减少旧版本 Pod。
- Recreate:先删除旧 Pod,再创建新 Pod,适合无法多进程部署的服务。
- 控制更新速率字段:
- maxSurge:超出期望的最大 Pod 数。
- maxUnavailable:更新时不可用的最大 Pod 数。
1
2
3
4strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 1
控制 Pod 水平伸缩
使用 kubectl scale
命令调整 Deployment 的副本数:
1 | kubectl scale deployment/hellok8s-go-http --replicas=10 |
存活探针(livenessProd)
存活探针用于检测容器何时需要重启,适合处理死锁等问题。重启这种状态下的容器有助于提高应用的可用性,即使其中存在缺陷。比如配置复杂,资源开销较大,容易误报与漏报。配置文件如下:
1 | livenessProbe: |
设置方法:可选 httpGet
、tcpSocket
、exec
、TCP
、gRPC
等方式。
就绪探针(readiness)
就绪探针用于判断容器何时准备好接收流量,未就绪的 Pod 将从服务中剔除。
这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端
先更新deployment
1 | kubectl apply -f deployment.yaml |
配置文件设置:
1 | readinessProbe: |
应用场景:在最大不可用设置为 maxUnavailable=1
时,确保至少一个 Pod 可用。
更新的暂停与恢复
- 更新时可使用 金丝雀发布,先更新部分 Pod 进行验证。
- 使用命令暂停和恢复更新:
1
2
3
4
5
6
7
8
9
10
11
12# 一次性执行两条命令
kubectl set image deployment/hellok8s-go-http hellok8s=leigg/hellok8s:v2
kubectl rollout pause deploy hellok8s-go-http
# 现在观察更新情况,会发现只有一个pod被更新
kubectl get pods
# 如果此刻想要回滚(N需要替换为具体版本号)
kubectl rollout undo deployment hellok8s-go-http --to-revision=N
# 若要继续更新
kubectl rollout resume deploy hellok8s-go-http
底层控制器(ReplicaSet)
- 功能:
- ReplicaSet 由 Deployment 管理,用于维护指定数量的 Pod 副本,确保它们始终处于运行状态。
- 特点:
- 比 Deployment 更低级。
- 不支持滚动更新和回滚等高级功能。
- 推荐使用场景:
- 通常建议使用 Deployment 或 DaemonSet 等高级控制器,而非直接使用 ReplicaSet。
使用 DaemonSet
- 定义:
- DaemonSet 是一种特殊的控制器,确保集群中的每个节点(或大部分节点)运行一个 Pod 副本。
- 功能:
- 当节点加入或退出集群时,DaemonSet 会自动在相应节点上添加或删除 Pod。
- 应用场景:
- 部署为节点提供服务或维护的 Pod,例如:
- 日志收集与转发
- 监控工具
- 节点级别的代理
- 部署为节点提供服务或维护的 Pod,例如:
- 配置要点:
- 端口映射:通常在模板中直接将容器端口映射到节点端口,避免同一节点上多个 Pod 副本导致端口冲突。
- 节点选择:
- 默认情况下,DaemonSet 不会在 master 节点上运行 Pod,因为 master 节点有“不允许调度 Pod”的污点。
- 若需在 master 节点运行 DaemonSet Pod,需要在 Pod 模板中配置污点容忍度(Tolerations)。
Job 和 CronJob
他们与Deployment 和 Daemonset 都是同级的控制器,都是用来执行一次性任务的,区别在于Job是一次性的,CronJob是周期性的
Job
Job 控制器可以执行 3 种类型的任务:
- 一次性任务:启动一个 Pod(除非启动失败)。一旦 Pod 成功终止,Job 就算完成了。
- 串行式任务:连续、多次地执行某个任务,上一个任务完成后,立即执行下个任务,直到全部执行完。
- 并行式任务:可以通过
spec.completions
属性指定执行次数。
使用job.yaml
测试一次性任务:
假设您已经有一个 Kubernetes 集群,并且kubectl
已配置好与集群通信。配置说明:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33# 应用 Job 配置
kubectl apply -f job.yaml
# 输出示例:
# job.batch/pods-job created
# 查看 Job 状态
kubectl get job
# 输出示例:
# NAME COMPLETIONS DURATION AGE
# pods-job 0/1 19s 19s
# 再次查看 Job 状态,确认完成
kubectl get job
# 输出示例:
# NAME COMPLETIONS DURATION AGE
# pods-job 1/1 36s 60s
# 查看 Pod 状态
kubectl get pods
# 输出示例:
# NAME READY STATUS RESTARTS AGE
# pods-simple-pod-abcde 0/1 Completed 0 4m41s
# 查看 Pod 日志(标准输出和错误)
kubectl logs pods-simple-pod-abcde
# 输出示例:
# Start Job!
# Job Done!
# 执行结束后,手动删除 Job,也可在 YAML 中配置自动删除
kubectl delete job pods-job
# 输出示例:
# job.batch "pods-job" deleted
completions
字段:设置任务需要执行的总次数(用于串行式任务)。parallelism
字段:设置任务并发数量(用于并行式任务)。
处理异常情况:backoffLimit
字段:设置任务执行失败时的重试次数,默认是 6 次。- **
restartPolicy
**:推荐设置为Never
(而不是OnFailure
),这样可以保留启动失败的 Pod 以便排查日志。
CronJob
基于Job,添加了时间管理功能,可以实现:在未来某个指定时间运行一次Job,周期性运行Job
1 | # 应用 CronJob 配置 |
其他控制器
除了前面介绍的 Deployment、DaemonSet、Job 和 CronJob 控制器,其他还有:
- ReplicationController 和 ReplicaSetController
- StatefulController
关于 ReplicationController 和 ReplicaSetController
在早期的 Kubernetes 版本中,ReplicationController 是最早提供的控制器,后来 ReplicaSetController 出现并替代了前者,二者没有本质上的区别,后者支持复合式的 selector。随着 Deployment 的出现,由于它们缺少其他控制器的更细粒度的生命周期管理功能,ReplicationController 和 ReplicaSetController 已经很少使用,但仍然保留下来。
在后续版本中,通常创建 Deployment 控制器,由它自动托管 ReplicaSetController,用户无需操心后者(但可以通过命令查看)。ReplicaSetController 也可通过模板创建,用户可自行查询。需要注意的是,手动创建的 ReplicaSetController 不能由 Deployment 控制器托管,因此 ReplicaSetController 不具备滚动更新、版本查看和回滚功能。
StatefulController
这是一种提供排序和唯一性保证的特殊 Pod 控制器
Service
Kubernetes 中的 Pod 是短暂的,它们的 IP 地址可能会随着重新调度、升级或缩放而改变。为了解决这个问题,Kubernetes 引入了 Service。Service 为一组 Pod 提供了一个稳定的访问入口,充当反向代理和负载均衡器。
核心功能
- 稳定的访问端点: Service 以 ServiceName 作为虚拟域名提供稳定的访问入口,屏蔽了后端 Pod IP 地址的变化。
- 负载均衡: Service 将请求分发到后端所有匹配的 Pod。
- 服务发现: Service 提供了内置的 DNS 服务发现机制,方便 Pod 之间的互相访问。
Service 类型
Kubernetes 提供了多种类型的 Service,以满足不同的应用场景:
- ClusterIP (默认): 为 Service 分配一个固定的集群内部的虚拟 IP。只能在集群内部访问。适用于内部数据库、API 服务等。
- NodePort: 基于
ClusterIP
,在每个节点上暴露一个静态端口,外部可以通过NodeIP:NodePort
访问服务。适用于测试环境、简单的 Web 应用等。 - LoadBalancer: 基于
NodePort
,还会使用云提供商的负载均衡器,将服务暴露到外部。适用于生产环境的 Web 应用、公开 API 服务等。
- Headless: 不分配 ClusterIP,不会通过 Kube-proxy 进行反向代理和负载均衡,而是通过 DNS 提供稳定的网络 ID 来访问, 并且 DNS 会将无头 Service 的后端解析为 Pod 的后端 IP 列表,以供集群内访问(不含节点),属于向内发布。适用于 StatefulSet 等有状态应用。
- ExternalName: 通过 CNAME 记录将 Service 域名指向外部域名。属于向内发布。适用于访问集群外部的服务,如外部数据库、认证服务等。
各类型 Service 配置详解
- ClusterIP:
1
2
3
4
5
6
7
8
9
10
11
12apiVersion: v1
kind: Service
metadata:
name: my-clusterip-service
spec:
type: ClusterIP # 默认类型,可省略
selector:
app: my-app # 选择带有 app: my-app 标签的 Pod
ports:
- protocol: TCP # 协议,默认为 TCP
port: 8080 # Service 的端口
targetPort: 80 # Pod 的端口 - NodePort:
1
2
3
4
5
6
7
8
9
10
11
12
13apiVersion: v1
kind: Service
metadata:
name: my-nodeport-service
spec:
type: NodePort
selector:
app: my-app
ports:
- protocol: TCP
port: 8080 # Service 的端口
targetPort: 80 # Pod 的端口
nodePort: 30000 # 节点端口,范围 30000-32767 - LoadBalancer:
1
2
3
4
5
6
7
8
9
10
11
12
13apiVersion: v1
kind: Service
metadata:
name: my-loadbalancer-service
spec:
type: LoadBalancer
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80
# 其他云提供商相关的配置 - Headless:
它不为整个服务分配任何集群 IP,而是通过分配的 DNS 域名来访问 Pod 服务。由于没有 Cluster IP,所以节点和集群外都无法直接访问 Service(但可以在节点直接访问 Pod IP)。无头 Service 主要提供给 StatefulSet(如数据库集群)使用。1
2
3
4
5
6
7
8
9
10
11
12apiVersion: v1
kind: Service
metadata:
name: my-headless-service
spec:
clusterIP: None # 设置为 None
selector:
app: my-app
ports:
- protocol: TCP
port: 80
targetPort: 80 - ExternalName:
- 不需要设置 selector 去选择为哪些 pod 实例提供服务,而是使用 DNS CNAME 机制把 svc 指向另外一个域名,这个域名可以是任何能够访问的虚拟地址(不能是 IP), 比如
mysql.db.svc
这样的建立在 db 命名空间内的 mysql 服务,也可以指定外部真实域名。1
2
3
4
5
6
7apiVersion: v1
kind: Service
metadata:
name: my-externalname-service
spec:
type: ExternalName
externalName: my.database.example.com # 外部域名
- ExternalIP
1
2
3
4
5
6
7
8apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
# ... 其他配置 ...
externalIPs:
- 192.168.1.100 # 外部 IP 地址
服务发现
- kube-DNS: Service 创建后,Kubernetes 会自动创建相应的 DNS 记录。Pod 可以通过 Service 名访问服务,例如
my-service.my-namespace.svc.cluster.local
。 - 环境变量: Kubernetes 会将 Service 信息注入到 Pod 的环境变量中。例如,对于名为
my-service
的 Service,会创建类似MY_SERVICE_SERVICE_HOST
和MY_SERVICE_SERVICE_PORT
的环境变量。
Ingress
解决问题:有几种Service类型可以对外暴露服务,但是需要占用节点端口,不支持域名以及SSL配置,还需要额外配置其他反向代理组件如Nginx
功能
- 路由规则:Ingress 允许你定义路由规则,使请求根据主机名和路径匹配路由到不同的后端服务。这使得可以在同一 IP 地址和端口上公开多个服务。
- Rewrite 规则:Ingress 支持 URL 重写,允许你在路由过程中修改请求的 URL 路径;
- TLS/SSL 支持:你可以为 Ingress 配置 TLS 证书,以加密传输到后端服务的流量;
- 负载均衡:Ingress 可以与云提供商的负载均衡器集成,以提供外部负载均衡和高可用性;
- 虚拟主机:你可以配置多个主机名(虚拟主机)来公开不同的服务。这意味着你可以在同一 IP 地址上托管多个域名;
- 自定义错误页面:你可以定义自定义错误页面,以提供用户友好的错误信息;
- 插件和控制器:社区提供了多个 Ingress 控制器,如 Nginx Ingress Controller 和 Traefik,它们为 Ingress 提供了更多功能和灵活性
Ingress 控制器
使用 Ingress 通常涉及两个组件:
- Ingress 清单 (Ingress Resource): Kubernetes 中的一种 API 对象,定义了流量路由规则。它描述了如何将外部请求映射到集群内的不同 Service。
- Ingress 控制器 (Ingress Controller): 一个在集群中运行的 Pod 或 Service,负责实现 Ingress 清单中定义的规则。它监听外部流量,并根据规则将其转发到正确的后端 Service。常见的 Ingress 控制器有 Nginx、Traefik 等。
流量链路: - 外部流量通过 公网DNS 解析到 IngressController Pod。
- Ingress 控制器根据 Ingress 规则将流量转发到后端 Service。
- 集群内部的 CoreDNS 解析 ServiceName 到 ClusterIP。
- kube-proxy 通过 iptables 规则将流量转发到 Pod 所在的 Node。
安装 Nginx Ingress 控制器
用 Helm 安装 Nginx Ingress 控制器:
- 下载安装文件:
1
2curl https://baltocdn.com/helm/signing.asc | gpg --dearmor | sudo tee /usr/share/keyrings/helm.gpg > /dev/null
helm version - 拉取镜像: 提前拉取镜像可以加快部署速度。
1
2
3
4
5
6# 添加 ingress-nginx 仓库
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
# 更新 Helm 仓库
helm repo update
# 创建 ingress-nginx 命名空间
kubectl create namespace ingress-nginx - 安装:
1
2helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx - 验证: 确保 Ingress 控制器 Pod 运行正常。
1
2
3
4
5
6# 查看 ingress-nginx 命名空间下的 Pod 状态
kubectl get pods -n ingress-nginx
# 示例输出
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-ddf5756b8-wm6vw 0/1 ContainerCreating 0 27s
Ingress 配置示例
以下是一个简单的 Ingress 配置示例:
1 | apiVersion: networking.k8s.io/v1 |
其他配置示例:
- 虚拟域名: 可以在
rules
中指定多个host
,实现不同域名到不同 Service 的路由。 - TLS/SSL 证书: 可以使用
tls
字段配置 HTTPS 证书。 - 路径重写: 可以使用 annotations 配置路径重写规则。
- 默认后端: 可以使用
defaultBackend
字段配置默认后端 Service。
Ingress 高可靠部署
为了提高 Ingress 的可用性,建议采用多节点部署,并可以考虑以下部署策略:
- Deployment + LoadBalancer: 使用 Deployment 部署 Ingress 控制器,并使用 LoadBalancer 类型的 Service 暴露服务。云提供商会自动创建负载均衡器并将流量分发到多个 Ingress 控制器 Pod。
- DaemonSet + HostNetwork + nodeSelector: 使用 DaemonSet 在每个指定的节点上运行一个 Ingress 控制器 Pod,并使用
hostNetwork
直接使用节点的网络和端口。这种方式可以提高性能,但每个节点只能运行一个 Pod。 - Deployment + NodePort: 使用 Deployment 部署 Ingress 控制器,并使用 NodePort 类型的 Service 暴露服务。每个节点都会打开指定的端口,外部流量可以通过
NodeIP:NodePort
访问服务。
Ingress 部署方案推荐
推荐使用 Deployment + LoadBalancer 的方式部署 Ingress,这种方式可以利用云提供商的负载均衡器实现高可用性和自动扩展。
Namespace
用来隔离集群内不同环境下的资源,仅同一 namespace 下的资源命名需要唯一,它的作用域仅针对带有名字空间的对象,例如 Deployment、Service 等。
创建多个 namespace:
1 | # namespaces.yaml |
ConfigMap 和 Secret
ConfigMap
K8s 使用 ConfigMap 来将你的配置数据和应用程序代码分开,它推荐我们将一般性的配置数据保存到 ConfigMap 资源清单中。不可配置大量数据
部署 ConfigMap 资源后,我们可以用四种方式使用它:
- 在容器命令和参数内
- 容器的环境变量(常见)
- 在只读卷里面添加一个文件,让应用来读取(常见)
- 编写代码在 Pod 中运行,使用 Kubernetes API 来读取 ConfigMap(不常见)
下面使用 ConfigMap 来保存应用Podhellok8s
的配置信息:
1 | # configmap-hellok8s.yaml |
Secret
用途:
- 存储敏感信息,如密码、Token、证书密钥等。
- 使用方式类似于 ConfigMap,但需注意特定要求。
关键点:
数据编码:
data
字段的值必须是 Base64 编码的字符串(创建时会进行解码检查)。- Pod 中获取到的 Secret 数据为明文。
模板语法:
- 使用
stringData
代替binaryData
,允许任何 UTF8 字符。 - 支持
type
字段,用于创建时验证资源合法性。
- 使用
Secret 类型:
类型 | 描述 |
---|---|
Opaque | 用户自定义的任意数据(默认) |
kubernetes.io/service-account-token | 服务账号令牌 |
kubernetes.io/dockercfg | ~/.dockercfg 文件的序列化形式 |
kubernetes.io/dockerconfigjson | ~/.docker/config.json 文件的序列化形式 |
kubernetes.io/basic-auth | 基本身份认证凭据 |
kubernetes.io/ssh-auth | SSH 身份认证凭据 |
kubernetes.io/tls | TLS 客户端或服务器端数据 |
bootstrap.kubernetes.io/token | 启动引导令牌数据 |
示例:
- TLS 类型:必须包含
tls.crt
和tls.key
字段,且创建后不可修改,只能删除重建。1
2
3
4
5
6
7
8
9apiVersion: v1
kind: Secret
metadata:
name: secret-tls
namespace: default
type: kubernetes.io/tls
data:
tls.crt: <base64-encoded-cert>
tls.key: <base64-encoded-key>
引用 Secret:
- 设置可选 Key:
1
2
3
4
5
6
7env:
- name: LOG_LEVEL
valueFrom:
secretKeyRef:
name: hellok8s-secret
key: not_found_key
optional: true- 如果指定的 Key 不存在,不会影响 Pod 启动(但 Secret 对象必须存在)。
拉取私有镜像使用 Secret:
- **配置
imagePullSecrets
**:- 直接在 Pod 上指定:
1
2
3spec:
imagePullSecrets:
- name: myregistrykey - 向 ServiceAccount 添加(推荐):
1
2
3
4
5
6apiVersion: v1
kind: ServiceAccount
metadata:
name: my-service-account
imagePullSecrets:
- name: myregistrykey
- 直接在 Pod 上指定:
- 所有使用该 ServiceAccount 的 Pod 会自动使用对应的 Secret。
创建 Secret: - 通过文件或字面量:
1
2
3
4
5
6
7
8
9使用字面量
kubectl create secret generic db-user-pass \
--from-literal=username=admin \
--from-literal=password='S!B*d$zDsb='
使用文件
kubectl create secret generic db-user-pass \
--from-file=./username.txt \
--from-file=./password.txt - 查看 Secret:
1
2
3kubectl describe secret db-user-pass
kubectl get secret db-user-pass -o jsonpath='{.data}'
echo <base64-encoded-password> | base64 --decode - 删除 Secret:
1
kubectl delete secret db-user-pass
Downward API
用途:
- 允许容器在不依赖 Kubernetes 客户端或 API 服务器的情况下,获取自身或集群的信息。
实现方式:
- 环境变量
- Downward API 卷中的文件
可用字段:
- **通过
fieldRef
**:metadata.name
:Pod 名称metadata.namespace
:Pod 命名空间metadata.uid
:Pod 唯一 IDmetadata.annotations['key']
:指定注解值metadata.labels['key']
:指定标签值
- 仅通过环境变量:
spec.serviceAccountName
:服务账号名称spec.nodeName
:节点名称status.hostIP
:节点主 IPstatus.hostIPs
:节点 IP 列表status.podIP
:Pod 主 IP
- **仅通过
resourceFieldRef
**:limits.cpu
、requests.cpu
:CPU 限制与请求limits.memory
、requests.memory
:内存限制与请求- 其他资源限制如
hugepages-*
、ephemeral-storage
示例:
- Pod 使用 Downward API:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37apiVersion: v1
kind: Pod
metadata:
name: busybox-use-downwardapi
spec:
containers:
- name: busybox
image: busybox
command: ["sh", "-c", "echo hellok8s, downwardAPI! PodName=$(POD_NAME) LIMITS_CPU=$(LIMITS_CPU) POD_IP=$(POD_IP) && sleep 3600"]
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: LIMITS_CPU
valueFrom:
resourceFieldRef:
containerName: busybox
resource: limits.cpu
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
volumeMounts:
- name: config
mountPath: /config/downward_api_info
readOnly: true
volumes:
- name: config
downwardAPI:
items:
- path: "POD_NAME"
fieldRef:
fieldPath: metadata.name
- path: "LABELS"
fieldRef:
fieldPath: metadata.labels - 测试结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18kubectl apply -f pod_use_downwardAPI.yaml
pod/busybox-use-downwardapi created
kubectl get pod
NAME READY STATUS RESTARTS AGE
busybox-use-downwardapi 1/1 Running 0 59s
kubectl logs busybox-use-downwardapi
hellok8s, downwardAPI! PodName=busybox-use-downwardapi LIMITS_CPU=1 POD_IP=20.2.36.86
kubectl exec -it busybox-use-downwardapi -- sh
/ # ls /config/downward_api_info/
LABELS POD_NAME
/ # cat /config/downward_api_info/LABELS
app="busybox"
label_test="some_value"
/ # cat /config/downward_api_info/POD_NAME
busybox-use-downwardapi - Secret:
- 用于存储和管理敏感数据。
- 支持多种类型,确保数据合法性。
- 可通过
stringData
简化配置。 - 支持
optional
引用,提升灵活性。 - 适用于配置私有镜像拉取凭证。
- Downward API:
- 提供 Pod 自身及集群信息给容器。
- 通过环境变量或文件卷实现。
- 支持多种字段,增强 Pod 配置的动态性。