00. はじめに
HashiCorpさんの**『Packer』**
コチラの初歩的な使い方を整理してみました。
00-1. 検証環境
・PackerはMulti Platformに対応しており、それが特徴でもありますが、今回はAWS上での確認のみになります。
■検証環境
Packer v1.1.1
Amazon Linux AMI release 2017.09
01. Packerとは
01-1. 特徴
・公式ドキュメントに簡潔に記載されています。
https://www.packer.io/intro/index.html
上記説明をもとに私の理解は下記になります。
-
各IaaS・仮想化ソフトウェア/コンテナ管理ソフトウェア(※)にて、カスタムしたマシンイメージを作成できるツール
※AWS、Azure、Google Cloud、VMware、VirtualBoxなど多数のプラットフォームに対応 -
一度の実行で複数の環境へマシンイメージを作成することが可能
例: AWSでAMIを作成、 並行してAzureでイメージを作成する 等 -
マシンイメージのプロビジョニングをコード管理できる
**「Infrastructure as Code」**の一種
01-2. Use Case
・ユースケースに関しても、公式に簡潔な説明があります。
https://www.packer.io/intro/use-cases.html
・主な用途としてはベースとなるイメージを要件に沿ってプロビジョニング(サーバセットアップ、コードデプロイ等)した上、カスタムイメージを作成することが挙げられるかと思います。
※個人的な話ですが、AMIを作成するツールと聞いていたため、触るまでは単なるバックアップツールの一種と思っていました。しかし、上述の特徴およびユースケースからも分かるように起動中のEC2からAMIを作成する機能は無いようです(バックアップツールではないのでした)。
→ AWSでしか確認していないため、他platformでも同様のことが言えるか確認できておりませんことご了承ください。
01-3. 考慮すべき事項
・例えば、AutoScalingの起動用AMIを継続的に更新したい場合
プロビジョニングのため該当AMIより一旦インスタンスを起動する必要があるため、同一サーバが重複起動することになります。その際の懸念点(例: S3 や Cloudwatch Logsへの自動ログアップデート処理など)を考慮しておく必要がありそうです。具体的にはそういったサービスは実稼働のlaunch時のみ行うようにする等が考えられるかと思います。
02. 実装例の前に
02-1. 導入はかんたん
・Packerの導入方法はとても簡単ですので、公式の説明を参照下さい。
https://www.packer.io/intro/getting-started/install.html
・また各種コマンドオプションもとてもシンプルになっています。
https://www.packer.io/docs/commands/build.html
https://www.packer.io/docs/commands/validate.html
→ 同HashiCorpさんの**『Terraform』**同様、構文チェックがあるのはありがたいです。
02-2. Tips
■ 日付付与
- 例えば「ami_name」をランダムにすべく、日付を付与する場合
{ "ami_name": "Packer {{isotime | clean_ami_name}}" }
↓この場合、下記のようなAMI名となります。
「Packer 2016-07-18T13-22-19Z」
https://www.packer.io/docs/templates/engine.html
- 文字列_YYYYMMDD-HHMM としたい場合は下記のように指定
{"文字列_{{isotime \"20060102-0304\"}}" }
↓ 下記のような出力結果に
「文字列_20171120-2008」
※format指定時の"(ダブルコーテーション)はエスケープする必要がありました。
※年は西暦後半2桁しか出力されないため、上二桁を足しています。
※TimezoneはUTC固定。この方法ではJSTなどに変更することは出来ないとのことです。
▽format Referece
a date | Numeric |
---|---|
Year | 06 |
Month | 01(or Jan) |
Date | 02 |
Hour | 03(or 15) |
Minute | 04 |
Second | 05 |
03. 実装例
03-1. 基本的なセットアップがされているAMIの作成
■ 概要
・amzn-linuxのAMIを基に、システム関連の設定、ログインユーザの作成など
サーバ構築時によく行われるセットアップを施したAMIを作成してみようと思います。
■ 詳細
- 公式配布のAmazon Linuxをベースに
- yum update実施 (amzn-linuxは配布時点で最新になっているので、この場合不要ですが)
- TimeZoneやlocaleを日本に
- 第三者のログイン用ユーザ「guest」を作成。公開鍵も配置
- swap領域も確保
03-2. 事前準備
■ 03-2-a. ファイル
・packer実行サーバ上に該当ユーザの鍵ペアを作成しておきました。
/home/guest/.ssh/authorized_keys
/home/guest/.ssh/guest.pem
■ 03-2-0. シェルスクリプト
・補足
Packerの**Template(JSON形式)**に直接記載することも可能ですが、
JSONの書式規定により正規表現の利用が難しいため、外部シェルスクリプトを呼び出すというシンプルな形をとってみました。
03-2-1. general.sh
・全般的なものを定義。今回はYum updateのみ
#!/bin/sh
sudo yum -y update
注意: 起動したインスタンスがインターネットに出られる環境であること
※起動時にアサインする VPC(NAT、IGW)、Subnet、SecurityGroupに注意
一時的に起動する場所のため本番と同じNW環境でなくとも問題ありません
03-2-2. system_setup.sh
・TimeZoneなどシステム関連の設定
#!/bin/sh
sudo sed -i -e 's/ZONE=\"UTC\"/ZONE=\"Asia\/Tokyo\"/g' /etc/sysconfig/clock
sudo cp -f /usr/share/zoneinfo/Japan /etc/localtime
sudo sed -i -e 's/repo_upgrade: security/repo_upgrade: none/g' /etc/cloud/cloud.cfg
sudo sed -i -e 's/en_US\.UTF-8/ja_JP\.UTF-8/g' /etc/sysconfig/i18n
03-2-3. create_user.sh
・ユーザ作成
#!/bin/sh
sudo groupadd -g 1000 guest
sudo useradd -u 1000 -g 1000 guest
sudo echo password1234 | sudo passwd --stdin guest
sudo gpasswd -a guest wheel
sudo sed -i -e "s/^#\(.*NOPASSWD.*\)/\1/" /etc/sudoers
sudo mkdir /home/guest/.ssh
sudo chmod 700 /home/guest/.ssh
秘密鍵をイメージ内に残したくないため、ここでは用意していません。
03-2-4. create_swap.sh
・Swap領域定義
#!/bin/sh
sudo dd if=/dev/zero of=/swap bs=1M count=1024
sudo mkswap /swap
sudo chmod 600 /swap
sudo swapon /swap
sudo sed -i -e '$ a /swap swap swap defaults 0 0' /etc/fstab
必要があれば
03-3. テンプレート
{
"builders":
[
{
"type": "amazon-ebs",
"ami_name": "mitzi_base_{{isotime \"20060102-0304\"}}",
"region": "ap-northeast-1",
"source_ami": "ami-2803ac4e",
"instance_type": "t2.micro",
"ssh_username": "ec2-user",
"security_group_ids": ["sg-962132f1", "sg-d02033b7"],
"vpc_id": "vpc-505ac034",
"subnet_id": "subnet-75f7da03",
"associate_public_ip_address": true,
"ssh_timeout": "5m",
"tags": {
"OS_Version": "Amazon Linux 2017.09.01",
"BillingType": "RESEARCH"
}
}
],
"provisioners": [
{
"type": "shell",
"scripts": [
"./scripts/general.sh",
"./scripts/system_setup.sh",
"./scripts/create_user.sh",
"./scripts/create_swap.sh"
]
},
{
"type": "file",
"source": "/home/guest/.ssh/authorized_keys",
"destination": "/tmp/authorized_keys"
},
{
"type": "shell",
"inline": [
"sudo mv /tmp/authorized_keys /home/guest/.ssh/authorized_keys",
"sudo chmod 600 /home/guest/.ssh/authorized_keys",
"sudo chown -R guest:guest /home/guest"
]
}
]
}
上述したテンプレートのポイント
・source_amiから起動したインスタンスへのSSH接続には、Packerが用意する一時的なキーペアが利用されます。
・Credential keyをコード上にもたせたくなかったため、packer実行インスタンスにIAM Roleを付与しています。
・事前に用意していた公開鍵を起動したインスタンスに配置しています。
→ 「/home/guest/.ssh/」配下に直接配置しようとしましたが権限無しとエラーになるため、一旦 /tmp 配下に配置しています。 (配置先のpermissionを777にしても駄目でした)
03-4. 実行結果
・実行すると下記のようなログが標準出力されます。
(日本語設定を盛り込んでから一部文字化けするようになりました。)
・構文チェック
[root@ip-10-100-5-195 ~]# packer validate basic-setup.json
Template validated successfully.
・build実行
[root@ip-10-100-5-195 ~]# packer build basic-setup.json
amazon-ebs output will be in this color.
==> amazon-ebs: Prevalidating AMI Name: mitzi_base_20171120-0731
amazon-ebs: Found Image ID: ami-2803ac4e
==> amazon-ebs: Creating temporary keypair: packer_5a1284ed-27b6-4c78-e7aa-9b86380a5e56
==> amazon-ebs: Launching a source AWS instance...
==> amazon-ebs: Adding tags to source instance
amazon-ebs: Adding tag: "Name": "Packer Builder"
amazon-ebs: Instance ID: i-01a24d283513a107a
==> amazon-ebs: Waiting for instance (i-01a24d283513a107a) to become ready...
==> amazon-ebs: Waiting for SSH to become available...
==> amazon-ebs: Connected to SSH!
==> amazon-ebs: Provisioning with shell script: ./scripts/general.sh
amazon-ebs: Loaded plugins: priorities, update-motd, upgrade-helper
amazon-ebs: No packages marked for update
==> amazon-ebs: Provisioning with shell script: ./scripts/system_setup.sh
==> amazon-ebs: Provisioning with shell script: ./scripts/create_user.sh
amazon-ebs: 繝ヲ繝シ繧カ繝シ guest 縺ョ繝代せ繝ッ繝シ繝峨r螟画峩縲
amazon-ebs: passwd: 縺吶∋縺ヲ縺ョ隱崎ィシ繝医□繧ッ繝ウ縺梧ュ」縺励¥譖エ譁ー縺ァ縺阪∪縺励◆縲
amazon-ebs: Adding user guest to group wheel
==> amazon-ebs: Provisioning with shell script: ./scripts/create_swap.sh
amazon-ebs: 1024+0 繝ャ繧ウ繝シ繝牙□蜉
amazon-ebs: 1024+0 繝ャ繧ウ繝シ繝牙□蜉
amazon-ebs: 1073741824 繝舌う繝 (1.1 GB) 繧ウ繝斐□縺輔l縺セ縺励◆縲 14.094 遘偵 76.2 MB/遘
amazon-ebs: 繧ケ繝ッ繝□□遨コ髢薙ヰ繝シ繧ク繝ァ繝ウ1繧定ィュ螳壹@縺セ縺吶√し繧、繧コ = 1048572 KiB
amazon-ebs: 繝ゥ繝吶Ν縺ッ縺ゅj縺セ縺帙s, UUID=7ee19a25-bb14-40da-8115-74eaf0d0dbe5
==> amazon-ebs: Uploading /home/guest/.ssh/authorized_keys => /tmp/authorized_keys
==> amazon-ebs: Provisioning with shell script: /tmp/packer-shell459700274
==> amazon-ebs: Stopping the source instance...
amazon-ebs: Stopping instance, attempt 1
==> amazon-ebs: Waiting for the instance to stop...
==> amazon-ebs: Creating the AMI: mitzi_base_20171120-0731
amazon-ebs: AMI: ami-ba2291dc
==> amazon-ebs: Waiting for AMI to become ready...
==> amazon-ebs: Adding tags to AMI (ami-ba2291dc)...
==> amazon-ebs: Tagging snapshot: snap-03cf7a7d44094beb7
==> amazon-ebs: Creating AMI tags
amazon-ebs: Adding tag: "BillingType": "RESEARCH"
amazon-ebs: Adding tag: "OS_Version": "Amazon Linux 2017.09.01"
==> amazon-ebs: Creating snapshot tags
==> amazon-ebs: Terminating the source AWS instance...
==> amazon-ebs: Cleaning up any extra volumes...
==> amazon-ebs: No volumes to clean up, skipping
==> amazon-ebs: Deleting temporary keypair...
Build 'amazon-ebs' finished.
==> Builds finished. The artifacts of successful builds are:
--> amazon-ebs: AMIs were created:
ap-northeast-1: ami-ba2291dc
・AMI作成が完了すると、一時的に起動したインスタンス(名称は「Packer Builder」)は自動で削除されます。
03-5. 確認
・意図したとおりのAMIが作成されたか、作成したAMIからインスタンスを起動して確認。
- guestユーザの秘密鍵でSSH接続出来ることを確認
- ユーザがsudo権限を持っていることを確認
- TimeZoneや文字設定が意図したとおりに設定されていることを確認
- Swapが有効になっていることを確認
- 作成されたAMIに意図したTagが付与されていることを確認
04. 備考
04-1. 必要なPolicy
・今回はIAM Roleを利用しましたが、Packer実行に最低限必要なAWS権限は下記公式のとおりになります。
https://www.packer.io/docs/builders/amazon.html#using-an-iam-task-or-instance-role
04-2. 独自key pairをアサインしているAMIを利用した場合
・build時にssh接続出来ない問題が発生しました。
同問題は他の方も経験しているようで下記の方が仰るように公開鍵を削除する処理を盛り込みましたが、私の場合はそれでも起動しませんでした。
https://qiita.com/ikuyamada/items/97c286990adfe1770acf
そこで、下記の方のコメント欄のやりとりにあるようにPackerの独自keyを使用せず、
「key pair」及び接続時の「秘密鍵」を指定する方法にて問題解決しています。
https://qiita.com/soeda_jp/items/e9f759e2db63637e8549
おわり
今回はここまでになります。