Flomesh Ingress 使用实践(一)基础功能

时间:2023-02-01 16:59:45

背景

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是 HTTP。提供负载均衡、SSL 终结和基于名称的虚拟托管。为了让 Ingress 资源工作,集群必须有一个正在运行的 Ingress 控制器。

Ingress 控制器 通过监控集群中的 Ingress 资源,对 HTTP 负载均衡器进行配置。

FSM 是 Flomesh 的另一款开源产品,用于 Kubernetes 南北向的流量管理。FSM 以可编程代理 Pipy 为核心,提供了 Ingress 管理器、Gateway API* 实现、负载均衡器以及跨集群的服务注册发现等功能。

从这篇开始,我们将通过多篇文章以演示的方式对 Flomesh Ingress 功能进行介绍。

演示

创建集群

export INSTALL_K3S_VERSION=v1.23.8+k3s2
curl -sfL https://get.k3s.io | sh -s - --disable traefik --disable servicelb --write-kubeconfig-mode 644 --write-kubeconfig ~/.kube/config

部署架构

Flomesh Ingress 使用实践(一)基础功能

安装 Flomesh Ingress

通过 Helm 安装 fsm。

helm repo add fsm https://flomesh-io.github.io/fsm
helm repo update

helm install \
  --namespace fsm \
  --create-namespace \
  --set fsm.version=0.2.1-alpha.2 \
  --set fsm.ingress.tls.enabled=true \
  --set fsm.serviceLB.enabled=true \
  fsm fsm/fsm

示例应用

这里使用的示例应用同时提供了 HTTP 8000 和 HTTPS 8443 访问,有下面几个 path

  • / 返回一个简单的 HTML 页面
  • /hi 返回 200 Hi, there!
  • /api/private 返回 401 Staff only

为了提供 HTTPS,需要为应用先签发 ca 和服务端证书。

openssl genrsa 2048 > ca-key.pem

openssl req -new -x509 -nodes -days 365000 \
   -key ca-key.pem \
   -out ca-cert.pem \
   -subj '/CN=flomesh.io'

openssl genrsa -out server-key.pem 2048
openssl req -new -key server-key.pem -out server.csr -subj '/CN=example.com'
openssl x509 -req -in server.csr -CA ca-cert.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -days 365

部署示例应用,先创建 Secret 将证书和密钥保存在 secret 中并挂载到应用 pod 中。

kubectl create namespace httpbin
# mount self-signed cert to sample app pod via secret
kubectl create secret generic -n httpbin server-cert \
  --from-file=./server-cert.pem \
  --from-file=./server-key.pem

kubectl apply -n httpbin -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - port: 8443
    name: https
  - port: 8000
    name: http    
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
  template:
    metadata:
      labels:
        app: httpbin
    spec:
      containers:
        - name: pipy
          image: addozhang/httpbin:latest
          env:
            - name: PIPY_CONFIG_FILE
              value: /etc/pipy/tutorial/gateway/main.js
          ports:
            - containerPort: 8443
            - containerPort: 8000
          volumeMounts:
          - name: cert
            mountPath: "/etc/pipy/tutorial/gateway/secret"
            readOnly: true
      volumes:
      - name: cert
        secret:
          secretName: server-cert
EOF

基础配置

HTTP 入口

下面的示例中,定义了 Ingress 资源,将 hostexample.compath/get/ 的请求路由到后端服务 httpbin8000 端口。

注意这里 Ingress 资源与后端服务应属于同一命名空间。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: httpbin
spec:
  ingressClassName: pipy
  rules:
  - host: example.com
    http:
      paths:
        - path: /
          pathType: Exact
          backend:
            service:
              name: httpbin
              port:
                number: 8000
        - path: /hi
          pathType: Exact
          backend:
            service:
              name: httpbin
              port:
                number: 8000

这里我们对部分字段进行说明:

  • metadata.name 字段定义了 Ingress 的资源名。
  • spec.ingressClassName 字段用于指定入口控制器的实现。ingressclass 是各个入口控制器实现定义的名字,这里我们用 pipy。通过 kubectl get ingressclass 可以查看已安装的入口控制器。
  • spec.rules 字段用于定义路由资源。
    • host 字段中定义了主机名 example.com
    • paths 字段定义了两个路径规则:匹配路径为 / 的请求,以及前缀为 /hi 的请求
    • backend 字段定义了用于处理该路径规则的后端服务 httpbin 以及端口 8000

查看 Ingress Controller Service,可以看到其类型是 LoadBalancer,且外部地址为 10.0.0.12,正好就是节点的 IP 地址。

kubectl get svc -n fsm -l app.kubernetes.io/component=controller
NAME                          TYPE           CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
fsm-ingress-pipy-controller   LoadBalancer   10.43.144.20   10.0.0.12     80:30508/TCP,443:30607/TCP   10m

应用上面的 Ingress 配置。

在访问 httpbin 服务的 /hi/ 端点时,我们可以使用节点的 IP 地址以及端口 80

export HOST_IP=10.0.0.12
curl http://example.com/hi --connect-to example.com:80:$HOST_IP:80
Hi, there!

curl http://example.com/ --connect-to example.com:80:$HOST_IP:80
<!DOCTYPE html>
<html>
  <head>
    <title>Hi, Pipy!</title>
  </head>
  <body>
    Hi, Pipy!
    <p>This is a web page served from Pipy.</p>
  </body>
</html>

HTTPS 入口

这个示例演示了如果配置入口控制器支持 HTTPS 访问。

FSM Ingress 默认是不启用 TLS 入口的,在安装时需要通过参数 --set fsm.ingress.tls.enabled=true 打开 TLS 入口功能。

下面的例子中的 Ingress 资源,配置了 https://example.com 访问入口。

  • 字段 spec.tls 是 TLS 配置的专属字段,可以配置多个 HTTPS 入口。
  • hosts 字段用于配置 SNI,可以配置多个 SNI。这里使用 example.com,也可以支持通配符 *.example.com
  • secretName 字段用于指定保存了证书和密钥的 Secret。注意 Ingress 资源和 Secret 应同属于一个命名空间。
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: httpbin
spec:
  ingressClassName: pipy
  rules:
  - host: example.com
    http:
      paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: httpbin
              port:
                number: 8000
  tls:
  - hosts:
    - example.com
    secretName: ingress-cert

签发证书。

openssl req -x509 -newkey rsa:4096 -keyout ingress-key.pem -out ingress-cert.pem -sha256 -days 365 -nodes -subj '/CN=example.com'

使用证书和密钥创建 Secret。

kubectl create secret tls ingress-cert --cert=ingress-cert.pem --key=ingress-key.pem -n httpbin

使用服务 ingress-cert.pem 作为 ca 证书发起请求进行测试,注意目前是禁用 Ingress mTLS 功能。

curl --cacert ingress-cert.pem https://example.com/hi --connect-to example.com:443:$HOST_IP:443
Hi, there!

进阶配置

接下来,我们介绍下 FSM Ingress 的高阶配置。高阶配置是通过 Ingress 资源的 metadata.annotations 字段进行设置的。目前支持的功能有:

  • 路径重写
  • 指定负载均衡算法
  • 会话保持

路径重写

这个例子演示如何使用路径重写的注解。

FSM 提供了 pipy.ingress.kubernetes.io/rewrite-target-frompipy.ingress.kubernetes.io/rewrite-target-to 两个注解来配置路径重写,这两个注解需要同时使用。

比如在下面的示例中,在路由规则中定义了路径前缀为 /httpbin 的请求路由到服务 httpbin14001 端口,然而服务 httpbin 本身是没有这个路径的。这时就需要用到路径重写功能了:

  • pipy.ingress.kubernetes.io/rewrite-target-from: ^/httpbin/? ,这里支持正则表达式,将路径起始的 /httpbin/ 进行重写
  • pipy.ingress.kubernetes.io/rewrite-target-to: /,这里指定了重写的内容是 /

综合起来就是将路径起始的 /httpbin/ 替换 /,比如 /httpbin/get 会被重写成 /get

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: httpbin
  annotations:
    pipy.ingress.kubernetes.io/rewrite-target-from: ^/httpbin/?
    pipy.ingress.kubernetes.io/rewrite-target-to: /
spec:
  ingressClassName: pipy
  rules:
  - host: example.com
    http:
      paths:
        - path: /httpbin
          pathType: Prefix
          backend:
            service:
              name: httpbin
              port:
                number: 8000

应用上面的 Ingress 配置。

现在我们要使用 /httpbin/hi 作为路径来访问 httpbin 的 /hi 端点。

curl http://example.com/httpbin/hi --connect-to example.com:80:$HOST_IP:80
Hi, there!

指定负载均衡算法

这个例子用于演示在后端存在多个实例时,指定路由的负载均衡算法。

默认情况下 FSM Ingress 使用 Round-Robin 负载均衡算法,可以通过注解 pipy.ingress.kubernetes.io/lb-type 指定其他的算法:

  • round-robin
  • hashing
  • least-work

在下面的示例中,通过注解指定使用 hashing 负载均衡算法。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: httpbin
  annotations:
    pipy.ingress.kubernetes.io/lb-type: 'hashing'
spec:
  ingressClassName: pipy
  rules:
  - host: example.com
    http:
      paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: httpbin
              port:
                number: 8000

为了直观展示演示效果,部署下面的示例应用,这个应用有 2 个实例,并在响应中携带主机名便于区分响应请求的示例。

kubectl create namespace httpbin
kubectl apply -n httpbin -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  replicas: 2
  selector:
    matchLabels:
      app: httpbin
  template:
    metadata:
      labels:
        app: httpbin
    spec:
      containers:
      - name: pipy
        image: addozhang/httpbin:latest
        ports:
          - containerPort: 8000
        command:
        - pipy
        - -e
        - |
          pipy()
          .listen(8000)
          .serveHTTP(new Message('Response from pod ' + os.env["HOSTNAME"]))
EOF

应用上面的 Ingress 配置,然后发送请求进行测试。

curl http://example.com/ --connect-to example.com:80:$HOST_IP:80
Response from pod httpbin-5f69c44674-t9cxc
curl http://example.com/ --connect-to example.com:80:$HOST_IP:80
Response from pod httpbin-5f69c44674-t9cxc
curl http://example.com/ --connect-to example.com:80:$HOST_IP:80
Response from pod httpbin-5f69c44674-t9cxc

会话保持

这个示例用于演示会话保持功能。

FSM Ingress 提供了注解 pipy.ingress.kubernetes.io/session-sticky 来配置会话保持,默认值为 false(等同于 no0off 或者 ),即不保持会话。如若需要保持会话,则需要将值设置为 trueyes1 或者 on

比如下面的例子中,将注解值设置为 true,用于在后端服务的两个实例之间进行会话保持。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: httpbin
  annotations:
    pipy.ingress.kubernetes.io/session-sticky: 'true'
spec:
  ingressClassName: pipy
  rules:
  - host: example.com
    http:
      paths:
        - path: /
          pathType: Prefix
          backend:
            service:
              name: httpbin
              port:
                number: 8000

为了直观展示演示效果,部署下面的示例应用,这个应用有 2 个实例,并在响应中携带主机名便于区分响应请求的示例。

kubectl create namespace httpbin
kubectl apply -n httpbin -f - <<EOF
apiVersion: v1
kind: ServiceAccount
metadata:
  name: httpbin
---
apiVersion: v1
kind: Service
metadata:
  name: httpbin
  labels:
    app: httpbin
    service: httpbin
spec:
  ports:
  - name: http
    port: 8000
  selector:
    app: httpbin
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
  labels:
    app: httpbin
spec:
  replicas: 2
  selector:
    matchLabels:
      app: httpbin
  template:
    metadata:
      labels:
        app: httpbin
    spec:
      containers:
        - name: pipy
          image: addozhang/httpbin:latest
          ports:
            - containerPort: 8000
          command:
            - pipy
            - -e
            - |
              pipy()
              .listen(8000)
              .serveHTTP(new Message('Response from pod ' + os.env["HOSTNAME"]))
EOF

应用上面的 Ingress 配置,多次请求进行测试,可以发现都是同一个实例进行响应。

curl http://example.com/ --connect-to example.com:80:$HOST_IP:80
Response from pod httpbin-5f69c44674-hrvqp
curl http://example.com/ --connect-to example.com:80:$HOST_IP:80
Response from pod httpbin-5f69c44674-hrvqp
curl http://example.com/ --connect-to example.com:80:$HOST_IP:80
Response from pod httpbin-5f69c44674-hrvqp

总结

本篇我们主要介绍了 FSM Ingress 的基础功能,包括 HTTP、HTTPS 以及在 7 层上流量处理能力。

在下一篇,我们将会围绕 TLS 能力来介绍 FSM Ingress 的进阶功能。