Ei pessoal. Hoje vamos falar sobre um pouco sobre injeção de sidecar do Istio em Kubernetes.
O Istio no Kubernetes funciona usando um modelo de deployment de sidecar, onde um contêiner auxiliar (sidecar) é anexado ao seu contêiner principal (serviço) em um único Pod. Ao fazer isso, seu serviço e o contêiner de sidecar compartilham a mesma rede e podem ser vistos como dois processos em um único host. Assim, o Istio pode interceptar todas as chamadas de rede de e para o seu contêiner principal e fazer sua mágica para melhorar a comunicação de serviço a serviço.
Esse contêiner de sidecar, denominado istio-proxy, pode ser injetado em seu pod de serviço de duas maneiras: manual e automaticamente. Mesmo essa técnica manual não é 100% feita à mão. Vamos ver.
Injeção manual
O Istio vem com uma ferramenta chamada istioctl. Sim, parece que é inspirado em alguma outra ferramenta amada :). Uma de suas funcionalidades é a capacidade de injetar o sidecar istio-proxy em seu pod de serviço. Vamos usá-la, usando um simples pod de busybox como exemplo:
$ cat busybox.yaml apiVersion: v1 kind: Pod metadata: name: busybox-test spec: containers: - name: busybox-container image: busybox command: ['sh', '-c', 'echo Hello Kubernetes! && sleep infinity'] $ istioctl kube-inject -f busybox.yaml > busybox-injected.yaml [jwendell@jw-laptop ~]$ cat busybox-injected.yaml apiVersion: v1 kind: Pod metadata: labels: name: busybox-test spec: containers: - command: - sh - -c - echo Hello Kubernetes! && sleep infinity image: busybox name: busybox-container - image: docker.io/istio/proxyv2:1.0.2 imagePullPolicy: IfNotPresent name: istio-proxy args: - proxy - sidecar ...
Como você pode ver acima, esse comando gerou outro arquivo yaml, semelhante ao input (pod busybox), mas com o sidecar (istio-proxy) adicionado ao pod. Então, de alguma forma, isso não é um trabalho manual de 100%, certo? Isso nos poupa um monte de digitação. Você está pronto para aplicar este yaml modificado ao seu cluster do kubernetes:
$ kubectl apply -f busybox-injected.yaml # ou, se você não quiser ter um arquivo intermediário, aplique diretamente usando o arquivo original: $ kubectl apply -f <(istioctl kube-inject -f busybox.yaml)
Uma pergunta natural que pode surgir é: de onde vêm esses dados? Como ele sabe que a imagem do sidecar é docker.io/istio/proxyv2:1.0.2
? A resposta é simples: Todos os dados vêm de um ConfigMap que vive no plano de controle do Istio, no namespace istio-system:
$ kubectl -n istio-system describe configmap istio-sidecar-injector Name: istio-sidecar-injector Namespace: istio-system Data ==== config: ---- policy: enabled template: |- initContainers: - name: istio-init image: "docker.io/istio/proxy_init:1.0.2" ...
Você pode editar este ConfigMap com os valores que deseja injetar nos seus pods. Como você pode ver, esse é basicamente um modelo que será adicionado à sua definição de pod. Se você quiser usar outra imagem para o contêiner istio-proxy, use outra tag ou deseje ajustar qualquer coisa que será injetada, essa é a coisa que você precisa editar. Lembre-se que este ConfigMap é usado para injeção em todos os seus pods na malha de serviço. Seja cuidadoso
Porque istioctl lê um ConfigMap para saber o que injetar, isso significa que você precisa ter acesso a um cluster do Kubernetes em funcionamento com o Istio devidamente instalado. Se por algum motivo você não tiver tal acesso, você ainda pode usar istioctl, fornecendo um arquivo de configuração local:
# execute isso antes, com acesso apropriado ao cluster k8s $ kubectl -n istio-system get configmap istio-sidecar-injector -o=jsonpath='{.data.config}' > inject-config.yaml # sinta-se à vontade para modificar esse arquivo, e você pode executar a qualquer momento depois: $ istioctl kube-inject --injectConfigFile inject-config.yaml ...
Injeção automática
A outra maneira de ter istio-proxy injetado em seus pods é dizendo ao Istio para fazer isso automaticamente por você. Na verdade, isso é ativado por padrão para todos os namespaces com o rótulo istio-injection=enabled
. Isso significa que, se um namespace tiver esse rótulo, todos os pods dentro dele receberão o sidecar istio-proxy automaticamente. Você não precisa executar istioctl ou fazer qualquer coisa com seus arquivos yaml!
O modo como funciona é bem simples: ele faz uso de uma funcionalidade do Kubernetes chamado MutatingWebhook que consiste em Kubernetes notificando o Istio sempre que um novo pod está prestes a ser criado e dando ao Istio a chance de modificar a especificação do pod durante seu uso, pouco antes de realmente criar esse pod. Assim, o Istio injeta o sidecar istio-proxy usando o template encontrado no ConfigMap que vimos acima.
Soa bem, certo?
Você pode estar se perguntando: Ei, isso é muito intrusivo! Sim, tudo depende das suas necessidades. Esta injeção automática é muito flexível:
- No istio-sidecar-injector ConfigMap, há um sinalizador booleano indicando se esta injeção automática está habilitada ou não.
- Somente os namespaces rotulados adequadamente receberão a injeção automática. Você pode escolher seletivamente quais namespaces terão injeção automática.
- Além disso, você pode ajustar esse rótulo, alterá-lo ou mesmo remover esse filtro (o que significa que a injeção ocorrerá automaticamente em todos os namespaces! Seja cuidadoso!) Editando o MutatingWebhookConfiguration:
kubectl -n istio-system edit MutatingWebhookConfiguration istio-sidecar-injector
. Procure o camponamespaceSelector
. - Você pode evitar que a injeção aconteça em pods seletivos. Se um pod tiver a anotação
sidecar.istio.io/inject: "false"
, então o Istio não irá injetar o sidecar nele. - Você pode inverter a lógica se você for mais conservador. Desative a injeção automática para todos e ative-a apenas para pods selecionados. Para isso, você só precisa definir a política como falsa (
kubectl -n istio-system edit configmap istio-sidecar-injector
) e anotar os pods que você deseja para injetar comsidecar.istio.io/inject: "true"
Eu quero mais flexibilidade!
Aqui está um caso de uso em que a flexibilidade acima não foi suficiente:
Para aqueles que não estão familiarizados com o Openshift (distribuição de Kubernetes da Red Hat), ele possui uma funcionalidade chamada source-to-image – s2i, que magicamente cria imagens de contêiner com base no código-fonte. Você fornece um repositório git (funciona com muitas linguagens de programação!) como entrada e obtém uma imagem de contêiner, sendo executada no Openshift como resultado.
É uma funcionilidade incrível! Não vou entrar em detalhes aqui, vou apenas dizer o que precisamos saber agora: para fazer isso, o Openshift cria um ou mais pods auxiliares, intermediários, para construir o código-fonte. Quando a compilação estiver concluída, os artefatos binários vão para a imagem do contêiner resultante, prontos para serem executados no Openshift, e esses conjuntos auxiliares são então descartados.
Se habilitarmos a injeção automática em um determinado namespace e usarmos a funcionalidade de s2i do Openshift, isso significa que todos os pods receberão o sidecar injetado. Mesmo aqueles pods compiladores (intermediários, auxiliares)! Pior, como eles não estão sob nosso controle (eles são criados pelo Openshift, não por nós), não podemos anotar para não obter o sidecar injetado. As compilações não se comportam bem com o sidecar injetado, então não queremos que eles sejam automaticamente injetados. O que fazer agora?
Bem, uma solução possível é seguir a abordagem conservadora explicada no último item acima: Desativar a injeção automática para todos e ativá-la apenas para os pods selecionados. Isso funciona, mas exige que você anote ativamente os pods que você deseja que a injeção automática aconteça.
Ou… Há uma nova solução para esse problema:
A nova solução
A partir da versão 1.1.0, a injeção automática do Istio tem uma maneira de adicionar exceções com base em rótulos, ou seja: não injetar o sidecar em pods que correspondam a esses rótulos, mesmo se a política for true e esse namespace for marcado para ter injeção automática. Você pode adicionar essas exceções no istio-sidecar-injector ConfigMap:
$ kubectl -n istio-system describe configmap istio-sidecar-injector apiVersion: v1 kind: ConfigMap metadata: name: istio-sidecar-injector data: config: |- policy: enabled neverInjectSelector: - matchExpressions: - {key: openshift.io/build.name, operator: Exists} - matchExpressions: - {key: openshift.io/deployer-pod-for.name, operator: Exists} template: |- initContainers: ...
Você pode ver acima um campo neverInjectSelector
. É uma matriz de seletores de rótulos de Kubernetes. Eles são comparados com OR, parando no primeiro jogo. A instrução acima significa: Nunca injetar em pods que tenham o rótulo openshift.io/build.name
ou openshift.io/deployer-pod-for.name
– os valores dos rótulos não importam, estamos apenas verificando se as chaves existem. Com essa regra adicionada, nosso caso de uso de s2i do Openshift está coberto, o que significa que os pods auxiliares não terão sidecars injetados (porque os pods auxiliares de s2i contêm esses rótulos).
Para completar, você também pode usar um campo chamado alwaysInjectSelector
, que sempre injetará o sidecar em pods que correspondam ao seletor de rótulo, apesar da política global.
A abordagem do seletor de rótulos oferece muita flexibilidade sobre como expressar essas exceções. Dê uma olhada em sua documentação para ver o que você pode fazer com eles!
Vale a pena observar que as anotações nos pods ainda têm a preferência. Se um pod é anotado com sidecar.istio.io/inject: "true/false"
, então ele será honrado. Então, a ordem de avaliação é:
Pod Annotations → NeverInjectSelector → AlwaysInjectSelector → Namespace Policy
Como este {Never,Always}InjectSelector
é uma adição recente, eu ainda tenho que atualizar as documentações para mencioná-lo, mas para todas as outras coisas, para mais informações e exemplos confira a documentação oficial.
Por que meu pod [não] está sendo intejado?
Essa é uma pergunta muito comum. Você seguiu todas as instruções (como rotular o namespace com istio-injection=enabled
) e seus pods não estão recebendo a injeção automática.
Ou bem pelo contrário, você anotou seu pod com sidecar.istio.io/inject: "false"
e ele está sendo injetado. Por que?
Uma coisa que você pode fazer para descobrir o que está acontecendo é olhar nos logs do pod sidecar-injector:
$ pod=$(kubectl -n istio-system get pods -l istio=sidecar-injector -o jsonpath='{.items[0].metadata.name}') $ kubectl -n istio-system logs -f $pod
Em seguida, você pode criar seus pods e monitorar aquele log por qualquer saída. Para uma saída de log mais detalhada – confie em mim, é realmente útil – devemos editar o deployment do sidecar-injector e anexar o argumento --log_output_level=default:debug
ao executável de contêiner sidecar-injector:
$ kubectl -n istio-system edit deployment istio-sidecar-injector ... containers: - args: - --caCertFile=/etc/istio/certs/root-cert.pem - --tlsCertFile=/etc/istio/certs/cert-chain.pem - --tlsKeyFile=/etc/istio/certs/key.pem - --injectConfig=/etc/istio/inject/config - --meshConfig=/etc/istio/config/mesh - --healthCheckInterval=2s - --healthCheckFile=/health - --log_output_level=default:debug image: docker.io/istio/sidecar_injector:1.0.2 imagePullPolicy: IfNotPresent ...
Salve e saia do seu editor. Agora o Kubernetes está fazendo deployment do pod sidecar-injector e o sinalizador depuração deve estar em vigor. Aguarde alguns segundos para o pod estar ativo, então você pode monitorar os logs novamente:
$ pod=$(kubectl -n istio-system get pods -l istio=sidecar-injector -o jsonpath='{.items[0].metadata.name}') $ kubectl -n istio-system logs -f $pod
Dica
Se mesmo com a saída de depuração ativada você não viu nada relevante em seus logs, isso significa que o pod sidecar-injector
não está sendo notificado sobre a criação do pod. Não está sendo invocado para fazer a injeção automática. Isso pode ser devido a uma configuração incorreta em relação ao rótulo do namespace. Verifique se o namespace está rotulado de acordo com o que está no MutatingWebhookConfiguration
. Por padrão, o namespace deve ter o rótulo istio-injection=enabled
. Verifique se isto foi alterado executando o kubectl -n istio-system edit MutatingWebhookConfiguration istio-sidecar-injector
e verifique o campo namespaceSelector
.
Quando terminar a sessão de depuração, você poderá editar o deployment novamente e remover esse argumento de depuração.
É isso. Espero que ajude. Sinta-se à vontade para perguntar ou comentar qualquer coisa, aqui ou no twitter! Nos vemos em breve!
0sem comentários ainda