25

.NET Core + Kubernetes:Service

 3 years ago
source link: http://beckjin.com/2020/05/23/aspnet-k8s-service/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

通过 .NET Core + Kubernetes:Deployment 文章的介绍,我们可以通过 Deployment 控制器快速创建一组 Pod 来提供服务,每个 Pod 都会被分配一个集群内可见的虚拟 IP 地址,然后通过一个独立的 Endpoint(Pod IP + ContainerPort)进行访问。但在提供服务时,并不能依赖 Pod 的 Endpoint,首先 Pod IP 会随着 Pod 的重建而变化,另外同一组 Pod 更希望是以整体对外提供高可用服务,组内的 Pod 在进行动态伸缩、滚动更新等操作后并不能影响服务稳定性。

因此,Kubernetes 中的 Service 对象就是解决此问题的核心,Service 代理 Pod 集合对外表现是为一个访问入口,它提供了一个虚拟的 IP 地址(ClusterIP)和端口,来自 ClusterIP + 端口 的请求将被负载均衡器 (kube-proxy)转发到后端某个 Pod 中的容器。所以借助 Service 的能力,非常方便的实现了服务发现与负载均衡。

本文将主要介绍 Kubernetes 中各 Service 类型的使用,目前有以下四种类型:

  • ClusterIP:默认类型,自动分配一个仅集群内部可以访问的虚拟 IP,也可使用 ClusterIP 字段指定固定 IP,选择此类型意味着只想这个服务在集群内部才可以被访问

  • NodePort:在 ClusterIP 基础上,在集群的每一个节点绑定一个端口,这样就可以通过任意的 :NodePort 来访问服务

  • LoadBalancer:在 NodePort 的基础上,通过创建一个外部的负载均衡器,将流量转发到每个节点 :NodePort。因为如果外部所有客户端都访问一个 NodeIP,该节点的压力将会很大,LoadBalancer 则可解决此问题

  • ExternalName:把集群外部的服务引入到集群内部来,在集群内部直接使用。没有任何类型代理被创建,这只有 kubernetes 1.7 或更高版本的 kube-dns 才支持

下面分别对这几种类型的使用方式进行介绍,在创建 Service 之前,还是先通过 Deployment 控制器创建一组 Pod,配置文件 k8sdemo-deployment.yaml 如下:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: k8sdemo-deployment
spec:
  replicas: 3
  selector:
    matchLabels:
      name: k8sdemo
  template:
    metadata:
      labels:
        name: k8sdemo
    spec:
      containers:
      - name: k8sdemo
        image: beckjin/k8sdemo:1.0.0
        ports:
        - containerPort: 80
        imagePullPolicy: IfNotPresent

ClusterIP 类型

创建 k8sdemo-service.yaml 文件,配置如下,主要是 portsselector 字段的设置,指定了 80 端口( port ) 映射到 Pod ContainerPort ( targetPort ) 端口,最终将通过 ClusterIP:80 访问服务。 selector 字段指定将 label 含 name:k8sdemo 的 Pod 作为这个 Service 指向的目标服务。

apiVersion: v1
kind: Service
metadata:
  name: k8sdemo-service
spec:
  ports:
  - port: 80                 # Service Port
    targetPort: 80           # Pod ContainerPort
  selector:
    name: k8sdemo
   # clusterIP: 10.1.19.92   # 取消注释指定IP

执行命令 kubectl apply -f k8sdemo-service.yaml 创建 Service,然后通过 kubectl get service 查看服务状态:

INBZ7zY.png!web

从上图可以看出 k8sdemo-service 被分配的 ClusterIP 是 10.1.19.96 ,所以可以在服务器上通过 curl http://10.1.19.96/WeatherForecast 来访问接口。

jEBrqy3.png!web

因为 ClusterIP 默认是自动分配的,所以每次重建会变化,如果需要固定,可通过 ClusterIP 字段设置。对于只需在集群内部提供的服务,ClusterIP 类型已足够。

NodePort 类型

基于 ClusterIP 类型中使用的配置文件 k8sdemo-service.yaml ,增加 type: NodePort 配置,重新创建 Service,如下:

apiVersion: v1
kind: Service
metadata:
  name: k8sdemo-service
spec:
  ports:
  - port: 80
    targetPort: 80
    # nodePort: 30080   # 取消注释指定端口
  selector:
    name: k8sdemo
  type: NodePort

FNrMZvF.png!web

从上图可以看出,除了类型变了, PORT(S)也变成了 80:30231/TCP ,即将 Service 的 80 端口与集群中各节点的 30231 端口进行映射,所以最终可以通过集群内任意的 NodeIP:30231 来访问,整个过程为: Client > NodeIP:NodePort > ClusterIP:ServicePort > PodIP:ContainerPort 。NodePort 默认分配的是 30000-32767 范围内随机选择的一个端口,实际使用时可以通过 nodePort 字段指定。请求结果如下:

iUNNRrq.png!web

注:192.168.124.10 是集群内某一台的 IP

LoadBalancer 类型

通过 NodePort 类型的使用介绍,已经了解可以通过 NodeIP:NodePort 方式来服务访问,而且 NodeIP 可以是集群内任意任何一台的 IP。而 LoadBalancer 则是在外层附加的负载均衡器,使请求能分摊到集群内各个节点上。

MetalLB 搭建

要使用 LoadBalancer 类型会稍微复杂一些,并不能只单纯的修改配置文件,因为一般自建的 Kubernetes 集群默认并不支持 LoadBalancer,所以它需要借助外部的负载均衡器来实现,这里将使用 MetalLB ( v0.9.3 ),安装请参考 Installation By Manifest ,步骤不复杂,但需要确保依赖镜像下载顺利,完成后查看 Pod 状态:

JVjQBrj.png!web

另外需要为 Metallb 设置地址池以及协议相关配置,Metallb 会监控服务对象的变化,当有新的 LoadBalancer 服务运行,但没有可申请的负载均衡器时,就会从配置的地址池中分配一个给该服务。这里以 Metallb Layer2 工作模式为例( Metallb 支持 Layer2/BGP 两种工作模式 ),创建一个资源类型为 ConfigMap 的配置文件 metallb-layer2-config.yaml ,内容如下:

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.124.200-192.168.124.210  # IP 地址范围需与自己的集群环境对应

Layer2 工作模式原理图:

iqa2ayN.jpg!web

配置修改

有了以上的准备工作后,只需要在 Service 配置文件将 type 修改为 LoadBalancer ,然后重新创建 Service,如下:

apiVersion: v1
kind: Service
metadata:
  name: k8sdemo-service
spec:
  ports:
  - port: 80
    targetPort: 80
  selector:
    name: k8sdemo
  type: LoadBalancer

mUzayeV.png!web

从上图可以看出,TYPE 已是 LoadBalancer,另外 EXTERNAL-IP 被分配为地址池中的 192.168.124.200 ,接下来就可以通过这个 IP 进行访问了,结果如下:

eQVrEbb.png!web

ExternalName

ExternalName 类型比较特殊,它没有 selector,也没有定义任何的端口, 对于运行在集群外部的服务,它通过返回该外部服务的别名这种方式来提供服务,如下:

apiVersion: v1
kind: Service
metadata:
  name: k8sdemo-external-service
spec:
  type: ExternalName
  externalName: mingdao.com

当访问 k8sdemo-external-service.default.svc.cluster.local 时,集群的 DNS 服务将返回值为 mingdao.com 的 CNAME 记录,访问这种类型的服务与其它的唯一不同的是重定向发生在 DNS 层,而且不会进行代理或转发。

进入 Kubernetes 集群的任意一个 Pod 中(必须是集群内部才可访问),如: kubectl exec -it k8sdemo-deployment-68cb864ff6-fzzdq -- /bin/bash ,执行 curl -L http://k8sdemo-external-service.default.svc.cluster.local/ 即会重定向请求到 mingdao.com ,结果如下:

RBJfAnJ.png!web


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK