Установка WSL 2
Сначала включим компонент Windows Subsystem for Linux (WSL). Для этого запустим PowerShell с правами администратора и выполним первую команду
Выполним следующую команду
Чтобы завершить установку, перезагрузим компьютер
Установим пакет обновления ядра Linux
Выберем WSL 2 по умолчанию для новых дистрибутивов Linux
Для целей этой статьи это необязательно, но установим дистрибутив Linux через Microsoft Store, например, Ubuntu 20.04 LTS
При первом запуске установленного дистрибутива введем имя пользователя и пароль
Чтобы увидеть запущенные дистрибутивы Linux, выполним в PowerShell команду
Чтобы завершить работу дистрибутива Linux, выполним команду
Файловая система запущенного дистрибутива Linux будет смонтирована по этому пути
За пределами сборки образов
До сих пор мы сосредоточились на процессе создания образа и обсудили советы по созданию оптимальных файлов Docker. Но давайте не будем забывать о некоторых дополнительных предварительных проверках и о том, что происходит после создания образа: его запуск.
16. Docker port socket and TCP protection
Докер-сокет — это большая привилегированная дверь в вашу хост-систему, которая, как недавно было замечено, может использоваться для вторжений и использования вредоносного программного обеспечения. Убедитесь, что ваш /var/run/docker.sock имеет правильные разрешения, и если докер доступен через TCP (что вообще не рекомендуется), убедитесь, что он должным образом защищен.
17. Цифровая подпись образов
Использование Docker Content Trust, Docker notary, Harbour notary или аналогичные инструменты для цифровой подписи ваших образов и последующей проверки их во время выполнения — одна из лучших практик Dockerfile.
Включение проверки подписи отличается в каждой среде выполнения. Например, в докере это делается с помощью переменной окружения DOCKER_CONTENT_TRUST: экспорт DOCKER_CONTENT_TRUST = 1
18. Изменение тэгов
Теги являются непостоянной ссылкой на конкретную версию изображения в определенный момент времени и могут измениться неожиданно и в любой момент.
19. Запуск как non-root
Ранее мы говорили об использовании пользователя без полномочий root при создании контейнера. Инструкция USER установит пользователя по умолчанию для контейнера, но за оркестратором или средой выполнения (например, docker run, kubernetes и т. д.) Остается последнее слово в том, кто является пользователем запущенного контейнера.
Избегайте запуска вашей среды от имени пользователя root.
Openshift и некоторые кластеры Kubernetes по умолчанию будут применять ограничительные политики, предотвращая запуск root контейнеров. Избегайте соблазна работать с правами root, чтобы обойти проблемы с разрешениями или владением, и вместо этого устраните реальную проблему.
20. Включить проверки
При использовании Docker или Docker Swarm по возможности включайте инструкцию HEALTHCHECK в свой Dockerfile
Это критически важно для длительно работающих или постоянных служб, чтобы гарантировать их работоспособность и управлять перезапуском службы в противном случае
Если вы запускаете образы в Kubernetes, используйте конфигурацию livenessProbe внутри определений контейнеров, поскольку инструкция Docker HEALTHCHECK не будет применяться.
Знакомство с Ansible
Недавно (тройку месяцев назад), я поработал с DevOps командой, почти каждый участник которой, негативно относился к docker. По причинам:
- docker правит iptables (хотя можно отключить в daemon.json)
- docker бажный и в проде запускать его не будем
- если docker daemon падает, то соответственно, падают все контейнеры с инфрастуктурой
- в docker нет необходимости
- зачем docker если, есть Ansible и виртуальные машины
На той же работе я и познакомился с еще одним инструментом — Ansible. Когда-то я слышал о нем, но не пробовал писать свои плейбуки. А теперь я начал писать свои таски и тут мое видение поменялось окончательно! Потому что я понял: у Ansible есть модули для запуска тех же docker контейнеров, сборок образов, сетей и пр., при этом контейнеры можно запустить не только локально, но и на удаленных серверах! Моему восторгу не было предела — я нашел НОРМАЛЬНЫЙ инструмент и выбросил свои Makefile и docker-compose файлы, они были заменены на yaml таски. Был уменьшен код за счет использования конструкций типа , , etc.
Прочие команды
Это общие операции, не привязанные к работе с конкретными сущностями типа образов и контейнеров. Но они тоже пригодятся при использовании Docker.
- docker version — показывает техническую информацию о самом Docker. Как о клиенте, так и о сервере.
- docker login — авторизует пользователя в реестре Docker.
- docker system prune — выполняет некую чистку, удалив из системы контейнеры, которые уже не используются, ненужные сети и образы без имен и меток.
version
К этой команде можно обратиться за подробной информацией о самом Docker. Она отобразит версию клиента и сервера Docker.
login
Об этой команде уже говорили. Она позволяет авторизоваться в реестре образов Docker Hub. Загружать туда образы и выгружать уже готовые к себе.
system prune
По умолчанию эта команда удаляет только данные без тегов, но можно настроить ее так, чтобы из системы были удалены все неиспользуемые элементы.
Допустим, чтобы стереть из Docker все образы и сверху зацепить еще тома, то надо ввести в терминал:
docker system prune –all –volumes
На этом все. Это даже больше того минимума, который нужен для работы с Docker на начальных этапах. Этих команд хватит, чтобы собрать образ, запустить контейнер и перейти к разработке. А уже по ходу дела вы сможете обращаться в документацию и обогащать свои знания.
Шаг 8 — Загрузка образов Docker в репозиторий Docker
Следующим логическим шагом после создания нового образа из существующего является предоставление доступа к этому образу нескольким вашим друзьям или всему миру на Docker Hub или в другом реестре Docker, к которому вы имели доступ. Чтобы добавить образ на Docker Hub или любой другой реестр Docker, у вас должна быть там учетная запись.
Данный раздел посвящен добавлению образа Docker на Docker Hub. Чтобы узнать, как создать свой собственный частный реестр Docker, ознакомьтесь со статьей Настройка частного реестра Docker на Ubuntu 14.04.
Чтобы загрузить свой образ, выполните вход в Docker Hub.
Вам будет предложено использовать для аутентификации пароль Docker Hub. Если вы указали правильный пароль, аутентификация должна быть выполнена успешно.
Примечание. Если ваше имя пользователя в реестре Docker отличается от локального имени пользователя, которое вы использовали при создании образа, вам потребуется пометить ваш образ именем пользователя в реестре. Для примера, приведенного на последнем шаге, вам необходимо ввести следующую команду:
Затем вы можете загрузить свой образ с помощью следующей команды:
Чтобы загрузить образ в репозиторий sammy, необходимо использовать следующую команду:
Данный процесс может занять некоторое время, необходимое на загрузку образов, но после завершения вывод будет выглядеть следующим образом:
После добавления образа в реестр он должен отображаться в панели вашей учетной записи, как на изображении ниже.
Если при попытке добавления возникает подобная ошибка, вы, скорее всего, не выполнили вход:
Выполните вход с помощью команды и повторите попытку загрузки. Проверьте, появился ли образ на вашей странице репозитория Docker Hub.
Теперь вы можете использовать , чтобы загрузить образ на новый компьютер и использовать его для запуска нового контейнера.
Загрузка образа на Docker Hub
Заходим на Docker Hub страницу регистрации. Создаем пользователя:
Переходим на страницу Repositories и создаем свой репозиторий, например, dmosk. Теперь можно загрузить наши образы в репозиторий.
Сначала авторизуемся в Linux под нашим зарегистрированным пользователем:
docker login —username dmosk
Задаем тег для одного из образов и загружаем его в репозиторий:
docker tag centos:my dmosk/dmosk:centos
docker push dmosk/dmosk:centos
Загрузим второй образ:
docker tag dmosk/nginx:v1 dmosk/dmosk:nginx
docker push dmosk/dmosk:nginx
В Docker Hub должны появиться наш образы:
Чтобы воспользоваться образом на другом компьютере, также авторизуемся под зарегистрированным пользователем docker:
docker login —username dmosk
Загружаем образ:
docker pull dmosk/dmosk:nginx
Запускаем его:
docker run -d -p 8080:80 dmosk/dmosk:nginx
Кастомизация сборки
По умолчанию mxe предоставляет MinGW версии 5.5.0 (по крайней мере это справедливо для сборки build-2019-06-02).
Если в проекте используются новые фичи С++ 17, то такая версия компилятора неудовлетворительна. К счастью, среда сборки предоставляет более новые версии в виде отдельных плагинов. Для решения нашей задачи, необходимо в команду сборки библиотек добавить инструкцию по использованию соответствующего плагина:
Данная команда создаст комплект для статической сборки 64-битных приложений с использованием компилятора седьмой версии (7.4.0). Если такой комплект уже существует, то он изменен не будет.
Со списком всех доступных плагинов можно ознакомиться на странице.
В директории mxe/src содержатся *.mk файлы, которые описывают параметры сборки того или иного пакета. При необходимости можно внести требуемые коррективы в уже существующий пакет или добавить свой собственный. Структура файлов описана вот тут — https://github.com/mxe/mxe/wiki/Add-a-New-Package:-Full-Version.
Для копирования pзависимостей проект mxe предоставляет утилиту copydlldeps.sh. Но это не единственный полезный инструмент, с их олным списокм можно ознакомиться на странице.
Код приложения
В открывшемся проекте рядом с главным классом, содержащим метод main, создаём ещё один класс — контроллер с методом hello ().
Класс помечен аннотацией @RestController, означающей, что он предназначен для обработки web-запросов. А метод помечен аннотацией @GetMapping c адресом «/» — перейдя по нему (выполнив get-запрос к http://localhost:8080/), мы получим сообщение «Hello Docker!»
Теперь открываем терминал и вводим команду:
Она упакует приложение в jar-файл и запустит его. Чтобы убедиться в корректности работы приложения — перейдём на http://localhost:8080/ в браузере и увидим заветное сообщение.
Теперь создаём файл с именем Dockerfile в корне проекта, который содержит инструкции для сборки образа со следующим текстом:
Вот что происходит, когда мы вводим этот код:
Команда | Описание |
---|---|
FROM adoptopenjdk/openjdk11:alpine-jre | Oбраз создаётся на основе alpine linux с установленной openjdk11 |
ARG JAR_FILE=target/spring-docker-simple-0.0.1-SNAPSHOT.jar | Переменной JAR_FILE присваивается путь к jar- архиву |
WORKDIR /opt/app | Назначаем рабочую директорию, в которой будут выполняться дальнейшие команды (перемещаемся в папку app) |
COPY ${JAR_FILE} app.jar | Наш jar-файл, указанный в JAR_FILE, копируется в папку app, и копии задаётся имя app.jar |
ENTRYPOINT | jar-файл запускается, собирается команда java -jar app.jar из заданной рабочей директории |
После этого в терминале вводим команду, с помощью которой собираем образ и запускаем контейнер.
Точка в конце важна, она указывает на расположение Dockerfile (символ «точка» означает текущую директорию. Проверьте, что образ создан командой docker images. Вывод должен быть таким:
Запускаем контейнер командой:
Опция -d означает старт процесса в фоновом режиме. Опция -p тоже важна — дело в том, что контейнер собирается в полностью изолированном окружении. Тот факт, что приложение внутри контейнера запущено на порту 8080, не означает, что оно доступно вне контейнера на этом порту.
Требуется явно указать, что порту 8080 в контейнере (здесь второе значение — это порт, на котором работает наше приложение в контейнере) соответствует порт 8080 на локальной машине, который будет использоваться при обращении к контейнеру. Поэтому пишем через двоеточие -p 8080:8080.
Теперь введём в терминале команду:
Не обращайте внимания на шумиху и не верьте мифам
Docker-мифология появилась благодаря веским причинам. Но она не помогает разработчикам.
Если вы, прочитав эту статью, все еще сомневаетесь, прошу вас выделить немного времени и попробовать пересмотреть свое отношение к Docker.
Если у вас есть вопросы по поводу того, каким образом разработчик может эффективно использовать Docker, свяжитесь со мной. Буду рад постараться вам помочь.
При желании изучить основы Docker и методы разработки приложений с его использованием можно начать с просмотра Guide to Learning Docker (с нуля) и Guide to Building Node.js Apps in Docker на WatchMeCode.
Оригинальный текст представлен по адресу: derickbailey.com/2017/01/30/10-myths-about-docker-that-stop-developers-cold/ (отсутствие активной ссылки обусловлено тем, что автор оригинального текста перенаправляет на сторонние ресурсы всех посетителей, пришедших по активным ссылкам на других сайтах).
Избегать избыточных привилегий
Этот совет следует принципу наименьших привилегий, таким образом ваше приложение получает доступ только к тем ресурсам и данным, которые ему необходимы для работы.
1. Rootless контейнеры
Отчет sysdig показал, что 58% образов выполняют процесс в контейнере от root (UID 0). Рекомендуем избегать этого. Существует очень небольшой круг задач, для решения которых нужно запускать контейнер от root, поэтому не забывайте добавлять команду и менять UID пользователя на non-root.
Более того ваша среда выполнения контейнеров может по умолчанию блокировать запуск процессов в контейнере от имени root (например, Openshift требует дополнительные ).
Чтобы настроить non-root контейнер вам потребуется выполнить несколько дополнительных шагов в вашем Dockerfile.
-
Необходимо убедится, что пользователь, указанный в команде существует внутри контейнера.
-
Предоставить необходимые разрешения на объекты файловой системы, которые процесс читает или записывает.
Вы можете увидеть контейнеры, которые начинаются как root, а затем используют gosu или su-exec для перехода к обычному пользователю.
Если необходимо выполнить команду от имени root, вы можете использовать sudo.
Приведенные рекомендации, намного лучше запуска от имени root, но они работают не во всех окружениях, например OpenShift.
Что происходит, когда запускается контейнер?
Или с помощью программы , или с помощью RESTful API, docker клиент говорит docker демону запустить контейнер.
Давайте разберемся с этой командой. Клиент запускается с помощью команды , с опцией , которая говорит, что будет запущен новый контейнер. Минимальными требованиями для запуска контейнера являются следующие атрибуты:
- какой образ использовать для создания контейнера. В нашем случае
- команду которую вы хотите запустить когда контейнер будет запущен. В нашем случае
Что же происходит под капотом, когда мы запускаем эту команду?
Docker, по порядку, делает следующее:
- скачивает образ ubuntu: docker проверяет наличие образа на локальной машине, и если его нет — то скачивает его с Docker Hub. Если же образ есть, то использует его для создания контейнера;
- создает контейнер: когда образ получен, docker использует его для создания контейнера;
- инициализирует файловую систему и монтирует read-only уровень: контейнер создан в файловой системе и read-only уровень добавлен образ;
- инициализирует сеть/мост: создает сетевой интерфейс, который позволяет docker-у общаться хост машиной;
- Установка IP адреса: находит и задает адрес;
- Запускает указанный процесс: запускает ваше приложение;
- Обрабатывает и выдает вывод вашего приложения: подключается и логирует стандартный вход, вывод и поток ошибок вашего приложения, что бы вы могли отслеживать как работает ваше приложение.
Теперь у вас есть рабочий контейнер. Вы можете управлять своим контейнером, взаимодействовать с вашим приложением. Когда решите остановить приложение, удалите контейнер.
Заключение
В итоге мы создали два простеньких микросервиса, упаковали их в докер-контейнеры и совместно запустили на одной машине. У полученной системы, однако, есть ряд недостатков:
- Плохая отказоустойчивость — у нас все работает на одном сервере
- Плохая масштабируемость — при увеличении нагрузки было бы неплохо автоматически разворачивать дополнительные экземпляры сервисов и балансировать нагрузку между ними
- Сложность запуска — нам понадобилось ввести как минимум 3 команды, причем с определенными параметрами (это только для 2 сервисов)
Для устранения вышеперечисленных проблем существует ряд решений, таких как Docker Swarm, Nomad, Kubernetes или OpenShift. Если вся система будет написана на Java можно посмотреть в сторону Spring Cloud (хорошая статья).
В следующей части я расскажу про то, как я настраивал Kubernetes и деплоил проект в Google Kubernetes Engine.