Service 可以将各个 Pod 与客户端通过固定的 IP、DNS 和端口连接起来。它是固定的前端和动态后端的中间层。前端主要由 IP, DNS 名称和 端口组成,始终不变;后端则由一系列 Pod 构成。
它利用 Label 来动态选择 (selector) 将流量转发至哪些 Pod。Service 中 Pod 列表 IP 维护是由 Label 筛选器和 Endpoint 对象(实际 pod 的 ip 存在这里)共同完成的。
每一个 Service 被创建的时候,都会得到一个关联的 EndPoint 对象。
同时,可以对请求进行负载均衡。
当通过 Service 转发流量到 Pod 时,通常会现在集群内部的 DNS 中查询 Service 的 IP 地址。流量被发送到该 IP 地址后,会被 Service 转发到其中一个 Pod。 k8s 元素应用是可以直接查询 Endpoint API,而无需查找 DNS 和使用 Service IP 的。
apiVersion: v1
kind: Service
metadata:
name: svc-test
labels:
chapter: services
spec: # 这里是 Service 的配置
# ipFamilyPolicy: PreferDualStack
# ipFamilies:
# - IPv4
# - IPv6
type: NodePort # 类型
ports:
- port: 8080 # Service 对集群内部暴露的端口
nodePort: 30001 # Service 对集群外外部暴露的端口
targetPort: 8080 # 应用 Pod 对外暴露的端口
protocol: TCP # 协议
selector:
chapter: services # Service 的标签选择器
# 部署 Service
kubectl apply -f svc.yml
# 查看 Service
kubectl get svc [<service name>]
kubectl describe svc [<service name>]
# 删除 Service
kubectl delete svc <service name>
# 查看 Endpoint 对象
kubectl get ep <endpoint name>
kubectl describe ep <endpoint name>
服务发现 (Service Discovery)
服务发现包括两个主要组件
服务注册
服务发现
服务注册
服务注册就是把服务的 Service 的连接信息注册到服务仓库,以便其他微服务能够发现它并进行连接。
服务仓库是一个内部 DNS 服务
服务是基于 DNS 注册的,而不是 Service 所在的 Pod
每个服务的名称、IP 地址和网络端口都会被注册
注册的具体过程如下:
用户创建 Service 时会 POST 一个新的 Service 部署文件到 API Server
该请求经过认证、授权、并遵从准入策略
Service 会被分配一个名为 ClusterIP 的虚拟 IP 地址
创建一个 Endpoint 对象来记录所有匹配该 Service 的 Pod,以便进行流量的负载均衡
配置 Pod 网络来承载发送至 ClusterIP 的流量
Service 的名称和 IP 被注册到集群 DNS 中
注册后端使用的是 IPVS (Linux IP Virtual Server, 即 IP 虚拟服务器), 1.11 以前是 iptables,后端配置被存储在一个 Endpoint 对象中,同时承载流量的网络也就绪了。
整个过程如下:
POST Service 配置到 API Server --> 分配 Cluster IP --> 配置被持久化到集群存储 --> 维护有 Pod IP 的 Endpoint 创建 --> 集群 DNS 发现新的 Service --> 创建 DNS 记录 --> kube-proxy 拉取 Service 配置 --> 创建 IPVS 规则
服务发现
服务发现使用集群 DNS 将名称解析为 IP 地址:k8s 会为每个容器注入 /etc/resolv.conf 文件,其中有集群 DNS 服务的 IP 地址和搜索域,容器会先向集群 DNS 发送一个将名称为 <service name> 解析为 IP 地址的请求,集群 DNS 会返回 <service name> 的 ClusterIP,然后容器就知道往哪个 IP 发送请求了。
与命名空间相关的 Service 名称:<object-name>.<namespace-name>.svc.cluster.local。比如 namespace 为 dev 的名称为 ent 的 Service 名称为 et.dev.svc.cluster.local
让这个发送到的请求到达需要一些“黑科技”,因为这个 IP 地址在一个特殊的名为服务网络 (Service network) 的网络上,没有路由可达。
以上三者称为 Pod 的状态 ID(stick ID)。状态 ID 在及时发生故障、扩缩容以及其他调度操作后,依然保持不变。
StatefulSet 中的 Pod 名字遵循 <StatefulSet-name>-<Integer> 的规则,其中 integer 是一个从零开始的索引号,第一个创建的是 0。StatefulSet 的名字必须是有效的 DNS 名字。StatefulSet 对 Pod 的启动和停止是有序的,扩缩容也是。
删除 StatefulSet 并不是按序种植所有 Pod 的,因此删除前要将其缩容至 0.
因为 DNS 不变,所以其他应用可以通过向 DNS 查询所有 Pod 信息来实现对这些 Pod 的直接连接:
headless Service:一个将 spec.clusterIP 设置为 None 的常规 k8s Service 对象
governing Service:当这个 headless Service 被设置为 StatefulSet 的 spec.ServiceName 时,他就成为了 StatefulSet 的 governing Service
在二者如此关联后,Service 会为所匹配的每个 Pod 副本创建 DNS SRV 记录。其他 Pod 可以通过对 headless Service 发起 DNS 查询来获取 StatefulSet 的信息。
k8s
k8s 与 docker 区别
k8s 概览
k8s 整体承担两个角色:
k8s 集群一般由一个主节点(master, 又称为 control plan 或者 head node)和若干子节点(node, 又称为 data plan)构成。 所谓 k8s 原生应用是指知道自己运行在 k8s 上的能够查询 k8s API 的应用。
Pod
Pod 是 k8s 的管理基本单位。一般一个 Pod 包含一个容器,当然也可以又多个容器。
Pod.yml
Deployment
Deploy.yaml
滚动升级
更新 deploy.yml 清单文件
回滚
Service
类型有
ClusterIP
(默认,从集群内部访问可用),NodePort
,LoadBalancer
,ExternalName
(可以将流量路由到集群之外的系统中去)命令式创建 Service (不推荐)
声明式创建
svc.yml
服务发现 (Service Discovery)
服务发现包括两个主要组件
服务注册
服务注册就是把服务的 Service 的连接信息注册到服务仓库,以便其他微服务能够发现它并进行连接。
注册的具体过程如下:
注册后端使用的是 IPVS (Linux IP Virtual Server, 即 IP 虚拟服务器), 1.11 以前是 iptables,后端配置被存储在一个 Endpoint 对象中,同时承载流量的网络也就绪了。
服务发现
服务发现使用集群 DNS 将名称解析为 IP 地址:k8s 会为每个容器注入
/etc/resolv.conf
文件,其中有集群 DNS 服务的 IP 地址和搜索域,容器会先向集群 DNS 发送一个将名称为<service name>
解析为 IP 地址的请求,集群 DNS 会返回<service name>
的 ClusterIP,然后容器就知道往哪个 IP 发送请求了。让这个发送到的请求到达需要一些“黑科技”,因为这个 IP 地址在一个特殊的名为服务网络
(Service network)
的网络上,没有路由可达。kube-proxy
的系统服务,会捕获发送到 ClusterIP 的流量,并转发至匹配 Service 的 Label 筛选器的 Pod 的 IP 地址。实践
namespace-server.yml
服务端问题排查
k8s 存储
k8s 的存储子系统被称为持久化卷子系统 (Persistent Volume Subsystem)。 k8s 支持多种途径的多种类型的存储,如 NFS, SMB, 对象存储等,他们在 k8s 统称为卷 (Volume)。k8s 通过 CSI(容器存储接口,拥有清晰接口的开放标准) 实现胶水层连接存储和持久化卷子系统,使各类型的存储以插件的形式映射到集群,使得 Pod 可以使用 PV。
PVS = PV(持久化卷,Persistent Volume) + PVC(持久化卷申请,Persistent Volume Claim) + SC(存储类,Storage Class)
创建 PV
gke-pv.yml
创建 PVC
gke-pvc.yml
将 Pod 与 PVC 相关联
volPod.yml
存储类
存储类允许用户为存储定义不同的类或层(tier)。用户可以根据存储的类型来自行决定如何定义类。 k8s 中存储类被作为资源定义在 storage.k8s.io/v1 的 API 组中。
单个存储类
多个存储类
用户可以配置任意梳理的 StorageClass 对象,不过每个 SC 只能关联一个存储后端。但每个存储后端可以提供多个存储的类/层,进一步对应多个 SC 对象。
部署和使用 SC 对象的基本流程
实践
ConfigMap
多数业务系统由应用和配置两部分组成。k8s 中将应用与配置解耦,它们被分别构建和存储。比如我们打包时镜像都是同一个,但是部署时,读取了不同环境的配置。
k8s 通过提供一个名为 ConfigMap (CM) 的对象,将配置数据从 Pod 中剥离出来,使用它可以动态地在 Pod 运行时注入数据。其实我们从名字就可以看出来,是配置映射。
ConfigMap 可以命令式,也可以用声明文件。通常用于存储以下非敏感配置数据
不应该使用 ConfigMap 来存储凭证或密码等敏感信息,而是应该采用 Secret 来存储,k8s 会对 Secret 的值进行混淆。
命令式
声明式
multimap.yml
ConfigMap 注入 Pod 和 容器
作为环境变量
multimap.yml
envPod.yml
与容器启动命令
startuppod.yml
与卷
对卷的更新会同步到容器中。
cmPod.yml
StatefulSet
StatefulSet
和 Deployment 都支持自愈、自动扩缩容、滚动更新等特性,但是 StatefulSet 可以确保以上三者称为 Pod 的状态 ID(stick ID)。状态 ID 在及时发生故障、扩缩容以及其他调度操作后,依然保持不变。
StatefulSet 中的 Pod 名字遵循
<StatefulSet-name>-<Integer>
的规则,其中 integer 是一个从零开始的索引号,第一个创建的是 0。StatefulSet 的名字必须是有效的 DNS 名字。StatefulSet 对 Pod 的启动和停止是有序的,扩缩容也是。删除 StatefulSet 并不是按序种植所有 Pod 的,因此删除前要将其缩容至 0.
因为 DNS 不变,所以其他应用可以通过向 DNS 查询所有 Pod 信息来实现对这些 Pod 的直接连接:
在二者如此关联后,Service 会为所匹配的每个 Pod 副本创建 DNS SRV 记录。其他 Pod 可以通过对 headless Service 发起 DNS 查询来获取 StatefulSet 的信息。
域名格式
<object-name>.<service-name>.<namespace-name>.svc.cluster.local