1. 概述
在上一篇文章 k8s搭配traefik 中,测试的两个业务 music 和 video 都是使用 kubernetes 的 ingress 创建的应用路由,traefik 本身也提供了另外一种路由 IngressRoute,这篇文章就是使用这种方式为两个业务创建路由。
开始试验前先介绍下两种路由。
2. 简介
2.1 Ingress
Ingress 公开了从集群外部到集群内服务 的 HTTP 和 HTTPS 路由。 流量路由由 Ingress 资源上定义的规则控制。
下面是一个将所有流量都发送到同一 Service 的简单 Ingress 示例:
可以将 Ingress 配置为服务提供外部可访问的 URL、负载均衡流量、终止 SSL/TLS,以及提供基于名称的虚拟主机等能力。Ingress 不会公开任意端口或协议。 将 HTTP 和 HTTPS 以外的服务公开到 Internet 时,通常使用 Service.Type=NodePort 或 Service.Type=LoadBalancer 类型的服务。
必须具有 Ingress 控制器 才能满足 Ingress 的要求。 仅创建 Ingress 资源本身没有任何效果。
Ingress 的一个简单资源示例如下:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: minimal-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- http:
paths:
- path: /testpath
pathType: Prefix
backend:
service:
name: test
port:
number: 80
与所有其他 Kubernetes 资源一样,Ingress 需要使用 apiVersion
、kind
和 metadata
字段。 Ingress 对象的命名必须是合法的 DNS 子域名名称
。 有关使用配置文件的一般信息,请参见部署应用
、 配置容器
、 管理资源
。 Ingress 经常使用注解(annotations)来配置一些选项,具体取决于 Ingress 控制器,例如 重写目标注解
。 不同的 Ingress 控制器
支持不同的注解。查看文档以供你选择 Ingress 控制器,以了解支持哪些注解。
Ingress 规约 提供了配置负载均衡器或者代理服务器所需的所有信息。 最重要的是,其中包含与所有传入请求匹配的规则列表。 Ingress 资源仅支持用于转发 HTTP 流量的规则,每个 HTTP 规则都包含以下信息:
- 可选的
host
。在此示例中,未指定host
,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。 如果提供了host
(例如 foo.bar.com),则rules
适用于该host
。 - 路径列表 paths(例如,
/testpath
),每个路径都有一个由serviceName
和servicePort
定义的关联后端。 在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。 backend
(后端)是 Service 文档 中所述的服务和端口名称的组合。 与规则的host
和path
匹配的对 Ingress 的 HTTP(和 HTTPS )请求将发送到列出的backend
。
通常在 Ingress 控制器中会配置 defaultBackend
(默认后端),以服务于任何不符合规约中 path
的请求。
以上内容摘选自k8s官方文档 ,有兴趣的话可以详细看一下官方说明。
2.2 Ingress Route
在早期版本中,Traefik 仅通过Kubernetes Ingress provider 来支持 Kubernetes ,它是一个严格意义上的 Kubernetes Ingress 控制器。
但是,由于社区表示需要在不求助于(大量)注释的情况下从 Traefik 功能中受益,因此 Traefik 工程团队 为 IngressRoute 类型开发了自定义资源定义 (CRD),定义如下,以便提供更好的方法配置对 Kubernetes 集群的访问。
关于 IngressRoute 的详细介绍可以查看官方说明文档 。
3. 开始实践
上面介绍了两个概念,接下来我们就动手实际创建一下两个服务的路由。我们先列一下当前的环境清单,规划一下我们的部署方案。
节点 | 角色 | IP | 配置 | Label |
---|---|---|---|---|
master | master, etcd | 192.168.1.100 | 4核4G50G | usefulness=schedule |
node1 | worker | 192.168.1.101 | 8核8G100G | usefulness=devops |
node2 | worker | 192.168.1.102 | 8核8G100G | usefulness=demo |
node3 | worker | 192.168.1.103 | 8核8G100G | usefulness=business |
如上列表所示,node1 节点我们打上了 devops 的标签,node2 我们打上了 demo 的标签,所以我们这次的实例计划如下:
- 安装 traefik,并将 traefik 的 pod 部署在 node1 上
- 自定义启动 dashboard
- 部署两个业务 music 和 video,并将它们的 pod 部署在 node2 上
- 创建两个 ingress-route,分别指向两个 service
下面是我们这次测试的三个域名,我们需要将它们解析到 node1 节点,因为我们接下来要将 traefik 的 pod 部署在 node1 上。
域名 | IP | 节点 | Label |
---|---|---|---|
traefik.local.com | 192.168.1.101 | node1 | usefulness=devops |
music.local.com | 192.168.1.101 | node1 | usefulness=devops |
video.local.com | 192.168.1.101 | node1 | usefulness=devops |
使用下面脚本将域名解析写入 hosts:
cat >> /etc/hosts <<EOF
192.168.1.101 traefik.local.com
192.168.1.101 music.local.com
192.168.1.101 video.local.com
EOF
3.1 安装 traefik
本次我们采用 helm 的方式安装 traefik,并提供一份 values.yaml 修改默认的一些配置选项,如下:
ingressRoute:
dashboard:
enabled: false
# Additional ingressRoute annotations (e.g. for kubernetes.io/ingress.class)
annotations: {}
# Additional ingressRoute labels (e.g. for filtering IngressRoute by custom labels)
labels: {}
logs:
general:
format: json
level: ERROR
access:
enabled: true
format: json
ports:
traefik:
expose: true
web:
port: 80
hostPort: 80
expose: true
exposedPort: 80
websecure:
port: 443
hostPort: 443
expose: true
exposedPort: 443
nodeSelector:
usefulness: devops
这份清单我们主要自定义了三项内容:
- 修改了ingressRoute,关闭默认的 dashboard,我们自定义开启 dashboard
- 修改了 ports,让 traefik 使用节点的 80 和 443 端口来监听流量
- 修改了 nodeSelector,指定 traefik 的 pod 都部署在 label 为 usefulness=devops 的节点上,也就是 node1 节点
保存上述配置为 values.yaml,接下来使用如下命令安装 traefik:
# 添加仓库
helm repo add traefik https://helm.traefik.io/traefik
# 更新
helm repo update
# 使用配置文件安装
helm install traefik traefik/traefik -f values.yaml
看到如下输出说明 traefik 已经安装成功了:
NAME: traefik
LAST DEPLOYED: Sun Dec 5 11:56:38 2021
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
我们查看下 deploy 和 pod,可以看到 pod 部署在了 node1 节点上:
root@master:~/k8s/traefik# kubectl get deploy -o wide
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
traefik 1/1 1 1 2m17s traefik traefik:2.5.4 app.kubernetes.io/instance=traefik,app.kubernetes.io/name=traefik
root@master:~/k8s/traefik# kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
traefik-5b747d6f97-hv2pl 1/1 Running 0 63s 10.233.90.16 node1 <none> <none>
3.2 安装 dashboard
我们启用 traefik 的 dashboard 的方式是创建一个 IngressRoute 资源,它的清单如下:
# dashboard.yaml
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: dashboard
spec:
entryPoints:
- web
routes:
- match: Host(`traefik.local.com`) && (PathPrefix(`/`) || PathPrefix(`/api`))
kind: Rule
services:
- name: api@internal
kind: TraefikService
以上内容保存为 dashboard.yaml,使用如下命令创建即可:
kubectl apply -f dashboard.yaml
我们查看下 该 ingressroute 的详细信息:
root@master:~/k8s/traefik# kubectl get ingressroute
NAME AGE
dashboard 4s
root@master:~/k8s/traefik# kubectl describe ingressroute dashboard
Name: dashboard
Namespace: default
Labels: <none>
Annotations: <none>
API Version: traefik.containo.us/v1alpha1
Kind: IngressRoute
Metadata:
Creation Timestamp: 2021-12-05T04:02:40Z
Generation: 1
Managed Fields:
API Version: traefik.containo.us/v1alpha1
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
.:
f:entryPoints:
f:routes:
Manager: kubectl-client-side-apply
Operation: Update
Time: 2021-12-05T04:02:40Z
Resource Version: 292491
UID: 20e64b25-f774-4896-9700-a3df630f9b0c
Spec:
Entry Points:
web
Routes:
Kind: Rule
Match: Host(`traefik.local.com`) && (PathPrefix(`/`) || PathPrefix(`/api`))
Services:
Kind: TraefikService
Name: api@internal
Events: <none>
这个时候,我们访问 http://traefik.local.com 就可以正常看到 traefik 的 dashboard 内容了:
3.3 部署业务
这里我们开始部署两个业务:music 和 video,这两个业务的默认请求分别返回如下内容
- music:this is music server!
- video:this is video server!
我们的计划是创建一个命名空间 demo,将该 demo 下的所有 pod 都部署在 node2 节点上,资源清单如下:
apiVersion: v1
kind: Namespace
metadata:
name: demo
annotations:
scheduler.alpha.kubernetes.io/node-selector: usefulness=demo
scheduler.alpha.kubernetes.io/node-selector 代表为该命名空间下的所有 pod 都配置指定的 nodeSelector,该功能需要开启插件 PodNodeSelector,详细信息参见我的另一篇文章 K8s零散小知识 。
将上述内容保存为 demo.yaml,并使用如下命令创建命名空间:
kubectl apply -f demo.yaml
接下来是我们创建这两个业务,两个 deploy 的资源清单如下:
music.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: music
namespace: demo
spec:
replicas: 1
minReadySeconds: 1
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: music
template:
metadata:
name: music
labels:
app: music
spec:
containers:
- name: music
image: jormin/music:latest
ports:
- containerPort: 80
protocol: TCP
readinessProbe:
periodSeconds: 60
httpGet:
path: /
port: 80
---
apiVersion: v1
kind: Service
metadata:
name: music
namespace: demo
spec:
type: NodePort
selector:
app: music
# 端口
ports:
- port: 80
targetPort: 80
video.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: video
namespace: demo
spec:
replicas: 1
minReadySeconds: 1
strategy:
rollingUpdate:
maxSurge: 1
maxUnavailable: 0
selector:
matchLabels:
app: video
template:
metadata:
name: video
labels:
app: video
spec:
containers:
- name: video
image: jormin/video:latest
ports:
- containerPort: 80
protocol: TCP
readinessProbe:
periodSeconds: 60
httpGet:
path: /
port: 80
---
apiVersion: v1
kind: Service
metadata:
name: video
namespace: demo
spec:
type: NodePort
selector:
app: video
# 端口
ports:
- port: 80
targetPort: 80
使用如下命令部署:
kubectl apply -f music.yaml
kubectl apply -f video.yaml
部署好后我们查看下这两个业务的资源,可以看到两个业务的 pod 都部署在了 node2 节点上。
root@master:~/k8s/traefik# kubectl get deploy -o wide -n demo
NAME READY UP-TO-DATE AVAILABLE AGE CONTAINERS IMAGES SELECTOR
music 1/1 1 1 88s music jormin/music:latest app=music
video 1/1 1 1 82s video jormin/video:latest app=video
root@master:~/k8s/traefik# kubectl get pod -o wide -n demo
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
music-655bb6dc78-mtxtj 1/1 Running 0 91s 10.233.96.8 node2 <none> <none>
video-7948dbfbb-2swbn 1/1 Running 0 85s 10.233.96.9 node2 <none> <none>
root@master:~/k8s/traefik# kubectl get svc -o wide -n demo
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
music NodePort 10.233.13.52 <none> 80:21066/TCP 95s app=music
video NodePort 10.233.5.118 <none> 80:31045/TCP 88s app=video
3.4 创建 IngressRoute
接下来我们创建两个 IngressRoute,分别指向 music 和 demo 的 service,资源清单如下:
apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
name: music-video
namespace: demo
spec:
entryPoints:
- web
routes:
- match: Host(`music.local.com`)
kind: Rule
services:
- name: music
port: 80
- match: Host(`video.local.com`)
kind: Rule
services:
- name: video
port: 80
上述内容保存为 ingress-route.yaml,使用如下命令创建:
kubectl apply -f ingress-route.yaml
创建好后查看下创建的路由信息:
root@master:~/k8s/traefik# kubectl get ingressroute -n demo
NAME AGE
music-video 7s
root@master:~/k8s/traefik# kubectl describe ingressroute music-video -n demo
Name: music-video
Namespace: demo
Labels: <none>
Annotations: <none>
API Version: traefik.containo.us/v1alpha1
Kind: IngressRoute
Metadata:
Creation Timestamp: 2021-12-05T04:32:09Z
Generation: 1
Managed Fields:
API Version: traefik.containo.us/v1alpha1
Fields Type: FieldsV1
fieldsV1:
f:metadata:
f:annotations:
.:
f:kubectl.kubernetes.io/last-applied-configuration:
f:spec:
.:
f:entryPoints:
f:routes:
Manager: kubectl-client-side-apply
Operation: Update
Time: 2021-12-05T04:32:09Z
Resource Version: 298837
UID: fc2e5007-9dcc-463d-a5fe-5b4d96dd201e
Spec:
Entry Points:
web
Routes:
Kind: Rule
Match: Host(`music.local.com`)
Services:
Name: music
Port: 80
Kind: Rule
Match: Host(`video.local.com`)
Services:
Name: video
Port: 80
Events: <none>
3.5 验证
经过上述几步,我们的操作就完成了,接下来我们使用 curl 进行验证:
➜ ~ curl music.local.com && echo ''
this is music server!
➜ ~ curl video.local.com && echo ''
this is video server!
4. 总结
本篇文章是在 k8s搭配traefik 的基础上,用 IngressRoute 替代了 Ingress,实际工作中才用 Traefik 这个控制器的话,我们更倾向于使用 IngressRoute 这种类型资源。