designetwork

ネットワークを軸としたIT技術メモ

TurbulenceでBOSH環境にカオスエンジニアリングを導入する

Cloud Foundry Advent Calender 2018 の24日目

先進的な企業・サービスで導入されているカオスエンジニアリングを実施してみる。Cloud FoundryやKubernetesなど、BOSHで構築された環境であれば、本記事の方式で比較的簡単に導入できる。

なお、本記事では、BOSH環境でのカオスエンジニアリングの導入・動作確認までを範囲としており、実サービスでの使用事例等は含みませんのでご了承ください。

前提環境

こちらで構築したBOSH環境で試験する。

TurbuleneをBOSHデプロイする

カオスエンジニアリングツールとしては、BOSH用に作られているTurbulenceを使用する。

github.com

TurbulenceのクライアントをUAAに登録する

事前準備としてBOSH DirectorのUAAにTurbulenceのクライアントを登録しておく。

$ vi uaac-login.sh
bosh int creds.yml --path /uaa_ssl/ca > uaa_ca_cert
uaac target https://$BOSH_ENVIRONMENT:8443 --ca-cert uaa_ca_cert
uaac token client get uaa_admin -s `bosh int creds.yml --path /uaa_admin_client_secret`

$ vi uaac-turbulence.sh
uaac client add turbulence \
  --name turbulence \
  --secret turbulence-secret \
  --authorized_grant_types client_credentials,refresh_token \
  --authorities  bosh.admin

$ ./uaac-login.sh
Target: https://192.168.1.222:8443
Context: uaa_admin, from client uaa_admin

Successfully fetched token via client credentials grant.
Target: https://192.168.1.222:8443
Context: uaa_admin, from client uaa_admin

$ ./uaac-turbulence.sh
  scope: uaa.none
  client_id: turbulence
  resource_ids: none
  authorized_grant_types: refresh_token client_credentials
  autoapprove:
  authorities: bosh.admin
  name: turbulence
  required_user_groups:
  lastmodified: 1545644889344
  id: turbulence

Turbulence APIサーバをデプロイする

git submodule add https://github.com/cppforlife/turbulence-release

Releaseファイルはここから取得する。
https://bosh.io/releases/github.com/cppforlife/turbulence-release?all=1

ops-files/turbulence-options.yml

- type: replace
  path: /releases/name=turbulence
  value:
    name: "turbulence"
    version: "0.10.0"
    url: "https://bosh.io/d/github.com/cppforlife/turbulence-release?v=0.10.0"
    sha1: "259344312796e23500b2836a15140f8f09ad99ee"

BOSH DirectorのCA Certが必要となるためファイルに書き出しておく。

bosh int ./creds.yml --path /director_ssl/ca > director_ca_cert

deploy-turbulence.sh

bosh deploy -d turbulence turbulence-release/manifests/example.yml \
  -o ops-files/turbulence-options.yml \
  -v turbulence_api_ip=10.244.0.101 \
  -v director_ip=$BOSH_ENVIRONMENT \
  --var-file director_ssl.ca=director_ca_cert \
  -v director_client=turbulence \
  -l turbulence_secret.yml

デプロイが完了したら、CredHubから認証情報を取得してWebGUIにアクセス確認する。

$ vi credhub-login.sh
bosh int ./creds.yml --path /credhub_ca/ca > credhub_ca_cert
credhub login -s $BOSH_ENVIRONMENT:8844 \
  --ca-cert credhub_ca_cert \
  --ca-cert uaa_ca_cert \
  --client-name credhub-admin \
  --client-secret `bosh int ./creds.yml --path /credhub_admin_client_secret`

$ ./credhub-login.sh
Setting the target url: https://192.168.1.222:8844
Login Successful

$ credhub get -n /bosh-lite/turbulence/turbulence_api_password
id: b954b174-f705-4b2b-96aa-89cf324122a3
name: /bosh-lite/turbulence/turbulence_api_password
type: password
value: x8LTqZBRzBFTlOtF9llfYg9bPIQ6Es
version_created_at: "2018-12-24T10:00:56Z"

https://<Turbulence IP>:8080Webブラウザでアクセスする。turbulence/xxx(credhub getの結果)でログインする。

まだIncidentを登録していないので何も表示されない。

Turbulence Agentを各VMにインストールする

TurbulenceはAgentを介してAPIサーバと各VMで処理をする。

BOSH VMへのAgentインストールは、共通的に適用できるようにRuntime Configを使用するとよい。

Director Runtime Config - Cloud Foundry BOSH

今回はサンプルとしてnginxのみに適用するが、include句の指定次第で環境全体にも適用できる。

$ vi ./turbulence-runtime.yml
---
releases:
- name: "turbulence"
  version: "0.10.0"
  url: "https://bosh.io/d/github.com/cppforlife/turbulence-release?v=0.10.0"
  sha1: "259344312796e23500b2836a15140f8f09ad99ee"

addons:
- name: turbulence_agent
  include:
    jobs:
    - name: nginx
      release: nginx
  jobs:
  - name: turbulence_agent
    release: turbulence
    consumes:
      api: {from: api, deployment: turbulence}
    properties:
      debug: false
$ vi ./deploy-turbulence-runtime.sh
bosh update-runtime-config turbulence-runtime.yml \
  --name=turbulence_agent \
  --no-redact
$ ./deploy-turbulence-runtime.sh
Using environment '192.168.1.222' as client 'admin'

+ releases:
+ - name: turbulence
+   sha1: 259344312796e23500b2836a15140f8f09ad99ee
+   url: https://bosh.io/d/github.com/cppforlife/turbulence-release?v=0.10.0
+   version: 0.10.0

+ addons:
+ - include:
+   - name: nginx
+     release: nginx
+   jobs:
+   - consumes:
+       api:
+         deployment: turbulence
+         from: api
+     name: turbulence_agent
+     properties:
+       debug: false
+     release: turbulence
+   name: turbulence_agent

Release 'turbulence/0.10.0' already exists.

Continue? [yN]: y

Succeeded

Runtime Configを設定したら、既存のDeploymentを再デプロイする。

$ ./deploy-nginx.sh
Using environment '192.168.1.222' as client 'admin'

Using deployment 'nginx'

  releases:
+ - name: turbulence
+   sha1: 259344312796e23500b2836a15140f8f09ad99ee
+   url: https://bosh.io/d/github.com/cppforlife/turbulence-release?v=0.10.0
+   version: 0.10.0

+ addons:
+ - include:
+     jobs:
+     - name: nginx
+       release: nginx
+   jobs:
+   - consumes:
+       api:
+         deployment: turbulence
+         from: api
+     name: turbulence_agent
+     properties:
+       debug: "<redacted>"
+     release: turbulence
+   name: turbulence_agent

Continue? [yN]: y

Task 31

Task 31 | 10:32:26 | Preparing deployment: Preparing deployment (00:00:02)
Task 31 | 10:32:29 | Preparing package compilation: Finding packages to compile (00:00:00)
Task 31 | 10:32:29 | Compiling packages: stress/6b00034151fd5be78893a537bd38818ad2a36bef (00:00:19)
Task 31 | 10:32:49 | Updating instance nginx: nginx/811fb685-b437-4994-b355-b36d7a58313d (0) (canary) (00:00:26)
Task 31 | 10:33:15 | Updating instance nginx: nginx/4de7506b-7a80-407e-9154-91dc98388385 (2) (00:00:26)
Task 31 | 10:33:41 | Updating instance nginx: nginx/d0684cd6-93f6-4fc0-b03c-d5380fdd87d2 (1) (00:00:25)

Task 31 Started  Mon Dec 24 10:32:26 UTC 2018
Task 31 Finished Mon Dec 24 10:34:06 UTC 2018
Task 31 Duration 00:01:40
Task 31 done

Succeeded

Processが追加された。

$ bosh -d nginx instances --ps
Using environment '192.168.1.222' as client 'admin'

Task 32. Done

Deployment 'nginx'

Instance                                    Process           Process State  AZ  IPs
nginx/4de7506b-7a80-407e-9154-91dc98388385  -                 running        z1  10.244.0.3
~                                           nginx             running        -   -
~                                           turbulence_agent  running        -   -
nginx/811fb685-b437-4994-b355-b36d7a58313d  -                 running        z1  10.244.0.2
~                                           nginx             running        -   -
~                                           turbulence_agent  running        -   -
nginx/d0684cd6-93f6-4fc0-b03c-d5380fdd87d2  -                 running        z1  10.244.0.4
~                                           nginx             running        -   -
~                                           turbulence_agent  running        -   -

3 instances

Succeeded

Taskを登録する

$ cat turbulence-release/docs/kill-scheduled.sh > turbulence-tasks/scheduled-kill-nginx.sh

nginxを2分毎に1〜2台殺してもらう。(※Limitの動作が期待通りにならない気がする...)

$ cat turbulence-tasks/scheduled-kill-nginx.sh
#!/bin/bash

body='
{
    "Schedule": "@every 2m",

    "Incident": {
        "Tasks": [{
            "Type": "Kill"
        }],

        "Selector": {
            "Deployment": {
                "Name": "nginx"
            },
            "Group": {
                "Name": "nginx"
            },
            "ID": {
                "Limit": "1-2"
            }
        }
    }
}
'

echo $body | curl -vvv -k -X POST https://turbulence:x8LTqZBRzBFTlOtF9llfYg9bPIQ6Es@10.244.0.101:8080/api/v1/scheduled_incidents -H 'Accept: application/json' -d @-

echo
$ ./turbulence-tasks/scheduled-kill-nginx.sh

所定の時間が経過すると、Taskが実行される。

各Incidentを開くと、どのインスタンスに対して処理を実行したかが確認できる。

ちなみに、原因は調査していないが、以下のように00sを付けた書き方だとうまく動かなかった。

body='
{
    "Schedule": "@every 2m 00s",

サービス継続確認

最低限のcurlでのアクセス確認をする。DNSラウンドロビンでアクセスしているが、OSの機能で障害VMを避けてアクセスできている。

20:02:31〜20:03:48あたりで.4がダウンしていることが分かる。BOSH Releaseに問題がなければ、障害VMはBOSHのHealth Monitorにより自動的に復旧される。

$ while true; do sleep 5; date "+%H:%M:%S"|tr '\n ' ' '; curl http://nginx.bosh.local -m 1; done
20:01:51     10.244.0.2
20:01:56     10.244.0.3
20:02:01     10.244.0.4
20:02:06     10.244.0.2
20:02:11     10.244.0.3
20:02:16     10.244.0.4
20:02:21     10.244.0.2
20:02:26     10.244.0.3
20:02:31     10.244.0.2
20:02:37     10.244.0.2
20:02:42     10.244.0.3
20:02:47     10.244.0.2
20:02:52     10.244.0.2
20:02:57     10.244.0.3
20:03:02     10.244.0.2
20:03:08     10.244.0.2
20:03:13     10.244.0.3
20:03:18     10.244.0.2
20:03:23     10.244.0.2
20:03:28     10.244.0.3
20:03:33     10.244.0.2
20:03:38     10.244.0.2
20:03:43     10.244.0.3
20:03:48     10.244.0.4
20:03:53     10.244.0.2
20:03:58     10.244.0.3
20:04:03     10.244.0.4
...

設定可能Incident

設定可能なIncidentはこちらに記載されている通り。

turbulence-release/api.md at master · cppforlife/turbulence-release · GitHub

Killbosh delete-vm VMCIDで、実際のVM障害とは異なったりするので、要確認。

運用スクリプト

Indicentを取得する。

$ cat turbulence-tasks/get_incidents.sh
curl -k https://turbulence:x8LTqZBRzBFTlOtF9llfYg9bPIQ6Es@10.244.0.101:8080/api/v1/scheduled_incidents

IDを引数としてDeleteする。

$ cat turbulence-tasks/delete-incidents.sh
#!/bin/sh

if [ $# -ne 1 ]; then
  exit 1
fi

curl -k -XDELETE https://turbulence:x8LTqZBRzBFTlOtF9llfYg9bPIQ6Es@10.244.0.101:8080/api/v1/scheduled_incidents/$1

まとめ - TurbulenceでBOSH環境にカオスエンジニアリングを導入する

Turbulenceにより、BOSH環境でランダムにインスタンスに障害を発生させるカオスエンジニアリングの導入を検証した。

本番リリース前の障害試験として実施することにより、想定外の障害動作を摘出することが期待できる。さらには本番環境に適用し、自動回復機能の常時試験も可能となる。