Докер / Основы Docker

Теперь потыкаем докер. Докер — это инструмент для контейнеризации. То есть это такая штука, которая позволяет упаковать ваш сервис в специальный файл сразу вместе с зависимостями. И его можно будет запустить одной командой.

Устанавливаем Docker

И так, сначала установил docker. Идем сюда https://docs.docker.com/engine/install/ubuntu/#prerequisites

там инструкция что делать, сначала подключаем репозиторий

а потом можно уже установить docker engine

sudo apt-get update
sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

чтобы проверить что он работает запустите

sudo docker run hello-world

увидим такое

Общая идея

Идея работы с докером заключается в следующем:

  1. Вы описываете в текстовом файлике который обычно называют Dockerfile команды необходимые для запуска вашего сервиса.
  2. Потом на базе этого файлика создается так называемый образ. Образ это своего рода виртуальный диск с операционной системой, библиотеками которые вы установили на шаге 1 и иногда командой для запуска сервиса. Кстати образ не всегда содержит сервис и может быть просто базовой операционной системой
  3. Но для нас образ по сути является упакованным сервисом вместе со всеми необходимыми зависимостями, который можно передать другому программисту. Либо выложить на специальный репозиторий для докер образов https://hub.docker.com
  4. Далее образ используется для создания копии сервиса. И запуска его уже в виде контейнера. Контейнер — это копия образа подтюненная под ваш проект.

В жизни, как правило, берут какой-то базовый докер-образ и расширяют под свои нужды.

Собственно, и попробуем создать свой образ и запихать в него наш сервис.

Создаем Dockerfile и собираем образ

И так создадим файлик Dockerfile, я прям в файле с проектом сделаю:

Первая строчка в этом файле должна быть строкой которая описывает базовый образ для нашего сервиса. Мы разрабатывали наш сервис на ubuntu так что нам потребуется такая строка:

FROM ubuntu:20.04

Кстати даже если у вас archlinux, то все равно можно смело писать ubuntu. Докер использует только ядро от основной системы (кстати, именно поэтому он без костылей работает только на linux), а так как ядро у убунты и арча плюс-минус одинаковые, то все должно успешно работать

У команды FROM сначала идет название образа. А затем используемая версия. Полный спиок доступных версий можно глняуть тут для ubuntu https://hub.docker.com/_/ubuntu?tab=tags

теперь можно попробовать собрать образ. Пишем

docker build -t my_daemon_image .

где my_daemon_image это имя, которые мы даем нашему образу, чтобы потом на основании его запускать контейнеры, а точка на конце означает, что использовать Dockerfile из текущей папки

увидим что-то такое

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

sudo groupadd docker # создаем группу docker
sudo usermod -aG docker $USER # добавляем нашего юзера в эту группу
newgrp docker # перезагружаем разрешения групп
sudo chown "$USER":"$USER" /home/"$USER"/.docker -R  # настраиваем разрешения файлов .docker
sudo chmod g+rwx "$HOME/.docker" -R

он может сказать, что группа docker уже существует, просто проигнорируйте это сообщение.

И так пробуем по новой создать наш образ. Пишем:

docker build -t my_daemon_image .

Увидим такое сообщение:

тут написано, что docker стянул образ ubuntы и создал на базе его наш образ my_daemon_image

Запускаем контейнер

Теперь можем попробовать запустить на базе этого образа контейнер. Пишем

docker run my_daemon_image

пока, если запустить, то ничего не произойдет. То есть как будто бы наша команда сработала вхолостую. Дело в том, что наш образ по сути просто копия ubuntы. Он запускается. Так как команда для запуска не указана он собственно сразу прекращает работу.

Вообще конечно что-то произошло. Как минимум, был создан контейнер (микро-копия нашего образа), и докер его даже запустил. Мы можем посмотреть эту следующим образом

docker container list -a

я дважды запускал команду docker run и у меня создалось два контейнера на базе образа my_daemon_image.

Вообще docker любит сжирать много место. И поэтому бывает полезным чистить не используемые контейнеры. Для этого есть команда

docker container rm <CONTAINER_ID>

Команда CMD

Добавим в наш Dockerfile команду

FROM ubuntu:20.04 

CMD ["echo", "Привет всем"]

CMD – это одна из главных команд docker файла, в которую прописывается команда, которая должна запустится при запуске контейнера. Пересоберем образ

docker build -t my_daemon_image .

Теперь запустим образ по новой:

ну вроде вывел, но чет не сильно много действия для простого echo.

Давайте попробуем запихать туда нашего демона. Одно из преимуществ докер контейнеров заключается в том, что вам не надо создавать service файл. По сути ваш Dockerfile это уже своего рода описание вашего сервиса.

Посмотрим где наш файлик лежит, у меня он находится тут и называется daemon:

попробуем вместо команды echo запустить демона.

Кстати я устал билдить и запускать вручную. Поэтому сделаю Makefile

# Makefile
all:
        docker build -t my_daemon_image .
        docker run my_daemon_image

и теперь могу запускать все команды разом путем вызова команды make

Теперь правим Dockerfile

FROM ubuntu:20.04 

CMD ["bin/Debug/net6.0/daemon"]

запускаю:

говорит файл не найден… может надо полный путь прописать

и полный путь не помогает…

Так в чем же дело?

Команда COPY

А дело в том, что docker запускается в изолированной среде, а это значит, что для того чтобы файл запустился, его надо сначала скопировать внутрь образа. А потом оттуда уже запустить.

FROM ubuntu:20.04

# скопировал файл bin/Debug/net6.0/daemon внутрь образа в папку /opt
COPY bin/Debug/net6.0/daemon /opt/daemon 

CMD ["/opt/daemon"]

запускаем:

о, уже интереснее. Правда все равно не запустилось. Говорит надо файл daemon.dll.

Давайте скопируем папку целиком:

FROM ubuntu:20.04

COPY bin/Debug/net6.0/ /opt/

CMD ["/opt/daemon"]

запускаем:

о, попытался запустится, но написал, что .NET не найден. Так вроде ж ставили .net…

Так я ж говорил, что докер работает изолированно.

Он просто использует ядро основной системы, доступа к основным файлам у него все равно нет. А значит нам надо поставить внутрь образа .net

Но как это сделать, ведь CMD используется под основную команду. А если копировать через COPY этож надо знать какие файлы.

Команда RUN

К счастью в dockerfile есть еще одна команда RUN с помощью которой можно вызывать команды внутри образа. А результаты работы этих команд будут зафиксированы в образе навечно.

В общем с помощью RUN и запускают команды для установки пакетов, их в отличии от CMD может быть сколь угодно много. Я скопирую команды отсюда https://docs.microsoft.com/ru-ru/dotnet/core/install/linux-ubuntu#2004- и добавлю к ним префикс RUN. Ну и уберу sudo, так как команды внутри образа запускаются под суперюзером

FROM ubuntu:20.04

RUN wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
RUN dpkg -i packages-microsoft-prod.deb
RUN rm packages-microsoft-prod.deb

RUN apt-get update; \
  apt-get install -y apt-transport-https && \
  apt-get update && \
  apt-get install -y aspnetcore-runtime-6.0

COPY bin/Debug/net6.0/ /opt/

CMD ["/opt/daemon"]

пробуем запустить:

у меня не нашел wget, давайте добавим команду по его установке

FROM ubuntu:20.04

RUN apt update && apt install -y wget # ДОБАВИЛ
RUN wget https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb
# …

Если вы испольузете filget или tree или cowsay не забудьте их добавить.

и так после достаточно длительного ожидания, я таки дождался вот такого результат

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

Особенности обновления внутренних файлов

Если подправить нашего демона и пересобрать через dotnet build, затем запустить наш make то образ на самом деле не пересоберется.

Видите после каждого шага Step */* он пишет Using cache.

Это потому что на каждую команду докер создает промежуточный образ. С одной стороны, это удобно, при пересборе не будут выполняться все команды с нуля, а с другой стороны это тратит лишнее место на диске, что при использовании больших образов может быстро забивать место.

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

Можно конечно скопируовать эти цифры

глянуть какие образы у нас уже есть в системе:

docker image list -a

как видим наш образ my_daemon_image весит 248МБ, а еще там есть промежуточные образы которые весят столько же. Можно попробовать удалить образ соответствующий команде COPY

правда он не даст нам удалить образ, так как от него есть зависимые. В моем случае мне надо еще удалить образ fd8552a13f13 которые соответствует my_daemon_image.

В общем это достаточно неудобно. Короче, если хотите сбросить кеш команды просто поменяйте ее немного.

И тогда, если пересобрать и запустить, увидим такое. Все работает =)

Подключаемся к контейнеру

попробуем подключится

красота! =)

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

можно глянуть инфу обо всех контейнерах которые мы запускали

docker container list -a

так как вы их скорее всего не планируете использовать, имеет смысл их удалить. Пишем

docker container prune

ну вот, 35МБ освободили))

А теперь давайте остановим запущенный контейнер. Узнаем его имя:

и остановим

во время остановки, главному процессу контейнера (который в CMD) посылается сигнал SIGTERM.

А теперь давайте обратно запустим его

docker start interesting_tharp 

контейнер автоматически запустится в режиме демона. То есть он будет работать вне текущей консоли. Но можно будет к нему так же успешно подключатся через

socat TCP:172.17.0.2:8080 -

кстати как и с journalctl в докере можно посмотреть лог контейнера

docker logs interesting_tharp -f

Попробуйте теперь сделать задание

5.1

Попробуйте собрать свой образ для вашего демона используя официальный образ от microsoft

https://hub.docker.com/_/microsoft-dotnet-runtime/