Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Déploiement d'une application Rails sans interruption de service

Déploiement d'une application Rails sans interruption de service

Parce que la pleine disponibilité d'une application est cruciale pour le bon ressenti de ses utilisateurs, et que pouvoir déployer sans se soucier de ce manque de disponibilité est un confort essentiel au bien-être des développeurs.

Présentation faite dans le cadre des apéros RubyNord.
http://ruby-nord.org/

Le code source de cette présentation est disponible sur https://gitlab.com/bobmaerten/presentation-zdd

Bob Maerten

April 19, 2016
Tweet

More Decks by Bob Maerten

Other Decks in Technology

Transcript

  1. Déploiement d'une application Rails
    sans interruption de service
    Apéro Ruby Nord - Avril 2016
    1 / 38

    View Slide

  2. Bob Maerten
    http://levups.com
    2 / 38

    View Slide

  3. Pourquoi le ZDD ?
    (Zero Downtime Deployment)
    3 / 38

    View Slide

  4. Parce que c'est pratique quand il y a des users !
    4 / 38

    View Slide

  5. Parce que les devs n'aiment pas deploy aux aurores !
    5 / 38

    View Slide

  6. Parce qu'il n'y a que les petites natures qui ne deploy
    pas plusieurs fois par jour !
    6 / 38

    View Slide

  7. Le ZDD dans l'esprit commun
    7 / 38

    View Slide

  8. Blue/Green
    8 / 38

    View Slide

  9. 9 / 38

    View Slide

  10. Blue/Green
    Avantages
    test avant bascule ;
    usage en pseudo A/B ;
    rollback "facile" ;
    architectures multi-éléments
    10 / 38

    View Slide

  11. Blue/Green
    Avantages
    test avant bascule ;
    usage en pseudo A/B ;
    rollback "facile" ;
    architectures multi-éléments
    Inconvénients
    doubles ressources ;
    gestion des sessions lors de la bascule ;
    delta DB pendant la bascule.
    11 / 38

    View Slide

  12. Mais pour des apps à taille "humaine" ?
    12 / 38

    View Slide

  13. Unicorn !
    13 / 38

    View Slide

  14. Unicorn
    Avantages
    protection du code non-threadsafe ;-)
    multi-process = multi-core ;
    isolation des crashes de workers
    14 / 38

    View Slide

  15. Unicorn
    Avantages
    protection du code non-threadsafe ;-)
    multi-process = multi-core ;
    isolation des crashes de workers
    Inconvénients
    optimisé pour les "fast-clients" (frontal web obligatoire)
    consommation mémoire
    15 / 38

    View Slide

  16. The killer feature!
    16 / 38

    View Slide

  17. Rolling restart
    $ k
    i
    l
    l -
    U
    S
    R
    2 <
    u
    n
    i
    c
    o
    r
    n
    _
    p
    i
    d
    >
    17 / 38

    View Slide

  18. Unicorn rolling restart en détail
    envoi du signal USR2 au master process ;
    18 / 38

    View Slide

  19. Unicorn rolling restart en détail
    envoi du signal USR2 au master process ;
    unicorn duplique son master ;
    u
    n
    i
    c
    o
    r
    n m
    a
    s
    t
    e
    r (
    o
    l
    d
    )
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    0
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    1
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    2
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n m
    a
    s
    t
    e
    r
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    0
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    1
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    2
    ]
    19 / 38

    View Slide

  20. Unicorn rolling restart en détail
    envoi du signal USR2 au master process ;
    unicorn duplique son master ;
    u
    n
    i
    c
    o
    r
    n m
    a
    s
    t
    e
    r (
    o
    l
    d
    )
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    0
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    1
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    2
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n m
    a
    s
    t
    e
    r
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    0
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    1
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    2
    ]
    envoi du signal T
    T
    O
    U à l'ancien master :
    ses workers se terminent après leur requête en cours ;
    20 / 38

    View Slide

  21. Unicorn rolling restart en détail
    envoi du signal USR2 au master process ;
    unicorn duplique son master ;
    u
    n
    i
    c
    o
    r
    n m
    a
    s
    t
    e
    r (
    o
    l
    d
    )
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    0
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    1
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    2
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n m
    a
    s
    t
    e
    r
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    0
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    1
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    2
    ]
    envoi du signal T
    T
    O
    U à l'ancien master :
    ses workers se terminent après leur requête en cours ;
    Plus de workers sur l'ancien master :
    envoi du signal Q
    U
    I
    T ;
    21 / 38

    View Slide

  22. Unicorn rolling restart en détail
    envoi du signal USR2 au master process ;
    unicorn duplique son master ;
    u
    n
    i
    c
    o
    r
    n m
    a
    s
    t
    e
    r (
    o
    l
    d
    )
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    0
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    1
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    2
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n m
    a
    s
    t
    e
    r
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    0
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    1
    ]
    \
    _ u
    n
    i
    c
    o
    r
    n w
    o
    r
    k
    e
    r
    [
    2
    ]
    envoi du signal T
    T
    O
    U à l'ancien master :
    ses workers se terminent après leur requête en cours ;
    Plus de workers sur l'ancien master :
    envoi du signal Q
    U
    I
    T ;
    sinon, envoi de H
    U
    P à l'ancien master et Q
    U
    I
    T au nouveau pour retourner à
    l'état initial.
    22 / 38

    View Slide

  23. config/unicorn.rb
    b
    e
    f
    o
    r
    e
    _
    f
    o
    r
    k d
    o |
    s
    e
    r
    v
    e
    r
    , w
    o
    r
    k
    e
    r
    |
    d
    e
    f
    i
    n
    e
    d
    ?
    (
    A
    c
    t
    i
    v
    e
    R
    e
    c
    o
    r
    d
    :
    :
    B
    a
    s
    e
    ) a
    n
    d A
    c
    t
    i
    v
    e
    R
    e
    c
    o
    r
    d
    :
    :
    B
    a
    s
    e
    .
    c
    o
    n
    n
    e
    c
    t
    i
    o
    n
    .
    d
    i
    s
    c
    o
    n
    n
    e
    c
    t
    !
    o
    l
    d
    _
    p
    i
    d = "
    #
    {
    s
    e
    r
    v
    e
    r
    .
    c
    o
    n
    f
    i
    g
    [
    :
    p
    i
    d
    ]
    }
    .
    o
    l
    d
    b
    i
    n
    "
    i
    f o
    l
    d
    _
    p
    i
    d !
    = s
    e
    r
    v
    e
    r
    .
    p
    i
    d
    b
    e
    g
    i
    n
    s
    i
    g = (
    w
    o
    r
    k
    e
    r
    .
    n
    r + 1
    ) >
    = s
    e
    r
    v
    e
    r
    .
    w
    o
    r
    k
    e
    r
    _
    p
    r
    o
    c
    e
    s
    s
    e
    s ? :
    Q
    U
    I
    T : :
    T
    T
    O
    U
    P
    r
    o
    c
    e
    s
    s
    .
    k
    i
    l
    l
    (
    s
    i
    g
    , F
    i
    l
    e
    .
    r
    e
    a
    d
    (
    o
    l
    d
    _
    p
    i
    d
    )
    .
    t
    o
    _
    i
    )
    r
    e
    s
    c
    u
    e E
    r
    r
    n
    o
    :
    :
    E
    N
    O
    E
    N
    T
    , E
    r
    r
    n
    o
    :
    :
    E
    S
    R
    C
    H
    e
    n
    d
    e
    n
    d
    e
    n
    d
    https://github.com/defunkt/unicorn/blob/master/examples/unicorn.conf.rb
    23 / 38

    View Slide

  24. config/deploy
    .rb
    n
    a
    m
    e
    s
    p
    a
    c
    e :
    d
    e
    p
    l
    o
    y d
    o
    t
    a
    s
    k :
    r
    e
    s
    t
    a
    r
    t d
    o
    i
    n
    v
    o
    k
    e '
    u
    n
    i
    c
    o
    r
    n
    :
    r
    e
    s
    t
    a
    r
    t
    '
    e
    n
    d
    e
    n
    d
    lib/capistrano/tasks/unicorn.cap
    n
    a
    m
    e
    s
    p
    a
    c
    e :
    u
    n
    i
    c
    o
    r
    n d
    o
    t
    a
    s
    k :
    e
    n
    v
    i
    r
    o
    n
    m
    e
    n
    t d
    o
    s
    e
    t :
    u
    n
    i
    c
    o
    r
    n
    _
    p
    i
    d
    , "
    #
    {
    s
    h
    a
    r
    e
    d
    _
    p
    a
    t
    h
    }
    /
    t
    m
    p
    /
    p
    i
    d
    s
    /
    u
    n
    i
    c
    o
    r
    n
    .
    p
    i
    d
    "
    e
    n
    d
    t
    a
    s
    k :
    r
    e
    s
    t
    a
    r
    t =
    > :
    e
    n
    v
    i
    r
    o
    n
    m
    e
    n
    t d
    o
    o
    n r
    o
    l
    e
    s
    (
    :
    a
    p
    p
    ) d
    o
    i
    f t
    e
    s
    t
    (
    "
    [ -
    f #
    {
    f
    e
    t
    c
    h
    (
    :
    u
    n
    i
    c
    o
    r
    n
    _
    p
    i
    d
    )
    } ]
    "
    )
    e
    x
    e
    c
    u
    t
    e :
    k
    i
    l
    l
    , "
    -
    s U
    S
    R
    2 $
    (
    < #
    {
    f
    e
    t
    c
    h
    (
    :
    u
    n
    i
    c
    o
    r
    n
    _
    p
    i
    d
    )
    }
    )
    "
    e
    l
    s
    e
    s
    t
    a
    r
    t
    _
    u
    n
    i
    c
    o
    r
    n
    e
    n
    d
    e
    n
    d
    e
    n
    d
    e
    n
    d
    24 / 38

    View Slide

  25. Quid d'un déploiement avec migration ?
    25 / 38

    View Slide

  26. Cas simples
    Creation de tables
    l'ancien code ne connait pas le modèle associé
    26 / 38

    View Slide

  27. Cas simples
    Creation de tables
    l'ancien code ne connait pas le modèle associé
    Ajout de colonne
    l'ancien code ne connait pas la colonne
    sauf si containte/validation SQL sur la colonne
    27 / 38

    View Slide

  28. Cas nécessitant un peu d'at
    tention
    Suppression de colonne
    a priori OK depuis Rails 4.0 si l'ancien code n'utilise plus l'attribut
    28 / 38

    View Slide

  29. Cas nécessitant un peu d'at
    tention
    Suppression de colonne
    a priori OK depuis Rails 4.0 si l'ancien code n'utilise plus l'attribut
    sinon migrer en 2 étapes : préciser à ActiveRecord d'ignorer la colonne
    c
    l
    a
    s
    s U
    s
    e
    r
    d
    e
    f s
    e
    l
    f
    .
    c
    o
    l
    u
    m
    n
    s
    s
    u
    p
    e
    r
    .
    r
    e
    j
    e
    c
    t { |
    c
    o
    l
    u
    m
    n
    | c
    o
    l
    u
    m
    n
    .
    n
    a
    m
    e =
    = "
    n
    o
    t
    e
    s
    " }
    e
    n
    d
    e
    n
    d
    29 / 38

    View Slide

  30. Cas nécessitant un peu d'at
    tention
    Suppression de colonne
    a priori OK depuis Rails 4.0 si l'ancien code n'utilise plus l'attribut
    sinon migrer en 2 étapes : préciser à ActiveRecord d'ignorer la colonne
    c
    l
    a
    s
    s U
    s
    e
    r
    d
    e
    f s
    e
    l
    f
    .
    c
    o
    l
    u
    m
    n
    s
    s
    u
    p
    e
    r
    .
    r
    e
    j
    e
    c
    t { |
    c
    o
    l
    u
    m
    n
    | c
    o
    l
    u
    m
    n
    .
    n
    a
    m
    e =
    = "
    n
    o
    t
    e
    s
    " }
    e
    n
    d
    e
    n
    d
    déployer une migration qui supprime la colonne
    30 / 38

    View Slide

  31. Renommage de colonne
    ajouter une nouvelle colonne
    31 / 38

    View Slide

  32. Renommage de colonne
    ajouter une nouvelle colonne
    s'assurer de l'écriture des données sur les 2 colonnes
    d
    e
    f f
    i
    r
    s
    t
    _
    n
    a
    m
    e
    s
    u
    p
    e
    r |
    | a
    t
    t
    r
    i
    b
    u
    t
    e
    s
    [
    "
    f
    n
    a
    m
    e
    "
    ]
    e
    n
    d
    32 / 38

    View Slide

  33. Renommage de colonne
    ajouter une nouvelle colonne
    s'assurer de l'écriture des données sur les 2 colonnes
    d
    e
    f f
    i
    r
    s
    t
    _
    n
    a
    m
    e
    s
    u
    p
    e
    r |
    | a
    t
    t
    r
    i
    b
    u
    t
    e
    s
    [
    "
    f
    n
    a
    m
    e
    "
    ]
    e
    n
    d
    migrer les données, mettre à jour les éventuelles requêtes SQL
    33 / 38

    View Slide

  34. Renommage de colonne
    ajouter une nouvelle colonne
    s'assurer de l'écriture des données sur les 2 colonnes
    d
    e
    f f
    i
    r
    s
    t
    _
    n
    a
    m
    e
    s
    u
    p
    e
    r |
    | a
    t
    t
    r
    i
    b
    u
    t
    e
    s
    [
    "
    f
    n
    a
    m
    e
    "
    ]
    e
    n
    d
    migrer les données, mettre à jour les éventuelles requêtes SQL
    supprimer l'ancienne colonne comme au point précédent
    34 / 38

    View Slide

  35. Renommage de colonne
    ajouter une nouvelle colonne
    s'assurer de l'écriture des données sur les 2 colonnes
    d
    e
    f f
    i
    r
    s
    t
    _
    n
    a
    m
    e
    s
    u
    p
    e
    r |
    | a
    t
    t
    r
    i
    b
    u
    t
    e
    s
    [
    "
    f
    n
    a
    m
    e
    "
    ]
    e
    n
    d
    migrer les données, mettre à jour les éventuelles requêtes SQL
    supprimer l'ancienne colonne comme au point précédent
    Dans tous les cas !
    testez vos migrations en local sur une version à jour de votre db de
    production
    35 / 38

    View Slide

  36. Démo ?
    Questions ?
    36 / 38

    View Slide

  37. Images
    campagne "préjugés" de Cofidis
    http://techblog.betgenius.com/content/images/2014/Nov/Simple-Blue-
    Green-2.jpg
    http://levups.com
    https://github.com/images/error/angry_unicorn.png
    37 / 38

    View Slide

  38. Google-est-mon-ami-graphie
    https://www.numergy.com/developpeurs-blog/bluegreen-deployment-le-
    deploiement-continu-facile/
    http://blog.octo.com/zero-downtime-deployment/
    http://blog.appsignal.com/blog/2016/03/17/ruby-magic-mastering-
    concurrency.html
    https://unicorn.bogomips.org/SIGNALS.html
    https://blog.codeship.com/rails-migrations-zero-downtime/
    http://jakeyesbeck.com/2016/02/07/how-to-remove-a-column-with-zero-
    downtime-in-ruby-on-rails/
    https://www.rainforestqa.com/blog/2014-06-27-zero-downtime-database-
    migrations/
    38 / 38

    View Slide