Ansible
packer
serverspec-runner
DrecomDay 11

Ansible+serverspec-runner+PackerでAWSとGCPに対応したシンプルなマルチクラウドインフラCI環境を構築する

eyecatch_AdventCalendar2018_11.png

これは ドリコム ドリコム Advent Calendar 2018 の11日目です。

10日目は北原(ちっそ)さんによる、 「私と仕事どっちが大事なの!?」にクリシン的に回答してみる です。


自己紹介

こんにちは。ドリコムでインフラ周りをやっているひらしーです。

今回はマルチクラウドに対応した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

[official][Github]

Chef,itamaeといったいわゆる構成管理ツールで、レッドハット社が開発しているOSSです。

本体はPythonで作られていますが拡張はPython以外の言語でも開発可能で、基本的にYAMLで作ることになります。

エージェントレスですが、リモート実行先にはPythonが実行できる環境が必要で、拡張モジュールの使用するライブラリによっては実行先のPythonのバージョンに注意が必要です。


Packer

[official][Github]

HachiCorp社の開発しているマシンイメージを管理するツールです。手軽に各クラウド業者に対応した仮想マシンイメージを作成することができます。


serverspec-runner

[Github][Qiita]

本家(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/files/nginx.repo

[nginx]

name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1


nginx/tasks/main.yml

---

- 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ファイルを作ります。


boot.yml

---

- name: bootstrap configuration
hosts: all
become: yes
roles:
- nginx


ansibleの実行テスト

今回はpacker内で自動で接続設定されるのですが、事前にインベントリファイルを用意することで既存サーバにて事前に実行テストすることも可能です。


image_vm.yml

---

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


spec/roles/nginx_spec.rb

require 'spec_helper'

describe package('nginx') do
it { should be_installed }
end



serverspec-runnerの実行

serverspec-runnerを実行するためには初期設定にて生成されたscenario.ymlというファイルを上書きしてテストシナリオを記載します。

こちらも既存のサーバに対して事前に実行できます。


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で取得します。


machine.json

{

"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