2.11. Integrar datos en el contenedor Docker
Muchas veces necesitaremos utilizar un input de manera sistemática en un contenedor. Por ejemplo, si queremos hacer un variant call utilizando GATK, los genomas de referencia siempre serán los mismos y tal vez es interesante guardarlos dentro del contenedor para asegurarnos la reproducibilidad de los resultados a fin de que no dependa de la referencia utilizada. Para un caso más simple introduciremos nuestro script num.py en el contenedor. Para ello crearemos una nueva imagen modificando el Dockerfile.
Añadiremos una nueva línea:
COPY num.py /home
Estaremos haciendo una copia del script en el home del contenedor. Hacemos otra imagen:
$ docker image build -t uoc/alpine-num .
Si ahora entramos de forma interactiva dentro del contenedor y listáis los archivos dentro de /home encontrareis el archivo num.py:
$ docker container run --rm -it uoc/alpine-num sh
El orden de los comandos en el Dockerfile es importante. Es recomendable hacer los comandos COPY después de los RUN, ya que al hacer el build, Docker va por orden, y si en algún momento queremos añadir otro script en lugar de num.py, si el COPY está al final del proceso, Docker utiliza los RUNs que tiene en memoria y no ha de construir la imagen de cero, y el proceso es mucho más rápido. Docker va línea a línea, y si esa capa o conjunto de capas ya la tiene en memoria caché agiliza el proceso no reinstalándolas.
Si los datos que queremos introducir dentro de nuestra imagen están en internet, directamente podemos copiarlos utilizando RUN. Podríamos añadir estas líneas en el Dockerfile como ejemplo:
RUN wget https://ftp.ncbi.nlm.nih.gov/refseq/H_sapiens/annotation/GRCh38_latest/refseq_identifiers/GRCh38_latest_clinvar.vcf.gz
Por defecto, el archivo se copia en el root del sistema del contenedor. Si quisiéramos moverlo a otra localización podríamos especificarla:
RUN mv GRCh38_latest_clinvar.vcf.gz /home
y la copiaría en nuestra carpeta /home.
Ahora que sabemos cómo introducir el script dentro del contenedor, podemos crear un nuevo contenedor que corra el script automáticamente. Debemos substituir CMD del Dockerfile a:
CMD ["python3","/home/num.py"]
De esta manera creamos una nueva imagen:
$ docker image build -t uoc/alpine-numpy-ex .
y ejecutamos directamente:
$ docker container run --rm uoc/alpine-numpy-ex
y nos devuelve el resultado del archivo num.py.
Si queremos introducir un nuevo script de Python (atcg.py) donde su resultado dependa de un argumento,
import sys filename = sys.argv[1] filenumb = len(filename) print (filenumb)
lo copiaremos mediante Dockerfile:
COPY atcg.py /home
y crearemos una nueva imagen:
$ docker image build -t uoc/alpine-atcg .
Si lo corremos directamente, tendremos el resultado del CMD (en nuestro caso, la versión de Python); mientras que si añadimos argumentos al comando run
, sobrescribe CMD y obtenemos el resultado del nuevo script:
$ docker container run --rm uoc/alpine-atcg python3 /home/atcg.py ATG
Si quisiéramos tener este nuevo script como los comandos por defecto deberíamos cambiar en el Dockerfile la línea de CMD por los valores por defecto y crear una nueva línea nombrada ENTRYPOINT para localizar el script:
ENTRYPOINT ["python3","atcg.py"] CMD ["ACTG"]
También podemos añadir antes que ENTRYPOINT y CMD una entrada para definir el directorio de trabajo:
WORKDIR /home
De esta manera, si creamos una nueva imagen uoc/alpine-entry
y ejecutamos el contenedor directamente,
$ docker container run --rm uoc/alpine-entry
nos devolverá «4» la longitud de la línea «ACTG» ubicada por defecto en el Dockerfile. Si ahora al final del comando run
añadimos otro input, este sustituirá al de CMD:
$ docker container run --rm uoc/alpine-entry Genetica
Devolverá «8».
La relación entre ENTRYPOINT y CMD la podemos observar en la siguiente tabla.
Tabla 5.
No ENTRYPOINT | ENTRYPOINT exec_entry p1_entry | ENTRYPOINT [«exec_entry», «p1_entry»] | |
No CMD | error, not allowed | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry |
CMD [«exec_cmd», «p1_cmd»] | exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry exec_cmd p1_cmd |
CMD exec_cmd p1_cmd | /bin/sh -c exec_cmd p1_cmd | /bin/sh -c exec_entry p1_entry | exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd |