1. Visão geral
Este laboratório mostra recursos desenvolvidos para simplificar o fluxo de trabalho dos engenheiros de software responsáveis por desenvolver aplicativos Python em um ambiente conteinerizado. O desenvolvimento de contêineres típico exige que o usuário entenda os detalhes dos contêineres e do processo de criação do contêiner. Além disso, os desenvolvedores geralmente precisam interromper o fluxo, saindo do ambiente de desenvolvimento integrado para testar e depurar os aplicativos em ambientes remotos. Com as ferramentas e tecnologias mencionadas neste tutorial, os desenvolvedores podem trabalhar efetivamente com aplicativos conteinerizados sem sair do ambiente de desenvolvimento integrado.
O que você vai aprender
Neste laboratório, você aprenderá métodos para desenvolver com contêineres no GCP, incluindo:
- Como criar um novo aplicativo inicial do Python
- Analisar o processo de desenvolvimento
- Desenvolver um serviço de descanso CRUD simples
2. Configuração e requisitos
Configuração de ambiente personalizada
- Faça login no Console do Google Cloud e crie um novo projeto ou reutilize um existente. Crie uma conta do Gmail ou do Google Workspace, se ainda não tiver uma.
- O Nome do projeto é o nome de exibição para os participantes do projeto. Ele é uma string de caracteres que não é usada pelas APIs do Google e pode ser atualizada a qualquer momento.
- O ID do projeto precisa ser exclusivo em todos os projetos do Google Cloud e não pode ser alterado após a definição. O Console do Cloud gera automaticamente uma string única, geralmente não importa o que seja. Na maioria dos codelabs, você precisará fazer referência ao ID do projeto, que geralmente é identificado como
PROJECT_ID
. Então, se você não gostar dele, gere outro ID aleatório ou crie um próprio e veja se ele está disponível. Em seguida, ele fica "congelado" depois que o projeto é criado. - Há um terceiro valor, um Número de projeto, que algumas APIs usam. Saiba mais sobre esses três valores na documentação.
- Em seguida, você precisará ativar o faturamento no Console do Cloud para usar os recursos/APIs do Cloud. A execução deste codelab não será muito cara, se tiver algum custo. Para encerrar os recursos e não gerar cobranças além deste tutorial, siga as instruções de "limpeza" encontradas no final do codelab. Novos usuários do Google Cloud estão qualificados para o programa de US$ 300 de avaliação sem custos.
Inicie o editor do Cloud Shell
Este laboratório foi elaborado e testado para uso com o Editor do Google Cloud Shell. Para acessar o editor,
- acesse seu projeto do Google em https://console.cloud.google.com.
- No canto superior direito, clique no ícone do editor do Cloud Shell
- Um novo painel será aberto na parte inferior da janela.
- Clique no botão "Abrir editor"
- O editor será aberto com um explorador à direita e o editor na área central.
- Um painel do terminal também deve estar disponível na parte inferior da tela
- Se o terminal NÃO estiver aberto, use a combinação de teclas "ctrl+" para abrir uma nova janela de terminal
Configuração do ambiente
No Cloud Shell, defina o ID e o número do projeto. Salve-as como variáveis PROJECT_ID
e PROJECT_ID
.
export PROJECT_ID=$(gcloud config get-value project)
export PROJECT_NUMBER=$(gcloud projects describe $PROJECT_ID \
--format='value(projectNumber)')
Conseguir o código-fonte
- O código-fonte deste laboratório está localizado em container-developer-workshop em GoogleCloudPlatform no GitHub. Clone-o com o comando abaixo e mude para o diretório.
git clone https://github.com/GoogleCloudPlatform/container-developer-workshop.git &&
cd container-developer-workshop/labs/python
mkdir music-service && cd music-service
cloudshell workspace .
Se o terminal NÃO estiver aberto, use a combinação de teclas "ctrl+" para abrir uma nova janela de terminal
Provisionar a infraestrutura usada neste laboratório
Neste laboratório, você vai implantar o código no GKE e acessar os dados armazenados em um banco de dados do Spanner. O script de configuração abaixo prepara essa infraestrutura para você. O processo de provisionamento levará mais de 10 minutos. Siga as próximas etapas enquanto a configuração é processada.
../setup.sh
3. Criar um novo aplicativo inicial do Python
- Crie um arquivo chamado
requirements.txt
e copie o conteúdo abaixo para ele.
Flask
gunicorn
google-cloud-spanner
ptvsd==4.3.2
- Crie um arquivo chamado
app.py
e cole o código a seguir nele
import os
from flask import Flask, request, jsonify
from google.cloud import spanner
app = Flask(__name__)
@app.route("/")
def hello_world():
message="Hello, World!"
return message
if __name__ == '__main__':
server_port = os.environ.get('PORT', '8080')
app.run(debug=False, port=server_port, host='0.0.0.0')
- Crie um arquivo chamado Dockerfile e cole o código abaixo nele
FROM python:3.8
ARG FLASK_DEBUG=0
ENV FLASK_DEBUG=$FLASK_DEBUG
ENV FLASK_APP=app.py
WORKDIR /app
COPY requirements.txt .
RUN pip install --trusted-host pypi.python.org -r requirements.txt
COPY . .
ENTRYPOINT ["python3", "-m", "flask", "run", "--port=8080", "--host=0.0.0.0"]
Observação: FLASK_DEBUG=1 permite recarregar automaticamente as alterações de código em um aplicativo flask do Python. Este Dockerfile permite que você transmita esse valor como um argumento de compilação.
Gerar manifestos
No seu terminal, execute o seguinte comando para gerar um skaffold.yaml e deployment.yaml padrão
- Inicialize o Skaffold com o seguinte comando
skaffold init --generate-manifests
Quando solicitado, use as setas para mover o cursor e a barra de espaço para selecionar as opções.
Opções disponíveis:
8080
para a portay
para salvar a configuração.
Atualizar configurações do Skaffold
- Alterar o nome padrão do aplicativo
- Abrir
skaffold.yaml
- Selecione o nome da imagem definido atualmente como
dockerfile-image
- Clique com o botão direito do mouse e escolha "Alterar todas as ocorrências"
- Digite o novo nome como
python-app
- Edite mais a seção de criação para
- adicione
docker.buildArgs
para transmitirFLASK_DEBUG=1
- Sincronize as configurações para carregar qualquer mudança em arquivos
*.py
do ambiente de desenvolvimento integrado para o contêiner em execução.
Após as edições, a seção de build no arquivo skaffold.yaml
ficará assim:
build:
artifacts:
- image: python-app
docker:
buildArgs:
FLASK_DEBUG: 1
dockerfile: Dockerfile
sync:
infer:
- '**/*.py'
Modificar o arquivo de configuração do Kubernetes
- Alterar o nome padrão
- Abrir arquivo
deployment.yaml
- Selecione o nome da imagem definido atualmente como
dockerfile-image
- Clique com o botão direito do mouse e escolha "Alterar todas as ocorrências"
- Digite o novo nome como
python-app
4. Orientações sobre o processo de desenvolvimento
Com a lógica de negócios adicionada, agora é possível implantar e testar seu aplicativo. Na seção a seguir, vamos destacar o uso do plug-in Cloud Code. Entre outras coisas, este plug-in se integra ao skaffold para otimizar seu processo de desenvolvimento. Quando você implantar no GKE nas etapas a seguir, o Cloud Code e o Skaffold vão criar automaticamente a imagem do contêiner, enviá-la para um Container Registry e implantar o aplicativo no GKE. Isso acontece nos bastidores abstraindo os detalhes do fluxo do desenvolvedor.
Implantar no Kubernetes
- No painel da parte de baixo do Editor do Cloud Shell, selecione Cloud Code Envio
- No painel exibido na parte superior, selecione Executar no Kubernetes. Se solicitado, selecione Sim para usar o contexto atual do Kubernetes.
Esse comando inicia um build do código-fonte e executa os testes. O build e os testes levarão alguns minutos para serem executados. Esses testes incluem testes de unidade e uma etapa de validação que verifica as regras definidas para o ambiente de implantação. Essa etapa de validação já está configurada e garante que você receba avisos de problemas de implantação mesmo enquanto estiver trabalhando no ambiente de desenvolvimento.
- Na primeira vez que você executar o comando, um prompt vai aparecer na parte de cima da tela perguntando se você quer o contexto atual do Kubernetes. Selecione "Yes" para aceitar e usar o contexto atual.
- Em seguida, será exibido um prompt perguntando qual registro de contêiner usar. Pressione "Enter" para aceitar o valor padrão fornecido
- Selecione a guia Output no painel inferior para visualizar o progresso e as notificações
- Selecione "Kubernetes: Run/Debug - Detalhado". no menu suspenso do canal à direita, é possível ver mais detalhes e os registros das transmissões ao vivo dos contêineres.
Quando o build e os testes forem concluídos, a guia "Output" vai mostrar: Attached debugger to container "python-app-8476f4bbc-h6dsl" successfully.
, e o URL http://localhost:8080 vai aparecer na lista.
- No terminal do Cloud Code, passe o cursor sobre o primeiro URL na saída (http://localhost:8080) e, na dica de ferramenta exibida, selecione "Abrir visualização na Web".
- Uma nova guia do navegador vai ser aberta mostrando a mensagem
Hello, World!
.
Fazer recarga automática
- Abra o arquivo
app.py
. - Alterar a mensagem de saudação para
Hello from Python
Na janela Output
, na visualização Kubernetes: Run/Debug
, o inspetor sincroniza os arquivos atualizados com o contêiner no Kubernetes.
Update initiated Build started for artifact python-app Build completed for artifact python-app Deploy started Deploy completed Status check started Resource pod/python-app-6f646ffcbb-tn7qd status updated to In Progress Resource deployment/python-app status updated to In Progress Resource deployment/python-app status completed successfully Status check succeeded ...
- Se você mudar para a visualização
Kubernetes: Run/Debug - Detailed
, vai perceber que ela reconhece mudanças no arquivo e depois cria e reimplanta o app.
files modified: [app.py]
Syncing 1 files for gcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Copying files:map[app.py:[/app/app.py]]togcr.io/veer-pylab-01/python-app:3c04f58-dirty@sha256:a42ca7250851c2f2570ff05209f108c5491d13d2b453bb9608c7b4af511109bd
Watching for changes...
[python-app] * Detected change in '/app/app.py', reloading
[python-app] * Restarting with stat
[python-app] * Debugger is active!
[python-app] * Debugger PIN: 744-729-662
- Atualize seu navegador para conferir os resultados atualizados.
Depuração
- Acesse a Visualização de depuração e interrompa a linha de execução atual
.
- Clique em
Cloud Code
no menu da parte de baixo e selecioneDebug on Kubernetes
para executar o aplicativo no mododebug
.
- Na visualização
Kubernetes Run/Debug - Detailed
da janelaOutput
, observe que o skaffold vai implantar esse aplicativo no modo de depuração.
- Na primeira vez que isso for executado, um prompt perguntará onde a origem está dentro do contêiner. Esse valor está relacionado aos diretórios no Dockerfile.
Pressione Enter para aceitar o padrão
Levará alguns minutos para o aplicativo ser criado e implantado.
- Quando o processo é concluído. Você vai notar um depurador anexado.
Port forwarding pod/python-app-8bd64cf8b-cskfl in namespace default, remote port 5678 -> http://127.0.0.1:5678
- A barra de status inferior muda de azul para laranja, indicando que está no modo de depuração.
- Na visualização
Kubernetes Run/Debug
, observe que um contêiner depurável foi iniciado.
**************URLs***************** Forwarded URL from service python-app: http://localhost:8080 Debuggable container started pod/python-app-8bd64cf8b-cskfl:python-app (default) Update succeeded ***********************************
Usar pontos de interrupção
- Abra o arquivo
app.py
. - Localize a instrução que diz
return message
- Adicione um ponto de interrupção a essa linha clicando no espaço em branco à esquerda do número da linha. Um indicador vermelho vai indicar que o ponto de interrupção está definido
- Atualize o navegador e observe que o depurador interrompe o processo no ponto de interrupção e permite investigar as variáveis e o estado do aplicativo que está sendo executado remotamente no GKE
- Clique para baixo na seção "VARIÁVEIS".
- Clique em "Locais" e encontre a variável
"message"
. - Clique duas vezes no nome da variável "message" No pop-up, mude o valor para algo diferente, como
"Greetings from Python"
. - Clique no botão "Continuar" no painel de controle de depuração
.
- Revise a resposta no navegador, que agora mostra o valor atualizado que você inseriu.
- Parar a "Depuração" pressionando o botão de parada
e remova o ponto de interrupção clicando nele novamente.
5. Como desenvolver um serviço CRUD simples de descanso
Neste ponto, o aplicativo está totalmente configurado para o desenvolvimento conteinerizado, e você já conheceu o fluxo de trabalho básico de desenvolvimento com o Cloud Code. Nas seções a seguir, você vai praticar o que aprendeu adicionando endpoints do serviço REST que se conectam a um banco de dados gerenciado no Google Cloud.
Programar o serviço REST
O código abaixo cria um serviço REST simples que usa o Spanner como banco de dados de apoio para o aplicativo. Crie o aplicativo copiando o código a seguir nele.
- Crie o aplicativo principal substituindo
app.py
pelo seguinte conteúdo:
import os
from flask import Flask, request, jsonify
from google.cloud import spanner
app = Flask(__name__)
instance_id = "music-catalog"
database_id = "musicians"
spanner_client = spanner.Client()
instance = spanner_client.instance(instance_id)
database = instance.database(database_id)
@app.route("/")
def hello_world():
return "<p>Hello, World!</p>"
@app.route('/singer', methods=['POST'])
def create():
try:
request_json = request.get_json()
singer_id = request_json['singer_id']
first_name = request_json['first_name']
last_name = request_json['last_name']
def insert_singers(transaction):
row_ct = transaction.execute_update(
f"INSERT Singers (SingerId, FirstName, LastName) VALUES" \
f"({singer_id}, '{first_name}', '{last_name}')"
)
print("{} record(s) inserted.".format(row_ct))
database.run_in_transaction(insert_singers)
return {"Success": True}, 200
except Exception as e:
return e
@app.route('/singer', methods=['GET'])
def get_singer():
try:
singer_id = request.args.get('singer_id')
def get_singer():
first_name = ''
last_name = ''
with database.snapshot() as snapshot:
results = snapshot.execute_sql(
f"SELECT SingerId, FirstName, LastName FROM Singers " \
f"where SingerId = {singer_id}",
)
for row in results:
first_name = row[1]
last_name = row[2]
return (first_name,last_name )
first_name, last_name = get_singer()
return {"first_name": first_name, "last_name": last_name }, 200
except Exception as e:
return e
@app.route('/singer', methods=['PUT'])
def update_singer_first_name():
try:
singer_id = request.args.get('singer_id')
request_json = request.get_json()
first_name = request_json['first_name']
def update_singer(transaction):
row_ct = transaction.execute_update(
f"UPDATE Singers SET FirstName = '{first_name}' WHERE SingerId = {singer_id}"
)
print("{} record(s) updated.".format(row_ct))
database.run_in_transaction(update_singer)
return {"Success": True}, 200
except Exception as e:
return e
@app.route('/singer', methods=['DELETE'])
def delete_singer():
try:
singer_id = request.args.get('singer')
def delete_singer(transaction):
row_ct = transaction.execute_update(
f"DELETE FROM Singers WHERE SingerId = {singer_id}"
)
print("{} record(s) deleted.".format(row_ct))
database.run_in_transaction(delete_singer)
return {"Success": True}, 200
except Exception as e:
return e
port = int(os.environ.get('PORT', 8080))
if __name__ == '__main__':
app.run(threaded=True, host='0.0.0.0', port=port)
Adicionar configurações de banco de dados
Para se conectar ao Spanner com segurança, configure o aplicativo para usar as identidades de carga de trabalho. Isso permite que o aplicativo atue como a própria conta de serviço e tenha permissões individuais ao acessar o banco de dados.
- Atualize o
deployment.yaml
. Adicione o seguinte código ao final do arquivo (mantenha os recuos da tabulação no exemplo abaixo)
serviceAccountName: python-ksa
nodeSelector:
iam.gke.io/gke-metadata-server-enabled: "true"
Implante e valide o aplicativo
- No painel da parte de baixo do Editor do Cloud Shell, selecione
Cloud Code
e depoisDebug on Kubernetes
na parte de cima da tela. - Quando o build e os testes forem concluídos, a guia "Output" vai mostrar:
Resource deployment/python-app status completed successfully
e um URL vai estar listado: "Forwarded URL from service python-app: http://localhost:8080" - Adicione algumas entradas.
No terminal cloudshell, execute o comando abaixo
curl -X POST http://localhost:8080/singer -H 'Content-Type: application/json' -d '{"first_name":"Cat","last_name":"Meow", "singer_id": 6}'
- Execute o comando abaixo no terminal para testar o GET
curl -X GET http://localhost:8080/singer?singer_id=6
- Teste a exclusão: agora tente excluir uma entrada executando o comando a seguir. Mude o valor do item-id, se necessário.
curl -X DELETE http://localhost:8080/singer?singer_id=6
This throws an error message
500 Internal Server Error
Identificar e corrigir o problema
- Modo de depuração e encontrar o problema. Veja algumas dicas:
- Sabemos que há algo errado com a função DELETE, pois ela não está retornando o resultado desejado. Portanto, defina o ponto de interrupção em
app.py
no métododelete_singer
. - Execute passo a passo e observe as variáveis em cada etapa para observar os valores das variáveis locais na janela esquerda.
- Para observar valores específicos, como
singer_id
erequest.args
, adicione essas variáveis à janela de observação.
- Observe que o valor atribuído a
singer_id
éNone
. Mude o código para corrigir o problema.
O snippet de código fixo ficaria assim:
@app.route('/delete-singer', methods=['DELETE', 'GET']) def delete_singer(): try: singer_id = request.args.get('singer_id')
- Depois que o aplicativo for reiniciado, teste novamente tentando excluir.
- Pare a sessão de depuração clicando no quadrado vermelho na barra de ferramentas de depuração
.
6. Limpeza
Parabéns! Neste laboratório, você criou um novo aplicativo Python do zero e o configurou para funcionar efetivamente com contêineres. Em seguida, você implantou e depurou seu aplicativo em um cluster remoto do GKE seguindo o mesmo fluxo de desenvolvedor encontrado nas pilhas de aplicativos tradicionais.
Para fazer a limpeza após a conclusão do laboratório:
- Exclua os arquivos usados no laboratório
cd ~ && rm -rf container-developer-workshop
- Exclua o projeto para remover toda a infraestrutura e os recursos relacionados