InSpecとKitchenを使用してAnsibleデプロイメントをテストする方法
序章
InSpec は、規制上の懸念、推奨事項、または要件を記述およびテストするために使用される、オープンソースの監査および自動テストフレームワークです。 人間が読める形式で、プラットフォームに依存しないように設計されています。 開発者は、ローカルでInSpecを使用するか、SSH、WinRM、またはDockerを使用してテストを実行できるため、テスト対象のインフラストラクチャにパッケージをインストールする必要はありません。
InSpecを使用すると、サーバー上で直接テストを実行できますが、インフラストラクチャに問題を引き起こす可能性のある人為的エラーの可能性があります。 このシナリオを回避するために、開発者は Kitchen を使用して仮想マシンを作成し、テストが実行されているマシンに選択したOSをインストールできます。 Kitchenは、テストランナー、つまりテスト自動化ツールであり、1つ以上の分離されたプラットフォームでインフラストラクチャコードをテストできます。 また、多くのテストフレームワークをサポートし、Vagrant、AWS、DigitalOcean、Docker、LXCコンテナーなどのさまざまなプラットフォーム用のドライバープラグインアーキテクチャに柔軟に対応します。
このチュートリアルでは、DigitalOceanUbuntu18.04ドロップレットで実行されているAnsibleプレイブックのテストを作成します。 テストランナーとしてKitchenを使用し、テストを作成するためにInSpecを使用します。 このチュートリアルを終了するまでに、AnsiblePlaybookのデプロイをテストできるようになります。
前提条件
このガイドを開始する前に、以下に加えてDigitalOceanアカウントが必要です。
- のローカルインストール
Ruby
あなたのマシンで。 シリーズのディストリビューションのチュートリアルに従ってRubyをインストールできます:Rubyのローカルプログラミング環境をインストールおよびセットアップする方法。 - Chef開発キット(ChefDK)がマシンにインストールされています。
- SSHキーの設定方法の手順1と2に従って、マシンにSSHキーを設定します。 SSH公開鍵をDigitalOceanアカウントにアップロードするには、SSH鍵をDigitalOceanアカウントに追加する方法チュートリアルに従ってください。
- 読み取りおよび書き込みDigitalOceanパーソナルアクセストークン。 トークンを安全な場所に記録してください。 このチュートリアルの後半で使用します。 これにより、このチュートリアルでテストを実行する場所であるDigitalOceanでドロップレットを作成できます。
ステップ1—キッチンのセットアップと初期化
キッチンに同梱されている前提条件の一部としてChefDKをインストールしました。 このステップでは、DigitalOceanと通信するようにKitchenを設定します。
Kitchenを初期化する前に、プロジェクトディレクトリを作成して移動します。 このチュートリアルでは、これを呼び出します ansible_testing_dir
.
次のコマンドを実行して、ディレクトリを作成します。
- mkdir ~/ansible_testing_dir
そしてそれに移動します:
- cd ~/ansible_testing_dir
使用する gem
kitchen-digitaloceanパッケージをローカルマシンにインストールします。 これはあなたが言うことを可能にします kitchen
テストの実行時にDigitalOceanドライバーを使用するには:
- gem install kitchen-digitalocean
プロジェクトディレクトリ内で、 kitchen init
コマンド指定 ansible_playbook
プロビジョニング担当者として digitalocean
キッチンを初期化するときのドライバーとして:
- kitchen init --provisioner=ansible_playbook --driver=digitalocean
次の出力が表示されます。
Outputcreate kitchen.yml
create chefignore
create test/integration/default
これにより、プロジェクトディレクトリ内に次のものが作成されました。
-
test/integration/default
テストファイルを保存するディレクトリです。 -
chefignore は、特定のファイルが Chef Infra Server にアップロードされないようにするために使用するファイルですが、このチュートリアルでは使用しません。
-
kitchen.yml
テスト構成(テストする対象とターゲットプラットフォーム)を説明するファイルです。
次に、CLIからドロップレットを作成するためのアクセス権を取得するために、DigitalOceanクレデンシャルを環境変数としてエクスポートする必要があります。 まず、次のコマンドを実行して、DigitalOceanアクセストークンから開始します。
- export DIGITALOCEAN_ACCESS_TOKEN="YOUR_DIGITALOCEAN_ACCESS_TOKEN"
SSHキーID番号も取得する必要があります。 ご了承ください YOUR_DIGITALOCEAN_SSH_KEY_IDS
シンボリック名ではなく、SSHキーの数値IDである必要があります。 DigitalOcean APIを使用すると、次のコマンドでキーの数値IDを取得できます。
- curl -X GET https://api.digitalocean.com/v2/account/keys -H "Authorization: Bearer $DIGITALOCEAN_ACCESS_TOKEN"
このコマンドから、SSHキーと関連するメタデータのリストが表示されます。 出力を読んで正しいキーを見つけ、出力内のID番号を特定します。
Output...
{"id":your-ID-number,"fingerprint":"fingerprint","public_key":"ssh-rsa your-ssh-key","name":"your-ssh-key-name"
...
注:出力を読みやすくして数値IDを取得したい場合は、検索してダウンロードできます。 jq
jqダウンロードページのOSに基づいています。 これで、パイプされた前のコマンドを実行できます jq
次のように:
- curl -X GET https://api.digitalocean.com/v2/account/keys -H "Authorization: Bearer $DIGITALOCEAN_ACCESS_TOKEN" | jq
SSHキー情報は次のようにフォーマットされています。
Output{
"ssh_keys": [
{
"id": YOUR_SSH_KEY_ID,
"fingerprint": "2f:d0:16:6b",
"public_key": "ssh-rsa AAAAB3NzaC1yc2 [email protected]",
"name": "sannikay"
}
],
}
SSH数値IDを特定したら、次のコマンドを使用してそれらをエクスポートします。
- export DIGITALOCEAN_SSH_KEY_IDS="YOUR_DIGITALOCEAN_SSH_KEY_ID"
初期化しました kitchen
DigitalOceanクレデンシャルの環境変数を設定します。 次に、コマンドラインから直接DigitalOceanドロップレットでテストを作成して実行します。
ステップ2—AnsiblePlaybookを作成する
このステップでは、プレイブックとロールを作成し、によって作成されたドロップレットにNginxとNode.jsを設定します。 kitchen
次のステップで。 プレイブックで指定された条件が満たされていることを確認するために、プレイブックに対してテストが実行されます。
まず、作成します roles
NginxとNode.jsの両方の役割のディレクトリ:
- mkdir -p roles/{nginx,nodejs}/tasks
これにより、次のようなディレクトリ構造が作成されます。
roles
├── nginx
│ └── tasks
└── nodejs
└── tasks
次に、を作成します main.yml
のファイル roles/nginx/tasks
お好みのエディタを使用したディレクトリ:
- nano roles/nginx/tasks/main.yml
このファイルで、次のコンテンツを追加してNginxをセットアップおよび起動するタスクを作成します。
---
- name: Update cache repositories and install Nginx
apt:
name: nginx
update_cache: yes
- name: Change nginx directory permission
file:
path: /etc/nginx/nginx.conf
mode: 0750
- name: start nginx
service:
name: nginx
state: started
コンテンツを追加したら、ファイルを保存して終了します。
の roles/nginx/tasks/main.yml
、Dropletのキャッシュリポジトリを更新するタスクを定義します。これは、 apt update
サーバー上で手動でコマンドを実行します。 このタスクは、Nginx構成ファイルのアクセス許可も変更し、Nginxサービスを開始します。
また、作成します main.yml
のファイル roles/nodejs/tasks
Node.jsを設定するタスクを定義するには:
- nano roles/nodejs/tasks/main.yml
このファイルに次のタスクを追加します。
---
- name: Update caches repository
apt:
update_cache: yes
- name: Add gpg key for NodeJS LTS
apt_key:
url: "https://deb.nodesource.com/gpgkey/nodesource.gpg.key"
state: present
- name: Add the NodeJS LTS repo
apt_repository:
repo: "deb https://deb.nodesource.com/node_{{ NODEJS_VERSION }}.x {{ ansible_distribution_release }} main"
state: present
update_cache: yes
- name: Install Node.js
apt:
name: nodejs
state: present
終了したら、ファイルを保存して終了します。
の roles/nodejs/tasks/main.yml
、最初に、Dropletのキャッシュリポジトリを更新するタスクを定義します。 次に、次のタスクで、Node.jsの信頼性を検証する手段として機能するNode.jsのGPGキーを追加します。 apt
リポジトリ。 最後の2つのタスクはNode.jsを追加します apt
リポジトリを作成し、Node.jsをインストールします。
次に、変数、ロールを実行する順序、スーパーユーザー特権設定などのAnsible構成を定義します。 これを行うには、という名前のファイルを作成します playbook.yml
、キッチンの入り口として機能します。 テストを実行すると、Kitchenは playbook.yml
ファイルを作成し、実行する役割を探します。 roles/nginx/tasks/main.yml
と roles/nodejs/tasks/main.yml
ファイル。
次のコマンドを実行して作成します playbook.yml
:
- nano playbook.yml
次のコンテンツをファイルに追加します。
---
- hosts: all
become: true
remote_user: ubuntu
vars:
NODEJS_VERSION: 8
ファイルを保存して終了します。
プレイブックで指定された条件が満たされていることを確認するためにテストを実行するAnsibleプレイブックの役割を作成しました。
ステップ3—InSpecテストを作成する
このステップでは、Node.jsがDropletにインストールされているかどうかを確認するためのテストを作成します。 テストを作成する前に、InSpecテストの例の形式を見てみましょう。 多くのテストフレームワークと同様に、InSpecコードは自然言語に似ています。 InSpecには、調査対象と対象の予想される状態の2つの主要なコンポーネントがあります。
describe '<entity>' do
it { <expectation> }
end
ブロックAでは、キーワード do
と end
ブロックを定義します。 The describe
キーワードは一般にテストスイートと呼ばれ、テストケースが含まれています。 The it
キーワードは、テストケースを定義するために使用されます。
<entity>
パッケージ名、サービス、ファイル、ネットワークポートなど、調べたい件名です。 The <expectation>
目的の結果または期待される状態を指定します。たとえば、Nginxをインストールするか、特定のバージョンにする必要があります。 InSpec DSLドキュメントをチェックして、InSpec言語の詳細を確認できます。
InSpecテストブロックの別の例:
control 'Can be anything unique' do
impact 0.7
title 'A human-readable title'
desc 'An optional description'
describe '<entity>' do
it { <expectation> }
end
end
ブロックAとブロックBの違いは control
ブロック。 The control
ブロックは、規制管理、推奨、または要件の手段として使用されます。 The control
ブロックには名前があります。 通常、一意のID、メタデータなど desc
, title
, impact
、そして最後に関連するグループ describe
チェックを実装するためのブロック。
desc
, title
、 と impact
コントロールの重要性とその目的を、簡潔で完全な説明とともに完全に説明するメタデータを定義します。 impact
からの範囲の数値を定義します 0.0
に 1.0
どこ 0.0
に <0.01
影響なしとして分類され、 0.01
に <0.4
低影響として分類され、 0.4
に <0.7
中程度の影響として分類され、 0.7
に <0.9
影響が大きいと分類され、 0.9
に 1.0
クリティカルコントロールとして分類されます。
次に、テストを実装します。 ブロックAの構文を使用して、InSpecのpackageリソースを使用して次のことをテストします。 Node.js
システムにインストールされています。 次の名前のファイルを作成します sample.rb
あなたの中で test/integration/default
テスト用のディレクトリ。
作成 sample.rb
:
- nano test/integration/default/sample.rb
ファイルに以下を追加します。
describe package('nodejs') do
it { should be_installed }
end
ここであなたのテストは使用しています package
Node.jsをチェックするためのリソースがインストールされています。
終了したら、ファイルを保存して終了します。
このテストを実行するには、編集する必要があります kitchen.yml
以前に作成したプレイブックを指定し、構成に追加します。
あなたの kitchen.yml
ファイル:
- nano ansible_testing_dir/kitchen.yml
のコンテンツを置き換えます kitchen.yml
次のように:
---
driver:
name: digitalocean
provisioner:
name: ansible_playbook
hosts: test-kitchen
playbook: ./playbook.yml
verifier:
name: inspec
platforms:
- name: ubuntu-18
driver_config:
ssh_key: PATH_TO_YOUR_PRIVATE_SSH_KEY
tags:
- inspec-testing
region: fra1
size: 1gb
private_networking: false
verifier:
inspec_tests:
- test/integration/default
suites:
- name: default
The platform
オプションには次のものがあります。
-
name
:使用している画像。 -
driver_config
:DigitalOceanドロップレット構成。 次のオプションを指定していますdriver_config
:ssh_key
:へのパスYOUR_PRIVATE_SSH_KEY
. 君のYOUR_PRIVATE_SSH_KEY
を作成するときに指定したディレクトリにありますssh
鍵。tags
:ドロップレットに関連付けられているタグ。region
:region
ドロップレットをホストする場所。size
:ドロップレットに持たせたいメモリ。
-
verifier
:これは、プロジェクトにInSpecテストが含まれていることを定義します。- The
inspec_tests
一部は、テストがプロジェクトの下に存在することを指定しますtest/integration/default
ディレクトリ。
- The
に注意してください name
と region
略語を使用します。 使用できる略語については、test-kitchenのドキュメントを確認してください。
構成を追加したら、ファイルを保存して終了します。
を実行します kitchen test
テストを実行するコマンド。 これにより、Node.jsがインストールされているかどうかが確認されます。現在、Node.jsの役割がないため、意図的に失敗します。 playbook.yml
ファイル:
- kitchen test
次のような出力が表示されます。
Output: failing test results-----> Starting Kitchen (v1.24.0)
-----> Cleaning up any prior instances of <default-ubuntu-18>
-----> Destroying <default-ubuntu-18>...
DigitalOcean instance <145268853> destroyed.
Finished destroying <default-ubuntu-18> (0m2.63s).
-----> Testing <default-ubuntu-18>
-----> Creating <default-ubuntu-18>...
DigitalOcean instance <145273424> created.
Waiting for SSH service on 138.68.97.146:22, retrying in 3 seconds
[SSH] Established
(ssh ready)
Finished creating <default-ubuntu-18> (0m51.74s).
-----> Converging <default-ubuntu-18>...
$$$$$$ Running legacy converge for 'Digitalocean' Driver
-----> Installing Chef Omnibus to install busser to run tests
PLAY [all] *********************************************************************
TASK [Gathering Facts] *********************************************************
ok: [localhost]
PLAY RECAP *********************************************************************
localhost : ok=1 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
Downloading files from <default-ubuntu-18>
Finished converging <default-ubuntu-18> (0m55.05s).
-----> Setting up <default-ubuntu-18>...
$$$$$$ Running legacy setup for 'Digitalocean' Driver
Finished setting up <default-ubuntu-18> (0m0.00s).
-----> Verifying <default-ubuntu-18>...
Loaded tests from {:path=>". ansible_testing_dir.test.integration.default"}
Profile: tests from {:path=>"ansible_testing_dir/test/integration/default"} (tests from {:path=>"ansible_testing_dir.test.integration.default"})
Version: (not specified)
Target: ssh://[email protected]:22
System Package nodejs
× should be installed
expected that System Package nodejs is installed
Test Summary: 0 successful, 1 failure, 0 skipped
>>>>>> ------Exception-------
>>>>>> Class: Kitchen::ActionFailed
>>>>>> Message: 1 actions failed.
>>>>>> Verify failed on instance <default-ubuntu-18>. Please see .kitchen/logs/default-ubuntu-18.log for more details
>>>>>> ----------------------
>>>>>> Please see .kitchen/logs/kitchen.log for more details
>>>>>> Also try running `kitchen diagnose --all` for configuration
4.54s user 1.77s system 5% cpu 2:02.33 total
プロビジョニングしたドロップレットにNode.jsがインストールされていないため、テストが失敗していることが出力に示されます kitchen
. を追加してテストを修正します nodejs
あなたへの役割 playbook.yml
ファイルを作成して、テストを再実行してください。
編集します playbook.yml
含めるファイル nodejs
役割:
- nano playbook.yml
次の強調表示された行をファイルに追加します。
---
- hosts: all
become: true
remote_user: ubuntu
vars:
NODEJS_VERSION: 8
roles:
- nodejs
ファイルを保存して閉じます。
次に、を使用してテストを再実行します kitchen test
指図:
- kitchen test
次の出力が表示されます。
Output......
Target: ssh://[email protected]:22
System Package nodejs
✔ should be installed
Test Summary: 1 successful, 0 failures, 0 skipped
Finished verifying <default-ubuntu-18> (0m4.89s).
-----> Destroying <default-ubuntu-18>...
DigitalOcean instance <145512952> destroyed.
Finished destroying <default-ubuntu-18> (0m2.23s).
Finished testing <default-ubuntu-18> (2m49.78s).
-----> Kitchen is finished. (2m55.14s)
4.86s user 1.77s system 3% cpu 2:56.58 total
Node.jsを使用してインストールしているため、テストに合格します。 nodejs
役割。
これがキッチンが行っていることの要約です Test Action
:
- ドロップレットが存在する場合は破棄します
- ドロップレットを作成します
- ドロップレットを収束します
- InSpecでドロップレットを検証します
- ドロップレットを破壊します
問題が発生した場合、KitchenはDropletの実行を中止します。 これは、Ansibleプレイブックが失敗した場合、InSpecが実行されず、Dropletが破棄されないことを意味します。 これにより、インスタンスの状態を検査して問題を修正する機会が得られます。 必要に応じて、最終的な破棄アクションの動作をオーバーライドできます。 のCLIヘルプを確認してください --destroy
を実行してフラグを立てます kitchen help test
指図。
最初のテストを作成し、問題を修正する前に1つのインスタンスが失敗した状態で、プレイブックに対して実行しました。 次に、テストファイルを拡張します。
ステップ4—テストケースを追加する
このステップでは、テストファイルにさらにテストケースを追加して、NginxモジュールがDropletにインストールされており、構成ファイルに適切な権限があるかどうかを確認します。
あなたの編集 sample.rb
さらにテストケースを追加するファイル:
- nano test/integration/default/sample.rb
次のテストケースをファイルの最後に追加します。
. . .
control 'nginx-modules' do
impact 1.0
title 'NGINX modules'
desc 'The required NGINX modules should be installed.'
describe nginx do
its('modules') { should include 'http_ssl' }
its('modules') { should include 'stream_ssl' }
its('modules') { should include 'mail_ssl' }
end
end
control 'nginx-conf' do
impact 1.0
title 'NGINX configuration'
desc 'The NGINX config file should owned by root, be writable only by owner, and not writeable or and readable by others.'
describe file('/etc/nginx/nginx.conf') do
it { should be_owned_by 'root' }
it { should be_grouped_into 'root' }
it { should_not be_readable.by('others') }
it { should_not be_writable.by('others') }
it { should_not be_executable.by('others') }
end
end
これらのテストケースは、 nginx-modules
ドロップレットに含める http_ssl
, stream_ssl
、 と mail_ssl
. あなたもチェックしています /etc/nginx/nginx.conf
ファイルのアクセス許可。
両方を使用しています it
と its
テストを定義するキーワード。 キーワード its
リソースのプロパティにアクセスするためにのみ使用されます。 例えば、 modules
のプロパティです nginx
.
テストケースを追加したら、ファイルを保存して終了します。
今実行します kitchen test
再度テストするコマンド:
- kitchen test
次の出力が表示されます。
Output...
Target: ssh://[email protected]:22
↺ nginx-modules: NGINX modules
↺ The `nginx` binary not found in the path provided.
× nginx-conf: NGINX configuration (2 failed)
× File /etc/nginx/nginx.conf should be owned by "root"
expected `File /etc/nginx/nginx.conf.owned_by?("root")` to return true, got false
× File /etc/nginx/nginx.conf should be grouped into "root"
expected `File /etc/nginx/nginx.conf.grouped_into?("root")` to return true, got false
✔ File /etc/nginx/nginx.conf should not be readable by others
✔ File /etc/nginx/nginx.conf should not be writable by others
✔ File /etc/nginx/nginx.conf should not be executable by others
System Package nodejs
✔ should be installed
Profile Summary: 0 successful controls, 1 control failure, 1 control skipped
Test Summary: 4 successful, 2 failures, 1 skipped
一部のテストが失敗していることがわかります。 あなたはそれらを追加することによってそれらを修正するつもりです nginx
プレイブックファイルへの役割とテストの再実行。 失敗したテストでは、次のことを確認しています。 nginx
現在サーバーに存在しないモジュールとファイルのアクセス許可。
あなたの playbook.yml
ファイル:
- nano ansible_testing_dir/playbook.yml
次の強調表示された行を役割に追加します。
---
- hosts: all
become: true
remote_user: ubuntu
vars:
NODEJS_VERSION: 8
roles:
- nodejs
- nginx
終了したら、ファイルを保存して閉じます。
次に、テストを再度実行します。
- kitchen test
次の出力が表示されます。
Output...
Target: ssh://[email protected]:22
✔ nginx-modules: NGINX version
✔ Nginx Environment modules should include "http_ssl"
✔ Nginx Environment modules should include "stream_ssl"
✔ Nginx Environment modules should include "mail_ssl"
✔ nginx-conf: NGINX configuration
✔ File /etc/nginx/nginx.conf should be owned by "root"
✔ File /etc/nginx/nginx.conf should be grouped into "root"
✔ File /etc/nginx/nginx.conf should not be readable by others
✔ File /etc/nginx/nginx.conf should not be writable by others
✔ File /etc/nginx/nginx.conf should not be executable by others
System Package nodejs
✔ should be installed
Profile Summary: 2 successful controls, 0 control failures, 0 controls skipped
Test Summary: 9 successful, 0 failures, 0 skipped
追加した後 nginx
プレイブックへの役割すべてのテストに合格しました。 出力は、 http_ssl
, stream_ssl
、 と mail_ssl
モジュールがDropletにインストールされ、構成ファイルに適切な権限が設定されます。
終了したら、またはドロップレットが不要になったら、を実行してドロップレットを破棄できます。 kitchen destroy
テストの実行後に削除するコマンド:
- kitchen destroy
このコマンドに続いて、次のような出力が表示されます。
Output-----> Starting Kitchen (v1.24.0)
-----> Destroying <default-ubuntu-18>...
Finished destroying <default-ubuntu-18> (0m0.00s).
-----> Kitchen is finished. (0m5.07s)
3.79s user 1.50s system 82% cpu 6.432 total
プレイブックのテストを作成し、テストを実行し、失敗したテストを修正して、すべてのテストに合格していることを確認しました。 これで、仮想環境を作成し、Ansible Playbookのテストを作成し、Kitchenを使用して仮想環境でテストを実行する準備が整いました。
結論
これで、Ansibleデプロイメントをテストするための柔軟な基盤ができました。これにより、ライブサーバーで実行する前にプレイブックをテストできます。 テストをプロファイルにパッケージ化することもできます。 プロファイルを使用して、Githubまたは Chef Supermarket を介してテストを共有し、ライブサーバーで簡単に実行できます。
InSpecとKitchenのより包括的な詳細については、公式InSpecドキュメントおよび公式Kitchenドキュメントを参照してください。