Sept bonnes pratiques pour Docker

Comme nous le savons tous, en tant que document texte, Dockerfile contient toutes les commandes et instructions permettant aux utilisateurs de créer des images. Docker peut créer automatiquement des images en lisant les instructions du Dockerfile. Par conséquent, les gens pensent souvent que l'écriture d'un Dockerfile devrait être très simple, il suffit de choisir un exemple sur Internet et de le personnaliser pour répondre aux besoins réels. Cependant, ce n'est pas le cas.

En raison des exigences strictes d'un environnement de production, notamment en termes de sécurité, de nombreux exemples peuvent convenir à un environnement de développement, mais pas nécessairement à un environnement de production. De plus, étant donné que Docker fournit également un ensemble de stratégies de guidage pour l'écriture de Dockerfile, cela conduit à Dockerfile comme écrire du code.Vous connaissez peut-être la syntaxe pertinente, mais vous ne pourrez peut-être pas écrire de code propre et concis dans un langage de programmation spécifique. Ci-dessous, je discuterai avec vous de 7 excellentes stratégies et pratiques théoriques qui sont plus pratiques lors de l'écriture de Dockerfiles.

1. Introduction

Examinons d'abord un exemple typique de Dockerfile :

Dockerfile
FROM eclipse-temurin:17
RUN mkdir /opt/app
ARG JAR_FILE
ADD target/${JAR_FILE} /opt/app/app.jar
CMD ["java", "-jar", "/opt/app/app.jar"]

Selon son contenu, ce Dockerfile fait ce qui suit :

  • lFROM : utilisez l'image Java Docker --eclipse-temurin:17 comme image de base ;
  • lRUN : créez un répertoire pour le fichier jar ;
  • lARG : évitez de coder en dur le nom du fichier jar dans le Dockerfile en fournissant un paramètre --JAR_FILE ;
  • lADD : ajoutez le fichier jar à l'image Docker ;
  • lCMD : Contient les commandes qui doivent être exécutées lors de l'exécution du conteneur.

On peut voir que le Dockerfile généré par chacun des paragraphes ci-dessus se trouve dans le répertoire Dockerfiles du référentiel Git. Et à la fin de chaque paragraphe, le nom du Dockerfile correspondant est également mentionné le cas échéant. Ci-dessous, nous mettrons en œuvre sept bonnes pratiques en modifiant ce Dockerfile.

2. Prérequis

Avant de continuer, les prérequis dont vous avez besoin sont :

  • Connaissances de base de Linux
  • Connaissance de base de Java et Spring Boot
  • Connaissance de base de Docker

3. Exemples d'applications

Pour démontrer les bonnes pratiques, j'ai pré-créé une application Spring Boot de base qui inclut les dépendances Spring Web. L'application peut être exécutée en invoquant la commande suivante dans le répertoire racine du référentiel :

Shell
$ MVN spring-boot:run

Et pour construire l'image Docker, j'utiliserai un fork du plugin dockerfile-maven-plugin de Spotify. Pour ce faire, je vais ajouter l'extrait de code suivant au fichier pom.

XML
<plugin>
<groupId>com.xenoamess.docker</groupId>
<artifactId>dockerfile-maven-plugin</artifactId>
<version>1.4.25</version>
<configuration>
<repository>mydeveloperplanet/dockerbestpractices</repository>
<tag>${project.version}</tag>
<buildArgs>
<JAR_FILE>${project.build.finalName}.jar</JAR_FILE>
</buildArgs>
</configuration>
</plugin>

La bonne chose à propos de l'utilisation de ce plugin est que vous pouvez facilement réutiliser la configuration. En même temps, afin de créer une image Docker via la commande Maven, vous pouvez construire le fichier jar en appelant la commande suivante :

Shell
$ mvn clean verify

Ensuite, veuillez créer l'image Docker en invoquant la commande suivante :

Shell
$ mvn dockerfile:build

La commande suivante vous permettra d'exécuter l'image Docker :

Shell
$ docker run --name dockerbestpractices mydeveloperplanet/dockerbestpractices:0.0.1-SNAPSHOT

Ensuite, veuillez utiliser le code suivant pour trouver l'adresse IP du conteneur en cours d'exécution :

Shell
$ docker inspect dockerbestpractices | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.3",
                   "IPAddress": "172.17.0.3"

L'adresse IP dans cet exemple est 172.17.0.3. Dans le même temps, l'application contient également un HelloController qui n'est utilisé que pour répondre aux messages hello. De plus, le point de terminaison Hello peut être appelé comme suit :

Shell
$ curl http://172.17.0.3:8080/hello
Hello Docker!

A ce stade, tout est prêt.

4. Excellentes pratiques

1. Quel miroir utiliser

Dans l'article précédent, nous avons mentionné que l'image utilisée dans le Dockerfile dans cet exemple est eclipse-temurin:17. Ensuite, regardons comment l'image est construite :

  • Veuillez visiter le lien sur DockerHub ;
  • Rechercher 'eclipse-temurin';
  • Accédez à "Balises" ;
  • recherche 17 ;
  • Trier par AZ ;
  • Cliquez sur l'onglet 17.

Si vous regardez attentivement les détails de chaque couche sur la page et que vous la comparez à la balise 17-JRE, vous remarquerez que la balise 17 inclut un JDK complet, tandis que la balise 17-JRE inclut uniquement le JRE. Bien sûr, ce dernier est suffisant pour exécuter des applications Java, après tout, l'exécution de diverses applications dans un environnement de production ne nécessite pas l'intégralité du JDK. De plus, comme les outils de développement peuvent être abusés, l'utilisation du JDK présente également certains problèmes de sécurité. De plus, la taille de l'image compressée de la balise 17 est de 235 Mo, tandis que la taille compressée de 17-jre n'est que de 89 Mo.

Pour réduire encore la taille de l'image, on peut utiliser une image "allégée" : 17-jre-alpine. La taille compressée de l'image est de 59 Mo, soit 30 Mo de moins que 17-jre, il est donc plus facile à distribuer.

Il convient de noter que les balises utilisées ci-dessus sont des balises génériques et pointent vers la dernière version. Cela peut convenir à un environnement de développement, mais pour un environnement de production, vous devez savoir à l'avance quelle version vous utilisez. La balise utilisée dans cet exemple est 17.0.5_8-jre-alpine. Si vous souhaitez renforcer davantage la sécurité, vous pouvez ajouter le hachage SHA256 à la version de l'image. Les hachages SHA256 se trouvent sur les pages qui couvrent ces couches. Lorsque la valeur de hachage de SHA256 ne correspond pas à la valeur de hachage définie dans le Dockerfile, le processus de création de l'image Docker échoue.

Dans cet exemple, la première ligne du Dockerfile :

Dockerfile
FROM eclipse-temurin:17

Armés des connaissances ci-dessus, nous pouvons changer cette ligne en :

Dockerfile
FROM eclipse-temurin: 17.0.5_8-jre-alpine@sha256:02c04793fa49ad5cd193c961403223755f9209a67894622e05438598b32f210e

Comme indiqué dans le code ci-dessous, une fois l'image Docker terminée, vous remarquerez que l'image (une fois décompressée) est passée de 475 Mo à 188 Mo.

Shell
$ docker images
REPOSITORY TAG              IMAGE ID       CREATED         SIZE
mydeveloperplanet/dockerbestpractices 0.0.1-SNAPSHOT   0b8d89616602   3 seconds ago   188MB

Le Dockerfile résultant est nommé 1-Dockerfile-specific-image dans le référentiel Git.

2. Ne pas exécuter en tant qu'utilisateur root

Par défaut, l'application s'exécutera en tant qu'utilisateur root dans le conteneur. Cela expose évidemment de nombreuses vulnérabilités et n'est pas nécessaire. Pour cela, vous devez définir un utilisateur au niveau du système pour votre application. Comme indiqué dans le code suivant, dans la première ligne des journaux lorsque le conteneur est démarré, vous pouvez voir que l'application a été démarrée par Root.

Shell
2022-11-26 09:03:41.210 INFO 1 --- [           main] m.MyDockerBestPracticesPlanetApplication : Starting MyDockerBestPracticesPlanetApplication v0.0.1-SNAPSHOT using Java 17.0.5 on 3b06feee6c65 with PID 1 (/opt/app/app.jar started by root in /)

Nous pouvons créer des utilisateurs au niveau du système en ajoutant le groupe javauser et l'utilisateur javauser à Dockerfile. Ensuite, faites-le en ajoutant les directives suivantes à votre Dockerfile. Parmi eux, javauser est un utilisateur au niveau du système et n'a pas d'autorité de connexion. Notez que les étapes de création de groupe et d'utilisateur sont regroupées sur une même ligne avec l'esperluette afin de ne créer qu'un seul calque.

Dockerfile
RUN addgroup——system javauser && adduser -S -S /usr/sbin/nologin -G javauser javauser

Le tableau suivant répertorie l'ensemble complet des paramètres disponibles pour adduser :

  • -h, le répertoire personnel du DIR
  • -g, le champ GECOS
  • -s, c'est-à-dire, connectez-vous SHELL
  • -G, pour le groupe
  • -S, c'est-à-dire créer un utilisateur au niveau du système
  • -D, c'est-à-dire ne pas définir de mot de passe
  • -H, c'est-à-dire ne pas créer de répertoire personnel
  • -u, à savoir UID, identifiant utilisateur
  • -k, le répertoire squelette (/etc/SKEL)

Dans le même temps, vous pouvez également changer le propriétaire du répertoire /opt/apt pour le nouveau javauser en ajoutant la ligne suivante, sinon javauser ne pourra pas accéder au répertoire :

Dockerfile
RUN chown -R javauser:javauser /opt/app

Enfin, vous devez vous assurer que javauser est bien utilisé dans le conteneur via la commande USER. Le Dockerfile complet correspondant est :

Dockerfile
FROM eclipse-temurin:17.0.5_8-jre-alpine@sha256:02c04793fa49ad5cd193c961403223755f9209a67894622e05438598b32f210e
RUN mkdir /opt/app
RUN addgroup --system javauser && adduser -S -s /usr/sbin/nologin -G javauser javauser
ARG JAR_FILE
ADD target/${JAR_FILE} /opt/app/app.jar
RUN chown -R javauser:javauser /opt/app
USER javauser
CMD ["java", "-jar", "/opt/app/app.jar"]
为了测试这个新的镜像,您首先需要通过如下命令,停止并删除正在运行的容器。
Shell
$ docker stop dockerbestpractices
$ docker rm dockerbestpractices

Après avoir reconstruit et réexécuté le conteneur, comme indiqué dans le code ci-dessous, vous pouvez voir dans la première ligne de journal que l'application a été démarrée par javauser.

Shell
2022-11-26 09:06:45.227 INFO 1 --- [           main] m.MyDockerBestPracticesPlanetApplication : Starting MyDockerBestPracticesPlanetApplication v0.0.1-SNAPSHOT using Java 17.0.5 on ab1bcd38dff7 with PID 1 (/opt/app/app.jar started by javauser in /)

De même, le Dockerfile résultant est nommé 2-Dockerfile-do-not-run-as-root dans le référentiel Git.

3. Utilisez WORKDIR

Dans le Dockerfile que vous utilisez, le répertoire /opt/app est créé une fois, c'est votre répertoire de travail après tout. Même s'il n'existe pas, Docker le créera pour vous par défaut. Vous n'avez donc pas à répéter ce chemin à chaque fois. Par exemple, vous verrez que la deuxième ligne du Dockerfile contient l'instruction RUN suivante :

Dockerfile
RUN mkdir /opt/app

Nous pouvons changer cela légèrement en utilisant la directive WORKDIR :

Dockerfile
WORKDIR /opt/app

Étant donné que la commande WORKDIR s'est assurée que vous vous trouviez dans ce répertoire, vous pouvez supprimer toute référence à /opt/app. Par conséquent, le nouveau Dockerfile ressemble au code suivant :

Dockerfile
FROM eclipse-temurin:17.0.5_8-jre-alpine@sha256:02c04793fa49ad5cd193c961403223755f9209a67894622e05438598b32f210e
WORKDIR /opt/app
RUN addgroup --system javauser && adduser -S -s /usr/sbin/nologin -G javauser javauser
ARG JAR_FILE
ADD target/${JAR_FILE} app.jar
RUN chown -R javauser:javauser .
USER javauser
CMD ["java", "-jar", "app.jar"]

Après avoir créé et réexécuté le conteneur, vous pouvez voir dans les journaux suivants que le fichier jar est toujours en cours d'exécution dans le répertoire /opt/app :

Shell
2022-11-26 16:07:18.503 INFO 1 --- [           main] m.MyDockerBestPracticesPlanetApplication : Starting MyDockerBestPracticesPlanetApplication v0.0.1-SNAPSHOT using Java 17.0.5 on fe5cf9223143 with PID 1 (/opt/app/app.jar started by javauser in /opt/app)

De même, le Dockerfile généré est nommé 3-Dockerfile-use-workdir dans le référentiel Git.

4. Utilisez ENTRYPOINT

Il existe une différence entre l'instruction CMD et l'instruction ENTRYPOINT. En bref, les scénarios d'utilisation des deux sont :

ENTRYPOINT : Lorsque vous devez toujours exécuter diverses commandes pour créer une image Docker exécutable, vous pouvez ajouter des paramètres à la commande aussi longtemps que vous le souhaitez.

CMD : Lorsque vous souhaitez fournir un ensemble de paramètres par défaut et leur permettre d'être remplacés par la ligne de commande lorsque le conteneur est en cours d'exécution.

Ensuite, dans le cas de l'exécution d'applications Java, il est préférable d'utiliser ENTRYPOINT. Par exemple, la dernière ligne du Dockerfile d'origine :

Dockerfile
CMD ["java", "-jar", "app.jar"]

peut désormais devenir :

Dockerfile
ENTRYPOINT ["java", "-jar", "app.jar"]

Terminez la construction et réexécutez le conteneur, et vous ne remarquerez aucune différence spécifique, le conteneur fonctionnera toujours comme d'habitude. Le Dockerfile résultant est nommé 4-Dockerfile-use-entrypoint dans le référentiel Git.

5. Utilisez COPIER au lieu d'AJOUTER

Les instructions COPY et ADD semblent également similaires. Cependant, COPY est meilleur que ADD.Après tout, COPY ne fait que copier des fichiers sur le miroir, et ADD a quelques fonctionnalités supplémentaires, telles que l'ajout de fichiers à partir de ressources distantes.

Comportement de la commande ADD dans Dockerfile :

Dockerfile
ADD target/${JAR_FILE} app.jar

Si la commande COPY est utilisée à la place, c'est :

Dockerfile
COPY target/${JAR_FILE} app.jar

Reconstruisez et exécutez le conteneur, et encore une fois, vous ne verrez pas de changement notable, à l'exception d'une commande COPY au lieu d'une commande ADD dans le journal de construction. Le Dockerfile résultant est disponible dans le référentiel Git sous le nom 5-Dockerfile-use-copy-instead-of-add.

6. Utilisez .dockerignore

Pour éviter l'ajout accidentel de fichiers aux images Docker, vous pouvez utiliser des fichiers .dockerignore pour spécifier quels fichiers peuvent être envoyés au démon Docker ou utilisés dans l'image. Une approche recommandée consiste à ignorer tous les fichiers et à n'ajouter explicitement que ceux que vous autorisez. En ajoutant un astérisque au fichier .dockerignore, nous pouvons exclure tous les sous-répertoires et fichiers. Bien sûr, afin de placer le fichier jar dans le contexte de la construction, vous pouvez également utiliser le point d'exclamation pour éviter d'ignorer le fichier jar. Comme indiqué dans le fichier dockerignore ci-dessous, nous pouvons l'ajouter au répertoire dans lequel nous exécutons les commandes Docker. Par exemple, dans cet exemple, nous l'ajoutons à la racine du référentiel Git.

Plain Text
**/**
!target/*.jar

Une fois la compilation terminée et le conteneur réexécuté, la modification peut ne pas être perceptible. Mais lorsque vous développez avec npm, le processus de création d'une image Docker est sensiblement raccourci car le répertoire node_modules n'est plus copié dans le contexte de la construction Docker. Notez que vous pouvez trouver le fichier dockerignore directement sous le répertoire Dockerfiles du référentiel Git.

7. Exécutez le démon Docker en mode non root

Par défaut, le démon Docker s'exécute en tant que root. Grâce à la discussion précédente, vous devez être conscient des problèmes de sécurité potentiels. Heureusement, à partir de Docker v20.10, nous pouvons exécuter le démon Docker en tant qu'utilisateur non root.

De plus, vous pouvez également profiter d'un moteur de conteneur sans démon -- Podman (https://podman.io/). Exécuter en mode non root par défaut. Alors que certains considèrent Podman comme un remplacement direct de Docker, ils diffèrent dans la façon dont ils montent les volumes dans les conteneurs.

V. Résumé

Ci-dessus, nous avons couvert 7 bonnes pratiques pour écrire des Dockerfiles et exécuter des conteneurs. Bien que l'écriture d'un Dockerfile ne soit pas compliquée, si vous voulez l'écrire correctement et de manière standardisée, vous devez tout de même passer du temps à rechercher et à comprendre ses instructions.

Voir plus d'excellents outils

Les ascenseurs spatiaux, MOSS, ChatGPT, etc. indiquent tous que 2023 n'est pas destinée à être une année ordinaire. Toute nouvelle technologie mérite d'être examinée, et nous devrions avoir cette sensibilité.

Au cours des dernières années, j'ai vaguement rencontré le low-code, et il est relativement populaire à l'heure actuelle, et de nombreux grands fabricants se sont joints les uns après les autres.

Concept de plate-forme low-code : grâce à la génération automatique de code et à la programmation visuelle, seule une petite quantité de code est nécessaire pour créer rapidement diverses applications.

Ce qui est low-code, à mon avis, c'est le glissement, le vrombissement et l'opération en un seul passage pour créer un système qui peut exécuter, front-end, back-end et base de données, le tout en une seule fois. Bien sûr, cela peut être l'objectif final.

Lien : www.jnpfsoft.com/?csdn , si vous êtes intéressé, faites-en également l'expérience.

L'avantage de JNPF est qu'il peut générer des codes front-end et back-end, ce qui offre une grande flexibilité et peut créer des applications plus complexes et personnalisées. Sa conception architecturale permet également aux développeurs de se concentrer sur le développement de la logique d'application et de l'expérience utilisateur sans se soucier des détails techniques sous-jacents.

Je suppose que tu aimes

Origine blog.csdn.net/wangonik_l/article/details/131942039
conseillé
Classement