Python on Kubernetes

Garantindo escalabilidade em aplicações produtivas

BIANCA ROSA

Agenda

  • Criando uma app Flask
  • Dockerfile + docker-compose
  • Subindo sua imagem Docker
  • Kubernetes 101
  • Testando num cluster local
  • Processo de build & deploy

Criando uma app Flask

Dica: escreva suas app base nas linguagens que você mais usa e evolua ela.

Dockerfile

FROM python:3.7.2-alpine3.7

RUN pip install pipenv

COPY Pipfile Pipfile
COPY Pipfile.lock Pipfile.lock

RUN pipenv install --system --deploy --ignore-pipfile

COPY . /app
WORKDIR /app


CMD ["gunicorn", "app.main:app", "-w", "4", "-b", "0.0.0.0:5000", "-t", "1000"]
docker build . -t k8s-flask-app

docker-compose

version: "3"
services:
  web:
    build:
      context: .
    command: gunicorn app.main:app --workers=4 --bind 0.0.0.0:5000 --reload
    volumes:
      - .:/app
    ports:
      - "5000:5000"
docker-compose up -d

Subindo sua imagem Docker

docker tag <image-id> <docker-hub-username>/k8s-flask-app


docker push <docker-hub-username>/k8s-flask-app

Kubernetes 101

Plataforma para gerenciar workloads e serviços contêinerizados.

Pods

Pods são a menor unidade computacional que roda dentro de um cluster Kubernetes.

ReplicaSet

Quando falamos de ReplicaSet, queremos garantir a disponibilidade de um número fixo e específico de pods.

Deployments

Um deployment é o estado desejado de seus Pods e ReplicaSets.

Configmaps

Permitem que a gente tenha configurações desacopladas de uma aplicação contêinerizada.

Secrets

Um secret é um objeto que contém dados sensíveis como senhas, tokens ou chaves.

apiVersion: v1
kind: ConfigMap
metadata:
  name: k8s-flask-app-env
data:
  NOT_SO_SECRET_VARIABLE: $(NOT_SO_SECRET_VARIABLE)

configmap.yaml

apiVersion: v1
kind: Secret
metadata:
  name: k8s-flask-app-secrets
type: Opaque
stringData:
  SECRET_VARIABLE: $(SECRET_VARIABLE)

secrets.yaml

apiVersion: v1
kind: Service
metadata:
  name: k8s-flask-app
spec:
  ports:
    - port: 5000
      targetPort: 5000
      protocol: TCP
      name: http
  selector:
    app: k8s-flask-app
  type: NodePort
---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: k8s-flask-app
spec:
  backend:
    serviceName: k8s-flask-app
    servicePort: 5000
---
apiVersion: extensions/v1beta1
kind: Deployment
metadata:
  name: k8s-flask-app
  labels:
    app: k8s-flask-app
spec:
  replicas: 8
  selector:
    matchLabels:
      app: k8s-flask-app
  template:
    metadata:
      labels:
        app: k8s-flask-app
    spec:
      containers:
        - name: k8s-flask-app
          image: biancarosa/k8s-flask-app
          envFrom:
            - configMapRef:
                name: k8s-flask-app-env
            - secretRef:
                name: k8s-flask-app-secrets
          ports:
            - containerPort: 5000

deployment.yaml

Testando num cluster local

minikube start



kubectl apply -f kubernetes/configmap.yaml

kubectl apply -f kubernetes/secrets.yaml

kubectl apply -f kubernetes/deployment.yaml

Subindo num cluster existente

** demo **

Processo de build & deploy

trigger:
- master

pool:
  vmImage: 'ubuntu-latest'

stages:
- stage: Test
  jobs:
  - job: Test
    steps:
    - task: UsePythonVersion@0
      displayName: 'Use Python 3.7'
      inputs:
        versionSpec: '3.7'
        architecture: 'x64'
    - script: pip install pipenv
      displayName: 'Install pipenv'

    - script: pipenv install --system --dev --deploy --ignore-pipfile
      displayName: 'Install dependencies'

    - script: make coverage
      displayName: 'Run tests'

    - script: make lint
      displayName: 'Run lint'

    - task: PublishTestResults@2
      condition: succeededOrFailed()
      inputs:
        testResultsFiles: '**/test-*.xml'
        testRunTitle: 'Publish test results for Python 3.7'

    - task: PublishCodeCoverageResults@1
      inputs:
        codeCoverageTool: Cobertura
        summaryFileLocation: '$(System.DefaultWorkingDirectory)/**/coverage.xml'
        reportDirectory: '$(System.DefaultWorkingDirectory)/**/htmlcov'

- stage: BuildAndPush
  jobs:
  - job: BuildAndPush
    steps:
    - task: Docker@2
      inputs:
        containerRegistry: 'dockerhub'
        repository: 'biancarosa/k8s-flask-app'
        command: 'buildAndPush'
        Dockerfile: 'Dockerfile'
        tags: |
          $(Build.BuildId)
          latest

- stage: Deploy
  jobs:
  - job: Deploy
    steps:
    - task: Kubernetes@1
      inputs:
        connectionType: 'Kubernetes Service Connection'
        kubernetesServiceEndpoint: 'k8s-flask-app-cluster'
        command: 'apply'
        arguments: '-f kubernetes/configmap.yaml'
        secretType: 'dockerRegistry'
        containerRegistryType: 'Azure Container Registry'
    - task: Kubernetes@1
      inputs:
        connectionType: 'Kubernetes Service Connection'
        kubernetesServiceEndpoint: 'k8s-flask-app-cluster'
        command: 'apply'
        arguments: '-f kubernetes/secrets.yaml'
        secretType: 'dockerRegistry'
        containerRegistryType: 'Azure Container Registry'
    - task: Kubernetes@1
      inputs:
        connectionType: 'Kubernetes Service Connection'
        kubernetesServiceEndpoint: 'k8s-flask-app-cluster'
        command: 'apply'
        arguments: '-f kubernetes/deployment.yaml'
        secretType: 'dockerRegistry'
        containerRegistryType: 'Azure Container Registry'

pipeline.yaml

Made with Slides.com