Поиск по сайту:

Как использовать Confd и Etcd для динамической перенастройки служб в CoreOS


Введение

CoreOS позволяет легко запускать службы в контейнерах Docker на кластере машин. Процедура для этого обычно включает в себя запуск одного или нескольких экземпляров службы, а затем регистрацию каждого экземпляра в etcd, распределенном хранилище ключей и значений CoreOS.

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

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

Предпосылки и цели

Чтобы работать с этим руководством, вы должны иметь общее представление о CoreOS и ее составных частях. В предыдущих руководствах мы настроили кластер CoreOS и познакомились с некоторыми инструментами, которые используются для управления вашими кластерами.

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

  • Как настроить кластер CoreOS в DigitalOcean
  • Как создать и запустить службу в кластере CoreOS
  • Как создать гибкие сервисы для кластера CoreOS с помощью файлов Fleet Unit Files

Кроме того, чтобы лучше познакомиться с некоторыми инструментами управления, которые мы будем использовать, вы можете ознакомиться с этими руководствами:

  • Как использовать Fleet и Fleetctl для управления кластером CoreOS
  • Как использовать Etcdctl и Etcd, распределенное хранилище ключей и значений CoreOS

Руководство «Как создать гибкие сервисы» особенно важно для этого руководства, так как шаблонные основные + вспомогательные сервисы будут служить основой для интерфейсного сервиса, который мы настроим в этом руководстве. Как мы уже говорили ранее, хотя В приведенных выше руководствах обсуждается создание служб Apache и помощников, в этом руководстве есть некоторые изменения конфигурации, которые упрощают запуск с нуля.В этом руководстве мы создадим модифицированные версии этих служб.

В этом руководстве мы сосредоточимся на создании нового контейнера приложения с помощью Nginx. Это будет служить обратным прокси для различных экземпляров Apache, которые мы можем создать из наших файлов шаблонов. Контейнер Nginx будет настроен с помощью confd для наблюдения за регистрацией службы, за которую отвечают наши вспомогательные службы.

Мы начнем с того же кластера из трех машин, который мы использовали в этой серии.

  • кореос-1
  • кореос-2
  • кореос-3

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

Настройка внутренних служб Apache

Мы начнем с настройки наших серверных служб Apache. Это в основном будет отражать последнюю часть предыдущего руководства, но мы пройдем всю процедуру здесь из-за некоторых тонких различий.

Войдите на одну из ваших машин с CoreOS, чтобы начать:

ssh -A core@ip_address

Настройка контейнера Apache

Мы начнем с создания базового контейнера Apache. Это фактически идентично предыдущему руководству, поэтому вам не нужно делать это снова, если у вас уже есть этот образ, доступный в вашей учетной записи Docker Hub. Мы создадим этот контейнер на основе образа контейнера Ubuntu 14.04.

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

docker run -i -t ubuntu:14.04 /bin/bash

Вы попадете в сеанс bash после запуска контейнера. Отсюда мы обновим локальный индекс пакета apt и установим apache2:

apt-get update
apt-get install apache2 -y

Мы также установим страницу по умолчанию:

echo "<h1>Running from Docker on CoreOS</h1>" > /var/www/html/index.html

Теперь мы можем выйти из контейнера, так как он находится в нужном нам состоянии:

exit

Войдите или создайте свою учетную запись в Docker Hub, набрав:

docker login

Вам нужно будет указать свое имя пользователя, пароль и адрес электронной почты для вашей учетной записи Docker Hub.

Затем получите идентификатор контейнера экземпляра, который вы только что покинули:

docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                     PORTS               NAMES
1db0c9a40c0d        ubuntu:14.04        "/bin/bash"         2 minutes ago       Exited (0) 4 seconds ago                       jolly_pare

Выделенное выше поле — это идентификатор контейнера. Скопируйте вывод, который вы видите на своем компьютере.

Теперь зафиксируйте, используя этот идентификатор контейнера, ваше имя пользователя Docker Hub и имя образа. Здесь мы будем использовать \apache:

docker commit 1db0c9a40c0d user_name/apache

Отправьте новый образ в Docker Hub:

docker push user_name/apache

Теперь можете использовать это изображение в своих служебных файлах.

Создание файла модуля шаблона службы Apache

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

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

cd ~
mkdir static templates instances

Теперь мы можем создать наш файл шаблона в каталоге templates:

vim templates/apache@.service

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

[Unit]
Description=Apache web server service on port %i

# Requirements
Requires=etcd.service
Requires=docker.service
Requires=apache-discovery@%i.service

# Dependency ordering
After=etcd.service
After=docker.service
Before=apache-discovery@%i.service

[Service]
# Let processes take awhile to start up (for first run Docker containers)
TimeoutStartSec=0

# Change killmode from "control-group" to "none" to let Docker remove
# work correctly.
KillMode=none

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Pre-start and Start
## Directives with "=-" are allowed to fail without consequence
ExecStartPre=-/usr/bin/docker kill apache.%i
ExecStartPre=-/usr/bin/docker rm apache.%i
ExecStartPre=/usr/bin/docker pull user_name/apache
ExecStart=/usr/bin/docker run --name apache.%i -p ${COREOS_PRIVATE_IPV4}:%i:80 \
user_name/apache /usr/sbin/apache2ctl -D FOREGROUND

# Stop
ExecStop=/usr/bin/docker stop apache.%i

[X-Fleet]
# Don't schedule on the same machine as other Apache instances
Conflicts=apache@*.service

Одна из модификаций, которую мы здесь сделали, заключается в использовании частного интерфейса вместо общедоступного. Поскольку все наши экземпляры Apache будут передавать трафик через обратный прокси-сервер Nginx вместо обработки подключений из открытой сети, это хорошая идея. Помните, что если вы используете частный интерфейс в DigitalOcean, на сервере, который вы развернули, должен быть установлен флаг «частная сеть» при создании.

Кроме того, не забудьте изменить user_name на ссылку на ваше имя пользователя Docker Hub, чтобы правильно извлечь файл Docker.

Создание файла модуля шаблона Sidekick

Теперь мы сделаем то же самое для сервиса sidekick. Этот мы немного изменим в ожидании информации, которая нам понадобится позже.

Откройте файл шаблона в вашем редакторе:

vim templates/apache-discovery@.service

В этом файле мы будем использовать следующую информацию:

[Unit]
Description=Apache web server on port %i etcd registration

# Requirements
Requires=etcd.service
Requires=apache@%i.service

# Dependency ordering and binding
After=etcd.service
After=apache@%i.service
BindsTo=apache@%i.service

[Service]

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Start
## Test whether service is accessible and then register useful information
ExecStart=/bin/bash -c '\
  while true; do \
    curl -f ${COREOS_PRIVATE_IPV4}:%i; \
    if [ $? -eq 0 ]; then \
      etcdctl set /services/apache/${COREOS_PRIVATE_IPV4} \'${COREOS_PRIVATE_IPV4}:%i\' --ttl 30; \
    else \
      etcdctl rm /services/apache/${COREOS_PRIVATE_IPV4}; \
    fi; \
    sleep 20; \
  done'

# Stop
ExecStop=/usr/bin/etcdctl rm /services/apache/${COREOS_PRIVATE_IPV4}

[X-Fleet]
# Schedule on the same machine as the associated Apache service
MachineOf=apache@%i.service

Приведенная выше конфигурация несколько отличается от конфигурации в предыдущем руководстве. Мы изменили значение, установленное командой etcdctl set. Вместо передачи объекта JSON мы устанавливаем простую комбинацию IP-адрес + порт. Таким образом, мы можем прочитать это значение напрямую, чтобы найти информацию о подключении, необходимую для доступа к этой службе.

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

Создайте экземпляр своих служб

Теперь давайте создадим два экземпляра этих сервисов.

Во-первых, давайте создадим символические ссылки. Перейдите в созданный вами каталог ~/instances и создайте ссылку, чтобы определить порты, на которых они будут работать. Мы хотим запустить одну службу на порту 7777, а другую на порту 8888:

cd ~/instances
ln -s ../templates/apache@.service apache@7777.service
ln -s ../templates/apache@.service apache@8888.service
ln -s ../templates/apache-discovery@.service apache-discovery@7777.service
ln -s ../templates/apache-discovery@.service apache-discovery@8888.service

Теперь мы можем запустить эти службы, передав каталог ~/instances в fleet:

fleetctl start ~/instances/*

После запуска ваших экземпляров (это может занять несколько минут) вы сможете увидеть записи etcd, сделанные вашими помощниками:

etcdctl ls --recursive /
/coreos.com
/coreos.com/updateengine
/coreos.com/updateengine/rebootlock
/coreos.com/updateengine/rebootlock/semaphore
/services
/services/apache
/services/apache/10.132.249.206
/services/apache/10.132.249.212

Если вы спросите значение одной из этих записей, вы увидите, что получили IP-адрес и номер порта:

etcdctl get /services/apache/10.132.249.206
10.132.249.206:8888

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

curl 10.132.249.206:8888
<h1>Running from Docker on CoreOS</h1>

Теперь у нас настроена внутренняя инфраструктура. Наш следующий шаг — ознакомиться с confd, чтобы мы могли отслеживать расположение /services/apache в etcd на наличие изменений и каждый раз перенастраивать Nginx. .

Создание контейнера Nginx

Мы запустим контейнер Nginx из той же базы Ubuntu 14.04, которую мы использовали для служб Apache.

Установка программного обеспечения

Запустите новый контейнер, набрав:

docker run -i -t ubuntu:14.04 /bin/bash

Обновите локальный кэш пакетов apt и установите Nginx. Нам также необходимо установить curl, так как базовый образ не включает его, и он нужен нам для мгновенного получения стабильного пакета confd с GitHub:

apt-get update
apt-get install nginx curl -y

Теперь мы можем перейти к версии 0.5.0, но это могло измениться. Щелкните правой кнопкой мыши ссылку на версию инструмента для Linux и выберите «копировать адрес ссылки» или любой другой доступный аналогичный параметр.

Теперь, вернувшись в контейнер Docker, используйте скопированный URL-адрес для загрузки приложения. Мы поместим это в каталог /usr/local/bin. Нам нужно выбрать confd в качестве выходного файла:

cd /usr/local/bin
curl -L https://github.com/kelseyhightower/confd/releases/download/v0.5.0/confd-0.5.0<^>-linux-amd64 -o confd

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

chmod +x confd

Мы также должны воспользоваться этой возможностью, чтобы создать структуру конфигурации, которую ожидает confd. Это будет в каталоге /etc:

mkdir -p /etc/confd/{conf.d,templates}

Создайте файл конфигурации Confd для чтения значений Etcd

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

Файлы конфигурации в confd используются для настройки службы для проверки определенных значений etcd и инициирования действий при обнаружении изменений. Они используют формат файла TOML, который прост в использовании и довольно интуитивно понятен.

Начните с создания файла в нашем каталоге конфигурации с именем nginx.toml:

vi /etc/confd/conf.d/nginx.toml

Здесь мы создадим наш файл конфигурации. Добавьте следующую информацию:

[template]

# The name of the template that will be used to render the application's configuration file
# Confd will look in `/etc/conf.d/templates` for these files by default
src = "nginx.tmpl"

# The location to place the rendered configuration file
dest = "/etc/nginx/sites-enabled/app.conf"

# The etcd keys or directory to watch.  This is where the information to fill in
# the template will come from.
keys = [ "/services/apache" ]

# File ownership and mode information
owner = "root"
mode = "0644"

# These are the commands that will be used to check whether the rendered config is
# valid and to reload the actual service once the new config is in place
check_cmd = "/usr/sbin/nginx -t"
reload_cmd = "/usr/sbin/service nginx reload"

В приведенном выше файле есть комментарии, объясняющие некоторые из основных идей, но мы можем просмотреть варианты, которые у вас есть ниже:

Directive Required? Type Description
src Yes String The name of the template that will be used to render the information. If this is located outside of /etc/confd/templates, the entire path is should be used.
dest Yes String The file location where the rendered configuration file should be placed.
keys Yes Array of strings The etcd keys that the template requires to be rendered correctly. This can be a directory if the template is set up to handle child keys.
owner No String The username that will be given ownership of the rendered configuration file.
group No String The group that will be given group ownership of the rendered configuration file.
mode No String The octal permissions mode that should be set for the rendered file.
check_cmd No String The command that should be used to check the syntax of the rendered configuration file.
reload_cmd No String The command that should be used to reload the configuration of the application.
prefix No String A part of the etcd hierarchy that comes before the keys in the keys directive. This can be used to make the .toml file more flexible.

Созданный нами файл сообщает нам несколько важных вещей о том, как будет функционировать наш экземпляр confd. Наш контейнер Nginx будет использовать шаблон, хранящийся в /etc/confd/templates/nginx.conf.tmpl, для отображения файла конфигурации, который будет помещен в /etc/nginx/sites-enabled/. app.conf. Файлу будет предоставлен набор разрешений 0644, и право собственности будет передано пользователю root.

Приложение confd будет искать изменения в узле /services/apache. При обнаружении изменения confd запросит новую информацию об этом узле. Затем он отобразит новую конфигурацию для Nginx. Он проверит файл конфигурации на наличие синтаксических ошибок и перезагрузит службу Nginx после того, как файл будет на месте.

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

Создайте файл шаблона Confd

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

Создайте файл, на который мы ссылались в нашем файле конфигурации выше. Поместите этот файл в наш каталог templates:

vi /etc/confd/templates/nginx.tmpl

В этом файле мы просто воссоздаем стандартный файл конфигурации обратного прокси-сервера Nginx. Однако мы будем использовать некоторый синтаксис шаблонов Go, чтобы заменить часть информации, которую confd извлекает из etcd.

Во-первых, мы настраиваем блок с «восходящими» серверами. Этот раздел используется для определения пула серверов, на которые Nginx может отправлять запросы. Формат обычно такой:

upstream pool_name {
    server server_1_IP:port_num;
    server server_2_IP:port_num;
    server server_3_IP:port_num;
}

Это позволяет нам передавать запросы в pool_name, и Nginx выберет один из определенных серверов для передачи запроса.

Идея нашего файла шаблона состоит в том, чтобы проанализировать etcd для IP-адресов и номеров портов наших веб-серверов Apache. Таким образом, вместо того, чтобы статически определять наши вышестоящие серверы, мы должны динамически заполнять эту информацию при рендеринге файла. Мы можем сделать это, используя шаблоны Go для динамического контента.

Для этого мы будем использовать это как наш блок:

upstream apache_pool {
{{ range getvs "/services/apache/*" }}
    server {{ . }};
{{ end }}
}

Давайте на мгновение объясним, что происходит. Мы открыли блок для определения восходящего пула серверов с именем apache_pool. Внутри мы указываем, что начинаем некоторый код языка Go, используя двойные скобки.

В этих скобках мы указываем конечную точку etcd, в которой хранятся интересующие нас значения. Мы используем range, чтобы сделать список повторяемым.

Мы используем это, чтобы передать все записи, полученные ниже расположения /services/apache в etcd, в блок range. Затем мы можем получить значение ключа в текущей итерации, используя одну точку внутри \{{ и \}}, которые указывают на вставленное значение. Мы используем это в цикле диапазона для заполнения пула серверов. Наконец, мы завершаем цикл директивой {{ end }}.

Примечание. Не забудьте добавить точку с запятой после директивы server внутри цикла. Если вы забудете об этом, конфигурация будет нерабочей.

После настройки пула серверов мы можем просто использовать прокси-проход, чтобы направлять все соединения в этот пул. Это будет просто стандартный блок сервера в качестве обратного прокси. Единственное, на что стоит обратить внимание, это access_log, в котором используется пользовательский формат, который мы сейчас создадим:

upstream apache_pool {
{{ range getvs "/services/apache/*" }}
    server {{ . }};
{{ end }}
}

server {
	listen 80 default_server;
	listen [::]:80 default_server ipv6only=on;

    access_log /var/log/nginx/access.log upstreamlog;

    location / {
        proxy_pass http://apache_pool;
        proxy_redirect off;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }
}

Это ответит на все подключения через порт 80 и передаст их в пул серверов в apache_pool, который создается путем просмотра записей etcd.

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

rm /etc/nginx/sites-enabled/default

Теперь самое время настроить формат журнала, на который мы ссылались в нашем файле шаблона. Это должно быть в блоке http конфигурации, доступном в основном файле конфигурации. Откройте это сейчас:

vi /etc/nginx/nginx.conf

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

. . .
http {
    ##
    # Basic Settings
    ##
    log_format upstreamlog '[$time_local] $remote_addr passed to: $upstream_addr: $request Upstream Response Time: $upstream_response_time Request time: $request_time';

    sendfile on;
    . . .

Сохраните и закройте файл, когда закончите.

Создание скрипта для запуска Confd

Нам нужно создать файл сценария, который будет вызывать confd с нашим файлом ресурсов шаблона и нашим файлом шаблона в соответствующее время.

Скрипт должен делать две вещи, чтобы наш сервис работал правильно:

  • Он должен запускаться при запуске контейнера, чтобы настроить начальные параметры Nginx на основе текущего состояния серверной инфраструктуры.
  • Он должен продолжать отслеживать изменения в регистрации etcd для серверов Apache, чтобы он мог перенастроить Nginx на основе доступных внутренних серверов.

Мы получим наш скрипт со страницы Марселя де Граафа на GitHub. Это хороший, простой скрипт, который делает точно то, что нам нужно. Мы внесем лишь несколько незначительных правок в наш сценарий.

Давайте поместим этот скрипт рядом с нашим исполняемым файлом confd. Мы назовем это confd-watch:

vi /usr/local/bin/confd-watch

Мы начнем с обычного заголовка bash, чтобы определить нужный нам интерпретатор. Затем мы установим некоторые параметры bash, чтобы скрипт немедленно завершал работу, если что-то пойдет не так. Он вернет значение последней неудачной или запущенной команды.

#!/bin/bash

set -eo pipefail

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

Подстановка параметров создается с помощью следующего синтаксиса: $ {имя_переменной:-значение_по умолчанию}. Это имеет свойство использовать значение var_name, если оно задано и не равно нулю, в противном случае по умолчанию используется default_value.

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

#!/bin/bash

set -eo pipefail

export ETCD_PORT=${ETCD_PORT:-4001}
export HOST_IP=${HOST_IP:-172.17.42.1}
export ETCD=$HOST_IP:$ETCD_PORT

Теперь мы будем использовать confd для рендеринга начальной версии файла конфигурации Nginx, читая значения из etcd, доступные при вызове этого скрипта. Мы будем использовать цикл until, чтобы постоянно пытаться построить начальную конфигурацию.

Циклическая конструкция может понадобиться в случае, если etcd недоступен сразу или в случае, если контейнер Nginx подключается к сети раньше внутренних серверов. Это позволяет ему многократно опрашивать etcd, пока он, наконец, не создаст действительную начальную конфигурацию.

Фактическая команда confd, которую мы вызываем, выполняется один раз, а затем завершается. Это сделано для того, чтобы мы могли подождать 5 секунд до следующего запуска, чтобы дать нашим внутренним серверам возможность зарегистрироваться. Мы подключаемся к полной переменной ETCD, созданной с использованием значений по умолчанию или переданных в параметрах, и используем файл ресурсов шаблона, чтобы определить поведение того, что мы хотим сделать:

#!/bin/bash

set -eo pipefail

export ETCD_PORT=${ETCD_PORT:-4001}
export HOST_IP=${HOST_IP:-172.17.42.1}
export ETCD=$HOST_IP:$ETCD_PORT

echo "[nginx] booting container. ETCD: $ETCD"

# Try to make initial configuration every 5 seconds until successful
until confd -onetime -node $ETCD -config-file /etc/confd/conf.d/nginx.toml; do
    echo "[nginx] waiting for confd to create initial nginx configuration"
    sleep 5
done

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

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

После перевода процесса confd в фоновый режим мы можем безопасно запускать Nginx, используя созданный файл конфигурации. Поскольку этот скрипт будет вызываться как наша команда Docker \run, нам нужно, чтобы он работал на переднем плане, чтобы контейнер не вышел в этот момент. Мы можем сделать это, просто завершив журналы, предоставив нам доступ к вся информация, которую мы записывали:

#!/bin/bash

set -eo pipefail

export ETCD_PORT=${ETCD_PORT:-4001}
export HOST_IP=${HOST_IP:-172.17.42.1}
export ETCD=$HOST_IP:$ETCD_PORT

echo "[nginx] booting container. ETCD: $ETCD."

# Try to make initial configuration every 5 seconds until successful
until confd -onetime -node $ETCD -config-file /etc/confd/conf.d/nginx.toml; do
    echo "[nginx] waiting for confd to create initial nginx configuration."
    sleep 5
done

# Put a continual polling `confd` process into the background to watch
# for changes every 10 seconds
confd -interval 10 -node $ETCD -config-file /etc/confd/conf.d/nginx.toml &
echo "[nginx] confd is now monitoring etcd for changes..."

# Start the Nginx service using the generated config
echo "[nginx] starting nginx service..."
service nginx start

# Follow the logs to allow the script to continue running
tail -f /var/log/nginx/*.log

Когда вы закончите с этим, сохраните и закройте файл.

Последнее, что нам нужно сделать, это сделать скрипт исполняемым:

chmod +x /usr/local/bin/confd-watch

Выйдите из контейнера сейчас, чтобы вернуться в хост-систему:

exit

Зафиксировать и отправить контейнер

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

Узнать ID контейнера:

docker ps -l
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS                          PORTS               NAMES
de4f30617499        ubuntu:14.04        "/bin/bash"         22 hours ago        Exited (0) About a minute ago                       stupefied_albattani

Выделенная строка — это идентификатор контейнера, который нам нужен. Зафиксируйте контейнер, используя этот идентификатор вместе с вашим именем пользователя Docker Hub и именем, которое вы хотели бы использовать для этого образа. В этом руководстве мы будем использовать имя \nginx_lb:

docker commit de4f30617499 user_name/nginx_lb

При необходимости войдите в свою учетную запись Docker Hub:

docker login

Теперь вы должны подтолкнуть свой зафиксированный образ, чтобы другие хосты могли его сбросить по мере необходимости:

docker push user_name/nginx_lb

Создайте файл статического модуля Nginx

Следующим шагом будет создание юнит-файла, который запустит только что созданный нами контейнер. Это позволит нам использовать fleet для управления процессом.

Так как это не будет шаблоном, мы поместим его в каталог ~/static, который мы создали в начале этого каталога:

vim static/nginx_lb.service

Мы начнем со стандартного раздела [Unit], чтобы описать службу и определить зависимости и порядок:

[Unit]
Description=Nginx load balancer for web server backends

# Requirements
Requires=etcd.service
Requires=docker.service

# Dependency ordering
After=etcd.service
After=docker.service

Далее нам нужно определить часть файла [Service]. Мы установим тайм-аут на ноль и снова настроим killmode на none, как мы это сделали с служебными файлами Apache. Мы снова получим файл среды, чтобы получить доступ к общедоступным и частным IP-адресам хоста, на котором работает этот контейнер.

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

Наконец, мы запустим контейнер. Это включает в себя запуск контейнера, присвоение ему имени, на которое мы ссылались в командах удаления и уничтожения, и передачу общедоступного IP-адреса хоста, на котором он работает, для сопоставления порта 80. Мы вызываем confd-watch сценарий, который мы написали как команду запуска.

[Unit]
Description=Nginx load balancer for web server backends

# Requirements
Requires=etcd.service
Requires=docker.service

# Dependency ordering
After=etcd.service
After=docker.service

[Service]
# Let the process take awhile to start up (for first run Docker containers)
TimeoutStartSec=0

# Change killmode from "control-group" to "none" to let Docker remove
# work correctly.
KillMode=none

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Pre-start and Start
## Directives with "=-" are allowed to fail without consequence
ExecStartPre=-/usr/bin/docker kill nginx_lb
ExecStartPre=-/usr/bin/docker rm nginx_lb
ExecStartPre=/usr/bin/docker pull user_name/nginx_lb
ExecStart=/usr/bin/docker run --name nginx_lb -p ${COREOS_PUBLIC_IPV4}:80:80 \
user_name/nginx_lb /usr/local/bin/confd-watch

Теперь все, что нам нужно, — это разобраться с командой остановки и указаниями расписания fleet. Мы хотим, чтобы этот контейнер запускался только на хостах, на которых не запущены другие экземпляры балансировки нагрузки или внутренние серверы Apache. Это позволит нашему сервису эффективно распределить нагрузку:

[Unit]
Description=Nginx load balancer for web server backends

# Requirements
Requires=etcd.service
Requires=docker.service

# Dependency ordering
After=etcd.service
After=docker.service

[Service]
# Let the process take awhile to start up (for first run Docker containers)
TimeoutStartSec=0

# Change killmode from "control-group" to "none" to let Docker remove
# work correctly.
KillMode=none

# Get CoreOS environmental variables
EnvironmentFile=/etc/environment

# Pre-start and Start
## Directives with "=-" are allowed to fail without consequence
ExecStartPre=-/usr/bin/docker kill nginx_lb
ExecStartPre=-/usr/bin/docker rm nginx_lb
ExecStartPre=/usr/bin/docker pull user_name/nginx_lb
ExecStart=/usr/bin/docker run --name nginx_lb -p ${COREOS_PUBLIC_IPV4}:80:80 \
user_name/nginx_lb /usr/local/bin/confd-watch

# Stop
ExecStop=/usr/bin/docker stop nginx_lb

[X-Fleet]
Conflicts=nginx.service
Conflicts=apache@*.service

Сохраните и закройте файл, когда закончите.

Запуск балансировщика нагрузки Nginx

У вас уже должны быть запущены два экземпляра Apache из предыдущего руководства. Вы можете проверить, набрав:

fleetctl list-units
UNIT				MACHINE				ACTIVE	SUB
apache-discovery@7777.service	197a1662.../10.132.249.206	active	running
apache-discovery@8888.service	04856ec4.../10.132.249.212	active	running
apache@7777.service		197a1662.../10.132.249.206	active	running
apache@8888.service		04856ec4.../10.132.249.212	active	running

Вы также можете дважды проверить, правильно ли они регистрируются в etcd, набрав:

etcdctl ls --recursive /services/apache
/services/apache/10.132.249.206
/services/apache/10.132.249.212

Теперь мы можем попытаться запустить нашу службу Nginx:

fleetctl start ~/static/nginx_lb.service
Unit nginx_lb.service launched on 96ec72cf.../10.132.248.177

Запуск службы может занять около минуты, в зависимости от того, сколько времени потребуется для извлечения изображения. После запуска, если вы проверите журналы с помощью команды fleetctl journal, вы сможете увидеть некоторую информацию журнала из confd. Это должно выглядеть примерно так:

fleetctl journal nginx_lb.service
-- Logs begin at Mon 2014-09-15 14:54:05 UTC, end at Tue 2014-09-16 17:13:58 UTC. --
Sep 16 17:13:48 lala1 docker[15379]: 2014-09-16T17:13:48Z d7974a70e976 confd[14]: INFO Target config /etc/nginx/sites-enabled/app.conf out of sync
Sep 16 17:13:48 lala1 docker[15379]: 2014-09-16T17:13:48Z d7974a70e976 confd[14]: INFO Target config /etc/nginx/sites-enabled/app.conf has been updated
Sep 16 17:13:48 lala1 docker[15379]: [nginx] confd is monitoring etcd for changes...
Sep 16 17:13:48 lala1 docker[15379]: [nginx] starting nginx service...
Sep 16 17:13:48 lala1 docker[15379]: 2014-09-16T17:13:48Z d7974a70e976 confd[33]: INFO Target config /etc/nginx/sites-enabled/app.conf in sync
Sep 16 17:13:48 lala1 docker[15379]: ==> /var/log/nginx/access.log <==
Sep 16 17:13:48 lala1 docker[15379]: ==> /var/log/nginx/error.log <==
Sep 16 17:13:58 lala1 docker[15379]: 2014-09-16T17:13:58Z d7974a70e976 confd[33]: INFO /etc/nginx/sites-enabled/app.conf has md5sum a8517bfe0348e9215aa694f0b4b36c9b should be 33f42e3b7cc418f504237bea36c8a03e
Sep 16 17:13:58 lala1 docker[15379]: 2014-09-16T17:13:58Z d7974a70e976 confd[33]: INFO Target config /etc/nginx/sites-enabled/app.conf out of sync
Sep 16 17:13:58 lala1 docker[15379]: 2014-09-16T17:13:58Z d7974a70e976 confd[33]: INFO Target config /etc/nginx/sites-enabled/app.conf has been updated

Как вы можете видеть, confd обратился к etcd за начальной конфигурацией. Затем он запустил nginx. После этого мы можем увидеть строки, в которых записи etcd были переоценены и создан новый файл конфигурации. Если вновь сгенерированный файл не соответствует md5sum существующего файла, файл отключается, а служба перезагружается.

Это позволяет нашей службе балансировки нагрузки в конечном итоге отслеживать наши внутренние серверы Apache. Если кажется, что confd постоянно обновляется, это может быть связано с тем, что ваши экземпляры Apache слишком часто обновляют свой TTL. Вы можете увеличить значения Sleep и TTL в шаблоне sidekick, чтобы избежать этого.

Чтобы увидеть балансировщик нагрузки в действии, вы можете запросить файл /etc/environments с хоста, на котором работает служба Nginx. Он содержит общедоступный IP-адрес хоста. Если вы хотите улучшить эту конфигурацию, рассмотрите возможность запуска вспомогательной службы, которая регистрирует эту информацию с помощью etcd, как мы это делали для экземпляров Apache:

fleetctl ssh nginx_lb cat /etc/environment
COREOS_PRIVATE_IPV4=10.132.248.177
COREOS_PUBLIC_IPV4=104.131.16.222

Теперь, если мы перейдем к общедоступному IPv4-адресу в нашем браузере, мы должны увидеть страницу, которую мы настроили в наших экземплярах Apache:

Теперь, если вы снова посмотрите на свои журналы, вы сможете увидеть информацию, указывающую, какой внутренний сервер фактически передал запрос:

fleetctl journal nginx_lb
. . .
Sep 16 18:04:38 lala1 docker[18079]: 2014-09-16T18:04:38Z 51c74658196c confd[28]: INFO Target config /etc/nginx/sites-enabled/app.conf in sync
Sep 16 18:04:48 lala1 docker[18079]: 2014-09-16T18:04:48Z 51c74658196c confd[28]: INFO Target config /etc/nginx/sites-enabled/app.conf in sync
Sep 16 18:04:48 lala1 docker[18079]: [16/Sep/2014:18:04:48 +0000] 108.29.37.206 passed to: 10.132.249.212:8888: GET / HTTP/1.1 Upstream Response Time: 0.003 Request time: 0.003

Заключение

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

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