通过Chef和Test Kitchen+Serverspec和Docker和Packer,开始进行基础设施即代码的工作
利用Chef、Test Kitchen+Serverspec、Docker、以及HashiCorp Packer,来体验基础设施即代码(Infrastructure as Code)。
-
- 使用Test Kitchen + Serverspec,在本地Docker容器上进行Cookbook的测试。
-
- 使用Packer从Cookbook生成Docker镜像,在本地启动已应用Cookbook的Docker容器进行操作验证。
- 使用Knife zero将经过测试的Cookbook应用到其他服务器上。
需要应用 Cookbook 的操作系统是 Ubuntu14.04、CentOS6 和 CentOS7 这三种。
验证环境
本次验证是在Amazon Web Services的东京区域进行的,但在其他云服务或裸金属服务器上也应该可以通过类似的步骤进行再现。可能的话。
- chef zero(knife zero)を動作させるWorkstationのスペック
环境建设
进行工作站的操作系统环境设置和中间件的安装。
允许没有tty的sudo执行
为了在Test Kitchen中使用Docker时需要的,我们将更改sudoers的设置,以便能够在没有tty的情况下使用sudo。
[centos@ip-10-0-0-11 ~]$ sudo sed -i -e 's/^Defaults requiretty/Defaults !requiretty/g' /etc/sudoers
[centos@ip-10-0-0-11 ~]$ sudo grep "requiretty" /etc/sudoers
Defaults !requiretty
安装包
安装各种软件包。
[centos@ip-10-0-0-11 ~]$ sudo yum update -y
[centos@ip-10-0-0-11 ~]$ sudo yum install git wget unzip -y
安装Docker
安装并启动Docker,并设置为自动启动Docker服务。
[centos@ip-10-0-0-11 ~]$ curl -sSL https://get.docker.com/ | sh
[centos@ip-10-0-0-11 ~]$ sudo usermod -aG docker centos
[centos@ip-10-0-0-11 ~]$ sudo systemctl enable docker.service
[centos@ip-10-0-0-11 ~]$ sudo systemctl start docker.service
如果您先注销一次,然后重新登录CentOS,就可以开始在CentOS中使用Docker了。
[centos@ip-10-0-0-11 ~]$ docker run hello-world
安装Packer
安装 Packer 并将其添加到路径中。
(在 CentOS7 中,已经安装了名为 /usr/sbin/packer 的其他 Packer 执行文件。请注意在 PATH 设置中不要错误地使用此 Packer)
[centos@ip-10-0-0-11 ~]$ wget -O /tmp/packer.zip https://dl.bintray.com/mitchellh/packer/packer_0.8.6_linux_amd64.zip
[centos@ip-10-0-0-11 ~]$ mkdir packer
[centos@ip-10-0-0-11 ~]$ unzip /tmp/packer.zip -d ./packer
[centos@ip-10-0-0-11 ~]$ echo 'export PATH=$HOME/packer:$PATH' >> ~/.bash_profile
[centos@ip-10-0-0-11 ~]$ source .bash_profile
[centos@ip-10-0-0-11 ~]$ which packer
~/packer/packer
安装Chef开发工具包
各种工具组成的以厨师为中心的生态系统是由Ruby编写的,并可以从rubygems上安装。然而,安装Ruby和这些gem涉及本地编译等一些麻烦事,因此这次我们将使用由厨师的开发者提供的”厨师开发工具包”——Chef Development Kit。
[centos@ip-10-0-0-11 ~]$ sudo yum install https://opscode-omnibus-packages.s3.amazonaws.com/el/7/x86_64/chefdk-0.7.0-1.el7.x86_64.rpm -y
准备作业仓库
创建基础设施即代码所需的Chef存储库。
[centos@ip-10-0-0-11 ~]$ chef generate repo chef-repo
[centos@ip-10-0-0-11 ~]$ cd chef-repo/
由于这次使用Knife和Test Kitchen,我们需要使用chef zero,在chef配置中设置使用本地模式。
[centos@ip-10-0-0-11 chef-repo]$ mkdir .chef
[centos@ip-10-0-0-11 chef-repo]$ echo 'local_mode true' > .chef/knife.rb
安装缺失的 gem。
创建一个Gemfile,并安装Chef Development Kit未包含的gem。
[centos@ip-10-0-0-11 chef-repo]$ vi Gemfile
[centos@ip-10-0-0-11 chef-repo]$ chef exec bundle install
source "https://rubygems.org"
gem "knife-zero"
gem "test-kitchen"
gem "kitchen-docker"
gem "serverspec"
也许,test-kitchen和serverspec可能会包含在Chef Development Kit中。
准备烹饪书籍。
这次简单起见,创建一个用于从包中安装Apache2和PHP并显示测试页面的食谱(不使用在 Chef Supermarket 公开的第三方 cookbook)。
为了使其能够在Ubuntu14.04和CentOS6、CentOS7上运行,将环境依赖部分,如包名等,移至attribute中。
生成烹饪书的模板
使用ChefDK的chef命令创建cookbook的模板。
[centos@ip-10-0-0-11 chef-repo]$ mkdir site-cookbooks
[centos@ip-10-0-0-11 chef-repo]$ chef generate cookbook site-cookbooks/testweb
[centos@ip-10-0-0-11 chef-repo]$ chef generate attribute site-cookbooks/testweb default
[centos@ip-10-0-0-11 chef-repo]$ chef generate template site-cookbooks/testweb index.php.erb
[centos@ip-10-0-0-11 chef-repo]$ touch Berksfile
食谱的修改
创建一个可执行以下操作的操作步骤清单。
-
- 更新包(使用yum update -y或apt-get update)
-
- 安装apache2和php
-
- 配置apache2的自动启动设置并重新启动
- 从模板生成并部署PHP文件
#
# Cookbook Name:: testweb
# Recipe:: default
#
# Copyright (c) 2015 The Authors, All Rights Reserved.
execute "package update" do
user "root"
command node["pkg"]["update"]
action :run
end
[node["pkg"]["httpd"], node["pkg"]["php"]].each do |pkg|
package pkg do
action :install
end
end
service node["pkg"]["httpd"] do
action [:enable, :restart]
end
template "#{node["web"]["docroot"]}/index.php" do
source "index.php.erb"
owner "root"
group "root"
mode 0644
variables(
:platform => node["platform"],
:version => node["platform_version"]
)
end
属性的修改
定义变量以供recipe使用。根据操作系统不同,包名称会有所变化,Chef会参考收集到的操作系统信息来设置适当的值。
default["web"]["docroot"] = "/var/www/html"
case node["platform_family"]
when "rhel", "fedora"
default["pkg"]["update"] = "yum update -y"
default["pkg"]["httpd"] = "httpd"
default["pkg"]["php"] = "php"
when "debian"
default["pkg"]["update"] = "apt-get update -y"
default["pkg"]["httpd"] = "apache2"
default["pkg"]["php"] = "php5"
else
raise
end
修改模板
创建用于由Apache2调用的PHP文件的模板。使用由配方提供的变量替换<%= … %>部分。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8"/>
<title>Web Test</title>
</head>
<body>
<h1>Web Test</h1>
<p>このサーバのプラットフォーム : <%= @platform %> <%= @version %></p>
<p>このサーバのIPアドレス : <?php echo $_SERVER['SERVER_ADDR']; ?></p>
</body>
</html>
解决cookbook的依赖关系
这次虽然没有使用第三方的cookbook,但是我使用自己制作的cookbook包括了 “testweb”,并通过Berkshelf解决了cookbook的依赖关系。
[centos@ip-10-0-0-11 chef-repo]$ vi Berksfile
[centos@ip-10-0-0-11 chef-repo]$ berks vendor cookbooks
source "https://supermarket.chef.io"
cookbook "testweb", path: "./site-cookbooks/testweb"
准备Test Kitchen + Serverspec。
使用Docker为”testweb”创建的上述cookbook,准备Test Kitchen + Serverspec环境以进行测试。
创建Test Kitchen的模板
首先创建Test Kitchen的模板。虽然chefignore有冲突,但可以快速忽略它。
[centos@ip-10-0-0-11 chef-repo]$ kitchen init --driver=kitchen-docker
create .kitchen.yml
conflict chefignore
Overwrite /home/centos/chef-repo/chefignore? (enter "h" for help) [Ynaqdh] n
skip chefignore
create test/integration/default
append .gitignore
append .gitignore
生成适用于Test Kitchen的CentOS7系列Docker镜像。
CentOS7的initd已经改为systemd,而DockerHub上的CentOS7镜像中不包含/sbin/service。因此,CentOS7不能像Ubuntu14.04或CentOS6那样方便地从Test Kitchen使用。
根据2k0ri的kitchen-docker,创建一个基于已安装initscripts的CentOS7 Docker容器的映像。
[centos@ip-10-0-0-11 chef-repo]$ mkdir docker
[centos@ip-10-0-0-11 chef-repo]$ cd docker
[centos@ip-10-0-0-11 docker]$ vi Dockerfile
[centos@ip-10-0-0-11 docker]$ docker build -t testweb/centos7 .
[centos@ip-10-0-0-11 docker]$ cd ..
FROM centos:centos7
RUN yum install -y initscripts
创建Test Kitchen的配置文件
修改Test Kitchen的配置文件.kitchen.yml。修改的三个要点如下:
-
- chef soloではなくchef zeroを使うようにprovisionerを変更
-
- Ubuntu、CentOS6、CentOS7でテストするようにplatformsを変更
CentOS7では、先ほど作成した initscripts インストール済みのイメージを、特権モードかつ起動コマンドを /sbin/init にして使用する
先ほど作ったcookbokを利用するようにrun_listを変更
---
driver:
name: docker
provisioner:
name: chef_zero
platforms:
- name: ubuntu-14.04
- name: centos-7.1
driver_config:
image: testweb/centos7
privileged: true
run_command: /sbin/init; sleep 3
- name: centos-6.6
suites:
- name: default
run_list:
- recipe[testweb]
attributes:
使用Serverspec创建测试脚本
创建一个用于测试名为”testweb”的cookbook的Serverspec脚本。根据Test Kitchen的规则,该脚本必须以*_spec.rb的名称存放在serverspec目录下。
[centos@ip-10-0-0-11 chef-repo]$ mkdir -p test/integration/default/serverspec
[centos@ip-10-0-0-11 chef-repo]$ mkdir -p test/integration/helpers/serverspec
[centos@ip-10-0-0-11 chef-repo]$ vi test/integration/helpers/serverspec/spec_helper.rb
[centos@ip-10-0-0-11 chef-repo]$ vi test/integration/default/serverspec/testweb_spec.rb
require 'serverspec'
set :backend, :exec
require 'spec_helper'
pkg = /redhat/i =~ os[:family] ? "httpd" : "apache2"
describe package(pkg) do
it { should be_installed }
end
describe service(pkg) do
it { should be_enabled }
it { should be_running }
end
describe process(pkg) do
it { should be_running }
end
describe port(80) do
it { should be_listening }
end
describe command("curl -LI http://localhost/index.php") do
its(:stdout) { should contain("HTTP/1.1 200 OK") }
end
describe command("curl http://localhost/index.php") do
its(:stdout) { should contain("<h1>Web Test</h1>") }
end
以下是要执行的7项测试。
-
- apache2のパッケージがインストールされているか
-
- apache2のサービスが自動起動する設定になっており、かつ起動中か
-
- apache2のプロセスが立ち上がっているか
-
- ポート80が待ち受けているか
-
- localhost/index.php にアクセスすると、ステータスコードが200でレスポンスが得られるか
-
- localhost/index.php から得られたHTMLに
Web Test
という文字列が含まれているか
测试厨房的执行
当执行 kitchen test 时,将自动执行以下1至5步骤。
如果测试成功,应该显示7个例子,0个失败。
[centos@ip-10-0-0-11 chef-repo]$ kitchen test
...
Package "apache2"
should be installed
Service "apache2"
should be enabled
should be running
Process "apache2"
should be running
Port "80"
should be listening
Command "curl -LI http://localhost/index.php"
stdout
should contain "HTTP/1.1 200 OK"
Command "curl http://localhost/index.php"
stdout
should contain "<h1>Web Test</h1>"
Finished in 0.11714 seconds (files took 0.61704 seconds to load)
7 examples, 0 failures
...
通过测试,我们确认在Ubuntu14.04、CentOS6和CentOS7上,我们的cookbook “testweb” 可以正常运行。
使用Packer创建Docker镜像
接下来,使用Packer从经过测试的菜谱创建Docker镜像。
创建配置文件
创建一个用于生成Docker镜像的配置文件,将配置分为以下三个阶段来描述。
builder : 指定したDockerイメージをPULLする
provisioner : PULLしたDockerイメージを元にプロビジョニングを行って、Dockerイメージを生成する
post-processor : 生成したDockerイメージをローカルリポジ
トリに登録する
今回は実行していないが、自動的にDockerHubにPUSHすることもできる
[centos@ip-10-0-0-11 chef-repo]$ mkdir packer
[centos@ip-10-0-0-11 chef-repo]$ vi packer/packer_docker_ubuntu14.04.json
[centos@ip-10-0-0-11 chef-repo]$ vi packer/packer_docker_centos6.json
[centos@ip-10-0-0-11 chef-repo]$ vi packer/packer_docker_centos7.json
在Ubuntu14.04中使用配置文件。
Packer在其内部使用curl安装Chef Client,但从DockerHub下载的Ubuntu14.04镜像中没有安装curl。
在执行chef-solo provisioner之前,在shell provisioner中安装curl,然后应用cookbook “testweb”。
{
"builders": [
{
"type": "docker",
"image": "ubuntu:14.04",
"export_path": "image.tar"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"apt-get update -y",
"apt-get install -y curl"
]
},
{
"type": "chef-solo",
"cookbook_paths": ["cookbooks"],
"run_list": ["testweb::default"]
}
],
"post-processors": [
{
"type": "docker-import",
"repository": "testweb/packer-docker-ubuntu14.04",
"tag": "0.1"
}
]
}
用于配置CentOS6的文件
此次的《testweb》食谱,安装该服务需要使用sudo,但是从DockerHub拉取的CentOS6镜像中并没有安装sudo。
在执行chef-solo provisioner之前,在shell provisioner中安装sudo,并配置使其可以在没有tty的情况下进行sudo,然后应用cookbook “testweb”。
{
"builders": [
{
"type": "docker",
"image": "centos:6",
"export_path": "image.tar"
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"yum -y update",
"yum install -y sudo",
"sed -i -e 's/^Defaults requiretty/Defaults !requiretty/g' /etc/sudoers"
]
},
{
"type": "chef-solo",
"cookbook_paths": ["cookbooks"],
"run_list": ["testweb::default"]
}
],
"post-processors": [
{
"type": "docker-import",
"repository": "testweb/packer-docker-centos6",
"tag": "0.1"
}
]
}
用于CentOS7的配置文件
CentOS 7变换了initd为systemd,此外,DockerHub上的CentOS 7镜像中不包含/sbin/service。同时,要在容器内启动服务,必须具有特权且将启动命令设置为/sbin/init。
因此,在shell provisioner上不仅需要安装必要的软件包,还需要在builder中显式地指定Packer在docker运行时的命令选项。
{
"builders": [
{
"type": "docker",
"image": "centos:7",
"export_path": "image.tar",
"run_command": [
"--privileged",
"-d",
"{{.Image}}",
"/sbin/init"
]
}
],
"provisioners": [
{
"type": "shell",
"inline": [
"yum -y update",
"yum install -y sudo initscripts",
"sed -i -e 's/^Defaults requiretty/Defaults !requiretty/g' /etc/sudoers"
]
},
{
"type": "chef-solo",
"cookbook_paths": ["cookbooks"],
"run_list": ["testweb::default"]
}
],
"post-processors": [
{
"type": "docker-import",
"repository": "testweb/packer-docker-centos7",
"tag": "0.1"
}
]
}
使用Packer生成的Docker镜像的启动
使用Packer工具创建并启动Ubuntu14.04、CentOS6和CentOS7的Docker镜像,确保容器按照”testweb” cookbook的要求进行配置。
生成 Docker 镜像
使用以下命令生成每个Docker镜像。
[centos@ip-10-0-0-11 chef-repo]$ packer build packer/packer_docker_ubuntu14.04.json
[centos@ip-10-0-0-11 chef-repo]$ packer build packer/packer_docker_centos6.json
[centos@ip-10-0-0-11 chef-repo]$ packer build packer/packer_docker_centos7.json
启动和确认Docker容器
注意:根据生成的图像来启动Docker容器。请注意,CentOS7的常规操作与其他操作步骤不同。
Ubuntu14.04是一个操作系统。
[centos@ip-10-0-0-11 chef-repo]$ docker run -it testweb/packer-docker-ubuntu14.04:0.1 /bin/bash
root@ad4c41852056:/# service apache2 start
* Starting web server apache2 AH00558: apache2: Could not reliably determine the server's fully qualified domain name, using 172.17.0.25. Set the 'ServerName' directive globally to suppress this message
*
root@ad4c41852056:/# curl http://localhost/index.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8"/>
<title>Web Test</title>
</head>
<body>
<h1>Web Test</h1>
<p>このサーバのプラットフォーム : ubuntu 14.04</p>
<p>このサーバのIPアドレス : ::1</p>
</body>
</html>
CentOS6 是 Linux 的一个操作系统分支。
[centos@ip-10-0-0-11 chef-repo]$ docker run -it testweb/packer-docker-centos6:0.1 /bin/bash
[root@2449245b8eaf /]# service httpd start
Starting httpd: httpd: Could not reliably determine the server's fully qualified domain name, using 172.17.0.26 for ServerName
[ OK ]
[root@2449245b8eaf /]# curl http://localhost/index.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8"/>
<title>Web Test</title>
</head>
<body>
<h1>Web Test</h1>
<p>このサーバのプラットフォーム : centos 6.7</p>
<p>このサーバのIPアドレス : ::1</p>
</body>
</html>
CentOS7 这个操作系统版本为 CentOS7
[centos@ip-10-0-0-11 chef-repo]$ CID=$(docker run --privileged -d testweb/packer-docker-centos7:0.1 /sbin/init)
[centos@ip-10-0-0-11 chef-repo]$ docker exec -it $CID /bin/bash
[root@21b348656b3f /]# service httpd start
Redirecting to /bin/systemctl start httpd.service
[root@21b348656b3f /]# curl http://localhost/index.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8"/>
<title>Web Test</title>
</head>
<body>
<h1>Web Test</h1>
<p>このサーバのプラットフォーム : centos 7.1.1503</p>
<p>このサーバのIPアドレス : ::1</p>
</body>
</html>
他的服务器配置
最后,将 “testweb” 这本烹饪书应用到其他服务器上。
将SSH密钥传输
利用Knife工具,使用密钥在SSH上实现连接,将注册在AWS上的密钥的pem文件通过scp传输至Chef zero工作站。
nmatsui@develop:~$ scp -i .ssh/aws_tokyo.pem /home/nmatsui/.ssh/aws_tokyo.pem centos@XXX.XXX.XXX.XXX:~/.ssh
启动EC2实例
指定传送的密钥文件,启动3个EC2实例,包括Ubuntu14.04(10.0.0.41),CentOS6(10.0.0.42),以及CentOS7(10.0.0.43)。
执行Bootstrap
执行knife zero bootstrap命令,将要配置的节点纳入管理。
[centos@ip-10-0-0-11 chef-repo]$ knife zero bootstrap 10.0.0.41 -x ubuntu -i ~/.ssh/aws_tokyo.pem --sudo
[centos@ip-10-0-0-11 chef-repo]$ knife zero bootstrap 10.0.0.42 -x root -i ~/.ssh/aws_tokyo.pem --sudo
[centos@ip-10-0-0-11 chef-repo]$ knife zero bootstrap 10.0.0.43 -x centos -i ~/.ssh/aws_tokyo.pem --sudo
[centos@ip-10-0-0-11 chef-repo]$ knife node list
ip-10-0-0-41.ap-northeast-1.compute.internal
ip-10-0-0-42.ap-northeast-1.compute.internal
ip-10-0-0-43.ap-northeast-1.compute.internal
运行列表的注册
在被管理的节点中的run_list中,注册”testweb”菜谱的配方。
[centos@ip-10-0-0-11 chef-repo]$ knife node run_list add ip-10-0-0-41.ap-northeast-1.compute.internal 'recipe[testweb::default]'
ip-10-0-0-41.ap-northeast-1.compute.internal:
run_list: recipe[testweb::default]
[centos@ip-10-0-0-11 chef-repo]$ knife node run_list add ip-10-0-0-42.ap-northeast-1.compute.internal 'recipe[testweb::default]'
ip-10-0-0-42.ap-northeast-1.compute.internal:
run_list: recipe[testweb::default]
[centos@ip-10-0-0-11 chef-repo]$ knife node run_list add ip-10-0-0-43.ap-northeast-1.compute.internal 'recipe[testweb::default]'
ip-10-0-0-43.ap-northeast-1.compute.internal:
run_list: recipe[testweb::default]
运行run_list
在管理下的每个节点上执行run_list。
[centos@ip-10-0-0-11 chef-repo]$ knife zero chef_client 'name:ip-10-0-0-41.ap-northeast-1.compute.internal' -x ubuntu -i ~/.ssh/aws_tokyo.pem --sudo
[centos@ip-10-0-0-11 chef-repo]$ knife zero chef_client 'name:ip-10-0-0-42.ap-northeast-1.compute.internal' -x root -i ~/.ssh/aws_tokyo.pem --sudo
[centos@ip-10-0-0-11 chef-repo]$ knife zero chef_client 'name:ip-10-0-0-43.ap-northeast-1.compute.internal' -x centos -i ~/.ssh/aws_tokyo.pem --sudo
确认动作
可以确认每个实例都已经进行了Apache2和PHP的配置。
[centos@ip-10-0-0-11 chef-repo]$ curl http://10.0.0.41/index.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8"/>
<title>Web Test</title>
</head>
<body>
<h1>Web Test</h1>
<p>このサーバのプラットフォーム : ubuntu 14.04</p>
<p>このサーバのIPアドレス : 10.0.0.41</p>
</body>
</html>
[centos@ip-10-0-0-11 chef-repo]$ curl http://10.0.0.42/index.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8"/>
<title>Web Test</title>
</head>
<body>
<h1>Web Test</h1>
<p>このサーバのプラットフォーム : centos 6.5</p>
<p>このサーバのIPアドレス : 10.0.0.42</p>
</body>
</html>
[centos@ip-10-0-0-11 chef-repo]$ curl http://10.0.0.43/index.php
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8"/>
<title>Web Test</title>
</head>
<body>
<h1>Web Test</h1>
<p>このサーバのプラットフォーム : centos 7.0.1406</p>
<p>このサーバのIPアドレス : 10.0.0.43</p>
</body>
</html>
总结
我追踪了一系列的流程,以Chef和 Serverspec为中心,开发了一个cookbook,在本地的Docker上进行了测试,然后将其应用于其他服务器。
由于适应CentOS7比我想象的要麻烦许多,因此希望工具方面能提供隐藏这些麻烦部分的改进。
由于我们在GitHub上公开了我们创建的烹饪书和Packer配置文件等,如果您有兴趣,请查看一下。