介绍
Packer是Hashicorp的命令行工具,用于为多个平台和环境快速创建相同的机器映像。 使用Packer,您可以使用称为模板的配置文件创建包含预配置操作系统和软件的计算机映像。 然后,您可以使用此映像创建新计算机。 您甚至可以使用单个模板来编排您的生产,暂存和开发环境的同时创建。
在本教程中,您将使用Packer在Ubuntu 16.04上配置Nginx Web服务器。 然后,您将使用Packer创建此Droplet的快照,并立即在您的DigitalOcean信息中心可用,以便您可以使用它来创建新的Droplet。
先决条件
在你可以开始使用Packer之前,你需要一些东西。
- 一个Ubuntu 16.04服务器通过遵循Ubuntu 16.04初始服务器设置指南设置 ,包括sudo非root用户和防火墙。
- 具有读写权限的DigitalOcean API令牌。 查看如何使用DigitalOcean API v2获取令牌。
第1步 - 下载并安装Packer
登录到服务器后,您将下载Packer二进制包,为当前用户安装Packer,并检查安装是否成功。
在您的系统上运行Packer的最简单的方法是从官方Hashicorp发布网站下载最新的二进制包。 在撰写本文时,最新版本为0.12.2。
使用curl
实用程序从Hashicorp网站下载二进制包。
curl -O https://releases.hashicorp.com/packer/0.12.2/packer_0.12.2_linux_amd64.zip
下载后,安装unzip
实用程序并使用它解压缩包内容到/usr/local/bin
目录,使Packer可供所有用户使用:
sudo apt install -y unzip
sudo unzip -d /usr/local/bin packer_0.12.2_linux_amd64.zip
通过在命令行中检查packer
是否可用来验证安装是否成功:
packer
成功安装将输出以下内容:
Outputusage: packer [--version] [--help] <command> [<args>]
Available commands are:
build build image(s) from template
fix fixes templates from old versions of packer
inspect see components of a template
push push a template and supporting files to a Packer build service
validate check that a template is valid
version Prints the Packer version
Packer现在安装并在您的机器上工作。 在下一步中,您将设置一个项目目录并配置模板以生成基本的Ubuntu快照。
第2步 - 配置DigitalOcean Builder
我们希望Packer创建一个Droplet,安装一些软件和配置文件,然后将Droplet转换成我们可以用来创建新机器的映像。 Packer使用一个称为模板的配置文件,其中包含了告诉Packer如何构建映像的所有细节。 我们使用JSON (配置文件的通用格式)来编写此配置。
在Packer-speak中, 构建器是一个JSON对象,其中包含您希望Packer创建的映像的蓝图。 使用digitalocean
构建器,您将指示Packer创建一个将在NYC1区域启动的512 MB Ubuntu 16.04 Droplet。
创建并更改为一个新目录,其中包含我们将在本教程中创建的模板和配置文件:
mkdir ~/packerProject
cd ~/packerProject
现在你有一个项目template.json
,在你的文本编辑器中打开一个名为template.json
的新文件:
nano ~/packerProject/template.json
每个构建器需要进入template.json
的builders
部分。 现在添加此部分,并将此代码放入文件中包括digitalocean
构建器:
{
"builders": [
{
"type": "digitalocean"
}]
}
type
键定义了哪个构建器Packer用来创建您的映像。 digitalocean
构建器创建了DigitalOcean Droplets,Packer从中创建了快照。
Packer现在知道你想为DigitalOcean创建一个图像,但它仍然需要几个键值对来完成构建。
通过添加这些键和值来完成配置您的Droplet,以从在NYC1区域启动的512 MB Ubuntu 16.04 Droplet生成快照。 修改您的文件,使其看起来像这样:
{
"builders": [
{
"type": "digitalocean",
"ssh_username": "root",
"api_token": "YOUR_DIGITALOCEAN_API_TOKEN",
"image": "ubuntu-16-04-x64",
"region": "nyc1",
"size": "512mb"
}]
}
Packer使用ssh_username
值连接到Droplet。 此值需要设置为“root”才能使Packer正常工作。
保存template.json
并退出文本编辑器。
前面的代码块包含创建DigitalOcean Droplet所需的最少配置,但是还有其他可用的配置选项,如下表所示:
键 | 值 | 需要 | 描述 |
---|---|---|---|
api_token |
串 | 是 | 用于访问您的帐户的API令牌。 它也可以通过环境变量DIGITALOCEAN_API_TOKEN ,如果设置。 |
image |
串 | 是 | 要使用的基本映像的名称(或块)。 这是将用于启动新的Droplet并提供它的映像。 有关如何获取接受的图像名称/ lug的列表的详细信息,请参阅https://developers.digitalocean.com/documentation/v2/#list-all-images 。 |
region |
串 | 是 | 要启动Droplet的区域的名称(或段)。因此,这是快照将可用的区域。 有关已接受的地区名称/子弹,请参阅https://developers.digitalocean.com/documentation/v2/#list-all-regions 。 |
size |
串 | 是 | 要使用的Droplet大小的名称(或块)。 有关已接受的大小名称/子弹,请参阅https://developers.digitalocean.com/documentation/v2/#list-all-sizes 。 |
api_url |
串 | 没有 | 非标准API端点的URL。 如果您使用的是DigitalOcean API兼容服务,请设置此项。 |
droplet_name |
串 | 没有 | 分配给Droplet的名称。 DigitalOcean将机器的主机名设置为此值。 |
private_networking |
布尔值 | 没有 | 设置为true 可为正在创建的Droplet启用专用网络。 默认值为false ,或未启用。 |
snapshot_name |
串 | 没有 | 将在您的帐户中显示的结果快照的名称。 这必须是唯一的。 |
state_timeout |
串 | 没有 | 等待时间,作为持续时间字符串,使Droplet在超时之前进入所需状态(例如“活动”)。 默认状态超时为“6m”。 |
user_data |
串 | 没有 | 使用Droplet启动的用户数据。 有关详细信息,请参阅Droplet元数据简介。 |
您现在有一个有效的模板,但您的API令牌是硬编码在您的模板。 这是一个坏的做法和潜在的安全风险。 在下一步中,您将为此令牌创建一个变量并将其移出template.json
。
第3步 - 创建和存储用户变量
Packer允许您在单独的文件中创建和存储变量的值。 当您准备好构建映像时,此文件可以通过命令行传递给Packer。
将变量存储在单独的文件中是将敏感信息或特定于环境的数据排除在模板外的理想方式。 如果您打算与团队成员共享或将其存储在面向公众的存储库(如GitHub)中,这是至关重要的。
即使你只保存一个本地副本,它是一个Packer最佳实践,存储模板外的变量。
在packerProject
目录中创建并打开一个新的JSON文件以存储此信息:
nano ~/packerProject/variables.json
现在,添加一个my_token
变量,并将其值设置为您的DigitalOcean API令牌:
{
"my_token": "YOUR_DIGITALOCEAN_API_TOKEN"
}
保存variables.json
并退出编辑器。
现在让我们配置我们的模板使用变量。 在使用my_token
变量或任何其他变量之前,您首先需要通过在template.json
文件开头的变量部分中定义该变量来告诉Packer该变量。
在编辑器中打开template.json
:
nano template.json
在您之前定义的builders
部分上方添加新的variables
部分。 在这个新节中,声明my_token
变量并将其默认值设置为空字符串:
{
"variables": {
"my_token":""
},
"builders": [
...
}
变量部分中定义的变量在全局范围内可用。
接下来,在builders
部分中替换my_token
的API令牌:
{
...
"builders": [
{
"type": "digitalocean",
"api_token": "{{ user `my_token` }}",
...
}]
}
如你所见,对用户变量的调用必须使用特定的格式: "{{ user ` variable_name ` }}
。引号和反引号是必需的,双花括号也是如此。
保存文件并退出编辑器。
现在,您有一个工作模板,可生成基本快照和单独的变量文件,以存储API密钥。 在验证和构建映像之前,让我们在模板中添加一个provisioners
部分,它将配置Packer在创建映像之前在机器上安装和设置Nginx Web服务器。
第4步 - 配置Provisioner
provisioners
部分是Packer在运行的Droplet上安装和配置软件,然后将其转换为机器映像。 与构建器一样,您可以使用不同类型的配置来配置Droplet。
为了配置Nginx,您将使用Packer的file
提供程序将配置文件上传到服务器,然后使用shell
提供程序执行使用这些文件的安装脚本。 file
程序允许您将文件和目录移入和移出正在运行的机器,然后再将其转换为图像。 使用shell
程序,您可以在该计算机上远程执行shell脚本。
Provisioner按照在模板中显示的顺序执行。 这意味着首先放置file
程序,因为您的shell脚本需要上传的文件。
在provisioners
中的builders
部分之后template.json
添加一个provisioners
文件部分,并设置要使用的两个配置文件:
{
...
"builders": [
{
...
}],
"provisioners": [
{
"type": "file"
},
{
"type": "shell"
}]
}
file
程序需要一个指向本地文件路径的source
和指向正在运行的机器上的现有文件路径的目标。 Packer只能将文件移动到已经存在的目标。 因此,我们通常将文件上传到/tmp
。
通过将突出显示的行添加到template.json来template.json
file
配置template.json
:
{
...
"provisioners": [
{
"type": "file",
"source": "configs/",
"destination": "/tmp"
},
...
}
我们将在下一步中在本地机器上创建configs
文件夹。 在我们做之前,让我们通过设置shell
来完成配置文件的编辑。
shell
程序需要一个包含应传递给正在运行的机器的脚本数组的scripts
键。 每个脚本按照模板中指定的顺序上传和执行。
现在,通过提供脚本的完整路径来配置shell
提供程序:
{
...
"provisioners": [
{
"type": "file",
"source": "configs/",
"destination": "/tmp"
},
{
"type": "shell",
"scripts": [
"scripts/configureNginx.sh"
]
}]
}
脚本必须单独列出,这允许您控制脚本的执行顺序。
您的模板的provisioners
部分已完成。 保存文件并退出Vim。
现在让我们创建一个shell脚本和配置文件,Packer将使用它来创建你的镜像。
第5步 - 添加配置文件和安装脚本
我们希望我们的映像具有完全配置的Nginx安装,具有正确的配置文件和默认网页。 在本节中,您将根据教程如何在Ubuntu 16.04上设置Nginx服务器块(虚拟主机),从一些预定义配置创建这些文件,因为Nginx配置超出了本教程的范围。
我们将通过创建和上传由单个安装脚本处理的三个单独的配置文件来配置Nginx的服务器。
首先,在项目文件夹中创建一个新目录以存储配置文件。
mkdir ~/packerProject/configs
更改为/configs
以创建您的Nginx配置文件:
cd ~/packerProject/configs
首先,您需要从新网域投放默认网页。 创建文件index.html.new
:
nano index.html.new
在此新文件中,插入以下内容:
HELLO FROM YOUR TEST PAGE
接下来,您需要一个Nginx配置文件,为您的域定义服务器块,然后定义该域的监听端口和网页位置。 创建一个名为newDomain.conf
的文件:
nano newDomain.conf
将以下配置放在此文件中:
server {
listen 80;
listen [::]:80;
server_name example.com;
location / {
root /var/www/html/newDomain;
index index.html index.htm;
}
}
在本示例中,我们使用example.com
作为占位符值。 从映像创建新计算机时,您必须登录到新计算机并更改此文件以反映指向计算机的实际域或IP地址。
最后,您希望Nginx从新目录/etc/nginx/vhost.d/加载域的/etc/nginx/vhost.d/
。 这意味着编辑主要的Nginx配置文件。
创建nginx.conf.new
:
nano nginx.conf.new
我们将使用一个默认的Nginx配置文件,但我们将修改它以包括我们的特定网站配置,并确保Nginx作为www-data
用户运行。 将以下内容放入此文件:
user www-data;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/vhost.d/*.conf;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
保存并退出文件。
配置文件就位,让我们定义Packer将用来安装我们需要的软件的脚本。 创建一个新文件夹以存储脚本:
mkdir ~/packerProject/scripts
现在切换到这个新目录并创建安装脚本configureNginx.sh
,它安装,配置,启用和启动Nginx Web服务器:
cd ~/packerProject/scripts
nano configureNginx.sh
将以下内容粘贴到文件中,使用刚创建的配置文件安装,配置和启动Nginx:
#!/bin/bash
# Script to install Nginx and enable on boot.
# Update your system:
apt-get update -y
apt-get upgrade -y
# Install Nginx:
apt-get install -y nginx
#Start Nginx service and enable to start on boot:
systemctl enable nginx
systemctl start nginx
# Create new 'vhost' directory for domain configuration:
mkdir /etc/nginx/vhost.d
# Create a new directory to serve new content.
mkdir -p /var/www/html/newDomain
# Create a copy of original configuration files and import configuration:
cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.original
cp /tmp/nginx.conf.new /etc/nginx/nginx.conf
# Copy over the server block configuration:
cp /tmp/newDomain.conf /etc/nginx/vhost.d/newDomain.conf
# Copy over the html test page:
cp /tmp/index.html.new /var/www/html/newDomain/index.html
# Restart Nginx:
systemctl restart nginx
您的模板已完成,您现在可以验证和构建快照。
第6步 - 验证和构建Droplet
现在是时候使用Packer的validate
子命令来测试你的模板了。 一旦模板验证成功,您将构建您的Droplet并创建快照。
更改为项目的根目录:
cd ~/packerProject
validate
子命令将检查您的模板的有效语法和配置选项:
packer validate -var-file=variables.json template.json
-var-file
标志读取variables.json
并在template.json
设置variables.json
的值。
您将看到以下输出:
OutputTemplate validated successfully.
如果有什么问题template.json
你会得到一个错误消息。 此消息将根据错误而有所不同,但大多数可以通过双重检查语法和更正任何拼写错误来解决。
build
子命令运行您在模板的builders
部分中定义的builders
。 换句话说,它告诉Packer构建您的Droplet,然后在您的DigitalOcean仪表板中创建该Droplet的快照。
调用packer build
构建Droplet并创建快照:
packer build -var-file=variables.json template.json
请注意, -var-file
标志对build
和validate
子命令的操作方式完全相同。
成功构建的输出将类似于以下内容:
Outputdigitalocean output will be in this color.
==> digitalocean: Creating temporary ssh key for Droplet...
==> digitalocean: Creating Droplet...
==> digitalocean: Waiting for Droplet to become active...
==> digitalocean: Waiting for SSH to become available...
==> digitalocean: Connected to SSH!
==> digitalocean: Gracefully shutting down Droplet...
==> digitalocean: Creating snapshot: packer-1488487459
==> digitalocean: Waiting for snapshot to complete...
==> digitalocean: Destroying Droplet...
==> digitalocean: Deleting temporary ssh key...
Build 'digitalocean' finished.
==> Builds finished. The artifacts of successful builds are:
--> digitalocean: A snapshot was created: 'packer-1488487459' (ID: 18252043) in region 'nyc1'
成功构建后,您将在您的DigitalOcean快照库中找到一个新快照。 您可以在输出中找到快照的名称。 在这个例子中,它是packer-1488487459
。
从这里,访问您的DigitalOcean仪表板,选择图像 ,新的快照将出现在您的列表中:
您现在可以使用此新快照创建新的Droplet。 选择更多并选择创建点滴 。 然后完成表单以创建新机器。
机器联机后,从仪表板确定其IP地址并登录到新机器:
ssh root@your_new_server_ip_address
然后编辑Nginx服务器配置文件:
nano /etc/nginx/vhost.d/newDomain.conf
并将example.com
替换为example.com
的IP地址或要使用的域名:
server {
listen 80;
listen [::]:80;
server_name your_new_server_ip_address;
location / {
root /var/www/html/newDomain;
index index.html index.htm;
}
}
或者,您可以使用sed
命令替换文件中的值,如下所示:
sudo sed -i 's/^.*server_name example.com/server_name your_new_server_ip_address/' /etc/nginx/vhost.d/newDomain.conf
你可以在本教程中了解更多关于sed
的信息。
然后重新启动Nginx服务器以应用更改:
sudo systemctl restart nginx
故障排除
有时,您可能遇到错误消息未能充分解释的问题。 在这些情况下,您可以通过启用调试模式,检查Packer日志或两者来提取有关构建的更多详细信息。
调试模式为远程构建中的每个步骤提供特定于构建器的调试信息。 启用DigitalOcean构建的调试模式还会在项目文件夹中生成临时私钥,您可以在将其转换为快照之前连接到并检查正在运行的Droplet。
您可以通过在命令行中将-debug
标志传递给packer build
来进入调试模式:
packer build -debug --var-file=variables.json template.json
如果您无法在调试模式下诊断问题,您可以尝试启用Packer日志。 这些日志主要用于调试本地构建器,但它们也可以提供有关远程构建的有用信息。
要启用Packer日志,请将PACKER_LOG
环境变量设置为除“0”或空字符串以外的任何值:
PACKER_LOG=1 packer build --var-file=variables.json template.json
日志将打印到控制台,除非您还设置了PACKER_LOG_PATH
环境变量。
如果您仍然有问题,您可以尝试与Packer社区中的人联系 。
结论
现在你对Packer的基础感到舒服,你可能有兴趣在这个基础上建立。
尝试向模板中添加第二个构建器,以在DigitalOcean快照旁创建本地测试环境。 例如, virtualbox-iso
构建器为VirtualBox生成映像, VirtualBox是企业和业余爱好者使用的免费的开源虚拟化产品。 您可以为VirtualBox映像定义post-processor
,并创建镜像您的DigitalOcean快照的Vagrant环境。 这将允许您在本地测试网站更改,然后将其推送到现场Droplet。 您可以在Vagrant后处理器文档中了解更多信息 。
或者您可能想要将Web服务器连接到数据库。 添加第二个digitalocean
构建器,并使用provisioners
部分中的only
键为每个构建应用不同的配置。
如果你更喜欢使用配置管理工具,Packer自带了对Ansible , Puppet , Chef等的开箱即用支持。 尝试使用这些配置程序之一进一步配置您的Droplet以匹配您的用例。 如果你从来没有尝试过配置管理,请看看如何创建Ansible Playbooks在Ubuntu上自动化系统配置 。