跳到主要内容

k3s 的 traefik 配置

traefik 是怎么部署在 K3s 上的

在 k8s 中,ingress 有 3 种部署方式:

1、IngressController 作为 DaemonSet 部署,暴露hostPort; 2、作为 Deployment 部署,给 IngressController 开一个 NodePort 服务; 3、作为 Deployment 部署,给 IngressController 暴露一个 LoadBalancer 服务(需要借助云厂商提供的cloud provider能力分配ip);

在 k3s 中,使用了一个叫 Klipper Load Balancer 负载均衡器(新版本是 ServiceLB),它会以 daemonset 的方式默认在所有节点上跑一个 pod(当节点配置污点时,会启动报错,这里需要自己配置),作为代理服务。所以在 k3s 中,traefik 以 Deployment 和 DaemonSet 方式部署效果是一样的。

对比 k8s 第二种方式部署的 traefik 情况如下:

可以看到 k3s 中的 traefik 变成下面这样的 svclb-traefik-xxxxx 的服务,这个服务是由 Klipper Load Balancer 生成的,它会根据 traefik 的端口和节点的 ip 生成一个负载均衡器,这个负载均衡器会将请求转发到 traefik 的 pod 上。

traefik 部署副本问题?

在k8s中 traefik 部署在哪个node 节点,哪个就可以接流量。但是在k3s中由于使用了 Klipper Load Balancer,默认所有节点都可以接流量,但是 svclb 这个 daemonset 是依赖 traefik 这个服务的,当 traefik 服务重启时,svclb 不可用。建议最少配置 2 个traefik pod,且开启非亲和性,让 pod 不要部署在一个 node 节点,提高可用性。

deployment:
enabled: true
# Can be either Deployment or DaemonSet
kind: Deployment
# Number of pods of the deployment (only applies when kind == Deployment)
replicas: 2 ## <<<< 至少两个副本,且开启 pod 非亲和性。

affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- traefik
topologyKey: kubernetes.io/hostname

traefik 监控配置?

在 charts 中 metrics 的监控接口并没有启动,我们可以修改 ports.metrics.expose 参数启动,如下:

  metrics:
port: 9100
expose: true # <<< 改为true,默认为false
exposedPort: 9100

之后增加一个 serviceMonitor,就能在 Prometheus 中看到该 target 了。

apiVersion: monitoring.coreos.com/v1
kind: ServiceMonitor
metadata:
name: traefik
namespace: cattle-monitoring-system
labels:
app.kubernetes.io/name: traefik
spec:
jobLabel: app.kubernetes.io/name
selector:
matchLabels:
app.kubernetes.io/name: traefik # 服务发现label
namespaceSelector:
matchNames:
- cattle-system # traefik安装的命名空间
endpoints:
- port: metrics
interval: 30s
path: /metrics

traefik 的修改(v1.19.1+k3s1 以前)

Rancher 中大部分组件都可以通过自定 helm chart 来安装,只要保持资源名字一致即可。可以先用 Rancher 的应用商店中的 chart 包安装一遍,创建些应用需要的 CRD,再使用 helm chart安装。

编辑文件

sudo vim ./traefik-config.yaml

自定义的 values 文件 k3s-values.yaml

# 默认的 ingressRouter 是开启的,使用内部 CRD service,自定义dashboard ingressroute时,可设置为 false,自己创建。
# ingressRoute:
# dashboard:
# enabled: false


deployment:
enabled: true
# Can be either Deployment or DaemonSet
kind: Deployment
# Number of pods of the deployment (only applies when kind == Deployment)
replicas: 2

ports:
traefik:
port: 9000
# You SHOULD NOT expose the traefik port on production deployments.
# If you want to access it from outside of your cluster,
# use `kubectl port-forward` or create a secure ingress
# kubectl port-forward $(kubectl get pods --selector "app.kubernetes.io/name=traefik" --output=name -n cattle-system) 9000:9000 -n cattle-system
expose: false
exposedPort: 9000
web:
port: 8000
expose: true
exposedPort: 80
websecure:
port: 8443
expose: true
exposedPort: 443
tls:
enabled: true # 需要开启,否则配置的ingress不支持配置tls证书
metrics:
port: 9100
expose: true
exposedPort: 9100

affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- traefik
topologyKey: kubernetes.io/hostname

最后再使用 helm 安装

# 安装
helm install traefik ./ -n kube-system -f traefik-config.yaml
# 更新自定义配置时使用
helm upgrade --install traefik ./ -n kube-system -f traefik-config.yaml

traefik 的修改(v1.19.1+k3s1 以后)

在 v1.19.1+k3s1 以后 K3s 通过 HelmChartConfig 资源进行自定义部署。HelmChartConfig 资源必须与对应的 HelmChart 名称和命名空间匹配,并且支持提供额外的 valuesContent,它作为附加值文件传递给 helm 命令。

创建一个名为 /var/lib/rancher/k3s/server/manifests/traefik-config.yaml 的文件并使用以下内容填充它:

sudo vim /var/lib/rancher/k3s/server/manifests/traefik-config.yaml

HelmChart 字段定义

apiVersion: helm.cattle.io/v1
kind: HelmChartConfig
metadata:
name: traefik
namespace: kube-system
spec:
valuesContent: |-
deployment:
enabled: true
kind: Deployment
replicas: 2

ports:
traefik:
port: 9000
expose: false
exposedPort: 9000
web:
port: 8000
expose: true
exposedPort: 80
websecure:
port: 8443
expose: true
exposedPort: 443
tls:
enabled: true
metrics:
port: 9100
expose: true
exposedPort: 9100

affinity:
podAntiAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
- labelSelector:
matchExpressions:
- key: app.kubernetes.io/name
operator: In
values:
- traefik
topologyKey: kubernetes.io/hostname

配置面板

# traefik-dashboard-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: traefik-ingress
namespace: kube-system
annotations:
kubernetes.io/ingress.class: traefik
spec:
rules:
- host: traefik.quicktoolset.top
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: traefik-dashboard
port:
number: 9000
# https://k3s.rocks/traefik-dashboard/
# traefik-dashboard-service.yaml
apiVersion: v1
kind: Service
metadata:
name: traefik-dashboard
namespace: kube-system
labels:
app.kubernetes.io/instance: traefik
app.kubernetes.io/name: traefik-dashboard
spec:
type: ClusterIP
ports:
- name: traefik
port: 9000
targetPort: traefik
protocol: TCP
selector:
app.kubernetes.io/instance: traefik-kube-system
app.kubernetes.io/name: traefik

然后修改配置

kubectl edit ingressroute/traefik-dashboard -oyaml -n kube-system
apiVersion: traefik.containo.us/v1alpha1                                                                                                        
kind: IngressRoute
metadata:
annotations:
meta.helm.sh/release-name: traefik
meta.helm.sh/release-namespace: kube-system
creationTimestamp: "2023-10-29T01:20:16Z"
generation: 2
labels:
app.kubernetes.io/instance: traefik-kube-system
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: traefik
helm.sh/chart: traefik-21.2.1_up21.2.0
name: traefik-dashboard
namespace: kube-system
resourceVersion: "962424"
uid: d317ac00-e189-482b-a810-a5c287095e77
spec:
entryPoints:
- traefik
routes:
- kind: Rule
match: Host(`traefik.quicktoolset.top`)
services:
- kind: TraefikService
name: api@internal

错误处理

Skipping service: no endpoints found"

time="2023-11-14T08:21:15Z" level=error msg="Skipping service: no endpoints found" servicePort="&ServiceBackendPort{Name:,Number:4080,}" ingress=zincsearch namespace=common-namespace providerName=kubernetes serviceName=zincsearch
time="2023-11-14T08:21:15Z" level=error msg="Skipping service: no endpoints found" serviceName=zincsearch providerName=kubernetes servicePort="&ServiceBackendPort{Name:,Number:4080,}" ingress=zincsearch namespace=common-namespace

经排查发现 cert-manager 组件的日志报错:

E1114 07:02:51.556942       1 controller.go:98] ingress 'common-namespace/zincsearch-ingress' in work queue no longer exists
E1114 07:56:07.684763 1 controller.go:98] ingress 'common-namespace/zincsearch' in work queue no longer exists
E1114 07:57:05.587583 1 controller.go:98] ingress 'common-namespace/zincsearch' in work queue no longer exists
E1114 08:20:29.944109 1 controller.go:98] ingress 'common-namespace/zincsearch-ingress' in work queue no longer exists

它们表明 cert-manager 正在尝试处理与 common-namespace 命名空间中的 zincsearch-ingresszincsearch 相关的 Ingress 资源,但这些 Ingress 资源在工作队列中已经不存在了。这种情况通常发生在以下几种情况中:

  1. Ingress 资源被删除或重命名:如果您删除或重命名了 Ingress 资源,cert-manager 可能还在尝试处理旧的资源,导致这种错误。
  2. 网络或 API 服务器问题:有时候,由于网络问题或 Kubernetes API 服务器的问题,cert-manager 可能无法正确地检索或更新 Ingress 资源的状态。
  3. 同步问题:在 Kubernetes 集群中,有时会出现资源状态同步的延迟,这可能导致 cert-manager 试图处理一个已经不再存在的资源。

解决步骤:

  1. 检查 Ingress 资源

    • 确认 zincsearch-ingresszincsearch Ingress 资源是否仍然存在于 common-namespace 命名空间中。
    • 使用命令 kubectl get ingress -n common-namespace 来查看当前的 Ingress 资源。
  2. 检查 cert-manager 日志

    • 查看 cert-manager 的更详细日志,以了解更多关于错误的信息。使用命令 kubectl logs -n cert-manager [cert-manager-pod-name]
  3. 重启 cert-manager

    • 有时重启 cert-manager 可以解决同步问题。使用命令 kubectl rollout restart deployment -n cert-manager
  4. 确认 cert-manager 配置

    • 确保 cert-manager 的配置正确,特别是与 Ingress 资源相关的部分。
  5. 检查 Kubernetes 集群状态

    • 确保 Kubernetes 集群运行正常,没有网络或 API 服务器的问题。
  6. 重新应用 Ingress 资源

    • 如果 Ingress 资源被删除或更改,重新应用正确的 Ingress 配置可能有助于解决问题。
  7. 检查证书请求

    • 确认与 Ingress 资源相关联的证书请求是否正确处理。使用命令 kubectl get certificate -n common-namespace

如果问题持续存在,可能需要更深入地检查 cert-manager 的配置和 Kubernetes 集群的状态,以确定具体的问题所在。