Entrypoint

Un ENTRYPOINT ha i compiti di:

  • verificare la sintassi e le opzioni del CMD
  • inizializzare l’ambiente

Un ENTRYPOINT è una procedura shell con la struttura:

#! /bin/sh

.....

exec “$@”

La procedura deve iniziare con lo hash-bang, nel nostro caso #! /bin/sh

L’ultima istruzione eseguita è il chain command: esegue il CMD con gli eventuali parametri.

Esempio: postgres-alpine

Postgres

Directory di progetto:

mkdir -p ~/docker/ex/postgres-alpine
cd ~/docker/ex/postgres-alpine

Dockerfile;

vim Dockerfile
FROM alpine:3.1

RUN apk add --update postgresql curl
RUN curl -o /usr/local/bin/gosu \
  -SL "https://github.com/tianon/gosu/releases/download/1.2/gosu-amd64"
RUN chmod +x /usr/local/bin/gosu
RUN apk del curl && rm -rf /var/cache/apk/*

RUN mkdir /docker-entrypoint-initdb.d
RUN mkdir -p /var/run/postgresql && \
    chown -R postgres /var/run/postgresql

ENV LANG en_US.utf8
ENV PATH /usr/lib/postgresql/9.3/bin:$PATH
ENV PGDATA /var/lib/postgresql/data

VOLUME /var/lib/postgresql/data

COPY docker-entrypoint.sh /
RUN chmod +x docker-entrypoint.sh

ENTRYPOINT ["/docker-entrypoint.sh"]

USER postgres
EXPOSE 5432
CMD ["postgres"]

Notare che la prima direttiva, FROM alpine:3.1, ha un'immagine con tag.

Non vogliamo usare il tag implicito latest ma una versione di alpine ben nota.

Con opportuna ricerca documentale in rete, abbiamo infatti scopero che alpine 3.1 ha nei suoi repository software la release postgres 9.3, che è la specifica versione che vogliamo installare.

Nostro Entrypoint

Uno entrypoint ha il compito di preparare l’ambiente al CMD che poi viene eseguito.

E’ quasi sempre una procedura shell.

Requisiti per la preparazione di immagini:

  • conoscenza di comandi amministrativi Linux nella distribuzione scelta per l’immagine di base
    • Ubuntu vs Alpine
  • conoscenza della programmazione shell
  • conoscenza di utilities che permettano il cambiamento batch e non interattivo di files di configurazione
    • sed, awk, tr, ecc.
vim entrypoint.sh
#!/bin/sh
chown -R postgres "$PGDATA"
# if the datadir does not exist, create it
if [ -z "$(ls -A "$PGDATA")" ]; then
        gosu postgres initdb
# listen on all network addresses
        sed -ri "s/^#(listen_addresses\s*=\s*)\S+/\1'*'/" "$PGDATA"/postgresql.conf
# acquire or set environment variables
        : ${POSTGRES_USER:=postgres}
        : ${POSTGRES_DB:=$POSTGRES_USER}
# if the password exists, use it with MD5
        if [ "$POSTGRES_PASSWORD" ]; then
                pass="PASSWORD '$POSTGRES_PASSWORD'"
                authMethod=md5
# otherwise warn, then trust an access without it
        else
                cat >&2 <<-'EOWARN'
****************************************************
WARNING: No password has been set for the database. 
****************************************************
EOWARN
                pass=
                authMethod=trust
        fi
# prepare the rest of the environment
        if [ "$POSTGRES_DB" != 'postgres' ]; then
                gosu postgres postgres --single -jE <<-EOSQL
                  CREATE DATABASE "$POSTGRES_DB" ;
EOSQL
                echo
        fi

        if [ "$POSTGRES_USER" = 'postgres' ]; then
                op='ALTER'
        else
                op='CREATE'
        fi

        gosu postgres postgres --single -jE <<-EOSQL
                $op USER "$POSTGRES_USER" WITH SUPERUSER $pass ;
EOSQL
        echo
# allow anybody to connect from any IP address
        { echo; echo "host all all 0.0.0.0/0 $authMethod"; } >> "$PGDATA"/pg_hba.conf
        exec gosu postgres "$@"
fi
exec "$@"

Important Le procedure shell, specie per entrypoint, possono essere complesse. Occorre acquisire un certo livello di esperienza di programmazione shell.

Utility gosu

L’invocazione di una procedura shell la esegue in una sottoshell, figlia di quella corrente.

  • Questo non si deve fare in un Entrypoint, se si vogliono compiere cambiamenti all’ambiente che durino
  • Al termine della sottoshell i cambiamenti sparirebbero

Si può usare gosu:

  • gosu esegue il suo argomento nella shell corrente, non in una sottoshell
  • riceve eventuali segnali inviati alla shell originale
  • è un programma originalmente scritto in Go

Alpine lo ha come package nativo nelle versioni più recenti, in quelle precedenti occorre scaricarlo da https://github.com/tianon/gosu

Build e Test

Build:

docker build -t postgres-alpine .

Lancio del contenitore:

docker run --name postalp -p 5435:5432 postgres-alpine

(porta 5435 non 5432 sullo host per evitare potenziali collisioni con un postgres già installato)

Tattica di debugging:

  • La prima volta non mettere l’opzione -d (detached)
    • L’output andrà su video
    • Permette il debugging specie di docker-entrypoint.sh
  • Fermare con Ctrl-C e rimuovere il contenitore
  • Le volte successive mettere l’opzione -d