1. Visão geral
O Microsoft .NET Core é uma versão de código aberto e multiplataforma do .NET que pode ser executada nativamente em contêineres. O .NET Core está disponível no GitHub e é mantido pela Microsoft e pela comunidade .NET. Este laboratório implanta um aplicativo .NET Core conteinerizado no Google Kubernetes Engine (GKE).
Este laboratório segue um padrão típico de desenvolvimento. Nele, os aplicativos são desenvolvidos no ambiente local de um desenvolvedor e depois implantados na produção. Na primeira parte do laboratório, um exemplo de aplicativo .NET Core é validado usando um contêiner em execução no Cloud Shell. Depois da validação, o app é implantado no Kubernetes usando o GKE. O laboratório inclui as etapas para criar um cluster do GKE.
Na segunda parte do laboratório, uma pequena alteração é feita no aplicativo, que mostra o nome do host do contêiner que está executando essa instância do aplicativo. O aplicativo atualizado é validado no Cloud Shell, e a implantação é atualizada para usar a nova versão. A ilustração abaixo mostra a sequência das atividades deste laboratório:
Custos
Se você executar este laboratório exatamente como foi escrito, os custos normais dos serviços a seguir serão aplicados
2. Configuração e requisitos
Pré-requisitos
Para concluir este laboratório, você precisa ter uma conta e um projeto do Google Cloud. Para instruções mais detalhadas sobre como criar um novo projeto, consulte este codelab.
Este laboratório usa o Docker em execução no Cloud Shell, disponível no console do Google Cloud e pré-configurado com várias ferramentas úteis, como gcloud e Docker. Confira abaixo como acessar o Cloud Shell. Clique no ícone do Cloud Shell no canto superior direito para exibi-lo no painel inferior da janela do console.
Opções alternativas de configuração para clusters do GKE (opcional)
Este laboratório exige um cluster do Kubernetes. Na próxima seção, vamos criar um cluster do GKE com uma configuração simples. Nesta seção, mostramos alguns comandos gcloud
que fornecem opções de configuração alternativas para usar ao criar um cluster do Kubernetes com o GKE. Por exemplo, usando os comandos abaixo, é possível identificar diferentes tipos de máquinas, zonas e até mesmo GPUs (aceleradores).
- Liste os tipos de máquina com este comando
gcloud compute machine-types list
. - Liste GPUs com este comando
gcloud compute accelerator-types list
- Liste as zonas do Compute com este comando
gcloud compute zones list
- Receba ajuda sobre qualquer comando gcloud
gcloud container clusters --help
- Por exemplo, isto fornece detalhes sobre a criação de um cluster do Kubernetes
gcloud container clusters create --help
- Por exemplo, isto fornece detalhes sobre a criação de um cluster do Kubernetes
Para uma lista completa das opções de configuração do GKE, consulte este documento.
Prepare-se para criar o cluster do Kubernetes
No Cloud Shell, é necessário definir algumas variáveis de ambiente e configurar o cliente gcloud. Isso é feito com os comandos a seguir.
export PROJECT_ID=YOUR_PROJECT_ID
export DEFAULT_ZONE=us-central1-c
gcloud config set project ${PROJECT_ID}
gcloud config set compute/zone ${DEFAULT_ZONE}
Crie um cluster do GKE
Como este laboratório implanta o app .NET Core no Kubernetes, é necessário criar um cluster. Use o comando a seguir para criar um novo cluster do Kubernetes no Google Cloud usando o GKE.
gcloud container clusters create dotnet-cluster \
--zone ${DEFAULT_ZONE} \
--num-nodes=1 \
--node-locations=${DEFAULT_ZONE},us-central1-b \
--enable-stackdriver-kubernetes \
--machine-type=n1-standard-1 \
--workload-pool=${PROJECT_ID}.svc.id.goog \
--enable-ip-alias
--num-nodes
é o número de nós a serem adicionados por zona e pode ser escalonado posteriormente;--node-locations
é uma lista de zonas separadas por vírgulas. Neste caso, a zona que você identifica na variável de ambiente acima eus-central1-b
são usadas- OBSERVAÇÃO: esta lista não pode conter cópias
- O
--workload-pool
estabelece a identidade da carga de trabalho para que as cargas de trabalho do GKE possam acessar os serviços do Google Cloud
Durante a criação do cluster, o seguinte é exibido
Creating cluster dotnet-cluster in us-central1-b... Cluster is being deployed...⠼
Configurar o kubectl
A CLI kubectl
é a principal maneira de interagir com um cluster do Kubernetes. Para usá-lo com o novo cluster recém-criado, ele precisa ser configurado para autenticar no cluster. Isso é feito com este comando.
$ gcloud container clusters get-credentials dotnet-cluster --zone ${DEFAULT_ZONE}
Fetching cluster endpoint and auth data.
kubeconfig entry generated for dotnet-cluster.
Agora é possível usar kubectl
para interagir com o cluster.
$ kubectl get nodes
NAME STATUS ROLES AGE VERSION
gke-dotnet-cluster-default-pool-02c9dcb9-fgxj Ready <none> 2m15s v1.16.13-gke.401
gke-dotnet-cluster-default-pool-ed09d7b7-xdx9 Ready <none> 2m24s v1.16.13-gke.401
3. Teste localmente e confirme a funcionalidade desejada
Este laboratório usa as seguintes imagens de contêiner do repositório .NET oficial no hub do Docker.
Executar o contêiner localmente para verificar a funcionalidade
No Cloud Shell, verifique se o Docker está funcionando corretamente e se o contêiner .NET funciona conforme o esperado. Para isso, execute o seguinte comando do Docker:
$ docker run --rm mcr.microsoft.com/dotnet/samples
Hello from .NET!
__________________
\
\
....
....'
....
..........
.............'..'..
................'..'.....
.......'..........'..'..'....
........'..........'..'..'.....
.'....'..'..........'..'.......'.
.'..................'... ......
. ......'......... .....
. ......
.. . .. ......
.... . .......
...... ....... ............
................ ......................
........................'................
......................'..'...... .......
.........................'..'..... .......
........ ..'.............'..'.... ..........
..'..'... ...............'....... ..........
...'...... ...... .......... ...... .......
........... ....... ........ ......
....... '...'.'. '.'.'.' ....
....... .....'.. ..'.....
.. .......... ..'........
............ ..............
............. '..............
...........'.. .'.'............
............... .'.'.............
.............'.. ..'..'...........
............... .'..............
......... ..............
.....
Environment:
.NET 5.0.1-servicing.20575.16
Linux 5.4.58-07649-ge120df5deade #1 SMP PREEMPT Wed Aug 26 04:56:33 PDT 2020
Confirmar a funcionalidade do app da Web
Um aplicativo da Web de exemplo também pode ser validado no Cloud Shell. O comando de execução do Docker abaixo cria um novo contêiner que expõe a porta 80
e o mapeia para a porta localhost
8080
. Lembre-se de que localhost
, neste caso, está no Cloud Shell.
$ docker run -it --rm -p 8080:80 --name aspnetcore_sample mcr.microsoft.com/dotnet/samples:aspnetapp
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
No XML encryptor configured. Key {64a3ed06-35f7-4d95-9554-8efd38f8b5d3} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
Como este é um app da Web, ele precisa ser visualizado e validado em um navegador. A próxima seção mostra como fazer isso no Cloud Shell usando a Visualização da Web.
4. Acessar serviços do Cloud Shell usando "Visualização na Web"
O Cloud Shell oferece a Visualização na Web, um recurso que permite usar um navegador para interagir com processos em execução na instância do Cloud Shell.
Usar "Visualização da Web" para conferir os apps no Cloud Shell
No Cloud Shell, clique no botão de visualização da Web e escolha Visualizar na porta 8080 (ou qualquer porta de visualização da Web configurada para ser usada).
Isso abrirá uma janela do navegador com um endereço como este:
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/?authuser=0
Usar a visualização da Web para ver o aplicativo de exemplo .NET
O app de exemplo que foi iniciado na última etapa agora pode ser acessado iniciando a Visualização da Web e carregando o URL fornecido. O código será semelhante a este:
5. Implantar no Kubernetes
Crie o arquivo YAML e aplique
A próxima etapa requer um arquivo YAML que descreva dois recursos do Kubernetes, uma implantação e um serviço. Crie um arquivo chamado dotnet-app.yaml
no Cloud Shell e adicione o seguinte conteúdo a ele.
apiVersion: apps/v1
kind: Deployment
metadata:
name: dotnet-deployment
labels:
app: dotnetapp
spec:
replicas: 3
selector:
matchLabels:
app: dotnetapp
template:
metadata:
labels:
app: dotnetapp
spec:
containers:
- name: dotnet
image: mcr.microsoft.com/dotnet/samples:aspnetapp
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
name: dotnet-service
spec:
selector:
app: dotnetapp
ports:
- protocol: TCP
port: 8080
targetPort: 80
Agora, use kubectl
para aplicar esse arquivo ao Kubernetes.
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment created
service/dotnet-service created
Observe as mensagens que indicam que os recursos desejados foram criados.
Conheça os recursos resultantes
Podemos usar a CLI kubectl
para examinar os recursos que foram criados acima. Primeiro, confira os recursos de implantação e confirme se a nova implantação está lá.
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
dotnet-deployment 3/3 3 3 80s
Agora vamos conferir os ReplicaSets. Deve haver um ReplicaSet criado pela implantação acima.
$ kubectl get replicaset
NAME DESIRED CURRENT READY AGE
dotnet-deployment-5c9d4cc4b9 3 3 3 111s
Por fim, confira os pods. A implantação indicou que deve haver três instâncias. O comando abaixo vai mostrar que há três instâncias. A opção -o wide
foi adicionada para que os nós em que essas instâncias estão em execução sejam mostrados.
$ kubectl get pod -o wide
NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES
dotnet-deployment-5c9d4cc4b9-cspqd 1/1 Running 0 2m25s 10.16.0.8 gke-dotnet-cluster-default-pool-ed09d7b7-xdx9 <none> <none>
dotnet-deployment-5c9d4cc4b9-httw6 1/1 Running 0 2m25s 10.16.1.7 gke-dotnet-cluster-default-pool-02c9dcb9-fgxj <none> <none>
dotnet-deployment-5c9d4cc4b9-vvdln 1/1 Running 0 2m25s 10.16.0.7 gke-dotnet-cluster-default-pool-ed09d7b7-xdx9 <none> <none>
Revisar o recurso de serviço
Um recurso de serviço no Kubernetes é um balanceador de carga. Os endpoints são determinados por rótulos nos pods. Dessa forma, assim que novos pods são adicionados à implantação pela operação kubectl scale deployment
acima, os pods resultantes ficam imediatamente disponíveis para as solicitações processadas por esse serviço.
O comando a seguir deve mostrar o recurso Service.
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dotnet-service ClusterIP 10.20.9.124 <none> 8080/TCP 2m50s
...
É possível ver mais detalhes sobre o serviço com o comando a seguir.
$ kubectl describe svc dotnet-service
Name: dotnet-service
Namespace: default
Labels: <none>
Annotations: <none>
Selector: app=dotnetapp
Type: ClusterIP
IP: 10.20.9.124
Port: <unset> 8080/TCP
TargetPort: 80/TCP
Endpoints: 10.16.0.7:80,10.16.0.8:80,10.16.1.7:80
Session Affinity: None
Events: <none>
Observe que o Serviço é do tipo ClusterIP
. Isso significa que qualquer pod no cluster pode resolver o nome do serviço, dotnet-service
, para o endereço IP dele. As solicitações enviadas ao serviço terão carga balanceada em todas as instâncias (pods). O valor Endpoints
acima mostra os IPs dos pods disponíveis atualmente para este serviço. Compare esses endereços com os IPs dos pods que foram gerados acima.
Verificar o app em execução
Neste ponto, o aplicativo estará ativo e pronto para solicitações dos usuários. Para acessá-lo, use um proxy. O comando a seguir cria um proxy local que aceita solicitações na porta 8080
e as transmite ao cluster do Kubernetes.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Agora use a visualização da Web no Cloud Shell para acessar o aplicativo da Web.
Adicione o seguinte código ao URL gerado pela visualização da Web: /api/v1/namespaces/default/services/dotnet-service:8080/proxy/
. O resultado será parecido com este:
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Parabéns pela implantação do app .NET Core no Google Kubernetes Engine. Em seguida, faremos uma alteração no app e reimplantá-lo.
6. Modificar o app
Nesta seção, o aplicativo será modificado para mostrar o host em que a instância está sendo executada. Assim, será possível confirmar se o balanceamento de carga está funcionando e se os pods disponíveis estão respondendo conforme o esperado.
Conseguir o código-fonte
git clone https://github.com/dotnet/dotnet-docker.git
cd dotnet-docker/samples/aspnetapp/
Atualizar o app para incluir o nome do host
vi aspnetapp/Pages/Index.cshtml
<tr>
<td>Host</td>
<td>@Environment.MachineName</td>
</tr>
Criar uma nova imagem de contêiner e testar localmente
Crie a nova imagem do contêiner com o código atualizado.
docker build --pull -t aspnetapp:alpine -f Dockerfile.alpine-x64 .
Como antes, teste o novo aplicativo localmente
$ docker run --rm -it -p 8080:80 aspnetapp:alpine
warn: Microsoft.AspNetCore.DataProtection.Repositories.FileSystemXmlRepository[60]
Storing keys in a directory '/root/.aspnet/DataProtection-Keys' that may not be persisted outside of the container. Protected data will be unavailable when container is destroyed.
warn: Microsoft.AspNetCore.DataProtection.KeyManagement.XmlKeyManager[35]
No XML encryptor configured. Key {f71feb13-8eae-4552-b4f2-654435fff7f8} may be persisted to storage in unencrypted form.
info: Microsoft.Hosting.Lifetime[0]
Now listening on: http://[::]:80
info: Microsoft.Hosting.Lifetime[0]
Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
Hosting environment: Production
info: Microsoft.Hosting.Lifetime[0]
Content root path: /app
Como antes, o app pode ser acessado usando a visualização na Web. Desta vez, o parâmetro "Host" deve estar visível, conforme mostrado aqui:
Abra uma nova guia no Cloud Shell e execute docker ps
para ver se o ID do contêiner corresponde ao valor "Host" mostrado acima.
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
ab85ce11aecd aspnetapp:alpine "./aspnetapp" 2 minutes ago Up 2 minutes 0.0.0.0:8080->80/tcp relaxed_northcutt
Marque e envie a imagem para que ela fique disponível para o Kubernetes
A imagem precisa ser marcada e enviada para que o Kubernetes seja capaz de extraí-la. Comece listando as imagens do contêiner e identifique a imagem desejada.
$ docker image list
REPOSITORY TAG IMAGE ID CREATED SIZE
aspnetapp alpine 95b4267bb6d0 6 days ago 110MB
Em seguida, marque essa imagem e envie-a para o Google Container Registry. Usando o ID da IMAGEM acima, a aparência vai ficar assim:
docker tag 95b4267bb6d0 gcr.io/${PROJECT_ID}/aspnetapp:alpine
docker push gcr.io/${PROJECT_ID}/aspnetapp:alpine
7. Implante novamente o aplicativo atualizado
Editar o arquivo YAML
Volte para o diretório em que o arquivo dotnet-app.yaml
está salvo. Encontre a linha a seguir no arquivo YAML
image: mcr.microsoft.com/dotnet/core/samples:aspnetapp
Isso precisa ser alterado para fazer referência à imagem do contêiner que foi criada e enviada ao gcr.io acima.
image: gcr.io/PROJECT_ID/aspnetapp:alpine
Não se esqueça de modificá-la para usar seu PROJECT_ID
. Ele vai ficar assim quando você terminar.
image: gcr.io/myproject/aspnetapp:alpine
Aplique o arquivo YAML atualizado
$ kubectl apply -f dotnet-app.yaml
deployment.apps/dotnet-deployment configured
service/dotnet-service unchanged
Observe que o recurso de implantação aparece como atualizado e o de serviço mostra o estado inalterado. Os pods atualizados podem ser vistos como antes, com o comando kubectl get pod
, mas, desta vez, adicionaremos o -w
, que acompanhará todas as mudanças conforme elas acontecem.
$ kubectl get pod -w
NAME READY STATUS RESTARTS AGE
dotnet-deployment-5c9d4cc4b9-cspqd 1/1 Running 0 34m
dotnet-deployment-5c9d4cc4b9-httw6 1/1 Running 0 34m
dotnet-deployment-5c9d4cc4b9-vvdln 1/1 Running 0 34m
dotnet-deployment-85f6446977-tmbdq 0/1 ContainerCreating 0 4s
dotnet-deployment-85f6446977-tmbdq 1/1 Running 0 5s
dotnet-deployment-5c9d4cc4b9-vvdln 1/1 Terminating 0 34m
dotnet-deployment-85f6446977-lcc58 0/1 Pending 0 0s
dotnet-deployment-85f6446977-lcc58 0/1 Pending 0 0s
dotnet-deployment-85f6446977-lcc58 0/1 ContainerCreating 0 0s
dotnet-deployment-5c9d4cc4b9-vvdln 0/1 Terminating 0 34m
dotnet-deployment-85f6446977-lcc58 1/1 Running 0 6s
dotnet-deployment-5c9d4cc4b9-cspqd 1/1 Terminating 0 34m
dotnet-deployment-85f6446977-hw24v 0/1 Pending 0 0s
dotnet-deployment-85f6446977-hw24v 0/1 Pending 0 0s
dotnet-deployment-5c9d4cc4b9-cspqd 0/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-vvdln 0/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-vvdln 0/1 Terminating 0 34m
dotnet-deployment-85f6446977-hw24v 0/1 Pending 0 2s
dotnet-deployment-85f6446977-hw24v 0/1 ContainerCreating 0 2s
dotnet-deployment-5c9d4cc4b9-cspqd 0/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-cspqd 0/1 Terminating 0 34m
dotnet-deployment-85f6446977-hw24v 1/1 Running 0 3s
dotnet-deployment-5c9d4cc4b9-httw6 1/1 Terminating 0 34m
dotnet-deployment-5c9d4cc4b9-httw6 0/1 Terminating 0 34m
A saída acima mostra a atualização gradual enquanto ela acontece. Primeiro, os contêineres novos são iniciados e, quando estão em execução, os contêineres antigos são encerrados.
Verificar o app em execução
Nesse ponto, o aplicativo está atualizado e pronto para solicitações dos usuários. Assim como antes, ele pode ser acessado usando um proxy.
$ kubectl proxy --port 8080
Starting to serve on 127.0.0.1:8080
Agora use a visualização da Web no Cloud Shell para acessar o aplicativo da Web.
Adicione o seguinte código ao URL gerado pela visualização da Web: /api/v1/namespaces/default/services/dotnet-service:8080/proxy/
. O resultado será parecido com este:
https://8080-cs-473655782854-default.us-central1.cloudshell.dev/api/v1/namespaces/default/services/dotnet-service:8080/proxy/
Confirme se o serviço do Kubernetes está distribuindo a carga
Atualize esse URL várias vezes e observe que o host muda conforme as solicitações são balanceadas em pods diferentes pelo serviço. Compare os valores de "Host" com a lista de pods acima para conferir se todos os pods estão recebendo tráfego.
Escalonar instâncias verticalmente
É fácil escalonar apps no Kubernetes. O comando a seguir escalona a implantação para até seis instâncias do aplicativo.
$ kubectl scale deployment dotnet-deployment --replicas 6
deployment.apps/dotnet-deployment scaled
Use este comando para conferir os novos pods e o estado atual deles
kubectl get pod -w
Observe que atualizar a mesma janela do navegador mostra que o tráfego está sendo balanceado em todos os novos pods.
8. Parabéns!
Neste laboratório, um aplicativo da Web de amostra .NET Core foi validado em um ambiente de desenvolvedor e, depois, implantado no Kubernetes usando o GKE. O aplicativo foi modificado para exibir o nome do host do contêiner em que estava sendo executado. A implantação do Kubernetes foi atualizada para a nova versão, e o app foi escalonado verticalmente para demonstrar como a carga é distribuída entre instâncias adicionais.
Para saber mais sobre o .NET e o Kubernetes, confira estes tutoriais. Eles se baseiam no que foi aprendido neste laboratório com a introdução do Istio Service Mesh para criar padrões mais sofisticados de roteamento e resiliência.
9. Limpar
Para evitar custos não intencionais, use os comandos a seguir para excluir o cluster e a imagem do contêiner que foram criadas neste laboratório.
gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine