Help us understand the problem. What is going on with this article?

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

More than 1 year has passed since last update.

自己紹介

こんにちは。ドリコムでインフラ周りをやっているひらしーです。
今回はマルチクラウドに対応した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

hiracy
インフラ系で主にRuby・Golang・Pythonでツール作ったりもしてます。
http://hiracy.hateblo.jp
drecom-inc
Drecom with entertainment として発明を産み続け、人々の期待を超えるサービスを提供することを目的とした会社です。
https://www.drecom.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした