一篇文章让你搞懂如何在K8s 里使用 Traefik 2.0作为Ingress Controller(上)

670 阅读6分钟

原文链接

了解K8s的同学应该都知道,如果想要把应用放到公网上供外部访问,那么不可避免的会接触到Ingress资源。本文以Traefik为例,让大家对Ingress和Traefik的使用有一定的了解。

Ingress是什么

我们来看看官方对于Ingress的解释

An API object that manages external access to the services in a cluster, typically HTTP. Ingress can provide load balancing, SSL termination and name-based virtual hosting.

  1. 它也是一个API对象,就如同其他的API对象Service、Deployment一样。
  2. 负责管理外部流量对于集群内部的访问,比如典型通过HTTP协议。
  3. 能承担负载均衡,SSL等职责

简单来说,它承担的就是一个边缘路由的职责,负责按照你期望的规则管理外部的访问请求,如下面所示:

    internet
        |
   [ Ingress ]
   --|-----|--
   [ Services ]

Traefik 是什么

上面我们简单说明了什么是Ingress,它是一个API资源对象。得益于K8s高度抽象的设计,可以让我们很方便地做技术选型去实现Ingress的工作。在这里,完成具体工作的组件就是Ingress Controller,Traefik就是其中之一。

traefik-architecture.png

市面上有非常多的Ingress Controller,比如Nginx Ingress、kong、istio等等。我之所以选择Traefik的原因是目前项目不算太复杂traefik足够应付,提供了较为简洁的UI界面,能够满足我目前的需求。不过稍微有点坑的地方就是Traefik 2.0的版本发布不久,文档的支持上稍微有点弱,有时候看文档会比较懵。因此,接下来的文章,我就以Traefik 2.0来说明如何在K8s中使用Ingress Controller。

在K8s集群中部署Traefik

在这里我没有使用Helm来部署Traefik(因为此时Helm上的Traefik版本还是低于2.0的),先来看看目录下有哪些yaml文件。

> #ls
> #traefik-config.yaml  traefik-crd.yaml  traefik-deploy.yaml  traefik-rbac.yaml

接下来我会展示这些文件中的重点内容,限于篇幅的原因,部分内容省略表示,可以在公众号留言我给出完整示例。

  • 先看看 traefik-config.yaml里面的内容:
kind: ConfigMap
apiVersion: v1
metadata:
  name: traefik-config
  namespace: kube-system
data:
  traefik.yaml: |-
    serversTransport:
      insecureSkipVerify: true
    api:
      ...
    metrics:
      ...
    entryPoints:
      web:
        address: ":80"
      websecure:
        address: ":443"
    providers:
      kubernetesCRD: ""
    log:
		...
    accessLog:
		...

这里面都是关于Traefik的一些配置,基本上看名字也能知道各个字段的含义,这里可能需要稍微注意一点的是entryPoints。在这里定义了两条入口,一条叫做web负责监听80端口的访问,一条叫做websecure负责监听443端口,也就是Https的请求。

  • 再来看看traefik-crd.yaml里面的内容
## IngressRoute
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutes.traefik.containo.us
spec:
  scope: Namespaced
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRoute
    plural: ingressroutes
    singular: ingressroute
---
## IngressRouteTCP
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: ingressroutetcps.traefik.containo.us
spec:
  scope: Namespaced
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: IngressRouteTCP
    plural: ingressroutetcps
    singular: ingressroutetcp
---
## Middleware
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: middlewares.traefik.containo.us
spec:
  scope: Namespaced
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: Middleware
    plural: middlewares
    singular: middleware
---
apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
  name: tlsoptions.traefik.containo.us
spec:
  scope: Namespaced
  group: traefik.containo.us
  version: v1alpha1
  names:
    kind: TLSOption
    plural: tlsoptions
    singular: tlsoption

这里的crd也就是CustomResourceDefinition,在Kubernetes 1.7 之后增加了对 CRD 资源二次开发能力来扩展 Kubernetes API,通过 CRD 我们可以向 Kubernetes API 中增加新资源类型,而不需要修改 Kubernetes 源码来创建自定义的 API server,该功能大大提高了 Kubernetes 的扩展能力。 在这里,Traefik从2.0的版本不在直接监听Ingress资源,而是通过自定的一个叫做IngressRoute的资源在进行工作,这点是需要注意的地方。除此之外,我们可以看到Traefik还申明了一些自定义的资源。这些资源的使用范围是namespace级别的,在k8s中还支持集群级别的crd。

  • traefik-rbac.yaml
apiVersion: v1
kind: ServiceAccount
metadata:
  name: ingress
  namespace: kube-system
---
kind: ClusterRole
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
rules:
  - apiGroups: [""]
    resources: ["services","endpoints","secrets"]
    verbs: ["get","list","watch"]
  - apiGroups: ["extensions"]
    resources: ["ingresses"]
    verbs: ["get","list","watch"]
  - apiGroups: ["extensions"]
    resources: ["ingresses/status"]
    verbs: ["update"]
	  ... 
---
kind: ClusterRoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
  name: traefik-ingress-controller
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: traefik-ingress-controller
subjects:
  - kind: ServiceAccount
    name: ingress
    namespace: kube-system

在这个文件里定义的是Traefik会涉及到的角色和权限,因为可能要代理各个命名空间的各个服务的流量,因此可以看到有Cluster级别的权限申明。

  • 最重要的traefik-deploy.yaml 前面的一系列准备工作准备完成之后,就是真正的部署Traefik了,由于内容比较多,为了方便理解我直接在相关的字段后添加注释来说明
apiVersion: v1
kind: Service
metadata:
  name: traefik
  namespace: kube-system
spec:
  ports:
    - name: web
      port: 80
    - name: websecure
      port: 443
    - name: admin
      port: 8080
  selector:
    app: traefik
---
apiVersion: apps/v1
kind: DaemonSet	# 以DaemonSet的形式运行Traefik
metadata:
  name: traefik-ingress-controller
  namespace: kube-system
  labels:
    app: traefik
spec:
  selector:
    matchLabels:
      app: traefik
  template:
    metadata:
      name: traefik
      labels:
        app: traefik
    spec:
      serviceAccountName: ingress		#使用了前文中定义的service account
      terminationGracePeriodSeconds: 1
      containers:
        - image: traefik:2.0		#使用的是traefik 2.0的版本
          name: traefik-ingress-lb
          env:	# 由于我的服务器是阿里的ECS,我需要配置Access Key以便能够使用Traefik的自动Https签名
          - name: ALICLOUD_ACCESS_KEY              
            value: <根据你自己的情况调整>               
          - name: ALICLOUD_SECRET_KEY              
            value: <根据你自己的情况调整> 
          ports:
            - name: web
              containerPort: 80
              hostPort: 80           #hostPort方式,将端口暴露到集群节点
            - name: websecure
              containerPort: 443
              hostPort: 443          #hostPort方式,将端口暴露到集群节点
            - name: admin
              containerPort: 8080
          resources:
            limits:
              cpu: 2000m
              memory: 1024Mi
            requests:
              cpu: 1000m
              memory: 1024Mi
          securityContext:
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
          args:
            - --entrypoints.web.Address=:80
            - --entrypoints.websecure.Address=:443
            - --api.insecure  # 开启webui需要该参数
            - --providers.kubernetescrd
            - --api
            - --api.dashboard=true
            - --accesslog
            # 使用acme.sh通过dns验证域名的所有权,以便能够自动签发https证书
            - --certificatesresolvers.default.acme.dnsChallenge=true
            - --certificatesresolvers.default.acme.dnsChallenge.provider=alidns
            # 邮箱配置
            - --certificatesResolvers.default.acme.email=你的邮箱@gmail.com
            # 保存ACME 证书的位置
            - --certificatesResolvers.default.acme.storage="acme.json"
            - --metrics.prometheus=true
          volumeMounts:
            - mountPath: "/config"
              name: "config"
      volumes:
        - name: config
          configMap:
            name: traefik-config
      tolerations:              #设置容忍所有污点,防止节点被设置污点
        - operator: "Exists"
      nodeSelector:             #设置node筛选器,在特定label的节点上启动,我为具有公网IP的节点打上了这个标签
        IngressProxy: "true"

到此为止,所有的准备工作都完成了,接下来在目录执行命令kubectl apply -f *,正常的话你可以看到上面几个文件内申明的所有资源都被正确的创建了,我们也可以通过命令验证下traefik是不是在正常运行。

屏幕快照 2020-01-14 下午3.33.52.png

正常情况下的话,你可以看到一个daemonset,一个service,和至少一个pod在运行。如果走到这步的话,那说明已经大功告成了。

结语

在本文中,阐述了如何使用Traefik作为Ingress Controller来监听集群外部的网络请求,在接下来的文章中,我将通过一个具体的例子展示如何暴露一个内部的服务到外网访问,以及如何进行自动Https证书签发等内容。

默认标题_公众号封面首图_2020-01-13-0.png