Ingress学习笔记

Ingress

我们已经知道,Service对集群之外暴露服务的主要方式有两种:NodePort和LoadBalancer,但是这两种方式,都有一定的缺点:

  • NodePort方式的缺点是会占用很多集群机器的端口,那么当集群服务变多的时候,这个缺点就愈发明显。
  • LoadBalancer的缺点是每个Service都需要一个LB,浪费,麻烦,并且需要Kubernetes之外的设备的支持。

基于这种现状,Kubernetes提供了Ingress资源对象,Ingress只需要一个NodePort或者一个LB就可以满足暴露多个Service的需求,工作机制大致如下图所示:

image

实际上,Ingress相当于一个七层的负载均衡器,是Kubernetes对反向代理的一个抽象,它的工作原理类似于Nginx,可以理解为Ingress里面建立了诸多映射规则,Ingress Controller通过监听这些配置规则并转化为Nginx的反向代理配置,然后对外提供服务。

  • Ingress:Kubernetes中的一个对象,作用是定义请求如何转发到Service的规则。
  • Ingress Controller:具体实现反向代理及负载均衡的程序,对Ingress定义的规则进行解析,根据配置的规则来实现请求转发,实现的方式有很多,比如Nginx,Contour,Haproxy等。

NGINX Ingress Controller

NGINX Ingress Controller 是使用 Kubernetes Ingress 资源对象构建的,用 ConfigMap 来存储 Nginx 配置的一种 Ingress Controller 实现。

要使用 Ingress 对外暴露服务,就需要提前安装一个 Ingress Controller,我们这里就先来安装 NGINX Ingress Controller,由于 nginx-ingress 所在的节点需要能够访问外网,这样域名可以解析到这些节点上直接使用,所以需要让 nginx-ingress 绑定节点的 80 和 443 端口,所以可以使用 hostPort 来进行访问,当然对于线上环境来说为了保证高可用,一般是需要运行多个 nginx-ingress 实例的,然后可以用一个 nginx/haproxy 作为入口,通过 keepalived 来访问边缘节点的 vip 地址。

边缘节点

所谓的边缘节点即集群内部用来向集群外暴露服务能力的节点,集群外部的服务通过该节点来调用集群内部的服务,边缘节点是集群内外交流的一个Endpoint。

接下来我们准备资源清单文件:(点击直接获取资源文件

$ wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/nginx-0.30.0/deploy/static/mandatory.yaml

$ vim mandatory.yaml
 ......
    spec:
      # wait up to five minutes for the drain of connections
      terminationGracePeriodSeconds: 300
      serviceAccountName: nginx-ingress-serviceaccount
      tolerations:  # 由于我这里的边缘节点只有master一个节点,所有需要加上容忍
        - operator: "Exists"
      nodeSelector:
        kubernetes.io/hostname: master   # master节点
      containers:
        - name: nginx-ingress-controller
          image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller:0.30.0
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
          securityContext:
            allowPrivilegeEscalation: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            # www-data -> 101
            runAsUser: 101
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              hostPort: 80  # 使用hostPort
              containerPort: 80
              protocol: TCP
            - name: https
              hostPort: 443  # 使用hostPort
              containerPort: 443
              protocol: TCP
......

$ kubectl apply -f mandatory.yaml                                               

安装后,会将 NGINX Ingress Controller 安装在一个统一的 ingress-nginx 的 namespace 下面:

$ kubectl get pods -n ingress-nginx
NAME                                        READY   STATUS    RESTARTS   AGE
nginx-ingress-controller-5c5b7696df-mt47r   1/1     Running   0          6m23s

安装完成后,我们可以在 controller 的 Pod 所在节点,也就是master节点上使用如下方式进行验证:

$ curl 127.0.0.1
<html>
<head><title>404 Not Found</title></head>
<body>
<center><h1>404 Not Found</h1></center>
<hr><center>nginx/1.17.8</center>
</body>
</html>

出现了上面的信息证明 Ingress Controller 已经安装成功。

Ingress

安装成功后,现在我们来为一个 nginx 应用创建一个 Ingress 资源,如下所示:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
  selector:
    matchLabels:
      app: my-nginx
  template:
    metadata:
      labels:
        app: my-nginx
    spec:
      containers:
        - name: my-nginx
          image: nginx
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    app: my-nginx
spec:
  ports:
    - port: 80
      protocol: TCP
      name: http
  selector:
    app: my-nginx
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-nginx
  annotations:
    kubernetes.io/ingress.class: "nginx"
spec:
  rules:
    - host: ngdemo.negan.com  # 将域名映射到 my-nginx 服务
      http:
        paths:
          - path: /
            backend:
              serviceName: my-nginx  # 将所有请求发送到 my-nginx 服务的 80 端口
              servicePort: 80     # 不过需要注意大部分Ingress controller都不是直接转发到Service
              # 而是只是通过Service来获取后端的Endpoints列表,直接转发到Pod,这样可以减少网络跳转
              # 提高性能

注意我们在 Ingress 资源对象中添加了一个 annotations:kubernetes.io/ingress.class: "nginx",这就是指定让这个 Ingress 通过 nginx-ingress 来处理。

上面资源创建成功后,然后我们可以将域名ngdemo.negan.com解析到nginx-ingress所在的边缘节点中的任意一个,当然也可以在本地/etc/hosts中添加对应的映射也可以,然后就可以通过域名进行访问了。

image

下图显示了客户端是如果通过 Ingress Controller 连接到其中一个 Pod 的流程,客户端首先对 ngdemo.negan.com 执行 DNS 解析,得到 Ingress Controller 所在节点的 IP,然后客户端向 Ingress Controller 发送 HTTP 请求,然后根据 Ingress 对象里面的描述匹配域名,找到对应的 Service 对象,并获取关联的 Endpoints 列表,将客户端的请求转发给其中一个 Pod。

image

HTTPS

如果我们需要用 HTTPS 来访问我们这个应用的话,就需要监听 443 端口了,同样用 HTTPS 访问应用必然就需要证书,这里我们用 openssl 来创建一个自签名的证书:

$ openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048 -keyout tls.key -out tls.crt -subj "/C=CN/ST=BJ/L=BJ/O=nginx/CN=negan.com"

然后通过 Secret 对象来引用证书文件:

$ kubectl create secret tls tls-secret --key tls.key --cert tls.crt
secret/tls-secret created

这个时候我们就可以创建一个 HTTPS 访问应用的:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-https
spec:
  rules:
  - host: foo.negan.com
    http:
      paths:
      - path: /
        backend:
          serviceName: my-nginx
          servicePort: 80
  tls:
  - hosts:
    - foo.negan.com
    secretName: tls-secret

创建完成后进行查看:

$ kubectl get ingress
NAME            CLASS    HOSTS              ADDRESS   PORTS     AGE
ingress-https   <none>   foo.negan.com                80, 443   27s
my-nginx        <none>   ngdemo.negan.com             80        50m

$ kubectl describe ingress ingress-https
Name:             ingress-https
Namespace:        default
Address:          
Default backend:  default-http-backend:80 (<error: endpoints "default-http-backend" not found>)
TLS:
  tls-secret terminates foo.negan.com
Rules:
  Host           Path  Backends
  ----           ----  --------
  foo.negan.com  
                 /   my-nginx:80 (10.244.1.9:80)
Annotations:     Events:
  Type           Reason  Age   From                      Message
  ----           ------  ----  ----                      -------
  Normal         CREATE  102s  nginx-ingress-controller  Ingress default/ingress-https

同样我们可以将域名foo.negan.com在本地/etc/hosts中添加对应的映射,然后就可以通过域名进行访问了。

image

Basic Auth

同样我们还可以在 Ingress Controller 上面配置一些基本的 Auth 认证,比如 Basic Auth,可以用 htpasswd 生成一个密码文件来验证身份验证。

$ htpasswd -c auth foo
New password:
Re-type new password:
Adding password for user foo

然后根据上面的 auth 文件创建一个 secret 对象:

$ kubectl create secret generic basic-auth --from-file=auth
secret/basic-auth created
$ kubectl get secret basic-auth -o yaml
apiVersion: v1
data:
  auth: Zm9vOiRhcHIxJGRiQ3ZFd3JFJFp4UDhkSXltZFNWV3RlQlVOa0RJby8K
kind: Secret
metadata:
  creationTimestamp: "2022-01-06T14:32:25Z"
  managedFields:
  - apiVersion: v1
    fieldsType: FieldsV1
    fieldsV1:
      f:data:
        .: {}
        f:auth: {}
      f:type: {}
    manager: kubectl
    operation: Update
    time: "2022-01-06T14:32:25Z"
  name: basic-auth
  namespace: default
  resourceVersion: "88611"
  selfLink: /api/v1/namespaces/default/secrets/basic-auth
  uid: 7578bd2f-a75d-4ae6-b081-41c7524340a0
type: Opaque

对上面的 my-nginx 应用创建一个具有 Basic Auth 的 Ingress 对象:

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: ingress-with-auth
  annotations:
    # 认证类型
    nginx.ingress.kubernetes.io/auth-type: basic
    # 包含 user/password 定义的 secret 对象名
    nginx.ingress.kubernetes.io/auth-secret: basic-auth
    # 要显示的带有适当上下文的消息,说明需要身份验证的原因
    nginx.ingress.kubernetes.io/auth-realm: 'Authentication Required - foo'
spec:
  rules:
  - host: foo.bar.com
    http:
      paths:
      - path: /
        backend:
          serviceName: my-nginx
          servicePort: 80

直接创建上面的资源对象,在浏览器中直接打开配置的域名:

image

输入我们设置的用户名和密码进行访问:

image

posted @ 2022-01-06 22:46  李大鹅  阅读(159)  评论(0编辑  收藏  举报