Differenze tra RUN CMD e ENTRYPOINT
Differenze tra RUN CMD e ENTRYPOINT: uno degli argomenti peggiori quando si tratta di avere a che fare con Docker. Oggi sfatiamo qualche mito e facciamo chiarezza su queste tre istruzioni.
Introduzione
Alcune istruzioni di Docker come quelle sopracitate sembrano molto simili e causano confusione tra gli utilizzatori che hanno appena iniziato a lavorare con Docker o lo fanno in modo discontinuo.
Versione breve
- RUN esegue i comandi in un nuovo layer e crea una nuova immagine, spesso definita intermedia. Questo comando viene spesso utilizzato per l’installazione di pacchetti tramite i vari package manager.
- CMD definisce il comando e/o i parametri predefiniti, che possono essere sovrascritti dalla riga di comando durante l’esecuzione del container Docker.
- ENTRYPOINT configura un container che verrà eseguito come eseguibile.
Versione lunga
Quando Docker esegue un container, esegue un’immagine al suo interno. Questa immagine viene solitamente creata eseguendo le istruzioni definire nel Dockerfile, che aggiungono livelli (o layer) sopra l’immagine esistente.
La prima istruzione rappresenta la distribuzione scelta -solitamente un sistema operativo con qualche pacchetto- e ogni livello aggiuntivo crea una nuova immagine.
L’immagine finale di Docker ricorda una cipolla con il sistema operativo all’interno e diversi livelli sopra questo primo strato. Ad esempio, la tua immagine può essere creata installando diversi pacchetti tramite apt e la tua applicazione può quindi basarsi su una versione minificata di Ubuntu 20.04.
Architettura Docker come una cipolla
Modalità diretta vs. modalità shell
Alcuni di questi comandi possono essere eseguiti in due modalità, diretta e shell: nel primo caso, la sintassi è del tipo:
$ RUN [“apt”, “install”, “python3”]
Nel secondo caso invece, la sintassi è la seguente:
$ RUN apt install python3
Quando l’istruzione viene eseguita con la modalità diretta, viene chiamato l’eseguibile e l’elaborazione della shell non avviene. Cosa vuol dire?
Vuol dire che il seguente comando inserito all’interno di un Dockerfile non riuscirebbe a valorizzare la variabile e produrrebbe un risultato pari a “Hello $name”:
ENTRYPOINT ["/bin/echo", "Hello $name"]
In questo altro caso, l’output sarebbe corretto:
ENV name John Doe
ENTRYPOINT ["/bin/bash", "-c", "echo Hello $name"]
$ Hello John Doe
Comando RUN
Abbiamo detto che il comando RUN esegue i comandi in un nuovo layer creando una nuova immagine. Quando usato per installare pacchetti software, fa sì che venga creata un’immagine intermedia che poi viene scartata una volta terminata l’esecuzione dell’attività richiesta.
Può essere eseguito in entrambe le modalità.
Esempi
$ RUN npm install python3
$ RUN apt update && apt install -y \
git \
xz\
unzip
Comando CMD
L’istruzione CMD consente di impostare un comando predefinito, che verrà eseguito solo quando si esegue il container senza specificare un comando. Se il container Docker viene eseguito con un comando, il comando predefinito verrà ignorato.
Se Dockerfile ha più di un’istruzione CMD, tutte le istruzioni CMD tranne l’ultima vengono ignorate.
Può essere eseguito in entrambe le modalità.
Esempi
$ CMD echo “Hello world”
$ CMD node server.js
Comando ENTRYPOINT
L’istruzione ENTRYPOINT consente di configurare un container che verrà eseguito come eseguibile. È simile all’istruzione CMD, perché consente anche di specificare un comando con dei parametri, ma a differenza è che i parametri non verranno ignorati quando il container Docker viene eseguito con i parametri passati tramite riga di comando.
Esempi
FROM ubuntu:trusty
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
ENTRYPOINT /bin/echo "Welcome, $name"
COPY entrypoint.sh /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
Esempi di utilizzo
Airflow
FROM python:3.8-alpine
ENV AIRFLOW_VERSION=1.10.11
ENV AIRFLOW_EXTRAS=async,all_dbs,celery,crypto,devel_hadoop,jdbc,ldap,password,redis,s3,samba,ssh,statsd
ENV AIRFLOW_HOME=/opt/airflow
ENV AIRFLOW_CONFIG=/opt/airflow/airflow.cfg
RUN set -xe \
&& apk add --no-cache \
build-base \
cyrus-sasl-dev \
freetds \
freetds-dev \
krb5-dev \
libffi-dev \
mariadb-dev \
postgresql-dev \
python3-dev \
&& pip install --no-cache-dir cython numpy psycopg2-binary \
&& pip install --no-cache-dir apache-airflow[${AIRFLOW_EXTRAS}]==${AIRFLOW_VERSION} \
&& apk del \
build-base \
cyrus-sasl-dev \
freetds-dev \
krb5-dev \
libffi-dev \
mariadb-dev \
postgresql-dev \
python3-dev \
&& rm -rf /root/.cache/pip
WORKDIR ${AIRFLOW_HOME}
VOLUME ${AIRFLOW_HOME}
EXPOSE 8080
ENTRYPOINT ["airflow"]
CMD ["--help"]
Django CMS
FROM vimagick/python:2
WORKDIR /app
RUN set -xe \
&& apk add -U py-pillow tzdata \
&& pip install --no-cache-dir djangocms-installer \
&& mkdir -p data \
&& djangocms --db sqlite://localhost/data/project.db \
--filer \
--languages en \
--no-input \
--parent-dir . \
--skip-empty-check \
--utc \
mysite
VOLUME /app/data
EXPOSE 80
CMD ["python", "manage.py", "runserver", "0.0.0.0:80"]
MariaDB
FROM alpine
RUN set -xe \
&& apk add -U bash \
mariadb \
mariadb-client \
tzdata \
&& mkdir -p /run/mysqld \
&& chown mysql:mysql /run/mysqld \
&& sed -Ei -e 's/^(bind-address|log)/#&/' \
-e 's/^\[mysqld\]$/&\nskip-host-cache\nskip-name-resolve\nuser=mysql/' /etc/mysql/my.cnf \
&& rm -rf /var/cache/apk/*
COPY docker-entrypoint.sh /
VOLUME /var/lib/mysql
ENTRYPOINT ["/docker-entrypoint.sh"]
EXPOSE 3306
CMD ["mysqld"]
Tomcat
FROM tomcat:8.0-alpine
ADD sample.war /usr/local/tomcat/webapps/
EXPOSE 8080
CMD ["catalina.sh", "run"]
Se ancora non fossero chiare le differenze, prova a dare un’occhiata tra le risorse utili per trovare ispirazione!