最近,我越来越喜欢不可变服务器的概念,同时使Zapier的基础架构自动化 。 这个概念很简单:永远不要在活动服务器上进行服务器升级或更改,而只是使用应用的更新来构建新服务器,而丢弃旧服务器。 您基本上可以在基础结构级别上获得不变性的所有好处,而且您不必担心配置漂移。 甚至更好的是,我不必再担心,尽管进行了广泛的测试,但有人还是会推动人为改变的伪装清单,使我们的前端Web服务器崩溃(确保我们可以回滚更改并进行恢复,但是仍然存在少量潜在中断)担心)。
显然,您需要一些好的工具来实现这一目标。 最近与Packer的一些鬼混让我整理了一个到目前为止令我有些满意的设置。
节点
在我们的基础架构项目中,我们有一个nodes.yaml,用于定义节点名称及其所属的AWS安全组。 这非常简单,可用于多种其他工具(例如vagrant )。
elasticsearch:
group: logging
zookeeper:
group: zookeeper
redis:
group: redis
size: m2.2xlarge
Rakefile
我们将此节点.yaml文件与rake一起使用,以生成打包程序模板以构建新的AMI。 这使我不必管理大量的打包程序模板,因为它们大多具有相同的功能。
require 'erb'
require 'yaml'
namespace :packer do
task :generate do
current_dir = File.dirname(__FILE__)
nodes = YAML.load_file( "#{current_dir}/nodes.yml")
nodes.each_key do |node_name|
include ERB::Util
template = File.read("#{current_dir}/packs/template.json.erb")
erb = ERB.new(template)
File.open("#{current_dir}/packs/#{node_name}.json", "w") do |f|
f.write(erb.result(binding))
end
end
end
end
它与简单的erb模板结合使用,该模板将节点名简单地注入其中。
{
"builders": [{
"type": "amazon-ebs",
"region": "us-east-1",
"source_ami": "ami-10314d79",
"instance_type": "t1.micro",
"ssh_username": "ubuntu",
"ami_name": "<%= node_name %> {{.CreateTime}}",
"security_group_id": "packer"
}],
"provisioners": [{
"type": "shell",
"script": "packs/install_puppet.sh"
}, {
"type": "shell",
"inline": [
"sudo apt-get upgrade -y",
"sudo sed -i /etc/puppet/puppet.conf -e \"s/nodename/<%= node_name %>-$(hostname)/\"",
"sudo puppet agent --test || true"
]
}]
这将为每个节点生成一个打包程序模板
- 在us-east-1中创建一个AMI
- 使用Ubuntu Server 13.04 AMI开始
- 将安全组设置为EC2中的打包程序。 我们创建此文件,并允许其访问puppetmaster的安全组。 否则,打包程序将创建一个随机的临时安全组,该组将无权访问任何其他组(如果至少遵循最佳实践)!
- 安装木偶
- 运行一次木偶以配置系统
我们也永远不会启用人偶代理(默认情况下不启动),以便它从不轮询更新。 我们还可以在完成后从服务器中删除木偶,以免AMI进入。
剧本
Packer具有使用户能够指定要运行的Shell命令和Shell文件的出色功能。 这对于引导程序是很好的,但对于执行更适合puppet的配置管理级别来说却不是那么好。 因此,我们的打包程序模板调用了一个shell脚本,以确保我们不使用过时的ruby linux发行版将其默认设置为安装木偶。 在安装过程中,它还会指定人偶主服务器名称(如果您使用VPC而不是EC2 classic,则不需要此名称,因为您只需将内部dns“人偶”分配给人偶主服务器即可)。
sleep 30,
wget http://apt.puppetlabs.com/puppetlabs-release-raring.deb
sudo dpkg -i puppetlabs-release-precise.deb
sudo apt-get update
sudo apt-get remove ruby1.8 -y
sudo apt-get install ruby1.9.3 puppet -y
sudo su -c 'echo """[main]
logdir=/var/log/puppet
vardir=/var/lib/puppet
ssldir=/var/lib/puppet/ssl
rundir=/var/run/puppet
factpath=$vardir/lib/facter
templatedir=$confdir/templates
[agent]
server = ip-10-xxx-xx-xx.ec2.internal
report = true
certname=nodename""" >> /etc/puppet/puppet.conf'
建立它
现在,我们需要做的就是为packer build packs/redis.json
构建一个新的AMI,运行packer build packs/redis.json
并开始繁荣! 创建,配置,映像和终止服务器。 现在,只需在jenkins中设置一些作业即可根据某些触发器生成这些作业,那么您就可以使不可变的基础架构实现自动化又迈了一步。
打扫干净
当然,您生成的每个AMI每天都会花费您一分钱或类似的费用。 这可能看起来很小,但是一旦每个AMI进行了100次修订,就会花钱! 因此,作为最后一步,我整理了一个简单的fabfile脚本来清理旧图像。 事实证明这很简单,因为我们在AMI名称中包含了unix时间戳。
import os
import boto
from fabric.api import task
class Images(object):
def __init__(self, **kwargs):
self.conn = boto.connect_ec2(**kwargs)
def get_ami_for_name(self, name):
(keys, AMIs) = self.get_amis_sorted_by_date(name)
return AMIs[0]
def get_amis_sorted_by_date(self, name):
amis = self.conn.get_all_images(filters={'name': '{}*'.format(name)})
AMIs = {}
for ami in amis:
(name, creation_date) = ami.name.split(' ')
AMIs[creation_date] = ami
# remove old ones!
keys = AMIs.keys()
keys.sort()
keys.reverse()
return (keys, AMIs)
def remove_old_images(self, name):
(keys, AMIs) = self.get_amis_sorted_by_date(name)
while len(keys) > 1:
key = keys.pop()
print("deregistering {}".format(key))
AMIs[key].deregister(delete_snapshot=True)
@task
def cleanup_old_amis(name):
'''
Usage: cleanup_old_amis:name={{ami-name}}
'''
images = Images(
aws_access_key_id=os.environ['AWS_ACCESS_KEY_ID'],
aws_secret_access_key=os.environ['AWS_SECRET_ACCESS_KEY']
)
images.remove_old_images(name)
将其设置为生成AMI的jenkins作业的构建后作业,您始终要确保只有最新的作业。 您可能还可以对此进行调整,以保留最后5个AMI,以便进行归档。
下一步是什么?
我承认我对这个概念还是有点新鲜。 理想情况下,我会很乐意将我们的基础架构调整到每个月(或每周!)服务器都可以使用新副本进行回收的地步。 诸如Web服务器或队列之类的瞬态服务器可以很容易地工作。 使用数据存储,这可能会更加棘手,因为您需要一种有效的策略来启动主实例的副本,将副本升级为主实例并淘汰旧的主实例。
最后的挑战是确定允许什么级别的可变性。 部署显然很好,因为它们不需要调整服务器配置,但是添加/删除用户又如何呢? 我们是采用全有还是全无的方法,还是允许在无需完全重建服务器的情况下更新SSH公钥之类的小细节?
翻译自: https://www.javacodegeeks.com/2013/07/immutable-servers-with-packer-and-puppet.html