如何使用 Puppet + Packer 佈署 AWS AMI
Packer 是由 Hashicorp 所開發的一個 build image 工具。
相較於 Docker,Packer 比較像是一個工具,提供你在各種 platform build image,而 Docker 只是其中一個 platform。
Requires
在開始之前,要先安裝 Packer 和 librarian-puppet 這兩個套件在要執行 build 的機器上。
Packer 安裝
在 Packer Download 頁面上尋找自己適合的 OS 安裝,我這邊是 MacOS。
$ wget https://releases.hashicorp.com/packer/1.1.3/packer_1.1.3_darwin_amd64.zip
$ unzip packer_1.1.3_darwin_amd64.zip
解開之後會得到一個 packer 的執行檔,把它放到 PATH 裡面方便執行
$ mv packer /usr/local/bin/
$ packer -h
usage: 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
librarian-puppet 安裝
librarian-puppet 是幫助 Puppet 管理 module 的套件,只需要攥寫 Puppetfile 來管理 module,就像 php 的 composer、nodejs 的 npm 一樣。
和 librarian-puppet 同性質的還有 r10k
由於 librarian-puppet 是用 Ruby 開發,所以使用 gem 來安裝 librarian-puppet
$ gem install librarian-puppet
用 Puppet + Packer build AMI
這個範例
先來看檔案結構:
├── Makefile
├── Puppetfile
├── manifests
│ └── site.pp
├── packer.json
└── scripts
└── puppet_install.sh
- manifests/init.pp
$php_version = '7.0'
class { 'apache':
server_tokens => 'Prod',
server_signature => 'Off',
default_vhost => false,
mpm_module => false,
}
apache::vhost { 'localhost':
port => 80,
docroot => '/var/www/html',
docroot_owner => 'www-data',
docroot_group => 'www-data',
directoryindex => 'index.php',
override => 'All',
options => ['-Indexes', '+ExecCGI']
}
$default_modules = ['rewrite', 'actions', 'ssl', 'worker']
$default_modules.each |String $module| {
class { "apache::mod::${module}": }
}
# Use fcgid run php
class { 'apache::mod::fcgid':
options => {
'AddHandler' => 'fcgid-script .php',
'FcgidWrapper' => '/usr/local/bin/php-wrapper .php',
'FcgidConnectTimeout' => 20,
}
}
file { '/usr/local/bin/php-wrapper':
ensure => file,
owner => root,
group => root,
mode => '0755',
content => "#!/bin/bash\nPHP_FCGI_MAX_REQUESTS=10000\nexport PHP_FCGI_MAX_REQUESTS\nexec /usr/bin/php-cgi";
}
# add 'ppa:ondrej/php' repository
$dependency_apt = ['locales', 'software-properties-common', 'python-software-properties']
package { $dependency_apt: ensure => present }
exec { 'install-ppa':
provider => 'shell',
environment => ['LANG=en_US.UTF-8'],
path => '/bin:/usr/sbin:/usr/bin:/sbin',
command => "/usr/sbin/locale-gen en_US.UTF-8 && add-apt-repository -y ppa:ondrej/php && apt-get update",
user => 'root',
unless => 'apt-cache policy | grep ondrej/php',
require => Package[$dependency_apt]
}
$php_package_list = [ "php${php_version}",
"libapache2-mod-php${php_version}",
"php${php_version}-mysql",
"php${php_version}-cli",
"php${php_version}-cgi",
"php${php_version}-common",
"php${php_version}-mcrypt",
"php${php_version}-gd",
"php${php_version}-json",
"php${php_version}-bcmath",
"php${php_version}-mbstring",
"php${php_version}-xml",
"php${php_version}-xmlrpc",
"php${php_version}-zip",
"php${php_version}-soap",
"php${php_version}-sqlite3",
"php${php_version}-curl",
"php${php_version}-opcache",
"php${php_version}-readline",
'php-mongodb',
'php-memcached'
]
package { $php_package_list:
ensure => present,
require => Exec['install-ppa'],
}
file { '/var/www/html/index.php':
ensure => present,
content => '<?php phpinfo(); ?>',
}
- Puppetfile 是拿來管理 module。
forge 'https://forgeapi.puppetlabs.com'
mod 'puppetlabs/apache'
mod 'puppetlabs/stdlib'
mod 'puppetlabs/concat'
mod 'puppetlabs/apt'
- packer.json 這個是 packer 的主要設定檔
{
"variables": {
"aws_profile": "{{env `AWS_PROFILE`}}"
},
"builders": [
{
"type": "amazon-ebs",
"profile": "{{ user `aws_profile`}}",
"name": "apache2-php70-ami",
"ami_description": "apache2-php70 (ap-northeast-1)",
"region": "ap-northeast-1",
"source_ami": "ami-bec974d8",
"instance_type": "t2.nano",
"ssh_username": "ubuntu",
"ami_name": "apache2-php70-ami-{{timestamp}}",
"tags": {
"Name": "apache2-php70-ami-{{timestamp}}"
}
}
],
"provisioners": [
{
"type": "shell",
"script": "scripts/puppet_install.sh",
"pause_before": "10s"
},
{
"type": "puppet-masterless",
"manifest_file": "manifests/init.pp",
"module_paths": "modules",
"puppet_bin_dir": "/opt/puppetlabs/bin"
}
]
}
builders 是拿來定義 AWS 的參數,我有另外把 AWS Profile 用變數設定 export。
provisioners 則是用什麼來 build image,這邊用到了 shell 來安裝 puppet-agent,而 puppet-masterless 來安裝 apache2 + php7。
- scripts/puppet_install.sh
用來安裝 puppet-agent 的 script。
#!/bin/bash
wget https://apt.puppetlabs.com/puppet5-release-xenial.deb
sudo dpkg -i puppet5-release-xenial.deb
sudo apt update && sudo apt-get install puppet-agent -y
- Makefile
這個檔案是用來方便 build 的工具,還有 AWS Profile Name
#
PROFILE?= demo_profile
#
.PHONY: build
#
build:
@AWS_PROFILE=${PROFILE} packer build packer-apache2-php7.json
處理 AWS Profile
AWS Profile 大致帶過怎麼寫 ...
[demo_profile]
aws_access_key_id = foo
aws_secret_access_key = bar
Build
要 build 之前先用 librarian-puppet 產生 modules。
$ librarian-puppet install
然後利用 Makefile 開始 build
$ make
apache2-php70-ami output will be in this color.
==> apache2-php70-ami: Prevalidating AMI Name...
apache2-php70-ami: Found Image ID: ami-bec974d8
==> apache2-php70-ami: Creating temporary keypair: packer_5a4b3a79-a62b-d6ae-5724-f114405b9f62
...
...
==> apache2-php70-ami: Deleting temporary keypair...
Build 'apache2-php70-ami' finished.
==> Builds finished. The artifacts of successful builds are:
--> apache2-php70-ami: AMIs were created:
ap-northeast-1: ami-cbc654ad
最後出現 AMI ID 就代表 build 成功囉!!!