Depuração de MQTT sobre websockets no Envoy 1.28.0
Migrei a nossa instalação do Envoy do Envoy 1.11.1 para o 1.28.0 e agora também estou a utilizar o SNI para selecionar o certificado correto.
Uma grande parte dessa migração consiste em atualizar a sintaxe da configuração do Envoy da API v2 para a API v3.
A atualização correu bem, exceto o facto de o nosso serviço MQTT baseado em websockets (baseado no VerneMQ) não funcionar como esperado.
No início, assumi que o problema estava no envoy. Depois de tentar várias opções de timeout e consultar a documentação do envoy, decidi experimentar uma nova rota e um broker diferente (Mosquitto) por trás dela.
A configuração seguinte funciona com o Mosquitto como intermediário, para o caso de alguém se deparar com o mesmo problema.
Aqui está um excerto do meu envoy.yaml (a configuração completa tem mais de 87000 linhas, geradas por um script de modelo, devido ao SNI e à necessidade de ter ouvintes individuais por domínio, como mencionei acima):
static_resources:
ouvintes:
- endereço:
socket_address:
endereço: 0.0.0.0
valor_da_porta: 443
per_connection_buffer_limit_bytes: 32768 # 32 KiB
filtros_de_ouvinte:
- nome: tls_inspector
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
cadeias_de_filtros:
- filter_chain_match:
nomes_do_servidor: ["picockpit.com", "www.picockpit.com", "picockpit.com:443", "www.picockpit.com:443"]
transport_socket:
nome: envoy.transport_sockets.tls
typed_config:
"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
tls_certificates:
- certificate_chain: { filename: "/certs/letsencrypt/live/picockpit.com/fullchain.pem" }
private_key: { filename: "/certs/letsencrypt/live/picockpit.com/privkey.pem" }
alpn_protocols: [ "h2,http/1.1" ]
filters:
- name: envoy.filters.network.http_connection_manager
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
stat_prefix: ingress_http
codec_type: AUTO
http_filters:
- nome: envoy.filters.http.compressor
typed_config:
'@type': type.googleapis.com/envoy.extensions.filters.http.compressor.v3.Compressor
compressor_library:
nome: text_optimized
typed_config:
'@type': type.googleapis.com/envoy.extensions.compression.gzip.compressor.v3.Gzip
compression_level: BEST_SPEED
compression_strategy: DEFAULT_STRATEGY
memory_level: 9
window_bits: 15
tamanho_do_conjunto: 16384
- nome: envoy.filters.http.router
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
common_http_protocol_options:
idle_timeout: 3600s # 1 hora
use_remote_address: true
xff_num_trusted_hops: 0
route_config:
virtual_hosts:
- nome: backend
domínios: ["picockpit.com", "www.picockpit.com", "picockpit.com:443", "www.picockpit.com:443"]
rotas:
- match: { path: "/pidoctor"}
redirecionar:
path_redirect: "/raspberry-pi/pidoctor-raspberry-pi-system-health-monitor/"
- match: { prefixo: "/pidoctor/"}
redirecionar:
redireccionar_caminho: "/raspberry-pi/pidoctor-raspberry-pi-system-health-monitor/"
- match: { prefixo: "/mqtt/test" }
route:
prefix_rewrite: "/mqtt"
cluster: target_test
tempo esgotado: 0s
idle_timeout: 0s
upgrade_configs:
- upgrade_type: "websocket"
ativado: verdadeiro
- match: { prefixo: "/" }
route:
cluster: target_main
timeout: 0s
clusters:
- nome: target_test
connect_timeout: 5s
per_connection_buffer_limit_bytes: 32768 # 32 KiB
type: STRICT_DNS
lb_policy: ROUND_ROBIN
load_assignment:
nome do cluster: target_test
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
endereço: mosquitto-test.test-network
valor_da_porta: 8025
Note-se que omiti uma grande parte da configuração de outros serviços, rotas, e não forneci a informação do cluster target_main (porque é irrelevante para a situação do MQTT sobre websockets).
Observe o valor timeout: 0s, que é importante para que as conexões MQTT continuem em vez de serem interrompidas após 15 segundos, como é o padrão.
Também destaquei outras partes que, na minha opinião, são relevantes para permitir que as ligações sejam actualizadas para websockets (para que o MQTT possa ser transportado através delas). Observe também o números de porta sendo passados como correspondências de domínio adicionais.
Mosquitto docker-compose.yml:
versão: "3.6
serviços:
mosquitto:
imagem: eclipse-mosquitto
nome_do_contêiner: mosquitto-test
hostname: mosquitto-test
redes:
- rede_teste
restart: "no"
utilizador: "root:root"
volumes:
- tipo: bind
fonte: ./mosquitto.conf
target: /mosquitto/config/mosquitto.conf
redes:
test_net:
externo:
nome: test-network
mosquitto.conf:
ouvinte 8025
protocolo websockets
allow_anonymous true
log_type all
Ferramenta utilizada para verificar a ligação:
Atualização 13.11.2023
O MQTT está novamente em linha, também com o VerneMQ:
Embora eu tenha reiniciado o VerneMQ várias vezes, aparentemente não esperei tempo suficiente para que ele se estabilizasse. Um colega reiniciou-o hoje e já está a funcionar. Aparentemente, demora 10 a 15 minutos (na nossa configuração) a tornar-se totalmente reativo e a funcionar corretamente.
Por conseguinte, posso confirmar que a configuração acima referida para o envoy também funciona com o VerneMQ.
Lição aprendida
Se algo não estiver a funcionar, tentar reproduzir o problema na interação com outra ferramenta - Se funcionar aí, então possivelmente o problema não está na primeira ferramenta que alterou, mas na segunda ferramenta com a qual precisa de trabalhar.
e mais alguns objectos:
Documentação Online
- https://www.envoyproxy.io/docs
- https://www.envoyproxy.io/docs/envoy/v1.28.0/ (especificamente para a versão atual do envoy)
- https://github.com/envoyproxy/envoy/blob/main/configs/envoyproxy_io_proxy_http3_downstream.yaml - exemplo de configuração HTTP3
- https://codilime.com/blog/envoy-configuration/ - enviado compreensivo
- https://pi3g.com/envoy-docker-and-websockets-debugging-and-configuration/ - sobre websockets e envoy (websockets são usados no PiCockpit para transportar MQTT) - este artigo anterior é baseado na v1.11.1 do envoy
Ferramentas úteis
- https://http3check.net/ - permite verificar o suporte HTTP/3
- Cliente websocket HiveMQ