kubernetes 对象的 yaml 描述语法说明

环境信息

  • Centos7 5.4.212-1
  • Docker 20.10.18
  • containerd.io-1.6.8
  • kubectl-1.25.0
  • kubeadm-1.25.0
  • kubelet-1.25.0

几乎所有的 Kubernetes 对象都包含两个嵌套的对象字段:

  • spec - 对象的期望状态(desired state)的描述信息
  • status - 对象当前的状态(current state

Kubernetes 系统的目标就是不停的调整对象的当前状态(current state)直到和期望状态(desired state)匹配。

常用字段说明

必需字段

在想要创建的 Kubernetes 对象所对应的 .yaml 文件中,必须配置的字段如下:[1]

  • apiVersion - 创建该对象所使用的 Kubernetes API 的版本
  • kind - 想要创建的对象的类别
  • metadata - 帮助唯一标识对象的一些数据,包括一个 name 字符串、UID 和可选的 namespace
  • spec - 你所期望的该对象的状态

metadata

帮助唯一标识对象的一些数据,包括一个 name 字符串、UID 和可选的 namespace

集群中的每一个对象都有一个 名称name)来标识在同类资源中的唯一性。name 也用来作为 url 中的资源名称 [2]

每个 Kubernetes 对象也有一个 UIDuid)来标识在整个集群中的唯一性。

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx-deployment
spec:
selector:
matchLabels:
app: nginx
replicas: 2
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

标签和选择算符

标签(labels) 是附加到 Kubernetes 对象(比如 Pod)上的键值对。 标签旨在用于指定对用户有意义且相关的对象的标识属性,但不直接对核心系统有语义含义。 标签可以用于组织和选择对象的子集。标签可以在创建时附加到对象,随后可以随时添加和修改。 每个对象都可以定义一组键/值标签。每个键对于给定对象必须是唯一的。[3]

通过 标签选择算符,客户端/用户可以识别一组对象。标签选择算符 是 Kubernetes 中的核心分组原语。

对于某些 API 类别(例如 ReplicaSet)而言,两个实例的标签选择算符不得在命名空间内重叠, 否则它们的控制器将互相冲突,无法确定应该存在的副本个数。

比较新的资源,例如 JobDeploymentReplicaSetDaemonSet, 也支持基于集合的需求。

selector:
matchLabels:
component: redis
matchExpressions:
- {key: tier, operator: In, values: [cache]}
- {key: environment, operator: NotIn, values: [dev]}

matchLabels 是由 {key,value} 对组成的映射。 matchLabels 映射中的单个 {key,value} 等同于 matchExpressions 的元素, 其 key 字段为 keyoperatorIn,而 values 数组仅包含 valuematchExpressions 是 Pod 选择算符需求的列表。 有效的运算符包括 InNotInExistsDoesNotExist。 在 InNotIn 的情况下,设置的值必须是非空的。 来自 matchLabelsmatchExpressions 的所有要求都按 逻辑与 的关系组合到一起 – 它们必须都满足才能匹配

字段选择器

字段选择器(Field selectors) 允许你根据一个或多个资源字段的值 筛选 Kubernetes 资源

下面是一些使用字段选择器查询的例子:

  • metadata.name=my-service
  • metadata.namespace!=default
  • status.phase=Pending

下面这个 kubectl 命令将筛选出 status.phase 字段值为 Running 的所有 Pod:

kubectl get pods --field-selector status.phase=Running

不同的 Kubernetes 资源类型支持不同的字段选择器。 所有资源类型都支持 metadata.namemetadata.namespace 字段。 使用不被支持的字段选择器会产生错误

你可在字段选择器中使用 ===!==== 的意义是相同的)操作符。 例如,下面这个 kubectl 命令将筛选所有不属于 default 命名空间的 Kubernetes 服务:

kubectl get services  --all-namespaces --field-selector metadata.namespace!=default

同标签和其他选择器一样, 字段选择器可以通过使用逗号分隔的列表组成一个选择链。 下面这个 kubectl 命令将筛选 status.phase 字段不等于 Running 同时 spec.restartPolicy 字段等于 Always 的所有 Pod:

kubectl get pods --field-selector=status.phase!=Running,spec.restartPolicy=Always

你能够跨多种资源类型来使用字段选择器。 下面这个 kubectl 命令将筛选出所有不在 default 命名空间中的 StatefulSetService

kubectl get statefulsets,services --all-namespaces --field-selector metadata.namespace!=default

metadata.ownerReferences

在 Kubernetes 中,一些对象是其他对象的 属主(Owner)。 例如,ReplicaSet 是一组 Pod 的属主。 具有属主的对象是属主的 附属(Dependent)[4]

附属对象有一个 metadata.ownerReferences 字段,用于引用其属主对象。 一个有效的属主引用,包含与附属对象同在一个命名空间下的对象名称和一个 UID。 Kubernetes 自动为一些对象的附属资源设置属主引用的值, 这些对象包含 ReplicaSetDaemonSetDeploymentJobCronJobReplicationController 等。

附属对象还有一个 ownerReferences.blockOwnerDeletion 字段,该字段使用布尔值, 用于控制特定的附属对象是否可以阻止垃圾收集删除其属主对象。 如果控制器(例如 Deployment 控制器) 设置了 metadata.ownerReferences 字段的值,Kubernetes 会自动设置 blockOwnerDeletion 的值为 true

ownerReferences:
- apiVersion: apps/v1
kind: ReplicaSet
name: test-nginx-deployment-54cb7448dc
uid: 34b3353a-bd05-4ac3-959f-d6fbc85a2d2e
controller: true
blockOwnerDeletion: true

Pod

Pod 资源定义官网 API 说明

apiVersion: v1
kind: Pod
metadata:
name: ${NAME}
namespace: ${NAMESPACE}
spec:
restartPolicy: Always
containers:
- name: ${NAME}
image: nginx:1.14.2
ports:
- containerPort: 80
name: http-web

args:
- sh
- -c
- ping 127.0.0.1

command:
- sh
- -c
- cd /code1/ && python manage.py runserver localhost:8080
env:
- name: DEMO_GREETING
value: "Hello from the environment"
- name: DEMO_FAREWELL
value: "Such a sweet sorrow"

volumeMounts:
- name: html
mountPath: /usr/share/nginx/html/
# subPath: logs/admin

resources:
requests:
cpu: 200m
memory: 10Mi
limits:
cpu: 200m
memory: 10Mi

nodeSelector:
disktype: ssd

nodeName: foo-node

imagePullSecrets:
- name: ${secret_name}

hostAliases:
- ip: "50.18.1.1"
hostnames:
- "mysql.master"
- "mysql.slave"
- "mongo.com"
- ip: "50.18.1.2"
hostnames:
- "meta-service.apollo.com"
- ip: "172.31.88.78"
hostnames:
- "redis.com"


  • .spec.containers[*].ports[*].containerPort - Pod 暴露的端口

  • .spec.containers[*].ports[*].name - Pod 暴露的端口的自定义名称,可在 ServicetargetPort 属性中,Probe 等功能中引用这些名称

  • .spec.restartPolicy - 重启策略,适用于 Pod 中的所有容器,可取值如下

    • Always - 默认值
    • OnFailure
    • Never

    Sidecar Container(使用 initContainers 定义的容器) 会忽略 Pod 级别定义的 restartPolicyinitContainers 定义的容器有其容器级别的 restartPolicy,默认也为 Always

    当 Kubelet 使用 restartPolicy 重启 Pod 中的容器时,会使用指数级别的时间间隔(10s, 20s,40s, …),5m 封顶

  • .spec.containers[*].args - 定义容器启动后执行的命令,会替换镜像中定义的默认执行程序

  • .spec.nodeSelector - 根据节点标签将 Pod 调度到指定节点 [13]

  • .spec.nodeName: foo-node - 将 Pod 调度到指定节点。 使用 nodeName 规则的优先级会高于使用 nodeSelector 或亲和性与非亲和性的规则,如果所指代的节点不存在,则 Pod 无法运行,而且在某些情况下可能会被自动删除。如果所指代的节点无法提供用来运行 Pod 所需的资源,Pod 会失败, 而其失败原因中会给出是否因为内存或 CPU 不足而造成无法运行。[12]

  • .spec.containers[*].env - 创建 Pod 时,可以为其下的容器设置环境变量。通过配置文件的 env 或者 envFrom 字段来设置环境变量。 [15]

  • .spec.containers[*].volumeMounts - 挂载卷,subPath 可用于指定挂载卷(name: html)的子目录到容器路径(mountPath),而不是直接挂载卷目录。

  • .spec.containers[*].resources - 资源限制

容器探针

探针(Probe) 主要用来检测 Pod 的状态,主要有 活跃(Liveness)就绪(Readiness)启动(Startup) 探针 [10]

  • livenessProbe - kubelet 使用 存活探针 来确定什么时候要重启容器,例如,存活探针可以探测到应用死锁(应用程序在运行,但是无法继续执行后面的步骤)情况。 重启这种状态下的容器有助于提高应用的可用性,即使其中存在缺陷。
  • readinessProbe - kubelet 使用 就绪探针 可以知道容器何时准备好接受请求流量,当一个 Pod 内的所有容器都就绪时,才能认为该 Pod 就绪。 这种信号的一个用途就是控制哪个 Pod 作为 Service 的后端。 若 Pod 尚未就绪,会被从 Service 的负载均衡器中剔除。
  • startupProbe - kubelet 使用 启动探针 来了解应用容器何时启动。 如果配置了这类探针,你就可以控制容器在启动成功后再进行存活性和就绪态检查, 确保这些存活、就绪探针不会影响应用的启动。 启动探针可以用于对慢启动容器进行存活性检测,避免它们在启动运行之前就被杀掉。

Probe 是由 kubelet 对容器执行的定期诊断。 要执行诊断,kubelet 既可以在容器内执行代码,也可以发出一个网络请求。[11]

使用探针来检查容器有四种不同的方法。 每个探针都必须准确定义为这四种机制中的一种:

  • exec - 在容器内执行指定命令。如果命令退出时返回码为 0 则认为诊断成功。
  • grpc - 使用 gRPC 执行一个远程过程调用。 目标应该实现 gRPC健康检查。 如果响应的状态是 “SERVING”,则认为诊断成功。 gRPC 探针是一个 alpha 特性,只有在你启用了 “GRPCContainerProbe” 特性门控时才能使用。
  • httpGet - 对容器的 IP 地址上指定端口和路径执行 HTTP GET 请求。如果响应的状态码大于等于 200 且小于 400,则诊断被认为是成功的。
  • tcpSocket - 对容器的 IP 地址上的指定端口执行 TCP 检查。如果端口打开,则诊断被认为是成功的。 如果远程系统(容器)在打开连接后立即将其关闭,这算作是健康的

exec 方式的探针,因为其实现涉及到对进程的 creation/forking,这会导致额外的资源消耗,如果主机上资源紧张,可能会导致探测失败。 [18]

每次探测都将获得以下三种结果之一:

  • Success(成功) - 容器通过了诊断。
  • Failure(失败) - 容器未通过诊断。
  • Unknown(未知) - 诊断失败,因此不会采取任何行动。

livenessProbe

exec 方式的 livenessProbe
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-exec
spec:
containers:
- name: liveness
image: registry.k8s.io/busybox
args:
- /bin/sh
- -c
- touch /tmp/healthy; sleep 30; rm -f /tmp/healthy; sleep 600
livenessProbe:
exec:
command:
- cat
- /tmp/healthy
initialDelaySeconds: 5
periodSeconds: 5
timeoutSeconds: 10
  • .spec.containers[*].args - 容器启动后执行的命令,取代镜像中默认的命令

  • .spec.containers[*].livenessProbe - 配置存活探针

    • exec.command - 配置探测方式为 exec 以及在容器内执行的命令,如果命令执行成功并且返回值为 0,kubelet 就会认为这个容器是健康存活的。 如果这个命令返回非 0 值,kubelet 会杀死这个容器并 重新启动 它。
    • periodSeconds - 指定 kubelet 应该每 5 秒执行一次存活探测,默认是 10 秒。最小值是 1
    • initialDelaySeconds - kubelet 在执行第一次探测前等待 5 秒, 默认是 0 秒
    • timeoutSeconds - 探测的超时后等待多少秒。默认值是 1 秒。最小值是 1。
    • successThreshold - 探针在失败后,被视为成功的最小连续成功数。默认值是 1。 存活和启动探测的这个值必须是 1。最小值是 1。
    • failureThreshold - 当探测失败时,Kubernetes 的重试次数。默认值是 3。最小值是 1
httpGet 方式的 livenessProbe
apiVersion: v1
kind: Pod
metadata:
labels:
test: liveness
name: liveness-http
spec:
containers:
- name: liveness
image: registry.k8s.io/liveness
args:
- /server
livenessProbe:
httpGet:
path: /healthz
port: 8080

# 非常用字段
host: test.domain
scheme: http

httpHeaders:
- name: Custom-Header
value: Awesome
- name: Accept
value: application/json
- name: Host
value: test.domain

initialDelaySeconds: 3
periodSeconds: 3
  • .spec.containers[*].livenessProbe - 配置存活探针
    • httpGet.path - 配置存活探针的探测方式为 httpGet ,并指定 HTTP GET 请求的路径(默认为 /)。返回大于或等于 200 并且小于 400 的任何代码都标示成功。其它返回代码都标示失败,失败时 kubelet 会杀死这个容器并将其重启
    • httpGet.port - 指定 HTTP GET 请求的端口
    • httpGet.httpHeaders - 请求中自定义的 HTTP 头。HTTP 头字段允许重复。
    • host - 连接使用的主机名,默认是 Pod 的 IP。也可以在 HTTP 头中设置 Host 来代替
    • scheme - 用于设置连接主机的方式(HTTP 还是 HTTPS)。默认是 “HTTP”
tcpSocket 方式的 livenessProbe

kubelet 会尝试在指定端口和容器建立套接字链接。 如果能建立连接,这个容器就被看作是健康的,如果不能则这个容器就被看作是有问题的。

对于 TCP 探测而言,kubelet 在节点上(不是在 Pod 里面)发起探测连接, 这意味着你不能在 host 参数上配置服务名称,因为 kubelet 不能解析服务名称。

apiVersion: v1
kind: Pod
metadata:
name: goproxy
labels:
app: goproxy
spec:
containers:
- name: goproxy
image: registry.k8s.io/goproxy:0.1
ports:
- containerPort: 8080
name: goproxy-port
readinessProbe:
tcpSocket:
port: goproxy-port
initialDelaySeconds: 5
periodSeconds: 10
livenessProbe:
tcpSocket:
port: 8080
initialDelaySeconds: 15
periodSeconds: 20
  • .spec.containers[*].livenessProbe - 配置存活探针
    • tcpSocket.port - 配置存活探针的探测方式为 tcpSocket,并指定 TCP 探测连接的端口,可以使用命名的端口

Init Containers

Init Containers 是 Pod 中的一个或多个特殊的容器,它会在应用容器 之前启动Init Containers 中一般会包含一些工具或者是在应用容器启动之前需要执行的脚本或者初始化事项。 [19]

sidecar container 是会在应用容器启动之前启动,并且会一直持续运行的容器。而 Init Containers 要在 Pod 初始化阶段(应用容器就绪前)结束(completion)。

在 Pod 的 Spec 配置中,Init Containers(使用 initContainers 字段) 处于和应用容器(使用 containers 字段) 同一级。

相比于普通的应用容器, Init Containers 有以下特征:

  • 在整个 Pod 就绪前, Init Containers 中的容器都要成功完成(completion)
  • 每个 init container 都必须在另一个 init container 启动成功之后才能启动
  • 如果 init container 启动失败,kubelet 会一直重启它(restartPolicyAlwaysOnFailure),直到成功;如果 Init ContainersrestartPolicyNever,并且 Init Containers 中有容器启动失败,那么 Kubernetes 会判定此 Pod 启动失败。

Init Containers 支持和普通容器一样的配置项,如资源限制、volumes、安全配置等。 但是,**init containers 不支持 lifecyclelivenessProbereadinessProbestartupProbe 字段**。Init containers 必须在 Pod 准备就绪前成功执行完成(completion)。sidecar containers 会在 Pod 的生命周期中持续运行,支持探针。

Init Containers 中如果有多个容器,kubelet 会按照顺序依次启动这些容器,前一个容器启动成功,后一个容器才能启动。所有的 init containers 都启动成功并完成后,kubelet 才会初始化应用容器。

Pod 拓扑分布约束

你可以使用 拓扑分布约束(Topology Spread Constraints) 来控制 Pod 在集群内故障域之间的分布, 例如区域(Region)、可用区(Zone)、节点和其他用户自定义拓扑域。 这样做有助于实现高可用并提升资源利用率。[16]

apiVersion: v1
kind: Pod
metadata:
name: example-pod
spec:
# 配置一个拓扑分布约束
topologySpreadConstraints:
- maxSkew: <integer>
minDomains: <integer> # 可选;自从 v1.25 开始成为 Beta
topologyKey: <string>
whenUnsatisfiable: <string>
labelSelector:
matchLabels:
app: my-app
### 其他 Pod 字段置于此处
  • maxSkew - 描述 Pod 在 topologyKey 选中的节点组成的区域 中均匀分布的程度(最大偏差)。比如值为 1 表示 任何给定的区域与其他区域之间的 Pod 数量的差异不能超过 1
  • topologyKey - 节点标签的 ,如果节点使用此 标记并且具有相同的 标签值, 则将这些节点视为处于同一区中。
  • whenUnsatisfiable - 指示如果 Pod 不满足分布约束时如何处理:
    • DoNotSchedule -(默认)告诉调度器不要调度。
    • ScheduleAnyway - 告诉调度器仍然继续调度,只是根据如何能将偏差最小化来对节点进行排序。
  • labelSelector - 定这个约束应该应用于哪些 Pod

拓扑域说明

topologyKey 中指定的 会用来获取每个 Node 上对应的 ,拥有 相同值的 Node 会被视为属于 同一个区域,而拥有 指定键(topologyKey) 的 Node 属于 同一个拓扑域

Kubernetes 将会尽量确保 Pods 在这些 拓扑域 (拥有相同 topologyKey 的 Node) 中的 区域 (拥有 相同值的 Node) 上均匀分布。

假设有以下四个 Node,它们分别位于两个不同的可用区(Zone):

  • Node1:topology.kubernetes.io/zone=zoneA

  • Node2:topology.kubernetes.io/zone=zoneA

  • Node3:topology.kubernetes.io/zone=zoneB

  • Node4:topology.kubernetes.io/zone=zoneB

如果你的 topologyKey 设置为 topology.kubernetes.io/zone,则 Kubernetes 将会认为 Node1Node2 属于(拓扑域 topology.kubernetes.io/zone)的同一个区域,Node3Node4 属于此拓扑域的另一个区域。因此,它会尽量保持这两个区域中的 Pod 数量均衡。

apiVersion: apps/v1
kind: Deployment
metadata:
name: my-deployment
spec:
replicas: 8 # 假设你想运行8个Pods
selector:
matchLabels:
app: my-app
template:
metadata:
labels:
app: my-app
spec:
containers:
- name: nginx
image: nginx:1.14.2
topologySpreadConstraints:
- maxSkew: 1
topologyKey: topology.kubernetes.io/zone
whenUnsatisfiable: DoNotSchedule
labelSelector:
matchLabels:
app: my-app

在这个例子中,Kubernetes 将会尽量确保 zoneAzoneB 中的 Pod 数量差异不超过 maxSkew(1),从而确保这些 Pods 在不同的区域中均匀分布。

Deployment

Deployment 资源定义官网 API 说明

apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx-deployment
labels:
app: nginx
spec:
replicas: 3
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:1.14.2
ports:
- containerPort: 80

strategy:
rollingUpdate:
maxSurge: 25%
maxUnavailable: 25%
type: RollingUpdate
  • .spec.replicas - Pod 副本的数量,会根据配置产生 附属对象 ReplicaSet

  • .spec.selector - 字段定义了 Deployment 如何查找要管理的 Pod

  • .spec.template - 定义了 Pod 相关的必要信息,本质上是定义一个 Pod 对象

    • .metadata.labels - 为容器添加的标签,slector 可以使用此标签查找要管理的 Pod
    • .spec.containers - 容器相关定义,包含启动的容器名称,使用的镜像,容器暴露的端口等
  • .spec.strategy - 指定用于用新 Pod 替换旧 Pod 的策略。.spec.strategy.type 可以是 RecreateRollingUpdateRollingUpdate 是默认值。[17]

    • .spec.strategy.type==Recreate - 如果值为 Recreate,在创建新 Pod 之前,所有现有的 Pod 会被杀死
    • .spec.strategy.type==RollingUpdate - Deployment 会在 .spec.strategy.type==RollingUpdate 时,采取 滚动更新的方式更新 Pod。你可以指定 maxUnavailablemaxSurge 来控制滚动更新 过程。
    • .spec.strategy.rollingUpdate.maxUnavailable - 是一个可选字段,用来指定 更新过程中不可用的 Pod 的个数上限,默认值为 25%。如果 .spec.strategy.rollingUpdate.maxSurge 为 0,则此值不能为 0
    • .spec.strategy.rollingUpdate.maxSurge - 是一个可选字段,用来指定可以创建的超出期望 Pod 个数的 Pod 数量, 如果 MaxUnavailable 为 0,则此值不能为 0,此字段的默认值为 25%

Service

Service 资源定义官网 API 说明

将运行在一组 Pods 上的应用程序公开为网络服务的抽象方法。主要为后端的一组 POD (通过选择算符选定)提供 Cluster-IP 和负载均衡(默认轮询)及高可用(只有就绪的 POD 才会被对外公布,未就绪的 POD 不会对外发布)。 [5]

有选择算符的 Service

Service 所针对的 Pod 集合通常是通过 选择算符 来确定的

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
selector:
app.kubernetes.io/name: MyApp
ports:
# 多端口 Service 时,必须提供所有的端口名称,以使端口无歧义
- name: http
protocol: TCP
port: 80
targetPort: 9376
- name: https
protocol: TCP
port: 443
targetPort: 9378

# 非常用字段
sessionAffinity: None
ipFamilies:
- IPv4
ipFamilyPolicy: SingleStack
internalTrafficPolicy: Cluster

type: ClusterIP
clusterIPs:
- 10.107.120.122
clusterIP: 10.107.120.122

externalTrafficPolicy: Cluster
internalTrafficPolicy: Cluster

# type 为 NodePort 时
type: NodePort
ports:
- nodePort: 30443
port: 443
targetPort: 8443
externalTrafficPolicy: Local
internalTrafficPolicy: Local
  • .spec.selector - 根据 选择算符 选择后端的 POD

  • .spec.ports[*].name - 对于某些服务,可能需要公开多个端口。 Kubernetes 允许在 Service 对象上配置多个端口定义。 为服务使用多个端口时,必须提供所有端口名称,以使它们无歧义

  • .spec.ports[*].protocol - Service 监听的端口协议,默认为 TCP

  • .spec.ports[*].port - Service 监听的端口

  • .spec.ports[*].targetPort - 后端 POD 监听的端口,不指定的情况(默认)下,targetPort 将被设置为与 port 字段相同的值。

  • .spec.sessionAffinity - 要确保每次都将来自特定客户端的连接传递到同一 Pod,可以设置为 ClientIP (默认为 None) 来基于客户端的 IP 地址选择会话亲和性。还可以通过适当设置 .spec.sessionAffinityConfig.clientIP.timeoutSeconds 来设置最大会话停留时间。(默认值为 10800 秒,即 3 小时)

  • .spec.ipFamilies - 定义分配 ip 版本(IPv4,IPv6)的顺序,此字段是不可变的,因为系统无法为已经存在的服务重新分配 .spec.ClusterIP。如果你想改变 .spec.ipFamilies,则需要删除并重新创建服务。可以配置为以下值:

    • IPv4
    • IPv6
    • "IPv4","IPv6"
    • "IPv6","IPv4"
  • .spec.ipFamilyPolicy - IP 地址族策略。[7]

    • SingleStack - 默认值。单栈服务。控制面使用第一个配置的服务集群 IP 范围为服务分配集群 IP
    • PreferDualStack - 为服务分配 IPv4 和 IPv6 集群 IP 地址
    • RequireDualStack - 从基于在 .spec.ipFamilies 数组中第一个元素的地址族的 .spec.ClusterIPs 列表中选择 .spec.ClusterIP
  • .spec.clusterIPs - IP 的主要字段,值根据 ipFamilies, ipFamilyPolicy 的配置计算而出,规则参考

  • .spec.clusterIP - IP 的次要字段,其取值从 .spec.ClusterIPs 计算而来。也可以通过此字段配置自定义的 IP 地址 [9]

  • .spec.type - 指定 Service 的类型,默认为 ClusterIP,可选值如下[8]

    • ClusterIP - 通过集群的内部 IP 暴露服务,选择该值时服务只能够在集群内部访问。 这也是默认值。
    • NodePort - 通过每个节点上的 IP 和静态端口(NodePort,默认范围 30000-32767)暴露服务。NodePort 服务会路由到自动创建的 ClusterIP 服务,此种类型,服务可通过节点 ip 和 NodePort (nodeIP:.spec.ports[*].nodePort)对外访问,也可以通过 .spec.clusterIP:.spec.ports[*].port 对外可见。1.24 及以后版本,nodePort 对应的端口不会在节点上监听
    • LoadBalancer - 使用云提供商的负载均衡器向外部暴露服务。 外部负载均衡器可以将流量路由到自动创建的 NodePort 服务和 ClusterIP 服务上。
    • ExternalName - 通过返回 CNAME 和对应值,可以将服务映射到 externalName 字段的内容(例如,foo.bar.example.com)。 无需创建任何类型代理。
  • .spec.externalTrafficPolicy - 控制来自于外部的流量是如何路由的, 可选值有 ClusterLocal

    • Cluster - 将外部流量路由到所有就绪的端点
    • Local - 只路由到当前节点上就绪的端点,如果当前节点上没有就绪的端点,kube-proxy 不会转发请求相关服务的任何流量
  • .spec.internalTrafficPolicy - 控制内部来源的流量是如何转发的,可选值有 ClusterLocal

Kubernetes 为该服务分配一个 IP 地址(有时称为 “集群 IP”),该 IP 地址由服务代理使用。

Pod 中的端口定义是有名字的,你可以在 ServicetargetPort 属性中引用这些名称。 例如,我们可以通过以下方式将 ServicetargetPort 绑定到 Pod 端口:

apiVersion: v1
kind: Pod
metadata:
name: nginx
labels:
app.kubernetes.io/name: proxy
spec:
containers:
- name: nginx
image: nginx:stable
ports:
- containerPort: 80
name: http-web-svc

---
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
selector:
app.kubernetes.io/name: proxy
ports:
- name: name-of-service-port
protocol: TCP
port: 80
targetPort: http-web-svc

没有选择算符的 Service

由于选择算符的存在,服务最常见的用法是为 Kubernetes Pod 的访问提供抽象, 但是当与相应的 Endpoints 对象一起使用且没有选择算符时, 服务也可以为其他类型的后端提供抽象,包括在集群外运行的后端 [6]

定义没有选择算符的 Service 实例:

apiVersion: v1
kind: Service
metadata:
name: my-service
spec:
ports:
- protocol: TCP
port: 80
targetPort: 9376

由于此服务没有选择算符,因此不会自动创建相应的 Endpoints 对象。 你可以通过手动添加 Endpoints 对象,将服务手动映射到运行该服务的网络地址和端口:

apiVersion: v1
kind: Endpoints
metadata:
# 这里的 name 要与 Service 的名字相同
name: my-service
subsets:
- addresses:
- ip: 192.0.2.42
ports:
- port: 9376

当你为某个 Service 创建一个 Endpoints 对象时,你要将新对象的名称设置为与 Service 的名称相同。

说明
端点 IPs 必须不可以 是:本地回路(IPv4 的 127.0.0.0/8, IPv6 的 ::1/128) 或本地链接(IPv4 的 169.254.0.0/16 和 224.0.0.0/24,IPv6 的 fe80::/64)。

端点 IP 地址不能是其他 Kubernetes 服务的集群 IP,因为 kube-proxy 不支持将虚拟 IP 作为目标。

无头服务(Headless Services)

有时不需要或不想要负载均衡,以及单独的 Service IP。 遇到这种情况,可以通过指定 Cluster IP(spec.clusterIP)的值为 None 来创建 Headless Service[14]

创建 Headless Services

apiVersion: v1
kind: Service
metadata:
name: kubia-headless
namespace: default
spec:
clusterIP: None #这使得服务成为 headless service
ports:
- port: 80
targetPort: 8080
selector:
app: kubia

使用此配置创建的 Headless Services,系统不会为其分配 CLUSTER-IP

当通过此服务的 FQDN kubia-headless.default.svc.cluster.local 解析时,系统会返回此服务 选择算符 .spec.selector 选择的 就绪 的后端 POD 的 IP。

如果配置的是 不带选择算符的服务解析规则如是,如果查找到了和 Service 的 name 一样的 endpoint,则解析到此 endpoint

Ingress

Ingress 可为 Service 提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS,以及基于名称的虚拟托管。

Ingress 不会公开任意端口或协议。 将 HTTP 和 HTTPS 以外的服务公开到 Internet 时,通常使用 Service.Type=NodePortService.Type=LoadBalancer 类型的 Service

你必须拥有一个 Ingress 控制器 才能满足 Ingress 的要求。 仅创建 Ingress 资源本身没有任何效果。

安装 ingress-nginx controller

wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.3.1/deploy/static/provider/cloud/deploy.yaml

编辑 ingress-nginxDeployment 配置文件,在 Deployment 中的 .spec.template.spec 下添加字段 hostNetwork: true,以使 ingress-nginx-controller 可以使用节点的主机网络提供对外访问

创建 Ingress 对象

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: example-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
ingressClassName: nginx
rules:
- host: hello-world.info
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port:
number: 8080

配置完成后,查看 Ingress 资源

$ kubectl get ingress -o wide
NAME CLASS HOSTS ADDRESS PORTS AGE
example-ingress nginx hello-world.info 80 131m

将域名解析到 ingress-nginx-controller 所在节点的 IP,即可在外部访问服务。

为了将 ingress-nginx-controller 固定启动在边缘节点,可以使用 DaemonSet 替代 Deployment

Ingress Controller 不会将请求转发给 Service,只通过 Service 获取到 Endpoints,从而决定将请求转发到哪个 Pod。从流量的流经路径看,流量是直接从 Ingress Controller 发送到了 Pod

脚注