nodebook
Version:
Node.js • Apprendre par la pratique. Familiarisez-vous avec JavaScript, Node.js et l'écosystème de modules npm. Apprenez à concevoir et à déployer des *applications web* et des *outils en ligne de commande*.
1,468 lines (1,387 loc) • 170 kB
HTML
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="generator" content="Asciidoctor 2.0.9">
<title>Déployer notre code</title>
<style>
#header,
#content,
#footer {
margin: 2rem auto;
max-width: 46rem;
}
#header {
margin-top: 0;
}
.title,
#toctitle {
color: var(--dark-accent);
}
a {
/* white-space: nowrap; */
}
img, iframe, video, audio {
max-width: 100%;
}
p {
font-weight: normal;
}
/* Taken out from book.css */
dl dt {
margin-bottom: 0.3125em;
font-weight: bold;
}
dl dd {
margin-bottom: 1.25em;
}
dt,
th.tableblock,
td.content,
div.footnote {
text-rendering: optimizeLegibility;
}
.literalblock pre,
.literalblock pre[class],
.listingblock pre,
.listingblock pre[class] {
overflow: auto;
word-wrap: break-word;
}
.literalblock pre.nowrap,
.literalblock pre[class].nowrap,
.listingblock pre.nowrap,
.listingblock pre[class].nowrap {
overflow-x: auto;
white-space: pre;
word-wrap: normal;
}
.listingblock {
margin: 0 0 2em;
}
.listingblock > .content {
position: relative;
}
.listingblock > .title {
font-weight: bold;
}
.listingblock code[data-lang]::before {
display: none;
content: attr(data-lang);
position: absolute;
font-size: 0.75em;
top: 1em;
right: 1em;
line-height: 1;
text-transform: uppercase;
color: #999;
}
.listingblock:hover code[data-lang]::before {
display: block;
}
.listingblock.terminal pre .command::before {
content: attr(data-prompt);
padding-right: 0.5em;
color: #999;
}
td.hdlist1,
td.hdlist2 {
vertical-align: top;
padding-right: 0.625em;
}
td.hdlist1 {
font-weight: bold;
}
.literalblock + .colist,
.listingblock + .colist {
margin-top: -1.5em;
}
.colist td:not([class]):first-child {
padding: 0.4em 0.75em 0 0.75em;
line-height: 1;
vertical-align: top;
}
.colist td:not([class]):first-child img {
max-width: none;
}
.colist td:not([class]):last-child {
padding: 0.25em 0;
}
/* Custom classes */
.line-through {
text-decoration: line-through;
}
.RemarquePreTitre,
#toctitle {
font-style: normal;
font-weight: bold;
}
.RemarquePreTitre::after {
content: "•";
padding-left: 5px;
}
.admonitionblock {
}
.admonitionblock > table,
.exampleblock {
--commented: rgba(17, 17, 68, .65);
--border-radius-base: 8px;
background-color: #fafafa;
border: 1px solid var(--dark-shade);
border-left: none;
border-right: none;
margin: 1.5em 0;
padding: 1em;
}
.exampleblock .title {
font-weight: bold;
}
.icon .title {
font-size: 2em;
}
.admonitionblock > table td.icon {
display: none;
vertical-align: middle;
}
@media screen and (min-width: 769px) {
.admonitionblock > table td.icon {
display: table-cell;
}
}
.admonitionblock > table td.icon {
padding-right: 1em;
}
.admonitionblock > table td.icon img {
max-width: none;
}
.colist ol {
margin-left: 1.5em; /* aligns with the listing edge */
padding-left: 0;
font-weight: bold; /* makes it stand out more */
}
.colist ol p {
margin: 0 0 .5em;
}
.listingblock:not(.prismjs) pre,
.language-bash.hljs {
background: #323232;
color: wheat;
margin: 0;
padding: 1rem;
}
.language-bash.hljs .hljs-built_in,
.language-bash.hljs .hljs-builtin-name {
color: white;
}
.language-bash.hljs .hljs-string {
color: lightgreen;
}
.language-bash.hljs .hljs-variable {
color: lightskyblue;
}
.keyseq {
font-weight: normal;
white-space: nowrap;
}
.language-bash.hljs .keyseq {
color: white;
}
.language-bash.hljs kbd {
background: transparent;
box-shadow: none;
color: white;
font-size: 0.8em;
font-weight: bold;
padding: 0.1em 0.4em;
}
.listingblock pre.highlightjs,
.listingblock pre.prismjs {
background-color: transparent;
margin: 0;
padding: 0;
}
.listingblock pre.highlightjs > code,
.listingblock pre.prismjs {
border-left: 4px solid var(--dark-accent);
padding-left: 1em;
font-size: .8em;
}
.listingblock pre.highlightjs > code.language-bash {
border-left-color: limegreen;
}
.token.comment .conum {
font-weight: normal;
}
.hdlist .hdlist1 {
text-align: right;
white-space: nowrap;
}
td > p:first-child {
margin-top: 0;
}
.hljs-comment {
font-style: normal !important;
}
#toc.toc2 a {
text-decoration: none;
white-space: normal;
}
#toc.toc2 a:hover,
#toc.toc2 a:focus {
text-decoration: underline;
}
#toc.toc2 ul {
list-style: none;
}
#toc.toc2 > ul {
padding-left: 0;
}
#toc.toc2 ul ul {
padding-left: 1em;
}
@media screen and (min-width: 769px) {
body {
padding-left: 25vw !important;
}
#header,
#content,
#footer {
margin-left: 0;
}
#toc.toc2 {
height: 100%;
left: 0;
max-width: 20vw;
overflow: auto;
padding: 1rem;
position: fixed;
top: 0;
z-index: 1000;
}
#toc.toc2 > ul {
font-size: 0.85em;
}
#toc li.active > a[href^="#"],
[id]:target {
background: #ffc;
}
}
.admonitionblock.context-callout > table {
border-width: 5px;
border-color: var(--brand-color);
}
</style>
<style type="text/css" class="prism-theme">/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
font-size: 1em;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
</style>
<link rel="stylesheet" href="https://oncletom.io/styles/blog.css?v=v3.3.2">
<!-- WebMentions -->
<link rel="pingback" href="https://webmention.io/oncletom.io/xmlrpc">
<link rel="webmention" href="https://webmention.io/oncletom.io/webmention">
<!-- Open Graph -->
<meta property="og:type" content="book">
<meta property="og:image" content="https://oncletom.io/images/publications/nodejs-cover.png">
<meta property="og:book:author" content="Thomas Parisot">
<meta property="og:book:isbn" content="978-2212139938">
<meta property="og:book:release_date" content="2018-12-06">
<meta property="og:book:tag" content="Node.js">
<meta property="og:book:tag" content="JavaScript">
<meta property="og:book:tag" content="npm">
<meta property="og:book:tag" content="Développement front-end">
<meta property="og:book:tag" content="Développement back-end">
<meta property="og:locale" content="fr_FR">
<meta property="og:site_name" content="Node.js • Apprendre par la pratique">
<!-- Twitter OpenGraph -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:site" content="@oncletom">
<meta name="twitter:creator" content="@oncletom">
<style type="text/css" class="extension-interactive-runner">[data-interactive-runtime="loaded"] .interactive--javascript code[data-lang]:before {
background-color: #fcfcfc;
border: 1px solid #0c2;
border-radius: 1px;
color: #0c2;
cursor: pointer;
display: inline-block;
font-weight: bold;
padding: .5em 1em;
top: -2em;
}
[lang="en"] [data-interactive-runtime="loaded"] .interactive--javascript code[data-lang]:before {
content: "▶ run code";
}
[lang="fr"] [data-interactive-runtime="loaded"] .interactive--javascript code[data-lang]:before {
content: "▶ voir le résultat";
}
.interactive--javascript code[data-label]:before {
content: attr(data-label);
}
.interactive iframe {
position: absolute;
visibility: hidden;
}
.interactive.status--loaded iframe {
position: inherit;
visibility: visible;
}
.interactive.status--loading {
opacity: .5;
}
</style>
<script class="extension-interactive-runner">
(function(d){
document.addEventListener('DOMContentLoaded', function(){
const script = d.createElement('script');
script.src = 'https://embed.runkit.com/';
script.async = true;
script.onload = function(){
function makeListingInteractive (element){
if (element.classList.contains('interactive--installed') || element.classList.contains('status--loading')) {
return;
}
const code = element.querySelector('code');
const nodeVersion = /interactive--runtime--node-([^\s]+)/.exec(element.className)[1];
const isEndpoint = element.classList.contains('interactive--endpoint');
const mode = isEndpoint ? 'endpoint' : null;
let preamble = '';
const source = code
.textContent
.replace(/\/\/\s*\(\d+\)$/gm, '')
.replace(/^["']?use strict["'][; ]*\n/, '');
if (isEndpoint) {
preamble = `process.nextTick(() => {
if (typeof module.exports === 'function') {
exports.endpoint = module.exports;
}
else if (typeof server !== 'undefined') {
exports.endpoint = server.listeners("request").pop();
}
});`
}
element.classList.add('status--loading');
// eslint-disable-next-line no-undef
RunKit.createNotebook({
nodeVersion,
element,
source,
mode,
preamble,
onLoad: function(ntbk) {
element.classList.add('interactive--installed');
element.classList.remove('status--loading');
element.classList.add('status--loaded');
code.parentNode.setAttribute('hidden', true);
ntbk.evaluate();
}
});
}
function installEvents () {
function getParent(el, condition) {
let parent = el;
while(parent = parent.parentNode) {
if (condition(parent)) {
return parent;
}
}
}
function hasClass(className) {
return function check(el) {
return el.classList.contains(className);
}
}
document.querySelector('body').addEventListener('click', function(el) {
if (el.target.classList.contains('language-javascript') || el.target.classList.contains('language-js')) {
const parentNode = getParent(
el.target,
hasClass('interactive--javascript')
);
makeListingInteractive(parentNode);
}
});
}
installEvents();
document.body.dataset.interactiveRuntime = 'loaded';
};
document.body.appendChild(script);
});
})(document);</script>
<style type="text/css">
.listingblock [data-bash-subs]::before {
content: attr(data-bash-subs) " ";
opacity: .5; }
.listingblock [data-bash-conum]::before {
content: "(" attr(data-bash-conum) ")";
font-weight: bold;
opacity: .7;
}</style>
<script>
(function(d){
d.addEventListener('DOMContentLoaded', function(){
const {origin} = window.location;
Array.from(document.querySelectorAll('a[href]'))
.filter(link => link.href.indexOf(origin) !== 0)
.forEach(link => {
link.setAttribute('target', '_blank');
link.setAttribute('rel', 'noopener');
});
});
})(document);</script>
<script>
(function(d){
d.addEventListener('DOMContentLoaded', function(){
const script = d.createElement('script');
script.src = 'https://unpkg.com/menuspy@1.3.0/dist/menuspy.js';
script.async = true;
script.onload = () => new MenuSpy(document.querySelector('#toc'), {enableLocationHash: false});
d.body.appendChild(script);
});
})(document);</script>
<style type="text/css">
#toc li.active > a[href^="#"] {
font-weight: bold;
}
#toc li.active > a[href^="#"]::before {
content: "▶ ";
display: inline-block;
position: absolute;
margin-left: -1.2em;
font-size: .8em;
margin-top: 3px;
}
</style>
</head>
<body class="book toc2 toc-left">
<div id="header">
<h1>Déployer notre code</h1>
<div id="toc" class="toc2">
<div id="toctitle">Table des matières</div>
<ul class="sectlevel1">
<li><a href="#deploy">1. Déployer une application Node</a>
<ul class="sectlevel2">
<li><a href="#deploy.notebook">1.1. En codant dans un navigateur web</a></li>
<li><a href="#deploy.sftp">1.2. En transférant des fichiers via SSH</a></li>
<li><a href="#deploy.github">1.3. En important du code depuis GitHub</a></li>
<li><a href="#deploy.cli">1.4. Avec l’outil en ligne de commande de l’hébergeur</a></li>
<li><a href="#deploy.git">1.5. En faisant git push depuis sa machine</a></li>
<li><a href="#deploy.clone">1.6. En faisant git pull lors d’une session SSH</a></li>
<li><a href="#deploy.recipe">1.7. Avec une recette de déploiement (Ansible, Chef, etc.)</a></li>
<li><a href="#deploy.docker">1.8. En publiant une image Docker</a></li>
<li><a href="#deploy.ci">1.9. En paramétrant un logiciel d’intégration continue</a></li>
</ul>
</li>
<li><a href="#hosting">2. Choisir son hébergement</a>
<ul class="sectlevel2">
<li><a href="#hosting.paas">2.1. Plate-forme de services (Platform as a Service, PaaS)</a></li>
<li><a href="#hosting.shared">2.2. Hébergement mutualisé</a></li>
<li><a href="#hosting.cloud">2.3. Serveur virtualisé, dédié ou cloud</a></li>
<li><a href="#hosting.lambda">2.4. Fonction événementielle (Serverless, Lambda)</a></li>
</ul>
</li>
<li><a href="#améliorer_la_portabilité_applicative">3. Améliorer la portabilité applicative</a>
<ul class="sectlevel2">
<li><a href="#node.version">3.1. Utiliser la bonne version de Node</a></li>
<li><a href="#port">3.2. L’application tourne mais elle est injoignable</a></li>
<li><a href="#configuration">3.3. S’affranchir des chemins et configurations écrits “en dur”</a></li>
<li><a href="#data-persistence">3.4. Faire persister les fichiers en dehors de notre application</a></li>
<li><a href="#database-migration">3.5. Versionner les schémas de base de données</a></li>
</ul>
</li>
<li><a href="#startup">4. Démarrer automatiquement une application</a>
<ul class="sectlevel2">
<li><a href="#lhébergeur_sen_occupe_à_notre_place">4.1. L’hébergeur s’en occupe à notre place</a></li>
<li><a href="#process-manager">4.2. Avec un gestionnaire de processus</a></li>
<li><a href="#system-service">4.3. En créant un service système</a></li>
<li><a href="#application-manager">4.4. Avec un serveur d’applications web</a></li>
</ul>
</li>
<li><a href="#monitoring">5. À quoi penser après la mise en ligne ?</a>
<ul class="sectlevel2">
<li><a href="#uptime">5.1. L’application a planté</a></li>
<li><a href="#exceptions">5.2. S’informer des erreurs applicatives</a></li>
<li><a href="#security.node">5.3. Notre version de Node fait l’objet d’une faille de sécurité</a></li>
<li><a href="#security.npm">5.4. Un des modules npm fait l’objet d’une faille de sécurité</a></li>
</ul>
</li>
<li><a href="#conclusion">6. Conclusion</a></li>
</ul>
</div>
</div>
<div id="content">
<div id="preamble">
<div class="sectionbody">
<div class="admonitionblock tip context-callout">
<table>
<tr>
<td class="icon">
<div class="title">💡</div>
</td>
<td class="content">
<div class="paragraph">
<p>Vous êtes en train de lire
le Chapitre 6
du <a href="https://oncletom.io/node.js/">livre “Node.js”</a>, écrit par
<a href="https://oncletom.io">Thomas Parisot</a> et publié aux
<a href="https://editions-eyrolles.com/Livre/9782212139938">Éditions Eyrolles</a>.</p>
</div>
<div class="paragraph">
<p>L’ouvrage vous plaît ? Achetez-le sur <a href="https://amzn.to/2E58PEw">Amazon.fr</a> ou en
<a href="https://www.placedeslibraires.fr/livre/9782212139938">librairie</a>.
<a href="https://opencollective.com/nodebook">Donnez quelques euros</a> pour contribuer
à sa gratuité en ligne.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>En déployant une application Node, nous améliorerons la
qualité de notre code en gommant les derniers bogues et en automatisant
la détection des erreurs et des failles de sécurité.</p>
</div>
<div class="exampleblock">
<div class="content">
<div class="ulist">
<div class="title">Sommaire</div>
<ul>
<li>
<p>Déployer une application Node</p>
</li>
<li>
<p>Choisir son hébergement</p>
</li>
<li>
<p>Améliorer la portabilité</p>
</li>
<li>
<p>Démarrer automatiquement nos applications</p>
</li>
<li>
<p>À quoi penser après la mise en ligne ?</p>
</li>
</ul>
</div>
</div>
</div>
<div class="quoteblock abstract">
<blockquote>
<div class="paragraph">
<p>Ce chapitre nous permettra d’y voir plus clair du côté de l’hébergement
et de la mise en ligne d’une application Node.
Nous pourrons choisir ce qui nous paraît le plus abordable,
que ça soit en termes d’argent ou de complexité d’utilisation.</p>
</div>
<div class="paragraph">
<p>Nous mettrons en œuvre les
<a href="../chapter-04/index.html#process.env">variables d’environnement</a>
du chapitre 4 pour que nos applications en ligne
fonctionnent de la même manière que sur notre ordinateur.</p>
</div>
<div class="paragraph">
<p>Enfin, nous verrons différents types de service pour être tenu·e informé·e
des erreurs applicatives et des failles de sécurité, sans effort.</p>
</div>
</blockquote>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">💬</div>
</td>
<td class="content">
<div class="title"><span class="RemarquePreTitre">Remarque</span> Versions de Node et npm</div>
<div class="paragraph">
<p>Le contenu de ce chapitre utilise les versions <strong>Node v10</strong>
et <strong>npm v6</strong>.
Ce sont les versions stables recommandées en 2019.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">💡</div>
</td>
<td class="content">
<div class="title"><span class="RemarquePreTitre">Pratique</span> Jouer avec les exemples dans un terminal</div>
<div class="paragraph">
<p>Les exemples titrés d’un nom de fichier peuvent être installés sur votre ordinateur.
Exécutez-les dans un terminal et amusez-vous à les modifier en parallèle de
votre lecture pour voir ce qui change.</p>
</div>
<div class="listingblock">
<div class="title">Installation des exemples via le module npm <code>nodebook</code></div>
<div class="content">
<pre><span data-bash-subs="$"></span>npm install --global nodebook
<span data-bash-subs="$"></span>nodebook install chapter-06
<span data-bash-subs="$"></span>cd $(nodebook dir chapter-06)</pre>
</div>
</div>
<div class="paragraph">
<p>La commande suivante devrait afficher un résultat qui confirme que vous êtes
au bon endroit :</p>
</div>
<div class="listingblock">
<div class="content">
<pre><span data-bash-subs="$"></span>node hello.js</pre>
</div>
</div>
<div class="paragraph">
<p>Suivez à nouveau les instructions d’installation pour rétablir les exemples
dans leur état initial.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
</div>
<div class="sect1">
<h2 id="deploy">1. Déployer une application Node</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Le choix de la technique de déploiement dépend de plusieurs facteurs qui
renvoient à eux-mêmes : l’hébergement peut dépendre du déploiement et vice-versa.</p>
</div>
<div class="paragraph">
<p>Je vous propose de partir balayer les différentes techniques de déploiement
avec des exemples et de voir quelles seraient les raisons d’opter pour l’une
ou l’autre d’entre elles.</p>
</div>
<div class="paragraph">
<p>Le choix est subjectif et vous appartient, en fonction de votre aisance
à vous en emparer.
C’est un sujet qui prend du temps avant d’être maîtrisé, donc n’hésitez pas
à vous y reprendre à plusieurs fois.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<caption class="title">Tableau 1. Quelles techniques de déploiement utiliser avec quel type d’hébergement ?</caption>
<colgroup>
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
<col style="width: 20%;">
</colgroup>
<thead>
<tr>
<th class="tableblock halign-left valign-top"></th>
<th class="tableblock halign-left valign-top"><a href="#hosting.paas"><em>PaaS</em></a></th>
<th class="tableblock halign-left valign-top"><a href="#hosting.shared">Mutualisé</a></th>
<th class="tableblock halign-left valign-top"><a href="#hosting.cloud">Cloud</a></th>
<th class="tableblock halign-left valign-top"><a href="#hosting.lambda">Lambda</a></th>
</tr>
</thead>
<tbody>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#deploy.notebook">Notebook web</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#deploy.sftp">SSH/SFTP</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">≈</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#deploy.github">Import GitHub</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#deploy.cli">CLI</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#deploy.git"><code>git push</code></a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#deploy.clone">SSH + <code>git pull</code></a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#deploy.recipe">Recette</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#deploy.docker"><code>docker push</code></a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✘</p></td>
</tr>
<tr>
<td class="tableblock halign-left valign-top"><p class="tableblock"><a href="#deploy.ci">Intégration continue</a></p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
<td class="tableblock halign-left valign-top"><p class="tableblock">✔</p></td>
</tr>
</tbody>
</table>
<div class="sect2">
<h3 id="deploy.notebook">1.1. En codant dans un navigateur web</h3>
<div class="paragraph">
<p>
</p>
</div>
<div class="paragraph">
<p>Le moyen le plus rapide d’exécuter un programme Node sans avoir à se préoccuper
du déploiement est d’utiliser un service en ligne et de modifier le code
avec un navigateur web.</p>
</div>
<div class="paragraph">
<p>Je recommande <em>RunKit</em> (<span class="URL"><a href="https://runkit.com" class="bare">runkit.com</a></span>) pour créer rapidement
du code qui tient dans un seul fichier, sans installer Node sur sa machine.
Le code est exécuté sur les serveurs de RunKit, le résultat s’affiche chez nous.
Les <a href="../chapter-05/index.html#modules">modules <code>npm</code></a>
(<a href="../chapter-05/index.html">chapitre 5</a>) sont installés automatiquement dans
leur version la plus récente.
</p>
</div>
<div class="imageblock">
<div class="content">
<img src="./images/runkit-notebook.png" alt="runkit notebook" width="85%">
</div>
<div class="title">Figure 1. Exemple de notebook RunKit dans le navigateur Firefox</div>
</div>
<div class="paragraph">
<p>RunKit propose aussi un modèle de <a href="#lambda">fonction éphémère</a> dont le
résultat devient accessible depuis une URL dédiée.
Essayez de copier/coller le code suivant dans un nouveau notebook en vous
rendant sur <span class="URL"><a href="https://runkit.com/new" class="bare">runkit.com/new</a></span> :</p>
</div>
<div class="listingblock">
<div class="title">runkit-endpoint.js</div>
<div class="content">
<pre class="highlight highlight-prismjs prismjs language-javascript"><code class="language-javascript" data-lang="javascript"><span class="token string">'use strict'</span><span class="token punctuation">;</span>
<span class="token keyword">const</span> pokemon <span class="token operator">=</span> <span class="token function">require</span><span class="token punctuation">(</span><span class="token string">'pokemon-random-name'</span><span class="token punctuation">)</span><span class="token punctuation">;</span> // <b class="conum">(1)</b>
exports<span class="token punctuation">.</span><span class="token function-variable function">endpoint</span> <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token parameter">request<span class="token punctuation">,</span> response</span><span class="token punctuation">)</span> <span class="token operator">=></span> <span class="token punctuation">{</span> // <b class="conum">(2)</b>
response<span class="token punctuation">.</span><span class="token function">end</span><span class="token punctuation">(</span><span class="token function">pokemon</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span>
<span class="token punctuation">}</span><span class="token punctuation">;</span></code></pre>
</div>
</div>
<div class="colist arabic">
<ol>
<li>
<p>Le module npm <span class="URL"><a href="https://npmjs.com/pokemon-random-name" class="bare">npmjs.com/pokemon-random-name</a></span> exporte une fonction qui retourne un nom aléatoire de Pokémon.</p>
</li>
<li>
<p><code>exports.endpoint</code> est spécifique à RunKit et accepte une fonction identique à l’événement <code>server.on('request')</code> du <a href="../chapter-04/index.html#http">module <code>http</code></a> (<a href="../chapter-04/index.html">chapitre 4</a>).</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Une fois sauvegardé puis cliqué sur le lien <span class="Menu">endpoint</span>,
un nouvel onglet s’ouvre et affiche un nom aléatoire de Pokémon.
C’est la valeur de retour passée à la réponse, comme on l’aurait fait
avec le <a href="../chapter-04/index.html#http">module <code>http</code></a> ou dans une
<a href="../chapter-07/index.html">application web</a> (<a href="../chapter-07/index.html">chapitre 7</a>).</p>
</div>
<div class="paragraph">
<p>Le service en ligne <em>Glitch</em> (<span class="URL"><a href="https://glitch.com" class="bare">glitch.com</a></span>) va plus loin
en développant, hébergeant et partageant des applications complètes.
Le service redéploie notre application à chaque changement.
Le fichier <code>.env</code> stocke les
<a href="../chapter-04/index.html#process.env">variables d’environnement</a> de manière
sécurisée – personne d’autre que nous n’y a accès.
</p>
</div>
<div class="imageblock">
<div class="content">
<img src="./images/glitch-app.png" alt="glitch app" width="85%">
</div>
<div class="title">Figure 2. Exemple d’application Node sur glitch.com</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">💡</div>
</td>
<td class="content">
<div class="title"><span class="RemarquePreTitre">Pratique</span> Console web</div>
<div class="paragraph">
<p>
Glitch nous offre même une console web : un <a href="../chapter-02/index.html#shell">terminal</a>
entièrement fonctionnel, depuis un navigateur !</p>
</div>
<div class="paragraph">
<p>C’est parfait pour <a href="../chapter-08/index.html">coder un outil en ligne de commande</a>
(<a href="../chapter-08/index.html">chapitre 8</a>) en travaillant depuis plusieurs
ordinateurs sans avoir à tout réinstaller à chaque fois.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">💡</div>
</td>
<td class="content">
<div class="title"><span class="RemarquePreTitre">Pratique</span> Remixez les exemples de cet ouvrage</div>
<div class="paragraph">
<p>Vous pouvez créer votre premier projet sur Glitch.
Remixez cet ouvrage en vous rendant sur
<span class="URL">https://glitch.com/edit/#!/remix/nodebook</span>.</p>
</div>
<div class="paragraph">
<p>Le contenu et les exemples seront copiés dans un nouveau projet,
exécutable et modifiable selon vos envies.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="deploy.sftp">1.2. En transférant des fichiers via SSH</h3>
<div class="paragraph">
<p></p>
</div>
<div class="paragraph">
<p>Transférer des fichiers est idéal pour débuter et lorsqu’on n’utilise pas Git
pour versionner son code.</p>
</div>
<div class="paragraph">
<p>Les services d’hébergement mutualisé, virtualisé ou dédié accordent
un accès à votre espace en ligne par le biais de
SSH (<span class="URL"><a href="https://fr.wikipedia.org/wiki/Secure_Shell" class="bare">fr.wikipedia.org/wiki/Secure_Shell</a></span>).
Ce protocole crée une connexion sécurisée : les commandes saisies dans votre
terminal font effet sur la machine sur laquelle vous êtes connecté·e.</p>
</div>
<div class="paragraph">
<p>Des logiciels comme <em>FileZilla Client</em> (<span class="URL"><a href="https://filezilla-project.org/" class="bare">filezilla-project.org/</a></span>)
servent d’interfaces graphiques pour transférer des fichiers vers une machine distante.</p>
</div>
<div class="paragraph">
<p>Les codes d’accès SSH se trouvent en général dans la section <span class="Menu">Aide</span>
ou <span class="Menu">Guides</span> de votre hébergeur.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="./images/filezilla-file-transfer.png" alt="filezilla file transfer" width="85%">
</div>
<div class="title">Figure 3. Exemple de connexion à un serveur SSH distant avec FileZilla Client sous macOS</div>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">💡</div>
</td>
<td class="content">
<div class="title"><span class="RemarquePreTitre">Windows</span> WinSCP</div>
<div class="paragraph">
<p><em>WinSCP</em> (<span class="URL"><a href="https://winscp.net" class="bare">winscp.net</a></span>) est une alternative libre à
FileZilla pour Windows.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">💬</div>
</td>
<td class="content">
<div class="title"><span class="RemarquePreTitre">Avancé</span> scp et rsync</div>
<div class="paragraph">
<p>
Notre terminal peut aussi servir à transférer des fichiers.
Deux programmes se basent sur SSH et sont installés par défaut sur la plupart
des ordinateurs Linux et macOS :</p>
</div>
<div class="ulist">
<ul>
<li>
<p><code>scp</code> pour envoyer des fichiers de machine à machine ;</p>
</li>
<li>
<p><code>rsync</code> pour n’envoyer que les fichiers qui ont été modifiés ou supprimés.</p>
</li>
</ul>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect2">
<h3 id="deploy.github">1.3. En important du code depuis GitHub</h3>
<div class="paragraph">
<p></p>
</div>
<div class="paragraph">
<p>Importer du code depuis GitHub est la manière la plus simple de transférer
tous les fichiers versionnés sans être familier avec Git.</p>
</div>
<div class="paragraph">
<p>La plate-forme de <a href="#deploy.notebook">programmation en ligne</a> Glitch
offre une option pour importer n’importe quel projet GitHub – à partir du moment
où le dépôt est public.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="./images/glitch-github-import.png" alt="glitch github import" width="85%">
</div>
<div class="title">Figure 4. Bouton d’import d’un dépôt GitHub sur glitch.com</div>
</div>
<div class="paragraph">
<p>Un clic sur le bouton <b class="button">Import from GitHub</b> ouvre une invite de saisie
destinée à mentionner le nom du dépôt GitHub concerné.
Le projet en cours sera entièrement remplacé par le contenu du dépôt distant.
C’est pratique pour récupérer des exercices ou pour apprendre en travaillant
sur du code écrit par quelqu’un d’autre.</p>
</div>
<div class="admonitionblock tip">
<table>
<tr>
<td class="icon">
<div class="title">💡</div>
</td>
<td class="content">
<div class="title"><span class="RemarquePreTitre">Pratique</span> Importer les exemples de cet ouvrage</div>
<div class="paragraph">
<p>Récupérez tout le contenu et les exemples de cet ouvrage
en recopiant <code>oncletom/nodebook</code> dans l’invite de saisie.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>La <a href="#paas">plate-forme de services</a> <em>Heroku</em> (<span class="URL"><a href="https://heroku.com" class="bare">heroku.com</a></span>)
pousse l’import GitHub un peu plus loin.
Sa fonctionnalité déploie l’application à chaque nouveau commit.
L’application redémarre ensuite automatiquement pour prendre les changements en compte.</p>
</div>
<div class="imageblock">
<div class="content">
<img src="./images/heroku-github-import.png" alt="heroku github import" width="85%">
</div>
<div class="title">Figure 5. Paramétrage de déploiement automatisé depuis un dépôt GitHub sur heroku.com</div>
</div>
<div class="paragraph">
<p>Une option nous permet de déployer une nouvelle version de l’application
à la suite d’une <a href="#deploy.ci">intégration continue réussie</a>.
Nous réduisons ainsi les risques de déployer une version défectueuse.</p>
</div>
</div>
<div class="sect2">
<h3 id="deploy.cli">1.4. Avec l’outil en ligne de commande de l’hébergeur</h3>
<div class="paragraph">
<p>
</p>
</div>
<div class="paragraph">
<p>L’outil en ligne de commande d’un hébergeur permet de gérer les déploiements
et d’autres aspects de l’hébergement en même temps.</p>
</div>
<div class="paragraph">
<p>La <a href="#paas">plate-forme de services</a> <em>now</em> (<span class="URL"><a href="https://zeit.co/now" class="bare">zeit.co/now</a></span>)
est un exemple de simplicité à ce niveau.
</p>
</div>
<div class="listingblock">
<div class="title">Installation et configuration de l’outil now</div>
<div class="content">
<pre><span data-bash-subs="$"></span>npm install -g now
<span data-bash-subs="$"></span>now login</pre>
</div>
</div>
<div class="paragraph">
<p>Dans un terminal, déplacez-vous vers le répertoire de l’application à déployer.
Il suffit de taper <code>now</code> pour transférer les fichiers.
Les dépendances s’installent et le déploiement est accessible quelques secondes
plus tard :</p>
</div>
<div class="listingblock">
<div class="content">
<pre><span data-bash-subs="$"></span>now
Deploying ~/workspace/dtc-innovation/food-coops-dashboards
<span data-bash-subs=">"></span>Using Node.js 9.10.1 (requested: `>=8.0.0`)
<span data-bash-subs=">"></span>https://food-coops-dashboards-okgwzegyus.now.sh
<span data-bash-subs=">"></span>Synced 1 file (169.84KB) [11s]
<span data-bash-subs=">"></span>Building...
<span data-bash-subs=">"></span>▲ npm install
<span data-bash-subs=">"></span>✓ Using "package-lock.json"
<span data-bash-subs=">"></span>⧗ Installing 9 main dependencies...
<span data-bash-subs=">"></span>▲ npm install
<span data-bash-subs=">"></span>added 389 packages in 8.609s
<span data-bash-subs=">"></span>▲ Snapshotting deployment
<span data-bash-subs=">"></span>Build completed
<span data-bash-subs=">"></span>Verifying instantiation in bru1
<span data-bash-subs=">"></span>✔ Scaled 1 instance in bru1 [31s]
<span data-bash-subs=">"></span>Success! Deployment ready</pre>
</div>
</div>
<div class="paragraph">
<p>En optant pour l’offre payante, nous pouvons aussi gérer les noms de domaine et
sous-domaines en leur attribuant l’URL du déploiement :</p>
</div>
<div class="listingblock">
<div class="content">
<pre><span data-bash-subs="$"></span>now alias food-coops-dashboards-okgwzegyus.now.sh my-domain.com</pre>
</div>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">💬</div>
</td>
<td class="content">
<div class="title"><span class="RemarquePreTitre">Pratique</span> Application de bureau</div>
<div class="paragraph">
<p>Le client en ligne de commande existe en version graphique.
Un glisser/déposer de fichiers suffit à lancer un déploiement.</p>
</div>
<div class="paragraph">
<p>Il se télécharge sur <span class="URL"><a href="https://zeit.co/download" class="bare">zeit.co/download</a></span>.</p>
</div>
</td>
</tr>
</table>
</div>
<div class="paragraph">
<p>L’outil de la <a href="#paas">plate-forme de services</a> <em>Heroku</em> suit une approche
légèrement différente.
Il nous informe de l’état de nos applications et en augmente ou diminue
la quantité de ressources allouée à leur fonctionnement.
Il simplifie la configuration de Git et
<a href="#deploy.git">délègue le déploiement</a> à ce dernier.
L’outil se télécharge sur
<span class="URL"><a href="https://devcenter.heroku.com/articles/heroku-cli" class="bare">devcenter.heroku.com/articles/heroku-cli</a></span>.</p>
</div>
<div class="listingblock">
<div class="title">Configuration de l’outil <code>heroku</code></div>
<div class="content">
<pre><span data-bash-subs="$"></span>heroku login</pre>
</div>
</div>
<div class="paragraph">
<p>La commande <code>heroku apps:create</code> crée une nouvelle application chez <em>Heroku</em>.
On peut faire la même chose dans un navigateur web en nous rendant sur
<span class="URL"><a href="https://dashboard.heroku.com/new-app" class="bare">dashboard.heroku.com/new-app</a></span>.
La commande <code>heroku git:remote</code> associe notre copie locale Git à cette application :</p>
</div>
<div class="listingblock">
<div class="title">Configuration de notre dépôt Git pour en faire une application Heroku</div>
<div class="content">
<pre><span data-bash-subs="$"></span>heroku apps:create --region eu
Creating app... done, ⬢ <mark>polar-taiga-61296</mark>, region is eu
https://polar-taiga-61296.herokuapp.com/
https://git.heroku.com/polar-taiga-61296.git
<span data-bash-subs="$"></span>heroku git:remote --app <mark>polar-taiga-61296</mark></pre>
</div>
</div>
<div class="paragraph">
<p>Il ne nous reste plus qu’à <a href="#deploy.git">pousser notre code avec Git</a>
pour terminer la mise en ligne.</p>
</div>
</div>
<div class="sect2">
<h3 id="deploy.git">1.5. En faisant git push depuis sa machine</h3>
<div class="paragraph">
<p></p>
</div>
<div class="paragraph">
<p>Le déploiement d’une branche Git est le moyen le plus facile d’automatiser
tous les aspects d’un déploiement.</p>
</div>
<div class="paragraph">
<p>Cette méthode est privilégiée par les <a href="#paas">plates-formes de services</a>
comme <em>Heroku</em>, <em>now</em> et <em>Clever Cloud</em>.
Chaque projet d’application est accessible via un dépôt Git distant
(<em>remote</em>) : un dépôt est utilisé pour versionner notre code (GitHub par exemple)
tandis qu’un autre sert pour réceptionner le code à déployer.</p>
</div>
<div class="paragraph">
<p>L’exemple suivant part du principe que notre terminal est positionné dans un
répertoire qui est un projet Git contenant au moins un <em>commit</em>.
Vous avez déjà configuré le dépôt distant à l’aide de
l'<a href="#deploy.cli">outil de déploiement</a> <em>Heroku</em> (<a href="#deploy.cli">section précédente</a>).</p>
</div>
<div class="paragraph">
<p>Nous pouvons vérifier si le dépôt est bien configuré à l’aide de la
commande <code>git remote</code> :</p>
</div>
<div class="listingblock">
<div class="title">Liste des dépôts distants d’un projet Git configuré pour Heroku</div>
<div class="content">
<pre><span data-bash-subs="$"></span>git remote -v
<mark>heroku</mark> https://git.heroku.com/mon-application.git (fetch)
<mark>heroku</mark> https://git.heroku.com/mon-application.git (push)
origin git@github.com:mon-compte/mon-application.git (fetch)
origin git@github.com:mon-compte/mon-application.git (push)</pre>
</div>
</div>
<div class="paragraph">
<p>La commande <code>heroku git:remote</code> crée un <em>remote</em> nommé
<code>heroku</code>.
Heroku redéploie notre application dès qu’on lui envoie du code avec
<code>git push heroku</code> :</p>
</div>
<div class="listingblock">
<div class="content">
<pre><span data-bash-subs="$"></span>git push heroku
<span data-bash-subs=">"></span>Counting objects: 4, done.
<span data-bash-subs=">"></span>Delta compression using up to 4 threads.
<span data-bash-subs=">"></span>Compressing objects: 100% (4/4), done.
<span data-bash-subs=">"></span>Writing objects: 100% (4/4), 17.77 KiB | 5.92 MiB/s, done.
<span data-bash-subs=">"></span>Total 4 (delta 2), reused 0 (delta 0)
<span data-bash-subs=">"></span>remote: Compressing source files... done.
<span data-bash-subs=">"></span>remote: Building source:
<span data-bash-subs=">"></span>remote:
<span data-bash-subs=">"></span>remote: -----> Node.js app detected
<span data-bash-subs=">"></span>remote:
<span data-bash-subs=">"></span>remote: -----> Creating runtime environment
<span data-bash-subs=">"></span>...
<span data-bash-subs=">"></span>remote: -----> Launching...
<span data-bash-subs=">"></span>remote: Released v30 // <b class="conum">(1)</b>
<span data-bash-subs=">"></span>remote: https://mon-application.herokuapp.com/ deployed
<span data-bash-subs=">"></span>remote:
<span data-bash-subs=">"></span>remote: Verifying deploy... done.</pre>
</div>
</div>
<div class="colist arabic">
<ol>
<li>
<p>C’est le trentième déploiement – on peut revenir à une version antérieure si nécessaire.</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>L’URL de l’application est rappelée dans les <em>logs</em> du déploiement.
En cas d’erreur, la version précédente de l’application reste en ligne.
Nous avons ainsi le temps de corriger le problème sans interruption de service.</p>
</div>
</div>
<div class="sect2">
<h3 id="deploy.clone">1.6. En faisant git pull lors d’une session SSH</h3>
<div class="paragraph">
<p>
</p>
</div>
<div class="paragraph">
<p>La récupération du code source à distance avec Git et SSH est une manière de déployer
similaire à la mise à jour et au démarrage d’une application sur notre ordinateur.</p>
</div>
<div class="paragraph">
<p>Cette technique s’applique si notre application est placée sur un
<a href="#hosting.shared">hébergement mutualisé</a>,
<a href="#hosting.vm">dédié ou virtualisé</a> ou une <a href="#hosting.cloud">offre cloud</a>.</p>
</div>
<div class="paragraph">
<p>L’exemple suivant illustre l’initialisation d’un projet via la connexion
SSH à un <a href="#hosting.shared">hébergement mutualisé</a> chez alwaysdata.</p>
</div>
<div class="listingblock">
<div class="title">Première récupération d’un dépôt Git lors d’une session SSH</div>
<div class="content">
<pre><span data-bash-subs="$"></span>ssh moncompte@ssh-moncompte.alwaysdata.net
<span data-bash-subs="$$"></span>git clone https://github.com/moncompte/monprojet .
<span data-bash-subs="$$"></span>npm install</pre>
</div>
</div>
<div class="paragraph">
<p>Nous avons cloné un projet comme nous aurions pu le faire si nous installions
notre projet depuis zéro sur notre ordinateur.</p>
</div>
<div class="paragraph">
<p>Dans le cas d’une mise à jour, nous récupérons les changements depuis le dépôt
distant avec <code>git pull</code>.
<code>npm install</code> met à jour les dépendances s’il y a des différences entre
le contenu du fichier <code>package.json</code> et les modules déjà installés
(<a href="../chapter-05/index.html">chapitre 5</a>).</p>
</div>
<div class="listingblock">
<div class="title">Mise à jour d’une application lors d’une session SSH</div>
<div class="content">
<pre><span data-bash-subs="$"></span>ssh moncompte@ssh-moncompte.alwaysdata.net
<span data-bash-subs="$$"></span>git pull
<span data-bash-subs="$$"></span>npm install</pre>
</div>
</div>
<div class="paragraph">
<p>Dans le cas d’alwaysdata, l’application se redémarre depuis
l'<a href="#hosting.shared">interface d’administration</a>.
Dans les autres cas, redémarrez l’application selon le procédé choisi après
avoir lu la section “<a href="#startup">Démarrer automatiquement nos applications</a>”.</p>
</div>
</div>
<div class="sect2">
<h3 id="deploy.recipe">1.7. Avec une recette de déploiement (Ansible, Chef, etc.)</h3>
<div class="paragraph">
<p>
</p>
</div>
<div class="paragraph">
<p>La recette de déploiement est la manière la plus complète de partager et
d’automatiser un déploiement complexe.</p>
</div>
<div class="paragraph">
<p>Cette méthode se place dans la continuité de
<a href="#deploy.clone"><code>git pull</code> lors d’une session SSH</a> : nous orchestrons les
actions nécessaires au déploiement en les listant dans un
fichier de configuration, en choisissant dans quel ordre les déclencher
et sur quel(s) serveur(s).</p>
</div>
<div class="paragraph">
<p>Nous retrouvons Puppet (<span class="URL"><a href="https://puppet.com" class="bare">puppet.com</a></span>),
Chef (<span class="URL"><a href="https://www.chef.io" class="bare">www.chef.io</a></span>) et Ansible (<span class="URL"><a href="https://ansible.com" class="bare">ansible.com</a></span>)
parmi les outils les plus utilisés et les mieux documentés.
Ils ont des philosophies de configuration et d’exécution différentes
– l’idéal est encore d’essayer d’écrire une première recette avec chacun d’entre
eux pour voir celui qui vous semble le plus naturel à utiliser.</p>
</div>
<div class="paragraph">
<p>Ma préférence va vers Ansible car le logiciel s’installe facilement
sur macOS et Linux, se configure avec une syntaxe que je connais déjà (YAML)
et je trouve ses messages d’erreurs in