Распределенные системы. Паттерны проектирования - [13]

Шрифт
Интервал

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

К примеру, можно передать путь к сертификату и порт прило-жения в виде переменных среды следующим образом: docker run -e=PROXY_PORT=8080 \

-e=CERTIFICATE_PATH=/путь/к/сертификату.crt ... Сценарий в контейнере воспользуется значениями этих перемен-ных  для  формирования  конфигурационного  файла  nginx.conf ,  который  укажет  серверу,  где  искать  файл  сертификата  и  куда  переадресовывать запросы.

Определение API всех контейнеров Если  учитывать,  что  вы  параметризуете  свои  контейнеры,  будет  очевидно, что каждый из них определяет некую «функцию», кото-рая вызывается при запуске контейнера. Эта функция является ча-стью API, определяемого вашим контейнером, но у него есть и дру-гие  составляющие,  включая  вызовы  к  внешним  по  отношению  46 Часть I. Одноузловые паттерны проектирования

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

Чтобы  понять,  насколько  важно  уделять  внимание  API  кон-тейнера, рассмотрим упомянутый ранее контейнер-прицеп для  управления конфигурацией. Для него мог бы оказаться полез-ным параметр  UPDATE_FREQUENCY , задающий частоту, с которой  необходимо  синхронизировать  конфигурацию  с  файловой  си-стемой. Очевидно, что если название параметра потом поменя-ется  на,  скажем,  UPDATE_PERIOD ,  то  это  уже  будет  нарушением  интерфейса  контейнера  и  воспрепятствует  его  корректному  применению другими пользователями.

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

Со  временем,  с  учетом  обратной  связи  от  пользователей,  раз-работчик решил, что длительные интервалы (минуты, часы) не-удобно указывать в секундах. Он модифицировал параметр так,  Глава 2. Паттерн Sidecar 47

чтобы тот принимал строки (10 минут, 5 секунд и т. п.). Посколь-ку старые значения параметров не смогут быть обработаны но-вым контейнером, такое изменение API окажется критическим. Представьте,  что  разработчик  предусмотрел  этот  вариант,  но  сделал так, чтобы значения без единиц измерения интерпрети-ровались  как  число  миллисекунд.  Такое  изменение,  хотя  и  не  приводит к ошибкам, является недопустимым, поскольку при-водит к более частым проверкам конфигурации и, как следствие,  большей нагрузке на сервер.

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

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

Как и в случае с программными библиотеками, ключ к созданию  полезной вещи — объяснение, как ею пользоваться. Мало поль-зы в создании гибкого, надежного, модульного контейнера, если  никто  не  может  понять,  как  с  ним  работать.  К  сожалению,  на  сегодняшний день доступно не так много формальных инстру-ментов, позволяющих документировать образы контейнеров, но  есть несколько полезных приемов, упрощающих работу. 48 Часть I. Одноузловые паттерны проектирования

У каждого контейнера есть конфигурационный файл  Dockerfile ,  на основе которого строится образ контейнера. Именно в нем  в первую очередь стоит искать документацию к контейнеру.  Некоторые части  Dockerfile  документируют работу контей-нера сами по себе. Одним из примеров может служить дирек-тива  EXPOSE , в которой перечислены сетевые порты, открытые  в контейнере. Указывать ее не обязательно, но это считается  хорошим тоном. Неплохо также снабдить ее комментарием,  поясняющим, какой конкретно сервис прослушивает данный  порт. Например: