O contedor non chega á API só por VPN? Fai un proxy no host
O túnel vive no host, así que o contedor non ten ruta cara ao enderezo privado. Publica un proxy inverso estreito e chámao como host.docker.internal.
O teu servizo de Compose esgota o tempo contra unha API que o navegador da mesma máquina abre sen queixa. Esa diferenza é a pista: a petición que funciona sae dun host cun túnel VPN, e a que se colga sae dun contedor coa súa propia pila de rede. Nada dentro do contedor coñece as rutas do túnel, así que o destino privado segue inalcanzable se escriba como se escriba o nome. A solución é un salto deliberado a través do host, non unha táboa de rutas máis astuta.
Comeza por por que difiren os dous camiños e logo decide onde debe vivir o túnel.
Por que o host funciona e o contedor non
Levantar o túnel dálle ao host unha interface, un conxunto de rutas e, normalmente, unha entrada de resolutor que achega a VPN. Un contedor non herda nada diso: arrinca cunha interface na ponte do motor e unha ruta por defecto que apunta a esa ponte. As rutas instaladas no host son invisibles para todo o que se executa dentro dese espazo de nomes.
Por iso os síntomas parecen incoherentes e non simplemente rotos. O host resolve o nome privado e conecta, porque alí existen tanto o nome como a ruta. O contedor envía o seu paquete a un enderezo privado que nada do camiño da ponte pode alcanzar, e a petición caduca. Engadir o nome ao ficheiro hosts do contedor non cambia nada, porque o propio enderezo é inalcanzable desde onde está o contedor.
Chega ao host desde dentro do contedor
Un proceso dentro do contedor que marca localhost está marcando o contedor. Docker Desktop publica host.docker.internal como un nome que apunta de volta á máquina que executa o motor; os motores de Linux déixano indefinido agás que o servizo o pida, que é o que fai a asignación de abaixo. Con iso, a aplicación apunta a un nome e un porto en lugar do enderezo privado.
services:
app:
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
INTERNAL_API_HOST: "host.docker.internal:8666"O túnel queda no lado do host do salto, e o porto desa variable é o que usará o proxy para escoitar. Un contedor que perde o túnel agora falla nun único salto coñecido en vez de tentar unha ruta que nunca tivo.
Elixe onde vive o túnel
Hai tres formas posibles para un servizo de Compose que necesita unha API privada, e difiren en que proceso posúe o túnel. Un cliente VPN pode executarse dentro da imaxe do contedor, o que esixe expor un dispositivo de túnel e conceder a capacidade de administración de rede que o acompaña. A aplicación pode compartir o espazo de nomes de rede dun contedor de túnel aparte, de modo que ambos procesos ven unha pila e unha táboa de rutas. Ou o túnel pode quedar no host, cun reenviador estreito do host ao contedor.
As dúas primeiras arrastran ficheiros de perfil, comportamento de reconexión e política de resolutor ao proxecto, e fan que cada servizo do ficheiro dependa da saúde do túnel. É demasiado acoplamento para unha soa chamada á API. Para unha pila de desenvolvemento que le un único punto final interno, o reenviador do lado do host é o cambio máis pequeno que aínda mantén as credenciais onde as xestiona o equipo de plataforma.
Publica un único camiño estreito no host
Comeza polo que herda o reenviador: execútao coa conta que posúe o túnel e os seus sockets de saída tomarán as rutas do host e o resolutor do host. Un bloque location de nginx cobre un camiño.
server {
listen 8666;
location /search/ {
proxy_pass http://10.20.30.40:8090/;
proxy_set_header Host internal-api.example;
proxy_read_timeout 60s;
}
}Trata o enderezo e o porto como substitutos e pon o par no que a API escoita de verdade. Cada upstream adicional merece o seu propio bloque location, e cada bloque debería admitir só os camiños que chama este servizo, para que o proxy non se converta nunha porta xeral á rede privada. Ata o oínte ao enderezo da ponte para os clientes que chegan por host-gateway, a loopback só cando o cliente comparte o espazo de nomes de rede do host, e nunca a 0.0.0.0. Elixe tamén un porto que siga libre: un número xa ocupado deixa o proxy sen escoitar mentres o contedor segue caducando, o que non se distingue do fallo de rede co que comezaches.
Portos, nomes e a decisión de TLS
A resolución de nomes divídese igual que o encamiñamento. As consultas públicas do contedor pasan polo resolutor do motor, así que un nome privado que resolve no host non resolverá dentro do contedor. O proxy é o compoñente que resolve o nome privado, porque herda o resolutor do host.
TLS é a decisión que se apraza e logo explica o seguinte fallo. Se o proxy reenvía os bytes intactos, o contedor ve o certificado propio da API e necesita a CA interna no seu almacén de confianza. Se o proxy remata TLS, esa confianza pasa ao host e a aplicación comeza a construír ligazóns desde a cabeceira host que recibe. Elixe un só arranxo e aplícao a cada recurso, porque unha redirección emitida polo outro apunta a un nome de host que o contedor non pode resolver.
Verifica desde dentro do servizo
Envía a petición desde o contedor, non desde o host: un curl simple contra o camiño do proxy e logo a comprobación de saúde da propia aplicación. Cando o proxy responde pero a aplicación segue fallando, compara tres cousas: a URL base que constrúe, calquera variable de proxy no seu contorno e a CA na que confía. Todas están no contorno e nos rexistros do contedor, e arranxar calquera delas deixa a VPN en paz.
Escribe o arranxo elixido no README do proxecto mentres está fresco. Se non, a próxima persoa que tropece con este tempo esgotado pasará a mesma tarde redescubrindo que o túnel vive no host.