ИТ База знаний
Полезно
— Онлайн генератор устойчивых паролей
— Онлайн калькулятор подсетей
— Руководство администратора FreePBX на русском языке
— Руководство администратора Cisco UCM/CME на русском языке
— Руководство администратора по Linux/Unix
Навигация
Серверные решения
Телефония
FreePBX и Asterisk
Настройка программных телефонов
Корпоративные сети
Протоколы и стандарты
Как работать с Dockerfile?
В продолжение статьи про Docker, сегодня мы расскажем про Dockerfile — скрипт, который позволяет автоматизировать процесс построения контейнеров — шаг за шагом, используя при этом base образ.
Онлайн курс по Linux
Мы собрали концентрат самых востребованных знаний, которые позволят тебе начать карьеру администратора Linux, расширить текущие знания и сделать уверенный шаг к DevOps
Докерфайлы и синтаксис для их создания
Как уже было сказано выше, каждый Докерфайл — по сути скрипт, который автоматически выполняет определенные действия или команды в base образе, для формирования нового образа.
Все подобные файлы начинаются с обозначения FROM — также как и процесс построения нового контейнера, далее следуют различные методы, команды, аргументы или условия, после применения которых получится Docker контейнер.
Для начала, быстренько пройдемся по синтаксису — он, кстати говоря, крайне простой, с командами, говорящими самими за себя.
В Докер файлах содержится два типа основных блоков — комментарии и команды с аргументами. Причем для всех команд подразумевается определенный порядок — подробнее об этом ниже.
Ниже типичный пример синтаксиса, где первая строка является комментарием, а вторая — командой.
Перед тем, как переходить к собственно написанию собственно Докерфайла, сначала разберем все возможные команды.
Все команды в Докерфайлах принято указывать заглавными буквами — к примеру RUN, CMD и т.д.
Создание своего собственного образа для установки MongoDB
Для начала создадим пустой файл и откроем его с помощью vim:
Затем мы можем указать комментариями для чего данный Докерфайл будет использоваться и все такое — это не обязательно, но может быть полезно в дальнейшем. На всякий случай напомню — все комментарии начинаются с символа #.
Далее, укажем базовый образ:
Затем, укажем автора:
После чего обновим репозитории(данный шаг совершенно необязателен, учитывая, что мы не будем их использовать ) :
После укажем команды и аргументы для скачивания MongoDB (установку проводим в соответствии с гайдом на официальном сайте):
После чего укажем дефолтный порт для MongoDB:
Вот как должен выглядеть у вас финальный файл — проверьте и, затем, можно сохранить изменения и закрыть файл:
Запуск контейнера Docker
Итак, мы готовы создать наш первый MongoDB образ с помощью Docker!
Далее запускаем наш новый MongoDB в контейнере!
Заключение
Всем спасибо за внимание, теперь вы можете очень много экспериментировать с созданием Докерфайлов и ваших собственных образов, не бойтесь пробовать упростить привычные вам процессы установки приложений с помощью контейнеров — возможности крайне велики, и мы постараемся охватить их в следующих статьях.
Исчерпывающее руководство по написанию Dockerfile для веб-приложений на Node.js
Данный пост состоит из нескольких примеров, начиная от простого Dockerfile до многоэтапных продакшен-сборок для веб-приложений Node.js. Краткое описание того, что охватывает данное руководство:
Если вы сразу хотите перейти к коду, посмотрите репозиторий на GitHub.
Оглавление
1. Простой пример Dockerfile
Соберём и запустим этот образ:
Приложение будет доступно по URL http://localhost:8080. Используйте Ctrl+C для завершения сервера.
Теперь предположим, что вы хотите, чтобы сборка пересобиралась при каждом изменении кода, то есть во время разработки. Тогда вы должны примонтировать файлы исходного кода в контейнер при запуске и остановки node-сервера.
2. Горячая перезагрузка с Nodemon
nodemon — популярный пакет, который будет отслеживать файлы в каталоге, в котором он был запущен. Если какие-нибудь файлы будут изменены, то nodemon автоматически перезагрузит ваше приложение.
Мы соберём образ и запустим nodemon, чтобы код приложения обновлялся всякий раз, когда в директории app происходят изменения.
3. Оптимизации
В вашем Dockerfile предпочитайте COPY вместо использования ADD, если вы не пытаетесь добавить tar-файлы для автоматической распаковки, следуя лучшим практикам Docker.
Откажитесь от использования команды start в файле package.json и выполняйте её непосредственно. Поэтому вместо этого:
Вы можете использовать это в вашем Dockerfile:
4. Обслуживание статических файлов
В приведённом выше Dockerfile предполагается, что вы используете API-сервер на Node.js. Допустим, вы хотите обслуживать приложение на React.js/Vue.js/Angular, используя Node.js.
Другой альтернативой является либо: 1) собирать файлы локально и использовать nginx docker для обслуживания этих статических файлов или 2) использовать конвейер (pipeline) CI/CD.
5. Одноэтапная продакшен-сборка
Соберите и запустите образ “всё в одном”:
Созданный образ будет весит приблизительно 700 Мб (в зависимости от вашей кодовой базы) из-за основного слоя Debian. Давайте посмотрим, как мы можем уменьшить размер.
6. Многоэтапная продакшен-сборка
С многоэтапной сборкой вы используете несколько выражений FROM в своём Dockerfile, но окончательный этап сборки будет использовать только одно из них, и в идеале это будет крошечный продакшен-образ с точно указанными зависимостями, требуемыми в продакшен-сервере.
В вышеприведённом сниппете образ, собранный с Alpine, занимает около 70 Мб, тем самым уменьшая размер в 10 раз. Вариант с использованием alpine обычно является очень безопасным выбором для уменьшения размеров образов.
Есть предложения по улучшению руководства? Или другие варианты использования, которые вы хотели бы видеть? Дайте мне знать в комментариях.
Присоединяйтесь к дискуссии на Reddit или HackerNews 🙂
Основы Docker за Х часов и Y дней
0. Вступление
Цель данной статьи собрать в небольшую кучку основную информацию, минимально достаточную для того, чтобы начать работать с докер на ежедневной основе и удалить с рабочей машины локально установленные apache, mysql, virtualenv, python3, mongodb, memchaced, redis, php5, php7 и весь остальной зоопарк, который мы используем при разработке, и который зачастую еще и конфликтует между собой от версии к версии.
А еще я в автобусе и ближайшие 7 часов мне все равно делать будет нечего. Ну и вдобавок я наконец-то соберу в одном месте ссылки и команды, за которыми мне самому периодически приходится лезть в документацию, например — как на маке добавить IP алиас к локалхосту: sudo ifconfig lo0 alias 10.200.10.1/24 (зачем это надо будет сказано позже)
Статья называется так, как называется, потому что я не смог более-менее точно подсчитать сколько нужно времени на поставленную задачу. У меня на это ушло примерно 6 часов в течение 3 дней. Во многом, потому что в тот момент я работал фулл тайм в офисе и осваивал докер, так сказать, без отрыва от производства.
У вас может уйти меньше дней или больше часов, зависит от того, как интенсивно вы будете вникать.
Но мое личное мнение – лучше все же не пытаться “ворваться” за один день. Просто потому что если Вы никогда не имели с этим дело, то в конце первого дня у вас будет где-то вот такая голова
И лучше в этот момент остановиться, переварить информацию, может даже поспать.
1. Теория
Если Вы ранее имели дело с виртуальными машинами и такими инструментами как virtualbox, vmware, vagrant и подобными штуками – лучше забудьте о них.
Лично моей ошибкой была попытка работать с докер как с виртуальной машиной. Докер – средство виртуализации процессов, а не систем. Важное правило – каждому процессу свой виртуальный контейнер.
Контейнер следует воспринимать как отдельный процесс и наоборот. Например не следует пихать в один контейнер mysql и redis. Или еще хуже всю связку apache+php+mysql.
Основные термины
Image (образ) – собранная подсистема, необходимая для работы процесса, сохраненная в образе.
Container (контейнер) – процесс, инициализированный на базе образа. То есть контейнер существует только когда запущен. Это как экземпляр класса, а образ это типа класс. Ну я думаю идея понятна.
Host (хост) – среда, в которой запускается докер. Проще говоря – ваша локальная машина.
Volume – это дисковое пространство между хостом и контейнером. Проще – это папка на вашей локальной машине примонтированная внутрь контейнера. Меняете тут меняется там, и наоборот, миракл.
Dockerfile – файл с набором инструкций для создания образа будущего контейнера
Service (сервис) – по сути это запущенный образ (один или несколько контейнеров), дополнительно сконфигурированный такими опциями как открытие портов, маппинг папок (volume) и прочее. Обычно это делается при помощи docker-compose.yml файла.
Docker-compose (докер-композ, чаще композер, но не путать с php composer) – тулза, облегчающая сборку и запуск системы состоящей из нескольких контейнеров, связанных между собой.
Build (билд, билдить) – процесс создания образа из набора инструкций в докерфайле, или нескольких докерфайлов, если билд делается с помощью композера
В данной статье позже (завтра) я опишу процесс сборки связки nginx+mysql+php7-fpm с примерами и описаниями dockerfile и docker-compose файлов.
Вкратце о том, как работает билдинг образов
Сначала есть docker hub, это такой репозиторий, куда все желающие самоутвердиться публикуют свои собственные сборки образов. За это некоторым большое спасибо, а некоторым нет, все как и в любой другой свалке пакетов.
Обычно докерфайл начинается с инструкции FROM, которая указывает с какого пакета/образа из хаба начать.
Далее обычно идет инструкция maintainer, задача которой увековечить имя создателя очередного гениального творения.
Затем наиболее часто встречающиеся команды:
RUN – выполняет команду внутри образа.
ADD – берет файлы с хоста и кладет внутрь образа.
А также COPY, EXPOSE, ENTRYPOINT, CMD — обо всем этом Вы узнаете в процессе.
Сейчас внимание. Докер выполняет инструкции из докерфайла последовательно поверх предыдущего результата. Таким образом организовано хранение кеша.
Не поняли? Сейчас покажу. Вот простейший докерфайл:
Как докер его билдит:
ID я тут написал условные, но важно помнить, что идентификаторы этих “промежуточных” образов напрямую связаны с самими инструкциями, с файлами которые добавляются инструкцией ADD и с ID родительского образа. То есть фактически перед выполнением каждого шага сначала вычисляется ID (хеш) образа, поиск такого ID в локальном кеше, и только если такого ID в кеше нет, тогда выполняется шаг и сохраняется в кеш, иначе – используется образ из кеша.
А также это значит, что если вы решите поменять команду например run apt-get install nginx на другую, то Хеш(ID) инструкции изменится и весь дальнейший кеш после этого использоваться не будет. Потому не удивляйтесь если после изменения одной буквы в имени maintainer’а у вас вся сборка будет пересобираться от самого начала.
Также исходя из описанного сценария выполнения команд становится понятно, почему не имеет смысла в инструкциях выполнять команды ничего не сохраняющие после своего выполнения – частый вопрос на stackoverflow — “я запустил что-то, а в следующей инструкции оно не запущено”. Например кто-то хочет активировать source env/bin/activate и в следующей инструкции выполнить pip install
или другой пример – запустить монгодб и в следующей инструкции создать юзера/базу или выполнить импорт базы из файла (есть причины почему лучше так не делать, но сейчас не об этом)
RUN source /app/env/bin/activate \
&& pip install something
Думаю для начала работы этой теории вполне достаточно.
2. Практика
Перед началом думаю следует пойти немного передохнуть и сделать себе чайку.
А когда вернетесь, сначала установите себе Docker и Docker Compose.
Небольшое отступление для тех, кто читает это под виндой.
Итак, на данном этапе у вас уже установлен докер, и в панели задач радостно побулькивает блоками #синийкит (извините, не удержался). Теперь сходите вот по этой ссылке и пройдите туториал Get Started.
Теперь давайте представим, что мы разрабатываем веб сайт на php и будем его публиковать связкой nginx+php7-fpm+mysql.
Вот очень примитивный dockerfile для php сервиса:
EXPOSE 9000
CMD [«php-fpm»]
Вкратце человеческим языком:
В случае с nginx и mysql нам даже не нужно писать свои dockerfile, так как никаких дополнительных расширений нам ставить не нужно. Вот как будет выглядеть docker-compose.yml нашего проекта
Директива volumes монтирует папки из хостмашины внурть контейнера, таким образом осуществляется конфигурирование nginx и сохранение данных бд при перезапуске.
Директива links связывает сервисы между собой, app связан с db, это значит что после запуска внутри контейнера app будет доступен хост “db”, и он будет указывать на соответствующий контейнер.
Есть довольно интересный темплейт yii2-starter-kit, в коробке которого можно найти неплохую реализацию описанной сборки php7-fpm nginx mysql а также mailcatcher.
Тем кому, как и мне, больше по душе python и django можно вообще не париться и все сделать по официальному туториалу от Docker — docs.docker.com/compose/django
плюс, после того как уже пришло понимание как это все работает, не составит особого труда переработать любую понравившуюся сборку под свои нужды.
Pitfalls
— MacOS. Доступ к сервису на хосте (к примеру mongo или mysql) из контейнера.
Из-за ограничений “Docker for Mac networking stack” нельзя “просто так взять и подключиться” к локалхост. Но есть два обходных пути:
а) официальный и простой (доступен в версии Docker начиная с 17.06) — использовать для подключения специальный DNS хост (доступно только в Docker for Mac) docker.for.mac.localhost. Источник.
б) добавить алиас IP к сетевому устройству lo0:
`sudo ifconfig lo0 alias 10.200.10.1/24`
и использовать этот адрес для подключения
— MongoDB. На маке нельзя монтировать внешний диск для данных. Причины описаны здесь
WARNING (Windows & OS X): The default Docker setup on Windows and OS X uses a VirtualBox VM to host the Docker daemon. Unfortunately, the mechanism VirtualBox uses to share folders between the host system and the Docker container is not compatible with the memory mapped files used by MongoDB
— Под Windows не работают симлинки в примонтированных томах. Ожидаемо, но очень неприятно узнавать об этом уже после того как все остальное заработало.
— Отличия entrypoint и command — вот тут подробно и понятно описана разница между entrypoint и command.
— UPD дополнение от saskasa: На маке скорость записи из контейнера на диск хоста (добавленный как VOLUME) очень медленная, для понимания масштабов — примерно в 50-100 раз.
Сборка и запуск примера приложения Todo
Это продолжение учебника, начало которого можно просмотреть здесь.
В оставшейся части этого руководства вы будете работать с простым диспетчером списка задач, который выполняется в Node. js. Если вы не знакомы с Node. js, не беспокойтесь! Реальное взаимодействие с JavaScript не требуется!
На этом этапе команда разработчиков довольно мала, и вы просто создаете приложение, чтобы доказать свое звание MVP. Вы хотите продемонстрировать, как он работает и что может делать без необходимости думать о том, как он будет работать для крупной группы, нескольких разработчиков и т. д.
Получение приложения
Перед запуском приложения необходимо получить исходный код приложения на компьютере. Для реальных проектов обычно клонируется репозиторий. Но в этом учебнике мы создали ZIP-файл, содержащий приложение.
Если вы используете Windows, на локальном компьютере должно быть установлено приложение «Docker для Windows» или Docker Community Edition. См. документацию по установке Docker для Windows. Процесс установки создает ZIP-файл, содержащий пример, доступный по адресу localhost. Если вы используете Mac, установите Docker Desktop для Mac.
Скачайте исходный код для приложения из репозитория Docker. Вы можете скачать для репозитория ZIP-файл. Чтобы скачать ZIP-файл, нажмите зеленую кнопку Code (Код) и выберите Download ZIP (Скачать ZIP). Откройте ZIP-файл и выберите «Извлечь все», чтобы извлечь исходный код приложения из папки app (приложение) в папку на жестком диске.
После извлечения используйте любой редактор кода, чтобы открыть проект. Если вам требуется редактор, можно использовать Visual Studio Code. Вы должны увидеть package.json и два подкаталога ( src и spec ).
Создание образа контейнера приложения
Создайте файл с именем Dockerfile в той же папке, где находится файл package.json со следующим содержимым.
Кроме того, вы можете щелкнуть Dockerfile правой кнопкой мыши и выбрать пункт Build Image… (Создать образ), а затем при появлении запроса указать тег.
После загрузки образа вы скопировали в свое приложение и использовали yarn для установки зависимостей приложения. Директива CMD указывает команду по умолчанию, которую необходимо выполнить при запуске контейнера из этого образа.
. в конце команды docker build указывает, что Docker должен искать Dockerfile в текущем каталоге.
Запуск контейнера приложения
Теперь, когда у вас есть образ, запустите приложение. Для этого используйте команду docker run (вспомнили?).
Запустите контейнер с помощью команды docker run и укажите имя образа, который вы только что создали:
Через несколько секунд откройте http://localhost:3000 в браузере. Вы должны увидеть приложение!
Добавьте элемент или два, чтобы увидеть, что он работает, как ожидалось. Элементы можно пометить как завершенные и удалить элементы. Внешний интерфейс успешно сохраняет элементы в серверной части. Просто и быстро, правда?
На этом этапе у вас должен быть работающий список задач с небольшими наборами элементов. Теперь сделаем несколько изменений и изучим управление контейнерами.
Если взглянуть на расширение VS Code, вы должны увидеть два контейнера, которые работают сейчас (это учебник и новый контейнер приложения).
Резюме
В этом разделе вы узнали о создании образа контейнера и Dockerfile. После создания образа вы запустили контейнер и увидели работающее приложение!
Далее предстоит внести изменения в приложение и узнать, как обновить работающее приложение с помощью нового образа. Кроме того, вы узнаете о некоторых других полезных командах.
Лабораторная работа: введение в Docker с нуля. Ваш первый микросервис
Привет, хабрапользователь! Сегодня я попробую представить тебе очередную статью о докере. Зачем я это делаю, если таких статей уже множество? Ответов здесь несколько. Во-первых не все они описывают то, что мне самому бы очень пригодилось в самом начале моего пути изучения докера. Во-вторых хотелось бы дать людям к теории немного практики прямо по этой теории. Одна из немаловажных причин — уложить весь накопленный за этот недолгий период изучения докера опыт (я работаю с ним чуть более полугода) в какой-то сформированный формат, до конца разложив для себя все по-полочкам. Ну и в конце-концов излить душу, описывая некоторые грабли на которые я уже наступил (дать советы о них) и вилы, решение которых в докере просто не предусмотрено из коробки и о проблемах которых стоило бы задуматься на этапе когда вас распирает от острого желания перевести весь мир вокруг себя в контейнеры до осознавания что не для всех вещей эта технология годна.
Что мы будем рассматривать в данной статье?
В Части 0 (теоретической) я расскажу вам о контейнерах, что это и с чем едят
В Частях 1-5 будет теория и практическое задание, где мы напишем микросервис на python, работающий с очередью rabbitmq.
В Части 6 — послесловие
Часть 0.0: прогреваем покрышки
Что такое Докер? это такой новомодный способ изоляции ваших приложений друг от друга с помощью linux namespaces. Он крайне легок в управлении, расширении, миграции и подходит для огромного спектра задач начиная от разработки приложений и сборки пакетов и заканчивая тестами-пятиминутками (запустить-проверить 1-2 команды-закрыть и забыть, да так чтобы еще и мусор за тобой прибрали). Читайте подробнее на офф сайте. Стоит только представлять себе на первом этапе, что контейнер состоит из слоев, т.е. из слепков состояний файловой системы. Я расскажу об этом чуть позже.
Лабораторная инфраструктура данной статьи может быть воспроизведена как на одной ОС с Linux на борту, так и на серии виртуальных машин\vps\whatever. Я делал все на одном дедике (dedicated server) с ОС Debian. Здесь и далее я предполагаю что вы работаете в одной ОС Debian 9. Команды и подходы от ОС к ОС могут отличаться, описать все в одной статье нереально, поэтому выбрана наиболее знакомая мне серверная ОС. Надеюсь для вас не составит труда установить себе виртуальную машину и дать ей выход в интернет.
Часть 0.1 Сравнение с VM
Важно помнить, что Docker — средство изоляции процесса(задачи), а это значит относиться к докеру как к виртуалке нельзя. Он является оберткой вокруг средств контейнеризации ( cgroups + namespaces ) конкретно linux kernel. Но он имеет многие похожие фишки, как и у виртуализации:
Часть 0.2 Процессы в контейнерах
Теперь, когда мы выяснили что такое docker, посмотрим в бинокль что там внутри. Нужно запомнить следующие постулаты:
Часть 0.3 Откуда брать эти ваши контейнеры? как хранить?
Существуют публичные и приватные хранилки официальных и неофициальных образов. Они называются docker registry. Самый популярный из них — Docker hub. Когда вы докеризуете какой-либо сервис, сходите сначала на хаб и посмотрите, а не сделал ли это кто-то уже за вас?
Также это будет сильным подспорьем для вас в момент изучения. поскольку на хабе виден готовый Dockerfile, то вам можно будет подглядеть у крутых дядек, а как они делают ту или иную штуку. Много их хранится на github и других подобных ресурсах.
Кроме публичных registry существуют еще и приватные — платные и бесплатные. Платные вам понадобятся, когда вы в замучавшись обуслуживанием воскликните «да пусть вот эти ребята следят за всей этой слоёной docker-вакханалией в этих ваших registry». И правда, когда вы активно пользуетесь при DevOps докером, когда люди или автоматика билдит сотни контейнеров, то рано или поздно у вас начнет подгорать от того как это чистить и обслуживать.
Не все конечно так плохо, естественно. Для десятка человек и пары билдов в день подойдет и свой registry. Для личного пользования тем более.
Зачем может пригодиться registry? это единое место хранения и обмена контейнерами между вами, другими людьми или автоматикой. И вот это по-настоящему крутая штука. Положить и забрать готовый контейнер можно всего одной командой, а при условии того что весит он копейки (по сравнению с VM), то плюсы просто на лицо. Правда стоит помнить, что docker — инструмент, docker registry не хранит данных ваших рабочих контейнеров. Таким образом можно иметь крайне удобный воркфлоу: разработчики пишут продукт, билдят новый контейнер, пушат его в репозиторий. Тестироващики забирают его, тестируют, дают отмашку. Девопсы затем с помощью, например, ansible накатывают контейнеры на прод. Причем вам абсолютно не важно, 1 у вас сервер или 100, с помощью ansible и registry раскатывание контейнеров в прод — одно удовольствие. Здесь же и весь фарш с CI — новые версии могут быть автоматически скачаны билд-сервером. Посему очень рекомендую, даже для личного пользования, завести себе VPS\DS с registry, это очень удобно — обмениваться контейнерами.
Скачать образ из репозитория:
Загрузить образ в репозиторий:
В приватные репозитории нужно еще и логиниться, для этого вам пригодится команда
где потребуется ввести логин и пароль.
Запустить свой registry можно всего одной командой:
Подробнее на офф сайте. SSL, авторизация — все это можно.
Часть 0.4 Контейнеры и образы (images)
Докер образ (image) — это некоторый набор слоёв. Каждый слой — результат работы команды в Dockerfile. Грубо говоря образ — это шаблон на основе которого вы будете запускать контейнеры. Все что запускается на основе этого образа есть контейнер, или инстанс. т.е. с одного образа можно запустить несколько одинаковых копий этого образа — контейнеров. Причем потом, после каких-то манипуляций, из этого контейнера можно создать шаблон — новый образ. Хранится все это безобразие в /var/lib/docker.
документация.
Список образов в вашей системе можно глянуть командой:
Часть 0.5 Как именовать контейнеры и образы (images)?
У образа существует 3 поля, имеющих отношения к именованию:
1) Репозиторий
2) Тег
3) Image ID
Репозиторий — реальный или нереальный, это то место, откуда или куда ваш образ будет скачан\загружен, или это просто выдуманное имя, если загружать никуда вы его не собираетесь.
Тег — обычно это версия продукта. По идее — это любой набор символов. (из разрешенного списка [a-z0-9.-] итп.) если тега нет, то автоматически используется слово latest. тег ставится через символ: от имени репозитория и подставляется автоматически если его не указать при push\pull.
ImageID — локально генерируемый уникальный ID вашего образа, по которому этим образом можно оперировать.
Таким образом вы или автор контейнера влияете на репозиторий и\или тег, а система локально — на ID. Но, кстати вам никто не мешает переиспользовать любое чужое имя репозитория, другой вопрос что запушить (push, загрузить) в него вы не сможете
1/2/3-blah.blah.blah — имя вашего локального образа, вы его выдумали
projects/my_first_docker — тоже имя вашего локального образа
projects/my_first_docker:latest — то же самое имя, но с тегом. эквивалентно предыдущему.
projects/my_first_docker:1.13.33 — а вот это уже конкретная версия образа в этом репозитории.
projects/my_first_docker:1.13.34
projects/my_first_docker:1.13.35
… итд — все это один и тот же проект, но версии ваших образов будут разными.
wallarm/node — а вот это уже скачанный с публичного докер хаба образ с лучшим WAF от компании Wallarm.
debian:stretch — образ debian, версия образа — stretch (не число, но слово)
centos:7 — аналогично debian.
mongo:3.2 — образ mongodb версии 3.2, скачанный с публичного репозитория
nginx — latest stable nginx — аналогично.
Причем чаще всего на хабе можно увидеть разнообразные версии нужного вам софта. Так например mongo даст вам скачать и 3.2 и 3.4 и 3.6 и даже дев версию. и разные промежуточные. и еще кучу других.
Следует помнить: один и тот же образ может иметь сколько угодно имен репозитория и тегов, но будет иметь одинаковый imageid. При этом, образ не будет удален, пока все теги этого образа не будут удалены. т.е. скачав образ debian, задав ему новый тег вы получите в списке 2 образа с разными именами, но одинаковыми imageid. и удалив debian:stretch, вы просто удалите один из тегов, а сам образ останется жить.
Чтобы задать другое имя для существующего образа используется команда:
Для удаления образа:
А вот контейнеры имеют 2 имени:
1) CONTAINER ID
2) Name
С id — тут то же самое — это уникальное имя конкретного уникального запущенного инстанса образа. проще говоря — уникальное имя запущенного образа. Образ может быть запущен сколько угодно раз и каждая копия получит уникальное имя.
Для удаления контейнера:
Часть 0.6 Ладно, все это чужие контейнеры, а как мне сделать свое, с нуля?
Для создания своих контейнеров существует механизм сборки — docker build. Он использует набор инструкций в Dockerfile для сборки вашего собственного образа. При сборке контейнера внутри используется оболочка sh и ваши команды исполняются в ней.
Следует знать следующее:
Часть 0.7 Напоследок и «Это не докер-вэй»
Докер довольно таки мусорит слоями при билде. Кроме того вы можете оставлять кучу остановленных контейнеров. Удалить все это одной командой
Это не докер-вэй
Докер по задумке виртуализирует именно один процесс (с любым кол-вом потомков). Никто вам не мешает конечно запихнуть в один контейнер и 10 процессов, но тогда пеняйте на себя.
Сделать это можно с помощью, например, supervisor.
Но, скажете вы, как же быть со службами, которые ну всю свою жизнь будут вместе и не должны разделяться? использовать контент, который будут генерировать друг для дргуга?
Для того чтобы сделать это красиво и правильно есть docker-compose — это yaml файл, который описывает N ваших контейнеров и их взаимоотношения. какой контейнер с каким скрестить, какие порты друг для друга открыть, какими данными поделиться.
В случае с compose вам, на самом деле, и управлять своим проектом проще. например, связка nginx+uwsgi+mongo — это 3 контейнера, хотя точно известно, что никто кроме nginx не будет ходить в uwsgi, а кроме uwsgi — никто в mongo. да и жить они будут всегда вместе. (ЭТО ЧАСТНЫЙ СЛУЧАЙ). Вот тут у нас получается следующая ситуация — ваше приложение (api) будет обновляться часто — вы его пишете и пушите каждый день. а, например, релизы nginx или mongodb выходят куда реже — может месяц, может дольше. Так зачем каждый раз билдить этого тяжеловеса, когда изменения то всего происходят в одном месте? Когда придет время обновить nginx, вы просто смените имя тега и всесто ребилда проекта, просто скачается новый контейнер с nginx.
Часть 1: когда уже практика?
На этом этапе можно попробовать потрогать контейнеры и поучить необходимые команды. Я — сторонник вбивания команд из туториалов вручную. Поэтому — только скриншоты.
1. Попробуем скачать дебиан образ
2. Глянем, что получилось. В моем случае, я для фильтра использовал слово debian, т.к. иначе вывалилось бы в выводе куча других образов. вам его использовать не обязательно. Ну и как результат — у вас будет 2 одинаковых imageid с разными тегами (первые две у меня).
З.Ы. привет в будущее! для тех, кто доживет до debian10 и будет читать эту статью, у вас будет не такой же результат что и у меня. Надеюсь, понимаете почему.
3. Запустим bash-процесс внутри образа debian:stretch. Посмотрите его pid — он равен единице. Т.е. это тот самый главный процесс, вокруг которого мы и пляшем. Обратите внимание — мы не смогли так просто выполнить ps aux — в контейнере нет нужного пакета. поставить его мы можем как и обычно — с помощью apt.
4. Выйдем из контейнера (exit | Ctrl+D ) и попробуем запустить баш заново — это в верхней консоли (вы можете открыть другую, я для простоты скриншотов сделал так). В нижнем окне — посмотрим список запущенных контейнеров
Вот это да! а куда делся procps? да никуда. его тут нет и не было. когда мы запустили образ заново — то взяли тот самый слепок без установленной программы. а куда делся результат нашей работы? А вон он валяется — в состоянии exited. Как и куча других запущенных мною ранее контейнеров и остановленных впоследствии.
Все это — мусор. Но его еще можно оживить:
1. выходим из контейнера в 1-м терминале
2. во втором терминале копируем ID контейнера, подходящего по timeshtamp как и тот который мы тогда остановили
3. в 1 терминале запускаем этот контейнер командой start. он уходит в background
4. во втором терминале смотрим список запущенных контейнеров — по таймштампу тот что запущен 7 секунд — явно наш.
5. Финт ушами. аттачимся к уже запущенному контейнеру с помощью команды exec и запускаем второй инстанс bash’a.
6. выполняем ps aux — обратите внимание, тот, первый bash, с pid 1 живет. а мы в контейнере теперь управляем им через bash с pid=7. и теперь, если мы выйдем, контейнер будет жить.
Итак:
run — взять образ X и создать контейнер Z с процессом Y
exec — взять контейнер Z и запустить в нем процесс N, при этом процесс Y будет работать как и прежде.
1) Контейнер может жить как в background, так и в foreground, лишь бы был жив процесс, который мы виртуализируем.
2) контейнер — это развернутый из образа инстанс
3) контейнеры можно останавливать и запускать без потери данных (но не стоит, это не докер-вэй)
Часть 2: делаем собственный докер-образ
Учимся использовать dockerfile.
Здесь — два подхода к разработке:
1) взять готовый и подточить под себя
2) сделать самому с нуля
Вариант первый — когда вы уверены что за вас прекрасно сделали уже всю работу. Например, зачем устанавливать nginx, когда можно взять готовый официальный контейнер с nginx. Особенно это касается тех систем, которые не ставятся в одну команду или которые например в debian имеют устаревшие версии, а на докер хабе их билдят более свежие стабильные. Тем более там уже сделана автосборка — свежие версии там прилетают довольно быстро.
Вариант второй — вы параноик или вам не нравится подход автора образа (например, официального не нашлось, а есть только на хабе васи пупкина). Никто не мешает посмотреть как сделал это василий, а потом пойти и сделать самому. Ну или в случае если вы просто пишете свою логику, которой точно нигде нет, как например докер образ для билда ваших deb-пакетов в jenkins. А ведь это очень удобно!
Dockerfile это файл с набором инструкций, которые будут совершены в чистом контейнере указанного вами образа, а на выходе вы получите свой образ.
Самое главное что вам нужно указать — это директивы FROM и CMD (в принципе не обязательно). Приведу пример такого файла:
FROM — какой образ взять за основу.
MAINTAINER — автор данной разработки (кого пинать за работу этого, нового образа)
RUN — команда, исполняемая внутри контейнера, на основании которой будет получен новый образ. Обратите внимание — обычные bash-переносы и && — делает код читаемым и красивым.
Вы также можете писать хоть 10 RUN — команд (но каждая с новой строки), в которых описывать свою логику.
EXPOSE — какие порты, по какому протоколу будут доступны снаружи контейнера при использовании docker-compose. Тут важно помнимать, что это не значит что они будут доступны извне сразу. т.е. имеется ввиду, что пока вы при запуске этого образа не укажете мапинг портов хост: контейнер, никак внутрь попасть не получится. поэтому нужно знать какие порты у вас на каком этапе прописаны:
И только так — получается, обращаясь на хост по 80 порту вы мапитесь в контейнер на 80 порт. при запуске контейнера этот порт был открыт, а внутри контейнера приложение слушает 80 порт. и так, вы с 80 порта хоста попадете на 80 порт приложения. Естественно порты можно делать любыми. Так, можно запустить множество контейнеров, внутри которых nginx’ы слушают 80-й порт, а мапинг снаружи сделать такой: 8000:80, 8001:80, 8002:80… ну итд, идея понятна. таким образом можно сделать 1 образ с одним конфигом, а запустить его паралельно и независимо множество раз.
CMD — и вот это самое ключевое место. Это та команда, которая будет выполняться при запуске контейнера и работать все это время. Помните да? пока работает процесс, живет контейнер. Здесь подход такой же как в systemd — процесс не должен уходить в background — это незачем, процесс то всего один внутри этого контейнера. Отсюда кстати и разные подходы логирования. например можно оставлять вывод в STDOUT, а можно в логи. Главное, чтобы процесс (главный процесс) после запуска оставался жить. а форков-тредов может быть сколько душе угодно.
Кроме CMD есть еще один вид шаманства — ENTRYPOINT. это обычно shell скрипт, которому в качестве аргумента передается значение CMD.
Нужно запомнить следующие вещи:
1) итоговая команда запуска процесса в контейнере = сумма ENTRYPOINT + CMD.
2) вы можете писать в докер файле любой вариант сочетания entrypoint и cmd — оставлять или то или другое, или оба сразу.
Зачем нужна такая сложность? Правильные пацаны пишут entrypoint для раскрытия всего многообразия возможностей. Если вы пишете образ чисто под себя, можно оставить чисто только один CMD и не писать ENTRYPOINT вообще.
Это примитивный пример что можно сделать, манипулируя вводами. Рассмотреть можно десятки сценариев различного использования комбинаций ENTRYPOINT+CMD. Просто помните, что можно предусмотреть несколько различных видов запуска вашего контейнера у конечного пользователя, который не имеет досупа к вашему dockerfile.
Помимо указанных мною команд, существуют и многие другие, например ADD, COPY — позволяет положить набор данных внутрь контейнера, причем если ADD в качестве источника указать архив, она распакует и положит содержимое в указанное место. удобно для редко изменяемого набора файлов.
Порядок в Dockerfile важен! (с точки зрения оптимизации)
При внесении изменений в Dockerfile и последующий ребилд образа затрагивает тот набор команд, начиная с которого происходит изменение.
Пример:
Внося изменения хоть в один символ конфига вы будете вызвать снова и снова каждый раз установку пака из 100500 программ. а вот если эти строчки расположить в обратном порядке, то билд будет происходить почти мгновенно — т.к. слой с этими программами уже существует.
Часть 3: связь ФС контейнера с хостом
Теперь давайте рассмотрим способ доступности данных с хостовой ос из контейнера. Причин этого может быть несколько: передача файлов, конфигурации, или просто сохранение обработанных данных на диск. Как я уже упоминал ранее данные не пропадают просто так если выключить контейнер, но они и остаются в этом контейнере. вытащить потом их — это ненужная и глупая задача. Докер-вэй — монтирование хостового каталога\файла внутрь контейнера. А если проще — мапинг папки\файла снаружи на папку\файл внутри. поскольку это монтирование, то изменение с одной стороны всегда будет видно и с другой. Таким образом, внося изменение в конфиг с хостовой ос — вы можете заставить сервис внутри контейнера работать по-другому. И наоборот — для сохранения базы данных на хосте в одном, строго определенном, удобном для вас месте. Или логов.
В этом месте позволю себе дать вам еще один линк на полезный ман, а именно на статью-сборник команд по докеру с кратким пояснением.Тыц
В отдельных случаях проще передавать переменные сред окружения, чем конфиги. и докер позволяет это делать, как зашитыми для билда в Dockerfile (ENV key=value), так и через ключ при запуске контейнера, так и через отдельный файл, если таких переменных много.
Часть 4: Удобный запускатор ваших сервисов
Когда вы уже набилдили себе парочку контейнеров, возникает вопрос, как же их автоматически запускать, не прикладывая к этому усилий? ответ прост — через systemd!
Например, создайте файл:
и занесите туда строки:
на что здесь следует обратить внимание:
Вот и пожалуйста — управляем вашей службой как обычным systemd демоном, а journalctl — смотрим stdout вашего демона.
Часть 5: приблизимся к реальности. Построим нашу инфраструктуру
Т.е. будет 3 контейнера, первый будет ставить задачи в очередь, последний считывать. Все это будет управляться через systemd.
sender.py будет ставить каждые 5 секунд в очередь рандомное число от 1 до 7, а воркер (receiver.py) будет считывать это число и имитировать работу — методом простоя кол-ва секунд равное полученному числу.
Мы будем пытаться сделать наши микросервисы так, как это делают правильные пацаны — внутри контейнера код сервиса, а конфиги и логи — снаружи. Это позволит нам поменять адреса очереди, логин, пароль итд без нового билда контейнера. Просто поправить файлик и перезапустить контейнер, вот и все дела. Таким образом нам понядобится директория в /etc и в /var/log, куда мы будем мапить логи и конфиги, соответственно. Вообще такой подход удобен. зайдя на любой сервер с докером, где могут сосуществовать рядом один или несколько микросервисов, вы всегда знате где искать конфиги или логи. А главное, у вас на сервере будет полная чистота. ssh да docker, чего еще надо? (ну, много чего, понятно, например службу мониторинга или puppet, но мы уже избавляемся от кучи всего сервисо-зависимого. А главное — сервис уже не зависит от хостовой ОС. Имейте весь парк серверов на дебиане, а внутри контейнера — все что угодно. Да, эти же аргументы приводят и к виртуализации, но тут история куда легковеснее. ведь мы тащим по сервакам инструмент, а не целую виртуализированную ОС)
1. RabbitMQ
RabbitMQ — диспетчер обмена сообщениями между приложениями. в нашем случае будет использована примитивная очередь. в эту очередь будет помещаться задание и приниматься воркером. Конфиги настройки очереди мы захардкодим внутрь контейнера. Всего в папке rabbitmq у нас будет 3 файла — Dockerfile и 2 файла с конфигами. Их содержимое — в спойлерах под листингом. Настройка очереди, пользователя, прав доступа — в json файле definitions.