Istio: O que acontece quando o painel de controle está inativo?

Olá, pessoal!

Eu fiz alguns experimentos no Istio derrubando alguns componentes do painel de controle e observando o que acontece com os aplicativos e a mesh de serviço. Abaixo você encontrará minhas anotações.

Pilot

O Pilot é responsável pelo recurso de gerenciamento de tráfego do Istio e também é responsável por atualizar todos os sidecars com a configuração de mesh mais recente.

Quando o Pilot inicia, ele escuta na porta 15010 (gRPC) e 8080 (HTTP legado).

Quando o sidecar do aplicativo (Envoy, Istio-Proxy) é iniciado, ele se conecta ao pilot.istio-system:15010, obtém a configuração inicial e mantém-se conectado.
Sempre que o pilot detecta uma alteração na mesh (ele monitora os recursos do kubernetes), ele envia uma nova configuração para o sidecars por meio dessa conexão gRPC.

– Se o pilot cair, essa conexão do gRPC entre o piloto e o sidecar será perdida e os sidecars tentarão se reconectar ao pilot indefinidamente.
– O tráfego não é afetado se o pilot estiver fora do ar, porque toda a configuração enviada para o sidecar reside na memória do sidecar.
– Mudanças na mesh (como novos pods, regras, serviços, etc) não chegam aos sidecars, porque o pilot não está lá para ouvir mudanças e encaminhá-las para o sidecars.
– Uma vez que o pilot está ativo novamente, os sidecars se conectam (porque estão sempre tentando se reconectar) a ele e pegam a configuração de mash mais recente.

Mixer Policy

Policy aplica a política de rede.

Mixer lê a configuração na inicialização e também monitora kubernetes para alterações. Uma vez que novas configurações são detectadas, o mixer as carrega em sua memória.

Os sidecars verificam (chamam) o pod mixer policy para cada solicitação direcionada a aplicação (serviço).

Se o pod mixer policy estiver inativo, todas as solicitações para o serviço falharão com um erro “503 UNAVAILABLE: no healthy upstream” – porque o sidecar não pôde se conectar ao pod policy.

No Istio 1.1, há uma nova configuração (policyCheckFailOpen) [global] que permite uma política “Fail Open”, isto é, se o pod mixer policy não estiver alcançável, todas as requisições terão sucesso em vez de falhar com um erro 503. Por padrão, essa configuração está definida com false, isto é, “Fail Close”.

Enquanto o mixer estiver inativo, tudo o que fazemos na mesh (como adicionar regras, alterar qualquer configuração, etc) não terá efeito nos aplicativos até que o mixer esteja ativo novamente.

Mixer Telemetry

Telemetry fornece informações de telemetria para os addons.

Os Sidecars chamam o pod Telemetry após cada solicitação ser concluída, fornecendo informações de telemetria aos adaptadores (Prometheus, etc). Ele faz isso em lotes de 100 solicitações ou 1 segundo (na configuração padrão), o que vier primeiro, para evitar chamadas excessivas ao pod Telemetry.

Se o pod Telemetry estiver desativado, os sidecars registram um erro (no stderr do pod) e descartam as informações de telemetria. As solicitações não são afetadas por isso, como acontece quando o pod Policy está inativo. Uma vez que o pod Telemetry está ativo novamente, ele começa a receber informações de telemetria dos sidecars.

Outras notas

Vale a pena observar que o Istio permite uma instalação personalizada de seus componentes de plano de controle. Por exemplo, se você não precisa de Policy, pode desabilitar totalmente o mixer policy. Essa modularidade está melhorando no Istio 1.1. Para mais informações, confira a documentação.

Além disso, o pilot, mixer policy e a mixer telemetry funcionam bem em uma configuração de alta disponibilidade (HA), com várias réplicas sendo executadas ao mesmo tempo. Na verdade, a configuração padrão vem com um HorizontalPodAutoscaler que varia de 1 a 5 para esses pods. (Curioso? Veja isso e isso).

Istio, injeção de sidecar: ativando injeção automática, adicionando exceções e depuração

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 campo namespaceSelector.
  • 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 com sidecar.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!

Istio, mTLS, depurando um erro 503

Olá pessoal. Neste post eu compartilharei com vocês um problema que tive enquanto testava o tutorial de Circuit Breaking na documentação do Istio. Vou seguir todos os passos que fiz durante a resolução deste problema, e espero que seja útil para alguém. Foi pelo menos para mim que aprendi um pouco mais sobre o Istio no processo.

As etapas da tarefa são bem simples:
1) Instalar um par de pods (um servindo httpbin + um com curl para se comunicar com o serviço httpbin)
2) Criar um objeto DestinationRule para que as chamadas para o serviço httpbin sejam limitadas (Circuit Breaking)
Bem simples, não? Então, vamos começar a diversão.

Vamos instalar os pods de httpbin e auxiliar:

$ kubectl create ns foo
$ kubectl apply -f <(istioctl kube-inject -f samples/httpbin/httpbin.yaml) -n foo
$ kubectl apply -f <(istioctl kube-inject -f samples/sleep/sleep.yaml) -n foo

$ kubectl -n foo get pod,svc
NAME                           READY     STATUS    RESTARTS   AGE
pod/httpbin-6bbb775889-wcp45   2/2       Running   0          35s
pod/sleep-5b597748b4-77kj5     2/2       Running   0          35s

NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)    AGE
service/httpbin   ClusterIP   10.105.25.98           8000/TCP   36s
service/sleep     ClusterIP   10.111.0.72            80/TCP     35s

No pod auxiliar, vamos invocar o serviço httpbin, usando o curl:

$ kubectl -n foo exec -it -c sleep sleep-5b597748b4-77kj5 -- curl http://httpbin:8000/get
{
  "args": {}, 
  "headers": {
    "Accept": "*/*", 
    "Content-Length": "0", 
    "Host": "httpbin:8000", 
    "User-Agent": "curl/7.35.0", 
    "X-B3-Sampled": "1", 
    "X-B3-Spanid": "b5d006d3d9bf1f4d", 
    "X-B3-Traceid": "b5d006d3d9bf1f4d", 
    "X-Request-Id": "970b84b2-999b-990c-91b4-b6c8d2534e77"
  }, 
  "origin": "127.0.0.1", 
  "url": "http://httpbin:8000/get"
}

Por enquanto, tudo bem. O próximo passo na tarefa de Circuit Breaking é adicionar uma DestinationRule:

$ cat <<EOF | kubectl -n foo apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: httpbin
spec:
  host: httpbin
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 1
      http:
        http1MaxPendingRequests: 1
        maxRequestsPerConnection: 1
    outlierDetection:
      consecutiveErrors: 1
      interval: 1s
      baseEjectionTime: 3m
      maxEjectionPercent: 100
EOF

Agora, vamos tentar novamente o serviço httpbin:

$ kubectl -n foo exec -it -c sleep sleep-5b597748b4-77kj5 -- curl http://httpbin:8000/get
upstream connect error or disconnect/reset before headers

Ops, alguma coisa deu errado. Vamos tornar o curl mais verboso:

$ kubectl -n foo exec -it -c sleep sleep-5b597748b4-77kj5 -- curl -v http://httpbin:8000/get
* Hostname was NOT found in DNS cache
*   Trying 10.105.235.142...
* Connected to httpbin (10.105.235.142) port 8000 (#0)
> GET /get HTTP/1.1
> User-Agent: curl/7.35.0
> Host: httpbin:8000
> Accept: */*
> 
< HTTP/1.1 503 Service Unavailable
< content-length: 57
< content-type: text/plain
< date: Tue, 28 Aug 2018 12:26:54 GMT
* Server envoy is not blacklisted
< server: envoy
< 
* Connection #0 to host httpbin left intact
upstream connect error or disconnect/reset before headers

Hmmm, erro 503… Por que? De acordo com a tarefa Circuit Breaking, deveria funcionar bem. Adicionamos apenas uma regra que define o número máximo de conexões TCP como 1 e, de fato, com o comando curl acima, geramos apenas uma conexão. Então, o que há de errado?

A primeira coisa que me veio à mente foi emitir o comando para verificar se Circuit Breaking estava em vigor:

$ kubectl -n foo exec -it -c istio-proxy sleep-5b597748b4-77kj5 -- curl localhost:15000/stats | grep httpbin | grep pending
cluster.outbound|8000||httpbin.foo.svc.cluster.local.upstream_rq_pending_active: 0
cluster.outbound|8000||httpbin.foo.svc.cluster.local.upstream_rq_pending_failure_eject: 0
cluster.outbound|8000||httpbin.foo.svc.cluster.local.upstream_rq_pending_overflow: 0
cluster.outbound|8000||httpbin.foo.svc.cluster.local.upstream_rq_pending_total: 5

Então, o valor 0 para a variável upstream_rq_pending_overflow confirma que nenhuma chamada foi capturada pelo Circuit Breaking.

Explicação do comando acima:
O sidecar do Istio (contêiner Envoy denominado istio-proxy) expõe (localmente) a porta 15000, que é acessível via HTTP e possui alguns utilitários, como a impressão de algumas estatísticas sobre o serviço.
Então, no comando acima nós executamos o curl (curl localhost:15000/stats) dentro do contêiner sidecar (-c istio-proxy) do pod auxiliar (sleep-5b597748b4-77kj5), filtrando a saída pelo serviço que queremos investigar (| grep httpbin) e depois filtrando para o estado pendente do circuit breaker (| grep pending).

Para confirmar que o culpado é o DestinationRule, vamos excluí-lo e tentar novamente:

$ kubectl -n foo delete DestinationRule httpbin
destinationrule.networking.istio.io "httpbin" deleted
$ kubectl -n foo exec -it -c sleep sleep-5b597748b4-77kj5 -- curl -v http://httpbin:8000/get
...
< HTTP/1.1 200 OK
...

Adicionando-o novamente:

...
< HTTP/1.1 503 Service Unavailable
...

Então, parece que o DestinationRule é o vilão aqui. Mas por quê? Precisamos investigar um pouco mais. Ei! E se verificarmos os logs do Envoy (istio-proxy sidecar)? Vamos fazer isso:

$ kubectl -n foo logs -c istio-proxy sleep-5b597748b4-77kj5 -f
# Em outro terminal, emita o comando curl (kubectl -n foo exec -it -c sleep sleep-5b597748b4-77kj5 -- curl -v http://httpbin:8000/get)
# Então, vemos no log:
[2018-08-28T13:06:56.454Z] "GET /get HTTP/1.1" 503 UC 0 57 0 - "-" "curl/7.35.0" "19095d07-320a-9be0-8ba5-e0d08cf58f52" "httpbin:8000" "172.17.0.14:8000"

Isso não ajuda. O log nos diz que o Envoy está recebendo o erro 503 do servidor. Então, vamos verificar os logs para o lado do servidor (httpbin):

$ kubectl -n foo logs -c istio-proxy httpbin-94fdb8c79-h9zrq -f
# Em outro terminal, emita o comando curl (kubectl -n foo exec -it -c sleep sleep-5b597748b4-77kj5 -- curl -v http://httpbin:8000/get)
# Log está vazio...

O que? Nós não vemos nada na saída do log. É como se a requisição não estivesse chegando ao servidor. Então, o que fazer agora?… Ei! E se pudéssemos aumentar a verbosidade do log? Talvez o pedido esteja chegando, mas não está sendo produzido? Vamos ver.

Lembre-se de que eu disse acima sobre a porta 15000 do Envoy sendo exposta localmente ao pod de serviço? Nós usamos isso para pegar estatísticas. Vamos dar uma olhada para descobrir o que mais oferece:

$ kubectl -n foo exec -it -c istio-proxy httpbin-94fdb8c79-h9zrq -- curl http://localhost:15000/help
admin commands are:
  /: Admin home page
  /certs: print certs on machine
...
  /logging: query/change logging levels
...

Ei! Parece que encontramos o que estávamos procurando: /logging. Vamos usá-lo:

$ kubectl -n foo exec -it -c istio-proxy httpbin-94fdb8c79-h9zrq -- curl http://localhost:15000/logging?level=trace
active loggers:
  admin: trace
...

O comando acima definiu todos os registradores de log de Envoy para o nível trace, o melhor. Para obter mais informações sobre essa interface administrativa, verifique os documentos do Envoy. Agora, vamos tentar recuperar o log do servidor Envoy e, esperançosamente, com o nível trace, obteremos algo (na verdade, recebemos muitos logs!):

$ kubectl -n foo logs -c istio-proxy httpbin-94fdb8c79-h9zrq -f
# Em outro terminal, emita o comando curl (kubectl -n foo exec -it -c sleep sleep-5b597748b4-77kj5 -- curl -v http://httpbin:8000/get)
# Agora, vemos nos logs (Eu filtrei parte do conteúdo não relevante):
[debug][filter] external/envoy/source/extensions/filters/listener/original_dst/original_dst.cc:18] original_dst: New connection accepted
[debug][main] external/envoy/source/server/connection_handler_impl.cc:217] [C31] new connection
[trace][connection] external/envoy/source/common/network/connection_impl.cc:389] [C31] socket event: 2
[trace][connection] external/envoy/source/common/network/connection_impl.cc:457] [C31] write ready
[debug][connection] external/envoy/source/common/ssl/ssl_socket.cc:111] [C31] handshake error: 2
[trace][connection] external/envoy/source/common/network/connection_impl.cc:389] [C31] socket event: 3
[trace][connection] external/envoy/source/common/network/connection_impl.cc:457] [C31] write ready
[debug][connection] external/envoy/source/common/ssl/ssl_socket.cc:111] [C31] handshake error: 1
[debug][connection] external/envoy/source/common/ssl/ssl_socket.cc:139] [C31] SSL error: 268435612:SSL routines:OPENSSL_internal:HTTP_REQUEST
[debug][connection] external/envoy/source/common/network/connection_impl.cc:133] [C31] closing socket: 0

Uau, isso parece interessante! Podemos ver que a requisição está realmente chegando ao servidor, mas está falhando devido a um erro de handshake e o Envoy está fechando a conexão. A questão agora é: Por que um erro de handshake? Por que o SSL está envolvido?

Quando falamos de SSL no contexto do Istio, lembramos do TLS Mútuo. Então eu fui à documentação do Istio, tentando encontrar algo relevante para o meu problema. A leitura da tarefa de tutorial de segurança abriu meus olhos!

Eu descobri que eu tinha instalado o Istio com o TLS Mútuo ativado!

Vamos fazer algumas verificações:

$ kubectl get MeshPolicy default -o yaml
apiVersion: authentication.istio.io/v1alpha1
kind: MeshPolicy
metadata: ...
spec:
  peers:
  - mtls: {}

$ kubectl -n istio-system get DestinationRule default -o yaml
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata: ...
spec:
  host: '*.local'
  trafficPolicy:
    tls:
      mode: ISTIO_MUTUAL

Essas saídas acima mostram que o mTLS está instalado no cluster. Esses objetos só existem quando o mTLS está ativado.

OK, olhando novamente os meus scripts de instalação, eu percebi que eu realmente baguncei tudo e instalei o Istio com o mTLS ativado. No entanto, a questão ainda está lá: por que o serviço httpbin está falhando? Sabendo que o mTLS está ativo na malha e lendo a documentação, não é difícil deduzir que o servidor está esperando uma conexão TLS e o cliente está emitindo um texto simples. Mudamos a pergunta novamente: Por que o cliente (sleep pod) está se conectando ao servidor (pod httpbin) usando texto simples?

Novamente, olhando para a documentação, encontramos a resposta. A maneira como o mTLS trabalha no Istio é simples: existe um objeto DestinationRule (chamado “default”, como podemos ver no comando acima) que instrui todo o tráfego na malha a passar pelo TLS. No entanto, quando criamos nossa própria DestinationRule, para o propósito da tarefa Circuit Breaking, sobrescrevemos essa configuração padrão com a nossa, que não tem nenhum TLS! Isso é indicado na documentação do TLS para o Istio (tradução livre do conteúdo):

Não se esqueça de que as regras de destino também são usadas por motivos além de autenticação, como a instalação do deployment de canary, mas a mesma ordem de precedência se aplica. Portanto, se um serviço exigir uma regra de destino específica por qualquer motivo – por exemplo, para um balanceador de carga de configuração – a regra deverá conter um bloco TLS semelhante com o modo ISTIO_MUTUAL, pois, do contrário, ele substituirá as configurações de TLS de malha ou namespace e desativará o TLS.

Então, está claro agora o que devemos fazer: Modificar a DestinationRule para a tarefa Circuit Breaking de forma a incluir o bloco TLS (linhas 20-21 abaixo):

cat <<EOF | kubectl -n foo apply -f -
apiVersion: networking.istio.io/v1alpha3
kind: DestinationRule
metadata:
  name: httpbin
spec:
  host: httpbin
  trafficPolicy:
    connectionPool:
      tcp:
        maxConnections: 1
      http:
        http1MaxPendingRequests: 1
        maxRequestsPerConnection: 1
    outlierDetection:
      consecutiveErrors: 1
      interval: 1s
      baseEjectionTime: 3m
      maxEjectionPercent: 100
    tls:
      mode: ISTIO_MUTUAL
EOF
destinationrule.networking.istio.io/httpbin configured

Agora, vamos tentar nosso serviço httpbin:

kubectl -n foo exec -it -c sleep sleep-5b597748b4-77kj5 -- curl -v http://httpbin:8000/get
...
< HTTP/1.1 200 OK
...

\o/

Agora, eu posso continuar com meu tutorial de Circuit Breaking!

Algumas lições aprendidas:
– Confirme se você está usando mTLS ou não; Habilitá-lo abre a porta para erros obscuros 🙂
– As DestinationRules têm ordem de precedência: as mais específicas sobrescrevem as globais
– Às vezes, podemos fazer bom uso da interface administrativa do Sidecar (porta local 15000)
– Sempre leia a documentação 🙂

Nos vemos em breve!

Istio, Service Mesh, Atualizações

Olá, pessoal! Este blog tá meio quieto ultimamente… Só quero dar uma atualização sobre o que eu ando fazendo estes dias. Segue…

Istio Logo

Projeto Istio

Desde Dezembro de 2017/Janeiro de 2018 eu mudei de time na Red Hat, e comecei a trabalhar com o Istio. Istio é uma ferramenta/plataforma que nos ajuda na implantação de micro-serviços, em vários aspectos diferentes. Eu planejo escrever mais posts sobre isso. Enquanto isso, você pode ler mais sobre o Istio no website do projeto. Vá por mim, Istio é um projeto fantástico que merece uma olhada, se você está envolvido de alguma forma, ou se planeja entrar no mundo dos micro-serviços, seja você dev ou ops :).

Por enquanto nosso trabalho tem sido principalmente em entender os benefícios do Istio quando usado com o Kubernetes e Openshift (versão melhorada e suportada do Kubernetes pela Red Hat). Isso quer dizer que estamos mais envolvidos em tarefas downstream, tipo criar binários RPM para RHEL e CentOS e imagens para containers, embora nós já estamos contribuindo com a comunidade upstream. O plano é se envolver bem mais com a comunidade, muito, muito em breve.

Eventos

Dezembro passado estive na Kubecon US em Austin, Texas. Foi minha primeira imersão no Istio, visto que rolou uma IstioCon, um pre-evento com um tanto de palestras e workshop. Em Maio desse ano estive na Kubecon EU, que aconteceu em Copenhaguen, Dinamarca. Todas as palestras estão disponíveis gratuitamente (em inglês) no canal da CNCF no YouTube. Confiram! Definitivamente é um ótimo material para se atualizar sobre o que tá acontecendo lá fora, não somente no mundo do Istio, mas no grande ecossistema do Kubernetes.

Em abril estive no ótimo TDC (The Developers Conference) edição Florianópolis, e dei uma palestra sobre o ecossistema dos Containers – alternativas ao Docker (CRI-O, Buildah, Podman, etc).

Agora em julho estarei palestrando no TDC São Paulo. Darei duas palestras sobre Istio e uma sobre o ecossistema de containers, praticamente a mesma que dei no TDC Floripa. O evento já é um sucesso tão grande que os organizadores tiveram que duplicar algumas trilhas, de forma a poder receber todos os inscritos. Assim sendo, minhas palestras nas trilhas de microservices (Introdução a Service Mesh com Istio) e devops (Como o Istio facilita deployments entre micro-serviços) serão duplicadas, assim terão 2 de cada, somando 5 no total! Espero que os brindes que levarei dêem pra todo mundo!

Em agosto viajarei para Boston, MA para participar do primeiro Devconf dos Estados Unidos! Darei uma palestra sobre Istio lá.

Acredito que mais eventos virão, visto o tanto de hype e momentum que Istio/Service Mesh está tendo em todo o lugar.

Planos

Meu plano é escrever no blog regularmente sobre as coisas legais que estamos fazendo com o Istio, e como ele pode ser usado para melhorar a implantação de micro-serviços, tornando a vida dos desenvolvedores e administradores mais fácil!

Nos vemos em breve!

Fedora 26 não conecta no wireless

Esta é uma dica rápida caso você, assim como eu, tenha o mesmo problema ao instalar o Fedora 26 (alfa).

O instalador não conseguiu conectar no meu roteador wireless, um D-Link. Mais especificamente, ele não estava recebendo um endereço IP do roteador. Ao que parece, um problema relacionado a DHCP.

Se for este o caso, abra um terminal (pressione a tecla Windows, digite “Terminal” e aperte Enter), e digite o seguinte comando:

sudo sh -c 'echo "send dhcp-client-identifier = hardware;" > /etc/dhcp/dhclient.conf'

Agora reconecte na rede wireless. Depois do primeiro boot – com o sistema já instalado – repita o comando acima, uma única vez.

A propósito, esse bug não é recente. Aconteceu comigo no Fedora 25, mas somente no sistema instalado. Dentro do instalador tudo funcionou perfeitamente. Agora, no F26, tá acontecendo desde o começo. Aqui o link do bugzilla, para os curiosos: https://bugzilla.redhat.com/show_bug.cgi?id=1154200.

Espero ter ajudado. Bom Fedora pra vcs!

Festa de lançamento do GNOME – Sucesso! Hands on em breve?

Aê gente! Passada rápida aqui só pra dizer que foi muito bacana a nossa festinha do GNOME.

Tivemos 2 palestras “informais” e muito, mas muito bate papo acerca do GNOME: tecnologia, comunidade, meritocracia, GNU e FSF e afins 🙂

O Georges escreveu sobre a festa em inglês, quem puder dá uma olhada lá.

No final ficou um sentimento que seria bacana termos um próximo encontro com hands-on, mais hacking e menos talk 🙂 Achei bacana a ideia e criei um meetup pra isso. Se tivermos gente interessada o suficiente podemos fazer. O que acham? Divulguem!

Festa de lançamento do GNOME 3.22 – Confirmada

Teremos festinha do GNOME em São Paulo!

Data: 01/10/2016 (Sábado) – 10h
Local: Red Hat Brasil – Av. Faria Lima, 3900 – 8º Andar. Mapa.

Como dito no post anterior a ideia é ser um evento informal, com algumas palestras e muito bate-papo. Quem quiser palestrar, acesse esse documento do Google Docs e adiciona seu nome lá. O importante é ser algo relacionado ao GNOME né 🙂

Quanto aos comes e bebes, acredito que podemos seguir o mesmo modelo colaborativo, né? Ou seja a gente mesmo leva os salgados, doces, refri, etc. O que vocês acham? Coloquem lá nesse mesmo documento o que vocês pretendem levar.

IMPORTANTE:
Temos um limite de vagas e, além disso precisamos do nome e RG de todos que comparecerão. Isso é pra controle de acesso ao prédio. Portanto, enviem para o e-mail jwendell@gnome.org seu nome e RG para que possamos confirmar sua vinda e autorizar a entrada de vocês.

Ansioso pra encontrar essa galera! Abraços e até lá!

Festa de lançamento do GNOME 3.22

Oi gente!

O GNOME 3.22 vai ser lançado essa semana, e como de costume vão rolar algumas comemorações ao redor do mundo. Aqui no Brasil a gente não tem muito o costume de celebrar esses lançamentos né?

Alguém anima fazer uma festinha?

Tem alguma regra pra fazer tais “festas”? Não! Basta juntar uns amigos e celebrar como quiser! Mas é bacana postar fotos e divulgar nas mídias né 🙂

Bom, dito isso, tava pensando em juntar um pessoal interessado numa festinha dessa em São Paulo. Pensei num esquema de mini-palestras abordando por exemplo as novidades da nova versão, como se tornar um colaborador, etc. Isso regado a salgados, refri, etc. Ambiente informal, conhecendo pessoas ou revendo amigos, fazendo networking e celebrando! Bacana né!

Dependendo da quantidade de pessoas interessadas, podemos usar o escritório da Red Hat em São Paulo pra isso. Só preciso ter uma noção de quantas pessoas iriam. Se você topa, me fala – via email, twitter, respondendo aqui nesse blog, sinal de fumaça, etc. de forma que eu vou montar uma lista. Se chegarmos num número bom (10?) eu fecho o lugar e atualizo a info aqui.

E aí, vamos animar?

UPDATE: A festa está confirmada: Mais info.