自己紹介
こんにちは。ドリコムでインフラ周りをやっているひらしーです。
今回はマルチクラウドに対応したCI環境を手軽に作れる手法の1つとして、Ansible+serverspec-runner+Packerを使った手法を紹介します。
インフラCI環境の構成について
サーバアプリケーション、インフラ周りの状態のテストを行うツールは数多く存在します。
世の中には複雑な構成をプラグラマブルに記載することが必要なケースもありますが、アプリケーションのロジック以外では比較的簡素な状態の確認のみで済む場合が多く、それよりもクラウドに依存した構成に柔軟に対応するためにインフラ構成を作り直すスピードが重要なケースが増えていると思います。
今回の構成はインフラ周りで比較的シンプル且つクラウド、ネットワークに依存しない記述で実現できる構成を検討しました。
サンプルの動作環境
本内容では以下の環境での動作を確認しています。
各ツールの詳細については以下で紹介している公式ページやgithubのリポジトリをご参照下さい。
また、クラウド側は事前設定にてVMインスタンスが画面操作等により作成可能、プライベートIPにてsshで接続できることとします。
-
ツール
- OS: CentOS 7.2, macOS 10.13.6(ローカル環境のみ)
- ruby: 2.4.5(ローカル環境)
- python: 2.7.10
- ansible: 2.6.2
- packer: 1.3.2
- serverspec-runner: 1.3.8
-
対応クラウド
- AWS
- GCP
各ツールの簡単な説明
Ansible
Chef,itamaeといったいわゆる構成管理ツールで、レッドハット社が開発しているOSSです。
本体はPythonで作られていますが拡張はPython以外の言語でも開発可能で、基本的にYAMLで作ることになります。
エージェントレスですが、リモート実行先にはPythonが実行できる環境が必要で、拡張モジュールの使用するライブラリによっては実行先のPythonのバージョンに注意が必要です。
Packer
HachiCorp社の開発しているマシンイメージを管理するツールです。手軽に各クラウド業者に対応した仮想マシンイメージを作成することができます。
serverspec-runner
本家(serverspec): [official][Github]
インフラ環境の状態を管理するツールとしてはデファクトスタンダードとなっているserverspecを手軽に管理・実行するツールです。
Ansibleでプロビジョニング
インストール
# CentOS
$ yum install ansible
# macOS
$ brew install ansible
roleの作成
Ansibleで実際に構成管理の内容を作る際にはAnsible Rolesに準拠するのが一般的です。
role構造のスケルトンの作成はansibleをインストールに付随されるansible-playbookのコマンドにて実現できます。
サンプルとしてnginxの設定を作ってみます。
$ mkdir advent-calendar-2018-ansible-serverspecrunner-packer-sample
$ cd advent-calendar-2018-ansible-serverspecrunner-packer-sample
$ ansible-galaxy init nginx
$ tree nginx
nginx
├── README.md
├── defaults
│ └── main.yml
├── files
├── handlers
│ └── main.yml
├── meta
│ └── main.yml
├── tasks
│ └── main.yml
├── templates
├── tests
│ ├── inventory
│ └── test.yml
└── vars
└── main.yml
構成管理設定の記述
Chef,Itamaeで言う所の"レシピ"をYAMLにて記載します。
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1
---
- name: set nginx repo file
copy:
src: nginx.repo
dest: /etc/yum.repos.d/nginx.repo
mode: 0644
owner: root
group: root
- name: install nginx
package:
name: nginx
state: present
role呼び出し元のYAMLファイルを作ります。
---
- name: bootstrap configuration
hosts: all
become: yes
roles:
- nginx
ansibleの実行テスト
今回はpacker内で自動で接続設定されるのですが、事前にインベントリファイルを用意することで既存サーバにて事前に実行テストすることも可能です。
---
default:
hosts:
image-vm:
vars:
ansible_host: xxx.xxx.xxx.xxx
ansible_connection: ssh
ansible_user: your_name
ansible_port: 22
ansible_ssh_common_args: '-o StrictHostKeyChecking=no'
ansible_ssh_private_key_file: /home/your_name/.ssh/id_rsa
$ ansible-playbook -i image_vm.yml boot.yml
PLAY [bootstrap configuration] *********************************************
TASK [Gathering Facts] *****************************************************
ok: [image-vm]
TASK [nginx : set nginx repo file] *****************************************
changed: [image-vm]
TASK [nginx : install nginx] ***********************************************
changed: [image-vm]
PLAY RECAP *****************************************************************
image-vm : ok=1 changed=2 unreachable=0 failed=0
serverspec-runnerによるインフラテスト
インストールと初期設定
# CentOS,macOS
$ gem install serverspec-runner
serverspec-runnerの初期設定を行うためにansible-galaxyで作成したディレクトリにて以下を実行します。
$ serverspec-runner --activate-specroot --specroot .
テストの作成
serverspec-runnerではserverspecと全く同じspecファイルが利用できます。
尚、スケルトン生成時にtestsディレクトリが自動生成されますがこちらは公式のtestsディレクトリのドキュメントに記載されているようにシェルスクリプトでのテスト方法が例示されています。
$ mkdir -p spec/roles
require 'spec_helper'
describe package('nginx') do
it { should be_installed }
end
serverspec-runnerの実行
serverspec-runnerを実行するためには初期設定にて生成されたscenario.ymlというファイルを上書きしてテストシナリオを記載します。
こちらも既存のサーバに対して事前に実行できます。
---
roles:
- image-vm
serverspec-runnerはansibleのインベントファイルを扱えるので前述で作成されたものを指定します。
$ serverspec-runner --inventory image_vm.yml --scenario scenario.yml
### start [roles@image-vm] (xxx.xxx.xxx.xxx) serverspec... ###
Package "nginx"
should be installed
Finished in 1.58 seconds (files took 1.87 seconds to load)
1 example, 0 failures
+-----------------------------------------+
|description | result |
+-----------------------------------------+
|roles@image-vm(xxx.xxx.xxx.xxx) | |
| Package "nginx" | |
| should be installed | OK |
+-----------------------------------------+
このようにシンプルな設定でサーバの状態をチェックすることができます。
Packerによる仮想マシンイメージの作成
インストールと初期設定
こちらの公式ページに従ってインストールして下さい。
ワンバイナリによる実行ファイルなのでプラットフォームにあった実行ファイルをコピーするだけでも問題ありません。
# CentOS
$ wget https://releases.hashicorp.com/packer/1.3.2/packer_1.3.2_linux_amd64.zip
$ unzip packer_1.3.2_linux_amd64.zip
$ sudo cp packer /usr/local/bin
$ sudo chown root: /usr/local/bin/packer
$ sudo chmod 755 /usr/local/bin/packer
# macOS
$ brew install packer
AWSでの操作のためにcredentialを設定します。既に設定済みの方は無視して下さい。
# ~/.aws/config にアクセスキー、シークレットキー設定が追加される
aws configure
設定ファイル
Packerはシンプルなkey-valueのみで構成されます。
今回は既に各クラウドにてVMインスタンスを起動できることが前提となっているため詳細は省かせて頂きます。
ポイントはpackerが実行するベースイメージに依存しないためにshell-local provisionerを使用してローカル環境からserverspec-runnerを実行している点です。packerで一時的に作られるVMインスタンスのIPアドレスはpacker実行中のログからegrepで取得します。
{
"builders": [
{
"type": "amazon-ebs",
"ssh_username": "ec2-user",
"ssh_private_key_file": "/path/to/your/private_key",
"ssh_keypair_name": "your_keypair_name",
"ssh_port": "22",
"ssh_pty": true,
"temporary_key_pair_name": "temp_your_keypair_name_{{isotime}}",
"region": "ap-northeast-1",
"availability_zone": "ap-northeast-1a",
"associate_public_ip_address": true,
"instance_type": "t2.micro",
"ami_name": "{{template_{{isotime | clean_ami_name}}",
"user_data": "#!/bin/sh -xe\nsed -i 's/^.*requiretty/#Defaults requiretty/' /etc/sudoers\nservice sshd reload",
"source_ami": "ami-xxxxxxxx",
"security_group_ids": ["{{user `aws_security_group_id`}}"],
"subnet_id": "sg-xxxxxxxx",
"ssh_interface": "private_ip",
"ami_block_device_mappings": [
{
"device_name": "/dev/sda1",
"delete_on_termination": true,
"volume_type": "gp2"
}
]
},
{
"type": "googlecompute",
"ssh_pty": true,
"ssh_port": "22",
"ssh_username": "your_gcp_account_user",
"ssh_private_key_file": "/path/to/your/private_key",
"disable_default_service_account": true,
"account_file": "/path/to/your/accout_file_path.json",
"zone": "asia-northeast1-a",
"project_id": "your_project_id",
"machine_type": "n1-standard-1",
"source_image_family": "centos-7",
"disk_size": "20",
"network": "your_gcp_network",
"subnetwork": "your_gcp_subnetwork",
"use_internal_ip": false,
"tags": [
"your_tag_1",
"your_tag_2"
],
"preemptible": true,
"on_host_maintenance": "TERMINATE",
"image_name": "template_{{isotime | clean_image_name}}"
}
],
"provisioners": [
{
"type": "ansible",
"playbook_file": "./boot.yml"
},
{
"type": "shell-local",
"command": "export PACKER_TARGET_IP=$(egrep -m1 -o '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}' ./packer.log); sed -i -e \"s/xxx.xxx.xxx.xxx/$PACKER_TARGET_IP/g\" ./image_vm.yml"
},
{
"type": "shell-local",
"command": "serverspec-runner --inventory image_vm.yml --scenario scenario.yml"
},
{
"type": "shell-local",
"command": "export PACKER_TARGET_IP=$(egrep -m1 -o '[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}' ./packer.log); sed -i -e \"s/$PACKER_TARGET_IP/xxx.xxx.xxx.xxx/g\" ./image_vm.yml"
}
]
}
Packerの実行
# AWS
$ packer build -only="amazon-ebs" machine.json | tee packer.log
# GCP
$ packer build -only="googlecompute" machine.json | tee packer.log
尚、ansibleの実行やテストに失敗する場合は、以下のようなオプションを付ければイメージを破棄しないので作られたVMインスタンスを調査をすることが可能です。
$ packer build -on-error=abort -only="googlecompute" machine.json | tee packer.log
最後に
本記事ではAnsible+serverspec-runner+Packerでマルチクラウドに対応したシンプルなインフラCI環境構築を紹介しました。
以下に今回の全てのコンテンツがありますので是非参考にして頂ければと思います。
github.com/hiracy/advent-calendar-2018-ansible-serverspecrunner-packer-sample