Aller au contenu

Comment créer votre lab K3S sur Google Cloud ?

Un guide pratique pour construire un laboratoire K3S sur Google Cloud, vous permettant de travailler sur Kubernetes de façon simple et épurée.

Un cluster K3S sur Google Cloud

Si vous étudiez Kubernetes, cela peut vous intéresser d’avoir un petit laboratoire pour expérimenter les concepts que vous avez appris.

Vous pouvez utiliser une solution locale, comme Kind, rapide et facile à manipuler, mais il peut également être intéressant pour vous de le faire fonctionner sur un fournisseur Cloud, plus proche d’un contexte de production.

Choix techniques

Google Cloud

Google Cloud est le seul, parmi les principaux fournisseurs, à offrir un Free Tier à vie pour certaines ressources, à moins que les conditions du contrat ne changent, ce qui pourrait arriver un jour, mais dont nous pouvons profiter pour l’instant.

Parmi les services éligibles au Free Tier, voici ceux qui pourraient nous intéresser :

  • Compute Engine : 1 VM instance non-preemptible e2-micro en Caroline du Sud (us-east1), la région US la plus proche de la France
  • Cloud Storage : 5 GB de stockage régional (us-east1) par mois, qui est une capacité de stockage plus que suffisante pour un homelab
  • Artifact Registry : 0.5 GB de stockage par mois
  • Secret Manager : 6 versions actives de secrets par mois
  • Cloud Logging : Allocation mensuelle gratuite de logging
  • Cloud Monitoring : Allocation mensuelle gratuite de métriques
  • Cloud Shell : Accès gratuit à Cloud Shell, incluant 5 GB de stockage de disque persistant

Il y a 3 régions éligibles pour ce Free Tier, mais habitant en France, j’ai choisi celle qui est la plus proche géographiquement : us-east1.

Dans tous les cas, les prix sont intéressants chez ce fournisseur, nous verrons que nous pouvons aussi utiliser les instances spot pour réduire les coûts.

Ce laboratoire sera aussi l'occasion d'améliorer vos compétences sur Google Cloud.

K3S

K3S est une distribution Kubernetes créée par Rancher, conçue pour être aussi légère que possible tout en étant compatible avec les standards de production Kubernetes.

C'est l'outil parfait pour notre laboratoire, car nous voulons avoir les instances les plus légères possibles pour maintenir les coûts bas, tout en ayant la capacité de tester des concepts de production.

Même GKE (Google Kubernetes Engine) vous coûtera au moins environ 70 $ par mois.

Avec K3S vous pouvez simplement configurer une instance e2-micro, avec le Free-Tier (mais ce sera très limité).

Si vous voulez plus de capacité, vous pouvez créer une instance e2-small, qui vous coûtera moins de 10 $ par mois (avec une instance spot).

K3S restera intéressant pour tous les cas d'usage où vous voulez réduire la taille des instances.

OpenTofu

Si vous n'avez pas suivi l'actualité, Hashicorp a changé la licence de Terraform et a eu quelques problèmes avec la communauté.

OpenTofu est un fork de Terraform, visant à rester open-source et dont le changelog est piloté par la communauté.

Actuellement, à la version 1.8, OpenTofu est sensiblement identique à Terraform, il suffit de changer le binaire, vous pouvez l'installer avec Tenv ou Mise (les différences apparaîtront au fur et à mesure que les feuilles de route respectives évolueront).

Ce laboratoire est donc l'occasion de vous familiariser avec l'utilisation de cet outil.

Mettre en place le lab

Maintenant nous allons voir comment implémenter cette solution, le cas d'usage est de déployer les ressources sur Google Cloud, créer le cluster K3S sur la VM, puis créer une API accessible depuis l'extérieur.

Créer le projet Google Cloud

D'abord, si vous ne l'avez pas déjà fait, vous devez créer un projet Google Cloud.

À la création du projet, vous pouvez activer une offre d'essai gratuit de 90 jours à 300 $, sur un large panel de services, avec quelques limitations cependant.

Après la fin de cette période, vous serez toujours éligible au free tier que nous avons déjà mentionné.

Déploiement de l’infrastructure

Voici les ressources que vous allez créer avec OpenTofu :

// Compute
// ----------------------------------

// The instance for K3S
resource "google_compute_instance" "k3s" {
  name         = "k3s-vm-1"
  machine_type = "e2-small" # This instance will have 2 Gb of RAM
  zone         = var.zone

  tags = ["web"]

  // Set the boot disk and the image (10 Gb)
  boot_disk {
    initialize_params {
      image = "debian-cloud/debian-12"
      size  = 10
    }
  }

	// Configuration to be a Spot Instance, to reduce costs
  scheduling {
    preemptible                 = false
    automatic_restart           = true
    provisioning_model          = "SPOT"
    instance_termination_action = "STOP"
  }

  // attach a disk for K3S
  attached_disk {
    source      = google_compute_disk.k3s_disk.id
    device_name = "k3s-disk"
  }

  network_interface {
    network = "default"

    access_config {
      // Ephemeral public IP
    }
  }

  labels = {
    env       = var.env
    region    = var.region
    app       = var.app_name
    sensitive = "false"
  }

  metadata_startup_script   = file("scripts/k3s-vm-startup.sh")
  allow_stopping_for_update = true
}

// Firewall
// ----------------------------------
resource "google_compute_firewall" "allow_http" {
  name    = "allow-http"
  network = "default"

  allow {
    protocol = "tcp"
    ports    = [
      "80", "443", // http/https
      "30080"      // ports opened to access the API via NodePort
    ]
  }

  source_ranges = ["0.0.0.0/0"]
  target_tags   = ["web"]
}

// Storage
// ----------------------------------

// The disk attached to the instance (15 Gb)
resource "google_compute_disk" "k3s_disk" {
  name = "k3s-disk"
  size = 15
  type = "pd-standard"
  zone = var.zone
}

// The bucket where you can store other data
resource "google_storage_bucket" "k3s-storage" {
  name     = var.bucket_name
  location = var.region

  labels = {
    env       = var.env
    region    = var.region
    app       = var.app_name
    sensitive = "false"
  }
}

// Registry
// ----------------------------------

// The Artifact Registry repository for our app
resource "google_artifact_registry_repository" "app-repo" {
  location      = "us-east1"
  repository_id = "app-repo"
  description   = "App Docker repository"
  format        = "DOCKER"

  docker_config {
    immutable_tags = true
  }
}

// Env vars
// ----------------------------------

variable "env" {
  type        = string
  default     = "dev"
  description = "Environment"
}

variable "region" {
  type        = string
  default     = "us-east1"
  description = "GCP Region"
}

variable "zone" {
  type        = string
  default     = "us-east1-a"
  description = "GCP Zone"
}

variable "app_name" {
  type        = string
  default     = "<name-of-your-cluster>"
  description = "Application name"
}

variable "project_name" {
  type        = string
  default     = "<name-of-your-gcp-project>"
  description = "GCP Project name"
}

variable "bucket_name" {
  type        = string
  default     = "<name-of-your-bucket>"
  description = "Bucket name"
}

// Provider
// ----------------------------------

// Connect to the GCP project
provider "google" {
  credentials = file("<my-gcp-creds>.json")
  project     = var.project_name
  region      = var.region
  zone        = var.zone
}

terraform {
  # Use a shared bucket (wich allows collaborative work)
  backend "gcs" {
    bucket      = "<my-bucket-for-states>"
    prefix      = "k3s-infra"
  }

  // Set versions
  required_version = ">=1.8.0"
  required_providers {
    google = {
      source  = "hashicorp/google"
      version = ">=4.0.0"
    }
  }
}

Le script de démarrage (dans "scripts/k3s-vm-startup.sh"), qui installera K3S automatiquement :

#!/bin/bash

# Format the disk if not already formatted
if ! lsblk | grep -q "/mnt/disks/k3s"; then
    mkfs.ext4 -m 0 -F -E lazy_itable_init=0,lazy_journal_init=0,discard /dev/disk/by-id/google-k3s-disk
    mkdir -p /mnt/disks/k3s
    mount -o discard,defaults /dev/disk/by-id/google-k3s-disk /mnt/disks/k3s
    chmod a+w /mnt/disks/k3s
fi

# ensure only run once
if [[ -f /etc/startup_was_launched ]]; then exit 0; fi
touch /etc/startup_was_launched

# apt install
apt update
apt install -y ncdu htop

# helm install
curl -fsSL -o get_helm.sh <https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3>
chmod 700 get_helm.sh
/bin/bash get_helm.sh

# bashrc config
rc=/root/.bashrc
echo "alias l='ls -lah'" >> $rc
echo "alias ll='ls -lh'" >> $rc
echo "alias k=kubectl" >> $rc
echo "export dry='--dry-run=client'" >> $rc
echo "export o='-oyaml'" >> $rc

# Install k3s and configure it to use the persistent disk for data storage
curl -sfL <https://get.k3s.io> | INSTALL_K3S_EXEC="--data-dir /mnt/disks/k3s" sh -

Créez un ServiceAccount pour se connecter à GCP avec OpenTofu, restreint à ces services :

(Pour la conformité aux privilèges minimaux, vous devez restreindre les éléments avec les conditions IAM)

  • Artifact Registry Administrator
  • Artifact Registry Repository Administrator
  • Cloud Functions Admin
  • Compute Admin
  • Compute Instance Admin
  • Compute Network Admin
  • Compute Security Admin
  • Secret Manager Admin
  • Service Account User
  • Storage Admin

Ensuite, les commandes pour créer l'infrastructure :

# OpenTofu setup
tofu init -get=true -upgrade
tofu workspace new dev
tofu workspace select dev

# Plan (to preview what will be changed)
tofu plan

# Apply (to create the infrastructure described in the IaC code)
tofu apply

Construire l’appli

Créez un Dockerfile pour votre application, nous utiliserons l'API HttpBin qui permet de tester toutes les requêtes que nous pouvons faire à une API Rest :

FROM python:3.12-slim

# Install dependencies
RUN pip install --no-cache-dir gunicorn httpbin

# Expose the application port
EXPOSE 80

# Launch the application
CMD ["gunicorn", "-b", "0.0.0.0:80", "httpbin:app"]

Faites le build et poussez l'image vers Artifact Registry :

# Build
docker build -t httpbin .

# Push to the registry
gcloud auth configure-docker us-east1-docker.pkg.dev
docker tag httpbin us-east1-docker.pkg.dev/<my-project>/app-repo/httpbin:v1
docker push us-east1-docker.pkg.dev/<my-project>/app-repo/httpbin:v1

Créer le Secret pour se connecter au registry

Créez un Service Account, avec le bon rôle "Artifact Registry Reader".

Cela vous permettra de pull l'image depuis Kubernetes.

D'abord, se connecter à votre instance :

gcloud compute ssh --zone "us-east1-a" "k3s-vm-1" --project "<my-project>”

Ensuite, stocker les identifiants sur Kubernetes comme ceci :

echo -n "<json_value>" > registry_key.json

k create secret docker-registry artifact-read \\
    --docker-server=us-east1-docker.pkg.dev \\
    --docker-username=_json_key \\
    --docker-password="$(cat registry_key.json)" \\
    --docker-email=valid-email@example.com

Deployer l’appli on Kubernetes

Voici le code pour déployer votre application :

# The deployment that will pull the image on Artifact Registry
apiVersion: apps/v1
kind: Deployment
metadata:
  name: httpbin
spec:
  replicas: 1
  selector:
    matchLabels:
      app: httpbin
  template:
    metadata:
      labels:
        app: httpbin
    spec:
      imagePullSecrets:
      - name: artifact-read
      containers:
      - name: httpbin
        image: us-east1-docker.pkg.dev/<my-project>/app-repo/httpbin:v1
        ports:
        - containerPort: 80
---
# The Service, configured as a NodePort to allow external access on some ports
apiVersion: v1
kind: Service
metadata:
  name: httpbin-service
spec:
  type: NodePort
  ports:
    - port: 80
      nodePort: 30080
  selector:
    app: httpbin

Ensuite pour lancer le déploiement sur Kubernetes :

# deploy the app
k apply -f httpbin.yaml

# Ensure the app is running
k get po -w

Accéder à l’API

Une fois qu'elle est au statut “running”, vous pouvez accéder à l'API à l'intérieur de la VM :

curl -I <http://localhost:30080/get>
# HTTP/1.1 200 OK

Ou à l'extérieur, depuis votre machine locale :

curl -I http://<my-ephemeral-ip>:30080/get
# HTTP/1.1 200 OK

Conclusion

Nous venons de voir comment déployer rapidement et avec des coûts minimaux un Kubernetes moderne et léger sur votre projet Google Cloud.

D’autres ateliers permettront d’enrichir ce laboratoire :

  • Ajouter un reverse proxy Traefik pour accéder à plusieurs applications
  • Ajouter Cloudflare pour protéger les URLs exposées
  • Mettre en place un pattern multi-masters, multi-workers
  • Définir une stratégie de Sauvegarde/Restauration
  • Configurer Cilium
  • Ajouter des outils de monitoring et d'alerting

Et bien d'autres choses…

Des sujets qui, je l’espère, feront l’objet de prochains articles sur le site média tech de référence : sfeir.dev !

Dernier