Как создать docker образ
Перейти к содержимому

Как создать docker образ

  • автор:

Подготовка собственного образа — Docker: Основы

На Docker Hub выложено множество готовых образов, которые используются администраторами и разработчиками: интерпретаторы и компиляторы языков, веб-сервера, базы данных и многое другое. Большую часть из них можно использовать на серверах без изменений, передав какие-то переменные окружения. Но для любого разрабатываемого приложения нужно создавать свой собственный образ. В него войдет код приложения и все его зависимости. Даже когда нам будет нужно изменить всего лишь конфигурацию, например Nginx, все равно придется создать свой собственный образ, в который добавлен конфигурационный файл.

В этом уроке мы научимся создавать Docker-образ на примере JavaScript проекта: данный язык программирования достаточно распространен в среде разработчиков. Но все описанные принципы так же будут подходить и для других языков. Для создания образа будем использовать популярный микрофреймворк fastify .

Для начала создадим каркас приложения с помощью готового шаблона:

cd /var/tmp # можно выбрать любую директорию mkdir docker-fastify-example cd docker-fastify-example docker run --user $(id -u) -it -w /out -v `pwd`:/out node npm init fastify Need to install the following packages: create-fastify Ok to proceed? (y) y # введите y generated .gitignore generated README.md generated app.js generated .vscode/launch.json generated plugins/README.md generated routes/root.js generated test/helper.js generated plugins/sensible.js generated plugins/support.js generated routes/README.md generated routes/example/index.js generated test/routes/root.test.js generated test/plugins/support.test.js generated test/routes/example.test.js --> project example generated successfully run 'npm install' to install the dependencies run 'npm start' to start the application run 'npm run dev' to start the application with pino-colada pretty logging (not suitable for production) run 'npm test' to execute the unit tests 

Эта команда создаст шаблон приложения в директории /out запущенного контейнера, которая, на самом деле, является директорией /var/tmp/docker-fastify-example на нашей машине. В итоге у нас получается такая структура проекта:

. # docker-fastify-example ├── README.md ├── app.js ├── package.json ├── plugins ├── routes └── test 

Для запуска этого приложения, нам нужно выполнить две основные задачи: установить зависимости и запустить сервер. Без Docker это выглядит так:

# Если не стоит npm, # то сюда еще входит установка Node.js npm install npm start # или npm run dev в режиме разработки 

Установку зависимостей нужно выполнить еще до создания образа, так как во время первой установки формируется файл package-lock.json. Он нужен для фиксации зависимостей: с его помощью мы гарантируем, что в образе будут использоваться ровно те зависимости, которые мы подключали во время разработки. Сделать это можно следующим образом:

# внутри директории docker-fastify-example docker run -it -w /out -v `pwd`:/out node npm install added 398 packages, and audited 560 packages in 45s 

Теперь директория с приложением выглядит так:

. ├── README.md ├── app.js ├── node_modules # тут хранятся зависимости ├── package-lock.json # новый файл ├── package.json ├── plugins ├── routes └── test 

Сборка и публикация Docker-образа

Docker создает образ на основе файла Dockerfile, в котором описываются необходимые команды. Мы начнем сразу с примера:

FROM node:20 WORKDIR /app COPY package.json . COPY package-lock.json . RUN npm ci COPY . . ENV FASTIFY_ADDRESS 0.0.0.0 # Команда, которая запускается автоматически # при старте контейнера CMD ["npm", "start"] 

В основном, команды Dockerfile интуитивно понятны. Видно, что мы «упаковываем» приложение в образ, выполняем установку зависимостей и описываем то, как его запустить. Подробнее о командах мы поговорим позже, а сейчас посмотрим, как собирается, запускается и пушится образ в Docker Hub.

Для сборки образа в директории с Dockerfile нужно выполнить команду указанную ниже:

# -t, --tag - имя образа и тега. По умолчанию latest # Точка в конце важна, подробнее про нее дальше docker build -t hexlet/docker-fastify-example . [+] Building 26.4s (12/12) FINISHED => [internal] load build definition from Dockerfile => => transferring dockerfile: 190B => [internal] load .dockerignore => => transferring context: 2B => [internal] load metadata for docker.io/library/node:18 => [auth] library/node:pull token for registry-1.docker.io => [internal] load build context => => transferring context: 63.29MB => [1/6] FROM docker.io/library/node:18@sha256:e5b7b3 => [2/6] WORKDIR /app => [3/6] COPY package.json . => [4/6] COPY package-lock.json . => [5/6] RUN npm ci => [6/6] COPY . . => exporting to image => => exporting layers => => writing image sha256:52f6fe => => naming to docker.io/library/docker-fastify-example 

Сборка образа занимает какое-то время: нужно подождать, пока выполнятся все команды. Как результат, в списке образов появляется образ с именем hexlet/docker-fastify-example и тегом latest. Его можно запустить и убедиться в работоспособности:

# По умолчанию Fastify стартует на 3000 порту # Docker запускает команду npm start docker run -it -p 3000:3000 hexlet/docker-fastify-example "level":30,"time":1651503036761,"pid":22,"hostname":"a9b1ea7fc320","msg":"Server listening at http://0.0.0.0:3000"> 

Для полной проверки, откройте в браузере ссылку http://localhost:3000 и убедитесь что сайт открылся. Остался последний шаг — загрузить образ на Docker Hub. Для этого понадобится подготовительная работа:

  1. Регистрация https://hub.docker.com/
  2. Подключение к аккаунту через запуск команды docker login в терминале. Docker попросит ввести имя пользователя и пароль
  3. Создание репозитория с именем docker-fastify-example в личном кабинете

Теперь, чтобы загрузить образ в Docker Hub, мы должны дать ему правильное имя. По соглашению, часть имени Docker-образа до символа /, должна совпадать с именем вашего пользователя Docker Hub. Чтобы так сделать, вам необходимо запустить команду сборки еще раз:

-t /docker-fastify-example . 

Теперь можно пушить:

-t /docker-fastify-example . # По умолчанию отправляется тег latest docker push /docker-fastify-example 

Если репозиторий публичный, то скачать и запустить этот образ сможет любой человек, с доступом в интернет.

Теги

Теги у Docker-репозиториев изменяемые. Если изменить образ и снова его запушить с тем же тегом, образ поменяется. Для тега latest это ожидаемое поведение, а вот для версий нет. За этим нужно следить самостоятельно и не менять образ для уже существующих тегов. Если меняется образ, то правильно создавать новый тег:

# Используем тег docker build -t /docker-fastify-example:v2 . docker push /docker-fastify-example:v2 

Команды Dockerfile

Dockerfile состоит из команд, которые выполнятся сверху вниз по очереди, формируя файловую систему образа. Каждая последующая команда «видит» результаты предыдущей команды. Ниже мы разберем наиболее популярные команды, которые встречаются в большинстве образов.

FROM

# Варианты # По умолчанию тег latest FROM ubuntu # С явно указанным тегом FROM node:18 

Образ — это в первую очередь файловая система, которая формируется на базе команд описанных в Dockerfile. Docker берет какую-то первоначальную файловую систему и затем изменяет ее в соответствии с описанием. Получившаяся структура файлов и становится образом. Откуда берется первоначальная файловая система?

Практически все образы в Docker формируются не с нуля, а на базе уже существующих образов. Образы формируют дерево, в котором одни образы наследуют файловые системы других образов начиная с базового образа scratch .

# Иерархия образов docker-fastify-example FROM node FROM buildpack-deps:bullseye FROM buildpack-deps:bullseye-scm FROM buildpack-deps:bullseye-curl FROM debian:bullseye FROM scratch 

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

WORKDIR

Команда WORKDIR задает рабочий каталог, относительно которого выполняются все действия во время формирования образа и при входе в контейнер:

-it hexlet/devops-fastify-app bash root@02d29c66ea06:/app# # мы оказались внутри /app 

WORKDIR автоматически создает директорию, если ее еще нет.

COPY

# файлы COPY package.json . # Аналогично # COPY package.json package.json COPY package-lock.json . # Копирование всех файлов внутрь COPY . . 

Команда COPY копирует файлы и директории с хост-машины внутрь Docker-образа. Она принимает два параметра: первый — что копируем, второй — куда копируем и под каким именем. Второй параметр может принимать три варианта:

  • Абсолютный путь, копирование происходит ровно по нему
  • Относительный путь, копирование происходит относительно установленной рабочей директории WORKDIR
  • Точка, файл или директория копируется как есть в рабочую директорию

Если точка идет первым параметром, то это обозначает что копироваться будет директория целиком.

Для полного понимания принципов работы команды COPY , нужно представлять что такое контекст. Помните, когда мы указывали точку во время сборки образа? Это и есть контекст:

-t hexlet/docker-fastify-example . 

Контекст — это директория, относительно которой работает первый параметр в COPY . Обычно контекстом указывают ту директорию, которая содержит Dockerfile. Но это не обязательно, ведь контекстом может быть и другая директория:

# Указана директория уровнем выше # Dockerfile должен лежать в текущей директории, из которой идет запуск docker build -t something .. 

Во время сборки образа, контекст целиком копируется внутрь системных директорий Docker, из которых в образ переносится все, что указано в команде COPY . Из-за этого иногда возникают проблемы. Контекст может содержать директории, которые не должны попадать в образ, например, .git , или зависимости установленные локально (node_modules), так как они все равно устанавливаются заново во время сборки. Чтобы избежать их попадания во внутрь, нужно создать файл .dockerignore и указать там те директории и файлы, которые не должны быть частью контекста. Принцип работы файла такой же, как и у .gitignore.

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

RUN

# Если базовый образ Ubuntu, то доступен apt RUN apt-get update && apt-get install -q curl RUN npm install 

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

RUN выполняется в не интерактивном режиме, это значит, что если выполняемая команда запросит пользовательский ввод, например разрешение на установку чего-либо, то мы не сможем выбрать ответ yes. Поэтому все команды в RUN запускают в неинтерактивном режиме:

# -q - ставить автоматически не задавая вопросов RUN apt-get install -q curl 

CMD

CMD задает команду, которая выполняется при запуске контейнера по умолчанию. Она используется только в том случае, если контейнер был запущен без указания команды

# Используется CMD docker run -it hexlet/docker-fastify-example # npm start # CMD не используется, так как явно указан bash docker run -it hexlet/docker-fastify-example bash 

ENV

Задает переменные окружения. Команды, выполняющиеся после ENV , видят эти переменные и могут их использовать.

С этой командой нужно быть острожнее. Переменные окружения созданы для того, чтобы их можно было менять, а их указание в Dockerfile фиксирует значения. По этому случаю, в Dockerfile обычно указывают только те переменные окружения, которые не зависят от среды запуска, как в примере выше. Нам в любом случае надо указать, что сервер должен запускаться на 0.0.0.0, иначе его будет невозможно увидеть снаружи. В большинстве ситуаций переменные окружения передаются снаружи для конкретного запуска:

Открыть доступ

Курсы программирования для новичков и опытных разработчиков. Начните обучение бесплатно

  • 130 курсов, 2000+ часов теории
  • 1000 практических заданий в браузере
  • 360 000 студентов

Наши выпускники работают в компаниях:

Руководство по Docker. Часть 1: образ, контейнер, сопоставление портов и основные команды

Docker — платформа с открытым исходным кодом для создания, развертывания и управления контейнеризированными приложениями.

Представьте, как вы устанавливаете программное обеспечение, требующее установку всех его зависимостей. Придется столкнуться со множеством ошибок, вручную выяснить и устранить все их причины. Каждый раз — попытки запустить всю систему заново, чтобы наконец-то правильно завершить установку. Именно в этот момент на помощь приходит Docker, пытаясь серьезно облегчить жизнь.

Содержание:

  1. Образы и контейнеры.
  2. Запуск первой программы в Docker.
  3. Основные команды контейнеров Docker.
  4. Командная строка Docker-контейнера.
  5. Пользовательские образы Docker.
  6. Запуск веб-приложения в Docker.
  7. Docker и сопоставление портов.
  8. Выводы.

1. Образы и контейнеры

Самые важные концепции Docker!

Образ — это файл, хранящий всю конфигурацию запуска программы в контейнере Docker. Посмотреть на список образов, созданных в системе, можно при помощи следующей команды:

docker images

Контейнер — это экземпляр образа. Docker-контейнер действует как виртуальная машина, но у него нет отдельной операционной системы. Каждый контейнер устанавливает приложения или программное обеспечение, следуя инструкциям, записанным в файлах образа.

Количество контейнеров не ограничено, все они работают на общей инфраструктуре хоста и общей операционной системе. Посмотреть на список контейнеров можно с помощью команды:

docker ps -a

2. Запуск первой программы в Docker

Первым делом нужно вывести на экран фразу “Hello, world!”.

В Docker каждому контейнеру нужен образ, поэтому, чтобы запустить hello-world , введите в консоль или терминал команду:

docker run hello-world

Попытка создания и запуска контейнера. Согласно документации API-клиента Docker, в установленном порядке выполняются следующие действия.

  1. Консоль: выполняет команду docker run hello-world .
  2. Docker client (CLI): получает команду и выполняет действия на ее основе.
  3. Сервер Docker: связывается с CLI, чтобы выдать клиенту из кэша образов (Image Cache) нужный образ с названием hello-world . Если такого образа нет, то сервер Docker связывается с хабом Docker, где скачивает образ с указанным именем.
  4. Docker Hub: хаб Docker получает запрос от сервера Docker на загрузку конкретного файла образа из общедоступного хранилища.
  5. Сервер Docker: снова ищет образ в разделе “Image Cache”, затем создает контейнер — экземпляр образа hello-world .
  6. Контейнер Docker: согласно первоначальным установкам из образа, в контейнере запускается простая программа hello-world .
  7. Консоль: в результате успешного выполнения всех вышеизложенных этапов отобразит информацию со скриншота.

3. Основные команды контейнеров Docker

Прочитайте перечень команд управления поведением контейнеров без изменения образов.

  • Удалить все неактивные контейнеры Docker:
docker system prune
  • Вывести на экран stderr и stdout от запущенных в контейнере программ, логирование контейнера Docker:
docker logs
  • Индекс активных контейнеров:
docker ps
  • Индекс всех контейнеров, включая неактивные:
docker ps -a
  • Индекс образов, установленных в системе на данный момент:
docker images
  • Деактивировать Docker-контейнер по идентификатору:
docker stop
  • Удалить неактивный контейнер по идентификатору:
docker rm
  • Удалить конкретный образ по его названию:
docker rmi

4. Командная строка Docker-контейнера

Рассмотрим набор самых часто задаваемых вопросов о пользовательском вводе команд с клавиатуры напрямую в контейнеры Docker.

  • Как выполнить команду внутри контейнера Docker?

docker exec -it

Здесь exec позволяет выполнять команды в контейнере, а флаг -it разрешает прием ввода от пользователя к процессу, а также вывод в терминал как результата выполнения процесса, так и сообщений об ошибках.

  • Как получить доступ к командной строке контейнера Docker?

Иногда удобно напрямую взаимодействовать с командной строкой контейнера. Для поиска ошибок и отладки, или вы можете запустить там все команды Linux одновременно. Убедитесь, что нужный контейнер запущен, а потом введите следующую команду:

docker exec -it sh

sh выдаст разрешение на ввод подсказок (prompt), и вы сможете выполнять внутри контейнера различные задачи, например:

  • Как разрешить ввод при запуске контейнера из образа Docker?

Допустим, что вы хотите сразу после скачивания из хаба Docker запустить образ под названием busybox . Что вы сделаете?

docker run busybox

Теперь, чтобы ответить на вышеуказанный вопрос, выполните следующую команду:

docker run -it busybox sh

Теперь все знания о полезных командах из двух пунктов руководства помогут вам подготовиться к следующему шагу.

5. Пользовательские образы Docker

Как добавить файлы в контейнер и какую операционную систему выбрать для образа?

Основной файл, хранящий все инструкции. Каждый dockerfile одинаково структурирован. Чтобы создать dockerfile для пользовательского образа необходимо указать:

  1. Базовый образ.
  2. Все зависимости и условия запуска программы.
  3. Команду для запуска программы при старте контейнера.
  • Docker Client

После создания dockerfile вы сразу попытаетесь собрать образ. Следовательно, Dockerfile отправляется клиенту Docker.

Далее клиент Docker передает все инструкции серверу Docker, который, в свою очередь, выполняет всю грязную работу, а именно все команды внутри dockerfile , и создает образ. Затем образ запускается, в результате чего создается контейнер, экземпляр образа.

  • Как же создать образ Docker?
  1. Сначала напишите dockerfile .
  2. Затем выполните команду docker build .
  3. Теперь запустите образ командой docker run .

Рассмотрим простой dockerfile , в котором установлен сервер базы данных Redis :

Теперь, глядя на Dockerfile, у вас наверняка возникли вопросы! Ответим на них сейчас:

  • Что такое базовый образ?

Ни у одного контейнера нет собственной операционной системы. Если вы хотите создать контейнер, то ему нужна отправная точка или базовый образ, с которого он может начать работу.

Alpine Linux — это как Windows и macOS, где можно установить почти все, что вам нужно. Alpine занимает мало памяти, что хорошо подходит для установки Redis. Базовый образ загружается через инструкцию FROM .

  • Как установить зависимости?

Посмотрите, в dockerfile написана инструкция ‘RUN’, которая выполняет команды внутри контейнера. APK означает менеджер пакетов Alpine Linux . Команды apk применяются в любой работающей системе на базе Alpine Linux для удаления, установки, обновления программного обеспечения.

  • Как указать команду для запуска приложения в контейнере?

Инструкцией CMD задается команда по умолчанию, которая будет выполняться только при запуске контейнера без указания команды. Приведенный выше пример запустит сервер Redis.

Теперь поговорим детальнее о команде docker build . Что происходит перед тем, как результат ее выполнения появляется на экране?

  1. Загружается контейнер с образом alpine .
  2. Файловая система из образа alpine переносится во временный контейнер, в нем и выполняются инструкции по установке базы данных Redis. После завершения установки временный контейнер удаляется, а файловая система переносится обратно в образ alpine , тем самым обновляя его. Теперь в новом образе установлен Redis.
  3. По аналогии создается еще один временный контейнер для установки команды запуска сервера Redis при каждом запуске образа в контейнере. Следом временный контейнер удаляется, а обновленная файловая система устанавливается в конечном пользовательском образе.
  4. Наконец, для запуска контейнера по шаблону пользовательского образа выполняется команда docker run .

Примечание: если вы обновите dockerfile образа и попытаетесь пересобрать его, то Docker получит кэш из предыдущего образа, чтобы пропустить тот же процесс. Он обновит только вновь добавленный раздел в Dockerfile. Порядок в Dockerfile также важен. Если вы измените порядок команд, то кэш обнулится!

  • Как установить специфическое имя для образа?

6. Запуск веб-приложения в Docker

В этом разделе создадим и докеризируем простое приложение на Node.js на основе пользовательского образа. Не беспокойтесь, вам не нужны знания о Node.js для выполнения пунктов руководства.

  • Шаг 1
    Напишите файл package.json , ведь Express.js — это бэкенд-фреймворк для веб-приложений на Node.js. Укажите в нем последнюю версию при помощи символа звездочки * , а затем укажите запуск по умолчанию для команды node index.js , где index.js — это основной веб-файл.
  • Шаг 2
    Напишите простой файл index.json . Планируется, что фреймворк Express.js через / получает запросы и отправляет обратно ответ. Чтобы получить запрос и ответ, необходимо установить порт для прослушивания.
  • Шаг 3
    Напишите dockerfile . Помните все ключевые шаги? Если забыли, то поднимитесь вверх по разделам и освежите знания. Тем не менее совершенно очевидно, что для запуска приложения понадобятся установленные, готовые к работе Node.js и NPM.
  • Шаг 4
    Постройте пользовательский образ, присвойте ему теги.
  • Шаг 5
    Запустите контейнер с приложением, запомните прослушиваемый порт.
  • Шаг 5
    Проверьте, можно ли получить доступ к приложению из браузера, действительно ли оно работает?
    О, нет! Что-то пошло не так? Пришло время выяснить.

Возможно, у вас сейчас на уме один вопрос: зачем в dockerfile написана команда WORKDIR /usr/app ?

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

7. Docker и сопоставление портов

Рассмотрим, как разобраться в настройках и правильно установить сопоставление портов!

При отправке запроса на порт 8080 вашей локальной машины, он не перенаправляется автоматически на контейнер, так как у контейнера собственное сетевое отображение.

Сопоставление портов позволит запросу на порт 8080 с локальной машины перенаправить запрос на порт 8080 Docker-контейнера, только для входящих запросов.

Однако по умолчанию Docker также позволяет исходящие запросы. Интересно, как это сделать? Проверьте зависимости в dockerfile , где NPM из контейнера напрямую обращается к интернету.

  • В команде запуска контейнера первый порт — это порт хост-машины или локальной машины, а второй порт — это порт контейнера:
docker run -p 8080:8080 mahedi/simpleweb

Выводы

Веб-приложение запущено и доступно при обращении к порту 8080 локальной машины.

Несмотря на успех, стоит упомянуть три распространенные ошибки, допускаемые в процессе работы с Docker Server и Docker Client.

  1. Неправильный выбор базового образа.
  2. Отсутствие в dockerfile команд ADD или COPY для переноса файлов хост-машины в контейнер.
  3. Неправильное сопоставление портов при создании контейнера.

Далее прилагаются скриншоты правильно запущенного в Docker веб-приложения Node.js, чтобы вы могли с ними свериться.

Самостоятельно ознакомьтесь с файлом dockerfile для построения пользовательского образа и запуска веб-приложения на Node.js.

Проанализируйте изображенный на скриншоте файл index.js с простым примером веб-приложения на Node.js. Данный файл запускается внутри контейнера Docker при помощи соответствующей команды из dockerfile .

На сегодня все! В следующей части руководства пойдет речь о последовательном создании нескольких контейнеров и докеризации сложных веб-систем со множеством зависимостей.

  • Среда разработки Entity Framework в Docker
  • Перенос сценариев CI в docker build
  • Тенденции в области программного обеспечения в 2022 году: 22 прогноза

dnncomp / Docker. Работа с образами.md

После того, как вы зарегистрировались, необходимо создать репозиторий (прямо как на GitHub).

На странице вы увидите плашку Create a Repository. Нажмите на неё.

После нажатия вы попадете на страницу создания репозитория. Создайте там свой репозиторий.

Отлично! Теперь у вас есть свой репозиторий, где можно хранить свои образы.

Команды для работы с репозиторием

После того, как у вас появился репозиторий, попробуйте загрузить в него свой образ.

Чтобы это сделать, необходимо локально авторизоваться, при необходимости сменить имя и тег образа и сделать пуш на Docker Hub.

  • Для локальной авторизации используется команда docker login. В таком случае у вас попросит username (как раз Docker ID) и пароль.
  • Чтобы сменить название образа и тег, нужно воспользоваться командой docker tag . В названии результирующего образа будет присутствовать ваш username, слеш (/), название репозитория.
  • Чтобы отправить образ на Docker Hub, нужно ввести команду docker push Чтобы локально разлогиниться, нужно ввести команду docker logout.
  • После того, как вы загрузили образ на Docker Hub, вы можете его скачать. Как вы уже знаете, делается это командой docker pull 🙂

Работа с файлами

docker volume ls — вывести список вольюмов

docker volume create — создать вольюм

docker volume rm — удалить вольюм

docker volume prune — удалить вольюмы, которые не используются контейнерами

Переменные окружения, логи и порты

Важный момент про порты

При поднятии контейнера можно прокинуть несколько портов.

Для этого нужно просто задать опцию -p несколько раз:

Например, можно сделать вот так:

docker run -p 80:80 -p 81:80 -p 82:80 nginx

В результате на компьютере мы свяжем порты 80, 81 и 82 с портом 80 в контейнере.

Чаще это нужно, когда программа в контейнере способна слушать несколько портов.

То есть что-то в духе:

docker run -p 80:80 -p 443:443 some_web_image

Переменные окружения

ENV — инструкция в Dockerfile, которая позволяет задавать переменные окружения в контейнерах.

 * Не задавайте через эту инструкцию секретные данные 

docker run -e = — позволяет задать переменную окружения в конкретном контейнере.

Логи

docker logs — позволяет вытащить логи из контейнера

 docker logs -f — не отключаемся от контейнера docker logs -t — добавляем время к логам 

Порты

EXPOSE — инструкция в Dockerfile, которая позволяет сообщить пользователю, какой(ие) порт(ы) слушает приложение внутри контейнера. Не прокидывает порты на хост.

docker run -p : — связывает порт внутри контейнера с портом на хосте.

docker run -p :: — по умолчанию адрес на хосте задается 0.0.0.0 (про него узнаем в следующем уроке). При поднятии можно изменить этот адрес. Например: docker run -p 127.0.0.1:80:80 nginx

Введение в сети

docker network ls — список сетей

docker network create — создать сеть

docker network rm — удалить сеть

docker run —net= — подключаем контейнер к сети

docker inspect — получить информацию об объектах докера (контейнер, образ, вольюм, сеть)

Веб-приложение в контейнерах

Многоэтапная сборка

Этапы задаются при помощи нескольких инструкций FROM .

Из одного этапа сборки в другой можно копировать артефакты при помощи COPY —from= .

FROM AS builder . . . FROM . . . COPY --from=builder  . . . 

YAML и docker-compose

docker-compose ps — список контейнеров

docker-compose up — поднять приложение

 docker-compose up — поднять конкретный контейнер docker-compose up -d — поднять контейнеры в фоновом режиме docker-compose -f docker-compose.dev.yml up — указать docker-compose.yaml файл 

docker-compose stop — остановить поднятые контейнеры

docker-compose start — запустить остановленные контейнеры

docker-compose down — остановить и удалить контейнеры и сеть

Веб-приложение в docker-compose

build — собираем образ, на основе которого поднимем сервис

image — образ, на основе которого поднимем сервис

container_name — название контейнера в сервисе

volumes — список вольюмов для сервиса

environment — переменные окружения в сервисе

networks — список сетей, к которым нужно подуключить сервис

ports — список портов, которые нужно прокинуть у сервиса

restart — указываем поведение сервиса при падении

deploy/replicas — указываем количество контейнеров у сервиса

depends_on — определяем зависимость между сервисами

healthcheck — задаем проверку для сервиса

Сборка docker образа под различные платформы

Разбираемся, как собирать docker image под различные платформы: amd64(x86-64), arm/v7, arm64/v8.

30 янв. 2022 · 6 минуты на чтение

Сборка docker образа под различные платформы

Docker образы стали стандартным инструментом для тестирования и развертывания ПО. Docker говорит нам: «Build once, deploy anywhere». Звучит отлично, но по умолчанию образы Docker, которые мы создаем, работают на платформе linux/amd64 (x86-64).

Это не учитывает пользователей других платформ. А это значительная аудитория, которая с каждым годом растет. В индустрии происходят важные изменения: ARM архитектура становится все более распространенной. Все больше ноутбуков использует ARM, а такие устройства как Raspberry Pi становятся мощнее. Не стоит забывать про Apple с их новыми процессорами серии M. Больше нельзя ожидать, что все программное обеспечение должно работать только на процессорах x86.

Как пример, на Raspberry Pi 4 (8 Gb) мне удалось запустить TeamCity в докере. А 4 из 5 образов мне приходится пересобирать под мой MacBook на M1 Max.

Если вы соберете образ на архитектуре x86, он не запустится на архитектуре ARM, потому что у этой архитектуры может не оказаться необходимых команд процессора.

Итак, как вы можете создавать образы для этих других платформ? Самый простой способ — просто запустить сборку на целевой платформе. Это сработает во многих случаях, но такие устройства как Raspberry Pi, обычно ограничены в мощности или вовсе неспособны создавать образы.

Чтобы решить эту проблему, Docker ввел принцип мульти-архитектурных сборок. При запуске образа с поддержкой нескольких архитектур клиенты контейнера автоматически выбирают вариант образа, соответствующий вашей операционной системе и архитектуре.

Мульти-архитектурный образ — это тип образа контейнера, который может сочетать варианты для различных архитектур, а иногда и для разных операционных систем.

Есть два способа использовать создания мультиархивного образа: с помощью манифеста докера или с помощью docker buildx. Рассмотрим оба этих варианта по порядку. Но сначала узнаем, что такое Docker Manifest.

Спонсор поста

Docker Manifest

Каждый образ Docker представлен манифестом. Манифест — это файл JSON, содержащий всю информацию об образе Docker. Он включает в себя ссылки на каждый из его слоев, их размеры, хэш, а также платформу, на которой он должен работать. Затем на этот манифест можно ссылаться с помощью тега, чтобы его было легко найти.

Например, если вы выполните следующую команду, вы получите манифест образа без нескольких архитектур в репозитории rustlang/rust с тегом nightly-slim :

$ docker manifest inspect --verbose rustlang/rust:nightly-slim [ < "Ref": "docker.io/rustlang/rust:nightly-slim@sha256:5acc1a41311e82d6ff877dac798f8283511d638883fa718a61dc396f4fd776b8", "Descriptor": < "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "digest": "sha256:5acc1a41311e82d6ff877dac798f8283511d638883fa718a61dc396f4fd776b8", "size": 742, "platform": < "architecture": "amd64", "os": "linux" >>, "SchemaV2Manifest": < "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "schemaVersion": 2, "config": < "mediaType": "application/vnd.docker.container.image.v1+json", "digest": "sha256:38f09f84d5e6b1c55bbe525d63d5734892c01207f7d67f1282bdb7ac9b9920fb", "size": 2492 >, "layers": [ < "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "digest": "sha256:6552179c3509e3c4314b4065e0d2790563d01cd474e2fdd58be4d46acd48af6a", "size": 27153731 >, < "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "digest": "sha256:72f0c721929d948d14bcc5b4b383727a8242000ecb86f1c05b7facf0b735af9c", "size": 262458494 >] > >, < "Ref": "docker.io/rustlang/rust:nightly-slim@sha256:ff4487a78b71c04f384c8232e489388e78d4bafc2b8a2ee11ff72471263a0724", "Descriptor": < "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "digest": "sha256:ff4487a78b71c04f384c8232e489388e78d4bafc2b8a2ee11ff72471263a0724", "size": 742, "platform": < "architecture": "arm64", "os": "linux" >>, "SchemaV2Manifest": < "mediaType": "application/vnd.docker.distribution.manifest.v2+json", "schemaVersion": 2, "config": < "mediaType": "application/vnd.docker.container.image.v1+json", "digest": "sha256:a44e240b23915e620ff22072223d082d4d7c90cd608eb09301879ff21cc54604", "size": 2491 >, "layers": [ < "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "digest": "sha256:4c7c9f6f1115fd4f35807a2f6c1375759365a991748aee0111873e55255f150b", "size": 25923216 >, < "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", "digest": "sha256:d05d169a780fb952804b80fa0472838866af0117b407b21fa3790eb4d291d77b", "size": 306612829 >] > > ]

Теперь возникает вопрос, как мы можем поместить несколько образов, каждый из которых поддерживает свою архитектуру, за одним и тем же тегом?

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

Создаем Manifest руками

Мы начнем с трудного пути, с создания docker manifest руками, потому что это самый старый инструмент для создания мульти-архитектурных образов.

Нам понадобится проект для сборки. Используем следующий Dockerfile, который просто основан на Debian и добавляет в него curl .

Теперь, когда мы создали наши образы и отправили их, мы можем использовать их при создании мулти-архитектурного образа:

docker manifest create \ your-username/multiarch-example:manifest-latest \ --amend your-username/multiarch-example:manifest-amd64 \ --amend your-username/multiarch-example:manifest-arm32v7 \ --amend your-username/multiarch-example:manifest-arm64v8 

После создания манифеста отправляем его в Docker Hub.

docker manifest push your-username/multiarch-example:manifest-latest

Если вы сейчас перейдете в Docker Hub, вы сможете увидеть новый тег:

Задавай вопросы

Docker Buildix

Есть фантастический проект под названием QEMU, который может эмулировать целую кучу платформ. Благодаря недавней работе buildx стало проще, чем когда-либо, использовать QEMU с Docker.

Проект на GitHub: github.com/docker/buildx

Интеграция QEMU основана на функции ядра Linux с немного загадочным именем обработчика binfmt_misc. Когда Linux сталкивается с форматом исполняемого файла, который он не распознает (например, для другой архитектуры), он проверяет с помощью обработчика, есть ли какие-либо приложения, настроенные для работы с этим форматом (например, эмулятор или виртуальная машина). Если есть, он передаст исполняемый файл этому приложению.

Чтобы это работало, нам нужно зарегистрировать интересующие нас платформы в ядре. Если вы используете Docker Desktop, это уже сделано для наиболее распространенных платформ. Если вы используете Linux, вы можете зарегистрировать обработчики запустив последний образ docker/binfmt, например:

$ docker run --privileged --rm docker/binfmt:a7996909642ee92942dcd6cff44b9b95f08dad64

После этого вам может потребоваться перезапустить Docker. Если вы хотите выбрать какие платформы зарегистрировать, или хотите использовать более эзотерическую платформу (например, PowerPC), взгляните на проект qus.

Слегка изменим наш первоначальный Dockerfile:

FROM debian:buster-slim RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* ENTRYPOINT [ "curl" ]

Чтобы создавать мульти-архитектурные образы, нам также необходимо создать экземпляр сборщика.

docker buildx create --use

Сборка под различные архитектуры выполняется с помощью 1 команды:

$ docker buildx build \ --push \ --platform linux/arm/v7,linux/arm64/v8,linux/amd64 \ --tag your-username/multiarch-example:buildx-latest .

В приведенном выше совете об эмуляции вы могли заметить, что мы использовали аргумент —platform для установки платформы сборки, но использовали образ, указанный в строке FROM , как debian:buster . Я уже говорил, что автоматически будет выбран образ платформы изходя из архитектуры машины, но в данном случае будут скачены 3 образа debian под указанные платформы.

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

Подписывайся на Twitter

Сборка в GitLab CI

И напоследок, расскажу как собирать контейнеры для различных архитектур используя GitLab CI. Например, когда вы пушите тег формата v.* , GitLab CI собирает образ и пушит его в Docker Hub.

docker-build: image: upagge/docker-buildx:latest stage: deploy only: - /^v.*$/ except: - branches services: - docker:dind before_script: - echo "$CI_REGISTRY_PASSWORD" | docker login $CI_REGISTRY --username $CI_REGISTRY_USER --password-stdin script: - docker buildx create --use - docker buildx build --push --platform linux/amd64,linux/arm64/v8 -t "$CI_REGISTRY_IMAGE:latest" -t "$CI_REGISTRY_IMAGE:$CI_COMMIT_TAG" .

Для сборки используется мой образ buildx, который основан на официальном образе Docker, в который добавлен buildx. Не забудьте указать свои переменные GitLab CI.

Заключение

Итак, мы узнали, что не все пользователи образов Docker используют amd64. С помощью buildx и QEMU можно поддерживать этих пользователей не прилагая много усилий.

Также разобрались, как автоматизировать этот процесс для репозиториев git с помощью GitLab CI, но это можно сделать и из любой другой системы CI.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *