# 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:

    ```powershell
    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:

    ```python
    print("Hello, Docker!")
    ```

- Create a Dockerfile in the same directory with the following content:

    ```dockerfile
    FROM python:3.9-slim
    COPY app.py /app.py
    CMD ["python", "/app.py"]
    ```

- Build the Docker image:

    ```powershell
    docker build -t hello-docker .
    ```

- Run the Docker image:

    ```powershell
    docker run hello-docker
    ```

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

    ```powershell
    docker run -v /path/on/host:/path/in/container hello-docker
    ```

- You can also run a container in the background using the `-d` flag:

    ```powershell
    docker run -d hello-docker
    ```

- To see the running containers, use the following command:

    ```powershell
    docker ps
    ```

- To stop a running container, use the following command:

    ```powershell
    docker stop <container_id>
    ```

- To remove a container, use the following command:

    ```powershell
    docker rm <container_id>
    ```

- To remove an image, use the following command:

    ```powershell
    docker rmi <image_id>
    ```

- To see the images, use the following command:

    ```powershell
    docker images
    ```

- To see the logs of a container, use the following command:

    ```powershell
    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`

   ```python
    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`

    ```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:

    ```powershell
    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:

    ```powershell
    docker buildx build --platform linux/amd64 -t flask-app-image .

5. Run the Docker container:

    ```powershell
    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):

    ```powershell
    docker tag flask-app-image albughdadim/flask-app-image
    docker push albughdadim/flask-app-image
    ```

7. Create a Kubernetes deployment file:

    `deployment.yaml`

```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

```

8. Create a Kubernetes service file:
    `service.yaml`

```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


```

9. Create an ingress file:

    `ingress.yaml`

```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

```

10. Download the `kubeconfig` file
11. Set the `KUBECONFIG` environment variable to point to your kubeconfig file:

    ```powershell
    export KUBECONFIG="/path/to/your/kubeconfig"
    ```

12. Check contexts to ensure you are connected to the correct cluster:

    ```powershell
    kubectl config get-contexts
    ```

    If needed, switch to the desired context:

    ```powershell
    kubectl config use-context ecmwf-meditwin
    ```

13. Create a namespace for your application:

    ```powershell
    kubectl create namespace flask-app-ns
    ```

14. Apply the deployment, service and ingress files:

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