Annexe 3: Automatisation du déploiement du cluster avec Vagrant et Ansible
- Présentation des outils utilisés
- Déploiement d’un cluster Docker Swarm avec Ansible sur le banc de test
- Structure du banc de test vagrant
- Fichier de configuration pour mettre en place un cluster Docker Swarm
- Automatisation de l’initialisation du cluster avec Ansible
- Lancement de l’infrastructure
- Finalisation de l’infrastructure
- Configuration client server NFS
- Proxy sur les workers
- Pour une utilisation hors de Vagrant
Note : A l'origine, mon brevet devait intégrer une automatisation du déploiement des cluster Docker Swarm. Toutefois, ce mécanisme s'est montré inutile tant le déploiement du cluster Docker Swarm est une tâche rapide à effectuer. Ce mécanisme ne trouverait de plus sont utilité que dans le cadre d'une infrastructure permettant le déploiement de cluster Docker et non le déploiement d'application sur un cluster Docker. De plus, les outils de gestion d'hyperviseur intègre de plus en plus souvent des outils permettant le déploiement de cluster Docker. J'ajouterais sont usage en tant que banc de test pour le déploiement d'applications n'est plus nécessaire puisqu'il est possible de déployer en une quinzaine de minutes un cluster Docker avec un seul hôte sur n'importe quelle machine Linux. Il retrouverait cette utilité s'il était possible d'utiliser Vagrant pour déployer directement des machines virtuelles via l'infrastructure Open Nebula de SIPR. J'ajouterais également que l'installation des outils nécessaires au banc de test (Vagrant, Virtual Box ou un autre Hyperviseur) sont plus long que l'installation de Docker lui-même sur la machine. J'ai toutefois laissé les notes que j'avais prises pour cette partie de mon travail en annexe afin qu'elles ne soient pas perdues pour un futur usage.
Présentation des outils utilisés
Vagrant
Introduction et concepts
D'après la Wikipedia1 :
Vagrant est un logiciel libre et open-source pour la création et la configuration des environnements de développement virtuel. Il peut être considéré comme un wrapper autour de logiciels de virtualisation comme VirtualBox.
[...] Vagrant n'impose plus VirtualBox, mais fonctionne également avec d'autres logiciels de virtualisation tels que VMware, et prend en charge les environnements de serveurs comme Amazon EC2, à condition d'utiliser une "box" prévue pour le système de virtualisation choisi.
[...] Vagrant fournit un support natif des conteneurs Docker à l'exécution, au lieu d'un système d'exploitation entièrement virtualisé. Cela permet de réduire la charge en ressources puisque Docker utilise des conteneurs Linux légers.
Fig.: Instanciation d’un cluster de 3 machines avec Vagrant et Libvirt.
Concepts clés de Vagrant :
- Box : image de machine virtuelle pour Vagrant. Elles peuvent être créées sur mesure, mais le plus simple est d’en télécharger une depuis le hub de Vagrant2
- Vagrantfile : c’est le fichier qui décrit les machines virtuelles à instancier, un Vagrantfile permet les opérations suivantes sur les machines :
- Configuration : nombre de cpu, RAM, configuration du réseau, partage de fichiers (via NFS)…
- Provisionning : le system de provisionning de Vagrant permet d’exécuter des opérations sur les machines avec différent fournisseurs : shell, file, ansible, docker, salt, puppet…
- Déploiement : Vagrant permet le déploiement d’application vers un hébergeur (Heroku, (S)FTP…)
- Provider : fournisseur utilisé pour l’instanciation des machines virtuelles (Virtualbox, Libvirt, VMWare, Amazon EC2, LXC, Docker…)
Hyperviseur
Pour instancier ses machines virtuelles, Vagrant nécessite la présence d’un hyperviseur sur les machines cibles.
Généralement c’est Virtualbox qui est utilisé. Virtualbox est un gestionnaire de machine virtuelle bien connu et disponible pour de nombreuses plateformes.
Toutefois, le support officiel de Virtualbox (appartenant à Oracle) a été abandonné dans les dernières versions de Debian pour lui préférer des solutions totalement libres : l’API de virtualisation Libvirt3 4 et l’hyperviseur QEMU-KVM.
Fig.: libvirt est une couche d’abstraction entre orchestrateurs de machines virtuelles et hyperviseurs.
Note :
- Libvirt peut être appelée depuis de nombreux orchestrateurs (Vagrant, OpenStack…) et supporte de nombreux hyperviseurs (Xen, KVM, LXC…)
- Libvirt et KVM peuvent être utilisés pour déployer des machines virtuelles dans l’environnement OpenNebula 5
Installer Vagrant et libvirt ou VirtualBox
L’idéal est d’installer la dernière version de VirtualBox et Vagrant depuis les sites officiels,
- La version de VirtualBox fournie dans les dépôts de la distribution Ubuntu 18.04 est tout à fait suffisante
- La version de Vagrant fournie par les dépôts d’Ubuntu 18.04 est dépassée et rentrent en conflit avec les plugins officiels, il est donc nécessaire de télécharger la version officielle depuis le site de Vagrant
- Installer le plugin docker-compose pour vagrant via
vagrant plugin install vagrant-docker-compose
6. Ce plugin donne accès à une commande de provisionnement pour docker-compose dans les fichiers Vagrantfile. Toutefois, il est tout à fait possible d'installer docker-compose le shell7 - Pour utiliser libvirt au lieu de virtualbox :
- Installer l'environnement libvirt, le gestionnaire de machine virtuelle virt-manager et l'hyperviseur QEMU-KVM :
sudo apt install QEMU-KVM libvirt-daemon-system virt-manager
- Installer les headers de libvirt (et les outilsnécessaires pour compiler le plugin libvirt pour vagrant) :
sudo apt install build-essentials libvirt-dev
- Installer le plugin libvirt pour vagrant :
vagrant plugin install vagrant-libvirt
- Installer l'environnement libvirt, le gestionnaire de machine virtuelle virt-manager et l'hyperviseur QEMU-KVM :
Utilisation de Vagrant :
# on recupere un vagrantfile definissant la box
vagrant init "ubuntu/xenial64"
# on crée la machine avec virtualbox pour libvirt, utiliser l'option --provider=libvirt
vagrant up --provider=virtualbox
# on se connecte à la machine en ssh
vagrant ssh
# on detruit la box
vagrant destroy
Exemple de configurations de machines virtuelles dans Vagrant pour vmware, virtualbox et libvirt
# Configure CPU & RAM per settings in machines.yml (Fusion)
srv.vm.provider 'vmware_fusion' do |vmw|
vmw.vmx['memsize'] = machine['ram']
vmw.vmx['numvcpus'] = machine['vcpu']
if machine['nested'] == true
vmw.vmx['vhv.enable'] = 'TRUE'
end #if machine['nested']
end # srv.vm.provider 'vmware_fusion'
# Configure CPU & RAM per settings in machines.yml (VirtualBox)
srv.vm.provider 'virtualbox' do |vb, override|
vb.memory = machine['ram']
vb.cpus = machine['vcpu']
override.vm.box = machine['box']['vb']
end # srv.vm.provider 'virtualbox'
# Configure CPU & RAM per settings in machines.yml (Libvirt)
srv.vm.provider 'libvirt' do |lv,override|
lv.memory = machine['ram']
lv.cpus = machine['vcpu']
override.vm.box = machine['box']['lv']
if machine['nested'] == true
lv.nested = true
end # if machine['nested']
end # srv.vm.provider 'libvirt'
Le fichier machine.yml contient les informations de configuration de chacune des machines, par exemple pour la machine manager :
- box:
vmw: "generic/ubuntu1604"
vb: "ubuntu/xenial64"
lv: "generic/ubuntu1604"
name: "manager"
nics:
- type: "private_network"
ip_addr: "192.168.100.100"
ram: "2048"
vcpu: "1"
Ansible
Introduction et concepts
Ansible est un système d’exécution automatique de tâches sur des parcs de machines. Il est en particulier utilisé pour la mise en place du déploiement continu ou la mise à jour d’infrastructure.
- Playbook: définitions de tâches ansible
- Inventaire: liste les machines sur lesquelles les tâches ansible seront exécutées
- Rôles: permettent de regrouper les tâches et dépendances de tâches ansible afin de les réutiliser et de les partager
Le principal avantage de Ansible est qu’il ne requiert pas d’agent spécifique sur les hôtes distants.
Ansible sera utilisé pour initialiser le cluster Docker Swarm sur les différents hôtes :
- configurer et initialiser un ou plusieurs managers
- configurer et initialiser les workers
Étapes du déploiement à automatiser
Il s'agira d'effectuer les tâches suivantes
- Configurer les services Docker sur le manager et les workers
- Initialiser le cluster sur le manager
- Initialiser le cluster sur les workers
- Lancer les conteneurs sur le manager : portainer/portainer-ce et registry:2
- Lancer le conteneur portainer/agent sur les workers
- Générer le proxy nginx sur le manager et le publier sur le registry
- Déployer le proxy nginx sur les workers depuis le registry
Dans un premier temps, le partage des fichiers entre les hôtes pourra être fait à travers le système de fichier de la machine de test (via Vagrant et Virtualbox). Mais par la suite, il devra être déployé sur le cluster lui-même via NFS.
Les outils que j’ai choisis d’utiliser sont :
- Vagrant pour instancier les machines virtuelles de mon infrastructure de test avec Virtualbox et libvirt + QEMU-KVM comme hyperviseur pour héberger les machines virtuelles
- Ansible pour l’automatisation de l’exécution de tâches sur les machines virtuelles
Jenkins
Jenkins est un outil dédier au déploiement et à l’intégration continue…
Déploiement d’un cluster Docker Swarm avec Ansible sur le banc de test
Structure du banc de test vagrant
Fig.: Schéma de l’infrastructure virtuelle de test
Fichier de configuration pour mettre en place un cluster Docker Swarm
Le cluster est composé de 3 nœuds : 1 manager et 2 workers. J’ai choisi d’utiliser la version stable de Debian pour les 3 machines virtuelles. L’hyperviseur utilisé est virtualbox, mais il peut être adapté très simplement pour libvirt.
workers=[
{
:hostname => "worker-1",
:ip => "192.168.100.11",
},
{
:hostname => "worker-2",
:ip => "192.168.100.12",
}
]
manager={
:hostname => "manager",
:ip => "192.168.100.10",
}
Vagrant.configure(2) do |config|
config.vm.box = "debian/stretch64"
# Install docker-py for ansible automation on all hosts
config.vm.provision "shell", inline: <<-SHELL
apt-get update
apt-get install -y python python-pip
pip install docker-py
SHELL
workers.each do |machine|
config.vm.define machine[:hostname] do |node|
node.vm.hostname = machine[:hostname]
node.vm.network "private_network", ip: machine[:ip], name: "ens5"
node.vm.provider "virtualbox" do |vb|
vb.memory = 1024
vb.cpus = 2
end
node.vm.provision "docker"
end
end
config.vm.define manager[:hostname] do |node|
node.vm.hostname = manager[:hostname]
node.vm.network "private_network", ip: manager[:ip], name: "ens5"
node.vm.provider "virtualbox" do |vb|
vb.memory = 1024
vb.cpus = 2
end
node.vm.provision :docker,
images: ["portainer/portainer-ce", "registry:2"]
node.vm.provision :docker_compose
# Initialize swarm cluster
node.vm.provision "ansible" do |ansible|
ansible.playbook = "playbooks/swarm.yml"
ansible.limit = "all"
ansible.extra_vars = {
swarm_iface: "ens5"
}
ansible.groups = {
"manager" => ["manager"],
"worker" => ["worker-[1:2]"],
}
end
end
end
Ce fichier initialise les hôtes avec les fonctionnalités suivantes :
- sur tous les hôtes :
- installation de docker-py pour l’automatisation via ansible
- workers :
- installation de docker
- manager
- installation de docker et ajout des images portainer/portainer-ce et registry:2
- ansible pour l’initialisation du cluster
Automatisation de l’initialisation du cluster avec Ansible
L’initialisation du cluster dans le fichier Vagrant se fait dans la configuration du manager :
node.vm.provision "ansible" do |ansible|
ansible.playbook = "playbooks/swarm.yml"
ansible.limit = "all"
ansible.extra_vars = {
swarm_iface: "eth1"
}
ansible.groups = {
"manager" => ["manager"],
"worker" => ["worker-[1:2]"],
}
end
Attention : l'interface réseau swarm_iface
à utiliser pour le cluster est spécifiée dans les variables de Ansible.
Ce code appelle un fichier playbook ansible swarm.yml
(source) qui va effectuer les tâches suivantes :
- la première liste de tâches crée deux groupes à partir de la liste des nœuds manager du cluster :
swarm_manager_operational
qui contient les nœuds manager déjà actifs etswarm_manager_bootstrap
qui contient les nœuds manager à activer - la seconde liste effectue la même opération pour les nœuds worker et crée les groupes
swarm_worker_operational
etswarm_worker_bootstrap
- si aucun nœud ne se trouve dans le groupe
swarm_manager_operational
, le premier nœud du groupeswarm_manager_bootstrap
est activé - sur le premier nœud manager actif
swarm_manager_operational[0]
on récupère les tokens nécessaires pour que les autres noeuds rejoignent le cluster, soit comme manager (swarm_manager_token
), soit comme worker (swarm_worker_token
). on récupère agalement la liste des adresses IP des managers - on ajoute les nœuds manager encore inactif au cluster
- on ajoute les nœuds worker inactif au cluster
---
# determine the status of each manager node and break them
# into two groups:
# - swarm_manager_operational (swarm is running and active)
# - swarm_manager_bootstrap (host needs to be joined to the cluster)
- hosts: manager
become: true
tasks:
- name: determine swarm status
shell: >
docker info --format \{\{.Swarm.LocalNodeState\}\}
register: swarm_status
- name: create swarm_manager_operational group
add_host:
hostname: "{{ item }}"
groups: swarm_manager_operational
with_items: "{{ ansible_play_hosts | default(play_hosts) }}"
when: "'active' in hostvars[item].swarm_status.stdout_lines"
run_once: true
- name: create swarm_manager_bootstrap group
add_host:
hostname: "{{ item }}"
groups: swarm_manager_bootstrap
with_items: "{{ ansible_play_hosts | default(play_hosts) }}"
when: "'active' not in hostvars[item].swarm_status.stdout_lines"
run_once: true
# determine the status of each worker node and break them
# into two groups:
# - swarm_worker_operational (host is joined to the swarm cluster)
# - swarm_worker_bootstrap (host needs to be joined to the cluster)
- hosts: worker
become: true
tasks:
- name: determine swarm status
shell: >
docker info --format \{\{.Swarm.LocalNodeState\}\}
register: swarm_status
- name: create swarm_worker_operational group
add_host:
hostname: "{{ item }}"
groups: swarm_worker_operational
with_items: "{{ ansible_play_hosts | default(play_hosts) }}"
when: "'active' in hostvars[item].swarm_status.stdout_lines"
run_once: true
- name: create swarm_worker_bootstrap group
add_host:
hostname: "{{ item }}"
groups: swarm_worker_bootstrap
with_items: "{{ ansible_play_hosts | default(play_hosts) }}"
when: "'active' not in hostvars[item].swarm_status.stdout_lines"
run_once: true
# when the swarm_manager_operational group is empty, meaning there
# are no hosts running swarm, we need to initialize one of the hosts
# then add it to the swarm_manager_operational group
- hosts: swarm_manager_bootstrap[0]
become: true
tasks:
- name: initialize swarm cluster
shell: >
docker swarm init
--advertise-addr={{ swarm_iface | default('eth0') }}:2377
when: "'swarm_manager_operational' not in groups"
register: bootstrap_first_node
- name: add initialized host to swarm_manager_operational group
add_host:
hostname: "{{ item }}"
groups: swarm_manager_operational
with_items: "{{ ansible_play_hosts | default(play_hosts) }}"
when: bootstrap_first_node | changed
# retrieve the swarm tokens and populate a list of ips listening on
# the swarm port 2377
- hosts: swarm_manager_operational[0]
become: true
vars:
iface: "{{ swarm_iface | default('eth0') }}"
tasks:
- name: retrieve swarm manager token
shell: docker swarm join-token -q manager
register: swarm_manager_token
- name: retrieve swarm worker token
shell: docker swarm join-token -q worker
register: swarm_worker_token
- name: populate list of manager ips
add_host:
hostname: "{{ hostvars[item]['ansible_' + iface]['ipv4']['address'] }}"
groups: swarm_manager_ips
with_items: "{{ ansible_play_hosts | default(play_hosts) }}"
# join the manager hosts not yet initialized to the swarm cluster
- hosts: swarm_manager_bootstrap:!swarm_manager_operational
become: true
vars:
token: "{{ hostvars[groups['swarm_manager_operational'][0]]['swarm_manager_token']['stdout'] }}"
tasks:
- name: join manager nodes to cluster
shell: >
docker swarm join
--advertise-addr={{ swarm_iface | default('eth0') }}:2377
--token={{ token }}
{{ groups['swarm_manager_ips'][0] }}:2377
# join the worker hosts not yet initialized to the swarm cluster
- hosts: swarm_worker_bootstrap
become: true
vars:
token: "{{ hostvars[groups['swarm_manager_operational'][0]]['swarm_worker_token']['stdout'] }}"
tasks:
- name: join worker nodes to cluster
shell: >
docker swarm join
--advertise-addr={{ swarm_iface | default('eth0') }}:2377
--token={{ token }}
{{ groups['swarm_manager_ips'][0] }}:2377
On installe aussi Docker-Py afin de pouvoir automatiser certaines tâches Docker via les plugins Ansible :
# Installation de pyopenssl (on suppose que Python Pip est déjà installé)
- hosts: all
become: true
tasks:
- name: Install pyopenssl
command: pip install pyopenssl
Lancement de l’infrastructure
# instanciation des vm
vagrant up
# après instanciation des vm
vagrant ssh manager
La commande docker node ls
permet de lister les hôtes du swarm et de vérifier leur statut :
vagrant@manager:~$ docker node ls
ID HOSTNAME STATUS AVAILABILITY MANAGER STATUS ENGINE VERSION
p02s2kqxm09ks721k4glwlnzc * manager Ready Active Leader 19.03.2
6wdzbrh8kdh0gf9doed3unj8e worker-1 Ready Active 19.03.2
x6a0vfv2rrg9lkh0z2refk5rw worker-2 Ready Active 19.03.2
Finalisation de l’infrastructure
Les images docker Portainer et le Registry sont déployés via le fichier Vagrantfile, mais elles pourraient l’être via Ansible.
Que ce soit le cas où non, il reste encore à instancier ces 2 services.
Mise en place de Portainer
Pour Portainer, l’opération est plutôt simple :
# launch portainer on the first manager node
- hosts: swarm_manager_operational[0]
become: true
tasks:
- name: portainer_create_volume
shell: >
docker volume create portainer_data
run_once: true
- name: portainer_run_container
shell: >
docker run -d
-p 8000:8000 -p 9000:9000
--restart=unless-stopped
--name portainer
-v /var/run/docker.sock:/var/run/docker.sock
-v portainer_data:/data
portainer/portainer-ce
run_once: true
Déploiement de Swarmpit
À la main :
git clone https://github.com/swarmpit/swarmpit -b master
docker stack deploy -c swarmpit/docker-compose.yml swarmpit
Avec l'installeur :
docker run -it --rm \
--name swarmpit-installer \
--volume /var/run/docker.sock:/var/run/docker.sock \
swarmpit/install:1.9
Version Ansible :
Mise en place du Registry
L’idée était d’expérimenter le déploiement d'un Registry sur le manager du cluster tel que cela avait été réalisé sur l’infrastructure de démonstration. Toutefois, cela s’est avéré délicat à réaliser : le sécuriser avec un certificat SSL s’est révélé un peu complexe dans les hôtes déployés via Vagrant.
L’utilisation du Registry n’étant pas requise pour la suite, je préfère la laisser de côté pour le moment. De plus ce problème n'existera pas lors du déploiement d’une véritable infrastructure puisque les machines se verront attribuer un fqdn ! Pour contourner le problème, un moyen simple est de déclarer un Registry non sécurisé.
Note : Afin de ne pas multiplier les hôtes inutilement, le Registry est instancié ici sur le manager, mais il pourrait l’être sur un hôte dédié.
Utiliser un Registry sécurisé
L’automatisation de la mise en place du Registry dans l'infrastructure Vagrant est un peu plus complexe. En particulier parce que son utilisation nécessite : un certificat ssl8 et, surtout, un FQDN dédié.
Ansible fournit un plugins pour gérer les certificats SSL. Ce plugin requiert l'installation d'une bibliothèque Python sur les hôtes :
# Installation de pyopenssl (on suppose que Python Pip est déjà installé)
- hosts: all
become: true
tasks:
- name: Install pyopenssl
command: pip install pyopenssl
Il est alors possible de générer le certificat nécessaire sur le manager :
# Création du certificat
- hosts: swarm_manager_operational[0]
become: true
tasks:
- name: ca generate key
openssl_privatekey:
path: /etc/ssl/private/registry_ownca.key
passphrase: ansible
cipher: aes256
size: 2048
- name: registry generate csr
openssl_csr:
path: /etc/ssl/private/docker_registry.csr
privatekey_path: /etc/ssl/private/docker_registry.key
- name: registry generate self-signed crt
openssl_certificate:
path: /etc/ssl/private/docker_registry.crt
csr_path: /etc/ssl/private/docker_registry.csr
ownca_privatekey_path: /etc/ssl/private/registry_ownca.key
ownca_privatekey_passphrase: ansible
# We need to allow IPs for testing on the vagrant machines
subject_alt_name:
- ip:192.168.100.10
provider: ownca
Mais impossible de faire fonctionner ce certificat en dehors de l'hôte local localhost
.
Quelques pistes pour contourner ce problème :
- générer le certificat (et sa clé) au préalable à la main sur une autre machine et l'injecter sur les hôtes
- attribuer un fqdn au manager
- en le définissant via le plugin DNS de Vagrant
- en altérant les fichiers /etc/hosts avec Ansible
- en altérant les fichiers /etc/hosts avec le plugin hostsupdater de Vagrant
Une fois généré, le certificat doit alors être copié dans les certificats reconnus par docker sur tous les hôtes. Cela peut se faire en utilisant la commande synchronize
de ansible 9.
# Dans le code qui suit, swarm.manager est le fqdn attribué au registry
- hosts: all
tasks:
- name: Create docker certificates folder
shell: mkdir -p /etc/docker/certs.d/swarm.manager\:5000/
- hosts: swarm_manager_operational[0]
tasks:
- name: Copy certificate in docker certificates folder
copy:
src: /etc/ssl/private/docker_registry.crt
dest: /etc/docker/certs.d/swarm.manager\:5000/ca.crt
- name: Transfer certificate from manager to workers
synchronize:
src: /etc/ssl/private/docker_registry.crt
dest: /etc/docker/certs.d/swarm.manager\:5000/ca.crt
Une fois le certificat installé sur les hôtes, il reste à lancer le service du Registry :
# Lancement du registry sur le manager
- hosts: swarm_manager_operational[0]
become: true
tasks:
- name: registry_run_registry
shell: >
docker run -d -p 5000:5000
--restart=always
-e REGISTRY_HTTP_TLS_CERTIFICATE=/certs/docker_registry.crt
-e REGISTRY_HTTP_TLS_KEY=/certs/docker_registry.key
-v /etc/ssl/private:/certs
-v /var/lib/docker/registry:/var/lib/registry
registry:2
run_once: true
Utiliser un Registry non sécurisé
Bien que cela ne soit pas recommandé, Docker offre la possibilité d'utiliser un Registry non sécurisé
Créer le Registry
# Lancement du registry sur le manager
- hosts: swarm_manager_operational[0]
become: true
tasks:
- name: registry_run_registry
shell: >
docker run -d -p 5000:5000
--restart=always
-v /var/lib/docker/registry:/var/lib/registry
registry:2
run_once: true
Il suffit alors de déclarer le registry dans la configuration de Docker sur chacun des hôtes du cluster (où 10.0.0.1 est l’IP du manager dans le cluster)
{
"insecure-registries" : ["10.0.0.1:5000"]
}
Cette opération pourrait être réalisée via Ansible.
Configuration client server NFS
La création et le partage du point de montage NFS entre les hôtes peut se faire facilement avec Ansible10.
EN CONSTRUCTION
Inventaire des hôtes (peut-être généré directement à partir de l’inventaire des nœuds Docker) :
[nfs_server]
10.0.0.1
[nfs_clients]
10.0.0.2
10.0.0.3
Template de configuration du server NFS exports.j2
:
# /etc/exports: the access control list for filesystems which may be exported
# to NFS clients. See exports(5).
#
# Example for NFSv2 and NFSv3:
# /srv/homes hostname1(rw,sync,no_subtree_check) hostname2(ro,sync,no_subtree_check)
#
# Example for NFSv4:
# /srv/nfs4 gss/krb5i(rw,sync,fsid=0,crossmnt,no_subtree_check)
# /srv/nfs4/homes gss/krb5i(rw,sync,no_subtree_check)
#
/dockerdata 10.0.0.1/24(rw,no_subtree_check,no_root_squash,async)
Sur le manager :
---
- hosts: nfs_server
remote_user: ubuntu
sudo: yes
tasks:
- name: Create mountable dir
file: path=/share state=directory mode=777 owner=root group=root
- name: make sure the mount drive has a filesystem
filesystem: fstype=ext4 dev={{ mountable_share_drive | default('/dev/xvdb') }}
- name: set mountpoints
mount: name=/share src={{ mountable_share_drive | default('/dev/xvdb') }} fstype=auto opts=defaults,nobootwait dump=0 passno=2 state=mounted
- name: Ensure NFS utilities are installed.
apt: name={{ item }} state=installed update_cache=yes
with_items:
- nfs-common
- nfs-kernel-server
- name: copy /etc/exports
template: src=exports.j2 dest=/etc/exports owner=root group=root
- name: restart nfs server
service: name=nfs-kernel-server state=restarted
Sur les clients :
- hosts: nfs_clients
remote_user: ubuntu
sudo: yes
tasks:
- name: Ensure NFS common is installed.
apt: name=nfs-common state=installed update_cache=yes
- name: Create mountable dir
file: path=/nfs state=directory mode=777 owner=root group=root
- name: set mountpoints
mount: name=/nfs src={{hostvars[groups['nfs_server'][0]]['ansible_eth0']['ipv4']['address']}}:/share fstype=nfs opts=defaults,nobootwait dump=0 passno=2 state=mounted
Proxy sur les workers
Étapes du Déploiement
- Générer les configurations des sites (Ansible)
- Build de l’image si nécessaire (Docker Compose)
- Déploiement du service (Docker Swarm)
Mise à jour des configurations
- Générer les configurations des sites (Ansible)
- Mettre à jour les services (Docker Swarm)
Template :
# templates/nginx_site_template.j2
upstream {{ stack_name }} {
server {{ item }};
}
server {
listen 80;
server_name {{ stack_name }}.{{ host_name }};
client_max_body_size 128M;
location / {
proxy_pass http://{{ stack_name }};
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Forwarded-Port 443;
proxy_set_header X-NGINX-UPSTREAM {{ stack_name }};
proxy_buffer_size 128k;
proxy_buffers 4 256k;
proxy_busy_buffers_size 256k;
proxy_set_header Host $host;
}
}
Playbook pour re-générer et recharger la configuration des proxies.
---
# Reload services for the proxy
- hosts: manager
become: true
tasks:
- name: get all services connected to the proxy network
shell: >
docker network inspect -f {{.Id}} UCLouvainProxy
register: proxy_services
- name: check directory exists
stat:
path: ../sites
register: sites_dir
- name: find existing configurations
find:
paths: ../sites
patterns: "*.conf"
register: files_to_delete
- name: delete existing configurations
file:
path: "{{ item.path }}"
state: absent
with_items: "{{ files_to_delete.files }}"
- name: generate new configurations
template:
src: ../templates/nginx_site_template.j2
dest: ../sites/{{ stack_name }}.conf
with_items: proxy_services.output.lines
vars:
host_name: apps.sisg.ucl.ac.be
stack_name: {{ item | regex_replace('^(.+)_(.+)$', '\\1') }}
when: item != "frontend_proxy"
- name: reload proxy service containers
shell: >
docker service update --force frontend_proxy
Note: version complète de l'update de service, si la version simplifiée ne fonctionne pas avec la version de Docker installée :
- name: get id of proxy service
shell: >
docker stack services frontend --filter "name=frontend_proxy" --format "{{.ID}}"
register: proxy_service_id
- name: reload proxy service containers
shell: >
docker service update --force {{ proxy_service_id.output }}
Pour une utilisation hors de Vagrant
Il n'est actuellement pas possible d’utiliser Vagrant pour instancier des machines dans OpenNebula. Afin de pouvoir automatiser la mise en place du cluster sur une infrastructure en dehors de Vagrant, plusieurs étapes supplémentaires sont donc nécessaires.
En effet, nous nous sommes basés sur le mécanisme de provisionning de Vagrant pour installer de nombreuses dépendances dont docker et docker-compose de manière « implicite ».
Playbooks pour l’installation des services
Un playbook Ansible supplémentaire permettra de le faire playbooks/baseinstall.yml
:
---
# Installation des packages de base nécessaires
- hosts: all
become: true
tasks:
- name: Install packages that allow apt to be used over HTTPS
apt:
name: "{{ packages }}"
state: present
update_cache: yes
vars:
packages:
- apt-transport-https
- ca-certificates
- curl
- gnupg-agent
- software-properties-common
- python-apt
- git
Un playbook Ansible supplémentaire permettra de le faire playbooks/dockerinstall.yml
:
---
# Installation de docker et docker-py sur des hôtes Debian et Ubuntu
- hosts: all
become: true
tasks:
- name: Add an apt signing key for Docker
apt_key:
url: https://download.docker.com/linux/debian/gpg
state: present
when: ansible_distribution == 'Debian'
- name: Add apt repository for stable version
apt_repository:
repo: deb [arch=amd64] https://download.docker.com/linux/debian {{ansible_lsb.release}} stable
state: present
when: ansible_distribution == 'Debian'
- name: Add an apt signing key for Docker
apt_key:
url: https://download.docker.com/linux/ubuntu/gpg
state: present
when: ansible_distribution == 'Ubuntu'
- name: Add apt repository for stable version
apt_repository:
repo: deb [arch=amd64] https://download.docker.com/linux/ubuntu {{ansible_lsb.release}} stable
state: present
when: ansible_distribution == 'Ubuntu'
- name: Install docker and its dependecies
apt:
name: "{{ packages }}"
state: present
update_cache: yes
vars:
packages:
- docker-ce
- docker-ce-cli
- containerd.io
notify:
- docker status
On installe aussi les paquets Python requis pour Ansible playbooks/pythoninstall.yml
:
---
# Installation de Python Pip et des paquets nécessaires pour l’automatisation avec Ansible
- hosts: all
become: true
tasks:
- name: Install Python and PIP and pip dependencies
apt:
name: "{{ packages }}"
state: present
update_cache: yes
vars:
packages:
- python
- python-pip
- name: Install pyopenssl
command: pip install pyopenssl
- name: Install docker-py
command: pip install docker-py
Inventaires et groupes ansibles
Dans le cas de l'automatisation avec Vagrant, le plugin vagrant-ansible se chargeait de définir les machines sur lesquelles effectuer le provisionning. Pour une utilisation grandeur nature, il faudra les définir dans les fichiers de configuration de Ansible.
Vagrant https://fr.wikipedia.org/wiki/Vagrant
Vagrantbox.es http://www.vagrantbox.es/
Site officiel https://libvirt.org/
« Documentation Ubuntu sur libvirt » https://ubuntu.com/server/docs/virtualization-libvirt
« Vagrant OpenNebula Tutorial » https://github.com/marcindulak/vagrant-opennebula-tutorial-centos7
« Vagrant Docker-Compose plugin » https://github.com/leighmcculloch/vagrant-docker-compose
voir https://ermaker.github.io/blog/2015/11/18/install-docker-and-docker-compose-on-vagrant.html et https://docs.docker.com/compose/install/ pour la marche à suivre
Private registry in swarm https://codeblog.dotsandbrackets.com/private-registry-swarm/ et Create your own docker registry https://www.frakkingsweet.com/create-your-own-docker-registry/
StackOverflow – How to copy files between two nodes using ansible https://stackoverflow.com/questions/25505146/how-to-copy-files-between-two-nodes-using-ansible
Setup NFS Server and Client Using Ansible https://advishnuprasad.com/blog/2016/03/29/setup-nfs-server-and-client-using-ansible/
« Wikipedia – Libvirt » https://fr.wikipedia.org/wiki/Libvirt
openssl_certificate – Generate and/or check OpenSSL certificates https://docs.ansible.com/ansible/latest/modules/openssl_certificate_module.html
« Share Compose configurations between files and projects » https://docs.docker.com/compose/extends/