Как использовать 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
для создания начальной конфигурации. Мы также настроили его в фоновом режиме, чтобы постоянно проверять наличие изменений. Это, в сочетании с динамической генерацией конфигурации на основе шаблонов, позволило нам постоянно иметь актуальную картину наших внутренних серверов.