飞道的博客

贫苦家庭用户的 Envoy xDS 控制平面

294人阅读  评论(0)


点击 "阅读原文" 可以获得更好的阅读体验。

前言

之前家里的路由器一直用的都是网件 R7000 搭载的梅林固件,虽说性能也还不错,比两百块钱的小米路由器强多了,但还是不能满足我的需求,装了某魔法软件后内存蹭蹭蹭爆满啊。终于有理由换软路由了,此时不换更待何时!

经过一番对比,最后决定在某宝上入手了一款低功耗的 J3160,4 核 4 G,700 大洋左右,刷了个 LEDE 系统,这下绝对够用了。跑了几个魔法软件和一堆容器也没耗多少资源,还是 x86 香啊!

R7000 就老老实实通过 Access Point 模式作为二级路由提供 WiFi 吧。

到这里有人可能要问了,说了这么多跟这篇文章的主题有什么关系呢?别急,下面进入主题。

01

背景

作为顶级贫苦玩家,肯定会在家里装上各种奇奇怪怪的应用,Aria2 和 Transmission 肯定不能少。作为顶级云原生狂热信徒,监控一条龙服务肯定不能少,至少应该上一套 GrafanaPrometheus。然而,这么多乱七八糟的端口,我可记不住。。。

我需要一款负载均衡器来反代所有的服务,别跟我说 Nginx,作为云原生舔狗,用 Nginx 是不可能的,必须用我的偶像 Envoy 来做反代啊,既能反代 Web 服务,还能代替防火墙的端口映射功能(就是反代 TCP 啦),最重要的是还能暴露所有 Upstream 服务的 metrics,再结合 Prometheus 和 Grafana,不香吗?(你想想,连 samba 和 UDP 服务都能监控)

第一步当然是让路由器获取外网 IP 了,现在上海电信用的都是 SDN 网关,破解都无从破解,但很多人不知道的是,其实你可以打电信客服电话让人家在后台把 SDN 网关改成桥接模式。。。 改完桥接模式就好办了,直接路由器拨号就是外网 IP。

下面就是改 LEDE Web 服务端口,因为 Envoy 得用 80 端口,所以把它的端口改成别的,比如 81 就不错:


   
  1. $ cat /etc/config/uhttpd
  2. config uhttpd 'main'
  3. list listen_http '0.0.0.0:81'
  4. list listen_http '[::]:81'
  5. list listen_proxy '127.0.0.1:8000'
  6. list listen_https '0.0.0.0:6443'
  7. list listen_https '[::]:6443'
  8. option home '/www'
  9. ...

改完之后重启 httpd 服务:

$ /etc/init.d/uhttpd restart

DDNS 和申请 https 证书什么的我就不说了,不是本文的重点。

02

基于文件的 xDS 动态更新

80443 端口被腾出来之后,就可以愉快地使用反代了。可是安装 Envoy 是个头疼的问题啊,编译太复杂,GetEnvoy[1] 项目又不支持 busybox,只能通过容器跑了。配置如图:

下面就是老老实实写配置文件,没什么可说的,但问题就出在这里,Upstream 服务不多倒好办,一旦变多,Envoy 配置文件会过于冗长,很容易看花眼。虽想到了用控制平面来动态更新配置,但我没必要单独起个控制平面服务,还有没有别的办法呢?有的,其实 Envoy 是可以将文件作为配置的订阅来源的。方法很简单,首先需要参加一个 Bootstrap 引导程序配置文件,里面定义了 node 信息和动态资源:


   
  1. $ cat envoy.yaml
  2. node:
  3. id: node0
  4. cluster: cluster0
  5. dynamic_resources:
  6. lds_config:
  7. path: /etc/envoy/lds.yaml
  8. cds_config:
  9. path: /etc/envoy/cds.yaml
  10. admin:
  11. access_log_path: "/dev/stdout"
  12. address:
  13. socket_address:
  14. address: "::"
  15. ipv4_compat: true
  16. port_value: 15001

Envoy 将使用 inotify[2](MacOS 用的是 kqueue)来监视文件的更改,一旦检测到更改,就立即订阅更新。查看系统是否支持 inotify:


   
  1. $ ll /proc/sys/fs/inotify/
  2. -rw-r--r-- 1 root root 0 Dec 23 16: 05 max_queued_events
  3. -rw-r--r-- 1 root root 0 Dec 23 16: 05 max_user_instances
  4. -rw-r--r-- 1 root root 0 Dec 23 16: 05 max_user_watches

lds.yaml 里是 Listener 的配置,cds.yaml 里是 Cluster 的配置,先往 lds.yaml 中加入如下的配置:


   
  1. version_info: "0"
  2. resources:
  3. - "@type": type.googleapis.com/envoy.api.v2.Listener
  4. name: listener_http_v4
  5. address:
  6. socket_address:
  7. address: 0.0 .0 .0
  8. port_value: 80
  9. filter_chains:
  10. - filters:
  11. - name: envoy.http_connection_manager
  12. typed_config:
  13. "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
  14. stat_prefix: ingress_http
  15. codec_type: AUTO
  16. access_log:
  17. name: envoy.file_access_log
  18. typed_config:
  19. "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog
  20. path: /dev/stdout
  21. route_config:
  22. name: http_route_v4
  23. virtual_hosts:
  24. - name: backend
  25. domains:
  26. - "router.fuckcloudnative.io"
  27. - "mynas.fuckcloudnative.io"
  28. routes:
  29. - match:
  30. prefix: "/"
  31. redirect:
  32. https_redirect: true
  33. port_redirect: 8443
  34. response_code: "FOUND"
  35. - name: default
  36. domains:
  37. - "*"
  38. routes:
  39. - match:
  40. prefix: "/"
  41. route:
  42. cluster: lede
  43. http_filters:
  44. - name: envoy.router

域名改成你自己的就好,路由分为两部分,通过那两个域名访问的就会被转到 https,其他的都转到 lede Web 服务。其实 http 转 https 的那部分路由可以删掉,因为国内的运营商基本上都把 80 端口封了,外网是无法访问的。第二部分不能删除,删除之后就不能通过内网访问 lede Web 界面了。

再加入 https 的配置:


   
  1. - "@type": type.googleapis.com/envoy.api.v2.Listener
  2. name: listener_https_v4
  3. address:
  4. socket_address:
  5. address: 0.0 .0 .0
  6. port_value: 8443
  7. filter_chains:
  8. - filter_chain_match:
  9. server_names: "router.fuckcloudnative.io"
  10. transport_socket:
  11. name: envoy.transport_sockets.tls
  12. typed_config:
  13. "@type": type.googleapis.com/envoy.api.v2.auth.DownstreamTlsContext
  14. common_tls_context:
  15. tls_certificates:
  16. - certificate_chain:
  17. filename: "/etc/ssl/router.fuckcloudnative.io/3207748_router.fuckcloudnative.io.pem"
  18. private_key:
  19. filename: "/etc/ssl/router.fuckcloudnative.io/3207748_router.fuckcloudnative.io.key"
  20. filters:
  21. - name: envoy.http_connection_manager
  22. typed_config:
  23. "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
  24. stat_prefix: ingress_https
  25. codec_type: AUTO
  26. access_log:
  27. name: envoy.file_access_log
  28. typed_config:
  29. "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog
  30. path: /dev/stdout
  31. route_config:
  32. name: https_route_v4_default
  33. virtual_hosts:
  34. - name: default
  35. domains:
  36. - "*"
  37. routes:
  38. - match:
  39. prefix: "/"
  40. route:
  41. cluster: lede
  42. http_filters:
  43. - name: envoy.router

接下来往 cds.yaml 中加入 Cluster 配置:


   
  1. version_info: "0"
  2. resources:
  3. - "@type": type.googleapis.com/envoy.api.v2.Cluster
  4. name: lede
  5. connect_timeout: 1s
  6. type: strict_dns
  7. dns_lookup_family: V4_ONLY
  8. lb_policy: ROUND_ROBIN
  9. load_assignment:
  10. cluster_name: lede
  11. endpoints:
  12. - lb_endpoints:
  13. - endpoint:
  14. address:
  15. socket_address:
  16. address: 127.0 .0 .1
  17. port_value: 81

由于 Docker 对 inotify 的支持不太友好,有时不会检测不到文件系统的更改,所以最好的办法是强制更改,原理很简单,将文件重命名,然后再改回来。写一个脚本就好了:


   
  1. $ cat apply.sh
  2. #!/bin/bash
  3. mv cds.yaml cds.yaml.temp
  4. mv cds.yaml.temp cds.yaml
  5. mv lds.yaml lds.yaml.temp
  6. mv lds.yaml.temp lds.yaml

注意:必须先更新 CDS,后更新 LDS

执行脚本之后,查看 Envoy 日志,发现配置已经生效:


   
  1. $ docker logs -f envoy
  2. [ 2019 -12 -23 09: 22: 14.644][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 71] cds: add 1 cluster(s), remove 0 cluster(s)
  3. [ 2019 -12 -23 09: 22: 14.648][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'lede'
  4. [ 2019 -12 -23 09: 22: 30.186][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_http_v4'
  5. [ 2019 -12 -23 09: 22: 45.881][ 1][warning][config] [source/server/listener_impl.cc: 287] adding listener '0.0.0.0:8443': filter chain match rules require TLS Inspector listener filter, but it isn 't configured, trying to inject it (this might fail if Envoy is compiled without it)
  6. [2019-12-23 09:22:45.882][1][info][upstream] [source/server/lds_api.cc:71] lds: add/update listener 'listener_https_v4 '

ipv6

上面只是 ipv4 的配置,如果你的宽带开启了 ipv6,还可以开启 ipv6 端口。至于我为什么要将 ipv4 和 ipv6 分开呢,因为据我测试,电信运营商只封了 ipv4 的 80443 端口,ipv6 还可以用,所以我需要为 ipv4 和 ipv6 分配不同的路由策略。在 lds.yaml 中加入 ipv6 的配置:


   
  1. - "@type": type.googleapis.com/envoy.api.v2.Listener
  2. name: listener_http_v6
  3. address:
  4. socket_address:
  5. address: "::"
  6. port_value: 80
  7. filter_chains:
  8. - filters:
  9. - name: envoy.http_connection_manager
  10. typed_config:
  11. "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
  12. stat_prefix: ingress_http
  13. codec_type: AUTO
  14. access_log:
  15. name: envoy.file_access_log
  16. typed_config:
  17. "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog
  18. path: /dev/stdout
  19. route_config:
  20. name: http_route_v6
  21. virtual_hosts:
  22. - name: backend
  23. domains:
  24. - "router.fuckcloudnative.io"
  25. - "mynas.fuckcloudnative.io"
  26. routes:
  27. - match:
  28. prefix: "/"
  29. redirect:
  30. https_redirect: true
  31. port_redirect: 443
  32. response_code: "FOUND"
  33. - name: default
  34. domains:
  35. - "*"
  36. routes:
  37. - match:
  38. prefix: "/"
  39. route:
  40. cluster: lede
  41. http_filters:
  42. - name: envoy.router
  43. - "@type": type.googleapis.com/envoy.api.v2.Listener
  44. name: listener_https_v6
  45. address:
  46. socket_address:
  47. address: "::"
  48. port_value: 443
  49. filter_chains:
  50. - filter_chain_match:
  51. server_names: "router.fuckcloudnative.io"
  52. transport_socket:
  53. name: envoy.transport_sockets.tls
  54. typed_config:
  55. "@type": type.googleapis.com/envoy.api.v2.auth.DownstreamTlsContext
  56. common_tls_context:
  57. tls_certificates:
  58. - certificate_chain:
  59. filename: "/etc/ssl/router.fuckcloudnative.io/3207748_router.fuckcloudnative.io.pem"
  60. private_key:
  61. filename: "/etc/ssl/router.fuckcloudnative.io/3207748_router.fuckcloudnative.io.key"
  62. filters:
  63. - name: envoy.http_connection_manager
  64. typed_config:
  65. "@type": type.googleapis.com/envoy.config.filter.network.http_connection_manager.v2.HttpConnectionManager
  66. stat_prefix: ingress_https
  67. codec_type: AUTO
  68. access_log:
  69. name: envoy.file_access_log
  70. typed_config:
  71. "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog
  72. path: /dev/stdout
  73. route_config:
  74. name: https_route_v6_default
  75. virtual_hosts:
  76. - name: default
  77. domains:
  78. - "*"
  79. routes:
  80. - match:
  81. prefix: "/"
  82. route:
  83. cluster: lede
  84. http_filters:
  85. - name: envoy.router

执行 apply.sh 使配置生效,查看日志:


   
  1. $ docker logs -f envoy
  2. [ 2019 -12 -23 09: 43: 44.431][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_http_v6'
  3. [ 2019 -12 -23 09: 43: 44.441][ 1][warning][config] [source/server/listener_impl.cc: 287] adding listener '[::]:443': filter chain match rules require TLS Inspector listener filter, but it isn 't configured, trying to inject it (this might fail if Envoy is compiled without it)
  4. [2019-12-23 09:43:44.441][1][info][upstream] [source/server/lds_api.cc:71] lds: add/update listener 'listener_https_v6 '

Grafana

Grafana 的安装我就不多说了,直接容器跑,配置如下:

为了能够通过反向代理正确访问 Grafana,需要对 Grafana 的配置做一些调整,修改 grafana.ini 中的以下几个字段:


   
  1. [server]
  2. domain = foo.bar
  3. root_url = %(protocol)s: //%(domain)s/grafana/

domain 的值换成你自己的域名。

修改 Listener listener_https_v4 的路由:


   
  1. route_config:
  2. name: https_route_v4_default
  3. virtual_hosts:
  4. - name: default
  5. domains:
  6. - "*"
  7. routes:
  8. - match:
  9. prefix: "/grafana/"
  10. route:
  11. cluster: grafana
  12. - match:
  13. prefix: "/"
  14. route:
  15. cluster: lede

修改 Listener listener_https_v6 的路由:


   
  1. route_config:
  2. name: https_route_v6_default
  3. virtual_hosts:
  4. - name: default
  5. domains:
  6. - "*"
  7. routes:
  8. - match:
  9. prefix: "/grafana/"
  10. route:
  11. cluster: grafana
  12. - match:
  13. prefix: "/"
  14. route:
  15. cluster: lede

cds.yaml 中添加 Cluster:


   
  1. - "@type": type.googleapis.com/envoy.api.v2.Cluster
  2. name: grafana
  3. connect_timeout: 1s
  4. type: strict_dns
  5. dns_lookup_family: V4_ONLY
  6. lb_policy: ROUND_ROBIN
  7. load_assignment:
  8. cluster_name: grafana
  9. endpoints:
  10. - lb_endpoints:
  11. - endpoint:
  12. address:
  13. socket_address:
  14. address: 127.0 .0 .1
  15. port_value: 3000

应用更新:

$ ./apply.sh

然后就可以通过 subpath 访问 Grafana 了。

TCP

一般情况下,路由器的端口映射都是通过 iptables 来做的,但我既然用了 Envoy,端口映射肯定还是要用 Envoy 来实现,毕竟 Grafana 真香。

Envoy 通过 TCP 代理即可实现端口映射功能,比如我想将 samba 服务暴露到公网,只需向 lds.yaml 中加入配置:


   
  1. - "@type": type.googleapis.com/envoy.api.v2.Listener
  2. name: listener_smb_local
  3. address:
  4. socket_address:
  5. address: "::"
  6. ipv4_compat: true
  7. port_value: 139
  8. filter_chains:
  9. - filters:
  10. - name: envoy.tcp_proxy
  11. typed_config:
  12. "@type": type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy
  13. stat_prefix: smb_local
  14. cluster: smb_local
  15. access_log:
  16. name: envoy.file_access_log
  17. typed_config:
  18. "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog
  19. path: /dev/stdout
  20. - "@type": type.googleapis.com/envoy.api.v2.Listener
  21. name: listener_smb_internet
  22. address:
  23. socket_address:
  24. address: "::"
  25. ipv4_compat: true
  26. port_value: 4450
  27. filter_chains:
  28. - filters:
  29. - name: envoy.tcp_proxy
  30. typed_config:
  31. "@type": type.googleapis.com/envoy.config.filter.network.tcp_proxy.v2.TcpProxy
  32. stat_prefix: smb_internet
  33. cluster: smb_internet
  34. access_log:
  35. name: envoy.file_access_log
  36. typed_config:
  37. "@type": type.googleapis.com/envoy.config.accesslog.v2.FileAccessLog
  38. path: /dev/stdout

其中 ipv4_compat: true 表示同时监听 ipv4 和 ipv6。445 端口也被运营商封了,所以可以使用 4450 端口。

再向 cds.yaml 中加入如下的配置:


   
  1. - "@type": type.googleapis.com/envoy.api.v2.Cluster
  2. name: smb_local
  3. connect_timeout: 1s
  4. type: strict_dns
  5. dns_lookup_family: V4_ONLY
  6. lb_policy: ROUND_ROBIN
  7. load_assignment:
  8. cluster_name: smb_local
  9. endpoints:
  10. - lb_endpoints:
  11. - endpoint:
  12. address:
  13. socket_address:
  14. address: 192.168 .100 .20
  15. port_value: 139
  16. - "@type": type.googleapis.com/envoy.api.v2.Cluster
  17. name: smb_internet
  18. connect_timeout: 1s
  19. type: strict_dns
  20. dns_lookup_family: V4_ONLY
  21. lb_policy: ROUND_ROBIN
  22. load_assignment:
  23. cluster_name: smb_internet
  24. endpoints:
  25. - lb_endpoints:
  26. - endpoint:
  27. address:
  28. socket_address:
  29. address: 192.168 .100 .20
  30. port_value: 445

将其中的地址改成你的 samba 服务内网地址。

配置生效后,就可以通过外网连接你的 samba 服务了。

当然了,我自己的 Upstream 服务远远不止这些,我只是针对每一种类型举一个示例,大家可以举一反三。看看我的:


   
  1. $ docker logs -f envoy
  2. [ 2019 -12 -23 10: 16: 58.199][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'prometheus'
  3. [ 2019 -12 -23 10: 16: 58.201][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'transmission'
  4. [ 2019 -12 -23 10: 16: 58.204][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'mynas'
  5. [ 2019 -12 -23 10: 16: 58.205][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'aria2'
  6. [ 2019 -12 -23 10: 16: 58.207][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'aria2_bt'
  7. [ 2019 -12 -23 10: 16: 58.209][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'aria2_dht'
  8. [ 2019 -12 -23 10: 16: 58.211][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'smb_local'
  9. [ 2019 -12 -23 10: 16: 58.213][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'smb_internet'
  10. [ 2019 -12 -23 10: 16: 58.215][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'transmission_bt'
  11. [ 2019 -12 -23 10: 16: 58.217][ 1][info][upstream] [source/common/upstream/cds_api_impl.cc: 87] cds: add/update cluster 'time'
  12. [ 2019 -12 -23 10: 16: 58.233][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_http_v6'
  13. [ 2019 -12 -23 10: 16: 58.243][ 1][warning][config] [source/server/listener_impl.cc: 287] adding listener '0.0.0.0:8443': filter chain match rules require TLS Inspector listener filter, but it isn 't configured, trying to inject it (this might fail if Envoy is compiled without it)
  14. [2019-12-23 10:16:58.243][1][info][upstream] [source/server/lds_api.cc:71] lds: add/update listener 'listener_https_v4 '
  15. [2019-12-23 10:16:58.254][1][warning][config] [source/server/listener_impl.cc:287] adding listener '[::]: 443 ': filter chain match rules require TLS Inspector listener filter, but it isn't configured, trying to inject it (this might fail if Envoy is compiled without it)
  16. [ 2019 -12 -23 10: 16: 58.255][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_https_v6'
  17. [ 2019 -12 -23 10: 16: 58.255][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_ntp'
  18. [ 2019 -12 -23 10: 16: 58.260][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_aria2'
  19. [ 2019 -12 -23 10: 16: 58.263][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_aria2_bt'
  20. [ 2019 -12 -23 10: 16: 58.265][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_aria2_dht'
  21. [ 2019 -12 -23 10: 16: 58.269][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_smb_local'
  22. [ 2019 -12 -23 10: 16: 58.272][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_smb_internet'
  23. [ 2019 -12 -23 10: 16: 58.275][ 1][info][upstream] [source/server/lds_api.cc: 71] lds: add/update listener 'listener_transmission_bt'

监控截图:

脚注

[1]

GetEnvoy: https://www.getenvoy.io/

[2]

inotify: https://www.infoq.cn/article/inotify-linux-file-system-event-monitoring


你可能还喜欢

点击下方图片即可阅读

让 Linux 防火墙新秀 nftables 为你的 VPS 保驾护航

云原生是一种信仰 ????

码关注公众号

后台回复◉图谱◉领取史上最强 Kubernetes 知识图谱

点击 "阅读原文" 获取更好的阅读体验!

❤️给个「在看」,是对我最大的支持❤️

转载:https://blog.csdn.net/alex_yangchuansheng/article/details/103692409
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场