# Введение в Docker контейнеризацию ## База Докер — система, которая помогает создавать виртуальное окружение. Это когда нам нужна виртуальная машина, но не другая операционка, а просто изолированный кусок системы с удобными настройками — определённые версии языков, утиллиты, переменные. Кроме создания такого окружения докер позволяет его быстро запускать и останавливать, клонировать, делиться им. Поэтому авторы CTF’ок и хакатонов теперь дают вам докер-образы. Тут образ — это описание окружения. Чтоб создать свой образ, надо написать докерфайл. В нём указывается, какой образ мы берём за основу для модификации, какие изменения вносим и что будет происходить при запуске. Вот так например выглядит `Dockerfile`: ```dockerfile # За основу возьмём официальный образ со всем необходимым для работы с MongoDB FROM mongo:latest # Внесём какие-то изменения RUN mkdir /home/mongodb RUN mkdir /home/mongodb/data RUN mkdir /home/mongodb/logs RUN chown -R mongodb:mongodb /home/mongodb COPY assets/mongodb.conf /home/mongodb COPY assets/index.js /home/mongodb RUN mongod \ --fork \ --config /home/mongodb/mongodb.conf \ && mongo \ mydb \ --host localhost \ /home/mongodb/index.js \ && mongod --dbpath /home/mongodb/data --shutdown \ && chown -R mongodb /home/mongodb # А вот это произойдёт при запуске образа CMD ["mongod", "--config", "/home/mongodb/mongodb.conf"] ``` Образом можно поделиться через Docker Hub — на соревновании перед работой может понадобиться загрузить оттуда образ и иногда изучить его содержимое. Докерфайла хватает, чтоб создать образ. Воспользуемся командой `build` и передадим ей файл и контекст. Контекст — это папка, в которой мы будем собирать образ. Если мы захотим скопировать что-то в образ с нашего компьютера, нужно задать «опорную точку». Например команда `COPY` в конфиге как раз выполняет копирование и она будет искать в нашем «контексте» те папки, которые мы попросим скопировать. Так выглядит итоговая команда сборки. ```shell docker build -f Dockerfile . ``` После сборки у нас появится образ с каким-то айди — он выведется в конце и будет доступен в списке при выполнении команды `docker images`. По этому айди мы можем запустить образ. Сделаем это: ```shell docker run 3d78h2zm128 ``` Фишка в том, что необязательно вводить весь айди — хватит того количества символов, которое уникально среди всех образов. Если в конце передать какую-то команду, то она выполнится внутри контейнера, если нет — выполнится команда из блока `CMD` в докерфайле. Когда запускаем образ, создаётся контейнер — это экземпляр образа. При запуске образа можно выполнить разные действия: - прокинуть порты, - запустить какую-то команду внутри, - создать общую папку между вашим компьютером и контейнером, чтоб делиться файлами. Для этого нужно передать параметры в команду запуска. Например `-p 8080:80` прокинет 80 порт изнутри в 8080 снаружи, а `-v host_folder:container_folder` синхронизирует папки. ### Запуск удобнее — немного автоматизации Редко запускается голый образ — нам нужна связь со внешним миром, — поэтому приходится каждый раз прописывать кучу параметров. Или можно задать все параметры один раз и не париться :) Для этого существует docker compose — правила сборки и запуска образа. Вот так выглядит файл `docker-compose.yml`: ```docker version: '3' services: # Так будет называться сервис, их можно сделать много — все запустятся сразу mongo: # Так будет происходить сборка build: context: . dockerfile: assets/Dockerfile # Прокидываем порт ports: - '27017:27017' # Попросим контейнер перезапускаться, если вдруг он упал restart: always # Тут же можем синхронизировать папки, # сменить название сервиса в сети, # задать переменные окружения и много чего ещё. ``` Запустить теперь можно при помощи команды `docker-compose up`, при запуске автоматически выполнится сборка, для ручной сборки используем параметр `--build`, для остановки — `docker-compose down`. Если мы опишем правила запуска нескольких разных образов, то у нас будет несколько сервисов и при запуске они будут видеть друг друга по именам — например можно будет подключиться к базе данных не по айпишнику, а просто написав `mongo` — это работает так же как и с `localhost` в системе. Внутри контейнера можно выполнить команду в любой момент, контейнер — это машина. Иногда нужно проверить записи в базе данных напрямую или отредактировать файлы, если вдруг образ чужой и его создание вы не контролировали. Чтоб повзаимодействовать, мы узнаём айди контейнера командой `docker ps`, которая покажет запущенные контейнеры. Затем выполняем команду. ```shell docker exec -it ID ls ``` `docker exec` как раз команда, которая позволяет выполнить что-то внутри, `-it` — параметр, который позволит нам увидеть удобный вывод, `ls` — та команда, которую мы запускаем внутри. Частое применение — это просто залезть в терминал контейнера. Для этого выполняем внутри команду `bash`, которая обозначает дефолтную оболочку терминала во многих образах. ```shell docker exec -it ID bash ``` Контейнер можно прервать, а потом запустить снова, можно и полностью удалить. Вот соответствующие команды: ```shell docker stop ID docker start ID docker rm ID ``` ## Ещё команды, с которыми придётся работать Когда мы запускаем образ или готовый контейнер — видим логи, `Ctrl + c` остановит контейнер. Чтоб просто запустить контейнер и продолжить работать с терминалом дальше, можно запустить контейнер в фоне. Для этого используется флаг `-d`. ```shell docker run ID -d ``` Логи не потеряны, если хочется их увидеть, можно выполнить команду: ```shell docker logs ID ``` или её же с флагом `-f` — тогда логи будут обновляться. Флаг `--rm` при запуске образа создаст контейнер на один раз, при остановке он будет удалён. Это иногда полезно, чтоб посмотреть какой-то вывод контейнера и не засорять память компьютера. Для вывода всех образов — `docker images`. Для вывода всех контейнеров — `docker ps -a`. Для удаления образа по айдишнику `docker rmi ID` — похоже на удаление контейнера `docker rm ID`. Многие флаги докера работают аналогично с docker compose: запуск в фоне, просмотр списка контейнеров. ## Что нужно понимать про производительность Контейнер не виртуальная машина, он меньше тратит. [Тут](https://stackoverflow.com/a/16048358) можно почитать почему. Но для работы контейнеров на Windows и Mac запускается одна виртуальная линукс-машина на все контейнеры, потому что докер не умеет напрямую работать с нелинукс системами. [Вот объяснение](https://web.archive.org/web/20150326185901/http://blog.dotcloud.com/under-the-hood-linux-kernels-on-dotcloud-part) от авторов докера почему. ## Дальнейшее изучение У докера хорошая документация, реально хорошая — она объясняет как устроены некоторые штуки и является хорошей кладовкой команд для терминала. Затем можно податься в Kubernetes — одно из основных применений контейнеризации, благодаря которому докер стал популярным.