零基础学习k8s基础命令

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-apiserverkube-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 支持许多容器运行环境,例如 containerdCRI-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 的小细节

    localhost127.0.0.1
    在计算机网络中,localhost 是回路网络接口的一个标准主机名,相对应的 ip 地址是 127.0.0.1127.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: 查看所有 apiversion

2.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) 是一种资源隔离机制,将同一集群中的资源划分为相互隔离的组。

命名空间可以在多个用户之间划分集群资源(通过资源配额)

  • 例如我们可以设置 devtestprod 等多个命名空间

同一命名空间作用域仅针对带有名字空间的对象,例如: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
namespacensNamespace
nodesnoNode
podspoPod
servicessvcService
deploymentsdeployDeployment
replicasetsrsReplicaSet
statefulsetsstsStatefulSet

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, DeploymentServiceReplicaSet
  • metadata- 描述对象的元数据,包括一个 name 字符串,UID 和可选的 namespace
  • spec- 对象的配置

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 的数量调整为 0

    kubectl 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 pvkuebctl 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              
  • 29
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

子非鱼2020

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值