1. Visão geral
O Microsoft .NET Core é uma versão multiplataforma e de código aberto 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 do .NET. Neste laboratório, você vai implantar um app .NET Core conteinerizado no Google Kubernetes Engine (GKE).
Este laboratório segue um padrão de desenvolvimento típico em que os aplicativos são desenvolvidos em um ambiente local e depois implantados na produção. Na primeira parte do laboratório, um exemplo de app .NET Core é validado usando um contêiner em execução no Cloud Shell. Depois de validado, 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 mudança é feita no app que mostra o nome do host do contêiner que está executando essa instância do app. O aplicativo atualizado é validado no Cloud Shell, e a implantação é atualizada para usar a nova versão. A ilustração a seguir mostra a sequência de atividades neste laboratório:

Custos
Se você executar este laboratório exatamente como descrito, os custos normais dos seguintes serviços serão aplicados:
2. Configuração e requisitos
Pré-requisitos
Para concluir este laboratório, é necessário ter uma conta e um projeto do Google Cloud. Para instruções mais detalhadas sobre como criar um novo projeto, consulte este codelab.
Neste laboratório, usamos o Docker em execução no Cloud Shell, que está disponível no console do Google Cloud e vem pré-configurado com muitas ferramentas úteis, como o gcloud e o Docker. Veja abaixo como acessar o Cloud Shell. Clique no ícone do Cloud Shell no canto superior direito para que ele apareça no painel inferior da janela do console.

Opções de configuração alternativas para o cluster do GKE (opcional)
Este laboratório requer um cluster do Kubernetes. Na próxima seção, um cluster do GKE com uma configuração simples será criado. Nesta seção, mostramos alguns comandos gcloud que fornecem opções de configuração alternativas para usar ao criar um cluster do Kubernetes usando o GKE. Por exemplo, usando os comandos abaixo, é possível identificar diferentes tipos de máquina, zonas e até GPUs (aceleradores).
- Liste os tipos de máquina com este comando:
gcloud compute machine-types list - Liste as GPUs com este comando:
gcloud compute accelerator-types list - Liste as zonas do Compute com este comando:
gcloud compute zones list - Receber ajuda sobre qualquer comando gcloud
gcloud container clusters --help- Por exemplo, isso fornece detalhes sobre como criar um cluster do Kubernetes
gcloud container clusters create --help
- Por exemplo, isso fornece detalhes sobre como criar um cluster do Kubernetes
Para ver uma lista completa de opções de configuração do GKE, consulte este documento.
Preparar-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}
Criar 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 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, que pode ser escalonado posteriormente.--node-locationsé uma lista de zonas separadas por vírgulas. Nesse caso, a zona identificada na variável de ambiente acima eus-central1-bsão usadas- OBSERVAÇÃO: esta lista não pode conter duplicatas
--workload-poolestabelece a identidade da carga de trabalho para que as cargas de trabalho do GKE possam acessar os serviços do Google Cloud
Enquanto o cluster está sendo criado, o seguinte é exibido:
Creating cluster dotnet-cluster in us-central1-b... Cluster is being deployed...⠼
Configurar o kubectl
A CLI kubectl é a principal forma de interagir com um cluster do Kubernetes. Para usá-lo com o novo cluster que acabou de ser criado, ele precisa ser configurado para autenticação no cluster. Para isso, use o comando a seguir.
$ 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. Testar localmente e confirmar a funcionalidade desejada
Este laboratório usa as seguintes imagens de contêiner do repositório oficial do .NET no Docker Hub.
Executar o contêiner localmente para verificar a funcionalidade
No Cloud Shell, verifique se o Docker está funcionando corretamente e se o contêiner do .NET funciona como esperado executando 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 amostra 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 a mapeia para a porta 8080 do localhost. Lembre-se de que, neste caso, localhost 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 esse é um app da Web, ele precisa ser visualizado e validado em um navegador da Web. 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 a "Visualização da Web"
O Cloud Shell oferece a Visualização da Web, um recurso que permite usar um navegador para interagir com processos em execução na instância do Cloud Shell.
Usar a "Visualização da Web" para ver 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 que a visualização da Web esteja configurada para usar).

Isso vai abrir uma janela do navegador com um endereço como este:
https://8080-cs-754738286554-default.us-central1.cloudshell.dev/
Visualizar o aplicativo de exemplo do .NET usando a Visualização da Web
Para ver o app de exemplo iniciado na última etapa, inicie a Visualização na Web e carregue o URL fornecido. O código será semelhante a este:

5. Implantar no Kubernetes
Criar o arquivo YAML e aplicar
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.
Analisar os recursos resultantes
Podemos usar a CLI kubectl para examinar os recursos criados acima. Primeiro, vamos analisar os recursos de implantação e confirmar se a nova implantação está lá.
$ kubectl get deployment
NAME READY UP-TO-DATE AVAILABLE AGE
dotnet-deployment 3/3 3 3 80s
Em seguida, confira 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 deveria haver três instâncias. O comando abaixo deve mostrar que há três instâncias. A opção -o wide é 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>
Analisar 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 forem adicionados à implantação pela operação kubectl scale deployment acima, os pods resultantes estarão imediatamente disponíveis para as solicitações processadas por esse serviço.
O comando a seguir mostra o recurso de serviço.
$ kubectl get svc
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
dotnet-service ClusterIP 10.20.9.124 <none> 8080/TCP 2m50s
...
Para ver mais detalhes sobre o serviço, use 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 o balanceamento de carga entre todas as instâncias (pods). O valor Endpoints acima mostra os IPs dos pods disponíveis no momento para esse serviço. Compare-os com os IPs dos pods que foram mostrados acima.
Verificar o app em execução
Nesse momento, o aplicativo está ativo e pronto para solicitações de 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 para o 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 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 por implantar um app .NET Core no Google Kubernetes Engine. Em seguida, vamos fazer 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. Isso vai permitir 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 da Web. Desta vez, o parâmetro Host deve estar visível, como mostrado aqui:

Abra uma nova guia no Cloud Shell e execute docker ps para verificar se o ID do contêiner corresponde ao valor do 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
Marcar e enviar a imagem para que ela fique disponível para o Kubernetes
A imagem precisa ser marcada e enviada para que o Kubernetes possa 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, o comando vai ficar assim:
docker tag 95b4267bb6d0 gcr.io/${PROJECT_ID}/aspnetapp:alpine
docker push gcr.io/${PROJECT_ID}/aspnetapp:alpine
7. Reimplantar o aplicativo atualizado
Editar o arquivo YAML
Volte para o diretório em que o arquivo dotnet-app.yaml está salvo. Encontre a seguinte linha 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 para gcr.io acima.
image: gcr.io/PROJECT_ID/aspnetapp:alpine
Não se esqueça de modificá-lo para usar seu PROJECT_ID. O resultado será semelhante a este:
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
O recurso de implantação aparece como atualizado, e o recurso de serviço aparece como inalterado. Os pods atualizados podem ser vistos como antes com o comando kubectl get pod, mas desta vez vamos adicionar o -w, que vai monitorar 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 conforme ela acontece. Primeiro, novos contêineres são iniciados e, quando estão em execução, os antigos são encerrados.
Verificar o app em execução
Nesse momento, o aplicativo é atualizado e fica pronto para as solicitações do usuário. 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 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/
Confirmar 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 por balanceamento de carga entre diferentes pods pelo serviço. Compare os valores de host com a lista de pods acima para verificar se todos os pods estão recebendo tráfego.
Escalonar instâncias verticalmente
É fácil escalonar apps no Kubernetes. O comando a seguir vai escalonar a implantação para até seis instâncias do aplicativo.
$ kubectl scale deployment dotnet-deployment --replicas 6
deployment.apps/dotnet-deployment scaled
Os novos pods e o estado atual deles podem ser visualizados com este comando:
kubectl get pod -w
Atualize a mesma janela do navegador para ver que o tráfego agora está sendo balanceado entre todos os novos pods.
8. Parabéns!
Neste laboratório, um aplicativo da Web de amostra do .NET Core foi validado em um ambiente de desenvolvimento e, em seguida, implantado no Kubernetes usando o GKE. O app foi modificado para mostrar o nome do host do contêiner em que estava sendo executado. Depois, 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 as 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, apresentando a malha de serviço do Istio para padrões de roteamento e resiliência mais sofisticados.
9. Limpar
Para evitar custos indesejados, use os comandos a seguir para excluir o cluster e a imagem do contêiner criados neste laboratório.
gcloud container clusters delete dotnet-cluster --zone ${DEFAULT_ZONE}
gcloud container images delete gcr.io/${PROJECT_ID}/aspnetapp:alpine