Day 3 - Part 2 - Introduction to Containerization, Docker and Kubernetes

Day 3 - Part 2 - Introduction to Containerization, Docker and Kubernetes#

Containerization#

A lightweight form of virtualization that packages an application and its dependencies into a single container. This ensures consistent behavior across environments.

Key Concepts#

Containers vs Virtual Machines#

Feature

Virtual Machines (VMs)

Containers

OS

Each VM runs its own OS

Containers share the host OS kernel

Size

VMs are large and resource-intensive

Containers are lightweight and fast

Isolation

Each VM is isolated from others

Containers run in isolated environments

Reproducibility

VMs can be inconsistent across environments

Containers are consistent across environments

Portability

VMs are less portable

Containers can run anywhere

Performance

VMs have overhead due to full OS

Containers have less overhead

Deployment

VMs require more resources for deployment

Containers are easier to deploy

Speed

VMs take longer to start up

Containers start almost instantly

Resource Usage

VMs consume more resources

Containers use fewer resources

Management

VMs require complex management

Containers are easier to manage

Networking

VMs have complex networking

Containers have simpler networking

Scalability

VMs are harder to scale

Containers are easier to scale

Docker#

A platform to develop, ship and run applications in containers.

Key Concepts#

  • Docker Engine: Core service that creates and runs containers.

  • Docker CLI: Command-line tool to interact with Docker.

  • Dockerfile: Script defining how to build a Docker image.

  • Docker Image: Immutable snapshot created from a Dockerfile.

  • Docker Container: Runtime instance of an image.

  • Docker Hub: Public registry for sharing images.

Docker in Action#

  • Run your first Docker container using the Docker CLI:

    docker run hello-world
    

    This command pulls the hello-world image from Docker Hub and runs it in a container. If you see a “Hello from Docker!” message, Docker is installed correctly.

  • Create a Docker image. To do that, first create a file named app.py with the following content:

    print("Hello, Docker!")
    
  • Create a Dockerfile in the same directory with the following content:

    FROM python:3.9-slim
    COPY app.py /app.py
    CMD ["python", "/app.py"]
    
  • Build the Docker image:

    docker build -t hello-docker .
    
  • Run the Docker image:

    docker run hello-docker
    
  • You can share a volume between the host and the container. To do that, run the following command:

    docker run -v /path/on/host:/path/in/container hello-docker
    
  • You can also run a container in the background using the -d flag:

    docker run -d hello-docker
    
  • To see the running containers, use the following command:

    docker ps
    
  • To stop a running container, use the following command:

    docker stop <container_id>
    
  • To remove a container, use the following command:

    docker rm <container_id>
    
  • To remove an image, use the following command:

    docker rmi <image_id>
    
  • To see the images, use the following command:

    docker images
    
  • To see the logs of a container, use the following command:

    docker logs <container_id>
    

Kubernetes#

A container orchestration platform that automates the deployment, scaling and management of containerized applications.

  • Scaling: Automatically scale up/down application instances based on demand.

  • Self-Healing: Automatically restarts containers that fail, replaces containers, and kills containers that don’t respond to health checks.

  • Service Discovery and Load Balancing: Exposes services and distributes traffic.

  • Automated Rollouts and Rollbacks: Gradually rolls out changes and reverts if something goes wrong.

  • Secret and Configuration Management: Manage sensitive data and application configuration separately from code.

  • Resource Efficiency: Efficiently packs containers onto nodes based on resource requirements and availability.

Key Concepts#

  • Pod: The smallest deployable unit in K8s; wraps one or more containers.

  • Node: A worker machine in the cluster (virtual or physical).

  • Cluster: A group of nodes managed by Kubernetes.

  • Deployment: Defines the desired state for pods and handles updates.

  • Service: An abstraction to expose pods to the network.

  • Ingress: Manages external access to services, usually via HTTP.

  • ConfigMap: Stores non-sensitive configuration data.

  • Secret: Stores sensitive data like passwords or keys.

  • Namespace: Provides a scope for names, allowing segregation of cluster resources.

Practical Exercise#

  1. Create a simple app as follows:

    app.py

     from flask import Flask
    
     app = Flask(__name__)
    
     @app.route('/')
     def hello():
         return "Hello from Docker!"
    
     if __name__ == '__main__':
         app.run(host='0.0.0.0', port=5000)
    
  2. Create a requirements file:

    requirements.txt

     flask
    
  3. Create a Dockerfile:

    Dockerfile

    FROM python:3.9-slim
    
    WORKDIR /app
    
    COPY requirements.txt .
    
    RUN pip install -r requirements.txt
    
    COPY app.py .
    
    CMD ["python", "app.py"]
    
  4. Build the Docker image:

    docker build -t flask-app-image .
    

    [!WARNING] If you are not using Linux, you may need to use docker buildx to build the image. You can do this by running the following command:

    docker buildx build --platform linux/amd64 -t flask-app-image .
    
  5. Run the Docker container:

    docker run -d -p 8000:5000 flask-app-image
    
  6. Access the app in your browser at http://localhost:8000.

  7. Push the Docker image to a registry (e.g., Docker Hub):

    docker tag flask-app-image albughdadim/flask-app-image
    docker push albughdadim/flask-app-image
    
  8. Create a Kubernetes deployment file:

    deployment.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: flask-app-deployment
  namespace: flask-app-ns
spec:
  replicas: 2
  selector:
    matchLabels:
      app: flask-app-deployment
  template:
    metadata:
      labels:
        app: flask-app-deployment
    spec:
      containers:
        - name: flask-container
          image: albughdadim/flask-app-image:latest
          ports:
            - containerPort: 5000
  1. Create a Kubernetes service file: service.yaml

apiVersion: v1
kind: Service
metadata:
  name: flask-service
  namespace: flask-app-ns
spec:
  type: ClusterIP
  selector:
    app: flask-app-deployment
  ports:
    - protocol: TCP
      port: 80
      targetPort: 5000
  1. Create an ingress file:

    ingress.yaml

apiVersion: networking.k8s.io/v1

kind: Ingress
metadata:
  name: flask-ingress
  namespace: flask-app-ns
  annotations:
    cert-manager.io/cluster-issuer: ecmwf-meditwin-issuer
    nginx.ingress.kubernetes.io/backend-protocol: HTTP
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/proxy-body-size: "500m"
    external-dns.alpha.kubernetes.io/hostname: moflask.internal.meditwin-project.eu
spec:
  rules:
    - host: moflask.internal.meditwin-project.eu
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: flask-service
                port:
                  number: 80
  tls:
    - hosts:
        - moflask.internal.meditwin-project.eu
      secretName: flask-tls
  1. Download the kubeconfig file

  2. Set the KUBECONFIG environment variable to point to your kubeconfig file:

    export KUBECONFIG="/path/to/your/kubeconfig"
    
  3. Check contexts to ensure you are connected to the correct cluster:

    kubectl config get-contexts
    

    If needed, switch to the desired context:

    kubectl config use-context ecmwf-meditwin
    
  4. Create a namespace for your application:

    kubectl create namespace flask-app-ns
    
  5. Apply the deployment, service and ingress files:

    kubectl apply -f deployment.yaml
    kubectl apply -f service.yaml
    kubectl apply -f ingress.yaml