48
loading...
This website collects cookies to deliver better user experience
az login
para fazer o login na sua conta e escolher qual será a subscription que será utilizada pra criar os recursos. Assim que o login estiver pronto, vamos começar criando o primeiro recurso, o resource group.az group create -l eastus -n ship-manager-pipeline
az acr create -n shipmanager --sku Basic -g ship-manager-pipeline
az acr update -n shipmanager --admin-enabled true
az cosmosdb create --kind MongoDB -n ship-manager-db -g ship-manager-pipeline
az aks create -n ship-manager -g ship-manager-pipeline \
--enable-addons http_application_routing \
--attach-acr shipmanager \
--vm-size Standard_B2s \
--generate-ssh-keys \
--node-count 2
az aks get-credentials -n ship-manager -g ship-manager-pipeline --admin
kubectl
instalado na máquina para este comando funcionar, caso não o possua, use o comando az aks install-cli
para instalá-lo.v*
for feito. Já o ambiente de teste será publicado em um push de qualquer outro branch que não seja o master
ou main
(dependendo do caso).Se você quiser acompanhar passo a passo, faça um fork do repositório, mas não se esqueça de remover a pasta .github
para que as actions sejam removidas.
kubernetes
na raiz do repositório, depois crie uma segunda pasta chamada ship-manager
.Poderíamos criar o chart do helm de forma automática via CLI, porém ele cria vários arquivos que não precisamos, então vamos criá-lo manualmente para facilitar.
ship-manager
crie mais duas pastas: templates
e charts
. Agora crie dois arquivos no mesmo nível da pasta templates
, um deles se chamará Chart.yaml
e o outro values.yaml
.charts
, nela, crie uma pasta backend
e, dentro desta última adicione um arquivo Chart.yaml
seguido de uma pasta templates
.kubernetes
└── ship-manager
├── Chart.yaml
├── charts
│ └── backend
│ ├── Chart.yaml
│ └── templates
│
├── templates
└── values.yaml
Chart.yaml
da pasta ship-manager
vamos escrever o seguinte:apiVersion: v2
name: ship-manager
description: Chart for the ship manager app
version: 0.1.0
backend
será o seguinte:apiVersion: v2
name: backend
description: Chart for the backend part of the ship manager app
version: 0.1.0
package.json
do Helm, ou seja, o arquivo que define o pacote que vamos instalar no nosso cluster.ship-manager
, é dependente de um backend localizado na pasta charts
, se criássemos outra pasta charts
dentro de backend
iríamos dizer que o backend é dependente dela e assim sucessivamente. Desta forma, quando damos apenas um comando, o Helm já instala todas as dependências em ordem para nós.frontend.yaml
dentro da pasta templates
localizada na pasta ship-manager
. Este template vai ser o que vai ser de fato criado dentro do cluster. Nele vamos ter todos os recursos do Kubernetes, começando pelo Deployment:apiVersion: apps/v1
kind: Deployment
metadata:
name: ship-manager-frontend
spec:
replicas: 1
selector:
matchLabels:
app: ship-manager-frontend
template:
metadata:
labels:
app: ship-manager-frontend
spec:
containers:
- image: {{ required "Registry is required" .Values.global.registryName }}/{{ required "Image name is required" .Values.frontend.imageName }}:{{ required "Image tag is required" .Values.global.imageTag }}
name: ship-manager-frontend
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
ports:
- containerPort: 8080
name: http
volumeMounts:
- name: config
mountPath: /usr/src/app/dist/config.js
subPath: config.js
volumes:
- name: config
configMap:
name: frontend-config
apiVersion: v1
kind: Service
metadata:
name: ship-manager-frontend
spec:
selector:
app: ship-manager-frontend
ports:
- name: http
port: 80
targetPort: 8080
--------
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ship-manager-frontend
annotations:
kubernetes.io/ingress.class: addon-http-application-routing
spec:
rules:
- host: {{ default "ship-manager-frontend" .Values.frontend.ingress.hostname }}.{{ .Values.global.dnsZone }}
http:
paths:
- path: /
backend:
serviceName: ship-manager-frontend
servicePort: http
--------
apiVersion: v1
kind: ConfigMap
metadata:
name: frontend-config
data:
config.js: |
const config = (() => {
return {
'VUE_APP_BACKEND_BASE_URL': 'http://{{ default "ship-manager-backend" .Values.backend.ingress.hostname }}.{{ .Values.global.dnsZone }}',
'VUE_APP_PROJECT_VERSION': '{{ .Values.global.imageTag }}'
}
})()
.Values
, este é o arquivo values.yaml
que vamos ver logo mais. Perceba também que a maioria das coisas que podem ser alteradas e que precisam ser alteradas, como o nome da imagem, a tag, o hostname e banco de dados, também são variáveis.apiVersion: apps/v1
kind: Deployment
metadata:
name: ship-manager-frontend
spec:
replicas: 1
selector:
matchLabels:
app: ship-manager-frontend
template:
metadata:
labels:
app: ship-manager-frontend
spec:
containers:
- image: {{ required "Registry is required" .Values.global.registryName }}/{{ required "Image name is required" .Values.frontend.imageName }}:{{ required "Image tag is required" .Values.global.imageTag }}
name: ship-manager-frontend
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
ports:
- containerPort: 8080
name: http
volumeMounts:
- name: config
mountPath: /usr/src/app/dist/config.js
subPath: config.js
volumes:
- name: config
configMap:
name: frontend-config
--------
apiVersion: v1
kind: Service
metadata:
name: ship-manager-frontend
spec:
selector:
app: ship-manager-frontend
ports:
- name: http
port: 80
targetPort: 8080
--------
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ship-manager-frontend
annotations:
kubernetes.io/ingress.class: addon-http-application-routing
spec:
rules:
- host: {{ default "ship-manager-frontend" .Values.frontend.ingress.hostname }}.{{ .Values.global.dnsZone }}
http:
paths:
- path: /
backend:
serviceName: ship-manager-frontend
servicePort: http
--------
apiVersion: v1
kind: ConfigMap
metadata:
name: frontend-config
data:
config.js: |
const config = (() => {
return {
'VUE_APP_BACKEND_BASE_URL': 'http://{{ default "ship-manager-backend" .Values.backend.ingress.hostname }}.{{ .Values.global.dnsZone }}',
'VUE_APP_PROJECT_VERSION': '{{ .Values.global.imageTag }}'
}
})()
backend.yaml
na pasta charts/backend/templates
:# backend.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: ship-manager-backend
spec:
replicas: 1
selector:
matchLabels:
app: ship-manager-backend
template:
metadata:
labels:
app: ship-manager-backend
spec:
containers:
- image: {{ required "Registry is required" .Values.global.registryName }}/{{ required "Image name is required" .Values.imageName }}:{{ required "Image tag is required" .Values.global.imageTag }}
name: ship-manager-backend
resources:
requests:
cpu: 100m
memory: 128Mi
limits:
cpu: 250m
memory: 256Mi
ports:
- containerPort: 3000
name: http
env:
- name: DATABASE_MONGODB_URI
valueFrom:
secretKeyRef:
key: database_mongodb_uri
name: backend-db
- name: DATABASE_MONGODB_DBNAME
value: {{ default "ship-manager" .Values.global.dbName }}
--------
apiVersion: v1
kind: Service
metadata:
name: ship-manager-backend
spec:
selector:
app: ship-manager-backend
ports:
- name: http
port: 80
targetPort: 3000
--------
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ship-manager-backend
annotations:
kubernetes.io/ingress.class: addon-http-application-routing
spec:
rules:
- host: {{ default "ship-manager-backend" .Values.ingress.hostname }}.{{ .Values.global.dnsZone }}
http:
paths:
- path: /
backend:
serviceName: ship-manager-backend
servicePort: http
--------
apiVersion: v1
kind: Secret
metadata:
name: backend-db
type: Opaque
stringData:
database_mongodb_uri: {{ required "DB Connection is required" .Values.global.dbConn | quote }}
required
, default
e quote
, esta são funções nativas do Helm e quebram um bom galho quando a gente precisa de funcionalidades mais complexas.values.yaml
é baseado em uma hierarquia de escopos, ou seja, tome como exemplo a nossa estrutura:# values.yaml
global:
chave: # Acessível a todos os charts, tanto o frontend como o backend como `.Values.global.chave`
backend:
chave: # Acessível somente ao frontend e ao backend, porém para o frontend será `.Values.backend.chave` e o backend usará como `.Values.chave`
frontend:
chave: # Acessível pelo frontend como `.Values.frontend.chave`, mas não pelo backend
chave: # Acessível somente ao frontend como `.Values.chave`
backend:
.backend
o escopo sofre um "levelling", ou seja, o escopo é removido de dentro de backend
então você não precisa acessar o valor como .Values.backend.chave
se você estiver dentro do chart backend
, mas somente como .Values.chave
.É possível ter mais arquivos values dentro dos charts dependentes e a regra se mantém a mesma, a diferença é que o chart de maior ordem será alterado, porém este padrão torna a manutenção bastante complexa.
global:
registryName:
imageTag:
dbName: ship-manager
dbConn:
dnsZone:
backend:
imageName: ship-manager-backend
ingress:
hostname:
frontend:
imageName: ship-manager-frontend
ingress:
hostname:
default
..github
e dentro dela uma pasta workflows
. O primeiro workflow será o mais simples, o de produção.workflows
vamos criar um arquivo deploy-production.yml
(pode ser qualquer nome, na verdade) e começar escrevendo o nome da nossa pipeline e quais são os gatilhos que vão fazer ela funcionar.name: Build and push the tagged build to production
on:
push:
tags:
- 'v*'
v*
, ou seja, v1.0.0
e até mesmo vabc
, se você quiser reduzir as possibilidades pode usar regex como v[0-9]\.[0-9]\.[0-9]
.name: Build and push the tagged build to production
on:
push:
tags:
- 'v*'
env:
IMAGE_NAME: ship-manager
jobs:
build_push_image:
runs-on: ubuntu-20.04
build_push_image
que vai rodar no ubuntu 20, e uma variável compartilhada que será o nome base da imagem. Agora vamos para a ação de verdade, vamos começar a criar os passos do nosso job, começando com dois super importantes:name: Build and push the tagged build to production
on:
push:
tags:
- 'v*'
env:
IMAGE_NAME: ship-manager
jobs:
build_push_image:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Set env
id: tags
run: echo tag=${GITHUB_REF#refs/tags/} >> $GITHUB_ENV
$GITHUB_REF
é ou o nome do branch ou o nome da tag como /refs/heads/main
ou /refs/tags/v1.0.0
, temos que remover o /refs/*
e ficar só com o final, por isso estamos usando uma substituição via shell para adicioná-la as variáveis globais, porém esta variável só funciona dentro deste job.Não podemos definir a variável dentro de env
porque esta chave não executa nenhum tipo de shell, portanto não podemos usar substituição de valores e nem expansões.
name: Build and push the tagged build to production
on:
push:
tags:
- 'v*'
env:
IMAGE_NAME: ship-manager
jobs:
build_push_image:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Set env
id: tags
run: echo tag=${GITHUB_REF#refs/tags/} >> $GITHUB_ENV
- name: Set up Buildx
uses: docker/setup-buildx-action@v1
- name: Login to ACR
uses: docker/login-action@v1
with:
# Username used to log in to a Docker registry. If not set then no login will occur
username: ${{secrets.ACR_LOGIN }}
# Password or personal access token used to log in to a Docker registry. If not set then no login will occur
password: ${{secrets.ACR_PASSWORD }}
# Server address of Docker registry. If not set then will default to Docker Hub
registry: ${{ secrets.ACR_NAME }}
- name: Build and push frontend image
uses: docker/build-push-action@v2
with:
# Docker repository to tag the image with
tags: ${{secrets.ACR_NAME}}/${{ env.IMAGE_NAME }}-frontend:latest,${{secrets.ACR_NAME}}/${{ env.IMAGE_NAME }}-frontend:${{env.tag}}
labels: |
image.revision=${{github.sha}}
image.release=${{github.ref}}
file: frontend/Dockerfile
context: frontend
push: true
- name: Build and push backend image
uses: docker/build-push-action@v2
with:
# Docker repository to tag the image with
tags: ${{secrets.ACR_NAME}}/${{ env.IMAGE_NAME }}-backend:latest,${{secrets.ACR_NAME}}/${{ env.IMAGE_NAME }}-backend:${{env.tag}}
labels: |
image.revision=${{github.sha}}
image.release=${{github.ref}}
file: backend/Dockerfile
context: backend
push: true
buildx
o utilitário de build de imagens do Docker, a segunda é o login em um registry, no caso será nosso ACR, e aqui temos o nosso primeiro secret
que vamos criar dentro do nosso repositório, que serão os dados de login do ACR.latest
e o nome da tag
do GitHub, desta forma sabemos quais são as imagens "de produção" e quais serão as de teste, adicionamos também duas labels para cada imagem, uma delas tem a revisão, o sha do nosso commit, e a outra o nome da tag.# inicio do arquivo
jobs:
build_push_image:
# job de envio da imagem
deploy:
runs-on: ubuntu-20.04
needs: build_push_image
steps:
- uses: actions/checkout@v2
- name: Set env
id: tags
run: echo tag=${GITHUB_REF#refs/tags/} >> $GITHUB_ENV
- name: Install Helm
uses: Azure/setup-helm@v1
with:
version: v3.3.1
needs
, isso diz que o segundo job só será executado se o primeiro passar. Fazemos o checkout e copiamos a criação da variável, depois rodamos um step simples de instalação do Helm na máquina.# inicio do arquivo
jobs:
build_push_image:
# job de envio da imagem
deploy:
runs-on: ubuntu-20.04
needs: build_push_image
steps:
- uses: actions/checkout@v2
- name: Set env
id: tags
run: echo tag=${GITHUB_REF#refs/tags/} >> $GITHUB_ENV
- name: Install Helm
uses: Azure/setup-helm@v1
with:
version: v3.3.1
- name: Get AKS Credentials
uses: Azure/aks-set-context@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
# Resource group name
resource-group: ship-manager-pipeline
# AKS cluster name
cluster-name: ship-manager
- name: Run Helm Deploy
run: |
helm upgrade \
ship-manager-prd \
./kubernetes/ship-manager \
--install \
--create-namespace \
--namespace production \
--set global.registryName=${{ secrets.ACR_NAME }} \
--set global.dbConn="${{ secrets.DB_CONNECTION }}" \
--set global.dnsZone=${{ secrets.DNS_NAME }} \
--set global.imageTag=${{env.tag}}
production
e setando os valores do values.yaml
através das flags --set
, isso torna tudo mais fácil quando precisarmos remover os dados, pois só precisamos remover o namespace e tudo é deletado.deploy-test
ficou assim:# deploy-test.yml
name: Build and push the tagged build to test
on:
push:
branches-ignore:
- 'main'
- 'master'
env:
IMAGE_NAME: ship-manager
jobs:
build_push_image:
runs-on: ubuntu-20.04
steps:
- uses: actions/checkout@v2
- name: Set env
id: tags
run: echo tag=${GITHUB_REF#refs/heads/} >> $GITHUB_ENV
- name: Set up Buildx
uses: docker/setup-buildx-action@v1
- name: Login to ACR
uses: docker/login-action@v1
with:
# Username used to log in to a Docker registry. If not set then no login will occur
username: ${{secrets.ACR_LOGIN }}
# Password or personal access token used to log in to a Docker registry. If not set then no login will occur
password: ${{secrets.ACR_PASSWORD }}
# Server address of Docker registry. If not set then will default to Docker Hub
registry: ${{ secrets.ACR_NAME }}
- name: Build and push frontend image
uses: docker/build-push-action@v2
with:
# Docker repository to tag the image with
tags: ${{ secrets.ACR_NAME }}/${{ env.IMAGE_NAME }}-frontend:${{env.tag}}
labels: |
image.revision=${{github.sha}}
file: frontend/Dockerfile
context: frontend
push: true
- name: Build and push backend image
uses: docker/build-push-action@v2
with:
# Docker repository to tag the image with
tags: ${{ secrets.ACR_NAME }}/${{ env.IMAGE_NAME }}-backend:${{env.tag}}
labels: |
image.revision=${{github.sha}}
file: backend/Dockerfile
context: backend
push: true
deploy:
runs-on: ubuntu-20.04
needs: build_push_image
steps:
- uses: actions/checkout@v2
- name: Set env
id: tags
run: echo tag=${GITHUB_REF#refs/tags/} >> $GITHUB_ENV
- name: Install Helm
uses: Azure/setup-helm@v1
with:
version: v3.3.1
- name: Get AKS Credentials
uses: Azure/aks-set-context@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
# Resource group name
resource-group: ship-manager-pipeline
# AKS cluster name
cluster-name: ship-manager
- name: Run Helm Deploy
run: |
helm upgrade \
ship-manager-${{env.tag}} \
./kubernetes/ship-manager \
--install \
--create-namespace \
--namespace test-${{env.tag}} \
--set global.registryName=${{ secrets.ACR_NAME }} \
--set global.dbConn="${{ secrets.DB_CONNECTION }}" \
--set global.dbName=ship-manager-test-${{env.tag}} \
--set global.dnsZone=${{ secrets.DNS_NAME }} \
--set backend.ingress.hostname=ship-manager-backend-${{env.tag}} \
--set frontend.ingress.hostname=ship-manager-frontend-${{env.tag}} \
--set global.imageTag=${{env.tag}}
Settings
e depois secrets
, então clique em New repository secret
para criar um novo secret local.ACR_LOGIN
que será o nome do nosso ACR, ou seja, shipmanager
. Outro chamado ACR_NAME
, que não é bem um segredo, porque é o DNS do nosso CR, porém assim evitamos de ter um valor fixo na nossa action, este valor é o shipmanager.azurecr.io
.Ambas as informações podem ser obtidas no portal da Azure, sabendo o nome do ACR o login é o mesmo e o DNS é sempre <nome>.azurecr.io
az acr credential show -n shipmanager --query "passwords[0].value" -o tsv
e deve ser colocada em outro secret chamado ACR_PASSWORD
.az ad sp create-for-rbac --sdk-auth
, este comando vai te devolver um JSON, copie todo o JSON e cole no secret chamado AZURE_CREDENTIALS
.DB_CONNECTION
pode ser obtida também pelo comando az cosmosdb keys list -n ship-manager-db -g ship-manager-pipeline --type connection-strings --query "connectionStrings[0].connectionString"
.az aks show -n ship-manager -g ship-manager-pipeline --query "addonProfiles.httpApplicationRouting.config.HTTPApplicationRoutingZoneName
e colocar no secret chamado DNS_NAME
.git tag -a v<versão> -m'nova versão
e depois git push --tags
para podermos fazer a trigger na nosso build. Teremos um pequeno delay e então uma saída como esta:kubectl get ing -n production
), ao acessar, vamos ter a nossa aplicação rodando: