Я создаю сервер, написанный на С++, и хочу развернуть его с помощью Docker с docker-compose. Что такое "правильный путь"? Должен ли я вызывать make
из Dockerfile или создавать вручную, загружать на какой-то сервер, а затем COPY
двоичные файлы из Dockerfile?
Создание скомпилированного приложения с помощью Docker
Ответ 1
У меня были трудности с автоматизацией нашей сборки с помощью docker-compose
, и я в итоге использовал docker build
для всего:
Три слоя для строительства
Выполнить → разработать → построить
Затем я копирую результаты сборки в образ 'deploy':
Выполнить → развернуть
Четыре слоя для игры:
Бежать- Содержит все пакеты, необходимые для запуска приложения - например, libsqlite3-0
-
FROM <projname>:run
- Содержит пакеты, необходимые для сборки
- например, g++, cmake, libsqlite3-dev
- Dockerfile выполняет любые внешние сборки
- например, шаги для сборки boost-python3 (не в репозиториях менеджера пакетов)
-
FROM <projname>:develop
- Содержит источник
- Dockerfile выполняет внутреннюю сборку (код, который часто меняется)
- Встроенные двоичные файлы копируются из этого образа для использования при развертывании
-
FROM <projname>:run
- Вывод сборки скопирован в образ и установлен
-
RUN
илиENTRYPOINT
используется для запуска приложения
Структура папок выглядит следующим образом:
.
├── run
│ └── Dockerfile
├── develop
│ └── Dockerfile
├── build
│ ├── Dockerfile
│ └── removeOldImages.sh
└── deploy
├── Dockerfile
└── pushImage.sh
Настройка сервера сборки означает выполнение:
docker build -f run -t <projName>:run
docker build -f develop -t <projName>:develop
Каждый раз, когда мы делаем сборку, это происходит:
# Execute the build
docker build -f build -t <projName>:build
# Install build outputs
docker build -f deploy -t <projName>:version
# If successful, push deploy image to dockerhub
docker tag <projName>:<version> <projName>:latest
docker push <projName>:<version>
docker push <projName>:latest
Я отсылаю людей к Dockerfiles как документацию о том, как собрать/запустить/установить проект.
Если сборка не удалась и выходной файл недостаточен для исследования, я могу запустить /bin/bash
в <projname>:build
и <projname>:build
что пошло не так.
Я собрал GitHub репозиторий вокруг этой идеи. Это хорошо работает для C++, но вы, вероятно, можете использовать его для чего угодно.
Я не исследовал эту функцию, но @TaylorEdmiston указал, что мой шаблон здесь очень похож на многоэтапные сборки, о которых я не знал, когда придумал это. Это выглядит как более элегантный (и лучше задокументированный) способ достижения того же самого.
Ответ 2
Я рекомендую полностью разработать, собрать и протестировать сам контейнер. Это подтверждает философию Docker о том, что среда разработчика аналогична производственной среде, см . Рабочая станция современного разработчика на MacOS с Docker.
Особенно в случае приложений C++, где обычно есть зависимости с общими библиотеками/объектными файлами.
Я не думаю, что пока существует стандартизированный процесс разработки для разработки, тестирования и развертывания приложений C++ в Docker.
Чтобы ответить на ваш вопрос, то, как мы делаем это сейчас, так это то, что контейнер воспринимается как среда разработки и применяется ряд практических мер в команде, таких как:
- Наша кодовая база (кроме конфигурационных файлов) всегда живет на общем томе (на локальной машине) (версия Git)
- Общие/зависимые библиотеки, двоичные файлы и т.д. Всегда находятся в контейнере
- Создайте и протестируйте в контейнере и перед фиксацией изображения очистите ненужные объектные файлы, библиотеки и т.д. И убедитесь, что изменения в
docker diff
соответствуют ожидаемым. - Изменения/обновления среды, включая общие библиотеки, зависимости, всегда документируются и сообщаются команде.
Ответ 3
То, как я буду делать это, - это запустить сборку за пределами вашего контейнера и только скопировать выходные данные сборки (ваши двоичные и любые необходимые библиотеки) в ваш контейнер. Затем вы можете загрузить свой контейнер в реестр контейнера (например, использовать размещенный или запустить свой собственный), а затем вытащить из этого реестра на свои производственные машины. Таким образом, поток может выглядеть следующим образом:
- построить двоичный
- проверка/проверка работоспособности самого двоичного файла
- создать изображение контейнера с двоичным
- test/sanity - проверьте изображение контейнера с помощью двоичного файла
- загрузить в реестр контейнеров
- развертывание на стадии/тестирование/qa, извлечение из реестра
- развертывание в prod, извлечение из реестра
Поскольку важно, чтобы вы тестировали перед производственным развертыванием, вы хотите протестировать точно то же самое, что вы будете развертывать в процессе производства, поэтому вы не хотите извлекать или модифицировать изображение Docker каким-либо образом после его создания.
Я бы не запускал сборку внутри контейнера, который вы планируете развернуть в prod, так как тогда ваш контейнер будет иметь всевозможные дополнительные артефакты (такие как выходы временной сборки, инструменты и т.д.), которые вам не нужны в производстве и без необходимости увеличивайте изображение контейнера с помощью вещей, которые вы не будете использовать для развертывания.
Ответ 4
Хотя решения, представленные в других ответах - и, в частности, предложение Мишы Брукман в комментариях к этому ответу об использовании одного Dockerfile для разработки и одного для производства - будут считаться идиоматическими в момент написания вопроса, следует Следует отметить, что проблемы, которые они пытаются решить, и, в частности, проблема очистки среды сборки для уменьшения размера изображения при сохранении возможности использования одной и той же среды контейнера при разработке и производстве, были эффективно решены несколькими Этап сборки, которые были введены в Docker 17.05.
Идея состоит в том, чтобы разделить Dockerfile на две части, одна из которых основана на вашей любимой среде разработки, такой как полноценный базовый образ Debian, который связан с созданием двоичных файлов, которые вы хотите развернуть в конце день, и другой, который просто запускает встроенные двоичные файлы в минимальной среде, такой как Alpine.
Таким образом, вы избегаете возможных несоответствий между средой разработки и производством, на которые ссылается blueskin в одном из комментариев, при этом гарантируя, что ваш производственный образ не будет загрязнен инструментами разработки.
В документации приведен следующий пример многоэтапной сборки приложения Go, которое вы затем перенесете в среду разработки C++ (с одной оговоркой, что Alpine использует musl, поэтому вам следует быть осторожным при компоновке в среде разработки).
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"]