如何利用Confd与Etcd对CoreOS中的服务进行动态重新配置

提供:ZStack云计算

系列教程

本教程为CoreOS上手指南系列九篇中的第七篇。

内容介绍

CoreOS允许大家在各集群成员上的Docker容器内轻松运行服务。作为基本流程,我们通常需要启动某服务的一个或多个实例,而后利用etcd加以注册。

在本篇教程中,我们将探讨confd工具,其专门用于观察分布式键-值存储中的内容变化。其运行于Docker容器之风,且可用于触发配置修改与服务重载。

先决条件与既定目标

为了完成本篇教程,大家应当对CoreOS及其各组件拥有基本了解。

如果对这方面内容不太熟悉,大家请首先参阅以下几篇教程:

另外,要了解更多与管理工具相关的内容,则请参阅:

其中“如何创建灵活服务”一文特别重要,因为其中涉及的服务将作为本篇教程内的前端服务。

在今天的文章中,我们将探讨如何利用Nginx创建新的应用容器。其将作为反向代理服务于各个Apache实例。该Nginx容器将利用confd进行配置,负责观察各sidekick服务所负责的服务注册表。

我们将继续使用本系列教程的三节点集群:

  • coreos-1
  • coreos-2
  • coreos-3

下面正式开始今天的内容。

配置后端Apache服务

我们首先从后端Apache服务开始。

登录至COreOS设备:

ssh -A [email protected]_address

Apache容器设置

我们首先创建基本Apache容器。由于之前在其它教程中已经提到,因此这里只简要进行说明。

下面提取基础镜像并启动容器实例:

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

容器启动后,我们会面对一套bash会话。在这里,更新本地apt软件包目录并安装apahce2:

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

这里需要提供用户名、密码以及邮箱地址。

接下来获取刚刚创建的实例的容器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用户名称以及镜像名称。这里我们使用apahce作为名称:

docker commit 1db0c9a40c0duser_name/apache

将新镜像推送至Docker Hub:

docker push user_name/apache

现在即可在服务文件中使用此镜像了。

创建Apache服务模板单元文件

现在我们已经拥有了可用容器,接下来创建模板单元文件,保证fleet与systemd能够正确管理该服务。

在开始之前,首先设置一套目录结构:

cd ~
mkdir static templates instances

现在,我们可以在模板目录内构建自己的模板文件了:

vim templates/[email protected]

将以下内容粘贴至文件中。大家可以参阅创建灵活的fleet服务单元文件教程了解各选项的含义:

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

# Requirements
Requir[email protected]%i.service

# Dependency ordering
[email protected]%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.%iExecStartPre=-/usr/bin/docker rm apache.%iExecStartPre=/usr/bin/docker pull user_name/apacheExecStart=/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
[email protected]*.service

我们这里需要修改以使用专有接口替代公共接口。由于我们的各Apache实例都会通过Nginx反射代理发送流量,因此这部分调整是必不可少的。注意,在DigitalOcean上使用专有接口的话,我们使用的各服务器必须具备“private networking”标记。

另外,注意变更user_name以引用我们的Docker Hub用户名,从而正确提取Docker文件。

创建sidekick模板单元文件

现在,我们需要以同样的方式处理sidekick服务。稍后我们会对内容进行调整以符合自身环境的实际情况。

打开该模板文件:

vim templates/[email protected]

在文件中使用以下信息:

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

# Requirements
[email protected]%i.service

# Dependency ordering and binding
[email protected]%[email protected]%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
[email protected]%i.service

以上配置与之前教程中的内容有所区别。我们利用etcdctl set命令调整了部分值。相较于发送JSON对象,这里我们设置了简单的IP地址加端口的组合。通过这种方式,我们能够直接读取该值以掌握前往此服务的必要连接信息。

我们还调整了相关信息以指定在其它文件中已经使用的专有接口。

进行服务实例化

现在为这些服务创建两个实例。

首先是创建符号链接。前往刚刚创建的~/instances目录,同时链接至服务运行所在的端口。我们希望让两项服务分别运行在端口7777与端口8888上:

cd ~/instances
ln -s ../templates/[email protected] [email protected]
ln -s ../templates/[email protected] [email protected]
ln -s ../templates/[email protected] [email protected]
ln -s ../templates/apache[email protected] [email protected]

现在,我们可以将~/instances目录发送至fleet以启动这些服务:

fleetctl start ~/instances/*

在实例启动后(可能需要几分钟),大家应该能够看到sidekicks生成的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,从而在Nginx每次变更及重新配置时在etcd中查看/services/apache的位置。

创建Nginx容器

我们继续以Ubuntu 14.04为系统平台启动Nginx容器。

安装软件

使用以下命令启动一套新容器:

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

更新我们的本地apt软件包缓存并安装Nginx。我们还需要安装curl,因为基础镜像中并不包含:

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

现在我们可以前往发布页面以获取GitHub上的confd。我们还需要找到其最新稳定版本的链接。目前该版本为v0.5.0,但具体链接可能有所变化。

现在重新回到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"

下面解释其中的部分关键内容:

DirectiveRequired?TypeDescription

src Yes String为用于渲染该信息的模板。如果其处于/etc/confd/templates位置之外,则使用完整路径。

dest Yes String为已渲染配置文件所在的位置。

keys Yes Array of strings为模板使用以实现正确渲染的etcd键。如果该模板被设定为处理子键,则其亦可为目录。

owner No String为作为渲染配置文件持有者的用户名称。

group No String为持有渲染配置文件的组。

mode No String为用于设定渲染文件的octal权限模式。

check_cmd No String用于检查渲染配置文件的语法。

reload_cmd No String命令用于重载该应用的配置。

prefix No String为etcd层次结构中的一部分,位于keys指令中的keys之前,用于确保.toml文件更为灵活。

我们创建的这个文件中包含大量重要内容,用于指定confd实例的功能实现方式。我们的Nginx容器所使用的模板存储于/etc/confd/templates/nginx.conf.tmpl内,用于渲染将被放置在/etc/nginx/sites-enabled/app.conf处的配置文件。该文件将被赋予0644权限组,并归属于root用户。

此confd应用将查找/services/apache节点中的变更。当变更出现时,confd会查询此节点中的新信息,而后为Nginx渲染新的配置。另外,它还能够对已有文件进行语法错误检查并重载Nginx服务。

现在我们的模板资源文件已经创建完成。

创建Confd模板文件

我们将使用confd项目GitHub说明文档内提供的模板文件。

创建的这个文件将在之前完成的配置文件内加以引用。将此文件放在我们的模板目录内:

vi /etc/confd/templates/nginx.tmpl

在文件中,我们需要重新创建标准Nginx反向代理配置文件。这里我们会使用部分Go模板化语法,用于替换confd从etcd中提取到的信息。

首先利用upstream服务器配置该块。此部分用于定义Nginx能够作为请求发送目标的服务器池。其格式如下:

upstreampool_name {
serverserver_1_IP:port_num;
serverserver_2_IP:port_num;
serverserver_3_IP:port_num;
}

这样我们就能将请求发送至pool_name处,而Nginx则会选择定义服务器之一来处理请求内容。

我们的模板文件负责解析etcd以获取Apache Web服务器的IP地址与端口编号。因此相较于以状态化方式定义upstream服务器,我们可以在所渲染的文件中动态填充这些信息。具体方式请参阅Go模板中的动态内容介绍。

要完成这一目标,我们使用以下内容作为配置块:

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

下面解释其具体含义。我们打开的块负责定义名为apache_pool的上游服务器池。在其中,我们使用双大括号以指定Go代码。

在这些大括号中,我们指定所需值的来源etcd端点。我们使用range使列表具备可迭代性。

我们将etcd中/services/apache位置下的全部条目发送至range块。而后,我们在利用“{{}}”中的一个点(.)来插入当前迭代中的键值。在range循环中,我们借此填充服务器池。最后,我们使用{{end}}指令作为循环结尾。

注意:记得在循环内的server指令后加上分号。如果不添加,则会造成配置无法生效。

在服务器池设置完成后,我们可以使用代理将全部连接引导至该池。需要注意的是access_log,这里我们暂时使用自定义格式:

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

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

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

location / {
    proxy_passhttp://apache_pool;
    proxy_redirectoff;
    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位置的服务器池。

另外,我们还需要移除默认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‘;

sendfileon;
. . .

完成后保存并退出。

创建脚本以运行Confd

我们需要创建一套脚本,负责利用模板资源文件并在合适的时间段内调用confd。

此脚本必须肩负以下两项职责,从而保证我们的服务能够正常运作:

  • 必须根据当前后端基础设施状态对Nginx设置进行初始化,从而匹配容器启动操作。
  • 必须持续观察etcd注册表中的Apache服务器变更,从而根据后端服务器可用状态重新配置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对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_PORTecho"[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; doecho"[nginx] waiting for confd to create initial nginx configuration"
sleep 5done

在初始配置设置完成后,脚本接下来需要以持续方式运行。我们要借此确保Nginx能够借此发现一切变更并保持更新。

这一次,我们设置连续轮询间隔,并将其无限期运行在后台进程内。我们提交同样的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_PORTecho"[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; doecho"[nginx] waiting for confd to create initial nginx configuration."
sleep 5done# 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 configecho"[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 de4f30617499user_name/nginx_lb

如果需要,请登录Docker Hub账户:

docker login

现在已经可以进行镜像提交了:

docker push user_name/nginx_lb

构建Nginx Static单元文件

下面我们需要构建一个单元文件,其负责启动我们创建完成的容器。在这里,我们使用fleet以控制此流程。

由于此文件没有现成模板可用,因此我们将在此前创建的~/static目录中从头进行创建:

vim static/nginx_lb.service

下面从标准的[Unit]部分开始,描述该服务并定义其依赖性与顺序:

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

# Requirements
Requires=etcd.serviceRequires=docker.service

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

接下来,我们定义文件中的[Serivce]部分。在这里我们将超时设置为0,同时将killmode设置为无。我们需要再次引入环境文件,从而访问此容器运行所在主机的公共与专有IP地址。

之后,我们将清理环境以确保该容器的早期版本不再存在。现在提取刚刚创建的容器以确保其处于最新版本。

最后,启动此容器。在此过程中,我们需要启动容器,根据remove与kill命令中的引用内容为其命名,而后为其提供运行所在主机的公共IP地址并映射至端口80。我们调用此前创建的confd-watch脚本作为run命令:

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

# Requirements
Requires=etcd.serviceRequires=docker.service

# Dependency ordering
After=etcd.serviceAfter=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_lbExecStartPre=-/usr/bin/docker rm nginx_lbExecStartPre=/usr/bin/docker pull user_name/nginx_lbExecStart=/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.serviceRequires=docker.service

# Dependency ordering
After=etcd.serviceAfter=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_lbExecStartPre=-/usr/bin/docker rm nginx_lbExecStartPre=/usr/bin/docker pull user_name/nginx_lbExecStart=/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][email protected]*.service

完成后保存并退出。

运行Nginx负载均衡

现在我们拥有此前完成的两套Apache实例。下面进行检查:

fleetctl list-units

UNIT                MACHINE             ACTIVE  SUB
apache-discovery[email protected]   197a1662.../10.132.249.206  active  running
[email protected]   04856ec4.../10.132.249.212  active  running
[email protected]     197a1662.../10.132.249.206  active  running
[email protected]     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

整个启动过程可能需要耗费1分钟左右,具体取决于镜像的获取速度。启动完成后,如果使用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条目已经被重新评估,同时生成新的配置文件。如果新生成的文件与原有文件的md5不符,则原有文件会被替换,服务也随之重载。

如此一来,我们的负载均衡服务最终将能够追踪Apache后端服务器。如果confd进行不断更新,则可能是因为我们的Apache实例刷新TTL的频率过高。这时,大家可以增加sidekick模板中的sleep与TTL值以解决这个问题。

要查看运行中的负载均衡机制,大家可以从运行Nginx服务的主机上查询/etc/environments文件。其中包含该主机的公共IP地址。如果大家希望进一步改善此配置,则可以考虑运行一项sidekick服务以将此信息注册至etcd:

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来生成其初始配置。我们也可以在后台设置以持续检查各项变更。这套体系与基于模板的动态配置相结合,将使我们实现后端服务器的不断更新。

本文来源自DigitalOcean Community。英文原文:How To Use Confd and Etcd to Dynamically Reconfigure Services in CoreOS By Justin Ellingwood

翻译:diradw

时间: 2024-11-03 03:37:22

如何利用Confd与Etcd对CoreOS中的服务进行动态重新配置的相关文章

如何利用fleet单元文件为CoreOS集群创建高灵活性服务

提供:ZStack云计算 系列教程 本教程为CoreOS上手指南系列九篇中的第六篇. 内容简介 CoreOS能够利用一系列工具以集群化与Docker容器化方式简化服务管理工作.其中etcd负责将各独立节点联系起来并提供全局数据平台,而大部分实际服务管理任务则由fleet守护进程实现. 在上一篇教程中,我们了解了如何利用fleetctl命令操纵服务及集群成员.在今天的教程中,我们将了解如何利用单元文件定义服务. 在接下来的内容中,我们将探讨如何构建fleet单元文件,外加在生产环境下提升服务健壮性

asp.net中利用JSON进行增删改查中运用到的方法

//asp.net中 利用JSON进行操作, //增加: //当点击"增加链接的时候",弹出增加信息窗口,然后,在窗体中输入完整信息,点击提交按钮. //这里我们需要考虑这些:我会进行异步提交,使用jquery中的方法,$.post("网页名",JSON,callback); //JSON的写法:{"name":name,"id":id},那我们对其进行假设,比方说,表单中的textbox很多,需要我们填写的信息 //也很多,

aspx利用cookie值来停止silverlight中的计时器

一.silverlight与silverlight中可以利用委托(delegate)来刷新frame.Refresh() 1.在子类中定义委托捕捉关闭事件按钮 1 public delegate void onCloseClick(object sender, RoutedEventArgs e); 2 public onCloseClick onclose; 3 private void CancelButton_Click(object sender, RoutedEventArgs e)

利用Entity Framework修改指定字段中的值

利用Entity Framework修改指定字段中的值一般我们编辑某些模型的时候会用到类似这样的代码: [HttpPost] public ActionResult Edit(Article model) { if (model.Id == 0) { return HttpNotFound(); } using (db) { db.Entry(model).State = EntityState.Modified; db.SaveChanges(); } return RedirectToAct

利用栈判断输入的表达式中的括号是否匹配(假设只含有左、右括号)

利用栈判断输入的表达式中的括号是否匹配(假设只含有左.右括号) bool Match(char exp[],int n) { int i=0; char e; bool match=true; SqStack *st; InitStack(st);//初始化栈 while(i<n && match)//扫描exp中所有字符 { if(exp[i]=='(')//当前字符为左括号,将其进栈 Push(st,exp[i]); else if(exp[i]==')')//当前字符为右括号

Java在利用反射条件下替换英文字母中的值

(1)创建两个Class: ReflectTest类如下: package cn.itcast.day01; import java.lang.reflect.Constructor; import java.lang.reflect.Field; public class ReflectTest { public static void main(String[] args) throws Exception { changeStringValue(pt1); System.out.print

Web前端开发如何利用css样式来控制Html中的h1/h2/h3标签不换行

  H1/H2/H3/H4标题标签常常使用在一个网页中唯一标题.重要栏目.重要标题等情形下. H1在一个网页中最好只使用一次,如对一个网页唯一标题使用.H2.H3.H4标签则可以在一个网页中多次出现,但必要随意添加或添加过度. 在Web前端开发中,经常要使用H1标签对关键字进行优化,可是如果是一行文字中的某个词加上了H1标记,就会换行.可以使用下面的方法,H标签就不会强制换行了.Css控制为一行文字中某个字加上<h2>标签不换行,display:inline; 解释为:内联对象的默认值.用该值

【转】asp.net 利用Global.asax 捕获整个解决方案中的异常错误

之前做项目的时候都是在每个页面中处理这不同的异常信息,一个页面数下来,很多个try{}catch{}语句块,令整个代码结构有些不够美观. 今天看到一篇帖子,是关于利用全局应用程序类来帮忙获取异常信息,利用 server.Transfer('''')指定接受错误的页面:加上在接受错误页面中利用 server.GetLastError() 获取前一个异常源. Global.asax 中的Application_Error 函数如下: protected void Application_Error(

2-5 利用RestTemplateCore简化调用Consul中的服务

1.必须要安装RestTemplateCore包 2.请求服务,必须要知道 a Consul服务器的地址:b 请求的服务名 ;c 具体请求的api接口 利用RestTemplateCore简化调用Consul中的服务代码如下: 原文地址:https://www.cnblogs.com/wholeworld/p/9291090.html