envoy、docker和websockets - 调试和配置
Websockets是一项令人兴奋的技术,它允许你将HTTP连接升级为一个长期持久的二进制连接,你可以用它来发送双向信息。
作为一个旁观者,MQTT协议可以使用websockets进行传输--例如,对于网站交付的JavaScript客户端,这是唯一(?
在任何情况下,由于websockets运行在与正常HTTP和HTTPS流量相同的端口上(80 / 443),企业网络更有可能让它们通过。
Envoy和websockets
Envoy支持websockets。有一些小问题。
无法将JSON解析为proto (INVALID_ARGUMENT:(route_config.virtual_hosts[3].route[0].route) use_websocket。无法找到字段.):
Envoy曾经用一个旧的指令 "use_websocket "支持websockets。在当前的envoy安装中(例如,我目前使用envoy 1.10.0-dev进行测试),这个指令已经消失了,它已经被取代。
这个想法是为了有更多的通用升级请求/可能性。
现在正确的语法是使用 升级_configs.请看我的envoy.yaml。
静态_资源。
听众。
- 地址。
socket_address:
地址。0.0.0.0
port_value: 80
filter_chains。
- 滤波器。
- 名称: envoy.http_connection_manager
配置。
upgrade_configs。
- 升级_类型: websocket
codec_type: auto
stat_prefix: ingress_http
use_remote_address: true
xff_num_trusted_hops。0
route_config。
virtual_hosts。
- 名称:调试
域。["debug.local:80"]
航线。
- 匹配。{前缀。"/" }
路线。
群组:target_dwebsocket
- 名称: 后台
域。["morpheus.local"]
航线。
- 匹配。{前缀。"/" }
重新定向。
path_redirect: "/"
https_redirect: true(snip)
- 地址。
socket_address:
地址。0.0.0.0
port_value:443
filter_chains。
- tls_context。
common_tls_context。
tls_certificates。
- certificate_chain: { filename: "/etc/envoy/example.crt" }
private_key: { filename: "/etc/envoy/example.key" }
alpn_protocols:[ "h2,http/1.1" ]
滤波器。
- 名称: envoy.http_connection_manager
配置。
upgrade_configs。
- 升级_类型: websocket
stat_prefix: ingress_https
use_remote_address: true
xff_num_trusted_hops。0
route_config。
virtual_hosts。
- 名称:调试
域。["debug.local:443"]
航线。
- 匹配。{前缀。"/" }
路线。
群组:target_dwebsocket
(snip)集群。
- 名称: target_dwebsocket
connect_timeout:0.25s
类型:strict_dns
lb_policy: round_robin
主持人。
- socket_address:
地址: dwse
port_value: 8080
根据文档,你可以在两个地方使用这个upgrade_configs指令。在http_connection_manager上(如我上面的例子),以及在单个路由上。
upgrade_configs。
- 升级_类型: websocket
路线不匹配
还有一件重要的事情要注意,如果你得到 "404"(错误:意外的服务器响应:404):由于一些奇怪的原因,:authority头将被设置为包括端口 - 因此,如果你只提供域名,将不匹配。
看看这条路线。
["debug.local:80"]
在这个例子中,端口是在域名之后指定的。在我的测试中,只有在指定端口的情况下,我才能通过envoy连接到我设置的websocket服务器。这是一个主要的绊脚石。
接下来,我将讨论,在这样的情况下如何调试envoy。
调试envoy、websockets、Docker。
让envoy的输出变得粗略
这是我的docker-compose.yaml。
版本: '3.7
服务。
使者。
建立。
背景:./
dockerfile。Dockerfile
container_name: penvoyage-morpheus-envoy
港口。
– “80:80”
– “443:443”
量。
- 类型:体积
来源:《笔仙》_卷宗
目标。/etc/envoy
网络。
- 使者网
1TP3用户。"2000:2000"
用户。"root:root"
重新开始:除非停止
环境。
日志级别: debug量。
penvoyage_volume。
外部。
名称:笔仙_卷网络。
envoy_net。
外部。
名称:我的桥梁网络
请注意,环境变量 "loglevel: debug "已被设置。
loglevel可以是以下之一。
- 痕迹
- 调试
- 信息
- 警告
现在启动容器将产生更多的输出。
docker-compose up
注意,我们并没有从容器中脱附,以直接看到它的输出。(一旦你满意了,你可以用以下方式运行它 docker-compose up -d)
你的输出现在看起来应该是这样的。
penvoyage-morpheus-envoy | [2019-05-18 14:20:32.093][1][debug][main] [source/server/server.cc:143] 冲洗统计资料
在一个来自websocket客户端的连接上,你将得到以下输出。
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.915][23][debug][main] [source/server/connection_handler_impl.cc:257] [C0] new connection
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.916][23][debug][http] [source/common/http/conn_manager_impl.cc:210] [C0] new stream
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.918][23][debug][http] [source/common/http/conn_manager_impl.cc:548] [C0][S4222457489265630919] 请求头信息完成(end_stream=false)。
鹏程万里-莫菲斯-恩沃伊 | ':authority', 'debug.local:80'
鹏程万里-莫菲斯-恩沃伊 | ':path', '/mqtt_drinks_are_free'.
penvoyage-morpheus-envoy | ':method', 'GET' 。
penvoyage-morpheus-envoy | 'sec-websocket-version', '13' 。
penvoyage-morpheus-envoy | 'sec-websocket-key', 'HQFiCTWiFMktGDPFXwzrjQ=='
penvoyage-morpheus-envoy | '连接', '升级'。
鹏程万里-莫菲斯-恩沃伊 | '升级', 'websocket'
penvoyage-morpheus-envoy | 'sec-websocket-extensions', 'permessage-deflate; client_max_window_bits' 。
鹏程万里-莫菲斯-恩沃伊 |
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.918][23][debug][router] [source/common/router/router.cc:321] [C0][S4222457489265630919] cluster 'target_dwebsocket' match for URL '/mqtt_drinks_are_free' 。
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.918][23][debug][router] [source/common/router/router.cc:379] [C0][S4222457489265630919] router decoding headers。
鹏程万里-莫菲斯-恩沃伊 | ':authority', 'debug.local:80'
penvoyage-morpheus-envoy | ':path', '/mqtt_drinks_are_free' 。
penvoyage-morpheus-envoy | ':method', 'GET' 。
penvoyage-morpheus-envoy | ':scheme', 'http' 。
penvoyage-morpheus-envoy | 'sec-websocket-version', '13' 。
penvoyage-morpheus-envoy | 'sec-websocket-key', 'HQFiCTWiFMktGDPFXwzrjQ=='
penvoyage-morpheus-envoy | '连接', '升级'。
penvoyage-morpheus-envoy | '升级', 'websocket'.
penvoyage-morpheus-envoy | 'sec-websocket-extensions', 'permessage-deflate; client_max_window_bits' 。
penvoyage-morpheus-envoy | 'content-length', '0' 。
penvoyage-morpheus-envoy | 'x-forwarded-for', '192.168.1.2'
penvoyage-morpheus-envoy | 'x-forwarded-proto', 'http' 。
penvoyage-morpheus-envoy | 'x-envoy-internal', 'true' 。
penvoyage-morpheus-envoy | 'x-request-id', 'ca8a765c-e549-4c45-988c-58b6c853df7b'
penvoyage-morpheus-envoy | 'x-envoy-expected-rq-timeout-ms', '15000'
鹏程万里-莫菲斯-恩沃伊 |
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.918][23][debug][pool] [source/common/http/http1/conn_pool.cc:82] 创建一个新连接
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.919][23][debug][client] [source/common/http/codec_client.cc:26] [C1] connecting
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.919][23][debug][connection] [source/common/network/connection_impl.cc:638] [C1] 连接到172.18.0.8:8080
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.919][23][debug][connection] [source/common/network/connection_impl.cc:647] [C1] 连接进行中
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.919][23][debug][pool] [source/common/http/conn_pool_base.cc:20] 由于没有可用连接,正在排队请求。
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.919][23][debug][connecting] [source/common/network/connection_impl.cc:516] [C1] connected
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.919][23][debug][client] [source/common/http/codec_client.cc:64] [C1] 已连接
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.919][23][debug][pool] [source/common/http/http1/conn_pool.cc:236] [C1] 连接到下一个请求
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.919][23][debug][router] [source/common/router/router.cc:1122] [C0][S4222457489265630919] pool ready
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.920][23][debug][router] [source/common/router/router.cc:669] [C0][S4222457489265630919] upstream headers complete: end_stream=false
penvoyage-morpheus-envoy | [2019-05-18 14:21:14.920][23][debug][http] [source/common/http/conn_manager_impl.cc:1234] [C0][S4222457489265630919] 通过codec(end_stream=false)编码头文件。
penvoyage-morpheus-envoy | ':status', '101' 。
penvoyage-morpheus-envoy | '升级', 'websocket'.
penvoyage-morpheus-envoy | '连接', '升级'。
penvoyage-morpheus-envoy | 'sec-websocket-accept', ' 2W9caJQU0JKW3MhWV6T8FHychjk='
penvoyage-morpheus-envoy | 'date', 'Sat, 18 May 2019 14:21:14 GMT'
penvoyage-morpheus-envoy | 'server', 'envoy' 。
penvoyage-morpheus-envoy | 'content-length', '0' 。
鹏程万里-莫菲斯-恩沃伊 |
注意我上面提到的 :authority header。
':authority', 'debug.local:80'
下面是一个普通网站请求的授权标头。
penvoyage-morpheus-envoy | ':method', 'GET' 。
penvoyage-morpheus-envoy | ':authority', 'morpheus.local' 。
penvoyage-morpheus-envoy | ':scheme', 'https' 。
注意第一种情况(通过websocket请求)中存在端口,而第二种情况中没有端口。
测试容器
请注意,这 https://hub.docker.com/r/dataferret/websocket-echo dataferret/websocket-echo的图像似乎是 坏了!
对于所使用的特定版本,扭曲/高速公路似乎有一些不兼容,因此我决定直接推出自己的版本。
我们将建立一个测试容器,它将呼应我们以websocket方式发送给它的输入。
这里是 码头文件(Dockerfile:
FROM python:stretch
COPY assets/websocketd /usr/bin/websocketd
运行 chmod +x /usr/bin/websocketd
COPY assets/run.py /opt/run.py
工作目录/optEXPOSE 8080
ENTRYPOINT ["websocketd", "-port=8080", "python3", "/opt/run.py"]
注意,使用ENTRYPOINT,当容器从docker-compose.yml中用CMD启动时,CMD值将作为参数传递给ENTRYPOINT命令。
websocketd是一个websocket守护程序,你可以从这里获得独立的可执行文件。
我下载了 https://github.com/joewalnes/websocketd/releases/download/v0.3.0/websocketd-0.3.0-linux_amd64.zip 并将其解压到我为docker容器创建的assets文件夹中。
这个漂亮的应用程序的总体思路是为你的应用程序提供一个包装,以便能够提供websockets。
你的应用程序所要做的就是在stdin上监听,并在stdout上写入响应/任何通信。
run.py - 这是一个简单的脚本,它将对写入它的所有内容进行回音。
#!/usr/bin/python
输入sys虽然是真的。
read_co = sys.stdin.readline()
print(read_co, end="")
sys.stdout.flush()
注意readline()的使用,它将在换行时立即返回字符串。否则你必须处理缓冲区/等待输入的累积。
还注意到sys.stdout.flush()。
最后,这里是 docker-compose.yml:
版本:'3.6
服务。
dwse。
建立。
背景:./
dockerfile。Dockerfile
container_name: dwse
主机名: dwse
港口。
– “8078:8080”
网络。
- 使者网
重新启动:总是
命令。"80"
#user。"root:root"网络。
envoy_net。
外部。
名称:我的桥梁网络
注意,我把它放在与上面的envoy相同的网络 "my-bridge-network "中。
这里的命令是一个随机的 "80",不会被应用程序解释,但你可以用它来创建更复杂的命令行等。
排排解
最后,我们希望客户端能够连接并能够看到我们发送给测试Websocket服务的消息,该服务是通过envoy代理的。
我建议 wscat 为这个。
wscat是一个npm(node.js)包。因此,你必须先安装npm。
apt install npm
然后你可以安装wscat。
npm install -g wscat
用一个公共的websocket回声服务测试wscat。
wscat -c ws://echo.websocket.org
获取wscat的命令行选项列表。
wscat -help
要通过envoy检查一个带有自签名证书的安全websocket连接,请执行以下操作。
wscat -c ws://debug.local/mqtt_drinks_are_free -no-check
提示
不合预期的服务器响应
错误。未预期的服务器响应。404
看一下你的路由:你是否像上面讨论的那样,把端口放在域中进行匹配?显然,对于websocket客户端来说,这似乎是必要的。
当有疑问时,在你的虚拟主机的末尾包括一个匹配所有域名,以使调试更容易。我建议在此设置一个固定的路径匹配,而不是前缀--通过改变你在wscat中使用的路径,这种方式,你可以验证哪个规则被匹配。
- 姓名:Matchverything
域。["*"]
航线。
- 匹配。{ path:"/mqtt" }
路线。{ 集群: target_dwebsocket }
无法解析JSON/无法找到字段。
无法将JSON解析为proto (INVALID_ARGUMENT:(route_config.virtual_hosts[3].route[0].route) use_websocket。无法找到字段.):
如上所述,这是一种传统的配置风格,已被新的配置所取代。
upgrade_configs。
- 升级_类型: websocket
没有群组匹配的URL
[source/common/router/router.cc:278] [C0][S5067510418337690596] URL'/'没有集群匹配。
见上文--URL的匹配显然包括Websockets的端口(在我的测试中),因此包括一个匹配所有域的规则,并从那里进行调试/使用我上面提供的语法。
route_config。
virtual_hosts。
- 名称:调试
域。["debug.local:80"]
航线。
- 匹配。{前缀。"/" }
路线。
群组:target_dwebsocket
自签证书
错误:自我签名的证书
用-no-check命令行参数运行wscat(这是一个双破折号,WordPress会把它弄乱)。
Websocket连接失败。
pcp-code.js:15 WebSocket连接到'wss://key:[email protected]/mqtt'失败。在WebSocket握手过程中出错。意外的响应代码。403
见上面的信息:很可能,这个javascript错误信息是由于域名匹配的错误配置造成的(见上文)。
高速公路错误。
"要使用txaio,你必须先选择一个框架" exceptions.RuntimeError:要使用taxaio,你必须先选择一个带有.use_twisted()或.use_txaio()的框架。
这是我在运行dataferret/websocket-echo图像时得到的错误。我建议使用我上面提供的代码作为替代。
参考文献。
- https://hub.docker.com/r/dataferret/websocket-echo 对我不起作用! 我在这篇博文中为一个简单的Docker websocket echo容器提供了替代代码
- https://github.com/vi/websocat 替代品WSCAT
- https://github.com/websockets/wscat
- https://github.com/erebe/wstunnel 允许你在websockets上进行隧道连接,包括 护网 - 有趣的东西!
- ws://echo.websocket.org - Websocket上的回声服务
- http://websocketd.com/ 负责处理WebSocket连接,并启动你的应用程序来处理WebSocket(相互隔离)。
- https://github.com/envoyproxy/envoy/issues/3301 这里讨论的是新式配置升级
- https://www.envoyproxy.io/docs/envoy/latest/intro/arch_overview/websocket.html - 官方特使文件
- https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route.proto#envoy-api-field-route-routeaction-upgrade-configs
- https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route.proto#envoy-api-msg-route-routeaction-upgradeconfig
- https://github.com/envoyproxy/envoy/issues/4740 - 我在这里看到了正确的配置方法,现对其进行阐述
- https://www.envoyproxy.io/docs/envoy/latest/operations/cli envoy命令行信息