¿Hay alguna forma de combinar imágenes de Docker en 1 contenedor?

86

Tengo algunos Dockerfiles en este momento.

Uno es para Cassandra 3.5, y es FROM cassandra:3.5

También tengo un Dockerfile para Kafka, pero es un poco más complejo. Es FROM java:openjdk-8-frey ejecuta un comando largo para instalar Kafka y Zookeeper.

Finalmente, tengo una aplicación escrita en Scala que usa SBT.

Para ese Dockerfile, es FROM broadinstitute/scala-baseimage, que me da Java 8, Scala 2.11.7 y STB 0.13.9, que son lo que necesito.

Quizás, no entiendo cómo funciona Docker, pero mi programa Scala tiene a Cassandra y Kafka como dependencias y, para propósitos de desarrollo, quiero que otros puedan simplemente clonar mi repositorio con el Dockerfiley luego poder construirlo con Cassandra, Kafka , Scala, Java y SBT todos integrados para que puedan simplemente compilar la fuente. Sin embargo, tengo muchos problemas con esto.

¿Cómo combino estos Dockerfiles? ¿Cómo puedo simplemente crear un ambiente con esas cosas incorporadas?

David
fuente
6
No combina imágenes de Docker
generalhenry
@generalhenry, si quisiera, ¿no podría simplemente copiar y pegar las cosas de la ventana acoplable necesarias para obtener Cassandra 3.5 y ponerlas en mi Dockerfile principal que me proporciona Java, Scala y SBT?
David
Si bien puede hacer que todo funcione en un solo contenedor, rara vez es deseable. Los contenedores le permiten separar limpiamente su red, escalado, registro, monitoreo, etc. . .
generalhenry
2
@generalhenry Claro, eso es a menudo lo que quieres hacer. Pero, ¿qué pasa si necesita rust para compilar un paquete de Python binario desde PyPi? En este caso, es posible que desee combinar imágenes de Docker de rust y Python. No funcionará componerlos.
Tobias Bergkvist

Respuestas:

91

Puede, con la función de compilaciones de varias etapas introducida en Docker 1.17

Mira esto:

FROM golang:1.7.3
WORKDIR /go/src/github.com/alexellis/href-counter/
RUN go get -d -v golang.org/x/net/html  
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .

FROM alpine:latest  
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/alexellis/href-counter/app .
CMD ["./app"]  

Luego construye la imagen normalmente:

docker build -t alexellis2/href-counter:latest

De: https://docs.docker.com/develop/develop-images/multistage-build/

El resultado final es la misma imagen de producción diminuta que antes, con una reducción significativa de la complejidad. No es necesario que cree ninguna imagen intermedia y no es necesario que extraiga ningún artefacto en su sistema local.

¿Como funciona? La segunda instrucción FROM inicia una nueva etapa de construcción con la imagen alpine: latest como base. La línea COPY --from = 0 copia solo el artefacto construido de la etapa anterior a esta nueva etapa. El SDK de Go y los artefactos intermedios se dejan atrás y no se guardan en la imagen final.

Mohammed Noureldin
fuente
7
Suponga que quiero combinar dos imágenes base que tienen muchas cosas y no las mantengo. Por ejemplo, si quiero ejecutar una aplicación Rust que tenga aceleración de GPU, quiero que mi imagen sea una combinación de nvidia-dockery rustlang/rust:nightly. Estas imágenes, a su vez, se superponen a otras imágenes. Para hacer esto usando compilaciones multicapa, tengo que conocer y especificar todos los archivos de una de las imágenes que quiero copiar a la otra imagen; parece imposible, particularmente porque ese conjunto puede cambiar cada vez que cambia la imagen ascendente. ¿Estoy leyendo esto bien?
masonk
3
@masonk He tenido éxito haciendo: FROM a/a:latest FROM b/b:latest COPY --from=0 / / Probablemente una práctica terrible, pero funciona. Fue principalmente por mi propia curiosidad que por algo que usaría en la producción.
McP
4
La maldita cosa no me funciona. Es como si el primer "DESDE" fuera completamente ignorado.
DimiDak
Lo mismo aquí: quiero hacer FROM image1; CMD image1command; FROM image2; CMD image2command;No funciona en absoluto. Siempre solo el segundo comando
CGFoX
eso frustra el propósito de tener contenedores, ¿no?
Luiz Felipe
22

No puede combinar archivos docker porque pueden producirse conflictos. Lo que quiere hacer es crear un nuevo archivo docker o crear una imagen personalizada.

TL; DR; Si su contenedor de desarrollo actual contiene todas las herramientas que necesita y funciona, guárdelo como una imagen y luego en un repositorio y cree un archivo docker para extraer esa imagen de ese repositorio.

Detalles: crear una imagen personalizada es mucho más fácil que crear un archivo docker con una imagen pública, ya que puede almacenar los hacks y modificaciones en la imagen. Para hacerlo, inicie un contenedor en blanco con una imagen básica de Linux (o broadinstitute / scala-baseimage), instale las herramientas que necesite y configúrelas hasta que todo funcione correctamente, luego guárdelo (el contenedor) como una imagen. Cree un nuevo contenedor a partir de esta imagen y pruebe para ver si puede compilar su código sobre él a través de docker-compose (o como quiera hacerlo / compilarlo). Si funciona, entonces tiene una imagen base de trabajo que puede cargar en un repositorio para que otros puedan extraerla.

Para crear un dockerfile con una imagen pública, deberá colocar todos los hacks, modificaciones y configuraciones en el dockerfile. Es decir, deberá colocar cada línea de comando que utilizó en un archivo de texto y reducir los hacks, modificaciones y configuración en líneas de comando. Al final, su dockerfile creará una imagen automáticamente y no necesita almacenar esta imagen en un repositorio y todo lo que necesita hacer es darles a otros el dockerfile y ellos pueden girar la imagen en su propia ventana acoplable.

Tenga en cuenta que una vez que tenga un archivo docker en funcionamiento, puede modificarlo fácilmente, ya que creará una nueva imagen cada vez que use el archivo docker. Con una imagen personalizada, es posible que tenga problemas en los que necesite reconstruir la imagen debido a conflictos. Por ejemplo, todas sus herramientas funcionan con openjdk hasta que instala una que no funciona. La solución puede implicar desinstalar openjdk y usar el de Oracle, pero toda la configuración que hizo para todas las herramientas que instaló se rompió.

Hin Fan Chan
fuente
9

La siguiente respuesta se aplica a Docker 1.7 y superior:

Preferiría usar --from=NAMEy from image as NAME ¿Por qué? Puede usar --from=0y superior, pero esto puede resultar un poco difícil de administrar cuando tiene muchas etapas de docker en dockerfile.

ejemplo de muestra:

FROM golang:1.7.3 as backend
WORKDIR /backend
RUN go get -d -v golang.org/x/net/html  
COPY app.go .
RUN  #install some stuff, compile assets....

FROM golang:1.7.3 as assets
WORKDIR /assets
RUN ./getassets.sh

FROM nodejs:latest as frontend 
RUN npm install
WORKDIR /assets
COPY --from=assets /asets .
CMD ["./app"] 

FROM alpine:latest as mergedassets
WORKDIR /root/
COPY --from=merge ./
COPY --from=backend ./backend .
CMD ["./app"]

Nota: Administrar el archivo docker correctamente ayudará a crear una imagen de la ventana acoplable más rápido. Internamente, la ventana acoplable usa el almacenamiento en caché de la capa de la ventana acoplable para ayudar con este proceso, en caso de que la imagen deba reconstruirse.

РАВИ
fuente
7

Sí, puede convertir una gran cantidad de software en una sola imagen de Docker ( GitLab hace esto, con una imagen que incluye Postgres y todo lo demás), pero generalhenry tiene razón: esa no es la forma típica de usar Docker.

Como dices, Cassandra y Kafka son dependencias de tu aplicación Scala, no son parte de la aplicación, por lo que no todas pertenecen a la misma imagen.

Tener que orquestar muchos contenedores con Docker Compose agrega una capa de administración adicional, pero le brinda mucha más flexibilidad:

  • sus contenedores pueden tener diferentes períodos de vida, por lo que cuando tiene una nueva versión de su aplicación para implementar, solo necesita ejecutar un nuevo contenedor de aplicaciones, puede dejar las dependencias en ejecución;
  • puede usar la misma imagen de la aplicación en cualquier entorno, usando diferentes configuraciones para sus dependencias - por ejemplo, en dev puede ejecutar un contenedor Kafka básico y en prod tenerlo agrupado en muchos nodos, el contenedor de su aplicación es el mismo;
  • sus dependencias también pueden ser utilizadas por otras aplicaciones, por lo que varios consumidores pueden ejecutarse en diferentes contenedores y todos trabajar con los mismos contenedores de Kafka y Cassandra;
  • además de toda la escalabilidad, registro, etc. ya mencionados.
Elton Stoneman
fuente
4

No puede combinar imágenes de Docker en 1 contenedor. Vea las discusiones detalladas en la edición de Moby, ¿Cómo combino varias imágenes en una a través de Dockerfile ?

Para su caso, es mejor no incluir todas las imágenes de Cassandra y Kafka. La aplicación solo necesitaría el controlador Cassandra Scala y el controlador Kafka Scala. El contenedor debe incluir solo los controladores.

CloudStax
fuente
2

Docker no fusiona las imágenes, pero no hay nada que le impida combinar los archivos de docker, si están disponibles, y convertirlos en una imagen amplia que necesitaría compilar. Sin embargo, hay momentos en los que esto tiene sentido, ya que para ejecutar múltiples procesos en un contenedor, la mayoría de los dogmas de Docker señalarán que esto es menos deseable, especialmente con la arquitectura de microservicios (sin embargo, las reglas deben romperse, ¿verdad?)

Amos Folarin
fuente
1

Necesitaba docker: latest y python: últimas imágenes para Gitlab CI. Esto es lo que se me ocurrió:

FROM ubuntu:latest
RUN apt update
RUN apt install -y sudo
RUN sudo apt install -y docker.io
RUN sudo apt install -y python3-pip
RUN sudo apt install -y python3
RUN docker --version
RUN pip3 --version
RUN python3 --version

Después de compilarlo y enviarlo a mi repositorio de Docker Hub:

docker build -t docker-hub-repo/image-name:latest path/to/Dockerfile
docker push docker-hub-repo/image-name:latest

No olvides docker loginantes de empujar

Espero eso ayude

Michael Belkin
fuente