Kong Gatewayのプラグインを利用すれば、カナリアリリースが簡単に設定することができます。しかもカナリアリリースのモードは単なるパーセンテージではなく、徐々に利用拡大や、Writelist&blacklistの設定もできます。
この記事では、カナリアリリースのやり方についてメモします。
https://docs.konghq.com/hub/kong-inc/canary/
ServiceとRouteの作成#
デモの例として、現在のバージョンをhttp://httpbin.org/xmlに、新規リリースのバージョンをhttp://httpbin.org/jsonにします。なので、現在のバージョンの場合は、xmlのレスポンス、新規リリースのバージョンの場合は、jsonのレスポンスが帰ってくるはず。
1
2
3
4
5
6
7
| ❯ http POST localhost:8001/services \
name=canary-api-service \
url=http://httpbin.org/xml
❯ http -f POST localhost:8001/services/canary-api-service/routes \
name=canary-api-route \
paths=/api/canary
|
動作確認、xmlのレスポンスが帰ってきた。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| ❯ http GET localhost:8000/api/canary
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 522
Content-Type: application/xml
Date: Wed, 25 May 2022 05:28:07 GMT
Server: gunicorn/19.9.0
Via: kong/2.8.1.0-enterprise-edition
X-Kong-Proxy-Latency: 0
X-Kong-Upstream-Latency: 295
<?xml version='1.0' encoding='us-ascii'?>
<!-- A SAMPLE set of slides -->
<slideshow
title="Sample Slide Show"
date="Date of publication"
author="Yours Truly"
>
<!-- TITLE SLIDE -->
<slide type="all">
<title>Wake up to WonderWidgets!</title>
</slide>
<!-- OVERVIEW -->
<slide type="all">
<title>Overview</title>
<item>Why <em>WonderWidgets</em> are great</item>
<item/>
<item>Who <em>buys</em> WonderWidgets</item>
</slide>
</slideshow>
|
カナリアリリースモード#
一定期間(Set a Period)#
翻訳が合っているかどうかが分からないが、リリースの開始時刻と移行期間を決めるモードです。リリース期間の初期段階はほとんど現バージョンで、そして徐々に新バージョンの出現確率が高くなり、最終的に新バージョンのみになるモードです。
以下のコマンドの設定から、リリースの開始が10
秒後、移行期間が120
秒となります。
1
2
3
4
5
6
7
8
9
|
❯ current_time=`expr $(date "+%s") + 10` && http -f POST http://localhost:8001/routes/canary-api-route/plugins \
name=canary \
config.start=$current_time \
config.duration=120 \
config.upstream_host=httpbin.org \
config.upstream_port=80 \
config.upstream_uri=/json \
config.hash=none
|
この設定で以下のコマンドを実行すると、最初はxmlのレスポンスになるが、徐々にJsonの方が増えていて、最終的に全部Jsonのレスポンスになります。
1
2
3
4
5
| ❯ for num in {1..70}; do
echo "Calling API #$num"
http http://localhost:8000/api/canary
sleep 0.5
done
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| ❯ http GET localhost:8000/api/canary
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 429
Content-Type: application/json
Date: Wed, 25 May 2022 07:27:08 GMT
Server: gunicorn/19.9.0
Via: kong/2.8.1.0-enterprise-edition
X-Kong-Proxy-Latency: 4
X-Kong-Upstream-Latency: 284
{
"slideshow": {
"author": "Yours Truly",
"date": "date of publication",
"slides": [
{
"title": "Wake up to WonderWidgets!",
"type": "all"
},
{
"items": [
"Why <em>WonderWidgets</em> are great",
"Who <em>buys</em> WonderWidgets"
],
"title": "Overview",
"type": "all"
}
],
"title": "Sample Slide Show"
}
}
|
これで動作確認が終わったので、次のリリース方式をテストするために、カナリアリリースプラグインを削除する
1
| ❯ plugin_id=$(http -f http://localhost:8001/routes/canary-api-route/plugins | jq -r '.data[].id') && http DELETE http://localhost:8001/plugins/$plugin_id
|
一定割合(Set a Percentage)#
トラフィックが現バージョンと新バージョンにアクセスする確率を一定にするモードです。設定した割合はパーセンテージであり、新バージョンにアクセスする割合となります。
以下のコマンドの設定、config.percentageから、現バージョンと新バージョンの割合が5割5割となっています。
1
2
3
4
5
6
7
| ❯ http -f POST http://localhost:8001/routes/canary-api-route/plugins \
name=canary \
config.percentage=50 \
config.upstream_host=httpbin.org \
config.upstream_port=80 \
config.upstream_uri=/json \
config.hash=none
|
以下のコマンドで確認すると、大体ですけど半分ずつとなっているはずです。
1
2
3
4
5
| ❯ for num in {1..10}; do
echo "Calling API #$num"
http http://localhost:8000/api/canary
sleep 0.5
done
|
もしパーセンテージのテストを段階的に行いたい場合、以下のコマンドで割合を変更することもできます。
1
2
3
4
5
6
7
8
| $ plugin_id=$(http -f http://localhost:8001/routes/canary-api-route/plugins | jq -r '.data[].id')
$ http -f PUT http://localhost:8001/routes/canary-api-route/plugins/$plugin_id \
name=canary \
config.percentage=90 \
config.upstream_host=httpbin.org \
config.upstream_port=80 \
config.upstream_uri=/json \
config.hash=none
|
これで動作確認が終わったので、次のリリース方式をテストするために、カナリアリリースプラグインを削除する
1
| ❯ plugin_id=$(http -f http://localhost:8001/routes/canary-api-route/plugins | jq -r '.data[].id') && http DELETE http://localhost:8001/plugins/$plugin_id
|
Whitelist and Blacklist#
こちらのモードでは、リクエストに含まれたAPI Key
でユーザConsumer
を特定し、Consumer
が所属するグループに基づいてどのバージョンにアクセスしにいくのかを設定できます。
Consumer
の特定とグループの登録は、key-auth
とacl
のプラグインを利用します。
まずはkey-auth
のプラグインを登録し、これでAPI key
なしだとアクセスができなくなります。
1
| ❯ http http://localhost:8001/routes/canary-api-route/plugins name=key-auth
|
次に、今回は2種類のConsumer
を準備し、それぞれ別のグループに登録します。
1
2
3
| ❯ http :8001/consumers username=vip-consumer && http :8001/consumers/vip-consumer/key-auth key=vip-api && http :8001/consumers/vip-consumer/acls group=vip-acl
❯ http :8001/consumers username=general-consumer && http :8001/consumers/general-consumer/key-auth key=general-api && http :8001/consumers/general-consumer/acls group=general-acl
|
上の設定が終わったら、グループ情報を用いてカナリアリリースプラグインを作成します。
ここで注意してほしいのは、config.hash
です。whitelist
かblacklist
を設定する必要がありまして、whitelist
に設定したグループは新バージョンにアクセスしにいきます。
1
2
3
4
5
6
7
| ❯ http -f POST http://localhost:8001/routes/canary-api-route/plugins \
name=canary \
config.hash=whitelist \
config.groups=vip-acl \
config.upstream_host=httpbin.org \
config.upstream_port=80 \
config.upstream_uri=/json
|
vip-api
を持ってアクセスするクライアント、つまりConsumer
のグループはvip-acl
であり、whitelist
に登録されているので新バージョンにアクセスしにいきます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| ❯ http http://localhost:8000/api/canary apiKey:vip-api
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 429
Content-Type: application/json
Date: Wed, 25 May 2022 07:33:45 GMT
Server: gunicorn/19.9.0
Via: kong/2.8.1.0-enterprise-edition
X-Kong-Proxy-Latency: 118
X-Kong-Upstream-Latency: 290
{
"slideshow": {
"author": "Yours Truly",
"date": "date of publication",
"slides": [
{
"title": "Wake up to WonderWidgets!",
"type": "all"
},
{
"items": [
"Why <em>WonderWidgets</em> are great",
"Who <em>buys</em> WonderWidgets"
],
"title": "Overview",
"type": "all"
}
],
"title": "Sample Slide Show"
}
}
|
一方、general-api
を持ってアクセスするクライアント、つまりConsumer
のグループはgeneral-acl
のため、現バージョンにアクセスしにいきます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
| ❯ http http://localhost:8000/api/canary apiKey:general-api
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 522
Content-Type: application/xml
Date: Wed, 25 May 2022 07:33:55 GMT
Server: gunicorn/19.9.0
Via: kong/2.8.1.0-enterprise-edition
X-Kong-Proxy-Latency: 5
X-Kong-Upstream-Latency: 289
<?xml version='1.0' encoding='us-ascii'?>
<!-- A SAMPLE set of slides -->
<slideshow
title="Sample Slide Show"
date="Date of publication"
author="Yours Truly"
>
<!-- TITLE SLIDE -->
<slide type="all">
<title>Wake up to WonderWidgets!</title>
</slide>
<!-- OVERVIEW -->
<slide type="all">
<title>Overview</title>
<item>Why <em>WonderWidgets</em> are great</item>
<item/>
<item>Who <em>buys</em> WonderWidgets</item>
</slide>
</slideshow>
|
アップグレードを完了させる#
カナリアリリースのテストが終わったら、特に問題がない場合、全てのトラフィックをプラグインなしで新バージョンにrouteしていきたい。
そこで実行すべきのは、serviceのUpstream Endpointを更新し、カナリアリリースプラグインを削除することです。
1
2
3
4
5
6
| # update service
❯ http -f PUT :8001/services/canary-api-service url=http://httpbin.org/json
# delete all plugins
❯ http http://localhost:8001/routes/canary-api-route/plugins | jq -r -c '.data[].id' | while read id; do
http --ignore-stdin DELETE http://localhost:8001/plugins/$id
done
|
これでカナリアリリースで新バージョンのサービスのアップグレードが完了した!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| ❯ http http://localhost:8000/api/canary
HTTP/1.1 200 OK
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 429
Content-Type: application/json
Date: Wed, 25 May 2022 07:35:55 GMT
Server: gunicorn/19.9.0
Via: kong/2.8.1.0-enterprise-edition
X-Kong-Proxy-Latency: 4
X-Kong-Upstream-Latency: 288
{
"slideshow": {
"author": "Yours Truly",
"date": "date of publication",
"slides": [
{
"title": "Wake up to WonderWidgets!",
"type": "all"
},
{
"items": [
"Why <em>WonderWidgets</em> are great",
"Who <em>buys</em> WonderWidgets"
],
"title": "Overview",
"type": "all"
}
],
"title": "Sample Slide Show"
}
}
|