Qu'est-ce que K3s ?

k3s est une distribution Kubernetes légère qui est optimisée pour les périphériques périphériques. À mon avis, il est également parfait pour le développement local de vos microservices k8s.  Mais est-il vraiment léger?  OUI! Les gens de rancher labs ont fait un excellent travail.

Ils ont supprimé toutes les fonctionnalités inutiles de k8s qui ne sont pas nécessaires par défaut pour le développement local et les cas marginaux. Cela signifie que les fonctionnalités suivantes sont supprimées:  

  • Fonctionnalités héritées et non par défaut
  • Fonctionnalités Alpha - Fonctionnalités actuelles
  • Fournisseurs de cloud dans l'arborescence
  • Pilotes de stockage dans l'arborescence
  • Docker (facultatif)

Dans la plupart des cas, vous disposez des fonctionnalités v1 (Déploiements, Services) et n'avez pas besoin des fonctionnalités alpha fournies par k8s. Ils ont également ajouté/modifié certaines fonctionnalités de k8s :

  • combinaison parfaite avec k3d;)
  • remplacé l'etcd par une surcharge et pour la base de données, SQLite3 optimisée
  • Gestion TLS - certificats SSL auto-gen pour une communication sécurisée à l'échelle du cluster
  • Utilisation de Flannel et CoreDNS pour le réseau de cluster

K3s vous offre deux façons de configurer un cluster. Le premier est le bootstrap classique de votre cluster directement sur votre hôte. C'est comme si vous utilisiez kubeadm uniquement pour les k3. Cette "méthode" de bootstrap peut être intéressante pour le cluster de production k3s en combinaison avec ansible. Pour plus d'informations et un guide de démarrage rapide, suivez les instructions du repository github.

La deuxième façon de démarrer un cluster k3s est en combinaison avec docker-in-docker. Cela signifie que vous pouvez déployer un cluster k3s multi-nœuds directement sur votre hôte. Tous les nœuds k3s master/workers sont entièrement encapsulés dans leur propre conteneur Docker. N'est-ce pas cool?!

Dans mon voyage avec Kubernetes, j'ai toujours cherché une solution de développement local qui est rapide au démarrage et avec moins de frais généraux k8s dont je n'ai pas vraiment besoin. J'ai testé  Minikube et Kind. Les deux ont fonctionné comme souhaité mais ne répondaient pas à mes exigences pour une solution de développement local parfaite.  Pour amorcer un cluster k3s docker-in-docker (dind), k3s fournit quelques options de configuration qui rendront tout cela magique. Mais pas besoin, Rancger vous offrent une solution plus simplifiée. Je vous présente k3d.

k3d

k3d n'est pas une nouvelle solution complexe pour amorcer un cluster" dind" k3s. Il s'agit d'une interface de ligne de commande permettant à votre terminal de gérer le cluster "dind" k3s sur votre hôte.  Je vais vous montrer la puissance des k3 avec une démo. Cela comprend l'installation, l'amorçage d'un cluster avec 10 workers et le déploiement d'un serveur http minimal avec un service.

installer k3d

J'ai utilisé le script d'installation du repository github, qui va télécharger le binaire et le déplacer vers /usr/local/bin

$ wget -q -O - https://raw.githubusercontent.com/rancher/k3d/master/install.sh | bash

Sur MacOS via Homebrew :

brew install k3d

Vérifiez votre installation

$ k3d -version
k3d version v1.3.4

bootstrap un cluster de 10 workers

Ma machine locale a les spécifications suivantes:

  • CPU: 2,8 GHz Intel Core i7 quatre cœurs
  • MÉMOIRE: 16 Go

Bien sûr, dans la plupart des cas de test, vous n'avez pas besoin d'un cluster de 10 workers, mais pour la démo, je pense que c'est correct ;-).

$ k3d create --workers 10

qui devrait donner la chose suivante :

INFO[0000] Created cluster network with ID 88d54013ece9c947e4f9ff44e3c384dc3f1863f586ba5cbcd0d5294c8e61d563
INFO[0000] Created docker volume  k3d-k3s-default-images
INFO[0000] Creating cluster [k3s-default]
INFO[0000] Creating server using docker.io/rancher/k3s:v0.10.0...
INFO[0001] Booting 10 workers for cluster k3s-default
INFO[0001] Created worker with ID 4938b2e1a7484cf5bd25deba2e00e89208a634795480562aa725502d9cfc670c
INFO[0002] Created worker with ID 4c20854f872669d32e0fd779ffebebcb08c4a7c35694cace60442e4c78140217
INFO[0002] Created worker with ID 05a0fbfdd0e8f5782e1a101af488058c2fd720076cbc4e1f002b793d37d4ddbd
INFO[0003] Created worker with ID 077d3498eb89824c4a776aec04f548beaebe6a62f609570b78dd13dad7812594
INFO[0003] Created worker with ID e8c952304a5f86bc0bf078c884d23a20b0cd8e2c20d16cf4bda5ac1706cb7fe4
INFO[0004] Created worker with ID 1ff2d2ab2210ba51944d488f0715c680430cb2798682b3dc26d9344c593f9f1e
INFO[0004] Created worker with ID 48d250ac6f59d49068b38460cb938ed19b81849ee4b57d9802fc6f4017ed06eb
INFO[0005] Created worker with ID 5fcea4e4296cacdcaf530f4df487fbe84eeeda50042c502628535c299420bcf7
INFO[0006] Created worker with ID ea607418c345931e2962c79dcc262b6fb696203aa07b7c0c2725eaf4fd40265c
INFO[0006] Created worker with ID c65907ea3eb1f48520ff4f5f72087e7f730cff0c32ab0c3b809aa6b9f7eb692b
INFO[0006] SUCCESS: created cluster [k3s-default]
INFO[0006] You can now use the cluster with:

export KUBECONFIG="$(k3d get-kubeconfig --name='k3s-default')"
kubectl cluster-info

On dirait que notre cluster est provisionné. Voyons si c'est bien le cas :

MBP-de-admin:~ admin$ docker ps
CONTAINER ID        IMAGE                                 COMMAND                  CREATED              STATUS              PORTS                    NAMES
c65907ea3eb1        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-9
ea607418c345        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-8
5fcea4e4296c        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-7
48d250ac6f59        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-6
1ff2d2ab2210        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-5
e8c952304a5f        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-4
077d3498eb89        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-3
05a0fbfdd0e8        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-2
4c20854f8726        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-1
4938b2e1a748        rancher/k3s:v0.10.0                   "/bin/k3s agent"         About a minute ago   Up About a minute                            k3d-k3s-default-worker-0
5cf8ba35fdc8        rancher/k3s:v0.10.0                   "/bin/k3s server --h…"   About a minute ago   Up About a minute   0.0.0.0:6443->6443/tcp   k3d-k3s-default-server

D'accord, nous voyons qu'il y a 10 conteneurs. L'un d'eux expose le port par défaut de l'API k8s. Vérifions maintenant si nous pouvons communiquer avec l'API via kubectl. Mais nous avons d'abord besoin du kubeconfig.

$ export KUBECONFIG="$(k3d get-kubeconfig --name='k3s_default')"

Jetez un œil au kubeconfig, vous pouvez voir qu'il pointe vers localhost:6443. Directement vers le conteneur k3d-k3s_default-server.

cat $KUBECONFIG
apiVersion: v1
clusters:
- cluster:
    certificate-authority-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJWekNCL3FBREFnRUNBZ0VBTUFvR0NDcUdTTTQ5QkFNQ01DTXhJVEFmQmdOVkJBTU1HR3N6Y3kxelpYSjIKWlhJdFkyRkFNVFUzTnpVMU9EQXdNakFlRncweE9URXlNamd4T0RNek1qSmFGdzB5T1RFeU1qVXhPRE16TWpKYQpNQ014SVRBZkJnTlZCQU1NR0dzemN5MXpaWEoyWlhJdFkyRkFNVFUzTnpVMU9EQXdNakJaTUJNR0J5cUdTTTQ5CkFnRUdDQ3FHU000OUF3RUhBMElBQkNRYnZ2YS9EUGE4bTFibmRhNm0zTTdBWWE1MXVSZmRjTnZnOWJuTHlHOEcKOTZhTExaeitjMlVIaTRHNG41bGJPZzhieFRML3NQVit0TVdNdVBmMEIweWpJekFoTUE0R0ExVWREd0VCL3dRRQpBd0lDcERBUEJnTlZIUk1CQWY4RUJUQURBUUgvTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSUU4dmRyeVdEVTN0CjFNNGkwbjB4RFpkb2tnUzBFTnZEMWFRbTFuRm1GQkgyQWlFQXdBQ2xZNElJTkwxRWtQSm1yc2pOcTVzUzlHQlQKQzM1aTUvWFhiaStSMm5zPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==
    server: https://127.0.0.1:6443
  name: default
contexts:
- context:
    cluster: default
    user: default
  name: default
current-context: default
kind: Config
preferences: {}
users:
- name: default
  user:
    password: 4d193c4260e151357194cc7beb9bfa4c
    username: admin

Permet d'obtenir des informations via kubectl

MBP-de-admin:~ admin$ kubectl get nodes
NAME                       STATUS   ROLES    AGE   VERSION
k3d-k3s-default-worker-0   Ready    <none>   26m   v1.16.2-k3s.1
k3d-k3s-default-worker-4   Ready    <none>   26m   v1.16.2-k3s.1
k3d-k3s-default-worker-5   Ready    <none>   26m   v1.16.2-k3s.1
k3d-k3s-default-server     Ready    master   26m   v1.16.2-k3s.1
k3d-k3s-default-worker-8   Ready    <none>   26m   v1.16.2-k3s.1
k3d-k3s-default-worker-1   Ready    <none>   26m   v1.16.2-k3s.1
k3d-k3s-default-worker-2   Ready    <none>   26m   v1.16.2-k3s.1
k3d-k3s-default-worker-6   Ready    <none>   26m   v1.16.2-k3s.1
k3d-k3s-default-worker-3   Ready    <none>   26m   v1.16.2-k3s.1
k3d-k3s-default-worker-7   Ready    <none>   26m   v1.16.2-k3s.1
k3d-k3s-default-worker-9   Ready    <none>   26m   v1.16.2-k3s.1

MBP-de-admin:~ admin$ kubectl get pods --all-namespaces
NAMESPACE     NAME                                      READY   STATUS              RESTARTS   AGE
kube-system   local-path-provisioner-58fb86bdfd-2gpmr   1/1     Running             0          26m
kube-system   coredns-57d8bbb86-9998n                   1/1     Running             0          26m
kube-system   svclb-traefik-x7c8m                       0/3     ContainerCreating   0          26m
kube-system   helm-install-traefik-r9hbq                0/1     Completed           0          26m
kube-system   svclb-traefik-wcnp8                       3/3     Running             0          26m
kube-system   svclb-traefik-fxqhw                       3/3     Running             0          26m
kube-system   svclb-traefik-mlrbz                       3/3     Running             0          26m
kube-system   svclb-traefik-tddph                       3/3     Running             0          26m
kube-system   svclb-traefik-657mx                       3/3     Running             0          26m
kube-system   svclb-traefik-j2crc                       3/3     Running             0          26m
kube-system   svclb-traefik-s8v2q                       3/3     Running             0          26m
kube-system   svclb-traefik-vzvps                       3/3     Running             0          26m
kube-system   svclb-traefik-zcfjk                       3/3     Running             0          26m
kube-system   svclb-traefik-jr9pq                       3/3     Running             0          26m
kube-system   traefik-65bccdc4bd-8xfrs                  1/1     Running             0          26m

Il semble que notre cluster soit opérationnel. Ceci permet maintenant de déployer notre démo de déploiement.

Visu depuis le dashboard de Docker for Mac

Deploy workload

Pour la démo, je déploie un serveur http minimalisme écrit en GoLang. Le déploiement comprend le déploiement k8s avec un nombre 20 répliques d'instances et un service k8s.

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: k3d-demo-deployment
  namespace: k3d-demo
spec:
  replicas: 20
  selector:
    matchLabels:
      app: k3d-demo
  template:
    metadata:
      labels:
        app: k3d-demo
    spec:
      containers:
      - name: k3d-demo
        image: agabert/beacon
        resources:
          requests:
            memory: "32Mi"
            cpu: "10m"
          limits:
            memory: "128Mi"
            cpu: "500m"
        ports:
        - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: k3d-demo
  namespace: k3d-demo
spec:
  ports:
  - port: 80
    targetPort: 80
    name: http
  selector:
    app: k3d-demo

Voyons maintenant ce qui se passe lorsque nous déployons :


$ kubectl create namespace k3d-demo

$ kubectl apply -f demo.yaml

deployment.apps/k3d-demo-deployment created
service/k3d-demo created

Surveillez ce qui se prépare :

$ watch kubectl get pods -n k3d-demo

Every 2,0s: kubectl get pods -n k3d-demo                                                                                        Sun Dec 29 17:34:38 2019

NAME                                  READY   STATUS    RESTARTS   AGE
k3d-demo-deployment-b844cdc59-cvc4l   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-7pk7x   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-gv8p2   1/1     Running   0          5m11s
k3d-demo-deployment-b844cdc59-4zhnq   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-8tbmc   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-62jh9   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-qpz5k   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-4lpjj   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-s4cnh   1/1     Running   0          5m11s
k3d-demo-deployment-b844cdc59-dhb27   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-26r58   1/1     Running   0          5m11s
k3d-demo-deployment-b844cdc59-5xmj4   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-f45fs   1/1     Running   0          5m11s
k3d-demo-deployment-b844cdc59-zprvx   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-652h9   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-tj7sb   1/1     Running   0          5m11s
k3d-demo-deployment-b844cdc59-zvfhz   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-6vhzv   1/1     Running   0          5m11s
k3d-demo-deployment-b844cdc59-nwzd6   1/1     Running   0          5m12s
k3d-demo-deployment-b844cdc59-k8vqc   1/1     Running   0          5m12s

Faire une requête http vers le service via un "helper-container" :

$ kubectl --namespace=k3d-demo run -it --image=alpine helper-container

$ wget -SO- k3d-demo/metrics

Connecting to k3d-demo (10.43.48.234:80)
  HTTP/1.1 200 OK
  Content-Type: text/plain; version=0.0.4; charset=utf-8
  Date: Sun, 29 Dec 2019 16:36:20 GMT
  Connection: close
  Transfer-Encoding: chunked

writing to stdout
# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0
go_gc_duration_seconds_sum 0
go_gc_duration_seconds_count 0
....

Bonus: Accès aux services depuis l'extérieur du cluster.

Essayons de rendre le serveur http disponible depuis l'extérieur du cluster. Ici, vous pouvez utiliser la ressource NodePort ou LoadBalancer.

---
apiVersion: v1
kind: Service
metadata:
  name: k3d-demo
  namespace: k3d-demo
spec:
  type: LoadBalancer
  ports:
  - port: 80
    targetPort: 80
    name: http
  selector:
    app: k3d-demo

Kubernetes va maintenant reconfigurer votre type de service de CLusterIP au nouveau type donné.

MBP-de-admin:~ admin$ kubectl -n k3d-demo get service
NAME       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)   AGE
k3d-demo   ClusterIP   10.43.48.234   <none>        80/TCP    8m49s
$ kubectl apply -f test.yml 
deployment.apps/k3d-demo-deployment unchanged
service/k3d-demo configured
$ kubectl -n k3d-demo get service
NAME       TYPE           CLUSTER-IP     EXTERNAL-IP               PORT(S)          AGE
k3d-demo   LoadBalancer   10.43.10.180   172.19.0.10,172.19.0.11   8080:31995/TCP   30s

Kubernetes a attribué deux adresses IP au Loadbalancer. Ils sont issus du Docker-Network de k3s.

docker network inspect k3s_default host 
[
    {
        "Name": "k3s_default",
        "Id": "db7ab3451ca0058255d084b5a8819ead8757b37ff7a1e333ad8c389331fd5b22",
        "Created": "2019-05-08T23:57:41.173744627+02:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.19.0.0/16",
                    "Gateway": "172.19.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "0edfb125443002ed05ab16cd019158098f936e76f961dbbd457b9e729bdd9f58": {
                "Name": "k3d-k3s_default-worker-6",
                "EndpointID": "9f7fc1aa3d1814d0d7ac6dd1538e1add04ed622261b050a1cdcdd64df43437d5",
                "MacAddress": "02:42:ac:13:00:09",
                "IPv4Address": "172.19.0.9/16",
                "IPv6Address": ""
            },
            "2db4c1b45db71fbec3a7998ad5f836de61fd84ce75506934b10b75b6b4b22fa9": {
                "Name": "k3d-k3s_default-worker-2",
                "EndpointID": "5c7f9d48e21288824aca72c60a893bbea303082b5c267f97b64be0253b4245b7",
                "MacAddress": "02:42:ac:13:00:05",
                "IPv4Address": "172.19.0.5/16",
                "IPv6Address": ""
          ....

Permet enfin de vérifier si nous pouvons accéder au point de terminaison via l'une des adresses IP fournies.

$ wget -SO- 172.19.0.10:80/metrics
--2019-05-09 00:07:39--  http://172.19.0.10/metrics
Connecting to 172.19.0.10:80... connected.
HTTP request sent, awaiting response... 
  HTTP/1.1 200 OK
  Content-Type: text/plain; version=0.0.4; charset=utf-8
  Date: Wed, 08 May 2019 22:07:39 GMT
  Transfer-Encoding: chunked
Length: unspecified [text/plain]
Saving to: ‘STDOUT’

-                                                  [<=>                                                                                                ]       0  --.-KB/s               # HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0"} 0
go_gc_duration_seconds{quantile="0.25"} 0
go_gc_duration_seconds{quantile="0.5"} 0
go_gc_duration_seconds{quantile="0.75"} 0
go_gc_duration_seconds{quantile="1"} 0

Cela marche parfaitement!

Liens utiles

k3s official website
k3s GitHub
k3d GitHub
Rancher