Tabla de Contenidos

Docker a fondo e Introducción a Kubernetes: aplicaciones basadas en contenedores

Notas del curso Docker a fondo e Introducción a Kubernetes: aplicaciones basadas en contenedores del Clúster TIC de Galicia.

Objetivos

En este curso aprenderás a dominar Docker desde cero hasta el nivel avanzado, y tendrás las nociones necesarias para empezar a trabajar con Kubernetes.

En la primera parte del curso, aprenderás los conceptos básicos de Docker, como qué es y cómo funciona, además de conocer los usos que tiene y lo que es un contenedor y una imagen. Aprenderás a gestionar imágenes y contenedores, cómo crear imágenes y subirlas a un repositorio.

En la segunda parte, abordaremos escenarios multicontenedor con herramientas como docker-compose, además de aprender a trabajar con networking en Docker y cómo usar contextos.

La tercera parte se centra en escenarios avanzados para, por ejemplo, usar Docker para compilar y crear imágenes sin tener que instalar el framework (como .NET o Node) o cómo depurar utilizando contenedores. También sabrás cómo sacar partido a la tecnología de contenedores en entornos Windows y no solo Linux.

En la última parte del curso abordaremos Kubernetes, el orquestador de contenedores más utilizado del mundo. Aprenderás los conceptos básicos de Kubernetes, cómo funciona, cómo crear y configurar pods, cómo desplegar aplicaciones sin estado y cómo exponer tu aplicación al exterior.

Con este curso, tendrás las habilidades necesarias para crear y desplegar aplicaciones en contenedores con Docker y Kubernetes, y estarás preparado para aplicar tus conocimientos en proyectos reales tanto para desarrollo como para despliegue de sistemas.

Temario

Preguntas

Conceptos básicos de Docker

¿Qué diferencia hay entre las instrucciones ARG y ENV del Dockerfile?

¿Qué diferencias hay entre bind mounts y volúmenes?

Dadas las siguientes instrucciones del Dockerfile:

CMD node server.js

y

CMD ["node", "server.js"]

Marca todas las opciones correctas.

La imagen “my-image” recibe peticiones por su puerto 5000. Creas un contenedor usando: docker run -p 5000:5000 my-image ¿Cómo envías peticiones al contenedor?

Tienes un contenedor de MySql. Te das cuenta de que cada vez que lo pones en marcha (con docker run), los datos de la base de datos se han perdido. ¿Qué soluciones hay? (Marca todas las correctas)

¿Qué relación hay entre contenedores y microservicios?

¿Qué relación hay entre imagen y contenedor?

Marca todas las respuestas correctas referentes a los bind mounts:

Desde un terminal ejecutas tres veces el comando “docker run -d hello-world”. ¿Qué es lo que ocurre?

¿Qué hace la sentencia FROM del Dockerfile?

Escenarios multicontenedor

¿Qué pasa si usas el comando?: docker compose up -f compose-file1.yml -f compose-file2.yml

¿Es buena idea que la configuración forme parte de la imagen?

Para configurar compose usamos:

Tienes el siguiente fichero compose:

version: '3.4'
services:
  helloworld:
    image: dockercampusmvp/go-hello-world
    ports:
     - "80:80"
  db:
    image: mysql
    profiles:
     - db

Si lo ejecutas con docker compose --profile db up, ¿Cuántos contenedores se crean?

Tienes un contenedor que:

Abres un navegador y verificas que http://localhost:8000 funciona correctamente. No obstante hay otro contenedor ejecutándose que se intenta conectar con este. Cuando intenta usar http://localhost:8000 da error. Ambos contenedores se han lanzado usando docker compose up.

¿Qué URL se podría usar para que el segundo contenedor se pueda comunicar con el primero?

Conceptos avanzados de Docker

Un desarrollador te pasa un fichero en Kotlin. No tienes SDK ni runtime de Kotlin en tu máquina, pero necesitas ejecutar el proyecto. ¿Qué harías?

Un desarrollador está haciendo pruebas con Docker y ha ejecutado el comando docker build -t myimage .. El Dockerfile es muy sencillo, simplemente contenía una sentencia FROM, pero la construcción de la imagen ha tardado mucho tiempo. El desarrollador ya tenía la imagen base descargada. ¿Qué otra posible causa puede haber?

Tienes un servidor de docker, pero quieres controlarlo desde tus ordenadores. ¿Cómo puedes hacerlo, de la forma más sencilla?

¿Es admisible que un contenedor use stdout para lanzar trazas?

¿Cuál es la mejor manera de pasarle un fichero de configuración a un contenedor? Sin este fichero el contenedor da error nada más arrancar

Tienes un StatefulSet con 3 pods. El StatefulSet define una plantilla de PVC. Luego se desescala el StatefulSeta dos pods. ¿Qué ocurre con el PVC del tercer pod, con la configuración por defecto del StatefulSet?

Un desarrollador necesita conectarse a la BBDD de desarrollo, pero no conoce la cadena de conexión. Actualmente hay un pod que usa dicha cadena de conexión. El YAML del pod es como sigue:

apiVersion: v1
kind: Pod
metadata:
  name: client
spec:
  containers:
  - name: app
    image: acme-org/client:v1
    env:
    - name: DB_CONSTR
      valueFrom:
      - secretKeyRef:
        name: db-data
        key: constr

¿Qué le indicas al desarrollador para que pueda ver la cadena de conexión?

¿Todos los pods de un StatefulSet tienen que tener la misma plantilla?

¿Es posible configurar un StatefulSet para que elimine un PVC cuando el StatefulSet es desescalado?

¿Puedes exponer MySQL a través de NodePort?

Resumen comandos

Listado de todos los comandos que salen en el curso:

Listado de ficheros:

Dockerfile:

Dockerfile multi-stage:

FROM golang:1.9-stretch
WORKDIR /go/src/hello_world
COPY hello_world.go .
RUN go build

FROM ubuntu:latest
WORKDIR /root/
COPY --from=0 /go/src/hello_world .
ENTRYPOINT ["/root/hello_world"]

Docker Compose:

version: '3'

services:
  sample-api:
    image: dockercampusmvp/nodejs-sample
    ports:
      - "9000:3000"
    environment:
      - PORT=80
    volumes:
      - /home/data:/var/lib/data
      - data-volume:/var/lib/data
      - data-volume2:/tmp
    profiles: ["backend"]

volumes:
  data-volume:
  
volumes:
  data-volume2:
     external: true

Multicontenedor:

version: '3'

services:
  client:
    image: dockercampusmvp/nodejs-sample
  server:
    image: dockercampusmvp/nodejs-sample

Construir imágenes con Docker Compose:

version: '3'

services:
  sample-api:
    image: dockercampusmvp/nodejs-sample:dev
    build:
      context: ./nodejs-sample
      dockerfile: Dockerfile

Kubernetes:

Creación de un pod de forma declarativa:

apiVersion: v1
kind: Pod
metadata:
  labels:
    run: hello-declarative
  name: hello-declarative
spec:
  containers:
  - image: dockercampusmvp/go-hello-world
    name: hello

Creación de pod de forma declarativa indicando variables de entorno:

apiVersion: v1
kind: Pod
metadata:
  name: mypod
spec:
  containers:
  - image: imagenAUsar
    name: ctr
    env:
    - name: FooVar
      value: FooValue
    - name: BarVar
      value: BarValue

Creando un servicio de forma declarativa:

apiVersion: v1
kind: Service
metadata:
  name: hello-svc-declarative
spec:
  ports:
  - port: 8080
    targetPort: 80
    name: http
    protocol: TCP
  - port: 9000
    targetPort: 3000
    name: metrics
  selector:
    run: hello-declarative

ReplicaSet:

apiVersion: apps/v1
kind: ReplicaSet
metadata:
  name: hello
spec:
  replicas: 1
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: dockercampusmvp/go-hello-world

Deployment:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: hello
spec:
  strategy:
    type: rollingUpdate
    rollingUpdate:
      maxSurge: 1               # El valor por defecto es 25%
      maxUnavailable: 20%       # El valor por defecto es 25%
  replicas: 5
  selector:
    matchLabels:
      app: hello
  template:
    metadata:
      labels:
        app: hello
    spec:
      containers:
      - name: hello
        image: dockercampusmvp/go-hello-world

ConfigMap:

apiVersion: v1
kind: ConfigMap
metadata:
  name: staging
data:
  api_url: https://api_service
  environment_name: staging

Config Map que contiene un fichero:

apiVersion: v1
kind: ConfigMap
metadata:
  name: config_staging
data:
  config.json: |
    {
        "environment": "staging",
        "api": {
            "server": "https://myapi"
        }
    }

Definición de volúmenes de pod:

spec:
  volumes:
  - name: shared
    emptyDir: {}

Montaje de un Config Map en un contenedor de un pod:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: demovol
spec:
  selector:
    matchLabels:
      app: demovol
  template:
    metadata:
      labels:
        app: demovol
    spec:
      # Definimos el volumen
      volumes:
      - name: cfg
        configMap:
          name: config_staging  # Nombre del configmap
      containers:
      - name: demo
        image: dockercampusmvp/go-hello-world
        # Montamos el volumen
        volumeMounts:
        - name: cfg     # Nombre del volumen
          mountPath: /app/config

Secretos:

apiVersion: v1
data:
  password: VmVyeUNvbXBsZXhQYXNzdzByZA==
kind: Secret
metadata:
  name: mysecret

Comando inicial de un contenedor:

apiVersion: v1
kind: Pod
metadata:
  name: command
spec:
  containers:
  - name: main
    image: alpine
    command:
    - /bin/ls
  restartPolicy: Never

Servicio NodePort:

apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  selector:
    app: hello
  ports:
  - name: http
    port: 8080
    targetPort: http
  type: NodePort

Servicio LoadBalancer:

apiVersion: v1
kind: Service
metadata:
  name: hello
spec:
  selector:
    app: hello
  ports:
  - name: http
    port: 8080
    targetPort: http
  type: LoadBalancer

Recurso Ingress:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  labels:
    app: demo-ingress
  name: demo-ingress
  annotations:
    ingress.kubernetes.io/ssl-redirect: "false"
spec:
  rules:
  - http:
      paths:
      - path: /api
        pathType: ImplementationSpecific
        backend:
          service:
            name: apisvc
            port:
              number: 80
      - path: /hello
        pathType: ImplementationSpecific
        backend:
          service:
            name: hellosvc
            port:
              number: 80

Recurso Ingress con hosts virtuales:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: hello-ingress
spec:
  rules:
  - host: dev.campusmvp.es
    http:
      paths:
      - pathType: Prefix
        path: "/api"
        backend:
          service:
            name: hellosvc
            port:
              name: http