1660532820
Cet article examine quelques bonnes pratiques à suivre lors de l'écriture de Dockerfiles et de l'utilisation de Docker en général. Alors que la plupart des pratiques répertoriées s'appliquent à tous les développeurs, quel que soit le langage, quelques-unes s'appliquent uniquement à ceux qui développent des applications basées sur Python.
Tirez parti des versions en plusieurs étapes pour créer des images Docker plus légères et plus sécurisées.
Les builds Docker en plusieurs étapes vous permettent de diviser vos Dockerfiles en plusieurs étapes. Par exemple, vous pouvez avoir une étape pour compiler et créer votre application, qui peut ensuite être copiée dans les étapes suivantes. Étant donné que seule la dernière étape est utilisée pour créer l'image, les dépendances et les outils associés à la création de votre application sont ignorés, laissant une image légère et modulaire prête pour la production.
Exemple de développement Web :
# temp stage
FROM python:3.9-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
# final stage
FROM python:3.9-slim
WORKDIR /app
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache /wheels/*
Dans cet exemple, le compilateur GCC est requis pour l'installation de certains packages Python. Nous avons donc ajouté une étape temporaire de construction pour gérer la phase de construction. Étant donné que l'image d'exécution finale ne contient pas de GCC, elle est beaucoup plus légère et plus sécurisée.
Comparaison de taille :
REPOSITORY TAG IMAGE ID CREATED SIZE
docker-single latest 8d6b6a4d7fb6 16 seconds ago 259MB
docker-multi latest 813c2fa9b114 3 minutes ago 156MB
Exemple de science des données :
# temp stage
FROM python:3.9 as builder
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /wheels jupyter pandas
# final stage
FROM python:3.9-slim
WORKDIR /notebooks
COPY --from=builder /wheels /wheels
RUN pip install --no-cache /wheels/*
Comparaison de taille :
REPOSITORY TAG IMAGE ID CREATED SIZE
ds-multi latest b4195deac742 2 minutes ago 357MB
ds-single latest 7c23c43aeda6 6 minutes ago 969MB
En résumé, les builds en plusieurs étapes peuvent réduire la taille de vos images de production, ce qui vous permet d'économiser du temps et de l'argent. De plus, cela simplifiera vos conteneurs de production. De plus, en raison de la petite taille et de la simplicité, il y a potentiellement une surface d'attaque plus petite.
Portez une attention particulière à l'ordre de vos commandes Dockerfile pour tirer parti de la mise en cache des couches.
Docker met en cache chaque étape (ou couche) dans un Dockerfile particulier pour accélérer les générations suivantes. Lorsqu'une étape change, le cache est invalidé non seulement pour cette étape particulière, mais pour toutes les étapes suivantes.
Exemple:
FROM python:3.9-slim
WORKDIR /app
COPY sample.py .
COPY requirements.txt .
RUN pip install -r /requirements.txt
Dans ce Dockerfile, nous avons copié le code de l'application avant d'installer les exigences. Maintenant, chaque fois que nous modifions sample.py , la construction réinstallera les packages. Ceci est très inefficace, en particulier lorsque vous utilisez un conteneur Docker comme environnement de développement. Par conséquent, il est crucial de conserver les fichiers qui changent fréquemment vers la fin du Dockerfile.
Vous pouvez également empêcher les invalidations de cache indésirables en utilisant un fichier .dockerignore pour exclure l'ajout de fichiers inutiles au contexte de construction Docker et à l'image finale. Plus d'informations ici sous peu.
Ainsi, dans le Dockerfile ci-dessus, vous devez déplacer la COPY sample.py .
commande vers le bas :
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r /requirements.txt
COPY sample.py .
Remarques:
RUN apt-get update
et RUN apt-get install
commandes. (Cela permet également de réduire la taille de l'image. Nous y reviendrons sous peu.)--no-cache=True
indicateur.Les images Docker plus petites sont plus modulaires et sécurisées.
Construire, pousser et extraire des images est plus rapide avec des images plus petites. Ils ont également tendance à être plus sécurisés car ils n'incluent que les bibliothèques et les dépendances système nécessaires à l'exécution de votre application.
Quelle image de base Docker devez-vous utiliser ?
Malheureusement, cela dépend.
Voici une comparaison de taille de différentes images de base Docker pour Python :
REPOSITORY TAG IMAGE ID CREATED SIZE
python 3.9.6-alpine3.14 f773016f760e 3 days ago 45.1MB
python 3.9.6-slim 907fc13ca8e7 3 days ago 115MB
python 3.9.6-slim-buster 907fc13ca8e7 3 days ago 115MB
python 3.9.6 cba42c28d9b8 3 days ago 886MB
python 3.9.6-buster cba42c28d9b8 3 days ago 886MB
Bien que la version Alpine, basée sur Alpine Linux , soit la plus petite, elle peut souvent entraîner une augmentation des temps de construction si vous ne trouvez pas de binaires compilés qui fonctionnent avec. Par conséquent, vous devrez peut-être construire vous-même les fichiers binaires, ce qui peut augmenter la taille de l'image (en fonction des dépendances requises au niveau du système) et les temps de construction (en raison de la nécessité de compiler à partir de la source).
Reportez-vous à La meilleure image de base Docker pour votre application Python et L'utilisation d'Alpine peut ralentir les constructions de Python Docker 50× pour en savoir plus sur les raisons pour lesquelles il est préférable d'éviter d'utiliser des images de base basées sur Alpine.
Au final, tout est une question d'équilibre. En cas de doute, commencez par une *-slim
saveur, en particulier en mode développement, pendant que vous construisez votre application. Vous souhaitez éviter d'avoir à mettre à jour en permanence le Dockerfile pour installer les dépendances nécessaires au niveau du système lorsque vous ajoutez un nouveau package Python. Au fur et à mesure que vous renforcez votre application et vos Dockerfile(s) pour la production, vous souhaiterez peut-être explorer l'utilisation d'Alpine pour l'image finale à partir d'une construction en plusieurs étapes.
N'oubliez pas non plus de mettre à jour régulièrement vos images de base pour améliorer la sécurité et augmenter les performances. Lorsqu'une nouvelle version d'une image de base est publiée - c'est-à-dire
3.9.6-slim
->3.9.7-slim
- vous devez extraire la nouvelle image et mettre à jour vos conteneurs en cours d'exécution pour obtenir tous les derniers correctifs de sécurité.
C'est une bonne idée de combiner autant que possible les commandes , et , car elles créent des RUN
calques COPY
. ADD
Chaque calque augmente la taille de l'image car ils sont mis en cache. Par conséquent, à mesure que le nombre de couches augmente, la taille augmente également.
Vous pouvez tester cela avec la docker history
commande :
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
dockerfile latest 180f98132d02 51 seconds ago 259MB
$ docker history 180f98132d02
IMAGE CREATED CREATED BY SIZE COMMENT
180f98132d02 58 seconds ago COPY . . # buildkit 6.71kB buildkit.dockerfile.v0
<missing> 58 seconds ago RUN /bin/sh -c pip install -r requirements.t… 35.5MB buildkit.dockerfile.v0
<missing> About a minute ago COPY requirements.txt . # buildkit 58B buildkit.dockerfile.v0
<missing> About a minute ago WORKDIR /app
...
Attention aux tailles. Seules les commandes RUN
, COPY
et ADD
ajoutent de la taille à l'image. Vous pouvez réduire la taille de l'image en combinant des commandes dans la mesure du possible. Par exemple:
RUN apt-get update
RUN apt-get install -y netcat
Peut être combiné en une seule RUN
commande :
RUN apt-get update && apt-get install -y netcat
Ainsi, on crée un seul calque au lieu de deux, ce qui réduit la taille de l'image finale.
Bien que ce soit une bonne idée de réduire le nombre de calques, il est beaucoup plus important que cela soit moins un objectif en soi et plus un effet secondaire de la réduction de la taille de l'image et des temps de construction. En d'autres termes, concentrez-vous davantage sur les trois pratiques précédentes - les constructions en plusieurs étapes, l'ordre de vos commandes Dockerfile et l'utilisation d'une petite image de base - plutôt que d'essayer d'optimiser chaque commande.
Remarques:
RUN
, COPY
, et ADD
créent chacun des calques.Des astuces:
step
qui les a créés.apt-get upgrade
exécutions car il met à niveau tous les packages vers la dernière version.Enfin, pour plus de lisibilité, c'est une bonne idée de trier les arguments multi-lignes par ordre alphanumérique :
RUN apt-get update && apt-get install -y \
git \
gcc \
matplotlib \
pillow \
&& rm -rf /var/lib/apt/lists/*
Par défaut, Docker exécute les processus de conteneur en tant que racine à l'intérieur d'un conteneur. Cependant, il s'agit d'une mauvaise pratique car un processus exécuté en tant que root à l'intérieur du conteneur s'exécute en tant que root dans l'hôte Docker. Ainsi, si un attaquant accède à votre conteneur, il a accès à tous les privilèges root et peut effectuer plusieurs attaques contre l'hôte Docker, comme-
Pour éviter cela, assurez-vous d'exécuter les processus de conteneur avec un utilisateur non root :
RUN addgroup --system app && adduser --system --group app
USER app
Vous pouvez aller plus loin et supprimer l'accès au shell et vous assurer qu'il n'y a pas non plus de répertoire personnel :
RUN addgroup --gid 1001 --system app && \
adduser --no-create-home --shell /bin/false --disabled-password --uid 1001 --system --group app
USER app
Vérifier:
$ docker run -i sample id
uid=1001(app) gid=1001(app) groups=1001(app)
Ici, l'application dans le conteneur s'exécute sous un utilisateur non root. Cependant, gardez à l'esprit que le démon Docker et le conteneur lui-même fonctionnent toujours avec les privilèges root. Assurez-vous de consulter Exécuter le démon Docker en tant qu'utilisateur non root pour obtenir de l'aide sur l'exécution du démon et des conteneurs en tant qu'utilisateur non root.
À utiliser COPY
sauf si vous êtes certain d'avoir besoin des fonctionnalités supplémentaires fournies avec ADD
.
Quelle est la différence entre COPY
et ADD
?
Les deux commandes vous permettent de copier des fichiers d'un emplacement spécifique dans une image Docker :
ADD <src> <dest>
COPY <src> <dest>
Bien qu'ils semblent servir le même objectif, ils ADD
ont quelques fonctionnalités supplémentaires :
COPY
est utilisé pour copier des fichiers ou des répertoires locaux de l'hôte Docker vers l'image.ADD
peut être utilisé pour la même chose ainsi que pour télécharger des fichiers externes. De plus, si vous utilisez un fichier compressé (tar, gzip, bzip2, etc.) comme <src>
paramètre, ADD
décompressera automatiquement le contenu à l'emplacement donné.# copy local files on the host to the destination
COPY /source/path /destination/path
ADD /source/path /destination/path
# download external file and copy to the destination
ADD http://external.file/url /destination/path
# copy and extract local compresses files
ADD source.file.tar.gz /destination/path
Lorsqu'un fichier requirements est modifié, l'image doit être reconstruite pour installer les nouveaux packages. Les étapes précédentes seront mises en cache, comme indiqué dans Réduire le nombre de couches . Le téléchargement de tous les packages lors de la reconstruction de l'image peut entraîner une activité réseau importante et prendre beaucoup de temps. Chaque reconstruction prend le même temps pour télécharger des packages communs à travers les versions.
Vous pouvez éviter cela en mappant le répertoire du cache pip à un répertoire sur la machine hôte. Ainsi, pour chaque reconstruction, les versions mises en cache persistent et peuvent améliorer la vitesse de construction.
Ajoutez un volume au docker exécuté en tant que -v $HOME/.cache/pip-docker/:/root/.cache/pip
ou en tant que mappage dans le fichier Docker Compose.
Le répertoire présenté ci-dessus est uniquement à titre de référence. Assurez-vous de mapper le répertoire de cache et non les packages de site (où résident les packages construits).
Le déplacement du cache de l'image Docker vers l'hôte peut vous faire gagner de l'espace dans l'image finale.
Si vous utilisez Docker BuildKit , utilisez les montages de cache BuildKit pour gérer le cache :
# syntax = docker/dockerfile:1.2
...
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
...
Pourquoi est-il recommandé de n'exécuter qu'un seul processus par conteneur ?
Supposons que votre pile d'applications se compose de deux serveurs Web et d'une base de données. Bien que vous puissiez facilement exécuter les trois à partir d'un seul conteneur, vous devez exécuter chacun dans un conteneur séparé pour faciliter la réutilisation et la mise à l'échelle de chacun des services individuels.
Vous pouvez écrire les commandes CMD
et ENTRYPOINT
dans vos fichiers Docker dans les formats tableau (exec) ou chaîne (shell) :
# array (exec)
CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "main:app"]
# string (shell)
CMD "gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app"
Les deux sont corrects et réalisent presque la même chose ; cependant, vous devez utiliser le format exec chaque fois que possible. De la documentation Docker :
CMD
et ENTRYPOINT
dans votre Dockerfile.["program", "arg1", "arg2"]
pas "program arg1 arg2"
. L'utilisation de la forme de chaîne oblige Docker à exécuter votre processus à l'aide de bash, qui ne gère pas correctement les signaux. Compose utilise toujours le formulaire JSON, donc ne vous inquiétez pas si vous remplacez la commande ou le point d'entrée dans votre fichier Compose.Ainsi, puisque la plupart des shells ne traitent pas les signaux vers les processus enfants, si vous utilisez le format shell, CTRL-C
(qui génère un SIGTERM
) peut ne pas arrêter un processus enfant.
Exemple:
FROM ubuntu:18.04
# BAD: shell format
ENTRYPOINT top -d
# GOOD: exec format
ENTRYPOINT ["top", "-d"]
Essayez les deux. Notez qu'avec la saveur du format shell, CTRL-C
cela ne tuera pas le processus. Au lieu de cela, vous verrez ^C^C^C^C^C^C^C^C^C^C^C
.
Une autre mise en garde est que le format shell porte le PID du shell, pas le processus lui-même.
# array format
root@18d8fd3fd4d2:/app# ps ax
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 python manage.py runserver 0.0.0.0:8000
7 ? Sl 0:02 /usr/local/bin/python manage.py runserver 0.0.0.0:8000
25 pts/0 Ss 0:00 bash
356 pts/0 R+ 0:00 ps ax
# string format
root@ede24a5ef536:/app# ps ax
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /bin/sh -c python manage.py runserver 0.0.0.0:8000
8 ? S 0:00 python manage.py runserver 0.0.0.0:8000
9 ? Sl 0:01 /usr/local/bin/python manage.py runserver 0.0.0.0:8000
13 pts/0 Ss 0:00 bash
342 pts/0 R+ 0:00 ps ax
Dois-je utiliser ENTRYPOINT ou CMD pour exécuter des processus de conteneur ?
Il existe deux manières d'exécuter des commandes dans un conteneur :
CMD ["gunicorn", "config.wsgi", "-b", "0.0.0.0:8000"]
# and
ENTRYPOINT ["gunicorn", "config.wsgi", "-b", "0.0.0.0:8000"]
Les deux font essentiellement la même chose : démarrez l'application sur config.wsgi
avec un serveur Gunicorn et liez-la à 0.0.0.0:8000
.
Le CMD
est facilement contournable. Si vous exécutez docker run <image_name> uvicorn config.asgi
, le CMD ci-dessus est remplacé par les nouveaux arguments - par exemple, uvicorn config.asgi
. Alors que pour remplacer la ENTRYPOINT
commande, il faut spécifier l' --entrypoint
option :
docker run --entrypoint uvicorn config.asgi <image_name>
Ici, il est clair que nous remplaçons le point d'entrée. Il est donc recommandé d'utiliser ENTRYPOINT
over CMD
pour éviter de remplacer accidentellement la commande.
Ils peuvent également être utilisés ensemble.
Par exemple:
ENTRYPOINT ["gunicorn", "config.wsgi", "-w"]
CMD ["4"]
Lorsqu'ils sont utilisés ensemble comme ceci, la commande qui est exécutée pour démarrer le conteneur est :
gunicorn config.wsgi -w 4
Comme indiqué ci-dessus, CMD
est facilement contournable. Ainsi, CMD
peut être utilisé pour passer des arguments à la ENTRYPOINT
commande. Le nombre de travailleurs peut être facilement modifié comme suit :
docker run <image_name> 6
Cela démarrera le conteneur avec six travailleurs Gunicorn plutôt que quatre.
Utilisez a HEALTHCHECK
pour déterminer si le processus en cours d'exécution dans le conteneur est non seulement opérationnel, mais également "sain".
Docker expose une API pour vérifier l'état du processus en cours d'exécution dans le conteneur, qui fournit bien plus d'informations que simplement si le processus est "en cours d'exécution" ou non puisque "en cours d'exécution" couvre "il est opérationnel", "en cours de lancement", et même "coincé dans un état d'erreur de boucle infinie". Vous pouvez interagir avec cette API via l' instruction HEALTHCHECK .
Par exemple, si vous diffusez une application Web, vous pouvez utiliser les éléments suivants pour déterminer si le point de /
terminaison est opérationnel et peut gérer les requêtes :
HEALTHCHECK CMD curl --fail http://localhost:8000 || exit 1
Si vous exécutez docker ps
, vous pouvez voir l'état du HEALTHCHECK
.
Exemple sain :
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09c2eb4970d4 healthcheck "python manage.py ru…" 10 seconds ago Up 8 seconds (health: starting) 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp xenodochial_clarke
Exemple malsain :
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09c2eb4970d4 healthcheck "python manage.py ru…" About a minute ago Up About a minute (unhealthy) 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp xenodochial_clarke
Vous pouvez aller plus loin et configurer un point de terminaison personnalisé utilisé uniquement pour les vérifications de l'état, puis configurer le HEALTHCHECK
pour tester les données renvoyées. Par exemple, si le point de terminaison renvoie une réponse JSON de {"ping": "pong"}
, vous pouvez demander au HEALTHCHECK
de valider le corps de la réponse.
Voici comment afficher l'état de la vérification de l'état à l'aide dedocker inspect
:
❯ docker inspect --format "{{json .State.Health }}" ab94f2ac7889
{
"Status": "healthy",
"FailingStreak": 0,
"Log": [
{
"Start": "2021-09-28T15:22:57.5764644Z",
"End": "2021-09-28T15:22:57.7825527Z",
"ExitCode": 0,
"Output": "..."
Ici, la sortie est coupée car elle contient toute la sortie HTML.
Vous pouvez également ajouter une vérification d'état à un fichier Docker Compose :
version: "3.8"
services:
web:
build: .
ports:
- '8000:8000'
healthcheck:
test: curl --fail http://localhost:8000 || exit 1
interval: 10s
timeout: 10s
start_period: 10s
retries: 3
Option :
test
: La commande à tester.interval
: L'intervalle à tester -- c'est-à-dire, testez chaque x
unité de temps.timeout
: Temps maximum d'attente de la réponse.start_period
: Quand commencer le bilan de santé. Il peut être utilisé lorsque des tâches supplémentaires sont effectuées avant que les conteneurs ne soient prêts, comme l'exécution de migrations.retries
: nombre maximal de tentatives avant de désigner un test comme failed
.Si vous utilisez un outil d'orchestration autre que Docker Swarm, c'est-à-dire Kubernetes ou AWS ECS, il est fort probable que l'outil dispose de son propre système interne pour gérer les vérifications de l'état. Reportez-vous à la documentation de l'outil particulier avant d'ajouter l'
HEALTHCHECK
instruction.
Dans la mesure du possible, évitez d'utiliser la latest
balise.
Si vous vous fiez à la latest
balise (qui n'est pas vraiment une "balise" puisqu'elle est appliquée par défaut lorsqu'une image n'est pas explicitement balisée), vous ne pouvez pas savoir quelle version de votre code s'exécute en fonction de la balise d'image. Cela complique les restaurations et facilite leur écrasement (accidentellement ou par malveillance). Les balises, comme votre infrastructure et vos déploiements, doivent être immuables .
Quelle que soit la façon dont vous traitez vos images internes, vous ne devez jamais utiliser la latest
balise pour les images de base, car vous pourriez déployer par inadvertance une nouvelle version avec des modifications radicales en production.
Pour les images internes, utilisez des balises descriptives pour identifier plus facilement la version du code en cours d'exécution, gérer les restaurations et éviter les collisions de noms.
Par exemple, vous pouvez utiliser les descripteurs suivants pour créer une balise :
Pour plus d'options, consultez cette réponse de la question Stack Overflow "Version correcte des images Docker".
Par exemple:
docker build -t web-prod-a072c4e5d94b5a769225f621f08af3d4bf820a07-0.1.4 .
Ici, nous avons utilisé ce qui suit pour former la balise :
web
prod
a072c4e5d94b5a769225f621f08af3d4bf820a07
0.1.4
Il est essentiel de choisir un schéma de marquage et d'être cohérent avec celui-ci. Étant donné que les hachages de commit permettent de lier rapidement une balise d'image au code, il est fortement recommandé de les inclure dans votre schéma de balisage.
Les secrets sont des informations sensibles telles que les mots de passe, les informations d'identification de la base de données, les clés SSH, les jetons et les certificats TLS, pour n'en nommer que quelques-uns. Ceux-ci ne doivent pas être intégrés à vos images sans être cryptés, car les utilisateurs non autorisés qui accèdent à l'image peuvent simplement examiner les couches pour en extraire les secrets.
N'ajoutez pas de secrets à vos Dockerfiles en clair, surtout si vous poussez les images vers un registre public comme Docker Hub :
FROM python:3.9-slim
ENV DATABASE_PASSWORD "SuperSecretSauce"
Au lieu de cela, ils doivent être injectés via :
En outre, vous pouvez aider à empêcher les fuites de secrets en ajoutant des fichiers et des dossiers secrets communs à votre fichier .dockerignore :
**/.env
**/.aws
**/.ssh
Enfin, soyez explicite sur les fichiers qui sont copiés sur l'image plutôt que de copier tous les fichiers de manière récursive :
# BAD
COPY . .
# GOOD
copy ./app.py .
Être explicite aide également à limiter le contournement du cache.
Vous pouvez transmettre des secrets via des variables d'environnement, mais ils seront visibles dans tous les processus enfants, conteneurs liés et journaux, ainsi que via docker inspect
. Il est également difficile de les mettre à jour.
$ docker run --detach --env "DATABASE_PASSWORD=SuperSecretSauce" python:3.9-slim
d92cf5cf870eb0fdbf03c666e7fcf18f9664314b79ad58bc7618ea3445e39239
$ docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' d92cf5cf870eb0fdbf03c666e7fcf18f9664314b79ad58bc7618ea3445e39239
DATABASE_PASSWORD=SuperSecretSauce
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LANG=C.UTF-8
GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568
PYTHON_VERSION=3.9.7
PYTHON_PIP_VERSION=21.2.4
PYTHON_SETUPTOOLS_VERSION=57.5.0
PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/c20b0cfd643cd4a19246ccf204e2997af70f6b21/public/get-pip.py
PYTHON_GET_PIP_SHA256=fa6f3fb93cce234cd4e8dd2beb54a51ab9c247653b52855a48dd44e6b21ff28b
Il s'agit de l'approche la plus simple de la gestion des secrets. Bien que ce ne soit pas le plus sûr, il gardera les gens honnêtes honnêtes car il fournit une fine couche de protection, aidant à garder les secrets cachés des yeux curieux et errants.
Transmettre des secrets en utilisant un volume partagé est une meilleure solution, mais ils doivent être chiffrés, via Vault ou AWS Key Management Service (KMS), car ils sont enregistrés sur disque.
Vous pouvez transmettre des secrets au moment de la construction en utilisant des arguments de construction , mais ils seront visibles pour ceux qui ont accès à l'image via docker history
.
Exemple:
FROM python:3.9-slim
ARG DATABASE_PASSWORD
Construire:
$ docker build --build-arg "DATABASE_PASSWORD=SuperSecretSauce" .
Si vous n'avez besoin d'utiliser les secrets que temporairement dans le cadre de la construction, c'est-à-dire des clés SSH pour cloner un référentiel privé ou télécharger un package privé, vous devez utiliser une construction en plusieurs étapes, car l'historique du générateur est ignoré pour les étapes temporaires :
# temp stage
FROM python:3.9-slim as builder
# secret
ARG SSH_PRIVATE_KEY
# install git
RUN apt-get update && \
apt-get install -y --no-install-recommends git
# use ssh key to clone repo
RUN mkdir -p /root/.ssh/ && \
echo "${PRIVATE_SSH_KEY}" > /root/.ssh/id_rsa
RUN touch /root/.ssh/known_hosts &&
ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts
RUN git clone git@github.com:testdrivenio/not-real.git
# final stage
FROM python:3.9-slim
WORKDIR /app
# copy the repository from the temp image
COPY --from=builder /your-repo /app/your-repo
# use the repo for something!
La construction en plusieurs étapes ne conserve l'historique que pour l'image finale. Gardez à l'esprit que vous pouvez utiliser cette fonctionnalité pour les secrets permanents dont vous avez besoin pour votre application, comme un identifiant de base de données.
Vous pouvez également utiliser la nouvelle --secret
option dans Docker build pour transmettre des secrets aux images Docker qui ne sont pas stockées dans les images.
# "docker_is_awesome" > secrets.txt
FROM alpine
# shows secret from default secret location:
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
Cela montera le secret à partir du secrets.txt
fichier.
Construisez l'image :
docker build --no-cache --progress=plain --secret id=mysecret,src=secrets.txt .
# output
...
#4 [1/2] FROM docker.io/library/alpine
#4 sha256:665ba8b2cdc0cb0200e2a42a6b3c0f8f684089f4cd1b81494fbb9805879120f7
#4 CACHED
#5 [2/2] RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
#5 sha256:75601a522ebe80ada66dedd9dd86772ca932d30d7e1b11bba94c04aa55c237de
#5 0.635 docker_is_awesome#5 DONE 0.7s
#6 exporting to image
Enfin, vérifiez l'historique pour voir si le secret fuit :
❯ docker history 49574a19241c
IMAGE CREATED CREATED BY SIZE COMMENT
49574a19241c 5 minutes ago CMD ["/bin/sh"] 0B buildkit.dockerfile.v0
<missing> 5 minutes ago RUN /bin/sh -c cat /run/secrets/mysecret # b… 0B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:aad4290d27580cc1a… 5.6MB
Pour en savoir plus sur les secrets de construction, consultez Ne divulguez pas les secrets de construction de votre image Docker .
Si vous utilisez Docker Swarm , vous pouvez gérer les secrets avec Docker secrets .
Par exemple, initialisez le mode Docker Swarm :
$ docker swarm init
Créez un secret Docker :
$ echo "supersecretpassword" | docker secret create postgres_password -
qdqmbpizeef0lfhyttxqfbty0
$ docker secret ls
ID NAME DRIVER CREATED UPDATED
qdqmbpizeef0lfhyttxqfbty0 postgres_password 4 seconds ago 4 seconds ago
Lorsqu'un conteneur a accès au secret ci-dessus, il se monte sur /run/secrets/postgres_password
. Ce fichier contiendra la valeur réelle du secret en clair.
Vous utilisez un autre outil d'orhestration ?
Nous avons déjà mentionné plusieurs fois l'utilisation d'un fichier .dockerignore . Ce fichier est utilisé pour spécifier les fichiers et dossiers que vous ne souhaitez pas ajouter au contexte de construction initial envoyé au démon Docker, qui construira ensuite votre image. En d'autres termes, vous pouvez l'utiliser pour définir le contexte de génération dont vous avez besoin.
Lorsqu'une image Docker est construite, l'intégralité du contexte Docker, c'est-à-dire la racine de votre projet, est envoyée au démon Docker avant que les commandes COPY
ou ADD
ne soient évaluées. Cela peut être assez coûteux, surtout si vous avez de nombreuses dépendances, des fichiers de données volumineux ou des artefacts de construction dans votre projet. De plus, la CLI Docker et le démon peuvent ne pas être sur la même machine. Ainsi, si le démon est exécuté sur une machine distante, vous devez être encore plus attentif à la taille du contexte de construction.
Que devez-vous ajouter au fichier .dockerignore ?
Exemple:
**/.git
**/.gitignore
**/.vscode
**/coverage
**/.env
**/.aws
**/.ssh
Dockerfile
README.md
docker-compose.yml
**/.DS_Store
**/venv
**/env
En résumé, un .dockerignore correctement structuré peut aider :
Le peluchage est le processus de vérification de votre code source pour les erreurs programmatiques et stylistiques et les mauvaises pratiques qui pourraient conduire à des défauts potentiels. Tout comme avec les langages de programmation, les fichiers statiques peuvent également être pelucheux. Avec vos Dockerfiles en particulier, les linters peuvent vous aider à vous assurer qu'ils sont maintenables, à éviter une syntaxe obsolète et à respecter les meilleures pratiques. Le peluchage de vos images devrait faire partie intégrante de vos pipelines CI.
Hadolint est le linter Dockerfile le plus populaire :
$ hadolint Dockerfile
Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly
Dockerfile:7 DL3042 warning: Avoid the use of cache directory with pip. Use `pip install --no-cache-dir <package>`
Dockerfile:9 DL3059 info: Multiple consecutive `RUN` instructions. Consider consolidation.
Dockerfile:17 DL3025 warning: Use arguments JSON notation for CMD and ENTRYPOINT arguments
Vous pouvez le voir en action en ligne sur https://hadolint.github.io/hadolint/ . Il existe également une extension de code VS.
Vous pouvez coupler vos Dockerfiles avec l'analyse d'images et de conteneurs pour les vulnérabilités.
Quelques options :
docker scan
commande CLI pour numériser des images.En résumé, peluchez et analysez vos Dockerfiles et images pour faire apparaître tout problème potentiel qui s'écarte des meilleures pratiques.
Comment savez-vous que les images utilisées pour exécuter votre code de production n'ont pas été altérées ?
La falsification peut passer par le fil via des attaques de l' homme du milieu (MITM) ou du fait que le registre est complètement compromis.
Docker Content Trust (DCT) permet la signature et la vérification des images Docker à partir de registres distants.
Pour vérifier l'intégrité et l'authenticité d'une image, définissez la variable d'environnement suivante :
DOCKER_CONTENT_TRUST=1
Maintenant, si vous essayez d'extraire une image qui n'a pas été signée, vous recevrez l'erreur suivante :
Error: remote trust data does not exist for docker.io/namespace/unsigned-image:
notary.docker.io does not have trust data for docker.io/namespace/unsigned-image
Vous pouvez en savoir plus sur la signature d'images dans la documentation Signature d'images avec Docker Content Trust .
Lorsque vous téléchargez des images depuis Docker Hub, assurez-vous d'utiliser des images officielles ou des images vérifiées provenant de sources fiables. Les équipes plus importantes devraient envisager d'utiliser leur propre registre de conteneurs privé interne .
Devriez-vous utiliser un environnement virtuel à l'intérieur d'un conteneur ?
Dans la plupart des cas, les environnements virtuels sont inutiles tant que vous vous en tenez à n'exécuter qu'un seul processus par conteneur. Étant donné que le conteneur lui-même fournit une isolation, les packages peuvent être installés à l'échelle du système. Cela dit, vous souhaiterez peut-être utiliser un environnement virtuel dans une construction en plusieurs étapes plutôt que de créer des fichiers de roue.
Exemple avec roues :
# temp stage
FROM python:3.9-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
# final stage
FROM python:3.9-slim
WORKDIR /app
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache /wheels/*
Exemple avec virtualenv :
# temp stage
FROM python:3.9-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install -r requirements.txt
# final stage
FROM python:3.9-slim
COPY --from=builder /opt/venv /opt/venv
WORKDIR /app
ENV PATH="/opt/venv/bin:$PATH"
It's a good idea to limit the memory usage of your Docker containers, especially if you're running multiple containers on a single machine. This can prevent any of the containers from using all available memory and thereby crippling the rest.
The easiest way to limit memory usage is to use --memory
and --cpu
options in the Docker cli:
$ docker run --cpus=2 -m 512m nginx
The above command limits the container usage to 2 CPUs and 512 megabytes of main memory.
You can do the same in a Docker Compose file like so:
version: "3.9"
services:
redis:
image: redis:alpine
deploy:
resources:
limits:
cpus: 2
memory: 512M
reservations:
cpus: 1
memory: 256M
Take note of the reservations
field. It's used to set a soft limit, which takes priority when the host machine has low memory or CPU resources.
Additional resources:
Les applications exécutées dans vos conteneurs Docker doivent écrire des messages de journal sur la sortie standard (stdout) et l'erreur standard (stderr) plutôt que sur un fichier.
Vous pouvez ensuite configurer le démon Docker pour envoyer vos messages de journal à une solution de journalisation centralisée (comme CloudWatch Logs ou Papertrail ).
Pour en savoir plus, consultez Traiter les journaux comme des flux d'événements à partir de l'application Twelve-Factor et Configurer les pilotes de journalisation à partir de la documentation Docker.
Gunicorn utilise un système de pulsation basé sur des fichiers pour s'assurer que tous les processus de travail forkés sont actifs.
Dans la plupart des cas, les fichiers heartbeat se trouvent dans "/tmp", qui est souvent en mémoire via tmpfs . Étant donné que Docker n'utilise pas tmpfs par défaut, les fichiers seront stockés sur un système de fichiers sauvegardé sur disque. Cela peut causer des problèmes , comme des gels aléatoires puisque le système de pulsation utilise os.fchmod
, ce qui peut bloquer un travailleur si le répertoire se trouve en fait sur un système de fichiers sauvegardé sur disque.
Heureusement, il existe une solution simple : remplacez le répertoire heartbeat par un répertoire mappé en mémoire via le --worker-tmp-dir
drapeau.
gunicorn --worker-tmp-dir /dev/shm config.wsgi -b 0.0.0.0:8000
Cet article a examiné plusieurs bonnes pratiques pour rendre vos Dockerfiles et images plus propres, plus légers et plus sécurisés.
Source : https://testdrive.io
1626775355
No programming language is pretty much as diverse as Python. It enables building cutting edge applications effortlessly. Developers are as yet investigating the full capability of end-to-end Python development services in various areas.
By areas, we mean FinTech, HealthTech, InsureTech, Cybersecurity, and that's just the beginning. These are New Economy areas, and Python has the ability to serve every one of them. The vast majority of them require massive computational abilities. Python's code is dynamic and powerful - equipped for taking care of the heavy traffic and substantial algorithmic capacities.
Programming advancement is multidimensional today. Endeavor programming requires an intelligent application with AI and ML capacities. Shopper based applications require information examination to convey a superior client experience. Netflix, Trello, and Amazon are genuine instances of such applications. Python assists with building them effortlessly.
Python can do such numerous things that developers can't discover enough reasons to admire it. Python application development isn't restricted to web and enterprise applications. It is exceptionally adaptable and superb for a wide range of uses.
Robust frameworks
Python is known for its tools and frameworks. There's a structure for everything. Django is helpful for building web applications, venture applications, logical applications, and mathematical processing. Flask is another web improvement framework with no conditions.
Web2Py, CherryPy, and Falcon offer incredible capabilities to customize Python development services. A large portion of them are open-source frameworks that allow quick turn of events.
Simple to read and compose
Python has an improved sentence structure - one that is like the English language. New engineers for Python can undoubtedly understand where they stand in the development process. The simplicity of composing allows quick application building.
The motivation behind building Python, as said by its maker Guido Van Rossum, was to empower even beginner engineers to comprehend the programming language. The simple coding likewise permits developers to roll out speedy improvements without getting confused by pointless subtleties.
Utilized by the best
Alright - Python isn't simply one more programming language. It should have something, which is the reason the business giants use it. Furthermore, that too for different purposes. Developers at Google use Python to assemble framework organization systems, parallel information pusher, code audit, testing and QA, and substantially more. Netflix utilizes Python web development services for its recommendation algorithm and media player.
Massive community support
Python has a steadily developing community that offers enormous help. From amateurs to specialists, there's everybody. There are a lot of instructional exercises, documentation, and guides accessible for Python web development solutions.
Today, numerous universities start with Python, adding to the quantity of individuals in the community. Frequently, Python designers team up on various tasks and help each other with algorithmic, utilitarian, and application critical thinking.
Progressive applications
Python is the greatest supporter of data science, Machine Learning, and Artificial Intelligence at any enterprise software development company. Its utilization cases in cutting edge applications are the most compelling motivation for its prosperity. Python is the second most well known tool after R for data analytics.
The simplicity of getting sorted out, overseeing, and visualizing information through unique libraries makes it ideal for data based applications. TensorFlow for neural networks and OpenCV for computer vision are two of Python's most well known use cases for Machine learning applications.
Thinking about the advances in programming and innovation, Python is a YES for an assorted scope of utilizations. Game development, web application development services, GUI advancement, ML and AI improvement, Enterprise and customer applications - every one of them uses Python to its full potential.
The disadvantages of Python web improvement arrangements are regularly disregarded by developers and organizations because of the advantages it gives. They focus on quality over speed and performance over blunders. That is the reason it's a good idea to utilize Python for building the applications of the future.
#python development services #python development company #python app development #python development #python in web development #python software development
1660532820
Cet article examine quelques bonnes pratiques à suivre lors de l'écriture de Dockerfiles et de l'utilisation de Docker en général. Alors que la plupart des pratiques répertoriées s'appliquent à tous les développeurs, quel que soit le langage, quelques-unes s'appliquent uniquement à ceux qui développent des applications basées sur Python.
Tirez parti des versions en plusieurs étapes pour créer des images Docker plus légères et plus sécurisées.
Les builds Docker en plusieurs étapes vous permettent de diviser vos Dockerfiles en plusieurs étapes. Par exemple, vous pouvez avoir une étape pour compiler et créer votre application, qui peut ensuite être copiée dans les étapes suivantes. Étant donné que seule la dernière étape est utilisée pour créer l'image, les dépendances et les outils associés à la création de votre application sont ignorés, laissant une image légère et modulaire prête pour la production.
Exemple de développement Web :
# temp stage
FROM python:3.9-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
# final stage
FROM python:3.9-slim
WORKDIR /app
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache /wheels/*
Dans cet exemple, le compilateur GCC est requis pour l'installation de certains packages Python. Nous avons donc ajouté une étape temporaire de construction pour gérer la phase de construction. Étant donné que l'image d'exécution finale ne contient pas de GCC, elle est beaucoup plus légère et plus sécurisée.
Comparaison de taille :
REPOSITORY TAG IMAGE ID CREATED SIZE
docker-single latest 8d6b6a4d7fb6 16 seconds ago 259MB
docker-multi latest 813c2fa9b114 3 minutes ago 156MB
Exemple de science des données :
# temp stage
FROM python:3.9 as builder
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /wheels jupyter pandas
# final stage
FROM python:3.9-slim
WORKDIR /notebooks
COPY --from=builder /wheels /wheels
RUN pip install --no-cache /wheels/*
Comparaison de taille :
REPOSITORY TAG IMAGE ID CREATED SIZE
ds-multi latest b4195deac742 2 minutes ago 357MB
ds-single latest 7c23c43aeda6 6 minutes ago 969MB
En résumé, les builds en plusieurs étapes peuvent réduire la taille de vos images de production, ce qui vous permet d'économiser du temps et de l'argent. De plus, cela simplifiera vos conteneurs de production. De plus, en raison de la petite taille et de la simplicité, il y a potentiellement une surface d'attaque plus petite.
Portez une attention particulière à l'ordre de vos commandes Dockerfile pour tirer parti de la mise en cache des couches.
Docker met en cache chaque étape (ou couche) dans un Dockerfile particulier pour accélérer les générations suivantes. Lorsqu'une étape change, le cache est invalidé non seulement pour cette étape particulière, mais pour toutes les étapes suivantes.
Exemple:
FROM python:3.9-slim
WORKDIR /app
COPY sample.py .
COPY requirements.txt .
RUN pip install -r /requirements.txt
Dans ce Dockerfile, nous avons copié le code de l'application avant d'installer les exigences. Maintenant, chaque fois que nous modifions sample.py , la construction réinstallera les packages. Ceci est très inefficace, en particulier lorsque vous utilisez un conteneur Docker comme environnement de développement. Par conséquent, il est crucial de conserver les fichiers qui changent fréquemment vers la fin du Dockerfile.
Vous pouvez également empêcher les invalidations de cache indésirables en utilisant un fichier .dockerignore pour exclure l'ajout de fichiers inutiles au contexte de construction Docker et à l'image finale. Plus d'informations ici sous peu.
Ainsi, dans le Dockerfile ci-dessus, vous devez déplacer la COPY sample.py .
commande vers le bas :
FROM python:3.9-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install -r /requirements.txt
COPY sample.py .
Remarques:
RUN apt-get update
et RUN apt-get install
commandes. (Cela permet également de réduire la taille de l'image. Nous y reviendrons sous peu.)--no-cache=True
indicateur.Les images Docker plus petites sont plus modulaires et sécurisées.
Construire, pousser et extraire des images est plus rapide avec des images plus petites. Ils ont également tendance à être plus sécurisés car ils n'incluent que les bibliothèques et les dépendances système nécessaires à l'exécution de votre application.
Quelle image de base Docker devez-vous utiliser ?
Malheureusement, cela dépend.
Voici une comparaison de taille de différentes images de base Docker pour Python :
REPOSITORY TAG IMAGE ID CREATED SIZE
python 3.9.6-alpine3.14 f773016f760e 3 days ago 45.1MB
python 3.9.6-slim 907fc13ca8e7 3 days ago 115MB
python 3.9.6-slim-buster 907fc13ca8e7 3 days ago 115MB
python 3.9.6 cba42c28d9b8 3 days ago 886MB
python 3.9.6-buster cba42c28d9b8 3 days ago 886MB
Bien que la version Alpine, basée sur Alpine Linux , soit la plus petite, elle peut souvent entraîner une augmentation des temps de construction si vous ne trouvez pas de binaires compilés qui fonctionnent avec. Par conséquent, vous devrez peut-être construire vous-même les fichiers binaires, ce qui peut augmenter la taille de l'image (en fonction des dépendances requises au niveau du système) et les temps de construction (en raison de la nécessité de compiler à partir de la source).
Reportez-vous à La meilleure image de base Docker pour votre application Python et L'utilisation d'Alpine peut ralentir les constructions de Python Docker 50× pour en savoir plus sur les raisons pour lesquelles il est préférable d'éviter d'utiliser des images de base basées sur Alpine.
Au final, tout est une question d'équilibre. En cas de doute, commencez par une *-slim
saveur, en particulier en mode développement, pendant que vous construisez votre application. Vous souhaitez éviter d'avoir à mettre à jour en permanence le Dockerfile pour installer les dépendances nécessaires au niveau du système lorsque vous ajoutez un nouveau package Python. Au fur et à mesure que vous renforcez votre application et vos Dockerfile(s) pour la production, vous souhaiterez peut-être explorer l'utilisation d'Alpine pour l'image finale à partir d'une construction en plusieurs étapes.
N'oubliez pas non plus de mettre à jour régulièrement vos images de base pour améliorer la sécurité et augmenter les performances. Lorsqu'une nouvelle version d'une image de base est publiée - c'est-à-dire
3.9.6-slim
->3.9.7-slim
- vous devez extraire la nouvelle image et mettre à jour vos conteneurs en cours d'exécution pour obtenir tous les derniers correctifs de sécurité.
C'est une bonne idée de combiner autant que possible les commandes , et , car elles créent des RUN
calques COPY
. ADD
Chaque calque augmente la taille de l'image car ils sont mis en cache. Par conséquent, à mesure que le nombre de couches augmente, la taille augmente également.
Vous pouvez tester cela avec la docker history
commande :
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
dockerfile latest 180f98132d02 51 seconds ago 259MB
$ docker history 180f98132d02
IMAGE CREATED CREATED BY SIZE COMMENT
180f98132d02 58 seconds ago COPY . . # buildkit 6.71kB buildkit.dockerfile.v0
<missing> 58 seconds ago RUN /bin/sh -c pip install -r requirements.t… 35.5MB buildkit.dockerfile.v0
<missing> About a minute ago COPY requirements.txt . # buildkit 58B buildkit.dockerfile.v0
<missing> About a minute ago WORKDIR /app
...
Attention aux tailles. Seules les commandes RUN
, COPY
et ADD
ajoutent de la taille à l'image. Vous pouvez réduire la taille de l'image en combinant des commandes dans la mesure du possible. Par exemple:
RUN apt-get update
RUN apt-get install -y netcat
Peut être combiné en une seule RUN
commande :
RUN apt-get update && apt-get install -y netcat
Ainsi, on crée un seul calque au lieu de deux, ce qui réduit la taille de l'image finale.
Bien que ce soit une bonne idée de réduire le nombre de calques, il est beaucoup plus important que cela soit moins un objectif en soi et plus un effet secondaire de la réduction de la taille de l'image et des temps de construction. En d'autres termes, concentrez-vous davantage sur les trois pratiques précédentes - les constructions en plusieurs étapes, l'ordre de vos commandes Dockerfile et l'utilisation d'une petite image de base - plutôt que d'essayer d'optimiser chaque commande.
Remarques:
RUN
, COPY
, et ADD
créent chacun des calques.Des astuces:
step
qui les a créés.apt-get upgrade
exécutions car il met à niveau tous les packages vers la dernière version.Enfin, pour plus de lisibilité, c'est une bonne idée de trier les arguments multi-lignes par ordre alphanumérique :
RUN apt-get update && apt-get install -y \
git \
gcc \
matplotlib \
pillow \
&& rm -rf /var/lib/apt/lists/*
Par défaut, Docker exécute les processus de conteneur en tant que racine à l'intérieur d'un conteneur. Cependant, il s'agit d'une mauvaise pratique car un processus exécuté en tant que root à l'intérieur du conteneur s'exécute en tant que root dans l'hôte Docker. Ainsi, si un attaquant accède à votre conteneur, il a accès à tous les privilèges root et peut effectuer plusieurs attaques contre l'hôte Docker, comme-
Pour éviter cela, assurez-vous d'exécuter les processus de conteneur avec un utilisateur non root :
RUN addgroup --system app && adduser --system --group app
USER app
Vous pouvez aller plus loin et supprimer l'accès au shell et vous assurer qu'il n'y a pas non plus de répertoire personnel :
RUN addgroup --gid 1001 --system app && \
adduser --no-create-home --shell /bin/false --disabled-password --uid 1001 --system --group app
USER app
Vérifier:
$ docker run -i sample id
uid=1001(app) gid=1001(app) groups=1001(app)
Ici, l'application dans le conteneur s'exécute sous un utilisateur non root. Cependant, gardez à l'esprit que le démon Docker et le conteneur lui-même fonctionnent toujours avec les privilèges root. Assurez-vous de consulter Exécuter le démon Docker en tant qu'utilisateur non root pour obtenir de l'aide sur l'exécution du démon et des conteneurs en tant qu'utilisateur non root.
À utiliser COPY
sauf si vous êtes certain d'avoir besoin des fonctionnalités supplémentaires fournies avec ADD
.
Quelle est la différence entre COPY
et ADD
?
Les deux commandes vous permettent de copier des fichiers d'un emplacement spécifique dans une image Docker :
ADD <src> <dest>
COPY <src> <dest>
Bien qu'ils semblent servir le même objectif, ils ADD
ont quelques fonctionnalités supplémentaires :
COPY
est utilisé pour copier des fichiers ou des répertoires locaux de l'hôte Docker vers l'image.ADD
peut être utilisé pour la même chose ainsi que pour télécharger des fichiers externes. De plus, si vous utilisez un fichier compressé (tar, gzip, bzip2, etc.) comme <src>
paramètre, ADD
décompressera automatiquement le contenu à l'emplacement donné.# copy local files on the host to the destination
COPY /source/path /destination/path
ADD /source/path /destination/path
# download external file and copy to the destination
ADD http://external.file/url /destination/path
# copy and extract local compresses files
ADD source.file.tar.gz /destination/path
Lorsqu'un fichier requirements est modifié, l'image doit être reconstruite pour installer les nouveaux packages. Les étapes précédentes seront mises en cache, comme indiqué dans Réduire le nombre de couches . Le téléchargement de tous les packages lors de la reconstruction de l'image peut entraîner une activité réseau importante et prendre beaucoup de temps. Chaque reconstruction prend le même temps pour télécharger des packages communs à travers les versions.
Vous pouvez éviter cela en mappant le répertoire du cache pip à un répertoire sur la machine hôte. Ainsi, pour chaque reconstruction, les versions mises en cache persistent et peuvent améliorer la vitesse de construction.
Ajoutez un volume au docker exécuté en tant que -v $HOME/.cache/pip-docker/:/root/.cache/pip
ou en tant que mappage dans le fichier Docker Compose.
Le répertoire présenté ci-dessus est uniquement à titre de référence. Assurez-vous de mapper le répertoire de cache et non les packages de site (où résident les packages construits).
Le déplacement du cache de l'image Docker vers l'hôte peut vous faire gagner de l'espace dans l'image finale.
Si vous utilisez Docker BuildKit , utilisez les montages de cache BuildKit pour gérer le cache :
# syntax = docker/dockerfile:1.2
...
COPY requirements.txt .
RUN --mount=type=cache,target=/root/.cache/pip \
pip install -r requirements.txt
...
Pourquoi est-il recommandé de n'exécuter qu'un seul processus par conteneur ?
Supposons que votre pile d'applications se compose de deux serveurs Web et d'une base de données. Bien que vous puissiez facilement exécuter les trois à partir d'un seul conteneur, vous devez exécuter chacun dans un conteneur séparé pour faciliter la réutilisation et la mise à l'échelle de chacun des services individuels.
Vous pouvez écrire les commandes CMD
et ENTRYPOINT
dans vos fichiers Docker dans les formats tableau (exec) ou chaîne (shell) :
# array (exec)
CMD ["gunicorn", "-w", "4", "-k", "uvicorn.workers.UvicornWorker", "main:app"]
# string (shell)
CMD "gunicorn -w 4 -k uvicorn.workers.UvicornWorker main:app"
Les deux sont corrects et réalisent presque la même chose ; cependant, vous devez utiliser le format exec chaque fois que possible. De la documentation Docker :
CMD
et ENTRYPOINT
dans votre Dockerfile.["program", "arg1", "arg2"]
pas "program arg1 arg2"
. L'utilisation de la forme de chaîne oblige Docker à exécuter votre processus à l'aide de bash, qui ne gère pas correctement les signaux. Compose utilise toujours le formulaire JSON, donc ne vous inquiétez pas si vous remplacez la commande ou le point d'entrée dans votre fichier Compose.Ainsi, puisque la plupart des shells ne traitent pas les signaux vers les processus enfants, si vous utilisez le format shell, CTRL-C
(qui génère un SIGTERM
) peut ne pas arrêter un processus enfant.
Exemple:
FROM ubuntu:18.04
# BAD: shell format
ENTRYPOINT top -d
# GOOD: exec format
ENTRYPOINT ["top", "-d"]
Essayez les deux. Notez qu'avec la saveur du format shell, CTRL-C
cela ne tuera pas le processus. Au lieu de cela, vous verrez ^C^C^C^C^C^C^C^C^C^C^C
.
Une autre mise en garde est que le format shell porte le PID du shell, pas le processus lui-même.
# array format
root@18d8fd3fd4d2:/app# ps ax
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 python manage.py runserver 0.0.0.0:8000
7 ? Sl 0:02 /usr/local/bin/python manage.py runserver 0.0.0.0:8000
25 pts/0 Ss 0:00 bash
356 pts/0 R+ 0:00 ps ax
# string format
root@ede24a5ef536:/app# ps ax
PID TTY STAT TIME COMMAND
1 ? Ss 0:00 /bin/sh -c python manage.py runserver 0.0.0.0:8000
8 ? S 0:00 python manage.py runserver 0.0.0.0:8000
9 ? Sl 0:01 /usr/local/bin/python manage.py runserver 0.0.0.0:8000
13 pts/0 Ss 0:00 bash
342 pts/0 R+ 0:00 ps ax
Dois-je utiliser ENTRYPOINT ou CMD pour exécuter des processus de conteneur ?
Il existe deux manières d'exécuter des commandes dans un conteneur :
CMD ["gunicorn", "config.wsgi", "-b", "0.0.0.0:8000"]
# and
ENTRYPOINT ["gunicorn", "config.wsgi", "-b", "0.0.0.0:8000"]
Les deux font essentiellement la même chose : démarrez l'application sur config.wsgi
avec un serveur Gunicorn et liez-la à 0.0.0.0:8000
.
Le CMD
est facilement contournable. Si vous exécutez docker run <image_name> uvicorn config.asgi
, le CMD ci-dessus est remplacé par les nouveaux arguments - par exemple, uvicorn config.asgi
. Alors que pour remplacer la ENTRYPOINT
commande, il faut spécifier l' --entrypoint
option :
docker run --entrypoint uvicorn config.asgi <image_name>
Ici, il est clair que nous remplaçons le point d'entrée. Il est donc recommandé d'utiliser ENTRYPOINT
over CMD
pour éviter de remplacer accidentellement la commande.
Ils peuvent également être utilisés ensemble.
Par exemple:
ENTRYPOINT ["gunicorn", "config.wsgi", "-w"]
CMD ["4"]
Lorsqu'ils sont utilisés ensemble comme ceci, la commande qui est exécutée pour démarrer le conteneur est :
gunicorn config.wsgi -w 4
Comme indiqué ci-dessus, CMD
est facilement contournable. Ainsi, CMD
peut être utilisé pour passer des arguments à la ENTRYPOINT
commande. Le nombre de travailleurs peut être facilement modifié comme suit :
docker run <image_name> 6
Cela démarrera le conteneur avec six travailleurs Gunicorn plutôt que quatre.
Utilisez a HEALTHCHECK
pour déterminer si le processus en cours d'exécution dans le conteneur est non seulement opérationnel, mais également "sain".
Docker expose une API pour vérifier l'état du processus en cours d'exécution dans le conteneur, qui fournit bien plus d'informations que simplement si le processus est "en cours d'exécution" ou non puisque "en cours d'exécution" couvre "il est opérationnel", "en cours de lancement", et même "coincé dans un état d'erreur de boucle infinie". Vous pouvez interagir avec cette API via l' instruction HEALTHCHECK .
Par exemple, si vous diffusez une application Web, vous pouvez utiliser les éléments suivants pour déterminer si le point de /
terminaison est opérationnel et peut gérer les requêtes :
HEALTHCHECK CMD curl --fail http://localhost:8000 || exit 1
Si vous exécutez docker ps
, vous pouvez voir l'état du HEALTHCHECK
.
Exemple sain :
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09c2eb4970d4 healthcheck "python manage.py ru…" 10 seconds ago Up 8 seconds (health: starting) 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp xenodochial_clarke
Exemple malsain :
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
09c2eb4970d4 healthcheck "python manage.py ru…" About a minute ago Up About a minute (unhealthy) 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp xenodochial_clarke
Vous pouvez aller plus loin et configurer un point de terminaison personnalisé utilisé uniquement pour les vérifications de l'état, puis configurer le HEALTHCHECK
pour tester les données renvoyées. Par exemple, si le point de terminaison renvoie une réponse JSON de {"ping": "pong"}
, vous pouvez demander au HEALTHCHECK
de valider le corps de la réponse.
Voici comment afficher l'état de la vérification de l'état à l'aide dedocker inspect
:
❯ docker inspect --format "{{json .State.Health }}" ab94f2ac7889
{
"Status": "healthy",
"FailingStreak": 0,
"Log": [
{
"Start": "2021-09-28T15:22:57.5764644Z",
"End": "2021-09-28T15:22:57.7825527Z",
"ExitCode": 0,
"Output": "..."
Ici, la sortie est coupée car elle contient toute la sortie HTML.
Vous pouvez également ajouter une vérification d'état à un fichier Docker Compose :
version: "3.8"
services:
web:
build: .
ports:
- '8000:8000'
healthcheck:
test: curl --fail http://localhost:8000 || exit 1
interval: 10s
timeout: 10s
start_period: 10s
retries: 3
Option :
test
: La commande à tester.interval
: L'intervalle à tester -- c'est-à-dire, testez chaque x
unité de temps.timeout
: Temps maximum d'attente de la réponse.start_period
: Quand commencer le bilan de santé. Il peut être utilisé lorsque des tâches supplémentaires sont effectuées avant que les conteneurs ne soient prêts, comme l'exécution de migrations.retries
: nombre maximal de tentatives avant de désigner un test comme failed
.Si vous utilisez un outil d'orchestration autre que Docker Swarm, c'est-à-dire Kubernetes ou AWS ECS, il est fort probable que l'outil dispose de son propre système interne pour gérer les vérifications de l'état. Reportez-vous à la documentation de l'outil particulier avant d'ajouter l'
HEALTHCHECK
instruction.
Dans la mesure du possible, évitez d'utiliser la latest
balise.
Si vous vous fiez à la latest
balise (qui n'est pas vraiment une "balise" puisqu'elle est appliquée par défaut lorsqu'une image n'est pas explicitement balisée), vous ne pouvez pas savoir quelle version de votre code s'exécute en fonction de la balise d'image. Cela complique les restaurations et facilite leur écrasement (accidentellement ou par malveillance). Les balises, comme votre infrastructure et vos déploiements, doivent être immuables .
Quelle que soit la façon dont vous traitez vos images internes, vous ne devez jamais utiliser la latest
balise pour les images de base, car vous pourriez déployer par inadvertance une nouvelle version avec des modifications radicales en production.
Pour les images internes, utilisez des balises descriptives pour identifier plus facilement la version du code en cours d'exécution, gérer les restaurations et éviter les collisions de noms.
Par exemple, vous pouvez utiliser les descripteurs suivants pour créer une balise :
Pour plus d'options, consultez cette réponse de la question Stack Overflow "Version correcte des images Docker".
Par exemple:
docker build -t web-prod-a072c4e5d94b5a769225f621f08af3d4bf820a07-0.1.4 .
Ici, nous avons utilisé ce qui suit pour former la balise :
web
prod
a072c4e5d94b5a769225f621f08af3d4bf820a07
0.1.4
Il est essentiel de choisir un schéma de marquage et d'être cohérent avec celui-ci. Étant donné que les hachages de commit permettent de lier rapidement une balise d'image au code, il est fortement recommandé de les inclure dans votre schéma de balisage.
Les secrets sont des informations sensibles telles que les mots de passe, les informations d'identification de la base de données, les clés SSH, les jetons et les certificats TLS, pour n'en nommer que quelques-uns. Ceux-ci ne doivent pas être intégrés à vos images sans être cryptés, car les utilisateurs non autorisés qui accèdent à l'image peuvent simplement examiner les couches pour en extraire les secrets.
N'ajoutez pas de secrets à vos Dockerfiles en clair, surtout si vous poussez les images vers un registre public comme Docker Hub :
FROM python:3.9-slim
ENV DATABASE_PASSWORD "SuperSecretSauce"
Au lieu de cela, ils doivent être injectés via :
En outre, vous pouvez aider à empêcher les fuites de secrets en ajoutant des fichiers et des dossiers secrets communs à votre fichier .dockerignore :
**/.env
**/.aws
**/.ssh
Enfin, soyez explicite sur les fichiers qui sont copiés sur l'image plutôt que de copier tous les fichiers de manière récursive :
# BAD
COPY . .
# GOOD
copy ./app.py .
Être explicite aide également à limiter le contournement du cache.
Vous pouvez transmettre des secrets via des variables d'environnement, mais ils seront visibles dans tous les processus enfants, conteneurs liés et journaux, ainsi que via docker inspect
. Il est également difficile de les mettre à jour.
$ docker run --detach --env "DATABASE_PASSWORD=SuperSecretSauce" python:3.9-slim
d92cf5cf870eb0fdbf03c666e7fcf18f9664314b79ad58bc7618ea3445e39239
$ docker inspect --format='{{range .Config.Env}}{{println .}}{{end}}' d92cf5cf870eb0fdbf03c666e7fcf18f9664314b79ad58bc7618ea3445e39239
DATABASE_PASSWORD=SuperSecretSauce
PATH=/usr/local/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
LANG=C.UTF-8
GPG_KEY=E3FF2839C048B25C084DEBE9B26995E310250568
PYTHON_VERSION=3.9.7
PYTHON_PIP_VERSION=21.2.4
PYTHON_SETUPTOOLS_VERSION=57.5.0
PYTHON_GET_PIP_URL=https://github.com/pypa/get-pip/raw/c20b0cfd643cd4a19246ccf204e2997af70f6b21/public/get-pip.py
PYTHON_GET_PIP_SHA256=fa6f3fb93cce234cd4e8dd2beb54a51ab9c247653b52855a48dd44e6b21ff28b
Il s'agit de l'approche la plus simple de la gestion des secrets. Bien que ce ne soit pas le plus sûr, il gardera les gens honnêtes honnêtes car il fournit une fine couche de protection, aidant à garder les secrets cachés des yeux curieux et errants.
Transmettre des secrets en utilisant un volume partagé est une meilleure solution, mais ils doivent être chiffrés, via Vault ou AWS Key Management Service (KMS), car ils sont enregistrés sur disque.
Vous pouvez transmettre des secrets au moment de la construction en utilisant des arguments de construction , mais ils seront visibles pour ceux qui ont accès à l'image via docker history
.
Exemple:
FROM python:3.9-slim
ARG DATABASE_PASSWORD
Construire:
$ docker build --build-arg "DATABASE_PASSWORD=SuperSecretSauce" .
Si vous n'avez besoin d'utiliser les secrets que temporairement dans le cadre de la construction, c'est-à-dire des clés SSH pour cloner un référentiel privé ou télécharger un package privé, vous devez utiliser une construction en plusieurs étapes, car l'historique du générateur est ignoré pour les étapes temporaires :
# temp stage
FROM python:3.9-slim as builder
# secret
ARG SSH_PRIVATE_KEY
# install git
RUN apt-get update && \
apt-get install -y --no-install-recommends git
# use ssh key to clone repo
RUN mkdir -p /root/.ssh/ && \
echo "${PRIVATE_SSH_KEY}" > /root/.ssh/id_rsa
RUN touch /root/.ssh/known_hosts &&
ssh-keyscan bitbucket.org >> /root/.ssh/known_hosts
RUN git clone git@github.com:testdrivenio/not-real.git
# final stage
FROM python:3.9-slim
WORKDIR /app
# copy the repository from the temp image
COPY --from=builder /your-repo /app/your-repo
# use the repo for something!
La construction en plusieurs étapes ne conserve l'historique que pour l'image finale. Gardez à l'esprit que vous pouvez utiliser cette fonctionnalité pour les secrets permanents dont vous avez besoin pour votre application, comme un identifiant de base de données.
Vous pouvez également utiliser la nouvelle --secret
option dans Docker build pour transmettre des secrets aux images Docker qui ne sont pas stockées dans les images.
# "docker_is_awesome" > secrets.txt
FROM alpine
# shows secret from default secret location:
RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
Cela montera le secret à partir du secrets.txt
fichier.
Construisez l'image :
docker build --no-cache --progress=plain --secret id=mysecret,src=secrets.txt .
# output
...
#4 [1/2] FROM docker.io/library/alpine
#4 sha256:665ba8b2cdc0cb0200e2a42a6b3c0f8f684089f4cd1b81494fbb9805879120f7
#4 CACHED
#5 [2/2] RUN --mount=type=secret,id=mysecret cat /run/secrets/mysecret
#5 sha256:75601a522ebe80ada66dedd9dd86772ca932d30d7e1b11bba94c04aa55c237de
#5 0.635 docker_is_awesome#5 DONE 0.7s
#6 exporting to image
Enfin, vérifiez l'historique pour voir si le secret fuit :
❯ docker history 49574a19241c
IMAGE CREATED CREATED BY SIZE COMMENT
49574a19241c 5 minutes ago CMD ["/bin/sh"] 0B buildkit.dockerfile.v0
<missing> 5 minutes ago RUN /bin/sh -c cat /run/secrets/mysecret # b… 0B buildkit.dockerfile.v0
<missing> 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/sh"] 0B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:aad4290d27580cc1a… 5.6MB
Pour en savoir plus sur les secrets de construction, consultez Ne divulguez pas les secrets de construction de votre image Docker .
Si vous utilisez Docker Swarm , vous pouvez gérer les secrets avec Docker secrets .
Par exemple, initialisez le mode Docker Swarm :
$ docker swarm init
Créez un secret Docker :
$ echo "supersecretpassword" | docker secret create postgres_password -
qdqmbpizeef0lfhyttxqfbty0
$ docker secret ls
ID NAME DRIVER CREATED UPDATED
qdqmbpizeef0lfhyttxqfbty0 postgres_password 4 seconds ago 4 seconds ago
Lorsqu'un conteneur a accès au secret ci-dessus, il se monte sur /run/secrets/postgres_password
. Ce fichier contiendra la valeur réelle du secret en clair.
Vous utilisez un autre outil d'orhestration ?
Nous avons déjà mentionné plusieurs fois l'utilisation d'un fichier .dockerignore . Ce fichier est utilisé pour spécifier les fichiers et dossiers que vous ne souhaitez pas ajouter au contexte de construction initial envoyé au démon Docker, qui construira ensuite votre image. En d'autres termes, vous pouvez l'utiliser pour définir le contexte de génération dont vous avez besoin.
Lorsqu'une image Docker est construite, l'intégralité du contexte Docker, c'est-à-dire la racine de votre projet, est envoyée au démon Docker avant que les commandes COPY
ou ADD
ne soient évaluées. Cela peut être assez coûteux, surtout si vous avez de nombreuses dépendances, des fichiers de données volumineux ou des artefacts de construction dans votre projet. De plus, la CLI Docker et le démon peuvent ne pas être sur la même machine. Ainsi, si le démon est exécuté sur une machine distante, vous devez être encore plus attentif à la taille du contexte de construction.
Que devez-vous ajouter au fichier .dockerignore ?
Exemple:
**/.git
**/.gitignore
**/.vscode
**/coverage
**/.env
**/.aws
**/.ssh
Dockerfile
README.md
docker-compose.yml
**/.DS_Store
**/venv
**/env
En résumé, un .dockerignore correctement structuré peut aider :
Le peluchage est le processus de vérification de votre code source pour les erreurs programmatiques et stylistiques et les mauvaises pratiques qui pourraient conduire à des défauts potentiels. Tout comme avec les langages de programmation, les fichiers statiques peuvent également être pelucheux. Avec vos Dockerfiles en particulier, les linters peuvent vous aider à vous assurer qu'ils sont maintenables, à éviter une syntaxe obsolète et à respecter les meilleures pratiques. Le peluchage de vos images devrait faire partie intégrante de vos pipelines CI.
Hadolint est le linter Dockerfile le plus populaire :
$ hadolint Dockerfile
Dockerfile:1 DL3006 warning: Always tag the version of an image explicitly
Dockerfile:7 DL3042 warning: Avoid the use of cache directory with pip. Use `pip install --no-cache-dir <package>`
Dockerfile:9 DL3059 info: Multiple consecutive `RUN` instructions. Consider consolidation.
Dockerfile:17 DL3025 warning: Use arguments JSON notation for CMD and ENTRYPOINT arguments
Vous pouvez le voir en action en ligne sur https://hadolint.github.io/hadolint/ . Il existe également une extension de code VS.
Vous pouvez coupler vos Dockerfiles avec l'analyse d'images et de conteneurs pour les vulnérabilités.
Quelques options :
docker scan
commande CLI pour numériser des images.En résumé, peluchez et analysez vos Dockerfiles et images pour faire apparaître tout problème potentiel qui s'écarte des meilleures pratiques.
Comment savez-vous que les images utilisées pour exécuter votre code de production n'ont pas été altérées ?
La falsification peut passer par le fil via des attaques de l' homme du milieu (MITM) ou du fait que le registre est complètement compromis.
Docker Content Trust (DCT) permet la signature et la vérification des images Docker à partir de registres distants.
Pour vérifier l'intégrité et l'authenticité d'une image, définissez la variable d'environnement suivante :
DOCKER_CONTENT_TRUST=1
Maintenant, si vous essayez d'extraire une image qui n'a pas été signée, vous recevrez l'erreur suivante :
Error: remote trust data does not exist for docker.io/namespace/unsigned-image:
notary.docker.io does not have trust data for docker.io/namespace/unsigned-image
Vous pouvez en savoir plus sur la signature d'images dans la documentation Signature d'images avec Docker Content Trust .
Lorsque vous téléchargez des images depuis Docker Hub, assurez-vous d'utiliser des images officielles ou des images vérifiées provenant de sources fiables. Les équipes plus importantes devraient envisager d'utiliser leur propre registre de conteneurs privé interne .
Devriez-vous utiliser un environnement virtuel à l'intérieur d'un conteneur ?
Dans la plupart des cas, les environnements virtuels sont inutiles tant que vous vous en tenez à n'exécuter qu'un seul processus par conteneur. Étant donné que le conteneur lui-même fournit une isolation, les packages peuvent être installés à l'échelle du système. Cela dit, vous souhaiterez peut-être utiliser un environnement virtuel dans une construction en plusieurs étapes plutôt que de créer des fichiers de roue.
Exemple avec roues :
# temp stage
FROM python:3.9-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
COPY requirements.txt .
RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
# final stage
FROM python:3.9-slim
WORKDIR /app
COPY --from=builder /app/wheels /wheels
COPY --from=builder /app/requirements.txt .
RUN pip install --no-cache /wheels/*
Exemple avec virtualenv :
# temp stage
FROM python:3.9-slim as builder
WORKDIR /app
ENV PYTHONDONTWRITEBYTECODE 1
ENV PYTHONUNBUFFERED 1
RUN apt-get update && \
apt-get install -y --no-install-recommends gcc
RUN python -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
COPY requirements.txt .
RUN pip install -r requirements.txt
# final stage
FROM python:3.9-slim
COPY --from=builder /opt/venv /opt/venv
WORKDIR /app
ENV PATH="/opt/venv/bin:$PATH"
It's a good idea to limit the memory usage of your Docker containers, especially if you're running multiple containers on a single machine. This can prevent any of the containers from using all available memory and thereby crippling the rest.
The easiest way to limit memory usage is to use --memory
and --cpu
options in the Docker cli:
$ docker run --cpus=2 -m 512m nginx
The above command limits the container usage to 2 CPUs and 512 megabytes of main memory.
You can do the same in a Docker Compose file like so:
version: "3.9"
services:
redis:
image: redis:alpine
deploy:
resources:
limits:
cpus: 2
memory: 512M
reservations:
cpus: 1
memory: 256M
Take note of the reservations
field. It's used to set a soft limit, which takes priority when the host machine has low memory or CPU resources.
Additional resources:
Les applications exécutées dans vos conteneurs Docker doivent écrire des messages de journal sur la sortie standard (stdout) et l'erreur standard (stderr) plutôt que sur un fichier.
Vous pouvez ensuite configurer le démon Docker pour envoyer vos messages de journal à une solution de journalisation centralisée (comme CloudWatch Logs ou Papertrail ).
Pour en savoir plus, consultez Traiter les journaux comme des flux d'événements à partir de l'application Twelve-Factor et Configurer les pilotes de journalisation à partir de la documentation Docker.
Gunicorn utilise un système de pulsation basé sur des fichiers pour s'assurer que tous les processus de travail forkés sont actifs.
Dans la plupart des cas, les fichiers heartbeat se trouvent dans "/tmp", qui est souvent en mémoire via tmpfs . Étant donné que Docker n'utilise pas tmpfs par défaut, les fichiers seront stockés sur un système de fichiers sauvegardé sur disque. Cela peut causer des problèmes , comme des gels aléatoires puisque le système de pulsation utilise os.fchmod
, ce qui peut bloquer un travailleur si le répertoire se trouve en fait sur un système de fichiers sauvegardé sur disque.
Heureusement, il existe une solution simple : remplacez le répertoire heartbeat par un répertoire mappé en mémoire via le --worker-tmp-dir
drapeau.
gunicorn --worker-tmp-dir /dev/shm config.wsgi -b 0.0.0.0:8000
Cet article a examiné plusieurs bonnes pratiques pour rendre vos Dockerfiles et images plus propres, plus légers et plus sécurisés.
Source : https://testdrive.io
1602968400
Python is awesome, it’s one of the easiest languages with simple and intuitive syntax but wait, have you ever thought that there might ways to write your python code simpler?
In this tutorial, you’re going to learn a variety of Python tricks that you can use to write your Python code in a more readable and efficient way like a pro.
Swapping value in Python
Instead of creating a temporary variable to hold the value of the one while swapping, you can do this instead
>>> FirstName = "kalebu"
>>> LastName = "Jordan"
>>> FirstName, LastName = LastName, FirstName
>>> print(FirstName, LastName)
('Jordan', 'kalebu')
#python #python-programming #python3 #python-tutorials #learn-python #python-tips #python-skills #python-development
1602666000
Today you’re going to learn how to use Python programming in a way that can ultimately save a lot of space on your drive by removing all the duplicates.
In many situations you may find yourself having duplicates files on your disk and but when it comes to tracking and checking them manually it can tedious.
Heres a solution
Instead of tracking throughout your disk to see if there is a duplicate, you can automate the process using coding, by writing a program to recursively track through the disk and remove all the found duplicates and that’s what this article is about.
But How do we do it?
If we were to read the whole file and then compare it to the rest of the files recursively through the given directory it will take a very long time, then how do we do it?
The answer is hashing, with hashing can generate a given string of letters and numbers which act as the identity of a given file and if we find any other file with the same identity we gonna delete it.
There’s a variety of hashing algorithms out there such as
#python-programming #python-tutorials #learn-python #python-project #python3 #python #python-skills #python-tips
1597751700
Magic Methods are the special methods which gives us the ability to access built in syntactical features such as ‘<’, ‘>’, ‘==’, ‘+’ etc…
You must have worked with such methods without knowing them to be as magic methods. Magic methods can be identified with their names which start with __ and ends with __ like init, call, str etc. These methods are also called Dunder Methods, because of their name starting and ending with Double Underscore (Dunder).
Now there are a number of such special methods, which you might have come across too, in Python. We will just be taking an example of a few of them to understand how they work and how we can use them.
class AnyClass:
def __init__():
print("Init called on its own")
obj = AnyClass()
The first example is _init, _and as the name suggests, it is used for initializing objects. Init method is called on its own, ie. whenever an object is created for the class, the init method is called on its own.
The output of the above code will be given below. Note how we did not call the init method and it got invoked as we created an object for class AnyClass.
Init called on its own
Let’s move to some other example, add gives us the ability to access the built in syntax feature of the character +. Let’s see how,
class AnyClass:
def __init__(self, var):
self.some_var = var
def __add__(self, other_obj):
print("Calling the add method")
return self.some_var + other_obj.some_var
obj1 = AnyClass(5)
obj2 = AnyClass(6)
obj1 + obj2
#python3 #python #python-programming #python-web-development #python-tutorials #python-top-story #python-tips #learn-python