创建 Service 和 Ingress

本文档介绍了如何在用户集群、混合集群或独立集群中为 Google Distributed Cloud 创建 Kubernetes Ingress 对象。一个 Ingress 与一个或多个 Service 相关联,每个 Service 与一组 Pod 相关联。

创建 Deployment

如需创建 Deployment,请按以下步骤操作:

  1. 创建 Deployment 清单:

    apiVersion: apps/v1
    kind: Deployment
    metadata:
      name: hello-deployment
    spec:
      selector:
        matchLabels:
          greeting: hello
      replicas: 3
      template:
        metadata:
          labels:
            greeting: hello
        spec:
          containers:
          - name: hello-world
            image: "gcr.io/google-samples/hello-app:2.0"
            env:
            - name: "PORT"
              value: "50000"
          - name: hello-kubernetes
            image: "gcr.io/google-samples/node-hello:1.0"
            env:
            - name: "PORT"
              value: "8080"
    

    在本练习中,您需要了解 Deployment 清单的以下要点:

    • 属于 Deployment 的每个 Pod 都具有 greeting: hello 标签。

    • 每个 Pod 都有两个容器。

    • env 字段指定 hello-app 容器侦听 TCP 端口 50000,node-hello 容器侦听 TCP 端口 8080。对于 hello-app,您可以通过查看源代码来查看 PORT 环境变量的效果。

  2. 将此清单复制到名为 hello-deployment.yaml 的文件。

  3. 创建 Deployment:

    kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f hello-deployment.yaml
    

    CLUSTER_KUBECONFIG 替换为您的集群的 kubeconfig 文件的名称。

使用 Service 公开 Deployment

如需为客户端提供稳定的方法来向 Deployment 的 Pod 发送请求,请创建一个 Service。

  1. 创建向集群内的客户端公开 Deployment 的 Service 清单:

    apiVersion: v1
    kind: Service
    metadata:
      name: hello-service
    spec:
      type: ClusterIP
      selector:
        greeting: hello
      ports:
      - name: world-port
        protocol: TCP
        port: 60000
        targetPort: 50000
      - name: kubernetes-port
        protocol: TCP
        port: 60001
        targetPort: 8080
    
  2. 将此清单复制到名为 hello-service.yaml 的文件。

  3. 创建 Service:

    kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f hello-service.yaml
    

    CLUSTER_KUBECONFIG 替换为您的集群的 kubeconfig 文件的名称。

  4. 查看 Service:

    kubectl --kubeconfig CLUSTER_KUBECONFIG get service hello-service --output yaml
    

    输出显示已提供给 Service 的 clusterIP 值。例如:

    apiVersion: v1
    kind: Service
    metadata:
      annotations:
        ...
    spec:
      clusterIP: 10.96.14.249
      clusterIPs:
      - 10.96.14.249
      ipFamilies:
      - IPv4
      ipFamilyPolicy: SingleStack
      ports:
      - name: world-port
        port: 60000
        protocol: TCP
        targetPort: 50000
      - name: kubernetes-port
        port: 60001
        protocol: TCP
        targetPort: 8080
      selector:
        greeting: hello
      sessionAffinity: None
      type: ClusterIP
    status:
      loadBalancer: {}
    

    在上面的输出中,ports 字段是一个 ServicePort 对象数组:一个对象名为 world-port,一个对象名为 kubernetes-port。如需详细了解 Service 字段,请参阅 Kubernetes 文档中的 ServiceSpec

    以下是客户端调用 Service 的方式:

    • 使用 world-port在某个集群节点上运行的客户端会向 port 上的 clusterIP(例如 10.96.14.249:60000)发送请求。Ingress 控制器会将请求转发到 targetPort 上的成员 Pod(例如 POD_IP_ADDRESS:50000,其中 POD_IP_ADDRESS 是成员 Pod 的 IP 地址)。

    • 使用 kubernetes-port在某个集群节点上运行的客户端会向 port 上的 clusterIP (10.96.14.249:60001) 发送请求。Ingress 控制器会将请求转发到 targetPort 上的成员 Pod (POD_IP_ADDRESS:8080)。

Ingress 组件

以下是与 Ingress 相关的一些集群组件:

  • istio-ingress Deployment。这是 Ingress 代理。Ingress 代理会根据 Ingress 对象中指定的规则将流量转发到内部 Service。

  • istio-ingress Service。此 Service 会公开 istio-ingress Deployment。

  • istiod Deployment。这是 Ingress 控制器。Ingress 控制器会监视 Ingress 对象的创建,并相应地配置 Ingress 代理。

所有这些 Istio 集群内组件都安装在 gke-system 命名空间中。此命名空间与完整的 Istio/Cloud Service Mesh 安装不冲突。

创建 Ingress

按照以下步骤创建 Ingress:

  1. 创建 Ingress 清单:

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: my-ingress
    spec:
      rules:
      - http:
          paths:
          - path: /greet-the-world
            pathType: Exact
            backend:
              service:
                name: hello-service
                port:
                  number: 60000
          - path: /greet-kubernetes
            pathType: Exact
            backend:
              service:
                name: hello-service
                port:
                  number: 60001
    
  2. 将此清单复制到名为 my-ingress.yaml 的文件。

  3. 创建 Ingress:

    kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f my-ingress.yaml
    

创建用户集群时,您需要在集群配置文件中指定 loadbalancer.ingressVIP 值。此 IP 地址是在集群负载均衡器上配置的。创建 Ingress 时,Ingress 会获得与其外部 IP 地址相同的 VIP 地址。

当客户端向您的用户集群 Ingress VIP 地址发送请求时,请求将路由到您的负载均衡器。负载均衡器使用 istio-ingress Service 将请求转发到在用户集群中运行的 Ingress 代理。 Ingress 代理会根据请求网址中的路径将请求转发到不同的后端。

/greet-the-world 路径

Ingress 清单中有一条规则指出,/greet-the-world 路径与 serviceName: hello-serviceservicePort: 60000 相关联。您应该还记得,60000 是 hello-service Service 的 world-port 部分中的 port 值。

- name: world-port
    port: 60000
    protocol: TCP
    targetPort: 50000

Ingress Service 将请求转发到 clusterIP:50000。然后,请求会转到 hello-service Service 的一个成员 Pod。该 Pod 中侦听端口 50000 的容器会显示 Hello World! 消息。

/greet-kubernetes 路径

Ingress 清单中有一条规则指出,/greet-kubernetes 路径与 serviceName: hello-serviceservicePort: 60001 相关联。您应该还记得,60001 是 hello-service Service 的 kubernetes-port 部分中的 port 值。

- name: kubernetes-port
    port: 60001
    protocol: TCP
    targetPort: 8080

Ingress Service 将请求转发到 clusterIP:8080。然后,请求会转到 hello-service Service 的一个成员 Pod。该 Pod 中监听端口 8080 的容器会显示 Hello Kubernetes! 消息。

测试 Ingress

  1. 使用 /greet-the-world 路径测试 Ingress:

    curl CLUSTER_INGRESS_VIP/greet-the-world
    

    CLUSTER_INGRESS_VIP 替换为 Ingress 的外部 IP 地址。

    输出结果会显示 Hello, world! 消息:

    Hello, world!
    Version: 2.0.0
    Hostname: ...
    
  2. 使用 /greet-kubernetes 路径测试 Ingress:

    curl CLUSTER_INGRESS_VIP/greet-kubernetes
    

    输出结果会显示 Hello, Kubernetes! 消息:

    Hello Kubernetes!
    

停用捆绑式 Ingress

与 Google Distributed Cloud 捆绑的 Ingress 功能仅支持入站流量功能。您可以选择与 IstioCloud Service Mesh 集成。这些产品具有功能全面的服务网格的额外优势,例如双向传输层安全协议 (mTLS)、能够管理服务之间的身份验证以及工作负载可观测性。如果与 Istio 或 Cloud Service Mesh 集成,我们建议您停用捆绑的 Ingress 功能。

您可以使用集群配置文件中的 spec.clusterNetwork.bundledIngress 字段启用或停用捆绑式 Ingress。此字段仅适用于 1.13.0 版及更高版本的集群。bundledIngress 字段默认为 true,不存在于生成的集群配置文件中。此字段是可变的,可在创建或更新 1.13.0 版或更高版本的集群时更改。

  • 如需停用捆绑的 Ingress 功能,请将 bundledIngress 字段添加到集群配置文件的 clusterNetwork 部分,并将其值设置为 false,如以下示例所示:

    apiVersion: v1
    kind: Namespace
    metadata:
      name: cluster-hybrid-basic
    ---
    apiVersion: baremetal.cluster.gke.io/v1
    kind: Cluster
    metadata:
      name: hybrid-basic
      namespace: cluster-hybrid-basic
    spec:
      type: hybrid
      profile: default
      anthosBareMetalVersion: 1.13.0
      gkeConnect:
        projectID: project-fleet
      controlPlane:
        nodePoolSpec:
          nodes:
          - address: 10.200.0.2
      clusterNetwork:
        bundledIngress: false
        pods:
          cidrBlocks:
          - 192.168.0.0/16
        services:
          cidrBlocks:
          - 10.96.0.0/20
    ...
    

为 Ingress 设置 HTTPS

如果您想要接受来自客户端的 HTTPS 请求,则 Ingress 代理必须具有证书,这样才能向客户端证明其身份。此代理还必须具有私钥才能完成 HTTPS 握手。

以下示例使用这些实体:

  • Ingress 代理:参与 HTTPS 握手,然后将数据包转发到 hello-service Service 的成员 Pod。

  • hello-service Service 的网域:Example Org 中的 altostrat.com

请按照以下步骤操作:

  1. 创建根证书和私钥。此示例使用 Root CA Example Org 中的根证书授权机构 root.ca.example.com

    openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -subj \
        '/O=Root CA Example Inc./CN=root.ca.example.com' -keyout root-ca.key \
        -out root-ca.crt
    
  2. 创建证书签名请求:

     openssl req -out server.csr -newkey rsa:2048 -nodes -keyout server.key -subj \
         "/CN=altostrat.com/O=Example Org"
    
  3. 为 Ingress 代理创建服务证书。

    openssl x509 -req -days 365 -CA root-ca.crt -CAkey root-ca.key -set_serial 0 \
        -in server.csr -out server.crt
    

    现在,您已经创建了以下证书和密钥:

    • root-ca.crt:根 CA 的证书
    • root-ca.key:根 CA 的私钥
    • server.crt:Ingress 代理的服务证书
    • server.key:Ingress 代理的私钥
  4. 创建包含服务证书和密钥的 Kubernetes Secret。

    kubectl create secret tls example-server-creds --key=server.key --cert=server.crt \
        --namespace gke-system
    

    生成的 Secret 名为 example-server-creds

创建 Deployment 和 Service

如果您已在本指南的 HTTP 部分中创建了 Deployment 和 Service,请保留它们。如果您尚未创建,请立即按照针对 HTTP 所述的步骤进行创建。

创建 Ingress

为 HTTPS 创建 Ingress 与为 HTTP 创建 Ingress 类似,但 HTTPS 的 Ingress 规范包含一个用于指定主机和 Secret 的 tls 部分。tls 部分中的 hosts 需要与 rules 部分中的 host 明确匹配。

如果您的后端 Service 位于单独的命名空间中,则您需要在与 Ingress 相同的命名空间中创建 ExternalName 类型的 Service,以将流量路由到后端 Service。

为 HTTPS 或 HTTP 创建 Ingress 的总体步骤相同,只是清单文件中的配置内容有所不同:

  1. 如果您之前在本文档的 HTTP 部分中创建了 Ingress,请先删除该 Ingress,然后再继续操作。

    kubectl --kubeconfig CLUSTER_KUBECONFIG delete ingress my-ingress
    
  2. 如需为您之前创建的 Service 处理流量,请创建一个具有 tls 部分的新 Ingress 清单:

    tls 配置可在客户端和 Ingress 代理之间启用 HTTPS。

    apiVersion: networking.k8s.io/v1
    kind: Ingress
    metadata:
      name: my-ingress-2
    spec:
      tls:
      - hosts:
        - altostrat.com
        secretName: example-server-creds
      rules:
      - host: altostrat.com
        http:
          paths:
          - path: /greet-the-world
            pathType: Exact
            backend:
              service:
                name: hello-service
                port:
                  number: 60000
          - path: /greet-kubernetes
            pathType: Exact
            backend:
              service:
                name: hello-service
                port:
                  number: 60001
    
  3. 将该清单保存到名为 my-ingress-2.yaml 的文件中,然后创建 Ingress:

    kubectl apply --kubeconfig CLUSTER_KUBECONFIG -f my-ingress-2.yaml
    
  4. 通过测试以下内容,确认 Ingress 已创建完毕且运行正常:

    • 测试 /greet-the-world 路径:

      curl -v --resolve altostrat.com:443:CLUSTER_INGRESS_VIP \
          https://altostrat.com/greet-the-world \
          --cacert root-ca.crt
      

      输出:

      Hello, world!
      Version: 2.0.0
      Hostname: hello-deployment-5ff7f68854-wqzp7
      
    • 测试 /greet-kubernetes 路径:

      curl -v --resolve altostrat.com:443:CLUSTER_INGRESS_VIP \
          https://altostrat.com/greet-kubernetes --cacert root-ca.crt
      

      输出:

      Hello Kubernetes!
      

创建 LoadBalancer 服务

LoadBalancer 类型的服务是在集群外部公开工作负载的另一种方法。如需查看有关创建 LoadBalancer 类型的 Service 的说明和示例,请参阅“部署应用”中的“创建 LoadBalancer 类型的 Service”

清理

  1. 删除 Ingress:

    kubectl --kubeconfig CLUSTER_KUBECONFIG delete ingress INGRESS_NAME
    

    INGRESS_NAME 替换为 Ingress 的名称,例如 my-ingressmy-ingress-2

  2. 删除 Service:

    kubectl --kubeconfig CLUSTER_KUBECONFIG delete service hello-service
    
  3. 删除 Deployment:

    kubectl --kubeconfig CLUSTER_KUBECONFIG delete deployment hello-deployment
    
  4. 删除 LoadBalancer 服务:

    kubectl --kubeconfig CLUSTER_KUBECONFIG delete service service-does-not-use-nodeports