Despliegue y configuración de infraestructura DevOps para tus proyectos
Albert Coronado
Posted on April 18, 2024
En este vídeo hemos aprendido a montarnos una pipeline de despliegue DevOps real usando cantidad de tecnologías, algunas de las cuales las hemos instalado: Gitlab, Gitlab CICD, Docker hub, Kubernetes(GKE) sobre Google Cloud Platform(GCP) o Kaniko.
Creación de un cluster sobre Google Cloud Platform(GCP)
Este paso lo hemos realizado desde la consola de GCP en https://console.cloud.google.com/ y es bastante visual, con lo que mejor os basáis en el vídeo.
Obviamente, podríamos haber contratado el cluster a través de cualquier otro proveeros cloud como Amazon o Azure o incluso instalar Kubernetes en nuestros propios servidores con K0s, K3s o similares(Tenemos vídeos y artículos de referencia sobre el tema).
Otra mejora hubiera sido contratar el servicio vía Gcloud o Terraform que nos permitía automatizar este paso. Pero esto ya es otra historia.
Configuración de credenciales de Docker Hub para el despliegue
Para poder desplegar sobre Kubernetes necesitamos un "Registry", que básicamente, se trata de un repositorio donde almacenar las imágenes que vamos a desplegar. En este caso, para no liar mas el vídeo, hemos optado por usar Docker Hub, que nos permite usarlo como servicio SaaS y con su capa gratuita nos vale.
Para ello hemos tenido que configurar unas credenciales y lo hemos hecho vía su interfaz web así que es mas fácil verlo en el vídeo que explicarlo en un artículo.
Me guardo para otro vídeo como instalar alguna alternativa como Nexus para tener nuestro propio registry.
Acceso a Google Cloud Platform(GCP) usando la imagen Docker con Cloud SDK
Para conectarnos al Kubernetes sobre GCP hemos optado por hacerlo usando una imagen de Docker que nos provee el mismo Google. Esta imagen incluye de serie cantidad de utilidades para interactuar con GCP y Kubernetes como son: gcloud(Herramienta CLI para interactuar con GCP), bq(Utilidad para interactuar con los servicios de ingeniería de datos de GCP), gsutils(para subir fichero a GCP), kubectl, Helm, etc.
Hemos arrancado el contenedor con el comando:
docker run --rm -it -v .:/poc google/cloud-sdk:latest
Este commando básicamente arranca el contenedor habilitando el modo interactivo(parámetros -it), montando la carpeta actual en la carpeta /poc(parámetro -v .:/poc) y haciendo que se autodestruya al salir del contenedor(parámetro --rm).
Una vez dentro nos hemos logueado en GCP y configurado el cliente de kubernetes(kubectl) para que use nuestro cluster:
# Login dentro de GCP
gcloud auth login
# Configuración de kubectl apuntando a nuestro cluster
gcloud container clusters get-credentials --zone "ZONA DEL CLUSTER" --project "PROYECTO DE GCP" "NOMBRE DEL CLUSTER"
Instalación de Gitlab sobre Kubernetes(GKE) sobre GCP
Para instalar Gitlab hemos usado el siguiente manifiesto de kubernetes(gitlab.yaml):
kind: Namespace
apiVersion: v1
metadata:
name: gitlab
labels:
name: gitlab-namespace
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: gitlab-deployment
namespace: gitlab
labels:
app: gitlab
spec:
replicas: 1
selector:
matchLabels:
app: gitlab
template:
metadata:
labels:
app: gitlab
spec:
containers:
- name: gitlab
image: gitlab/gitlab-ce:16.7.5-ce.0
env:
- name: GITLAB_OMNIBUS_CONFIG
value: "external_url 'http://gitlab-service'"
- name: GITLAB_ROOT_PASSWORD
value: "YoutubePlus!"
ports:
- containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
namespace: gitlab
name: gitlab-service
spec:
type: LoadBalancer
selector:
app: gitlab
ports:
- protocol: TCP
port: 80
targetPort: 80
Hemos usado el siguiente comando para aplicarlo:
Kubectl apply -f gitlab.yaml
Instalación del Runner de Gitlab
Los Runners de Gitlab son los encargado de ejecutar las tareas de las pipelines de Gitlab CICD y los hay de muchos tipos(locales, amazon, Docker, etc). Nosotros hemos optado por instalar una de tipo kubernetes.
Los Runners se instalan a nivel de grupo, por lo que hemos necesitado primero crear un grupo de gitlab y una vez dentro, ir a la pestaña "CI / CD" y "Runners" para obtener el token necesario para instalarlo(Esto lo podríamos haber automatizado usando el API de Gitlab, por si ha alguien le interesa).
Ya con nuestro token(parámetro registration-token) hemos usado el siguiente manifiesto para instalar el runner(gitlab-runner.yaml):
apiVersion: v1
kind: ServiceAccount
metadata:
name: gitlab-service-account-sa
namespace: gitlab
labels:
app: native-app
imagePullSecrets:
- name: registry-credentials
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: gitlab-service-account-cr
namespace: gitlab
labels:
app: native-app
rules:
- apiGroups:
- ''
- apps
- autoscaling
- batch
- build.openshift.io
- image.openshift.io
- route.openshift.io
resources:
- services
- pods
- pods/log
- pods/exec
- pods/attach
- namespaces
- replicationcontrollers
- daemonsets.apps
- deployments
- replicasets
- statefulsets
- horizontalpodautoscalers
- cronjobs
- jobs
- daemonsets
- buildconfigs
- builds
- imagestreams
- imagestreamtags
- buildconfigs/instantiatebinary
- routes
- secrets
verbs:
- create
- watch
- get
- list
- update
- patch
- delete
- deletecollection
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: gitlab-service-account-rbcr
namespace: gitlab
roleRef:
kind: ClusterRole
name: gitlab-service-account-cr
apiGroup: rbac.authorization.k8s.io
subjects:
- kind: ServiceAccount
name: gitlab-service-account-sa
namespace: gitlab
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: runner-0
namespace: gitlab
spec:
selector:
matchLabels:
name: runner-pod-0
template:
metadata:
namespace: gitlab
labels:
name: runner-pod-0
spec:
serviceAccount: gitlab-service-account-sa
serviceAccountName: gitlab-service-account-sa
containers:
- name: pod
image: alpinelinux/gitlab-runner:latest
command:
- "/bin/sh"
- "-c"
- |
gitlab-runner register \
--kubernetes-privileged true \
--non-interactive \
--run-untagged=false \
--executor kubernetes \
--kubernetes-pull-policy always \
--request-concurrency 10 \
--name 'My Runner' \
--kubernetes-image alpine:latest \
--kubernetes-image-pull-secrets registry-credentials \
--kubernetes-namespace gitlab \
--kubernetes-service-account gitlab-service-account-sa \
--tag-list kubernetes \
--url "http://gitlab-service/" \
--registration-token "GR1348941zBB3FQSts6S282gMoNNC"
cat /etc/gitlab-runner/config.toml
gitlab-runner run
Hemos usado el siguiente comando para aplicarlo:
Kubectl apply -f gitlab-runner.yaml
Creación de nuestro primer repo y programación de nuestra api python
El siguiente paso ha sido crear nuestro repo de Gitlab y clonarlo(Con cuidado con la URL que te autogenera el Gitlab) usando el comando:
git clone http://URL DE NUESTRO REPO
Hemos añadido dos fichero con nuestra aplicación:
requirements.txt(Contiene las dependencias de nuestra aplicación Python)
fastapi>=0.68.0,<0.69.0
uvicorn>=0.15.0,<0.16.0
main.py(Contiene nuestra aplicación Python)
from fastapi import FastAPI
import uvicorn
app = FastAPI()
@app.get("/")
def root():
return {"msg": "Hello World"}
uvicorn.run(app, host="0.0.0.0", port=8080)
Dockerfile para generar las imágenes Docker
En nuestro repo también hemos creado un fichero llamado "Dockerfile" que contiene las instrucciones para generar la imagen que vamos a desplegar en Kubernetes:
FROM python:3.9
RUN mkdir /code
WORKDIR /code
ADD requirements.txt /code/requirements.txt
ADD main.py /code/main.py
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
CMD ["python", "main.py"]
Chart Helm para el despliegue
También nos falta una carpeta llamada helm que va a contener todos los ficheros con nuestro chart para desplegar el repo:
/Helm/Chart.yaml (Fichero que describe nuestro chart)
name: my-super-app
version: 0.0.1
appVersion: "1.0"
description: Mi Super App
/helm/values.yaml(Valores o variables que configuran nuestro chart)
appname: test1
environment: dev
namespace: test1
image: test1
/helm/templates/deployment.yaml (Template Helm para configurar el despliegue)
apiVersion: apps/v1
kind: Deployment
metadata:
name: {{ $.Values.appname }}-{{ $.Values.environment }}-deployment
namespace: {{ $.Values.namespace }}
spec:
selector:
matchLabels:
name: {{ $.Values.appname }}-{{ $.Values.environment }}-pod
template:
metadata:
namespace: {{ $.Values.namespace }}
labels:
name: {{ $.Values.appname }}-{{ $.Values.environment }}-pod
spec:
containers:
- name: pod
image: {{ $.Values.image }}
imagePullSecrets:
- name: regcred
/helm/templates/service.yaml (Template Helm para configurar el servicio)
apiVersion: v1
kind: Service
metadata:
name: {{ $.Values.appname }}-service
namespace: {{ $.Values.namespace }}
spec:
type: LoadBalancer
selector:
name: {{ $.Values.appname }}-{{ $.Values.environment }}-pod
ports:
- protocol: TCP
port: 8080
targetPort: 8080
Configurar el Pipeline de despliegue
Esto es quizás lo mas importante del vídeo, en Gitlab CICD, el pipeline de despliegue viene configurado en un fichero en el propio repo llamado ".gitlab-ci.yml". En nuestro caso nos hemos configurado un pipeline con cuatro stages "version", "tests", "build image" y "deploy":
variables:
KUBERNETES_NAMESPACE: test1
DOCKER_REGISTRY_HOST: https://index.docker.io/v1/
DOCKER_REGISTRY_USER: acoronadoc
DOCKER_REGISTRY_PASSWORD: dckr_pat_GegpqyANy7NViEmIDjNQATERUmM
default:
tags:
- kubernetes
image: alpine:latest
stages:
- version
- test
- build-image
- deploy
version:
stage: version
script:
- echo "VERSION=$(date +%Y%m%d%H%M%S%N)-$CI_COMMIT_BRANCH" >> build.env
artifacts:
reports:
dotenv: build.env
test:
stage: test
script:
- echo "Testing..."
buid-image:
stage: build-image
image:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- pwd
- ls
- mkdir -p /kaniko/.docker
- |
echo "{ \"auths\": { \"$DOCKER_REGISTRY_HOST\": { \"auth\": \"$(echo -n $DOCKER_REGISTRY_USER:$DOCKER_REGISTRY_PASSWORD | base64 -w0)\" } } }" > /kaniko/.docker/config.json
- cat /kaniko/.docker/config.json
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination=$DOCKER_REGISTRY_USER/$CI_PROJECT_NAME:$VERSION
deploy:
stage: deploy
script:
- apk add kubectl helm
- cd helm
- kubectl create namespace $KUBERNETES_NAMESPACE --dry-run=client -o yaml | kubectl apply -f -
- kubectl create secret docker-registry regcred -n $KUBERNETES_NAMESPACE --docker-server=$DOCKER_REGISTRY_HOST --docker-username=$DOCKER_REGISTRY_USER --docker-password=$DOCKER_REGISTRY_PASSWORD --dry-run=client -o yaml | kubectl apply -f -
- helm upgrade --install $CI_PROJECT_NAME . --set image=$DOCKER_REGISTRY_USER/$CI_PROJECT_NAME:$VERSION
Si lo analizáis bien veréis que tenemos un script por stage, en el vídeo los he comentado.
Subir código y ver como se despliega
Con todo ahora ya solo nos queda añadir todos los ficheros al repo(Cuidado porque el .gitlab-ci.yml se tiene que especificar al ser oculto), hacer el commit y hacer el push para ver los resultados:
git add *
git add .gitlab-ci.yml
git commit -m "Primer commit"
git push
Y el resto ya es historia :D
Posted on April 18, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.