# Введение в 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 — одно из основных применений контейнеризации, благодаря которому докер стал популярным.