Jouer avec Sƍzu - 1ers pas

  • Objectif de ce blog post: mettre en place un environnement pour pouvoir “jouer” avec Sƍzu
  • Niveau: dĂ©butant
  • ce que j’écris n’engage que moi bien sĂ»r 😃
  • le rĂ©seau n’est vraiment pas mon domaine 😖
  • Nous verrons (rapidement)
    • comment installer Sƍzu
    • comment l’utiliser pour rĂ©partir la charge sur plusieurs VMs
    • comment l’utiliser pour dĂ©ployer une nouvelle version de site Et tout cela de maniĂšre transparente mais Ă  la main âœ‹đŸ€š pour mieux comprendre

1) Sƍzu?

Sƍzu est un reverse proxy HTTP dĂ©veloppĂ© chez https://www.clever-cloud.com/ qui peut changer sa configuration au runtime, sans re-dĂ©marrer. Le proxy expose une chaĂźne de communication (communication channel in 🇬🇧) et du coup on peut envoyer des commandes Ă  chaud Ă  Sƍzu.

Si vous voulez savoir ce qu’est un reverse proxy parce que vous n’y connaissez rien ou presque sur le sujet, je vous conseille cette vidĂ©o Comprendre le Proxy et le Reverse Proxy en 5 minutes par “Cookie connectĂ©â€.

Si vous voulez en savoir plus sur Sƍzu, je vous conseille la lecture de ce blog post (le comment, le pourquoi de Sƍzu): Hot reloading configuration: why and how? par Geoffroy Couprie le papa de Sƍzu (à qui je n’ai rien fait relire 😜)

Et le repository GitHub est par ici: https://github.com/sozu-proxy/sozu

✹✹✹ Remerciements pour leurs explications

2) Mise en place du terrain de jeux

  • Nous aurons besoin de plusieurs VMs (virtual machines), j’ai choisi d’utiliser:
    • VirtualBox https://www.virtualbox.org/
    • Vagrant https://www.vagrantup.com/
    • â„č vous pouvez adapter avec vos outils, l’important c’est de pouvoir simuler plusieurs machines
    • Les installations de VirtualBox et Vagrant sont trĂšs simples Ă  faire
  • Il faudra “builder” Sƍzu
    • Nous devront donc installer Rust (car, je le rappelle Sƍzu est codĂ© en Rust).
    • ⚠ Sƍzu est encore en 🚧 donc certaines choses peuvent changer

Qu’allons nous faire?: hĂ©berger un mĂȘme code source de site web en NodeJS dans plusieurs VMs

Tout d’abord crĂ©ez une arborescence pour hĂ©berger nos expĂ©rimentations:

sandbox
   ├── hello-earth-v1   # ici le code source de mon site web
   ├── vms              # le projet Vagrant pour gĂ©nĂ©rer et provisionner nos VM
   ├── sozu-demo        # ici nous builderons le projet sozu

A) Le code source du site web

J’ai utilisĂ© ExpressJS. vous allez avoir besoin uniquement de 2 fichiers:

  • index.js
  • package.js
sandbox
   ├── hello-earth-v1   
   │   ├── index.js
   │   └── package.json   
   ├── vms              
   ├── sozu-demo        

index.js

â„č j’utilise la librairie 'project-name-generator' pour gĂ©nĂ©rer un nom de machine diffĂ©rent pour chacune des VMs.

const express = require("express");
const bodyParser = require("body-parser");
const generate = require('project-name-generator');

let port = process.env.PORT || 8080;

let app = express();
app.use(bodyParser.json())
app.use(bodyParser.urlencoded({extended: false}))

let machineName = generate({ words: 3, number: true }).dashed

app.disable('etag');

app.get('/', (req, res) => {
  res.send(`
    <!doctype html>
    <html>
      <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <title>WebApp</title>
        <meta name="description" content="">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <style>
        .container
        {
          min-height: 100vh;
          display: flex;
          justify-content: center;
          align-items: center;
          text-align: center;
        }
        .title
        {
          font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
          display: block;
          font-weight: 300;
          font-size: 40px;
          color: #35495e;
          letter-spacing: 1px;
        }
        .subtitle
        {
          font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
          font-weight: 300;
          font-size: 30px;
          color: #526488;
          word-spacing: 5px;
          padding-bottom: 15px;
        }
        </style>
      </head>
      <body>
        <section class="container">
          <div>
            <h1 class="title">
              👋 I am ${machineName}
            </h1>
            <h2 class="subtitle">
              Hello 🌍 v1
            </h2>
          </div>
        </section>
      </body>
    </html>
  `)
})

app.listen(port)
console.log(`🌍 ${machineName} is started - listening on `, port)

package.json

{
  "name": "hello-earth",
  "scripts": {
    "start": "node index.js"
  },
  "main": "index.js",
  "dependencies": {
    "body-parser": "^1.17.2",
    "express": "^4.15.3",
    "project-name-generator": "^2.1.3"
  }
}

B) Notre projet Vagrant

Dans le répertoire vms, allez créer un fichier Vagrantfile

sandbox
   ├── hello-earth-v1   
   │   ├── index.js
   │   └── package.json   
   ├── vms   
   │   └── Vagrantfile               
   ├── sozu-demo        

Vagrantfile

Que fait le script?

  • je crĂ©e 3 VMs Ă  partie d’une image bento/ubuntu-17.04
  • chaque VM aura une ip fixe et se nomme webappN:
    • webapp1 : 192.168.1.21
    • webapp2 : 192.168.1.22
    • webapp3 : 192.168.1.23
  • j’expose le port 8080 (vous pourrez donc accĂ©der Ă  la webapp par http://192.168.1.2N:8080/)
  • je copie le code source de /hello-earth-v1 dans le rĂ©pertoire /hello-earth de la vm
  • j’installe NodeJS
  • j’installe les packages nĂ©cessaires pour faire fonctionner la webapp (npm install)
BOX_IMAGE = "bento/ubuntu-17.04"
NODE_COUNT = 3

Vagrant.configure(2) do |config|
  config.vm.box = BOX_IMAGE

  (1..NODE_COUNT).each do |i|
    config.vm.define "webapp#{i}" do |node|

      node.vm.network :forwarded_port, guest: 8080, host: 9090 + i
      node.vm.network "public_network", ip: "192.168.1.2#{i}", bridge: "en0: Wi-Fi (AirPort)"

      node.vm.provider "virtualbox" do |node|
        node.memory = 256
        node.cpus = 1
      end
      
      node.vm.synced_folder '.', '/vagrant', disabled: true
      node.vm.provision "file", source: "../hello-earth-v1", destination: "hello-earth"
      
      node.vm.provision :shell, inline: <<-SHELL
        echo "👋 Installing NodeJS..."
        apt-get install curl python-software-properties -y
        curl -sL https://deb.nodesource.com/setup_7.x | sudo bash -
        apt-get install nodejs -y
        cd hello-earth
        npm install
        echo "😜 bye! 👋👋👋"
      SHELL

    end
  end
end

Nous pouvons dĂšs maintenant gĂ©nĂ©rer les VMs et les dĂ©marrer ainsi que les webapps. â„č Mais, vous n`ĂȘtes pas obligĂ©s de le faire tout de suite.

Générer (et démarrer les VMs) les VM

C’est tout simple:

cd vms
vagrant up
# ⏳ patientez un peu, il faut tout de mĂȘme rĂ©cupĂ©rer l'image de l'OS

DĂ©marrer les webapps

Il faut donc démarrer les webapps au sein de chaque VM, comme cela:

vagrant ssh webapp1 -c "cd hello-earth; npm start"&
vagrant ssh webapp2 -c "cd hello-earth; npm start"&
vagrant ssh webapp3 -c "cd hello-earth; npm start"&

Vous devriez obtenir cette sortie:

🌍 delirious-flagrant-expert-3862 is started - listening on  8080
🌍 stimulating-jazzy-spoon-4201 is started - listening on  8080
🌍 knotty-responsible-muscle-3315 is started - listening on  8080

â„č je gĂ©nĂšre un nom alĂ©atoire pour chaque application pour mieux les reconnaĂźtre.

Vous pouvez dĂ©jĂ  tester l’accĂšs aux applications:

Maintenant passons aux choses sĂ©rieuses 😉

C) Installation de Sƍzu

Suivre les Ă©tapes suivantes (cela peut ĂȘtre un peu long selon a configuration de votre machine):

# Installer Rust et le tooling associé
curl https://sh.rustup.rs -sSf | sh
# wait... đŸ€”

# cloner Sƍzu
cd sozu-demo
git clone https://github.com/sozu-proxy/sozu

# compiler Sƍzu
cd sozu
cd ctl && cargo build; cd ../bin && cargo build
# wait... đŸ€”

Vous devrier avoir cette arborescence:

sandbox
   ├── hello-earth-v1   
   │   ├── index.js
   │   └── package.json   
   ├── vms   
   │   └── Vagrantfile               
   ├── sozu-demo  
   │   └── sozu       

Dans le répertoire /sozu/demo nous allons créer:

  • un rĂ©pertoire command_folder
  • un fichier vide state.json
  • un fichier 404.html
  • un fichier 503.html
  • un fichier demo.toml

404.html

HTTP/1.1 404 nope
Cache-Control: no-cache
Content-Type: text/html; charset=UTF-8
Connection: close
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">
    <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
    <meta http-equiv="Pragma" content="no-cache">
    <style>
    .title
    {
      font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
      display: block;
      font-weight: 300;
      font-size: 60px;
      color: #35495e;
      letter-spacing: 1px;
    }
    </style>
  </head>
  <body>
    <h1 class="title">
      😡 Nope❗
    </h1>
  </body>
</html>

503.html

HTTP/1.1 503 your application is in deployment
Cache-Control: no-cache
Content-Type: text/html; charset=UTF-8
Connection: close
<!doctype html>
<html>
  <head>
    <meta charset="utf-8">

    <style>
    .title
    {
      font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
      display: block;
      font-weight: 300;
      font-size: 60px;
      color: #35495e;
      letter-spacing: 1px;
    }
    </style>
  </head>
  <body>
    <h1 class="title">
      😱 Ouch...
    </h1>
  </body>
</html>

demo.toml

C’est le fichier de configuration utilisĂ© par Sƍzu

# sozu proxy simple demo config file

command_socket = "./command_folder/sock"
saved_state    = "./state.json"
log_level      = "info"
log_target     = "stdout"
command_buffer_size = 16384
worker_count = 2
handle_process_affinity = false

# you need this, but currently it`s not used
[metrics]
address = "192.168.59.103"
port = 8125

[http]
address = "127.0.0.1"
max_connections = 10
port = 9090
buffer_size = 16384
answer_404 = "./404.html"
answer_503 = "./503.html"


[applications]

[applications.webapp]
hostname = "localhost"
frontends = [ "HTTP" ] # list of proxy tags
backends  = [ "192.168.1.21:8080", "192.168.1.22:8080", "192.168.1.22:8080" ] # list of IP/port 

Les 2 sections les plus importantes pour notre “POC” sont [http] et [applications.webapp]:

  • avec [http], vous pouvez dĂ©duire que vos allez accĂ©der au reverse-proxy via l’ip 127.0.0.1 et le port http 9090
  • avec [applications.webapp], le nom de domaine pour accĂ©der au proxy est "localhost" et vous serez redirigĂ©s vers une des machines comprises dans la liste des ip sur un port particulier
  • â„č dans [applications.webapp] c’est vous qui donnez le nom webapp, vous l’appelez comme vous voulez

Maintenant vous devriez avoir cette arborescence:

sandbox
   ├── hello-earth-v1   
   │   ├── index.js
   │   └── package.json   
   ├── vms   
   │   └── Vagrantfile               
   ├── sozu-demo  
   │   ├── 404.html  
   │   ├── 503.html   
   │   ├── demo.toml   
   │   ├── state.json  
   │   ├── command_folder/       
   │   └── sozu/      

3) On joue 
 Une 1ùre fois!

Si vous ne l’avez pas dĂ©jĂ  fait, gĂ©nĂ©rez vos VM et lancez les webapps

Dans un 1er templ, lancez Sƍzu:

cd sozu-demo
./sozu/target/debug/sozu start -c ./demo.toml

A) 1er contact

Maintenant ouvrez 2 navigateurs diffĂ©rents (j’ai fait le test avec Chrome et FireFox) avec l’url http://localhost:9090/ et vous obtiendrez ceci:

Vous pouvez voir que Sƍzu vous a redirigĂ© sur des machines diffĂ©rentes (pour une seule et mĂȘme url)

B) Tuons des VMs

ArrĂȘtons 2 VMs:

# obtenir la liste des VMs
cd vms
vagrant status

# Vous allez obtenir ceci:
Current machine states:

webapp1                   running (virtualbox)
webapp2                   running (virtualbox)
webapp3                   running (virtualbox)

# arrĂȘtons webapp2 et webapp3  ... et supprimons les
vagrant halt webapp2; vagrant destroy webapp2 -f
vagrant halt webapp3; vagrant destroy webapp3 -f

vagrant status
# nouveau status
webapp1                   running (virtualbox)
webapp2                   not created (virtualbox)
webapp3                   not created (virtualbox)

  • Raffraichissez vos navigateurs
  • Il va falloir attendre un peu: Sƍzu dispose d’un circuit-breaker qui va tenter pendant un petit moment de joindre les 2 machines “disparues”
  • puis Sƍzu va “se rabattre” sur la machine restante

Et vous devriez obtenir ceci (j’ai fait un test supplĂ©mentaire avec Safari):

ArrĂȘtons la derniĂšre VM

vagrant halt webapp1; vagrant destroy webapp1 -f
  • Raffraichissez vos navigateurs
  • Attendez un peu
 (selon les navigateurs et leur cache, les comportements peuvent ĂȘtre variables)

Et vous devriez obtenir notre favuleuse page 503:

4) On relance les sites

Alors on recrée les VMs une nouvelle fois, on démarre les webapps:

cd vms
vagrant up

Et vous relancez la webapp ExpressJS de la 1Ăšre VM:

vagrant ssh webapp1 -c "cd hello-earth; npm start"

Ensuite nous allons “expliquer” à Sozu que nous avons une nouvelle machine à nouveau disponible en lui passant la commande suivante:

cd sozu-demo
./sozu/target/debug/sozuctl --config ./demo.toml  backend add --id webapp --ip 192.168.1.21 --port 8080
  • Raffraichissez vos navigateurs
  • Attendez un peu

  • Et đŸ„đŸ„đŸ„

Et ceci sans redĂ©marrer Sƍzu đŸ˜đŸ€—

Relancez les autres webapps:

vagrant ssh webapp2 -c "cd hello-earth; npm start"&
vagrant ssh webapp3 -c "cd hello-earth; npm start"&

Expliquons Ă  Sƍzu qu’il y a nouveau des machines supplĂ©mentaires:

./sozu/target/debug/sozuctl --config ./demo.toml  backend add --id webapp --ip 192.168.1.22 --port 8080

puis

./sozu/target/debug/sozuctl --config ./demo.toml  backend add --id webapp --ip 192.168.1.23 --port 8080
  • Raffraichissez vos navigateurs
  • Et voilĂ :

5) On fait une mise Ă  jour de site

J’ai crĂ©Ă© une nouvelle version du site dans un rĂ©pertoire /hello-earth-v2 avec une Ă©volution majeure, j’ai modifiĂ© une partie du code html:

<body>
  <section class="container">
    <div>
      <h1 class="title">
        👋 I am ${machineName} I ❀ đŸŒ
      </h1>
      <h2 class="subtitle">
        Hello 🌍 v2
      </h2>
    </div>
  </section>
</body>

A) Nouveau projet Vagrant

Je vais donc créer de nouvelles VMs dans un répéertoire /vms-new

sandbox
   ├── hello-earth-v1   
   │   ├── index.js
   │   └── package.json   
   ├── hello-earth-v2  
   │   ├── index.js
   │   └── package.json      
   ├── vms   
   │   └── Vagrantfile   
   ├── vms-new   
   │   └── Vagrantfile                 
   ├── sozu-demo       
   │   └── sozu/      


 Avec un nouveau Vagrantfile

  • chaque VM aura une nouvelle ip fixe et se nomme webapp_newN:
    • webapp1 : 192.168.1.31
    • webapp2 : 192.168.1.32
    • webapp3 : 192.168.1.33
  • je copie le code source de /hello-earth-v2 dans le rĂ©pertoire /hello-earth de la vm
BOX_IMAGE = "bento/ubuntu-17.04"
NODE_COUNT = 3

Vagrant.configure(2) do |config|
  config.vm.box = BOX_IMAGE

  (1..NODE_COUNT).each do |i|
    config.vm.define "webapp_new#{i}" do |node|

      node.vm.network :forwarded_port, guest: 8080, host: 9100 + i
      node.vm.network "public_network", ip: "192.168.1.3#{i}", bridge: "en0: Wi-Fi (AirPort)"

      node.vm.provider "virtualbox" do |node|
        node.memory = 256
        node.cpus = 1
      end
      
      node.vm.synced_folder '.', '/vagrant', disabled: true
      node.vm.provision "file", source: "../hello-earth-v2", destination: "hello-earth"
      
      node.vm.provision :shell, inline: <<-SHELL
        echo "👋 Installing NodeJS..."
        apt-get install curl python-software-properties -y
        curl -sL https://deb.nodesource.com/setup_7.x | sudo bash -
        apt-get install nodejs -y
        cd hello-earth
        npm install
        echo "😜 bye! 👋👋👋"
      SHELL

    end
  end
end

B) Création, provisionning et lancement

On provisionne donc les nouvelles machines:

vagrant up
# puis
vagrant ssh webapp_new1 -c "cd hello-earth; npm start"
vagrant ssh webapp_new2 -c "cd hello-earth; npm start"
vagrant ssh webapp_new3 -c "cd hello-earth; npm start"

On “👋 notifie” Sƍzu de l’arrivĂ©e de nouvelles machines

./sozu/target/debug/sozuctl --config ./demo.toml  backend add --id webapp --ip 192.168.1.31 --port 8080
./sozu/target/debug/sozuctl --config ./demo.toml  backend add --id webapp --ip 192.168.1.32 --port 8080
./sozu/target/debug/sozuctl --config ./demo.toml  backend add --id webapp --ip 192.168.1.33 --port 8080

Ensuite on “👋 notifie” Sƍzu du dĂ©part des anciennes machines (cette fois çi avec le mot-clĂ© remove)

./sozu/target/debug/sozuctl --config ./demo.toml  backend remove --id webapp --ip 192.168.1.21 --port 8080
./sozu/target/debug/sozuctl --config ./demo.toml  backend remove --id webapp --ip 192.168.1.22 --port 8080
./sozu/target/debug/sozuctl --config ./demo.toml  backend remove --id webapp --ip 192.168.1.23 --port 8080
  • Raffraichissez vos navigateurs
  • Attendez un peu

  • Et ✹🐿 le nouveau site a Ă©tĂ© dĂ©plotĂ© sans aucun arrĂȘt relance đŸ•ș

VoilĂ , c’est tout pour aujourd’hui. Sƍzu Ă©volue et de nouvelles fonctionnalitĂ©s sont Ă  venir. Et de mon cĂŽtĂ© je pense dĂ©jĂ  au prochain tuto Sƍzu avec des microservices Vert.x.

blog comments powered by Disqus

Related posts