Есть ли способ объединить образы Docker в 1 контейнер?

У меня сейчас есть несколько файлов Dockerfile.

Один для Cassandra 3.5, и это FROM cassandra:3.5

У меня также есть Dockerfile для Kafka, но он немного сложнее. Это FROM java:openjdk-8-fre, и он запускает длинную команду для установки Kafka и Zookeeper.

Наконец, у меня есть приложение, написанное на Scala, которое использует SBT.

Для этого Dockerfile это FROM broadinstitute/scala-baseimage, что дает мне Java 8, Scala 2.11.7 и STB 0.13.9, которые мне нужны.

Возможно, я не понимаю, как работает Docker, но моя программа на Scala имеет Cassandra и Kafka в качестве зависимостей и для целей разработки, я хочу, чтобы другие могли просто клонировать мой репозиторий с помощью Dockerfile, а затем иметь возможность создавать его с помощью Cassandra, Kafka, Scala, Java и SBT встроены так, чтобы можно было просто скомпилировать исходный код. Хотя у меня с этим много проблем.

Как мне объединить эти Dockerfiles? Как мне просто создать среду с этими вещами, запеченными в ней?


person David    schedule 21.09.2016    source источник
comment
Вы не объединяете образы Docker, а затем составляете: docs.docker.com/compose   -  person generalhenry    schedule 22.09.2016
comment
@generalhenry, если бы я захотел, не мог бы я просто скопировать и вставить материал докера, необходимый для получения Cassandra 3.5, и поместить его в мой основной файл Dockerfile, который дает мне Java, Scala и SBT?   -  person David    schedule 22.09.2016
comment
Хотя вы можете заставить все работать в одном контейнере, это редко бывает желательным. Контейнеры позволяют четко разделить сеть, масштабирование, ведение журнала, мониторинг и т. д. . .   -  person generalhenry    schedule 22.09.2016
comment
@generalhenry Конечно, это часто то, что вы хотите сделать. Но что, если вам нужен rust для компиляции бинарного пакета Python из PyPi? В этом случае вы можете объединить образы докеров rust и python. Составить их не получится.   -  person Tobias Bergkvist    schedule 22.06.2020


Ответы (8)


Это возможно благодаря функции многоэтапных сборок, представленной в Docker 1.17.

Взгляните на это:

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"]  

Затем создайте образ в обычном режиме:

docker build -t alexellis2/href-counter:latest

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

Конечным результатом является то же крошечное производственное изображение, что и раньше, со значительным снижением сложности. Вам не нужно создавать какие-либо промежуточные образы и вообще не нужно извлекать какие-либо артефакты в вашу локальную систему.

Как это работает? Вторая инструкция FROM запускает новую стадию сборки с образцом alpine:latest в качестве основы. Строка COPY --from=0 копирует только созданный артефакт из предыдущего этапа в этот новый этап. Go SDK и любые промежуточные артефакты остаются позади и не сохраняются в конечном образе.

person Mohammed Noureldin    schedule 06.01.2018
comment
Предположим, я хочу объединить два базовых образа, в которых много чего происходит и которые я не обслуживаю. Например, если я хочу запустить приложение Rust с ускорением графического процессора, я хочу, чтобы мое изображение было слиянием nvidia-docker и rustlang/rust:nightly. Эти изображения, в свою очередь, накладываются поверх других изображений. Чтобы сделать это с помощью многослойных сборок, я должен знать и указать все файлы из одного из изображений, которые я хочу скопировать в другое изображение, что кажется невозможным, особенно потому, что этот набор может меняться всякий раз, когда изменяется исходное изображение. Я правильно это читаю? - person masonk; 17.02.2019
comment
@masonk Мне удалось: FROM a/a:latest FROM b/b:latest COPY --from=0 / / Наверное, ужасная практика, но она работает. Это было в основном для моего собственного любопытства, чем что-то, что я буду использовать в производстве. - person McP; 28.02.2019
comment
Черт побери, у меня не работает. Это похоже на то, что первый FROM полностью игнорируется. - person DimiDak; 05.07.2019
comment
То же самое здесь: я хочу сделать FROM image1; CMD image1command; FROM image2; CMD image2command; Это вообще не работает. Всегда только 2-я команда - person CGFoX; 09.08.2019
comment
это противоречит цели наличия контейнеров, не так ли? - person Luiz Felipe; 13.05.2020
comment
@LuizFelipe, нет, с чего бы это? Это уменьшает репликацию кода. - person Mohammed Noureldin; 13.05.2020
comment
@masonk Для конкретного решения nvidia вы можете использовать nvidia-container-runtime и флаг --gpus для доступа к графическому процессору из докера. docs.docker.com/config/containers/resource_constraints/#gpu помогло меня. - person massanishi; 02.10.2020
comment
@CGFoX То же самое, только загружен второй FROM image. - person questionto42; 20.03.2021

Вы не можете объединять файлы докеров, так как могут возникнуть конфликты. Что вы хотите сделать, так это создать новый файл докеры или создать собственный образ.

TL;ДР; Если ваш текущий контейнер для разработки содержит все необходимые вам инструменты и работает, сохраните его как образ, а затем в репозиторий и создайте файл докеры, чтобы извлечь из этого образа этот репозиторий.

Подробности: создание пользовательского образа намного проще, чем создание файла докеров с использованием общедоступного образа, поскольку вы можете хранить любые хаки и моды в образе. Для этого запустите пустой контейнер с базовым образом Linux (или wideinstitute/scala-baseimage), установите все необходимые инструменты и настройте их, пока все не заработает правильно, а затем сохраните его (контейнер) как образ. Создайте новый контейнер из этого образа и проверьте, можете ли вы создать свой код поверх него с помощью docker-compose (или как вы хотите это сделать/собрать). Если это работает, то у вас есть рабочее базовое изображение, которое вы можете загрузить в репозиторий, чтобы другие могли его извлечь.

Чтобы создать файл докеров с общедоступным образом, вам нужно будет поместить все хаки, моды и настройки в сам файл докеров. То есть вам нужно будет поместить каждую командную строку, которую вы использовали, в текстовый файл и сократить любые хаки, моды и настройки в командные строки. В конце ваш dockerfile автоматически создаст образ, и вам не нужно хранить этот образ в репозитории, и все, что вам нужно сделать, это предоставить другим dockerfile, и они смогут раскрутить образ в своем собственном docker.

Обратите внимание, что если у вас есть рабочий файл dockerfile, вы можете легко его настроить, так как он будет создавать новый образ каждый раз, когда вы используете файл dockerfile. С пользовательским образом вы можете столкнуться с проблемами, когда вам нужно перестроить образ из-за конфликтов. Например, все ваши инструменты работают с openjdk, пока вы не установите тот, который не работает. Исправление может включать удаление openjdk и использование оракула, но вся конфигурация, которую вы сделали для всех инструментов, которые вы установили, сломалась.

person Hin Fan Chan    schedule 21.09.2016
comment
Этот ответ устарел с момента введения многоэтапных сборок. - person slikts; 11.01.2021

Следующий ответ относится к докеру 1.7 и выше:

Я бы предпочел использовать --from=NAME и from image as NAME Почему? Вы можете использовать --from=0 и выше, но с этим может быть немного сложно справиться, если у вас много этапов докера в файле докера.

пример примера:

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"]

Примечание. Правильное управление dockerfile поможет быстрее создать образ Docker. Внутренне докер использует кэширование уровня докеров, чтобы помочь в этом процессе, если образ необходимо перестроить.

person РАВИ    schedule 23.03.2019

Да, вы можете объединить множество программ в один образ Docker (GitLab делает это с одним изображением, которое включает Postgres и все остальное), но generalhenry правильно — это нетипичный способ использования Docker.

Как вы сказали, Cassandra и Kafka являются зависимостями для вашего приложения Scala, они не являются частью приложения, поэтому они не принадлежат одному и тому же образу.

Необходимость организовывать множество контейнеров с помощью Docker Compose добавляет дополнительный уровень администрирования, но дает вам гораздо больше гибкости:

  • ваши контейнеры могут иметь разный срок службы, поэтому, когда у вас есть новая версия вашего приложения для развертывания, вам нужно только запустить новый контейнер приложения, вы можете оставить зависимости запущенными;
  • вы можете использовать один и тот же образ приложения в любой среде, используя разные конфигурации для ваших зависимостей, например. в dev вы можете запустить базовый контейнер Kafka, а в prod он будет кластеризован на многих узлах, ваш контейнер приложения будет таким же;
  • ваши зависимости могут использоваться и другими приложениями, поэтому несколько потребителей могут работать в разных контейнерах, и все они будут работать с одними и теми же контейнерами Kafka и Cassandra;
  • плюс все уже упомянутые масштабируемость, ведение журналов и т. д.
person Elton Stoneman    schedule 21.09.2016

Вы не могли объединить образы докеров в 1 контейнер. См. подробные обсуждения в выпуске Moby, Как объединить несколько изображений в одно с помощью Dockerfile .

В вашем случае лучше не включать все изображения Кассандры и Кафки. Приложению потребуется только драйвер Cassandra Scala и драйвер Kafka Scala. Контейнер должен включать только драйверы.

person CloudStax    schedule 13.10.2017

Docker не выполняет слияние образов, но ничто не мешает вам объединить файлы докеров, если они доступны, и свернуть их в толстый образ, который вам нужно будет построить. Однако бывают случаи, когда это имеет смысл, поскольку для запуска нескольких процессов в контейнере большинство догм Docker будут указывать на это как на менее желательное, особенно с микросервисной архитектурой (однако существуют правила, которые нужно нарушать, верно?)

person Community    schedule 22.09.2016

Мне нужны были образы docker:latest и python:latest для Gitlab CI. Вот что я придумал:

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

После сборки и отправки в репозиторий Docker Hub:

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

Не забудьте docker login перед отправкой

Надеюсь, поможет

person Michael Belkin    schedule 14.03.2020

Когда вы можете захотеть объединить образы Docker?

Как отмечают здесь другие, вы обычно не хотите помещать свою базу данных и свое приложение в один и тот же образ Docker. В идеале вы хотите, чтобы образ Docker обертывал один процесс/среду выполнения. Это позволяет увеличивать/уменьшать масштаб каждого процесса и перезапускать его по отдельности.

Допустим, вы хотите использовать некоторые общие C-библиотеки/исполняемые файлы, которые недоступны в диспетчере пакетов используемого вами образа, но кто-то другой создал образ, в котором они предварительно скомпилированы — и вы можете не захотеть перекомпилировать эти двоичные файлы как часть вашей сборки (в зависимости от того, сколько времени это займет). Есть ли способ быстро создать образ POC-Docker, содержащий все эти исполняемые файлы/библиотеки, на основе существующих образов?

Докер и композиция

Соответствующее обсуждение: https://github.com/moby/moby/issues/3378

Чего не хватает Docker, так это хорошего способа компоновки изображений. Вы можете копировать отдельные файлы или целые файловые системы из других образов в свои, используя COPY --from=<image> <from-path> <to-path>. Не существует встроенного способа копирования переменных окружения из другого изображения в ваше собственное.

Тем не менее, я лично создал пользовательский интерфейс/парсер для Dockerfiles, который добавляет ключевое слово INCLUDE <image>. Это копирует всю файловую систему вместе с переменными среды в ваш образ:

DOCKER_BUILDKIT=1 docker build -t myimage .
#syntax=bergkvist/includeimage
FROM alpine:3.12.0
INCLUDE rust:1.44-alpine3.12
INCLUDE python:3.8.3-alpine3.12

nixpkgs.dockerИнструменты

если вам нужны действительно компонуемые сборки Docker, я рекомендую проверить dockerTools в nixpkgs. Это также приведет к большему количеству воспроизводимых (и, как правило, очень маленьких) изображений. См. https://nix.dev/tutorials/building-and-running-docker-images

docker load < $(nix-build docker-image.nix)
# docker-image.nix
let
  pkgs = import <nixpkgs> {};
  python = pkgs.python38;
  rustc = pkgs.rustc;
in pkgs.dockerTools.buildImage {
  name = "myimage";
  tag = "latest";
  contents = [ python rustc ];
}
person Tobias Bergkvist    schedule 16.06.2021