Aller au contenu
Sylvain Joffraud l4w
Retour

Override d’entrypoint : l’image perd son ENTRYPOINT

Je veux faire un sed sur un fichier de conf au démarrage d’un conteneur tiers, avant que son daemon ne lise la conf. Pattern naturel : un entrypoint wrapper qui fait le sed puis ré-exécute la commande originale via $@.

services:
  app:
    image: vendor/app:latest
    entrypoint:
      - /bin/sh
      - -c
      - |
        sed "s|PLACEHOLDER|$$VALUE|g" /tmp/conf.tmpl > /opt/conf.xml
        exec "$$0" "$$@"

Logique attendue : sh -c reçoit les positional args, $0 et $@ reproduiront le CMD original.

Symptôme

Container démarre, exit 0 en 1 seconde, aucun log applicatif. Pas de stack trace, pas d’erreur. Restart loop avec restart: unless-stopped.

Cause

Inspection de l’image :

$ docker image inspect vendor/app:latest \
    --format '{{json .Config.Entrypoint}} | {{json .Config.Cmd}}'
["/opt/app/bin/java"] | ["-jar","server.jar","conf/app.xml"]

L’image a ENTRYPOINT=["java"] et CMD=["-jar","server.jar","conf/app.xml"]. La commande effective au run normal : java -jar server.jar conf/app.xml.

Quand on override entrypoint: dans compose, on remplace ENTRYPOINT de l’image. Seul le CMD reste, devenant les positional args du nouvel entrypoint. Dans le wrapper, "$0" vaut -jar (premier élément du CMD), pas java.

exec "-jar" server.jar conf/app.xml essaie d’exécuter un binaire littéralement nommé -jar. Selon le shell : exit 127, exit 0 silencieux (sh traite -jar comme une option d’exec), ou comportement indéfini. Avec set -e, le résultat varie d’un shell à l’autre.

Le piège est insidieux : on suppose souvent qu’override d’entrypoint = override de CMD = équivalent. C’est faux. Docker stocke ENTRYPOINT et CMD séparément, et entrypoint: en compose ne touche QUE le premier.

Solution

1. Hardcoder le binaire dans le wrapper (le plus simple, légèrement fragile aux mises à jour d’image) :

entrypoint:
  - /bin/sh
  - -c
  - |
    sed "s|PLACEHOLDER|$$VALUE|g" /tmp/conf.tmpl > /opt/conf.xml
    cd /opt/app
    exec /opt/app/bin/java -jar server.jar conf/app.xml

2. Garder $@ correct via un séparateur -- :

entrypoint:
  - /bin/sh
  - -c
  - |
    sed "s|PLACEHOLDER|$$VALUE|g" /tmp/conf.tmpl > /opt/conf.xml
    exec /opt/app/bin/java "$$@"
  - --

Le -- devient $0 (placeholder), le CMD original devient $@. Stable même si le CMD change dans une mise à jour d’image.

3. Build une image custom : FROM vendor/app + COPY entrypoint + ENTRYPOINT [...]. Le plus propre si la modif est durable.

Lessons learned

Questions fréquentes

Mon conteneur Docker fait exit 0 en 1 seconde sans aucun log, pourquoi ?
Un exit 0 immédiat et sans log applicatif signale souvent un entrypoint wrapper qui a planté en silence, pas l’application. Ne cherchez pas dans les logs de l’app : inspectez la commande effective avec docker inspect <container> --format '{{.Config.Entrypoint}}'. Si votre wrapper sh -c tente d’exécuter un argument commençant par -, le shell peut le traiter comme une option et sortir en 0 sans erreur visible.
En docker-compose, est-ce que entrypoint: remplace seulement le CMD ou aussi l’ENTRYPOINT de l’image ?
La clé entrypoint: en compose remplace l’ENTRYPOINT de l’image, pas le CMD. Docker stocke ENTRYPOINT et CMD séparément : après votre override, seul le CMD de l’image subsiste et devient les arguments positionnels de votre nouvel entrypoint. Si l’image mettait son binaire (java, python, node) dans ENTRYPOINT, ce binaire disparaît et n’apparaît pas dans $@.
Dans mon entrypoint wrapper sh -c, exec $0 $@ n’exécute pas la bonne commande, pourquoi ?
Parce qu’après l’override, $0 ne vaut pas le binaire de l’image mais le premier élément du CMD restant (par exemple -jar au lieu de java). Du coup exec -jar server.jar tente de lancer un binaire littéralement nommé -jar. Vérifiez ce que contiennent réellement ENTRYPOINT et CMD avec docker image inspect vendor/app:latest --format '{{json .Config.Entrypoint}} | {{json .Config.Cmd}}'.
Comment préserver $@ correctement dans un wrapper sh -c en docker-compose ?
Ajoutez un placeholder -- comme dernier élément du tableau entrypoint:. Ce -- devient $0 tandis que le CMD original de l’image se retrouve intact dans $@, et vous appelez ensuite exec /opt/app/bin/java "$$@". C’est stable même si le CMD change lors d’une mise à jour d’image, contrairement au fait de hardcoder la commande dans le wrapper.

Partager cet article :

Article précédent
Hash avec `$` en env var : tronqué en silence
Article suivant
Bind mount fantôme : git ne remplace pas un dossier