Installer Kubernetes : passer des serveurs physiques aux conteneurs


Thu 03 March 2016 Par Rémi Cattiau

Pendant des années, j'ai utilisé des serveurs physiques proposant des services basiques tels que Apache, Bind, MySQL et PHP. Il m'arrivait de temps en temps de migrer un serveur car un autre fournisseur pouvait m'offrir un meilleur prix. Mais la mise à niveau pouvait être compliquée lorsque plusieurs sites Web étaient hébergés sur ce serveur, car certains d'entre eux étaient basés sur des versions spécifiques de PHP. Pour ces raisons, je finissais généralement par garder certains serveurs pour m'éviter d'avoir à les migrer. C'est un scénario que beaucoup d'entre vous doivent connaître !

Cela fait quelques temps que je suis le développement de l'écosystème Docker et je savais que mes problèmes seraient résolus si je me décidais à migrer vers un système de conteneurs. J'ai alors décidé de migrer 3 serveurs Gentoo hébergés en France pour passer à 2 serveurs Debian hébergés aux États-Unis et 1 serveur Gentoo hébergé en France.

KubernetesJe vais vous expliquer aujourd'hui comment installer Docker et gérer les conteneurs à l'aide de Kubernetes.

Il existe plusieurs systèmes pouvant gérer les conteneurs : Amazon EC2 Container Service, Rancher, Kubernetes, etc. J'ai choisi Kubernetes car il peut être installé sur plusieurs environnements et n'impose pas de garder un seul fournisseur. Nous utilisons également Kubernetes chez Nuxeo.

Installer Docker


La première étape est plutôt simple.

Installer Docker sur Debian :

apt-get install docker.io

Installer Docker sur Gentoo :

emerge -v docker

L'étape suivante est d'installer Kubernetes.

Installer Kubernetes


Si vous passez par une procédure d'installation de Docker simple, vous n'aurez pas de cluster et vous ne pourrez pas bénéficier des avantages de Kubernetes.

Le docker multiple est une meilleure solution. Il utilise un service Docker pour exécuter etcd et flannel afin d'autoriser le partage du réseau et de permettre à Kubernetes de stocker et de partager sa configuration.

[email protected]:/home/shared# etcdctl member list
eacd7f155934262: name=b5.loopingz.com peerURLs=http://91.121.82.118:2380 clientURLs=http://91.121.82.118:2379,http://91.121.82.118:4001
2f0f8b2f17fffe3c: name=c2.loopingz.com peerURLs=http://198.245.51.134:2380 clientURLs=http://198.245.51.134:2379,http://198.245.51.134:4001
88314cdfe9bc1797: name=default peerURLs=http://142.4.214.129:2380 clientURLs=http://142.4.214.129:2379,http://142.4.214.129:4001

Vous disposez maintenant de réseaux différents :


  • 0.0.0/16 représente les services Kubernetes / équilibreurs de charge

  • 1.0.0/16 est l'endroit où les pods seront créés. Chaque noeud du cluster aura un 10.1.xx.0/24 pour ses pods

flannel.1 Link encap:Ethernet  HWaddr c2:67:be:06:2c:11
inet addr:10.1.72.0 Bcast:0.0.0.0 Mask:255.255.0.0
inet6 addr: fe80::c067:beff:fe06:2c11/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1450 Metric:1
RX packets:5412360 errors:0 dropped:0 overruns:0 frame:0
TX packets:4174530 errors:0 dropped:21 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:1651767659 (1.5 GiB) TX bytes:3453452284 (3.2 GiB)

Installer GlusterFS


En fonction de la configuration de vos pods et du node selector, les conteneurs peuvent être créés sur n'importe quel noeud du cluster. Cela signifie que vous avez besoin de répartir votre stockage entre les noeuds. Vous disposez de différentes solutions, telles qu'Amazon EFS (toujours en bêta), Google Cloud Storage, GlusterFS, etc.

GlusterFS est une solution ouverte de stockage partagé. Elle fonctionne comme un programme fantôme et utilise le port UDP 24007. Dans mon prochain article de blog je parlerai plus en détail de l'installation de GlusterFS et des benchmarks réalisés.

Installer les règles de pare-feu


Pour mettre à jour le pare-feu sur tous les serveurs simultanément, j'ai créé un petit script shell qui détecte les modifications sur le noeud etcd et met à jour le pare-feu en fonction des règles et des noeuds du cluster.

Mettre à jour la configuration du pare-feu


Le pare-feu est configuré grâce au fichiera fw.conf, partagé sur un volume GlusterFS.

#!/bin/sh
MD5_TARGET=md5sum /home/shared/configs/firewall/fw.conf | awk '{print $1}'
MD5_NEW=md5sum fw.conf | awk '{print $1}'
if [ "$MD5_TARGET" == "$MD5_NEW" ]; then
echo "Not config change"
exit 0
fi
cp fw.conf /home/shared/configs/firewall/
etcdctl set /cluster/firewall/update $MD5_NEW

La commande curl avec ?wait=true expire uniquement lorsque la valeur est modifiée par le script ci-dessus. Le pare-feu de l'hôte est alors mis à jour :

while :
do
curl -L http://127.0.0.1:4001/v2/keys/cluster/firewall/update?wait=true
NEW_HASH=etcdctl get /cluster/firewall/update
if [ "$NEW_HASH" != "$FW_HASH" ]; then
echo "Update the firewall"
source /usr/local/bin/firewall_builder
FW_HASH=$NEW_HASH
fi
done

Installer le repository Docker


Il est préférable de disposer de votre propre repository de conteneurs pour stocker la définition des conteneurs qui doit être utilisée avec Kubernetes.

Utilisons donc Kubernetes pour déployer notre premier :

Le format par défaut est YAML, mais j'ai une préférence pour JSON.

Voici le fichier docker-rc.json

{
"apiVersion": "v1",
"kind": "ReplicationController",
"metadata": {
"name": "docker-repository",
"labels": {
"app": "docker-repository",
"version": "v1"
}
},
"spec": {
"replicas": 1,
"selector": {
"app": "docker-repository",
"version": "v1"
},
"template": {
"metadata": {
"labels": {
"app": "docker-repository",
"version": "v1"
}
},
"spec": {
"volumes": [
{
"name": "config",
"hostPath": { "path": "/home/shared/configs/docker" }
},{
"name": "data",
"hostPath": { "path": "/home/shared/docker" }
}
],
"containers": [
{
"name": "registry",
"image": "registry:2.2.1",
"volumeMounts": [{"name":"config", "mountPath":"/etc/docker/"},{"name":"data", "mountPath": "/var/lib/registry
"}],
"resources": {
"limits": {
"cpu": "100m",
"memory": "50Mi"
},
"requests": {
"cpu": "100m",
"memory": "50Mi"
}
},
"ports": [
{
"containerPort": 5000
}
]
}
]
}
}
}
}

Et le fichier docker-svc.json

{
"apiVersion": "v1",
"kind": "Service",
"metadata": {
"name": "docker-repository",
"labels": {
"app": "docker-repository"
}
},
"spec": {
"type": "LoadBalancer",
"selector": {
"app": "docker-repository"
},
"clusterIP": "10.0.0.204",
"ports": [
{
"protocol": "TCP",
"port": 5000,
"targetPort": 5000
}
]
}
}

Installer un proxy Nginx


Pour pouvoir héberger plusieurs domaines sur votre serveur, il vous faut un proxy inverse. L'installation de Nginx n'est pas compliquée, il vous suffit de faire attention à bien ajouter certains en-têtes :

Dans mon cas, le premier hôte sera docker.loopingz.com

server {
listen 443;
server_name docker.loopingz.com;
access_log /var/log/nginx/docker.loopingz.com_access_log main;
error_log /var/log/nginx/docker.loopingz.com_error_log info;
client_max_body_size 0;
chunked_transfer_encoding on;
location / {
include /etc/nginx/conf.d/dev-auth;
proxy_pass http://10.0.0.204:5000;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
add_header 'Docker-Distribution-Api-Version' 'registry/2.0' always;
}
ssl_ciphers ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:AES:CAMELLIA:DES-CBC3-SHA:!aNULL:!eNULL:!EXPORT:!DES:!RC4:!MD5:!PSK:!aECDH:!EDH-DSS-DES-CBC3-SHA:!EDH-RSA-DES-CBC3-SHA:!KRB5-DES-CBC3-SHA:!CAMELLIA;
ssl_prefer_server_ciphers on;
ssl_certificate /etc/letsencrypt/live/docker.loopingz.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/docker.loopingz.com/privkey.pem;
}

Vous pouvez voir dans ce fichier que nous utilisons l'IP 10.0.0.204, qui est l'IP du cluster définie dans le fichier docker-svc.json. Kubernetes associe cette IP à nos conteneurs Docker.

Je veux que Nginx soit déployé sur tous les noeuds du cluster pour avoir un point d'entrée http/https vers le cluster à partir de tous les noeuds. Définissons donc le replicationController de Nginx.

{
"apiVersion": "v1",
"kind": "ReplicationController",
"metadata": {
"name": "nginx",
"labels": {
"app": "nginx",
"version": "v1"
}
},
"spec": {
"replicas": 3,
"selector": {
"app": "nginx",
"version": "v1"
},
"template": {
"metadata": {
"labels": {
"app": "nginx",
"version": "v1"
}
},
"spec": {
"volumes": [
{
"name": "config",
"hostPath": { "path": "/home/shared/configs/nginx/" }
},{
"name": "logs",
"hostPath": { "path": "/home/shared/logs/nginx/" }
},{
"name": "certs",
"hostPath": { "path": "/home/shared/letsencrypt/" }
},{
"name": "static",
"hostPath": { "path": "/home/shared/nginx/" }
}
],
"containers": [
{
"name": "nginx",
"image": "nginx:latest",
"volumeMounts": [{"name": "static", "readOnly": true, "mountPath": "/var/www/"},{"name": "certs", "readOnly": true, "mountPath": "/etc/letsencrypt"}, {"name":"logs", "mountPath":"/var/log/nginx/"},{"name":"config", "readOnly": true, "mountPath":"/etc/nginx/conf.d/"}],
"resources": {
"limits": {
"cpu": "100m",
"memory": "50Mi"
},
"requests": {
"cpu": "100m",
"memory": "50Mi"
}
},
"ports": [
{
"containerPort": 80,
"hostPort": 80
},{
"containerPort": 443,
"hostPort": 443
}
]
}
]
}
}
}
}

Let's Encrypt


Pour autoriser SSL sur l'ensemble de l'hôte virtuel, j'utilise désormais Let's encrypt, un outil qui permet d'obtenir un certificat SSL gratuit pour une période de 3 mois. La commande est automatisée, donc les hôtes Nginx ont tous ce code dans leur configuration :

location /.well-known/acme-challenge {
add_header "Content-Type:" "application/jose+json" always;
root /etc/nginx/conf.d;
}

Vous avez désormais toutes les informations nécessaires pour installer Kubernetes, alors n'hésitez pas à essayer ! J'aborderai dans un prochain article les benchmarks sur GlusterFS et la création de nouveaux services !


Tagged: Docker, GlusterFS, How to, kubernetes