Alguns antecedentes:
Criei uma rede moderadamente complexa usando o vpc da Amazon. É uma rede de três camadas em duas zonas de disponibilidade. Cada camada possui uma sub-rede na zona-ae na zona-b. A camada de apresentação está no topo, há uma camada de aplicativo no meio e uma camada de núcleo na parte inferior.
Todos os grupos de segurança e ACLs das sub-redes estão atualmente permitindo TODO o tráfego de entrada e saída para me ajudar a reduzir a área de superfície do problema.
A tabela de roteamento da camada de apresentação está apontando todo o tráfego para um gateway da Internet. O gateway NAT está em uma sub-rede segregada, apontando também todo o tráfego para o gateway da Internet.
Meu aplicativo possui dois componentes, uma interface do usuário (React.js) e uma API (Node / Express). Eles são implantados como imagens de janela de encaixe. Na frente de cada um existe um balanceador de carga clássico.
O UI-ELB está voltado para a Internet e reside na camada de apresentação, roteando o tráfego de 80/443 para a porta 8080 e está associado ao meu app-ec2, que é colocado na sub-rede da camada de aplicativo.
Minha API tem um balanceador de carga interno à sua frente. O API-ELB está na camada de aplicativo (na mesma sub-rede que o app-ec2) e pega o tráfego na porta 80/443 e o direciona para o api-ec2 no núcleo na porta 3000.
Ambos os balanceadores de carga estão transferindo o certificado antes de passar o tráfego para suas instâncias.
Eu tenho ambos os meus balanceadores de carga associados como alias no Route53 e referenciados nos aplicativos por seu bonito URL ( https://app.website.com ). Cada balanceador de carga passa as verificações de integridade definidas e relata todas as instâncias ec2 em uso.
Por fim, na API, habilitei o cors usando o pacote cors nodejs.
Aqui está um diagrama rápido e sujo da minha rede.
O problema:
O APP-ELB me encaminha com êxito para o aplicativo. No entanto, quando o aplicativo tenta enviar uma solicitação GET para o API-ELB, ele primeiro envia uma solicitação OPTIONS que atinge o tempo limite com o código de erro 408.
Onde fica estranho
Algumas das coisas mais estranhas que encontrei durante a depuração são:
- Posso fazer o SSH na instância app-ec2 e executar uma curvatura bem-sucedida no API-ELB. Eu tentei muitos, e todos eles funcionam. Alguns exemplos são:
curl -L https://api.website.com/system/healthcheck
ecurl -L -X OPTIONS https://api.website.com/system/healthcheck
. Ele sempre retorna as informações desejadas. - Mudei o aplicativo inteiro da minha rede para um vpc padrão público e ele funciona como deveria.
- Eu tenho o api-ec2 gravando todas as solicitações de rede no console. Embora mostre as solicitações de verificação de integridade, não mostra nenhuma solicitação do app-ec2. Isso me leva a acreditar que o tráfego nem sequer está atingindo a API.
Realmente, a maior coisa que me deixa com uma perda total é que o enrolamento do api elb interno funciona, mas os axios solicitam o mesmo URL exato não. Isso não faz sentido para mim.
O que eu tentei
No início, passei muito tempo jogando com as regras da ACL e com os grupos de segurança, pensando que fiz algo errado. Eventualmente, acabei de dizer "estrague tudo" e abri tudo para tentar tirar esse pedaço da equação.
Passei muito tempo brincando com Cors na minha API. Eventualmente, aterrissando na configuração que tenho agora, que é o app.use(cors())
retorno de chamada padrão fornecido pelo pacote do nó cors. Também incluí o app.options('*', cors())
recomendado na documentação.
Eu pesquisei tudo no google, mas especificamente se preciso definir alguns cabeçalhos personalizados especiais com os elbs? Mas parece que não consigo encontrar nada. Além disso, quando mudei meu aplicativo para fora da rede, ele funcionou muito bem.
Tenho certeza de que tentei muitas outras coisas, mas essas parecem ser as mais pertinentes. o que estou perdendo? Sei que essa é uma questão potencialmente muito vaga e ampla, e um post enorme, mas agradeço qualquer insight e seu tempo de leitura!
fonte
curl -X OPTIONS 127.0.0.1...
no app-ec2? SóOPTIONS
está quebrado? Os ELBs são "Clássico" e não "Aplicativo", correto? Todas as instâncias conseguem acessar corretamente a Internet via NAT, por exemplocurl ipv4.icanhazip.com
? (Sim, eu pergunto por um motivo que pode parecer obscuro.)Respostas:
Então, o que você tem é:
Como seu API ELB está em uma zona privada, ele não pode ser acessado da Internet.
Seu frontend no React.js é executado no navegador do usuário e não nos servidores da interface do usuário; esses servidores servem apenas arquivos estáticos.
Você tem duas opções: configurar seus servidores front-end para redirecionar as chamadas da API para o API ELB ou apenas atualizar o API ELB para a Internet.
A armadilha usual dos aplicativos JavaScript é esquecer que eles são executados no navegador do usuário e não nos servidores front-end, como faria um aplicativo JEE.
fonte
Isso soa como um problema de roteamento assimétrico ou n-path. Aqui está o que provavelmente está acontecendo:
A máquina A no endereço IP 192.168.1.1 inicia uma solicitação [SYN] através do LB em 192.168.1.10. o LB faz o proxy da carga útil para a Máquina B em 192.168.1.2, então a carga útil agora tem origem: 192.168.1.1 e destino: 192.168.1.2 (que costumava ser 192.168.1.10).
Então, o que acontece agora quando 192.168.1.2 responde com um [SYN, ACK]? O que deve acontecer é que a Máquina B deve responder à Máquina A através do balanceador de carga- normalmente devido a uma rota ou gateway padrão no servidor que roteia o tráfego através do LB. Nesse caso, no entanto, a máquina está na mesma sub-rede, portanto, a rota / gateway não é usada e a tabela de roteamento ignorada pelo servidor. Isso significa que, quando o servidor responde, o [SYN, ACK] parece que a Máquina A é proveniente de um IP diferente do IP com o qual a Máquina A iniciou a solicitação - esperava um IP de origem 192.168.1.10 (o LB), mas está vendo um [SYN, ACK] vindo de 192.168.1.2 (máquina B) e, portanto, o LB não consegue estabelecer uma conexão com a máquina B nesse cenário porque a resposta foi para o dispositivo errado.
A razão pela qual isso funciona para o tráfego externo é devido à sua rota padrão - as respostas para todos os outros são roteadas através do ELB. O ELB vê que estava iniciando uma conexão e intercepta automaticamente a resposta e troca a fonte de 192.168.1.2 de volta para 192.168.1.10.
Portanto, para uma solução para esse problema, você pode implementar o balanceamento de carga com um braço (também conhecido como balanceador de carga em um stick). O que isso fará é usar um NAT de origem na interface interna do balanceador de carga (então, suponha que você tenha uma interface externa 192.168.1.10 no seu balanceador de carga e 192.168.1.11 na interface interna). Isso fará com que todo o tráfego pareça vir do 192.168.1.11 da perspectiva da Máquina B, o que deve resolver o problema de conexão.
Parece, no entanto, que o seu AWS ELB não oferece suporte ao SNAT , portanto, você precisará colocar seus hosts e ELB em sub-redes diferentes ou usar algo que suporte SNATs, como o Virtual Edition da F5, que vem com sabores de hora em hora ou BYOL . Porém, tenha cuidado com as limitações de conexão com o SNAT - se você precisar de mais de 30k conexões simultâneas, encontrará a exaustão da porta SNAT e precisará começar a usar um pool SNAT. .
Portanto, sua melhor solução (por custos e para evitar problemas futuros) seria garantir que o cliente e o servidor estejam em sub-redes diferentes.
A melhor maneira de confirmar seria usar o tcpdump no host de conexão e / ou no servidor back-end e procurar respostas vindas diretamente do / para o servidor back-end em vez de passar pelo balanceador de carga. Você pode carregar seu arquivo de despejo no WireShark para descobrir exatamente o que está acontecendo.
fonte