From d45e8587655008199a0d7409d3b62153dc5afec6 Mon Sep 17 00:00:00 2001 From: Bot <bot@takima.fr> Date: Wed, 25 Oct 2023 03:02:47 +0000 Subject: [PATCH] Added chapter 01-docker --- public/404.html | 126 +- public/assets/microsoftStoreWsl.JPG | Bin 0 -> 51055 bytes public/ch1-discover-docker-td/index.html | 1751 ++++++++++++++++++++++ public/ch1-discover-docker-tp/index.html | 1260 ++++++++++++++++ public/ch1-getting-start/index.html | 692 +++++++++ public/cheatsheet/index.html | 196 ++- public/index.html | 167 ++- public/search/search_index.json | 2 +- public/sitemap.xml | 15 + public/sitemap.xml.gz | Bin 195 -> 199 bytes 10 files changed, 4089 insertions(+), 120 deletions(-) create mode 100644 public/assets/microsoftStoreWsl.JPG create mode 100644 public/ch1-discover-docker-td/index.html create mode 100644 public/ch1-discover-docker-tp/index.html create mode 100644 public/ch1-getting-start/index.html diff --git a/public/404.html b/public/404.html index c311688..2d8797d 100644 --- a/public/404.html +++ b/public/404.html @@ -13,7 +13,7 @@ - <title>Devops</title> + <title>Docker</title> @@ -72,7 +72,7 @@ <header class="md-header" data-md-component="header"> <nav class="md-header__inner md-grid" aria-label="Header"> - <a href="/." title="Devops" class="md-header__button md-logo" aria-label="Devops" data-md-component="logo"> + <a href="/." title="Docker" class="md-header__button md-logo" aria-label="Docker" data-md-component="logo"> <img src="/assets/logo.png" alt="logo"> @@ -84,7 +84,7 @@ <div class="md-header__ellipsis"> <div class="md-header__topic"> <span class="md-ellipsis"> - Devops + Docker </span> </div> <div class="md-header__topic" data-md-component="header-topic"> @@ -152,11 +152,15 @@ - <li class="md-tabs__item"> - <a href="/." class="md-tabs__link"> - Devops in Action - Guide - </a> - </li> + + + + <li class="md-tabs__item"> + <a href="/ch1-getting-start/" class="md-tabs__link"> + TD + </a> + </li> + @@ -164,11 +168,15 @@ - <li class="md-tabs__item"> - <a href="/cheatsheet/" class="md-tabs__link"> - Cheatsheet - </a> - </li> + + + + <li class="md-tabs__item"> + <a href="/ch1-discover-docker-tp/" class="md-tabs__link"> + TP + </a> + </li> + </ul> @@ -192,12 +200,12 @@ <nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0"> <label class="md-nav__title" for="__drawer"> - <a href="/." title="Devops" class="md-nav__button md-logo" aria-label="Devops" data-md-component="logo"> + <a href="/." title="Docker" class="md-nav__button md-logo" aria-label="Docker" data-md-component="logo"> <img src="/assets/logo.png" alt="logo"> </a> - Devops + Docker </label> <ul class="md-nav__list" data-md-scrollfix> @@ -209,13 +217,60 @@ + + <li class="md-nav__item md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_1" type="checkbox" id="__nav_1" > + + + + + <label class="md-nav__link" for="__nav_1"> + TD + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TD" data-md-level="1"> + <label class="md-nav__title" for="__nav_1"> + <span class="md-nav__icon md-icon"></span> + TD + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + <li class="md-nav__item"> + <a href="/ch1-getting-start/" class="md-nav__link"> + Getting Start - Docker + </a> + </li> + + + + + + + + + <li class="md-nav__item"> - <a href="/." class="md-nav__link"> - Devops in Action - Guide + <a href="/ch1-discover-docker-td/" class="md-nav__link"> + TD - Docker </a> </li> + + + </ul> + </nav> + </li> + + @@ -224,13 +279,46 @@ + + <li class="md-nav__item md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2" type="checkbox" id="__nav_2" > + + + + + <label class="md-nav__link" for="__nav_2"> + TP + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TP" data-md-level="1"> + <label class="md-nav__title" for="__nav_2"> + <span class="md-nav__icon md-icon"></span> + TP + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + <li class="md-nav__item"> - <a href="/cheatsheet/" class="md-nav__link"> - Cheatsheet + <a href="/ch1-discover-docker-tp/" class="md-nav__link"> + TP - Docker </a> </li> + + + </ul> + </nav> + </li> + + </ul> </nav> diff --git a/public/assets/microsoftStoreWsl.JPG b/public/assets/microsoftStoreWsl.JPG new file mode 100644 index 0000000000000000000000000000000000000000..fad59bea0067a425a2edf659b0085e3efec347be GIT binary patch literal 51055 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 literal 0 HcmV?d00001 diff --git a/public/ch1-discover-docker-td/index.html b/public/ch1-discover-docker-td/index.html new file mode 100644 index 0000000..fd75e74 --- /dev/null +++ b/public/ch1-discover-docker-td/index.html @@ -0,0 +1,1751 @@ + +<!doctype html> +<html lang="en" class="no-js"> + <head> + + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + + + + <link rel="icon" href="../assets/images/favicon.png"> + <meta name="generator" content="mkdocs-1.3.0, mkdocs-material-8.2.15"> + + + + <title>TD - Docker - Docker</title> + + + + <link rel="stylesheet" href="../assets/stylesheets/main.c382b1dc.min.css"> + + + <link rel="stylesheet" href="../assets/stylesheets/palette.cc9b2e1e.min.css"> + + + + <meta name="theme-color" content="#e92063"> + + + + + + + + + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"> + <style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style> + + + + <script>__md_scope=new URL("..",location),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script> + + + + + + </head> + + + + + + + + <body dir="ltr" data-md-color-scheme="" data-md-color-primary="pink" data-md-color-accent=""> + + + + <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off"> + <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off"> + <label class="md-overlay" for="__drawer"></label> + <div data-md-component="skip"> + + + <a href="#decouverte-de-docker" class="md-skip"> + Skip to content + </a> + + </div> + <div data-md-component="announce"> + + </div> + + + + +<header class="md-header" data-md-component="header"> + <nav class="md-header__inner md-grid" aria-label="Header"> + <a href=".." title="Docker" class="md-header__button md-logo" aria-label="Docker" data-md-component="logo"> + + <img src="../assets/logo.png" alt="logo"> + + </a> + <label class="md-header__button md-icon" for="__drawer"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg> + </label> + <div class="md-header__title" data-md-component="header-title"> + <div class="md-header__ellipsis"> + <div class="md-header__topic"> + <span class="md-ellipsis"> + Docker + </span> + </div> + <div class="md-header__topic" data-md-component="header-topic"> + <span class="md-ellipsis"> + + TD - Docker + + </span> + </div> + </div> + </div> + + + + <label class="md-header__button md-icon" for="__search"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg> + </label> + <div class="md-search" data-md-component="search" role="dialog"> + <label class="md-search__overlay" for="__search"></label> + <div class="md-search__inner" role="search"> + <form class="md-search__form" name="search"> + <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required> + <label class="md-search__icon md-icon" for="__search"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg> + </label> + <nav class="md-search__options" aria-label="Search"> + + <button type="reset" class="md-search__icon md-icon" aria-label="Clear" tabindex="-1"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg> + </button> + </nav> + + </form> + <div class="md-search__output"> + <div class="md-search__scrollwrap" data-md-scrollfix> + <div class="md-search-result" data-md-component="search-result"> + <div class="md-search-result__meta"> + Initializing search + </div> + <ol class="md-search-result__list"></ol> + </div> + </div> + </div> + </div> +</div> + + + </nav> + +</header> + + <div class="md-container" data-md-component="container"> + + + + + +<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs"> + <div class="md-tabs__inner md-grid"> + <ul class="md-tabs__list"> + + + + + + + + + + + + <li class="md-tabs__item"> + <a href="../ch1-getting-start/" class="md-tabs__link md-tabs__link--active"> + TD + </a> + </li> + + + + + + + + + + + + <li class="md-tabs__item"> + <a href="../ch1-discover-docker-tp/" class="md-tabs__link"> + TP + </a> + </li> + + + + </ul> + </div> +</nav> + + + + <main class="md-main" data-md-component="main"> + <div class="md-main__inner md-grid"> + + + + <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" > + <div class="md-sidebar__scrollwrap"> + <div class="md-sidebar__inner"> + + + + + +<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0"> + <label class="md-nav__title" for="__drawer"> + <a href=".." title="Docker" class="md-nav__button md-logo" aria-label="Docker" data-md-component="logo"> + + <img src="../assets/logo.png" alt="logo"> + + </a> + Docker + </label> + + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + + + + + + <li class="md-nav__item md-nav__item--active md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_1" type="checkbox" id="__nav_1" checked> + + + + + <label class="md-nav__link" for="__nav_1"> + TD + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TD" data-md-level="1"> + <label class="md-nav__title" for="__nav_1"> + <span class="md-nav__icon md-icon"></span> + TD + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + <li class="md-nav__item"> + <a href="../ch1-getting-start/" class="md-nav__link"> + Getting Start - Docker + </a> + </li> + + + + + + + + + + + + <li class="md-nav__item md-nav__item--active"> + + <input class="md-nav__toggle md-toggle" data-md-toggle="toc" type="checkbox" id="__toc"> + + + + + + <label class="md-nav__link md-nav__link--active" for="__toc"> + TD - Docker + <span class="md-nav__icon md-icon"></span> + </label> + + <a href="./" class="md-nav__link md-nav__link--active"> + TD - Docker + </a> + + + +<nav class="md-nav md-nav--secondary" aria-label="Table of contents"> + + + + + + + <label class="md-nav__title" for="__toc"> + <span class="md-nav__icon md-icon"></span> + Table of contents + </label> + <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> + + <li class="md-nav__item"> + <a href="#configuration" class="md-nav__link"> + Configuration + </a> + + <nav class="md-nav" aria-label="Configuration"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#prerequis" class="md-nav__link"> + Prérequis + </a> + +</li> + + <li class="md-nav__item"> + <a href="#configuration-de-votre-ordinateur" class="md-nav__link"> + Configuration de votre ordinateur + </a> + +</li> + + <li class="md-nav__item"> + <a href="#execution-de-votre-premier-conteneur" class="md-nav__link"> + Exécution de votre premier conteneur + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#executer-un-docker" class="md-nav__link"> + Exécuter un Docker + </a> + +</li> + + <li class="md-nav__item"> + <a href="#les-network-docker" class="md-nav__link"> + Les Network Docker + </a> + + <nav class="md-nav" aria-label="Les Network Docker"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#objectif" class="md-nav__link"> + Objectif: + </a> + +</li> + + <li class="md-nav__item"> + <a href="#verifier-les-reseaux-docker-existants" class="md-nav__link"> + Vérifier les réseaux Docker existants + </a> + +</li> + + <li class="md-nav__item"> + <a href="#creer-un-reseau-personnalise" class="md-nav__link"> + Créer un réseau personnalisé + </a> + +</li> + + <li class="md-nav__item"> + <a href="#executer-des-conteneurs-connectes-au-reseau-personnalise" class="md-nav__link"> + Exécuter des conteneurs connectés au réseau personnalisé + </a> + +</li> + + <li class="md-nav__item"> + <a href="#verifier-la-connectivite" class="md-nav__link"> + Vérifier la connectivité + </a> + +</li> + + <li class="md-nav__item"> + <a href="#supprimer-le-reseau" class="md-nav__link"> + Supprimer le réseau + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#la-publication" class="md-nav__link"> + La publication + </a> + + <nav class="md-nav" aria-label="La publication"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#objectif_1" class="md-nav__link"> + Objectif : + </a> + +</li> + + <li class="md-nav__item"> + <a href="#verification-des-ports-existants" class="md-nav__link"> + Vérification des ports existants + </a> + +</li> + + <li class="md-nav__item"> + <a href="#creer-un-conteneur-avec-des-ports-publies" class="md-nav__link"> + Créer un conteneur avec des ports publiés + </a> + +</li> + + <li class="md-nav__item"> + <a href="#acces-a-lapplication-via-le-port-publie" class="md-nav__link"> + Accès à l'application via le port publié + </a> + +</li> + + <li class="md-nav__item"> + <a href="#gestion-des-ports-publies" class="md-nav__link"> + Gestion des ports publiés + </a> + +</li> + + <li class="md-nav__item"> + <a href="#arreter-et-supprimer-les-conteneurs" class="md-nav__link"> + Arrêter et supprimer les conteneurs + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#les-volumes-docker" class="md-nav__link"> + Les volumes Docker + </a> + + <nav class="md-nav" aria-label="Les volumes Docker"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#creation-dun-conteneur-avec-un-volume" class="md-nav__link"> + Création d'un Conteneur avec un Volume + </a> + + <nav class="md-nav" aria-label="Création d'un Conteneur avec un Volume"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#utilisation-du-volume-depuis-le-conteneur" class="md-nav__link"> + Utilisation du Volume depuis le Conteneur + </a> + +</li> + + <li class="md-nav__item"> + <a href="#verification-de-la-persistance-des-donnees" class="md-nav__link"> + Vérification de la Persistance des Données + </a> + +</li> + + <li class="md-nav__item"> + <a href="#creation-dun-deuxieme-conteneur-avec-le-meme-volume" class="md-nav__link"> + Création d'un Deuxième Conteneur avec le Même Volume + </a> + +</li> + + <li class="md-nav__item"> + <a href="#clean" class="md-nav__link"> + clean + </a> + +</li> + + </ul> + </nav> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#images-docker" class="md-nav__link"> + Images Docker + </a> + + <nav class="md-nav" aria-label="Images Docker"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#creez-votre-premiere-image" class="md-nav__link"> + Créez votre première image + </a> + +</li> + + <li class="md-nav__item"> + <a href="#creez-une-application-python-flask-qui-affiche-des-images-de-chats-aleatoires" class="md-nav__link"> + Créez une application Python Flask qui affiche des images de chats aléatoires. + </a> + + <nav class="md-nav" aria-label="Créez une application Python Flask qui affiche des images de chats aléatoires."> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#apppy" class="md-nav__link"> + app.py + </a> + +</li> + + <li class="md-nav__item"> + <a href="#requirementstxt" class="md-nav__link"> + requirements.txt + </a> + +</li> + + <li class="md-nav__item"> + <a href="#templatesindexhtml" class="md-nav__link"> + templates/index.html + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#redigez-un-dockerfile" class="md-nav__link"> + Rédigez un Dockerfile + </a> + +</li> + + <li class="md-nav__item"> + <a href="#builder-notre-image" class="md-nav__link"> + Builder notre image + </a> + +</li> + + <li class="md-nav__item"> + <a href="#executez-votre-image" class="md-nav__link"> + Exécutez votre image + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#docker-compose" class="md-nav__link"> + Docker Compose + </a> + + <nav class="md-nav" aria-label="Docker Compose"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#pourquoi-faire" class="md-nav__link"> + Pourquoi faire ? + </a> + +</li> + + <li class="md-nav__item"> + <a href="#utilisation" class="md-nav__link"> + Utilisation + </a> + +</li> + + </ul> + </nav> + +</li> + + </ul> + +</nav> + + </li> + + + + + </ul> + </nav> + </li> + + + + + + + + + + + + <li class="md-nav__item md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2" type="checkbox" id="__nav_2" > + + + + + <label class="md-nav__link" for="__nav_2"> + TP + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TP" data-md-level="1"> + <label class="md-nav__title" for="__nav_2"> + <span class="md-nav__icon md-icon"></span> + TP + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + <li class="md-nav__item"> + <a href="../ch1-discover-docker-tp/" class="md-nav__link"> + TP - Docker + </a> + </li> + + + + + </ul> + </nav> + </li> + + + + </ul> +</nav> + </div> + </div> + </div> + + + + <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" > + <div class="md-sidebar__scrollwrap"> + <div class="md-sidebar__inner"> + + +<nav class="md-nav md-nav--secondary" aria-label="Table of contents"> + + + + + + + <label class="md-nav__title" for="__toc"> + <span class="md-nav__icon md-icon"></span> + Table of contents + </label> + <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> + + <li class="md-nav__item"> + <a href="#configuration" class="md-nav__link"> + Configuration + </a> + + <nav class="md-nav" aria-label="Configuration"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#prerequis" class="md-nav__link"> + Prérequis + </a> + +</li> + + <li class="md-nav__item"> + <a href="#configuration-de-votre-ordinateur" class="md-nav__link"> + Configuration de votre ordinateur + </a> + +</li> + + <li class="md-nav__item"> + <a href="#execution-de-votre-premier-conteneur" class="md-nav__link"> + Exécution de votre premier conteneur + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#executer-un-docker" class="md-nav__link"> + Exécuter un Docker + </a> + +</li> + + <li class="md-nav__item"> + <a href="#les-network-docker" class="md-nav__link"> + Les Network Docker + </a> + + <nav class="md-nav" aria-label="Les Network Docker"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#objectif" class="md-nav__link"> + Objectif: + </a> + +</li> + + <li class="md-nav__item"> + <a href="#verifier-les-reseaux-docker-existants" class="md-nav__link"> + Vérifier les réseaux Docker existants + </a> + +</li> + + <li class="md-nav__item"> + <a href="#creer-un-reseau-personnalise" class="md-nav__link"> + Créer un réseau personnalisé + </a> + +</li> + + <li class="md-nav__item"> + <a href="#executer-des-conteneurs-connectes-au-reseau-personnalise" class="md-nav__link"> + Exécuter des conteneurs connectés au réseau personnalisé + </a> + +</li> + + <li class="md-nav__item"> + <a href="#verifier-la-connectivite" class="md-nav__link"> + Vérifier la connectivité + </a> + +</li> + + <li class="md-nav__item"> + <a href="#supprimer-le-reseau" class="md-nav__link"> + Supprimer le réseau + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#la-publication" class="md-nav__link"> + La publication + </a> + + <nav class="md-nav" aria-label="La publication"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#objectif_1" class="md-nav__link"> + Objectif : + </a> + +</li> + + <li class="md-nav__item"> + <a href="#verification-des-ports-existants" class="md-nav__link"> + Vérification des ports existants + </a> + +</li> + + <li class="md-nav__item"> + <a href="#creer-un-conteneur-avec-des-ports-publies" class="md-nav__link"> + Créer un conteneur avec des ports publiés + </a> + +</li> + + <li class="md-nav__item"> + <a href="#acces-a-lapplication-via-le-port-publie" class="md-nav__link"> + Accès à l'application via le port publié + </a> + +</li> + + <li class="md-nav__item"> + <a href="#gestion-des-ports-publies" class="md-nav__link"> + Gestion des ports publiés + </a> + +</li> + + <li class="md-nav__item"> + <a href="#arreter-et-supprimer-les-conteneurs" class="md-nav__link"> + Arrêter et supprimer les conteneurs + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#les-volumes-docker" class="md-nav__link"> + Les volumes Docker + </a> + + <nav class="md-nav" aria-label="Les volumes Docker"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#creation-dun-conteneur-avec-un-volume" class="md-nav__link"> + Création d'un Conteneur avec un Volume + </a> + + <nav class="md-nav" aria-label="Création d'un Conteneur avec un Volume"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#utilisation-du-volume-depuis-le-conteneur" class="md-nav__link"> + Utilisation du Volume depuis le Conteneur + </a> + +</li> + + <li class="md-nav__item"> + <a href="#verification-de-la-persistance-des-donnees" class="md-nav__link"> + Vérification de la Persistance des Données + </a> + +</li> + + <li class="md-nav__item"> + <a href="#creation-dun-deuxieme-conteneur-avec-le-meme-volume" class="md-nav__link"> + Création d'un Deuxième Conteneur avec le Même Volume + </a> + +</li> + + <li class="md-nav__item"> + <a href="#clean" class="md-nav__link"> + clean + </a> + +</li> + + </ul> + </nav> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#images-docker" class="md-nav__link"> + Images Docker + </a> + + <nav class="md-nav" aria-label="Images Docker"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#creez-votre-premiere-image" class="md-nav__link"> + Créez votre première image + </a> + +</li> + + <li class="md-nav__item"> + <a href="#creez-une-application-python-flask-qui-affiche-des-images-de-chats-aleatoires" class="md-nav__link"> + Créez une application Python Flask qui affiche des images de chats aléatoires. + </a> + + <nav class="md-nav" aria-label="Créez une application Python Flask qui affiche des images de chats aléatoires."> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#apppy" class="md-nav__link"> + app.py + </a> + +</li> + + <li class="md-nav__item"> + <a href="#requirementstxt" class="md-nav__link"> + requirements.txt + </a> + +</li> + + <li class="md-nav__item"> + <a href="#templatesindexhtml" class="md-nav__link"> + templates/index.html + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#redigez-un-dockerfile" class="md-nav__link"> + Rédigez un Dockerfile + </a> + +</li> + + <li class="md-nav__item"> + <a href="#builder-notre-image" class="md-nav__link"> + Builder notre image + </a> + +</li> + + <li class="md-nav__item"> + <a href="#executez-votre-image" class="md-nav__link"> + Exécutez votre image + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#docker-compose" class="md-nav__link"> + Docker Compose + </a> + + <nav class="md-nav" aria-label="Docker Compose"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#pourquoi-faire" class="md-nav__link"> + Pourquoi faire ? + </a> + +</li> + + <li class="md-nav__item"> + <a href="#utilisation" class="md-nav__link"> + Utilisation + </a> + +</li> + + </ul> + </nav> + +</li> + + </ul> + +</nav> + </div> + </div> + </div> + + + <div class="md-content" data-md-component="content"> + <article class="md-content__inner md-typeset"> + + + + +<h1 id="decouverte-de-docker">Découverte de Docker</h1> +<h2 id="configuration">Configuration</h2> +<h3 id="prerequis">Prérequis</h3> +<p>Il n'est pas nécessaire d'avoir des compétences spécifiques pour ce tutoriel, si ce n'est une certaine familiarité avec les command line et l'utilisation d'un éditeur de texte. +Une expérience antérieure dans le développement d'applications web sera utile, mais n'est pas nécessaire.</p> +<h3 id="configuration-de-votre-ordinateur">Configuration de votre ordinateur</h3> +<p>Comme dit précédemment il vous faudra avoir un outil docker fonctionnel. Pour vérifier que tout focntionne testons une commande docker :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>docker<span class="w"> </span>run<span class="w"> </span>registry.takima.io/school/proxy/hello-world +<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>Unable<span class="w"> </span>to<span class="w"> </span>find<span class="w"> </span>image<span class="w"> </span><span class="s1">'registry.takima.io/school/proxy/hello-world:latest'</span><span class="w"> </span>locally +<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a>latest:<span class="w"> </span>Pulling<span class="w"> </span>from<span class="w"> </span>registry.takima.io/school/proxy/hello-world +<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a>03f4658f8b78:<span class="w"> </span>Pull<span class="w"> </span><span class="nb">complete</span> +<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a>a3ed95caeb02:<span class="w"> </span>Pull<span class="w"> </span><span class="nb">complete</span> +<a id="__codelineno-0-6" name="__codelineno-0-6" href="#__codelineno-0-6"></a>Digest:<span class="w"> </span>sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7 +<a id="__codelineno-0-7" name="__codelineno-0-7" href="#__codelineno-0-7"></a>Status:<span class="w"> </span>Downloaded<span class="w"> </span>newer<span class="w"> </span>image<span class="w"> </span><span class="k">for</span><span class="w"> </span>registry.takima.io/school/proxy/hello-world:latest +<a id="__codelineno-0-8" name="__codelineno-0-8" href="#__codelineno-0-8"></a> +<a id="__codelineno-0-9" name="__codelineno-0-9" href="#__codelineno-0-9"></a>Hello<span class="w"> </span>from<span class="w"> </span>Docker. +<a id="__codelineno-0-10" name="__codelineno-0-10" href="#__codelineno-0-10"></a>... +</code></pre></div> +<p>Ce message indique que votre installation semble fonctionner correctement.</p> +<p>Si ce n'est pas le cas parcourez le <code>Getting Start</code> pour installer correctement docker</p> +<h3 id="execution-de-votre-premier-conteneur">Exécution de votre premier conteneur</h3> +<p>Maintenant que vous avez tout configuré, il est temps de mettre les mains dans le cambouis.<br /> +Dans cette section, vous allez exécuter un conteneur Alpine Linux (une distribution Linux légère) sur votre système et découvrir la commande <code>docker run</code>.</p> +<p>Pour commencer, exécutez la commande suivante dans votre terminal pour récuperer (télécharger) l'image depuis le registry takima :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>$<span class="w"> </span>docker<span class="w"> </span>pull<span class="w"> </span>registry.takima.io/school/proxy/alpine +</code></pre></div> +<p>Puis on va tag cette image pour créer une copie et lui donner un nom plus commode à utiliser pour nos prochaines lignes de commandes :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>$<span class="w"> </span>docker<span class="w"> </span>tag<span class="w"> </span>registry.takima.io/school/proxy/alpine<span class="w"> </span>alpine +</code></pre></div> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>En fonction de la manière dont vous avez installé Docker sur votre système, vous pourriez voir une erreur de permission refusée après avoir exécuté la commande ci-dessus. Essayez les commandes du tutoriel de démarrage pour <a href="https://docs.docker.com/get-started/#/step-3-verify-your-installation" target="_blank">vérifier votre installation</a>. Si vous êtes sous Linux, vous devrez peut-être préfixer vos commandes <code>docker</code> avec <code>sudo</code>. Vous pouvez également <a href="https://docs.docker.com/engine/installation/linux/ubuntulinux/#/create-a-docker-group" target="_blank">créer un groupe Docker</a> pour résoudre ce problème.</p> +</div> +<p>La commande <code>pull</code> récupère l'image Alpine depuis notre registre Docker et la sauvegarde sur notre système. Vous pouvez utiliser la commande <code>docker images</code> pour voir la liste de toutes les images sur votre système.</p> +<p>Nous aurions pu aussi récupérer une image Docker publique (Docker Hub) qui contient une multitude d'images standard en utilisant :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a>$<span class="w"> </span>docker<span class="w"> </span>pull<span class="w"> </span>alpine +</code></pre></div> +<p>Cependant Docker Hub est soumis à un rate limit donc nous allons éviter de pull dessus dans cette formation et utiliser le registry Takima.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a>$<span class="w"> </span>docker<span class="w"> </span>images +<a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a>RÉPERTOIRE<span class="w"> </span>TAG<span class="w"> </span>ID<span class="w"> </span>DE<span class="w"> </span>L<span class="err">'</span>IMAGE<span class="w"> </span>CRÉÉ<span class="w"> </span>TAILLE<span class="w"> </span>VIRTUELLE +<a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a>alpine<span class="w"> </span>dernière<span class="w"> </span>c51f86c28340<span class="w"> </span>il<span class="w"> </span>y<span class="w"> </span>a<span class="w"> </span><span class="m">4</span><span class="w"> </span>semaines<span class="w"> </span><span class="m">1</span>,109<span class="w"> </span>Mo +<a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a>registry.takima.io/school/proxy/alpine<span class="w"> </span>dernière<span class="w"> </span>c51f86c28340<span class="w"> </span>il<span class="w"> </span>y<span class="w"> </span>a<span class="w"> </span><span class="m">4</span><span class="w"> </span>semaines<span class="w"> </span><span class="m">1</span>,109<span class="w"> </span>Mo +<a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a>registry.takima.io/school/proxy/hello-world<span class="w"> </span>dernière<span class="w"> </span>690ed74de00f<span class="w"> </span>il<span class="w"> </span>y<span class="w"> </span>a<span class="w"> </span><span class="m">5</span><span class="w"> </span>mois<span class="w"> </span><span class="m">960</span><span class="w"> </span>o +</code></pre></div> +<h2 id="executer-un-docker">Exécuter un Docker</h2> +<p>Génial ! Exécutons maintenant un conteneur Docker basé sur cette image. Pour cela, nous allons utiliser la commande <code>docker run</code>.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a>$<span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span>alpine<span class="w"> </span>ls<span class="w"> </span>-l +<a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>total<span class="w"> </span><span class="m">48</span> +<a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a>drwxr-xr-x<span class="w"> </span><span class="m">2</span><span class="w"> </span>root<span class="w"> </span>root<span class="w"> </span><span class="m">4096</span><span class="w"> </span><span class="m">2</span><span class="w"> </span>mars<span class="w"> </span><span class="m">16</span>:20<span class="w"> </span>bin +<a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a>drwxr-xr-x<span class="w"> </span><span class="m">5</span><span class="w"> </span>root<span class="w"> </span>root<span class="w"> </span><span class="m">360</span><span class="w"> </span><span class="m">18</span><span class="w"> </span>mars<span class="w"> </span><span class="m">09</span>:47<span class="w"> </span>dev +<a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a>drwxr-xr-x<span class="w"> </span><span class="m">13</span><span class="w"> </span>root<span class="w"> </span>root<span class="w"> </span><span class="m">4096</span><span class="w"> </span><span class="m">18</span><span class="w"> </span>mars<span class="w"> </span><span class="m">09</span>:47<span class="w"> </span>etc +<a id="__codelineno-5-6" name="__codelineno-5-6" href="#__codelineno-5-6"></a>drwxr-xr-x<span class="w"> </span><span class="m">2</span><span class="w"> </span>root<span class="w"> </span>root<span class="w"> </span><span class="m">4096</span><span class="w"> </span><span class="m">2</span><span class="w"> </span>mars<span class="w"> </span><span class="m">16</span>:20<span class="w"> </span>home +<a id="__codelineno-5-7" name="__codelineno-5-7" href="#__codelineno-5-7"></a>drwxr-xr-x<span class="w"> </span><span class="m">5</span><span class="w"> </span>root<span class="w"> </span>root<span class="w"> </span><span class="m">4096</span><span class="w"> </span><span class="m">2</span><span class="w"> </span>mars<span class="w"> </span><span class="m">16</span>:20<span class="w"> </span>lib +<a id="__codelineno-5-8" name="__codelineno-5-8" href="#__codelineno-5-8"></a>... +<a id="__codelineno-5-9" name="__codelineno-5-9" href="#__codelineno-5-9"></a>... +</code></pre></div> +<p>Qu'est-il arrivé ? En coulisses, il s'est passé beaucoup de choses. Lorsque vous appelez <code>run</code> :</p> +<ol> +<li> +<p>Le client Docker contacte le daemon Docker.</p> +</li> +<li> +<p>Le daemon Docker vérifie si l'image (dans ce cas, Alpine) est disponible localement, et si ce n'est pas le cas, la télécharge depuis le registry Docker. (Comme nous avons exécuté <code>docker pull registry.takima.io/school/proxy/alpine</code> auparavant puis tag cette image en la nommant <code>alpine</code>, l'étape de téléchargement n'est pas nécessaire)</p> +</li> +<li> +<p>Le daemon Docker crée le conteneur, puis exécute une commande dans ce conteneur.</p> +</li> +<li> +<p>Le daemon Docker transmet la sortie de la commande au client Docker.</p> +</li> +</ol> +<p>Lorsque vous exécutez <code>docker run alpine</code>, vous avez fourni une commande (<code>ls -l</code>), alors Docker a démarré la commande spécifiée et vous avez vu la liste.</p> +<p>Essayons quelque chose de plus utile : afficher un Hello World bien sur!</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a>$<span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span>alpine<span class="w"> </span><span class="nb">echo</span><span class="w"> </span><span class="s2">"hello from alpine"</span> +<a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a>hello<span class="w"> </span>from<span class="w"> </span>alpine +</code></pre></div> +<p>OK, voilà une sortie réelle. Dans ce cas, le client Docker a exécuté fidèlement la commande <code>echo</code> dans notre conteneur Alpine, puis l'a quitté. Si vous avez remarqué, tout cela s'est produit très rapidement. Imaginez démarrer une machine virtuelle, exécuter une commande, puis la détruire. Maintenant, vous savez pourquoi on dit que les conteneurs sont rapides !</p> +<p>Essayons une autre commande.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>$ docker run alpine /bin/sh +</code></pre></div> +<p>Attendez, il ne s'est rien passé ! Est-ce un bogue ? Eh bien, non. Ces exécutions interactives se ferment après avoir exécuté les commandes scriptées, sauf si elles sont exécutées dans un terminal interactif. Pour éviter que cet exemple ne se ferme, vous devez utiliser <code>docker run -it alpine /bin/sh</code> .</p> +<p>Vous êtes maintenant à l'intérieur du shell du conteneur et vous pouvez naviguer dans le conteneur. Si vous lancez <code>exit</code> ou que vous tapez <code>CTRL-D</code>, vous quitterez le shell du conteneur et il s'arrêtera également (attention il ne se détruira pas). Le conteneur est censé être éphémère.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a>$ exit +</code></pre></div> +<p>Maintenant, voyons comment lister les conteneurs avec la commande <code>docker ps</code>. <code>docker ps</code> est la commande qui listera tous les conteneurs en cours d'exécution.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a>$<span class="w"> </span>docker<span class="w"> </span>ps +<a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a>CONTAINER<span class="w"> </span>ID<span class="w"> </span>IMAGE<span class="w"> </span>COMMAND<span class="w"> </span>CREATED<span class="w"> </span>STATUS<span class="w"> </span>PORTS<span class="w"> </span>NAMES +</code></pre></div> +<p>Comme vous n'avez pas de conteneur qui tourne actuelement (seulement des conteneurs qui ont été lancés mais qui sont maintenant stoppés), cette commande renvoie une liste vide. Essayons une petite variante : <code>docker ps -a</code></p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a>$<span class="w"> </span>docker<span class="w"> </span>ps<span class="w"> </span>-a +<a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>CONTAINER<span class="w"> </span>ID<span class="w"> </span>IMAGE<span class="w"> </span>COMMAND<span class="w"> </span>CREATED<span class="w"> </span>STATUS<span class="w"> </span>PORTS<span class="w"> </span>NAMES +<a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a>36171a5da744<span class="w"> </span>alpine<span class="w"> </span><span class="s2">"/bin/sh"</span><span class="w"> </span><span class="m">5</span><span class="w"> </span>minutes<span class="w"> </span>ago<span class="w"> </span>Exited<span class="w"> </span><span class="o">(</span><span class="m">0</span><span class="o">)</span><span class="w"> </span><span class="m">2</span><span class="w"> </span>minutes<span class="w"> </span>ago<span class="w"> </span>fervent_newton +<a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a>a6a9d46d0b2f<span class="w"> </span>alpine<span class="w"> </span><span class="s2">"echo 'hello from alp"</span><span class="w"> </span><span class="m">6</span><span class="w"> </span>minutes<span class="w"> </span>ago<span class="w"> </span>Exited<span class="w"> </span><span class="o">(</span><span class="m">0</span><span class="o">)</span><span class="w"> </span><span class="m">6</span><span class="w"> </span>minutes<span class="w"> </span>ago<span class="w"> </span>lonely_kilby +<a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a>ff0a5c3750b9<span class="w"> </span>alpine<span class="w"> </span><span class="s2">"ls -l"</span><span class="w"> </span><span class="m">8</span><span class="w"> </span>minutes<span class="w"> </span>ago<span class="w"> </span>Exited<span class="w"> </span><span class="o">(</span><span class="m">0</span><span class="o">)</span><span class="w"> </span><span class="m">8</span><span class="w"> </span>minutes<span class="w"> </span>ago<span class="w"> </span>elated_ramanujan +<a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a>c317d0a9e3d2<span class="w"> </span>hello-world<span class="w"> </span><span class="s2">"/hello"</span><span class="w"> </span><span class="m">34</span><span class="w"> </span>seconds<span class="w"> </span>ago<span class="w"> </span>Exited<span class="w"> </span><span class="o">(</span><span class="m">0</span><span class="o">)</span><span class="w"> </span><span class="m">12</span><span class="w"> </span>minutes<span class="w"> </span>ago<span class="w"> </span>stupefied_mcclintock +</code></pre></div> +<p>Vous voyez maintenant une liste de tous les conteneurs que vous avez exécutés. Remarquez que la colonne "STATUS" indique que ces conteneurs ont été arrêtés il y a quelques minutes. Vous vous demandez probablement s'il y a un moyen d'exécuter plus d'une commande dans un conteneur.</p> +<p>Essayons cela maintenant :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a>$<span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span>-it<span class="w"> </span>alpine<span class="w"> </span>/bin/sh +<a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a>/<span class="w"> </span><span class="c1"># ls</span> +<a id="__codelineno-11-3" name="__codelineno-11-3" href="#__codelineno-11-3"></a>bin<span class="w"> </span>dev<span class="w"> </span>etc<span class="w"> </span>home<span class="w"> </span>lib<span class="w"> </span>linuxrc<span class="w"> </span>media<span class="w"> </span>mnt<span class="w"> </span>proc<span class="w"> </span>root<span class="w"> </span>run<span class="w"> </span>sbin<span class="w"> </span>sys<span class="w"> </span>tmp<span class="w"> </span>usr<span class="w"> </span>var +<a id="__codelineno-11-4" name="__codelineno-11-4" href="#__codelineno-11-4"></a>/<span class="w"> </span><span class="c1"># uname -a</span> +<a id="__codelineno-11-5" name="__codelineno-11-5" href="#__codelineno-11-5"></a>Linux<span class="w"> </span>97916e8cb5dc<span class="w"> </span><span class="m">4</span>.4.27-moby<span class="w"> </span><span class="c1">#1 SMP Wed Oct 26 14:01:48 UTC 2016 x86_64 Linux</span> +</code></pre></div> +<p>L'exécution de la commande <code>run</code> avec l'option <code>-it</code> nous connecte à un terminal interactif dans le conteneur. Maintenant, vous pouvez exécuter autant de commandes que vous le souhaitez dans le conteneur. Prenez le temps d'exécuter vos commandes préférées.</p> +<div class="admonition astuce"> +<p class="admonition-title">Astuce</p> +<p><code>run -it</code> est une commande très utile pour déboguer le bas niveau d'un conteneur.</p> +</div> +<p>Vous savez faire tourner un container et le lancer en mode interactif. Mais il manque une fonctionnalité essentielle lorsque l'on veut faire tourner un service. Pour l'illustrer lancer un nginx :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a>$<span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span>registry.takima.io/school/proxy/nginx +</code></pre></div> +<div class="admonition question"> +<p class="admonition-title">Question</p> +<p>Que se passe-t-il dans ce cas ? +Que se passe-t-il si vous fermez votre shell ou ctr+c par exemple (fermez votre shell relancez le et lancez un <code>docker ps -a</code>)</p> +</div> +<p>En fait votre conteneur s'est lancé et ne vous rend pas la main et vous avez alors le log nginx sur votre shell. Lorsque vous quittez votre shell, le conteneur se stoppe et votre nginx se stoppe donc également. +Ce n'est pas un comportement souhaitable lorsque l'on veut lancer des services en tache de fond, c'est ce qu'on apelle un daemon. Docker nous permet de le faire avec une simple option <code>-d</code></p> +<p>relancez votre conteneur Nginx en mode service</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a>$<span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span>-d<span class="w"> </span>--name<span class="w"> </span>mon-nginx<span class="w"> </span>registry.takima.io/school/proxy/nginx<span class="w"> </span> +<a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a>b611eea1536bcfc79f87e1bfd57a3f10bbe4577f03e405329f5626cd66a64e54 +</code></pre></div> +<p>Maintenant lancez un <code>docker ps</code>. Vous verrez votre docker nginx tourner</p> +<p>Voilà <code>docker run</code> n'a plus de secret pour vous, et c'est parfait car c'est probablement la commande que vous utiliserez le plus fréquemment. Il est judicieux de prendre le temps de vous familiariser avec elle. Pour en savoir plus sur <code>run</code>, utilisez <code>docker run --help</code> pour voir la liste de tous les indicateurs qu'elle prend en charge. Au fur et à mesure de votre progression, vous verrez quelques variantes de <code>docker run</code>.</p> +<p>Pour la suite taguez votre image nginx :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a>$<span class="w"> </span>docker<span class="w"> </span>tag<span class="w"> </span>registry.takima.io/school/proxy/nginx<span class="w"> </span>nginx +</code></pre></div> +<p>A tout moment vous pouvez inspecter un container avec <code>docker container inspect</code>, par exemple pour récupérer son status :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="c1"># get the state of a container</span> +<a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a>docker<span class="w"> </span>container<span class="w"> </span>inspect<span class="w"> </span><span class="se">\</span> +<a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a>--format<span class="w"> </span><span class="s1">'{{.State.Status}}'</span><span class="w"> </span><span class="se">\</span> +<a id="__codelineno-15-4" name="__codelineno-15-4" href="#__codelineno-15-4"></a>mon-nginx +</code></pre></div> +<div class="admonition success"> +<p class="admonition-title">Success</p> +<p>Point cours pour voir les Network Docker</p> +</div> +<h2 id="les-network-docker">Les Network Docker</h2> +<p>Faire tourner un applicatif c'est bien mais lorsque l'on fait du web c'est essentiel de pouvoir publier son application et que cette application bénéficie d'une connectivité (IP), mais c'est aussi essentiel d'isoler les conteneurs dans des réseaux séparés. Après tout c'est aussi l'une des promesses de docker l'isolation.</p> +<h3 id="objectif">Objectif:</h3> +<ul> +<li>Comprendre les concepts de base des réseaux Docker.</li> +<li>Créer et gérer des réseaux Docker.</li> +<li>Connecter des conteneurs à des réseaux personnalisés.</li> +</ul> +<h3 id="verifier-les-reseaux-docker-existants">Vérifier les réseaux Docker existants</h3> +<p>Pour lister les reseaux actuels :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a>$<span class="w"> </span>docker<span class="w"> </span>network<span class="w"> </span>ls +<a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a>NETWORK<span class="w"> </span>ID<span class="w"> </span>NAME<span class="w"> </span>DRIVER<span class="w"> </span>SCOPE +<a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a>df8d9a03e3e1<span class="w"> </span>bridge<span class="w"> </span>bridge<span class="w"> </span><span class="nb">local</span> +<a id="__codelineno-16-4" name="__codelineno-16-4" href="#__codelineno-16-4"></a>f0b4def22265<span class="w"> </span>host<span class="w"> </span>host<span class="w"> </span><span class="nb">local</span> +<a id="__codelineno-16-5" name="__codelineno-16-5" href="#__codelineno-16-5"></a>a7cd984404b5<span class="w"> </span>none<span class="w"> </span>null<span class="w"> </span><span class="nb">local</span> +</code></pre></div> +<p>Ce sont les réseaux par défault. On peut créer autant de nouveau réseaux isolés que l'on souhaite et lorsque l'on créé un reseau il sera de type bridge par défaut</p> +<p>Lorsque l'on créer un conteneur il se trouve attaché par défaut au réseau nommé <code>bridge</code>. On déconseille donc de l'utilser pour faire tourner nos applications.</p> +<h3 id="creer-un-reseau-personnalise">Créer un réseau personnalisé</h3> +<p>Créez un réseau Docker personnalisé en utilisant la commande <code>docker network create</code>. Par exemple, créez un réseau nommé "app" :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a>docker<span class="w"> </span>network<span class="w"> </span>create<span class="w"> </span>app +</code></pre></div> +<p>Vous pouvez vérifier que le réseau a été créé en utilisant la commande <code>docker network ls</code>.</p> +<h3 id="executer-des-conteneurs-connectes-au-reseau-personnalise">Exécuter des conteneurs connectés au réseau personnalisé</h3> +<p>Créez deux conteneurs connectés au réseau "app". Par exemple, nous pouvons créer un conteneur web et un conteneur de base de données :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a>docker<span class="w"> </span>run<span class="w"> </span>-d<span class="w"> </span>--name<span class="w"> </span>webapp<span class="w"> </span>--network<span class="w"> </span>app<span class="w"> </span>nginx +</code></pre></div> +<p>Le conteneur <code>webapp</code> est maintenant connecté au réseau `app``</p> +<h3 id="verifier-la-connectivite">Vérifier la connectivité</h3> +<p>Vous pouvez maintenant vérifier si un conteneur présent sur ce réseau <code>app</code> peut communiquer avec la webapp. Exécutez un conteneur de test connecté au même réseau :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a>docker<span class="w"> </span>run<span class="w"> </span>-it<span class="w"> </span>--rm<span class="w"> </span>--network<span class="w"> </span>app<span class="w"> </span>registry.takima.io/school/proxy/busybox<span class="w"> </span>sh +</code></pre></div> +<p>À l'intérieur de ce conteneur de test, essayez de "pinguer" le conteneur webapp en utilisant leurs noms de conteneurs comme hôtes. Assurez-vous que la connectivité fonctionne avec un wget sur le port 80 par exemple .</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-20-1" name="__codelineno-20-1" href="#__codelineno-20-1"></a>$<span class="w"> </span>wget<span class="w"> </span>webapp +<a id="__codelineno-20-2" name="__codelineno-20-2" href="#__codelineno-20-2"></a>Connecting<span class="w"> </span>to<span class="w"> </span>webapp<span class="w"> </span><span class="o">(</span><span class="m">172</span>.19.0.2:80<span class="o">)</span> +<a id="__codelineno-20-3" name="__codelineno-20-3" href="#__codelineno-20-3"></a>saving<span class="w"> </span>to<span class="w"> </span><span class="s1">'index.html'</span> +<a id="__codelineno-20-4" name="__codelineno-20-4" href="#__codelineno-20-4"></a>index.html<span class="w"> </span><span class="m">100</span>%<span class="w"> </span><span class="p">|</span>**********************************************************************************************<span class="p">|</span><span class="w"> </span><span class="m">615</span><span class="w"> </span><span class="m">0</span>:00:00<span class="w"> </span>ETA +<a id="__codelineno-20-5" name="__codelineno-20-5" href="#__codelineno-20-5"></a><span class="s1">'index.html'</span><span class="w"> </span>saved +<a id="__codelineno-20-6" name="__codelineno-20-6" href="#__codelineno-20-6"></a> +<a id="__codelineno-20-7" name="__codelineno-20-7" href="#__codelineno-20-7"></a>$<span class="w"> </span>cat<span class="w"> </span>index.html +</code></pre></div> +<h3 id="supprimer-le-reseau">Supprimer le réseau</h3> +<p>Pour supprimer le réseau personnalisé que vous avez créé, utilisez la commande suivante :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-21-1" name="__codelineno-21-1" href="#__codelineno-21-1"></a>docker<span class="w"> </span>network<span class="w"> </span>rm<span class="w"> </span>app +</code></pre></div> +<div class="admonition notes"> +<p class="admonition-title">Notes</p> +<p>Si un réseau est utilisé, impossible de le détruire.</p> +</div> +<div class="admonition success"> +<p class="admonition-title">Success</p> +<p>Point cours sur la publication des conteneurs</p> +</div> +<h2 id="la-publication">La publication</h2> +<p>Nous avons isolé les conteneurs dans un réseau et c'est parfait pour la sécurité. Mais certains services sont plus utiles lorsqu'ils sont accèssibles. Nous allons donc voir comment publier un service sur le Host qui l'héberge.</p> +<h3 id="objectif_1">Objectif :</h3> +<ul> +<li>Comprendre comment publier des ports de conteneurs Docker.</li> +<li>Tester l'accessibilité des applications via les ports publiés.</li> +</ul> +<h3 id="verification-des-ports-existants">Vérification des ports existants</h3> +<p>Avant de commencer, listez les conteneurs actuellement en cours d'exécution sur votre système à l'aide de la commande suivante :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-22-1" name="__codelineno-22-1" href="#__codelineno-22-1"></a>docker<span class="w"> </span>ps +</code></pre></div> +<p>Cette commande affichera les conteneurs actifs, mais notez qu'aucun port n'est actuellement publié pour eux.</p> +<h3 id="creer-un-conteneur-avec-des-ports-publies">Créer un conteneur avec des ports publiés</h3> +<p>Créez un conteneur avec un serveur web Nginx et publiez son port 80 sur un port de votre machine hôte, par exemple, le port 8080. Utilisez la commande suivante :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-23-1" name="__codelineno-23-1" href="#__codelineno-23-1"></a>docker<span class="w"> </span>run<span class="w"> </span>-d<span class="w"> </span>-p<span class="w"> </span><span class="m">8080</span>:80<span class="w"> </span>--name<span class="w"> </span>monsite<span class="w"> </span>registry.takima.io/school/proxy/nginx +</code></pre></div> +<p>Cela démarrera un conteneur Nginx avec son port 80 publié sur le port 8080 de votre machine hôte.</p> +<h3 id="acces-a-lapplication-via-le-port-publie">Accès à l'application via le port publié</h3> +<p>Ouvrez un navigateur web et accédez à <code>http://localhost:8080</code>. Vous devriez voir la page d'accueil Nginx, ce qui signifie que le serveur web du conteneur est accessible via le port publié.</p> +<h3 id="gestion-des-ports-publies">Gestion des ports publiés</h3> +<p>Pour afficher les ports publiés par un conteneur en cours d'exécution, utilisez la commande <code>docker port</code>. Par exemple :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-24-1" name="__codelineno-24-1" href="#__codelineno-24-1"></a>docker<span class="w"> </span>port<span class="w"> </span>monsite +</code></pre></div> +<h3 id="arreter-et-supprimer-les-conteneurs">Arrêter et supprimer les conteneurs</h3> +<p>Lorsque vous avez terminé, arrêtez et supprimez les conteneurs que vous avez créés :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-25-1" name="__codelineno-25-1" href="#__codelineno-25-1"></a>docker<span class="w"> </span>stop<span class="w"> </span>monsite +<a id="__codelineno-25-2" name="__codelineno-25-2" href="#__codelineno-25-2"></a>docker<span class="w"> </span>rm<span class="w"> </span>monsite +</code></pre></div> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>rm -f est utile pour aller plus vite.</p> +</div> +<div class="admonition success"> +<p class="admonition-title">Success</p> +<p>Point cours sur les Volumes Docker</p> +</div> +<h2 id="les-volumes-docker">Les volumes Docker</h2> +<p>placez vous dans un dossier /td-volumes</p> +<h3 id="creation-dun-conteneur-avec-un-volume">Création d'un Conteneur avec un Volume</h3> +<p>Nous allons ici utiliser un bind volume : Il s'agit d'un volume monté depuis un dossier du host (celui qui lance les dockers)</p> +<ol> +<li> +<p>Créez un répertoire vide sur votre système hôte pour servir de volume. Par exemple : + <div class="highlight"><pre><span></span><code><a id="__codelineno-26-1" name="__codelineno-26-1" href="#__codelineno-26-1"></a>mkdir<span class="w"> </span>mon_volume +</code></pre></div></p> +</li> +<li> +<p>Créez un conteneur en utilisant la commande <code>docker run</code> avec un volume monté à partir du répertoire que vous venez de créer. Assurez-vous de remplacer <code>chemin_vers_votre_volume</code> par le chemin absolu du répertoire que vous avez créé à l'étape précédente : + !!! tip + Si vous êtes bien dans le dossier utilisé la variables de dossier courrant <code>$(pwd)</code> + "$(pwd)"/mon_volume:/containerDir + <div class="highlight"><pre><span></span><code><a id="__codelineno-27-1" name="__codelineno-27-1" href="#__codelineno-27-1"></a>docker<span class="w"> </span>run<span class="w"> </span>-d<span class="w"> </span>-v<span class="w"> </span><span class="s2">"</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span><span class="s2">"</span>/mon_volume:/data<span class="w"> </span>--name<span class="w"> </span>mon-conteneur<span class="w"> </span>nginx +</code></pre></div></p> +</li> +<li> +<p>Vérifiez que le conteneur est en cours d'exécution en utilisant <code>docker ps</code>.</p> +</li> +</ol> +<h4 id="utilisation-du-volume-depuis-le-conteneur">Utilisation du Volume depuis le Conteneur</h4> +<ol> +<li> +<p>Exécutez un shell interactif dans le conteneur que vous venez de créer : + <div class="highlight"><pre><span></span><code><a id="__codelineno-28-1" name="__codelineno-28-1" href="#__codelineno-28-1"></a>docker<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>-it<span class="w"> </span>mon-conteneur<span class="w"> </span>/bin/bash +</code></pre></div></p> +</li> +<li> +<p>À l'intérieur du conteneur, créez un fichier ou un répertoire dans le répertoire monté : + <div class="highlight"><pre><span></span><code><a id="__codelineno-29-1" name="__codelineno-29-1" href="#__codelineno-29-1"></a>touch<span class="w"> </span>/data/mon_fichier.txt +</code></pre></div></p> +</li> +<li> +<p>Quittez le shell du conteneur en utilisant <code>exit</code>.</p> +</li> +</ol> +<h4 id="verification-de-la-persistance-des-donnees">Vérification de la Persistance des Données</h4> +<ol> +<li> +<p>Arrêtez et supprimez le conteneur : + <div class="highlight"><pre><span></span><code><a id="__codelineno-30-1" name="__codelineno-30-1" href="#__codelineno-30-1"></a>docker<span class="w"> </span>stop<span class="w"> </span>mon-conteneur +<a id="__codelineno-30-2" name="__codelineno-30-2" href="#__codelineno-30-2"></a>docker<span class="w"> </span>rm<span class="w"> </span>mon-conteneur +</code></pre></div></p> +</li> +<li> +<p>Vérifiez que le répertoire et le fichier que vous avez créés sont toujours présents dans le répertoire du volume sur votre système hôte : + <div class="highlight"><pre><span></span><code><a id="__codelineno-31-1" name="__codelineno-31-1" href="#__codelineno-31-1"></a>ls<span class="w"> </span>mon_volume +</code></pre></div></p> +</li> +</ol> +<h4 id="creation-dun-deuxieme-conteneur-avec-le-meme-volume">Création d'un Deuxième Conteneur avec le Même Volume</h4> +<ol> +<li> +<p>Créez un deuxième conteneur en utilisant le même volume que le premier : + <div class="highlight"><pre><span></span><code><a id="__codelineno-32-1" name="__codelineno-32-1" href="#__codelineno-32-1"></a>docker<span class="w"> </span>run<span class="w"> </span>-d<span class="w"> </span>-v<span class="w"> </span><span class="s2">"</span><span class="k">$(</span><span class="nb">pwd</span><span class="k">)</span><span class="s2">"</span>/mon_volume:/data<span class="w"> </span>--name<span class="w"> </span>mon-conteneur-bis<span class="w"> </span>nginx +</code></pre></div></p> +</li> +<li> +<p>Exécutez un shell interactif dans le deuxième conteneur : + <div class="highlight"><pre><span></span><code><a id="__codelineno-33-1" name="__codelineno-33-1" href="#__codelineno-33-1"></a>docker<span class="w"> </span><span class="nb">exec</span><span class="w"> </span>-it<span class="w"> </span>mon-conteneur-bis<span class="w"> </span>/bin/bash +</code></pre></div></p> +</li> +<li> +<p>Vérifiez que le fichier ou le répertoire que vous avez créé est présent dans le deuxième conteneur, même s'il s'agit d'un conteneur différent.</p> +</li> +<li> +<p>Quittez le shell du deuxième conteneur en utilisant <code>exit</code>.</p> +</li> +</ol> +<h4 id="clean">clean</h4> +<ol> +<li> +<p>Supprimez le deuxième conteneur : + <div class="highlight"><pre><span></span><code><a id="__codelineno-34-1" name="__codelineno-34-1" href="#__codelineno-34-1"></a>docker<span class="w"> </span>stop<span class="w"> </span>mon-conteneur-bis +<a id="__codelineno-34-2" name="__codelineno-34-2" href="#__codelineno-34-2"></a>docker<span class="w"> </span>rm<span class="w"> </span>mon-conteneur-bis +</code></pre></div></p> +</li> +<li> +<p>Supprimez le répertoire du volume sur votre système hôte : + <div class="highlight"><pre><span></span><code><a id="__codelineno-35-1" name="__codelineno-35-1" href="#__codelineno-35-1"></a>rm<span class="w"> </span>-rf<span class="w"> </span>mon_volume +</code></pre></div></p> +</li> +</ol> +<div class="admonition success"> +<p class="admonition-title">Success</p> +<p>Point cours sur les Images Docker</p> +</div> +<h2 id="images-docker">Images Docker</h2> +<p>Dans cette section, plongeons plus en profondeur dans ce que sont les images Docker. Vous allez construire votre propre image, utiliser cette image pour exécuter une application localement, et enfin, pousser certaines de vos propres images vers Docker Hub.</p> +<p>Les images Docker sont la base des conteneurs. Dans l'exemple précédent, vous avez pull une image <code>nginx</code> depuis le registre et avez demandé au client Docker d'exécuter un conteneur basé sur cette image. Pour voir la liste des images disponibles localement sur votre système, exécutez la commande <code>docker images</code>.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-36-1" name="__codelineno-36-1" href="#__codelineno-36-1"></a>$<span class="w"> </span>docker<span class="w"> </span>images +<a id="__codelineno-36-2" name="__codelineno-36-2" href="#__codelineno-36-2"></a>REPOSITORY<span class="w"> </span>TAG<span class="w"> </span>IMAGE<span class="w"> </span>ID<span class="w"> </span>CREATED<span class="w"> </span>SIZE +<a id="__codelineno-36-3" name="__codelineno-36-3" href="#__codelineno-36-3"></a>dockersamples/static-site<span class="w"> </span>latest<span class="w"> </span>92a386b6e686<span class="w"> </span><span class="m">2</span><span class="w"> </span>hours<span class="w"> </span>ago<span class="w"> </span><span class="m">190</span>.5<span class="w"> </span>MB +<a id="__codelineno-36-4" name="__codelineno-36-4" href="#__codelineno-36-4"></a>nginx<span class="w"> </span>latest<span class="w"> </span>af4b3d7d5401<span class="w"> </span><span class="m">3</span><span class="w"> </span>hours<span class="w"> </span>ago<span class="w"> </span><span class="m">190</span>.5<span class="w"> </span>MB +</code></pre></div> +<p>Vous aurez une liste différente d'images sur votre machine. Le <code>TAG</code> fait référence à une version de l'image et l'<code>image ID</code> est l'identifiant unique correspondant de cette image.</p> +<p>Pour simplifier, vous pouvez considérer une image comme un référentiel Git : les images peuvent être <a href="https://docs.docker.com/engine/reference/commandline/commit/" target="_blank">commitées</a> avec des modifications et avoir plusieurs versions. Lorsque vous ne fournissez pas de numéro de version spécifique, le client par défaut est "latest" (dernière version).</p> +<p>Par exemple, vous pourriez extraire une version spécifique de l'image <code>ubuntu</code> comme suit :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-37-1" name="__codelineno-37-1" href="#__codelineno-37-1"></a>$<span class="w"> </span>docker<span class="w"> </span>pull<span class="w"> </span>registry.takima.io/school/proxy/ubuntu:12.04 +</code></pre></div> +<p>Si vous ne spécifiez pas le numéro de version de l'image, le client Docker utilisera, comme mentionné précédemment, une version nommée "latest" (dernière version).</p> +<p>Donc, par exemple, la commande <code>docker pull</code> ci-dessous extraira une image nommée <code>registry.takima.io/school/proxy/ubuntu:latest</code> :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-38-1" name="__codelineno-38-1" href="#__codelineno-38-1"></a>$<span class="w"> </span>docker<span class="w"> </span>pull<span class="w"> </span>registry.takima.io/school/proxy/ubuntu +</code></pre></div> +<p>Pour obtenir une nouvelle image Docker, vous pouvez soit l'obtenir depuis un registre (tel que le Docker Hub ou bien un registre personnel comme nous sommes en train de faire) soit la créer vous-même. Il existe des centaines de milliers d'images disponibles sur le <a href="https://hub.docker.com/" target="_blank">Docker Hub</a>. Vous pouvez également rechercher des images directement depuis la ligne de commande à l'aide de <code>docker search</code>.</p> +<p>Une distinction importante concernant les images se situe entre les <em>images de base</em> (base images) et les <em>images enfants</em> (child images).</p> +<ul> +<li> +<p>Les images de base sont des images qui n'ont pas d'images parent, généralement des images avec un système d'exploitation tel qu'Ubuntu, Alpine ou Debian.</p> +</li> +<li> +<p>Les images enfants sont des images qui se construisent sur des images de base et ajoutent des fonctionnalités supplémentaires.</p> +</li> +</ul> +<p>Un autre concept clé est l'idée d'images officielles et d'images utilisateur (user images). (Les deux peuvent être des images de base ou des images enfants.)</p> +<ul> +<li> +<p>Les images officielles sont des images vérifiées par Docker. Docker, Inc. sponsorise une équipe dédiée responsable de la révision et de la publication de tout le contenu des référentiels officiels. Cette équipe travaille en collaboration avec les mainteneurs de logiciels, des experts en sécurité et la communauté Docker élargie. Elles ne sont pas préfixées par un nom d'organisation ou d'utilisateur. Dans la liste des images ci-dessus, les images <code>python</code>, <code>node</code>, <code>alpine</code> et <code>nginx</code> sont des images officielles (de base). Pour en savoir plus à leur sujet, consultez la <a href="https://docs.docker.com/docker-hub/official_repos/" target="_blank">documentation sur les images officielles</a>.</p> +</li> +<li> +<p>Les images utilisateur sont des images créées et partagées par des utilisateurs comme vous. Elles se construisent sur des images de base et ajoutent des fonctionnalités supplémentaires. Typiquement, elles sont formatées comme <code>utilisateur/nom-de-l'image</code>. La valeur <code>utilisateur</code> dans le nom de l'image correspond à votre nom d'utilisateur ou d'organisation Docker Hub.</p> +</li> +</ul> +<h3 id="creez-votre-premiere-image">Créez votre première image</h3> +<p>Maintenant que vous avez une meilleure compréhension des images, il est temps de créer la vôtre. Notre principal objectif ici est de créer une image qui isole une petite application <a href="https://flask.palletsprojects.com" target="_blank">Flask</a>.</p> +<p>L'objectif de cet exercice est de créer une image Docker qui exécutera une application Flask.</p> +<p>Nous allons commencer par rassembler les composants pour un générateur aléatoire d'images de chats construit avec Python Flask, puis dockeriser cela en écrivant un <em>Dockerfile</em>. Enfin, nous allons construire l'image, puis l'exécuter.</p> +<h3 id="creez-une-application-python-flask-qui-affiche-des-images-de-chats-aleatoires">Créez une application Python Flask qui affiche des images de chats aléatoires.</h3> +<p>Dans le cadre de cet atelier, nous avons créé une petite application Python Flask amusante qui affiche une image de chat aléatoire au chargement - car, vous savez, qui n'aime pas les chats ?</p> +<p>Commencez par créer un répertoire appelé <code>flask-app</code> où nous allons créer les fichiers suivants :</p> +<ul> +<li>app.py</li> +<li>requirements.txt</li> +<li>templates/index.html</li> +<li>Dockerfile</li> +</ul> +<p>Assurez-vous de vous déplacer vers <code>cd flask-app</code> avant de commencer à créer les fichiers, car vous ne voulez pas ajouter tout un tas d'autres fichiers à votre image.</p> +<h4 id="apppy">app.py</h4> +<p>Créer le fichier python app.py avec ce contenu:</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-39-1" name="__codelineno-39-1" href="#__codelineno-39-1"></a><span class="kn">from</span> <span class="nn">flask</span> <span class="kn">import</span> <span class="n">Flask</span><span class="p">,</span> <span class="n">render_template</span> +<a id="__codelineno-39-2" name="__codelineno-39-2" href="#__codelineno-39-2"></a><span class="kn">import</span> <span class="nn">random</span> +<a id="__codelineno-39-3" name="__codelineno-39-3" href="#__codelineno-39-3"></a> +<a id="__codelineno-39-4" name="__codelineno-39-4" href="#__codelineno-39-4"></a><span class="n">app</span> <span class="o">=</span> <span class="n">Flask</span><span class="p">(</span><span class="vm">__name__</span><span class="p">)</span> +<a id="__codelineno-39-5" name="__codelineno-39-5" href="#__codelineno-39-5"></a> +<a id="__codelineno-39-6" name="__codelineno-39-6" href="#__codelineno-39-6"></a><span class="c1"># liste des images</span> +<a id="__codelineno-39-7" name="__codelineno-39-7" href="#__codelineno-39-7"></a><span class="n">images</span> <span class="o">=</span> <span class="p">[</span> +<a id="__codelineno-39-8" name="__codelineno-39-8" href="#__codelineno-39-8"></a><span class="s2">"https://c.tenor.com/GTcT7HODLRgAAAAM/smiling-cat-creepy-cat.gif"</span><span class="p">,</span> +<a id="__codelineno-39-9" name="__codelineno-39-9" href="#__codelineno-39-9"></a><span class="s2">"https://media0.giphy.com/media/10dU7AN7xsi1I4/giphy.webp?cid=ecf05e47gk63rd81vzlot57qmebr7drtgf6a3khmzvjsdtu7&rid=giphy.webp&ct=g"</span><span class="p">,</span> +<a id="__codelineno-39-10" name="__codelineno-39-10" href="#__codelineno-39-10"></a><span class="s2">"https://media0.giphy.com/media/S6VGjvmFRu5Qk/giphy.webp?cid=ecf05e478yofpawrhffnnvb3sgjkos96vyfo5mtqhds35as6&rid=giphy.webp&ct=g"</span><span class="p">,</span> +<a id="__codelineno-39-11" name="__codelineno-39-11" href="#__codelineno-39-11"></a><span class="s2">"https://media3.giphy.com/media/JIX9t2j0ZTN9S/200w.webp?cid=ecf05e47gk63rd81vzlot57qmebr7drtgf6a3khmzvjsdtu7&rid=200w.webp&ct=g"</span> +<a id="__codelineno-39-12" name="__codelineno-39-12" href="#__codelineno-39-12"></a> <span class="p">]</span> +<a id="__codelineno-39-13" name="__codelineno-39-13" href="#__codelineno-39-13"></a> +<a id="__codelineno-39-14" name="__codelineno-39-14" href="#__codelineno-39-14"></a><span class="nd">@app</span><span class="o">.</span><span class="n">route</span><span class="p">(</span><span class="s1">'/'</span><span class="p">)</span> +<a id="__codelineno-39-15" name="__codelineno-39-15" href="#__codelineno-39-15"></a><span class="k">def</span> <span class="nf">index</span><span class="p">():</span> +<a id="__codelineno-39-16" name="__codelineno-39-16" href="#__codelineno-39-16"></a> <span class="n">url</span> <span class="o">=</span> <span class="n">random</span><span class="o">.</span><span class="n">choice</span><span class="p">(</span><span class="n">images</span><span class="p">)</span> +<a id="__codelineno-39-17" name="__codelineno-39-17" href="#__codelineno-39-17"></a> <span class="k">return</span> <span class="n">render_template</span><span class="p">(</span><span class="s1">'index.html'</span><span class="p">,</span> <span class="n">url</span><span class="o">=</span><span class="n">url</span><span class="p">)</span> +<a id="__codelineno-39-18" name="__codelineno-39-18" href="#__codelineno-39-18"></a> +<a id="__codelineno-39-19" name="__codelineno-39-19" href="#__codelineno-39-19"></a><span class="k">if</span> <span class="vm">__name__</span> <span class="o">==</span> <span class="s2">"__main__"</span><span class="p">:</span> +<a id="__codelineno-39-20" name="__codelineno-39-20" href="#__codelineno-39-20"></a> <span class="n">app</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">host</span><span class="o">=</span><span class="s2">"0.0.0.0"</span><span class="p">)</span> +</code></pre></div> +<h4 id="requirementstxt">requirements.txt</h4> +<p>Ce fichier requirements.txt référence les modules Python necessaire pour lancer notre app Python</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-40-1" name="__codelineno-40-1" href="#__codelineno-40-1"></a>Flask==0.10.1 +</code></pre></div> +<h4 id="templatesindexhtml">templates/index.html</h4> +<p>Créez le dossier <code>templates</code> et créez dans ce dossier le fichier index.html avec le contenu suivant:</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-41-1" name="__codelineno-41-1" href="#__codelineno-41-1"></a><span class="p"><</span><span class="nt">html</span><span class="p">></span> +<a id="__codelineno-41-2" name="__codelineno-41-2" href="#__codelineno-41-2"></a> <span class="p"><</span><span class="nt">head</span><span class="p">></span> +<a id="__codelineno-41-3" name="__codelineno-41-3" href="#__codelineno-41-3"></a> <span class="p"><</span><span class="nt">style</span> <span class="na">type</span><span class="o">=</span><span class="s">"text/css"</span><span class="p">></span> +<a id="__codelineno-41-4" name="__codelineno-41-4" href="#__codelineno-41-4"></a><span class="w"> </span><span class="nt">body</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-41-5" name="__codelineno-41-5" href="#__codelineno-41-5"></a><span class="w"> </span><span class="k">background</span><span class="p">:</span><span class="w"> </span><span class="kc">black</span><span class="p">;</span> +<a id="__codelineno-41-6" name="__codelineno-41-6" href="#__codelineno-41-6"></a><span class="w"> </span><span class="k">color</span><span class="p">:</span><span class="w"> </span><span class="kc">white</span><span class="p">;</span> +<a id="__codelineno-41-7" name="__codelineno-41-7" href="#__codelineno-41-7"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-41-8" name="__codelineno-41-8" href="#__codelineno-41-8"></a><span class="w"> </span><span class="nt">div</span><span class="p">.</span><span class="nc">container</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-41-9" name="__codelineno-41-9" href="#__codelineno-41-9"></a><span class="w"> </span><span class="k">max-width</span><span class="p">:</span><span class="w"> </span><span class="mi">500</span><span class="kt">px</span><span class="p">;</span> +<a id="__codelineno-41-10" name="__codelineno-41-10" href="#__codelineno-41-10"></a><span class="w"> </span><span class="k">margin</span><span class="p">:</span><span class="w"> </span><span class="mi">100</span><span class="kt">px</span><span class="w"> </span><span class="kc">auto</span><span class="p">;</span> +<a id="__codelineno-41-11" name="__codelineno-41-11" href="#__codelineno-41-11"></a><span class="w"> </span><span class="k">border</span><span class="p">:</span><span class="w"> </span><span class="mi">20</span><span class="kt">px</span><span class="w"> </span><span class="kc">solid</span><span class="w"> </span><span class="kc">white</span><span class="p">;</span> +<a id="__codelineno-41-12" name="__codelineno-41-12" href="#__codelineno-41-12"></a><span class="w"> </span><span class="k">padding</span><span class="p">:</span><span class="w"> </span><span class="mi">10</span><span class="kt">px</span><span class="p">;</span> +<a id="__codelineno-41-13" name="__codelineno-41-13" href="#__codelineno-41-13"></a><span class="w"> </span><span class="k">text-align</span><span class="p">:</span><span class="w"> </span><span class="kc">center</span><span class="p">;</span> +<a id="__codelineno-41-14" name="__codelineno-41-14" href="#__codelineno-41-14"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-41-15" name="__codelineno-41-15" href="#__codelineno-41-15"></a><span class="w"> </span><span class="nt">h4</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-41-16" name="__codelineno-41-16" href="#__codelineno-41-16"></a><span class="w"> </span><span class="k">text-transform</span><span class="p">:</span><span class="w"> </span><span class="kc">uppercase</span><span class="p">;</span> +<a id="__codelineno-41-17" name="__codelineno-41-17" href="#__codelineno-41-17"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-41-18" name="__codelineno-41-18" href="#__codelineno-41-18"></a><span class="w"> </span><span class="p"></</span><span class="nt">style</span><span class="p">></span> +<a id="__codelineno-41-19" name="__codelineno-41-19" href="#__codelineno-41-19"></a> <span class="p"></</span><span class="nt">head</span><span class="p">></span> +<a id="__codelineno-41-20" name="__codelineno-41-20" href="#__codelineno-41-20"></a> <span class="p"><</span><span class="nt">body</span><span class="p">></span> +<a id="__codelineno-41-21" name="__codelineno-41-21" href="#__codelineno-41-21"></a> <span class="p"><</span><span class="nt">div</span> <span class="na">class</span><span class="o">=</span><span class="s">"container"</span><span class="p">></span> +<a id="__codelineno-41-22" name="__codelineno-41-22" href="#__codelineno-41-22"></a> <span class="p"><</span><span class="nt">h4</span><span class="p">></span>Cat Gif of the day<span class="p"></</span><span class="nt">h4</span><span class="p">></span> +<a id="__codelineno-41-23" name="__codelineno-41-23" href="#__codelineno-41-23"></a> <span class="p"><</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">"{{url}}"</span> <span class="p">/></span> +<a id="__codelineno-41-24" name="__codelineno-41-24" href="#__codelineno-41-24"></a> <span class="p"><</span><span class="nt">p</span><span class="p">></span> +<a id="__codelineno-41-25" name="__codelineno-41-25" href="#__codelineno-41-25"></a> <span class="p"><</span><span class="nt">small</span> +<a id="__codelineno-41-26" name="__codelineno-41-26" href="#__codelineno-41-26"></a> <span class="p">></span>Courtesy: +<a id="__codelineno-41-27" name="__codelineno-41-27" href="#__codelineno-41-27"></a> <span class="p"><</span><span class="nt">a</span> +<a id="__codelineno-41-28" name="__codelineno-41-28" href="#__codelineno-41-28"></a> <span class="na">href</span><span class="o">=</span><span class="s">"http://www.buzzfeed.com/copyranter/the-best-cat-gif-post-in-the-history-of-cat-gifs"</span> +<a id="__codelineno-41-29" name="__codelineno-41-29" href="#__codelineno-41-29"></a> <span class="p">></span>Buzzfeed<span class="p"></</span><span class="nt">a</span> +<a id="__codelineno-41-30" name="__codelineno-41-30" href="#__codelineno-41-30"></a> <span class="p">></</span><span class="nt">small</span> +<a id="__codelineno-41-31" name="__codelineno-41-31" href="#__codelineno-41-31"></a> <span class="p">></span> +<a id="__codelineno-41-32" name="__codelineno-41-32" href="#__codelineno-41-32"></a> <span class="p"></</span><span class="nt">p</span><span class="p">></span> +<a id="__codelineno-41-33" name="__codelineno-41-33" href="#__codelineno-41-33"></a> <span class="p"></</span><span class="nt">div</span><span class="p">></span> +<a id="__codelineno-41-34" name="__codelineno-41-34" href="#__codelineno-41-34"></a> <span class="p"></</span><span class="nt">body</span><span class="p">></span> +<a id="__codelineno-41-35" name="__codelineno-41-35" href="#__codelineno-41-35"></a><span class="p"></</span><span class="nt">html</span><span class="p">></span> +</code></pre></div> +<h3 id="redigez-un-dockerfile">Rédigez un Dockerfile</h3> +<p>Dernier fichier et pas des moindre le Dockerfile.</p> +<p>Nous voulons créer une image Docker avec cette application web. Comme mentionné précédemment, toutes les images utilisateur sont basées sur une image de base.</p> +<p>Étant donné que notre application est écrite en Python, nous allons créer notre propre image embarquant notre app Python basée sur <a href="https://hub.docker.com/_/alpine" target="_blank">Alpine</a>. Nous allons le faire en utilisant un Dockerfile.</p> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>L'image <code>Alpine</code> est souvent utilisé comme image de base car elle est très légère</p> +</div> +<p>Un <a href="https://docs.docker.com/engine/reference/builder/" target="_blank">Dockerfile</a> est un fichier texte qui contient une liste de commandes que le daemon Docker appelle lors de la création d'une image. Le Dockerfile contient toutes les informations dont Docker a besoin pour exécuter l'application : une image Docker de base qui servira de socle , l'emplacement de votre code de projet, ses dépendances éventuelles, et les commandes à exécuter au démarrage.</p> +<p>C'est un moyen simple d'automatiser le processus de création d'images.</p> +<ol> +<li> +<p>Créez un fichier appelé Dockerfile et ajoutez-y le contenu comme décrit ci-dessous.</p> +<p>Nous allons commencer par spécifier notre image de base en utilisant le mot clé <code>FROM</code> :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-42-1" name="__codelineno-42-1" href="#__codelineno-42-1"></a><span class="k">FROM</span><span class="w"> </span><span class="s">registry.takima.io/school/proxy/alpine:3.6</span> +</code></pre></div> +</li> +<li> +<p>La prochaine étape consiste généralement à écrire les commandes de copie des fichiers et à installer les dépendances nécessaire pour notre application. Mais d'abord, nous allons installer le package Python pip dans la distribution Linux Alpine. Cela n'installera pas seulement le package pip, mais aussi d'autres dépendances, y compris l'interpréteur Python. Ajoutez la commande <a href="https://docs.docker.com/engine/reference/builder/#run" target="_blank">RUN</a> suivante :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-43-1" name="__codelineno-43-1" href="#__codelineno-43-1"></a><span class="k">RUN</span><span class="w"> </span>apk<span class="w"> </span>add<span class="w"> </span>--update<span class="w"> </span>py2-pip +</code></pre></div> +</li> +<li> +<p>Ajoutons les fichiers qui composent l'application Flask.</p> +<p>Installez toutes les dépendances Python nécessaires pour le bon fonctionnement de notre application. Cela sera accompli en ajoutant les lignes suivantes :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-44-1" name="__codelineno-44-1" href="#__codelineno-44-1"></a><span class="k">COPY</span><span class="w"> </span>requirements.txt<span class="w"> </span>/usr/src/app/ +<a id="__codelineno-44-2" name="__codelineno-44-2" href="#__codelineno-44-2"></a><span class="k">RUN</span><span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>--no-cache-dir<span class="w"> </span>-r<span class="w"> </span>/usr/src/app/requirements.txt +</code></pre></div> +<p>Copiez les fichiers que vous avez créés précédemment dans notre image en utilisant la commande <a href="https://docs.docker.com/engine/reference/builder/#copy" target="_blank">COPY</a>.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-45-1" name="__codelineno-45-1" href="#__codelineno-45-1"></a><span class="k">COPY</span><span class="w"> </span>app.py<span class="w"> </span>/usr/src/app/ +<a id="__codelineno-45-2" name="__codelineno-45-2" href="#__codelineno-45-2"></a><span class="k">COPY</span><span class="w"> </span>templates/index.html<span class="w"> </span>/usr/src/app/templates/ +</code></pre></div> +</li> +<li> +<p>Spécifiez le numéro de port qui doit être exposé. Comme notre application Flask s'exécute sur le port <code>5000</code>, c'est ce que nous allons exposer.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-46-1" name="__codelineno-46-1" href="#__codelineno-46-1"></a><span class="k">EXPOSE</span><span class="w"> </span><span class="s">5000</span> +</code></pre></div> +</li> +<li> +<p>La dernière étape est de définir la commande pour lancer l'application : <code>python ./app.py</code>. Utilisez la commande <a href="https://docs.docker.com/engine/reference/builder/#entrypoint" target="_blank">ENTRYPOINT</a> pour cela :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-47-1" name="__codelineno-47-1" href="#__codelineno-47-1"></a><span class="k">ENTRYPOINT</span><span class="w"> </span><span class="p">[</span><span class="s2">"python"</span><span class="p">,</span><span class="w"> </span><span class="s2">"/usr/src/app/app.py"</span><span class="p">]</span> +</code></pre></div> +<p>Le but principal de <code>ENTRYPOINT</code> est d'indiquer au conteneur la commande qu'il doit exécuter par défaut lors de son démarrage.</p> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>En vérité ENTRYPOINT servira à définir l'executable et le CMD servira à définir les paramètres de l'executable</p> +</div> +</li> +<li> +<p>Vérifiez votre Dockerfile.</p> +<p>Notre Dockerfile est maintenant prêt. Voici à quoi il ressemble :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-48-1" name="__codelineno-48-1" href="#__codelineno-48-1"></a><span class="c"># notre image de base</span> +<a id="__codelineno-48-2" name="__codelineno-48-2" href="#__codelineno-48-2"></a><span class="k">FROM</span><span class="w"> </span><span class="s">registry.takima.io/school/proxy/alpine:3.6</span> +<a id="__codelineno-48-3" name="__codelineno-48-3" href="#__codelineno-48-3"></a> +<a id="__codelineno-48-4" name="__codelineno-48-4" href="#__codelineno-48-4"></a><span class="c"># Installer Python et pip</span> +<a id="__codelineno-48-5" name="__codelineno-48-5" href="#__codelineno-48-5"></a><span class="k">RUN</span><span class="w"> </span>apk<span class="w"> </span>add<span class="w"> </span>--update<span class="w"> </span>py2-pip +<a id="__codelineno-48-6" name="__codelineno-48-6" href="#__codelineno-48-6"></a> +<a id="__codelineno-48-7" name="__codelineno-48-7" href="#__codelineno-48-7"></a><span class="c"># Installer les modules Python nécessaires par l'application Python</span> +<a id="__codelineno-48-8" name="__codelineno-48-8" href="#__codelineno-48-8"></a><span class="k">COPY</span><span class="w"> </span>requirements.txt<span class="w"> </span>/usr/src/app/ +<a id="__codelineno-48-9" name="__codelineno-48-9" href="#__codelineno-48-9"></a><span class="k">RUN</span><span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>--no-cache-dir<span class="w"> </span>-r<span class="w"> </span>/usr/src/app/requirements.txt +<a id="__codelineno-48-10" name="__codelineno-48-10" href="#__codelineno-48-10"></a> +<a id="__codelineno-48-11" name="__codelineno-48-11" href="#__codelineno-48-11"></a><span class="c"># Copier les fichiers nécessaires pour l'exécution de l'application</span> +<a id="__codelineno-48-12" name="__codelineno-48-12" href="#__codelineno-48-12"></a><span class="k">COPY</span><span class="w"> </span>app.py<span class="w"> </span>/usr/src/app/ +<a id="__codelineno-48-13" name="__codelineno-48-13" href="#__codelineno-48-13"></a><span class="k">COPY</span><span class="w"> </span>templates/index.html<span class="w"> </span>/usr/src/app/templates/ +<a id="__codelineno-48-14" name="__codelineno-48-14" href="#__codelineno-48-14"></a> +<a id="__codelineno-48-15" name="__codelineno-48-15" href="#__codelineno-48-15"></a><span class="c"># Indiquer le numéro de port que le conteneur doit exposer</span> +<a id="__codelineno-48-16" name="__codelineno-48-16" href="#__codelineno-48-16"></a><span class="k">EXPOSE</span><span class="w"> </span><span class="s">5000</span> +<a id="__codelineno-48-17" name="__codelineno-48-17" href="#__codelineno-48-17"></a> +<a id="__codelineno-48-18" name="__codelineno-48-18" href="#__codelineno-48-18"></a><span class="c"># Exécuter l'application</span> +<a id="__codelineno-48-19" name="__codelineno-48-19" href="#__codelineno-48-19"></a><span class="k">ENTRYPOINT</span><span class="w"> </span><span class="p">[</span><span class="s2">"python"</span><span class="p">,</span><span class="w"> </span><span class="s2">"/usr/src/app/app.py"</span><span class="p">]</span> +</code></pre></div> +</li> +</ol> +<h3 id="builder-notre-image">Builder notre image</h3> +<p>Maintenant que notre <code>Dockerfile</code>est prêt, il est temps de builder notre application. +La commande <code>docker build</code> s'occupe du travail de créer une image Docker à partir du Dockerfile.</p> +<p>Lorsque vous exécutez la commande <code>docker build</code>, assurez-vous de remplacer <code><YOUR_USERNAME></code> par votre nom d'utilisateur. Ce nom d'utilisateur devrait être le même que celui que vous avez créé lors de votre inscription sur <a href="https://cloud.docker.com/" target="_blank">Docker Hub</a>. Si vous ne l'avez pas encore fait mettez le nom que vous souhaitez pour votre tag.</p> +<p>La commande <code>docker build</code> est assez simple. Elle prend un nom de balise ou tag facultatif avec l'option <code>-t</code> et l'emplacement du répertoire contenant le Dockerfile. Le point <code>.</code> indique le répertoire actuel :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-49-1" name="__codelineno-49-1" href="#__codelineno-49-1"></a>$<span class="w"> </span>docker<span class="w"> </span>build<span class="w"> </span>-t<span class="w"> </span><YOUR_USERNAME>/myfirstapp<span class="w"> </span>. +<a id="__codelineno-49-2" name="__codelineno-49-2" href="#__codelineno-49-2"></a>Sending<span class="w"> </span>build<span class="w"> </span>context<span class="w"> </span>to<span class="w"> </span>Docker<span class="w"> </span>daemon<span class="w"> </span><span class="m">9</span>.728<span class="w"> </span>kB +<a id="__codelineno-49-3" name="__codelineno-49-3" href="#__codelineno-49-3"></a>Step<span class="w"> </span><span class="m">1</span><span class="w"> </span>:<span class="w"> </span>FROM<span class="w"> </span>alpine:latest +<a id="__codelineno-49-4" name="__codelineno-49-4" href="#__codelineno-49-4"></a><span class="w"> </span>---><span class="w"> </span>0d81fc72e790 +<a id="__codelineno-49-5" name="__codelineno-49-5" href="#__codelineno-49-5"></a>Step<span class="w"> </span><span class="m">2</span><span class="w"> </span>:<span class="w"> </span>RUN<span class="w"> </span>apk<span class="w"> </span>add<span class="w"> </span>--update<span class="w"> </span>py-pip +<a id="__codelineno-49-6" name="__codelineno-49-6" href="#__codelineno-49-6"></a><span class="w"> </span>---><span class="w"> </span>Running<span class="w"> </span><span class="k">in</span><span class="w"> </span>8abd4091b5f5 +<a id="__codelineno-49-7" name="__codelineno-49-7" href="#__codelineno-49-7"></a>fetch<span class="w"> </span>http://dl-4.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz +<a id="__codelineno-49-8" name="__codelineno-49-8" href="#__codelineno-49-8"></a>fetch<span class="w"> </span>http://dl-4.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz +<a id="__codelineno-49-9" name="__codelineno-49-9" href="#__codelineno-49-9"></a><span class="o">(</span><span class="m">1</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>libbz2<span class="w"> </span><span class="o">(</span><span class="m">1</span>.0.6-r4<span class="o">)</span> +<a id="__codelineno-49-10" name="__codelineno-49-10" href="#__codelineno-49-10"></a><span class="o">(</span><span class="m">2</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>expat<span class="w"> </span><span class="o">(</span><span class="m">2</span>.1.0-r2<span class="o">)</span> +<a id="__codelineno-49-11" name="__codelineno-49-11" href="#__codelineno-49-11"></a><span class="o">(</span><span class="m">3</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>libffi<span class="w"> </span><span class="o">(</span><span class="m">3</span>.2.1-r2<span class="o">)</span> +<a id="__codelineno-49-12" name="__codelineno-49-12" href="#__codelineno-49-12"></a><span class="o">(</span><span class="m">4</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>gdbm<span class="w"> </span><span class="o">(</span><span class="m">1</span>.11-r1<span class="o">)</span> +<a id="__codelineno-49-13" name="__codelineno-49-13" href="#__codelineno-49-13"></a><span class="o">(</span><span class="m">5</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>ncurses-terminfo-base<span class="w"> </span><span class="o">(</span><span class="m">6</span>.0-r6<span class="o">)</span> +<a id="__codelineno-49-14" name="__codelineno-49-14" href="#__codelineno-49-14"></a><span class="o">(</span><span class="m">6</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>ncurses-terminfo<span class="w"> </span><span class="o">(</span><span class="m">6</span>.0-r6<span class="o">)</span> +<a id="__codelineno-49-15" name="__codelineno-49-15" href="#__codelineno-49-15"></a><span class="o">(</span><span class="m">7</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>ncurses-libs<span class="w"> </span><span class="o">(</span><span class="m">6</span>.0-r6<span class="o">)</span> +<a id="__codelineno-49-16" name="__codelineno-49-16" href="#__codelineno-49-16"></a><span class="o">(</span><span class="m">8</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>readline<span class="w"> </span><span class="o">(</span><span class="m">6</span>.3.008-r4<span class="o">)</span> +<a id="__codelineno-49-17" name="__codelineno-49-17" href="#__codelineno-49-17"></a><span class="o">(</span><span class="m">9</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>sqlite-libs<span class="w"> </span><span class="o">(</span><span class="m">3</span>.9.2-r0<span class="o">)</span> +<a id="__codelineno-49-18" name="__codelineno-49-18" href="#__codelineno-49-18"></a><span class="o">(</span><span class="m">10</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>python<span class="w"> </span><span class="o">(</span><span class="m">2</span>.7.11-r3<span class="o">)</span> +<a id="__codelineno-49-19" name="__codelineno-49-19" href="#__codelineno-49-19"></a><span class="o">(</span><span class="m">11</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>py-setuptools<span class="w"> </span><span class="o">(</span><span class="m">18</span>.8-r0<span class="o">)</span> +<a id="__codelineno-49-20" name="__codelineno-49-20" href="#__codelineno-49-20"></a><span class="o">(</span><span class="m">12</span>/12<span class="o">)</span><span class="w"> </span>Installing<span class="w"> </span>py-pip<span class="w"> </span><span class="o">(</span><span class="m">7</span>.1.2-r0<span class="o">)</span> +<a id="__codelineno-49-21" name="__codelineno-49-21" href="#__codelineno-49-21"></a>Executing<span class="w"> </span>busybox-1.24.1-r7.trigger +<a id="__codelineno-49-22" name="__codelineno-49-22" href="#__codelineno-49-22"></a>OK:<span class="w"> </span><span class="m">59</span><span class="w"> </span>MiB<span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="m">23</span><span class="w"> </span>packages +<a id="__codelineno-49-23" name="__codelineno-49-23" href="#__codelineno-49-23"></a><span class="w"> </span>---><span class="w"> </span>976a232ac4ad +<a id="__codelineno-49-24" name="__codelineno-49-24" href="#__codelineno-49-24"></a>Removing<span class="w"> </span>intermediate<span class="w"> </span>container<span class="w"> </span>8abd4091b5f5 +<a id="__codelineno-49-25" name="__codelineno-49-25" href="#__codelineno-49-25"></a>Step<span class="w"> </span><span class="m">3</span><span class="w"> </span>:<span class="w"> </span>COPY<span class="w"> </span>requirements.txt<span class="w"> </span>/usr/src/app/ +<a id="__codelineno-49-26" name="__codelineno-49-26" href="#__codelineno-49-26"></a><span class="w"> </span>---><span class="w"> </span>65b4be05340c +<a id="__codelineno-49-27" name="__codelineno-49-27" href="#__codelineno-49-27"></a>Removing<span class="w"> </span>intermediate<span class="w"> </span>container<span class="w"> </span>29ef53b58e0f +<a id="__codelineno-49-28" name="__codelineno-49-28" href="#__codelineno-49-28"></a>Step<span class="w"> </span><span class="m">4</span><span class="w"> </span>:<span class="w"> </span>RUN<span class="w"> </span>pip<span class="w"> </span>install<span class="w"> </span>--no-cache-dir<span class="w"> </span>-r<span class="w"> </span>/usr/src/app/requirements.txt +<a id="__codelineno-49-29" name="__codelineno-49-29" href="#__codelineno-49-29"></a><span class="w"> </span>---><span class="w"> </span>Running<span class="w"> </span><span class="k">in</span><span class="w"> </span>a1f26ded28e7 +<a id="__codelineno-49-30" name="__codelineno-49-30" href="#__codelineno-49-30"></a>Collecting<span class="w"> </span><span class="nv">Flask</span><span class="o">==</span><span class="m">0</span>.10.1<span class="w"> </span><span class="o">(</span>from<span class="w"> </span>-r<span class="w"> </span>/usr/src/app/requirements.txt<span class="w"> </span><span class="o">(</span>line<span class="w"> </span><span class="m">1</span><span class="o">))</span> +<a id="__codelineno-49-31" name="__codelineno-49-31" href="#__codelineno-49-31"></a><span class="w"> </span>Downloading<span class="w"> </span>Flask-0.10.1.tar.gz<span class="w"> </span><span class="o">(</span>544kB<span class="o">)</span> +<a id="__codelineno-49-32" name="__codelineno-49-32" href="#__codelineno-49-32"></a>Collecting<span class="w"> </span>Werkzeug><span class="o">=</span><span class="m">0</span>.7<span class="w"> </span><span class="o">(</span>from<span class="w"> </span><span class="nv">Flask</span><span class="o">==</span><span class="m">0</span>.10.1->-r<span class="w"> </span>/usr/src/app/requirements.txt<span class="w"> </span><span class="o">(</span>line<span class="w"> </span><span class="m">1</span><span class="o">))</span> +<a id="__codelineno-49-33" name="__codelineno-49-33" href="#__codelineno-49-33"></a><span class="w"> </span>Downloading<span class="w"> </span>Werkzeug-0.11.4-py2.py3-none-any.whl<span class="w"> </span><span class="o">(</span>305kB<span class="o">)</span> +<a id="__codelineno-49-34" name="__codelineno-49-34" href="#__codelineno-49-34"></a>Collecting<span class="w"> </span>Jinja2><span class="o">=</span><span class="m">2</span>.4<span class="w"> </span><span class="o">(</span>from<span class="w"> </span><span class="nv">Flask</span><span class="o">==</span><span class="m">0</span>.10.1->-r<span class="w"> </span>/usr/src/app/requirements.txt<span class="w"> </span><span class="o">(</span>line<span class="w"> </span><span class="m">1</span><span class="o">))</span> +<a id="__codelineno-49-35" name="__codelineno-49-35" href="#__codelineno-49-35"></a><span class="w"> </span>Downloading<span class="w"> </span>Jinja2-2.8-py2.py3-none-any.whl<span class="w"> </span><span class="o">(</span>263kB<span class="o">)</span> +<a id="__codelineno-49-36" name="__codelineno-49-36" href="#__codelineno-49-36"></a>Collecting<span class="w"> </span>itsdangerous><span class="o">=</span><span class="m">0</span>.21<span class="w"> </span><span class="o">(</span>from<span class="w"> </span><span class="nv">Flask</span><span class="o">==</span><span class="m">0</span>.10.1->-r<span class="w"> </span>/usr/src/app/requirements.txt<span class="w"> </span><span class="o">(</span>line<span class="w"> </span><span class="m">1</span><span class="o">))</span> +<a id="__codelineno-49-37" name="__codelineno-49-37" href="#__codelineno-49-37"></a><span class="w"> </span>Downloading<span class="w"> </span>itsdangerous-0.24.tar.gz<span class="w"> </span><span class="o">(</span>46kB<span class="o">)</span> +<a id="__codelineno-49-38" name="__codelineno-49-38" href="#__codelineno-49-38"></a>Collecting<span class="w"> </span>MarkupSafe<span class="w"> </span><span class="o">(</span>from<span class="w"> </span>Jinja2><span class="o">=</span><span class="m">2</span>.4->Flask<span class="o">==</span><span class="m">0</span>.10.1->-r<span class="w"> </span>/usr/src/app/requirements.txt<span class="w"> </span><span class="o">(</span>line<span class="w"> </span><span class="m">1</span><span class="o">))</span> +<a id="__codelineno-49-39" name="__codelineno-49-39" href="#__codelineno-49-39"></a><span class="w"> </span>Downloading<span class="w"> </span>MarkupSafe-0.23.tar.gz +<a id="__codelineno-49-40" name="__codelineno-49-40" href="#__codelineno-49-40"></a>Installing<span class="w"> </span>collected<span class="w"> </span>packages:<span class="w"> </span>Werkzeug,<span class="w"> </span>MarkupSafe,<span class="w"> </span>Jinja2,<span class="w"> </span>itsdangerous,<span class="w"> </span>Flask +<a id="__codelineno-49-41" name="__codelineno-49-41" href="#__codelineno-49-41"></a><span class="w"> </span>Running<span class="w"> </span>setup.py<span class="w"> </span>install<span class="w"> </span><span class="k">for</span><span class="w"> </span>MarkupSafe +<a id="__codelineno-49-42" name="__codelineno-49-42" href="#__codelineno-49-42"></a><span class="w"> </span>Running<span class="w"> </span>setup.py<span class="w"> </span>install<span class="w"> </span><span class="k">for</span><span class="w"> </span>itsdangerous +<a id="__codelineno-49-43" name="__codelineno-49-43" href="#__codelineno-49-43"></a><span class="w"> </span>Running<span class="w"> </span>setup.py<span class="w"> </span>install<span class="w"> </span><span class="k">for</span><span class="w"> </span>Flask +<a id="__codelineno-49-44" name="__codelineno-49-44" href="#__codelineno-49-44"></a>Successfully<span class="w"> </span>installed<span class="w"> </span>Flask-0.10.1<span class="w"> </span>Jinja2-2.8<span class="w"> </span>MarkupSafe-0.23<span class="w"> </span>Werkzeug-0.11.4<span class="w"> </span>itsdangerous-0.24 +<a id="__codelineno-49-45" name="__codelineno-49-45" href="#__codelineno-49-45"></a>You<span class="w"> </span>are<span class="w"> </span>using<span class="w"> </span>pip<span class="w"> </span>version<span class="w"> </span><span class="m">7</span>.1.2,<span class="w"> </span>however<span class="w"> </span>version<span class="w"> </span><span class="m">8</span>.1.1<span class="w"> </span>is<span class="w"> </span>available. +<a id="__codelineno-49-46" name="__codelineno-49-46" href="#__codelineno-49-46"></a>You<span class="w"> </span>should<span class="w"> </span>consider<span class="w"> </span>upgrading<span class="w"> </span>via<span class="w"> </span>the<span class="w"> </span><span class="s1">'pip install --upgrade pip'</span><span class="w"> </span>command. +<a id="__codelineno-49-47" name="__codelineno-49-47" href="#__codelineno-49-47"></a><span class="w"> </span>---><span class="w"> </span>8de73b0730c2 +<a id="__codelineno-49-48" name="__codelineno-49-48" href="#__codelineno-49-48"></a>Removing<span class="w"> </span>intermediate<span class="w"> </span>container<span class="w"> </span>a1f26ded28e7 +<a id="__codelineno-49-49" name="__codelineno-49-49" href="#__codelineno-49-49"></a>Step<span class="w"> </span><span class="m">5</span><span class="w"> </span>:<span class="w"> </span>COPY<span class="w"> </span>app.py<span class="w"> </span>/usr/src/app/ +<a id="__codelineno-49-50" name="__codelineno-49-50" href="#__codelineno-49-50"></a><span class="w"> </span>---><span class="w"> </span>6a3436fca83e +<a id="__codelineno-49-51" name="__codelineno-49-51" href="#__codelineno-49-51"></a>Removing<span class="w"> </span>intermediate<span class="w"> </span>container<span class="w"> </span>d51b81a8b698 +<a id="__codelineno-49-52" name="__codelineno-49-52" href="#__codelineno-49-52"></a>Step<span class="w"> </span><span class="m">6</span><span class="w"> </span>:<span class="w"> </span>COPY<span class="w"> </span>templates/index.html<span class="w"> </span>/usr/src/app/templates/ +<a id="__codelineno-49-53" name="__codelineno-49-53" href="#__codelineno-49-53"></a><span class="w"> </span>---><span class="w"> </span>8098386bee99 +<a id="__codelineno-49-54" name="__codelineno-49-54" href="#__codelineno-49-54"></a>Removing<span class="w"> </span>intermediate<span class="w"> </span>container<span class="w"> </span>b783d7646f83 +<a id="__codelineno-49-55" name="__codelineno-49-55" href="#__codelineno-49-55"></a>Step<span class="w"> </span><span class="m">7</span><span class="w"> </span>:<span class="w"> </span>EXPOSE<span class="w"> </span><span class="m">5000</span> +<a id="__codelineno-49-56" name="__codelineno-49-56" href="#__codelineno-49-56"></a><span class="w"> </span>---><span class="w"> </span>Running<span class="w"> </span><span class="k">in</span><span class="w"> </span>31401b7dea40 +<a id="__codelineno-49-57" name="__codelineno-49-57" href="#__codelineno-49-57"></a><span class="w"> </span>---><span class="w"> </span>5e9988d87da7 +<a id="__codelineno-49-58" name="__codelineno-49-58" href="#__codelineno-49-58"></a>Removing<span class="w"> </span>intermediate<span class="w"> </span>container<span class="w"> </span>31401b7dea40 +<a id="__codelineno-49-59" name="__codelineno-49-59" href="#__codelineno-49-59"></a>Step<span class="w"> </span><span class="m">8</span><span class="w"> </span>:<span class="w"> </span>CMD<span class="w"> </span>python<span class="w"> </span>/usr/src/app/app.py +<a id="__codelineno-49-60" name="__codelineno-49-60" href="#__codelineno-49-60"></a><span class="w"> </span>---><span class="w"> </span>Running<span class="w"> </span><span class="k">in</span><span class="w"> </span>78e324d26576 +<a id="__codelineno-49-61" name="__codelineno-49-61" href="#__codelineno-49-61"></a><span class="w"> </span>---><span class="w"> </span>2f7357a0805d +<a id="__codelineno-49-62" name="__codelineno-49-62" href="#__codelineno-49-62"></a>Removing<span class="w"> </span>intermediate<span class="w"> </span>container<span class="w"> </span>78e324d26576 +<a id="__codelineno-49-63" name="__codelineno-49-63" href="#__codelineno-49-63"></a>Successfully<span class="w"> </span>built<span class="w"> </span>2f7357a0805d +</code></pre></div> +<p>Si vous n'avez pas l'image <code>registry.takima.io/school/proxy/alpine:3.6</code>, le docker commencera par la télécharger, puis créera votre image. Par conséquent, la sortie de la commande sera sensiblement différente. Si tout s'est bien passé, votre image devrait être prête ! Exécutez <code>docker images ls</code> et vérifiez si votre image (<code><YOUR_USERNAME>/myfirstapp</code>) apparaît.</p> +<h3 id="executez-votre-image">Exécutez votre image</h3> +<p>La prochaine étape de cette section consiste à exécuter l'image pour voir si elle fonctionne réellement.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-50-1" name="__codelineno-50-1" href="#__codelineno-50-1"></a>$<span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span>-p<span class="w"> </span><span class="m">8888</span>:5000<span class="w"> </span>--name<span class="w"> </span>myfirstapp<span class="w"> </span>YOUR_USERNAME/myfirstapp +<a id="__codelineno-50-2" name="__codelineno-50-2" href="#__codelineno-50-2"></a><span class="w"> </span>*<span class="w"> </span>Running<span class="w"> </span>on<span class="w"> </span>http://0.0.0.0:5000/<span class="w"> </span><span class="o">(</span>Appuyez<span class="w"> </span>sur<span class="w"> </span>CTRL+C<span class="w"> </span>pour<span class="w"> </span>quitter<span class="o">)</span> +</code></pre></div> +<p>Accédez à <a href="http://localhost:8888" target="_blank">http://localhost:8888</a>, et votre application devrait être en ligne.</p> +<p><img alt="gif_cat" src="../assets/Gif-cat.png" /></p> +<p>Appuyez sur le bouton "Actualiser" dans le navigateur Web pour voir quelques images de chat supplémentaires.</p> +<h2 id="docker-compose">Docker Compose</h2> +<h3 id="pourquoi-faire">Pourquoi faire ?</h3> +<p>Maintenant que vous savez builder une image, lancer un conteneur, créer des volumes et des networks, vous allez vite vous apercevoir que cela peut être fastidieux de lancer toutes ces commandes une part une pour tout reconstruire ou bien tout supprimmer. Alors bien sur vous pourriez créer de multiple script bash par exemple pour lancer tout cela, puis un autre pour tous supprimer. Mais cela s'avère difficilement maintenanble et non scalable. Et en plus c'est une approche impérative. Heureusement Docker nous aide à éviter cela avec un outil : Docker Compose. Et en plus, c'est cool, car c'est du déclaratif. Et ça, lorsque l'on veut déployer des infras, on aime beaucoup.</p> +<h3 id="utilisation">Utilisation</h3> +<p>Pour utiliser docker compose il vous faudra décrire votre stack à déployer (ensemble des objets docker : conteneurs, volume, network et bien d'autre).</p> +<ol> +<li> +<p>placer vous dans un nouveau dossier et créez un fichier compose.yaml :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-51-1" name="__codelineno-51-1" href="#__codelineno-51-1"></a><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s">'3'</span> +<a id="__codelineno-51-2" name="__codelineno-51-2" href="#__codelineno-51-2"></a><span class="nt">services</span><span class="p">:</span> +<a id="__codelineno-51-3" name="__codelineno-51-3" href="#__codelineno-51-3"></a><span class="w"> </span><span class="nt">webapp</span><span class="p">:</span> +<a id="__codelineno-51-4" name="__codelineno-51-4" href="#__codelineno-51-4"></a><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">registry.takima.io/school/proxy/nginx</span> +<a id="__codelineno-51-5" name="__codelineno-51-5" href="#__codelineno-51-5"></a><span class="w"> </span><span class="nt">ports</span><span class="p">:</span> +<a id="__codelineno-51-6" name="__codelineno-51-6" href="#__codelineno-51-6"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="s">"8082:80"</span> +<a id="__codelineno-51-7" name="__codelineno-51-7" href="#__codelineno-51-7"></a><span class="w"> </span><span class="nt">networks</span><span class="p">:</span> +<a id="__codelineno-51-8" name="__codelineno-51-8" href="#__codelineno-51-8"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">my-network</span> +<a id="__codelineno-51-9" name="__codelineno-51-9" href="#__codelineno-51-9"></a><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span> +<a id="__codelineno-51-10" name="__codelineno-51-10" href="#__codelineno-51-10"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">my-volume:/app/data</span> +<a id="__codelineno-51-11" name="__codelineno-51-11" href="#__codelineno-51-11"></a><span class="w"> </span><span class="nt">environment</span><span class="p">:</span> +<a id="__codelineno-51-12" name="__codelineno-51-12" href="#__codelineno-51-12"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">DATABASE_URL=mysql://dbuser:dbpassword@db/dbname</span> +<a id="__codelineno-51-13" name="__codelineno-51-13" href="#__codelineno-51-13"></a><span class="w"> </span><span class="nt">db</span><span class="p">:</span> +<a id="__codelineno-51-14" name="__codelineno-51-14" href="#__codelineno-51-14"></a><span class="w"> </span><span class="nt">image</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">mysql:5.7</span> +<a id="__codelineno-51-15" name="__codelineno-51-15" href="#__codelineno-51-15"></a><span class="w"> </span><span class="nt">networks</span><span class="p">:</span> +<a id="__codelineno-51-16" name="__codelineno-51-16" href="#__codelineno-51-16"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">my-network</span> +<a id="__codelineno-51-17" name="__codelineno-51-17" href="#__codelineno-51-17"></a><span class="w"> </span><span class="nt">environment</span><span class="p">:</span> +<a id="__codelineno-51-18" name="__codelineno-51-18" href="#__codelineno-51-18"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">MYSQL_ROOT_PASSWORD=rootpassword</span> +<a id="__codelineno-51-19" name="__codelineno-51-19" href="#__codelineno-51-19"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">MYSQL_DATABASE=dbname</span> +<a id="__codelineno-51-20" name="__codelineno-51-20" href="#__codelineno-51-20"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">MYSQL_USER=dbuser</span> +<a id="__codelineno-51-21" name="__codelineno-51-21" href="#__codelineno-51-21"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">MYSQL_PASSWORD=dbpassword</span> +<a id="__codelineno-51-22" name="__codelineno-51-22" href="#__codelineno-51-22"></a><span class="w"> </span><span class="nt">volumes</span><span class="p">:</span> +<a id="__codelineno-51-23" name="__codelineno-51-23" href="#__codelineno-51-23"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">db-data:/var/lib/mysql</span> +<a id="__codelineno-51-24" name="__codelineno-51-24" href="#__codelineno-51-24"></a> +<a id="__codelineno-51-25" name="__codelineno-51-25" href="#__codelineno-51-25"></a><span class="nt">networks</span><span class="p">:</span> +<a id="__codelineno-51-26" name="__codelineno-51-26" href="#__codelineno-51-26"></a><span class="w"> </span><span class="nt">my-network</span><span class="p">:</span> +<a id="__codelineno-51-27" name="__codelineno-51-27" href="#__codelineno-51-27"></a><span class="w"> </span><span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">bridge</span> +<a id="__codelineno-51-28" name="__codelineno-51-28" href="#__codelineno-51-28"></a> +<a id="__codelineno-51-29" name="__codelineno-51-29" href="#__codelineno-51-29"></a><span class="nt">volumes</span><span class="p">:</span> +<a id="__codelineno-51-30" name="__codelineno-51-30" href="#__codelineno-51-30"></a><span class="w"> </span><span class="nt">my-volume</span><span class="p">:</span> +<a id="__codelineno-51-31" name="__codelineno-51-31" href="#__codelineno-51-31"></a><span class="w"> </span><span class="nt">db-data</span><span class="p">:</span> +</code></pre></div> +</li> +<li> +<p>Lancez la stack</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-52-1" name="__codelineno-52-1" href="#__codelineno-52-1"></a>$<span class="w"> </span>docker<span class="w"> </span>compose<span class="w"> </span>up<span class="w"> </span>-d +</code></pre></div> +</li> +<li> +<p>Verifiez l'état des objets</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-53-1" name="__codelineno-53-1" href="#__codelineno-53-1"></a>$<span class="w"> </span>docker<span class="w"> </span>compose<span class="w"> </span>ps +</code></pre></div> +<div class="highlight"><pre><span></span><code><a id="__codelineno-54-1" name="__codelineno-54-1" href="#__codelineno-54-1"></a>$<span class="w"> </span>docker<span class="w"> </span>ps +<a id="__codelineno-54-2" name="__codelineno-54-2" href="#__codelineno-54-2"></a>$<span class="w"> </span>docker<span class="w"> </span>volume<span class="w"> </span>ls +<a id="__codelineno-54-3" name="__codelineno-54-3" href="#__codelineno-54-3"></a>$<span class="w"> </span>docker<span class="w"> </span>network<span class="w"> </span>ls +</code></pre></div> +<div class="admonition question"> +<p class="admonition-title">Question</p> +<p>Que constatez vous au niveau du nom des objets Docker</p> +</div> +</li> +<li> +<p>stopez la stack compose</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-55-1" name="__codelineno-55-1" href="#__codelineno-55-1"></a>$<span class="w"> </span>docker<span class="w"> </span>compose<span class="w"> </span>stop +</code></pre></div> +</li> +<li> +<p>supprimmez la stack compose</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-56-1" name="__codelineno-56-1" href="#__codelineno-56-1"></a>$<span class="w"> </span>docker<span class="w"> </span>compose<span class="w"> </span>down<span class="w"> </span>--volumes +</code></pre></div> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>L'option <code>--volumes</code> permet le purger les volumes. A utiliser avec parcimonie pour ne pas perdre de données !</p> +</div> +</li> +</ol> +<p>Maintenant que vous savez comment exécuter des conteneurs Docker et créer des Dockerfiles, ou des stack entière avec compose, passons à la partie pratique.</p> +<p align="center">© Takima 2023</p> +<p>"</p> + + + </article> + </div> + </div> + + </main> + + <footer class="md-footer"> + + <nav class="md-footer__inner md-grid" aria-label="Footer"> + + + <a href="../ch1-getting-start/" class="md-footer__link md-footer__link--prev" aria-label="Previous: Getting Start - Docker" rel="prev"> + <div class="md-footer__button md-icon"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg> + </div> + <div class="md-footer__title"> + <div class="md-ellipsis"> + <span class="md-footer__direction"> + Previous + </span> + Getting Start - Docker + </div> + </div> + </a> + + + + <a href="../ch1-discover-docker-tp/" class="md-footer__link md-footer__link--next" aria-label="Next: TP - Docker" rel="next"> + <div class="md-footer__title"> + <div class="md-ellipsis"> + <span class="md-footer__direction"> + Next + </span> + TP - Docker + </div> + </div> + <div class="md-footer__button md-icon"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11H4Z"/></svg> + </div> + </a> + + </nav> + + <div class="md-footer-meta md-typeset"> + <div class="md-footer-meta__inner md-grid"> + <div class="md-copyright"> + + + Made with + <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener"> + Material for MkDocs + </a> + +</div> + + </div> + </div> +</footer> + + </div> + <div class="md-dialog" data-md-component="dialog"> + <div class="md-dialog__inner md-typeset"></div> + </div> + <script id="__config" type="application/json">{"base": "..", "features": ["navigation.tabs", "navigation.instant"], "search": "../assets/javascripts/workers/search.2a1c317c.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.config.lang": "en", "search.config.pipeline": "trimmer, stopWordFilter", "search.config.separator": "[\\s\\-]+", "search.placeholder": "Search", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version.title": "Select version"}}</script> + + + <script src="../assets/javascripts/bundle.a6c66575.min.js"></script> + + + </body> +</html> \ No newline at end of file diff --git a/public/ch1-discover-docker-tp/index.html b/public/ch1-discover-docker-tp/index.html new file mode 100644 index 0000000..7ba5962 --- /dev/null +++ b/public/ch1-discover-docker-tp/index.html @@ -0,0 +1,1260 @@ + +<!doctype html> +<html lang="en" class="no-js"> + <head> + + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + + + + <link rel="icon" href="../assets/images/favicon.png"> + <meta name="generator" content="mkdocs-1.3.0, mkdocs-material-8.2.15"> + + + + <title>TP - Docker - Docker</title> + + + + <link rel="stylesheet" href="../assets/stylesheets/main.c382b1dc.min.css"> + + + <link rel="stylesheet" href="../assets/stylesheets/palette.cc9b2e1e.min.css"> + + + + <meta name="theme-color" content="#e92063"> + + + + + + + + + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"> + <style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style> + + + + <script>__md_scope=new URL("..",location),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script> + + + + + + </head> + + + + + + + + <body dir="ltr" data-md-color-scheme="" data-md-color-primary="pink" data-md-color-accent=""> + + + + <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off"> + <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off"> + <label class="md-overlay" for="__drawer"></label> + <div data-md-component="skip"> + + + <a href="#decouvrir-docker" class="md-skip"> + Skip to content + </a> + + </div> + <div data-md-component="announce"> + + </div> + + + + +<header class="md-header" data-md-component="header"> + <nav class="md-header__inner md-grid" aria-label="Header"> + <a href=".." title="Docker" class="md-header__button md-logo" aria-label="Docker" data-md-component="logo"> + + <img src="../assets/logo.png" alt="logo"> + + </a> + <label class="md-header__button md-icon" for="__drawer"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg> + </label> + <div class="md-header__title" data-md-component="header-title"> + <div class="md-header__ellipsis"> + <div class="md-header__topic"> + <span class="md-ellipsis"> + Docker + </span> + </div> + <div class="md-header__topic" data-md-component="header-topic"> + <span class="md-ellipsis"> + + TP - Docker + + </span> + </div> + </div> + </div> + + + + <label class="md-header__button md-icon" for="__search"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg> + </label> + <div class="md-search" data-md-component="search" role="dialog"> + <label class="md-search__overlay" for="__search"></label> + <div class="md-search__inner" role="search"> + <form class="md-search__form" name="search"> + <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required> + <label class="md-search__icon md-icon" for="__search"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg> + </label> + <nav class="md-search__options" aria-label="Search"> + + <button type="reset" class="md-search__icon md-icon" aria-label="Clear" tabindex="-1"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg> + </button> + </nav> + + </form> + <div class="md-search__output"> + <div class="md-search__scrollwrap" data-md-scrollfix> + <div class="md-search-result" data-md-component="search-result"> + <div class="md-search-result__meta"> + Initializing search + </div> + <ol class="md-search-result__list"></ol> + </div> + </div> + </div> + </div> +</div> + + + </nav> + +</header> + + <div class="md-container" data-md-component="container"> + + + + + +<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs"> + <div class="md-tabs__inner md-grid"> + <ul class="md-tabs__list"> + + + + + + + + + + <li class="md-tabs__item"> + <a href="../ch1-getting-start/" class="md-tabs__link"> + TD + </a> + </li> + + + + + + + + + + + + + + <li class="md-tabs__item"> + <a href="./" class="md-tabs__link md-tabs__link--active"> + TP + </a> + </li> + + + + </ul> + </div> +</nav> + + + + <main class="md-main" data-md-component="main"> + <div class="md-main__inner md-grid"> + + + + <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" > + <div class="md-sidebar__scrollwrap"> + <div class="md-sidebar__inner"> + + + + + +<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0"> + <label class="md-nav__title" for="__drawer"> + <a href=".." title="Docker" class="md-nav__button md-logo" aria-label="Docker" data-md-component="logo"> + + <img src="../assets/logo.png" alt="logo"> + + </a> + Docker + </label> + + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + + + + <li class="md-nav__item md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_1" type="checkbox" id="__nav_1" > + + + + + <label class="md-nav__link" for="__nav_1"> + TD + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TD" data-md-level="1"> + <label class="md-nav__title" for="__nav_1"> + <span class="md-nav__icon md-icon"></span> + TD + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + <li class="md-nav__item"> + <a href="../ch1-getting-start/" class="md-nav__link"> + Getting Start - Docker + </a> + </li> + + + + + + + + + + <li class="md-nav__item"> + <a href="../ch1-discover-docker-td/" class="md-nav__link"> + TD - Docker + </a> + </li> + + + + + </ul> + </nav> + </li> + + + + + + + + + + + + + + <li class="md-nav__item md-nav__item--active md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2" type="checkbox" id="__nav_2" checked> + + + + + <label class="md-nav__link" for="__nav_2"> + TP + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TP" data-md-level="1"> + <label class="md-nav__title" for="__nav_2"> + <span class="md-nav__icon md-icon"></span> + TP + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + + + <li class="md-nav__item md-nav__item--active"> + + <input class="md-nav__toggle md-toggle" data-md-toggle="toc" type="checkbox" id="__toc"> + + + + + + <label class="md-nav__link md-nav__link--active" for="__toc"> + TP - Docker + <span class="md-nav__icon md-icon"></span> + </label> + + <a href="./" class="md-nav__link md-nav__link--active"> + TP - Docker + </a> + + + +<nav class="md-nav md-nav--secondary" aria-label="Table of contents"> + + + + + + + <label class="md-nav__title" for="__toc"> + <span class="md-nav__icon md-icon"></span> + Table of contents + </label> + <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> + + <li class="md-nav__item"> + <a href="#objectifs" class="md-nav__link"> + Objectifs + </a> + + <nav class="md-nav" aria-label="Objectifs"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#bonnes-pratiques" class="md-nav__link"> + Bonnes pratiques + </a> + +</li> + + <li class="md-nav__item"> + <a href="#application-cible" class="md-nav__link"> + Application cible + </a> + +</li> + + <li class="md-nav__item"> + <a href="#images-de-base" class="md-nav__link"> + Images de base + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#base-de-donnees" class="md-nav__link"> + Base de données + </a> + + <nav class="md-nav" aria-label="Base de données"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#fondamentaux" class="md-nav__link"> + Fondamentaux + </a> + +</li> + + <li class="md-nav__item"> + <a href="#initialisation-de-la-base-de-donnees" class="md-nav__link"> + Initialisation de la base de données + </a> + +</li> + + <li class="md-nav__item"> + <a href="#persistez-les-donnees" class="md-nav__link"> + Persistez les données + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#api-backend" class="md-nav__link"> + API Backend + </a> + + <nav class="md-nav" aria-label="API Backend"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#fondamentaux_1" class="md-nav__link"> + Fondamentaux + </a> + +</li> + + <li class="md-nav__item"> + <a href="#build-multistage" class="md-nav__link"> + Build multistage + </a> + + <nav class="md-nav" aria-label="Build multistage"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#api-backend-simple" class="md-nav__link"> + API Backend simple + </a> + +</li> + + <li class="md-nav__item"> + <a href="#api-backend-3-tiers" class="md-nav__link"> + API Backend 3-tiers + </a> + +</li> + + </ul> + </nav> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#serveur-http" class="md-nav__link"> + Serveur HTTP + </a> + + <nav class="md-nav" aria-label="Serveur HTTP"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#choisissez-une-image-de-base-appropriee" class="md-nav__link"> + Choisissez une image de base appropriée. + </a> + +</li> + + <li class="md-nav__item"> + <a href="#recuperation-de-la-configuration" class="md-nav__link"> + Récupération de la Configuration + </a> + +</li> + + <li class="md-nav__item"> + <a href="#reverse-proxy-rpx" class="md-nav__link"> + Reverse Proxy (RPX) + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#creons-une-stack" class="md-nav__link"> + Créons une stack + </a> + + <nav class="md-nav" aria-label="Créons une stack"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#docker-compose" class="md-nav__link"> + Docker-compose + </a> + +</li> + + <li class="md-nav__item"> + <a href="#bonus-amelioration-du-docker-compose" class="md-nav__link"> + Bonus - Amélioration du Docker compose + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#bonus-publication-de-vos-images" class="md-nav__link"> + Bonus - Publication de vos images + </a> + +</li> + + <li class="md-nav__item"> + <a href="#conclusion" class="md-nav__link"> + Conclusion + </a> + +</li> + + </ul> + +</nav> + + </li> + + + + + </ul> + </nav> + </li> + + + + </ul> +</nav> + </div> + </div> + </div> + + + + <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" > + <div class="md-sidebar__scrollwrap"> + <div class="md-sidebar__inner"> + + +<nav class="md-nav md-nav--secondary" aria-label="Table of contents"> + + + + + + + <label class="md-nav__title" for="__toc"> + <span class="md-nav__icon md-icon"></span> + Table of contents + </label> + <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> + + <li class="md-nav__item"> + <a href="#objectifs" class="md-nav__link"> + Objectifs + </a> + + <nav class="md-nav" aria-label="Objectifs"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#bonnes-pratiques" class="md-nav__link"> + Bonnes pratiques + </a> + +</li> + + <li class="md-nav__item"> + <a href="#application-cible" class="md-nav__link"> + Application cible + </a> + +</li> + + <li class="md-nav__item"> + <a href="#images-de-base" class="md-nav__link"> + Images de base + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#base-de-donnees" class="md-nav__link"> + Base de données + </a> + + <nav class="md-nav" aria-label="Base de données"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#fondamentaux" class="md-nav__link"> + Fondamentaux + </a> + +</li> + + <li class="md-nav__item"> + <a href="#initialisation-de-la-base-de-donnees" class="md-nav__link"> + Initialisation de la base de données + </a> + +</li> + + <li class="md-nav__item"> + <a href="#persistez-les-donnees" class="md-nav__link"> + Persistez les données + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#api-backend" class="md-nav__link"> + API Backend + </a> + + <nav class="md-nav" aria-label="API Backend"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#fondamentaux_1" class="md-nav__link"> + Fondamentaux + </a> + +</li> + + <li class="md-nav__item"> + <a href="#build-multistage" class="md-nav__link"> + Build multistage + </a> + + <nav class="md-nav" aria-label="Build multistage"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#api-backend-simple" class="md-nav__link"> + API Backend simple + </a> + +</li> + + <li class="md-nav__item"> + <a href="#api-backend-3-tiers" class="md-nav__link"> + API Backend 3-tiers + </a> + +</li> + + </ul> + </nav> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#serveur-http" class="md-nav__link"> + Serveur HTTP + </a> + + <nav class="md-nav" aria-label="Serveur HTTP"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#choisissez-une-image-de-base-appropriee" class="md-nav__link"> + Choisissez une image de base appropriée. + </a> + +</li> + + <li class="md-nav__item"> + <a href="#recuperation-de-la-configuration" class="md-nav__link"> + Récupération de la Configuration + </a> + +</li> + + <li class="md-nav__item"> + <a href="#reverse-proxy-rpx" class="md-nav__link"> + Reverse Proxy (RPX) + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#creons-une-stack" class="md-nav__link"> + Créons une stack + </a> + + <nav class="md-nav" aria-label="Créons une stack"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#docker-compose" class="md-nav__link"> + Docker-compose + </a> + +</li> + + <li class="md-nav__item"> + <a href="#bonus-amelioration-du-docker-compose" class="md-nav__link"> + Bonus - Amélioration du Docker compose + </a> + +</li> + + </ul> + </nav> + +</li> + + <li class="md-nav__item"> + <a href="#bonus-publication-de-vos-images" class="md-nav__link"> + Bonus - Publication de vos images + </a> + +</li> + + <li class="md-nav__item"> + <a href="#conclusion" class="md-nav__link"> + Conclusion + </a> + +</li> + + </ul> + +</nav> + </div> + </div> + </div> + + + <div class="md-content" data-md-component="content"> + <article class="md-content__inner md-typeset"> + + + + +<h1 id="decouvrir-docker">Découvrir Docker</h1> +<h2 id="objectifs">Objectifs</h2> +<h3 id="bonnes-pratiques">Bonnes pratiques</h3> +<p>Tout au long du TP vous allez créer des fichiers liés a docker ou docker-compose ou liés a du code que nous allons utiliser. Pour vous y retrouver, créez une structure de fichiers appropriée, 1 dossier par image buildée par exemple.</p> +<h3 id="application-cible">Application cible</h3> +<p>Application trois tiers:</p> +<ul> +<li>Serveur HTTP (simple revers proxy dans un premier temps)</li> +<li>API Backend</li> +<li>Base de données</li> +</ul> +<p>Pour chacune de ces applications, nous suivrons le même processus : choisir l'image de base Docker appropriée, créer et configurer cette image, ajouter les spécificités de notre application et, à un moment donné, la faire fonctionner. Notre objectif final est d'avoir une application trois tiers qui tourne.</p> +<h3 id="images-de-base">Images de base</h3> +<p>Ces images seront celles utilisées comme point de départ pour builder vos images custom qui feront tourner votre application.</p> +<ul> +<li><a href="https://hub.docker.com/_/httpd" target="_blank">Serveur HTTP</a></li> +<li><a href="https://hub.docker.com/_/openjdk" target="_blank">API Backend</a></li> +<li><a href="https://hub.docker.com/_/postgres" target="_blank">Base de données</a></li> +</ul> +<p>Pour éviter le <strong>rate limit</strong> de Docker Hub nous vous fournissons ces même image dans notre registry public :</p> +<h2 id="base-de-donnees">Base de données</h2> +<h3 id="fondamentaux">Fondamentaux</h3> +<p>Nous utiliserons l'image : postgres:14.1-alpine.</p> +<p>Faisons fonctionner un simple serveur PostgreSQL. Voici ce qui serait un Dockerfile minimal :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="k">FROM</span><span class="w"> </span><span class="s">registry.takima.io/school/proxy/postgres:14.1-alpine</span> +<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a> +<a id="__codelineno-0-3" name="__codelineno-0-3" href="#__codelineno-0-3"></a><span class="k">ENV</span><span class="w"> </span><span class="nv">POSTGRES_DB</span><span class="o">=</span>db<span class="w"> </span><span class="se">\</span> +<a id="__codelineno-0-4" name="__codelineno-0-4" href="#__codelineno-0-4"></a><span class="w"> </span><span class="nv">POSTGRES_USER</span><span class="o">=</span>usr<span class="w"> </span><span class="se">\</span> +<a id="__codelineno-0-5" name="__codelineno-0-5" href="#__codelineno-0-5"></a><span class="w"> </span><span class="nv">POSTGRES_PASSWORD</span><span class="o">=</span><span class="nb">pwd</span> +</code></pre></div> +<p>Construisez cette image et démarrez un conteneur correctement, vous devriez pouvoir accéder à votre base de données en fonction du port de publication que vous choisissez : localhost:PORT.</p> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>Ici les variable ENV sont indiquer pour être prise par défault. Il est ensuite conseiller de les override lors du lancement du conteneur avec l'option <code>-e</code></p> +</div> +<p>Votre base de données PostgreSQL devrait être opérationnelle. Connectez-vous à votre base de données et vérifiez que tout fonctionne correctement. +N'oubliez pas de nommer votre image Docker et votre conteneur.</p> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>Si vous avez des difficultés, revenez à la partie <strong>Construire l'image</strong> et <strong>Exécuter votre image</strong> sur TD01 - Docker (<a href="http://school.pages.takima.io/devops-resources/ch1-discover-docker-td/" target="_blank">TD 1 Découverte de Docker</a>).</p> +</div> +<p>Relancez votre base de données et <a href="https://hub.docker.com/_/adminer/" target="_blank">Adminer</a> avec <code>--network app-network</code> pour permettre la communication Adminer/Base de données. +Nous utilisons <code>-–network</code> pour placer adminer et postgres dans le même réseau, au lieu de <code>-–link</code> car ce dernier est obsolète.</p> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>N'oubliez pas de créer votre réseau +<div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>docker<span class="w"> </span>network<span class="w"> </span>create<span class="w"> </span>app-network +</code></pre></div> +Si vous n'avez pas placé le conteneur dans le bon réseau vous pouvez le connecter avec <code>docker network connect my_net my_container</code></p> +</div> +<p>Est-il correct d'avoir des mots de passe écrits en clair dans un fichier ? Vous pouvez plutôt définir ces paramètres d'environnement lors de l'exécution de l'image en utilisant l'option <code>-e</code>.</p> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>Pourquoi exécutons-nous le conteneur avec une option <code>-e</code> pour fournir les variables d'environnement ?</p> +</div> +<h3 id="initialisation-de-la-base-de-donnees">Initialisation de la base de données</h3> +<p>parfois il peut être pratique d'avoir notre structure de base de données initialisée à l'intérieur même de l'image Docker ainsi que quelques données initiales. Tout script SQL trouvé dans <code>/docker-entrypoint-initdb.d</code> sera exécuté dans l'ordre alphabétique, ajoutons donc quelques scripts à notre image :</p> +<p><strong>01-CreateScheme.sql</strong></p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">public</span><span class="p">.</span><span class="n">departments</span> +<a id="__codelineno-2-2" name="__codelineno-2-2" href="#__codelineno-2-2"></a><span class="p">(</span> +<a id="__codelineno-2-3" name="__codelineno-2-3" href="#__codelineno-2-3"></a><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="nb">SERIAL</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span> +<a id="__codelineno-2-4" name="__codelineno-2-4" href="#__codelineno-2-4"></a><span class="w"> </span><span class="n">name</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span> +<a id="__codelineno-2-5" name="__codelineno-2-5" href="#__codelineno-2-5"></a><span class="p">);</span> +<a id="__codelineno-2-6" name="__codelineno-2-6" href="#__codelineno-2-6"></a> +<a id="__codelineno-2-7" name="__codelineno-2-7" href="#__codelineno-2-7"></a><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="k">public</span><span class="p">.</span><span class="n">students</span> +<a id="__codelineno-2-8" name="__codelineno-2-8" href="#__codelineno-2-8"></a><span class="p">(</span> +<a id="__codelineno-2-9" name="__codelineno-2-9" href="#__codelineno-2-9"></a><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="nb">SERIAL</span><span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="p">,</span> +<a id="__codelineno-2-10" name="__codelineno-2-10" href="#__codelineno-2-10"></a><span class="w"> </span><span class="n">department_id</span><span class="w"> </span><span class="nb">INT</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="k">REFERENCES</span><span class="w"> </span><span class="n">departments</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">),</span> +<a id="__codelineno-2-11" name="__codelineno-2-11" href="#__codelineno-2-11"></a><span class="w"> </span><span class="n">first_name</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="p">,</span> +<a id="__codelineno-2-12" name="__codelineno-2-12" href="#__codelineno-2-12"></a><span class="w"> </span><span class="n">last_name</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">20</span><span class="p">)</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span> +<a id="__codelineno-2-13" name="__codelineno-2-13" href="#__codelineno-2-13"></a><span class="p">);</span> +</code></pre></div> +<p><strong>02-InsertData.sql</strong></p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">departments</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">'IT'</span><span class="p">);</span> +<a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">departments</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">'RH'</span><span class="p">);</span> +<a id="__codelineno-3-3" name="__codelineno-3-3" href="#__codelineno-3-3"></a><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">departments</span><span class="w"> </span><span class="p">(</span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="s1">'Administration'</span><span class="p">);</span> +<a id="__codelineno-3-4" name="__codelineno-3-4" href="#__codelineno-3-4"></a> +<a id="__codelineno-3-5" name="__codelineno-3-5" href="#__codelineno-3-5"></a> +<a id="__codelineno-3-6" name="__codelineno-3-6" href="#__codelineno-3-6"></a><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">students</span><span class="w"> </span><span class="p">(</span><span class="n">department_id</span><span class="p">,</span><span class="w"> </span><span class="n">first_name</span><span class="p">,</span><span class="w"> </span><span class="n">last_name</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s1">'Eli'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Copter'</span><span class="p">);</span> +<a id="__codelineno-3-7" name="__codelineno-3-7" href="#__codelineno-3-7"></a><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">students</span><span class="w"> </span><span class="p">(</span><span class="n">department_id</span><span class="p">,</span><span class="w"> </span><span class="n">first_name</span><span class="p">,</span><span class="w"> </span><span class="n">last_name</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="s1">'Emma'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Carena'</span><span class="p">);</span> +<a id="__codelineno-3-8" name="__codelineno-3-8" href="#__codelineno-3-8"></a><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">students</span><span class="w"> </span><span class="p">(</span><span class="n">department_id</span><span class="p">,</span><span class="w"> </span><span class="n">first_name</span><span class="p">,</span><span class="w"> </span><span class="n">last_name</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">2</span><span class="p">,</span><span class="w"> </span><span class="s1">'Jack'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Uzzi'</span><span class="p">);</span> +<a id="__codelineno-3-9" name="__codelineno-3-9" href="#__codelineno-3-9"></a><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">students</span><span class="w"> </span><span class="p">(</span><span class="n">department_id</span><span class="p">,</span><span class="w"> </span><span class="n">first_name</span><span class="p">,</span><span class="w"> </span><span class="n">last_name</span><span class="p">)</span><span class="w"> </span><span class="k">VALUES</span><span class="w"> </span><span class="p">(</span><span class="mi">3</span><span class="p">,</span><span class="w"> </span><span class="s1">'Aude'</span><span class="p">,</span><span class="w"> </span><span class="s1">'Javel'</span><span class="p">);</span> +</code></pre></div> +<p>Modifiez votre dockerfile en ajoutant ces scripts dans <code>/docker-entrypoint-initdb.d</code>, reconstruisez votre image et vérifiez que vos scripts ont été exécutés au démarrage et que les données sont présentes dans votre conteneur.</p> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>Lorsque nous parlons de <code>/docker-entrypoint-initdb.d</code>, il s'agit de l'intérieur du conteneur, vous devez donc copier le contenu de votre répertoire dans le répertoire du conteneur.</p> +</div> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>N'oubliez pas de lancer adminer : +<div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a><span class="w"> </span>docker<span class="w"> </span>run<span class="w"> </span><span class="se">\</span> +<a id="__codelineno-4-2" name="__codelineno-4-2" href="#__codelineno-4-2"></a><span class="w"> </span>-p<span class="w"> </span><span class="s2">"8090:8080"</span><span class="w"> </span><span class="se">\</span> +<a id="__codelineno-4-3" name="__codelineno-4-3" href="#__codelineno-4-3"></a><span class="w"> </span>--net<span class="o">=</span>app-network<span class="w"> </span><span class="se">\</span> +<a id="__codelineno-4-4" name="__codelineno-4-4" href="#__codelineno-4-4"></a><span class="w"> </span>--name<span class="o">=</span>adminer<span class="w"> </span><span class="se">\</span> +<a id="__codelineno-4-5" name="__codelineno-4-5" href="#__codelineno-4-5"></a><span class="w"> </span>-d<span class="w"> </span><span class="se">\</span> +<a id="__codelineno-4-6" name="__codelineno-4-6" href="#__codelineno-4-6"></a><span class="w"> </span>registry.takima.io/school/proxy/adminer +</code></pre></div></p> +</div> +<h3 id="persistez-les-donnees">Persistez les données</h3> +<p>Vous avez peut-être remarqué, en effctuant des modifications de données sur votre base avec adminer, que si votre conteneur de base de données est détruit, toutes vos données sont réinitialisées. Une base de données doit persister durablement les données : c'est sont rôle. Utilisez des volumes pour persister les données sur le disque de l'hôte.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a>-v<span class="w"> </span>/my/own/datadir:/var/lib/postgresql/data +</code></pre></div> +<p>Vérifiez que les données survivent lorsque votre conteneur est détruit.</p> +<div class="admonition link"> +<p class="admonition-title">Link</p> +<p><a href="https://docs.docker.com/storage/volumes/" target="_blank">Volumes Docker</a></p> +</div> +<div class="admonition question"> +<p class="admonition-title">Question</p> +<p>Pourquoi avons-nous besoin d'un volume attaché à notre conteneur PostgreSQL ?</p> +</div> +<h2 id="api-backend">API Backend</h2> +<h3 id="fondamentaux_1">Fondamentaux</h3> +<p>Pour commencer, nous allons simplement exécuter une classe Java "Hello World" dans nos conteneurs, et seulement après nous exécuterons un fichier JAR. Dans les deux cas, choisissez l'image appropriée en gardant à l'esprit que <strong>nous n'avons besoin que d'un environnement d'exécution Java</strong>.</p> +<p>Voici une implémentation de <strong>Hello World</strong> en Java :</p> +<p><strong>Main.java</strong></p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">Main</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-6-2" name="__codelineno-6-2" href="#__codelineno-6-2"></a> +<a id="__codelineno-6-3" name="__codelineno-6-3" href="#__codelineno-6-3"></a><span class="w"> </span><span class="kd">public</span> +<a id="__codelineno-6-4" name="__codelineno-6-4" href="#__codelineno-6-4"></a> +<a id="__codelineno-6-5" name="__codelineno-6-5" href="#__codelineno-6-5"></a><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kt">void</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="n">String</span><span class="o">[]</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-6-6" name="__codelineno-6-6" href="#__codelineno-6-6"></a><span class="w"> </span><span class="n">System</span><span class="p">.</span><span class="na">out</span><span class="p">.</span><span class="na">println</span><span class="p">(</span><span class="s">"Hello World!"</span><span class="p">);</span> +<a id="__codelineno-6-7" name="__codelineno-6-7" href="#__codelineno-6-7"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-6-8" name="__codelineno-6-8" href="#__codelineno-6-8"></a><span class="p">}</span> +</code></pre></div> +<p>1- Compilez avec votre version cible de Java : <code>javac Main.java</code>. +2- Rédigez le Dockerfile.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>FROM<span class="w"> </span><span class="c1"># TODO: Choisissez une version de Java JRE</span> +<a id="__codelineno-7-2" name="__codelineno-7-2" href="#__codelineno-7-2"></a><span class="c1"># TODO: Ajoutez le code Java (bytecode, .class)</span> +<a id="__codelineno-7-3" name="__codelineno-7-3" href="#__codelineno-7-3"></a><span class="c1"># TODO: Exécutez le code Java avec la commande : "java Main"</span> +</code></pre></div> +<p>3- Maintenant, pour lancer l'application, vous devez faire la même chose que dans l'étape de base 1.</p> +<p>Ici, vous avez un premier aperçu de votre application Backend.</p> +<p>Dans l'étape suivante, nous enrichirons le build (en utilisant Maven au lieu de javac minimaliste) et exécuterons un fichier JAR au lieu d'un simple .class.</p> +<p>→ Si cela réussit, vous devriez voir "Hello World" dans votre console.</p> +<h3 id="build-multistage">Build multistage</h3> +<p>Dans la section précédente, nous avons construit du code Java sur notre machine pour le faire fonctionner dans un conteneur Docker. Ne serait-il pas formidable que Docker gère également le build ? Vous avez probablement remarqué que les images Docker openjdk par défaut contiennent... Eh bien... un JDK ! Le problème du JDK c'est qu'il contient beaucoup de librairy et d'outil qui ne sont pas nécéssaire pour le run. Pour le run on préfère avoir un JRE qui est bien moins lourd. Or rappelons que le but des images Docker est d'être le plus léger possible. Cela tombe bien le multistage build sert à cela : utiliser une image pour builder (JDK par exemple), puis transmettre le build dans une autre image qui servira au run (JRE par exemple).</p> +<p>Créez un build multistage en utilisant le <a href="https://docs.docker.com/develop/develop-images/multistage-build/" target="_blank">multistage</a>.</p> +<p>Votre Dockerfile devrait ressembler à ceci :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a><span class="k">FROM</span><span class="w"> </span><span class="s">registry.takima.io/school/proxy/openjdk:17</span> +<a id="__codelineno-8-2" name="__codelineno-8-2" href="#__codelineno-8-2"></a><span class="c"># Construire Main.java</span> +<a id="__codelineno-8-3" name="__codelineno-8-3" href="#__codelineno-8-3"></a><span class="c"># TODO : dans les prochaines étapes (pas maintenant)</span> +<a id="__codelineno-8-4" name="__codelineno-8-4" href="#__codelineno-8-4"></a> +<a id="__codelineno-8-5" name="__codelineno-8-5" href="#__codelineno-8-5"></a><span class="k">FROM</span><span class="w"> </span><span class="s">registry.takima.io/school/proxy/eclipse-temurin:17-jre-alpine</span> +<a id="__codelineno-8-6" name="__codelineno-8-6" href="#__codelineno-8-6"></a><span class="c"># Copiez les ressources de l'étape précédente</span> +<a id="__codelineno-8-7" name="__codelineno-8-7" href="#__codelineno-8-7"></a><span class="k">COPY</span><span class="w"> </span>--from<span class="o">=</span><span class="m">0</span><span class="w"> </span>/usr/src/Main.class<span class="w"> </span>. +<a id="__codelineno-8-8" name="__codelineno-8-8" href="#__codelineno-8-8"></a><span class="c"># Exécutez le code Java avec le JRE</span> +<a id="__codelineno-8-9" name="__codelineno-8-9" href="#__codelineno-8-9"></a><span class="c"># TODO : dans les prochaines étapes (pas maintenant)</span> +</code></pre></div> +<p>Ne remplissez pas le Dockerfile maintenant, nous devrons le faire dans les prochaines étapes.</p> +<h4 id="api-backend-simple">API Backend simple</h4> +<p>Nous déploierons une application Spring Boot fournissant une API simple avec un seul point d'accès de publication.</p> +<p>Créez votre application Spring Boot sur : <a href="https://start.spring.io/#!type=maven-project&language=java&platformVersion=2.7.7&packaging=jar&jvmVersion=17&groupId=fr.takima.training&artifactId=simpleapi&name=simpleapi&description=Demo%20project%20for%20Spring%20Boot&packageName=fr.takima.training.simpleapi&dependencies=web" target="_blank">Spring Initializer</a>.</p> +<p>Utilisez la configuration suivante :</p> +<ul> +<li>Projet : Maven</li> +<li>Langage : Java 17</li> +<li>Spring Boot : 2.7.5</li> +<li>Emballage : Jar</li> +<li>Dépendances : <strong>Spring Web</strong></li> +</ul> +<p>Générez le projet et créez une classe GreetingController simple :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a><span class="kn">package</span><span class="w"> </span><span class="nn">fr.takima.training.simpleapi.controller</span><span class="p">;</span> +<a id="__codelineno-9-2" name="__codelineno-9-2" href="#__codelineno-9-2"></a> +<a id="__codelineno-9-3" name="__codelineno-9-3" href="#__codelineno-9-3"></a><span class="kn">import</span><span class="w"> </span><span class="nn">org.springframework.web.bind.annotation.*</span><span class="p">;</span> +<a id="__codelineno-9-4" name="__codelineno-9-4" href="#__codelineno-9-4"></a> +<a id="__codelineno-9-5" name="__codelineno-9-5" href="#__codelineno-9-5"></a><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.concurrent.atomic.AtomicLong</span><span class="p">;</span> +<a id="__codelineno-9-6" name="__codelineno-9-6" href="#__codelineno-9-6"></a> +<a id="__codelineno-9-7" name="__codelineno-9-7" href="#__codelineno-9-7"></a><span class="nd">@RestController</span> +<a id="__codelineno-9-8" name="__codelineno-9-8" href="#__codelineno-9-8"></a><span class="kd">public</span><span class="w"> </span><span class="kd">class</span> <span class="nc">GreetingController</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-9-9" name="__codelineno-9-9" href="#__codelineno-9-9"></a> +<a id="__codelineno-9-10" name="__codelineno-9-10" href="#__codelineno-9-10"></a><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">template</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"Hello, %s!"</span><span class="p">;</span> +<a id="__codelineno-9-11" name="__codelineno-9-11" href="#__codelineno-9-11"></a><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">AtomicLong</span><span class="w"> </span><span class="n">counter</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">AtomicLong</span><span class="p">();</span> +<a id="__codelineno-9-12" name="__codelineno-9-12" href="#__codelineno-9-12"></a> +<a id="__codelineno-9-13" name="__codelineno-9-13" href="#__codelineno-9-13"></a><span class="w"> </span><span class="nd">@GetMapping</span><span class="p">(</span><span class="s">"/"</span><span class="p">)</span> +<a id="__codelineno-9-14" name="__codelineno-9-14" href="#__codelineno-9-14"></a><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Greeting</span><span class="w"> </span><span class="nf">greeting</span><span class="p">(</span><span class="nd">@RequestParam</span><span class="p">(</span><span class="n">value</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"name"</span><span class="p">,</span><span class="w"> </span><span class="n">defaultValue</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s">"World"</span><span class="p">)</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">name</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-9-15" name="__codelineno-9-15" href="#__codelineno-9-15"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Greeting</span><span class="p">(</span><span class="n">counter</span><span class="p">.</span><span class="na">incrementAndGet</span><span class="p">(),</span><span class="w"> </span><span class="n">String</span><span class="p">.</span><span class="na">format</span><span class="p">(</span><span class="n">template</span><span class="p">,</span><span class="w"> </span><span class="n">name</span><span class="p">));</span> +<a id="__codelineno-9-16" name="__codelineno-9-16" href="#__codelineno-9-16"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-9-17" name="__codelineno-9-17" href="#__codelineno-9-17"></a> +<a id="__codelineno-9-18" name="__codelineno-9-18" href="#__codelineno-9-18"></a><span class="w"> </span><span class="kd">class</span> <span class="nc">Greeting</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-9-19" name="__codelineno-9-19" href="#__codelineno-9-19"></a> +<a id="__codelineno-9-20" name="__codelineno-9-20" href="#__codelineno-9-20"></a><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="n">id</span><span class="p">;</span> +<a id="__codelineno-9-21" name="__codelineno-9-21" href="#__codelineno-9-21"></a><span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">content</span><span class="p">;</span> +<a id="__codelineno-9-22" name="__codelineno-9-22" href="#__codelineno-9-22"></a> +<a id="__codelineno-9-23" name="__codelineno-9-23" href="#__codelineno-9-23"></a><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="nf">Greeting</span><span class="p">(</span><span class="kt">long</span><span class="w"> </span><span class="n">id</span><span class="p">,</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="n">content</span><span class="p">)</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-9-24" name="__codelineno-9-24" href="#__codelineno-9-24"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">id</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">id</span><span class="p">;</span> +<a id="__codelineno-9-25" name="__codelineno-9-25" href="#__codelineno-9-25"></a><span class="w"> </span><span class="k">this</span><span class="p">.</span><span class="na">content</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">content</span><span class="p">;</span> +<a id="__codelineno-9-26" name="__codelineno-9-26" href="#__codelineno-9-26"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-9-27" name="__codelineno-9-27" href="#__codelineno-9-27"></a> +<a id="__codelineno-9-28" name="__codelineno-9-28" href="#__codelineno-9-28"></a><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="kt">long</span><span class="w"> </span><span class="nf">getId</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-9-29" name="__codelineno-9-29" href="#__codelineno-9-29"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">id</span><span class="p">;</span> +<a id="__codelineno-9-30" name="__codelineno-9-30" href="#__codelineno-9-30"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-9-31" name="__codelineno-9-31" href="#__codelineno-9-31"></a> +<a id="__codelineno-9-32" name="__codelineno-9-32" href="#__codelineno-9-32"></a><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">getContent</span><span class="p">()</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-9-33" name="__codelineno-9-33" href="#__codelineno-9-33"></a><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">content</span><span class="p">;</span> +<a id="__codelineno-9-34" name="__codelineno-9-34" href="#__codelineno-9-34"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-9-35" name="__codelineno-9-35" href="#__codelineno-9-35"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-9-36" name="__codelineno-9-36" href="#__codelineno-9-36"></a><span class="p">}</span> +</code></pre></div> +<p>Vous pouvez maintenant construire et démarrer votre application. Bien sûr, vous aurez besoin de Maven et d'un JDK-17.</p> +<p>Que diriez-vous d'avoir un conteneur pour construire et exécuter notre API simpliste ?</p> +<p>Oh, attendez, nous avons Docker, voici comment vous pourriez construire et exécuter votre application avec Docker :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a><span class="c"># Build</span> +<a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a><span class="k">FROM</span><span class="w"> </span><span class="s">registry.takima.io/school/proxy/maven:3.8.5-openjdk-17-slim</span><span class="w"> </span><span class="k">AS</span><span class="w"> </span><span class="s">myapp-build</span> +<a id="__codelineno-10-3" name="__codelineno-10-3" href="#__codelineno-10-3"></a><span class="k">ENV</span><span class="w"> </span>MYAPP_HOME<span class="w"> </span>/opt/myapp +<a id="__codelineno-10-4" name="__codelineno-10-4" href="#__codelineno-10-4"></a><span class="k">WORKDIR</span><span class="w"> </span><span class="s">$MYAPP_HOME</span> +<a id="__codelineno-10-5" name="__codelineno-10-5" href="#__codelineno-10-5"></a><span class="k">COPY</span><span class="w"> </span>pom.xml<span class="w"> </span>. +<a id="__codelineno-10-6" name="__codelineno-10-6" href="#__codelineno-10-6"></a><span class="k">COPY</span><span class="w"> </span>src<span class="w"> </span>./src +<a id="__codelineno-10-7" name="__codelineno-10-7" href="#__codelineno-10-7"></a><span class="k">RUN</span><span class="w"> </span>mvn<span class="w"> </span>package<span class="w"> </span>-DskipTests +<a id="__codelineno-10-8" name="__codelineno-10-8" href="#__codelineno-10-8"></a> +<a id="__codelineno-10-9" name="__codelineno-10-9" href="#__codelineno-10-9"></a><span class="c"># Run</span> +<a id="__codelineno-10-10" name="__codelineno-10-10" href="#__codelineno-10-10"></a><span class="k">FROM</span><span class="w"> </span><span class="s">registry.takima.io/school/proxy/openjdk:17-slim</span> +<a id="__codelineno-10-11" name="__codelineno-10-11" href="#__codelineno-10-11"></a><span class="k">ENV</span><span class="w"> </span>MYAPP_HOME<span class="w"> </span>/opt/myapp +<a id="__codelineno-10-12" name="__codelineno-10-12" href="#__codelineno-10-12"></a><span class="k">WORKDIR</span><span class="w"> </span><span class="s">$MYAPP_HOME</span> +<a id="__codelineno-10-13" name="__codelineno-10-13" href="#__codelineno-10-13"></a><span class="k">COPY</span><span class="w"> </span>--from<span class="o">=</span>myapp-build<span class="w"> </span><span class="nv">$MYAPP_HOME</span>/target/*.jar<span class="w"> </span><span class="nv">$MYAPP_HOME</span>/myapp.jar +<a id="__codelineno-10-14" name="__codelineno-10-14" href="#__codelineno-10-14"></a> +<a id="__codelineno-10-15" name="__codelineno-10-15" href="#__codelineno-10-15"></a><span class="k">ENTRYPOINT</span><span class="w"> </span>java<span class="w"> </span>-jar<span class="w"> </span>myapp.jar +</code></pre></div> +<div class="admonition question"> +<p class="admonition-title">Question</p> +<p>Pourquoi avons-nous besoin d'un build multistage ? Essayez de comprendre chaque étape de ce dockerfile</p> +</div> +<div class="admonition check"> +<p class="admonition-title">Check</p> +<p>Une application Spring Boot simple avec un point de terminaison HelloWorld qui fonctionne.</p> +</div> +<p>Avez-vous remarqué que Maven télécharge toutes les bibliothèques à chaque build de l'image ? Vous pouvez contribuer à économiser de l'énergie et du temps en mettant en cache les bibliothèques lorsque le fichier pom de Maven n'a pas été modifié en exécutant la commande : <code>mvn dependency:go-offline</code>.</p> +<h4 id="api-backend-3-tiers">API Backend 3-tiers</h4> +<p>Nous avons vu comment builder et lancer du code Java. Passons maintenant au build et à l'exécution de l'API qui nous intéresse pour notre application 3-tiers. Vous pouvez obtenir le code source compressé ici : <a href="https://github.com/takima-training/simple-api-student" target="_blank">simple-api</a>.</p> +<p>Ajustez la configuration dans le fichier <code>simple-api/src/main/resources/application.yml</code>. Vous devrez indiquer les configuration pour accèder à votre base de données. Normalement pour ce type de configuration qui dépend de l'environement il est fortement recommandé de ne pas hardcoder le setting dans l'image. On preferera utiliser des variables d'environnement que l'on indiquera au lancement du docker (option <code>-e</code> ou bien via un fichier d'env).</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a><span class="w"> </span><span class="nt">datasource</span><span class="p">:</span> +<a id="__codelineno-11-2" name="__codelineno-11-2" href="#__codelineno-11-2"></a><span class="w"> </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="s">"jdbc:postgresql://${DATABASE_URL}:${DATABASE_PORT}/${POSTGRES_DB}</span> +</code></pre></div> +<p>Créer le Dockerfile de votre API comme vous l'avez fait dans le chapitre précédent. Builder votre image API et lancer le docker à partir de cette image.</p> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>Attention votre API doit pouvoir se connecter à la Base de données. Comment faire ? C'est très simpe : placez votre contenaur API dans le même réseau que votre DB, créez un <code>docker network</code> si ce n'est pas déjà fait. Rappelez-vous également que dans un network vous pouvez requêter un conteneur sur son nom.</p> +</div> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>Votre API doit-être publiée pour pouvoir y accèder. Vous aurez surement besoin de l'option <code>-p</code> au lancement du conteneur. Faites attention a utiliser un port disponible sur votre Host.</p> +</div> +<p>Une fois que tout est correctement lancé, vous devriez pouvoir accéder à votre API, par exemple sur : <a href="http://localhost:8080/departments/IT/students" target="_blank">/departments/IT/students</a>. (dépend du port mapping que vous avez réalisé)</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a><span class="p">[</span> +<a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-12-3" name="__codelineno-12-3" href="#__codelineno-12-3"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span> +<a id="__codelineno-12-4" name="__codelineno-12-4" href="#__codelineno-12-4"></a><span class="w"> </span><span class="nt">"firstname"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Eli"</span><span class="p">,</span> +<a id="__codelineno-12-5" name="__codelineno-12-5" href="#__codelineno-12-5"></a><span class="w"> </span><span class="nt">"lastname"</span><span class="p">:</span><span class="w"> </span><span class="s2">"Copter"</span><span class="p">,</span> +<a id="__codelineno-12-6" name="__codelineno-12-6" href="#__codelineno-12-6"></a><span class="w"> </span><span class="nt">"department"</span><span class="p">:</span><span class="w"> </span><span class="p">{</span> +<a id="__codelineno-12-7" name="__codelineno-12-7" href="#__codelineno-12-7"></a><span class="w"> </span><span class="nt">"id"</span><span class="p">:</span><span class="w"> </span><span class="mi">1</span><span class="p">,</span> +<a id="__codelineno-12-8" name="__codelineno-12-8" href="#__codelineno-12-8"></a><span class="w"> </span><span class="nt">"name"</span><span class="p">:</span><span class="w"> </span><span class="s2">"IT"</span> +<a id="__codelineno-12-9" name="__codelineno-12-9" href="#__codelineno-12-9"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-12-10" name="__codelineno-12-10" href="#__codelineno-12-10"></a><span class="w"> </span><span class="p">}</span> +<a id="__codelineno-12-11" name="__codelineno-12-11" href="#__codelineno-12-11"></a><span class="p">]</span> +</code></pre></div> +<p>Explorez d'autres points d'accès de votre API, jetez un coup d'Å“il aux contrôleurs dans le code source.</p> +<div class="admonition check"> +<p class="admonition-title">Check</p> +<p>Une API Web fonctionne sur votre base de données.</p> +</div> +<h2 id="serveur-http">Serveur HTTP</h2> +<h3 id="choisissez-une-image-de-base-appropriee">Choisissez une image de base appropriée.</h3> +<ul> +<li><a href="https://hub.docker.com/_/httpd" target="_blank">Serveur HTTP</a></li> +</ul> +<p>Créez une page de destination simple : <code>index.html</code> et placez-la dans votre conteneur.</p> +<p>Cela devrait suffire pour l'instant, démarrez votre conteneur et vérifiez que tout fonctionne comme prévu.</p> +<p>Voici quelques commandes que vous pouvez essayer pour vérifier :</p> +<ul> +<li>docker stats</li> +<li>docker inspect</li> +<li>docker logs</li> +</ul> +<p>!!! link - <a href="https://hub.docker.com/_/httpd" target="_blank">Httpd</a> - <a href="https://httpd.apache.org/docs/2.4/getting-started.html" target="_blank">Mise en route</a></p> +<h3 id="recuperation-de-la-configuration">Récupération de la Configuration</h3> +<p>Vous utilisez la configuration Apache par défaut, et cela devrait suffire pour l'instant. Utilisez <code>docker exec</code> pour récupérer cette configuration par défaut depuis votre conteneur en cours d'exécution <code>/usr/local/apache2/conf/httpd.conf</code>.</p> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>Vous pouvez également utiliser <code>docker cp</code>.</p> +</div> +<div class="admonition check"> +<p class="admonition-title">Check</p> +<p>Avoir un fichier httpd.conf en local prêt à être édité.</p> +</div> +<h3 id="reverse-proxy-rpx">Reverse Proxy (RPX)</h3> +<p>Nous allons configurer le serveur HTTP en tant que serveur reverse proxy devant notre application. Ce serveur pourrait être utilisé pour fournir une application front, configurer un endpoint SSL ou gérer la répartition de charge. </p> +<p>Donc, cela peut être assez utile même si, dans notre cas, nous garderons les choses simples. En effet, il servira de passerelle pour accéder à votre API (proxyPass).</p> +<p>Voici la documentation : <a href="https://httpd.apache.org/docs/2.4/en/howto/reverse_proxy.html" target="_blank">Reverse Proxy</a>.</p> +<p>Ajoutez ce qui suit à la configuration httpd.conf que vous avez récupéré précédemment, modifier votre Dockerfile pour injecter cette configuration :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a>ServerName<span class="w"> </span>localhost +<a id="__codelineno-13-2" name="__codelineno-13-2" href="#__codelineno-13-2"></a> +<a id="__codelineno-13-3" name="__codelineno-13-3" href="#__codelineno-13-3"></a> +<a id="__codelineno-13-4" name="__codelineno-13-4" href="#__codelineno-13-4"></a><VirtualHost<span class="w"> </span>*:80> +<a id="__codelineno-13-5" name="__codelineno-13-5" href="#__codelineno-13-5"></a>ProxyPreserveHost<span class="w"> </span>On +<a id="__codelineno-13-6" name="__codelineno-13-6" href="#__codelineno-13-6"></a>ProxyPass<span class="w"> </span>/<span class="w"> </span>http://YOUR_BACKEND_LINK:8080/ +<a id="__codelineno-13-7" name="__codelineno-13-7" href="#__codelineno-13-7"></a>ProxyPassReverse<span class="w"> </span>/<span class="w"> </span>http://YOUR_BACKEND_LINK:8080/ +<a id="__codelineno-13-8" name="__codelineno-13-8" href="#__codelineno-13-8"></a></VirtualHost> +<a id="__codelineno-13-9" name="__codelineno-13-9" href="#__codelineno-13-9"></a>LoadModule<span class="w"> </span>proxy_module<span class="w"> </span>modules/mod_proxy.so +<a id="__codelineno-13-10" name="__codelineno-13-10" href="#__codelineno-13-10"></a>LoadModule<span class="w"> </span>proxy_http_module<span class="w"> </span>modules/mod_proxy_http.so +</code></pre></div> +<div class="admonition tip"> +<p class="admonition-title">Tip</p> +<p>COPY ./httpd.conf /usr/local/apache2/conf/httpd.conf devrait être présent dans votre dockerfile +YOUR_BACKEND_LINK correspond à l'url de votre API (nom du docker api grâce au DNS interne)</p> +</div> +<p>lancez votre front httpd avec les bonnes options (bon network notamment)</p> +<p>Vérifiez que votre reverse proxy sert bien l'API</p> +<h2 id="creons-une-stack">Créons une stack</h2> +<p>Maintenant imaginons que je vous demande de tout détruire et de tout reconstruire. Où bien de me cloner cette stack 3-tiers, vous devrez relancer l'ensemble des commandes, créer les networks, les volumes, les conteneurs. Heureusement, pour créer des stack complête <strong>Docker Compose</strong> arrive à la rescousse !</p> +<h3 id="docker-compose">Docker-compose</h3> +<p>1- Installez <a href="https://docs.docker.com/compose/install/" target="_blank">docker-compose</a> si la commande <code>docker compose</code> ne fonctionne pas.</p> +<p>Vous avez peut-être remarqué qu'il peut être assez fastidieux d'orchestrer manuellement le démarrage, l'arrêt et la reconstruction de nos conteneurs. Heureusement, un outil utile appelé <a href="https://docs.docker.com/compose/" target="_blank">docker-compose</a> est utile dans ces situations.</p> +<p>2- Créez un fichier docker-compose.yml avec la structure suivante pour définir et piloter nos conteneurs :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a><span class="nt">version</span><span class="p">:</span><span class="w"> </span><span class="s">"3.7"</span> +<a id="__codelineno-14-2" name="__codelineno-14-2" href="#__codelineno-14-2"></a> +<a id="__codelineno-14-3" name="__codelineno-14-3" href="#__codelineno-14-3"></a><span class="nt">services</span><span class="p">:</span> +<a id="__codelineno-14-4" name="__codelineno-14-4" href="#__codelineno-14-4"></a><span class="w"> </span><span class="nt">backend</span><span class="p">:</span> +<a id="__codelineno-14-5" name="__codelineno-14-5" href="#__codelineno-14-5"></a><span class="w"> </span><span class="nt">build</span><span class="p">:</span> +<a id="__codelineno-14-6" name="__codelineno-14-6" href="#__codelineno-14-6"></a><span class="w"> </span><span class="c1">#TODO</span> +<a id="__codelineno-14-7" name="__codelineno-14-7" href="#__codelineno-14-7"></a><span class="w"> </span><span class="nt">networks</span><span class="p">:</span> +<a id="__codelineno-14-8" name="__codelineno-14-8" href="#__codelineno-14-8"></a><span class="w"> </span><span class="c1">#TODO</span> +<a id="__codelineno-14-9" name="__codelineno-14-9" href="#__codelineno-14-9"></a><span class="w"> </span><span class="nt">depends_on</span><span class="p">:</span> +<a id="__codelineno-14-10" name="__codelineno-14-10" href="#__codelineno-14-10"></a><span class="w"> </span><span class="c1">#TODO</span> +<a id="__codelineno-14-11" name="__codelineno-14-11" href="#__codelineno-14-11"></a> +<a id="__codelineno-14-12" name="__codelineno-14-12" href="#__codelineno-14-12"></a><span class="w"> </span><span class="nt">database</span><span class="p">:</span> +<a id="__codelineno-14-13" name="__codelineno-14-13" href="#__codelineno-14-13"></a><span class="w"> </span><span class="nt">build</span><span class="p">:</span> +<a id="__codelineno-14-14" name="__codelineno-14-14" href="#__codelineno-14-14"></a><span class="w"> </span><span class="c1">#TODO</span> +<a id="__codelineno-14-15" name="__codelineno-14-15" href="#__codelineno-14-15"></a><span class="w"> </span><span class="nt">networks</span><span class="p">:</span> +<a id="__codelineno-14-16" name="__codelineno-14-16" href="#__codelineno-14-16"></a><span class="w"> </span><span class="c1">#TODO</span> +<a id="__codelineno-14-17" name="__codelineno-14-17" href="#__codelineno-14-17"></a> +<a id="__codelineno-14-18" name="__codelineno-14-18" href="#__codelineno-14-18"></a><span class="w"> </span><span class="nt">httpd</span><span class="p">:</span> +<a id="__codelineno-14-19" name="__codelineno-14-19" href="#__codelineno-14-19"></a><span class="w"> </span><span class="nt">build</span><span class="p">:</span> +<a id="__codelineno-14-20" name="__codelineno-14-20" href="#__codelineno-14-20"></a><span class="w"> </span><span class="c1">#TODO</span> +<a id="__codelineno-14-21" name="__codelineno-14-21" href="#__codelineno-14-21"></a><span class="w"> </span><span class="nt">ports</span><span class="p">:</span> +<a id="__codelineno-14-22" name="__codelineno-14-22" href="#__codelineno-14-22"></a><span class="w"> </span><span class="c1">#TODO</span> +<a id="__codelineno-14-23" name="__codelineno-14-23" href="#__codelineno-14-23"></a><span class="w"> </span><span class="nt">networks</span><span class="p">:</span> +<a id="__codelineno-14-24" name="__codelineno-14-24" href="#__codelineno-14-24"></a><span class="w"> </span><span class="c1">#TODO</span> +<a id="__codelineno-14-25" name="__codelineno-14-25" href="#__codelineno-14-25"></a><span class="w"> </span><span class="nt">depends_on</span><span class="p">:</span> +<a id="__codelineno-14-26" name="__codelineno-14-26" href="#__codelineno-14-26"></a><span class="w"> </span><span class="c1">#TODO</span> +<a id="__codelineno-14-27" name="__codelineno-14-27" href="#__codelineno-14-27"></a> +<a id="__codelineno-14-28" name="__codelineno-14-28" href="#__codelineno-14-28"></a><span class="nt">networks</span><span class="p">:</span> +<a id="__codelineno-14-29" name="__codelineno-14-29" href="#__codelineno-14-29"></a><span class="w"> </span><span class="nt">my-network</span><span class="p">:</span> +</code></pre></div> +<p>Docker-compose gérera les trois conteneurs et un réseau pour nous.</p> +<p>Une fois que vos conteneurs sont orchestrés par docker-compose, vous devriez disposer d'une application parfaitement fonctionnelle. Assurez-vous de pouvoir accéder à votre API sur <a href="http://localhost/" target="_blank">localhost</a>.</p> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>Les ports de votre backend et de votre base de données ne doivent pas être ouverts sur votre machine hôte.</p> +</div> +<div class="admonition question"> +<p class="admonition-title">Question</p> +<p>Pourquoi <strong>docker compose</strong> est-il si important ?</p> +</div> +<div class="admonition check"> +<p class="admonition-title">Check</p> +<p>Une application 3-tiers fonctionne avec docker-compose.</p> +</div> +<h3 id="bonus-amelioration-du-docker-compose">Bonus - Amélioration du Docker compose</h3> +<ol> +<li> +<p>Ajoutez un volume à votre base de données </p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="nt">volumes</span><span class="p">:</span> +<a id="__codelineno-15-2" name="__codelineno-15-2" href="#__codelineno-15-2"></a><span class="w"> </span><span class="nt">my_db_volume</span><span class="p">:</span> +<a id="__codelineno-15-3" name="__codelineno-15-3" href="#__codelineno-15-3"></a><span class="w"> </span><span class="nt">driver</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">local</span> +</code></pre></div> +</li> +<li> +<p>Utilisez un fichier .env pour stocker vos variables d'environnement (pour la DB par exemple):</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="nt">database</span><span class="p">:</span> +<a id="__codelineno-16-2" name="__codelineno-16-2" href="#__codelineno-16-2"></a><span class="w"> </span><span class="nt">env_file</span><span class="p">:</span> +<a id="__codelineno-16-3" name="__codelineno-16-3" href="#__codelineno-16-3"></a><span class="w"> </span><span class="p p-Indicator">-</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">database/.env</span> +</code></pre></div> +</li> +<li> +<p>ségreguez votre stack avec plusieurs networks : un network front (front, API) / un network back (DB, API)</p> +</li> +<li> +<p>Mettez en place un health check sur votre base de données, et un depends_on sur votre API qui fonctionnera sur la readiness de votre DB</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a><span class="c1"># coté compose api</span> +<a id="__codelineno-17-2" name="__codelineno-17-2" href="#__codelineno-17-2"></a><span class="w"> </span><span class="nt">depends_on</span><span class="p">:</span> +<a id="__codelineno-17-3" name="__codelineno-17-3" href="#__codelineno-17-3"></a><span class="w"> </span><span class="nt">database</span><span class="p">:</span> +<a id="__codelineno-17-4" name="__codelineno-17-4" href="#__codelineno-17-4"></a><span class="w"> </span><span class="nt">condition</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">service_healthy</span> +<a id="__codelineno-17-5" name="__codelineno-17-5" href="#__codelineno-17-5"></a> +<a id="__codelineno-17-6" name="__codelineno-17-6" href="#__codelineno-17-6"></a><span class="c1"># coté compose database</span> +<a id="__codelineno-17-7" name="__codelineno-17-7" href="#__codelineno-17-7"></a><span class="w"> </span><span class="nt">healthcheck</span><span class="p">:</span> +<a id="__codelineno-17-8" name="__codelineno-17-8" href="#__codelineno-17-8"></a><span class="w"> </span><span class="nt">test</span><span class="p">:</span><span class="w"> </span><span class="p p-Indicator">[</span><span class="s">"CMD-SHELL"</span><span class="p p-Indicator">,</span><span class="w"> </span><span class="s">"pg_isready</span><span class="nv"> </span><span class="s">-U</span><span class="nv"> </span><span class="s">postgres"</span><span class="p p-Indicator">]</span> +<a id="__codelineno-17-9" name="__codelineno-17-9" href="#__codelineno-17-9"></a><span class="w"> </span><span class="nt">interval</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">10s</span> +<a id="__codelineno-17-10" name="__codelineno-17-10" href="#__codelineno-17-10"></a><span class="w"> </span><span class="nt">timeout</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">5s</span> +<a id="__codelineno-17-11" name="__codelineno-17-11" href="#__codelineno-17-11"></a><span class="w"> </span><span class="nt">retries</span><span class="p">:</span><span class="w"> </span><span class="l l-Scalar l-Scalar-Plain">3</span> +</code></pre></div> +</li> +</ol> +<h2 id="bonus-publication-de-vos-images">Bonus - Publication de vos images</h2> +<p>Vos images Docker sont stockées localement, publions-les pour qu'elles puissent être utilisées par d'autres membres de l'équipe ou sur d'autres machines. En effet l'un des éléments fondamental de l'écosystème est la publication des images (le plus leger possible), dans le flow de dévelopement c'est ce qu'on appelle le <code>Delivery</code>. Dans notre cas nous allons publier nos images sur le registry Docker principale : <code>Docker Hub</code></p> +<p>Vous aurez besoin d'un compte <a href="https://hub.docker.com/" target="_blank">Docker Hub</a>.</p> +<p>1- Connectez-vous à votre compte Docker Hub fraîchement créé avec <code>docker login</code>.</p> +<p>2- Étiquetez votre image. Jusqu'à présent, nous n'avons utilisé que l'étiquette "latest", maintenant que nous voulons la publier, ajoutons des informations de version significatives à nos images.</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-18-1" name="__codelineno-18-1" href="#__codelineno-18-1"></a>docker<span class="w"> </span>tag<span class="w"> </span>my-database<span class="w"> </span>USERNAME/my-database:1.0 +</code></pre></div> +<p>3- Ensuite, poussez votre image sur Docker Hub :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-19-1" name="__codelineno-19-1" href="#__codelineno-19-1"></a>docker<span class="w"> </span>push<span class="w"> </span>USERNAME/my-database +</code></pre></div> +<p><strong>Docker Hub</strong> n'est pas le seul registre d'images Docker, et vous pouvez également héberger vos propres images (c'est évidemment le choix de la plupart des entreprises).</p> +<p>Une fois que vous avez publié vos images sur Docker Hub, vous les verrez dans votre compte : il serait très utile d'avoir une documentation pour votre image si vous prévoyez de la partager avec d'autres personnes.</p> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>Vous pouvez également utiliser le registre Google Container, Amazon ECR ou bien au plus proche de votre code sur Gitlab ou Github.Vous pouvez aussi déployer votre propre registry avec des outils comme <code>Portus</code> ou <code>Harbor</code>.</p> +</div> +<div class="admonition check"> +<p class="admonition-title">Check</p> +<p>Des images Docker publiées.</p> +</div> +<h2 id="conclusion">Conclusion</h2> +<p>Cela devrait vous donner un bon aperçu de l'utilisation de Docker. Vous avez mis en place une application trois tiers, configuré une base de données, une API Backend et un serveur HTTP. Tout cela est orchestré par Docker Compose. Enfin, vous avez publié vos images sur Docker Hub pour les partager avec d'autres.</p> +<p align="center">© Takima 2023</p> +<p>"</p> + + + </article> + </div> + </div> + + </main> + + <footer class="md-footer"> + + <nav class="md-footer__inner md-grid" aria-label="Footer"> + + + <a href="../ch1-discover-docker-td/" class="md-footer__link md-footer__link--prev" aria-label="Previous: TD - Docker" rel="prev"> + <div class="md-footer__button md-icon"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg> + </div> + <div class="md-footer__title"> + <div class="md-ellipsis"> + <span class="md-footer__direction"> + Previous + </span> + TD - Docker + </div> + </div> + </a> + + + </nav> + + <div class="md-footer-meta md-typeset"> + <div class="md-footer-meta__inner md-grid"> + <div class="md-copyright"> + + + Made with + <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener"> + Material for MkDocs + </a> + +</div> + + </div> + </div> +</footer> + + </div> + <div class="md-dialog" data-md-component="dialog"> + <div class="md-dialog__inner md-typeset"></div> + </div> + <script id="__config" type="application/json">{"base": "..", "features": ["navigation.tabs", "navigation.instant"], "search": "../assets/javascripts/workers/search.2a1c317c.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.config.lang": "en", "search.config.pipeline": "trimmer, stopWordFilter", "search.config.separator": "[\\s\\-]+", "search.placeholder": "Search", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version.title": "Select version"}}</script> + + + <script src="../assets/javascripts/bundle.a6c66575.min.js"></script> + + + </body> +</html> \ No newline at end of file diff --git a/public/ch1-getting-start/index.html b/public/ch1-getting-start/index.html new file mode 100644 index 0000000..2fc940f --- /dev/null +++ b/public/ch1-getting-start/index.html @@ -0,0 +1,692 @@ + +<!doctype html> +<html lang="en" class="no-js"> + <head> + + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width,initial-scale=1"> + + + + <link rel="icon" href="../assets/images/favicon.png"> + <meta name="generator" content="mkdocs-1.3.0, mkdocs-material-8.2.15"> + + + + <title>Getting Start - Docker - Docker</title> + + + + <link rel="stylesheet" href="../assets/stylesheets/main.c382b1dc.min.css"> + + + <link rel="stylesheet" href="../assets/stylesheets/palette.cc9b2e1e.min.css"> + + + + <meta name="theme-color" content="#e92063"> + + + + + + + + + <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> + <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,700,700i%7CRoboto+Mono:400,400i,700,700i&display=fallback"> + <style>:root{--md-text-font:"Roboto";--md-code-font:"Roboto Mono"}</style> + + + + <script>__md_scope=new URL("..",location),__md_get=(e,_=localStorage,t=__md_scope)=>JSON.parse(_.getItem(t.pathname+"."+e)),__md_set=(e,_,t=localStorage,a=__md_scope)=>{try{t.setItem(a.pathname+"."+e,JSON.stringify(_))}catch(e){}}</script> + + + + + + </head> + + + + + + + + <body dir="ltr" data-md-color-scheme="" data-md-color-primary="pink" data-md-color-accent=""> + + + + <input class="md-toggle" data-md-toggle="drawer" type="checkbox" id="__drawer" autocomplete="off"> + <input class="md-toggle" data-md-toggle="search" type="checkbox" id="__search" autocomplete="off"> + <label class="md-overlay" for="__drawer"></label> + <div data-md-component="skip"> + + + <a href="#getting-start" class="md-skip"> + Skip to content + </a> + + </div> + <div data-md-component="announce"> + + </div> + + + + +<header class="md-header" data-md-component="header"> + <nav class="md-header__inner md-grid" aria-label="Header"> + <a href=".." title="Docker" class="md-header__button md-logo" aria-label="Docker" data-md-component="logo"> + + <img src="../assets/logo.png" alt="logo"> + + </a> + <label class="md-header__button md-icon" for="__drawer"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2Z"/></svg> + </label> + <div class="md-header__title" data-md-component="header-title"> + <div class="md-header__ellipsis"> + <div class="md-header__topic"> + <span class="md-ellipsis"> + Docker + </span> + </div> + <div class="md-header__topic" data-md-component="header-topic"> + <span class="md-ellipsis"> + + Getting Start - Docker + + </span> + </div> + </div> + </div> + + + + <label class="md-header__button md-icon" for="__search"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg> + </label> + <div class="md-search" data-md-component="search" role="dialog"> + <label class="md-search__overlay" for="__search"></label> + <div class="md-search__inner" role="search"> + <form class="md-search__form" name="search"> + <input type="text" class="md-search__input" name="query" aria-label="Search" placeholder="Search" autocapitalize="off" autocorrect="off" autocomplete="off" spellcheck="false" data-md-component="search-query" required> + <label class="md-search__icon md-icon" for="__search"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M9.5 3A6.5 6.5 0 0 1 16 9.5c0 1.61-.59 3.09-1.56 4.23l.27.27h.79l5 5-1.5 1.5-5-5v-.79l-.27-.27A6.516 6.516 0 0 1 9.5 16 6.5 6.5 0 0 1 3 9.5 6.5 6.5 0 0 1 9.5 3m0 2C7 5 5 7 5 9.5S7 14 9.5 14 14 12 14 9.5 12 5 9.5 5Z"/></svg> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg> + </label> + <nav class="md-search__options" aria-label="Search"> + + <button type="reset" class="md-search__icon md-icon" aria-label="Clear" tabindex="-1"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M19 6.41 17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41Z"/></svg> + </button> + </nav> + + </form> + <div class="md-search__output"> + <div class="md-search__scrollwrap" data-md-scrollfix> + <div class="md-search-result" data-md-component="search-result"> + <div class="md-search-result__meta"> + Initializing search + </div> + <ol class="md-search-result__list"></ol> + </div> + </div> + </div> + </div> +</div> + + + </nav> + +</header> + + <div class="md-container" data-md-component="container"> + + + + + +<nav class="md-tabs" aria-label="Tabs" data-md-component="tabs"> + <div class="md-tabs__inner md-grid"> + <ul class="md-tabs__list"> + + + + + + + + + + + + <li class="md-tabs__item"> + <a href="./" class="md-tabs__link md-tabs__link--active"> + TD + </a> + </li> + + + + + + + + + + + + <li class="md-tabs__item"> + <a href="../ch1-discover-docker-tp/" class="md-tabs__link"> + TP + </a> + </li> + + + + </ul> + </div> +</nav> + + + + <main class="md-main" data-md-component="main"> + <div class="md-main__inner md-grid"> + + + + <div class="md-sidebar md-sidebar--primary" data-md-component="sidebar" data-md-type="navigation" > + <div class="md-sidebar__scrollwrap"> + <div class="md-sidebar__inner"> + + + + + +<nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0"> + <label class="md-nav__title" for="__drawer"> + <a href=".." title="Docker" class="md-nav__button md-logo" aria-label="Docker" data-md-component="logo"> + + <img src="../assets/logo.png" alt="logo"> + + </a> + Docker + </label> + + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + + + + + + <li class="md-nav__item md-nav__item--active md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_1" type="checkbox" id="__nav_1" checked> + + + + + <label class="md-nav__link" for="__nav_1"> + TD + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TD" data-md-level="1"> + <label class="md-nav__title" for="__nav_1"> + <span class="md-nav__icon md-icon"></span> + TD + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + + + <li class="md-nav__item md-nav__item--active"> + + <input class="md-nav__toggle md-toggle" data-md-toggle="toc" type="checkbox" id="__toc"> + + + + + + <label class="md-nav__link md-nav__link--active" for="__toc"> + Getting Start - Docker + <span class="md-nav__icon md-icon"></span> + </label> + + <a href="./" class="md-nav__link md-nav__link--active"> + Getting Start - Docker + </a> + + + +<nav class="md-nav md-nav--secondary" aria-label="Table of contents"> + + + + + + + <label class="md-nav__title" for="__toc"> + <span class="md-nav__icon md-icon"></span> + Table of contents + </label> + <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> + + <li class="md-nav__item"> + <a href="#installer-le-moteur-docker" class="md-nav__link"> + Installer le moteur Docker + </a> + + <nav class="md-nav" aria-label="Installer le moteur Docker"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#sur-linux" class="md-nav__link"> + Sur Linux + </a> + +</li> + + <li class="md-nav__item"> + <a href="#sur-windows" class="md-nav__link"> + Sur Windows + </a> + +</li> + + <li class="md-nav__item"> + <a href="#sur-macos" class="md-nav__link"> + Sur MacOs + </a> + + <nav class="md-nav" aria-label="Sur MacOs"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#etapes-pour-installer-docker-sur-macos-sans-docker-desktop" class="md-nav__link"> + Étapes pour installer Docker sur macOS sans Docker Desktop : + </a> + +</li> + + </ul> + </nav> + +</li> + + </ul> + </nav> + +</li> + + </ul> + +</nav> + + </li> + + + + + + + + + + <li class="md-nav__item"> + <a href="../ch1-discover-docker-td/" class="md-nav__link"> + TD - Docker + </a> + </li> + + + + + </ul> + </nav> + </li> + + + + + + + + + + + + <li class="md-nav__item md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2" type="checkbox" id="__nav_2" > + + + + + <label class="md-nav__link" for="__nav_2"> + TP + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TP" data-md-level="1"> + <label class="md-nav__title" for="__nav_2"> + <span class="md-nav__icon md-icon"></span> + TP + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + <li class="md-nav__item"> + <a href="../ch1-discover-docker-tp/" class="md-nav__link"> + TP - Docker + </a> + </li> + + + + + </ul> + </nav> + </li> + + + + </ul> +</nav> + </div> + </div> + </div> + + + + <div class="md-sidebar md-sidebar--secondary" data-md-component="sidebar" data-md-type="toc" > + <div class="md-sidebar__scrollwrap"> + <div class="md-sidebar__inner"> + + +<nav class="md-nav md-nav--secondary" aria-label="Table of contents"> + + + + + + + <label class="md-nav__title" for="__toc"> + <span class="md-nav__icon md-icon"></span> + Table of contents + </label> + <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> + + <li class="md-nav__item"> + <a href="#installer-le-moteur-docker" class="md-nav__link"> + Installer le moteur Docker + </a> + + <nav class="md-nav" aria-label="Installer le moteur Docker"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#sur-linux" class="md-nav__link"> + Sur Linux + </a> + +</li> + + <li class="md-nav__item"> + <a href="#sur-windows" class="md-nav__link"> + Sur Windows + </a> + +</li> + + <li class="md-nav__item"> + <a href="#sur-macos" class="md-nav__link"> + Sur MacOs + </a> + + <nav class="md-nav" aria-label="Sur MacOs"> + <ul class="md-nav__list"> + + <li class="md-nav__item"> + <a href="#etapes-pour-installer-docker-sur-macos-sans-docker-desktop" class="md-nav__link"> + Étapes pour installer Docker sur macOS sans Docker Desktop : + </a> + +</li> + + </ul> + </nav> + +</li> + + </ul> + </nav> + +</li> + + </ul> + +</nav> + </div> + </div> + </div> + + + <div class="md-content" data-md-component="content"> + <article class="md-content__inner md-typeset"> + + + + +<h1 id="getting-start">Getting Start</h1> +<h2 id="installer-le-moteur-docker">Installer le moteur Docker</h2> +<p>Vous pouvez passer cette étape si vous avez déjà installé Docker.</p> +<div class="admonition warning"> +<p class="admonition-title">Warning</p> +<p>À partir de 2022, <strong>Docker Desktop</strong> nécessite désormais un abonnement payant pour une utilisation commerciale dans les grandes entreprises.</p> +</div> +<ul> +<li><em>Docker Desktop</em> est un utilitaire pratique qui inclut le <em>Docker Engine</em>, ainsi qu'une interface utilisateur graphique.</li> +<li>Le <em>Docker Engine</em> est la technologie de base derrière Docker. Il s'agit d'un logiciel open source qui fonctionne sous Linux en tant que service qui permet d'exécuter des conteneurs au-dessus du noyau Linux. Il est responsable du cycle de vie des conteneurs et de l'isolation des ressources physiques (calcul, mémoire, stockage) auxquelles les conteneurs peuvent accéder. Le moteur peut s'exécuter sur une machine physique ou virtuelle, mais il ne peut fonctionner que sur un noyau Linux, c'est-à -dire sur un système d'exploitation basé sur Linux. Il est important de comprendre que le moteur Docker ne s'exécute que sous Linux.</li> +<li>Le <em>Docker CLI</em> est un outil en ligne de commande gratuit (<code>CLI</code>) pour créer et gérer des <strong>objets Docker</strong>, tels que des <code>images</code>, des <code>conteneurs</code>, des <code>réseaux</code> et des <code>volumes</code>.</li> +</ul> +<p>Voici les alternatives à Docker Desktop</p> +<h3 id="sur-linux">Sur Linux</h3> +<p>Sur Linux c'est très simple il suffit d'installer docker. Nous allons utiliser ici le script clef en main d'installation :</p> +<ul> +<li>Installez le <em>Docker Engine</em> pour Linux <a href="https://docs.docker.com/engine/install/">documentation officielle</a>: + <div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a>curl<span class="w"> </span>-fsSL<span class="w"> </span>https://get.docker.com<span class="w"> </span>-o<span class="w"> </span>get-docker.sh +<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a>sudo<span class="w"> </span>sh<span class="w"> </span>get-docker.sh +</code></pre></div></li> +<li>Ajoutez votre utilisateur au groupe docker : + <div class="highlight"><pre><span></span><code><a id="__codelineno-1-1" name="__codelineno-1-1" href="#__codelineno-1-1"></a>sudo<span class="w"> </span>usermod<span class="w"> </span>-aG<span class="w"> </span>docker<span class="w"> </span><span class="nv">$USER</span> +</code></pre></div></li> +</ul> +<div class="admonition warning"> +<p class="admonition-title">Warning</p> +<p>En tant qu'utilisateur Linux, vous pouvez utiliser uniquement le <strong>Moteur Docker</strong>, même si la documentation officielle encourage l'adoption de <a href="https://docs.docker.com/desktop/linux/install/">Docker Desktop pour Linux</a>.</p> +</div> +<h3 id="sur-windows">Sur Windows</h3> +<p>Nous allons utiliser <code>Windows Linux Subsystem</code></p> +<p>Tout d'abord, il faut vérifier que la fonctionnalité est bien activée sur Windows. Depuis la barre de recherche Windows, ouvrez <code>Activer ou désactiver une fonctionnalité Windows</code> et cochez <code>Sous-sytème Windows pour Linux</code> et <code>plateforme de machine virtuelle</code>.</p> +<p>Puis installez les deux applications du Microsoft Store</p> +<p><img alt="alt text" src="../assets/microsoftStoreWsl.JPG" /></p> +<p>Maintenant, ouvrez l'application Ubuntu précédemment installée. Il va vous être demandé de configurer un user et un mot de passe.</p> +<p>Enfin, nous allons installer docker</p> +<ol> +<li>Update Ubtuntu</li> +</ol> +<div class="highlight"><pre><span></span><code><a id="__codelineno-2-1" name="__codelineno-2-1" href="#__codelineno-2-1"></a>sudo<span class="w"> </span>apt<span class="w"> </span>update +</code></pre></div> +<ol> +<li>Installer Docker</li> +</ol> +<div class="highlight"><pre><span></span><code><a id="__codelineno-3-1" name="__codelineno-3-1" href="#__codelineno-3-1"></a>curl<span class="w"> </span>-fsSL<span class="w"> </span>https://get.docker.com<span class="w"> </span>-o<span class="w"> </span>get-docker.sh +<a id="__codelineno-3-2" name="__codelineno-3-2" href="#__codelineno-3-2"></a>sudo<span class="w"> </span>sh<span class="w"> </span>get-docker.sh +</code></pre></div> +<ol> +<li>Vérifier l'installation</li> +</ol> +<div class="highlight"><pre><span></span><code><a id="__codelineno-4-1" name="__codelineno-4-1" href="#__codelineno-4-1"></a>docker<span class="w"> </span>-v +</code></pre></div> +<ol> +<li>Using docker without root permissions</li> +</ol> +<p>A cette étape, docker a besoin des permissions root pour fonctionner : <code>docker run registry.takima.io/school/proxy/hello-world</code>, va renvoyer une erreur de permissions. +Pour pallier à cela executez les commandes suivantes</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-5-1" name="__codelineno-5-1" href="#__codelineno-5-1"></a>sudo<span class="w"> </span>addgroup<span class="w"> </span>--system<span class="w"> </span>docker +<a id="__codelineno-5-2" name="__codelineno-5-2" href="#__codelineno-5-2"></a>sudo<span class="w"> </span>adduser<span class="w"> </span><span class="nv">$USER</span><span class="w"> </span>docker +<a id="__codelineno-5-3" name="__codelineno-5-3" href="#__codelineno-5-3"></a>newgrp<span class="w"> </span>docker +<a id="__codelineno-5-4" name="__codelineno-5-4" href="#__codelineno-5-4"></a>sudo<span class="w"> </span>chown<span class="w"> </span>root:docker<span class="w"> </span>/var/run/docker.sock +<a id="__codelineno-5-5" name="__codelineno-5-5" href="#__codelineno-5-5"></a>sudo<span class="w"> </span>chmod<span class="w"> </span>g+w<span class="w"> </span>/var/run/docker.sock +</code></pre></div> +<ol> +<li>Test</li> +</ol> +<div class="highlight"><pre><span></span><code><a id="__codelineno-6-1" name="__codelineno-6-1" href="#__codelineno-6-1"></a>docker<span class="w"> </span>run<span class="w"> </span>registry.takima.io/school/proxy/hello-world +</code></pre></div> +<ol> +<li>Installer Docker-compose</li> +</ol> +<div class="highlight"><pre><span></span><code><a id="__codelineno-7-1" name="__codelineno-7-1" href="#__codelineno-7-1"></a>sudo<span class="w"> </span>curl<span class="w"> </span>-L<span class="w"> </span><span class="s2">"https://github.com/docker/compose/releases/download/v2.23.0/docker-compose-</span><span class="k">$(</span>uname<span class="w"> </span>-s<span class="k">)</span><span class="s2">-</span><span class="k">$(</span>uname<span class="w"> </span>-m<span class="k">)</span><span class="s2">"</span><span class="w"> </span>-o<span class="w"> </span>/usr/local/bin/docker-compose +</code></pre></div> +<ol> +<li>Vérifier l'installation</li> +</ol> +<div class="highlight"><pre><span></span><code><a id="__codelineno-8-1" name="__codelineno-8-1" href="#__codelineno-8-1"></a>docker-compose<span class="w"> </span>-v +</code></pre></div> +<ol> +<li>Troubleshooting</li> +</ol> +<p>Si en utilisant docker-compose vous avez cette erreur : +Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?</p> +<p>Lancez le service docker</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-9-1" name="__codelineno-9-1" href="#__codelineno-9-1"></a>sudo<span class="w"> </span>service<span class="w"> </span>docker<span class="w"> </span>start +</code></pre></div> +<h3 id="sur-macos">Sur MacOs</h3> +<p>Installer Docker Engine sur macOS sans utiliser Docker Desktop peut être plus complexe. Pour ce faire nous allons utiliser <code>Minikube</code> qui embarque égallement une implémentation du daemon Docker qui nous permettra de lancer des conteneurs. En plus, c'est parfait, car vous serez prêt à vous exercer a Kubernetes, mais c'est une autre histoire.</p> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>Avant de commencer, assurez-vous que vous avez installé <a href="https://brew.sh">Homebrew</a> sur votre système macOS. Si ce n'est pas le cas, vous pouvez l'installer en exécutant la commande suivante dans votre terminal : +<code>bash +/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</code></p> +</div> +<h4 id="etapes-pour-installer-docker-sur-macos-sans-docker-desktop">Étapes pour installer Docker sur macOS sans Docker Desktop :</h4> +<p>Pour cela on utioisera une approche <strong>Docker Machine</strong> c'est à dire lancer une VM pour faire tourner docker engine.</p> +<ol> +<li>Install hyperkit and minikube</li> +</ol> +<div class="highlight"><pre><span></span><code><a id="__codelineno-10-1" name="__codelineno-10-1" href="#__codelineno-10-1"></a>brew<span class="w"> </span>install<span class="w"> </span>hyperkit +<a id="__codelineno-10-2" name="__codelineno-10-2" href="#__codelineno-10-2"></a>brew<span class="w"> </span>install<span class="w"> </span>minikube +</code></pre></div> +<p>Attention pour les puces M1 darwin/arm64, il y a incompatibilité avec les driver de virtualisation hyperkit ou virtualbox. Un drivers compatible est QEMU :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-11-1" name="__codelineno-11-1" href="#__codelineno-11-1"></a>brew<span class="w"> </span>install<span class="w"> </span>qemu +</code></pre></div> +<ol> +<li>Install Docker CLI</li> +</ol> +<div class="highlight"><pre><span></span><code><a id="__codelineno-12-1" name="__codelineno-12-1" href="#__codelineno-12-1"></a>brew<span class="w"> </span>install<span class="w"> </span>docker +<a id="__codelineno-12-2" name="__codelineno-12-2" href="#__codelineno-12-2"></a>brew<span class="w"> </span>install<span class="w"> </span>docker-compose +</code></pre></div> +<ol> +<li>Start minikube</li> +</ol> +<p>suivant votre driver de virtualisation :</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-13-1" name="__codelineno-13-1" href="#__codelineno-13-1"></a>minikube<span class="w"> </span>config<span class="w"> </span><span class="nb">set</span><span class="w"> </span>driver<span class="w"> </span>qemu +</code></pre></div> +<p>puis</p> +<div class="highlight"><pre><span></span><code><a id="__codelineno-14-1" name="__codelineno-14-1" href="#__codelineno-14-1"></a>minikube<span class="w"> </span>start +</code></pre></div> +<ol> +<li>Tell Docker CLI to talk to minikube's VM</li> +</ol> +<div class="highlight"><pre><span></span><code><a id="__codelineno-15-1" name="__codelineno-15-1" href="#__codelineno-15-1"></a><span class="nb">eval</span><span class="w"> </span><span class="k">$(</span>minikube<span class="w"> </span>docker-env<span class="k">)</span> +</code></pre></div> +<h1 id="save-ip-to-a-hostname">Save IP to a hostname</h1> +<div class="highlight"><pre><span></span><code><a id="__codelineno-16-1" name="__codelineno-16-1" href="#__codelineno-16-1"></a><span class="nb">echo</span><span class="w"> </span><span class="s2">"`minikube ip` docker.local"</span><span class="w"> </span><span class="p">|</span><span class="w"> </span>sudo<span class="w"> </span>tee<span class="w"> </span>-a<span class="w"> </span>/etc/hosts<span class="w"> </span>><span class="w"> </span>/dev/null +</code></pre></div> +<h1 id="test">Test</h1> +<div class="highlight"><pre><span></span><code><a id="__codelineno-17-1" name="__codelineno-17-1" href="#__codelineno-17-1"></a>docker<span class="w"> </span>run<span class="w"> </span>hello-world +</code></pre></div> +<p>Cela devrait vous permettre d'utiliser Docker Engine sur macOS sans Docker Desktop.</p> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>Minikube exécute une configuration Kubernetes et, par conséquent, exécute de nombreux conteneurs qui ne sont pas nécessaires si l'on n'utilise pas Kubernetes. Nous pouvons exécuter la commande <code>minikube pause</code> pour mettre en pause les conteneurs liés à Kubernetes afin qu'ils ne consomment pas de ressources système inutilement.</p> +</div> + + + </article> + </div> + </div> + + </main> + + <footer class="md-footer"> + + <nav class="md-footer__inner md-grid" aria-label="Footer"> + + + + <a href="../ch1-discover-docker-td/" class="md-footer__link md-footer__link--next" aria-label="Next: TD - Docker" rel="next"> + <div class="md-footer__title"> + <div class="md-ellipsis"> + <span class="md-footer__direction"> + Next + </span> + TD - Docker + </div> + </div> + <div class="md-footer__button md-icon"> + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11H4Z"/></svg> + </div> + </a> + + </nav> + + <div class="md-footer-meta md-typeset"> + <div class="md-footer-meta__inner md-grid"> + <div class="md-copyright"> + + + Made with + <a href="https://squidfunk.github.io/mkdocs-material/" target="_blank" rel="noopener"> + Material for MkDocs + </a> + +</div> + + </div> + </div> +</footer> + + </div> + <div class="md-dialog" data-md-component="dialog"> + <div class="md-dialog__inner md-typeset"></div> + </div> + <script id="__config" type="application/json">{"base": "..", "features": ["navigation.tabs", "navigation.instant"], "search": "../assets/javascripts/workers/search.2a1c317c.min.js", "translations": {"clipboard.copied": "Copied to clipboard", "clipboard.copy": "Copy to clipboard", "search.config.lang": "en", "search.config.pipeline": "trimmer, stopWordFilter", "search.config.separator": "[\\s\\-]+", "search.placeholder": "Search", "search.result.more.one": "1 more on this page", "search.result.more.other": "# more on this page", "search.result.none": "No matching documents", "search.result.one": "1 matching document", "search.result.other": "# matching documents", "search.result.placeholder": "Type to start searching", "search.result.term.missing": "Missing", "select.version.title": "Select version"}}</script> + + + <script src="../assets/javascripts/bundle.a6c66575.min.js"></script> + + + </body> +</html> \ No newline at end of file diff --git a/public/cheatsheet/index.html b/public/cheatsheet/index.html index 0645199..15be1b0 100644 --- a/public/cheatsheet/index.html +++ b/public/cheatsheet/index.html @@ -13,7 +13,7 @@ - <title>Cheatsheet - Devops</title> + <title>Cheatsheet - Docker</title> @@ -77,7 +77,7 @@ <header class="md-header" data-md-component="header"> <nav class="md-header__inner md-grid" aria-label="Header"> - <a href=".." title="Devops" class="md-header__button md-logo" aria-label="Devops" data-md-component="logo"> + <a href=".." title="Docker" class="md-header__button md-logo" aria-label="Docker" data-md-component="logo"> <img src="../assets/logo.png" alt="logo"> @@ -89,7 +89,7 @@ <div class="md-header__ellipsis"> <div class="md-header__topic"> <span class="md-ellipsis"> - Devops + Docker </span> </div> <div class="md-header__topic" data-md-component="header-topic"> @@ -157,25 +157,31 @@ - <li class="md-tabs__item"> - <a href=".." class="md-tabs__link"> - Devops in Action - Guide - </a> - </li> + + + + <li class="md-tabs__item"> + <a href="../ch1-getting-start/" class="md-tabs__link"> + TD + </a> + </li> + - - - <li class="md-tabs__item"> - <a href="./" class="md-tabs__link md-tabs__link--active"> - Cheatsheet - </a> - </li> + + + + <li class="md-tabs__item"> + <a href="../ch1-discover-docker-tp/" class="md-tabs__link"> + TP + </a> + </li> + </ul> @@ -199,12 +205,12 @@ <nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0"> <label class="md-nav__title" for="__drawer"> - <a href=".." title="Devops" class="md-nav__button md-logo" aria-label="Devops" data-md-component="logo"> + <a href=".." title="Docker" class="md-nav__button md-logo" aria-label="Docker" data-md-component="logo"> <img src="../assets/logo.png" alt="logo"> </a> - Devops + Docker </label> <ul class="md-nav__list" data-md-scrollfix> @@ -216,13 +222,60 @@ + + <li class="md-nav__item md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_1" type="checkbox" id="__nav_1" > + + + + + <label class="md-nav__link" for="__nav_1"> + TD + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TD" data-md-level="1"> + <label class="md-nav__title" for="__nav_1"> + <span class="md-nav__icon md-icon"></span> + TD + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + <li class="md-nav__item"> - <a href=".." class="md-nav__link"> - Devops in Action - Guide + <a href="../ch1-getting-start/" class="md-nav__link"> + Getting Start - Docker </a> </li> + + + + + + + + <li class="md-nav__item"> + <a href="../ch1-discover-docker-td/" class="md-nav__link"> + TD - Docker + </a> + </li> + + + + + </ul> + </nav> + </li> + + @@ -230,21 +283,44 @@ - - - <li class="md-nav__item md-nav__item--active"> + + <li class="md-nav__item md-nav__item--nested"> - <input class="md-nav__toggle md-toggle" data-md-toggle="toc" type="checkbox" id="__toc"> + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2" type="checkbox" id="__nav_2" > - - <a href="./" class="md-nav__link md-nav__link--active"> - Cheatsheet - </a> + <label class="md-nav__link" for="__nav_2"> + TP + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TP" data-md-level="1"> + <label class="md-nav__title" for="__nav_2"> + <span class="md-nav__icon md-icon"></span> + TP + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + <li class="md-nav__item"> + <a href="../ch1-discover-docker-tp/" class="md-nav__link"> + TP - Docker + </a> + </li> + + + + + </ul> + </nav> </li> @@ -269,6 +345,28 @@ + <label class="md-nav__title" for="__toc"> + <span class="md-nav__icon md-icon"></span> + Table of contents + </label> + <ul class="md-nav__list" data-md-component="toc" data-md-scrollfix> + + <li class="md-nav__item"> + <a href="#resume-des-commandes-du-dockerfile" class="md-nav__link"> + Résumé des commandes du Dockerfile + </a> + +</li> + + <li class="md-nav__item"> + <a href="#docker-docker-compose" class="md-nav__link"> + Docker & docker-compose + </a> + +</li> + + </ul> + </nav> </div> </div> @@ -282,6 +380,30 @@ <h1 id="cheatsheet">Cheatsheet</h1> +<h2 id="resume-des-commandes-du-dockerfile">Résumé des commandes du Dockerfile</h2> +<p>Voici un bref résumé des quelques commandes de base que nous avons utilisées dans notre Dockerfile.</p> +<ul> +<li><code>FROM</code> démarre le Dockerfile. Il est obligatoire que le Dockerfile commence par la commande <code>FROM</code>. Les images sont créées en couches, ce qui signifie que vous pouvez utiliser une autre image comme image de base pour la vôtre. La commande <code>FROM</code> définit votre couche de base. En tant qu'arguments, vous pouvez ajouter le nom de l'image. Facultativement, vous pouvez ajouter le nom d'utilisateur de Docker Cloud du mainteneur et la version de l'image, dans le format <code>nom_utilisateur/nom_image:version</code>.</li> +<li><code>RUN</code> est utilisé pour construire l'image que vous créez. Pour chaque commande <code>RUN</code>, Docker exécute la commande, puis crée une nouvelle couche de l'image. De cette manière, vous pouvez revenir en arrière facilement à des états précédents de votre image. La syntaxe d'une instruction <code>RUN</code> consiste à placer le texte complet de la commande shell après <code>RUN</code> (par exemple, <code>RUN mkdir /user/local/foo</code>). Cela s'exécutera automatiquement dans un shell <code>/bin/sh</code>. Vous pouvez définir un shell différent comme ceci : <code>RUN /bin/bash -c 'mkdir /user/local/foo</code>.</li> +<li><code>COPY</code> copie des fichiers locaux dans le conteneur.</li> +<li><code>CMD</code> définit les commandes qui s'exécuteront sur l'image au démarrage. Contrairement à <code>RUN</code>, cela ne crée pas une nouvelle couche pour l'image, mais exécute simplement la commande. Il ne peut y avoir qu'une seule commande <code>CMD</code> par Dockerfile/Image. Si vous avez besoin d'exécuter plusieurs commandes, la meilleure façon de le faire est de faire en sorte que <code>CMD</code> exécute un script. <code>CMD</code> exige que vous lui disiez où exécuter la commande, contrairement à <code>RUN</code>. Donc, les exemples de commandes <code>CMD</code> seraient :</li> +</ul> +<div class="highlight"><pre><span></span><code><a id="__codelineno-0-1" name="__codelineno-0-1" href="#__codelineno-0-1"></a><span class="w"> </span>CMD<span class="w"> </span><span class="o">[</span><span class="s2">"python"</span>,<span class="w"> </span><span class="s2">"./app.py"</span><span class="o">]</span> +<a id="__codelineno-0-2" name="__codelineno-0-2" href="#__codelineno-0-2"></a><span class="w"> </span>CMD<span class="w"> </span><span class="o">[</span><span class="s2">"/bin/bash"</span>,<span class="w"> </span><span class="s2">"echo"</span>,<span class="w"> </span><span class="s2">"Hello World"</span><span class="o">]</span> +</code></pre></div> +<ul> +<li><code>EXPOSE</code> crée un indice pour les utilisateurs d'une image indiquant les ports qui fournissent des services. Il est inclus dans les informations qui peuvent être récupérées via <code>$ docker inspect <id-conteneur></code>.</li> +</ul> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>La commande <code>EXPOSE</code> ne rend pas réellement les ports accessibles à l'hôte ! Pour cela, vous devez publier les ports en utilisant le drapeau <code>-p</code> lors de l'utilisation de <code>$ docker run</code>.</p> +</div> +<div class="admonition note"> +<p class="admonition-title">Note</p> +<p>Si vous souhaitez en savoir plus sur les Dockerfiles, consultez les <a href="https://docs.docker.com/develop/develop-images/dockerfile_best-practices/" target="_blank">meilleures pratiques pour l'écriture de Dockerfiles</a>.</p> +</div> +<p>(source : <a href="https://github.com/docker/labs/tree/master/beginner">https://github.com/docker/labs/tree/master/beginner</a>)</p> +<h2 id="docker-docker-compose">Docker & docker-compose</h2> </article> @@ -292,26 +414,6 @@ <footer class="md-footer"> - <nav class="md-footer__inner md-grid" aria-label="Footer"> - - - <a href=".." class="md-footer__link md-footer__link--prev" aria-label="Previous: Devops in Action - Guide" rel="prev"> - <div class="md-footer__button md-icon"> - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M20 11v2H8l5.5 5.5-1.42 1.42L4.16 12l7.92-7.92L13.5 5.5 8 11h12Z"/></svg> - </div> - <div class="md-footer__title"> - <div class="md-ellipsis"> - <span class="md-footer__direction"> - Previous - </span> - Devops in Action - Guide - </div> - </div> - </a> - - - </nav> - <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class="md-copyright"> diff --git a/public/index.html b/public/index.html index 8f6b1cb..60569b6 100644 --- a/public/index.html +++ b/public/index.html @@ -13,7 +13,7 @@ - <title>Devops</title> + <title>Docker</title> @@ -63,7 +63,7 @@ <div data-md-component="skip"> - <a href="#devops-in-action-guide" class="md-skip"> + <a href="#docker-in-action-guide" class="md-skip"> Skip to content </a> @@ -77,7 +77,7 @@ <header class="md-header" data-md-component="header"> <nav class="md-header__inner md-grid" aria-label="Header"> - <a href="." title="Devops" class="md-header__button md-logo" aria-label="Devops" data-md-component="logo"> + <a href="." title="Docker" class="md-header__button md-logo" aria-label="Docker" data-md-component="logo"> <img src="assets/logo.png" alt="logo"> @@ -89,13 +89,13 @@ <div class="md-header__ellipsis"> <div class="md-header__topic"> <span class="md-ellipsis"> - Devops + Docker </span> </div> <div class="md-header__topic" data-md-component="header-topic"> <span class="md-ellipsis"> - Devops in Action - Guide + Docker in Action - Guide </span> </div> @@ -155,15 +155,17 @@ - - - <li class="md-tabs__item"> - <a href="." class="md-tabs__link md-tabs__link--active"> - Devops in Action - Guide - </a> - </li> + + + + <li class="md-tabs__item"> + <a href="ch1-getting-start/" class="md-tabs__link"> + TD + </a> + </li> + @@ -171,11 +173,15 @@ - <li class="md-tabs__item"> - <a href="cheatsheet/" class="md-tabs__link"> - Cheatsheet - </a> - </li> + + + + <li class="md-tabs__item"> + <a href="ch1-discover-docker-tp/" class="md-tabs__link"> + TP + </a> + </li> + </ul> @@ -199,12 +205,12 @@ <nav class="md-nav md-nav--primary md-nav--lifted" aria-label="Navigation" data-md-level="0"> <label class="md-nav__title" for="__drawer"> - <a href="." title="Devops" class="md-nav__button md-logo" aria-label="Devops" data-md-component="logo"> + <a href="." title="Docker" class="md-nav__button md-logo" aria-label="Docker" data-md-component="logo"> <img src="assets/logo.png" alt="logo"> </a> - Devops + Docker </label> <ul class="md-nav__list" data-md-scrollfix> @@ -215,21 +221,58 @@ - - - <li class="md-nav__item md-nav__item--active"> + + <li class="md-nav__item md-nav__item--nested"> - <input class="md-nav__toggle md-toggle" data-md-toggle="toc" type="checkbox" id="__toc"> + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_1" type="checkbox" id="__nav_1" > - - <a href="." class="md-nav__link md-nav__link--active"> - Devops in Action - Guide - </a> + <label class="md-nav__link" for="__nav_1"> + TD + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TD" data-md-level="1"> + <label class="md-nav__title" for="__nav_1"> + <span class="md-nav__icon md-icon"></span> + TD + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + + <li class="md-nav__item"> + <a href="ch1-getting-start/" class="md-nav__link"> + Getting Start - Docker + </a> + </li> + + + + + + + + + + <li class="md-nav__item"> + <a href="ch1-discover-docker-td/" class="md-nav__link"> + TD - Docker + </a> + </li> + + + + + </ul> + </nav> </li> @@ -241,13 +284,46 @@ + + <li class="md-nav__item md-nav__item--nested"> + + + <input class="md-nav__toggle md-toggle" data-md-toggle="__nav_2" type="checkbox" id="__nav_2" > + + + + + <label class="md-nav__link" for="__nav_2"> + TP + <span class="md-nav__icon md-icon"></span> + </label> + + <nav class="md-nav" aria-label="TP" data-md-level="1"> + <label class="md-nav__title" for="__nav_2"> + <span class="md-nav__icon md-icon"></span> + TP + </label> + <ul class="md-nav__list" data-md-scrollfix> + + + + + + <li class="md-nav__item"> - <a href="cheatsheet/" class="md-nav__link"> - Cheatsheet + <a href="ch1-discover-docker-tp/" class="md-nav__link"> + TP - Docker </a> </li> + + + </ul> + </nav> + </li> + + </ul> </nav> @@ -281,10 +357,15 @@ -<h1 id="devops-in-action-guide">Devops in Action - Guide</h1> -<p>For each step you have a td to discover the subject and a tp to put it into practice.<br /> -The tps follow each other and the goal is to make you start from a local application and get to an application delivered in production and accessible to all.<br /> -For that we will give you each a server and a java application.</p> +<h1 id="docker-in-action-guide">Docker in Action - Guide</h1> +<p>Pour commencez à découvrir Docker vous aurez un TD pour découvrir le sujet avec des points de cours pour introduire des nouveaux concepts. Ensuite vous aurez un TP pour le mettre en pratique.</p> +<p><strong>Session Docker</strong></p> +<p><a href="ch1-discover-docker-td/">Les TD Docker sont disponibles ici</a></p> +<p><a href="ch1-discover-docker-tp/">Les TP Docker sont disponibles ici</a></p> +<p><a href="https://docs.google.com/presentation/d/1tXws87X_plAKUFQZdC8-jF5M013_KcOIDFrl40W21tk/edit?usp=share_link">Les diapositives Docker sont disponibles ici</a></p> +<p><strong>Veuillez lire attentivement les indications !</strong></p> +<p align="center">© Takima 2023</p> +<p>"</p> </article> @@ -295,26 +376,6 @@ For that we will give you each a server and a java application.</p> <footer class="md-footer"> - <nav class="md-footer__inner md-grid" aria-label="Footer"> - - - - <a href="cheatsheet/" class="md-footer__link md-footer__link--next" aria-label="Next: Cheatsheet" rel="next"> - <div class="md-footer__title"> - <div class="md-ellipsis"> - <span class="md-footer__direction"> - Next - </span> - Cheatsheet - </div> - </div> - <div class="md-footer__button md-icon"> - <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11H4Z"/></svg> - </div> - </a> - - </nav> - <div class="md-footer-meta md-typeset"> <div class="md-footer-meta__inner md-grid"> <div class="md-copyright"> diff --git a/public/search/search_index.json b/public/search/search_index.json index 341d80e..8960c16 100644 --- a/public/search/search_index.json +++ b/public/search/search_index.json @@ -1 +1 @@ -{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"Devops in Action - Guide For each step you have a td to discover the subject and a tp to put it into practice. The tps follow each other and the goal is to make you start from a local application and get to an application delivered in production and accessible to all. For that we will give you each a server and a java application.","title":"Devops in Action - Guide"},{"location":"#devops-in-action-guide","text":"For each step you have a td to discover the subject and a tp to put it into practice. The tps follow each other and the goal is to make you start from a local application and get to an application delivered in production and accessible to all. For that we will give you each a server and a java application.","title":"Devops in Action - Guide"},{"location":"cheatsheet/","text":"Cheatsheet","title":"Cheatsheet"},{"location":"cheatsheet/#cheatsheet","text":"","title":"Cheatsheet"}]} \ No newline at end of file +{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"Docker in Action - Guide Pour commencez \u00e0 d\u00e9couvrir Docker vous aurez un TD pour d\u00e9couvrir le sujet avec des points de cours pour introduire des nouveaux concepts. Ensuite vous aurez un TP pour le mettre en pratique. Session Docker Les TD Docker sont disponibles ici Les TP Docker sont disponibles ici Les diapositives Docker sont disponibles ici Veuillez lire attentivement les indications ! \u00a9 Takima 2023 \"","title":"Docker in Action - Guide"},{"location":"#docker-in-action-guide","text":"Pour commencez \u00e0 d\u00e9couvrir Docker vous aurez un TD pour d\u00e9couvrir le sujet avec des points de cours pour introduire des nouveaux concepts. Ensuite vous aurez un TP pour le mettre en pratique. Session Docker Les TD Docker sont disponibles ici Les TP Docker sont disponibles ici Les diapositives Docker sont disponibles ici Veuillez lire attentivement les indications ! \u00a9 Takima 2023 \"","title":"Docker in Action - Guide"},{"location":"ch1-discover-docker-td/","text":"D\u00e9couverte de Docker Configuration Pr\u00e9requis Il n'est pas n\u00e9cessaire d'avoir des comp\u00e9tences sp\u00e9cifiques pour ce tutoriel, si ce n'est une certaine familiarit\u00e9 avec les command line et l'utilisation d'un \u00e9diteur de texte. Une exp\u00e9rience ant\u00e9rieure dans le d\u00e9veloppement d'applications web sera utile, mais n'est pas n\u00e9cessaire. Configuration de votre ordinateur Comme dit pr\u00e9c\u00e9demment il vous faudra avoir un outil docker fonctionnel. Pour v\u00e9rifier que tout focntionne testons une commande docker : docker run registry.takima.io/school/proxy/hello-world Unable to find image 'registry.takima.io/school/proxy/hello-world:latest' locally latest: Pulling from registry.takima.io/school/proxy/hello-world 03f4658f8b78: Pull complete a3ed95caeb02: Pull complete Digest: sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7 Status: Downloaded newer image for registry.takima.io/school/proxy/hello-world:latest Hello from Docker. ... Ce message indique que votre installation semble fonctionner correctement. Si ce n'est pas le cas parcourez le Getting Start pour installer correctement docker Ex\u00e9cution de votre premier conteneur Maintenant que vous avez tout configur\u00e9, il est temps de mettre les mains dans le cambouis. Dans cette section, vous allez ex\u00e9cuter un conteneur Alpine Linux (une distribution Linux l\u00e9g\u00e8re) sur votre syst\u00e8me et d\u00e9couvrir la commande docker run . Pour commencer, ex\u00e9cutez la commande suivante dans votre terminal pour r\u00e9cuperer (t\u00e9l\u00e9charger) l'image depuis le registry takima : $ docker pull registry.takima.io/school/proxy/alpine Puis on va tag cette image pour cr\u00e9er une copie et lui donner un nom plus commode \u00e0 utiliser pour nos prochaines lignes de commandes : $ docker tag registry.takima.io/school/proxy/alpine alpine Note En fonction de la mani\u00e8re dont vous avez install\u00e9 Docker sur votre syst\u00e8me, vous pourriez voir une erreur de permission refus\u00e9e apr\u00e8s avoir ex\u00e9cut\u00e9 la commande ci-dessus. Essayez les commandes du tutoriel de d\u00e9marrage pour v\u00e9rifier votre installation . Si vous \u00eates sous Linux, vous devrez peut-\u00eatre pr\u00e9fixer vos commandes docker avec sudo . Vous pouvez \u00e9galement cr\u00e9er un groupe Docker pour r\u00e9soudre ce probl\u00e8me. La commande pull r\u00e9cup\u00e8re l'image Alpine depuis notre registre Docker et la sauvegarde sur notre syst\u00e8me. Vous pouvez utiliser la commande docker images pour voir la liste de toutes les images sur votre syst\u00e8me. Nous aurions pu aussi r\u00e9cup\u00e9rer une image Docker publique (Docker Hub) qui contient une multitude d'images standard en utilisant : $ docker pull alpine Cependant Docker Hub est soumis \u00e0 un rate limit donc nous allons \u00e9viter de pull dessus dans cette formation et utiliser le registry Takima. $ docker images R\u00c9PERTOIRE TAG ID DE L ' IMAGE CR\u00c9\u00c9 TAILLE VIRTUELLE alpine derni\u00e8re c51f86c28340 il y a 4 semaines 1 ,109 Mo registry.takima.io/school/proxy/alpine derni\u00e8re c51f86c28340 il y a 4 semaines 1 ,109 Mo registry.takima.io/school/proxy/hello-world derni\u00e8re 690ed74de00f il y a 5 mois 960 o Ex\u00e9cuter un Docker G\u00e9nial ! Ex\u00e9cutons maintenant un conteneur Docker bas\u00e9 sur cette image. Pour cela, nous allons utiliser la commande docker run . $ docker run alpine ls -l total 48 drwxr-xr-x 2 root root 4096 2 mars 16 :20 bin drwxr-xr-x 5 root root 360 18 mars 09 :47 dev drwxr-xr-x 13 root root 4096 18 mars 09 :47 etc drwxr-xr-x 2 root root 4096 2 mars 16 :20 home drwxr-xr-x 5 root root 4096 2 mars 16 :20 lib ... ... Qu'est-il arriv\u00e9 ? En coulisses, il s'est pass\u00e9 beaucoup de choses. Lorsque vous appelez run : Le client Docker contacte le daemon Docker. Le daemon Docker v\u00e9rifie si l'image (dans ce cas, Alpine) est disponible localement, et si ce n'est pas le cas, la t\u00e9l\u00e9charge depuis le registry Docker. (Comme nous avons ex\u00e9cut\u00e9 docker pull registry.takima.io/school/proxy/alpine auparavant puis tag cette image en la nommant alpine , l'\u00e9tape de t\u00e9l\u00e9chargement n'est pas n\u00e9cessaire) Le daemon Docker cr\u00e9e le conteneur, puis ex\u00e9cute une commande dans ce conteneur. Le daemon Docker transmet la sortie de la commande au client Docker. Lorsque vous ex\u00e9cutez docker run alpine , vous avez fourni une commande ( ls -l ), alors Docker a d\u00e9marr\u00e9 la commande sp\u00e9cifi\u00e9e et vous avez vu la liste. Essayons quelque chose de plus utile : afficher un Hello World bien sur! $ docker run alpine echo \"hello from alpine\" hello from alpine OK, voil\u00e0 une sortie r\u00e9elle. Dans ce cas, le client Docker a ex\u00e9cut\u00e9 fid\u00e8lement la commande echo dans notre conteneur Alpine, puis l'a quitt\u00e9. Si vous avez remarqu\u00e9, tout cela s'est produit tr\u00e8s rapidement. Imaginez d\u00e9marrer une machine virtuelle, ex\u00e9cuter une commande, puis la d\u00e9truire. Maintenant, vous savez pourquoi on dit que les conteneurs sont rapides ! Essayons une autre commande. $ docker run alpine /bin/sh Attendez, il ne s'est rien pass\u00e9 ! Est-ce un bogue ? Eh bien, non. Ces ex\u00e9cutions interactives se ferment apr\u00e8s avoir ex\u00e9cut\u00e9 les commandes script\u00e9es, sauf si elles sont ex\u00e9cut\u00e9es dans un terminal interactif. Pour \u00e9viter que cet exemple ne se ferme, vous devez utiliser docker run -it alpine /bin/sh . Vous \u00eates maintenant \u00e0 l'int\u00e9rieur du shell du conteneur et vous pouvez naviguer dans le conteneur. Si vous lancez exit ou que vous tapez CTRL-D , vous quitterez le shell du conteneur et il s'arr\u00eatera \u00e9galement (attention il ne se d\u00e9truira pas). Le conteneur est cens\u00e9 \u00eatre \u00e9ph\u00e9m\u00e8re. $ exit Maintenant, voyons comment lister les conteneurs avec la commande docker ps . docker ps est la commande qui listera tous les conteneurs en cours d'ex\u00e9cution. $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES Comme vous n'avez pas de conteneur qui tourne actuelement (seulement des conteneurs qui ont \u00e9t\u00e9 lanc\u00e9s mais qui sont maintenant stopp\u00e9s), cette commande renvoie une liste vide. Essayons une petite variante : docker ps -a $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 36171a5da744 alpine \"/bin/sh\" 5 minutes ago Exited ( 0 ) 2 minutes ago fervent_newton a6a9d46d0b2f alpine \"echo 'hello from alp\" 6 minutes ago Exited ( 0 ) 6 minutes ago lonely_kilby ff0a5c3750b9 alpine \"ls -l\" 8 minutes ago Exited ( 0 ) 8 minutes ago elated_ramanujan c317d0a9e3d2 hello-world \"/hello\" 34 seconds ago Exited ( 0 ) 12 minutes ago stupefied_mcclintock Vous voyez maintenant une liste de tous les conteneurs que vous avez ex\u00e9cut\u00e9s. Remarquez que la colonne \"STATUS\" indique que ces conteneurs ont \u00e9t\u00e9 arr\u00eat\u00e9s il y a quelques minutes. Vous vous demandez probablement s'il y a un moyen d'ex\u00e9cuter plus d'une commande dans un conteneur. Essayons cela maintenant : $ docker run -it alpine /bin/sh / # ls bin dev etc home lib linuxrc media mnt proc root run sbin sys tmp usr var / # uname -a Linux 97916e8cb5dc 4 .4.27-moby #1 SMP Wed Oct 26 14:01:48 UTC 2016 x86_64 Linux L'ex\u00e9cution de la commande run avec l'option -it nous connecte \u00e0 un terminal interactif dans le conteneur. Maintenant, vous pouvez ex\u00e9cuter autant de commandes que vous le souhaitez dans le conteneur. Prenez le temps d'ex\u00e9cuter vos commandes pr\u00e9f\u00e9r\u00e9es. Astuce run -it est une commande tr\u00e8s utile pour d\u00e9boguer le bas niveau d'un conteneur. Vous savez faire tourner un container et le lancer en mode interactif. Mais il manque une fonctionnalit\u00e9 essentielle lorsque l'on veut faire tourner un service. Pour l'illustrer lancer un nginx : $ docker run registry.takima.io/school/proxy/nginx Question Que se passe-t-il dans ce cas ? Que se passe-t-il si vous fermez votre shell ou ctr+c par exemple (fermez votre shell relancez le et lancez un docker ps -a ) En fait votre conteneur s'est lanc\u00e9 et ne vous rend pas la main et vous avez alors le log nginx sur votre shell. Lorsque vous quittez votre shell, le conteneur se stoppe et votre nginx se stoppe donc \u00e9galement. Ce n'est pas un comportement souhaitable lorsque l'on veut lancer des services en tache de fond, c'est ce qu'on apelle un daemon. Docker nous permet de le faire avec une simple option -d relancez votre conteneur Nginx en mode service $ docker run -d --name mon-nginx registry.takima.io/school/proxy/nginx b611eea1536bcfc79f87e1bfd57a3f10bbe4577f03e405329f5626cd66a64e54 Maintenant lancez un docker ps . Vous verrez votre docker nginx tourner Voil\u00e0 docker run n'a plus de secret pour vous, et c'est parfait car c'est probablement la commande que vous utiliserez le plus fr\u00e9quemment. Il est judicieux de prendre le temps de vous familiariser avec elle. Pour en savoir plus sur run , utilisez docker run --help pour voir la liste de tous les indicateurs qu'elle prend en charge. Au fur et \u00e0 mesure de votre progression, vous verrez quelques variantes de docker run . Pour la suite taguez votre image nginx : $ docker tag registry.takima.io/school/proxy/nginx nginx A tout moment vous pouvez inspecter un container avec docker container inspect , par exemple pour r\u00e9cup\u00e9rer son status : # get the state of a container docker container inspect \\ --format '{{.State.Status}}' \\ mon-nginx Success Point cours pour voir les Network Docker Les Network Docker Faire tourner un applicatif c'est bien mais lorsque l'on fait du web c'est essentiel de pouvoir publier son application et que cette application b\u00e9n\u00e9ficie d'une connectivit\u00e9 (IP), mais c'est aussi essentiel d'isoler les conteneurs dans des r\u00e9seaux s\u00e9par\u00e9s. Apr\u00e8s tout c'est aussi l'une des promesses de docker l'isolation. Objectif: Comprendre les concepts de base des r\u00e9seaux Docker. Cr\u00e9er et g\u00e9rer des r\u00e9seaux Docker. Connecter des conteneurs \u00e0 des r\u00e9seaux personnalis\u00e9s. V\u00e9rifier les r\u00e9seaux Docker existants Pour lister les reseaux actuels : $ docker network ls NETWORK ID NAME DRIVER SCOPE df8d9a03e3e1 bridge bridge local f0b4def22265 host host local a7cd984404b5 none null local Ce sont les r\u00e9seaux par d\u00e9fault. On peut cr\u00e9er autant de nouveau r\u00e9seaux isol\u00e9s que l'on souhaite et lorsque l'on cr\u00e9\u00e9 un reseau il sera de type bridge par d\u00e9faut Lorsque l'on cr\u00e9er un conteneur il se trouve attach\u00e9 par d\u00e9faut au r\u00e9seau nomm\u00e9 bridge . On d\u00e9conseille donc de l'utilser pour faire tourner nos applications. Cr\u00e9er un r\u00e9seau personnalis\u00e9 Cr\u00e9ez un r\u00e9seau Docker personnalis\u00e9 en utilisant la commande docker network create . Par exemple, cr\u00e9ez un r\u00e9seau nomm\u00e9 \"app\" : docker network create app Vous pouvez v\u00e9rifier que le r\u00e9seau a \u00e9t\u00e9 cr\u00e9\u00e9 en utilisant la commande docker network ls . Ex\u00e9cuter des conteneurs connect\u00e9s au r\u00e9seau personnalis\u00e9 Cr\u00e9ez deux conteneurs connect\u00e9s au r\u00e9seau \"app\". Par exemple, nous pouvons cr\u00e9er un conteneur web et un conteneur de base de donn\u00e9es : docker run -d --name webapp --network app nginx Le conteneur webapp est maintenant connect\u00e9 au r\u00e9seau `app`` V\u00e9rifier la connectivit\u00e9 Vous pouvez maintenant v\u00e9rifier si un conteneur pr\u00e9sent sur ce r\u00e9seau app peut communiquer avec la webapp. Ex\u00e9cutez un conteneur de test connect\u00e9 au m\u00eame r\u00e9seau : docker run -it --rm --network app registry.takima.io/school/proxy/busybox sh \u00c0 l'int\u00e9rieur de ce conteneur de test, essayez de \"pinguer\" le conteneur webapp en utilisant leurs noms de conteneurs comme h\u00f4tes. Assurez-vous que la connectivit\u00e9 fonctionne avec un wget sur le port 80 par exemple . $ wget webapp Connecting to webapp ( 172 .19.0.2:80 ) saving to 'index.html' index.html 100 % | ********************************************************************************************** | 615 0 :00:00 ETA 'index.html' saved $ cat index.html Supprimer le r\u00e9seau Pour supprimer le r\u00e9seau personnalis\u00e9 que vous avez cr\u00e9\u00e9, utilisez la commande suivante : docker network rm app Notes Si un r\u00e9seau est utilis\u00e9, impossible de le d\u00e9truire. Success Point cours sur la publication des conteneurs La publication Nous avons isol\u00e9 les conteneurs dans un r\u00e9seau et c'est parfait pour la s\u00e9curit\u00e9. Mais certains services sont plus utiles lorsqu'ils sont acc\u00e8ssibles. Nous allons donc voir comment publier un service sur le Host qui l'h\u00e9berge. Objectif : Comprendre comment publier des ports de conteneurs Docker. Tester l'accessibilit\u00e9 des applications via les ports publi\u00e9s. V\u00e9rification des ports existants Avant de commencer, listez les conteneurs actuellement en cours d'ex\u00e9cution sur votre syst\u00e8me \u00e0 l'aide de la commande suivante : docker ps Cette commande affichera les conteneurs actifs, mais notez qu'aucun port n'est actuellement publi\u00e9 pour eux. Cr\u00e9er un conteneur avec des ports publi\u00e9s Cr\u00e9ez un conteneur avec un serveur web Nginx et publiez son port 80 sur un port de votre machine h\u00f4te, par exemple, le port 8080. Utilisez la commande suivante : docker run -d -p 8080 :80 --name monsite registry.takima.io/school/proxy/nginx Cela d\u00e9marrera un conteneur Nginx avec son port 80 publi\u00e9 sur le port 8080 de votre machine h\u00f4te. Acc\u00e8s \u00e0 l'application via le port publi\u00e9 Ouvrez un navigateur web et acc\u00e9dez \u00e0 http://localhost:8080 . Vous devriez voir la page d'accueil Nginx, ce qui signifie que le serveur web du conteneur est accessible via le port publi\u00e9. Gestion des ports publi\u00e9s Pour afficher les ports publi\u00e9s par un conteneur en cours d'ex\u00e9cution, utilisez la commande docker port . Par exemple : docker port monsite Arr\u00eater et supprimer les conteneurs Lorsque vous avez termin\u00e9, arr\u00eatez et supprimez les conteneurs que vous avez cr\u00e9\u00e9s : docker stop monsite docker rm monsite Tip rm -f est utile pour aller plus vite. Success Point cours sur les Volumes Docker Les volumes Docker placez vous dans un dossier /td-volumes Cr\u00e9ation d'un Conteneur avec un Volume Nous allons ici utiliser un bind volume : Il s'agit d'un volume mont\u00e9 depuis un dossier du host (celui qui lance les dockers) Cr\u00e9ez un r\u00e9pertoire vide sur votre syst\u00e8me h\u00f4te pour servir de volume. Par exemple : mkdir mon_volume Cr\u00e9ez un conteneur en utilisant la commande docker run avec un volume mont\u00e9 \u00e0 partir du r\u00e9pertoire que vous venez de cr\u00e9er. Assurez-vous de remplacer chemin_vers_votre_volume par le chemin absolu du r\u00e9pertoire que vous avez cr\u00e9\u00e9 \u00e0 l'\u00e9tape pr\u00e9c\u00e9dente : !!! tip Si vous \u00eates bien dans le dossier utilis\u00e9 la variables de dossier courrant $(pwd) \"$(pwd)\"/mon_volume:/containerDir docker run -d -v \" $( pwd ) \" /mon_volume:/data --name mon-conteneur nginx V\u00e9rifiez que le conteneur est en cours d'ex\u00e9cution en utilisant docker ps . Utilisation du Volume depuis le Conteneur Ex\u00e9cutez un shell interactif dans le conteneur que vous venez de cr\u00e9er : docker exec -it mon-conteneur /bin/bash \u00c0 l'int\u00e9rieur du conteneur, cr\u00e9ez un fichier ou un r\u00e9pertoire dans le r\u00e9pertoire mont\u00e9 : touch /data/mon_fichier.txt Quittez le shell du conteneur en utilisant exit . V\u00e9rification de la Persistance des Donn\u00e9es Arr\u00eatez et supprimez le conteneur : docker stop mon-conteneur docker rm mon-conteneur V\u00e9rifiez que le r\u00e9pertoire et le fichier que vous avez cr\u00e9\u00e9s sont toujours pr\u00e9sents dans le r\u00e9pertoire du volume sur votre syst\u00e8me h\u00f4te : ls mon_volume Cr\u00e9ation d'un Deuxi\u00e8me Conteneur avec le M\u00eame Volume Cr\u00e9ez un deuxi\u00e8me conteneur en utilisant le m\u00eame volume que le premier : docker run -d -v \" $( pwd ) \" /mon_volume:/data --name mon-conteneur-bis nginx Ex\u00e9cutez un shell interactif dans le deuxi\u00e8me conteneur : docker exec -it mon-conteneur-bis /bin/bash V\u00e9rifiez que le fichier ou le r\u00e9pertoire que vous avez cr\u00e9\u00e9 est pr\u00e9sent dans le deuxi\u00e8me conteneur, m\u00eame s'il s'agit d'un conteneur diff\u00e9rent. Quittez le shell du deuxi\u00e8me conteneur en utilisant exit . clean Supprimez le deuxi\u00e8me conteneur : docker stop mon-conteneur-bis docker rm mon-conteneur-bis Supprimez le r\u00e9pertoire du volume sur votre syst\u00e8me h\u00f4te : rm -rf mon_volume Success Point cours sur les Images Docker Images Docker Dans cette section, plongeons plus en profondeur dans ce que sont les images Docker. Vous allez construire votre propre image, utiliser cette image pour ex\u00e9cuter une application localement, et enfin, pousser certaines de vos propres images vers Docker Hub. Les images Docker sont la base des conteneurs. Dans l'exemple pr\u00e9c\u00e9dent, vous avez pull une image nginx depuis le registre et avez demand\u00e9 au client Docker d'ex\u00e9cuter un conteneur bas\u00e9 sur cette image. Pour voir la liste des images disponibles localement sur votre syst\u00e8me, ex\u00e9cutez la commande docker images . $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE dockersamples/static-site latest 92a386b6e686 2 hours ago 190 .5 MB nginx latest af4b3d7d5401 3 hours ago 190 .5 MB Vous aurez une liste diff\u00e9rente d'images sur votre machine. Le TAG fait r\u00e9f\u00e9rence \u00e0 une version de l'image et l' image ID est l'identifiant unique correspondant de cette image. Pour simplifier, vous pouvez consid\u00e9rer une image comme un r\u00e9f\u00e9rentiel Git : les images peuvent \u00eatre commit\u00e9es avec des modifications et avoir plusieurs versions. Lorsque vous ne fournissez pas de num\u00e9ro de version sp\u00e9cifique, le client par d\u00e9faut est \"latest\" (derni\u00e8re version). Par exemple, vous pourriez extraire une version sp\u00e9cifique de l'image ubuntu comme suit : $ docker pull registry.takima.io/school/proxy/ubuntu:12.04 Si vous ne sp\u00e9cifiez pas le num\u00e9ro de version de l'image, le client Docker utilisera, comme mentionn\u00e9 pr\u00e9c\u00e9demment, une version nomm\u00e9e \"latest\" (derni\u00e8re version). Donc, par exemple, la commande docker pull ci-dessous extraira une image nomm\u00e9e registry.takima.io/school/proxy/ubuntu:latest : $ docker pull registry.takima.io/school/proxy/ubuntu Pour obtenir une nouvelle image Docker, vous pouvez soit l'obtenir depuis un registre (tel que le Docker Hub ou bien un registre personnel comme nous sommes en train de faire) soit la cr\u00e9er vous-m\u00eame. Il existe des centaines de milliers d'images disponibles sur le Docker Hub . Vous pouvez \u00e9galement rechercher des images directement depuis la ligne de commande \u00e0 l'aide de docker search . Une distinction importante concernant les images se situe entre les images de base (base images) et les images enfants (child images). Les images de base sont des images qui n'ont pas d'images parent, g\u00e9n\u00e9ralement des images avec un syst\u00e8me d'exploitation tel qu'Ubuntu, Alpine ou Debian. Les images enfants sont des images qui se construisent sur des images de base et ajoutent des fonctionnalit\u00e9s suppl\u00e9mentaires. Un autre concept cl\u00e9 est l'id\u00e9e d'images officielles et d'images utilisateur (user images). (Les deux peuvent \u00eatre des images de base ou des images enfants.) Les images officielles sont des images v\u00e9rifi\u00e9es par Docker. Docker, Inc. sponsorise une \u00e9quipe d\u00e9di\u00e9e responsable de la r\u00e9vision et de la publication de tout le contenu des r\u00e9f\u00e9rentiels officiels. Cette \u00e9quipe travaille en collaboration avec les mainteneurs de logiciels, des experts en s\u00e9curit\u00e9 et la communaut\u00e9 Docker \u00e9largie. Elles ne sont pas pr\u00e9fix\u00e9es par un nom d'organisation ou d'utilisateur. Dans la liste des images ci-dessus, les images python , node , alpine et nginx sont des images officielles (de base). Pour en savoir plus \u00e0 leur sujet, consultez la documentation sur les images officielles . Les images utilisateur sont des images cr\u00e9\u00e9es et partag\u00e9es par des utilisateurs comme vous. Elles se construisent sur des images de base et ajoutent des fonctionnalit\u00e9s suppl\u00e9mentaires. Typiquement, elles sont format\u00e9es comme utilisateur/nom-de-l'image . La valeur utilisateur dans le nom de l'image correspond \u00e0 votre nom d'utilisateur ou d'organisation Docker Hub. Cr\u00e9ez votre premi\u00e8re image Maintenant que vous avez une meilleure compr\u00e9hension des images, il est temps de cr\u00e9er la v\u00f4tre. Notre principal objectif ici est de cr\u00e9er une image qui isole une petite application Flask . L'objectif de cet exercice est de cr\u00e9er une image Docker qui ex\u00e9cutera une application Flask. Nous allons commencer par rassembler les composants pour un g\u00e9n\u00e9rateur al\u00e9atoire d'images de chats construit avec Python Flask, puis dockeriser cela en \u00e9crivant un Dockerfile . Enfin, nous allons construire l'image, puis l'ex\u00e9cuter. Cr\u00e9ez une application Python Flask qui affiche des images de chats al\u00e9atoires. Dans le cadre de cet atelier, nous avons cr\u00e9\u00e9 une petite application Python Flask amusante qui affiche une image de chat al\u00e9atoire au chargement - car, vous savez, qui n'aime pas les chats ? Commencez par cr\u00e9er un r\u00e9pertoire appel\u00e9 flask-app o\u00f9 nous allons cr\u00e9er les fichiers suivants : app.py requirements.txt templates/index.html Dockerfile Assurez-vous de vous d\u00e9placer vers cd flask-app avant de commencer \u00e0 cr\u00e9er les fichiers, car vous ne voulez pas ajouter tout un tas d'autres fichiers \u00e0 votre image. app.py Cr\u00e9er le fichier python app.py avec ce contenu: from flask import Flask , render_template import random app = Flask ( __name__ ) # liste des images images = [ \"https://c.tenor.com/GTcT7HODLRgAAAAM/smiling-cat-creepy-cat.gif\" , \"https://media0.giphy.com/media/10dU7AN7xsi1I4/giphy.webp?cid=ecf05e47gk63rd81vzlot57qmebr7drtgf6a3khmzvjsdtu7&rid=giphy.webp&ct=g\" , \"https://media0.giphy.com/media/S6VGjvmFRu5Qk/giphy.webp?cid=ecf05e478yofpawrhffnnvb3sgjkos96vyfo5mtqhds35as6&rid=giphy.webp&ct=g\" , \"https://media3.giphy.com/media/JIX9t2j0ZTN9S/200w.webp?cid=ecf05e47gk63rd81vzlot57qmebr7drtgf6a3khmzvjsdtu7&rid=200w.webp&ct=g\" ] @app . route ( '/' ) def index (): url = random . choice ( images ) return render_template ( 'index.html' , url = url ) if __name__ == \"__main__\" : app . run ( host = \"0.0.0.0\" ) requirements.txt Ce fichier requirements.txt r\u00e9f\u00e9rence les modules Python necessaire pour lancer notre app Python Flask==0.10.1 templates/index.html Cr\u00e9ez le dossier templates et cr\u00e9ez dans ce dossier le fichier index.html avec le contenu suivant: < html > < head > < style type = \"text/css\" > body { background : black ; color : white ; } div . container { max-width : 500 px ; margin : 100 px auto ; border : 20 px solid white ; padding : 10 px ; text-align : center ; } h4 { text-transform : uppercase ; } </ style > </ head > < body > < div class = \"container\" > < h4 > Cat Gif of the day </ h4 > < img src = \"{{url}}\" /> < p > < small > Courtesy: < a href = \"http://www.buzzfeed.com/copyranter/the-best-cat-gif-post-in-the-history-of-cat-gifs\" > Buzzfeed </ a ></ small > </ p > </ div > </ body > </ html > R\u00e9digez un Dockerfile Dernier fichier et pas des moindre le Dockerfile. Nous voulons cr\u00e9er une image Docker avec cette application web. Comme mentionn\u00e9 pr\u00e9c\u00e9demment, toutes les images utilisateur sont bas\u00e9es sur une image de base. \u00c9tant donn\u00e9 que notre application est \u00e9crite en Python, nous allons cr\u00e9er notre propre image embarquant notre app Python bas\u00e9e sur Alpine . Nous allons le faire en utilisant un Dockerfile. Tip L'image Alpine est souvent utilis\u00e9 comme image de base car elle est tr\u00e8s l\u00e9g\u00e8re Un Dockerfile est un fichier texte qui contient une liste de commandes que le daemon Docker appelle lors de la cr\u00e9ation d'une image. Le Dockerfile contient toutes les informations dont Docker a besoin pour ex\u00e9cuter l'application : une image Docker de base qui servira de socle , l'emplacement de votre code de projet, ses d\u00e9pendances \u00e9ventuelles, et les commandes \u00e0 ex\u00e9cuter au d\u00e9marrage. C'est un moyen simple d'automatiser le processus de cr\u00e9ation d'images. Cr\u00e9ez un fichier appel\u00e9 Dockerfile et ajoutez-y le contenu comme d\u00e9crit ci-dessous. Nous allons commencer par sp\u00e9cifier notre image de base en utilisant le mot cl\u00e9 FROM : FROM registry.takima.io/school/proxy/alpine:3.6 La prochaine \u00e9tape consiste g\u00e9n\u00e9ralement \u00e0 \u00e9crire les commandes de copie des fichiers et \u00e0 installer les d\u00e9pendances n\u00e9cessaire pour notre application. Mais d'abord, nous allons installer le package Python pip dans la distribution Linux Alpine. Cela n'installera pas seulement le package pip, mais aussi d'autres d\u00e9pendances, y compris l'interpr\u00e9teur Python. Ajoutez la commande RUN suivante : RUN apk add --update py2-pip Ajoutons les fichiers qui composent l'application Flask. Installez toutes les d\u00e9pendances Python n\u00e9cessaires pour le bon fonctionnement de notre application. Cela sera accompli en ajoutant les lignes suivantes : COPY requirements.txt /usr/src/app/ RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt Copiez les fichiers que vous avez cr\u00e9\u00e9s pr\u00e9c\u00e9demment dans notre image en utilisant la commande COPY . COPY app.py /usr/src/app/ COPY templates/index.html /usr/src/app/templates/ Sp\u00e9cifiez le num\u00e9ro de port qui doit \u00eatre expos\u00e9. Comme notre application Flask s'ex\u00e9cute sur le port 5000 , c'est ce que nous allons exposer. EXPOSE 5000 La derni\u00e8re \u00e9tape est de d\u00e9finir la commande pour lancer l'application : python ./app.py . Utilisez la commande ENTRYPOINT pour cela : ENTRYPOINT [ \"python\" , \"/usr/src/app/app.py\" ] Le but principal de ENTRYPOINT est d'indiquer au conteneur la commande qu'il doit ex\u00e9cuter par d\u00e9faut lors de son d\u00e9marrage. Tip En v\u00e9rit\u00e9 ENTRYPOINT servira \u00e0 d\u00e9finir l'executable et le CMD servira \u00e0 d\u00e9finir les param\u00e8tres de l'executable V\u00e9rifiez votre Dockerfile. Notre Dockerfile est maintenant pr\u00eat. Voici \u00e0 quoi il ressemble : # notre image de base FROM registry.takima.io/school/proxy/alpine:3.6 # Installer Python et pip RUN apk add --update py2-pip # Installer les modules Python n\u00e9cessaires par l'application Python COPY requirements.txt /usr/src/app/ RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt # Copier les fichiers n\u00e9cessaires pour l'ex\u00e9cution de l'application COPY app.py /usr/src/app/ COPY templates/index.html /usr/src/app/templates/ # Indiquer le num\u00e9ro de port que le conteneur doit exposer EXPOSE 5000 # Ex\u00e9cuter l'application ENTRYPOINT [ \"python\" , \"/usr/src/app/app.py\" ] Builder notre image Maintenant que notre Dockerfile est pr\u00eat, il est temps de builder notre application. La commande docker build s'occupe du travail de cr\u00e9er une image Docker \u00e0 partir du Dockerfile. Lorsque vous ex\u00e9cutez la commande docker build , assurez-vous de remplacer <YOUR_USERNAME> par votre nom d'utilisateur. Ce nom d'utilisateur devrait \u00eatre le m\u00eame que celui que vous avez cr\u00e9\u00e9 lors de votre inscription sur Docker Hub . Si vous ne l'avez pas encore fait mettez le nom que vous souhaitez pour votre tag. La commande docker build est assez simple. Elle prend un nom de balise ou tag facultatif avec l'option -t et l'emplacement du r\u00e9pertoire contenant le Dockerfile. Le point . indique le r\u00e9pertoire actuel : $ docker build -t <YOUR_USERNAME>/myfirstapp . Sending build context to Docker daemon 9 .728 kB Step 1 : FROM alpine:latest ---> 0d81fc72e790 Step 2 : RUN apk add --update py-pip ---> Running in 8abd4091b5f5 fetch http://dl-4.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz fetch http://dl-4.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz ( 1 /12 ) Installing libbz2 ( 1 .0.6-r4 ) ( 2 /12 ) Installing expat ( 2 .1.0-r2 ) ( 3 /12 ) Installing libffi ( 3 .2.1-r2 ) ( 4 /12 ) Installing gdbm ( 1 .11-r1 ) ( 5 /12 ) Installing ncurses-terminfo-base ( 6 .0-r6 ) ( 6 /12 ) Installing ncurses-terminfo ( 6 .0-r6 ) ( 7 /12 ) Installing ncurses-libs ( 6 .0-r6 ) ( 8 /12 ) Installing readline ( 6 .3.008-r4 ) ( 9 /12 ) Installing sqlite-libs ( 3 .9.2-r0 ) ( 10 /12 ) Installing python ( 2 .7.11-r3 ) ( 11 /12 ) Installing py-setuptools ( 18 .8-r0 ) ( 12 /12 ) Installing py-pip ( 7 .1.2-r0 ) Executing busybox-1.24.1-r7.trigger OK: 59 MiB in 23 packages ---> 976a232ac4ad Removing intermediate container 8abd4091b5f5 Step 3 : COPY requirements.txt /usr/src/app/ ---> 65b4be05340c Removing intermediate container 29ef53b58e0f Step 4 : RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt ---> Running in a1f26ded28e7 Collecting Flask == 0 .10.1 ( from -r /usr/src/app/requirements.txt ( line 1 )) Downloading Flask-0.10.1.tar.gz ( 544kB ) Collecting Werkzeug> = 0 .7 ( from Flask == 0 .10.1->-r /usr/src/app/requirements.txt ( line 1 )) Downloading Werkzeug-0.11.4-py2.py3-none-any.whl ( 305kB ) Collecting Jinja2> = 2 .4 ( from Flask == 0 .10.1->-r /usr/src/app/requirements.txt ( line 1 )) Downloading Jinja2-2.8-py2.py3-none-any.whl ( 263kB ) Collecting itsdangerous> = 0 .21 ( from Flask == 0 .10.1->-r /usr/src/app/requirements.txt ( line 1 )) Downloading itsdangerous-0.24.tar.gz ( 46kB ) Collecting MarkupSafe ( from Jinja2> = 2 .4->Flask == 0 .10.1->-r /usr/src/app/requirements.txt ( line 1 )) Downloading MarkupSafe-0.23.tar.gz Installing collected packages: Werkzeug, MarkupSafe, Jinja2, itsdangerous, Flask Running setup.py install for MarkupSafe Running setup.py install for itsdangerous Running setup.py install for Flask Successfully installed Flask-0.10.1 Jinja2-2.8 MarkupSafe-0.23 Werkzeug-0.11.4 itsdangerous-0.24 You are using pip version 7 .1.2, however version 8 .1.1 is available. You should consider upgrading via the 'pip install --upgrade pip' command. ---> 8de73b0730c2 Removing intermediate container a1f26ded28e7 Step 5 : COPY app.py /usr/src/app/ ---> 6a3436fca83e Removing intermediate container d51b81a8b698 Step 6 : COPY templates/index.html /usr/src/app/templates/ ---> 8098386bee99 Removing intermediate container b783d7646f83 Step 7 : EXPOSE 5000 ---> Running in 31401b7dea40 ---> 5e9988d87da7 Removing intermediate container 31401b7dea40 Step 8 : CMD python /usr/src/app/app.py ---> Running in 78e324d26576 ---> 2f7357a0805d Removing intermediate container 78e324d26576 Successfully built 2f7357a0805d Si vous n'avez pas l'image registry.takima.io/school/proxy/alpine:3.6 , le docker commencera par la t\u00e9l\u00e9charger, puis cr\u00e9era votre image. Par cons\u00e9quent, la sortie de la commande sera sensiblement diff\u00e9rente. Si tout s'est bien pass\u00e9, votre image devrait \u00eatre pr\u00eate ! Ex\u00e9cutez docker images ls et v\u00e9rifiez si votre image ( <YOUR_USERNAME>/myfirstapp ) appara\u00eet. Ex\u00e9cutez votre image La prochaine \u00e9tape de cette section consiste \u00e0 ex\u00e9cuter l'image pour voir si elle fonctionne r\u00e9ellement. $ docker run -p 8888 :5000 --name myfirstapp YOUR_USERNAME/myfirstapp * Running on http://0.0.0.0:5000/ ( Appuyez sur CTRL+C pour quitter ) Acc\u00e9dez \u00e0 http://localhost:8888 , et votre application devrait \u00eatre en ligne. Appuyez sur le bouton \"Actualiser\" dans le navigateur Web pour voir quelques images de chat suppl\u00e9mentaires. Docker Compose Pourquoi faire ? Maintenant que vous savez builder une image, lancer un conteneur, cr\u00e9er des volumes et des networks, vous allez vite vous apercevoir que cela peut \u00eatre fastidieux de lancer toutes ces commandes une part une pour tout reconstruire ou bien tout supprimmer. Alors bien sur vous pourriez cr\u00e9er de multiple script bash par exemple pour lancer tout cela, puis un autre pour tous supprimer. Mais cela s'av\u00e8re difficilement maintenanble et non scalable. Et en plus c'est une approche imp\u00e9rative. Heureusement Docker nous aide \u00e0 \u00e9viter cela avec un outil : Docker Compose. Et en plus, c'est cool, car c'est du d\u00e9claratif. Et \u00e7a, lorsque l'on veut d\u00e9ployer des infras, on aime beaucoup. Utilisation Pour utiliser docker compose il vous faudra d\u00e9crire votre stack \u00e0 d\u00e9ployer (ensemble des objets docker : conteneurs, volume, network et bien d'autre). placer vous dans un nouveau dossier et cr\u00e9ez un fichier compose.yaml : version : '3' services : webapp : image : registry.takima.io/school/proxy/nginx ports : - \"8082:80\" networks : - my-network volumes : - my-volume:/app/data environment : - DATABASE_URL=mysql://dbuser:dbpassword@db/dbname db : image : mysql:5.7 networks : - my-network environment : - MYSQL_ROOT_PASSWORD=rootpassword - MYSQL_DATABASE=dbname - MYSQL_USER=dbuser - MYSQL_PASSWORD=dbpassword volumes : - db-data:/var/lib/mysql networks : my-network : driver : bridge volumes : my-volume : db-data : Lancez la stack $ docker compose up -d Verifiez l'\u00e9tat des objets $ docker compose ps $ docker ps $ docker volume ls $ docker network ls Question Que constatez vous au niveau du nom des objets Docker stopez la stack compose $ docker compose stop supprimmez la stack compose $ docker compose down --volumes Note L'option --volumes permet le purger les volumes. A utiliser avec parcimonie pour ne pas perdre de donn\u00e9es ! Maintenant que vous savez comment ex\u00e9cuter des conteneurs Docker et cr\u00e9er des Dockerfiles, ou des stack enti\u00e8re avec compose, passons \u00e0 la partie pratique. \u00a9 Takima 2023 \"","title":"TD - Docker"},{"location":"ch1-discover-docker-td/#decouverte-de-docker","text":"","title":"D\u00e9couverte de Docker"},{"location":"ch1-discover-docker-td/#configuration","text":"","title":"Configuration"},{"location":"ch1-discover-docker-td/#prerequis","text":"Il n'est pas n\u00e9cessaire d'avoir des comp\u00e9tences sp\u00e9cifiques pour ce tutoriel, si ce n'est une certaine familiarit\u00e9 avec les command line et l'utilisation d'un \u00e9diteur de texte. Une exp\u00e9rience ant\u00e9rieure dans le d\u00e9veloppement d'applications web sera utile, mais n'est pas n\u00e9cessaire.","title":"Pr\u00e9requis"},{"location":"ch1-discover-docker-td/#configuration-de-votre-ordinateur","text":"Comme dit pr\u00e9c\u00e9demment il vous faudra avoir un outil docker fonctionnel. Pour v\u00e9rifier que tout focntionne testons une commande docker : docker run registry.takima.io/school/proxy/hello-world Unable to find image 'registry.takima.io/school/proxy/hello-world:latest' locally latest: Pulling from registry.takima.io/school/proxy/hello-world 03f4658f8b78: Pull complete a3ed95caeb02: Pull complete Digest: sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7 Status: Downloaded newer image for registry.takima.io/school/proxy/hello-world:latest Hello from Docker. ... Ce message indique que votre installation semble fonctionner correctement. Si ce n'est pas le cas parcourez le Getting Start pour installer correctement docker","title":"Configuration de votre ordinateur"},{"location":"ch1-discover-docker-td/#execution-de-votre-premier-conteneur","text":"Maintenant que vous avez tout configur\u00e9, il est temps de mettre les mains dans le cambouis. Dans cette section, vous allez ex\u00e9cuter un conteneur Alpine Linux (une distribution Linux l\u00e9g\u00e8re) sur votre syst\u00e8me et d\u00e9couvrir la commande docker run . Pour commencer, ex\u00e9cutez la commande suivante dans votre terminal pour r\u00e9cuperer (t\u00e9l\u00e9charger) l'image depuis le registry takima : $ docker pull registry.takima.io/school/proxy/alpine Puis on va tag cette image pour cr\u00e9er une copie et lui donner un nom plus commode \u00e0 utiliser pour nos prochaines lignes de commandes : $ docker tag registry.takima.io/school/proxy/alpine alpine Note En fonction de la mani\u00e8re dont vous avez install\u00e9 Docker sur votre syst\u00e8me, vous pourriez voir une erreur de permission refus\u00e9e apr\u00e8s avoir ex\u00e9cut\u00e9 la commande ci-dessus. Essayez les commandes du tutoriel de d\u00e9marrage pour v\u00e9rifier votre installation . Si vous \u00eates sous Linux, vous devrez peut-\u00eatre pr\u00e9fixer vos commandes docker avec sudo . Vous pouvez \u00e9galement cr\u00e9er un groupe Docker pour r\u00e9soudre ce probl\u00e8me. La commande pull r\u00e9cup\u00e8re l'image Alpine depuis notre registre Docker et la sauvegarde sur notre syst\u00e8me. Vous pouvez utiliser la commande docker images pour voir la liste de toutes les images sur votre syst\u00e8me. Nous aurions pu aussi r\u00e9cup\u00e9rer une image Docker publique (Docker Hub) qui contient une multitude d'images standard en utilisant : $ docker pull alpine Cependant Docker Hub est soumis \u00e0 un rate limit donc nous allons \u00e9viter de pull dessus dans cette formation et utiliser le registry Takima. $ docker images R\u00c9PERTOIRE TAG ID DE L ' IMAGE CR\u00c9\u00c9 TAILLE VIRTUELLE alpine derni\u00e8re c51f86c28340 il y a 4 semaines 1 ,109 Mo registry.takima.io/school/proxy/alpine derni\u00e8re c51f86c28340 il y a 4 semaines 1 ,109 Mo registry.takima.io/school/proxy/hello-world derni\u00e8re 690ed74de00f il y a 5 mois 960 o","title":"Ex\u00e9cution de votre premier conteneur"},{"location":"ch1-discover-docker-td/#executer-un-docker","text":"G\u00e9nial ! Ex\u00e9cutons maintenant un conteneur Docker bas\u00e9 sur cette image. Pour cela, nous allons utiliser la commande docker run . $ docker run alpine ls -l total 48 drwxr-xr-x 2 root root 4096 2 mars 16 :20 bin drwxr-xr-x 5 root root 360 18 mars 09 :47 dev drwxr-xr-x 13 root root 4096 18 mars 09 :47 etc drwxr-xr-x 2 root root 4096 2 mars 16 :20 home drwxr-xr-x 5 root root 4096 2 mars 16 :20 lib ... ... Qu'est-il arriv\u00e9 ? En coulisses, il s'est pass\u00e9 beaucoup de choses. Lorsque vous appelez run : Le client Docker contacte le daemon Docker. Le daemon Docker v\u00e9rifie si l'image (dans ce cas, Alpine) est disponible localement, et si ce n'est pas le cas, la t\u00e9l\u00e9charge depuis le registry Docker. (Comme nous avons ex\u00e9cut\u00e9 docker pull registry.takima.io/school/proxy/alpine auparavant puis tag cette image en la nommant alpine , l'\u00e9tape de t\u00e9l\u00e9chargement n'est pas n\u00e9cessaire) Le daemon Docker cr\u00e9e le conteneur, puis ex\u00e9cute une commande dans ce conteneur. Le daemon Docker transmet la sortie de la commande au client Docker. Lorsque vous ex\u00e9cutez docker run alpine , vous avez fourni une commande ( ls -l ), alors Docker a d\u00e9marr\u00e9 la commande sp\u00e9cifi\u00e9e et vous avez vu la liste. Essayons quelque chose de plus utile : afficher un Hello World bien sur! $ docker run alpine echo \"hello from alpine\" hello from alpine OK, voil\u00e0 une sortie r\u00e9elle. Dans ce cas, le client Docker a ex\u00e9cut\u00e9 fid\u00e8lement la commande echo dans notre conteneur Alpine, puis l'a quitt\u00e9. Si vous avez remarqu\u00e9, tout cela s'est produit tr\u00e8s rapidement. Imaginez d\u00e9marrer une machine virtuelle, ex\u00e9cuter une commande, puis la d\u00e9truire. Maintenant, vous savez pourquoi on dit que les conteneurs sont rapides ! Essayons une autre commande. $ docker run alpine /bin/sh Attendez, il ne s'est rien pass\u00e9 ! Est-ce un bogue ? Eh bien, non. Ces ex\u00e9cutions interactives se ferment apr\u00e8s avoir ex\u00e9cut\u00e9 les commandes script\u00e9es, sauf si elles sont ex\u00e9cut\u00e9es dans un terminal interactif. Pour \u00e9viter que cet exemple ne se ferme, vous devez utiliser docker run -it alpine /bin/sh . Vous \u00eates maintenant \u00e0 l'int\u00e9rieur du shell du conteneur et vous pouvez naviguer dans le conteneur. Si vous lancez exit ou que vous tapez CTRL-D , vous quitterez le shell du conteneur et il s'arr\u00eatera \u00e9galement (attention il ne se d\u00e9truira pas). Le conteneur est cens\u00e9 \u00eatre \u00e9ph\u00e9m\u00e8re. $ exit Maintenant, voyons comment lister les conteneurs avec la commande docker ps . docker ps est la commande qui listera tous les conteneurs en cours d'ex\u00e9cution. $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES Comme vous n'avez pas de conteneur qui tourne actuelement (seulement des conteneurs qui ont \u00e9t\u00e9 lanc\u00e9s mais qui sont maintenant stopp\u00e9s), cette commande renvoie une liste vide. Essayons une petite variante : docker ps -a $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 36171a5da744 alpine \"/bin/sh\" 5 minutes ago Exited ( 0 ) 2 minutes ago fervent_newton a6a9d46d0b2f alpine \"echo 'hello from alp\" 6 minutes ago Exited ( 0 ) 6 minutes ago lonely_kilby ff0a5c3750b9 alpine \"ls -l\" 8 minutes ago Exited ( 0 ) 8 minutes ago elated_ramanujan c317d0a9e3d2 hello-world \"/hello\" 34 seconds ago Exited ( 0 ) 12 minutes ago stupefied_mcclintock Vous voyez maintenant une liste de tous les conteneurs que vous avez ex\u00e9cut\u00e9s. Remarquez que la colonne \"STATUS\" indique que ces conteneurs ont \u00e9t\u00e9 arr\u00eat\u00e9s il y a quelques minutes. Vous vous demandez probablement s'il y a un moyen d'ex\u00e9cuter plus d'une commande dans un conteneur. Essayons cela maintenant : $ docker run -it alpine /bin/sh / # ls bin dev etc home lib linuxrc media mnt proc root run sbin sys tmp usr var / # uname -a Linux 97916e8cb5dc 4 .4.27-moby #1 SMP Wed Oct 26 14:01:48 UTC 2016 x86_64 Linux L'ex\u00e9cution de la commande run avec l'option -it nous connecte \u00e0 un terminal interactif dans le conteneur. Maintenant, vous pouvez ex\u00e9cuter autant de commandes que vous le souhaitez dans le conteneur. Prenez le temps d'ex\u00e9cuter vos commandes pr\u00e9f\u00e9r\u00e9es. Astuce run -it est une commande tr\u00e8s utile pour d\u00e9boguer le bas niveau d'un conteneur. Vous savez faire tourner un container et le lancer en mode interactif. Mais il manque une fonctionnalit\u00e9 essentielle lorsque l'on veut faire tourner un service. Pour l'illustrer lancer un nginx : $ docker run registry.takima.io/school/proxy/nginx Question Que se passe-t-il dans ce cas ? Que se passe-t-il si vous fermez votre shell ou ctr+c par exemple (fermez votre shell relancez le et lancez un docker ps -a ) En fait votre conteneur s'est lanc\u00e9 et ne vous rend pas la main et vous avez alors le log nginx sur votre shell. Lorsque vous quittez votre shell, le conteneur se stoppe et votre nginx se stoppe donc \u00e9galement. Ce n'est pas un comportement souhaitable lorsque l'on veut lancer des services en tache de fond, c'est ce qu'on apelle un daemon. Docker nous permet de le faire avec une simple option -d relancez votre conteneur Nginx en mode service $ docker run -d --name mon-nginx registry.takima.io/school/proxy/nginx b611eea1536bcfc79f87e1bfd57a3f10bbe4577f03e405329f5626cd66a64e54 Maintenant lancez un docker ps . Vous verrez votre docker nginx tourner Voil\u00e0 docker run n'a plus de secret pour vous, et c'est parfait car c'est probablement la commande que vous utiliserez le plus fr\u00e9quemment. Il est judicieux de prendre le temps de vous familiariser avec elle. Pour en savoir plus sur run , utilisez docker run --help pour voir la liste de tous les indicateurs qu'elle prend en charge. Au fur et \u00e0 mesure de votre progression, vous verrez quelques variantes de docker run . Pour la suite taguez votre image nginx : $ docker tag registry.takima.io/school/proxy/nginx nginx A tout moment vous pouvez inspecter un container avec docker container inspect , par exemple pour r\u00e9cup\u00e9rer son status : # get the state of a container docker container inspect \\ --format '{{.State.Status}}' \\ mon-nginx Success Point cours pour voir les Network Docker","title":"Ex\u00e9cuter un Docker"},{"location":"ch1-discover-docker-td/#les-network-docker","text":"Faire tourner un applicatif c'est bien mais lorsque l'on fait du web c'est essentiel de pouvoir publier son application et que cette application b\u00e9n\u00e9ficie d'une connectivit\u00e9 (IP), mais c'est aussi essentiel d'isoler les conteneurs dans des r\u00e9seaux s\u00e9par\u00e9s. Apr\u00e8s tout c'est aussi l'une des promesses de docker l'isolation.","title":"Les Network Docker"},{"location":"ch1-discover-docker-td/#objectif","text":"Comprendre les concepts de base des r\u00e9seaux Docker. Cr\u00e9er et g\u00e9rer des r\u00e9seaux Docker. Connecter des conteneurs \u00e0 des r\u00e9seaux personnalis\u00e9s.","title":"Objectif:"},{"location":"ch1-discover-docker-td/#verifier-les-reseaux-docker-existants","text":"Pour lister les reseaux actuels : $ docker network ls NETWORK ID NAME DRIVER SCOPE df8d9a03e3e1 bridge bridge local f0b4def22265 host host local a7cd984404b5 none null local Ce sont les r\u00e9seaux par d\u00e9fault. On peut cr\u00e9er autant de nouveau r\u00e9seaux isol\u00e9s que l'on souhaite et lorsque l'on cr\u00e9\u00e9 un reseau il sera de type bridge par d\u00e9faut Lorsque l'on cr\u00e9er un conteneur il se trouve attach\u00e9 par d\u00e9faut au r\u00e9seau nomm\u00e9 bridge . On d\u00e9conseille donc de l'utilser pour faire tourner nos applications.","title":"V\u00e9rifier les r\u00e9seaux Docker existants"},{"location":"ch1-discover-docker-td/#creer-un-reseau-personnalise","text":"Cr\u00e9ez un r\u00e9seau Docker personnalis\u00e9 en utilisant la commande docker network create . Par exemple, cr\u00e9ez un r\u00e9seau nomm\u00e9 \"app\" : docker network create app Vous pouvez v\u00e9rifier que le r\u00e9seau a \u00e9t\u00e9 cr\u00e9\u00e9 en utilisant la commande docker network ls .","title":"Cr\u00e9er un r\u00e9seau personnalis\u00e9"},{"location":"ch1-discover-docker-td/#executer-des-conteneurs-connectes-au-reseau-personnalise","text":"Cr\u00e9ez deux conteneurs connect\u00e9s au r\u00e9seau \"app\". Par exemple, nous pouvons cr\u00e9er un conteneur web et un conteneur de base de donn\u00e9es : docker run -d --name webapp --network app nginx Le conteneur webapp est maintenant connect\u00e9 au r\u00e9seau `app``","title":"Ex\u00e9cuter des conteneurs connect\u00e9s au r\u00e9seau personnalis\u00e9"},{"location":"ch1-discover-docker-td/#verifier-la-connectivite","text":"Vous pouvez maintenant v\u00e9rifier si un conteneur pr\u00e9sent sur ce r\u00e9seau app peut communiquer avec la webapp. Ex\u00e9cutez un conteneur de test connect\u00e9 au m\u00eame r\u00e9seau : docker run -it --rm --network app registry.takima.io/school/proxy/busybox sh \u00c0 l'int\u00e9rieur de ce conteneur de test, essayez de \"pinguer\" le conteneur webapp en utilisant leurs noms de conteneurs comme h\u00f4tes. Assurez-vous que la connectivit\u00e9 fonctionne avec un wget sur le port 80 par exemple . $ wget webapp Connecting to webapp ( 172 .19.0.2:80 ) saving to 'index.html' index.html 100 % | ********************************************************************************************** | 615 0 :00:00 ETA 'index.html' saved $ cat index.html","title":"V\u00e9rifier la connectivit\u00e9"},{"location":"ch1-discover-docker-td/#supprimer-le-reseau","text":"Pour supprimer le r\u00e9seau personnalis\u00e9 que vous avez cr\u00e9\u00e9, utilisez la commande suivante : docker network rm app Notes Si un r\u00e9seau est utilis\u00e9, impossible de le d\u00e9truire. Success Point cours sur la publication des conteneurs","title":"Supprimer le r\u00e9seau"},{"location":"ch1-discover-docker-td/#la-publication","text":"Nous avons isol\u00e9 les conteneurs dans un r\u00e9seau et c'est parfait pour la s\u00e9curit\u00e9. Mais certains services sont plus utiles lorsqu'ils sont acc\u00e8ssibles. Nous allons donc voir comment publier un service sur le Host qui l'h\u00e9berge.","title":"La publication"},{"location":"ch1-discover-docker-td/#objectif_1","text":"Comprendre comment publier des ports de conteneurs Docker. Tester l'accessibilit\u00e9 des applications via les ports publi\u00e9s.","title":"Objectif :"},{"location":"ch1-discover-docker-td/#verification-des-ports-existants","text":"Avant de commencer, listez les conteneurs actuellement en cours d'ex\u00e9cution sur votre syst\u00e8me \u00e0 l'aide de la commande suivante : docker ps Cette commande affichera les conteneurs actifs, mais notez qu'aucun port n'est actuellement publi\u00e9 pour eux.","title":"V\u00e9rification des ports existants"},{"location":"ch1-discover-docker-td/#creer-un-conteneur-avec-des-ports-publies","text":"Cr\u00e9ez un conteneur avec un serveur web Nginx et publiez son port 80 sur un port de votre machine h\u00f4te, par exemple, le port 8080. Utilisez la commande suivante : docker run -d -p 8080 :80 --name monsite registry.takima.io/school/proxy/nginx Cela d\u00e9marrera un conteneur Nginx avec son port 80 publi\u00e9 sur le port 8080 de votre machine h\u00f4te.","title":"Cr\u00e9er un conteneur avec des ports publi\u00e9s"},{"location":"ch1-discover-docker-td/#acces-a-lapplication-via-le-port-publie","text":"Ouvrez un navigateur web et acc\u00e9dez \u00e0 http://localhost:8080 . Vous devriez voir la page d'accueil Nginx, ce qui signifie que le serveur web du conteneur est accessible via le port publi\u00e9.","title":"Acc\u00e8s \u00e0 l'application via le port publi\u00e9"},{"location":"ch1-discover-docker-td/#gestion-des-ports-publies","text":"Pour afficher les ports publi\u00e9s par un conteneur en cours d'ex\u00e9cution, utilisez la commande docker port . Par exemple : docker port monsite","title":"Gestion des ports publi\u00e9s"},{"location":"ch1-discover-docker-td/#arreter-et-supprimer-les-conteneurs","text":"Lorsque vous avez termin\u00e9, arr\u00eatez et supprimez les conteneurs que vous avez cr\u00e9\u00e9s : docker stop monsite docker rm monsite Tip rm -f est utile pour aller plus vite. Success Point cours sur les Volumes Docker","title":"Arr\u00eater et supprimer les conteneurs"},{"location":"ch1-discover-docker-td/#les-volumes-docker","text":"placez vous dans un dossier /td-volumes","title":"Les volumes Docker"},{"location":"ch1-discover-docker-td/#creation-dun-conteneur-avec-un-volume","text":"Nous allons ici utiliser un bind volume : Il s'agit d'un volume mont\u00e9 depuis un dossier du host (celui qui lance les dockers) Cr\u00e9ez un r\u00e9pertoire vide sur votre syst\u00e8me h\u00f4te pour servir de volume. Par exemple : mkdir mon_volume Cr\u00e9ez un conteneur en utilisant la commande docker run avec un volume mont\u00e9 \u00e0 partir du r\u00e9pertoire que vous venez de cr\u00e9er. Assurez-vous de remplacer chemin_vers_votre_volume par le chemin absolu du r\u00e9pertoire que vous avez cr\u00e9\u00e9 \u00e0 l'\u00e9tape pr\u00e9c\u00e9dente : !!! tip Si vous \u00eates bien dans le dossier utilis\u00e9 la variables de dossier courrant $(pwd) \"$(pwd)\"/mon_volume:/containerDir docker run -d -v \" $( pwd ) \" /mon_volume:/data --name mon-conteneur nginx V\u00e9rifiez que le conteneur est en cours d'ex\u00e9cution en utilisant docker ps .","title":"Cr\u00e9ation d'un Conteneur avec un Volume"},{"location":"ch1-discover-docker-td/#utilisation-du-volume-depuis-le-conteneur","text":"Ex\u00e9cutez un shell interactif dans le conteneur que vous venez de cr\u00e9er : docker exec -it mon-conteneur /bin/bash \u00c0 l'int\u00e9rieur du conteneur, cr\u00e9ez un fichier ou un r\u00e9pertoire dans le r\u00e9pertoire mont\u00e9 : touch /data/mon_fichier.txt Quittez le shell du conteneur en utilisant exit .","title":"Utilisation du Volume depuis le Conteneur"},{"location":"ch1-discover-docker-td/#verification-de-la-persistance-des-donnees","text":"Arr\u00eatez et supprimez le conteneur : docker stop mon-conteneur docker rm mon-conteneur V\u00e9rifiez que le r\u00e9pertoire et le fichier que vous avez cr\u00e9\u00e9s sont toujours pr\u00e9sents dans le r\u00e9pertoire du volume sur votre syst\u00e8me h\u00f4te : ls mon_volume","title":"V\u00e9rification de la Persistance des Donn\u00e9es"},{"location":"ch1-discover-docker-td/#creation-dun-deuxieme-conteneur-avec-le-meme-volume","text":"Cr\u00e9ez un deuxi\u00e8me conteneur en utilisant le m\u00eame volume que le premier : docker run -d -v \" $( pwd ) \" /mon_volume:/data --name mon-conteneur-bis nginx Ex\u00e9cutez un shell interactif dans le deuxi\u00e8me conteneur : docker exec -it mon-conteneur-bis /bin/bash V\u00e9rifiez que le fichier ou le r\u00e9pertoire que vous avez cr\u00e9\u00e9 est pr\u00e9sent dans le deuxi\u00e8me conteneur, m\u00eame s'il s'agit d'un conteneur diff\u00e9rent. Quittez le shell du deuxi\u00e8me conteneur en utilisant exit .","title":"Cr\u00e9ation d'un Deuxi\u00e8me Conteneur avec le M\u00eame Volume"},{"location":"ch1-discover-docker-td/#clean","text":"Supprimez le deuxi\u00e8me conteneur : docker stop mon-conteneur-bis docker rm mon-conteneur-bis Supprimez le r\u00e9pertoire du volume sur votre syst\u00e8me h\u00f4te : rm -rf mon_volume Success Point cours sur les Images Docker","title":"clean"},{"location":"ch1-discover-docker-td/#images-docker","text":"Dans cette section, plongeons plus en profondeur dans ce que sont les images Docker. Vous allez construire votre propre image, utiliser cette image pour ex\u00e9cuter une application localement, et enfin, pousser certaines de vos propres images vers Docker Hub. Les images Docker sont la base des conteneurs. Dans l'exemple pr\u00e9c\u00e9dent, vous avez pull une image nginx depuis le registre et avez demand\u00e9 au client Docker d'ex\u00e9cuter un conteneur bas\u00e9 sur cette image. Pour voir la liste des images disponibles localement sur votre syst\u00e8me, ex\u00e9cutez la commande docker images . $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE dockersamples/static-site latest 92a386b6e686 2 hours ago 190 .5 MB nginx latest af4b3d7d5401 3 hours ago 190 .5 MB Vous aurez une liste diff\u00e9rente d'images sur votre machine. Le TAG fait r\u00e9f\u00e9rence \u00e0 une version de l'image et l' image ID est l'identifiant unique correspondant de cette image. Pour simplifier, vous pouvez consid\u00e9rer une image comme un r\u00e9f\u00e9rentiel Git : les images peuvent \u00eatre commit\u00e9es avec des modifications et avoir plusieurs versions. Lorsque vous ne fournissez pas de num\u00e9ro de version sp\u00e9cifique, le client par d\u00e9faut est \"latest\" (derni\u00e8re version). Par exemple, vous pourriez extraire une version sp\u00e9cifique de l'image ubuntu comme suit : $ docker pull registry.takima.io/school/proxy/ubuntu:12.04 Si vous ne sp\u00e9cifiez pas le num\u00e9ro de version de l'image, le client Docker utilisera, comme mentionn\u00e9 pr\u00e9c\u00e9demment, une version nomm\u00e9e \"latest\" (derni\u00e8re version). Donc, par exemple, la commande docker pull ci-dessous extraira une image nomm\u00e9e registry.takima.io/school/proxy/ubuntu:latest : $ docker pull registry.takima.io/school/proxy/ubuntu Pour obtenir une nouvelle image Docker, vous pouvez soit l'obtenir depuis un registre (tel que le Docker Hub ou bien un registre personnel comme nous sommes en train de faire) soit la cr\u00e9er vous-m\u00eame. Il existe des centaines de milliers d'images disponibles sur le Docker Hub . Vous pouvez \u00e9galement rechercher des images directement depuis la ligne de commande \u00e0 l'aide de docker search . Une distinction importante concernant les images se situe entre les images de base (base images) et les images enfants (child images). Les images de base sont des images qui n'ont pas d'images parent, g\u00e9n\u00e9ralement des images avec un syst\u00e8me d'exploitation tel qu'Ubuntu, Alpine ou Debian. Les images enfants sont des images qui se construisent sur des images de base et ajoutent des fonctionnalit\u00e9s suppl\u00e9mentaires. Un autre concept cl\u00e9 est l'id\u00e9e d'images officielles et d'images utilisateur (user images). (Les deux peuvent \u00eatre des images de base ou des images enfants.) Les images officielles sont des images v\u00e9rifi\u00e9es par Docker. Docker, Inc. sponsorise une \u00e9quipe d\u00e9di\u00e9e responsable de la r\u00e9vision et de la publication de tout le contenu des r\u00e9f\u00e9rentiels officiels. Cette \u00e9quipe travaille en collaboration avec les mainteneurs de logiciels, des experts en s\u00e9curit\u00e9 et la communaut\u00e9 Docker \u00e9largie. Elles ne sont pas pr\u00e9fix\u00e9es par un nom d'organisation ou d'utilisateur. Dans la liste des images ci-dessus, les images python , node , alpine et nginx sont des images officielles (de base). Pour en savoir plus \u00e0 leur sujet, consultez la documentation sur les images officielles . Les images utilisateur sont des images cr\u00e9\u00e9es et partag\u00e9es par des utilisateurs comme vous. Elles se construisent sur des images de base et ajoutent des fonctionnalit\u00e9s suppl\u00e9mentaires. Typiquement, elles sont format\u00e9es comme utilisateur/nom-de-l'image . La valeur utilisateur dans le nom de l'image correspond \u00e0 votre nom d'utilisateur ou d'organisation Docker Hub.","title":"Images Docker"},{"location":"ch1-discover-docker-td/#creez-votre-premiere-image","text":"Maintenant que vous avez une meilleure compr\u00e9hension des images, il est temps de cr\u00e9er la v\u00f4tre. Notre principal objectif ici est de cr\u00e9er une image qui isole une petite application Flask . L'objectif de cet exercice est de cr\u00e9er une image Docker qui ex\u00e9cutera une application Flask. Nous allons commencer par rassembler les composants pour un g\u00e9n\u00e9rateur al\u00e9atoire d'images de chats construit avec Python Flask, puis dockeriser cela en \u00e9crivant un Dockerfile . Enfin, nous allons construire l'image, puis l'ex\u00e9cuter.","title":"Cr\u00e9ez votre premi\u00e8re image"},{"location":"ch1-discover-docker-td/#creez-une-application-python-flask-qui-affiche-des-images-de-chats-aleatoires","text":"Dans le cadre de cet atelier, nous avons cr\u00e9\u00e9 une petite application Python Flask amusante qui affiche une image de chat al\u00e9atoire au chargement - car, vous savez, qui n'aime pas les chats ? Commencez par cr\u00e9er un r\u00e9pertoire appel\u00e9 flask-app o\u00f9 nous allons cr\u00e9er les fichiers suivants : app.py requirements.txt templates/index.html Dockerfile Assurez-vous de vous d\u00e9placer vers cd flask-app avant de commencer \u00e0 cr\u00e9er les fichiers, car vous ne voulez pas ajouter tout un tas d'autres fichiers \u00e0 votre image.","title":"Cr\u00e9ez une application Python Flask qui affiche des images de chats al\u00e9atoires."},{"location":"ch1-discover-docker-td/#apppy","text":"Cr\u00e9er le fichier python app.py avec ce contenu: from flask import Flask , render_template import random app = Flask ( __name__ ) # liste des images images = [ \"https://c.tenor.com/GTcT7HODLRgAAAAM/smiling-cat-creepy-cat.gif\" , \"https://media0.giphy.com/media/10dU7AN7xsi1I4/giphy.webp?cid=ecf05e47gk63rd81vzlot57qmebr7drtgf6a3khmzvjsdtu7&rid=giphy.webp&ct=g\" , \"https://media0.giphy.com/media/S6VGjvmFRu5Qk/giphy.webp?cid=ecf05e478yofpawrhffnnvb3sgjkos96vyfo5mtqhds35as6&rid=giphy.webp&ct=g\" , \"https://media3.giphy.com/media/JIX9t2j0ZTN9S/200w.webp?cid=ecf05e47gk63rd81vzlot57qmebr7drtgf6a3khmzvjsdtu7&rid=200w.webp&ct=g\" ] @app . route ( '/' ) def index (): url = random . choice ( images ) return render_template ( 'index.html' , url = url ) if __name__ == \"__main__\" : app . run ( host = \"0.0.0.0\" )","title":"app.py"},{"location":"ch1-discover-docker-td/#requirementstxt","text":"Ce fichier requirements.txt r\u00e9f\u00e9rence les modules Python necessaire pour lancer notre app Python Flask==0.10.1","title":"requirements.txt"},{"location":"ch1-discover-docker-td/#templatesindexhtml","text":"Cr\u00e9ez le dossier templates et cr\u00e9ez dans ce dossier le fichier index.html avec le contenu suivant: < html > < head > < style type = \"text/css\" > body { background : black ; color : white ; } div . container { max-width : 500 px ; margin : 100 px auto ; border : 20 px solid white ; padding : 10 px ; text-align : center ; } h4 { text-transform : uppercase ; } </ style > </ head > < body > < div class = \"container\" > < h4 > Cat Gif of the day </ h4 > < img src = \"{{url}}\" /> < p > < small > Courtesy: < a href = \"http://www.buzzfeed.com/copyranter/the-best-cat-gif-post-in-the-history-of-cat-gifs\" > Buzzfeed </ a ></ small > </ p > </ div > </ body > </ html >","title":"templates/index.html"},{"location":"ch1-discover-docker-td/#redigez-un-dockerfile","text":"Dernier fichier et pas des moindre le Dockerfile. Nous voulons cr\u00e9er une image Docker avec cette application web. Comme mentionn\u00e9 pr\u00e9c\u00e9demment, toutes les images utilisateur sont bas\u00e9es sur une image de base. \u00c9tant donn\u00e9 que notre application est \u00e9crite en Python, nous allons cr\u00e9er notre propre image embarquant notre app Python bas\u00e9e sur Alpine . Nous allons le faire en utilisant un Dockerfile. Tip L'image Alpine est souvent utilis\u00e9 comme image de base car elle est tr\u00e8s l\u00e9g\u00e8re Un Dockerfile est un fichier texte qui contient une liste de commandes que le daemon Docker appelle lors de la cr\u00e9ation d'une image. Le Dockerfile contient toutes les informations dont Docker a besoin pour ex\u00e9cuter l'application : une image Docker de base qui servira de socle , l'emplacement de votre code de projet, ses d\u00e9pendances \u00e9ventuelles, et les commandes \u00e0 ex\u00e9cuter au d\u00e9marrage. C'est un moyen simple d'automatiser le processus de cr\u00e9ation d'images. Cr\u00e9ez un fichier appel\u00e9 Dockerfile et ajoutez-y le contenu comme d\u00e9crit ci-dessous. Nous allons commencer par sp\u00e9cifier notre image de base en utilisant le mot cl\u00e9 FROM : FROM registry.takima.io/school/proxy/alpine:3.6 La prochaine \u00e9tape consiste g\u00e9n\u00e9ralement \u00e0 \u00e9crire les commandes de copie des fichiers et \u00e0 installer les d\u00e9pendances n\u00e9cessaire pour notre application. Mais d'abord, nous allons installer le package Python pip dans la distribution Linux Alpine. Cela n'installera pas seulement le package pip, mais aussi d'autres d\u00e9pendances, y compris l'interpr\u00e9teur Python. Ajoutez la commande RUN suivante : RUN apk add --update py2-pip Ajoutons les fichiers qui composent l'application Flask. Installez toutes les d\u00e9pendances Python n\u00e9cessaires pour le bon fonctionnement de notre application. Cela sera accompli en ajoutant les lignes suivantes : COPY requirements.txt /usr/src/app/ RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt Copiez les fichiers que vous avez cr\u00e9\u00e9s pr\u00e9c\u00e9demment dans notre image en utilisant la commande COPY . COPY app.py /usr/src/app/ COPY templates/index.html /usr/src/app/templates/ Sp\u00e9cifiez le num\u00e9ro de port qui doit \u00eatre expos\u00e9. Comme notre application Flask s'ex\u00e9cute sur le port 5000 , c'est ce que nous allons exposer. EXPOSE 5000 La derni\u00e8re \u00e9tape est de d\u00e9finir la commande pour lancer l'application : python ./app.py . Utilisez la commande ENTRYPOINT pour cela : ENTRYPOINT [ \"python\" , \"/usr/src/app/app.py\" ] Le but principal de ENTRYPOINT est d'indiquer au conteneur la commande qu'il doit ex\u00e9cuter par d\u00e9faut lors de son d\u00e9marrage. Tip En v\u00e9rit\u00e9 ENTRYPOINT servira \u00e0 d\u00e9finir l'executable et le CMD servira \u00e0 d\u00e9finir les param\u00e8tres de l'executable V\u00e9rifiez votre Dockerfile. Notre Dockerfile est maintenant pr\u00eat. Voici \u00e0 quoi il ressemble : # notre image de base FROM registry.takima.io/school/proxy/alpine:3.6 # Installer Python et pip RUN apk add --update py2-pip # Installer les modules Python n\u00e9cessaires par l'application Python COPY requirements.txt /usr/src/app/ RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt # Copier les fichiers n\u00e9cessaires pour l'ex\u00e9cution de l'application COPY app.py /usr/src/app/ COPY templates/index.html /usr/src/app/templates/ # Indiquer le num\u00e9ro de port que le conteneur doit exposer EXPOSE 5000 # Ex\u00e9cuter l'application ENTRYPOINT [ \"python\" , \"/usr/src/app/app.py\" ]","title":"R\u00e9digez un Dockerfile"},{"location":"ch1-discover-docker-td/#builder-notre-image","text":"Maintenant que notre Dockerfile est pr\u00eat, il est temps de builder notre application. La commande docker build s'occupe du travail de cr\u00e9er une image Docker \u00e0 partir du Dockerfile. Lorsque vous ex\u00e9cutez la commande docker build , assurez-vous de remplacer <YOUR_USERNAME> par votre nom d'utilisateur. Ce nom d'utilisateur devrait \u00eatre le m\u00eame que celui que vous avez cr\u00e9\u00e9 lors de votre inscription sur Docker Hub . Si vous ne l'avez pas encore fait mettez le nom que vous souhaitez pour votre tag. La commande docker build est assez simple. Elle prend un nom de balise ou tag facultatif avec l'option -t et l'emplacement du r\u00e9pertoire contenant le Dockerfile. Le point . indique le r\u00e9pertoire actuel : $ docker build -t <YOUR_USERNAME>/myfirstapp . Sending build context to Docker daemon 9 .728 kB Step 1 : FROM alpine:latest ---> 0d81fc72e790 Step 2 : RUN apk add --update py-pip ---> Running in 8abd4091b5f5 fetch http://dl-4.alpinelinux.org/alpine/v3.3/main/x86_64/APKINDEX.tar.gz fetch http://dl-4.alpinelinux.org/alpine/v3.3/community/x86_64/APKINDEX.tar.gz ( 1 /12 ) Installing libbz2 ( 1 .0.6-r4 ) ( 2 /12 ) Installing expat ( 2 .1.0-r2 ) ( 3 /12 ) Installing libffi ( 3 .2.1-r2 ) ( 4 /12 ) Installing gdbm ( 1 .11-r1 ) ( 5 /12 ) Installing ncurses-terminfo-base ( 6 .0-r6 ) ( 6 /12 ) Installing ncurses-terminfo ( 6 .0-r6 ) ( 7 /12 ) Installing ncurses-libs ( 6 .0-r6 ) ( 8 /12 ) Installing readline ( 6 .3.008-r4 ) ( 9 /12 ) Installing sqlite-libs ( 3 .9.2-r0 ) ( 10 /12 ) Installing python ( 2 .7.11-r3 ) ( 11 /12 ) Installing py-setuptools ( 18 .8-r0 ) ( 12 /12 ) Installing py-pip ( 7 .1.2-r0 ) Executing busybox-1.24.1-r7.trigger OK: 59 MiB in 23 packages ---> 976a232ac4ad Removing intermediate container 8abd4091b5f5 Step 3 : COPY requirements.txt /usr/src/app/ ---> 65b4be05340c Removing intermediate container 29ef53b58e0f Step 4 : RUN pip install --no-cache-dir -r /usr/src/app/requirements.txt ---> Running in a1f26ded28e7 Collecting Flask == 0 .10.1 ( from -r /usr/src/app/requirements.txt ( line 1 )) Downloading Flask-0.10.1.tar.gz ( 544kB ) Collecting Werkzeug> = 0 .7 ( from Flask == 0 .10.1->-r /usr/src/app/requirements.txt ( line 1 )) Downloading Werkzeug-0.11.4-py2.py3-none-any.whl ( 305kB ) Collecting Jinja2> = 2 .4 ( from Flask == 0 .10.1->-r /usr/src/app/requirements.txt ( line 1 )) Downloading Jinja2-2.8-py2.py3-none-any.whl ( 263kB ) Collecting itsdangerous> = 0 .21 ( from Flask == 0 .10.1->-r /usr/src/app/requirements.txt ( line 1 )) Downloading itsdangerous-0.24.tar.gz ( 46kB ) Collecting MarkupSafe ( from Jinja2> = 2 .4->Flask == 0 .10.1->-r /usr/src/app/requirements.txt ( line 1 )) Downloading MarkupSafe-0.23.tar.gz Installing collected packages: Werkzeug, MarkupSafe, Jinja2, itsdangerous, Flask Running setup.py install for MarkupSafe Running setup.py install for itsdangerous Running setup.py install for Flask Successfully installed Flask-0.10.1 Jinja2-2.8 MarkupSafe-0.23 Werkzeug-0.11.4 itsdangerous-0.24 You are using pip version 7 .1.2, however version 8 .1.1 is available. You should consider upgrading via the 'pip install --upgrade pip' command. ---> 8de73b0730c2 Removing intermediate container a1f26ded28e7 Step 5 : COPY app.py /usr/src/app/ ---> 6a3436fca83e Removing intermediate container d51b81a8b698 Step 6 : COPY templates/index.html /usr/src/app/templates/ ---> 8098386bee99 Removing intermediate container b783d7646f83 Step 7 : EXPOSE 5000 ---> Running in 31401b7dea40 ---> 5e9988d87da7 Removing intermediate container 31401b7dea40 Step 8 : CMD python /usr/src/app/app.py ---> Running in 78e324d26576 ---> 2f7357a0805d Removing intermediate container 78e324d26576 Successfully built 2f7357a0805d Si vous n'avez pas l'image registry.takima.io/school/proxy/alpine:3.6 , le docker commencera par la t\u00e9l\u00e9charger, puis cr\u00e9era votre image. Par cons\u00e9quent, la sortie de la commande sera sensiblement diff\u00e9rente. Si tout s'est bien pass\u00e9, votre image devrait \u00eatre pr\u00eate ! Ex\u00e9cutez docker images ls et v\u00e9rifiez si votre image ( <YOUR_USERNAME>/myfirstapp ) appara\u00eet.","title":"Builder notre image"},{"location":"ch1-discover-docker-td/#executez-votre-image","text":"La prochaine \u00e9tape de cette section consiste \u00e0 ex\u00e9cuter l'image pour voir si elle fonctionne r\u00e9ellement. $ docker run -p 8888 :5000 --name myfirstapp YOUR_USERNAME/myfirstapp * Running on http://0.0.0.0:5000/ ( Appuyez sur CTRL+C pour quitter ) Acc\u00e9dez \u00e0 http://localhost:8888 , et votre application devrait \u00eatre en ligne. Appuyez sur le bouton \"Actualiser\" dans le navigateur Web pour voir quelques images de chat suppl\u00e9mentaires.","title":"Ex\u00e9cutez votre image"},{"location":"ch1-discover-docker-td/#docker-compose","text":"","title":"Docker Compose"},{"location":"ch1-discover-docker-td/#pourquoi-faire","text":"Maintenant que vous savez builder une image, lancer un conteneur, cr\u00e9er des volumes et des networks, vous allez vite vous apercevoir que cela peut \u00eatre fastidieux de lancer toutes ces commandes une part une pour tout reconstruire ou bien tout supprimmer. Alors bien sur vous pourriez cr\u00e9er de multiple script bash par exemple pour lancer tout cela, puis un autre pour tous supprimer. Mais cela s'av\u00e8re difficilement maintenanble et non scalable. Et en plus c'est une approche imp\u00e9rative. Heureusement Docker nous aide \u00e0 \u00e9viter cela avec un outil : Docker Compose. Et en plus, c'est cool, car c'est du d\u00e9claratif. Et \u00e7a, lorsque l'on veut d\u00e9ployer des infras, on aime beaucoup.","title":"Pourquoi faire ?"},{"location":"ch1-discover-docker-td/#utilisation","text":"Pour utiliser docker compose il vous faudra d\u00e9crire votre stack \u00e0 d\u00e9ployer (ensemble des objets docker : conteneurs, volume, network et bien d'autre). placer vous dans un nouveau dossier et cr\u00e9ez un fichier compose.yaml : version : '3' services : webapp : image : registry.takima.io/school/proxy/nginx ports : - \"8082:80\" networks : - my-network volumes : - my-volume:/app/data environment : - DATABASE_URL=mysql://dbuser:dbpassword@db/dbname db : image : mysql:5.7 networks : - my-network environment : - MYSQL_ROOT_PASSWORD=rootpassword - MYSQL_DATABASE=dbname - MYSQL_USER=dbuser - MYSQL_PASSWORD=dbpassword volumes : - db-data:/var/lib/mysql networks : my-network : driver : bridge volumes : my-volume : db-data : Lancez la stack $ docker compose up -d Verifiez l'\u00e9tat des objets $ docker compose ps $ docker ps $ docker volume ls $ docker network ls Question Que constatez vous au niveau du nom des objets Docker stopez la stack compose $ docker compose stop supprimmez la stack compose $ docker compose down --volumes Note L'option --volumes permet le purger les volumes. A utiliser avec parcimonie pour ne pas perdre de donn\u00e9es ! Maintenant que vous savez comment ex\u00e9cuter des conteneurs Docker et cr\u00e9er des Dockerfiles, ou des stack enti\u00e8re avec compose, passons \u00e0 la partie pratique. \u00a9 Takima 2023 \"","title":"Utilisation"},{"location":"ch1-discover-docker-tp/","text":"D\u00e9couvrir Docker Objectifs Bonnes pratiques Tout au long du TP vous allez cr\u00e9er des fichiers li\u00e9s a docker ou docker-compose ou li\u00e9s a du code que nous allons utiliser. Pour vous y retrouver, cr\u00e9ez une structure de fichiers appropri\u00e9e, 1 dossier par image build\u00e9e par exemple. Application cible Application trois tiers: Serveur HTTP (simple revers proxy dans un premier temps) API Backend Base de donn\u00e9es Pour chacune de ces applications, nous suivrons le m\u00eame processus : choisir l'image de base Docker appropri\u00e9e, cr\u00e9er et configurer cette image, ajouter les sp\u00e9cificit\u00e9s de notre application et, \u00e0 un moment donn\u00e9, la faire fonctionner. Notre objectif final est d'avoir une application trois tiers qui tourne. Images de base Ces images seront celles utilis\u00e9es comme point de d\u00e9part pour builder vos images custom qui feront tourner votre application. Serveur HTTP API Backend Base de donn\u00e9es Pour \u00e9viter le rate limit de Docker Hub nous vous fournissons ces m\u00eame image dans notre registry public : Base de donn\u00e9es Fondamentaux Nous utiliserons l'image : postgres:14.1-alpine. Faisons fonctionner un simple serveur PostgreSQL. Voici ce qui serait un Dockerfile minimal : FROM registry.takima.io/school/proxy/postgres:14.1-alpine ENV POSTGRES_DB = db \\ POSTGRES_USER = usr \\ POSTGRES_PASSWORD = pwd Construisez cette image et d\u00e9marrez un conteneur correctement, vous devriez pouvoir acc\u00e9der \u00e0 votre base de donn\u00e9es en fonction du port de publication que vous choisissez : localhost:PORT. Note Ici les variable ENV sont indiquer pour \u00eatre prise par d\u00e9fault. Il est ensuite conseiller de les override lors du lancement du conteneur avec l'option -e Votre base de donn\u00e9es PostgreSQL devrait \u00eatre op\u00e9rationnelle. Connectez-vous \u00e0 votre base de donn\u00e9es et v\u00e9rifiez que tout fonctionne correctement. N'oubliez pas de nommer votre image Docker et votre conteneur. Tip Si vous avez des difficult\u00e9s, revenez \u00e0 la partie Construire l'image et Ex\u00e9cuter votre image sur TD01 - Docker ( TD 1 D\u00e9couverte de Docker ). Relancez votre base de donn\u00e9es et Adminer avec --network app-network pour permettre la communication Adminer/Base de donn\u00e9es. Nous utilisons -\u2013network pour placer adminer et postgres dans le m\u00eame r\u00e9seau, au lieu de -\u2013link car ce dernier est obsol\u00e8te. Tip N'oubliez pas de cr\u00e9er votre r\u00e9seau docker network create app-network Si vous n'avez pas plac\u00e9 le conteneur dans le bon r\u00e9seau vous pouvez le connecter avec docker network connect my_net my_container Est-il correct d'avoir des mots de passe \u00e9crits en clair dans un fichier ? Vous pouvez plut\u00f4t d\u00e9finir ces param\u00e8tres d'environnement lors de l'ex\u00e9cution de l'image en utilisant l'option -e . Tip Pourquoi ex\u00e9cutons-nous le conteneur avec une option -e pour fournir les variables d'environnement ? Initialisation de la base de donn\u00e9es parfois il peut \u00eatre pratique d'avoir notre structure de base de donn\u00e9es initialis\u00e9e \u00e0 l'int\u00e9rieur m\u00eame de l'image Docker ainsi que quelques donn\u00e9es initiales. Tout script SQL trouv\u00e9 dans /docker-entrypoint-initdb.d sera ex\u00e9cut\u00e9 dans l'ordre alphab\u00e9tique, ajoutons donc quelques scripts \u00e0 notre image : 01-CreateScheme.sql CREATE TABLE public . departments ( id SERIAL PRIMARY KEY , name VARCHAR ( 20 ) NOT NULL ); CREATE TABLE public . students ( id SERIAL PRIMARY KEY , department_id INT NOT NULL REFERENCES departments ( id ), first_name VARCHAR ( 20 ) NOT NULL , last_name VARCHAR ( 20 ) NOT NULL ); 02-InsertData.sql INSERT INTO departments ( name ) VALUES ( 'IT' ); INSERT INTO departments ( name ) VALUES ( 'RH' ); INSERT INTO departments ( name ) VALUES ( 'Administration' ); INSERT INTO students ( department_id , first_name , last_name ) VALUES ( 1 , 'Eli' , 'Copter' ); INSERT INTO students ( department_id , first_name , last_name ) VALUES ( 2 , 'Emma' , 'Carena' ); INSERT INTO students ( department_id , first_name , last_name ) VALUES ( 2 , 'Jack' , 'Uzzi' ); INSERT INTO students ( department_id , first_name , last_name ) VALUES ( 3 , 'Aude' , 'Javel' ); Modifiez votre dockerfile en ajoutant ces scripts dans /docker-entrypoint-initdb.d , reconstruisez votre image et v\u00e9rifiez que vos scripts ont \u00e9t\u00e9 ex\u00e9cut\u00e9s au d\u00e9marrage et que les donn\u00e9es sont pr\u00e9sentes dans votre conteneur. Tip Lorsque nous parlons de /docker-entrypoint-initdb.d , il s'agit de l'int\u00e9rieur du conteneur, vous devez donc copier le contenu de votre r\u00e9pertoire dans le r\u00e9pertoire du conteneur. Tip N'oubliez pas de lancer adminer : docker run \\ -p \"8090:8080\" \\ --net = app-network \\ --name = adminer \\ -d \\ registry.takima.io/school/proxy/adminer Persistez les donn\u00e9es Vous avez peut-\u00eatre remarqu\u00e9, en effctuant des modifications de donn\u00e9es sur votre base avec adminer, que si votre conteneur de base de donn\u00e9es est d\u00e9truit, toutes vos donn\u00e9es sont r\u00e9initialis\u00e9es. Une base de donn\u00e9es doit persister durablement les donn\u00e9es : c'est sont r\u00f4le. Utilisez des volumes pour persister les donn\u00e9es sur le disque de l'h\u00f4te. -v /my/own/datadir:/var/lib/postgresql/data V\u00e9rifiez que les donn\u00e9es survivent lorsque votre conteneur est d\u00e9truit. Link Volumes Docker Question Pourquoi avons-nous besoin d'un volume attach\u00e9 \u00e0 notre conteneur PostgreSQL ? API Backend Fondamentaux Pour commencer, nous allons simplement ex\u00e9cuter une classe Java \"Hello World\" dans nos conteneurs, et seulement apr\u00e8s nous ex\u00e9cuterons un fichier JAR. Dans les deux cas, choisissez l'image appropri\u00e9e en gardant \u00e0 l'esprit que nous n'avons besoin que d'un environnement d'ex\u00e9cution Java . Voici une impl\u00e9mentation de Hello World en Java : Main.java public class Main { public static void main ( String [] args ) { System . out . println ( \"Hello World!\" ); } } 1- Compilez avec votre version cible de Java : javac Main.java . 2- R\u00e9digez le Dockerfile. FROM # TODO: Choisissez une version de Java JRE # TODO: Ajoutez le code Java (bytecode, .class) # TODO: Ex\u00e9cutez le code Java avec la commande : \"java Main\" 3- Maintenant, pour lancer l'application, vous devez faire la m\u00eame chose que dans l'\u00e9tape de base 1. Ici, vous avez un premier aper\u00e7u de votre application Backend. Dans l'\u00e9tape suivante, nous enrichirons le build (en utilisant Maven au lieu de javac minimaliste) et ex\u00e9cuterons un fichier JAR au lieu d'un simple .class. \u2192 Si cela r\u00e9ussit, vous devriez voir \"Hello World\" dans votre console. Build multistage Dans la section pr\u00e9c\u00e9dente, nous avons construit du code Java sur notre machine pour le faire fonctionner dans un conteneur Docker. Ne serait-il pas formidable que Docker g\u00e8re \u00e9galement le build ? Vous avez probablement remarqu\u00e9 que les images Docker openjdk par d\u00e9faut contiennent... Eh bien... un JDK ! Le probl\u00e8me du JDK c'est qu'il contient beaucoup de librairy et d'outil qui ne sont pas n\u00e9c\u00e9ssaire pour le run. Pour le run on pr\u00e9f\u00e8re avoir un JRE qui est bien moins lourd. Or rappelons que le but des images Docker est d'\u00eatre le plus l\u00e9ger possible. Cela tombe bien le multistage build sert \u00e0 cela : utiliser une image pour builder (JDK par exemple), puis transmettre le build dans une autre image qui servira au run (JRE par exemple). Cr\u00e9ez un build multistage en utilisant le multistage . Votre Dockerfile devrait ressembler \u00e0 ceci : FROM registry.takima.io/school/proxy/openjdk:17 # Construire Main.java # TODO : dans les prochaines \u00e9tapes (pas maintenant) FROM registry.takima.io/school/proxy/eclipse-temurin:17-jre-alpine # Copiez les ressources de l'\u00e9tape pr\u00e9c\u00e9dente COPY --from = 0 /usr/src/Main.class . # Ex\u00e9cutez le code Java avec le JRE # TODO : dans les prochaines \u00e9tapes (pas maintenant) Ne remplissez pas le Dockerfile maintenant, nous devrons le faire dans les prochaines \u00e9tapes. API Backend simple Nous d\u00e9ploierons une application Spring Boot fournissant une API simple avec un seul point d'acc\u00e8s de publication. Cr\u00e9ez votre application Spring Boot sur : Spring Initializer . Utilisez la configuration suivante : Projet : Maven Langage : Java 17 Spring Boot : 2.7.5 Emballage : Jar D\u00e9pendances : Spring Web G\u00e9n\u00e9rez le projet et cr\u00e9ez une classe GreetingController simple : package fr.takima.training.simpleapi.controller ; import org.springframework.web.bind.annotation.* ; import java.util.concurrent.atomic.AtomicLong ; @RestController public class GreetingController { private static final String template = \"Hello, %s!\" ; private final AtomicLong counter = new AtomicLong (); @GetMapping ( \"/\" ) public Greeting greeting ( @RequestParam ( value = \"name\" , defaultValue = \"World\" ) String name ) { return new Greeting ( counter . incrementAndGet (), String . format ( template , name )); } class Greeting { private final long id ; private final String content ; public Greeting ( long id , String content ) { this . id = id ; this . content = content ; } public long getId () { return id ; } public String getContent () { return content ; } } } Vous pouvez maintenant construire et d\u00e9marrer votre application. Bien s\u00fbr, vous aurez besoin de Maven et d'un JDK-17. Que diriez-vous d'avoir un conteneur pour construire et ex\u00e9cuter notre API simpliste ? Oh, attendez, nous avons Docker, voici comment vous pourriez construire et ex\u00e9cuter votre application avec Docker : # Build FROM registry.takima.io/school/proxy/maven:3.8.5-openjdk-17-slim AS myapp-build ENV MYAPP_HOME /opt/myapp WORKDIR $MYAPP_HOME COPY pom.xml . COPY src ./src RUN mvn package -DskipTests # Run FROM registry.takima.io/school/proxy/openjdk:17-slim ENV MYAPP_HOME /opt/myapp WORKDIR $MYAPP_HOME COPY --from = myapp-build $MYAPP_HOME /target/*.jar $MYAPP_HOME /myapp.jar ENTRYPOINT java -jar myapp.jar Question Pourquoi avons-nous besoin d'un build multistage ? Essayez de comprendre chaque \u00e9tape de ce dockerfile Check Une application Spring Boot simple avec un point de terminaison HelloWorld qui fonctionne. Avez-vous remarqu\u00e9 que Maven t\u00e9l\u00e9charge toutes les biblioth\u00e8ques \u00e0 chaque build de l'image ? Vous pouvez contribuer \u00e0 \u00e9conomiser de l'\u00e9nergie et du temps en mettant en cache les biblioth\u00e8ques lorsque le fichier pom de Maven n'a pas \u00e9t\u00e9 modifi\u00e9 en ex\u00e9cutant la commande : mvn dependency:go-offline . API Backend 3-tiers Nous avons vu comment builder et lancer du code Java. Passons maintenant au build et \u00e0 l'ex\u00e9cution de l'API qui nous int\u00e9resse pour notre application 3-tiers. Vous pouvez obtenir le code source compress\u00e9 ici : simple-api . Ajustez la configuration dans le fichier simple-api/src/main/resources/application.yml . Vous devrez indiquer les configuration pour acc\u00e8der \u00e0 votre base de donn\u00e9es. Normalement pour ce type de configuration qui d\u00e9pend de l'environement il est fortement recommand\u00e9 de ne pas hardcoder le setting dans l'image. On preferera utiliser des variables d'environnement que l'on indiquera au lancement du docker (option -e ou bien via un fichier d'env). datasource : url : \"jdbc:postgresql://${DATABASE_URL}:${DATABASE_PORT}/${POSTGRES_DB} Cr\u00e9er le Dockerfile de votre API comme vous l'avez fait dans le chapitre pr\u00e9c\u00e9dent. Builder votre image API et lancer le docker \u00e0 partir de cette image. Tip Attention votre API doit pouvoir se connecter \u00e0 la Base de donn\u00e9es. Comment faire ? C'est tr\u00e8s simpe : placez votre contenaur API dans le m\u00eame r\u00e9seau que votre DB, cr\u00e9ez un docker network si ce n'est pas d\u00e9j\u00e0 fait. Rappelez-vous \u00e9galement que dans un network vous pouvez requ\u00eater un conteneur sur son nom. Tip Votre API doit-\u00eatre publi\u00e9e pour pouvoir y acc\u00e8der. Vous aurez surement besoin de l'option -p au lancement du conteneur. Faites attention a utiliser un port disponible sur votre Host. Une fois que tout est correctement lanc\u00e9, vous devriez pouvoir acc\u00e9der \u00e0 votre API, par exemple sur : /departments/IT/students . (d\u00e9pend du port mapping que vous avez r\u00e9alis\u00e9) [ { \"id\" : 1 , \"firstname\" : \"Eli\" , \"lastname\" : \"Copter\" , \"department\" : { \"id\" : 1 , \"name\" : \"IT\" } } ] Explorez d'autres points d'acc\u00e8s de votre API, jetez un coup d'\u0153il aux contr\u00f4leurs dans le code source. Check Une API Web fonctionne sur votre base de donn\u00e9es. Serveur HTTP Choisissez une image de base appropri\u00e9e. Serveur HTTP Cr\u00e9ez une page de destination simple : index.html et placez-la dans votre conteneur. Cela devrait suffire pour l'instant, d\u00e9marrez votre conteneur et v\u00e9rifiez que tout fonctionne comme pr\u00e9vu. Voici quelques commandes que vous pouvez essayer pour v\u00e9rifier : docker stats docker inspect docker logs !!! link - Httpd - Mise en route R\u00e9cup\u00e9ration de la Configuration Vous utilisez la configuration Apache par d\u00e9faut, et cela devrait suffire pour l'instant. Utilisez docker exec pour r\u00e9cup\u00e9rer cette configuration par d\u00e9faut depuis votre conteneur en cours d'ex\u00e9cution /usr/local/apache2/conf/httpd.conf . Note Vous pouvez \u00e9galement utiliser docker cp . Check Avoir un fichier httpd.conf en local pr\u00eat \u00e0 \u00eatre \u00e9dit\u00e9. Reverse Proxy (RPX) Nous allons configurer le serveur HTTP en tant que serveur reverse proxy devant notre application. Ce serveur pourrait \u00eatre utilis\u00e9 pour fournir une application front, configurer un endpoint SSL ou g\u00e9rer la r\u00e9partition de charge. Donc, cela peut \u00eatre assez utile m\u00eame si, dans notre cas, nous garderons les choses simples. En effet, il servira de passerelle pour acc\u00e9der \u00e0 votre API (proxyPass). Voici la documentation : Reverse Proxy . Ajoutez ce qui suit \u00e0 la configuration httpd.conf que vous avez r\u00e9cup\u00e9r\u00e9 pr\u00e9c\u00e9demment, modifier votre Dockerfile pour injecter cette configuration : ServerName localhost <VirtualHost *:80> ProxyPreserveHost On ProxyPass / http://YOUR_BACKEND_LINK:8080/ ProxyPassReverse / http://YOUR_BACKEND_LINK:8080/ </VirtualHost> LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so Tip COPY ./httpd.conf /usr/local/apache2/conf/httpd.conf devrait \u00eatre pr\u00e9sent dans votre dockerfile YOUR_BACKEND_LINK correspond \u00e0 l'url de votre API (nom du docker api gr\u00e2ce au DNS interne) lancez votre front httpd avec les bonnes options (bon network notamment) V\u00e9rifiez que votre reverse proxy sert bien l'API Cr\u00e9ons une stack Maintenant imaginons que je vous demande de tout d\u00e9truire et de tout reconstruire. O\u00f9 bien de me cloner cette stack 3-tiers, vous devrez relancer l'ensemble des commandes, cr\u00e9er les networks, les volumes, les conteneurs. Heureusement, pour cr\u00e9er des stack compl\u00eate Docker Compose arrive \u00e0 la rescousse ! Docker-compose 1- Installez docker-compose si la commande docker compose ne fonctionne pas. Vous avez peut-\u00eatre remarqu\u00e9 qu'il peut \u00eatre assez fastidieux d'orchestrer manuellement le d\u00e9marrage, l'arr\u00eat et la reconstruction de nos conteneurs. Heureusement, un outil utile appel\u00e9 docker-compose est utile dans ces situations. 2- Cr\u00e9ez un fichier docker-compose.yml avec la structure suivante pour d\u00e9finir et piloter nos conteneurs : version : \"3.7\" services : backend : build : #TODO networks : #TODO depends_on : #TODO database : build : #TODO networks : #TODO httpd : build : #TODO ports : #TODO networks : #TODO depends_on : #TODO networks : my-network : Docker-compose g\u00e9rera les trois conteneurs et un r\u00e9seau pour nous. Une fois que vos conteneurs sont orchestr\u00e9s par docker-compose, vous devriez disposer d'une application parfaitement fonctionnelle. Assurez-vous de pouvoir acc\u00e9der \u00e0 votre API sur localhost . Note Les ports de votre backend et de votre base de donn\u00e9es ne doivent pas \u00eatre ouverts sur votre machine h\u00f4te. Question Pourquoi docker compose est-il si important ? Check Une application 3-tiers fonctionne avec docker-compose. Bonus - Am\u00e9lioration du Docker compose Ajoutez un volume \u00e0 votre base de donn\u00e9es volumes : my_db_volume : driver : local Utilisez un fichier .env pour stocker vos variables d'environnement (pour la DB par exemple): database : env_file : - database/.env s\u00e9greguez votre stack avec plusieurs networks : un network front (front, API) / un network back (DB, API) Mettez en place un health check sur votre base de donn\u00e9es, et un depends_on sur votre API qui fonctionnera sur la readiness de votre DB # cot\u00e9 compose api depends_on : database : condition : service_healthy # cot\u00e9 compose database healthcheck : test : [ \"CMD-SHELL\" , \"pg_isready -U postgres\" ] interval : 10s timeout : 5s retries : 3 Bonus - Publication de vos images Vos images Docker sont stock\u00e9es localement, publions-les pour qu'elles puissent \u00eatre utilis\u00e9es par d'autres membres de l'\u00e9quipe ou sur d'autres machines. En effet l'un des \u00e9l\u00e9ments fondamental de l'\u00e9cosyst\u00e8me est la publication des images (le plus leger possible), dans le flow de d\u00e9velopement c'est ce qu'on appelle le Delivery . Dans notre cas nous allons publier nos images sur le registry Docker principale : Docker Hub Vous aurez besoin d'un compte Docker Hub . 1- Connectez-vous \u00e0 votre compte Docker Hub fra\u00eechement cr\u00e9\u00e9 avec docker login . 2- \u00c9tiquetez votre image. Jusqu'\u00e0 pr\u00e9sent, nous n'avons utilis\u00e9 que l'\u00e9tiquette \"latest\", maintenant que nous voulons la publier, ajoutons des informations de version significatives \u00e0 nos images. docker tag my-database USERNAME/my-database:1.0 3- Ensuite, poussez votre image sur Docker Hub : docker push USERNAME/my-database Docker Hub n'est pas le seul registre d'images Docker, et vous pouvez \u00e9galement h\u00e9berger vos propres images (c'est \u00e9videmment le choix de la plupart des entreprises). Une fois que vous avez publi\u00e9 vos images sur Docker Hub, vous les verrez dans votre compte : il serait tr\u00e8s utile d'avoir une documentation pour votre image si vous pr\u00e9voyez de la partager avec d'autres personnes. Note Vous pouvez \u00e9galement utiliser le registre Google Container, Amazon ECR ou bien au plus proche de votre code sur Gitlab ou Github.Vous pouvez aussi d\u00e9ployer votre propre registry avec des outils comme Portus ou Harbor . Check Des images Docker publi\u00e9es. Conclusion Cela devrait vous donner un bon aper\u00e7u de l'utilisation de Docker. Vous avez mis en place une application trois tiers, configur\u00e9 une base de donn\u00e9es, une API Backend et un serveur HTTP. Tout cela est orchestr\u00e9 par Docker Compose. Enfin, vous avez publi\u00e9 vos images sur Docker Hub pour les partager avec d'autres. \u00a9 Takima 2023 \"","title":"TP - Docker"},{"location":"ch1-discover-docker-tp/#decouvrir-docker","text":"","title":"D\u00e9couvrir Docker"},{"location":"ch1-discover-docker-tp/#objectifs","text":"","title":"Objectifs"},{"location":"ch1-discover-docker-tp/#bonnes-pratiques","text":"Tout au long du TP vous allez cr\u00e9er des fichiers li\u00e9s a docker ou docker-compose ou li\u00e9s a du code que nous allons utiliser. Pour vous y retrouver, cr\u00e9ez une structure de fichiers appropri\u00e9e, 1 dossier par image build\u00e9e par exemple.","title":"Bonnes pratiques"},{"location":"ch1-discover-docker-tp/#application-cible","text":"Application trois tiers: Serveur HTTP (simple revers proxy dans un premier temps) API Backend Base de donn\u00e9es Pour chacune de ces applications, nous suivrons le m\u00eame processus : choisir l'image de base Docker appropri\u00e9e, cr\u00e9er et configurer cette image, ajouter les sp\u00e9cificit\u00e9s de notre application et, \u00e0 un moment donn\u00e9, la faire fonctionner. Notre objectif final est d'avoir une application trois tiers qui tourne.","title":"Application cible"},{"location":"ch1-discover-docker-tp/#images-de-base","text":"Ces images seront celles utilis\u00e9es comme point de d\u00e9part pour builder vos images custom qui feront tourner votre application. Serveur HTTP API Backend Base de donn\u00e9es Pour \u00e9viter le rate limit de Docker Hub nous vous fournissons ces m\u00eame image dans notre registry public :","title":"Images de base"},{"location":"ch1-discover-docker-tp/#base-de-donnees","text":"","title":"Base de donn\u00e9es"},{"location":"ch1-discover-docker-tp/#fondamentaux","text":"Nous utiliserons l'image : postgres:14.1-alpine. Faisons fonctionner un simple serveur PostgreSQL. Voici ce qui serait un Dockerfile minimal : FROM registry.takima.io/school/proxy/postgres:14.1-alpine ENV POSTGRES_DB = db \\ POSTGRES_USER = usr \\ POSTGRES_PASSWORD = pwd Construisez cette image et d\u00e9marrez un conteneur correctement, vous devriez pouvoir acc\u00e9der \u00e0 votre base de donn\u00e9es en fonction du port de publication que vous choisissez : localhost:PORT. Note Ici les variable ENV sont indiquer pour \u00eatre prise par d\u00e9fault. Il est ensuite conseiller de les override lors du lancement du conteneur avec l'option -e Votre base de donn\u00e9es PostgreSQL devrait \u00eatre op\u00e9rationnelle. Connectez-vous \u00e0 votre base de donn\u00e9es et v\u00e9rifiez que tout fonctionne correctement. N'oubliez pas de nommer votre image Docker et votre conteneur. Tip Si vous avez des difficult\u00e9s, revenez \u00e0 la partie Construire l'image et Ex\u00e9cuter votre image sur TD01 - Docker ( TD 1 D\u00e9couverte de Docker ). Relancez votre base de donn\u00e9es et Adminer avec --network app-network pour permettre la communication Adminer/Base de donn\u00e9es. Nous utilisons -\u2013network pour placer adminer et postgres dans le m\u00eame r\u00e9seau, au lieu de -\u2013link car ce dernier est obsol\u00e8te. Tip N'oubliez pas de cr\u00e9er votre r\u00e9seau docker network create app-network Si vous n'avez pas plac\u00e9 le conteneur dans le bon r\u00e9seau vous pouvez le connecter avec docker network connect my_net my_container Est-il correct d'avoir des mots de passe \u00e9crits en clair dans un fichier ? Vous pouvez plut\u00f4t d\u00e9finir ces param\u00e8tres d'environnement lors de l'ex\u00e9cution de l'image en utilisant l'option -e . Tip Pourquoi ex\u00e9cutons-nous le conteneur avec une option -e pour fournir les variables d'environnement ?","title":"Fondamentaux"},{"location":"ch1-discover-docker-tp/#initialisation-de-la-base-de-donnees","text":"parfois il peut \u00eatre pratique d'avoir notre structure de base de donn\u00e9es initialis\u00e9e \u00e0 l'int\u00e9rieur m\u00eame de l'image Docker ainsi que quelques donn\u00e9es initiales. Tout script SQL trouv\u00e9 dans /docker-entrypoint-initdb.d sera ex\u00e9cut\u00e9 dans l'ordre alphab\u00e9tique, ajoutons donc quelques scripts \u00e0 notre image : 01-CreateScheme.sql CREATE TABLE public . departments ( id SERIAL PRIMARY KEY , name VARCHAR ( 20 ) NOT NULL ); CREATE TABLE public . students ( id SERIAL PRIMARY KEY , department_id INT NOT NULL REFERENCES departments ( id ), first_name VARCHAR ( 20 ) NOT NULL , last_name VARCHAR ( 20 ) NOT NULL ); 02-InsertData.sql INSERT INTO departments ( name ) VALUES ( 'IT' ); INSERT INTO departments ( name ) VALUES ( 'RH' ); INSERT INTO departments ( name ) VALUES ( 'Administration' ); INSERT INTO students ( department_id , first_name , last_name ) VALUES ( 1 , 'Eli' , 'Copter' ); INSERT INTO students ( department_id , first_name , last_name ) VALUES ( 2 , 'Emma' , 'Carena' ); INSERT INTO students ( department_id , first_name , last_name ) VALUES ( 2 , 'Jack' , 'Uzzi' ); INSERT INTO students ( department_id , first_name , last_name ) VALUES ( 3 , 'Aude' , 'Javel' ); Modifiez votre dockerfile en ajoutant ces scripts dans /docker-entrypoint-initdb.d , reconstruisez votre image et v\u00e9rifiez que vos scripts ont \u00e9t\u00e9 ex\u00e9cut\u00e9s au d\u00e9marrage et que les donn\u00e9es sont pr\u00e9sentes dans votre conteneur. Tip Lorsque nous parlons de /docker-entrypoint-initdb.d , il s'agit de l'int\u00e9rieur du conteneur, vous devez donc copier le contenu de votre r\u00e9pertoire dans le r\u00e9pertoire du conteneur. Tip N'oubliez pas de lancer adminer : docker run \\ -p \"8090:8080\" \\ --net = app-network \\ --name = adminer \\ -d \\ registry.takima.io/school/proxy/adminer","title":"Initialisation de la base de donn\u00e9es"},{"location":"ch1-discover-docker-tp/#persistez-les-donnees","text":"Vous avez peut-\u00eatre remarqu\u00e9, en effctuant des modifications de donn\u00e9es sur votre base avec adminer, que si votre conteneur de base de donn\u00e9es est d\u00e9truit, toutes vos donn\u00e9es sont r\u00e9initialis\u00e9es. Une base de donn\u00e9es doit persister durablement les donn\u00e9es : c'est sont r\u00f4le. Utilisez des volumes pour persister les donn\u00e9es sur le disque de l'h\u00f4te. -v /my/own/datadir:/var/lib/postgresql/data V\u00e9rifiez que les donn\u00e9es survivent lorsque votre conteneur est d\u00e9truit. Link Volumes Docker Question Pourquoi avons-nous besoin d'un volume attach\u00e9 \u00e0 notre conteneur PostgreSQL ?","title":"Persistez les donn\u00e9es"},{"location":"ch1-discover-docker-tp/#api-backend","text":"","title":"API Backend"},{"location":"ch1-discover-docker-tp/#fondamentaux_1","text":"Pour commencer, nous allons simplement ex\u00e9cuter une classe Java \"Hello World\" dans nos conteneurs, et seulement apr\u00e8s nous ex\u00e9cuterons un fichier JAR. Dans les deux cas, choisissez l'image appropri\u00e9e en gardant \u00e0 l'esprit que nous n'avons besoin que d'un environnement d'ex\u00e9cution Java . Voici une impl\u00e9mentation de Hello World en Java : Main.java public class Main { public static void main ( String [] args ) { System . out . println ( \"Hello World!\" ); } } 1- Compilez avec votre version cible de Java : javac Main.java . 2- R\u00e9digez le Dockerfile. FROM # TODO: Choisissez une version de Java JRE # TODO: Ajoutez le code Java (bytecode, .class) # TODO: Ex\u00e9cutez le code Java avec la commande : \"java Main\" 3- Maintenant, pour lancer l'application, vous devez faire la m\u00eame chose que dans l'\u00e9tape de base 1. Ici, vous avez un premier aper\u00e7u de votre application Backend. Dans l'\u00e9tape suivante, nous enrichirons le build (en utilisant Maven au lieu de javac minimaliste) et ex\u00e9cuterons un fichier JAR au lieu d'un simple .class. \u2192 Si cela r\u00e9ussit, vous devriez voir \"Hello World\" dans votre console.","title":"Fondamentaux"},{"location":"ch1-discover-docker-tp/#build-multistage","text":"Dans la section pr\u00e9c\u00e9dente, nous avons construit du code Java sur notre machine pour le faire fonctionner dans un conteneur Docker. Ne serait-il pas formidable que Docker g\u00e8re \u00e9galement le build ? Vous avez probablement remarqu\u00e9 que les images Docker openjdk par d\u00e9faut contiennent... Eh bien... un JDK ! Le probl\u00e8me du JDK c'est qu'il contient beaucoup de librairy et d'outil qui ne sont pas n\u00e9c\u00e9ssaire pour le run. Pour le run on pr\u00e9f\u00e8re avoir un JRE qui est bien moins lourd. Or rappelons que le but des images Docker est d'\u00eatre le plus l\u00e9ger possible. Cela tombe bien le multistage build sert \u00e0 cela : utiliser une image pour builder (JDK par exemple), puis transmettre le build dans une autre image qui servira au run (JRE par exemple). Cr\u00e9ez un build multistage en utilisant le multistage . Votre Dockerfile devrait ressembler \u00e0 ceci : FROM registry.takima.io/school/proxy/openjdk:17 # Construire Main.java # TODO : dans les prochaines \u00e9tapes (pas maintenant) FROM registry.takima.io/school/proxy/eclipse-temurin:17-jre-alpine # Copiez les ressources de l'\u00e9tape pr\u00e9c\u00e9dente COPY --from = 0 /usr/src/Main.class . # Ex\u00e9cutez le code Java avec le JRE # TODO : dans les prochaines \u00e9tapes (pas maintenant) Ne remplissez pas le Dockerfile maintenant, nous devrons le faire dans les prochaines \u00e9tapes.","title":"Build multistage"},{"location":"ch1-discover-docker-tp/#api-backend-simple","text":"Nous d\u00e9ploierons une application Spring Boot fournissant une API simple avec un seul point d'acc\u00e8s de publication. Cr\u00e9ez votre application Spring Boot sur : Spring Initializer . Utilisez la configuration suivante : Projet : Maven Langage : Java 17 Spring Boot : 2.7.5 Emballage : Jar D\u00e9pendances : Spring Web G\u00e9n\u00e9rez le projet et cr\u00e9ez une classe GreetingController simple : package fr.takima.training.simpleapi.controller ; import org.springframework.web.bind.annotation.* ; import java.util.concurrent.atomic.AtomicLong ; @RestController public class GreetingController { private static final String template = \"Hello, %s!\" ; private final AtomicLong counter = new AtomicLong (); @GetMapping ( \"/\" ) public Greeting greeting ( @RequestParam ( value = \"name\" , defaultValue = \"World\" ) String name ) { return new Greeting ( counter . incrementAndGet (), String . format ( template , name )); } class Greeting { private final long id ; private final String content ; public Greeting ( long id , String content ) { this . id = id ; this . content = content ; } public long getId () { return id ; } public String getContent () { return content ; } } } Vous pouvez maintenant construire et d\u00e9marrer votre application. Bien s\u00fbr, vous aurez besoin de Maven et d'un JDK-17. Que diriez-vous d'avoir un conteneur pour construire et ex\u00e9cuter notre API simpliste ? Oh, attendez, nous avons Docker, voici comment vous pourriez construire et ex\u00e9cuter votre application avec Docker : # Build FROM registry.takima.io/school/proxy/maven:3.8.5-openjdk-17-slim AS myapp-build ENV MYAPP_HOME /opt/myapp WORKDIR $MYAPP_HOME COPY pom.xml . COPY src ./src RUN mvn package -DskipTests # Run FROM registry.takima.io/school/proxy/openjdk:17-slim ENV MYAPP_HOME /opt/myapp WORKDIR $MYAPP_HOME COPY --from = myapp-build $MYAPP_HOME /target/*.jar $MYAPP_HOME /myapp.jar ENTRYPOINT java -jar myapp.jar Question Pourquoi avons-nous besoin d'un build multistage ? Essayez de comprendre chaque \u00e9tape de ce dockerfile Check Une application Spring Boot simple avec un point de terminaison HelloWorld qui fonctionne. Avez-vous remarqu\u00e9 que Maven t\u00e9l\u00e9charge toutes les biblioth\u00e8ques \u00e0 chaque build de l'image ? Vous pouvez contribuer \u00e0 \u00e9conomiser de l'\u00e9nergie et du temps en mettant en cache les biblioth\u00e8ques lorsque le fichier pom de Maven n'a pas \u00e9t\u00e9 modifi\u00e9 en ex\u00e9cutant la commande : mvn dependency:go-offline .","title":"API Backend simple"},{"location":"ch1-discover-docker-tp/#api-backend-3-tiers","text":"Nous avons vu comment builder et lancer du code Java. Passons maintenant au build et \u00e0 l'ex\u00e9cution de l'API qui nous int\u00e9resse pour notre application 3-tiers. Vous pouvez obtenir le code source compress\u00e9 ici : simple-api . Ajustez la configuration dans le fichier simple-api/src/main/resources/application.yml . Vous devrez indiquer les configuration pour acc\u00e8der \u00e0 votre base de donn\u00e9es. Normalement pour ce type de configuration qui d\u00e9pend de l'environement il est fortement recommand\u00e9 de ne pas hardcoder le setting dans l'image. On preferera utiliser des variables d'environnement que l'on indiquera au lancement du docker (option -e ou bien via un fichier d'env). datasource : url : \"jdbc:postgresql://${DATABASE_URL}:${DATABASE_PORT}/${POSTGRES_DB} Cr\u00e9er le Dockerfile de votre API comme vous l'avez fait dans le chapitre pr\u00e9c\u00e9dent. Builder votre image API et lancer le docker \u00e0 partir de cette image. Tip Attention votre API doit pouvoir se connecter \u00e0 la Base de donn\u00e9es. Comment faire ? C'est tr\u00e8s simpe : placez votre contenaur API dans le m\u00eame r\u00e9seau que votre DB, cr\u00e9ez un docker network si ce n'est pas d\u00e9j\u00e0 fait. Rappelez-vous \u00e9galement que dans un network vous pouvez requ\u00eater un conteneur sur son nom. Tip Votre API doit-\u00eatre publi\u00e9e pour pouvoir y acc\u00e8der. Vous aurez surement besoin de l'option -p au lancement du conteneur. Faites attention a utiliser un port disponible sur votre Host. Une fois que tout est correctement lanc\u00e9, vous devriez pouvoir acc\u00e9der \u00e0 votre API, par exemple sur : /departments/IT/students . (d\u00e9pend du port mapping que vous avez r\u00e9alis\u00e9) [ { \"id\" : 1 , \"firstname\" : \"Eli\" , \"lastname\" : \"Copter\" , \"department\" : { \"id\" : 1 , \"name\" : \"IT\" } } ] Explorez d'autres points d'acc\u00e8s de votre API, jetez un coup d'\u0153il aux contr\u00f4leurs dans le code source. Check Une API Web fonctionne sur votre base de donn\u00e9es.","title":"API Backend 3-tiers"},{"location":"ch1-discover-docker-tp/#serveur-http","text":"","title":"Serveur HTTP"},{"location":"ch1-discover-docker-tp/#choisissez-une-image-de-base-appropriee","text":"Serveur HTTP Cr\u00e9ez une page de destination simple : index.html et placez-la dans votre conteneur. Cela devrait suffire pour l'instant, d\u00e9marrez votre conteneur et v\u00e9rifiez que tout fonctionne comme pr\u00e9vu. Voici quelques commandes que vous pouvez essayer pour v\u00e9rifier : docker stats docker inspect docker logs !!! link - Httpd - Mise en route","title":"Choisissez une image de base appropri\u00e9e."},{"location":"ch1-discover-docker-tp/#recuperation-de-la-configuration","text":"Vous utilisez la configuration Apache par d\u00e9faut, et cela devrait suffire pour l'instant. Utilisez docker exec pour r\u00e9cup\u00e9rer cette configuration par d\u00e9faut depuis votre conteneur en cours d'ex\u00e9cution /usr/local/apache2/conf/httpd.conf . Note Vous pouvez \u00e9galement utiliser docker cp . Check Avoir un fichier httpd.conf en local pr\u00eat \u00e0 \u00eatre \u00e9dit\u00e9.","title":"R\u00e9cup\u00e9ration de la Configuration"},{"location":"ch1-discover-docker-tp/#reverse-proxy-rpx","text":"Nous allons configurer le serveur HTTP en tant que serveur reverse proxy devant notre application. Ce serveur pourrait \u00eatre utilis\u00e9 pour fournir une application front, configurer un endpoint SSL ou g\u00e9rer la r\u00e9partition de charge. Donc, cela peut \u00eatre assez utile m\u00eame si, dans notre cas, nous garderons les choses simples. En effet, il servira de passerelle pour acc\u00e9der \u00e0 votre API (proxyPass). Voici la documentation : Reverse Proxy . Ajoutez ce qui suit \u00e0 la configuration httpd.conf que vous avez r\u00e9cup\u00e9r\u00e9 pr\u00e9c\u00e9demment, modifier votre Dockerfile pour injecter cette configuration : ServerName localhost <VirtualHost *:80> ProxyPreserveHost On ProxyPass / http://YOUR_BACKEND_LINK:8080/ ProxyPassReverse / http://YOUR_BACKEND_LINK:8080/ </VirtualHost> LoadModule proxy_module modules/mod_proxy.so LoadModule proxy_http_module modules/mod_proxy_http.so Tip COPY ./httpd.conf /usr/local/apache2/conf/httpd.conf devrait \u00eatre pr\u00e9sent dans votre dockerfile YOUR_BACKEND_LINK correspond \u00e0 l'url de votre API (nom du docker api gr\u00e2ce au DNS interne) lancez votre front httpd avec les bonnes options (bon network notamment) V\u00e9rifiez que votre reverse proxy sert bien l'API","title":"Reverse Proxy (RPX)"},{"location":"ch1-discover-docker-tp/#creons-une-stack","text":"Maintenant imaginons que je vous demande de tout d\u00e9truire et de tout reconstruire. O\u00f9 bien de me cloner cette stack 3-tiers, vous devrez relancer l'ensemble des commandes, cr\u00e9er les networks, les volumes, les conteneurs. Heureusement, pour cr\u00e9er des stack compl\u00eate Docker Compose arrive \u00e0 la rescousse !","title":"Cr\u00e9ons une stack"},{"location":"ch1-discover-docker-tp/#docker-compose","text":"1- Installez docker-compose si la commande docker compose ne fonctionne pas. Vous avez peut-\u00eatre remarqu\u00e9 qu'il peut \u00eatre assez fastidieux d'orchestrer manuellement le d\u00e9marrage, l'arr\u00eat et la reconstruction de nos conteneurs. Heureusement, un outil utile appel\u00e9 docker-compose est utile dans ces situations. 2- Cr\u00e9ez un fichier docker-compose.yml avec la structure suivante pour d\u00e9finir et piloter nos conteneurs : version : \"3.7\" services : backend : build : #TODO networks : #TODO depends_on : #TODO database : build : #TODO networks : #TODO httpd : build : #TODO ports : #TODO networks : #TODO depends_on : #TODO networks : my-network : Docker-compose g\u00e9rera les trois conteneurs et un r\u00e9seau pour nous. Une fois que vos conteneurs sont orchestr\u00e9s par docker-compose, vous devriez disposer d'une application parfaitement fonctionnelle. Assurez-vous de pouvoir acc\u00e9der \u00e0 votre API sur localhost . Note Les ports de votre backend et de votre base de donn\u00e9es ne doivent pas \u00eatre ouverts sur votre machine h\u00f4te. Question Pourquoi docker compose est-il si important ? Check Une application 3-tiers fonctionne avec docker-compose.","title":"Docker-compose"},{"location":"ch1-discover-docker-tp/#bonus-amelioration-du-docker-compose","text":"Ajoutez un volume \u00e0 votre base de donn\u00e9es volumes : my_db_volume : driver : local Utilisez un fichier .env pour stocker vos variables d'environnement (pour la DB par exemple): database : env_file : - database/.env s\u00e9greguez votre stack avec plusieurs networks : un network front (front, API) / un network back (DB, API) Mettez en place un health check sur votre base de donn\u00e9es, et un depends_on sur votre API qui fonctionnera sur la readiness de votre DB # cot\u00e9 compose api depends_on : database : condition : service_healthy # cot\u00e9 compose database healthcheck : test : [ \"CMD-SHELL\" , \"pg_isready -U postgres\" ] interval : 10s timeout : 5s retries : 3","title":"Bonus - Am\u00e9lioration du Docker compose"},{"location":"ch1-discover-docker-tp/#bonus-publication-de-vos-images","text":"Vos images Docker sont stock\u00e9es localement, publions-les pour qu'elles puissent \u00eatre utilis\u00e9es par d'autres membres de l'\u00e9quipe ou sur d'autres machines. En effet l'un des \u00e9l\u00e9ments fondamental de l'\u00e9cosyst\u00e8me est la publication des images (le plus leger possible), dans le flow de d\u00e9velopement c'est ce qu'on appelle le Delivery . Dans notre cas nous allons publier nos images sur le registry Docker principale : Docker Hub Vous aurez besoin d'un compte Docker Hub . 1- Connectez-vous \u00e0 votre compte Docker Hub fra\u00eechement cr\u00e9\u00e9 avec docker login . 2- \u00c9tiquetez votre image. Jusqu'\u00e0 pr\u00e9sent, nous n'avons utilis\u00e9 que l'\u00e9tiquette \"latest\", maintenant que nous voulons la publier, ajoutons des informations de version significatives \u00e0 nos images. docker tag my-database USERNAME/my-database:1.0 3- Ensuite, poussez votre image sur Docker Hub : docker push USERNAME/my-database Docker Hub n'est pas le seul registre d'images Docker, et vous pouvez \u00e9galement h\u00e9berger vos propres images (c'est \u00e9videmment le choix de la plupart des entreprises). Une fois que vous avez publi\u00e9 vos images sur Docker Hub, vous les verrez dans votre compte : il serait tr\u00e8s utile d'avoir une documentation pour votre image si vous pr\u00e9voyez de la partager avec d'autres personnes. Note Vous pouvez \u00e9galement utiliser le registre Google Container, Amazon ECR ou bien au plus proche de votre code sur Gitlab ou Github.Vous pouvez aussi d\u00e9ployer votre propre registry avec des outils comme Portus ou Harbor . Check Des images Docker publi\u00e9es.","title":"Bonus - Publication de vos images"},{"location":"ch1-discover-docker-tp/#conclusion","text":"Cela devrait vous donner un bon aper\u00e7u de l'utilisation de Docker. Vous avez mis en place une application trois tiers, configur\u00e9 une base de donn\u00e9es, une API Backend et un serveur HTTP. Tout cela est orchestr\u00e9 par Docker Compose. Enfin, vous avez publi\u00e9 vos images sur Docker Hub pour les partager avec d'autres. \u00a9 Takima 2023 \"","title":"Conclusion"},{"location":"ch1-getting-start/","text":"Getting Start Installer le moteur Docker Vous pouvez passer cette \u00e9tape si vous avez d\u00e9j\u00e0 install\u00e9 Docker. Warning \u00c0 partir de 2022, Docker Desktop n\u00e9cessite d\u00e9sormais un abonnement payant pour une utilisation commerciale dans les grandes entreprises. Docker Desktop est un utilitaire pratique qui inclut le Docker Engine , ainsi qu'une interface utilisateur graphique. Le Docker Engine est la technologie de base derri\u00e8re Docker. Il s'agit d'un logiciel open source qui fonctionne sous Linux en tant que service qui permet d'ex\u00e9cuter des conteneurs au-dessus du noyau Linux. Il est responsable du cycle de vie des conteneurs et de l'isolation des ressources physiques (calcul, m\u00e9moire, stockage) auxquelles les conteneurs peuvent acc\u00e9der. Le moteur peut s'ex\u00e9cuter sur une machine physique ou virtuelle, mais il ne peut fonctionner que sur un noyau Linux, c'est-\u00e0-dire sur un syst\u00e8me d'exploitation bas\u00e9 sur Linux. Il est important de comprendre que le moteur Docker ne s'ex\u00e9cute que sous Linux. Le Docker CLI est un outil en ligne de commande gratuit ( CLI ) pour cr\u00e9er et g\u00e9rer des objets Docker , tels que des images , des conteneurs , des r\u00e9seaux et des volumes . Voici les alternatives \u00e0 Docker Desktop Sur Linux Sur Linux c'est tr\u00e8s simple il suffit d'installer docker. Nous allons utiliser ici le script clef en main d'installation : Installez le Docker Engine pour Linux documentation officielle : curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh Ajoutez votre utilisateur au groupe docker : sudo usermod -aG docker $USER Warning En tant qu'utilisateur Linux, vous pouvez utiliser uniquement le Moteur Docker , m\u00eame si la documentation officielle encourage l'adoption de Docker Desktop pour Linux . Sur Windows Nous allons utiliser Windows Linux Subsystem Tout d'abord, il faut v\u00e9rifier que la fonctionnalit\u00e9 est bien activ\u00e9e sur Windows. Depuis la barre de recherche Windows, ouvrez Activer ou d\u00e9sactiver une fonctionnalit\u00e9 Windows et cochez Sous-syt\u00e8me Windows pour Linux et plateforme de machine virtuelle . Puis installez les deux applications du Microsoft Store Maintenant, ouvrez l'application Ubuntu pr\u00e9c\u00e9demment install\u00e9e. Il va vous \u00eatre demand\u00e9 de configurer un user et un mot de passe. Enfin, nous allons installer docker Update Ubtuntu sudo apt update Installer Docker curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh V\u00e9rifier l'installation docker -v Using docker without root permissions A cette \u00e9tape, docker a besoin des permissions root pour fonctionner : docker run registry.takima.io/school/proxy/hello-world , va renvoyer une erreur de permissions. Pour pallier \u00e0 cela executez les commandes suivantes sudo addgroup --system docker sudo adduser $USER docker newgrp docker sudo chown root:docker /var/run/docker.sock sudo chmod g+w /var/run/docker.sock Test docker run registry.takima.io/school/proxy/hello-world Installer Docker-compose sudo curl -L \"https://github.com/docker/compose/releases/download/v2.23.0/docker-compose- $( uname -s ) - $( uname -m ) \" -o /usr/local/bin/docker-compose V\u00e9rifier l'installation docker-compose -v Troubleshooting Si en utilisant docker-compose vous avez cette erreur : Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? Lancez le service docker sudo service docker start Sur MacOs Installer Docker Engine sur macOS sans utiliser Docker Desktop peut \u00eatre plus complexe. Pour ce faire nous allons utiliser Minikube qui embarque \u00e9gallement une impl\u00e9mentation du daemon Docker qui nous permettra de lancer des conteneurs. En plus, c'est parfait, car vous serez pr\u00eat \u00e0 vous exercer a Kubernetes, mais c'est une autre histoire. Note Avant de commencer, assurez-vous que vous avez install\u00e9 Homebrew sur votre syst\u00e8me macOS. Si ce n'est pas le cas, vous pouvez l'installer en ex\u00e9cutant la commande suivante dans votre terminal : bash /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\" \u00c9tapes pour installer Docker sur macOS sans Docker Desktop : Pour cela on utioisera une approche Docker Machine c'est \u00e0 dire lancer une VM pour faire tourner docker engine. Install hyperkit and minikube brew install hyperkit brew install minikube Attention pour les puces M1 darwin/arm64, il y a incompatibilit\u00e9 avec les driver de virtualisation hyperkit ou virtualbox. Un drivers compatible est QEMU : brew install qemu Install Docker CLI brew install docker brew install docker-compose Start minikube suivant votre driver de virtualisation : minikube config set driver qemu puis minikube start Tell Docker CLI to talk to minikube's VM eval $( minikube docker-env ) Save IP to a hostname echo \"`minikube ip` docker.local\" | sudo tee -a /etc/hosts > /dev/null Test docker run hello-world Cela devrait vous permettre d'utiliser Docker Engine sur macOS sans Docker Desktop. Note Minikube ex\u00e9cute une configuration Kubernetes et, par cons\u00e9quent, ex\u00e9cute de nombreux conteneurs qui ne sont pas n\u00e9cessaires si l'on n'utilise pas Kubernetes. Nous pouvons ex\u00e9cuter la commande minikube pause pour mettre en pause les conteneurs li\u00e9s \u00e0 Kubernetes afin qu'ils ne consomment pas de ressources syst\u00e8me inutilement.","title":"Getting Start - Docker"},{"location":"ch1-getting-start/#getting-start","text":"","title":"Getting Start"},{"location":"ch1-getting-start/#installer-le-moteur-docker","text":"Vous pouvez passer cette \u00e9tape si vous avez d\u00e9j\u00e0 install\u00e9 Docker. Warning \u00c0 partir de 2022, Docker Desktop n\u00e9cessite d\u00e9sormais un abonnement payant pour une utilisation commerciale dans les grandes entreprises. Docker Desktop est un utilitaire pratique qui inclut le Docker Engine , ainsi qu'une interface utilisateur graphique. Le Docker Engine est la technologie de base derri\u00e8re Docker. Il s'agit d'un logiciel open source qui fonctionne sous Linux en tant que service qui permet d'ex\u00e9cuter des conteneurs au-dessus du noyau Linux. Il est responsable du cycle de vie des conteneurs et de l'isolation des ressources physiques (calcul, m\u00e9moire, stockage) auxquelles les conteneurs peuvent acc\u00e9der. Le moteur peut s'ex\u00e9cuter sur une machine physique ou virtuelle, mais il ne peut fonctionner que sur un noyau Linux, c'est-\u00e0-dire sur un syst\u00e8me d'exploitation bas\u00e9 sur Linux. Il est important de comprendre que le moteur Docker ne s'ex\u00e9cute que sous Linux. Le Docker CLI est un outil en ligne de commande gratuit ( CLI ) pour cr\u00e9er et g\u00e9rer des objets Docker , tels que des images , des conteneurs , des r\u00e9seaux et des volumes . Voici les alternatives \u00e0 Docker Desktop","title":"Installer le moteur Docker"},{"location":"ch1-getting-start/#sur-linux","text":"Sur Linux c'est tr\u00e8s simple il suffit d'installer docker. Nous allons utiliser ici le script clef en main d'installation : Installez le Docker Engine pour Linux documentation officielle : curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh Ajoutez votre utilisateur au groupe docker : sudo usermod -aG docker $USER Warning En tant qu'utilisateur Linux, vous pouvez utiliser uniquement le Moteur Docker , m\u00eame si la documentation officielle encourage l'adoption de Docker Desktop pour Linux .","title":"Sur Linux"},{"location":"ch1-getting-start/#sur-windows","text":"Nous allons utiliser Windows Linux Subsystem Tout d'abord, il faut v\u00e9rifier que la fonctionnalit\u00e9 est bien activ\u00e9e sur Windows. Depuis la barre de recherche Windows, ouvrez Activer ou d\u00e9sactiver une fonctionnalit\u00e9 Windows et cochez Sous-syt\u00e8me Windows pour Linux et plateforme de machine virtuelle . Puis installez les deux applications du Microsoft Store Maintenant, ouvrez l'application Ubuntu pr\u00e9c\u00e9demment install\u00e9e. Il va vous \u00eatre demand\u00e9 de configurer un user et un mot de passe. Enfin, nous allons installer docker Update Ubtuntu sudo apt update Installer Docker curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh V\u00e9rifier l'installation docker -v Using docker without root permissions A cette \u00e9tape, docker a besoin des permissions root pour fonctionner : docker run registry.takima.io/school/proxy/hello-world , va renvoyer une erreur de permissions. Pour pallier \u00e0 cela executez les commandes suivantes sudo addgroup --system docker sudo adduser $USER docker newgrp docker sudo chown root:docker /var/run/docker.sock sudo chmod g+w /var/run/docker.sock Test docker run registry.takima.io/school/proxy/hello-world Installer Docker-compose sudo curl -L \"https://github.com/docker/compose/releases/download/v2.23.0/docker-compose- $( uname -s ) - $( uname -m ) \" -o /usr/local/bin/docker-compose V\u00e9rifier l'installation docker-compose -v Troubleshooting Si en utilisant docker-compose vous avez cette erreur : Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running? Lancez le service docker sudo service docker start","title":"Sur Windows"},{"location":"ch1-getting-start/#sur-macos","text":"Installer Docker Engine sur macOS sans utiliser Docker Desktop peut \u00eatre plus complexe. Pour ce faire nous allons utiliser Minikube qui embarque \u00e9gallement une impl\u00e9mentation du daemon Docker qui nous permettra de lancer des conteneurs. En plus, c'est parfait, car vous serez pr\u00eat \u00e0 vous exercer a Kubernetes, mais c'est une autre histoire. Note Avant de commencer, assurez-vous que vous avez install\u00e9 Homebrew sur votre syst\u00e8me macOS. Si ce n'est pas le cas, vous pouvez l'installer en ex\u00e9cutant la commande suivante dans votre terminal : bash /bin/bash -c \"$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)\"","title":"Sur MacOs"},{"location":"ch1-getting-start/#etapes-pour-installer-docker-sur-macos-sans-docker-desktop","text":"Pour cela on utioisera une approche Docker Machine c'est \u00e0 dire lancer une VM pour faire tourner docker engine. Install hyperkit and minikube brew install hyperkit brew install minikube Attention pour les puces M1 darwin/arm64, il y a incompatibilit\u00e9 avec les driver de virtualisation hyperkit ou virtualbox. Un drivers compatible est QEMU : brew install qemu Install Docker CLI brew install docker brew install docker-compose Start minikube suivant votre driver de virtualisation : minikube config set driver qemu puis minikube start Tell Docker CLI to talk to minikube's VM eval $( minikube docker-env )","title":"\u00c9tapes pour installer Docker sur macOS sans Docker Desktop :"},{"location":"ch1-getting-start/#save-ip-to-a-hostname","text":"echo \"`minikube ip` docker.local\" | sudo tee -a /etc/hosts > /dev/null","title":"Save IP to a hostname"},{"location":"ch1-getting-start/#test","text":"docker run hello-world Cela devrait vous permettre d'utiliser Docker Engine sur macOS sans Docker Desktop. Note Minikube ex\u00e9cute une configuration Kubernetes et, par cons\u00e9quent, ex\u00e9cute de nombreux conteneurs qui ne sont pas n\u00e9cessaires si l'on n'utilise pas Kubernetes. Nous pouvons ex\u00e9cuter la commande minikube pause pour mettre en pause les conteneurs li\u00e9s \u00e0 Kubernetes afin qu'ils ne consomment pas de ressources syst\u00e8me inutilement.","title":"Test"},{"location":"cheatsheet/","text":"Cheatsheet R\u00e9sum\u00e9 des commandes du Dockerfile Voici un bref r\u00e9sum\u00e9 des quelques commandes de base que nous avons utilis\u00e9es dans notre Dockerfile. FROM d\u00e9marre le Dockerfile. Il est obligatoire que le Dockerfile commence par la commande FROM . Les images sont cr\u00e9\u00e9es en couches, ce qui signifie que vous pouvez utiliser une autre image comme image de base pour la v\u00f4tre. La commande FROM d\u00e9finit votre couche de base. En tant qu'arguments, vous pouvez ajouter le nom de l'image. Facultativement, vous pouvez ajouter le nom d'utilisateur de Docker Cloud du mainteneur et la version de l'image, dans le format nom_utilisateur/nom_image:version . RUN est utilis\u00e9 pour construire l'image que vous cr\u00e9ez. Pour chaque commande RUN , Docker ex\u00e9cute la commande, puis cr\u00e9e une nouvelle couche de l'image. De cette mani\u00e8re, vous pouvez revenir en arri\u00e8re facilement \u00e0 des \u00e9tats pr\u00e9c\u00e9dents de votre image. La syntaxe d'une instruction RUN consiste \u00e0 placer le texte complet de la commande shell apr\u00e8s RUN (par exemple, RUN mkdir /user/local/foo ). Cela s'ex\u00e9cutera automatiquement dans un shell /bin/sh . Vous pouvez d\u00e9finir un shell diff\u00e9rent comme ceci : RUN /bin/bash -c 'mkdir /user/local/foo . COPY copie des fichiers locaux dans le conteneur. CMD d\u00e9finit les commandes qui s'ex\u00e9cuteront sur l'image au d\u00e9marrage. Contrairement \u00e0 RUN , cela ne cr\u00e9e pas une nouvelle couche pour l'image, mais ex\u00e9cute simplement la commande. Il ne peut y avoir qu'une seule commande CMD par Dockerfile/Image. Si vous avez besoin d'ex\u00e9cuter plusieurs commandes, la meilleure fa\u00e7on de le faire est de faire en sorte que CMD ex\u00e9cute un script. CMD exige que vous lui disiez o\u00f9 ex\u00e9cuter la commande, contrairement \u00e0 RUN . Donc, les exemples de commandes CMD seraient : CMD [ \"python\" , \"./app.py\" ] CMD [ \"/bin/bash\" , \"echo\" , \"Hello World\" ] EXPOSE cr\u00e9e un indice pour les utilisateurs d'une image indiquant les ports qui fournissent des services. Il est inclus dans les informations qui peuvent \u00eatre r\u00e9cup\u00e9r\u00e9es via $ docker inspect <id-conteneur> . Note La commande EXPOSE ne rend pas r\u00e9ellement les ports accessibles \u00e0 l'h\u00f4te ! Pour cela, vous devez publier les ports en utilisant le drapeau -p lors de l'utilisation de $ docker run . Note Si vous souhaitez en savoir plus sur les Dockerfiles, consultez les meilleures pratiques pour l'\u00e9criture de Dockerfiles . (source : https://github.com/docker/labs/tree/master/beginner ) Docker & docker-compose","title":"Cheatsheet"},{"location":"cheatsheet/#cheatsheet","text":"","title":"Cheatsheet"},{"location":"cheatsheet/#resume-des-commandes-du-dockerfile","text":"Voici un bref r\u00e9sum\u00e9 des quelques commandes de base que nous avons utilis\u00e9es dans notre Dockerfile. FROM d\u00e9marre le Dockerfile. Il est obligatoire que le Dockerfile commence par la commande FROM . Les images sont cr\u00e9\u00e9es en couches, ce qui signifie que vous pouvez utiliser une autre image comme image de base pour la v\u00f4tre. La commande FROM d\u00e9finit votre couche de base. En tant qu'arguments, vous pouvez ajouter le nom de l'image. Facultativement, vous pouvez ajouter le nom d'utilisateur de Docker Cloud du mainteneur et la version de l'image, dans le format nom_utilisateur/nom_image:version . RUN est utilis\u00e9 pour construire l'image que vous cr\u00e9ez. Pour chaque commande RUN , Docker ex\u00e9cute la commande, puis cr\u00e9e une nouvelle couche de l'image. De cette mani\u00e8re, vous pouvez revenir en arri\u00e8re facilement \u00e0 des \u00e9tats pr\u00e9c\u00e9dents de votre image. La syntaxe d'une instruction RUN consiste \u00e0 placer le texte complet de la commande shell apr\u00e8s RUN (par exemple, RUN mkdir /user/local/foo ). Cela s'ex\u00e9cutera automatiquement dans un shell /bin/sh . Vous pouvez d\u00e9finir un shell diff\u00e9rent comme ceci : RUN /bin/bash -c 'mkdir /user/local/foo . COPY copie des fichiers locaux dans le conteneur. CMD d\u00e9finit les commandes qui s'ex\u00e9cuteront sur l'image au d\u00e9marrage. Contrairement \u00e0 RUN , cela ne cr\u00e9e pas une nouvelle couche pour l'image, mais ex\u00e9cute simplement la commande. Il ne peut y avoir qu'une seule commande CMD par Dockerfile/Image. Si vous avez besoin d'ex\u00e9cuter plusieurs commandes, la meilleure fa\u00e7on de le faire est de faire en sorte que CMD ex\u00e9cute un script. CMD exige que vous lui disiez o\u00f9 ex\u00e9cuter la commande, contrairement \u00e0 RUN . Donc, les exemples de commandes CMD seraient : CMD [ \"python\" , \"./app.py\" ] CMD [ \"/bin/bash\" , \"echo\" , \"Hello World\" ] EXPOSE cr\u00e9e un indice pour les utilisateurs d'une image indiquant les ports qui fournissent des services. Il est inclus dans les informations qui peuvent \u00eatre r\u00e9cup\u00e9r\u00e9es via $ docker inspect <id-conteneur> . Note La commande EXPOSE ne rend pas r\u00e9ellement les ports accessibles \u00e0 l'h\u00f4te ! Pour cela, vous devez publier les ports en utilisant le drapeau -p lors de l'utilisation de $ docker run . Note Si vous souhaitez en savoir plus sur les Dockerfiles, consultez les meilleures pratiques pour l'\u00e9criture de Dockerfiles . (source : https://github.com/docker/labs/tree/master/beginner )","title":"R\u00e9sum\u00e9 des commandes du Dockerfile"},{"location":"cheatsheet/#docker-docker-compose","text":"","title":"Docker & docker-compose"}]} \ No newline at end of file diff --git a/public/sitemap.xml b/public/sitemap.xml index 8e8a840..5fb7164 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -10,4 +10,19 @@ <lastmod>2023-10-25</lastmod> <changefreq>daily</changefreq> </url> + <url> + <loc>None</loc> + <lastmod>2023-10-25</lastmod> + <changefreq>daily</changefreq> + </url> + <url> + <loc>None</loc> + <lastmod>2023-10-25</lastmod> + <changefreq>daily</changefreq> + </url> + <url> + <loc>None</loc> + <lastmod>2023-10-25</lastmod> + <changefreq>daily</changefreq> + </url> </urlset> \ No newline at end of file diff --git a/public/sitemap.xml.gz b/public/sitemap.xml.gz index af7a02da7801d4acd775a990f7c60028e3381e30..a867d2ac4e3f8374b2ebec9bc0b650371d96c88d 100644 GIT binary patch literal 199 zcmb2|=HLi#wMb?9Uz}NznwwalSCN~;@OF|f-ysEo*6;J0Ht0KN`&<dwRs7P6-$7%W ziip;NtGB)Dld9O~p50UV+30>+#Mf6mRa){}HmjcFJn9koGAnnsP`v)~oPO<niTwFZ zT^g;q8imry^(+!?jV!XE>c1T3<SU#o<7E^R*04<2=eY71^X6GylIlNiMjlNFdELsf zvDj2UW8>MPS)#i|BKL&)Y&&H3>e<z~*PUjYH%~Pde%Srza6Px()-UV+Fmrs^|N9;j G0|Nl^K3CWP literal 195 zcmb2|=HO6kwMb?9Uz}NznwwalSCN~;a5UgF-ysEoJ>RuCzB8>g40L{ZcCq?j2B(dx z3j-pz{Q7>$-RJ*-op0W}Nl}Z8+h5%sXVvg=-67UQiOl6S$uZFnZ-1R5U!ZIBk4LP; z<>Z?MhhlpUcU~%#;heFe+LGbp!les#ZDELYJ#|8`n@81$k$d*Rwc(E*&3#sqbi29b z#&5SNYwkQ{J$<@%)0$hymRY@ws^$NsD3$r9{c`UnlS}bW@1J@;@t@j01@51$>5L2v E0Gjk+2LJ#7 -- GitLab