Kubernetes 实战 —— 05. 服务:让客户端发现 pod 并与之通信(上)

简介

pod 通常需要对来自集群内部其他 pod ,以及来自集群外部的客户端的 HTTP 请求作出响应,所以需要一种寻找其他 pod 的方法来使用其他 pod 提供的服务。

在 Kubernetes 中通过服务 (service) 解决以下问题:

pod 是短暂的: pod 随时启动和关闭
Kubernetes 在 pod 启动前会给已经调度到节点上的 pod 分配 IP 地址:客户端不能提前知道 pod 的 IP 地址
水平伸缩意味着多个 pod 可能提供相同的服务:每个 pod 都有自己的 IP 地址

介绍服务

Kubernetes 服务是一种为一组功能相同的 pod 提供但以不变的接入点的资源。当服务存在时,它的 IP 地址和端口不会改变。与服务建立的连接会被路由到提供该服务的任意一个 pod 上。

在这里插入图片描述
创建服务
服务使用标签选择器(03. pod: 运行于 Kubernetes 中的容器 中介绍过标签选择器及使用方式)来指定属于同一组的 pod在这里插入图片描述](https://jq.qq.com/?_wv=1027&k=0IsBuUb0)
通过 kubectl expose 创建服务

创建服务的最简单的方法就是通过 kubectl expose ,在 02. 开始使用 Kubernetes 和 Docker 中就使用该方法创建服务来暴露 Deployment 。

通过 YAML 描述文件来创建服务

为了将创建服务,我们需要使用以下 kubia-svc.yaml 描述文件进创建。

# 遵循 v1 版本的 Kubernetes API
apiVersion: v1
# 资源类型为 Service
kind: Service
metadata:
  # Service 的名称
  name: kubia
spec://加入Java开发交流君样:756584822一起吹水聊天
  # 该服务可用的端口
  ports:
    # 第一个可用端口的名字
    - name: http
      # 可用端口为 80
      port: 80
      # 服务将连接转发到容器的 8080 端口
      targetPort: 8080
    # 第二个可用端口的名字
    - name: https
      # 可用端口为 443
      port: 443
      # 服务将连接转发到容器的 8443 端口
      targetPort: 8443
  # 具有 app=kubia 标签的 pod 都属于该服务
  selector:
    app: kubia

kubectl create -f kubia-svc.yaml: 创建服务

kubectl get services: 查看当前所有服务

NAME         TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
kubernetes   ClusterIP   10.96.0.1        <none>        443/TCP          12d
kubia        ClusterIP   10.111.241.144   <none>        80/TCP,443/TCP   5s
//加入Java开发交流君样:756584822一起吹水聊天

可以发现刚刚启动的服务已经被分配了一个内部集群 IP ,并且对外暴露了两个端口。服务的主要目标就是使集群内部的其他 pod 可以访问当前这组 pod ,但通常也希望对外暴露服务。
从集群内部测试服务

可以通过以下三种方式向服务发送请求:

创建一个 pod ,它将请求发送到服务的集群 IP 并记录响应。可以通过 kubectl logs 查看 pod 日志检查服务的响应
使用 ssh 远程登录到其中一个 Kubernetes 节点上,然后使用 curl 命令
通过 kubectl exec 命令在一个已经存在的 pod 中执行 curl 命令
在运行的容器中远程执行命令 P125

kubectl exec kubia-9495d9bf5-2mmv2 -- curl -s 10.111.241.144: 在 pod kubia-9495d9bf5-2mmv2 运行命令 curl -s 10.111.241.144

– 代表 kubectl 命令项的结束,在 – 之后的内容是指在 pod 内部需要执行的命令。如果需要执行的命令没有以 - 开始的参数,那么 – 不是必须的。

在这里插入图片描述
配置服务上的会话亲和性

如果希望特定客户端产生的所有请求每次都指向同一个 pod ,可以设置服务的 spec.sessionAffinity 属性为 ClientIP ,而不是默认值 None 。

参考文献

spec:
  sessionAffinity: ClientIP

这种方式会使服务代理将来自同一个客户端 IP 的所有请求转发至同一个 pod 。 Kubernetes 仅支持两种形式的会话亲和性服务: None 和 ClientIP 。
同一个服务暴露多个端口

我们在前面已将创建了暴露多个端口的服务,这样通过一个集群 IP ,使用一个服务就可以将多个端口全部暴露出来。

注意:在创建一个有多个端口的服务的时候,必须给每个端口指定名字。

注意:标签选择器应用于整个服务,不能对每个端口做单独的配置。如果不同的 pod 有不同的端口映射关系,需要创建两个服务。

使用命名的端口

我们可以将 pod 端口定义改为如下形式:

kind: Pod
spec:
  containers:
    - name: kubia
      ports:
        # 应用监听端口 8080 ,并命名为 http
        - name: http
          containerPort: 8080
        # 应用监听端口 8443 ,并命名为 https
        - name: https
          containerPort: 8443
          //加入Java开发交流君样:756584822一起吹水聊天

然后我们就可以将在服务中引用命名的端口:

kind: Service
spec:
  ports://加入Java开发交流君样:756584822一起吹水聊天
    - name: http
      port: 80
      targetPort: 8080
    - name: https
      port: 443
      targetPort: https

采用命名端口的方式可以使得更换 pod 端口时无须更改服务的 spec ,并且不同的 pod 可以使用不同的端口。

服务发现
现在可以通过一个单一稳定的 IP 地址访问到 pod ,但是还没法让客户端 pod 知道服务的 IP 和端口,所以我们需要配置进行发现服务。

通过环境变量发现服务

在 pod 开始运行时, Kubernetes 会初始化一系列环境变量指向现在存在的服务。如果 pod 先于服务启动,那么可以先把这些 pod 删除,等待 Development 自动创建新的 pod ,这样所有 pod 都能拥有现存服务的的相关环境变量了。

kubectl exec kubia-9495d9bf5-4jbtf env: 查看指定 pod 的环境变量,可以发现其中有 kubia 和 kubernetes 服务的 IP 地址和端口号的环境变量

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
HOSTNAME=kubia-9495d9bf5-4jbtf

//加入Java开发交流君样:756584822一起吹水聊天
KUBERNETES_SERVICE_HOST=10.96.0.1
KUBERNETES_SERVICE_PORT_HTTPS=443
KUBIA_SERVICE_HOST=10.111.232.152
KUBIA_SERVICE_PORT=80
KUBIA_SERVICE_PORT_HTTP=80
KUBIA_SERVICE_PORT_HTTPS=443

注意:环境变量中,服务名作为前缀时,所有字母变为变为大写,且服务名称中的 - 将被转换为

通过 DNS 发现服务

命名空间 kube-system 下有几个以 coredns 为前缀的 pod 。这些 pod 运行 DNS 服务,在集群中的其他 pod 都被配置成使用其作为 dns (Kubernetes 通过修改每个容器的 etc/resolv.conf 文件实现 ) 。运行在 pod 上的进程 DNS 查询都会被 Kubernetes 自身的 DNS 服务器响应,该服务器知道系统中运行的所有服务。

注意: pod 是否使用内部的 DNS 服务器是根据 pod 的 spec.dnsPolicy 属性决定的。

每个服务从内部 DNS 服务器中获得一个 DNS 条目,客户端的 pod 在知道服务名称的情况下可以通过全限定域名 (FQDN) 来访问。

通过 FQDN 连接服务

我们可以通过 kubia.default.svc.cluster.local 来访问 kubia 服务。其中 kubia 对应于服务名称, default 表示服务所在的命名空间, svc.cluster.local 是所在集群本地服务名称中使用的可配置集群域后缀。 P131

注意:客户端仍然必须知道服务的端口号。如果服务没有使用标准端口号,那么客户端仍然需要从环境变量中获取端口号。

如果服务和客户端在同一个命名空间下,那么可是直接使用服务名(例如: kubia )指代服务。
在 pod 容器中运行 shell

kubectl exec -ti bash: 可以在一个 pod 容器上运行 bash (也可指定其他形式的 shell )

无法 ping 通服务 IP 的原因

服务的集群 IP 是一个虚拟 IP ,并且只有在与服务端口结合时才有意义。将在后续文章中详细讲解。

连接集群外部的服务
在集群中运行的客户端 pod 可以像连接到内部服务一样连接到外部服务,这样做可以充分利用负载均衡和服务发现。

介绍服务 endpoints

服务并不是与 pod 直接相连的,而是通过 Endpoints 资源与 pod 连接。 Endpoints 资源就是暴露一个服务的 IP 地址和端口列表,和其他 Kubernetes 资源一样。
kubectl get endpoints kubia: 查看 kubia 的 endpoints 基本信息

NAME    ENDPOINTS                                                  AGE
kubia   10.88.0.2:8443,10.88.0.3:8443,10.88.0.3:8443 + 3 more...   3d

服务中在 spec.selector 定义了 pod 选择器,但是在重定向传入连接时不会直接使用它。选择器用于构建 IP 和端口列表,然后存储在 Endpoints 资源中。当客户端连接到服务时,服务代理选择这些 IP 和端口对中的一个,并将传入连接重定向到该位置监听的服务器。

手动配置服务的 endpoints
如果将服务的 endpoints 与服务解耦,那么就可以手动配置和更新它们。

如果创建了不包含 pod 选择器的服务, Kubernetes 将不会创建 Endpoints 资源。这样就需要创建 Endpoints 资源来指定该服务的 endpoints 列表。

创建没有 pod 选择器的服务

使用以下描述文件 external-service.yaml 可以创建一个不指定 pod 选择器的服务。

# 遵循 v1 版本的 Kubernetes API
apiVersion: v1
# 资源类型为 Service
kind: Service
metadata:
  # Service 的名称
  name: external-service
spec:
  # 该服务可用的端口
  ports:
    # 第一个可用端口的名字
    - name: http
      # 可用端口为 80
      port: 80
      targetPort: http
    # 第二个可用端口的名字
    - name: https
      # 可用端口为 443
      port: 443
      targetPort: https

使用以下描述文件 external-service-endpoints.yaml 可以创建一个 Endpoints 资源。


# 遵循 v1 版本的 Kubernetes API
apiVersion: v1
# 资源类型为 Endpoints
kind: Endpoints
metadata:
  # Endpoints 的名称,与对应的 Service 名称一致
  name: external-service
# 该 Endpoints 的子集
subsets:
  # 第一个子集的地址信息
  - addresses:
      # 地址包含以下 ip 列表
      - ip: 11.11.11.11
      - ip: 22.22.22.22
    # 第一个子集的端口信息
    ports://加入Java开发交流君样:756584822一起吹水聊天
      # 每个 ip 可用的端口列表
      # 【注意】这个名字必须和服务端端口的名字对应
      - name: http
        port: 80
      - name: https
        port: 443\

Endpoints 对象需要与服务具有相同的名称,并包含该服务将要重定向的目标 IP 地址和端口列表。当服务和 Endpoints 都创建后,服务就会自动使用对应当 Endpoints ,并能够像具有 pod 选择器那样当服务正常使用
在这里插入图片描述
为外部服务创建别名
除了手动配置服务的 Endpoints 来代替公开外部服务的方法,还可以通过其完全限定域名 (FQDN) 来访问外部服务。

创建 ExternalName 类型的服务

通过以下描述文件 external-service-externalname.yaml 可以创建一个 ExternalName 类型的服务,这个服务会将请求转发到 spec.externalName 指定的实际服务的完全限定域名。

# 遵循 v1 版本的 Kubernetes API
apiVersion: v1
# 资源类型为 Service
kind: Service
metadata:
  # Service 的名称
  name: external-service
spec:
  # Service 的类型为 ExternalName
  type: ExternalName
  # 这个服务将所有请求都转发到 someapi.somecompany.com
  externalName: leetcode-cn.com
  # 该服务可用的端口
  ports:
    # 第一个可用端口的名字
    - name: http
      # 可用端口为 80
      port: 80
      # 使用 ExternalName 时, targetPort 将被忽略
    # 第一个可用端口的名字
    - name: https
      # 可用端口为 443
      port: 443//加入Java开发交流君样:756584822一起吹水聊天
      # 使用 ExternalName 时, targetPort 将被忽略

服务创建完成后, pod 可以通过 external-service(.default.svc.cluster.local) 域名(括号内的可不加)连接到外部服务,而不用使用外部服务的实际 FQDN 。这样允许修改服务的定义,并且在以后可以修改 externalName 指向到不同的服务,或者将类型变为 ClusterIP 并为服务创建 Endpoints 。

ExternalName 服务仅在 DNS 级别实施——为服务创建了简单 CNAME DNS记录。因此,连接到服务的客户端将直接连接到外部服务,完全绕过服务代理,所以这类型的服务不会获得集群 IP 。

注意: CNAME 记录指向完全限定的域名而不是 IP 地址。

image

最新2020整理收集的一些高频面试题(都整理成文档),有很多干货,包含mysql,netty,spring,线程,spring cloud、jvm、源码、算法等详细讲解,也有详细的学习规划图,面试题整理等,需要获取这些内容的朋友请加Q君样:756584822

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值