创建 Service 和 Ingress

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

准备工作

获取从集群到管理员工作站的 SSH 连接。

创建 Deployment

以下是 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 环境变量的效果。

将此清单复制到名为 hello-deployment.yaml 的文件,然后创建该 Deployment:

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

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

使用 Service 公开 Deployment

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

以下是将 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

将此清单复制到名为 hello-service.yaml 的文件,然后创建该 Service:

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

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

查看 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。该请求将被转发到 targetPort 上的成员 Pod。在此示例中为 POD_IP_ADDRESS:50000

  • 使用 kubernetes-port:在其中一个集群节点上运行的客户端会向 port 上的 clusterIP 发送请求。在此示例中为 10.96.14.249:60001。该请求将被转发到 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/Anthos Service Mesh 安装不冲突。

创建 Ingress

以下是 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

将此清单复制到名为 my-ingress.yaml 的文件,然后创建该 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

使用 /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: ...

使用 /greet-kubernetes 路径测试 Ingress:

curl CLUSTER_INGRESS_VIP/greet-kubernetes

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

Hello Kubernetes!

停用捆绑式 Ingress

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

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

以下示例集群配置文件展示了如何配置集群以停用捆绑式 Ingress 功能:

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

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

删除 Ingress:

kubectl --kubeconfig CLUSTER_KUBECONFIG delete ingress my-ingress

如需处理您之前创建的 Service 的流量,请创建一个具有 tls 部分的新 Ingress。这将在客户端和入站流量代理之间启用 HTTPS。

以下是 Ingress 的清单:

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

将该清单保存到名为 my-ingress-2.yaml 的文件中,然后创建 Ingress:

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

通过测试确认。

  • 测试 /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 类型是 NodePort 类型的扩展。因此,LoadBalancer 类型的 Service 具有集群 IP 地址以及一个或多个 nodePort 值。 默认情况下,Kubernetes 会将节点端口分配给 LoadBalancer 服务。这些分配可能会快速从分配给集群的 2,768 中耗尽可用的节点端口。如需保存节点端口,请通过将 LoadBalancer Service 规范中的 allocateLoadBalancerNodePorts 字段设置为 false 来停用负载均衡器节点端口分配。此设置可防止 Kubernetes 将节点端口分配给 LoadBalancer 服务。如需了解详情,请参阅 Kubernetes 文档中的停用负载均衡器 NodePort 分配

以下清单用于创建不使用任何节点端口的服务:

apiVersion: v1
kind: Service
metadata:
  name: service-does-not-use-nodeports
spec:
  selector:
    app: my-app
  type: LoadBalancer
  ports:
  - port: 8000
  # Set allocateLoadBalancerNodePorts to false
  allocateLoadBalancerNodePorts: false

清理

删除 Ingress:

kubectl --kubeconfig CLUSTER_KUBECONFIG delete ingress INGRESS_NAME

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

删除 Service:

kubectl --kubeconfig CLUSTER_KUBECONFIG delete service hello-service

删除 Deployment:

kubectl --kubeconfig CLUSTER_KUBECONFIG delete deployment hello-deployment

删除 LoadBalancer 服务:

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