Blank white background with no objects or features visible.

NOVA PESQUISA: 80% dos custos de IA são invisíveis na fatura. Mais de 200 líderes revelam para onde o dinheiro vai. Leia→

Contêineres de Servidor SSH Para Desenvolvimento no Kubernetes

By Chirag Jain

Updated: February 29, 2024

No ano passado, discutimos a execução de Jupyter Notebooks Hospedados e VS Code (Code Server) em seus Clusters Kubernetes. Comparamos várias abordagens e soluções existentes, incluindo o aluguel e o gerenciamento de VMs. Em seguida, descrevemos nossas abordagens para resolver problemas de usabilidade, a fim de tornar a experiência mais agradável e abstrair os detalhes do Kubernetes.

Desde então, recebemos muitos feedbacks de nossos clientes, principalmente a falta de uma melhor experiência de desenvolvimento para aplicativos completos. Embora o Jupyter Lab seja ótimo para notebooks interativos e edição leve, levá-lo a capacidades completas de IDE pode exigir muito ajuste com Extensões Jupyter e ainda pode não ser uma ótima experiência para bases de código que não são Python. Para resolver essas deficiências, lançamos Code Server suporte, que é mais ou menos o VS Code no navegador. Embora seja uma solução sem configuração e facilite muitos problemas de Experiência do Desenvolvedor com o Jupyter Lab, os usuários relataram atrito ao trabalhar com extensões do VS Code.

Por exemplo, Pylance, a extensão que oferece excelente suporte à linguagem Python no VS Code, não pode ser instalada no Code Server devido a uma licença proprietária da Microsoft. Em vez disso, os usuários precisam contar com uma combinação de Jedi e Pyright que ainda não estão no mesmo nível do Pylance. Outro exemplo é Github CoPilot – embora seja possível instalá-lo no Code Server, requer ajuste manual do arquivo de extensão e a atualização da versão do Code Server.

Embora a experiência de edição do Code Server não seja ruim, às vezes há um atraso perceptível e texto embaralhado no terminal, o que pode ser irritante. Sempre soubemos que conectar o VS Code local com o VS Code Server remoto via SSH ou Tunnels seria uma experiência melhor.

O objetivo é permitir que os usuários criem um deployment executando o Servidor OpenSSH em uma imagem de contêiner baseada em Ubuntu, com a mesma persistência de disco do Jupyter Lab, e permitir que os usuários se conectem a ele.

Veja como fica na plataforma (documentação):

Neste post, explicaremos como implementamos a conexão a contêineres via SSH sem fornecer acesso direto ao cluster e sem enviar tráfego para fora da VPC.

Istio e Roteamento

Ao implantar aplicações, geralmente configuramos um nome de domínio para acessar esses serviços, mas comprar um domínio para cada aplicação é proibitivamente caro. Em vez disso, compramos um único domínio (por exemplo, acmecorp.com), configuramos subdomínios (docs.acmecorp.com) e/ou prefixos de caminho (acmecorp.com/blog/) e então usamos um roteador para corresponder regras e rotear o tráfego para diferentes aplicações.

Usamos Istio para todo o nosso roteamento de ingress. O Istio, entre muitas funcionalidades, oferece abstrações convenientes para configurar o Envoy proxy subjacente que realmente lida com todo o roteamento.

Vamos entender como uma requisição HTTP é roteada

No exemplo acima, quando um usuário tenta acessar https://myapp.acmecorp.com/api/v1

  1. Primeiro, *.acmecorp.com é resolvido via DNS para o IP público do balanceador de carga externo. A porta é inferida como 443 devido ao protocolo HTTPS.
  2. Uma conexão TCP é estabelecida com o balanceador de carga e o payload da requisição HTTP é enviado
  3. O Balanceador de Carga roteia o payload da requisição para os Pods de Ingress do Istio
  4. O Istio Ingress analisa todas as VirtualServices(e as configurações de Gateway), corresponde ao nome do host (mais importante, o subdomínio myapp) e ao prefixo de caminho, e roteia para o Serviço Kubernetes correspondente
  5. O Kubernetes roteia a requisição para um dos Endpoints (Pod) do Serviço

Isso é chamado de Roteamento de Camada 7 porque usamos campos reais da especificação HTTP para fazer o roteamento.

Roteamento no contexto de SSH

SSH usa um protocolo personalizado que utiliza TCP para transporte. Uma conexão SSH simples se parece com o seguinte

ssh user@somemachine.acmecorp.com -p 22

Aqui estamos tentando nos conectar a somemachine.acmecorp.com na porta 22. Aqui somemachine.acmecorp.com:22 precisa ser resolvido para uma combinação única de endereço IP e porta para alcançar o destino. Mas lembre-se que em nossa configuração, todos os subdomínios estão configurados para apontar para o mesmo balanceador de carga – o que significa que abc.acmecorp.com, xyz.acmecorp.com, somemachine.acmecorp.com todos se resolvem para o mesmo endereço IP e então Istio/Envoy deve analisar o subdomínio e decidir para onde rotear. Mas no caso de SSH, isso não é possível porque, depois de resolver o endereço IP e estabelecer uma conexão TCP, tudo o que o Istio vê é o IP e o número da porta do balanceador de carga, e o conteúdo real dos pacotes está sendo criptografado por SSH. Então, como podemos rotear para múltiplos destinos SSH diferentes no cluster?

Opção 1: Usar portas únicas no mesmo Balanceador de Carga

Como só precisamos garantir combinações únicas de endereço IP e portas, podemos simplesmente atribuir portas diferentes no balanceador de carga a contêineres SSH únicos

Podemos então configurar Correspondência de porta de rota TCP usando Istio

Aqui, todo o tráfego TCP que chega à porta 22 do Balanceador de Carga alcançará o Serviço A e todo o tráfego TCP na porta 23 alcançará o Serviço B.

Embora isso funcione bem, existem algumas limitações

  • Um máximo de 65.535 contêineres SSH pode ser alcançado por trás de um único balanceador de carga. Isso não é um grande problema porque, realisticamente, não esperamos tantos contêineres SSH implantados ao mesmo tempo.
  • O problema mais complicado é abrir e liberar portas dinamicamente e com precisão no balanceador de carga externo sem nunca interromper qualquer outro tráfego normal. Embora certamente possível, qualquer bug ou condição de corrida poderia causar sérias interrupções para outras aplicações. Sem mencionar que abrir portas arbitrárias é um grande risco de segurança para muitos de nossos clientes.

Opção 2: Usar um novo Balanceador de Carga para cada contêiner SSH

Neste caso, apontamos explicitamente abc.acmecorp.com e xyz.acmecorp.com para dois Balanceadores de Carga externos diferentes, em vez do curinga *.acmecorp.com. Agora eles apontam para um endereço IP único cada e podem ser roteados por dois diferentes Istio Gateway (ligado um-para-um a um balanceador de carga externo). A limitação óbvia aqui é que provisionar um novo balanceador de carga por contêiner SSH torna-se proibitivamente caro.

Existe alguma forma de aproveitar o roteamento em nível HTTP, mas ainda assim trabalhar apenas com tráfego TCP? Apresentamos o HTTP CONNECT!

Proxying usando HTTP CONNECT

O HTTP CONNECT método permite estabelecer um "túnel" entre dois destinos através de um Proxy. Imagine os velhos tempos das centrais telefônicas - você quer ligar para um número, mas não tem uma linha direta para alcançá-lo; em vez disso, um operador intermediário facilita a conexão em seu nome e depois se afasta para permitir que as duas partes se comuniquem.

Telephone switchboard - Wikipedia

Recomendamos assistir ao seguinte vídeo para uma boa explicação: https://www.youtube.com/watch?v=PAJ5kK50qp8

Felizmente, no nosso caso, já usamos um proxy capaz de usar CONNECT - o Envoy Proxy. Vejamos como funcionaria no nosso caso de uso:

  1. O cliente abre uma conexão para acmecorp.com:80 - o balanceador de carga externo que roteia o tráfego para o Envoy.
  2. O cliente envia uma requisição HTTP CONNECT

CONNECT svc-a.ns.cluster.svc.local:80 HTTP/1.1
Host: svc-a.ns.cluster.svc.local

que está instruindo o Envoy a estabelecer uma Conexão TCP com svc-a.ns.cluster.svc.local:80 em seu nome

  1. Uma vez estabelecida a conexão, um 200 OK é retornado ao cliente.
  2. A partir deste ponto, o Envoy deixa de se importar com o conteúdo do tráfego e atua como um "túnel", permitindo que o tráfego flua entre o cliente e o pod. Pode ser qualquer coisa que funcione sobre TCP, incluindo, mas não se limitando a, SSH.

Note que svc-a.ns.cluster.svc.local:80 é um Serviço Kubernetes e não aponta para nenhum endereço IP público; em vez disso, só pode ser resolvido dentro do Cluster Kubernetes. Como o Envoy reside dentro do cluster, podemos configurá-lo para alcançar os pods por trás dele.

Tudo o que resta é configurar o Envoy para fazer tal roteamento. Infelizmente, o Istio não possui abstrações de alto nível para configurar isso facilmente; em vez disso, temos que aplicar patches à configuração do Envoy usando Envoy Filters

Envoy Filters

Compreender as capacidades do Envoy e os Envoy Filters está fora do escopo desta publicação de blog, mas considere-o apenas como uma forma conveniente de modificar as regras de roteamento do Istio usando pequenos patches. Para habilitar o roteamento baseado em CONNECT, precisamos

  1. Ter uma porta exposta publicamente no LoadBalancer para aceitar tráfego TCP (por exemplo, digamos 2222) e configurar o correspondente Istio Gateway para aceitar tráfego HTTP. Optamos por manter a porta 80 porque já a usamos para tráfego HTTP normal e o tráfego SSH será criptografado de qualquer forma.
  2. Configure a porta exposta publicamente no Gateway para aceitar requisições do tipo CONNECT. Descobrimos que isso já está habilitado para requisições na Porta 80. Para qualquer outra porta, você pode aplicar um Filtro Envoy da seguinte forma:
   Ex.: Habilitar CONNECT na Porta 2222apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
spec:
 configPatches:
   - applyTo: NETWORK_FILTER
     match:
       context: GATEWAY
       listener:
         filterChain:
           filter:
             name: envoy.filters.network.http_connection_manager
         portNumber: 2222
     patch:
       operation: MERGE
       value:
         typed_config:
           '@type': >-
             type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
           http2_protocol_options:
             allow_connect: true
           upgrade_configs:
             - upgrade_type: CONNECT
 workloadSelector:
   labels:
     app: tfy-istio-ingress
  1. Para cada Contêiner SSH, configure o roteamento baseado em CONNECT:

apiVersion: networking.istio.io/v1alpha3
kind: EnvoyFilter
metadata:
 name: svc-a-ns-ssh-envoy-filter
 namespace: istio-system
spec:
 configPatches:
   - applyTo: NETWORK_FILTER
     match:
       context: GATEWAY
       listener:
         filterChain:
           filtro:
             name: envoy.filters.network.http_connection_manager
         númeroDaPorta: 80
     patch:
       operation: MERGE
       valor:
         typed_config:
           '@type': >-
             type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
           route_config:
             name: local_route
             hosts_virtuais:
               - domínios:
                   - svc-a.ns.svc.cluster.local:80
                 name: svc-a-ns-ssh-vh
                 rotas:
                   - correspondência:
                       connect_matcher: {}
                     rota:
                       cluster: >-
                         outbound|80||svc-a.ns.svc.cluster.local
                       configuracoes_de_upgrade:
                         - config_de_conexao: {}
                           habilitado: true
                           tipo_de_upgrade: CONNECT
 seletorDeCargaDeTrabalho:
   rotulos:
     istio: tfy-istio-ingress

Esse é um YAML de aparência bem assustadora, mas tudo o que estamos fazendo é modificar o listener na porta 80 no Gateway para corresponder a requisições CONNECT para svc-a.ns.svc.cluster.local:80 e roteá-las para outbound|80||svc-a.ns.svc.cluster.local, ou seja, a porta 80 do serviço Kubernetes svc-a.ns.svc.cluster.local onde nosso servidor OpenSSH está aguardando conexões SSH dentro do Contêiner.

Iniciando CONNECT no Lado do Cliente SSH

Por si só, o cliente SSH não sabe nada sobre HTTP CONNECT. Em vez disso, ele oferece uma ProxyCommand opção que permite a outros programas facilitar a Conexão SSH. Aqui usamos o ProxyTunnel projeto que facilita isso. A configuração em ~/.ssh/config é a seguinte

Host svc-a-ns
 Usuário jovyan
 Nome do Host svc-a.ns.svc.cluster.local
 Porta 80
 Intervalo de Atividade do Servidor 100
 Arquivo de Identidade ~/.ssh/my-private-key
 Comando Proxy proxytunnel -v -p ssh.acmecorp.com:80 -o %h -d %h:%p

Com tudo isso feito, os usuários podem agora conectar-se facilmente e configurar seu fluxo de trabalho de desenvolvimento favorito - seja Neovim, VS Code, JetBrains IDE, etc.

Limitações e Soluções Potenciais

Embora este recurso melhore significativamente a Experiência do Desenvolvedor em termos de edição e execução de código, algumas limitações ainda se aplicam porque ainda estamos a ser executados dentro de um contentor.

  • O Docker não funciona porque já estamos dentro de um contentor. Teoricamente, é possível fazer algumas coisas funcionarem com DIND, mas isso vem com os seus desafios.
  • As alterações feitas no sistema de ficheiros raiz do contentor / não são persistentes após reinícios do contentor. Nós fornecemos uma forma de estender a nossa imagem de Servidor SSH e iniciar a partir dessas imagens personalizadas.
  • Os Pods do Kubernetes são concebidos para serem efémeros e podem ser movidos, mas isso é indesejável para um ambiente de desenvolvimento. Nós configuramos orçamentos de interrupção de pod para evitar que o pod seja movido.
  • Embora o proxy seja transparente, o tráfego ainda flui através do balanceador de carga e dos pods Istio Envoy. Isso significa que fazer algo incomum no desenvolvimento, como carregar/descarregar ficheiros enormes, pode consumir largura de banda e recursos e afetar outro tráfego. É melhor usar um conjunto separado de pods LoadBalancer, Gateway, Envoy para conectar-se a Contentores SSH.

Contentores de servidor SSH no Kubernetes na TrueFoundry

TrueFoundry é uma PaaS de implementação de ML/LLM sobre Kubernetes para acelerar os fluxos de trabalho dos desenvolvedores, ao mesmo tempo que lhes permite total flexibilidade no teste e implementação de modelos, garantindo total segurança e controlo para a equipa de Infraestrutura. Através da nossa plataforma, permitimos que as equipas Implantar e monitorar modelos em 15 minutos com 100% de confiabilidade, escalabilidade e a capacidade de reverter em segundos - permitindo-lhes economizar custos e lançar modelos em produção mais rapidamente, possibilitando a concretização de valor de negócio real.  

The fastest way to build, govern and scale your AI

Sign Up
Table of Contents

Govern, Deploy and Trace AI in Your Own Infrastructure

Book a 30-min with our AI expert

Book a Demo

The fastest way to build, govern and scale your AI

Book Demo

Discover More

October 5, 2023
|
5 min read

<Webinar> Vitrine de GenAI para Empresas

Best Fine Tuning Tools for Model Training
May 3, 2024
|
5 min read

As 6 Melhores Ferramentas de Fine Tuning Para Treinamento de Modelos em 2026

May 25, 2023
|
5 min read

LLMs de Código Aberto: Abrace ou Pereça

August 24, 2023
|
5 min read

Implantações de Machine Learning em 2023

May 21, 2026
|
5 min read

Adicionando OAuth2 a Jupyter Notebooks no Kubernetes

Engenharia e Produto
May 21, 2026
|
5 min read

Uma equipe de 2 pessoas atendendo um modelo para 1,5 milhão de pessoas com TrueFoundry

Engenharia e Produto
May 21, 2026
|
5 min read

Acelere o Processamento de Dados em 30–40x com NVIDIA RAPIDS no TrueFoundry

GPU
Engenharia e Produto
May 21, 2026
|
5 min read

Uma Parceria para IA Responsável: Truefoundry e Enkrypt AI

No items found.
No items found.

Recent Blogs

Black left pointing arrow symbol on white background, directional indicator.
Black left pointing arrow symbol on white background, directional indicator.
Take a quick product tour
Start Product Tour
Product Tour