Ceci est une ancienne révision du document !


Table des matières

Dernière mise-à-jour : 2020/01/30 03:44

DVOPF2 - Orchestration de Conteneurs avec Docker Swarm et Kubernetes

L'Orchestration de Conteneurs

Les principales solutions de containerization sont :

Les principales solutions d'orchestration de conteneurs sont :

L'orchestration de conteneurs apporte :

  • La haute disponibilité,
  • L'équilibrage de charge,
  • L'augmentation et la réduction du Services (Scale up / scale down) automatiques et transparentes.

Présentation de Kubernetes (k8s)

Master

  • Contrôleur du cluster,
  • Responsable de l'orchestration.

Le Master contient :

  • Serveur API,
    • Front end,
  • Service etcd,
    • Key-value store qui stocke toutes les données utilisées pour gérer le cluster et gére les vérrous,
  • Controlleur,
    • Surveille l'état des conteneurs, neouds et end-points. Responsable de la mise en place de nouveaux conteneurs en cas de défaillances.
  • Ordonnonceur,
    • Distribue les conteneurs existants aux noeuds et cherche des nouveaux conteneurs et les attribue aux noeuds,

Noeuds (Minion)

  • Machine physique ou virtuelle sur laquelle est installé Kubernetes,
  • Un travailleur sur lequels Kubernetes lance des conteneurs,

Le Noeud contient :

  • Service kubelet,
    • Agent qui s'exécute sur chaque noeud. Responsable de la surveillance des conteneurs.
  • Container runtime,
    • Docker,
    • rkt (rocket),
    • CRI-O (crio).

Que Contient ce Cours ?

Vous allez apprendre l'orchestration de conteneurs grâce à des LABS :

  • LAB #1 - Installation de Kubernetes avec Minikube,
  • LAB #2 - Importation de la Machine Virtuelle Kubemaster,
  • LAB #3 - Installation de Docker, kubeadm, kubelet et kubectl,
  • LAB #4 - Création et Configuration des Noeuds,
  • LAB #5 - Création du Réseau pour Kubernetes,
  • LAB #6 - Connexion à vos Machines Virtuelles avec SSH,
  • LAB #7 - Création du Cluster Kubernetes,
  • LAB #8 - Création d'un POD,
  • LAB #9 - Utilisation de Contrôleurs de Réplication et ReplicaSets,
  • LAB #10 - Gestion des Deployments,
  • LAB #11 - Gestion du Réseau et des Services,
  • LAB #12 - Gestion de l'Architecture des Microservices.

LAB #1 - Création de Machines Virtuelles Docker avec Docker Machine

Préparation

VirtualBox

Téléchargez la bonne version de VirtualBox 64 bits à partir du lien https://www.virtualbox.org/wiki/Downloads et installez-la.

Docker-CE

Installer Docker-CE sur votre machine hôte :

Mac
Linux
Windows 7, 8 et 10
  • Téléchargez Docker Toolbox à partir de cette page https://docs.docker.com/toolbox/toolbox_install_windows/
  • Fermez toutes les machines virtuelles ainsi que VirtualBox,
  • Installez DockerToolBox.exe (décochez VirtualBox dans la liste de produits à installer),
  • Exécutez Docker Toolbox et laissez l'application créer une machine virtuelle,
  • Ouvrez VirtualBox. Vous verrez une machine virtuelle Default,
  • Ouvrez cmd.

Installation de docker-machine

Pour installer docker-machine sur votre machine hôte, utilisez une des commandes suivantes :

Mac

curl -L https://github.com/docker/machine/releases/download/v0.12.2/docker-machine-`uname -s`-`uname -m` >/usr/local/bin/docker-machine && chmod +x /usr/local/bin/docker-machine

Linux

curl -L https://github.com/docker/machine/releases/download/v0.12.2/docker-machine-`uname -s`-`uname -m` >/tmp/docker-machine && chmod +x /tmp/docker-machine && cp /tmp/docker-machine /usr/local/bin/docker-machine

Windows

Si vous utilisez Git BASH :

if [[ ! -d "$HOME/bin" ]]; then mkdir -p "$HOME/bin"; fi && curl -L https://github.com/docker/machine/releases/download/v0.12.2/docker-machine-Windows-x86_64.exe > "$HOME/bin/docker-machine.exe" && chmod +x "$HOME/bin/docker-machine.exe"

Si vous n'utilisez PAS Git BASH :

Téléchargez la version adéquate pour l'architecture de votre machine à partir de l'adresse https://github.com/docker/machine/releases/.

Création du manager1

Créez la machine virtuelle manager1 :

hnorris-laptop ~ # docker-machine create --driver virtualbox manager1
Creating CA: /root/.docker/machine/certs/ca.pem
Creating client certificate: /root/.docker/machine/certs/cert.pem
Running pre-create checks...
(manager1) Image cache directory does not exist, creating it at /root/.docker/machine/cache...
(manager1) No default Boot2Docker ISO found locally, downloading the latest release...
(manager1) Latest release for github.com/boot2docker/boot2docker is v17.06.2-ce
(manager1) Downloading /root/.docker/machine/cache/boot2docker.iso from https://github.com/boot2docker/boot2docker/releases/download/v17.06.2-ce/boot2docker.iso...
(manager1) 0%....10%....20%....30%....40%....50%....60%....70%....80%....90%....100%
Creating machine...
(manager1) Copying /root/.docker/machine/cache/boot2docker.iso to /root/.docker/machine/machines/manager1/boot2docker.iso...
(manager1) Creating VirtualBox VM...
(manager1) Creating SSH key...
(manager1) Starting the VM...
(manager1) Check network to re-create if needed...
(manager1) Found a new host-only adapter: "vboxnet0"
(manager1) Waiting for an IP...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env manager1

Les options de la commande docker-machine sont :

hnorris-laptop ~ # docker-machine --help
Usage: docker-machine [OPTIONS] COMMAND [arg...]

Create and manage machines running Docker.

Version: 0.12.2, build 9371605

Author:
  Docker Machine Contributors - <https://github.com/docker/machine>

Options:
  --debug, -D					Enable debug mode
  --storage-path, -s "/root/.docker/machine"	Configures storage path [$MACHINE_STORAGE_PATH]
  --tls-ca-cert 				CA to verify remotes against [$MACHINE_TLS_CA_CERT]
  --tls-ca-key 					Private key to generate certificates [$MACHINE_TLS_CA_KEY]
  --tls-client-cert 				Client cert to use for TLS [$MACHINE_TLS_CLIENT_CERT]
  --tls-client-key 				Private key used in client TLS auth [$MACHINE_TLS_CLIENT_KEY]
  --github-api-token 				Token to use for requests to the Github API [$MACHINE_GITHUB_API_TOKEN]
  --native-ssh					Use the native (Go-based) SSH implementation. [$MACHINE_NATIVE_SSH]
  --bugsnag-api-token 				BugSnag API token for crash reporting [$MACHINE_BUGSNAG_API_TOKEN]
  --help, -h					show help
  --version, -v					print the version
  
Commands:
  active		Print which machine is active
  config		Print the connection config for machine
  create		Create a machine
  env			Display the commands to set up the environment for the Docker client
  inspect		Inspect information about a machine
  ip			Get the IP address of a machine
  kill			Kill a machine
  ls			List machines
  provision		Re-provision existing machines
  regenerate-certs	Regenerate TLS Certificates for a machine
  restart		Restart a machine
  rm			Remove a machine
  ssh			Log into or run a command on a machine with SSH.
  scp			Copy files between machines
  start			Start a machine
  status		Get the status of a machine
  stop			Stop a machine
  upgrade		Upgrade a machine to the latest version of Docker
  url			Get the URL of a machine
  version		Show the Docker Machine version or a machine docker version
  help			Shows a list of commands or help for one command
  
Run 'docker-machine COMMAND --help' for more information on a command.

Création des Travailleurs

Créez maintenant 5 travailleurs - worker1 jusqu'à worker5 :

hnorris-laptop ~ # docker-machine create --driver virtualbox worker1
Running pre-create checks...
Creating machine...
(worker1) Copying /root/.docker/machine/cache/boot2docker.iso to /root/.docker/machine/machines/worker1/boot2docker.iso...
(worker1) Creating VirtualBox VM...
(worker1) Creating SSH key...
(worker1) Starting the VM...
(worker1) Check network to re-create if needed...
(worker1) Waiting for an IP...
Waiting for machine to be running, this may take a few minutes...
Detecting operating system of created instance...
Waiting for SSH to be available...
Detecting the provisioner...
Provisioning with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect your Docker Client to the Docker Engine running on this virtual machine, run: docker-machine env worker1
hnorris-laptop ~ # docker-machine create --driver virtualbox worker2
...
hnorris-laptop ~ # docker-machine create --driver virtualbox worker3
...
hnorris-laptop ~ # docker-machine create --driver virtualbox worker4
...
hnorris-laptop ~ # docker-machine create --driver virtualbox worker5
...

Les options de la sous-commande create de la commande docker-machine sont :

hnorris-laptop ~ # docker-machine create --help 
Usage: docker-machine create [OPTIONS] [arg...]

Create a machine

Description:
   Run 'docker-machine create --driver name' to include the create flags for that driver in the help text.

Options:
   
   --driver, -d "virtualbox"										Driver to create machine with. [$MACHINE_DRIVER]
   --engine-env [--engine-env option --engine-env option]						Specify environment variables to set in the engine
   --engine-insecure-registry [--engine-insecure-registry option --engine-insecure-registry option]	Specify insecure registries to allow with the created engine
   --engine-install-url "https://get.docker.com"							Custom URL to use for engine installation [$MACHINE_DOCKER_INSTALL_URL]
   --engine-label [--engine-label option --engine-label option]						Specify labels for the created engine
   --engine-opt [--engine-opt option --engine-opt option]						Specify arbitrary flags to include with the created engine in the form flag=value
   --engine-registry-mirror [--engine-registry-mirror option --engine-registry-mirror option]		Specify registry mirrors to use [$ENGINE_REGISTRY_MIRROR]
   --engine-storage-driver 										Specify a storage driver to use with the engine
   --swarm												Configure Machine to join a Swarm cluster
   --swarm-addr 											addr to advertise for Swarm (default: detect and use the machine IP)
   --swarm-discovery 											Discovery service to use with Swarm
   --swarm-experimental											Enable Swarm experimental features
   --swarm-host "tcp://0.0.0.0:3376"									ip/socket to listen on for Swarm master
   --swarm-image "swarm:latest"										Specify Docker image to use for Swarm [$MACHINE_SWARM_IMAGE]
   --swarm-join-opt [--swarm-join-opt option --swarm-join-opt option]					Define arbitrary flags for Swarm join
   --swarm-master											Configure Machine to be a Swarm master
   --swarm-opt [--swarm-opt option --swarm-opt option]							Define arbitrary flags for Swarm master
   --swarm-strategy "spread"										Define a default scheduling strategy for Swarm
   --tls-san [--tls-san option --tls-san option]							Support extra SANs for TLS certs
   --virtualbox-boot2docker-url 									The URL of the boot2docker image. Defaults to the latest available version [$VIRTUALBOX_BOOT2DOCKER_URL]
   --virtualbox-cpu-count "1"										number of CPUs for the machine (-1 to use the number of CPUs available) [$VIRTUALBOX_CPU_COUNT]
   --virtualbox-disk-size "20000"									Size of disk for host in MB [$VIRTUALBOX_DISK_SIZE]
   --virtualbox-host-dns-resolver									Use the host DNS resolver [$VIRTUALBOX_HOST_DNS_RESOLVER]
   --virtualbox-hostonly-cidr "192.168.99.1/24"								Specify the Host Only CIDR [$VIRTUALBOX_HOSTONLY_CIDR]
   --virtualbox-hostonly-nicpromisc "deny"								Specify the Host Only Network Adapter Promiscuous Mode [$VIRTUALBOX_HOSTONLY_NIC_PROMISC]
   --virtualbox-hostonly-nictype "82540EM"								Specify the Host Only Network Adapter Type [$VIRTUALBOX_HOSTONLY_NIC_TYPE]
   --virtualbox-hostonly-no-dhcp									Disable the Host Only DHCP Server [$VIRTUALBOX_HOSTONLY_NO_DHCP]
   --virtualbox-import-boot2docker-vm 									The name of a Boot2Docker VM to import [$VIRTUALBOX_BOOT2DOCKER_IMPORT_VM]
   --virtualbox-memory "1024"										Size of memory for host in MB [$VIRTUALBOX_MEMORY_SIZE]
   --virtualbox-nat-nictype "82540EM"									Specify the Network Adapter Type [$VIRTUALBOX_NAT_NICTYPE]
   --virtualbox-no-dns-proxy										Disable proxying all DNS requests to the host [$VIRTUALBOX_NO_DNS_PROXY]
   --virtualbox-no-share										Disable the mount of your home directory [$VIRTUALBOX_NO_SHARE]
   --virtualbox-no-vtx-check										Disable checking for the availability of hardware virtualization before the vm is started [$VIRTUALBOX_NO_VTX_CHECK]
   --virtualbox-share-folder 										Mount the specified directory instead of the default home location. Format: dir:name [$VIRTUALBOX_SHARE_FOLDER]
   --virtualbox-ui-type "headless"									Specify the UI Type: (gui|sdl|headless|separate) [$VIRTUALBOX_UI_TYPE]

Lister les VM Docker

Pour lister les VM Docker ainsi que leurs états, il convient d'utilise la sous-commande ls de la commande docker-machine :

hnorris-laptop ~ # docker-machine ls
NAME       ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER        ERRORS
manager1   -        virtualbox   Running   tcp://192.168.99.100:2376           v17.06.2-ce   
worker1    -        virtualbox   Running   tcp://192.168.99.101:2376           v17.06.2-ce   
worker2    -        virtualbox   Running   tcp://192.168.99.102:2376           v17.06.2-ce   
worker3    -        virtualbox   Running   tcp://192.168.99.103:2376           v17.06.2-ce   
worker4    -        virtualbox   Running   tcp://192.168.99.104:2376           v17.06.2-ce   
worker5    -        virtualbox   Running   tcp://192.168.99.105:2376           v17.06.2-ce   

Important - Si vous avez Windows 7, 8 ou 10 et vous utilisez Docker Toolbox, les adresses IP des machines seront décalées de 1. Par exemple pour manager1, l'adresse sera 192.168.99.101 et pour worker1 192.168.99.102. Vous devez donc en tenir compte dans la suite du cours.

Obtenir l'adresse IP des VM

Une autre façon d'obtenir les adresses IP des VM est d'utiliser la sous-commande ip :

hnorris-laptop ~ # docker-machine ip manager1
192.168.99.100
hnorris-laptop ~ # docker-machine ip worker1
192.168.99.101
hnorris-laptop ~ # docker-machine ip worker2
192.168.99.102
hnorris-laptop ~ # docker-machine ip worker3
192.168.99.103
hnorris-laptop ~ # docker-machine ip worker4
192.168.99.104
hnorris-laptop ~ # docker-machine ip worker5
192.168.99.105

Se connecter à une VM Docker

Pour se connecter à une VM Docker, il convient d'utiliser la sous-commande ssh de la commande docker-machine :

hnorris-laptop ~ # docker-machine ssh manager1
                        ##         .
                  ## ## ##        ==
               ## ## ## ## ##    ===
           /"""""""""""""""""\___/ ===
      ~~~ {~~ ~~~~ ~~~ ~~~~ ~~~ ~ /  ===- ~~~
           \______ o           __/
             \    \         __/
              \____\_______/
 _                 _   ____     _            _
| |__   ___   ___ | |_|___ \ __| | ___   ___| | _____ _ __
| '_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|
| |_) | (_) | (_) | |_ / __/ (_| | (_) | (__|   <  __/ |
|_.__/ \___/ \___/ \__|_____\__,_|\___/ \___|_|\_\___|_|
Boot2Docker version 17.06.2-ce, build HEAD : ff16afa - Wed Sep  6 00:17:25 UTC 2017
Docker version 17.06.2-ce, build cec0b72
docker@manager1:~$ 

Important - Notez que la distribution de la VM est Boot2Docker. Cette distribution est basée sur Tiny Core Linux, s'exécute entièrement dans la mémoire vive, pèse 27 Mo et démarre en approximativement 5 secondes.

Ayant été créées par root, les VM Docker ainsi que leurs fichiers associés sont stockés dans le répertoire /root/.docker/machine/machines/ :

hnorris-laptop ~ # locate manager1
/root/.docker/machine/machines/manager1
/root/.docker/machine/machines/manager1/boot2docker.iso
/root/.docker/machine/machines/manager1/ca.pem
/root/.docker/machine/machines/manager1/cert.pem
/root/.docker/machine/machines/manager1/config.json
/root/.docker/machine/machines/manager1/disk.vmdk
/root/.docker/machine/machines/manager1/id_rsa
/root/.docker/machine/machines/manager1/id_rsa.pub
/root/.docker/machine/machines/manager1/key.pem
/root/.docker/machine/machines/manager1/manager1
/root/.docker/machine/machines/manager1/server-key.pem
/root/.docker/machine/machines/manager1/server.pem
/root/.docker/machine/machines/manager1/manager1/Logs
/root/.docker/machine/machines/manager1/manager1/manager1.vbox
/root/.docker/machine/machines/manager1/manager1/manager1.vbox-prev
/root/.docker/machine/machines/manager1/manager1/Logs/VBox.log

LAB #2 - Orchéstration avec Docker Swarm,

Initialiser Docker Swarm

Pour initialiser Docker swarm, il convient d'utiliser la commande docker swarm init à partir de la VM Docker manager1 en stipulant l'adresse IP de manager1 :

docker@manager1:~$ docker swarm init --advertise-addr 192.168.99.100
Swarm initialized: current node (yuwpmvtfmdxn8i7nllkyzkxkp) is now a manager.

To add a worker to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5bd9w9tapfqmd41f2psqdkoqwfo48fqsznnalk2slc28vlp6uh-004kp8y71m09nd7p8ft7ldku0 192.168.99.100:2377

To add a manager to this swarm, run 'docker swarm join-token manager' and follow the instructions.

Le Statut Leader

Consultez le statut de la VM Docker manager1 :

docker@manager1:~$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
yuwpmvtfmdxn8i7nllkyzkxkp *   manager1            Ready               Active              Leader

A un instant t il ne peut y avoir q'un seul Leader. Il est possible de créer d'autres noeuds de gestion en le rejoignant à swarm en utilisant le token prévu à cet effet. Par contre ces noeuds de gestion restent en attente d'une éventuelle défaillance du Leader actuel.

Pour connaître le token nécessaire pour ejoindre swarm en tant que noeud de gestion, saisissez la commande suivante :

docker@manager1:~$ docker swarm join-token manager
To add a manager to this swarm, run the following command:

    docker swarm join --token SWMTKN-1-5bd9w9tapfqmd41f2psqdkoqwfo48fqsznnalk2slc28vlp6uh-8rvbxvqewsrv6yyts7z2lq9pt 192.168.99.100:2377

Rejoindre le Swarm

Rejoignez les 5 machines travailleurs à swarm en utilisant le token worker :

docker@worker1:~$ docker swarm join --token SWMTKN-1-5bd9w9tapfqmd41f2psqdkoqwfo48fqsznnalk2slc28vlp6uh-004kp8y71m09nd7p8ft7ldku0 192.168.99.100:2377
This node joined a swarm as a worker.
docker@worker2:~$ docker swarm join --token SWMTKN-1-5bd9w9tapfqmd41f2psqdkoqwfo48fqsznnalk2slc28vlp6uh-004kp8y71m09nd7p8ft7ldku0 192.168.99.100:2377
This node joined a swarm as a worker.
docker@worker3:~$ docker swarm join --token SWMTKN-1-5bd9w9tapfqmd41f2psqdkoqwfo48fqsznnalk2slc28vlp6uh-004kp8y71m09nd7p8ft7ldku0 192.168.99.100:2377
This node joined a swarm as a worker.
docker@worker4:~$ docker swarm join --token SWMTKN-1-5bd9w9tapfqmd41f2psqdkoqwfo48fqsznnalk2slc28vlp6uh-004kp8y71m09nd7p8ft7ldku0 192.168.99.100:2377
This node joined a swarm as a worker.
docker@worker5:~$ docker swarm join --token SWMTKN-1-5bd9w9tapfqmd41f2psqdkoqwfo48fqsznnalk2slc28vlp6uh-004kp8y71m09nd7p8ft7ldku0 192.168.99.100:2377
This node joined a swarm as a worker.

L'état des VM Docker peut être consulter en utilisant de nouveau la commande docker node ls :

docker@manager1:~$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
1f5qtolgtonqmhjk5ppwc8x1b     worker1             Ready               Active              
kmyjdwp9ojhzje4hlw7ffhuxv     worker2             Ready               Active              
oyszb44k8yw5btz3c1wq2ot2e     worker4             Ready               Active              
p6jpyopzzy0zg4znegi63hzjq     worker5             Ready               Active              
yitkfnk99ecisrny9g3r9kfhk     worker3             Ready               Active              
yuwpmvtfmdxn8i7nllkyzkxkp *   manager1            Ready               Active              Leader

Notez que vous ne pouvez pas utiliser cette commande à partir d'un travailleur :

docker@worker5:~$ docker node ls
Error response from daemon: This node is not a swarm manager. Worker nodes can't be used to view or modify cluster state. Please run this command on a manager node or promote the current node to a manager.

Consulter les Informations de Swarm

Il est possible de visualiser les informations concernant le swarm en utilisant la commande docker info :

docker@manager1:~$ docker info
...
Swarm: active
 NodeID: yuwpmvtfmdxn8i7nllkyzkxkp
 Is Manager: true
 ClusterID: sqll9xmii9qkrd35d1limn1od
 Managers: 1
 Nodes: 6
 Orchestration:
  Task History Retention Limit: 5
 Raft:
  Snapshot Interval: 10000
  Number of Old Snapshots to Retain: 0
  Heartbeat Tick: 1
  Election Tick: 3
 Dispatcher:
  Heartbeat Period: 5 seconds
 CA Configuration:
  Expiry Duration: 3 months
  Force Rotate: 0
 Root Rotation In Progress: false
 Node Address: 192.168.99.100
 Manager Addresses:
  192.168.99.100:2377
...

Important - Quand le moteur Docker est en mode swarm, les noeuds de gestion implémentent le Raft Consensus Algorithm pour gérer l'état du cluster.

Démarrer un Service

Dans cet example, nous allons démarrer le service nginx avec les propriètes suivantes :

  • Mappage du port nginx sur le port 80 de la machine hôte,
  • 5 instances du service,
  • Un nom unique de web.
docker@manager1:~$ docker service create --replicas 5 -p 80:80 --name web nginx
4xtuwgbvr17lvfzoumh1y4mq4
Since --detach=false was not specified, tasks will be created in the background.
In a future release, --detach=false will become the default.

Pour consulter l'état de ce service, utilisez la commande docker service ls :

docker@manager1:~$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
4xtuwgbvr17l        web                 replicated          5/5                 nginx:latest        *:80->80/tcp

Ce service fonctionne dans des conteneurs Docker :

docker@manager1:~$ docker service ps web
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE                ERROR               PORTS
jkm2hapcthht        web.1               nginx:latest        worker3             Running             Running about a minute ago                       
q55eqdhr1qf1        web.2               nginx:latest        worker4             Running             Running about a minute ago                       
imqdkw4ei6gs        web.3               nginx:latest        manager1            Running             Running about a minute ago                       
k4vjd0g7ijww        web.4               nginx:latest        worker1             Running             Running about a minute ago                       
b7xbmy1npgf9        web.5               nginx:latest        worker2             Running             Running about a minute ago

Important - Notez qu'il n'y a pas de conteneur sur worker5.

Pour constater le lancement du daemon nginx, lancez la commande docker ps sur la machine manager1 :

docker@manager1:~$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
4107cb687eda        nginx:latest        "nginx -g 'daemon ..."   2 minutes ago       Up 2 minutes        80/tcp              web.3.imqdkw4ei6gskwacnb4pime5f

Connectez-vous sur chaque VM Docker pour constater que le service nginx fonctionne :

docker@manager1:/$ curl 192.168.99.100
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
docker@manager1:/$ curl 192.168.99.101
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
docker@manager1:/$ curl 192.168.99.102
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
docker@manager1:/$ curl 192.168.99.103
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
docker@manager1:/$ curl 192.168.99.104
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>
docker@manager1:/$ curl 192.168.99.105
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Important - Notez que le service est même disponible en consultant l'adresse IP de worker5.

Augmentation et Réduction du Service

Actuellement, il existe 5 conteneurs en cours d'exécution. Pour procéder à un scale-up à 8 conteneurs, il convient d'utiliser la commande docker service scale :

docker@manager1:/$ docker service scale web=8
web scaled to 8

Notez que la commande docker service ls confirme le fait qu'il y a 8 replicas :

docker@manager1:/$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
4xtuwgbvr17l        web                 replicated          8/8                 nginx:latest        *:80->80/tcp

Des trois replicas supplémentaires, deux ont été lancés sur worker5 tandis que le troisième a été lancé sur worker1 :

docker@manager1:/$ docker service ps web
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
jkm2hapcthht        web.1               nginx:latest        worker3             Running             Running 20 minutes ago                       
q55eqdhr1qf1        web.2               nginx:latest        worker4             Running             Running 20 minutes ago                       
imqdkw4ei6gs        web.3               nginx:latest        manager1            Running             Running 20 minutes ago                       
k4vjd0g7ijww        web.4               nginx:latest        worker1             Running             Running 20 minutes ago                       
b7xbmy1npgf9        web.5               nginx:latest        worker2             Running             Running 20 minutes ago                       
kg3bivcg0wln        web.6               nginx:latest        worker5             Running             Running 47 seconds ago                       
ik3u0jfgey64        web.7               nginx:latest        worker5             Running             Running 47 seconds ago                       
6bw5ptw7xao8        web.8               nginx:latest        worker1             Running             Running 57 seconds ago 

Consulter le Statut d'un Noeud

Pour se renseigner sur le statut du noeud courant, il convient d'utiliser la commande docker node inspect avec le mot clef self :

docker@manager1:/$ docker node inspect self
[
    {
        "ID": "yuwpmvtfmdxn8i7nllkyzkxkp",
        "Version": {
            "Index": 9
        },
        "CreatedAt": "2017-09-08T11:43:55.289178512Z",
        "UpdatedAt": "2017-09-08T11:43:55.89870884Z",
        "Spec": {
            "Labels": {},
            "Role": "manager",
            "Availability": "active"
        },
        "Description": {
            "Hostname": "manager1",
            "Platform": {
                "Architecture": "x86_64",
                "OS": "linux"
            },
            "Resources": {
                "NanoCPUs": 1000000000,
                "MemoryBytes": 1044123648
            },
            "Engine": {
                "EngineVersion": "17.06.2-ce",
                "Labels": {
                    "provider": "virtualbox"
                },
                "Plugins": [
                    {
                        "Type": "Log",
                        "Name": "awslogs"
                    },
                    {
                        "Type": "Log",
                        "Name": "fluentd"
                    },
                    {
                        "Type": "Log",
                        "Name": "gcplogs"
                    },
                    {
                        "Type": "Log",
                        "Name": "gelf"
                    },
                    {
                        "Type": "Log",
                        "Name": "journald"
                    },
                    {
                        "Type": "Log",
                        "Name": "json-file"
                    },
                    {
                        "Type": "Log",
                        "Name": "logentries"
                    },
                    {
                        "Type": "Log",
                        "Name": "splunk"
                    },
                    {
                        "Type": "Log",
                        "Name": "syslog"
                    },
                    {
                        "Type": "Network",
                        "Name": "bridge"
                    },
                    {
                        "Type": "Network",
                        "Name": "host"
                    },
                    {
                        "Type": "Network",
                        "Name": "macvlan"
                    },
                    {
                        "Type": "Network",
                        "Name": "null"
                    },
                    {
                        "Type": "Network",
                        "Name": "overlay"
                    },
                    {
                        "Type": "Volume",
                        "Name": "local"
                    }
                ]
            },
            "TLSInfo": {
                "TrustRoot": "-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUNuU4I89kxId2QXulofRKxJa9XRcwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwOTA4MTEzOTAwWhcNMzcwOTAzMTEz\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABEqgLUbyjyNuP35aAzW+aqVB8AkghvpF5hq1KnMveHbl4Ilr+EyDjlYZkbnt\nGb/xmsy/tOP8uz598ZX/JlR4fZyjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBSzoKGrN0ELfEIMsjxuYj5LAckD2jAKBggqhkjO\nPQQDAgNIADBFAiB34DOvDtIYjJ+GzbPMGu9Dd/cJGvy7CJg1tNUG3SoOrAIhAJZ4\nTJBucTomFSDsj5Y/R6TfhcpXpsksk7JwYgEglu44\n-----END CERTIFICATE-----\n",
                "CertIssuerSubject": "MBMxETAPBgNVBAMTCHN3YXJtLWNh",
                "CertIssuerPublicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESqAtRvKPI24/floDNb5qpUHwCSCG+kXmGrUqcy94duXgiWv4TIOOVhmRue0Zv/GazL+04/y7Pn3xlf8mVHh9nA=="
            }
        },
        "Status": {
            "State": "ready",
            "Addr": "192.168.99.100"
        },
        "ManagerStatus": {
            "Leader": true,
            "Reachability": "reachable",
            "Addr": "192.168.99.100:2377"
        }
    }
]

Pour se renseigner sur le statut d'un autre noeud, il convient d'utiliser la commande docker node inspect avec le nom du noeud concerné :

docker@manager1:/$ docker node inspect worker1
[
    {
        "ID": "1f5qtolgtonqmhjk5ppwc8x1b",
        "Version": {
            "Index": 15
        },
        "CreatedAt": "2017-09-08T11:48:42.011596185Z",
        "UpdatedAt": "2017-09-08T11:48:42.093455479Z",
        "Spec": {
            "Labels": {},
            "Role": "worker",
            "Availability": "active"
        },
        "Description": {
            "Hostname": "worker1",
            "Platform": {
                "Architecture": "x86_64",
                "OS": "linux"
            },
            "Resources": {
                "NanoCPUs": 1000000000,
                "MemoryBytes": 1044123648
            },
            "Engine": {
                "EngineVersion": "17.06.2-ce",
                "Labels": {
                    "provider": "virtualbox"
                },
                "Plugins": [
                    {
                        "Type": "Log",
                        "Name": "awslogs"
                    },
                    {
                        "Type": "Log",
                        "Name": "fluentd"
                    },
                    {
                        "Type": "Log",
                        "Name": "gcplogs"
                    },
                    {
                        "Type": "Log",
                        "Name": "gelf"
                    },
                    {
                        "Type": "Log",
                        "Name": "journald"
                    },
                    {
                        "Type": "Log",
                        "Name": "json-file"
                    },
                    {
                        "Type": "Log",
                        "Name": "logentries"
                    },
                    {
                        "Type": "Log",
                        "Name": "splunk"
                    },
                    {
                        "Type": "Log",
                        "Name": "syslog"
                    },
                    {
                        "Type": "Network",
                        "Name": "bridge"
                    },
                    {
                        "Type": "Network",
                        "Name": "host"
                    },
                    {
                        "Type": "Network",
                        "Name": "macvlan"
                    },
                    {
                        "Type": "Network",
                        "Name": "null"
                    },
                    {
                        "Type": "Network",
                        "Name": "overlay"
                    },
                    {
                        "Type": "Volume",
                        "Name": "local"
                    }
                ]
            },
            "TLSInfo": {
                "TrustRoot": "-----BEGIN CERTIFICATE-----\nMIIBajCCARCgAwIBAgIUNuU4I89kxId2QXulofRKxJa9XRcwCgYIKoZIzj0EAwIw\nEzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwOTA4MTEzOTAwWhcNMzcwOTAzMTEz\nOTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH\nA0IABEqgLUbyjyNuP35aAzW+aqVB8AkghvpF5hq1KnMveHbl4Ilr+EyDjlYZkbnt\nGb/xmsy/tOP8uz598ZX/JlR4fZyjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB\nAf8EBTADAQH/MB0GA1UdDgQWBBSzoKGrN0ELfEIMsjxuYj5LAckD2jAKBggqhkjO\nPQQDAgNIADBFAiB34DOvDtIYjJ+GzbPMGu9Dd/cJGvy7CJg1tNUG3SoOrAIhAJZ4\nTJBucTomFSDsj5Y/R6TfhcpXpsksk7JwYgEglu44\n-----END CERTIFICATE-----\n",
                "CertIssuerSubject": "MBMxETAPBgNVBAMTCHN3YXJtLWNh",
                "CertIssuerPublicKey": "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESqAtRvKPI24/floDNb5qpUHwCSCG+kXmGrUqcy94duXgiWv4TIOOVhmRue0Zv/GazL+04/y7Pn3xlf8mVHh9nA=="
            }
        },
        "Status": {
            "State": "ready",
            "Addr": "192.168.99.101"
        }
    }
]

L'option –pretty produit une sortie plus facilement lisible :

docker@manager1:/$ docker node inspect --pretty worker1
ID:			            1f5qtolgtonqmhjk5ppwc8x1b
Hostname:              	worker1
Joined at:             	2017-09-08 11:48:42.011596185 +0000 utc
Status:
 State:			        Ready
 Availability:         	Active
 Address:		        192.168.99.101
Platform:
 Operating System:	    linux
 Architecture:		    x86_64
Resources:
 CPUs:			        1
 Memory:		        995.8MiB
Plugins:
 Log:		        awslogs, fluentd, gcplogs, gelf, journald, json-file, logentries, splunk, syslog
 Network:		        bridge, host, macvlan, null, overlay
 Volume:		        local
Engine Version:		    17.06.2-ce
Engine Labels:
 - provider=virtualbox
TLS Info:
 TrustRoot:
-----BEGIN CERTIFICATE-----
MIIBajCCARCgAwIBAgIUNuU4I89kxId2QXulofRKxJa9XRcwCgYIKoZIzj0EAwIw
EzERMA8GA1UEAxMIc3dhcm0tY2EwHhcNMTcwOTA4MTEzOTAwWhcNMzcwOTAzMTEz
OTAwWjATMREwDwYDVQQDEwhzd2FybS1jYTBZMBMGByqGSM49AgEGCCqGSM49AwEH
A0IABEqgLUbyjyNuP35aAzW+aqVB8AkghvpF5hq1KnMveHbl4Ilr+EyDjlYZkbnt
Gb/xmsy/tOP8uz598ZX/JlR4fZyjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB
Af8EBTADAQH/MB0GA1UdDgQWBBSzoKGrN0ELfEIMsjxuYj5LAckD2jAKBggqhkjO
PQQDAgNIADBFAiB34DOvDtIYjJ+GzbPMGu9Dd/cJGvy7CJg1tNUG3SoOrAIhAJZ4
TJBucTomFSDsj5Y/R6TfhcpXpsksk7JwYgEglu44
-----END CERTIFICATE-----

 Issuer Subject:	MBMxETAPBgNVBAMTCHN3YXJtLWNh
 Issuer Public Key:	MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAESqAtRvKPI24/floDNb5qpUHwCSCG+kXmGrUqcy94duXgiWv4TIOOVhmRue0Zv/GazL+04/y7Pn3xlf8mVHh9nA==

Haute Disponibilité

Quand un noeud est actif, il est capable de recevoir de nouvelles tâches à partir du manager :

  • pendant un scale-up,
  • pendant une une mise à jour progressive,
  • quand un autre noeud reçois une instruction de se mettre en indisponibilité,
  • quand un service se mets en échec sur un autre noeud

Rappelez-vous que la swarm contient 6 VM Docker :

docker@manager1:/$ docker node ls
ID                            HOSTNAME            STATUS              AVAILABILITY        MANAGER STATUS
1f5qtolgtonqmhjk5ppwc8x1b     worker1             Ready               Active              
kmyjdwp9ojhzje4hlw7ffhuxv     worker2             Ready               Active              
oyszb44k8yw5btz3c1wq2ot2e     worker4             Ready               Active              
p6jpyopzzy0zg4znegi63hzjq     worker5             Ready               Active              
yitkfnk99ecisrny9g3r9kfhk     worker3             Ready               Active              
yuwpmvtfmdxn8i7nllkyzkxkp *   manager1            Ready               Active              Leader

et que sur les 6 VM Docker, il y a 8 conteneurs,

docker@manager1:/$ docker service ps web
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
jkm2hapcthht        web.1               nginx:latest        worker3             Running             Running 25 minutes ago                       
q55eqdhr1qf1        web.2               nginx:latest        worker4             Running             Running 25 minutes ago                       
imqdkw4ei6gs        web.3               nginx:latest        manager1            Running             Running 25 minutes ago                       
k4vjd0g7ijww        web.4               nginx:latest        worker1             Running             Running 25 minutes ago                       
b7xbmy1npgf9        web.5               nginx:latest        worker2             Running             Running 25 minutes ago                       
kg3bivcg0wln        web.6               nginx:latest        worker5             Running             Running 5 minutes ago                        
ik3u0jfgey64        web.7               nginx:latest        worker5             Running             Running 5 minutes ago                        
6bw5ptw7xao8        web.8               nginx:latest        worker1             Running             Running 5 minutes ago  

dont deux se trouvent sur worker1 :

docker@manager1:/$ docker node ps worker1
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE            ERROR               PORTS
k4vjd0g7ijww        web.4               nginx:latest        worker1             Running             Running 26 minutes ago                       
6bw5ptw7xao8        web.8               nginx:latest        worker1             Running             Running 6 minutes ago       

Mettez worker1 en mode d'indisponibilité en utilisant l'option –availability drain :

docker@manager1:/$ docker node update --availability drain worker1
worker1

Constatez que le service web a été déplacé sur deux autres noeuds, manager1 et worker4 :

docker@manager1:/$ docker service ps web
ID                  NAME                IMAGE               NODE                DESIRED STATE       CURRENT STATE             ERROR               PORTS
jkm2hapcthht        web.1               nginx:latest        worker3             Running             Running 29 minutes ago                        
q55eqdhr1qf1        web.2               nginx:latest        worker4             Running             Running 29 minutes ago                        
imqdkw4ei6gs        web.3               nginx:latest        manager1            Running             Running 29 minutes ago                        
6cv6j4tz0nk5        web.4               nginx:latest        manager1            Running             Running 33 seconds ago                        
k4vjd0g7ijww         \_ web.4           nginx:latest        worker1             Shutdown            Shutdown 33 seconds ago                       
b7xbmy1npgf9        web.5               nginx:latest        worker2             Running             Running 29 minutes ago                        
kg3bivcg0wln        web.6               nginx:latest        worker5             Running             Running 9 minutes ago                         
ik3u0jfgey64        web.7               nginx:latest        worker5             Running             Running 9 minutes ago                         
wht3r8c9wga6        web.8               nginx:latest        worker4             Running             Running 33 seconds ago                        
6bw5ptw7xao8         \_ web.8           nginx:latest        worker1             Shutdown            Shutdown 33 seconds ago 

Supprimer un Service

Pour supprimer un service il convient d'utiliser la commande docker service rm

docker@manager1:/$ docker service rm web
web
docker@manager1:/$ docker service ls
ID                  NAME                MODE                REPLICAS            IMAGE               PORTS
docker@manager1:/$ docker service inspect web
[]
Status: Error: no such service: web, Code: 1

LAB #3 - Installation de Kubernetes avec Minikube

Minikube permet de créer un cluster avec un seul noeud.

Préparation

kubectl

Installer kubectl sur votre machine hôte : Voir le lien https://kubernetes.io/docs/tasks/tools/install-kubectl/.

Minikube

Installer Minikube sur votre machine hôte : Voir le lien https://kubernetes.io/docs/tasks/tools/install-minikube/.

Installation

Installez un cluster Kubernetes dans une seule machine virtuelle (noeud) en utilisant minikube :

trainee@kubernetes:~$ minikube start
😄  minikube v0.34.1 on linux (amd64)
🔥  Creating virtualbox VM (CPUs=2, Memory=2048MB, Disk=20000MB) ...
💿  Downloading Minikube ISO ...
 184.30 MB / 184.30 MB [============================================] 100.00% 0s

📶  "minikube" IP address is 192.168.99.100
🐳  Configuring Docker as the container runtime ...
✨  Preparing Kubernetes environment ...
💾  Downloading kubeadm v1.13.3
💾  Downloading kubelet v1.13.3
🚜  Pulling images required by Kubernetes v1.13.3 ...
🚀  Launching Kubernetes v1.13.3 using kubeadm ... 
🔑  Configuring cluster permissions ...
🤔  Verifying component health .....
💗  kubectl is now configured to use "minikube"
🏄  Done! Thank you for using minikube!

Vérifiez que Kubernetes est bien installé en utilisant la commande kubectl :

trainee@kubernetes:~$ kubectl get nodes
NAME       STATUS   ROLES    AGE     VERSION
minikube   Ready    master   4m47s   v1.13.3

Installez maintenant un conteneur, appelé ici un POD :

trainee@kubernetes:~$ kubectl run hello-minikube --image=k8s.gcr.io/echoserver:1.10 --port=8080
kubectl run --generator=deployment/apps.v1 is DEPRECATED and will be removed in a future version. Use kubectl run --generator=run-pod/v1 or kubectl create instead.
deployment.apps/hello-minikube created

Vérifiez le statut du POD :

trainee@kubernetes:~$ kubectl get pods
NAME                              READY   STATUS    RESTARTS   AGE
hello-minikube-6fd785d459-7j47t   1/1     Running   0          78s

Ce POD est un serveur web. Nous avons besoin de le configurer de façon à pouvoir le contacter de notre machine hôte :

trainee@kubernetes:~$ kubectl expose deployment hello-minikube --type=NodePort
service/hello-minikube exposed

Pour accèder à la page web du POD, nous avons besoin de son adresse IP :

trainee@kubernetes:~$ minikube service hello-minikube --url
http://192.168.99.100:30915

Consultez la page web du POD :

trainee@kubernetes:~$ curl http://192.168.99.100:30915


Hostname: hello-minikube-6fd785d459-7j47t

Pod Information:
	-no pod information available-

Server values:
	server_version=nginx: 1.13.3 - lua: 10008

Request Information:
	client_address=172.17.0.1
	method=GET
	real path=/
	query=
	request_version=1.1
	request_scheme=http
	request_uri=http://192.168.99.100:8080/

Request Headers:
	accept=*/*
	host=192.168.99.100:30915
	user-agent=curl/7.58.0

Request Body:
	-no body in request-

Supprimez maintenant le déploiement :

trainee@kubernetes:~$ kubectl delete deployment hello-minikube
deployment.extensions "hello-minikube" deleted

LAB #4 - Importation de la Machine Virtuelle Kubemaster

Importez la machine virtuelle kubemaster :

Fichier > Importer un appareil virtuel ...

Le nom d'utilisateur et le mot de passe sont :

Utilisateur Mot de Passe
trainee trainee

Exécutez la commande apt-get update :

trainee@kubemaster:~$ sudo su -
[sudo] password for trainee: trainee
root@kubemaster:~# apt-get update
Hit:1 http://fr.archive.ubuntu.com/ubuntu xenial InRelease
Hit:2 http://fr.archive.ubuntu.com/ubuntu xenial-updates InRelease      
Hit:3 http://fr.archive.ubuntu.com/ubuntu xenial-backports InRelease    
Hit:4 http://security.ubuntu.com/ubuntu xenial-security InRelease
Reading package lists... Done   

Important : Notez que le mot de passe trainee ne sera pas en clair.

Notez que les machines virtuelles utilisées avec Kubernetes doivent être sous une des distrubutions suivantes :

  • Ubuntu 16.04+,
  • Debian 9,
  • CentOS 7,
  • RHEL 7,
  • Fedora 25/26 (best-effort),
  • HypriotOS v1.0.1+,
  • Container Linux (tested with 1800.6.0).

Chaque machine doit avoir :

  • Un minimum de 2 GO de RAM,
  • Un minimu de 2 CPU.

Les machines doivent :

  • être dans le même réseau,
  • posséder un nom d'hôte unique, une adresse MAC unique ainsi qu'un product_uuid unique,
  • avoir le swap désactivé,
  • avoir l'utilisation de dnsmasq par NetworkManager sous Systemd désactivée.

Certains ports doivent être ouverts sur le noeud maître :

Protocole Direction Port(s) Exécutable
TCP Entrante 6443* Kubernetes API server
TCP Entrante 2379-2380 etcd server client API
TCP Entrante 10250 Kubelet API
TCP Entrante 10251 kube-scheduler
TCP Entrante 10252 kube-controller-manager

ainsi que sur chaque noeud travailleur :

Protocole Direction Port(s) Exécutable
TCP Entrante 10250 Kubelet API
TCP Entrante 30000-32767 Services NodePort

LAB #5 - Installation de Docker, kubeadm, kubelet et kubectl

Vous allez créer un cluster comprenant un noeud maître et deux noeuds travailleurs.

Installation de Docker

Utilisez APT pour installer Docker :

  
root@kubemaster:~# apt-get install docker.io

Vérifiez que Docker-CE soit bien installé :

root@kubemaster:~# docker version
Client:
 Version:           18.06.1-ce
 API version:       1.38
 Go version:        go1.10.4
 Git commit:        e68fc7a
 Built:             Fri Jan 25 14:33:54 2019
 OS/Arch:           linux/amd64
 Experimental:      false

Server:
 Engine:
  Version:          18.06.1-ce
  API version:      1.38 (minimum version 1.12)
  Go version:       go1.10.4
  Git commit:       e68fc7a
  Built:            Thu Jan 24 10:56:33 2019
  OS/Arch:          linux/amd64
  Experimental:     false

Important : A la date de la création de ce cours Kubernetes version 1.13.3 n'a été validé qu'avec des versions 18.06 et inférieures de Docker-CE.

Installation de kubeadm, kubelet et kubectl

Installez les prérequis pour l'installation de Docker-CE :

root@kubemaster:~# apt-get -y install apt-transport-https curl 

Ajoutez la clef GPG pour le dépôt Kubernetes :

root@kubemaster:~# curl -s https://packages.cloud.google.com/apt/doc/apt-key.gpg | apt-key add -
OK

Ajoutez le dépôt de Kubernetes :

root@kubemaster:~# echo "deb https://apt.kubernetes.io/ kubernetes-xenial main" | tee -a /etc/apt/sources.list.d/kubernetes.list
deb https://apt.kubernetes.io/ kubernetes-xenial main

Procédez à l'installation de kubeadm, kubelet et kubectl :

root@kubemaster:~# apt-get update && apt-get install -y kubeadm kubelet kubectl

A Faire : Prenez une instantanée de votre machine virtuelle.

LAB #6 - Création et Configuration des Noeuds

Arrêtez la machine virtuelle et créez deux clones en générant de nouvelles adresses MAC pour toutes les interfaces réseau :

A Faire : Prenez une instantanée de vos trois machines virtuelles.

LAB #7 - Création du Réseau pour Kubernetes

Chaque machine virtuelle possède deux interfaces réseau. La première est NATée afin de pouvoir sortir sur Internet. Afin de créer un cluster efficace, il faut configurer la deuxième interface en adresse IP fixe. Créez donc le réseau privé hôte vboxnet1 en 192.168.56.1/24 dans VirtualBox :

Réseau > Créer

Configurez ensuite la deuxième interface réseau de chaque machine virtuelle en la plaçant dans le réseau privé hôte :

Détails > Réseau > Interface 2

Cochez Activer l'interface réseau et choisissez :

  • Mode d'accès réseau =⇒ Réseau privé hôte
  • Nom =⇒ vboxnet1

Démarrez les trois machines virtuelles. Modifiez le nom d'hôte et le fichier /etc/hosts des machines virtuelles kubenode1 et kubenode2 :

trainee@kubemaster:~$ sudo su -
[sudo] password for trainee: trainee
root@kubemaster:~# nmcli g hostname kubenode1
root@kubemaster:~# vi /etc/hosts
root@kubemaster:~# cat /etc/hosts
127.0.0.1	localhost
127.0.1.1	kubenode1

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
trainee@kubemaster:~$ sudo su -
[sudo] password for trainee: trainee
root@kubemaster:~# nmcli g hostname kubenode2
root@kubemaster:~# vi /etc/hosts
root@kubemaster:~# cat /etc/hosts
127.0.0.1	localhost
127.0.1.1	kubenode2

# The following lines are desirable for IPv6 capable hosts
::1     ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters

Important : Notez que le mot de passe trainee ne sera pas en clair.

Configurez l'interface enp0s8 de la machine virtuelle kubemaster :

root@kubemaster:~# nmcli connection add con-name ip_fixe ifname enp0s8 type ethernet ip4 192.168.56.2/24
Connection 'ip_fixe' (928f7107-5d45-4f70-98fb-5d27eeb444e5) successfully added.
root@kubemaster:~# nmcli c show
NAME                UUID                                  TYPE            DEVICE  
Wired connection 2  421548fa-f55a-357e-8ae4-8791eee9a58c  802-3-ethernet  enp0s3  
docker0             f21f037e-e14f-4608-b09e-10f428225b39  bridge          docker0 
ip_fixe             928f7107-5d45-4f70-98fb-5d27eeb444e5  802-3-ethernet  enp0s8  
Wired connection 1  3a77257b-e878-3e0d-97d9-d35b0112c03e  802-3-ethernet  --      
root@kubemaster:~# nmcli connection del "Wired connection 1"
Connection 'Wired connection 1' (3a77257b-e878-3e0d-97d9-d35b0112c03e) successfully deleted.
root@kubemaster:~# nmcli c show
NAME                UUID                                  TYPE            DEVICE  
Wired connection 2  421548fa-f55a-357e-8ae4-8791eee9a58c  802-3-ethernet  enp0s3  
docker0             f21f037e-e14f-4608-b09e-10f428225b39  bridge          docker0 
ip_fixe             928f7107-5d45-4f70-98fb-5d27eeb444e5  802-3-ethernet  enp0s8

Configurez l'interface enp0s8 de la machine virtuelle kubenode1 :

root@kubemaster:~# nmcli connection add con-name ip_fixe ifname enp0s8 type ethernet ip4 192.168.56.3/24
Connection 'ip_fixe' (6e95709f-3444-4d5e-a771-c4dd84fa0357) successfully added.
root@kubemaster:~# nmcli c show
NAME                UUID                                  TYPE            DEVICE  
Wired connection 2  58dd66a0-816a-314d-8352-027ea04bf85a  802-3-ethernet  enp0s3  
docker0             ebf6bb55-3fe7-4f27-8aba-4cbf142cb43b  bridge          docker0 
ip_fixe             6e95709f-3444-4d5e-a771-c4dd84fa0357  802-3-ethernet  enp0s8  
Wired connection 1  17af16eb-2503-36f6-b85e-c7e977ed11d2  802-3-ethernet  --      
root@kubemaster:~# nmcli connection del "Wired connection 1"
Connection 'Wired connection 1' (17af16eb-2503-36f6-b85e-c7e977ed11d2) successfully deleted.
root@kubemaster:~# nmcli c show
NAME                UUID                                  TYPE            DEVICE  
Wired connection 2  58dd66a0-816a-314d-8352-027ea04bf85a  802-3-ethernet  enp0s3  
docker0             ebf6bb55-3fe7-4f27-8aba-4cbf142cb43b  bridge          docker0 
ip_fixe             6e95709f-3444-4d5e-a771-c4dd84fa0357  802-3-ethernet  enp0s8 

Configurez l'interface enp0s8 de la machine virtuelle kubenode2 :

root@kubemaster:~# nmcli connection add con-name ip_fixe ifname enp0s8 type ethernet ip4 192.168.56.4/24
Connection 'ip_fixe' (06a92de1-a047-4a73-858e-c3fced41b0b8) successfully added.
root@kubemaster:~# nmcli c show
NAME                UUID                                  TYPE            DEVICE  
Wired connection 2  93f8951b-77f2-3f34-898f-6fbaa6ea8df6  802-3-ethernet  enp0s3  
docker0             3c445d6a-c1a3-422d-9a0a-72d19e6ac8bc  bridge          docker0 
ip_fixe             06a92de1-a047-4a73-858e-c3fced41b0b8  802-3-ethernet  enp0s8  
Wired connection 1  e9944ce6-1ec4-3a5b-9b49-cb53f9ae4bc4  802-3-ethernet  --      
root@kubemaster:~# nmcli connection del "Wired connection 1"
Connection 'Wired connection 1' (e9944ce6-1ec4-3a5b-9b49-cb53f9ae4bc4) successfully deleted.
root@kubemaster:~# nmcli c show
NAME                UUID                                  TYPE            DEVICE  
Wired connection 2  93f8951b-77f2-3f34-898f-6fbaa6ea8df6  802-3-ethernet  enp0s3  
docker0             3c445d6a-c1a3-422d-9a0a-72d19e6ac8bc  bridge          docker0 
ip_fixe             06a92de1-a047-4a73-858e-c3fced41b0b8  802-3-ethernet  enp0s8  

A Faire : Re-démarrez les trois machines virtuelles.

Installez le paquet openssh-server dans chaque machine virtuelle :

trainee@kubemaster:~$ sudo su -
[sudo] password for trainee: trainee
root@kubemaster:~# apt-get install -y openssh-server
trainee@kubenode1:~$ sudo su -
[sudo] password for trainee: trainee
root@kubenode1:~# apt-get install -y openssh-server
trainee@kubenode2:~$ sudo su -
[sudo] password for trainee: trainee
root@kubenode2:~# apt-get install -y openssh-server

Important : Notez que le mot de passe trainee ne sera pas en clair.

A Faire : Prenez une instantanée de vos trois machines virtuelles.

LAB #8 - Connexion à vos Machines Virtuelles avec SSH

Vous devez vous connecter aux machines virtuelles de la façon suivante :

MAC et Linux

Ouvrez un terminal et tapez la commande suivante pour la machine kubemaster :

$ ssh -l trainee localhost -p 2022

Ouvrez un autre terminal et tapez la commande suivante pour la machine kubenode1 :

$ ssh -l trainee localhost -p 3022

Ouvrez un autre terminal et tapez la commande suivante pour la machine kubenode2 :

$ ssh -l trainee localhost -p 4022

Windows

Ouvrez putty et utilisez les informations suivantes pour vous connecter à kubemaster :

  • Host Name –> localhost
  • Port –> 2022

Ouvrez putty et utilisez les informations suivantes pour vous connecter à kubenode1 :

  • Host Name –> localhost
  • Port –> 3022

Ouvrez putty et utilisez les informations suivantes pour vous connecter à kubenode2 :

  • Host Name –> localhost
  • Port –> 4022

Vérifiez la connectivité de chaque machine virtuelle :

trainee@kubemaster:~$ ping -c 4 192.168.56.3
PING 192.168.56.3 (192.168.56.3) 56(84) bytes of data.
64 bytes from 192.168.56.3: icmp_seq=1 ttl=64 time=0.762 ms
64 bytes from 192.168.56.3: icmp_seq=2 ttl=64 time=0.765 ms
64 bytes from 192.168.56.3: icmp_seq=3 ttl=64 time=0.819 ms
64 bytes from 192.168.56.3: icmp_seq=4 ttl=64 time=0.682 ms

--- 192.168.56.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3006ms
rtt min/avg/max/mdev = 0.682/0.757/0.819/0.048 ms
trainee@kubemaster:~$ ping -c 4 192.168.56.4
PING 192.168.56.4 (192.168.56.4) 56(84) bytes of data.
64 bytes from 192.168.56.4: icmp_seq=1 ttl=64 time=1.26 ms
64 bytes from 192.168.56.4: icmp_seq=2 ttl=64 time=0.710 ms
64 bytes from 192.168.56.4: icmp_seq=3 ttl=64 time=0.684 ms
64 bytes from 192.168.56.4: icmp_seq=4 ttl=64 time=0.710 ms

--- 192.168.56.4 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3002ms
rtt min/avg/max/mdev = 0.684/0.841/1.260/0.242 ms
trainee@kubemaster:~$ ping -c 4 www.free.fr
PING www.free.fr (212.27.48.10) 56(84) bytes of data.
64 bytes from www.free.fr (212.27.48.10): icmp_seq=1 ttl=53 time=64.6 ms
64 bytes from www.free.fr (212.27.48.10): icmp_seq=2 ttl=53 time=76.3 ms
64 bytes from www.free.fr (212.27.48.10): icmp_seq=3 ttl=53 time=75.3 ms
64 bytes from www.free.fr (212.27.48.10): icmp_seq=4 ttl=53 time=87.2 ms

--- www.free.fr ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 64.674/75.894/87.200/7.975 ms
trainee@kubenode1:~$ ping -c 4 192.168.56.2
PING 192.168.56.2 (192.168.56.2) 56(84) bytes of data.
64 bytes from 192.168.56.2: icmp_seq=1 ttl=64 time=0.605 ms
64 bytes from 192.168.56.2: icmp_seq=2 ttl=64 time=0.597 ms
64 bytes from 192.168.56.2: icmp_seq=3 ttl=64 time=0.717 ms
64 bytes from 192.168.56.2: icmp_seq=4 ttl=64 time=0.773 ms

--- 192.168.56.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3045ms
rtt min/avg/max/mdev = 0.597/0.673/0.773/0.074 ms
trainee@kubenode1:~$ ping -c 4 192.168.56.4
PING 192.168.56.4 (192.168.56.4) 56(84) bytes of data.
64 bytes from 192.168.56.4: icmp_seq=1 ttl=64 time=0.609 ms
64 bytes from 192.168.56.4: icmp_seq=2 ttl=64 time=0.494 ms
64 bytes from 192.168.56.4: icmp_seq=3 ttl=64 time=0.475 ms
64 bytes from 192.168.56.4: icmp_seq=4 ttl=64 time=0.385 ms

--- 192.168.56.4 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 0.385/0.490/0.609/0.084 ms
trainee@kubenode1:~$ ping -c 4 www.free.fr
PING www.free.fr (212.27.48.10) 56(84) bytes of data.
64 bytes from www.free.fr (212.27.48.10): icmp_seq=1 ttl=53 time=73.0 ms
64 bytes from www.free.fr (212.27.48.10): icmp_seq=2 ttl=53 time=72.5 ms
64 bytes from www.free.fr (212.27.48.10): icmp_seq=3 ttl=53 time=71.3 ms
64 bytes from www.free.fr (212.27.48.10): icmp_seq=4 ttl=53 time=70.4 ms

--- www.free.fr ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 70.466/71.831/73.024/1.005 ms
trainee@kubenode2:~$ ping -c 4 192.168.56.2
PING 192.168.56.2 (192.168.56.2) 56(84) bytes of data.
64 bytes from 192.168.56.2: icmp_seq=1 ttl=64 time=0.725 ms
64 bytes from 192.168.56.2: icmp_seq=2 ttl=64 time=0.715 ms
64 bytes from 192.168.56.2: icmp_seq=3 ttl=64 time=0.766 ms
64 bytes from 192.168.56.2: icmp_seq=4 ttl=64 time=0.716 ms

--- 192.168.56.2 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3011ms
rtt min/avg/max/mdev = 0.715/0.730/0.766/0.034 ms
trainee@kubenode2:~$ ping -c 4 192.168.56.3
PING 192.168.56.3 (192.168.56.3) 56(84) bytes of data.
64 bytes from 192.168.56.3: icmp_seq=1 ttl=64 time=0.455 ms
64 bytes from 192.168.56.3: icmp_seq=2 ttl=64 time=0.694 ms
64 bytes from 192.168.56.3: icmp_seq=3 ttl=64 time=0.704 ms
64 bytes from 192.168.56.3: icmp_seq=4 ttl=64 time=0.723 ms

--- 192.168.56.3 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 0.455/0.644/0.723/0.109 ms
trainee@kubenode2:~$ ping -c 4 www.free.fr
PING www.free.fr (212.27.48.10) 56(84) bytes of data.
64 bytes from www.free.fr (212.27.48.10): icmp_seq=1 ttl=53 time=84.5 ms
64 bytes from www.free.fr (212.27.48.10): icmp_seq=2 ttl=53 time=80.1 ms
64 bytes from www.free.fr (212.27.48.10): icmp_seq=3 ttl=53 time=75.6 ms
64 bytes from www.free.fr (212.27.48.10): icmp_seq=4 ttl=53 time=56.9 ms

--- www.free.fr ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3005ms
rtt min/avg/max/mdev = 56.993/74.368/84.598/10.513 ms

LAB #9 - Création du Cluster Kubernetes

Préparation du kubemaster

Désactivez le swap sur kubemaster :

root@kubemaster:~# swapoff -a
root@kubemaster:~# vi /etc/fstab
root@kubemaster:~# cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/sda1 during installation
UUID=3fbe79a6-9d57-4bb0-b39a-ef1c0a306807 /               ext4    errors=remount-ro 0       1
# swap was on /dev/sda5 during installation
# UID=7c2c6644-2a9f-46cd-9326-d02ce30157d4 none            swap    sw              0       0

Désactivez l'utilisation de dnsmasq par NetworkManager :

root@kubemaster:~# vi /etc/NetworkManager/NetworkManager.conf
root@kubemaster:~# cat /etc/NetworkManager/NetworkManager.conf
[main]
plugins=ifupdown,keyfile,ofono
#dns=dnsmasq

[ifupdown]
managed=false
root@kubemaster:~# systemctl restart NetworkManager
root@kubemaster:~# nmcli connection up ip_fixe

Initialisation du Maître du Cluster

Initialisez le maître du cluster kubemaster en spécifiant le CIDR de l'extension Calico ainsi que l'adresse IP du maître :

root@kubemaster:~# kubeadm init --pod-network-cidr=192.168.0.0/16 --apiserver-advertise-address=192.168.56.2
[init] Using Kubernetes version: v1.13.3
[preflight] Running pre-flight checks
[preflight] Pulling images required for setting up a Kubernetes cluster
[preflight] This might take a minute or two, depending on the speed of your internet connection
[preflight] You can also perform this action in beforehand using 'kubeadm config images pull'
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Activating the kubelet service
[certs] Using certificateDir folder "/etc/kubernetes/pki"
[certs] Generating "etcd/ca" certificate and key
[certs] Generating "etcd/server" certificate and key
[certs] etcd/server serving cert is signed for DNS names [kubemaster localhost] and IPs [192.168.56.2 127.0.0.1 ::1]
[certs] Generating "etcd/peer" certificate and key
[certs] etcd/peer serving cert is signed for DNS names [kubemaster localhost] and IPs [192.168.56.2 127.0.0.1 ::1]
[certs] Generating "etcd/healthcheck-client" certificate and key
[certs] Generating "apiserver-etcd-client" certificate and key
[certs] Generating "ca" certificate and key
[certs] Generating "apiserver-kubelet-client" certificate and key
[certs] Generating "apiserver" certificate and key
[certs] apiserver serving cert is signed for DNS names [kubemaster kubernetes kubernetes.default kubernetes.default.svc kubernetes.default.svc.cluster.local] and IPs [10.96.0.1 192.168.56.2]
[certs] Generating "front-proxy-ca" certificate and key
[certs] Generating "front-proxy-client" certificate and key
[certs] Generating "sa" key and public key
[kubeconfig] Using kubeconfig folder "/etc/kubernetes"
[kubeconfig] Writing "admin.conf" kubeconfig file
[kubeconfig] Writing "kubelet.conf" kubeconfig file
[kubeconfig] Writing "controller-manager.conf" kubeconfig file
[kubeconfig] Writing "scheduler.conf" kubeconfig file
[control-plane] Using manifest folder "/etc/kubernetes/manifests"
[control-plane] Creating static Pod manifest for "kube-apiserver"
[control-plane] Creating static Pod manifest for "kube-controller-manager"
[control-plane] Creating static Pod manifest for "kube-scheduler"
[etcd] Creating static Pod manifest for local etcd in "/etc/kubernetes/manifests"
[wait-control-plane] Waiting for the kubelet to boot up the control plane as static Pods from directory "/etc/kubernetes/manifests". This can take up to 4m0s
[apiclient] All control plane components are healthy after 29.026069 seconds
[uploadconfig] storing the configuration used in ConfigMap "kubeadm-config" in the "kube-system" Namespace
[kubelet] Creating a ConfigMap "kubelet-config-1.13" in namespace kube-system with the configuration for the kubelets in the cluster
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "kubemaster" as an annotation
[mark-control-plane] Marking the node kubemaster as control-plane by adding the label "node-role.kubernetes.io/master=''"
[mark-control-plane] Marking the node kubemaster as control-plane by adding the taints [node-role.kubernetes.io/master:NoSchedule]
[bootstrap-token] Using token: tj88c6.amxztch66b7favdx
[bootstrap-token] Configuring bootstrap tokens, cluster-info ConfigMap, RBAC Roles
[bootstraptoken] configured RBAC rules to allow Node Bootstrap tokens to post CSRs in order for nodes to get long term certificate credentials
[bootstraptoken] configured RBAC rules to allow the csrapprover controller automatically approve CSRs from a Node Bootstrap Token
[bootstraptoken] configured RBAC rules to allow certificate rotation for all node client certificates in the cluster
[bootstraptoken] creating the "cluster-info" ConfigMap in the "kube-public" namespace
[addons] Applied essential addon: CoreDNS
[addons] Applied essential addon: kube-proxy

Your Kubernetes master has initialized successfully!

To start using your cluster, you need to run the following as a regular user:

  mkdir -p $HOME/.kube
  sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  sudo chown $(id -u):$(id -g) $HOME/.kube/config

You should now deploy a pod network to the cluster.
Run "kubectl apply -f [podnetwork].yaml" with one of the options listed at:
  https://kubernetes.io/docs/concepts/cluster-administration/addons/

You can now join any number of machines by running the following on each node
as root:

  kubeadm join 192.168.56.2:6443 --token tj88c6.amxztch66b7favdx --discovery-token-ca-cert-hash sha256:44fc222fbb6f4c1bb9be01e1547e9417fd093bd38079253150e3fa78da9b30b6

Important : Notez le message Your Kubernetes master has initialized successfully!.

A Faire : Copiez dans un fichier la dernière ligne de la sortie, par exemple kubeadm join 192.168.56.2:6443 –token tj88c6.amxztch66b7favdx –discovery-token-ca-cert-hash sha256:44fc222fbb6f4c1bb9be01e1547e9417fd093bd38079253150e3fa78da9b30b6.

Créez maintenant la variable KUBECONFIG :

root@kubemaster:~# export KUBECONFIG=/etc/kubernetes/admin.conf

Installation d'une Extension Réseau

Afin que les PODs puissent communiquer entre eux, il faut installer une extension pour le réseau . Il existe plusieurs extensions sur lesquelles nous reviendrons plus tard dans ce cours :

  • Calico,
  • Cilium,
  • Flannel,
  • Kube-router,
  • Romana,
  • Weave Net,
  • JuniperContrail/TungstenFabric,
  • Canal (utilise Flannel pour le réseau et Calico pour le pare-feu).

Afin d'obtenir un cluster nous allons utiliser la première extension de la liste, à savoir Calico :

root@kubemaster:~# kubectl apply -f https://docs.projectcalico.org/v3.5/getting-started/kubernetes/installation/hosted/kubernetes-datastore/calico-networking/1.7/calico.yaml
configmap/calico-config created
daemonset.extensions/calico-node created
serviceaccount/calico-node created
customresourcedefinition.apiextensions.k8s.io/felixconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgppeers.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/bgpconfigurations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/ippools.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/hostendpoints.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/clusterinformations.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworkpolicies.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/globalnetworksets.crd.projectcalico.org created
customresourcedefinition.apiextensions.k8s.io/networkpolicies.crd.projectcalico.org created
clusterrole.rbac.authorization.k8s.io/calico-node created
clusterrolebinding.rbac.authorization.k8s.io/calico-node created

Vérifiez que tout est dans un état Running :

root@kubemaster:~# kubectl get pods --all-namespaces
NAMESPACE     NAME                                 READY   STATUS    RESTARTS   AGE
kube-system   calico-node-jrhn2                    1/1     Running   0          2m26s
kube-system   coredns-86c58d9df4-86gf9             1/1     Running   0          4m15s
kube-system   coredns-86c58d9df4-vhnvx             1/1     Running   0          4m15s
kube-system   etcd-kubemaster                      1/1     Running   0          3m33s
kube-system   kube-apiserver-kubemaster            1/1     Running   0          3m44s
kube-system   kube-controller-manager-kubemaster   1/1     Running   0          3m36s
kube-system   kube-proxy-hlp6b                     1/1     Running   0          4m15s
kube-system   kube-scheduler-kubemaster            1/1     Running   0          3m41s

Connexion des Travailleurs au Maître

Désactivez le swap sur kubenode1 et kubenode2 :

root@kubenode1:~# swapoff -a
root@kubenode1:~# vi /etc/fstab
root@kubenode1:~# cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/sda1 during installation
UUID=3fbe79a6-9d57-4bb0-b39a-ef1c0a306807 /               ext4    errors=remount-ro 0       1
# swap was on /dev/sda5 during installation
# UUID=7c2c6644-2a9f-46cd-9326-d02ce30157d4 none            swap    sw              0       0
root@kubenode2:~# swapoff -a
root@kubenode2:~# vi /etc/fstab
root@kubenode2:~# cat /etc/fstab
# /etc/fstab: static file system information.
#
# Use 'blkid' to print the universally unique identifier for a
# device; this may be used with UUID= as a more robust way to name devices
# that works even if disks are added and removed. See fstab(5).
#
# <file system> <mount point>   <type>  <options>       <dump>  <pass>
# / was on /dev/sda1 during installation
UUID=3fbe79a6-9d57-4bb0-b39a-ef1c0a306807 /               ext4    errors=remount-ro 0       1
# swap was on /dev/sda5 during installation
# UUID=7c2c6644-2a9f-46cd-9326-d02ce30157d4 none            swap    sw              0       0

Utilisez la commande sauvegardée ci-dessus pour joindre les deux noeuds au Maître :

root@kubenode1:~# kubeadm join 192.168.56.2:6443 --token tj88c6.amxztch66b7favdx --discovery-token-ca-cert-hash sha256:44fc222fbb6f4c1bb9be01e1547e9417fd093bd38079253150e3fa78da9b30b6
[preflight] Running pre-flight checks
[discovery] Trying to connect to API Server "192.168.56.2:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://192.168.56.2:6443"
[discovery] Requesting info from "https://192.168.56.2:6443" again to validate TLS against the pinned public key
[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "192.168.56.2:6443"
[discovery] Successfully established connection with API Server "192.168.56.2:6443"
[join] Reading configuration from the cluster...
[join] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet] Downloading configuration for the kubelet from the "kubelet-config-1.13" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[tlsbootstrap] Waiting for the kubelet to perform the TLS Bootstrap...
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "kubenode1" as an annotation

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.
root@kubenode2:~# kubeadm join 192.168.56.2:6443 --token tj88c6.amxztch66b7favdx --discovery-token-ca-cert-hash sha256:44fc222fbb6f4c1bb9be01e1547e9417fd093bd38079253150e3fa78da9b30b6
[preflight] Running pre-flight checks
[discovery] Trying to connect to API Server "192.168.56.2:6443"
[discovery] Created cluster-info discovery client, requesting info from "https://192.168.56.2:6443"
[discovery] Requesting info from "https://192.168.56.2:6443" again to validate TLS against the pinned public key
[discovery] Cluster info signature and contents are valid and TLS certificate validates against pinned roots, will use API Server "192.168.56.2:6443"
[discovery] Successfully established connection with API Server "192.168.56.2:6443"
[join] Reading configuration from the cluster...
[join] FYI: You can look at this config file with 'kubectl -n kube-system get cm kubeadm-config -oyaml'
[kubelet] Downloading configuration for the kubelet from the "kubelet-config-1.13" ConfigMap in the kube-system namespace
[kubelet-start] Writing kubelet configuration to file "/var/lib/kubelet/config.yaml"
[kubelet-start] Writing kubelet environment file with flags to file "/var/lib/kubelet/kubeadm-flags.env"
[kubelet-start] Activating the kubelet service
[tlsbootstrap] Waiting for the kubelet to perform the TLS Bootstrap...
[patchnode] Uploading the CRI Socket information "/var/run/dockershim.sock" to the Node API object "kubenode2" as an annotation

This node has joined the cluster:
* Certificate signing request was sent to apiserver and a response was received.
* The Kubelet was informed of the new secure connection details.

Run 'kubectl get nodes' on the master to see this node join the cluster.

A Faire : Prenez une instantanée de vos trois machines virtuelles.

LAB #10 - Création d'un POD

Présentation d'un POD

Un POD est un objet qui encapsule un conteneur. Le conteneur est un instance d'une application. La relation entre un POD et une conteneur d'application est en règle générale 1:1, c'est-à-dire que dans le cas d'une augmentation de la charge, des PODs additionnels sont créés, chacun contenant un conteneur d'application au lieu de créer plusieurs conteneurs dans le même POD.

A l'inverse, dans le cas d'une réduction de la charge, des PODs sont détruits. Avec Kubernetes, on ne crée pas de conteneurs multiples du même type dans le même POD. Par contre, il est possible d'avoirs des conteneurs de types différents dans le même POD.

Dans ce cas on parle d'un conteneur d'application et un ou des conteneur(s) Helper. Le conteneur d'application et le conteneur Helper peuvent communiquer directement parce qu'ils partagent le même espace réseau. De même, ils ont accès au même espace de stockage.

Un POD permet donc de dispenser l'administrateur de la gestion de liens Docker ainsi que des volumes.

Lors da la création d'un POD avec la commande kubectl, celle-ci télécharge l'image Docker necéssaire à la création du conteneur à partir du Docker Hub.

Création Manuelle d'un POD

Commencez par créer un POD dénommé nginx à partir de l'image nginx :

root@kubemaster:~# kubectl run --generator=run-pod/v1 nginx --image=nginx
pod/nginx created

Visualisez les PODS avec la commande kubectl :

root@kubemaster:~# kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
nginx   1/1     Running   0          8s

Consultez les informations concernant ce POD :

root@kubemaster:~# kubectl describe pods
Name:               nginx
Namespace:          default
Priority:           0
PriorityClassName:  <none>
Node:               kubenode2/10.0.2.15
Start Time:         Fri, 22 Feb 2019 14:05:35 +0100
Labels:             run=nginx
Annotations:        cni.projectcalico.org/podIP: 192.168.2.6/32
Status:             Running
IP:                 192.168.2.6
Containers:
  nginx:
    Container ID:   docker://8820884671a7ff86177acd33c25d5c22aad2693f907533f25c6bccbbcf72975a
    Image:          nginx
    Image ID:       docker-pullable://nginx@sha256:dd2d0ac3fff2f007d99e033b64854be0941e19a2ad51f174d9240dda20d9f534
    Port:           <none>
    Host Port:      <none>
    State:          Running
      Started:      Fri, 22 Feb 2019 14:05:39 +0100
    Ready:          True
    Restart Count:  0
    Environment:    <none>
    Mounts:
      /var/run/secrets/kubernetes.io/serviceaccount from default-token-v72fl (ro)
Conditions:
  Type              Status
  Initialized       True 
  Ready             True 
  ContainersReady   True 
  PodScheduled      True 
Volumes:
  default-token-v72fl:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  default-token-v72fl
    Optional:    false
QoS Class:       BestEffort
Node-Selectors:  <none>
Tolerations:     node.kubernetes.io/not-ready:NoExecute for 300s
                 node.kubernetes.io/unreachable:NoExecute for 300s
Events:
  Type    Reason     Age   From                Message
  ----    ------     ----  ----                -------
  Normal  Pulling    84s   kubelet, kubenode2  pulling image "nginx"
  Normal  Pulled     81s   kubelet, kubenode2  Successfully pulled image "nginx"
  Normal  Created    81s   kubelet, kubenode2  Created container
  Normal  Started    81s   kubelet, kubenode2  Started container
  Normal  Scheduled  79s   default-scheduler   Successfully assigned default/nginx to kubenode2

Important : Notez que la dernière ligne indique clairement que le POD tourne sur kubenode2.

Utilisez maintenant le commande kubectl avec l'option -o wide :

root@kubemaster:~# kubectl get pods -o wide
NAME    READY   STATUS    RESTARTS   AGE   IP            NODE        NOMINATED NODE   READINESS GATES
nginx   1/1     Running   0          5m    192.168.2.6   kubenode2   <none>           <none>

Important : Notez que l'adresse IP du POD est la 192.168.2.6. Cette adresse est dynamique. Si le POD s'arrête et un autre démarre, l'adresse IP du nouveau POD sera différent.

Important : Notez que dans la colonne NOMINATED NODE il est marqué <none>. En effet il est possible d'assigner un POD à un noeud spécifique grâce à l'utilisation d'une étiquette définie pour le ou les noeuds nominés. Ce sujet est traité plus en détail dans le cours Maîtriser l'Orchestration avec Kubernetes. Pour plus d'information maintenant, consultez le lien https://kubernetes.io/docs/tasks/configure-pod-container/assign-pods-nodes/.

Important : Notez que dans la colonne READINESS GATES il est marqué <none>. En effet il est possible d'assigner à un POD des conditions spécifiques pour que Kubenetes considère que le POD est dans un état de ready. Ce sujet est traité plus en détail dans le cours Maîtriser l'Orchestration avec Kubernetes. Pour plus d'information maintenant, consultez le lien https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#pod-readiness-gate.

Création d'un POD à l'aide d'un fichier YAML

Kubernetes utilise des fichiers YAML pour créer des objets. Par conséquent, la définition du POD à créer est décrite dans un fichier YAML. Créez le fichier pod-definition.yaml :

root@kubemaster:~# vi pod-definition.yaml
root@kubemaster:~# cat pod-definition.yaml 
---
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:
  containers:
    - name: nginx-container
      image: nginx

Dans ce fichier on trouve les champs suivants :

apiVersion

  • Ce champs est obligatoire,
  • La version de l'API diffère selon le type d'objet qui est créé,
  • La valeur du champs est sous la forme d'une chaîne.

kind

  • Ce champs est obligatoire,
  • La valeur de l'apiServer par rapport au type d'objet est :
kind apiVersion
Pod v1
Service v1
ReplicaSet apps/v1
Deployment apps/v1

metadata

  • Ce champs est obligatoire,
  • Il contient des informations telles le nom et les étiquettes,
  • Les informations sont sous la forme d'un dictionnaire YAML :
metadata:
  name: myapp-pod
  labels: 
    app: myapp
    type: front-end

spec

  • Ce champs est obligatoire,
  • Il contient des informations pour Kubernetes spécifiques au type d'objet à créer,
  • Les informations sont sous la forme d'un liste YAML :
spec:
  containers:
    - name: nginx-container
      image: nginx

Utilisation du Fichier YAML

Utilisez maintenant le fichier YAML afin de créer un POD :

root@kubemaster:~# kubectl create -f pod-definition.yaml 
pod/myapp-pod created
root@kubemaster:~# kubectl get pods
NAME        READY   STATUS    RESTARTS   AGE
myapp-pod   1/1     Running   0          19s
nginx       1/1     Running   0          68m

A Faire : Prenez une instantanée de vos trois machines virtuelles.

LAB #11 - Utilisation de Contrôleurs de Réplication et ReplicaSets

Contrôleurs de Réplication

Un Contrôleur de Réplication permet d'exécuter plusieurs instances du même POD de façon à offrir de la haute disponibilité au cas où l'application crash et le POD se met en échec. Même dans le cas où il n'y a qu'un seul POD, le Contrôleur de Réplication peut démarrer automatiquement un autre POD contenant l'application :

Un Contrôleur de Réplication permet aussi de démarrer de nouveaux PODs en cas d'augmentation de la charge ainsi que d'assurer l'équilibrage de la charge entre les PODs :

Dans le cas où le premier noeud venait à court de ressources, un Contrôleur de Réplication est capable de démarrer de nouveaux PODs sur un deuxième noeud :

Pour créer un Contrôleur de Réplication, il convient de créer un fichier YAML. Créez donc le fichier cr-definition.yaml :

root@kubemaster:~# vi cr-definition.yaml
root@kubemaster:~# cat cr-definition.yaml
---
apiVersion: v1
kind: ReplicationController 
metadata:
  name: myapp-cr
  labels:
    app: myapp
    type: front-end
spec:
  template:

    metadata:
      name: myapp-pod
      labels:
        app: myapp
        type: front-end
    spec:
      containers:
      - name: nginx-container
        image: nginx

  replicas: 3

Dans ce fichier est placée une section appelée template. Cette section est un gabarit pour la création de PODs supplémentaires et est indentique au contenu du fichier pod-definition.yaml sans les champs apiVersion et kind :

root@kubemaster:~# cat pod-definition.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:
  containers:
    - name: nginx-container
      image: nginx

Le champs replicas qui se trouve au même niveau de template indique le nombre de PODs à créer.

Utilisez le fichier rc-definition.yaml pour créer le Contrôleur de Réplication :

root@kubemaster:~# kubectl create -f cr-definition.yaml 
replicationcontroller/myapp-cr created

Pour visualiser le Contrôleur de Réplication, utilisez la commande suivante :

root@kubemaster:~# kubectl get replicationcontroller
NAME       DESIRED   CURRENT   READY   AGE
myapp-cr   3         3         3       71s

Pour visualiser les PODs créés par le Contrôleur de Réplication, utilisez la commande suivante :

root@kubemaster:~# kubectl get pods
NAME             READY   STATUS    RESTARTS   AGE
myapp-cr-257q6   1/1     Running   0          4m11s
myapp-cr-cvnxh   1/1     Running   0          4m11s
myapp-pod        1/1     Running   0          24h
nginx            1/1     Running   1          26h

Important : Notez que le Contrôleur de Réplication a créé deux replicas myapp-cr-257q6 et myapp-cr-cvnxh car le premier existait déjà myapp-pod. Pour indentifier un POD du même type déjà en place, le Contrôleur de Réplication se fie au champ labels dans la section template.

Supprimez maintenant le POD myapp-pod :

root@kubemaster:~# kubectl delete pod myapp-pod
pod "myapp-pod" deleted

Constatez ensuite la réaction du Contrôleur de Réplication :

root@kubemaster:~# kubectl get pods
NAME             READY   STATUS    RESTARTS   AGE
myapp-cr-257q6   1/1     Running   0          8m4s
myapp-cr-cvnxh   1/1     Running   0          8m4s
myapp-cr-g8mpp   1/1     Running   0          8s
nginx            1/1     Running   1          26h

Important : Notez que le Contrôleur de Réplication a créé le POD myapp-cr-g8mpp.

Pour consulter le statut d'un Contrôleur de Réplication, utilisez la commande suivante :

root@kubemaster:~# kubectl describe replicationcontrollers/myapp-cr
Name:         myapp-cr
Namespace:    default
Selector:     app=myapp,type=front-end
Labels:       app=myapp
              type=front-end
Annotations:  <none>
Replicas:     3 current / 3 desired
Pods Status:  3 Running / 0 Waiting / 0 Succeeded / 0 Failed
Pod Template:
  Labels:  app=myapp
           type=front-end
  Containers:
   nginx-container:
    Image:        nginx
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Events:
  Type    Reason            Age   From                    Message
  ----    ------            ----  ----                    -------
  Normal  SuccessfulCreate  36m   replication-controller  Created pod: myapp-cr-257q6
  Normal  SuccessfulCreate  36m   replication-controller  Created pod: myapp-cr-cvnxh
  Normal  SuccessfulCreate  28m   replication-controller  Created pod: myapp-cr-g8mpp

Pour suprimer un Contrôleur de Réplication, utilisez la commande suivante :

root@kubemaster:~# kubectl delete replicationcontroller myapp-cr
replicationcontroller "myapp-cr" deleted

ReplicaSets

Un ReplicaSet remplit la même fonction qu'un Contrôleur de Réplication. ReplicaSets sont la façon la plus récente de gérer la réplication.

Pour créer un ReplicaSet, créez le fichier replicaset-definition.yaml :

root@kubemaster:~# vi replicaset.definition.yaml
root@kubemaster:~# cat replicaset.definition.yaml
---
apiVersion: apps/v1
kind: ReplicaSet 
metadata:
  name: myapp-replicaset
  labels:
    app: myapp
    type: front-end
spec:
  template:

    metadata:
      name: myapp-pod
      labels:
        app: myapp
        type: front-end
    spec:
      containers:
      - name: nginx-container
        image: nginx

  replicas: 3
  selector: 
    matchLabels:
      type: front-end

Important : Notez que dans le cas d'un ReplicaSet, celui-ci identifie les PODs sous son conrtôle par la valeur du champ matchLabels..

Utilisez le fichier replicaset-definition.yaml pour créer le ReplicaSet :

root@kubemaster:~# kubectl create -f replicaset-definition.yaml 
replicaset.apps/myapp-replicaset created

Pour visualiser le ReplicaSet, utilisez la commande suivante :

root@kubemaster:~# kubectl get replicaset
NAME               DESIRED   CURRENT   READY   AGE
myapp-replicaset   3         3         3       67s

Pour visualiser les PODs créés par le ReplicaSet, utilisez la commande suivante :

root@kubemaster:~# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
myapp-replicaset-2cllb   1/1     Running   0          87s
myapp-replicaset-5nnsg   1/1     Running   0          87s
myapp-replicaset-w4prx   1/1     Running   0          87s
nginx                    1/1     Running   1          26h

Modifiez maintenant le fichier replicaset-definition.yaml en augmentant le nombre de replicas de 3 à 6 :

root@kubemaster:~# vi replicaset-definition.yaml 
root@kubemaster:~# cat replicaset-definition.yaml 
---
apiVersion: apps/v1
kind: ReplicaSet 
metadata:
  name: myapp-replicaset
  labels:
    app: myapp
    type: front-end
spec:
  template:

    metadata:
      name: myapp-pod
      labels:
        app: myapp
        type: front-end
    spec:
      containers:
      - name: nginx-container
        image: nginx

  replicas: 6 
  selector: 
    matchLabels:
      type: front-end

Exécutez ensuite la commande kubectl replace :

root@kubemaster:~# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
myapp-replicaset-2cllb   1/1     Running   0          16m
myapp-replicaset-5nnsg   1/1     Running   0          16m
myapp-replicaset-w4prx   1/1     Running   0          16m
nginx                    1/1     Running   1          26h
root@kubemaster:~# kubectl replace -f replicaset-definition.yaml 
replicaset.apps/myapp-replicaset replaced

Visualiser le ReplicaSet :

root@kubemaster:~# kubectl get replicaset
NAME               DESIRED   CURRENT   READY   AGE
myapp-replicaset   6         6         6       17m

Visualiser les PODs créés par le ReplicaSet :

root@kubemaster:~# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
myapp-replicaset-2cllb   1/1     Running   0          18m
myapp-replicaset-5nnsg   1/1     Running   0          18m
myapp-replicaset-8mw6z   1/1     Running   0          87s
myapp-replicaset-fm7mq   1/1     Running   0          87s
myapp-replicaset-q4fhf   1/1     Running   0          87s
myapp-replicaset-w4prx   1/1     Running   0          18m
nginx                    1/1     Running   1          26h

Exécutez ensuite la commande suivante :

root@kubemaster:~# kubectl scale --replicas=9 -f replicaset-definition.yaml 
replicaset.apps/myapp-replicaset scaled

Visualiser le ReplicaSet :

root@kubemaster:~# kubectl get replicaset
NAME               DESIRED   CURRENT   READY   AGE
myapp-replicaset   9         9         6       20m

Visualiser les PODs créés par le ReplicaSet :

root@kubemaster:~# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
myapp-replicaset-28lhh   1/1     Running   0          2m59s
myapp-replicaset-2cllb   1/1     Running   0          23m
myapp-replicaset-5nnsg   1/1     Running   0          23m
myapp-replicaset-8mw6z   1/1     Running   0          6m14s
myapp-replicaset-bsx9v   1/1     Running   0          2m59s
myapp-replicaset-fm7mq   1/1     Running   0          6m14s
myapp-replicaset-q4fhf   1/1     Running   0          6m14s
myapp-replicaset-vxw5c   1/1     Running   0          2m59s
myapp-replicaset-w4prx   1/1     Running   0          23m
nginx                    1/1     Running   1          26h

Notez que dans ce cas, la valeur des replicas dans le fichier replicaset-definition.yaml n'a pas été modifiée :

root@kubemaster:~# cat replicaset-definition.yaml 
---
apiVersion: apps/v1
kind: ReplicaSet 
metadata:
  name: myapp-replicaset
  labels:
    app: myapp
    type: front-end
spec:
  template:

    metadata:
      name: myapp-pod
      labels:
        app: myapp
        type: front-end
    spec:
      containers:
      - name: nginx-container
        image: nginx

  replicas: 6 
  selector: 
    matchLabels:
      type: front-end

Dernièrement, exécutez la commande suivante :

root@kubemaster:~# kubectl scale --replicas=3 replicaset myapp-replicaset
replicaset.extensions/myapp-replicaset scaled

Visualiser le ReplicaSet :

root@kubemaster:~# kubectl get replicaset
NAME               DESIRED   CURRENT   READY   AGE
myapp-replicaset   3         3         3       25m

Visualiser les PODs créés par le ReplicaSet :

root@kubemaster:~# kubectl get pods
NAME                     READY   STATUS    RESTARTS   AGE
myapp-replicaset-2cllb   1/1     Running   0          26m
myapp-replicaset-5nnsg   1/1     Running   0          26m
myapp-replicaset-w4prx   1/1     Running   0          26m
nginx                    1/1     Running   1          26h

Important : Notez que ce sont les premiers PODs créés qui sont retenus.

Créez maintenant un POD en dehors du ReplicaSet :

root@kubemaster:~# kubectl create -f pod-definition.yaml 
pod/myapp-pod created

Consultez la liste des PODs :

root@kubemaster:~# kubectl get pods
NAME                     READY   STATUS        RESTARTS   AGE
myapp-pod                0/1     Terminating   0          3s
myapp-replicaset-2cllb   1/1     Running       0          13h
myapp-replicaset-5nnsg   1/1     Running       0          13h
myapp-replicaset-w4prx   1/1     Running       0          13h
nginx                    1/1     Running       1          39h

Important : Notez que myapp-pod est dans un état Terminating. En effet le ReplicaSet ne permet pas la création d'un POD ayant la même étiquette que celle spécifiée par le champ matchLabels du fichier replicaset-definition.yaml.

Pour supprimer le ReplicaSet, utilisez la commande suivante :

root@kubemaster:~# kubectl delete replicaset myapp-replicaset
replicaset.extensions "myapp-replicaset" deleted

Consultez maintenant tous les objets du cluster :

root@kubemaster:~# kubectl get all
NAME        READY   STATUS    RESTARTS   AGE
pod/nginx   1/1     Running   1          39h

NAME                 TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/kubernetes   ClusterIP   10.96.0.1      <none>        443/TCP        47h
service/nginx        NodePort    10.97.86.167   <none>        80:31072/TCP   46h

A Faire : Prenez une instantanée de vos trois machines virtuelles.

LAB #12 - Gestion des Deployments

Un Deployment sous Kubernetes est un objet hiérarchiquement supérieur à un ReplicaSet :

Le Deployment permet la gestion des :

  • déploiements de PODs (Rollouts),
  • mises à jour roulantes (Rolling Updates),
  • retours en arrière (Rollbacks).

Avant de continuer, supprimez le service nginx et le POD nginx :

root@kubemaster:~# kubectl delete service nginx
service "nginx" deleted
root@kubemaster:~# kubectl delete pod nginx
pod "nginx" deleted

Vérifiez qu'il ne reste que l'objet ClusterIP :

root@kubemaster:~# kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2d4h

Rollouts

Pour créer un Deployment, il convient de créer un fichier YAML. Créez donc le fichier deployment-definition.yaml :

root@kubemaster:~# vi deployment-definition.yaml 
root@kubemaster:~# cat deployment-definition.yaml 
---
apiVersion: apps/v1
kind: Deployment 
metadata:
  name: myapp-deployment
  labels:
    app: myapp
    type: front-end
spec:
  template:

    metadata:
      name: myapp-pod
      labels:
        app: myapp
        type: front-end
    spec:
      containers:
      - name: nginx-container
        image: nginx

  replicas: 3
  selector: 
    matchLabels:
      type: front-end

Utilisez la commande suivante pour créer le Deployment :

root@kubemaster:~# kubectl create -f deployment-definition.yaml
deployment.apps/myapp-deployment created

Constatez la création de celui-ci :

root@kubemaster:~# kubectl get deployments
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
myapp-deployment   3/3     3            3           25s

Notez que la création du Deployment a également créé un ReplicaSet :

root@kubemaster:~# kubectl get replicasets
NAME                          DESIRED   CURRENT   READY   AGE
myapp-deployment-667d46455b   3         3         3       48s

Important : Notez que la valeur 667d46455b est générée d'une manière aléatoire en intren par Kubernetes.

Bien entendu, la création de Deployment a créé le nombre de PODs indiqué dans le fichier YAML :

root@kubemaster:~# kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
myapp-deployment-667d46455b-8hlqf   1/1     Running   0          78s
myapp-deployment-667d46455b-fgk9q   1/1     Running   0          78s
myapp-deployment-667d46455b-qrm8s   1/1     Running   0          78s

Pour voir tous ces objets en même temps, utilisez la commande kubectl get all :

root@kubemaster:~# kubectl get all
NAME                                    READY   STATUS    RESTARTS   AGE
pod/myapp-deployment-667d46455b-8hlqf   1/1     Running   0          98s
pod/myapp-deployment-667d46455b-fgk9q   1/1     Running   0          98s
pod/myapp-deployment-667d46455b-qrm8s   1/1     Running   0          98s

NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2d8h

NAME                               READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/myapp-deployment   3/3     3            3           99s

NAME                                          DESIRED   CURRENT   READY   AGE
replicaset.apps/myapp-deployment-667d46455b   3         3         3       99s

Pour obtenir plus d'informations concernant le Deployment, utilisez la commande kubectl describe :

root@kubemaster:~# kubectl describe deployments
Name:                   myapp-deployment
Namespace:              default
CreationTimestamp:      Sun, 24 Feb 2019 14:58:32 +0100
Labels:                 app=myapp
                        type=front-end
Annotations:            deployment.kubernetes.io/revision: 1
Selector:               type=front-end
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=myapp
           type=front-end
  Containers:
   nginx-container:
    Image:        nginx
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   myapp-deployment-667d46455b (3/3 replicas created)
Events:
  Type    Reason             Age   From                   Message
  ----    ------             ----  ----                   -------
  Normal  ScalingReplicaSet  2m1s  deployment-controller  Scaled up replica set myapp-deployment-667d46455b to 3

Lors du Rollout du Deployment une Révision est créée. Cette Révision est incrémentée lors de chaque mise-à-jour :

Pour consulter le statut du Rollout, il convient d'utiliser la commande suivante :

root@kubemaster:~# kubectl rollout status deployment/myapp-deployment
deployment "myapp-deployment" successfully rolled out

Pour consulter la liste des Révisions, utilisez la commande suivante :

root@kubemaster:~# kubectl rollout history deployment/myapp-deployment
deployment.extensions/myapp-deployment 
REVISION  CHANGE-CAUSE
1         <none>

Important : Notez que la valeur de CHANGE-CAUSE est <none> parce que l'option –record n'a pas été spécifiée sur la ligne de commande.

Supprimez donc le Deployment avec la commande suivante :

root@kubemaster:~# kubectl delete deployment myapp-deployment
deployment.extensions "myapp-deployment" deleted

Vérifiez la suppression du Deployment :

root@kubemaster:~# kubectl get all
NAME                 TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
service/kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   2d8h

Créez le Deployment de nouveau en ajoutant l'option –record :

root@kubemaster:~# kubectl create -f deployment-definition.yaml --record
deployment.apps/myapp-deployment created

Consultez le statut du Rollout :

root@kubemaster:~# kubectl rollout status deployment/myapp-deployment
deployment "myapp-deployment" successfully rolled out

Consultez la liste des Révisions :

root@kubemaster:~# kubectl rollout history deployment/myapp-deployment
deployment.extensions/myapp-deployment 
REVISION  CHANGE-CAUSE
1         kubectl create --filename=deployment-definition.yaml --record=true

Important : Notez que la valeur de CHANGE-CAUSE est la commande qui a été saisie.

Rolling Updates

Il existe deux méthodes de Deployment en cas de mise-à-jours :

  • Recreate,
    • Dans ce cas tous les PODs existants sont détruits en même temps et des PODs contenant la mise-à-jour sont créés dans un deuxième temps. L'inconvénient de cette méthode est évident - entre la déstruction des PODs et la re-création des nouveaux PODs, l'application n'est pas disponible,
  • Rolling Update
    • Dans ce cas, les PODs sont détruits un-par-un. Après chaque déstruction, un nouveau POD est créé contenant la mise-à-jour. De cette façon, l'application reste disponible.

Important : Notez que Rolling Update est la méthode par défaut.

Modifiez maintenant le fichier deployment-description.yaml en spécifiant la version 1.12 de nginx :

root@kubemaster:~# vi deployment-definition.yaml 
root@kubemaster:~# cat deployment-definition.yaml 
---
apiVersion: apps/v1
kind: Deployment 
metadata:
  name: myapp-deployment
  labels:
    app: myapp
    type: front-end
spec:
  template:

    metadata:
      name: myapp-pod
      labels:
        app: myapp
        type: front-end
    spec:
      containers:
      - name: nginx-container
        image: nginx:1.12

  replicas: 3 
  selector: 
    matchLabels:
      type: front-end

Appliquez ce changement :

root@kubemaster:~# kubectl apply -f deployment-definition.yaml --record
Warning: kubectl apply should be used on resource created by either kubectl create --save-config or kubectl apply
deployment.apps/myapp-deployment configured

Consultez le statut du Deployment :

root@kubemaster:~# kubectl rollout status deployment/myapp-deployment
Waiting for deployment "myapp-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "myapp-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "myapp-deployment" rollout to finish: 1 out of 3 new replicas have been updated...
Waiting for deployment "myapp-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "myapp-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "myapp-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "myapp-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "myapp-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "myapp-deployment" successfully rolled out

Notez qu'il y a maintenant une Révision supplémentaire :

root@kubemaster:~# kubectl rollout history deployment/myapp-deployment
deployment.extensions/myapp-deployment 
REVISION  CHANGE-CAUSE
1         kubectl create --filename=deployment-definition.yaml --record=true
2         kubectl apply --filename=deployment-definition.yaml --record=true

Consultez les détails du Deployment myapp-deployment :

root@kubemaster:~# kubectl describe deployment myapp-deployment
Name:                   myapp-deployment
Namespace:              default
CreationTimestamp:      Sun, 24 Feb 2019 15:03:04 +0100
Labels:                 app=myapp
                        type=front-end
Annotations:            deployment.kubernetes.io/revision: 2
                        kubectl.kubernetes.io/last-applied-configuration:
                          {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"kubernetes.io/change-cause":"kubectl apply --filename=deployment-d...
                        kubernetes.io/change-cause: kubectl apply --filename=deployment-definition.yaml --record=true
Selector:               type=front-end
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=myapp
           type=front-end
  Containers:
   nginx-container:
    Image:        nginx:1.12
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   myapp-deployment-5bb6c654c8 (3/3 replicas created)
Events:
  Type    Reason             Age    From                   Message
  ----    ------             ----   ----                   -------
  Normal  ScalingReplicaSet  5m3s   deployment-controller  Scaled up replica set myapp-deployment-667d46455b to 3
  Normal  ScalingReplicaSet  2m52s  deployment-controller  Scaled up replica set myapp-deployment-5bb6c654c8 to 1
  Normal  ScalingReplicaSet  2m12s  deployment-controller  Scaled down replica set myapp-deployment-667d46455b to 2
  Normal  ScalingReplicaSet  2m12s  deployment-controller  Scaled up replica set myapp-deployment-5bb6c654c8 to 2
  Normal  ScalingReplicaSet  100s   deployment-controller  Scaled down replica set myapp-deployment-667d46455b to 1
  Normal  ScalingReplicaSet  100s   deployment-controller  Scaled up replica set myapp-deployment-5bb6c654c8 to 3
  Normal  ScalingReplicaSet  94s    deployment-controller  Scaled down replica set myapp-deployment-667d46455b to 0

Important : Notez que l'image utilisée est bien la nginx:1.12. Notez ensuite que dans la section Events, les PODs ont été Scaled down un-par-un et Scaled up un-par-un. Notez aussi que la valeur de StrategyType peut être soit Recreate soit RollingUpdate. Dernièrement, notez la valeur de RollingUpdateStrategy. 25% max unavailable indique qu'à un instant “t” 75% des PODs doivent être disponibles tandis que 25% max surge indique le nombre total des PODs ne peut pas dépasser 1,25 fois la valeur du champ Replicas.

Lors de la mise-à-jour le Deployment crée un autre ReplicaSet contenant les PODs mis-à-jour en suivant la méthode Rolling Update. Ceci peut être vu en regardant la sortie de la commande kubectl get replicasets :

 
root@kubemaster:~# kubectl get replicasets
NAME                          DESIRED   CURRENT   READY   AGE
myapp-deployment-5bb6c654c8   3         3         3       3m34s
myapp-deployment-667d46455b   0         0         0       5m45s

La modification de la version de l'image peut aussi être effectuée sur la ligne de commande :

root@kubemaster:~# kubectl set image deployment/myapp-deployment nginx-container=nginx:1.14 --record
deployment.extensions/myapp-deployment image updated

Le nom du conteneur nginx-container est défini dans le fichier de définition du POD :

root@kubemaster:~# cat pod-definition.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:
  containers:
    - name: nginx-container
      image: nginx

Consultez le statut du Deployment :

root@kubemaster:~# kubectl rollout status deployment/myapp-deployment
Waiting for deployment "myapp-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "myapp-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "myapp-deployment" rollout to finish: 2 out of 3 new replicas have been updated...
Waiting for deployment "myapp-deployment" rollout to finish: 1 old replicas are pending termination...
Waiting for deployment "myapp-deployment" rollout to finish: 1 old replicas are pending termination...
deployment "myapp-deployment" successfully rolled out

Notez qu'il y a maintenant une Révision supplémentaire :

root@kubemaster:~# kubectl rollout history deployment/myapp-deployment
deployment.extensions/myapp-deployment 
REVISION  CHANGE-CAUSE
1         kubectl create --filename=deployment-definition.yaml --record=true
2         kubectl apply --filename=deployment-definition.yaml --record=true
3         kubectl set image deployment/myapp-deployment nginx-container=nginx:1.14 --record=true

Lors de la mise-à-jour le Deployment crée un autre ReplicaSet contenant les PODs mis-à-jour en suivant la méthode Rolling Update. Ceci peut être vu en regardant la sortie de la commande kubectl get replicasets :

 
root@kubemaster:~# kubectl get replicasets
NAME                          DESIRED   CURRENT   READY   AGE
myapp-deployment-5bb6c654c8   0         0         0       6m8s
myapp-deployment-5c4b64dd4    3         3         3       2m1s
myapp-deployment-667d46455b   0         0         0       8m19s

Consultez les détails du Deployment myapp-deployment :

root@kubemaster:~# kubectl describe deployment myapp-deployment
Name:                   myapp-deployment
Namespace:              default
CreationTimestamp:      Sun, 24 Feb 2019 15:03:04 +0100
Labels:                 app=myapp
                        type=front-end
Annotations:            deployment.kubernetes.io/revision: 3
                        kubectl.kubernetes.io/last-applied-configuration:
                          {"apiVersion":"apps/v1","kind":"Deployment","metadata":{"annotations":{"kubernetes.io/change-cause":"kubectl apply --filename=deployment-d...
                        kubernetes.io/change-cause: kubectl set image deployment/myapp-deployment nginx-container=nginx:1.14 --record=true
Selector:               type=front-end
Replicas:               3 desired | 3 updated | 3 total | 3 available | 0 unavailable
StrategyType:           RollingUpdate
MinReadySeconds:        0
RollingUpdateStrategy:  25% max unavailable, 25% max surge
Pod Template:
  Labels:  app=myapp
           type=front-end
  Containers:
   nginx-container:
    Image:        nginx:1.14
    Port:         <none>
    Host Port:    <none>
    Environment:  <none>
    Mounts:       <none>
  Volumes:        <none>
Conditions:
  Type           Status  Reason
  ----           ------  ------
  Available      True    MinimumReplicasAvailable
  Progressing    True    NewReplicaSetAvailable
OldReplicaSets:  <none>
NewReplicaSet:   myapp-deployment-5c4b64dd4 (3/3 replicas created)
Events:
  Type    Reason             Age                    From                   Message
  ----    ------             ----                   ----                   -------
  Normal  ScalingReplicaSet  11m                    deployment-controller  Scaled up replica set myapp-deployment-667d46455b to 3
  Normal  ScalingReplicaSet  9m12s                  deployment-controller  Scaled up replica set myapp-deployment-5bb6c654c8 to 1
  Normal  ScalingReplicaSet  8m32s                  deployment-controller  Scaled down replica set myapp-deployment-667d46455b to 2
  Normal  ScalingReplicaSet  8m32s                  deployment-controller  Scaled up replica set myapp-deployment-5bb6c654c8 to 2
  Normal  ScalingReplicaSet  8m                     deployment-controller  Scaled down replica set myapp-deployment-667d46455b to 1
  Normal  ScalingReplicaSet  8m                     deployment-controller  Scaled up replica set myapp-deployment-5bb6c654c8 to 3
  Normal  ScalingReplicaSet  7m54s                  deployment-controller  Scaled down replica set myapp-deployment-667d46455b to 0
  Normal  ScalingReplicaSet  5m5s                   deployment-controller  Scaled up replica set myapp-deployment-5c4b64dd4 to 1
  Normal  ScalingReplicaSet  4m45s                  deployment-controller  Scaled down replica set myapp-deployment-5bb6c654c8 to 2
  Normal  ScalingReplicaSet  4m20s (x4 over 4m45s)  deployment-controller  (combined from similar events): Scaled down replica set myapp-deployment-5bb6c654c8 to 0

Important : Notez que l'image utilisée est bien la nginx:1.14.

Rollbacks

Grâce au système des Révisions, il est possible de revenir en arrière vers la version précédente N-1 de l'application. Saisissez la commande suivante :

root@kubemaster:~# kubectl rollout undo deployment/myapp-deployment
deployment.extensions/myapp-deployment rolled back

Saisissez la commande kubectl get replicasets :

root@kubemaster:~# kubectl get replicasets
NAME                          DESIRED   CURRENT   READY   AGE
myapp-deployment-5bb6c654c8   3         3         3       11m
myapp-deployment-5c4b64dd4    0         0         0       7m18s
myapp-deployment-667d46455b   0         0         0       13m

Important : Notez que l'application est revenue à la version précédente.

Utilisez la commande kubectl rollout history :

root@kubemaster:~# kubectl rollout history deployment/myapp-deployment
deployment.extensions/myapp-deployment 
REVISION  CHANGE-CAUSE
1         kubectl create --filename=deployment-definition.yaml --record=true
3         kubectl set image deployment/myapp-deployment nginx-container=nginx:1.14 --record=true
4         kubectl apply --filename=deployment-definition.yaml --record=true

Important : Notez que Révision 2 est devenue la Révision 4 démontrant ainsi le Rollback.

Créez maintenant une erreur d'un Rollout :

root@kubemaster:~# kubectl set image deployment/myapp-deployment nginx-container=nginx1.14 --record
deployment.extensions/myapp-deployment image updated

Important : Notez que l'erreur est nginx1.14 qui devrait être nginx:1.14.

Constatez le statut du Deployment :

root@kubemaster:~# kubectl rollout status deployment/myapp-deployment
Waiting for deployment "myapp-deployment" rollout to finish: 1 out of 3 new replicas have been updated...

Important : Notez que le Rollout est bloqué. L'erreur error: deployment “myapp-deployment” exceeded its progress deadline va être retournée au bout d'une dixaine de minutes !

Pour visualiser ce qui se passe, utilisez la commande kubectl get deployments :

^Croot@kubemaster:~# kubectl get deployments
NAME               READY   UP-TO-DATE   AVAILABLE   AGE
myapp-deployment   3/3     1            3           24m

La commande kubectl get pods démontre un statut de ImagePullBackOff pour le premier POD dans le nouveau ReplicaSet qui indique que Kubernetes ne peut pas effectuer le pull de l'image à partir de Docker Hub :

root@kubemaster:~# kubectl get pods
NAME                                READY   STATUS             RESTARTS   AGE
myapp-deployment-5bb6c654c8-q6mk9   1/1     Running            0          17m
myapp-deployment-5bb6c654c8-qq5pj   1/1     Running            0          17m
myapp-deployment-5bb6c654c8-zz579   1/1     Running            0          17m
myapp-deployment-7fb978884b-7mgnn   0/1     ImagePullBackOff   0          12m

En consultant l'historique du Rollout, une Révision supplémentaire a été ajoutée suite à la commande en erreur :

root@kubemaster:~# kubectl rollout history deployment/myapp-deployment
deployment.extensions/myapp-deployment 
REVISION  CHANGE-CAUSE
1         kubectl create --filename=deployment-definition.yaml --record=true
3         kubectl set image deployment/myapp-deployment nginx-container=nginx:1.14 --record=true
4         kubectl apply --filename=deployment-definition.yaml --record=true
5         kubectl set image deployment/myapp-deployment nginx-container=nginx1.14 --record=true

Pour rectifier cette erreur il convient de faire un Rollback :

root@kubemaster:~# kubectl rollout undo deployment/myapp-deployment
deployment.extensions/myapp-deployment rolled back

Constatez ensuite la réussite de la commande :

root@kubemaster:~# kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
myapp-deployment-5bb6c654c8-q6mk9   1/1     Running   0          24m
myapp-deployment-5bb6c654c8-qq5pj   1/1     Running   0          24m
myapp-deployment-5bb6c654c8-zz579   1/1     Running   0          24m
root@kubemaster:~# kubectl rollout history deployment/myapp-deployment
deployment.extensions/myapp-deployment 
REVISION  CHANGE-CAUSE
1         kubectl create --filename=deployment-definition.yaml --record=true
3         kubectl set image deployment/myapp-deployment nginx-container=nginx:1.14 --record=true
5         kubectl set image deployment/myapp-deployment nginx-container=nginx1.14 --record=true
6         kubectl apply --filename=deployment-definition.yaml --record=true

A Faire : Prenez une instantanée de vos trois machines virtuelles.

LAB #13 - Gestion du Réseau et des Services

Kubernetes impose des conditions pour l'implmentation d'un réseau :

  • Les PODs sur un noeud peuven communiquer avec tous les PODs sur tous le noeuds sans utiliset NAT,
  • Les agents sur un noeud (par exemple kubelet) peuvent communiquer avec tous les PODs sur le noeud.

Important : La description technique et détaillée de l'approche réseau de Kubernetes peut être consultée à l'adresse : https://kubernetes.io/docs/concepts/cluster-administration/networking/.

Dans le cluster de ce cours, le réseau mis en place pour Kubernetes est le 192.168.56.0/24 :

root@kubemaster:~# ifconfig enp0s8
enp0s8    Link encap:Ethernet  HWaddr 08:00:27:85:a2:9d  
          inet addr:192.168.56.2  Bcast:192.168.56.255  Mask:255.255.255.0
          inet6 addr: fe80::144f:5340:f32:34f8/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:533192 errors:0 dropped:0 overruns:0 frame:0
          TX packets:773213 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:49793997 (49.7 MB)  TX bytes:821628826 (821.6 MB)
root@kubenode1:~# ifconfig enp0s8
enp0s8    Link encap:Ethernet  HWaddr 08:00:27:73:26:89  
          inet addr:192.168.56.3  Bcast:192.168.56.255  Mask:255.255.255.0
          inet6 addr: fe80::3fe7:3080:6d6a:171b/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:391942 errors:0 dropped:0 overruns:0 frame:0
          TX packets:273657 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:291617590 (291.6 MB)  TX bytes:25364500 (25.3 MB)
root@kubenode2:~# ifconfig enp0s8
enp0s8    Link encap:Ethernet  HWaddr 08:00:27:85:f3:3c  
          inet addr:192.168.56.4  Bcast:192.168.56.255  Mask:255.255.255.0
          inet6 addr: fe80::779b:ad9f:2e18:9ec2/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:193701 errors:0 dropped:0 overruns:0 frame:0
          TX packets:130808 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:146512826 (146.5 MB)  TX bytes:12439676 (12.4 MB)

Actuellement il y a 3 PODs dans le cluster :

root@kubemaster:~# kubectl get pods
NAME                                READY   STATUS    RESTARTS   AGE
myapp-deployment-5bb6c654c8-q6mk9   1/1     Running   0          17h
myapp-deployment-5bb6c654c8-qq5pj   1/1     Running   0          17h
myapp-deployment-5bb6c654c8-zz579   1/1     Running   0          17h

En sachant que le réseau par défaut de Docker est bridge, constatons que les conteneurs dans ces PODs ne sont pas dans ce réseau :

root@kubemaster:~# docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "3dba8dc1f10d2902e5ec5c4130082950ccbdb9c9efc3b64b78edd57d5914bbf7",
        "Created": "2019-02-21T12:04:40.654203509+01:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {},
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

Important : Notez que cette sortie démontre que la section “Containers”: {}, est vide.

En effet sous Kubernetes, les adresses IP ne sont pas attachées aux conteneurs mais aux PODs :

root@kubemaster:~# kubectl get pods -o wide
NAME                                READY   STATUS    RESTARTS   AGE   IP             NODE        NOMINATED NODE   READINESS GATES
myapp-deployment-5bb6c654c8-q6mk9   1/1     Running   0          40m   192.168.1.30   kubenode1   <none>           <none>
myapp-deployment-5bb6c654c8-qq5pj   1/1     Running   0          40m   192.168.1.31   kubenode1   <none>           <none>
myapp-deployment-5bb6c654c8-zz579   1/1     Running   0          40m   192.168.2.31   kubenode2   <none>           <none>

Important : Notez que les adresses 192.168.1.x sont associées aux PODs sur kubenode1 tandis que les adresses 192.168.2.x sont associées aux PODs sur kubenode2. Ces adresses sont issues du réseau 192.168.0.0/16 stipulé par l'option –pod-network-cidr lors de l'initialisation du maître du cluster.

En sachant que dans chaque POD existe un conteneur Nginx, testez si vous pouvez afficher la page d'accueil de Nginx en vous connectant à kubenode1 et kubenode2 à partir de votre machine hôte soit avec un navigateur soit en ligne de commande :

trainee@kubernetes:~$ curl 192.168.56.3
curl: (7) Failed to connect to 192.168.56.3 port 80: Connexion refusée
trainee@kubernetes:~$ curl 192.168.56.4
curl: (7) Failed to connect to 192.168.56.4 port 80: Connexion refusée

Important : Notez l'échec de la connexion.

Testez maintenant si vous pouvez afficher la page d'accueil de Nginx en vous connectant à un des PODs à partir de votre machine hôte soit avec un navigateur soit en ligne de commande :

trainee@kubernetes:~$ curl 192.168.1.30
curl: (7) Failed to connect to 192.168.1.30 port 80: Aucun chemin d'accès pour atteindre l'hôte cible

Bien évidement, il est possible d'afficher la page en vous connectant à un des PODs de l'intérieur du cluster :

trainee@kubernetes:~$ ssh 192.168.56.2
The authenticity of host '192.168.56.2 (192.168.56.2)' can't be established.
ECDSA key fingerprint is SHA256:pBg4jSkCMVZguOzmJjsqUjKfusz757jTqrvbDpISIXg.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '192.168.56.2' (ECDSA) to the list of known hosts.
trainee@192.168.56.2's password: 
Welcome to Ubuntu 16.04.5 LTS (GNU/Linux 4.4.0-142-generic x86_64)

 * Documentation:  https://help.ubuntu.com
 * Management:     https://landscape.canonical.com
 * Support:        https://ubuntu.com/advantage

33 packages can be updated.
0 updates are security updates.

Last login: Sun Feb 24 14:56:54 2019 from 10.0.2.2
trainee@kubemaster:~$ curl 192.168.1.30
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Important : Retenez donc qu'à ce stade il n'est pas possible d'afficher la page d'accueil de Nginx en vous connectant de l'extérieur du cluster.

Lors de l'installation du cluster nous avons spécifié l'utilisation d'une extension réseau appelée Calico, issue de la liste suivante :

Important : Une étude comparitive des extensions réseau pour Kubernetes peut être trouvée à la page : https://itnext.io/benchmark-results-of-kubernetes-network-plugins-cni-over-10gbit-s-network-36475925a560.

Ces extensions permettent la mise en place de Services :

  • NodePort,
    • Ce Service rend un POD accessible sur un port du Noeud le contenant,
  • ClusterIP
    • Ce Service crée une adresse IP virtuelle afin de permettre la communication entre de services différents dansle cluster, par exemple des serveurs front-end avec des serveurs back-end,
  • LoadBalancer
    • Ce service provisionne un equilibrage de charge pour l'application dans certains fournisseurs Cloud.

Le Service NodePort

Le Service NodePort définit trois ports :

  • TargetPort : le port sur le POD,
  • Port : le port sur le Service lié à un IP du Cluster,
  • NodePort : le port sur le Noeud issu de la plage 30000-32767.

Si dans le même noeud, plusieurs PODs ont les étiquettes qui correspondent au selector du Service, le Service identifie les PODs et s'étend automatiquement pour englober tous les PODs. Les PODs sont appelés des End-Points :

Important : Notez que dans ce cas l'équilibrage de charge est automatique est utilise l'algorythme Random avec une affinity de session..

De même, quand les PODs sont distribués sur plusieurs noeuds, le Service s'étend pour tout englober :

Créez donc le fichier YAML service-definition.yaml :

root@kubemaster:~# vi service-definition.yaml
root@kubemaster:~# cat service-definition.yaml
apiVersion: v1
kind: Service
metadata:
  name: myapp-service

spec:
  type: NodePort
  ports:
    - targetPort: 80
      port: 80
      nodePort: 30008
  selector: 
    app: myapp
    type: front-end

Important : Notez que la valeur par défaut du champ type est ClusterIP. Notez aussi que dans ports, seul le champ port est obligatoire. Si le champ targetPort est manquant, sa valeur par défaut est celle du champ port. Si le champ nodePort est manquant, sa valeur par défaut est le premier port disponible dans la plage 30 000 et 32 767. Dernièrement, il est possble de spécifier de multiples définitions de ports dans le service.

Le champs selector contient les étiquettes des PODs concernés par la mise en place du Service :

root@kubemaster:~# cat pod-definition.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
    type: front-end
spec:
  containers:
    - name: nginx-container
      image: nginx

Créez le Service en utilisant le fichier service-definition.yaml :

root@kubemaster:~# kubectl create -f service-definition.yaml 
service/myapp-service created

Constatez la création du Service :

root@kubemaster:~# kubectl get services
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
kubernetes      ClusterIP   10.96.0.1       <none>        443/TCP        2d9h
myapp-service   NodePort    10.109.119.46   <none>        80:30008/TCP   57s

Important : Notez que le Service a une adresse IP du cluster et qu'il a exposé le port 30 008.

Testez maintenant si vous pouvez afficher la page d'accueil de Nginx en vous connectant à un des PODs à partir de votre machine hôte en utilisant le port exposé soit avec un navigateur soit en ligne de commande :

trainee@kubernetes:~$ curl 192.168.56.3:30008
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

trainee@kubernetes:~$ curl 192.168.56.4:30008
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
    body {
        width: 35em;
        margin: 0 auto;
        font-family: Tahoma, Verdana, Arial, sans-serif;
    }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
</body>
</html>

Le Service ClusterIP

Le Service ClusterIP permet de regrouper les PODs offrant le même service afin de faciliter la communication, par exemple :

  • 3 PODs front-end = une adresse ClusterIP,
  • 3 PODs back-end = une autre adresse ClusterIP.

Pour créer un Service ClusterIP, créez le fichier clusterip-definition.yaml :

root@kubemaster:~# vi clusterip-definition.yaml 
root@kubemaster:~# cat clusterip-definition.yaml 
apiVersion: v1
kind: Service
metadata:
  name: back-end 

spec:
  type: ClusterIP 
  ports:
    - targetPort: 80
      port: 80
  selector: 
    app: myapp
    type: front-end

Créez le Service en utilisant le fichier clusterip-definition.yaml :

root@kubemaster:~# kubectl create -f clusterip-definition.yaml 
service/back-end created

Vérifiez maintenant la présence du Service :

root@kubemaster:~# kubectl get services
NAME            TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)        AGE
back-end        ClusterIP   10.96.138.47    <none>        80/TCP         31s
kubernetes      ClusterIP   10.96.0.1       <none>        443/TCP        2d10h
myapp-service   NodePort    10.109.119.46   <none>        80:30008/TCP   29m

LAB #14 - Gestion de l'Architecture des Microservices

Menu