Ingress

PPG007 ... 2022-4-8 About 4 min

# Ingress

Ingress 是从 Kubernetes 集群外部访问集群内部服务的入口,通常情况下,service 和 pod 仅在集群内部网络可以通过 IP 或者域名访问,

Ingress 可以配置不同的转发规则以根据不同的规则设置访问集群内不同的 Service 所对应的后端 Pods。

单纯的创建 Ingress 没有意义,需要一个 Ingress Controller 来实现 Ingress

Kubernetes 默认没有 Ingress Controller,官方维护的是 AWS、GCE 和 Nginx Ingress Controller,可以在这里 (opens new window)找到所有的可以选择的控制器,这里我们使用 Nginx Ingress Controller。

直接使用下面的命令安装即可:

kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.2/deploy/static/provider/cloud/deploy.yaml
1

但是需要注意,默认的配置文件中有一个 externalTrafficPolicy,这样的话外部只有访问 Ingress Controller 被分配到的节点的 IP 及 端口才能访问,注释掉即可从集群任意一个节点访问。

安装后,可以通过下面的命令获取端口的映射关系:

kubectl get service -n ingress-nginx
1

默认情况下,Nginx Ingress Controller 的 80 及 443 端口会被随机分配两个端口映射,在外部可以通过这两个端口进行访问。

# 创建 Ingress 路由

使用下面的配置文件创建一个简单的 http 后台 pod 的路由:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-http-demo
  namespace: example
spec:
  ingressClassName: nginx # 制定使用 Nginx Ingress Controller
  rules:
    - host: client.com # 如果使用这个域名访问集群,那么会对应到下面的路径
      http:
        paths:
          - path: /client # 如果路径为 /client 就进入下面的这个 client-service,例如:http://client.com/client
            pathType: Prefix # 前缀匹配模式
            backend:
              service:
                name: client-service # 服务名
                port:
                  number: 8000 # client-service 的端口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

应用后,即可通过 Ingress Controller 80 端口映射的端口从集群外部发起 http 请求了。

# 路径重写

修改 Ingress 资源配置文件:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-http-demo
  namespace: example
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /$1
spec:
  ingressClassName: nginx
  rules:
    - host: client.com
      http:
        paths:
          - path: /client(/|$)(.*)
            pathType: Prefix
            backend:
              service:
                name: client-service
                port:
                  number: 8000
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

Nginx Ingress Controller 的高级功能都可以通过添加 annotations 实现,上面添加了一个重写的 annotation,这样所有 path 中被 (.*) 捕获的内容都会被替换到 annotation 中设置的 $n 中,例如上面的例子中,重定向的结果示例如下:

  • client.com/client rewrites to client.com/。
  • client.com/client/ rewrites to client.com/。
  • client.com/client/new rewrites to client.com/new。

Tips

关于 Nginx Ingress 的 annotations 及其示例可见这里 (opens new window)

一些高级用法示例也可以参考这里 (opens new window)

# Nginx Ingress gRPC

现在我们会将一个已经实现的简单的 gRPC 服务通过 Ingress 暴露出来并在外部使用客户端进行调用。

实现并暴露服务后,我们需要为服务端生成自签名 TLS 证书,因为 Nginx Ingress Controller 对于 gRPC 只能通过其 443 端口代理,并且需要证书。

这里我们使用的域名是 server.com。

首先请确保系统中已安装 OpenSSL,然后创建下面的 /tmp/openssl.cnf 文件:

[ req ]
#default_bits           = 2048
#default_md             = sha256
#default_keyfile        = privkey.pem
distinguished_name      = req_distinguished_name
attributes              = req_attributes
req_extensions          = v3_req

[ req_distinguished_name ]
countryName                     = Country Name (2 letter code)
countryName_min                 = 2
countryName_max                 = 2
stateOrProvinceName             = State or Province Name (full name)
localityName                    = Locality Name (eg, city)
0.organizationName              = Organization Name (eg, company)
organizationalUnitName          = Organizational Unit Name (eg, section)
commonName                      = Common Name (eg, fully qualified host name)
commonName_max                  = 64
emailAddress                    = Email Address
emailAddress_max                = 64

[ req_attributes ]
challengePassword               = A challenge password
challengePassword_min           = 4
challengePassword_max           = 20

[v3_req]
# Extensions to add to a certificate request
basicConstraints = CA:FALSE
keyUsage = nonRepudiation, digitalSignature, keyEncipherment
subjectAltName = @alt_names

[alt_names]
DNS.1 = server.com
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

执行下面的命令签署证书请求:

openssl req -new -nodes -keyout grpc.key -out grpc.csr -config /tmp/openssl.cnf -subj "/C=CN/ST=Zhejiang/L=Hangzhou/O=AlibabaCloud/OU=ContainerService/CN=server.com"
1

执行下面的命令签署证书:

openssl x509 -req -days 3650 -in grpc.csr -signkey grpc.key -out grpc.crt -extensions v3_req -extfile /tmp/openssl.cnf
1

此时会生成三个文件:grpc.crt grpc.csr grpc.key。

执行下面的命令将证书添加到集群中 gRPC 服务所在的 namespace:

kubectl create secret tls grpc-secret --key grpc.key --cert grpc.crt -n example
1

然后通过下面的 Ingress 配置文件创建 gRPC Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-grpc-demo
  namespace: example
  annotations:
    nginx.ingress.kubernetes.io/backend-protocol: "GRPC" # 添加 GRPC 协议支持的 annotation
spec:
  ingressClassName: nginx
  rules:
    - host: server.com
      http:
        paths:
          - path: / # 对于 gRPC 这里就不要写路径了
            pathType: Prefix
            backend:
              service:
                name: demo-service
                port:
                  number: 8000
  tls:
    - secretName: grpc-secret
      hosts:
        - server.com # 证书对应的域名
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

将之前生成的 grpc.crt 证书文件拷贝到客户端,编写客户端启动函数:

func TestDemo(t *testing.T) {
	c, err := credentials.NewClientTLSFromFile("../ssl/grpc.crt", "server.com") // 读取自签名证书
	assert.NoError(t, err)
	cc, err := grpc.Dial("server.com:31837", grpc.WithTransportCredentials(c))
	assert.NoError(t, err)
	s := proto.NewDemoServiceClient(cc)
	m := map[string]int{}
	for i := 0; i < 10; i++ {
		resp, err := s.Demo(context.Background(), &proto.Empty{})
		assert.NoError(t, err)
		m[resp.Value] = 2
	}
	assert.GreaterOrEqual(t, len(m), 1) // 这里可以看到是有负载均衡的
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
Last update: April 8, 2022 11:57
Contributors: PPG007