k8s整合Traefik
k8s整合Traefik
介绍
公司的k8s云测试环境,集群和核心组件研究部署的差不多了,此处用阿里云进行模拟,记录安装traefik过程
Traefik 是一个开源的可以使服务发布变得轻松有趣的边缘路由器。它负责接收你系统的请求,然后使用合适的组件来对这些请求进行处理。
除了众多的功能之外,Traefik 的与众不同之处还在于它会自动发现适合你服务的配置。当 Traefik 在检查你的服务时,会找到服务的相关信息并找到合适的服务来满足对应的请求。
Traefik 兼容所有主流的集群技术,比如 Kubernetes,Docker,Docker Swarm,AWS,Mesos,Marathon,等等;并且可以同时处理多种方式。(甚至可以用于在裸机上运行的比较旧的软件。)
使用 Traefik,不需要维护或者同步一个独立的配置文件:因为一切都会自动配置,实时操作的(无需重新启动,不会中断连接)。使用 Traefik,你可以花更多的时间在系统的开发和新功能上面,而不是在配置和维护工作状态上面花费大量时间。
核心概念
Traefik 是一个边缘路由器,是你整个平台的大门,拦截并路由每个传入的请求:它知道所有的逻辑和规则,这些规则确定哪些服务处理哪些请求;传统的反向代理需要一个配置文件,其中包含路由到你服务的所有可能路由,而 Traefik 会实时检测服务并自动更新路由规则,可以自动服务发现。
首先,当启动 Traefik 时,需要定义 entrypoints
(入口点),然后,根据连接到这些 entrypoints 的路由来分析传入的请求,来查看他们是否与一组规则相匹配,如果匹配,则路由可能会将请求通过一系列中间件转换过后再转发到你的服务上去。在了解 Traefik 之前有几个核心概念我们必须要了解:
Providers
用来自动发现平台上的服务,可以是编排工具、容器引擎或者 key-value 存储等,比如 Docker、Kubernetes、FileEntrypoints
监听传入的流量(端口等…),是网络入口点,它们定义了接收请求的端口(HTTP 或者 TCP)。Routers
分析请求(host, path, headers, SSL, …),负责将传入请求连接到可以处理这些请求的服务上去。Services
将请求转发给你的应用(load balancing, …),负责配置如何获取最终将处理传入请求的实际服务。Middlewares
中间件,用来修改请求或者根据请求来做出一些判断(authentication, rate limiting, headers, ...),中间件被附件到路由上,是一种在请求发送到你的服务之前(或者在服务的响应发送到客户端之前)调整请求的一种方法。
安装
首先下载helm,根据自己的k8s版本来选择相应的版本,此处K8s的版本为v1.22.3
[root@iZbp1h218d2igdnxe7q5o9Z ~]# tar -zvxf helm-v3.6.3-linux-amd64.tar.gz
linux-amd64/
linux-amd64/helm
linux-amd64/LICENSE
linux-amd64/README.md
[root@iZbp1h218d2igdnxe7q5o9Z ~]# mv linux-amd64/helm /usr/local/bin/
[root@iZbp1h218d2igdnxe7q5o9Z ~]# helm version
version.BuildInfo{Version:"v3.6.3", GitCommit:"d506314abfb5d21419df8c7e7e68012379db2354", GitTreeState:"clean", GoVersion:"go1.16.5"}
[root@iZbp1h218d2igdnxe7q5o9Z ~]#
使用 Helm 来快速安装 traefik,首先获取 Helm Chart 包:
git clone https://github.com/traefik/traefik-helm-chart
[root@iZbp1h218d2igdnxe7q5o9Z ~]# cd traefik-helm-chart
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]# ll
总用量 44
-rw-r--r-- 1 root root 205 4月 22 13:36 artifacthub-repo.yml
-rw-r--r-- 1 root root 915 4月 22 13:36 CONTRIBUTING.md
-rw-r--r-- 1 root root 327 4月 22 13:40 dashboard.yaml
-rw-r--r-- 1 root root 1514 4月 22 13:38 deployment-prod.yaml
-rw-r--r-- 1 root root 11573 4月 22 13:36 LICENSE
drwxr-xr-x 2 root root 67 4月 22 13:36 lint
-rw-r--r-- 1 root root 3847 4月 22 13:36 Makefile
-rw-r--r-- 1 root root 473 4月 22 13:49 nginx-ir.yaml
-rw-r--r-- 1 root root 3231 4月 22 13:36 README.md
-rw-r--r-- 1 root root 2360 4月 22 13:36 TESTING.md
drwxr-xr-x 5 root root 157 4月 22 13:36 traefik
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]#
创建一个定制的 values 配置文件:
deployment:
enabled: true
kind: Deployment
ingressClass:
enabled: true
isDefaultClass: false
ingressRoute: # 不用自动创建,我们自己处理
dashboard:
enabled: false
#
# 配置 providers
#
providers:
kubernetesCRD: # 开启 crd provider
enabled: true
allowCrossNamespace: true # 是否允许跨命名空间
allowExternalNameServices: true # 是否允许使用 ExternalName 的服务
kubernetesIngress: # 开启 ingress provider
enabled: true
allowExternalNameServices: true
logs:
general:
level: DEBUG
access:
enabled: true
ports:
web:
port: 8000
hostPort: 80 # 使用 hostport 模式
websecure:
port: 8443
hostPort: 443 # 使用 hostport 模式
metrics:
port: 9100
hostPort: 9101
# 监听1024以下的端口需要修改traefik默认的安全上下文配置
securityContext:
capabilities:
drop: []
readOnlyRootFilesystem: false
runAsGroup: 0
runAsNonRoot: false
runAsUser: 0
nodeSelector: # 固定到边缘节点
kubernetes.io/hostname: "iZbp1h218d2igdnxe7q5o9Z"
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]# kubectl get nodes
NAME STATUS ROLES AGE VERSION
izbp1h218d2igdnxe7q5o7z Ready <none> 104m v1.22.3
izbp1h218d2igdnxe7q5o8z Ready <none> 104m v1.22.3
izbp1h218d2igdnxe7q5o9z Ready control-plane,master 105m v1.22.3
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]#
这里我们使用 hostport 模式将 Traefik 固定到 iZbp1h218d2igdnxe7q5o9Z节点上,因为只有这个节点有外网 IP,所以我们这里 是作为流量的入口点。直接使用上面的 values 文件安装 traefik:
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]# helm upgrade --install traefik ./traefik -f ./values.yaml --namespace kube-system
Release "traefik" does not exist. Installing it now.
NAME: traefik
LAST DEPLOYED: Mon May 16 23:00:30 2022
NAMESPACE: kube-system
STATUS: deployed
REVISION: 1
TEST SUITE: None
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]# kubectl get pods -n kube-system -l app.kubernetes.io/name=traefik
NAME READY STATUS RESTARTS AGE
traefik-7586cd775d-8n58k 1/1 Running 0 3h2m
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]#
我们可以首先创建一个用于 Dashboard 访问的 IngressRoute 资源清单:
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]# cat dashboard.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-dashboard
namespace: kube-system
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.dalianpai.cn`) # 指定域名
kind: Rule
services:
- name: api@internal
kind: TraefikService
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]#
其中的 TraefikService
是 Traefik Service
的一个 CRD 实现,这里我们使用的 api@internal
这个 TraefikService
,表示我们访问的是 Traefik 内置的应用服务。
如果没有域名的话,则可以在hosts文件中进行配置,有的话直接阿里云配置域名解析
进行访问
如果要让 Traefik 去处理默认的 Ingress 资源对象,则我们就需要使用名为 traefik
的 IngressClass 了,因为没有指定默认的:
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]# kubectl get ingressclass
NAME CONTROLLER PARAMETERS AGE
traefik traefik.io/ingress-controller <none> 3h7m
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]#
创建如下所示的一个 Ingress 资源对象,这里的核心是 ingressClassName
要指向 traefik
这个 IngressClass:
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]# cat nginx-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-nginx-by-traefik
namespace: default
spec:
ingressClassName: traefik # 使用 traefk 的 IngressClass
rules:
- host: wgr.nginx.com # 将域名映射到 my-nginx 服务
http:
paths:
- path: /
pathType: Prefix
backend:
service: # 将所有请求发送到 my-nginx 服务的 80 端口
name: nginx-svc
port:
number: 80
[root@iZbp1h218d2igdnxe7q5o9Z traefik-helm-chart]#
测试:
HTTPS访问
由于测试环境中所有的应用都是通过https的方式进行访问,此处先用https的方式访问dashboard,操作如下:
先去阿里云申请免费的SSL证书,然后通过 Secret 对象来引用证书文件:
kubectl create secret tls test-tls --cert=7783854_traefik.dalianpai.cn.pem --key=7783854_traefik.dalianpai.cn.key
创建资源清单
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: ingressroute-tls
spec:
entryPoints:
- websecure
routes:
- match: Host(`traefik.dalianpai.cn`)
kind: Rule
services:
- name: api@internal
kind: TraefikService
tls:
secretName: test-tls
测试
HTTP强转HTTPS
中间件
由于一开始创建了http访问dashboard的路由,如果我删除,只留出https,再通过 http
来访问的话呢就不行了,就会404了,因为我们根本就没有简单监听端口这个入口点。
所以要想通过 http
来访问应用的话自然我们需要监听下 web
这个入口点。如果只希望用户通过 https 来访问应用的话呢?可以让 http 强制跳转到 https 服务去,对的,在 Traefik 中也是可以配置强制跳转的,只是这个功能现在是通过中间件来提供的了。如下所示,我们使用 redirectScheme
中间件来创建提供强制跳转服务:
apiVersion: traefik.containo.us/v1alpha1
kind: Middleware
metadata:
name: redirect-https
spec:
redirectScheme:
scheme: https
然后将这个中间件附加到 http 的服务上面去,因为 https 的不需要跳转:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: traefik-dashboard
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.dalianpai.cn`) # 指定域名
kind: Rule
services:
- name: api@internal
kind: TraefikService
middlewares:
- name: redirect-https
这样就可以通过http方式直接跳转到https,traefik还有很多插件,后面在介绍。
redirectTo方式
实际的测试环境中可能有几十上百个应用,如果通过中间件的方法,则都要绑定,过于繁琐,下面介绍一下web转发的方法。
创建测试nginx:
# 部署nginx
[root@iZbp1h218d2igdnxe7q5o9Z ~]# sudo kubectl create deployment nginx --image=nginx:1.14-alpine
# 暴露端口
[root@iZbp1h218d2igdnxe7q5o9Z ~]# sudo kubectl expose deployment nginx --port=80 --type=NodePort
# 查看服务状态
[root@iZbp1h218d2igdnxe7q5o9Z ~]# sudo kubectl get pods,service
NAME READY STATUS RESTARTS AGE
pod/nginx-65c4bffcb6-bvf8w 1/1 Running 0 52m
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
service/kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 124m
service/nginx NodePort 10.110.254.105 <none> 80:32701/TCP 52m
给nginx申请证书,并创建tls
修改配置:
ports:
web:
port: 8000
hostPort: 80 # 使用 hostport 模式
redirectTo: websecure
先卸载之前的安装:
helm uninstall traefik -n kube-system
在重新应用
helm upgrade --install traefik ./traefik -f ./values.yaml --namespace kube-system
测试,http会自动跳转到https
上面的配置使用的是hostport模式,ingress的话,一般采用hostnetwork,如果配置hostnetwork可以用下面的配置
# traefik是dashboard的配置,web和websecure是入口的配置
ports:
traefik:
expose: false
port: 9000
web:
expose: false
port: 80
redirectTo: websecure
websecure:
expose: false
port: 443
# 使用hostNetwork
hostNetwork: true
hostPort 与 hostNetwork 异同
相同点
hostPort 与 hostNetwork 本质上都是暴露 pod 宿主机 IP 给终端用户,因为 pod 生命周期并不固定,随时都有可能被完爆,故 IP 的不确定最终导致用户使用上的不方便;此外宿主机端口占用也导致不能在同一台机子上有多个程序使用同一端口。因此一般情况下,不要使用 hostPort 方式。
不同点
使用 hostNetwork,pod 实际上用的是 pod 宿主机的网络地址空间:即 pod IP 是宿主机 IP,而非 cni 分配的 pod IP,端口是宿主机网络监听接口。
使用 hostPort,pod IP 并非宿主机 IP,而是 cni 分配的 pod IP,跟其他普通的 pod 使用一样的 ip 分配方式,端口并非宿主机网络监听端口,只是使用了 DNAT 机制将 hostPort 指定的端口映射到了容器的端口之上(可以通过 iptables 命令进行查看)。外部访问此 pod 时,仍然使用宿主机和 hostPort 方式
当 pod 同时使用了 hostNetwork 和 hostPort,那么 hostNetwork 将会直接使用宿主机网络命名空间,hostPort 其实就形同虚设了。可以认为 hostNetwork 选项优先级要高于 hostPort。