最終的に出来るもの
GCE の カスタムイメージ
最終的なコードは以下に記載しています
github.com
Packer とは
Hashicorp 社が提供している、マシンイメージをマルチプラットフォームにて作成することが出来る OSS のツールです
具体的に言うと AWS なら カスタム AMI、Azure なら VM のカスタムイメージなどを作成することが出来ます
www.packer.io
今回は Pakcer を用いて GCE のカスタムイメージを作成します
また、Packer の中で Provisioners の機能を使うことで、インスタンスの構築工程をコードで管理することが出来ます
www.packer.io
今回は Ansible を使用します
Packer の中で Provisioners を使う基本例
以下のように、 packer.json の中で builders
と provisioners
ブロックがあり、それぞれ設定します
今回は builders
に Google Compute Builder (type: googlecompute)、 provisioners
に Ansible Provisioner (type: ansible) を設定します
具体的な例は こちら を参照してください
{
"builders" : [
{
"{{ ここに Builders の設定する : https://www.packer.io/docs/builders/index.html }}"
}
],
"provisioners": [
{
"{{ ここに Provisioners の設定する : https://www.packer.io/docs/provisioners/index.html }}"
}
]
}
環境の準備
カスタムイメージを作成するために GCP 上のネットワークを作成します
Packer のデフォルトの設定だと default
の VPC ネットワークを使用しますが、今回は自分で VPC ネットワークを作成し、その中で作成するようにします
export _project_id='pkg-gcp-instance-packer'
export _region='asia-northeast1'
export _vpc_network_name='iganari-test-vpc'
export _subnet_name='iganari-test-subnet'
export _firewall_ssh='allow-ssh-all'
gcloud beta compute networks create ${_vpc_network_name} \
--project ${_project_id} \
--bgp-routing-mode=global \
--subnet-mode=custom
gcloud beta compute networks subnets create ${_subnet_name} \
--project ${_project_id} \
--network ${_vpc_network_name} \
--region ${_region} \
--range 172.16.0.0/12
gcloud compute firewall-rules create ${_firewall_ssh} \
--project ${_project_id} \
--direction=INGRESS \
--priority=1000 \
--network ${_vpc_network_name} \
--action=ALLOW \
--rules=tcp:22,icmp \
--source-ranges=0.0.0.0/0 \
--target-tags=allow-ssh-all
---> これで環境の準備が完了しました!!
Packer を用いて基本的なイメージを作る
上記で作成したネットワークおよび Firewall Rules を用いて、Packer を実行します
{
"builders" : [
{
"type": "googlecompute",
"project_id": "pkg-gcp-instance-packer",
"source_image": "ubuntu-minimal-1910-eoan-v20200406",
"disk_size": "20",
"ssh_username": "packer",
"zone": "asia-northeast1-a",
"network": "iganari-test-vpc",
"subnetwork": "iganari-test-subnet"
}
]
}
packer build packer.json
# packer build packer.json
googlecompute: output will be in this color.
==> googlecompute: Checking image does not exist...
==> googlecompute: Creating temporary SSH key for instance...
==> googlecompute: Using image: ubuntu-minimal-1910-eoan-v20200406
==> googlecompute: Creating instance...
googlecompute: Loading zone: asia-northeast1-a
googlecompute: Loading machine type: n1-standard-1
googlecompute: Requesting instance creation...
googlecompute: Waiting for creation operation to complete...
googlecompute: Instance has been created!
==> googlecompute: Waiting for the instance to become running...
googlecompute: IP: 104.198.85.143
==> googlecompute: Using ssh communicator to connect: 104.198.85.143
==> googlecompute: Waiting for SSH to become available...
==> googlecompute: Connected to SSH!
==> googlecompute: Deleting instance...
googlecompute: Instance has been deleted!
==> googlecompute: Creating image...
==> googlecompute: Deleting disk...
googlecompute: Disk has been deleted!
Build 'googlecompute' finished.
==> Builds finished. The artifacts of successful builds are:
--> googlecompute: A disk image was created: packer-1588585792
---> packer-1588585792
という名前の カスタムイメージが出来ました!!
Packer の中で、Ansible を使用する
次に provisioners の設定を加えていきます
今回は Ansible の細かい説明は省きます
- packer.json
provisioners
の他に、 builders
のブロックの中に tags
と scopes
を追加
tags
は、インスタンスにネットワークタグを付与する設定であり、 Ansible (Remote) を実行するが故に 22 ポートを ALLOW する FireWall Rules が必要
scorep
アクセススコープを設定するものであり、Ansible のなかで Monitoring agent をインストールする際に、monitoring API にアクセス出来るようにしておく必要があるから設定
{
"builders" : [
{
"type": "googlecompute",
"project_id": "pkg-gcp-instance-packer",
"source_image": "ubuntu-minimal-1910-eoan-v20200406",
"disk_size": "20",
"ssh_username": "packer",
"zone": "asia-northeast1-a",
"network": "iganari-test-vpc",
"subnetwork": "iganari-test-subnet",
"tags": "allow-ssh-all",
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
}
],
"provisioners": [
{
"type": "ansible",
"playbook_file": "./ansible/playbook.yml",
"groups": "pkg-gcp-custom-image-ubuntu1910",
"user": "packer"
}
]
}
$ tree
.
├── ansible
│ ├── ansible.cfg
│ ├── playbook.yml
│ └── roles
│ ├── common
│ │ └── tasks
│ │ └── main.yml
│ ├── gcp_logging-agent
│ │ ├── README.md
│ │ ├── handlers
│ │ │ └── main.yml
│ │ └── tasks
│ │ └── main.yml
│ ├── gcp_monitoring-agent
│ │ ├── README.md
│ │ ├── handlers
│ │ │ └── main.yml
│ │ └── tasks
│ │ └── main.yml
│ ├── nginx
│ │ ├── README.md
│ │ ├── files
│ │ │ └── etc
│ │ │ ├── apt
│ │ │ │ └── sources.list.d
│ │ │ │ └── nginx.list
│ │ │ ├── logrotate.d
│ │ │ │ └── nginx
│ │ │ └── nginx
│ │ │ ├── nginx.conf
│ │ │ ├── sites-available
│ │ │ └── sites-enabled
│ │ │ └── default.conf
│ │ ├── handlers
│ │ │ └── main.yml
│ │ └── tasks
│ │ └── main.yml
│ └── user
│ ├── files
│ │ └── etc
│ │ └── sudoers.d
│ │ └── iganari
│ ├── readme.md
│ └── tasks
│ └── main.yml
└── packer.json
#
# playbook.yml
#
- hosts: pkg-gcp-custom-image-ubuntu1910
remote_user: packer
become: yes
roles:
- role: common
tags: common
- role: nginx
tags: nginx
- role: gcp_logging-agent
tags: gcp_logging-agent
- role: gcp_monitoring-agent
tags: gcp_monitoring-agent
- role: user
tags: user
packer build packer.json
# packer build packer.json
googlecompute: output will be in this color.
==> googlecompute: Checking image does not exist...
==> googlecompute: Creating temporary SSH key for instance...
==> googlecompute: Using image: ubuntu-minimal-1910-eoan-v20200406
==> googlecompute: Creating instance...
googlecompute: Loading zone: asia-northeast1-a
googlecompute: Loading machine type: n1-standard-1
googlecompute: Requesting instance creation...
googlecompute: Waiting for creation operation to complete...
googlecompute: Instance has been created!
==> googlecompute: Waiting for the instance to become running...
googlecompute: IP: 104.198.85.143
==> googlecompute: Using ssh communicator to connect: 104.198.85.143
==> googlecompute: Waiting for SSH to become available...
==> googlecompute: Connected to SSH!
==> googlecompute: Provisioning with Ansible...
==> googlecompute: Executing Ansible: ansible-playbook --extra-vars packer_build_name=googlecompute packer_builder_type=googlecompute -o IdentitiesOnly=yes -i
/tmp/packer-provisioner-ansible505942569 /home/gcloud/package-gcp/compute/instances/create-custom-image-using-packer/ansible/playbook.yml -e ansible_ssh_priv
ate_key_file=/tmp/ansible-key999591282
googlecompute: [DEPRECATION WARNING]: The TRANSFORM_INVALID_GROUP_CHARS settings is set to
googlecompute: allow bad characters in group names by default, this will change, but still be
googlecompute: user configurable on deprecation. This feature will be removed in version 2.10.
googlecompute: Deprecation warnings can be disabled by setting deprecation_warnings=False in
googlecompute: ansible.cfg.
googlecompute: [WARNING]: Invalid characters were found in group names but not replaced, use
googlecompute: -vvvv to see details
googlecompute:
googlecompute: PLAY [pkg-gcp-custom-image-ubuntu1910] *****************************************
googlecompute:
googlecompute: TASK [Gathering Facts] *********************************************************
googlecompute: ok: [default]
googlecompute:
googlecompute: TASK [common : Upgrade all packages to the latest version] *********************
googlecompute: changed: [default]
--割愛--
googlecompute:
googlecompute: PLAY RECAP *********************************************************************
googlecompute: default : ok=29 changed=23 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
googlecompute:
==> googlecompute: Deleting instance...
googlecompute: Instance has been deleted!
==> googlecompute: Creating image...
==> googlecompute: Deleting disk...
googlecompute: Disk has been deleted!
Build 'googlecompute' finished.
==> Builds finished. The artifacts of successful builds are:
--> googlecompute: A disk image was created: packer-1588587422
---> packer-1588587422
という名前の カスタムイメージが出来ました!!
var-file に変数を切り出す
packer コマンドは -var-file
オプションを使うことで変数を切り出すことが出来ます
www.packer.io
{{user `変数名`}}
これを使い、packer.json の中で変更する可能性のものを切り出します
{
"builders" : [
{
"type": "googlecompute",
"project_id": "{{user `_project_id`}}",
"source_image": "{{user `_source_image`}}",
"disk_size": "{{user `_disk_size`}}",
"ssh_username": "{{user `_ssh_username`}}",
"zone": "{{user `_zone`}}",
"network": "{{user `_network`}}",
"subnetwork": "{{user `_subnetwork`}}",
"tags": "{{user `_tags`}}",
"image_name": "{{user `_image_name`}}-{{isotime | clean_resource_name}}",
"scopes": [
"https://www.googleapis.com/auth/cloud-platform"
]
}
],
"provisioners": [
{
"type": "ansible",
"playbook_file": "{{user `_playbook_file`}}",
"groups": "{{user `_groups`}}",
"user": "{{user `_ssh_username`}}"
}
]
}
{
"_project_id": "pkg-gcp-instance-packer",
"_source_image": "ubuntu-minimal-1910-eoan-v20200406",
"_disk_size": "20",
"_ssh_username": "packer",
"_zone": "asia-northeast1-a",
"_network": "projects/pkg-gcp-instance-packer/global/networks/iganari-test-vpc",
"_subnetwork": "projects/pkg-gcp-instance-packer/regions/asia-northeast1/subnetworks/iganari-test-subnet",
"_tags": "allow-ssh-all",
"_image_name": "pkg-gcp-custom-image-ubuntu1910",
"_playbook_file": "./ansible/playbook.yml",
"_groups": "pkg-gcp-custom-image-ubuntu1910"
}
packer build -var-file=variables.json packer.json
--割愛--
==> Builds finished. The artifacts of successful builds are:
--> googlecompute: A disk image was created: pkg-gcp-custom-image-ubuntu1910-2020-05-04t10-30-09z
---> pkg-gcp-custom-image-ubuntu1910-2020-05-04t10-30-09z
という名前の カスタムイメージが出来ました!!
Packer の Variables は使い方が豊富なので、運用に合わせて使い分けましょう
https://www.packer.io/docs/from-1.5/variables/
変更する値のなかで、ホストマシンの環境変数を使いたい場合があります
その時は
{{env `_region`}}
を使用することが出来ます
変数によっては、あまり変更しないもの/よく変更するものとあるので、運用の仕方によってやりかたを変えましょう
今回は プロジェクト ID、VPC ネットワーク、サブネットワーク、Firwall Rules を環境変数にしたい場合を記載します
{
"_project_id": "{{env `_project_id`}}",
"_source_image": "ubuntu-minimal-1910-eoan-v20200406",
"_disk_size": "20",
"_ssh_username": "packer",
"_zone": "{{env `_region`}}-a",
"_network": "projects/{{env `_project_id`}}/global/networks/{{env `_vpc_network_name`}}",
"_subnetwork": "projects/{{env `_project_id`}}/regions/{{env `_region`}}/subnetworks/{{env `_subnet_name`}}",
"_tags": "allow-ssh-all",
"_image_name": "pkg-gcp-custom-image-ubuntu1910",
"_playbook_file": "./ansible/playbook.yml",
"_groups": "pkg-gcp-custom-image-ubuntu1910"
}
export _project_id='pkg-gcp-instance-packer'
export _region='asia-northeast1'
export _vpc_network_name='iganari-test-vpc'
export _subnet_name='iganari-test-subnet'
export _firewall_ssh='allow-ssh-all'
packer build -var-file=variables.json packer.json
--割愛--
==> Builds finished. The artifacts of successful builds are:
--> googlecompute: A disk image was created: pkg-gcp-custom-image-ubuntu1910-2020-05-04t10-39-07z
---> pkg-gcp-custom-image-ubuntu1910-2020-05-04t10-39-07z
という名前の カスタムイメージが出来ました!!
作成したカスタムイメージを使って、インスタンスを起動
実際に意図したカスタムイメージに仕上がっているか確認します
今回は Ansible にて、nginx をインストールしておいたので、それを確認します
export _firewall_http='allow-http-all'
gcloud compute firewall-rules create ${_firewall_http} \
--project ${_project_id} \
--direction=INGRESS \
--priority=1001 \
--network ${_vpc_network_name} \
--action=ALLOW \
--rules=tcp:80,icmp \
--source-ranges=0.0.0.0/0 \
--target-tags=${_firewall_http}
- gcloud コマンドによるインスタンス作成
- 先程作成したカスタムイメージ
pkg-gcp-custom-image-ubuntu1910-2020-05-04t10-39-07z
を使用
export _custom_image='pkg-gcp-custom-image-ubuntu1910-2020-05-04t10-39-07z'
gcloud beta compute instances create packer-test\
--project=${_project_id}\
--zone=${_region}-a\
--machine-type=f1-micro\
--subnet=${_subnet_name}\
--scopes=https://www.googleapis.com/auth/cloud-platform\
--tags=${_firewall_ssh},${_firewall_http}\
--image=${_custom_image}\
--image-project=${_project_id}\
--boot-disk-size=20GB\
--boot-disk-type=pd-standard
# gcloud beta compute instances create packer-test\
> --project=${_project_id}\
> --zone=${_region}-a\
> --machine-type=f1-micro\
> --subnet=${_subnet_name}\
> --scopes=https://www.googleapis.com/auth/cloud-platform\
> --tags=${_firewall_ssh},${_firewall_http}\
> --image=${_custom_image}\
> --image-project=${_project_id}\
> --boot-disk-size=20GB\
> --boot-disk-type=pd-standard
WARNING: You have selected a disk size of under [200GB]. This may result in poor I/O performance. For more information, see: https://developers.google.com/compute/docs/disks#performance.
Created [https://www.googleapis.com/compute/beta/projects/pkg-gcp-instance-packer/zones/asia-northeast1-a/instances/packer-test].
NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS
packer-test asia-northeast1-a f1-micro 172.16.0.18 104.198.85.143 RUNNING
# curl 104.198.85.143
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
---> nginx が起動していることが分かり、意図したカスタムイメージの作成は成功しているようです!!
リソース削除
作業が完了したので、余計なリソースは削除しておきましょう
gcloud beta compute instances delete packer-test\
--project=${_project_id}\
--zone=${_region}-a
gcloud compute firewall-rules delete ${_firewall_ssh}
gcloud compute firewall-rules delete ${_firewall_http}
gcloud beta compute networks subnets delete ${_subnet_name} \
--project ${_project_id}
gcloud beta compute networks delete ${_vpc_network_name} \
--project ${_project_id}
まとめ
Packer を使って GCE のカスタムイメージを作成する一例をまとめることが出来ました
Packer の使い方はもっといろいろあるので、是非試してみてください ;)
Have fun!