Pull to refresh

Написание кода в docker окружении

Reading time4 min
Views23K

В компании, где я работаю — большинство сервисов запускаются и работают в docker-контейнерах.


В связи с этим, у моих коллег-новичков-в-докере часто возникает вопрос — а как писать код и запускать его в этом чёртовом контейнере???



Для человека, написавшего около сотни docker-образов и запускающего их несколько раз в день — такой вопрос уже не стоит, но когда я разбирался с докером в давние времена — мысль "Как же писать код в докере? Это же сверхнеудобно!" долго была актуальной.


В статье я опишу свои практики работы с образами docker, которые позволяют писать код "как у себя в home", и даже лучше.


Итак, что такое готовый docker-образ?


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


Запуск готового образа


Для начала разберём, как запускать образ. Предполагается, что название образа нам известно.


  • название может представлять собой имя образа на hub.docker.com: kaktuss/clickhouse-udp-proxy;
  • название может содержать в себе имя приватного docker registry (репозитория docker образов вашей компании): my-private-registry.com/kaktuss/clickhouse-udp-proxy;
  • в названии может содержаться версия образа: my-private-registry.com/kaktuss/clickhouse-udp-proxy:0.1.

И это всё — имя образа.


В простейшем случае образ запускается так:


docker run --rm -it kaktuss/clickhouse-udp-proxy

Часто образ нужно сконфигурировать переменными окружения. Откуда их брать?


  • нужные значения переменных окружения описаны на docker hub — если это публичный крупный поддерживаемый образ с docker hub;
  • в сценариях nomad, docker swarm, kubernetes, приватной документации — если это приватный образ вашей компании;
  • иногда, переменные нигде не описаны и о нужных значениях требуется догадываться по их названиям и просмотру docker-образа.

Примеры подсмотренных переменных окружения:


С docker hub



Из сценария nomad



Запускаем контейнер с переменными окружения


docker run --rm -it -e CLICKHOUSE_ADDR=127.0.0.1:9000 kaktuss/clickhouse-udp-proxy

Если переменных несколько


docker run --rm -it -e CONSUL_HTTP_ADDR="consul.query.consul:8500" -e VAULT_ADDR="http://vault.query.consul:8200/" -e DC_NAME="deac" -e SYS_NODE="b1" ...

В итоге, мы получили работающий и сконфигурированный сервис.


Попадаем внутрь контейнера


Для упрощения программирования "внутри" контейнера — нам нужно в него попасть. При описанном выше запуске — запускается сам сервис, но мы сами не попадаем "внутрь".


Для попадания "внутрь" — нужно переопределить команду старта образа.


Это делается указанием имени shell-оболочки после имени образа


docker run --rm -it -e CLICKHOUSE_ADDR=127.0.0.1:9000 kaktuss/clickhouse-udp-proxy ash

Shell-оболочка зависит от дистрибутива, на котором построен образ.


  • это может быть ash, как в примере выше;
  • bash;
  • или даже sh, в самом простом случае.

Попробуйте один из вариантов и не ошибётесь.


После подобного запуска мы оказываемся в консоли внутри контейнера.


Инициализация сервиса


После того, как мы попали в консоль внутри контейнера — мы попали в "голый" образ. Часто образы, после запуска, проводят первичную инициализацию (формируют файлы настроек и т.д.) и после этого запускают сам сервис.


Инициализация делается через команду старта, которую мы выше заменили на shell-оболочку. Поэтому, инициализацию нужно запустить вручную. Для этого, открываем Dockerfile и смотрим содержимое инструкции CMD.


CMD ["/usr/local/bin/entrypoint.sh"]

И именно его и запускаем.


/ # /usr/local/bin/entrypoint.sh

или короче


/ # entrypoint.sh

Сервис инициализировался и запустился, теперь мы можем нажать Ctrl+C и снова попасть в консоль, имея контейнер, готовый к повторному запуску сервиса.


Написание кода внутри контейнера


Когда сервис запускается внутри контейнера — он использует те скрипты/бинарные файлы, которые уже находятся внутри. Как нам их редактировать?


Элементарно. Нужно редактировать их извне, в любимом редакторе, в своей домашней папке, а потом просто скопировать в контейнер и запустить.


Даём доступ контейнеру к своей домашней папке используя опцию


-v ~/:/d

docker run --rm -it -e CLICKHOUSE_ADDR=127.0.0.1:9000 -v ~/:/d kaktuss/clickhouse-udp-proxy ash

После запуска данной команды мы будем находиться в консоли контейнера, сможем неограниченно редактировать свой код извне и копировать его в контейнер. Домашняя папка будет доступна в контейнере в папке /d.


/ # cp /d/my-repo/script.pl /usr/local/bin/script.pl

После копирования обновленного скрипта или бинарного файла — запускаем сервис (способом, описанным в разделе инициализации) и получаем сервис с нашими правками, работающий в исходном окружении.


В зависимости от ваших нужд — скрипты/файлы можно не копировать в контейнер, а запускать их сразу из /d/my-repo.


Граничные случаи и лайфхаки


ENTRYPOINT


Некоторые образы (довольно редко) используют команду старта в виде ENTRYPOINT. Что это такое — можно посмотреть в Dockerfile reference. Нам же нужно только помнить, что перезапись команды старта для таких образов выглядит иначе


docker run --rm -it -e CLICKHOUSE_ADDR=127.0.0.1:9000 -v ~/:/d --entrypoint ash kaktuss/clickhouse-udp-proxy

Точка старта переопределяется опцией --entrypoint.


"Облачные" окружения


Если сервисы работают в Consul, docker swarm, kubernetes окружении, то они могут использовать такие переменные окружения, которые доступны только контейнеру, запущенному в этом облаке и не будут доступны контейнеру, запущенному на компьютере разработчика.



В указанном примере используются "облачные" адреса в переменных CONSUL_HTTP_ADDR и VAULT_ADDR. В таких случаях вам нужно использовать внешние адреса данных сервисов.


Повторные запуски


Писать каждый раз полностью команду docker run — излишне. Всю команду старта с переменными удобно сохранить с sh файл. Который потом достаточно просто запускать.


Переиспользование переменных окружения


Если часть переменных окружения требуется для многих сервисов, то эти переменные можно вынести в отдельный файл и передавать контейнеру специальной опцией


docker run --env-file=~/my_docker_env

Запуск без sudo


В локальной разработке запускать контейнеры с sudo — утомительно. Для исправления — добавляем своего пользователя в группу docker. После этого, вместо


sudo docker run ....

можно писать просто


docker run
Tags:
Hubs:
Total votes 14: ↑10 and ↓4+6
Comments21

Articles