[Get started with Test Kitchen with CentOS on Amazon Web Services]

本 module 一開始也要選擇環境,我選的是 CentOS 7 -> Amazon Web Services。

到目前我們學到的 Chef 開發流程如下:

  1. 寫 Chef 程式碼
  2. 上傳至 Chef server
  3. 在 node 上跑 chef-client 指令
  4. 連線進 node 看新的設定是否生效
  5. 重複以上流程

學習筆記 part 7 中,我們上傳了一個有問題的 recipe 導致 web server 啟動失敗,我們必須想辦法在這組設定在上 production 環境之前先發現問題,而不是跑了 chef-client 失敗了才去修正。

一般會想到也常被使用的方法就是再 build 一個測試環境先跑過再上 production,但這邊要介紹的是 Test Kitchen,可以用來快速實驗新的變更有沒有問題。

用 Test Kitchen 的做法差別在於它會建立一個臨時的測試環境來測試我們的程式碼,就不用每次都上傳到 Chef server 之後還要再跑 chef-client 才能知道結果;這個臨時的環境可以是 workstation 上的 virtual machine 或雲端機器或 container instance。

就以我自己初學到現在的使用經驗,每做一個小修改就要上傳到 Chef server 再跑 chef-cient 這整個流程其實還蠻沒效率的。而 Test Kitchen 就是為了解決這個問題,讓我們可以在本機端先跑過測試再上 Chef server。這個流程以下會稱為 local development。

使用 local development 的話整個開發流程就會變成:

  1. 更新 Chef 程式碼
  2. 在 local 端跑測試確認變更是否正確
  3. 將程式碼上傳至 Chef server
  4. 在 node 上執行 chef-client
  5. 重複以上流程

使用 Test Kitchen 另一個好處就是不管你的 work station 是什麼環境都沒關係。

[Set up your workstation]

Test Kitchen 可以整合 EC2 的 AMI、instance type (t2.micro, t2.small 之類的)、security groups,感覺考慮的很周到。

要設定之前必須先準備好以下的項目:

  1. AWS credentials file (用來登入機器的 .pem)
  2. Region and availability zone (例 us-east-1b)
  3. VPC subnet ID
  4. Security group ID
  5. CentOS 7 AMI ID (我是用新加坡的 ami-7d2eab1e)
  6. AWS key pair file (須有開機器的權限)

這些項目如果平常有在用 EC2 的話應該都不陌生,就不多解釋了。

如果你用的是 Linux 或 macOS,可以在 ~/.aws/credentials 設定好 key pair 以節省麻煩:

[default]
aws_access_key_id = ACCESS_KEY
aws_secret_access_key = SECRET_KEY

如果你有在用 aws-cli 的話這部分應該就已經是設定好的狀態了。

[Apply a cookbook locally]

整個 kitchen 的測試流程如下:

  1. kitchen create — Test Kitchen 在虛擬環境中開一台機器
  2. kitchen converge — Test Kitchen 套用 cookbook 到虛擬環境
  3. kitchen login — Test Kitchen 建立 SSH 連線到虛擬環境
  4. verify — 我們要在這個步驟人工驗證設定是否正確
  5. kitchen destroy — Test Kitchen 把虛擬環境關掉

這邊特別說明,在這個 module 裡我們只會學到人工確認結果 (上述的 verify 步驟),但在典型的實作上會寫自動測試,在之後的 module 裡會學到。

從 GitHub 取得 learn_chef_httpd cookbook

先新增好資料夾 ~/learn-chef/cookbooks 並進入,然後下指令:

$ git clone https://github.com/learn-chef/learn_chef_httpd.git
Cloning into 'learn_chef_httpd'...
remote: Counting objects: 97, done.
remote: Total 97 (delta 0), reused 0 (delta 0), pack-reused 97
Unpacking objects: 100% (97/97), done.

新增 kitchen 設定檔

如果是用 chef generate cookbook 生成的 cookbook 目錄,會內含一個 .kitchen.yaml 的檔案。剛剛 clone 下來的 repository 底下就有這個檔案。

進入 learn_chef_httpd 目錄,開啟檔案 .kitchen.yml ,並修改成以下內容:

---
driver:
name: ec2
aws_ssh_key_id: learn-chef
region: us-east-1
availability_zone: b
subnet_id: subnet-3f22bd15
instance_type: t2.micro
image_id: ami-46c1b650
security_group_ids: ["sg-0e08cf75"]
retryable_tries: 120

provisioner:
name: chef_zero

verifier:
name: inspec

transport:
ssh_key: /root/.ssh/learn-chef.pem

platforms:
- name: centos-7.3

suites:
- name: default
run_list:
- recipe[learn_chef_httpd::default]
verifier:
inspec_tests:
- test/smoke/default
attributes:

然後把上面粗體的部分換成你自己的設定。

這裡我有個小疑問,就是 availability_zone 不知道可不可以設定多個?簡單辜狗了一下沒有找到答案,先不管。

上面這個 .kitchen.yml 的說明如下:

  • driver常用的 drivers 在此
  • provistioner:這裡用的 chef_zero 讓我們的 local 環境偽裝成 Chef server 環境。
  • transport:指定連線到 instance 的方法
  • verifier:指定用哪個 application 來跑自動測試。這部分以後才會學到。
  • platforms:指明是什麼作業系統。
  • suites:指定要用套用哪些設定到虛擬環境裡。

比較詳細的說明可參考 https://docs.chef.io/config_yml_kitchen.html

產生測試實例

先用 kitchen list 看看到目前為止 kitchen 裡有什麼東西:

$ kitchen list
Instance Driver Provisioner Verifier Transport Last Action Last Error
default-centos-73 Ec2 ChefZero Inspec Ssh <Not Created> <None>

接下來用 kitchen create 產生測試實例:

$ kitchen create
-----> Starting Kitchen (v1.16.0)
-----> Creating <default-centos-73>...
Detected platform: centos version 7 on x86_64. Instance Type: t2.micro. Default username: cento
s (default).
If you are not using an account that qualifies under the AWS
free-tier, you may be charged to run these suites. The charge
should be minimal, but neither Test Kitchen nor its maintainers
are responsible for your incurred costs.
Instance <i-0c08c42b8616f52eb> requested.
Polling AWS for existence, attempt 0...
Attempting to tag the instance, 0 retries
EC2 instance <i-0c08c42b8616f52eb> created.
Waited 0/600s for instance <i-0c08c42b8616f52eb> volumes to be ready.
Waited 0/600s for instance <i-0c08c42b8616f52eb> to become ready.
Waited 5/600s for instance <i-0c08c42b8616f52eb> to become ready.
Waited 10/600s for instance <i-0c08c42b8616f52eb> to become ready.
Waited 15/600s for instance <i-0c08c42b8616f52eb> to become ready.
Waited 20/600s for instance <i-0c08c42b8616f52eb> to become ready.
EC2 instance <i-0c08c42b8616f52eb> ready.
Waiting for SSH service on ec2-54-169-127-55.ap-southeast-1.compute.amazonaws.com:22, retrying in 3 seconds
Waiting for SSH service on ec2-54-169-127-55.ap-southeast-1.compute.amazonaws.com:22, retrying in 3 seconds
Waiting for SSH service on ec2-54-169-127-55.ap-southeast-1.compute.amazonaws.com:22, retrying in 3 seconds
[SSH] Established
Finished creating <default-centos-73> (1m16.46s).
-----> Kitchen is finished. (1m24.67s)

打開 EC2 console 看一下,真的自動開了一台 id 是 i-0c08c42b8616f52eb 的機器!

再次用 kitchen list 看一下:

$ kitchen list
Instance Driver Provisioner Verifier Transport Last Action Last Error
default-centos-73 Ec2 ChefZero Inspec Ssh Created <None>

套用 cookbook

執行 kitchen converge

$ kitchen converge
-----> Starting Kitchen (v1.16.0)
-----> Converging <default-centos-73>...
(...略)
Running handlers:
Running handlers complete
Chef Client finished, 4/4 resources updated in 15 seconds
Finished converging <default-centos-73> (0m49.56s).
-----> Kitchen is finished. (0m52.10s)

我們可以用 echo $? 指令來確認這中間是否有錯誤產生,如果有的話,就會回傳不是 0 的數字,你就必須把錯誤修掉。

$ echo $?
0

我這次回傳的是 0,所以沒有問題。用 kitchen list 看一下狀態:

$ kitchen list
Instance Driver Provisioner Verifier Transport Last Action Last Error
default-centos-73 Ec2 ChefZero Inspec Ssh Converged <None>

可以看到 Last Action 已經變成 Converged

另外,第一次執行 kitchen converge 會跑比較久,第二次之後因為相關的套件都已經安裝好,就會變比較快了。

檢驗設定是否正確

前面有說過,實作上我們應該要寫自動測試來檢驗結果,但現在會先用人工驗證,這邊要做的是登入測試的 instance 然後 curl localhost 看有沒有回傳 “hello world” 即可。

登入的話只要用 kitchen login 指令即可,因為登入資訊在前面的步驟都已經設定好了。

$ kitchen login
Last login: Wed Sep 27 11:30:38 2017 from 122-116-172-114.hinet-ip.hinet.net
$ curl localhost
<html>
<body>
<h1>hello world</h1>
</body>
</html>

確認沒有問題。

但這樣方式還是有點遜,我們可以用類似 knife ssh 的方式連線到測試環境去下命令,這個指令叫 kitchen exec

$ kitchen exec -c 'curl localhost'
-----> Execute command on default-centos-73.
<html>
<body>
<h1>hello world</h1>
</body>
</html>

kitchen exec 的文件在這裡,可以找到 -c 這個選項就是你想下的指令內容。

關閉測試環境

確認完結果之後就可以把測試環境摧毀掉了,指令也很簡單,用 kitchen destroy

$ kitchen destroy
-----> Starting Kitchen (v1.16.0)
-----> Destroying <default-centos-73>...
EC2 instance <i-0c08c42b8616f52eb> destroyed.
Finished destroying <default-centos-73> (0m0.61s).
-----> Kitchen is finished. (0m3.16s)

進 EC2 console 看一下,instance 已被 terminated。

再看一下 kitchen list

$ kitchen list
Instance Driver Provisioner Verifier Transport Last Action Last Error
default-centos-73 Ec2 ChefZero Inspec Ssh <Not Created> <None>

Last Action 又變回 <Not Created> 了。

測驗

Test Kitchen:

  • Lets you run your cookbooks in temporary environments.
  • Manages the virtual machines you use for temporary environments.
  • Makes it easier to work with the hypervisor that manages the virtual machines.

Which of these commands generates the .kitchen.yml file?

  • kitchen cat .kitchen.yml
  • file '/.kitchen.yml'
  • chef generate cookbook

Which of these commands directs Test Kitchen to add a temporary environment?

  • kitchen make
  • kitchen create
  • kitchen build

The kitchen converge command:

  • Runs chef-client on the instance.
  • Runs automated tests on the instance.
  • Downloads the latest version of the provisioner.

Which command gets rid of unwanted instances?

  • kitchen delete
  • kitchen clean
  • kitchen destroy

答案是 1, 3, 2, 1, 3。

--

--