Arquitetura software
DevOps e CI/CD
DevOps e a pratica de unir desenvolvimento e operacoes para entregar software de forma rapida, confiavel e continua. Este guia cobre as principais ferramentas e praticas.
Infrastructure as Code (IaC)
Terraform
Ferramenta de IaC mais popular para provisionar infraestrutura em multiplos provedores.
# main.tf
terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
backend "s3" {
bucket = "meu-terraform-state"
key = "prod/terraform.tfstate"
region = "us-east-1"
}
}
provider "aws" {
region = var.aws_region
}
# VPC
resource "aws_vpc" "main" {
cidr_block = "10.0.0.0/16"
enable_dns_hostnames = true
tags = {
Name = "main-vpc"
Environment = var.environment
}
}
# EKS Cluster
module "eks" {
source = "terraform-aws-modules/eks/aws"
version = "~> 19.0"
cluster_name = "meu-cluster"
cluster_version = "1.28"
vpc_id = aws_vpc.main.id
subnet_ids = module.vpc.private_subnets
eks_managed_node_groups = {
default = {
min_size = 2
max_size = 10
desired_size = 3
instance_types = ["t3.medium"]
}
}
}# Comandos basicos
terraform init # Inicializa providers
terraform plan # Preview das mudancas
terraform apply # Aplica mudancas
terraform destroy # Remove recursosAnsible
Automacao de configuracao e deploy.
# playbook.yml
---
- name: Configure web servers
hosts: webservers
become: yes
vars:
app_version: "1.2.3"
tasks:
- name: Update apt cache
apt:
update_cache: yes
cache_valid_time: 3600
- name: Install Docker
apt:
name:
- docker.io
- docker-compose
state: present
- name: Start Docker service
service:
name: docker
state: started
enabled: yes
- name: Deploy application
docker_container:
name: myapp
image: "myrepo/myapp:{{ app_version }}"
state: started
restart_policy: always
ports:
- "80:8080"
env:
DATABASE_URL: "{{ database_url }}"CI/CD Pipelines
GitHub Actions
# .github/workflows/ci.yml
name: CI/CD Pipeline
on:
push:
branches: [main, develop]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'pnpm'
- name: Install dependencies
run: pnpm install
- name: Run tests
run: pnpm test
- name: Run linter
run: pnpm lint
build:
needs: test
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Login to Container Registry
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v5
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
deploy:
needs: build
runs-on: ubuntu-latest
if: github.ref == 'refs/heads/main'
steps:
- name: Deploy to Kubernetes
uses: azure/k8s-deploy@v4
with:
namespace: production
manifests: |
k8s/deployment.yaml
k8s/service.yaml
images: |
${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}Azure Pipelines
# azure-pipelines.yml
trigger:
branches:
include:
- main
- develop
pool:
vmImage: 'ubuntu-latest'
variables:
dockerRegistry: 'myregistry.azurecr.io'
imageName: 'myapp'
stages:
- stage: Build
jobs:
- job: BuildAndTest
steps:
- task: NodeTool@0
inputs:
versionSpec: '20.x'
- script: |
npm ci
npm test
npm run build
displayName: 'Build and Test'
- task: Docker@2
inputs:
containerRegistry: 'AzureContainerRegistry'
repository: '$(imageName)'
command: 'buildAndPush'
Dockerfile: '**/Dockerfile'
tags: |
$(Build.BuildId)
latest
- stage: Deploy
condition: eq(variables['Build.SourceBranch'], 'refs/heads/main')
jobs:
- deployment: DeployToAKS
environment: 'production'
strategy:
runOnce:
deploy:
steps:
- task: KubernetesManifest@0
inputs:
action: 'deploy'
kubernetesServiceConnection: 'AKS-Connection'
namespace: 'production'
manifests: 'k8s/*.yaml'Jenkins Pipeline
// Jenkinsfile
pipeline {
agent any
environment {
DOCKER_REGISTRY = 'myregistry.com'
IMAGE_NAME = 'myapp'
}
stages {
stage('Checkout') {
steps {
checkout scm
}
}
stage('Test') {
steps {
sh 'npm ci'
sh 'npm test'
}
}
stage('Build') {
steps {
script {
docker.build("${DOCKER_REGISTRY}/${IMAGE_NAME}:${BUILD_NUMBER}")
}
}
}
stage('Push') {
when {
branch 'main'
}
steps {
script {
docker.withRegistry("https://${DOCKER_REGISTRY}", 'docker-credentials') {
docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${BUILD_NUMBER}").push()
docker.image("${DOCKER_REGISTRY}/${IMAGE_NAME}:${BUILD_NUMBER}").push('latest')
}
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sh """
kubectl set image deployment/myapp \
myapp=${DOCKER_REGISTRY}/${IMAGE_NAME}:${BUILD_NUMBER} \
--namespace=production
"""
}
}
}
post {
always {
cleanWs()
}
failure {
slackSend(channel: '#devops', message: "Build failed: ${env.JOB_NAME} ${env.BUILD_NUMBER}")
}
}
}Container Registry
Tipos
| Registry | Tipo | Caracteristica |
|---|---|---|
| Docker Hub | Public/Private | Mais popular |
| GitHub Container Registry | Private | Integrado ao GitHub |
| Amazon ECR | Private | Integrado a AWS |
| Google Artifact Registry | Private | Integrado ao GCP |
| Azure Container Registry | Private | Integrado ao Azure |
| Harbor | Self-hosted | Enterprise, scanning |
Dockerfile Multi-stage
# Build stage
FROM node:20-alpine AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage
FROM node:20-alpine AS runner
WORKDIR /app
ENV NODE_ENV=production
RUN addgroup --system --gid 1001 nodejs
RUN adduser --system --uid 1001 nextjs
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
USER nextjs
EXPOSE 3000
CMD ["node", "server.js"]GitOps
ArgoCD
# application.yaml
apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
name: myapp
namespace: argocd
spec:
project: default
source:
repoURL: https://github.com/org/myapp-manifests
targetRevision: HEAD
path: overlays/production
destination:
server: https://kubernetes.default.svc
namespace: production
syncPolicy:
automated:
prune: true
selfHeal: true
syncOptions:
- CreateNamespace=trueFlux
# flux-system/gotk-sync.yaml
apiVersion: source.toolkit.fluxcd.io/v1
kind: GitRepository
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 1m
url: https://github.com/org/fleet-infra
ref:
branch: main
---
apiVersion: kustomize.toolkit.fluxcd.io/v1
kind: Kustomization
metadata:
name: flux-system
namespace: flux-system
spec:
interval: 10m
path: ./clusters/production
prune: true
sourceRef:
kind: GitRepository
name: flux-systemSecrets Management
HashiCorp Vault
# Iniciar servidor dev
vault server -dev
# Armazenar secret
vault kv put secret/myapp/database \
username="admin" \
password="super-secret"
# Ler secret
vault kv get secret/myapp/databaseExternal Secrets Operator (Kubernetes)
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
name: myapp-secrets
spec:
refreshInterval: 1h
secretStoreRef:
kind: ClusterSecretStore
name: vault-backend
target:
name: myapp-secrets
data:
- secretKey: database-password
remoteRef:
key: secret/myapp/database
property: passwordEstrategias de Deploy
Blue-Green
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: myapp
spec:
selector:
app: myapp
version: blue # Trocar para 'green' no deploy
ports:
- port: 80
targetPort: 8080Canary (Argo Rollouts)
apiVersion: argoproj.io/v1alpha1
kind: Rollout
metadata:
name: myapp
spec:
replicas: 10
strategy:
canary:
steps:
- setWeight: 10
- pause: {duration: 5m}
- setWeight: 30
- pause: {duration: 5m}
- setWeight: 60
- pause: {duration: 5m}
selector:
matchLabels:
app: myapp
template:
metadata:
labels:
app: myapp
spec:
containers:
- name: myapp
image: myapp:v2