Motivé par nos apprentissages précédents, nous voilà enfin près – du moins, nous tenterons l’exercice – à bâtir une image docker personnalisée.
Pré-requis:
Comment démarrer ce module?
Nous savons comment personnaliser un conteneur, puis le publier sur hub.docker.com.
Oui je sais, ce faisant, nous modifions le travail d’une autre personne.
Pas vraiment de mérite à celà.
Alors voici comment nous élever au dessus de la foule 👨👩👦👦:
Il est possible de créer un image personnalisée à partir d’un fichier de directives.
Quoique cela ne soit pas une obligation, ce fichier est habituellement nommé ‘Dockerfile‘.
Il propose la structure de directives suivante:
FROM – À partir d’une image existante, comme par exemple, ‘alpine’
RUN – Dans l’image, exécuter les commandes suivantes, comme par exemple, ‘apt update && apt install nano’
Note: RUN utilise la syntaxe ‘/bin/sh -c‘ pour exécuter la commande.
CMD – Lors du démarrage du conteneur, exécuter la commande suivante: ‘/bin/bash’
Référence: Best practices
Testons,
1.1 – Dans un dossier vide, créer un fichier Dockerfile
# À partir d'une image FROM ubuntu # Optionnel MAINTAINER Alain Boudreault <aboudrea@cstj.qc.ca> LABEL Maintainer="Moi" LABEL Auteur="Moi Moi" # Exécuter des commandes dans l'image RUN apt-get update RUN mkdir /toto # Commande à exécuter au démarrage d'un conteneur CMD ["echo"," --------------------------\n","Ici la voix des Mistérons!\n","--------------------------\n"]
NOTE: Il est aussi possible d’utiliser LABEL au lieu de MAINTAINER, ce qui est présentement la méthode recommandée.
NOTE2: Les directives peuvent-être inscrites en caractères minuscules mais NON RECOMMANDÉ.
1.2 – Construire l’image: docker build
$ docker build -t exercice01:latest .
NOTE: ATTENTION au . à la fin de la commande!!
[+] Building 0.5s (6/6) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 364B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/ubuntu:latest 0.4s => [1/2] FROM docker.io/library/ubuntu@sha256:703218c0465075f4425e58fac086e09e1de5c340b12976ab9eb8ad26615c3715 0.0s => CACHED [2/2] RUN apt-get update 0.0s => exporting to image 0.0s => => exporting layers 0.0s => => writing image sha256:6a083629e78cc15116597087eabb9cce0dea4180748039cd18a1d90487ec4a10 0.0s => => naming to docker.io/library/exercice01:latest
1.3 – Tester l’image via un conteneur: docker run
$ docker run exercice01 -------------------------- Ici la voix des Mistérons! --------------------------
Note: Si le fichier source n’est pas nommé ‘Dockerfile’, il faut alors utiliser l’option -f:
docker build -f Dockerfile02 -t docker_build_02 .
1.4 – Copier un fichier vers l’image – créer le fichier ‘hello.sh’
#!/bin/bash echo "------------------------------" echo " Ici la voix des Mistérons!" echo "------------------------------"
1.4b – Modifier le fichier Dockerfile
# À partir d'une image FROM ubuntu # Optionel MAINTAINER Alain Boudreault <aboudrea@cstj.qc.ca> # Exécuter des commandes dans l'image RUN apt-get update # Ajouter un fichier à l'image ADD hello.sh / RUN chmod a+x /hello.sh # Commande à éxécuter au démarrage d'un conteneur CMD ["/hello.sh"]
1.5 – Reconstruire l’image
docker build -t exercice01:latest .
1.6 – Tester dans un conteneur
$ docker run exercice01
1.7 – Labo – Modifier le projet pour obtenir le résultat suivant au démarrage du conteneur version 1.1
docker run exercice01:1.1
Résultat:
-------------------------------------- Ici la voix des Mistérons! -------------------------------------- Il est : 16:30:50 dans le conteneur --------------------------------------
Note: Sous Linux, pour afficher l’heure dans un echo : $(date +%X)
NOTE: Démonstration de l’enseignant à partir d’un session Ubuntu – Explication des dépendances OS.
2.1 – À partir d’une session Ubuntu, étant donné le programme ‘C‘ suivant:
// Fichier: bonjour.cpp // Exemple d'une application 'bin' pour Linux // Par Alain Boudreault // Fichier source compilé avec 'gcc' sous Ubuntu // Il est possible d'avoir à installer g++ et gcc: apt install g++ gcc #include <stdio.h>; #define MAXITERATION 10 /** Incroyable mais oui, il n'y a qu'une seule fonction dans ce programme ;-) */ int main(){ for (int i=1; i <= MAXITERATION; i++) { printf("%d\t - Bonjour 420-4D4 !!!\n", i); } // for i return 0; } // main()
2.2 – Compiler le code source:
$ g++ bonjour.cpp -o bonjour
2.3 – À partir du Dockerfile suivant:
FROM ubuntu COPY bonjour /bin CMD ["/bin/bonjour"]
2.4 – Produire l’image:
docker build -t alainboudreault/bonjour420:latest .
2.5 – Tester l’image – une version est disponible sur hub.docker à ‘alainboudreault/bonjour420’
docker run alainboudreault/bonjour420
Laboratoire 2.6 – Durée 30 minutes
1 – Programmer un Dockerfile qui:
2 – Produire et tester l’image
3 – Publier l’image sous docker-hub-login/bonjour-420:latest:
4 – Copier le lien dans la conversation Teams
Suggestion, tester vos étapes dans un conteneur Ubuntu avant de rédiger le Dockerfile.
Pré-requis
git, doit-être installé sur votre poste de travail:
3.1 – Obtenir les fichiers de départ à partir de https://github.com/ve2cuy/4204d4.git
Note: Placez-vous dans un dossier de travail vide!
git clone https://github.com/ve2cuy/4204d4.git
3.2 – Se déplacer vers ‘4204d4/module01/semaine02/exercice03’
cd 4204d4/module01/semaine02/exercice03
3.3 – Construire une image nommée ‘test‘ à partir du fichier ‘Dockerfile‘ ainsi que le contenu du dossier.
docker build -t test .
3.4 – Tester l’image
docker run -d -p 99:80 test
3.9 – Une version fonctionnelle ici:
docker run -d -p 99:80 alainboudreault/420-4d4:exemple03
docker pull votreCompteHub.docker.com/semaine02-lab4
5.1 – Voici une installation qui requière la définition d’une zone de temps (TIME_ZONE)
# EXEMPLE AVEC UN TIME-ZONE # docker build -t test:v1 . FROM ubuntu:20.04 LABEL maintainer="alain.boudreault@me.com" RUN apt-get update && apt-get install -yq tzdata ENV TZ="America/New_York" RUN apt install -y mc
5.2 – Exemple d’utilisation d’une variable d’environnement lors du build
FROM openjdk:jre-alpine LABEL maintainer="dev@someproject.org" ARG BUILD_DATE LABEL org.label-schema.build-date=$BUILD_DATE COPY tool.tar.gz /mnt WORKDIR /mnt RUN tar zxvf /mnt/tool.tar.gz CMD ["/mnt/tool/tool.sh"]
docker build --no-cache=true --build-arg \ BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \ -t mytool:latest .
5.3 – Le fichier .dockerignore
# Voici un exemple de contenu: .git .gitignore node_modules npm-debug.log Dockerfile* docker-compose* README.md LICENSE .vscode
5.4 – La directive WORKDIR, exemple d’utilisation:
FROM nginx # Se déplacer dans le dossier racine du serveur Web: WORKDIR /usr/share/nginx/html # Copier les fichiers du dossier courant vers le dossier WORKDIR COPY . ./
5.5 – Différences entre les directives ADD et COPY
Si les fonctions supplémentaires de ADD ne sont pas requises, il est recommandé d’utiliser la directive COPY.
5.6 – Dockerfile de l’image Alpine
FROM scratch ADD alpine-minirootfs-3.15.0-x86_64.tar.gz / CMD ["/bin/sh"]
5.7 – Dockerfile de WordPress
FROM php:7.4-apache # persistent dependencies RUN set -eux; \ apt-get update; \ apt-get install -y --no-install-recommends \ # Ghostscript is required for rendering PDF previews ghostscript \ ; \ rm -rf /var/lib/apt/lists/* # install the PHP extensions we need (https://make.wordpress.org/hosting/handbook/handbook/server-environment/#php-extensions) RUN set -ex; \ \ savedAptMark="$(apt-mark showmanual)"; \ \ apt-get update; \ apt-get install -y --no-install-recommends \ libfreetype6-dev \ libicu-dev \ libjpeg-dev \ libmagickwand-dev \ libpng-dev \ libwebp-dev \ libzip-dev \ ; \ \ docker-php-ext-configure gd \ --with-freetype \ --with-jpeg \ --with-webp \ ; \ docker-php-ext-install -j "$(nproc)" \ bcmath \ exif \ gd \ intl \ mysqli \ zip \ ; \ # https://pecl.php.net/package/imagick pecl install imagick-3.6.0; \ docker-php-ext-enable imagick; \ rm -r /tmp/pear; \ \ # some misbehaving extensions end up outputting to stdout <img draggable="false" data-mce-resize="false" data-mce-placeholder="1" data-wp-emoji="1" class="emoji" alt="🙈" src="https://s.w.org/images/core/emoji/11/svg/1f648.svg"> (https://github.com/docker-library/wordpress/issues/669#issuecomment-993945967) out="$(php -r 'exit(0);')"; \ [ -z "$out" ]; \ err="$(php -r 'exit(0);' 3>&1 1>&2 2>&3)"; \ [ -z "$err" ]; \ \ extDir="$(php -r 'echo ini_get("extension_dir");')"; \ [ -d "$extDir" ]; \ # reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies apt-mark auto '.*' > /dev/null; \ apt-mark manual $savedAptMark; \ ldd "$extDir"/*.so \ | awk '/=>/ { print $3 }' \ | sort -u \ | xargs -r dpkg-query -S \ | cut -d: -f1 \ | sort -u \ | xargs -rt apt-mark manual; \ \ apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false; \ rm -rf /var/lib/apt/lists/*; \ \ ! { ldd "$extDir"/*.so | grep 'not found'; }; \ # check for output like "PHP Warning: PHP Startup: Unable to load dynamic library 'foo' (tried: ...) err="$(php --version 3>&1 1>&2 2>&3)"; \ [ -z "$err" ] # set recommended PHP.ini settings # see https://secure.php.net/manual/en/opcache.installation.php RUN set -eux; \ docker-php-ext-enable opcache; \ { \ echo 'opcache.memory_consumption=128'; \ echo 'opcache.interned_strings_buffer=8'; \ echo 'opcache.max_accelerated_files=4000'; \ echo 'opcache.revalidate_freq=2'; \ echo 'opcache.fast_shutdown=1'; \ } > /usr/local/etc/php/conf.d/opcache-recommended.ini # https://wordpress.org/support/article/editing-wp-config-php/#configure-error-logging RUN { \ # https://www.php.net/manual/en/errorfunc.constants.php # https://github.com/docker-library/wordpress/issues/420#issuecomment-517839670 echo 'error_reporting = E_ERROR | E_WARNING | E_PARSE | E_CORE_ERROR | E_CORE_WARNING | E_COMPILE_ERROR | E_COMPILE_WARNING | E_RECOVERABLE_ERROR'; \ echo 'display_errors = Off'; \ echo 'display_startup_errors = Off'; \ echo 'log_errors = On'; \ echo 'error_log = /dev/stderr'; \ echo 'log_errors_max_len = 1024'; \ echo 'ignore_repeated_errors = On'; \ echo 'ignore_repeated_source = Off'; \ echo 'html_errors = Off'; \ } > /usr/local/etc/php/conf.d/error-logging.ini RUN set -eux; \ a2enmod rewrite expires; \ \ # https://httpd.apache.org/docs/2.4/mod/mod_remoteip.html a2enmod remoteip; \ { \ echo 'RemoteIPHeader X-Forwarded-For'; \ # these IP ranges are reserved for "private" use and should thus *usually* be safe inside Docker echo 'RemoteIPTrustedProxy 10.0.0.0/8'; \ echo 'RemoteIPTrustedProxy 172.16.0.0/12'; \ echo 'RemoteIPTrustedProxy 192.168.0.0/16'; \ echo 'RemoteIPTrustedProxy 169.254.0.0/16'; \ echo 'RemoteIPTrustedProxy 127.0.0.0/8'; \ } > /etc/apache2/conf-available/remoteip.conf; \ a2enconf remoteip; \ # https://github.com/docker-library/wordpress/issues/383#issuecomment-507886512 # (replace all instances of "%h" with "%a" in LogFormat) find /etc/apache2 -type f -name '*.conf' -exec sed -ri 's/([[:space:]]*LogFormat[[:space:]]+"[^"]*)%h([^"]*")/\1%a\2/g' '{}' + RUN set -eux; \ version='5.9.1'; \ sha1='15746f848cd388e270bae612dccd0c83fa613259'; \ \ curl -o wordpress.tar.gz -fL "https://wordpress.org/wordpress-$version.tar.gz"; \ echo "$sha1 *wordpress.tar.gz" | sha1sum -c -; \ \ # upstream tarballs include ./wordpress/ so this gives us /usr/src/wordpress tar -xzf wordpress.tar.gz -C /usr/src/; \ rm wordpress.tar.gz; \ \ # https://wordpress.org/support/article/htaccess/ [ ! -e /usr/src/wordpress/.htaccess ]; \ { \ echo '# BEGIN WordPress'; \ echo ''; \ echo 'RewriteEngine On'; \ echo 'RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]'; \ echo 'RewriteBase /'; \ echo 'RewriteRule ^index\.php$ - [L]'; \ echo 'RewriteCond %{REQUEST_FILENAME} !-f'; \ echo 'RewriteCond %{REQUEST_FILENAME} !-d'; \ echo 'RewriteRule . /index.php [L]'; \ echo ''; \ echo '# END WordPress'; \ } > /usr/src/wordpress/.htaccess; \ \ chown -R www-data:www-data /usr/src/wordpress; \ # pre-create wp-content (and single-level children) for folks who want to bind-mount themes, etc so permissions are pre-created properly instead of root:root # wp-content/cache: https://github.com/docker-library/wordpress/issues/534#issuecomment-705733507 mkdir wp-content; \ for dir in /usr/src/wordpress/wp-content/*/ cache; do \ dir="$(basename "${dir%/}")"; \ mkdir "wp-content/$dir"; \ done; \ chown -R www-data:www-data wp-content; \ chmod -R 777 wp-content VOLUME /var/www/html COPY --chown=www-data:www-data wp-config-docker.php /usr/src/wordpress/ COPY docker-entrypoint.sh /usr/local/bin/ ENTRYPOINT ["docker-entrypoint.sh"] CMD ["apache2-foreground"]