k8s 基础命令
文章目录
前言
命令补全
kubectl 作为 Kubernetes 的命令行工具(CLI),是 Kubernetes 用户日常使用和管理员日常管理必须掌握的工具。
kubectl 提供了大量的子命令,用于 Kubernetes 集群的管理和各种功能的实现。
kubectl 提供了如下帮助命令:
kubectl -h
查看子命令列表kubectl options
查看全局选项kubectl <command> --help
查看子命令的帮助kubectl [command] [PARAMS] -o=<format>
设置输出格式(如 json、yaml、jsonpath 等)kubectl explain [RESOURCE]
查看资源的定义
但以上方法虽然详细,但不够快捷。
kubectl 命令自动补全
的配置方法,可以帮助你更加快速地获取自己想要执行命令。具体方法如下:
安装 bash-completion:
apt install -y bash-completion
执行 source 命令:
source /usr/share/bash-completion/bash_completion
如果想让系统中的所有用户都能拥有命令补全的功能,则执行如下命令:
kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null
如果只需要当前用户拥有命令自动补全功能,则执行如下命令:
echo 'source <(kubectl completion bash)' >> ~/.bashrc
source ~/.bashrc
验证自动补全的效果(双击 Tab 键
)
k8s 是用来解决什么问题的
Kubernetes (K8s) 是一个开源的容器编排平台,主要用于自动化部署、扩展和管理容器化应用程序。它解决了现代应用程序开发和运维中的以下几个主要问题:
1. 容器管理和编排
- 问题: 在现代微服务架构中,应用程序通常分解为多个容器服务。手动管理这些容器,尤其是在大规模集群中,变得非常复杂。
- Kubernetes 的解决方案: Kubernetes 提供了自动化的容器管理功能,可以自动处理容器的部署、扩展、负载均衡和故障恢复。K8s 可以根据需求自动启动、停止和重新分配容器,确保系统的高可用性和可扩展性。
2. 应用的自动化部署
- 问题: 传统的应用程序部署过程繁琐,且容易出现人为错误。需要考虑不同的环境(如开发、测试、生产环境)的配置差异。
- Kubernetes 的解决方案: Kubernetes 允许使用声明式配置文件(如 YAML 或 JSON 文件)定义应用程序的部署规范。通过这些配置文件,Kubernetes 可以确保应用程序以一致的方式在不同的环境中部署。
3. 弹性扩展和资源管理
- 问题: 应用程序的负载可能随时变化,传统的静态资源分配无法有效利用计算资源,导致浪费或资源不足的问题。
- Kubernetes 的解决方案: Kubernetes 允许根据流量负载自动扩展或缩减容器实例数量(水平扩展),并且可以根据资源使用情况动态调整容器的资源分配(垂直扩展),从而优化资源使用效率。
4. 服务发现与负载均衡
- 问题: 在微服务架构中,不同服务之间的通信需要自动发现和负载均衡,否则会造成服务之间的连接问题。
- Kubernetes 的解决方案: Kubernetes 内置服务发现机制和负载均衡器,可以自动分配 IP 地址和 DNS 名称,确保容器间的通信畅通无阻,并实现流量的均衡分配。
5. 持续交付与持续集成(CI/CD)
- 问题: 软件开发生命周期中的代码更新频繁,需要自动化的构建、测试、部署流程,以缩短发布周期并减少出错概率。
- Kubernetes 的解决方案: Kubernetes 与 CI/CD 工具集成良好,可以实现从代码提交到生产部署的自动化流水线。开发者可以快速迭代并安全地发布新版本应用。
6. 高可用性和自我修复
- 问题: 系统中的服务故障可能会导致应用程序不可用,手动恢复服务需要时间且可能不及时。
- Kubernetes 的解决方案: Kubernetes 提供自我修复机制,可以自动监控容器的健康状况。如果容器出现故障,Kubernetes 会自动重启或替换容器,确保应用的高可用性。
7. 跨平台和多云支持
- 问题: 企业通常需要在不同的基础设施(如本地数据中心、云环境)中运行应用程序,确保应用的一致性和可移植性是个挑战。
- Kubernetes 的解决方案: Kubernetes 是跨平台的,支持在不同的云提供商和本地环境中运行,确保应用程序在任何环境下都能保持一致的行为。
k8s 组件
控制平面组件(Control Plane Components)
控制平面组件会为集群做出全局决策,比如资源的调度。 以及检测和响应集群事件,例如当不满足部署的 replicas
字段时,要启动新的 Pod)。
控制平面组件可以在集群中的任何节点上运行。 然而,为了简单起见,设置脚本通常会在同一个计算机上启动所有控制平面组件, 并且不会在此计算机上运行用户容器。 请参阅 使用 kubeadm 构建高可用性集群 中关于跨多机器控制平面设置的示例。
- kube-apiserver
API 服务器是 Kubernetes 控制平面 的组件, 该组件负责公开了 Kubernetes API,负责处理接受请求的工作。 API 服务器是 Kubernetes 控制平面的前端。
Kubernetes API 服务器的主要实现是 kube-apiserver。 kube-apiserver
设计上考虑了水平扩缩,也就是说,它可通过部署多个实例来进行扩缩。 你可以运行 kube-apiserver
的多个实例,并在这些实例之间平衡流量。
- etcd
一致且高可用的键值存储,用作 Kubernetes 所有集群数据的后台数据库。
如果你的 Kubernetes 集群使用 etcd 作为其后台数据库, 请确保你针对这些数据有一份 备份 计划。
你可以在官方 文档 中找到有关 etcd 的深入知识。
- kube-scheduler
kube-scheduler
是 控制平面 的组件, 负责监视新创建的、未指定运行 节点(node) 的 Pods, 并选择节点来让 Pod 在上面运行。
调度决策考虑的因素包括单个 Pod 及 Pods 集合的资源需求、软硬件及策略约束、 亲和性及反亲和性规范、数据位置、工作负载间的干扰及最后时限。
- kube-controller-manager
kube-controller-manager 是 控制平面 的组件, 负责运行 控制器 进程。
从逻辑上讲, 每个 控制器 都是一个单独的进程, 但是为了降低复杂性,它们都被编译到同一个可执行文件,并在同一个进程中运行。
有许多不同类型的控制器。以下是一些例子:
节点控制器(Node Controller):负责在节点出现故障时进行通知和响应
任务控制器(Job Controller):监测代表一次性任务的 Job 对象,然后创建 Pod 来运行这些任务直至完成
端点分片控制器(EndpointSlice controller):填充端点分片(EndpointSlice)对象(以提供 Service 和 Pod 之间的链接)。
服务账号控制器(ServiceAccount controller):为新的命名空间创建默认的服务账号(ServiceAccount)。
以上并不是一个详尽的列表。
- cloud-controller-manager
一个 Kubernetes 控制平面 组件, 嵌入了特定于云平台的控制逻辑。 云控制器管理器(Cloud Controller Manager)允许将你的集群连接到云提供商的 API 之上, 并将与该云平台交互的组件同与你的集群交互的组件分离开来。
cloud-controller-manager
仅运行特定于云平台的控制器。 因此如果你在自己的环境中运行 Kubernetes,或者在本地计算机中运行学习环境, 所部署的集群不需要有云控制器管理器。
与 kube-controller-manager
类似,cloud-controller-manager
将若干逻辑上独立的控制回路组合到同一个可执行文件中, 供你以同一进程的方式运行。 你可以对其执行水平扩容(运行不止一个副本)以提升性能或者增强容错能力。
下面的控制器都包含对云平台驱动的依赖:
节点控制器(Node Controller):用于在节点终止响应后检查云提供商以确定节点是否已被删除
路由控制器(Route Controller):用于在底层云基础架构中设置路由
服务控制器(Service Controller):用于创建、更新和删除云提供商负载均衡器
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 (容器运行环境接口) 的其他任何实现。
零、Node
Node 管理
禁止 Pod 调度到该节点上。
kubectl cordon <node>
驱逐该节点上的所有 Pod。
kubectl drain <node>
一、Pod 容器集
Pod:是包含一个或多个容器的容器组,是 Kubernetes 中创建和管理的最小对象。
Pod 有以下特点:
-
Pod 是 Kubernetes 中
最小的调度单位
(原子单元),Kubernetes 直接管理 Pod 而不是容器。 -
同一个 Pod 中的容器总是会被自动安排到集群中的
同一节点
(物理机或虚拟机)上,并且一起调度
-
Pod 可以理解为运行特定应用的 “逻辑主机”,这些容器共享存储、网络和配置声明(如资源限制)
-
每个 Pod 有唯一的 IP 地址。
IP地址分配给Pod
,在同一个 Pod 内,所有容器共享一个 IP 地址和端口空间,Pod 内的容器可以使用 localhost 互相通信。 -
说到 localhost 顺便拓展一下之前没有注意过的 localhost 和 127.0.0.1 的小细节
localhost 127.0.0.1 在计算机网络中,localhost 是回路网络接口的一个标准主机名,相对应的 ip 地址是 127.0.0.1 127.0.0.1 是一个 环回地址,根本 不是一个网络地址 - localhost 是一个保留域名,同时也是一个特殊的 DNS 主机名,代表分配给引用这个名称的计算机的 ip 地址,即所在计算机本身。 - localhost 是 不经过网卡传输 的,它 不受 网络防火墙和网卡相关的限制。 127.0.0.1 是通过 网卡传输的,依赖网卡,并 受到网络防火墙和网卡相关的限制。 在 host 文件中,localhost 指向的 ip 是 127.0.0.1, 可通过编辑 host 文件改变指向 一个重要应用 方法: ping 127.0.0.1 功能:检测本机的回路是否正常,如果得到一个成功的 ping 返回,则可以认定你的 IP 栈是被初始化过的,TCP/IP 协议安装正确。如果失败,那么你的 IP 栈失败,TCP/IP 协议必须重新安装
pod 的创建流程
- 第一步通过
apiserver REST API
创建一个Pod
- 然后
apiserver
接收到数据后将数据写入到etcd
中 - 由于
kube-scheduler
通过apiserver watch API
一直在监听资源的变化,这个时候发现有一个新的Pod
,但是这个时候该Pod
还没和任何Node
节点进行绑定,所以kube-scheduler
就经过一系列复杂的调度策略,选择出一个合适的Node
节点,将该Pod
和该目标Node
进行绑定,当然也会更新到etcd
中去的 - 这个时候一样的目标
Node
节点上的kubelet
通过apiserver watch API
检测到有一个新的Pod
被调度过来了,他就将该Pod
的相关数据传递给后面的容器运行时(container runtime
),比如Docker
,让他们去运行该Pod
- 而且
kubelet
还会通过container runtime
获取Pod
的状态,然后更新到apiserver
中,当然最后也是写入到etcd
中去的。
创建 Pod
kubectl run mynginx --image=nginx:1.22
查看 Pod
kubectl get pod
kubectl get pod -o wide # 查看详细信息
查看 Pod 运行日志
kubectl logs -f mynginx
查看 Pod 信息
kubectl describe pod mynginx
kubectl describe pod/mynginx
两种语法都可,但是官方更推荐第二种
映射端口
kubectl port-forward pod/mynginx 4000:80
确保虚拟机使用的是桥接网络(Bridged Network),而不是NAT模式。桥接网络模式允许虚拟机和物理机在同一局域网中相互访问。你可以在虚拟机管理软件(如VirtualBox或VMware)的网络设置中修改网络模式。
如果适配器使用的是NAT模式,则需要使用下面的命令
kubectl port-forward --address 0.0.0.0 pod/mynginx 4000:80
将 nginx
默认的 80
端口映射到本机的 4000
端口,打开浏览器或者 curl
来访问 http://127.0.0.1:4000
, 查看是否成功访问 nginx
默认页面!
进入 Pod 内部
kubectl exec -it mynginx -- /bin/bash
退出容器内部
使用 exit
或者使用 Ctrl+D 组合键
创建一个一次性 Pod
# 添加-it --rm意思是在退出pod的时候删除这个pod
kubectl run my-busybox --image=busybox -it --rm
删除一个 Pod
kubectl delete pod mynginx
查看所有 namespace 下的 pod
kubectl get pod --all-namespaces
或简写
kubectl get pod -A
注意:有三个常用的帮助命令:
1.
kubectl api-versions
: 查看所有 apiversion2.
kubectl explain pod/deploy
: 编写 yaml 文件时的说明文档3.
kubectl expose --help
: 可以查看帮助信息
二、Deployment(部署)和 ReplicaSet(副本集)
Deployment:是对 ReplicaSet 和 Pod 更高级的抽象。它是 Pod 拥有多副本,自愈,扩缩容,滚动升级等能力
ReplicaSet(副本集):是一个 Pod 的集合。它可以设置运行 Pod 的数量,确保任何时间都有指定的 Pod 副本在运行。
通常我们不直接使用 ReplicaSet,而是在 Deployment 中声明
创建拥有三个副本集的 Deploy
kubectl create deployment nginx-deploy --image=nginx:1.22 --replicas=3
创建之后查看
kubectl get deploy
注:deployment 可以简写成 deploy,replicaSet 可以简写成 rs
deployment 不是直接部署 Pod 的,而是通过 replicaSet 来控制 Pod 数量的
Pod 自愈
-
我删除 nginx-deploy-7455bbdd-trp8l 之后,k8s 又自动创建了一个 nginx-deploy-7455bbdd-5hk2j
-
这是因为副本集在维护指定数量的 Pod,他会一直保持在数量 3 上
Pod 缩放
-
手动扩容
先熟悉一个命令
# 观察replicaSet的运行状态 kubectl get replicaSet --watch
我们将副本集数量调整为 5
kubectl scale deploy nginx-deploy --replicas=5
-
手动缩容
我们将副本集数量调整回 3
kubectl scale deploy nginx-deploy --replicas=3
滚动更新
-
先查看 deploy 的版本
kubectl get deploy -o wide
可以看到 images 的版本是 1.22
-
更新镜像
kubectl set image deploy/nginx-deploy nginx=nginx:1.23
注: 第一个 nginx 是容器名,也就是 CONTAINERS 的值
-
观察运行过程
版本回滚
-
查看历史版本
kubectl rollout history deploy/nginx-deploy --revision=1 # --revision=查看具体版本号的详情
-
回滚版本
kubectl rollout undo deploy/nginx-deploy --to-revision=1 回滚到上一个版本可以不用增加`--to-revision`参数, 默认就是回滚上一个版本
暂停版本回滚
kuebctl rollout pause deploy/nginx-deploy
查看更新状态
kubectl rollout status deploy/nginx-deploy
三、Service 服务
Service:是将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法
Service 为一组 Pod 提供相同的 DNS 名,并且在他们之间进行负载均衡
Kubernetes 为 Pod 提供分配了 IP 地址,但 IP 地址可能会发生变化
集群内的容器可以通过 service 名称访问服务,而不需要担心 Pod 的 IP 发生变化
# 这个命令只能在容器内部相互访问,并不能在宿主机访问
kubectl expose deploy/nginx-deploy --name=nginx-server --port 8080 --target-port=80
–port 是公开的服务端口
–target-port 是容器端口
ServiceType 取值
- NodePort vs. ClusterIP
NodePort
功能:
外部访问:NodePort暴露一个端口(范围30000-32767)在每个节点的INTERNAL-IP上,使得外部客户端可以通过节点的IP地址和这个端口访问服务。
内部访问:集群内部的Pod也可以通过节点的INTERNAL-IP和NodePort访问服务。
使用场景:
适用于需要从外部网络访问Kubernetes服务的场景。
可用于调试和特定的内部通信需求。
ClusterIP
功能:
内部访问:ClusterIP服务在集群内部创建一个虚拟IP地址,只能从集群内部访问,不能直接从外部访问。
DNS支持:服务可以通过Kubernetes内置的DNS系统,通过服务名称进行访问,简化了服务间的通信。
使用场景:
适用于集群内部的服务间通信。
便于管理和负载均衡。
- 使用INTERNAL-IP和NodePort vs. ClusterIP
NodePort的灵活性:
外部和内部访问:NodePort服务可以通过节点的INTERNAL-IP和特定的端口进行访问,适用于需要同时支持外部和内部访问的场景。
高可用性:因为NodePort在每个节点上都开放端口,所以即使某个节点不可用,服务依然可以通过其他节点的IP和端口进行访问。
手动管理:NodePort要求你手动管理端口号,这在规模较大的集群中可能变得复杂。
ClusterIP的便捷性:
内部服务发现:ClusterIP通过Kubernetes DNS系统,使得服务可以通过服务名称进行访问,简化了服务发现和负载均衡。
自动负载均衡:ClusterIP服务会自动将流量分发到后端的Pod,无需手动管理。
安全性:ClusterIP仅在集群内部可访问,减少了暴露在外部网络的风险。
推荐实践
内部服务通信:使用ClusterIP服务,借助Kubernetes的DNS系统进行服务发现和负载均衡,简化管理和提高安全性。
外部访问:使用NodePort服务,或者在需要更高层级管理和负载均衡时使用LoadBalancer服务(前提是集群环境支持),这会在外部负载均衡器上分配一个固定IP地址并自动配置端口映射。
特定需求的内部通信:在一些特定的调试或配置场景下,可以使用INTERNAL-IP和NodePort,但这不是常见的做法。
结论
INTERNAL-IP和NodePort可以提供更灵活的访问方式,满足内部和外部的访问需求,但它们需要手动管理和配置,适合特定场景。ClusterIP通过简化服务发现和自动负载均衡,更适合集群内部的服务通信。根据实际需求选择合适的服务类型,可以更好地利用Kubernetes的网络功能。
当你创建一个 NodePort 类型的 Service 时,Kubernetes 会在每个节点(虚拟机)上打开一个特定的端口(范围在 30000-32767),并将其映射到 Pod 的端口。这意味着你可以通过任意节点的 IP 和 NodePort 来访问该 Service,无论 Pod 运行在哪个节点上。
当你尝试从物理机访问时,你需要使用虚拟机的 IP 和 NodePort 进行访问,因为 NodePort 是 Kubernetes 节点对外开放的端口。例如:
- 如果虚拟机的 IP 地址是
192.168.1.100
,你应该使用http://192.168.1.100:31452
进行访问。
这是因为 NodePort 类型的 Service 会在每个节点的指定端口(如 31452
)上暴露该 Service,而 8081
是集群内部的端口,只能在 Kubernetes 内部通过 ClusterIP 访问。
- 为什么
8081
无法访问
8081
是 Service 内部的端口,它是通过 ClusterIP 暴露的,仅在 Kubernetes 集群内部可见。如果你尝试从集群外部(如物理机)直接访问 8081
,连接会被拒绝,因为该端口未在集群节点的网络接口上开放。
# 下面这个命令在容器内部和宿主机都可以进行访问
kubectl expose deploy/nginx-deploy --name=nginx-outside --type=NodePort --port=8081 --target-port=80
四、命名空间(Namespace)
命名空间(Namespace) 是一种资源隔离机制,将同一集群中的资源划分为相互隔离的组。
命名空间可以在多个用户之间划分集群资源(通过资源配额)
- 例如我们可以设置
dev
,test
,prod
等多个命名空间
同一命名空间作用域仅针对带有名字空间的对象,例如:Deployment,Service 等
这种作用域对集群访问的对象不适用,例如 StorageClass, Node, PersistentVolume 等。
创建命名空间
kubectl create ns develop
给 Pod 指定命名空间
kubectl run nginx --image=nginx:1.22 -n develop
查看指定命名空间下的 Pod
kubectl get pod -n develop
如果不指定命名空间,我们查看的 Pod 都默认是 default 下的 Pod
kubectl config set-context $(kubectl config current-context) --namespace=develop
删除命名空间
kubectl delete ns develop
删除命名空间之后会清空命名空间下的所有内容
但是如果有占用等错误情况,需要手动处理之后,命名空间才能被删除
资源限额
kubectl config set-context --current –namespace=default
namespace 是命名空间,里面有很多资源,那么我们可以对命名空间资源做个限制,防止该命名空间 部署的资源超过限制。 如何对 namespace 资源做限额呢?
# vim namespace-quota.yaml
apiVersion: v1
kind: ResourceQuota
metadata:
name: mem-cpu-quota
namespace: test
spec:
hard:
requests.cpu: "2"
requests.memory: 2Gi
limits.cpu: "4"
limits.memory: 4Gi
#创建的 ResourceQuota 对象将在 test 名字空间中添加以下限制:
每个容器必须设置内存请求(memory request),内存限额(memory limit),cpu 请求(cpu request)和 cpu 限额(cpu limit)。
所有容器的内存请求总额不得超过 2GiB。 所有容器的内存限额总额不得超过 4 GiB。 所有容器的 CPU 请求总额不得超过 2 CPU。 所有容器的 CPU 限额总额不得超过 4CPU。
五、YAML 语法
课外拓展:
云原生的代表技术包括:
- 容器
- 服务网格
- 微服务
- 不可变基础设施
声明式API
管理对象
-
命令行指令
例如,使用
kubectl
命令来创建和管理 Kubernetes 对象命令行就好比口头传达,简单,快速,高效
但它功能有限,不适合复杂场景,操作不容易追溯,多用于开发和调试
-
声明式配置
Kubernetes 使用 yaml 文件来描述 Kubernetes 对象
声明式配置就好比申请表,学习难度大且配置麻烦
好处是操作留痕,适合操作复杂的对象,多用于生产
常用命令缩写
名称 | 缩写 | Kind |
---|---|---|
namespace | ns | Namespace |
nodes | no | Node |
pods | po | Pod |
services | svc | Service |
deployments | deploy | Deployment |
replicasets | rs | ReplicaSet |
statefulsets | sts | StatefulSet |
YAML 规范
- 缩进代表上下级关系
- 缩进时不允许使用 Tab 键,只允许使用空格,通常缩进 2 个空格
:
键值对,后面必须有空格-
列表,后面必须有空格[]
数组#
注释|
多行文本块---
表示文档的开始,多用于分割多个资源对象
生成 yaml 文件
kubectl run 名字 --image=镜像 --dry-run=client -o yaml > pod.yaml
如:
kubectl run pod1 --image=nginx --dry-run=client -o yaml > pod1.yaml
--dry-run=client
:模拟创建 pod,但不会真的创建
-o yaml > pod.yaml
:以 yaml 文件的格式输出,然后把结果重定向到 pod.yaml
里
配置对象
在创建的 Kubernetes 对象所对应的 yaml
文件中,需要配置的字段如下:
apiVersion
- Kubernetes API 的版本kind
- 对象类别,例如Pod
,Deployment
,Service
,ReplicaSet
等metadata
- 描述对象的元数据,包括一个 name 字符串,UID 和可选的 namespacespec
- 对象的配置
Pod 的 yaml 实例
# 文件名:my-pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: nginx
spec:
containers:
- name: nginx
image: nginx:1.22
ports:
- containerPort: 80
运行 yaml 文件创建 Pod:
kubectl apply -f my-pod.yaml
运行 yaml 文件删除 Pod:
kubectl delete -f my-pod.yaml
标签
标签(Labels) 是附加到对象(比如 Pod)上的键值对,用于补充对象的描述信息。
标签使用户能够以松散的方式管理对象映射,而无需客户端存储这些映射。
由于一个集群中可能管理成千上万个容器,我们可以使用标签高效的进行选择和操作容器集合。
-
键的格式:
前缀(可选)/名称(必须)
-
有效名称和值
必须为 63 个字符或更少(可以为空)
如果不为空,必须以字母数字字符([a-z0-9A-Z])开头和结尾
包含破折号
-
,下划线_
, 点.
和字母或数字
实例:
# 文件名:pod-label.yaml
apiVersion: v1
kind: Pod
metadata:
name: label-demo
labels:
environment: production
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.22
ports:
- containerPort: 80
创建 Pod:kubectl apply -f pod-label.yaml
查看 Pod 创建:kubectl get pod
查看详细 Lables:kubectl get pod --show-labels
, 运行结果:
可以根据 labels 进行筛选查看:kubectl get pod -l "app=nginx"
如果有多个条件,可以使用逗号分隔,他们关系是与的关系:kubectl get pod -l "app=nginx,environment=production"
选择器
标签选择器 可以识别一组对象。标签不支持唯一性。
标签选择器最常见的用法是为 Service 选择一组 Pod 作为后端。
实例:
# 文件名:my-service.yaml
apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
type: NodePort
selector:
app: nginx
ports:
# 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
- port: 80
targetPort: 80
# 可选字段
# 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号
#(默认:30000-32767)
nodePort: 30007
创建 svc: kubectl apply -f my-service.yaml
查看创建的 svc: kubectl get svc
查看 svc 详情: kubectl describe svc/my-service
六、金丝雀部署(灰度发布)
金丝雀部署(canary deployment)也被称为灰度发布
早期,工人下矿井之前会放入一只金丝雀检验井下是否存在有毒气体。
采用金丝雀部署,你可以在生产环境的基础建设中小范围的部署新的应用代码。
一旦应用签署发布,只有少数用户被路由到它,最大限度的降低影响。
如果没有错误发生,则将新版本逐渐推广到整个基础设施。
部署过程
部署第一个版本
发布 v1 版本的应用,镜像使用 nginx:1.22
, 数量为 3。
-
创建 deploy-v1.yaml 文件
-
创建 Namespace
apiVersion: v1
kind: Namespace
metadata:
name: dev
labels:
name: dev
- 创建 Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment-v1
namespace: dev
labels:
app: nginx-deployment-v1
spec:
replicas: 3
selector:
matchLabels: # 需要跟template.metadata.labels一致
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.22
ports:
- containerPort: 80
- 创建外部访问的 Service
apiVersion: v1
kind: Service
metadata:
name: canary-demo
namespace: dev
spec:
type: NodePort
selector:
app: nginx
ports:
# 默认情况下,为了方便起见,`targetPort` 被设置为与 `port` 字段相同的值。
- port: 80
targetPort: 80
# 可选字段
# 默认情况下,为了方便起见,Kubernetes 控制平面会从某个范围内分配一个端口号
#(默认:30000-32767)
nodePort: 30008
-
运行 yaml 文件
kubectl apply -f deploy-v1.yaml
-
查看 dev 命名空间下的所有内容
kubectl get all -n dev
创建 Canary Deployment
发布新版本的应用,镜像使用 docker/getting-started, 数量为 1。
-
创建 deploy-canary.yaml 文件
-
编写 yaml 文件
apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment-canary namespace: dev labels: app: nginx-deployment-canary spec: replicas: 1 selector: matchLabels: # 需要跟template.metadata.labels一致 app: nginx template: metadata: labels: app: nginx track: canary spec: containers: - name: new-nginx image: docker/getting-started ports: - containerPort: 80
-
创建 Pod
kubectl apply -f deploy-canary.yaml
-
查看加入新 Pod 信息
kubectl get all -n dev
再次查看 service 详情,发现增加了一个 more,表示新创建的 Pod 加入到了 service 中
-
多访问几次 10.105.71.245,查看效果
当访问到这个,就证明成功访问到了新 Pod 里面!
扩大使用范围
-
将新应用数量扩展为 3
kubectl scale deploy nginx-deployment-canary --replicas=3 -n dev
-
将
deployment-v1
的数量调整为 0kubectl scale deploy nginx-deployment-v1 --replicas=0 -n dev
清空命名空间环境
kubectl delete all --all -n=dev
局限性
按照 Kubernetes 默认支持的这种方式进行金丝雀发布,有一定的局限性:
- 不能根据用户注册时间、地区等请求中的内容属性进行流量分配
- 同一个用户如果多次请求该 Service,有可能第一次请求到了旧版本的 Pod,第二次请求到了新版本的 Pod
在 Kubernetes 中不能解决上述局限性的原因是:Kubernetes Service 只在 TCP 层面解决了负载均衡的问题,并不对请求响应的消息内容做任何解析和识别,如果想要更完善地实现金丝雀发布,可以考虑 Istio 灰度发布
七、运行有状态应用
以 MySQL 数据库为例,在 Kubernetes 集群中运行一个有状态的应用。
部署数据库几乎覆盖了 Kubernetes 中常见的对象和概念:
- 配置文件–ConfigMap
- 保存密码–Secret
- 数据存储–持久卷(PV)和持久卷声明(PVC)
- 动态创建卷–存储类(StorageClass)
- 部署多个实例–StatefulSet
- 数据库访问–Headless Service
- 主从复制–初始化容器和 sidecar
- 数据库调试–port-forward
- 部署 Mysql 集群–helm
创建 Mysql 数据库
-
创建一个 mysql-pod.yaml 的文件
-
配置环境变量
使用 MySQL 镜像创建 Pod,需要使用环境变量设置 MySQL 的初始密码。
环境变量配置实例:
apiVersion: v1 kind: Pod metadata: name: mysql-pod labels: app: mysql spec: containers: - name: mysql image: mysql:5.7 env: - name: MYSQL_ROOT_PASSWORD value: "123456" ports: - containerPort: 3306 volumeMounts: - mountPath: /var/lib/mysql name: data-volume volumes: - name: data-volume hostPath: # directory location on host path: /home/mysql/data # this field is optional type: DirectoryOrCreate
-
挂载卷
将数据存储在容器中,随着容器使用过程中数据变得越来越多,不方便数据的迁移和恢复。
一旦容器被删除,数据也会被删除。将数据存储到 卷(Volume) 中,删除容器时,卷不会被删除。
hostPath 卷
hostPath 卷将主机节点上的文件或目录挂载到 Pod 中。
hostPath 的 type 值:
DirectoryOrCreate 目录不存在则会创建 Directory 挂载已存在目录,不存在会报错。 FileOrCreate 文件不存在则自动创建。不会自动创建文件的父目录,必须确保文件路径已经存在。 File 挂载已存在的文件。不存在会报错 Socket 挂载 UNIX 套接字。例如挂载/var/run/docker.sock 进程 -
创建 Pod:
kubectl apply -f mysql-pod.yaml
-
查看 Pod 信息:
kubectl get pod -o wide
, 发现运行在 node2 节点,查看后发现 node2 节点/home 目录下自动创建了 mysql 文件夹
ConfigMap
在 Docker 中,我们一般通过绑定挂载的方式将配置文件挂载到容器里。
在 Kubernetes 集群中,容器可能被调度到任意节点,配置文件需要能在集群任意节点上访问,分发和更新。
ConfigMap
- 用来在键值对数据库(etcd)中保存非加密数据。一般用来保存配置文件。
- 可以用作环境变量、命令行参数或者存储卷。
- 将环境配置信息与容器镜像解耦,便于配置的修改。
- 在设计上不是用来保存大量数据的。
- 在 ConfigMap 中保存的无数据不可超过 1MiB。
- 超出此限制,需要考虑挂载存储卷或者访问文件存储服务。
实例:
apiVersion: v1
kind: ConfigMap
metadata:
name: game-demo
data:
# 类属性键;每一个键都映射到一个简单的值
player_initial_lives: "3"
ui_properties_file_name: "user-interface.properties"
# 类文件键
game.properties: |
enemy.types=aliens,monsters
player.maximum-lives=5
user-interface.properties: |
color.good=purple
color.bad=yellow
allow.textmode=true
完整的 yaml 文件:
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
- mountPath: /etc/mysql/conf.d
name: conf-volume
readOnly: true
volumes:
- name: conf-volume
configMap:
name: mysql-config
- name: data-volume
hostPath:
# directory location on host
path: /home/mysql/data
# this field is optional
type: DirectoryOrCreate
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
# 创建自己的配置文件
mysql.cnf: |
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
删除之前的 Pod:kubectl delete pod/mysql-pod
重新创建 Pod:kubectl apply -f mysql-pod.yaml
查看 configMap:kubectl describe cm mysql-config
注:configMap 可以缩写为 cm
进入 mysql-pod 内部:kubectl exec -it mysql-pod -- bash
进入 mysql:mysql -u root -p 123456
查看 mysql 字符集:show variables like '%char%';
编辑 Pod 配置:kubectl edit cm mysql-config
, 添加一行注释,然后进入容器查看/etc/mysql/conf.d/mysql.cnf 文件, 发现文件已经被自动更新了。
Secret
Secret 用于保存机密数据的对象。一般由于保存密码、令牌或密钥等。
data
字段用来存储 base64 编码数据
stringData
存储未编码的字符串
Secret 意味着你不需要在应用程序代码中包含机密数据,减少机密数据(如密码)泄露的风险。
Secret 可以用作环境变量、命令行参数或者存储卷文件。
实例:
apiVersion: v1
kind: Secret
metadata:
name: mysql-password
type: Opaque
data:
PASSWORD: MTIzNDU2Cg==
完整代码:
apiVersion: v1
kind: Secret
metadata:
name: mysql-password
type: Opaque
data:
PASSWORD: MTIzNDU2Cg==
---
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-password
key: PASSWORD
optional: false #此值为默认值
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
- mountPath: /etc/mysql/conf.d
name: conf-volume
readOnly: true
volumes:
- name: conf-volume
configMap:
name: mysql-config
- name: data-volume
hostPath:
# directory location on host
path: /home/mysql/data
# this field is optional
type: DirectoryOrCreate
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
# 创建自己的配置文件
mysql.cnf: |
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
重新创建,能够发现创建的时候多创建了一个 secret
查看 secret:kubectl describe secret/mysql-password
能够看到密码不显示
因为 secret 在环境变量中使用,如果修改 secret 的话,并不能实现自动更新,必须要重启 Pod
卷(Volume)
将数据存储在容器中,一旦容器被删除,数据也会被删除。
卷是独立于容器之外的一块存储区域,通过挂载(Mount)的方式供 Pod 中的容器使用。
-
使用场景
卷可以在多个容器之间共享数据。
卷可以将容器数据存储在外部存储或者云存储上。
卷更容易备份或迁移。
常见的卷类型
-
临时卷(Ephemeral Volume): 与 Pod 一起创建和删除,生命周期与 Pod 相同
emptyDir - 作为缓存或存储日志
configMap、secret、downwardAPI - 给 Pod 注入数据
-
持久卷(Persistent Volume): 删除 Pod 后,持久卷不会被删除
本地存储 - hostPath、local
网络存储 - NFS
分布式存储 - Ceph(cephfs 文件存储、rbd 块存储)
-
投射卷(Projected Volumes): Projected 卷可以将多个卷映射到同一个目录上
临时卷
- 与 Pod 一起创建和删除,生命周期与 Pod 相同
- emptyDir - 初始内容为空的本地临时文件
- configMap - 为 Pod 注入配置文件
- secret - 为 Pod 注入加密数据
持久卷和持久卷声明
-
本地存储
hostPath - 节点主机上的目录或文件
(仅供单节点测试使用;多节点集群请用 local 卷代替)
local - 节点上挂载的本地存储设备(不支持动态创建卷)
-
网络存储
NFS - 网络文件系统(NFS)
-
分布式存储
Ceph(cephfs 文件存储、rbd 块存储)
使用持久卷和声明
-
创建持久卷(PV)
创建持久卷(PV)是服务端的行为,通常集群管理员会提前创建一些常用规格的持久卷以备使用。
hostPath
仅供单节点测试使用,当 Pod 被重新创建时,可能会被调度到与原先不同的节点上,导致新的 Pod 没有数据。多节点集群使用本地存储,可以使用local
卷创建local
类型的持久卷,需要先创建存储类(StorageClass)实例:
# 创建一个local-storage.yaml的文件 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: Immediate
local
卷不支持动态创建,必须手动创建持久卷(PV)。创建
local
类型的持久卷,必须设置nodeAffinity
属性。调度器使用
nodeAffinity
信息来讲使用local
卷的 Pod 调度到持久卷所在的节点上,不会出现 Pod 被调度到别的节点上的情况。如果节点出现故障,Pod 会创建失败。实例:
apiVersion: v1 kind: PersistentVolume metadata: name: example-pv spec: capacity: storage: 2Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Delete storageClassName: local-storage local: path: /mnt/disks/ssd1 nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node2
完整创建持久卷的实例:
# 创建一个local-storage.yaml的文件 apiVersion: storage.k8s.io/v1 kind: StorageClass metadata: name: local-storage provisioner: kubernetes.io/no-provisioner volumeBindingMode: Immediate --- apiVersion: v1 kind: PersistentVolume metadata: name: example-pv spec: capacity: storage: 2Gi volumeMode: Filesystem accessModes: - ReadWriteOnce persistentVolumeReclaimPolicy: Delete storageClassName: local-storage local: path: /mnt/disks/ssd1 nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node2
在目录下创建持久卷:
kubectl apply-f local-storage.yaml
查看持久卷:
kubectl get pv
-
创建持久卷声明(PVC)
持久卷声明(PVC)是用户端的行为,用户在创建 Pod 时,无法知道集群中 PV 的状态(名称,容量,是否可用等),用户也无需关心这些内容,只需要在生命中提出申请,集群会自动匹配符合需求的持久卷(PV)。
Pod 使用持久卷声明(PVC)作为存储卷。
PVC 实例:
# 创建一个pvc.yaml apiVersion: v1 kind: PersistentVolumeClaim metadata: name: task-pv-claim spec: storageClassName: local-storage accessModes: - ReadWriteOnce resources: requests: storage: 2Gi
创建 PVC:
kubectl apply -f pvc.yaml
查看 PV 和 PVC:
kubectl get pv
和kuebctl get pvc
发现状态都是 Bound,说明已经绑定成功了!
-
使用 PVC 作为卷
Pod 的配置文件指定了 PersistentVolumeClaim,但没有指定 PersistentVolume。
对 Pod 而言,PersistentVolumeClaim 就是一个存储卷。
PVC 卷实例:
apiVersion: v1 kind: Pod metadata: name: mypod spec: containers: - name: myfrontend image: nginx volumeMounts: - mountPath: "/var/www/html" name: mypd volumes: - name: mypd persistentVolumeClaim: claimName: myclaim
完整 yaml 文件,修改 mysql-pod.yaml
apiVersion: v1 kind: Secret metadata: name: mysql-password type: Opaque data: PASSWORD: MTIzNDU2Cg== --- apiVersion: v1 kind: Pod metadata: name: mysql-pod labels: app: mysql spec: containers: - name: mysql image: mysql:5.7 env: - name: MYSQL_ROOT_PASSWORD valueFrom: secretKeyRef: name: mysql-password key: PASSWORD optional: false #此值为默认值 ports: - containerPort: 3306 volumeMounts: - mountPath: /var/lib/mysql name: data-volume - mountPath: /etc/mysql/conf.d name: conf-volume readOnly: true volumes: - name: conf-volume configMap: name: mysql-config - name: data-volume persistentVolumeClaim: claimName: task-pv-claim # hostPath: # # directory location on host # path: /home/mysql/data # # this field is optional # type: DirectoryOrCreate --- apiVersion: v1 kind: ConfigMap metadata: name: mysql-config data: # 创建自己的配置文件 mysql.cnf: | [mysqld] character-set-server=utf8mb4 collation-server=utf8mb4_general_ci init-connect='SET NAMES utf8mb4' [client] default-character-set=utf8mb4 [mysql] default-character-set=utf8mb4
重新创建 Pod:
kubectl apply -f mysql-pod.yaml
动态创建持久卷
# 创建一个local-path-pvc.yaml文件
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: local-pvc-1
spec:
storageClassName: local-path
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 2Gi
运行这个 PVC
卷绑定模式
-
Immediate
立即创建创建 PVC 后,立即创建 PV 并完成绑定。
-
WaitForFirstConsumer
延迟创建当使用该 PVC 的 Pod 被创建时,才会自动创建 PV 并完成绑定。
再次修改 mysql-pod.yaml
apiVersion: v1
kind: Secret
metadata:
name: mysql-password
type: Opaque
data:
PASSWORD: MTIzNDU2Cg==
---
apiVersion: v1
kind: Pod
metadata:
name: mysql-pod
labels:
app: mysql
spec:
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
valueFrom:
secretKeyRef:
name: mysql-password
key: PASSWORD
optional: false #此值为默认值
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
- mountPath: /etc/mysql/conf.d
name: conf-volume
readOnly: true
volumes:
- name: conf-volume
configMap:
name: mysql-config
- name: data-volume
persistentVolumeClaim:
claimName: local-pvc-1
# hostPath:
# # directory location on host
# path: /home/mysql/data
# # this field is optional
# type: DirectoryOrCreate
---
apiVersion: v1
kind: ConfigMap
metadata:
name: mysql-config
data:
# 创建自己的配置文件
mysql.cnf: |
[mysqld]
character-set-server=utf8mb4
collation-server=utf8mb4_general_ci
init-connect='SET NAMES utf8mb4'
[client]
default-character-set=utf8mb4
[mysql]
default-character-set=utf8mb4
回收策略
-
删除(Delete)
如果没有指定,默认为
Delete
当 PVC 被删除时,关联的 PV 对象也会被自动删除。
-
保留(Retain)
当 PVC 对象被删除时,PV 卷仍然存在,数据卷状态变为“已释放(
Released
)”。此时卷上仍保留有数据,该卷还不能用于其他 PVC,需要手动删除 PV。
八、有状态应用集(StatefulSet)
如果我们需要部署多个 MySQL 实例,就需要用到 StatefulSet。
StatefulSet 是用来管理有状态的应用。一般用于管理数据库、缓存等。
与 Deployment 类似,StatefulSet 用来管理 Pod 集合的部署和扩缩。
Deployment 用来部署无状态应用。StatefulSet 用来部署有状态应用。
实例:
# 创建statefulset.yaml文件
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-sts
spec:
selector:
matchLabels:
app: mysql # 必须匹配 .spec.template.metadata.labels
serviceName: "mysql-svc"
replicas: 3 # 默认值是 1
minReadySeconds: 10 # 默认值是 0
template:
metadata:
labels:
app: mysql # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
volumeClaimTemplates:
- metadata:
name: data-volume
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-path"
resources:
requests:
storage: 200Mi
九、无头服务
无头服务(Headless Service)可以为 StatefulSet 成员提供稳定的 DNS 地址。
在不需要负载均衡的情况下,可以指定 Cluster IP 的值为 “None” 来创建无头服务。
注意:
StatefulSet
中的ServiceName
必须要跟Service
中的metadata.name
一致
实例:
apiVersion: v1
kind: Service
metadata:
name: mysql-svc
labels:
app: mysql-svc
spec:
ports:
- port: 3306
name: mysql
# headless service 无头服务
clusterIP: None
selector:
app: mysql
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mysql-sts
spec:
selector:
matchLabels:
app: mysql # 必须匹配 .spec.template.metadata.labels
serviceName: "mysql-svc"
replicas: 3 # 默认值是 1
minReadySeconds: 10 # 默认值是 0
template:
metadata:
labels:
app: mysql # 必须匹配 .spec.selector.matchLabels
spec:
terminationGracePeriodSeconds: 10
containers:
- name: mysql
image: mysql:5.7
env:
- name: MYSQL_ROOT_PASSWORD
value: "123456"
ports:
- containerPort: 3306
volumeMounts:
- mountPath: /var/lib/mysql
name: data-volume
volumeClaimTemplates:
- metadata:
name: data-volume
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: "local-path"
resources:
requests:
storage: 2Gi
十、端口转发
通常。集群中的数据库不直接对外访问。
但是,有时候我们需要图形化工具连接到数据库进行操作或者调试。
我们可以使用端口转发来访问集群中的应用。
kubectl port-forward
可以将本地端口的连接转发给容器。
此命令在前台运行,命令终止后,转发会话将结束。
示例代码:
kubectl port-forward --address 0.0.0.0 svc/tomcat 8079:8079
# 后台运行
kubectl port-forward --address 0.0.0.0 svc/tomcat 8079:8079 &
注意:前面的是宿主机需要访问的端口,后面是监听的service端口
characterEncoding=UTF-8&useSSL=false&zeroDateTimeBehavior=convertToNull&serverTimezone=Australia/Sydney
十一、外部访问数据库
除了上面的端口转发,我们也可以写一个 service 的 yaml 文件利用 NodePort 进行外部访问
示例代码:
创建一个 mysql-ex-svc.yaml
文件
apiVersion: v1
kind: Service
metadata:
name: mysql-external-svc
spec:
type: NodePort
ports:
- port: 3306
targetPort: 3306
nodePort: 30009
selector:
app: mysql
创建该服务:
kubectl apply -f mysql-ex-svc.yaml
进行访问:
十二、Ingress
Ingress / Service
在一个 k8s cluster 中, 如果我们需要从外部访问集群可以使用 Type 为 NodePort 的 Service 对外暴露我们的服务。以下是一个依赖关系样例:
# 创建一个nginx-pod
controlplane ~ ➜ kubectl run nginx-pod --image=nginx --port=80
pod/nginx-pod created
再使用以下内容,创建一个 Service nginx-pod-svc.yml
Service
apiVersion: v1
kind: Service
metadata:
name: nginx-pod-svc
spec:
type: NodePort
selector:
run: nginx-pod #通过run命令生成的pod所产生的标签
ports:
- port: 80
targetPort: 80
nodePort: 30001
查看 Service 信息并测试
controlplane ~ ➜ kubectl describe svc nginx-pod-svc
Name: nginx-pod-svc
Namespace: default
Labels: <none>
Annotations: <none>
Selector: run=nginx-pod
Type: NodePort
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.98.194.212
IPs: 10.98.194.212
Port: <unset> 80/TCP
TargetPort: 80/TCP
NodePort: <unset> 30001/TCP
Endpoints: 10.244.1.2:80
Session Affinity: None
External Traffic Policy: Cluster
Events: <none>
# 本地测试 curl localhost:30001 访问成功
controlplane ~ ➜ curl localhost:30001
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
但是一般我们不想记住 IP 地址和端口号,而是使用域名来对应用进行访问,并且可以使用 https 进行安全链接,我们可以使用 Ingress,这样我们就可以不用暴露 Service 的端口到 Node 上,通过域名进行访问应用。以下是依赖关系样例:
如果只有 Ingress 不足以实现流量转发,还需要 Ingress Controller 去实现
它的作用是评估和处理 Ingress rules/管理重定向/进入 cluster 的入口
Ingress Controller
如果外部拥有云平台的 Load Balancer,流量路由路径就会是这样:
Practise
例子:
controlplane ~ ➜ cat nginx-pod-svc.yml
apiVersion: v1
kind: Service
metadata:
name: nginx-pod-svc
spec:
type: ClusterIP
selector:
run: nginx-pod #通过run命令生成的pod所产生的标签
ports:
- port: 80
targetPort: 80
controlplane ~ ➜ kubectl replace -f nginx-pod-svc.yml
service/nginx-pod-svc replaced
controlplane ~ ➜ kubectl describe svc nginx-pod-svc
Name: nginx-pod-svc
Namespace: default
Labels: <none>
Annotations: <none>
Selector: run=nginx-pod
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.98.194.212
IPs: 10.98.194.212
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.2:80
Session Affinity: None
Events: <none>
controlplane ~ ➜ curl localhost:30001
curl: (7) Failed to connect to localhost port 30001 after 0 ms: Connection refused
要使用 Ingress,首先需要 Ingress Controller
Ingress-Nginx Controller
这里使用 Ingress-Nginx Controller
# 使用helm安装
helm upgrade --install ingress-nginx ingress-nginx \
--repo https://kubernetes.github.io/ingress-nginx \
--namespace ingress-nginx --create-namespace
# 生成一个新的namespace
controlplane ~ ➜ kubectl get ns
NAME STATUS AGE
default Active 4h15m
ingress-nginx Active 71s
kube-flannel Active 4h15m
kube-node-lease Active 4h15m
kube-public Active 4h15m
kube-system Active 4h15m
# 生成新的Pod
controlplane ~ ➜ kubectl get pod -n ingress-nginx
NAME READY STATUS RESTARTS AGE
ingress-nginx-controller-69bd47995d-gr55b 1/1 Running 0 72s
然后我们创建提供 httpd 服务的 Pod 和 Service
controlplane ~ ➜ kubectl run httpd-pod --image=httpd --port=80
pod/httpd-pod created
httpd-pod-svc.yml
apiVersion: v1
kind: Service
metadata:
name: httpd-pod-svc
spec:
type: ClusterIP
selector:
run: httpd-pod #通过run命令生成的pod所产生的标签
ports:
- port: 80
targetPort: 80
测试
controlplane ~ ➜ vi httpd-pod-svc.yml
controlplane ~ ➜ kubectl apply -f httpd-pod-svc.yml
service/httpd-pod-svc created
controlplane ~ ➜ kubectl describe svc httpd-pod-svc
Name: httpd-pod-svc
Namespace: default
Labels: <none>
Annotations: <none>
Selector: run=httpd-pod
Type: ClusterIP
IP Family Policy: SingleStack
IP Families: IPv4
IP: 10.98.177.252
IPs: 10.98.177.252
Port: <unset> 80/TCP
TargetPort: 80/TCP
Endpoints: 10.244.1.3:80
Session Affinity: None
Events: <none>
controlplane ~ ➜ curl 10.98.177.252
<html><body><h1>It works!</h1></body></html>
配置 Ingress,通过不同的域名访问不同的服务
# 查看ingressclass
controlplane ~ ➜ kubectl get ingressclasses.networking.k8s.io
NAME CONTROLLER PARAMETERS AGE
nginx k8s.io/ingress-nginx <none> 19m
Ingress
创建 Ingress ingress.yml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: http-ing
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
ingressClassName: nginx
rules:
- host: nginx-pod-svc.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: nginx-pod-svc
port:
number: 80
- host: httpd-pod-svc.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: httpd-pod-svc
port:
number: 80