如何使用Confd和Etcd在CoreOS中动态重新配置服务
介绍
CoreOS 允许您在跨机器集群的 Docker 容器中轻松运行服务。 这样做的过程通常涉及启动一个或多个服务实例,然后将每个实例注册到 etcd
,CoreOS 的分布式键值存储。
通过利用这种模式,相关服务可以获得有关基础设施状态的有价值信息,并使用这些知识来告知自己的行为。 这使得服务可以在重要的 etcd
值更改时动态配置自己。
在本指南中,我们将讨论一个名为 confd
的工具,该工具专门用于观察分布式键值存储的变化。 它在 Docker 容器中运行,用于触发配置修改和服务重新加载。
先决条件和目标
为了阅读本指南,您应该对 CoreOS 及其组件有基本的了解。 在之前的指南中,我们设置了 CoreOS 集群并熟悉了一些用于管理集群的工具。
以下是您在开始阅读本文之前应阅读的指南。 我们将修改这些指南中描述的一些服务的行为,因此虽然理解材料很重要,但在使用本指南时应该重新开始:
此外,为了更熟悉我们将使用的一些管理工具,您需要阅读以下指南:
“如何创建灵活服务”指南对本指南尤为重要,因为模板化的 main + sidekick 服务将作为我们将在本指南中设置的前端服务的基础。 如前所述,尽管上述指南讨论了 Apache 和 Sidekick 服务的创建,但本指南有一些配置更改,可以更轻松地从头开始。 我们将在本指南中创建这些服务的修改版本。
在本教程中,我们将专注于使用 Nginx 创建一个新的应用程序容器。 这将作为我们可以从模板文件生成的各种 Apache 实例的反向代理。 Nginx 容器将配置 confd
以监视我们的 sidekick 服务负责的服务注册。
我们将从本系列中一直使用的相同的三机集群开始。
- coreos-1
- coreos-2
- coreos-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 帐户的用户名、密码和电子邮件地址。
接下来,获取刚刚离开的实例的容器 ID:
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
上面突出显示的字段是容器 ID。 复制您在自己的计算机上看到的输出。
现在,使用该容器 ID、您的 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 反向代理 传递流量,而不是处理来自开放 Web 的连接,这是一个好主意。 请记住,如果您在 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
以便我们可以观察 etcd
中的 /services/apache
位置的变化并每次重新配置 Nginx。
创建 Nginx 容器
我们将从用于 Apache 服务的同一 Ubuntu 14.04 基础启动 Nginx 容器。
安装软件
通过键入以下内容启动一个新容器:
docker run -i -t ubuntu:14.04 /bin/bash
更新本地 apt
包缓存并安装 Nginx。 我们还需要安装 curl
,因为基础镜像不包含它,我们需要它来暂时从 GitHub 获取稳定的 confd
包:
apt-get update apt-get install nginx curl -y
现在,我们可以在浏览器中访问 GitHub 上 confd
的 发布页面 。 我们需要找到最新稳定版本的链接。 在撰写本文时,即 v0.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"
上面的文件有解释一些基本想法的注释,但我们可以在下面查看您的选项:
指示 | 必需的? | 类型 | 描述 |
---|---|---|---|
源代码 | 是的 | 细绳 | 将用于呈现信息的模板的名称。 如果它位于 /etc/confd/templates 之外,则应使用整个路径。
|
目的地 | 是的 | 细绳 | 应放置呈现的配置文件的文件位置。 |
钥匙 | 是的 | 字符串数组 | 模板需要正确渲染的 etcd 键。 如果模板设置为处理子键,这可以是一个目录。
|
所有者 | 不 | 细绳 | 将被赋予渲染配置文件所有权的用户名。 |
团体 | 不 | 细绳 | 将被赋予对呈现的配置文件的组所有权的组。 |
模式 | 不 | 细绳 | 应该为渲染文件设置的八进制权限模式。 |
check_cmd | 不 | 细绳 | 用于检查呈现的配置文件语法的命令。 |
reload_cmd | 不 | 细绳 | 应该用于重新加载应用程序配置的命令。 |
字首 | 不 | 细绳 | etcd 层次结构的一部分,位于 keys 指令中的键之前。 这可用于使 .toml 文件更灵活。
|
我们创建的文件告诉我们一些关于我们的 confd
实例将如何运行的重要信息。 我们的 Nginx 容器将使用存储在 /etc/confd/templates/nginx.conf.tmpl
的模板来渲染将放置在 /etc/nginx/sites-enabled/app.conf
的配置文件。 该文件将被授予 0644
权限集,并且所有权将授予 root 用户。
confd
应用程序将在 /services/apache
节点处查找更改。 当看到更改时,confd
将查询该节点下的新信息。 然后它将为 Nginx 渲染一个新的配置。 它将检查配置文件的语法错误,并在文件到位后重新加载 Nginx 服务。
我们现在已经创建了模板资源文件。 我们应该处理将用于渲染 Nginx 配置文件的实际模板文件。
创建 Confd 模板文件
对于我们的模板文件,我们将使用 confd
项目的 GitHub 文档 中的示例来帮助我们开始。
创建我们在上面的配置文件中引用的文件。 将此文件放在我们的 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
以获取我们的 Apache Web 服务器的 IP 地址和端口号。 因此,与其静态定义上游服务器,不如在文件渲染时动态填写这些信息。 我们可以通过对动态内容使用 Go 模板 来做到这一点。
为此,我们将改为使用它作为我们的块:
upstream apache_pool { {{ range getvs "/services/apache/*" }} server {{ . }}; {{ end }} }
让我们解释一下发生了什么。 我们打开了一个块来定义一个名为 apache_pool
的上游服务器池。 在内部,我们指定我们使用双括号开始一些 Go 语言代码。
在这些括号中,我们指定保存我们感兴趣的值的 etcd
端点。 我们使用 range
使列表可迭代。
我们使用它来将从 etcd
中的 /services/apache
位置下方检索到的所有条目传递到 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 上的所有连接并将它们传递到通过查看 etcd
条目生成的 apache_pool
处的服务器池。
当我们处理服务的这方面时,我们应该删除默认的 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 设置。
- 它必须继续监视 Apache 服务器的
etcd
注册更改,以便它可以根据可用的后端服务器重新配置 Nginx。
我们将从 Marcel de Graaf 的 GitHub 页面 获取我们的脚本。 这是一个不错的、简单的脚本,它 正是 我们需要的。 我们只会对我们的场景进行一些小的编辑。
让我们将此脚本放在我们的 confd
可执行文件旁边。 我们称之为 confd-watch
:
vi /usr/local/bin/confd-watch
我们将从传统的 bash
标头开始,以识别我们需要的解释器。 然后我们将设置一些 bash
选项,以便脚本在出现任何问题时立即失败。 它将返回最后一个失败或运行的命令的值。
#!/bin/bash set -eo pipefail
接下来,我们要设置一些变量。 通过使用 bash
参数替换,我们将设置默认值,但构建了一些灵活性,让我们在调用脚本时覆盖硬编码值。 这基本上只是独立设置连接地址的每个组件,然后将它们组合在一起以获得所需的完整地址。
使用以下语法创建参数替换:${var_name:-default_value}
。 这具有使用 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
通过读取调用此脚本时可用的 etcd
中的值来呈现 Nginx 配置文件的初始版本。 我们将使用 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 “运行”命令,我们需要让它在前台运行,这样容器就不会在此时退出。 我们可以通过跟踪日志来做到这一点,让我们可以访问我们一直在记录的所有信息:
#!/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
突出显示的字符串是我们需要的容器 ID。 使用此 ID 以及您的 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 地址。
然后,我们将清理我们的环境,以确保该容器的任何先前版本都被杀死并删除。 我们拉下刚刚创建的容器,以确保我们始终拥有最新版本。
最后,我们将启动容器。 这包括启动容器,为其命名我们在 remove 和 kill 命令中引用的名称,并将运行它的主机的公共 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。 您可以在 Sidekick 模板中增加 sleep 和 TTL 值来避免这种情况。
要查看负载均衡器的运行情况,您可以从运行 Nginx 服务的主机请求 /etc/environments
文件。 这包含主机的公共 IP 地址。 如果您想使此配置更好,请考虑运行一个使用 etcd
注册此信息的 sidekick 服务,就像我们为 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
生成其初始配置。 我们还在后台对其进行了设置,以不断检查更改。 这与基于模板的动态配置生成相结合,使我们能够始终如一地了解后端服务器的最新情况。