Novembro de 2018

Volume 33 – Número 12

Contêineres – Pronto para colocar em funcionamento com os Serviços de Kubernetes do Azure

Por Chander Dhall

No mundo da orquestração de contêiner, parece que todos estão falando sobre o Kubernetes. Originalmente desenvolvido pelo Google, o Kubernetes é uma plataforma de software livre para orquestrar contêineres do Docker (ou qualquer iniciativa de contêiner aberto) em clusters de VMs (máquinas virtuais), com suporte para implantações, reversões, dimensionamentos e diversos outros recursos. Administrar um cluster Kubernetes pode ser um desafio complexo, assim, a equipe do Azure forneceu uma solução gerenciada chamada de AKS (Serviço de Kubernetes do Azure) para tornar o processo bem mais fácil.

Neste artigo, vou demonstrar como implantar um cluster do AKS, criar um ACR (Registro de Contêiner do Azure) seguro, implantar um aplicativo ASP.NET Web API e expor esse aplicativo na Internet por meio da Entrada do Kubernetes e do DNS do Azure. A intenção aqui não é escrever um artigo detalhado sobre o Kubernetes, mas sobre tudo o que é preciso para colocar um aplicativo em funcionamento rapidamente usando a oferta de AKS do Azure.

Um dos benefícios do AKS do Azure é ter um plano de controle, composto por nós mestre e de configuração, totalmente gerenciado. O plano de controle do Kubernetes normalmente é composto por pelo menos um nó mestre e um, três ou cinco nós de configuração etcd. Como você pode imaginar, o gerenciamento desses serviços principais pode ser entediante e oneroso. Com o AKS, é possível atualizar os serviços principais ou expandir nós de trabalho adicionais com o clicar de um botão. Além disso, no momento, não há nenhum encargo adicional para esses nós de gerenciamento. Você paga apenas pelos nós de trabalho que executarem seus serviços.

O código discutido neste artigo pode ser encontrado em bit.ly/2zaFQeq. No repositório, estão incluídos um aplicativo ASP.NET Web API básico, além de um Dockerfile e manifestos do Kubernetes.

Criação de um cluster do AKS do Kubernetes

A CLI do Azure é usada para criar e gerenciar recursos em uma assinatura da Nuvem do Azure. Ela pode ser encontrada em bit.ly/2OcwFQb. Ao longo deste artigo, vou usá-la para gerenciar vários serviços do Azure. O Azure Cloud Shell (bit.ly/2yESmTP) é um shell baseado na Web, o que permite que os desenvolvedores executem comandos sem instalar a CLI localmente.

Usando o trecho de código a seguir, vamos começar criando um grupo de recursos para manter o cluster do AKS e o contêiner de registro.

> group create --name my-aks-cluster --location eastus2

Depois que o grupo de recursos for criado, é possível criar um cluster com um único nó. Embora VMs do tamanho de imagens intermitentes B1 sejam permitidas, é recomendável que você use pelo menos uma instância de memória de 2 núcleos, 7 GB (D-series ou superior). Se for usada uma menor, há uma tendência de ela não ser confiável ao fazer o dimensionamento ou a atualização de um cluster. Também é preciso levar em consideração que, atualmente, o AKS tem suporte apenas a nós de um único tipo, portanto, não é possível escalar verticalmente para uma instância VM maior em posteriormente. Futuramente, haverá suporte para a adição de nós de um tipo diferente, com vários pools de nós, mas, por ora, é preciso escolher um tamanho de nó que atenda às necessidades dos serviços que se pretende executar.

Relaxe e seja paciente durante a criação do cluster, pois o processo geralmente leva de 10 a 12 minutos. Este é o código para iniciar a operação:

> az aks create --resource-group my-aks-cluster --name my-aks-cluster
  --node-count 1 --generate-ssh-keys --kubernetes-version 1.11.2
  --node-vm-size Standard_D2s_v3

Para obter imagens do Docker no cluster do AKS, é preciso ser usuário de um registro do Docker. É aceitável usar um registro público do Docker para as distribuições de código-fonte aberto, mas a maioria dos projetos prefere proteger o código do aplicativo em um registro privado.

O Azure fornece uma solução de registro segura e gerenciada do Docker com o ACR. Para configurar uma instância do ACR, execute o seguinte comando:

> az acr create --resource-group my-aks-cluster
  --name <REGISTRY_NAME> --sku Basic --admin-enabled true

Observe que o nome do registro deve ser exclusivo entre todos os nomes de registro de ACR hospedados no Azure.

A CLI do Kubernetes

O kubectl é usado para interagir com um cluster do AKS. Ele está disponível para todos os sistemas operacionais e tem várias abordagens de instalação. Você pode encontrar mais informações em bit.ly/2Q58CnJ. Há também um painel baseado na Web que pode ser bastante útil para obter uma visão geral rápida do estado do cluster, mas ele não abrange todas as operações de API disponíveis e, muitas vezes, é possível que você tenha de fazer fallback para a CLI do kubectl. Mesmo que você não seja fã das operações de linha de comando, com o passar do tempo, acabará apreciando o poder do kubectl. Junto com a CLI do Azure, qualquer operação pode ser executada sem sair do shell.

Depois que o kubectl tiver sido instalado, as credenciais podem ser importadas localmente para autenticar ao cluster usando o seguinte comando:

> az aks get-credentials --resource-group my-aks-cluster
  --name my-aks-cluster

A execução desse comando atualiza o ~/.kube/config com o URI do cluster, além da autoridade e das credenciais de assinatura. Ela também adiciona um contexto para configurar o cluster como a configuração atual. A configuração do kubectl pode manter o contexto de vários clusters, o que pode facilmente ser alterado usando o comando de configuração do kubectl. Além disso, há utilitários de código-fonte aberto disponíveis para facilitar a troca de contextos (kubectx e kubectxwin).

Depois de as credenciais terem sido importadas, a conectividade com o cluster pode ser testada listando os nós em execução com o comando kubectl get nodes. Você deve ver algo parecido com isto:

> kubectl get nodes
NAME                     STATUS    ROLES     AGE       VERSION
aks-default-34827916-0   Ready     agent     1d        v1.11.2

Adição de um segredo do Registro de Contêiner para implantações

O Kubernetes tem uma maneira segura de armazenar dados confidenciais usando segredos. Por exemplo, para impedir que as credenciais do ACR sejam armazenadas no código-fonte, é preciso criar e referenciar um segredo a partir de manifestos de implantação do Kubernetes. Para recuperar as credenciais para o serviço do ACR, execute o seguinte comando:

> az acr credential show --name <REGISTRY_NAME>

Depois, use o kubectl para gerar um tipo especial de segredo (docker-registry) que foi projetado especificamente para armazenar os tokens de credenciais fornecidos pelo Docker. O código a seguir criará um segredo chamado my-docker-creds usando as credenciais que foram recuperadas pela consulta do ACR. Note que o nome de usuário diferencia letras maiúsculas de minúsculas e que o ACR usará, por padrão, letras minúsculas para a conta de administrador interna:

> kubectl create secret docker-registry my-docker-creds
  --docker-server=<REGISTRY_NAME>.azurecr.io --docker-username=<REGISTRY_USERNAMEE>
  --docker-password=<REGISTRY_PASSWORD> --docker-email=<ANY_EMAIL>

Por fim, execute o comando a seguir para confirmar se o segredo foi criado:

> kubectl describe secrets my-docker-creds
Name:         my-docker-creds
Namespace:    default
Type:  kubernetes.io/dockerconfigjson
Data
====
.dockerconfigjson:  223 bytes

Criação de um contêiner do Docker

Todos os aplicativos no AKS são implantados como contêineres do Docker. Este é o código para um Dockerfile que cria uma imagem do Docker que pode ser enviada para o cluster:

FROM microsoft/dotnet:2.1-sdk AS builder
COPY . /app
WORKDIR /app
RUN dotnet publish -f netcoreapp2.1 -c Debug -o /publish
FROM microsoft/dotnet:2.1.3-aspnetcore-runtime
COPY --from=builder /publish .
ENTRYPOINT ["/bin/bash", "-c", "dotnet WebApiApp.dll"]

Este Dockerfile usa uma abordagem multiestágios que divide o build em etapas separadas para a compilação e o tempo de execução. Isso reduz significativamente o tamanho da imagem geral ao não incluir o SDK inteiro para a distribuição.

Como efetuar push da imagem ao registro

O Docker funciona com o conceito de imagens e contêineres locais para executar imagens. Não é possível efetuar push de uma imagem do Docker diretamente para o cluster. Em vez disso, a imagem deve ser hospedada em um local que o Kubernetes possa acessar para efetuar pull da imagem localmente para o cluster. O registro do ACR é um local seguro que permite que as imagens sejam gerenciadas centralmente entre ambientes de desenvolvimento, integração contínua e cluster.

A imagem deve ser criada e marcada com o formato <REGISTRY>/<REPOSITORY>/<IMAGE>:<TAG> para que o Docker saiba onde efetuar push da imagem de upstream. O repositório, que pode ter qualquer nome, fornece uma maneira de separar as imagens de registro em grupos lógicos. O código a seguir demonstra como compilar e marcar uma imagem antes de enviar por push ao ACR. Embora haja suporte para a marca mais recente, ao trabalhar com o Kubernetes, é altamente recomendável usar o controle de versão semântico. Ele facilita o gerenciamento de implantações e reversões quando é possível aproveitar os números de versão. Eis o código:

> az acr login --name <REGISTRY_NAME>
> docker build -t <REGISTRY_NAME>.azurecr.io/api/my-webapi-app:1.0 .
> docker push <REGISTRY_NAME>.azurecr.io/api/my-webapi-app:1.0
baf6b1178a5b: Pushed
b3f8eefa2758: Pushed
393dd8f4a879: Pushed
0ad9ffac9ae9: Pushed
8ea427f58308: Pushed
cdb3f9544e4c: Pushed
1.0: digest: sha256:47399f3b2365a9 size: 1579

Agora confirme que foi efetuado push da imagem em upstream, executando:

> az acr login --name <REGISTRY_NAME>
> az acr repository list --name <REGISTRY_NAME>

Implantando o aplicativo

O Kubernetes usa manifestos para descrever todos os objetos no cluster. Os manifestos são arquivos yaml gerenciados por meio da API do Kubernetes. Um tipo de manifesto de implantação é usado para descrever os recursos, a origem da imagem e o estado desejado do aplicativo. A Figura 1 é um manifesto simples que informa ao Kubernetes qual contêiner usar, a quantidade desejada de instâncias em execução do contêiner e os rótulos para ajudar a descrever o aplicativo no cluster. Ela também adiciona o nome do segredo a ser autenticado ao ACR ao efetuar pull da imagem remota.

Figura 1 Deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-webapi-app
spec:
  selector:
    matchLabels:
      app: my-webapi-app
  replicas: 2
  template:
    metadata:
      labels:
        app: my-webapi-app
    spec:
      containers:
      - name: my-webapi-app
        image: <REGISTRY_NAME>.azurecr.io/api/my-webapi-app:1.0
        livenessProbe:
          initialDelaySeconds: 10
          path: /health
          periodSeconds: 5
        ports:
        - containerPort: 80
      imagePullSecrets:
      - name: my-docker-creds

Implante o manifesto com o seguinte comando:

> kubectl apply -f ./deployment.yaml

O Kubernetes usa um conceito chamado de pod para agrupar um ou mais contêineres em uma instância escalável lógica dentro do cluster. Normalmente, você terá um contêiner por pod. Isso permite que você dimensione qualquer serviço para seu aplicativo de modo independente. Um equívoco comum é colocar todos os serviços de um aplicativo — como aplicativo Web e banco de dados — em um único pod. Isso faz com que não seja possível o front-end da Web dimensionar o banco de dados individualmente, e você perderá muitos dos benefícios do Kubernetes como resultado.

Há um cenário comum em que é aceitável ter um contêiner adicional em um pod — trata-se de um conceito chamado de sidecar. Imagine um contêiner que observe seu contêiner de aplicativo e forneça métricas ou registros em log. Colocar ambos os contêineres em um único pod traz benefícios reais nessa instância. Caso contrário, geralmente é melhor manter a taxa de um contêiner por pod até que você tenha uma compreensão sólida das limitações (e benefícios) do agrupamento de contêineres.

Assim que a implantação for concluída, o status do pod de aplicativo pode ser verificado com o comando a seguir:

> kubectl get pods
NAME                             READY     STATUS    RESTARTS   AGE
my-webapi-app-64cdf6b449-9hsks   2/2       Running   0          2m

Note que as duas instâncias de pods estão em execução para satisfazer o conjunto de réplicas.

Criação de um serviço

Agora que o contêiner de Docker do aplicativo está implantado no cluster, um serviço é necessário para torná-lo detectável. Um Serviço Kubernetes torna seus pods identificáveis para outros pods no cluster. Ele faz isso se registrando com o DNS interno do cluster. Ele também fornece o balanceamento de carga em todas as réplicas de pod e gerencia a disponibilidade de pod durante as atualizações de pod. Um serviço é um conceito muito interessante do Kubernetes e é necessário para gerar disponibilidade durantes atualizações das implantações do tipo contínua, azul/verde e canário de aplicativos. O comando a seguir criará um serviço para a implantação:

> kubectl expose deployment/my-webapi-app
service "my-webapi-app" exposed

Agora, execute o comando a seguir para exibir o serviço em execução no cluster:

> kubectl get services
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
my-web-api   ClusterIP   10.0.0.157   <none>        443/TCP   1d

Por padrão, os serviços só são acessíveis de dentro do cluster, por isso a ausência de um external-ip. A CLI do kubectl proporciona a conveniência de abrir um proxy entre o computador local e o cluster para verificar interativamente se ele está em execução, o que pode ser visto neste código:

> kubectl port-forward services/my-webapi-app 8080:80
> curl http://localhost:8080
StatusCode        : 200
StatusDescription : OK
Content           : Hello from our Kubernetes hosted service!

Adição de roteamento de HTTP

O Kubernetes é seguro por padrão, e você deve expor explicitamente os serviços que deseja acessar de fora do cluster. Trata-se de um recurso de design excelente sob uma perspectiva de segurança, mas pode ser confuso para um usuário novato. A maneira mais comum para acessar serviços baseados em HTTP dentro do cluster é usando um controlador de Entrada do Kubernetes. Controladores de Entrada proporcionam uma maneira de rotear solicitações para serviços internos com base em um nome de host e um caminho por meio de um ponto de entrada de proxy HTTP.

Antes da adição da Entrada ao Kubernetes, a principal maneira de expor um serviço era usando um tipo de serviço LoadBalancer. Isso geraria uma proliferação de balanceadores de carga — um por serviço —, sendo que cada um precisaria ser gerenciado separadamente. Com a Entrada, cada serviço no cluster pode ser acessado por um único Azure Load Balancer, reduzindo significativamente o custo e a complexidade. 

O AKS oferece um complemento conveniente para estender o cluster com um proxy Nginx que atua como um controlador de Entrada para lidar com essas solicitações. Ele pode ser habilitado por meio da CLI do Azure com o seguinte comando:

> az aks enable-addons --resource-group my-aks-cluster
  --name my-aks-cluster --addons http_application_routing

Use o comando mostrado na Figura 2 para confirmar que os serviços de roteamento estão em execução.

Figura 2 Confirmar a execução de serviços de roteamento

> kubectl get pods --all-namespaces
NAMESPACE     NAME                                                              READY     
kube-system   addon-http-application-routing-default-http-backend-74d455htfw9   1/1      
kube-system   addon-http-application-routing-external-dns-7cf57b9cc7-lqhl5      1/1     
kube-system   addon-http-application-routing-nginx-ingress-controller-5595b2v   1/1

Você deve ver três novos controladores na lista: o controlador de Entrada, o DNS externo e o back-end padrão. O back-end padrão é usado para fornecer uma resposta para clientes quando uma rota não puder ser encontrada em um serviço existente. É muito semelhante a um manipulador 404 Não Encontrado em um aplicativo ASP.NET comum, exceto pelo fato de ele ser executado como um contêiner de Docker separado. É interessante observar que, embora o complemento HTTP seja ótimo para experimentos, ele não é destinado ao uso em produção.

Exposição do serviço

Uma Entrada é uma combinação de um controlador de Entrada e uma definição de Entrada. Cada serviço terá uma definição de Entrada que informa ao controlador de Entrada como expor o serviço. O comando a seguir vai obter o nome DNS para o cluster:

> az aks show --resource-group my-aks-cluster
  --name my-aks-cluster --query
  addonProfiles.httpApplicationRouting.config.HTTPApplicationRoutingZoneName
  -o table
Result
--------------------------------------
<CLUSTER_PREFIX>.eastus2.aksapp.io

A anotação de entrada kubernetes.io/ingress.class notifica o controlador de Entrada para lidar com essa especificação, conforme mostrado na Figura 3. Ao usar o nome DNS de cluster resolvido anteriormente, será adicionado um subdomínio para o host, juntamente com um caminho raiz “/”. Além disso, o nome do serviço e sua porta exposta internamente devem ser adicionados para informar ao controlador de Entrada onde rotear a solicitação dentro do cluster.

Figura 3 Ingress.yaml

apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-webapi-app
  annotations:
    kubernetes.io/ingress.class: addon-http-application-routing
spec:
  rules:
  - host: my-webapi-app.<CLUSTER_PREFIX>.eastus2.aksapp.io
    http:
      paths:
      - path: /
        backend:
          serviceName: my-webapi-app
          servicePort: 80

Assim, o manifesto de Entrada pode ser aplicado com este comando:

> kubectl apply -f ./ingress.yaml

Pode demorar alguns minutos para que as entradas DNS sejam criadas e propagadas, portanto, seja paciente. O status do serviço DNS pode ser verificado com o portal do Azure, da seguinte forma:

> curl http://my-webapi-app.<CLUSTER_PREFIX>.eastus2.aksapp.io
StatusCode        : 200
StatusDescription : OK
Content           : Hello from our Kubernetes hosted service!

Conclusão

Neste ponto, temos um cluster do AKS de nó único em execução juntamente com um serviço do ACR hospedando as imagens do Docker do aplicativo com segredos seguros. Isso deve ser um ótimo ponto de partida para explorar os diversos recursos adicionais que o AKS Kubernetes do Azure pode oferecer. Eu tenho uma filosofia simples: “Compre o que lhe dá uma base e construa o que lhe diferencia”. Como você pode ver, o Azure simplifica o Kubernetes para que os desenvolvedores e os profissionais do DevOps possam se concentrar em tarefas mais importantes.


Chander Dhall*, CEO da Cazton, oito vezes ganhador do Microsoft MVP, desenvolvedor especialista do Google e líder de tecnologia mundialmente reconhecido em soluções de arquitetura e de implementação.*

Agradeço à Microsoft por fornecer uma revisão técnica deste artigo: Brendan Burns


Discuta esse artigo no fórum do MSDN Magazine