traefik与TLS自签证书

traefik与TLS自签证书

traefik与TLS自签证书

最近发现一个神器 traefik, 其实很久以前就 star 了的, 只是没有多加去了解;
这几天在测试各种负载均衡器, 又好好看了一下它, 感觉十分惊艳;

给太长不看的同学说一下本文核心:

  1. 一个端口代理所有
  2. TLS SNI
  3. 负载均衡的路径配置由谁维护

测试过程中被这个 TLS 的证书配置拦住了好久, 刚才终于理解了一些, 值得记录分享一下;

关于负载均衡的路径信息该在哪个节点维护

一般来说数据流有 3 个区域: 客户端 -> 负载均衡器(lb) -> N 个上游服务器(service)

路线一: 在客户端配置

  1. 一般模式: 客户端请求的 header 头内有一个字段包含 service 的标记, lb 根据这个标记转发到不同的服务器;
    这个标记一般是个 cookie, lb 查询本地映射关系后转到指定的服务器;
    如果连 lb 这里的映射信息也不想维护, 干脆可以在这个标记里面写明 server 的 ip
    这个标记首次是怎么来的呢?可以是客户端内置的一个列表, 也可以是首次访问时 lb 随机指定;

  2. 特殊模式: 客户先请求一个接口, 获取一个地址列表, 然后选一个再发起业务请求, 例如DNS

优点: 简单, 可控, 客户端调试时很容易定位到这个请求是哪个服务器处理的;
缺点: 极端情况下变更服务节点可能需要同时改这三个区域的配置;

路线二: 在 LB 上配置

传统的 nginx, haprox 等都是这样做的; 即 LB 根据自身独立的路径配置规则然后转发到后端;

优点: 好理解, 可控程度高
缺点: 后端变更后(如新增节点), 需要再改一次 lb 的配置

路线三: 在后端服务上配置

核心是 lb 动态获取后端服务的变化, 有多种细分实现方法;

  1. 方法一: lb 提供一个接口, 后端服务上线时自动去 lb 上注册, 下线时主动注销;

  2. 方法二: 有一个公共注册中心或配置中心(etcd, consul, redis, eureka, mysql 等),
    service 去注册和注销, lb 动态去拉取更新, 或者是 lb 进行 watch 监听;

    这两个方法都存在的问题: 程序异常停止没主动下线时的问题, 需要配合健康检查;

  3. 路线三: 和方法二差不多, 但 lb 不是监听注册中心, 而是监听 docker, kubernetes 等组件的 api, 服务一有变动立即可知; 程序内部不需要引入注册类的库, 历史老旧应用无需改造即可适配, 特别适合云原生环境;

优点: 一次变更只需要后端的服务改一次
缺点: 假设某个节点服务异常, 希望隔离流量待排查, 而不是直接下线服务时, 比较麻烦;

路线选择

根据实际情况选, 绝对性能型优先客户端负载, 可控型优先lb配置, 自动化程度要求高就由后端配置;
如果是云原生或微服务场景更推荐路线三

关于 traefik 我的看法

做负载均衡软件很多, 但这么有特色的就很少了; 当然其它代理或多或少也能实现这些功能, 但配置起来就麻烦一些;

traefik 的思路也是监听 docker 或 kubernetes 的 api, 但同时也支持监听 etcd, consol, 文件 等

特色: 一个端口代理所有

场景

假设你有很多的服务组件需要同时暴露出来, 需要考虑什么呢?

如果是单机, 首先得规划每个组件各自的端口, 避免冲突, 而且得考虑后续新增的组件端口规划;
如果生产环境等网络策略要求比较严格的地方, 还得去申请网络策略, 也需要考虑;

为了方便, 往往我们的做法都是提前申请和规划一个端口范围, 给以后的应用做预留

有没有一个办法可以做到 一个端口 代理所有业务呢?
如 http, https, redis, mysql, memcache 等杂七杂八的各种服务都通过一个端口来提供服务呢?后续新增的组件也都通过这个端口提供服务呢?再也不去管理这个lb的配置了呢?


以前的办法是(代理 + 隧道), 简单点就如 ssh 隧道, 但这种方式还是比较麻烦的, 还是要经常去配置几个节点, 不方便;

新办法核心原理: 根据TLS请求的 sni 来路由

简述步骤

  1. 制作一个通配符证书, 例如 *.services.wait
  2. traefik 监听一个端口, 并使用 这张证书
  3. 请求到来后, 根据 sni 路由到指定的 traefik 内部服务;
  4. traefik 内部服务负载到不同的后端节点上

实例

traefik TLS 的拦(难)点解析

  1. 如果没有提供默认证书, 则会使用 traefik 自己创建的一个证书
  2. 如果 sni 没有匹配的证书, 则会走默认证书;
    特别注意这里的匹配 是"完全等于"; 即便是通配符证书也不等的; 所以下例走的是默认证书;
  3. tls 的内容属于动态内容, 不能加到静态配置里去
  4. 后端服务只需要在声明路由时 tls=true 即可;
1
2
3
4
5
6
7
## tls.yml
tls:
  stores:
    default:
      defaultCertificate:
        certFile: /ssl/services.wait.pem
        keyFile: /ssl/services.wait-key.pem
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
## docker-compose.yml
version: '3'
services:
  traefik:
    image: traefik:v2.9.8
    command:
      - "--api.insecure=true"
      - "--log.level=DEBUG"
      - "--entrypoints.port443.address=:443"
      - "--providers.docker"
      - "--providers.docker.exposedbydefault=false"
      - "--providers.file"
      - "--providers.file.directory=/etc/traefik/dynamic_conf"
      - "--providers.file.watch=true"

    ports:
      - "443:443"
      - "8080:8080"   # web ui, 必须先启用 --api.insecure=true
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./tls.yml:/etc/traefik/dynamic_conf/tls.yml:ro
      - /home/wait/sync/git/mycode/ssl/services.wait/:/ssl/:ro

  baseWeb:
    restart: unless-stopped
    image: registry.wait/cwx/base-web:0.0.4
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.r-web.entrypoints=port443"
      - "traefik.http.routers.r-web.rule=Host(`web.services.wait`)"
      - "traefik.http.routers.r-web.tls=true"
    deploy:
      replicas: 2

  redis:
    restart: unless-stopped
    image: registry.wait/cwx/db/redis:7.0.7-alpine3.17
    command:
      [
        "--maxmemory", "128mb"
      ]
    labels:
      - "traefik.enable=true"
      - "traefik.tcp.routers.r-redis.entrypoints=port443"
      - "traefik.tcp.routers.r-redis.rule=HostSNI(`redis.services.wait`)"
      - "traefik.tcp.routers.r-redis.tls=true"

验证

1
curl --cacert /home/wait/code/ssl/ca/cwxCA.pem https://web.services.wait
1
2
3
4
5
6
7
redis-cli -h redis.services.wait -p 443 \
--tls \
--cert /home/wait/code/ssl/services.wait/services.wait.pem \
--key /home/wait/code/ssl/services.wait/services.wait-key.pem \
--cacert /home/wait/code/ssl/ca/cwxCA.pem \
--sni redis.services.wait \
info

微信搜索IT运维小秋

Licensed under CC BY-NC-SA 4.0
转载或引用本文时请遵守许可协议,知会作者并注明出处
不得用于商业用途!
最后更新于 2023-02-07 00:00 UTC