traefik与TLS自签证书
最近发现一个神器 traefik, 其实很久以前就 star 了的, 只是没有多加去了解;
这几天在测试各种负载均衡器, 又好好看了一下它, 感觉十分惊艳;
给太长不看的同学说一下本文核心:
- 一个端口代理所有
- TLS SNI
- 负载均衡的路径配置由谁维护
测试过程中被这个 TLS 的证书配置拦住了好久, 刚才终于理解了一些, 值得记录分享一下;
关于负载均衡的路径信息该在哪个节点维护
一般来说数据流有 3 个区域: 客户端 -> 负载均衡器(lb) -> N 个上游服务器(service)
路线一: 在客户端配置
-
一般模式: 客户端请求的 header 头内有一个字段包含 service 的标记, lb 根据这个标记转发到不同的服务器;
这个标记一般是个 cookie, lb 查询本地映射关系后转到指定的服务器;
如果连 lb 这里的映射信息也不想维护, 干脆可以在这个标记里面写明 server 的 ip
这个标记首次是怎么来的呢?可以是客户端内置的一个列表, 也可以是首次访问时 lb 随机指定; -
特殊模式: 客户先请求一个接口, 获取一个地址列表, 然后选一个再发起业务请求, 例如DNS
优点: 简单, 可控, 客户端调试时很容易定位到这个请求是哪个服务器处理的;
缺点: 极端情况下变更服务节点可能需要同时改这三个区域的配置;
路线二: 在 LB 上配置
传统的 nginx, haprox 等都是这样做的; 即 LB 根据自身独立的路径配置规则然后转发到后端;
优点: 好理解, 可控程度高
缺点: 后端变更后(如新增节点), 需要再改一次 lb 的配置
路线三: 在后端服务上配置
核心是 lb 动态获取后端服务的变化, 有多种细分实现方法;
-
方法一: lb 提供一个接口, 后端服务上线时自动去 lb 上注册, 下线时主动注销;
-
方法二: 有一个公共注册中心或配置中心(etcd, consul, redis, eureka, mysql 等),
service 去注册和注销, lb 动态去拉取更新, 或者是 lb 进行 watch 监听;这两个方法都存在的问题: 程序异常停止没主动下线时的问题, 需要配合健康检查;
-
路线三: 和方法二差不多, 但 lb 不是监听注册中心, 而是监听 docker, kubernetes 等组件的 api, 服务一有变动立即可知; 程序内部不需要引入注册类的库, 历史老旧应用无需改造即可适配, 特别适合云原生环境;
优点: 一次变更只需要后端的服务改一次
缺点: 假设某个节点服务异常, 希望隔离流量待排查, 而不是直接下线服务时, 比较麻烦;
路线选择
根据实际情况选, 绝对性能型优先客户端负载, 可控型优先lb配置, 自动化程度要求高就由后端配置;
如果是云原生或微服务场景更推荐路线三
关于 traefik 我的看法
做负载均衡软件很多, 但这么有特色的就很少了; 当然其它代理或多或少也能实现这些功能, 但配置起来就麻烦一些;
traefik 的思路也是监听 docker 或 kubernetes 的 api, 但同时也支持监听 etcd, consol, 文件 等
特色: 一个端口代理所有
场景
假设你有很多的服务组件需要同时暴露出来, 需要考虑什么呢?
如果是单机, 首先得规划每个组件各自的端口, 避免冲突, 而且得考虑后续新增的组件端口规划;
如果生产环境等网络策略要求比较严格的地方, 还得去申请网络策略, 也需要考虑;
为了方便, 往往我们的做法都是提前申请和规划一个端口范围, 给以后的应用做预留
有没有一个办法可以做到 一个端口 代理所有业务呢?
如 http, https, redis, mysql, memcache 等杂七杂八的各种服务都通过一个端口来提供服务呢?后续新增的组件也都通过这个端口提供服务呢?再也不去管理这个lb的配置了呢?
有
以前的办法是(代理 + 隧道), 简单点就如 ssh 隧道, 但这种方式还是比较麻烦的, 还是要经常去配置几个节点, 不方便;
新办法核心原理: 根据TLS请求的 sni 来路由
简述步骤
- 制作一个通配符证书, 例如 *.services.wait
- traefik 监听一个端口, 并使用 这张证书
- 请求到来后, 根据 sni 路由到指定的 traefik 内部服务;
- traefik 内部服务负载到不同的后端节点上
实例
traefik TLS 的拦(难)点解析
- 如果没有提供默认证书, 则会使用 traefik 自己创建的一个证书
- 如果 sni 没有匹配的证书, 则会走默认证书;
特别注意这里的匹配 是"完全等于"; 即便是通配符证书也不等的; 所以下例走的是默认证书; - tls 的内容属于动态内容, 不能加到静态配置里去
- 后端服务只需要在声明路由时 tls=true 即可;
|
|
|
|
验证
|
|
|
|