Глава 11. Контейнеры/ Микрослужбы

Введение

Контейнеры предлагают некий уровень абстракции на прикладном уровне, сдвигая необходимый процесс установки пакетов и зависимостей с процесса развёртывания в процесс сборки. Это важно по той причине, что инженеры теперь снаряжают элементы кода, которые исполняются и снабжаются неким единообразным способом вне зависимости от имеющейся среды. Предложение контейнеров как исполняемых элементов снижает значение риска путаницы зависимостей и настроек между средами. Принимая это во внимание, это придаёт организациям большой импульс для развёртывания своих приложений в платформах контейнеров. При запуске приложения в некоторой платформе контейнера, является широкой практикой размещать в контейнерах всё что позволяет имеющийся стек, включая вашего посредника и балансировщика нагрузки. NGINX и NGINX Plus запросто помещаются в контейнер и доставляются ими. Они также содержат множество функций, которые превращают доставку приложений в контейнерах более подвижной. Данная глава сосредоточена на сборке образов контейнеров NGINX и NGINX Plus, свойства, которые делают более простой работу в средах с контейнерами, а также развёртывание ваших образов в Kubernetes и OpenShift.

Записи DNS SRV

Задача

Вы бы хотели применять свою имеющуюся реализацию записей DNS SRV в качестве своего источника для серверов восходящего потока с помощью NGINX Plus.

Решение

Определите соответствующую директиву службы со значением http некого сервера восходящего потока чтобы указать NGINX на необходимость применения имеющихся записей SRV в качестве некого пула балансировки нагрузки:


http {
    resolver 10.0.0.2;

    upstream backend {
        zone backends 64k;
        server api.example.internal service=http resolve;
    }
}
 	   

Данная функциональность присутствует исключительно в NGINX Plus. Данная настройка инструктирует NGINX Plus выполнять разрешение DNS с некого сервера DNS 10.0.0.2 и настраивать некий пул серверов восходящего потока в отдельной директиве server. Эта директива server определяется с параметром resolve и указывает на необходимости периодического повторного разрешения данного имени домена. Сам параметр service=http и значение сообщают NGINX что это некая запись SRV, которая содержит список IP и портов и балансировку нагрузки по ним если они были настроены при помощи директивы server.

Обсуждение

Динамичная инфраструктура становится всё более популярной благодаря спросу и внедрению инфраструктур на облачной основе. Среды с автоматическим шкалированием масштабируются горизонтально, увеличивая или уменьшая общее число серверов в имеющемся пуле для соответствия текущей нагрузке. Горизонтальное масштабирование требует некой балансировки нагрузки, которая способна добавлять или удалять ресурсы из имеющегося пула. Обладая некой записью SRV вы снимаете с себя ответственность за отслеживание текущего списка серверов и передаёте её DNS. Такой тип настройки чрезвычайно заманчив для сред с контейнерами, так как вы можете иметь контейнеры с изменяемыми номерами портов, причём возможно даже с одним и тем же адресом IP. Важно также отметить, что полезная нагрузка записи UDP DNS ограничена примерно 512 байтами.

Применение официального образа NGINX

Задача

Вам требуется быстро поднять и запустить образ NGINX из Docker Hub.

Решение

Воспользуйтесь имеющимся в Docker Hub образом NGINX. Этот образ содержит некие установленные по умолчанию настройки. Вам необходимо либо смонтировать некий локальный каталог настроек, либо создать какой- то Dockerfile и ADD в своих настройках для сборки требуемого образа с изменённой конфигурацией. В нашем случае мы монтируем некий том, в котором настройки по умолчанию NGINX обслуживают статическое модерживое чтобы продемонстрировать его возможности при помощи некой отдельной команды:


$ docker run --name my-nginx -p 80:80 \
    -v /path/to/content:/usr/share/nginx/html:ro -d nginx
		

Данная команда docker вытаскивает необходимый образ nginx:latest с Docker Hub если не обнаруживает его локально. Эта команда затем запускает данный образ NGINX как некий контейнер Docker, устанавливая соответствие localhost:80 порту 80 самого контейнера NGINX. Она также монтирует имеющийся локальный каталог /path/to/content/ в качестве некого тома контейнера в /usr/share/nginx/html/ с доступом только для чтения. Установленная по умолчанию конфигурация NGINX будет обслуживать этот каталог в качестве статического содержимого. При определении соответствия от вашей локальной машины в некий контейнер, первым идёт значение порта или каталога локальной машины, а порт или каталог самого контейнера идут вторыми.

Обсуждение

NGINX сделал некий официальный образ Docker, доступный через Docker Hub. Этот официальный образ Docker превращает в очень быструю процедуру получения и запуска NGINX в Docker для предпочитаемой вами платформы. В данном разделе мы имели возможность заполучить поднятым и запущенным NGINX в контейнере при помощи одной единственной команды! Официальная основная линия образа Docker NGINX, которой мы и воспользовались в данном примере состоит в построении образа Docker Debian Jessie. Однако вы можете также выбирать официальные образы, собранные на Alpine Linux. Dockerfile и исходные коды для этих официальных образов доступны в GitHub. Вы можете расширить данный официальный образ построив свой собственный Dockerfile и определив свой официальный образ в соответствующей команде FROM.

Также ознакомьтесь

  • Official NGINX Docker image, NGINX

  • Docker repo on GitHub

  • {Прим. пер.: наш перевод вышедшей в феврале 2019 книги Docker для разработчиков Rails Роба Айзенберга. Вас не должен смущать Rails, даже если вы с ним не знакомы. Он скорее служит наполнителем для предмета книги с целью демонстрации приёмов и методов работы с типичным стеком приложений Docker.}

Создание Dockerfile NGINX

Задача

Для сборки некого образа Docker вам требуется создать некий Dockerfile NGINX.

Решение

Начните с FROM для предпочитаемого вами дистрибутива образа Docker. Для установки NGINX воспользуйтесь командой RUN. Для добавления своих файлов настроек примените команду ADD. Чтобы указать Docker выставить заданные порты прибегните к помощи команды EXPOSE или сделайте это вручную при запуске своего образа в качестве контейнера. После того как ваш образ создаст экземпляр контейнера, для запуска NGINX воспользуйтесь CMD. Вам требуется запустить NGINX в фоновом режиме. Для этого вам понадобится запустить NGINX при помощи -g "daemon off;" или daemon off; в ваших настройках. Данный пример будет позднее использовать daemon off; в соответствующем файле настроек внутри своего основного контекста. Вы также пожелаете изменить свои настройки NGINX чтобы выполнять регистрацию для доступа к журналу в /dev/stdout и на /dev/stderr для журнала ошибок; сделав это вы поместите свои журналы в руки самого демона Docker, что сделает их более доступными для вас на основе драйвера журналов, который вы выбрали при помощи Docker:


Dockerfile:
FROM centos:7

# Install epel repo to get nginx and install nginx
RUN yum -y install epel-release && \
    yum -y install nginx

# add local configuration files into the image
ADD /nginx-conf /etc/nginx

EXPOSE 80 443

CMD ["nginx"]
 	   

Получаемая структура каталогов выглядит так:


.
├── Dockerfile
└── nginx-conf
    ├── conf.d
    │   └── default.conf
    ├── fastcgi.conf
    ├── fastcgi_params
    ├── koi-utf
    ├── koi-win
    ├── mime.types
    ├── nginx.conf
    ├── scgi_params
    ├── uwsgi_params
    └── win-utf
 	   

Я выбираю размещение настроек NGINX целиком внутри данного каталога Docker для простоты доступа ко всем его настройкам при помощи всего одной строки в соответствующем Dockerfile чтобы добавлять все свои настройки NGINX.

Обсуждение

Вы можете найти полезным создание своего собственного Dockerfile когда вам требуется полный контроль над необходимыми установкой пакетов и их обновлением. Распространено поддержание своего собственного репозитория образов с тем, чтобы вы знали что ваш базовый образ надёжен и проверен вашей командой прежде чем запуксать его в промышленное применение.

Сборка NGINX Plus

Задача

Вам требуется собрать некий образ Docker NGINX Plus для запуска NGINX Plus в среде с контейнерами.

Решение

Для построения образа Docker NGINX Plus воспользуйтесь этим Dockerfile. Вам потребуется выгрузить свои сертификаты репозитория NGINX Plus и держать их в том же каталоге, что и данный Dockerfile под именами nginx-repo.crt и nginx-repo.key, соответственно. С их помощью данный Dockerfile выполнит всю остающуюся работу по установке для вас NGINX Plus применяя и присоединяя журналы регистрации доступа и ошибок NGINX в имеющемся коллекторе журналов Docker.


FROM debian:stretch-slim

LABEL maintainer="NGINX <docker-maint@nginx.com>"

# Download certificate and key from the customer portal
# (https://cs.nginx.com) and copy to the build context

COPY nginx-repo.crt /etc/ssl/nginx/
COPY nginx-repo.key /etc/ssl/nginx/

# Install NGINX Plus
RUN set -x \
  && APT_PKG="Acquire::https::plus-pkgs.nginx.com::" \
  && REPO_URL="https://plus-pkgs.nginx.com/debian" \
  && apt-get update && apt-get upgrade -y \
  && apt-get install \
    --no-install-recommends --no-install-suggests\
    -y apt-transport-https ca-certificates gnupg1 \
  && \
  NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62;\
  found=''; \
  for server in \
    ha.pool.sks-keyservers.net \
    hkp://keyserver.ubuntu.com:80 \
    hkp://p80.pool.sks-keyservers.net:80 \
    pgp.mit.edu \
  ; do \
    echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
    apt-key adv --keyserver "$server" --keyserver-options \
      timeout=10 --recv-keys "$NGINX_GPGKEY" \
    && found=yes \
    && break;\
  done;\
  test -z "$found" && echo >&2 \
    "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
  echo "${APT_PKG}Verify-Peer "true";"\
    >> /etc/apt/apt.conf.d/90nginx \
  && echo \
    "${APT_PKG}Verify-Host "true";">>\
    /etc/apt/apt.conf.d/90nginx \
  && echo "${APT_PKG}SslCert \
    "/etc/ssl/nginx/nginx-repo.crt";" >> \
    /etc/apt/apt.conf.d/90nginx \
  && echo "${APT_PKG}SslKey \
    "/etc/ssl/nginx/nginx-repo.key";" >> \
      /etc/apt/apt.conf.d/90nginx \
  && printf \
    "deb ${REPO_URL} stretch nginx-plus" \
    > /etc/apt/sources.list.d/nginx-plus.list \
  && apt-get update && apt-get install -y nginx-plus \
  && apt-get remove --purge --auto-remove -y gnupg1 \
  && rm -rf /var/lib/apt/lists/*

# Forward request logs to Docker log collector
RUN ln -sf /dev/stdout /var/log/nginx/access.log \
  && ln -sf /dev/stderr /var/log/nginx/error.log

EXPOSE 80
STOPSIGNAL SIGTERM

CMD ["nginx", "-g", "daemon off;"]
 	   

Для сборки этим Dockerfile некого образа Docker, исполните приводимое ниже в том каталоге, который содержит данный Dockerfile, а также ваши сертификат и ключ репозитория NGINX Plus:


$ docker build --no-cache -t nginxplus .
		

Данная команда docker build применяет флаг --no-cache чтобы обеспечить то, что вне зависимости от того что вы собирали ранее, из репозитория NGINX Plus были вытянуты для обновления свежие пакеты NGINX Plus. Если для вас будет допустимо применение той же самой версии NGINX Plus, которую мы собирали ранее, вы можете опустить данный флаг --no-cache. В этом примере наш новый образ Docker снабжается тегом nginxplus.

Обсуждение

Создавая для NGINX Plus свой собственный образ, вы получаете возможность настраивать свой контейнер NGINX Plus так, как считаете нужным и переносить его в любую среду Docker. Это вскрывает всю мощность и расширенную функциональность NGINX Plus для вашей среды контейнеров. Данный Dockerfile не использует имеющееся свойство ADD Dockerfile для добавления в ваши настройки; вам придётся добавлять свои установки вручную.

Также ознакомьтесь

NGINX blog on Docker images

Применение переменных среды NGINX

Задача

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

Решение

Для установки в NGINX переменных из своей среды воспользуйтесь модулем ngx_http_perl_module:


daemon off;
env APP_DNS;
include /usr/share/nginx/modules/*.conf;
...
http {
  perl_set $upstream_app 'sub { return $ENV{"APP_DNS"}; }';
  server {
    ...
    location / {
      proxy_pass https://$upstream_app;
    }
  }
}
 	   

Для применения perl_set вам требуется иметь установленным ngx_http_perl_module; вы можете сделать это загружая данный модуль динамически или статически при построении из исходного кода. По умолчанию NGINX стирает переменные среды из своего окружения: вам требуется объявлять все переменные, которые вы не желаете удалять, в директиве env. Сама директива perl_set получает два параметра: имя той переменной, которую вы бы хотели установить т строку perl, которая вычисляет необходимый результат.

Приводимый ниже Dockerfile, который динамически загружает ngx_http_perl_module, устанавливает этот модуль из своей утилиты управления пакетами. При установке модулей из соответствующей утилиты пакета для CentOS они помещаются в каталог /usr/lib64/nginx/modules/, а файлы настройки, которые динамически загружают эти модули помещаются в каталог /usr/share/nginx/modules/. Именно по этой причине в предыдущем фрагменте кода мы пометили все файлы настройки в такой путь:


FROM centos:7

# Install epel repo to get nginx and install nginx
RUN yum -y install epel-release && \
yum -y install nginx nginx-mod-http-perl

# add local configuration files into the image
ADD /nginx-conf /etc/nginx

EXPOSE 80 443

CMD ["nginx"]
		

Обсуждение

Обычной практикой при использовании Docker является применение переменных окружения для изменения того порядка, в котором работают контейнеры. В своей конфигурации NGINX вы можете применять переменные среды с тем чтобы ваш Dockerfile NGINX мог быть использован во многих разнообразных средах.

Контроллер Ingress Kubernetes

Задача

Вы развёртываете своё приложение в Kubernetes и вам требуется контроллер ingress.

Решение

Убедитесь что у вас имеется доступ к необходимому образу контроллера ingress. Для NGINX вы можете применять образ nginx/nginx-ingress из Docker hub. Для NGINX Plus вам потребуется построить свой собственный образ и разместить его в своём собственном частном реесте Docker. Вы можете найти инструкции по сборке и активной доставке своего собственного контроллера Kubernetes NGINX Plus Ingress в NGINX Inc’s GitHub.

Посетите папку Kubernetes Ingress Controller Deployments в репозитории kubernetes-ingress GitHub. Все команды, которым вы будете следовать, будут запускаться из данного каталога в некой локальной копии этого репозитория.

Создайте пространство имён и учётную запись службы для своего контроллера ingress; они оба имеют название nginx-ingress:


$ kubectl apply -f common/ns-and-sa.yaml
		

Создайте некий ключ безопасности с помощью сертификата TLS и ключ для своего контроллера ingress:


$ kubectl apply -f common/default-server-secret.yaml
		

Эти сертификат и ключ самостоятельно подписываются и создаются NGINX Inc. для целей проверок и примеров. Рекомендуется применять свой собственный, так как данный ключ имеет общий открытый доступ.

В качестве варианта вы можете создать некое соответствие настроек для индивидуальной конфигурации NGINX (такое соответстиве настроек предоставляется пустым; но вы ознакомиться с подробностями индивидуализации и комментариями здесь):


$ kubectl apply -f common/nginx-config.yaml
		

Если в вашем кластере включён RBAC (Role-Based Access Control), создайте некую роль кластера и свяжите ей с соответствующей учётной записью этой службы. Для выполнения данного шага вам следует быть администратором кластера:


$ kubectl apply -f rbac/rbac.yaml
		

Теперь разверните свой контроллер ingress. В данном репозитории сделаны доступными два примера оснащений: Deployment и DaemonSet. Если вы планируете динамически изменять общее число реплик контроллера ingress, применяйте Deployment. Для развёртывания контроллера ingress во всех узлах или в неком подмножестве узлов воспользуйтесь DaemonSet.

Если вы намерены применять манифесты Deployment NGINX Plus, вам следует исправить соответствующий файл YAML и определить свои собственные реестр и образ.

Для Deployment NGINX:


$ kubectl apply -f deployment/nginx-ingress.yaml
		

Для Deployment NGINX Plus:


$ kubectl apply -f deployment/nginx-plus-ingress.yaml
		

Для DaemonSet NGINX:


$ kubectl apply -f daemon-set/nginx-ingress.yaml
		

Для DaemonSet NGINX Plus:


$ kubectl apply -f daemon-set/nginx-plus-ingress.yaml
		

Убедитесь что ваш контроллер ingress запущен:


$ kubectl get pods --namespace=nginx-ingress
		

Если вы создали DaemonSet, порты контроллера ingress 80 и 443 соответствуют тем же самым портам того узла, в котором запущен этот контроллер. Для доступа к данному контроллеру ingress применяйте данные порт и IP адрес каждого из узлов в которых запущены контроллеры ingress. Если вы развернули Deployment, продолжите следующим шагом.

Для ваших методов Deployment имеются два метода доступа к подам контроллера ingress. Вы можете указать Kubernetes случайным образом выделять некий порт узла, который соответствует вашему поду контроллера ingress. Такая служба имеет тип NodePort. Другим вариантом является создание службы с типом LoadBalancer. При создании некой службы типа LoadBalancer, Kubernetes собирает некий балансировщик нагрузки для конкретной облачной платформы, такой как Amazon Web Services, Microsoft Azure и Google Cloud Compute.

Для создания некой службы с типом NodePort примените следующее:


$ kubectl create -f service/nodeport.yaml
		

Чтобы настроить необходимый открытый для данного пода порт статически, измените соответствующий YAML и добавьте атрибут nodePort: {port} в настройки всех подлежащих открытию портов.

Для создания некой службы с типом LoadBalancer для Google Cloud Compute или Azure воспользуйтесь таким кодом:


$ kubectl create -f service/loadbalancer.yaml
		

Для создания службы с типом LoadBalancer под Amazon Web Services:


$ kubectl create -f service/loadbalancer-aws-elb.yaml
		

Под AWS Kubernetes создаёт классический ELB в режиме TCP с включённым протоколом посредника (прокси). Вам следует настроить NGINX для применения этого протокола TCP. Для этого вы можете в своё соответствие настроек, упомянутом ранее как файл common/nginx-confg.yaml, вы можете добавить следующее:


proxy-protocol: "True"
real-ip-header: "proxy_protocol"
set-real-ip-from: "0.0.0.0/0"
 	   

Затем обновите это соответствие настроек:


$ kubectl apply -f common/nginx-config.yaml
		

Вы можете теперь выполнять адресацию своего пода по его NodePort или выполняя запросы к имеющемуся балансировщику нагрузки, созданному от его имени.

Обсуждение

На момсент написания данных строк {редакция 2019}, Kubernetes является ведущей платформой для оркестрации и управления контейнерами. Контроллер ingress является пограничным модулем, который набравляет обмен остальной части вашего приложения. NGINX идеально подходит для данной роли и упрощает настройку со своими комментариями. Проект NGINX-Ingress предлагает контроллер ingress с открытым исходным кодом сразу после его установки из некого образа Dockerfile, а также NGINX Plus с несколькими шагами добавления ваших ключа и сертификата репозитория. Включение вашего кластера Kubernetes с помощью контроллера ingress NGINX предоставляет все те же свойства, которые имеются у NGINX, но с дополнительными функциями сетевых сред Kubernetes и DNS для маршрутизации обмена.

Маршрутизатор OpenShift

Задача

Вам требуется развернуть своё приложение в OpenShift и вы желаете применять в качестве маршрутизатора NGINX.

Решение

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

Зарегистрируйтесь в своём кластере OpenShift под администратором:


$ oc login -u system:admin
		

Выберите проект default:


$ oc project default
		

Выполните резервное копирование настроек установленного по умолчанию Маршрутизатора, на случай если вам потребуется его восстановить:


$ oc get -o yaml service/router dc/router \
    clusterrolebinding/router-router-role \
    serviceaccount/router > default-router-backup.yaml
		

Удалите этот Маршрутизатор:


$ oc delete -f default-router-backup.yaml
		

Разверните Маршрутизатор NGINX:


$ oc adm router router --images={image} --type='' \
    --selector='node-role.kubernetes.io/infra=true'
		

В данном примере значение {image} должно указывать на образ Маршрутизатора NGINX в вашем реестре. Значение параметра selector определяет некую метку селектора для узлов в которых будет развёрнут ваш Маршрутизатор: node-role.kubernetes.io/infra=true. Применяйте тот селектор, который актуален в вашей среде.

Проверьте что ваш Маршрутизатор NGINX поднят и работает:


$ oc get pods
		

Вы должны обнаружить некий под Маршрутизатора с названием router-1-{string}.

По умолчанию страница заглушки NGINX доступна через порт 1936 того узла, в котором запущен ваш Маршрутизатор (вы можете изменить этот порт при помощи переменной среды STATS_PORT). Для доступа к страницам вне данного узла вам потребуется добавить некую запись в свои правила IPtables для такого узла:


$ sudo iptables -I OS_FIREWALL_ALLOW -p tcp -s {ip range} \
    -m tcp --dport 1936 -j ACCEPT
		

Откройте свой браузер в http://{node-ip}:1936/stub_status чтобы получить доступ к странице состояния заглушки.

Обсуждение

Маршрутизатор OpenShift является точкой входа для внешних запросов, ограничивающей запущенные в OpenShift приложения. Задание этого Маршрутизатора состоит в получении входящих запросов и направлении их в соответствующий под приложения. Возможности балансировки нагрузки и маршрутизации NGINX делают его великолепным выбором для применения в качестве Маршрутизатора OpenShift. Переключение с устанавливаемого по умолчанию Маршрутизатора на Маршрутизатор NGINX включает все имеющиеся функции и всю мощность NGINX основным проникновением в ваше приложение OpenStack.