Bootstrap

第七篇:k8s loadbalancer与ingress实践

k8s可以通过三种方式将集群内服务暴露到外网,分别是NodePort、LoadBalancer、Ingress,其中NodePort作为基础通信形式我们在《k8s网络模型与集群通信》中进行了介绍,这里我们主要关注LoadBalancer和Ingress

LoadBalancer

loadbalancer是服务暴露到因特网的标准形式,和nodeport一样我们只需在创建service是指定type为loadbalancer即可,接着Service 的通过status.loadBalancer字段将需要创建的负载均衡器信息发布供负载均衡服务创建。不过loadbalancer是云服务商”专属“,像腾讯云CLB、阿里云SLB,这样在创建service时会自动帮我们创建一个负载均衡器。

大多数云上负载均衡也是基于nodeport,他们的结构如下:

如果要在本地创建一个负载均衡器如何实现呢?

MetalLB[1],一个CNCF沙箱项目,使用标准路由协议(ARP/BGP),实现裸机K8s集群的负载均衡器。

安装方式可参考官方文档:installation[2]

L2(子网)模式的结构,图源[3]

安装后我们获得如下两个组件:

metallb-system/controller deployment。用于处理IP分配的控制器。•metallb-system/speakerdaemonset。集群中每个节点启动一个协议服务守护进程。

接着添加一个configmap配置metallb IP池。

apiVersion: v1
kind: ConfigMap
metadata:
  namespace: metallb-system
  name: config
data:
  config: |
    address-pools:
    - name: default
      protocol: layer2
      addresses:
      - 192.168.1.240-192.168.1.250

这样当我们创建一个loadbalancer类型的service时,EXTERNAL-IP将会从地址池中获取一个用于外部访问的IP 192.168.1.243 当外部流量进入时,ARP将我们的请求地址广播获取所属的service,接着k8s内部 通过iptables 规则和 kube-proxy,将流量从服务端点引导到后端。

#nginx_deployment_service.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
  namespace: metallb-system
spec:
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        ports:
        - name: http
          containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  namespace: metallb-system
  name: nginx
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx
  type: LoadBalancer

查看service kubectl get svc

NAME     TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)        AGE
nginx    LoadBalancer   10.96.243.159   192.168.1.243   80:31052/TCP   40h

测试访问:curl 192.168.1.243

# curl 192.168.1.243
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>

负载均衡可以建立在 OSI 网络模型的不同级别上,主要是在 L4(传输层,例如 TCP/UDP)和 L7(应用层,例如 HTTP)上。在 Kubernetes 中,Services是 L4 的抽象,LoadBalancer类型负载均衡依然有局限性,同时我们看到每创建一个service对应的负载均衡器都会消耗一个静态IP,这并不合理。当然k8s中的另一种资源对象ingress可工作在 L7 层实现应用程序协议(HTTP/HTTPS)的负载均衡。

Ingress

Ingress[4] 公开了从集群外部到集群内服务[5]的 HTTP 和 HTTPS 路由。流量路由由 Ingress 资源上定义的规则控制。我们可以将 Ingress 配置为服务提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS,以及提供基于名称的虚拟主机等能力。

我们所说的Ingress包含两个部分:

•ingress k8s资源对象:流量路由规则的控制•ingress-controller控制器:控制器的实现有非常多,可参考官方文档中列表Ingress 控制器[6],这里我们使用k8s官方维护的控制器NGINX Ingress Controller[7]

外部流量进入集群时先经过ingress-controller,然后根据ingress配置的路由规则将请求转发到后端service。

ingress-controller

ingress-controller其实就是守护进程加一个反向代理的应用,守护进程不断监听集群中资源的变化,将ingress中的配置信息生成反向代理配置。在nginx-ingress controller中即生成nginx.conf的配置文件。

在本文中因为我们上面已经配置好了loadbalancer的服务,这样我们创建一个type为LoadBalancer的service关联这组pod,再把域名解析指向该地址,就实现了集群服务的对外暴露。当然你也可以使用NodePortHostnetwork的方式,感兴趣的小伙伴可以进行测试。

ingress-controller不是k8s内部组件,可以通过helm或资源清单方式安装,可查看ingress-nginx deploy[8]

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

然后我们编辑service

kubectl edit service/ingress-nginx-controller -n ingress-nginx

修改spec.type为LoadBalancer即可。

这样我们创建好了nginx-ingress controller,下一步就要配置ingress路由规则。

ingress规则

host:k8s.com

基于url的路由:

•/api/v1•/api/v2

这两个url分别路由到不同的service中

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: test
  namespace: training
  annotations:
    ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: k8s.com
    http:
      paths:
      - path: /api/v1
        backend:
          serviceName: service-apiv1
          servicePort: 80
      - path: /api/v2
        backend:
          serviceName: service-apiv2
          servicePort: 80

ingress.kubernetes.io/rewrite-target是nginx-ingress controller的一个注解,当后端服务中暴露的 URL 与 Ingress 规则中指定的路径不同时可以通过此重定向。

查看svc可以看到此时控制器已经获得了一个EXTERNAL-IP

#kubectl get svc -n ingress-nginx
NAME                                 TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
ingress-nginx-controller             LoadBalancer   10.96.87.23    192.168.1.245   80:32603/TCP,443:31906/TCP   621d
ingress-nginx-controller-admission   ClusterIP      10.96.109.70   <none>          443/TCP                      621d

现在nginx-ingress controller和ingress路由规则都有了。

我们可以进入到nginx-ingress controller pod中查看nginx.conf可以看到此时我们的ingress配置已经被生成为路由规则。

接下来就是指定我们的backend,即上面的server-apiv1/2

我们添加两个用于暴露的service和deployment,和loadbalancer中测试清单一样,我们稍稍修改一下名称即可。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-apiv1
  namespace: training
spec:
  selector:
    matchLabels:
      app: nginx-apiv1
  template:
    metadata:
      labels:
        app: nginx-apiv1
    spec:
      containers:
      - name: nginx-apiv1
        image: nginx:latest
        ports:
        - name: http
          containerPort: 80

---
apiVersion: v1
kind: Service
metadata:
  namespace: training
  name: service-apiv1
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: nginx-apiv1
  type: NodePort

将nginx-apiv1换成nginx-apiv2创建出另一个service和deployment。

最后修改hosts解析k8s.com

192.168.1.245 k8s.com

使用curl命令测试url路由(记得在pod中添加测试文件,否则虽然url进行了路由但会出现404)。

# curl k8s.com/api/v1/index.html
api v1
# curl k8s.com/api/v2/index.html
api v2

这样我们对ingress有了初步了解,ingress的路由规则可自定项较多也比较繁杂,可通过官方文档进一步学习。

;