Traefik
В первую очередь, я использую Traefik – что это такое, более подробно можно прочитать на сайте https://doc.traefik.io/traefik/.
Traefik – это реверс-прокси, который позволяет нам публиковать ресурсы в интернете. Это альтернатива Nginx, обладающая немного более богатыми возможностями. Основная преимущество Traefik – он может в автоматическом режиме публиковать уже развернутые контейнеры в интернете.
Traefik слушает два порта: 80 и 443. Все входящие запросы на эти порты он анализирует через свои правила и смотрит, относительно какого URL идет обращение. И в зависимости от URL запросы заворачиваются на контейнеры, которым установлен определенный лейбл.
Дополнительным плюсом является то, что Traefik с коробки может работать с сертификатами выписанными Letsencript, как для основного домена, так и поддоменов любого уровня.
Часть 0.1 Сравнение с VM
- независимость — контейнер может быть перемещен на любую ОС с docker-службой на борту и контейнер будет работать. (Официально — да, по факту, не уверен что совместимость такая радужная, как пони, радуга и бабочки. Если у вас есть иной опыт — прошу поделиться)
- самодостаточность — контейнер будет выполнять свои функции в любом месте, где бы его не запустили.
- Внутри контейнера находится минимально необходимый набор софта, необходимый для работы вашего процесса. Это уже не полноценная ОС, которую надо мониторить, следить за остатком места итд итп.
- Используется другой подход к виртуализации. почитать об этом. Я имею ввиду сам прицип — там нет привычной хостовой ОС.
- Следует особо относиться к контейнеру и генерируемым им данным. Контейнер это инструмент обработки данных, но не инструмент их хранения. Как пример — контейнер — это гвоздезабивающая машина, подаваемые на вход данные — доска и гвоздь, результат работы — забить гвоздь в доску. При этом доска с гвоздем не остается частью той самой гвоздезабивающей машины, результат отдельно, инструмент отдельно. Выходные данные не должны сохраняться внутри контейнера (можно, но это не docker-way). Поэтому, контейнер это либо worker (отработал, отчитался в очередь), либо, если это, например, веб-сервер, то нужно использовать внешние тома. (все это очень просто, не стоит в этом моменте грустить).
apache
Начнем, пожалуй, с самого популярного сервера — Apache.
Создадим директорию проекта:
- project
- src
- docker-compose.yml
Конфиг будет выглядеть таким образом:
Что здесь происходит:
- — указываем какой образ нам нужно и его версию (список доступных версий и модификаций можно посмотреть в соответствующем docker-hub).
- — пробрасываем порты между docker и нашей машиной, т.е. все запросы которые будут идти на 80 порт нашей машины, будут транслироваться на 80 порт docker.
- — линкуем директорию на нашей машине, с рабочей директорий apache, т.е. все файлы находящиеся в директории src, будут доступны для apache, как будто они лежат в директории htdocs (обратное тоже работает, все что создано в docker «копируется» на локальную машину).
Создадим файл src/index.html в рабочей директории с содержимым:
Запускаем наш проект:
Переходим в браузер по адресу ПК и наблюдаем приветствие нашего сервера.
Чтобы уничтожить контейнеры нашего проекта, достаточно в консоле выполнить Ctrl+C.
Работа в фоновом режиме
Если вам необходимо запустить docker и далее работать в консоле, то можно запустить проект в фоном режиме:
После запуска, консоль будет доступна для работы.
Чтобы в данной ситуации уничтожить контейнер, необходимо его остановить и удалить, но для начала, нужно узнать его ID:
В моем случае получился такой вывод:
Теперь останавливаем и удаляем наш контейнер:
Либо можно поступить грубо и сразу удалить:
Другие Типы Монтирования
Есть два других типа томов Docker, которые мы еще не обсуждали: bind mount и tempfs mount.
Bind Mount
Bind mount используются для монтирования существующего пути на хосте в контейнер. Используя —mount совместно с <host path>:<container path>, вы можете указать существующие каталоги, которые будут монтироваться в контейнер. Это очень удобно при использовании информации о конфигурации, такой как каталоги внутри /etc. Это также полезно, когда у вас есть информация, которую вы хотите использовать в контейнере, например, существующие наборы данных или статические файлы веб-сайта.
Tempfs Mount
Задача tempfs монтирования состоит в том, чтобы обеспечить доступное для записи расположение, которое специально не сохраняет информацию после окончания срока службы контейнера. Возможно, вы думаете: «зачем это нужно?” В контейнере, который не имеет подключенного тома, все записи идут в тонкий слой R/W, вставленный во время выполнения. Любая запись, направленная на этот слой, влияет на файловую систему, поскольку эти записи выполняются на базовом хосте. Обычно это не является проблемой, если вы не пишете значительные объемы одноразовых данных (таких как журналы). В этом случае вы можете наблюдать снижение производительности, так как файловая система должна обрабатывать все эти вызовы write. tempfs монтирование было создано для предоставления контейнерам временного пути записи, который не влияет на операции файловой системы. В частности, tempfs это эфемерное монтирование, которое записывается непосредственно в память. Этот том можно создать с помощью —tempfs аргумента.
Драйвера Томов
По умолчанию тома хранят информацию о базовой хост-системе. Docker также имеет концепцию, называемую драйверами томов, которая позволяет указать, как и где хранить тома. Например, вы можете хранить том Docker внутри корзины Amazon S3. Это может быть удобно, если вы хотите, чтобы информация сохранялась не только за пределами срока службы контейнера, но и за пределами срока службы хоста.
Создание микросервисов
Чтобы иметь возможность проверить состояние каждого из сервисов, в их зависимости был добавлен Spring Actuator. Он создаст эндпойнт /actuator/health и будет возвращать 200 статус, если сервис готов принимать траффик, или 504 в случае проблем. В данном случае это довольно фиктивная проверка, так как сервисы очень просты, и при каком-то форсмажоре они скорее станут полностью недоступны, чем сохранят частичную работоспособность. Но в реальных системах Actuator может помочь диагностировать проблему до того, как об нее начнут биться пользователи. Например, при возникновении проблем с доступом к БД, мы сможем автоматически на это среагировать, прекратив обрабатывать запросы сломанным экземпляром сервиса.
Сервис бекенда будет просто считать и отдавать количество принятых запросов.
Код контроллера:
Тест на контроллер:
Сервис Gateway
Шлюз будет переадресовывать запрос сервису бекенда, дополняя его следующей информацией:
id шлюза
Он нужен, чтобы можно было по ответу сервера отличить один экземпляр шлюза от другого
Некий «секрет», который будет играть роль очень важного пароля (№ ключа шифрования важной куки). Конфигурация в application.properties:
Конфигурация в application.properties:
Адаптер для связи с бекендом:
Контроллер:
Разворачивание контейнера с сервером 1С:Предприятия
В репозитарии https://github.com/thedemoncat/onec-images-docs я собрал коллекцию своих образов для развертывания платформы 1С:Предприятие. Причем скачивание платформы будет происходить во время сборки образа и от вас понадобиться только логин/пароль к https://releases.1c.ru/
Теперь возьмем образ 1С, который вы можете без проблем развернуть у себя из репозитория https://github.com/thedemoncat/onec-instance/. При развертывании в среде wsl2 нужно сделать несколько больше действий, для того чтобы получить доступ к платформе. Подробнее можно почитать в readme.md
Этот репозиторий нужно обязательно клонировать с сабмодулями командой:
Дело в том, что здесь два репозитория подключены в качестве сабмодулей, без их загрузки ничего не запустится.
Внутри репозитория есть файл-пример env.example.
Его нужно скопировать, скопированный файл назвать .env и указать в нем свой логин-пароль от ИТС.
На OS Linux запускаю сборку образов и запуск сервера командой:
На Linux дополнительно потребуется указать пароль суперпользователя, чтобы произошла запись в файла в hosts. Иначе при подключении к 1С-ке вы получите проблему несоответствия принадлежности клиента и сервера.
На OS Windows нужно в ручную прописать IP адрес виртуальной машины wsl2 в файл hosts.
В качестве примера я создам чистую серверную базу:
-
Кластер серверов 1С:Предприятие: onec_server
-
Имя информационной базы в кластере: test
-
Тип СУБД: PostgreSQL
-
Сервер баз данных: db
-
Имя базы данных: test
-
Пользователь баз данных: postgres
-
Пароль пользователя баз данных оставляем пустым
Нажимаю ОК и жду, пока база создастся. Несколько секунд – и все, база рабочая, можно играться.
Таким образом можно разворачивать быстрые 1С-окружения. Фишка репозитория в том, что можно указать версию платформы, она выкачается и соберется в нужной версии.
Утилита Docker
Все действия с контейнерами выполняются утилитой docker. Ее можно запускать от имени вашего пользователя после того, как он был добавлен в группу программы. Синтаксис утилиты очень прост:
$ docker опции команда опции_команды аргументы
Давайте сначала рассмотрим основные опции утилиты их всего несколько:
- -D — включить режим отладки;
- -H — подключиться к серверу, запущенному на другом компьютере;
- -l — изменить уровень ведения логов, доступно: debug,info,warn,error,fatal;
- -v — показать версию;
- —help вывести справку по команде или утилите в целом;
Команд намного больше, ниже приведены все команды, которые вы можете использовать в своих программах:
- attach — подключиться к запущенному контейнеру;
- build — собрать образ из инструкций dockerfile;
- commit — создать новый образ из изменений контейнера;
- cp — копировать файлы между контейнером и файловой системой;
- create — создать новый контейнер;
- diff — проверить файловую систему контейнера;
- events — посмотреть события от контейнера;
- exec — выполнить команду в контейнере;
- export — извлечь содержимое контейнера в архив;
- history — посмотреть историю изменений образа;
- images — список установленных образов;
- import — создать контейнер из архива tar;
- info — посмотреть информацию о системе;
- inspect — посмотреть информацию о контейнере;
- kill — остановить запущенный контейнер;
- load — загрузить образ из архива;
- login — авторизация в официальном репозитории Docker;
- logout — выйти из репозитория Docker;
- logs — посмотреть логи контейнера;
- pause — приостановить все процессы контейнера;
- port — подброс портов для контейнера;
- ps — список запущенных контейнеров;
- pull — скачать образ контейнера из репозитория;
- push — отправить образ в репозиторий;
- restart — перезапустить контейнер;
- rm — удалить контейнер;
- run — выполнить команду в контейнере;
- save — сохранить образ в архив tar;
- search — поиск образов в репозитории по заданному шаблону;
- start — запустить контейнер;
- stats — статистика использования ресурсов контейнером;
- stop — остановить контейнер;
- top — посмотреть запущенные процессы в контейнере;
- unpause — проложить выполнение процессов в контейнере.
В этой статье мы будем часто использовать команду run, рассмотрим ее опции:
- -e — переменные окружения для команды;
- -h — имя хоста контейнера;
- -i — интерактивный режим, связывающий stdin терминала с командой;
- -m — ограничение памяти для команды;
- -u — пользователь, от имени которого будет выполнена команда;
- -t — связать tty с контейнером для работы ввода и вывода;
- -v — примонтировать директорию основной системы в контейнер.
Теперь, когда мы рассмотрели все основы, приведем несколько примеров работы с контейнерами. Это очень просто.
Хранение образов
Для хранения Docker-контейнеров, их образов, необходим реестр (registry). Строго говоря, registry — сервис для хранения репозиториев (AWS ECR, Azure CR, Docker Hub и так далее). В репозитории хранятся образы, сгруппированные по имени.
При использовании werf для сборки приложений и/или их деплоя в Kubernetes доступны опции и , которые позволяют определить, где и как (в одном или нескольких репозиториях*) будут храниться образы приложения в registry.
* Проблематика и история внедрения этой опции описывалась в статье «Поддержка monorepo и multirepo в werf и при чём здесь Docker Registry».
Параметр может быть как адресом registry, так и repository. По сути это значение является основой для построения имени образа, а вовсе не обязательно репозиторием, в котором будут храниться образы (шаблоны, описанные ниже, проясняют этот момент).
Для параметра поддерживаются два значения, которые определяют шаблон, по которому строится конечное имя образа:
- — шаблон для режима monorepo;
- — шаблон для режима multirepo.
Примечание: при использовании единственного и безымянного образа () значение параметра опции не имеет никакого смысла, т.к. конечный образ будет всегда использовать значение в качестве репозитория ().
Так как образы не могут храниться в registry, использование режима monorepo совместно с registry не имеет никакого смысла. По этой же причине нельзя использовать безымянный образ () и registry, независимо от значения .
Поддержка возможных опций
Таким образом, пользователю доступны 3 возможные комбинации значений этих параметров. В таблице ниже представлен список реализаций Docker Registry и поддержка в них этих комбинаций:
реализация | registry + multirepo | repository + monorepo | repository + multirepo |
---|---|---|---|
AWS ECR* | + | + | + |
Azure CR | + | + | + |
Default (DTR) | + | + | + |
Docker Hub | + | + | — |
GCR | + | + | + |
GitHub Packages | + | + | — |
GitLab Registry | + | + | + |
Harbor | + | + | + |
Quay | + | + | — |
* В AWS ECR репозитории не создаются автоматически при публикации образа. Требуется создать их вручную (через UI или API) перед использованием.
Как видно по таблице, единственное ограничение распространяется на Docker Hub, GitHub Packages и Quay: в них не поддерживаются многоуровневые имена репозиториев ().
Тип реализации, равно как и значение , определяется автоматически. Для реализаций, которые поддерживают всё без исключения, multirepo — всегда значения по умолчанию. Значение для остальных имплементаций определяется автоматически по переданному значению .
Для того, чтобы задать имя имплементации вручную, можно воспользоваться опцией . Это может понадобиться, например, если в качестве адреса используется алиас из или необходимо вручную запустить очистку GitLab Registry с помощью werf (при использовании в pipeline проставит необходимое значение при необходимости).
Иллюстрация
А теперь небольшой пример, который прояснит всё вышесказанное.
Рассмотрим следующий :
… и аккаунт на Docker Hub под названием .
Параметр приведёт к следующим тегам:
Параметр приведёт к следующим тегам:
Пояснение к werf build-and-publish
Это команда для сборки образов, описанных в , и их последующей публикации в реестре. Используемые параметры:
- — краткая запись опции , которая определяет хранилище стадий (подробнее про зависимость образов от стадий — см. в документации);
- — краткая запись опции ;
- — одна из возможных опций тегирования (подробнее про именования и доступные схемы тегирования — в ).
Создание сервисов на основе контейнеров Docker
docker-composedocker-compose.yml
- блок services включает в себя описание всех создаваемых сервисов;
- в каждом сервисе есть раздел image, который определяет используемый образ для создания контейнеров на его основе. Подобный формат записи позволяет получать образы с хранилища, доступного по адресу gitlab.example.ru:4567. Последний аргумент ${CI_COMMIT_SHA} — переменная окружения, связанная с значением хеша текущего коммита, которую мы используем для различия сборок друг от друга в данном руководстве;
- блок deploy используется только при использовании команды docker stack deploy. Мы используем три ключевых слова внутри данного блока:
- replicas — количество копий контейнера;
- placement — расположение контейнеров относительно рабочих нодов;
- restart_policy — условия перезапуска контейнеров;
- открытие портов для общения между сервисами и внешней средой осуществляется в разделе ports;
- для выполнения дополнительных команд при старте сервиса используется блок command.
docker-composeNginx
Nginxbuild GitLab CI
example.ru
DOCKER_HOSTURLRegistry$CI_COMMIT_SHARedislatest
- вначале файла указывается блок image, который определяет на основе какого образа будет осуществляться сборка проекта;
- в блоке services мы дополнительно подключаем образ с настроенным Docker, для запуска контейнеров Docker внутри Docker;
- раздел before_script содержит команды, выполняемые перед запуском каждой стадии сборки проекта;
- блок stages перечисляет в каком порядке будут исполняться различные стадии выполнения работ;
- выполнение каждого раздела происходит в именованном блоке, например, unittests, который в свою очередь состоит из нескольких разделов:
- поле stage указывает в какой стадии выполняется блок;
- поле variables может содержать дополнительные переменные окружения, необходимые для выполнения операций;
- поле script содержит список команд, которые будут выполнены в этом блоке;
- поле tags определяет на каких GitLab Runners может выполняться даный блок.
Часть 0.2 Процессы в контейнерах
- Контейнер живет, пока живет процесс, вокруг которого рождается контейнер.
- Внутри контейнера этот процесс имеет pid=1
- Рядом с процессом с pid=1 можно порождать сколько угодно других процессов (в пределах возможностей ОС, естественно), но убив (рестартовав) именно процесс с pid=1, контейнер выходит. (см п.1)
- Внутри контейнера вы увидите привычное согласно стандартам FHS расположение директорий. Расположение это идентично исходному дистрибутиву (с которого взят контейнер).
- Данные, создаваемые внутри контейнера остаются в контейнере и нигде более не сохраняются (ну, еще к этому слою есть доступ из хостовой ОС). удалив контейнер — потеряете все ваши изменения. Поэтому данные в контейнерах не хранят, а выносят наружу, на хостовую ОС.
Docker
Dockerfile
Одна из важнейшей характеристик образа — это его размер. Компактный образ быстрее скачается с удаленного репозитория, займет меньше места, и ваш сервис быстрее стартует. Любой образ строится на основании базового образа, и рекомендуется выбирать наиболее минималистичный вариант. Хорошим вариантом является Alpine — полноценный дистрибутив Linux с минимумом пакетов.
Для начала попробуем написать Dockerfile «в лоб» (сразу скажу, что это плохой способ, не делайте так):
Здесь мы используем базовый образ на основе Alpine с уже установленным JDK для сборки нашего проекта. Командой ADD мы добавляем в образ текущую директорию src, отмечаем ее рабочей (WORKDIR) и запускаем сборку. Команда EXPOSE 8080 сигнализирует докеру, что приложение в контейнере будет использовать его порт 8080 (это не сделает приложение доступным извне, но позволит обратиться к приложению, например, из другого контейнера в той же сети докера).
Чтобы упаковать сервисы в образы надо выполнить команды из корня каждого проекта:
В результате получаем образ размером в 456 Мбайт (из них базовый образ JDK 340 занял Мбайт). И все притом, что классов в нашем проекте по пальцем пересчитать. Чтобы уменьшить размер нашего образа:
- Используем многошаговую сборку. На первом шаге соберем проект, на втором установим JRE, а третим шагом скопируем все это в новый чистый Alpine образ. Итого в финальном образе окажутся только необходимые компоненты.
- Воспользуемся модуляризацией java. Начиная с Java 9, можно с помощью инструмента jlink создать JRE только из нужных модулей
Для любознательных, вот хорошая статья про подходы уменьшения размеров образа https://habr.com/ru/company/ruvds/blog/485650/.
Итоговый Dockerfile:
Пересоздаем образ, и он в итоге похудел в 6 раз, составив 77 МБайт. Неплохо. После, готовые образы можно загрузить в реестр образов, чтобы ваши образы были доступны для скачивания из интернета.
Совместный запуск сервисов в Docker
Для начала наши сервисы должны быть в одной сети. В докере существует несколько типов сетей, и мы используем самый примитивный из них — bridge, позволяющий объединять в сеть контейнеры, запущенные на одном хосте. Создадим сеть следующей командой:
Далее запустим контейнер бекенда с именем ‘backend’ с образом microservices-backend:1.0.0:
Запускаем шлюз:
В этой команде мы указываем, что мы пробрасываем 80 порт нашего хоста на 8080 порт контейнера. Опции env мы используем для установки переменных среды, которые автоматически будут вычитаны спрингом и переопределят свойства из application.properties.
Немного о docker
Для наглядности дальнейшего изложения необходимо привести описание некоторых компонент docker.
Image
это шаблон только для чтения с инструкциями по созданию контейнера. Для того, чтобы собрать image необходимо создать Dockerfile, в котором описываются все шаги сборки. Каждый такой шаг создает отдельный слой внутри image. Каждый последующий слой накладывается поверх всех предыдущих и содержит лишь изменения, которые необходимо внести в предшествующий слой.
Например, для Dockerfile:
docker-образ будет иметь следующую структуру:
Слои внутри image кешируются и могут быть переиспользованы, если никаких изменений не обнаружено. Если слой , то все последующие создаются с нуля. Для внесения изменений в образ контейнера (и соответственно в окружение запускаемого процесса) достаточно поправить Dockerfile и запустить сборку образа.
Контейнер
— это запускаемый экземпляр image. Его можно создать, запустить, остановить, удалить и пр. По умолчанию, контейнеры изолированы друг от друга и хост-системы. При старте контейнер запускает команду, которая может быть указана в или , и останавливается при ее завершении. Допустимой является ситуация, когда присутствуют и CMD и ENTRYPOINT, как они взаимодействуют .
При создании каждого контейнера добавляется новый слой поверх всех существующих. Он доступен для записи в текущем контейнере, и уничтожается вместе с контейнером. Все операции записи, создания новых файлов при работе контейнера применяются к этому слою, image всегда остается неизменным. Таким образом структура слоев созданного контейнера будет иметь вид:
При использовании команды каждый раз будет контейнер, со своим слоем для записи. В задачах сборки это означает, что при каждом запуске будет создавать новое чистое окружение, которое никак не связано с предыдущими выполнениями. Список созданных контейнеров можно посмотреть, выполнив команду: .
Большая поверхность атаки
Последний аспект безопасности, который стоит рассмотреть, является прямым следствием того, как работает Docker — и это потенциально очень большая поверхность атаки. Таким рискам подвержена любая IT-организация, но особенно та, что полагается на эфемерную природу контейнерной инфраструктуры.
Поскольку Docker позволяет быстро создавать и разворачивать приложения и так же быстро их удалять, трудно уследить, какие именно приложения развернуты в вашей организации.
В таких условиях атакам может подвергнуться потенциально намного больше элементов вашей инфраструктуры.
Вы не в курсе статистики развертывания приложений в вашей организации? Тогда задайте себе следующие вопросы:
- Какие приложения сейчас у вас развернуты?
- Кто развернул их?
- Когда их развернули?
- Почему их развернули?
- Как долго они должны работать?
- Кто за них отвечает?
- Когда их в последний раз проверяли на безопасность?
Надеюсь, вам не очень сложно ответить на эти вопросы. В любом случае, давайте рассмотрим, какие действия можно предпринять на практике.
Внедрите контрольный журнал с правильным логированием
Внутри приложения обычно ведется учет действий пользователя, таких как:
- Когда пользователь создал свой аккаунт
- Когда он его активировал
- Когда пользователь последний раз менял пароль и т.п.
Помимо этих действий следует вести учет действий и по каждому контейнеру, который создается и разворачивается в вашей организации.
Не стоит этот учет излишне усложнять. Следует вести учет таких действий, как:
- Когда приложение было развернуто
- Кто его развернул
- Почему его развернули
- Каковы его намерения
- Когда его следует остановить
Большинство инструментов непрерывной разработки должны уметь записывать эту информацию — такая опция должна быть доступна либо в самом инструменте, либо при помощи кастомных скриптов на определенном языке программирования.
Вдобавок стоит внедрить уведомления по почте или любым другим способом (IRC, Slack или HipChat). Этот прием позволит убедиться, что все могут видеть, когда что разворачивается.
Таким образом, если случилось что-то неподобающее, спрятать это не получится.
Я не призываю перестать доверять своим сотрудникам, но лучше всегда быть в курсе того, что происходит. Прежде чем я закончу эту статью, пожалуйста, не поймите меня неправильно.
Я не предлагаю вам нырнуть за борт и увязнуть в создании множества новых процессов.
Такой подход, скорей всего, только лишит вас тех преимуществ, которые дает использование контейнеров, и будет совершенно ненужным.
И тем не менее, если вы хотя бы обдумаете эти вопросы и будуте регулярно уделять им время впоследствии, вы будете лучше информированы и сможете снизить количество белых пятен в вашей организации, которые могут подвергнуться атакам извне.
Ограничьте потребление доступных ресурсов
Что нужно вашему приложению?
Это совершенно легкое приложение, потребляющее не более 50Мb памяти? Тогда зачем давать ему больше? Выполняет ли приложение более интенсивный процессинг, которому требуется 4+ CPU? Тогда дайте ему к ним доступ, но не более того.
Если вы включаете анализ, профилирование и бенчмаркинг в непрерывный процесс разработки, тогда вы должны знать, какие ресурсы необходимы вашему приложению.
Поэтому когда вы разворачиваете контейнеры, убедитесь, что у них есть доступ только к самому необходимому.
Для этого используйте следующие команды для Docker:
Вот пример конфига из :
Больше информации можно найти при помощи команды или же в разделе “” документации Docker.
Повторение — мать учения
Теперь все вышеизложенное — но на примере демонстрационного приложения Hello World. Считаем, что демона мы на своем компе уже поселили, ссылка на установку — чуть выше по тексту.
Выполняем команду docker run hello-world и видим следующий результат:
Давайте разбираться.
Первое что произошло после команды «докер, запусти образ с именем hello-world», это попытка найти его локально и запустить.
Попытка не увенчалась успехом (у меня была чистая установка докера), причем, обратите внимание, искался образ не hello-world, а hello-world:latest.Через двоеточие указывается тег — что-то вроде версии или модификации образа. Если его не указать, будет искаться самая свежая версия с общепринятым тегом latest
Тогда докер решает поискать этот образ на docker hub`е и скачать его оттуда.https://hub.docker.com/_/hello-world
Примерно такие строки будут друг друга заменять, но это может произойти очень быстро, так как образ очень мал. Вы будете их чаще видеть при загрузке больших и многослойных (про слои чуть позже) образов.
А далее видим следующие строки:
Тут нам сообщают хэш образа и статус, говорящий, что загружен новый образ. Также в этот момент выводится и приветствие с сообщением. Но перед этим происходит еще кое-что.
А именно — из только что скачанного образа был создан контейнер. Если перевести на язык ООП, создался объект sleepy_antonelli (контейнер) экземпляр класса hello-world (образа). Sleepy_antonelli — это рандомно сгенерированное имя контейнера, поскольку мы не указали его явно.
Ну и, наконец, сам текст появляется на экране. Он, кстати, и есть результат работы приложения в контейнере.
Собственно, это тут и написано (вместе с призывом не останавливаться на достигнутом и ссылкой на документацию). Но давайте проверим сами.
Если выполнить команду docker images, мы увидим скачанный образ.
А если выполнить команду docker ps -a — флаг «a» показывает все образы, даже те, что остановлены в данный момент — то увидим и сам контейнер, как раз с именем sleepy_antonelli.
Имена можно задавать и самим, но нам пока это не нужно, поэтому докер сам генерирует для имени два рандомных слова. Также тут видны его ID, образ, с которого он был сделан, команду, ради которой был запущен (в данном случае — написать приветствие), когда был создан, код завершения работы (0 — это штатное завершение) со временем, порты (в данном случае они никак не пробрасывались, потому и пусто) и, наконец, имя.
Из одного образа можно создать много контейнеров. Также их можно останавливать, запускать и удалять
Но важно помнить, что образ — это шаблон. Он создается один раз и не меняется, а контейнеры можно модифицировать индивидуально
Это то же самое, что получить с завода (образа) 10 одинаковых контейнеров для перевозок грузов, но перекрасить каждый в разный цвет, повесить разные замки, и по-разному назвать.
Контейнер и образ
Давайте разделим Docker на две основные части: образ и контейнер.
Давайте вспомним, когда мы покупали ПО на дисках. Образ – это и есть лазерный диск, слепок вашего программного обеспечения. Образ у вас один. Он может находиться у вас, в облаке, вы можете его собрать из исходников.
У диска есть дорожки, а у образа – слои. Каждая команда, которая формирует этот образ, накладывает в образе свой слой. Это позволяет в дальнейшем экономить дисковое пространство за счет переиспользования слоев нескольких образов. Допустим, у вас есть 3-4 образа, каждый из которых использует в качестве первого слоя операционную систему Ubuntu. С Docker вам не надо в вашей системе хранить несколько слоев с Ubuntu: вам достаточно одного слоя, который будут использовать все образы. Это называется переиспользование.
Когда вы начинаете запускать свое приложение на базе образа, вы запускаете контейнер – это, по факту, экземпляр приложения, сущность, которая предоставляет вам сервис.
Контейнер с точки зрения концепции Docker – некая сущность, которая живет недолго. Он должен родиться, поработать, и в дальнейшем уничтожиться. Все данные которые необходимо сохранить, монтируются к контейнеру в качестве подключаемых разделов.
Что такое Docker
Docker представляет собой систему управления контейнерами. Она позволяет «упаковать» приложение или веб-сайт со всем его окружением и зависимостями в контейнер, которым в дальнейшем можно легко и просто управлять: переносить на другой сервер, масштабировать, обновлять.
Docker был написан на языке программирования Go и выпущен в 2013 году. Изначально он работал только с Linux-системами, однако на данный момент его можно использовать также в Windows и macOS. Несмотря на то, что проект является относительно новым, Докер широко используется многими специалистами и продолжает завоевывать популярность.
Важной частью экосистемы Docker является Docker Hub – открытый репозиторий образов контейнеров. В нем можно найти десятки готовых приложений от официальных разработчиков
Среди них – nginx, MySQL, Apache, Gitlab, Redmine, Elasticsearch, Jenkins и другие.
Например, для запуска WordPress с помощью Docker достаточно выполнить следующие команды:
docker run --name wp-mysql -e MYSQL_ROOT_PASSWORD=wpmsqlpsswd -d mysql:5.7 <вывод пропущен> docker run --name my-wordpress --link wp-mysql:mysql -d -p 80:80 wordpress
После этого откройте в браузере страницу http://12.34.56.78 (здесь укажите реальный IP-адрес вашего VDS) и приступите к настройке CMS!
Теперь расскажем о том, что представляет собой Docker. Три основных термина в экосистеме Docker:
- Образ (image) – шаблон, который используется для создания контейнеров. Представляет собой слепок файловой системы, в котором расположен код приложения и его окружение.
- Реестр (registry) – репозиторий образов. Docker Hub, о котором шла речь выше, – это публичный репозиторий, где хранится огромное количество образов.
- Контейнер (container) – запущенное приложение, т.е. совокупность процессов и образа.
Шаг 7 — Извлечение образов из частного реестра Docker
Вернитесь на сервер реестра, чтобы вы могли протестировать извлечение образа с клиентского сервера. Также для тестирования можно использовать третий сервер.
Войдите в систему, указав имя пользователя и пароль, которые вы задали ранее:
Теперь вы готовы к извлечению образа. Используйте доменное имя и имя образа, к которому вы добавили метку на предыдущем шаге:
Docker загрузит образ и вернется в командную строку. Если вы запустите образ на сервере реестра, вы увидите, что ранее созданный файл находится в этом образе:
Выведите список файлов в оболочке bash:
Вы увидите файл , который вы создали для этого образа:
Вы завершили настройку защищенного реестра для отправки и получения образов.
GitHub Packages
Как хранить образы
Если есть необходимость хранения образов в отдельных packages, то пользователь не должен указывать package в images repo:
Если в werf конфигурации используется безымянный образ () или пользователь хочет хранить все образы в одном package, то необходимо использовать конкретный package в качестве параметра для images repo:
Пример
Пользователь имеет следующее:
- Два образа в werf.yaml: frontend, backend.
- GitHub репозиторий: github.com/company/project.
Есть два возможных способа хранения конечных образов:
- Параметр images repo docker.pkg.github.com/company/project приведёт к следующим тегам: и .
- Параметр images repo docker.pkg.github.com/company/project/app приведёт к следующим тегам: и .
Как чистить stages и images
Для удаления версий package приватного репозитория используется GraphQL. От пользователя требуется token с , , и scopes.
Для того, чтобы задать параметры, следует использовать следующие опции и соответствующие переменные окружения:
- Для stages storage: .
- Для images repo: .
- И то и другое: .