ECS Fargate ワークロードを Sysdig Serverless Agent で保護する (3)

Amazon Elastic Container Service (ECS) の実行環境に AWS Fargate を用いた構成(以下、ECS Fargate )は、AWS でコンテナの実行環境を整える際の強力な選択肢のひとつです。
Fargate を用いることで、ユーザが考慮すべきセキュリティリスクは( EC2 を用いるよりも)少なくなりますが、すべてがカバーされるものではありません。

この連載記事では、ECS Fargate で実行されるワークロードを Sysdig の Serverless Agent を使って保護するための方法をご紹介します。
第 3 回となる今回は、Serverless Agent がアプリケーションに与える性能影響に触れます。

  1. Sysdig と Serverless Agent の概要
  2. Serverless Agent のセットアップとイベントキャプチャ
  3. Serverless Agent がアプリケーションに与える性能影響 (この記事)

Serverless Agent 利用時の性能への影響

前々回 ご紹介したように、Serverless Agent は通常 Sysdig Agent が行っているようなカーネルインストゥルメンテーションが適用できないため、ptrace を使ってワークロードのシステムコールを検出しています。
以下では、Serverless Agent の導入によってアプリケーションの性能にどの程度の影響が及ぶのかを見てゆきます。

公開されているベンチマーク結果

まずは Sysdig から公開されているベンチマークの結果をご紹介します。
以下は、Sysdig のブログに掲載されている、Fargate で I / O 集約型のワークロード ( 100k IOPS以上 ) を実行した際の所要時間の計測結果です。

f:id:polar3130:20220324144205p:plain 引用元

このケースでは、通常の Sysdig Agent が行っているカーネルインストゥルメンテーションにやや劣る程度の性能が出ており、単純な ptrace の実行時よりもかなり良好な結果が得られているようです。

また、pdig のリポジトリでは OSBench というベンチマークツールを使ったシナリオ毎のベンチマーク結果も紹介されています。1
ここでは、システムコールが多量に発生するワークロードにおいて、元の性能と比して 1 / 4 程度まで性能劣化したケースがあったと紹介されています。

f:id:polar3130:20220324144445p:plain 引用元

アプリケーションの稼働中に ptrace を実行し続けると聞くとかなりの性能低下が起きてもおかしくないように思えますが、pdig では seccomp フィルタを用いてインストゥルメンテーションを行うシステムコールの種類を限定することにより、大幅な性能劣化が起きないよう工夫しているようです。

UNIX Benchmarks を使った実測

自身の環境でもベンチマークを使って Serverless Agent の影響を計測してみます。
今回は計測のために以下の環境を用意しました。

Sysdig

  • Orchestrator Agent : v2.3.0 ( latest )
  • Workload Agent : v2.3.0 ( latest )
  • ランタイムポリシ : デフォルトで定義済みのポリシのみを利用(キャプチャなし)

ECS Fargate

  • プラットフォームバージョン : 1.4
  • タスク定義 : CPU - 0.25 core, Memory - 512 MiB
  • コンテナイメージ : debian:11.2

ベンチマークツール

今回は、アプリケーションの正常稼働時をイメージして Serverless Agent の有無による性能影響を比較したいと考えたため、ポリシに基づくキャプチャは設定していません。
ベンチマークの実行中にキャプチャの取得も行うと、更に性能への影響が大きくなるのではと推測していますが、ポリシに基づいて構成されたキャプチャが実行されるのは、本来想定されていない(例えばアプリケーションの脆弱性が悪用されるなどした際の)異常な振る舞いが検出されている状況に相当すると考えられます。

UNIX Bench の実行結果は以下のとおりです。

UNIX Bench シナリオ別 Index 値の比較(1 コア使用)

f:id:polar3130:20220324143723p:plain

コンソールの出力も残しておきます。
マルチコア環境のため、1 コアのみを使用した場合とすべてのコアを使用した場合の 2 通りの結果が得られています。
Serverless Agent なしの結果は、単純な ECS Fargate タスク ( CPU 0.25 core, Memory 512 MiB ) のベンチマーク結果としてもみることができますね。

Serverless Agent なし

root@ip-10-0-2-155:/byte-unixbench/UnixBench# ./Run
gcc -o pgms/arithoh -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Darithoh src/arith.c 
gcc -o pgms/register -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum='register int' src/arith.c 
gcc -o pgms/short -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=short src/arith.c 
gcc -o pgms/int -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=int src/arith.c 
gcc -o pgms/long -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=long src/arith.c 
gcc -o pgms/float -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=float src/arith.c 
gcc -o pgms/double -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=double src/arith.c 
gcc -o pgms/hanoi -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/hanoi.c 
gcc -o pgms/syscall -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/syscall.c 
gcc -o pgms/context1 -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/context1.c 
gcc -o pgms/pipe -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/pipe.c 
gcc -o pgms/spawn -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/spawn.c 
gcc -o pgms/execl -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/execl.c 
gcc -o pgms/dhry2 -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DHZ= ./src/dhry_1.c ./src/dhry_2.c
gcc -o pgms/dhry2reg -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DHZ= -DREG=register ./src/dhry_1.c ./src/dhry_2.c
gcc -o pgms/looper -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/looper.c 
gcc -o pgms/fstime -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/fstime.c 
gcc -o pgms/whetstone-double -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DDP -DGTODay -DUNIXBENCH src/whets.c -lm
make all
make[1]: Entering directory '/byte-unixbench/UnixBench'
make distr
make[2]: Entering directory '/byte-unixbench/UnixBench'
Checking distribution of files
./pgms  exists
./src  exists
./testdir  exists
./tmp  exists
./results  exists
make[2]: Leaving directory '/byte-unixbench/UnixBench'
make programs
make[2]: Entering directory '/byte-unixbench/UnixBench'
make[2]: Nothing to be done for 'programs'.
make[2]: Leaving directory '/byte-unixbench/UnixBench'
make[1]: Leaving directory '/byte-unixbench/UnixBench'
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
sh: 1: 3dinfo: not found

   #    #  #    #  #  #    #          #####   ######  #    #   ####   #    #
   #    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #
   #    #  # #  #  #    ##            #####   #####   # #  #  #       ######
   #    #  #  # #  #    ##            #    #  #       #  # #  #       #    #
   #    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #
    ####   #    #  #  #    #          #####   ######  #    #   ####   #    #

   Version 5.1.3                      Based on the Byte Magazine Unix Benchmark

   Multi-CPU version                  Version 5 revisions by Ian Smith,
                                      Sunnyvale, CA, USA
   January 13, 2011                   johantheghost at yahoo period com

------------------------------------------------------------------------------
   Use directories for:
      * File I/O tests (named fs***) = /byte-unixbench/UnixBench/tmp
      * Results                      = /byte-unixbench/UnixBench/results
------------------------------------------------------------------------------

Use of uninitialized value in printf at ./Run line 1479.
Use of uninitialized value in printf at ./Run line 1739.

1 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

1 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

1 x Execl Throughput  1 2 3

1 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

1 x File Copy 256 bufsize 500 maxblocks  1 2 3

1 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

1 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

1 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

1 x Process Creation  1 2 3

1 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

1 x Shell Scripts (1 concurrent)  1 2 3

1 x Shell Scripts (8 concurrent)  1 2 3

2 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

2 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

2 x Execl Throughput  1 2 3

2 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

2 x File Copy 256 bufsize 500 maxblocks  1 2 3

2 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

2 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

2 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

2 x Process Creation  1 2 3

2 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

2 x Shell Scripts (1 concurrent)  1 2 3

2 x Shell Scripts (8 concurrent)  1 2 3

========================================================================
   BYTE UNIX Benchmarks (Version 5.1.3)

   System: ip-10-0-2-155.ec2.internal: GNU/Linux
   OS: GNU/Linux -- 4.14.262-200.489.amzn2.x86_64 -- #1 SMP Fri Feb 4 20:34:30 UTC 2022
   Machine: x86_64 (unknown)
   Language: en_US.utf8 (charmap="ANSI_X3.4-1968", collate="ANSI_X3.4-1968")
   CPU 0: Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz (5000.0 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET
   CPU 1: Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz (5000.0 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET
   ; runlevel 

------------------------------------------------------------------------
Benchmark Run: Tue Mar 22 2022 11:36:09 - 12:04:11
2 CPUs in system; running 1 parallel copy of tests

Dhrystone 2 using register variables        9969676.8 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                     1606.8 MWIPS (9.8 s, 7 samples)
Execl Throughput                                999.1 lps   (30.0 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        111280.7 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks           30110.6 KBps  (30.1 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks        324679.7 KBps  (30.0 s, 2 samples)
Pipe Throughput                              166992.3 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                   8030.5 lps   (10.0 s, 7 samples)
Process Creation                               1990.5 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                   1834.1 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                    213.3 lpm   (60.2 s, 2 samples)
System Call Overhead                          90003.2 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0    9969676.8    854.3
Double-Precision Whetstone                       55.0       1606.8    292.1
Execl Throughput                                 43.0        999.1    232.4
File Copy 1024 bufsize 2000 maxblocks          3960.0     111280.7    281.0
File Copy 256 bufsize 500 maxblocks            1655.0      30110.6    181.9
File Copy 4096 bufsize 8000 maxblocks          5800.0     324679.7    559.8
Pipe Throughput                               12440.0     166992.3    134.2
Pipe-based Context Switching                   4000.0       8030.5     20.1
Process Creation                                126.0       1990.5    158.0
Shell Scripts (1 concurrent)                     42.4       1834.1    432.6
Shell Scripts (8 concurrent)                      6.0        213.3    355.6
System Call Overhead                          15000.0      90003.2     60.0
                                                                   ========
System Benchmarks Index Score                                         207.9

------------------------------------------------------------------------
Benchmark Run: Tue Mar 22 2022 12:04:11 - 12:32:30
2 CPUs in system; running 2 parallel copies of tests

Dhrystone 2 using register variables        7625041.1 lps   (10.1 s, 7 samples)
Double-Precision Whetstone                     1508.2 MWIPS (9.6 s, 7 samples)
Execl Throughput                                730.5 lps   (29.9 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks         79684.4 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks           21435.4 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks        234122.1 KBps  (30.0 s, 2 samples)
Pipe Throughput                              120705.5 lps   (10.1 s, 7 samples)
Pipe-based Context Switching                  22937.9 lps   (10.1 s, 7 samples)
Process Creation                               1755.0 lps   (30.1 s, 2 samples)
Shell Scripts (1 concurrent)                   1675.1 lpm   (60.1 s, 2 samples)
Shell Scripts (8 concurrent)                    206.6 lpm   (60.4 s, 2 samples)
System Call Overhead                          62776.6 lps   (10.1 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0    7625041.1    653.4
Double-Precision Whetstone                       55.0       1508.2    274.2
Execl Throughput                                 43.0        730.5    169.9
File Copy 1024 bufsize 2000 maxblocks          3960.0      79684.4    201.2
File Copy 256 bufsize 500 maxblocks            1655.0      21435.4    129.5
File Copy 4096 bufsize 8000 maxblocks          5800.0     234122.1    403.7
Pipe Throughput                               12440.0     120705.5     97.0
Pipe-based Context Switching                   4000.0      22937.9     57.3
Process Creation                                126.0       1755.0    139.3
Shell Scripts (1 concurrent)                     42.4       1675.1    395.1
Shell Scripts (8 concurrent)                      6.0        206.6    344.3
System Call Overhead                          15000.0      62776.6     41.9
                                                                   ========
System Benchmarks Index Score                                         183.0

Serverless Agent あり

root@ip-10-0-1-237:/byte-unixbench/UnixBench# /opt/draios/bin/pdig -t 0 ./Run
gcc -o pgms/arithoh -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Darithoh src/arith.c 
gcc -o pgms/register -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum='register int' src/arith.c 
gcc -o pgms/short -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=short src/arith.c 
gcc -o pgms/int -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=int src/arith.c 
gcc -o pgms/long -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=long src/arith.c 
gcc -o pgms/float -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=float src/arith.c 
gcc -o pgms/double -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=double src/arith.c 
gcc -o pgms/hanoi -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/hanoi.c 
gcc -o pgms/syscall -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/syscall.c 
gcc -o pgms/context1 -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/context1.c 
gcc -o pgms/pipe -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/pipe.c 
gcc -o pgms/spawn -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/spawn.c 
gcc -o pgms/execl -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/execl.c 
gcc -o pgms/dhry2 -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DHZ= ./src/dhry_1.c ./src/dhry_2.c
gcc -o pgms/dhry2reg -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DHZ= -DREG=register ./src/dhry_1.c ./src/dhry_2.c
gcc -o pgms/looper -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/looper.c 
gcc -o pgms/fstime -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/fstime.c 
gcc -o pgms/whetstone-double -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DDP -DGTODay -DUNIXBENCH src/whets.c -lm
make all
make[1]: Entering directory '/byte-unixbench/UnixBench'
make distr
make[2]: Entering directory '/byte-unixbench/UnixBench'
Checking distribution of files
./pgms  exists
./src  exists
./testdir  exists
./tmp  exists
./results  exists
make[2]: Leaving directory '/byte-unixbench/UnixBench'
make programs
make[2]: Entering directory '/byte-unixbench/UnixBench'
make[2]: Nothing to be done for 'programs'.
make[2]: Leaving directory '/byte-unixbench/UnixBench'
make[1]: Leaving directory '/byte-unixbench/UnixBench'
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
sh: 1: 3dinfo: not found

   #    #  #    #  #  #    #          #####   ######  #    #   ####   #    #
   #    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #
   #    #  # #  #  #    ##            #####   #####   # #  #  #       ######
   #    #  #  # #  #    ##            #    #  #       #  # #  #       #    #
   #    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #
    ####   #    #  #  #    #          #####   ######  #    #   ####   #    #

   Version 5.1.3                      Based on the Byte Magazine Unix Benchmark

   Multi-CPU version                  Version 5 revisions by Ian Smith,
                                      Sunnyvale, CA, USA
   January 13, 2011                   johantheghost at yahoo period com

------------------------------------------------------------------------------
   Use directories for:
      * File I/O tests (named fs***) = /byte-unixbench/UnixBench/tmp
      * Results                      = /byte-unixbench/UnixBench/results
------------------------------------------------------------------------------


1 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

1 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

1 x Execl Throughput  1 2 3

1 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

1 x File Copy 256 bufsize 500 maxblocks  1 2 3

1 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

1 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

1 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

1 x Process Creation  1 2 3

1 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

1 x Shell Scripts (1 concurrent)  1 2 3

1 x Shell Scripts (8 concurrent)  1 2 3

2 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

2 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

2 x Execl Throughput  1 2 3

2 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

2 x File Copy 256 bufsize 500 maxblocks  1 2 3

2 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

2 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

2 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

2 x Process Creation  1 2 3

2 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

2 x Shell Scripts (1 concurrent)  1 2 3

2 x Shell Scripts (8 concurrent)  1 2 3

========================================================================
   BYTE UNIX Benchmarks (Version 5.1.3)

   System: ip-10-0-1-237.ec2.internal: GNU/Linux
   OS: GNU/Linux -- 4.14.262-200.489.amzn2.x86_64 -- #1 SMP Fri Feb 4 20:34:30 UTC 2022
   Machine: x86_64 (unknown)
   Language: en_US.utf8 (charmap="ANSI_X3.4-1968", collate="ANSI_X3.4-1968")
   CPU 0: Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz (5000.0 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET
   CPU 1: Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz (5000.0 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET
   12:03:52 up 40 min,  0 users,  load average: 0.03, 0.05, 0.06; runlevel 

------------------------------------------------------------------------
Benchmark Run: Tue Mar 22 2022 12:03:52 - 12:31:58
2 CPUs in system; running 1 parallel copy of tests

Dhrystone 2 using register variables        9977280.2 lps   (10.1 s, 7 samples)
Double-Precision Whetstone                     1611.5 MWIPS (9.6 s, 7 samples)
Execl Throughput                                167.3 lps   (30.0 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        105244.6 KBps  (30.1 s, 2 samples)
File Copy 256 bufsize 500 maxblocks           29112.7 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks        309992.6 KBps  (30.0 s, 2 samples)
Pipe Throughput                              156500.2 lps   (10.1 s, 7 samples)
Pipe-based Context Switching                   8305.2 lps   (10.1 s, 7 samples)
Process Creation                                546.5 lps   (30.1 s, 2 samples)
Shell Scripts (1 concurrent)                    426.9 lpm   (60.1 s, 2 samples)
Shell Scripts (8 concurrent)                     61.4 lpm   (60.6 s, 2 samples)
System Call Overhead                           2077.6 lps   (10.1 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0    9977280.2    855.0
Double-Precision Whetstone                       55.0       1611.5    293.0
Execl Throughput                                 43.0        167.3     38.9
File Copy 1024 bufsize 2000 maxblocks          3960.0     105244.6    265.8
File Copy 256 bufsize 500 maxblocks            1655.0      29112.7    175.9
File Copy 4096 bufsize 8000 maxblocks          5800.0     309992.6    534.5
Pipe Throughput                               12440.0     156500.2    125.8
Pipe-based Context Switching                   4000.0       8305.2     20.8
Process Creation                                126.0        546.5     43.4
Shell Scripts (1 concurrent)                     42.4        426.9    100.7
Shell Scripts (8 concurrent)                      6.0         61.4    102.3
System Call Overhead                          15000.0       2077.6      1.4
                                                                   ========
System Benchmarks Index Score                                          92.5

------------------------------------------------------------------------
Benchmark Run: Tue Mar 22 2022 12:31:58 - 13:00:25
2 CPUs in system; running 2 parallel copies of tests

Dhrystone 2 using register variables        7069816.8 lps   (10.1 s, 7 samples)
Double-Precision Whetstone                     1484.8 MWIPS (9.7 s, 7 samples)
Execl Throughput                                194.1 lps   (29.9 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks         76349.2 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks           20649.5 KBps  (30.1 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks        223618.9 KBps  (30.0 s, 2 samples)
Pipe Throughput                              114207.0 lps   (10.1 s, 7 samples)
Pipe-based Context Switching                  22455.3 lps   (10.1 s, 7 samples)
Process Creation                                531.1 lps   (30.1 s, 2 samples)
Shell Scripts (1 concurrent)                    454.6 lpm   (60.2 s, 2 samples)
Shell Scripts (8 concurrent)                     60.7 lpm   (61.3 s, 2 samples)
System Call Overhead                           3124.8 lps   (10.1 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0    7069816.8    605.8
Double-Precision Whetstone                       55.0       1484.8    270.0
Execl Throughput                                 43.0        194.1     45.1
File Copy 1024 bufsize 2000 maxblocks          3960.0      76349.2    192.8
File Copy 256 bufsize 500 maxblocks            1655.0      20649.5    124.8
File Copy 4096 bufsize 8000 maxblocks          5800.0     223618.9    385.5
Pipe Throughput                               12440.0     114207.0     91.8
Pipe-based Context Switching                   4000.0      22455.3     56.1
Process Creation                                126.0        531.1     42.1
Shell Scripts (1 concurrent)                     42.4        454.6    107.2
Shell Scripts (8 concurrent)                      6.0         60.7    101.2
System Call Overhead                          15000.0       3124.8      2.1
                                                                   ========
System Benchmarks Index Score                                          91.3

多くのシナリオで、Serverless Agent をインストールした場合には性能が劣化する結果となりました。
System Benchmarks Index Score は以下のとおりでした。

  • Serverless Agent なしの場合 : 207.9
  • Serverless Agent ありの場合 : 92.5

50 %程度スコアが落ち込んでいることがわかります。
シナリオによってはスコアに差がないものもあり、これらは別のボトルネックにぶつかっているか、インストゥルメンテーションの影響を受けにくい状況になっていると考えられます。2

Sysdig Agent との比較

Sysdig のドキュメントやブログによると、Serverless Agent と比べ、通常の Sysdig Agent はカーネルインストゥルメンテーションが行えるため性能への影響を与えにくいとされています。
影響の度合いを比較するため、EC2 に Sysdig Agent を入れたときの UNIX Bench のスコアも計測しました。

EC2 インスタンスは以下の設定のものを用意しました。

実行結果は以下のとおりです。
前述の Fargate タスクの検証時よりも CPU, メモリ が多く割り当てられている環境なので、ベースラインが異なる点にご注意ください。

UNIX Bench シナリオ別 Index 値の比較(1 コア使用)

f:id:polar3130:20220324143727p:plain

コンソールの出力も残しておきます。
Sysdig Agent なしの結果は、単純な EC2 (t2.micro) のベンチマーク結果としてもみることができますね。

Sysdig Agent なし

root@ip-10-0-101-148:~/byte-unixbench/UnixBench# ./Run
gcc -o pgms/arithoh -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Darithoh src/arith.c 
gcc -o pgms/register -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum='register int' src/arith.c 
gcc -o pgms/short -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=short src/arith.c 
gcc -o pgms/int -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=int src/arith.c 
gcc -o pgms/long -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=long src/arith.c 
gcc -o pgms/float -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=float src/arith.c 
gcc -o pgms/double -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=double src/arith.c 
gcc -o pgms/hanoi -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/hanoi.c 
gcc -o pgms/syscall -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/syscall.c 
gcc -o pgms/context1 -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/context1.c 
gcc -o pgms/pipe -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/pipe.c 
gcc -o pgms/spawn -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/spawn.c 
gcc -o pgms/execl -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/execl.c 
gcc -o pgms/dhry2 -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DHZ= ./src/dhry_1.c ./src/dhry_2.c
gcc -o pgms/dhry2reg -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DHZ= -DREG=register ./src/dhry_1.c ./src/dhry_2.c
gcc -o pgms/looper -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/looper.c 
gcc -o pgms/fstime -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/fstime.c 
gcc -o pgms/whetstone-double -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DDP -DGTODay -DUNIXBENCH src/whets.c -lm
make all
make[1]: Entering directory '/root/byte-unixbench/UnixBench'
make distr
make[2]: Entering directory '/root/byte-unixbench/UnixBench'
Checking distribution of files
./pgms  exists
./src  exists
./testdir  exists
./tmp  exists
./results  exists
make[2]: Leaving directory '/root/byte-unixbench/UnixBench'
make programs
make[2]: Entering directory '/root/byte-unixbench/UnixBench'
make[2]: Nothing to be done for 'programs'.
make[2]: Leaving directory '/root/byte-unixbench/UnixBench'
make[1]: Leaving directory '/root/byte-unixbench/UnixBench'
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
sh: 1: 3dinfo: not found

   #    #  #    #  #  #    #          #####   ######  #    #   ####   #    #
   #    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #
   #    #  # #  #  #    ##            #####   #####   # #  #  #       ######
   #    #  #  # #  #    ##            #    #  #       #  # #  #       #    #
   #    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #
    ####   #    #  #  #    #          #####   ######  #    #   ####   #    #

   Version 5.1.3                      Based on the Byte Magazine Unix Benchmark

   Multi-CPU version                  Version 5 revisions by Ian Smith,
                                      Sunnyvale, CA, USA
   January 13, 2011                   johantheghost at yahoo period com

------------------------------------------------------------------------------
   Use directories for:
      * File I/O tests (named fs***) = /root/byte-unixbench/UnixBench/tmp
      * Results                      = /root/byte-unixbench/UnixBench/results
------------------------------------------------------------------------------


1 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

1 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

1 x Execl Throughput  1 2 3

1 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

1 x File Copy 256 bufsize 500 maxblocks  1 2 3

1 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

1 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

1 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

1 x Process Creation  1 2 3

1 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

1 x Shell Scripts (1 concurrent)  1 2 3

1 x Shell Scripts (8 concurrent)  1 2 3

========================================================================
   BYTE UNIX Benchmarks (Version 5.1.3)

   System: ip-10-0-101-148: GNU/Linux
   OS: GNU/Linux -- 4.19.0-14-cloud-amd64 -- #1 SMP Debian 4.19.171-2 (2021-01-30)
   Machine: x86_64 (unknown)
   Language: en_US.utf8 (charmap="ANSI_X3.4-1968", collate="ANSI_X3.4-1968")
   CPU 0: Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz (4788.9 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET
   12:19:51 up 18 min,  1 user,  load average: 0.39, 0.20, 0.08; runlevel Mar

------------------------------------------------------------------------
Benchmark Run: Wed Mar 23 2022 12:19:51 - 12:47:47
1 CPU in system; running 1 parallel copy of tests

Dhrystone 2 using register variables       33428472.8 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                     5339.8 MWIPS (9.1 s, 7 samples)
Execl Throughput                               3989.5 lps   (30.0 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        545115.4 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks          139827.1 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks       1704837.6 KBps  (30.0 s, 2 samples)
Pipe Throughput                              683291.4 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                 198564.9 lps   (10.0 s, 7 samples)
Process Creation                              11331.7 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                   8369.9 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                   1099.6 lpm   (60.0 s, 2 samples)
System Call Overhead                         385521.0 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   33428472.8   2864.5
Double-Precision Whetstone                       55.0       5339.8    970.9
Execl Throughput                                 43.0       3989.5    927.8
File Copy 1024 bufsize 2000 maxblocks          3960.0     545115.4   1376.6
File Copy 256 bufsize 500 maxblocks            1655.0     139827.1    844.9
File Copy 4096 bufsize 8000 maxblocks          5800.0    1704837.6   2939.4
Pipe Throughput                               12440.0     683291.4    549.3
Pipe-based Context Switching                   4000.0     198564.9    496.4
Process Creation                                126.0      11331.7    899.3
Shell Scripts (1 concurrent)                     42.4       8369.9   1974.0
Shell Scripts (8 concurrent)                      6.0       1099.6   1832.7
System Call Overhead                          15000.0     385521.0    257.0
                                                                   ========
System Benchmarks Index Score                                        1060.0

Sysdig Agent あり

root@ip-10-0-101-110:~/byte-unixbench/UnixBench# ./Run
gcc -o pgms/arithoh -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Darithoh src/arith.c 
gcc -o pgms/register -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum='register int' src/arith.c 
gcc -o pgms/short -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=short src/arith.c 
gcc -o pgms/int -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=int src/arith.c 
gcc -o pgms/long -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=long src/arith.c 
gcc -o pgms/float -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=float src/arith.c 
gcc -o pgms/double -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -Ddatum=double src/arith.c 
gcc -o pgms/hanoi -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/hanoi.c 
gcc -o pgms/syscall -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/syscall.c 
gcc -o pgms/context1 -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/context1.c 
gcc -o pgms/pipe -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/pipe.c 
gcc -o pgms/spawn -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/spawn.c 
gcc -o pgms/execl -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/execl.c 
gcc -o pgms/dhry2 -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DHZ= ./src/dhry_1.c ./src/dhry_2.c
gcc -o pgms/dhry2reg -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DHZ= -DREG=register ./src/dhry_1.c ./src/dhry_2.c
gcc -o pgms/looper -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/looper.c 
gcc -o pgms/fstime -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME src/fstime.c 
gcc -o pgms/whetstone-double -Wall -pedantic -O3 -ffast-math -march=native -mtune=native -I ./src -DTIME -DDP -DGTODay -DUNIXBENCH src/whets.c -lm
make all
make[1]: Entering directory '/root/byte-unixbench/UnixBench'
make distr
make[2]: Entering directory '/root/byte-unixbench/UnixBench'
Checking distribution of files
./pgms  exists
./src  exists
./testdir  exists
./tmp  exists
./results  exists
make[2]: Leaving directory '/root/byte-unixbench/UnixBench'
make programs
make[2]: Entering directory '/root/byte-unixbench/UnixBench'
make[2]: Nothing to be done for 'programs'.
make[2]: Leaving directory '/root/byte-unixbench/UnixBench'
make[1]: Leaving directory '/root/byte-unixbench/UnixBench'
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
locale: Cannot set LC_CTYPE to default locale: No such file or directory
locale: Cannot set LC_MESSAGES to default locale: No such file or directory
locale: Cannot set LC_ALL to default locale: No such file or directory
sh: 1: 3dinfo: not found

   #    #  #    #  #  #    #          #####   ######  #    #   ####   #    #
   #    #  ##   #  #   #  #           #    #  #       ##   #  #    #  #    #
   #    #  # #  #  #    ##            #####   #####   # #  #  #       ######
   #    #  #  # #  #    ##            #    #  #       #  # #  #       #    #
   #    #  #   ##  #   #  #           #    #  #       #   ##  #    #  #    #
    ####   #    #  #  #    #          #####   ######  #    #   ####   #    #

   Version 5.1.3                      Based on the Byte Magazine Unix Benchmark

   Multi-CPU version                  Version 5 revisions by Ian Smith,
                                      Sunnyvale, CA, USA
   January 13, 2011                   johantheghost at yahoo period com

------------------------------------------------------------------------------
   Use directories for:
      * File I/O tests (named fs***) = /root/byte-unixbench/UnixBench/tmp
      * Results                      = /root/byte-unixbench/UnixBench/results
------------------------------------------------------------------------------


1 x Dhrystone 2 using register variables  1 2 3 4 5 6 7 8 9 10

1 x Double-Precision Whetstone  1 2 3 4 5 6 7 8 9 10

1 x Execl Throughput  1 2 3

1 x File Copy 1024 bufsize 2000 maxblocks  1 2 3

1 x File Copy 256 bufsize 500 maxblocks  1 2 3

1 x File Copy 4096 bufsize 8000 maxblocks  1 2 3

1 x Pipe Throughput  1 2 3 4 5 6 7 8 9 10

1 x Pipe-based Context Switching  1 2 3 4 5 6 7 8 9 10

1 x Process Creation  1 2 3

1 x System Call Overhead  1 2 3 4 5 6 7 8 9 10

1 x Shell Scripts (1 concurrent)  1 2 3

1 x Shell Scripts (8 concurrent)  1 2 3

========================================================================
   BYTE UNIX Benchmarks (Version 5.1.3)

   System: ip-10-0-101-110: GNU/Linux
   OS: GNU/Linux -- 4.19.0-14-cloud-amd64 -- #1 SMP Debian 4.19.171-2 (2021-01-30)
   Machine: x86_64 (unknown)
   Language: en_US.utf8 (charmap="ANSI_X3.4-1968", collate="ANSI_X3.4-1968")
   CPU 0: Intel(R) Xeon(R) CPU E5-2676 v3 @ 2.40GHz (4800.0 bogomips)
          Hyper-Threading, x86-64, MMX, Physical Address Ext, SYSENTER/SYSEXIT, SYSCALL/SYSRET
   12:16:57 up 16 min,  1 user,  load average: 0.27, 0.28, 0.12; runlevel Mar

------------------------------------------------------------------------
Benchmark Run: Wed Mar 23 2022 12:16:57 - 12:44:59
1 CPU in system; running 1 parallel copy of tests

Dhrystone 2 using register variables       33409055.1 lps   (10.0 s, 7 samples)
Double-Precision Whetstone                     5323.8 MWIPS (9.1 s, 7 samples)
Execl Throughput                               2359.3 lps   (30.0 s, 2 samples)
File Copy 1024 bufsize 2000 maxblocks        440257.2 KBps  (30.0 s, 2 samples)
File Copy 256 bufsize 500 maxblocks          109575.1 KBps  (30.0 s, 2 samples)
File Copy 4096 bufsize 8000 maxblocks       1401446.4 KBps  (30.0 s, 2 samples)
Pipe Throughput                              515217.8 lps   (10.0 s, 7 samples)
Pipe-based Context Switching                 159861.0 lps   (10.0 s, 7 samples)
Process Creation                               6785.3 lps   (30.0 s, 2 samples)
Shell Scripts (1 concurrent)                   5027.4 lpm   (60.0 s, 2 samples)
Shell Scripts (8 concurrent)                    762.4 lpm   (60.0 s, 2 samples)
System Call Overhead                         137465.4 lps   (10.0 s, 7 samples)

System Benchmarks Index Values               BASELINE       RESULT    INDEX
Dhrystone 2 using register variables         116700.0   33409055.1   2862.8
Double-Precision Whetstone                       55.0       5323.8    968.0
Execl Throughput                                 43.0       2359.3    548.7
File Copy 1024 bufsize 2000 maxblocks          3960.0     440257.2   1111.8
File Copy 256 bufsize 500 maxblocks            1655.0     109575.1    662.1
File Copy 4096 bufsize 8000 maxblocks          5800.0    1401446.4   2416.3
Pipe Throughput                               12440.0     515217.8    414.2
Pipe-based Context Switching                   4000.0     159861.0    399.7
Process Creation                                126.0       6785.3    538.5
Shell Scripts (1 concurrent)                     42.4       5027.4   1185.7
Shell Scripts (8 concurrent)                      6.0        762.4   1270.6
System Call Overhead                          15000.0     137465.4     91.6
                                                                   ========
System Benchmarks Index Score                                         753.1

多くのシナリオで、Sysdig Agent をインストールした場合には性能が劣化する結果となりました。
System Benchmarks Index Score は以下のとおりでした。

  • Sysdig Agent なしの場合 : 1060.0
  • Sysdig Agent ありの場合 : 753.1

25 %程度スコアが落ち込んでいることがわかります。
割り当てリソースの差などもあるため一概には言えませんが、Serverless Agent よりは性能影響は少なそうだという印象です。

簡単にですが、Serverless Agent および Sysdig Agent でインストゥルメンテーションを行うことでベンチマークのスコアにどの程度の影響があるのかを見ることができました。
当然ながら、アプリケーションの特性に応じて性能への影響度合いは変化するため、条件によっては今回の結果より悪くなることもあれば良くなることもあると考えられます。
また、ランタイムポリシ(Sysdig Secure Rules)の数や内容、エージェントのバージョン、タスク定義で設定しているリソース( CPU, メモリ)など様々な影響要によって結果が変わってくると思います。

補足 (1)

いくつかの仕様やセットアップ手順の確認のために Sysdig のサポートへ問い合わせをしたのですが、大変親切に対応頂きました。
とてもレスポンスが早く、技術的な内容にも正確に回答頂き、サポート品質の重要性を再認識しました。
なお、サポートとのやり取りは英語になります。

補足 (2)

今回は ECS Fargate で起動した Debian のコンテナに ECS Exec でログインしてベンチマークを叩いていたのですが、その際 ECS Exec 実行時の気付きがあったのでこちらも書き残しておきます。

ECS Exec 実行時、以下のエラーメッセージが表示された場合は、タスクロールの権限 が不足している可能性があります。
本当に内部エラーの場合もあるかもしれませんが、まずは必要な IAM アクセス許可 (ssmmessages:...) が付与できているか確認することをおすすめします。

$ aws ecs execute-command ...

The Session Manager plugin was installed successfully. Use the AWS CLI to start a session.

An error occurred (TargetNotConnectedException) when calling the ExecuteCommand operation: The execute command failed due to an internal error. Try again later.

まとめ

全 3 回の連載を通じて、ECS Fargate ワークロードを Sysdig Serverless Agent で保護する方法をご紹介しました。
Sysdig Serverless Agent や ECS Fargate 環境のランタイム保護については海外を含めてまだ情報が少なく、実際に使ってみることで色々と新しい気付きを得られました。
今回の記事が、ECS Fargate ワークロードのセキュリティ対策を検討されている方や Sysdig の利用を検討されている方のお役に立てば幸いです。

参考

記事執筆にあたり参考にさせて頂いた情報へのリンクを列挙しておきます。

Sysdig のアーキテクチャを理解するにあたり、少し古い情報ですが以下のホワイトペーパーなどが役立ちました。

Fargate 利用時にユーザが考慮すべきセキュリティ対策については、以下に挙げる AWS re:Invent 2019 の Fargate 環境におけるセキュリティベストプラクティスを紹介したセッションや、Sysdig のブログに掲載されている Fargate 環境の脅威モデリングに関する記事が参考になりました。

サンプルアプリケーションの CloudFormation テンプレート作成時に以下を参考にさせて頂きました。


  1. ブログのほうではどのようなベンチマークツールを使用したのかが書かれていませんでしたが、もしかすると同じベンチマークの結果を指しているのかもしれません

  2. あくまで推測ですが、Serverless Agent は性能とのバランスを取るために一部のシステムコールの検出を seccomp で除外しているため、除外対象のシステムコールが多かったことで性能差が生じにくいシナリオがあった可能性もあるのではと考えます

ECS Fargate ワークロードを Sysdig Serverless Agent で保護する (2)

Amazon Elastic Container Service (ECS) の実行環境に AWS Fargate を用いた構成(以下、ECS Fargate )は、AWS でコンテナの実行環境を整える際の強力な選択肢のひとつです。
Fargate を用いることで、ユーザが考慮すべきセキュリティリスクは( EC2 を用いるよりも)少なくなりますが、すべてがカバーされるものではありません。

この連載記事では、ECS Fargate で実行されるワークロードを Sysdig の Serverless Agent を使って保護するための方法をご紹介します。
第 2 回となる今回は、Serverless Agent のセットアップとイベントキャプチャに触れます。

  1. Sysdig と Serverless Agent の概要
  2. Serverless Agent のセットアップとイベントキャプチャ (この記事)
  3. Serverless Agent がアプリケーションに与える性能影響

Sysdig Serverless Agent のセットアップ

Sysdig Serverless Agent を ECS Fargate 環境にセットアップするまでの流れを見てゆきます。
公式ドキュメント の手順に沿って、セットアップ時の注意点やスクリーンショットを交えながらご紹介します。
セットアップに使用するサンプルアプリケーションの CloudFormation テンプレートを こちら に公開しましたので、必要に応じてご参照ください。

Orchestrator Agent のインストール

まず、Sysdig から配布されている Orchestrator Agent の CloudFormation テンプレート をダウンロードし、CloudFormation で対象の VPC にデプロイします。

このとき、パラメータとして指定する Sysdig Collector Host は、Sysdig の SaaS をどのリージョンで利用しているかでエンドポイントの URL が変わってくる点に注意が必要です。
今回は US West (Oregon) リージョンで作成した Sysdig アカウントを利用します。
そのため、公式ドキュメントの SaaS エンドポイントの一覧 から、Sysdig Collector Host に設定すべきエンドポイントは ingest-us2.app.sysdig.com であることがわかります。

Orchestrator Agent のデプロイが完了すると、コンソールの ECS のページからエージェントのサービスやタスクの状態を確認することができます。

以降の Workload Agent のインストール準備に必要となるため、CloudFormation の出力 (Output) タブの OrchestratorHost と OrchestratorPort の値を控えておきます。

Workload Agent のインストール

次に、Workload Agent をインストール(保護対象のワークロードのタスク定義に追加)します。
Workload Agent をタスク定義に追加する方法は以下の 4 つがありますが、今回は Kilt を使った自動挿入の流れをご紹介します。

  • Kilt によるタスク定義の自動挿入
  • Terraform Sysdig Provider によるタスク定義の自動挿入
  • 手動でタスク定義に Workload Agent と一連の設定を加える
  • コンテナイメージのビルド時に Workload Agent を同梱する

Kilt によるタスク定義の自動挿入を行うためには、まず公式ドキュメントの リンク から端末環境に合わせたインストーラをダウンロードします。
リンクの URL を見るに、Mac 向けのインストーラamd64 版のみの提供となっており、M1 Mac (arm64) には対応していないようですね。

実行時引数に OrchestratorHost と OrchestratorPort の値を与えて、ダウンロードしたインストーラを実行します。

% ./installer-macos-amd64 cfn-macro install -r us-east-1 MySysdigMacro sysdi-Sysdi-1H9B3SZS3U6J-3ab0b532f7c361ef.elb.us-east-1.amazonaws.com 6667
Kilt detected the following:
 * account id: ************
 * region: us-east-1
kilt bucket name: sysdig-cloud-************-us-east-1
Installing macro MySysdigMacro...
Uploading lambda code. This might take a while...DONE
Uploading kilt definition cfn-macro-MySysdigMacro.kilt.cfg...DONE
Submitted cloudformation stack 'KiltMacroMySysdigMacro'. Follow creation progress in AWS console
After installation is completed you will be able to use "Transform: ["MySysdigMacro"]" in your template to automatically instrument it

実行すると、インストール先のタスク定義の CloudFormation テンプレートに Transform セクションを加えるよう案内されます。
また、コンソールで確認すると、自動挿入を行うための Lambda が CloudFormation でデプロイされていることがわかります。

これで自動挿入の準備は完了です。

サンプルアプリケーションの実行と Sysdig による保護

Serverless Agent を利用する準備が整ったので、サンプルアプリケーションを CloudFormation でデプロイします。

今回、サンプルアプリケーションには Falco の GitHub リポジトリで公開されている event-generator を利用しました。
Sysdig で検出されるような様々なシステムコールの実行、ファイルの参照や変更といったアクションを延々と行うアプリケーションとなっています。
作成したサンプルアプリケーションの CloudFormation テンプレートの Transform セクションに、Kilt のマクロを指定してデプロイします。

github.com

タスク定義を参照すると、前述のマクロによって Workload Agent や一連の設定が自動挿入されたことを確認できます。
具体的には、以下のような設定が追加されます。

  • Entrypoint に /opt/draios/bin/instrument を追加する
  • SYS_PTRACE の Linux Capability を追加する
  • 環境変数に Orchestrator Agent の宛先情報を追加する
  • WorkloadAgent のコンテナ定義を追加する
{
  "ipcMode": null,
  "executionRoleArn": "arn:aws:iam::************:role/ecsTaskExecutionRole",
  "containerDefinitions": [
    {
      "dnsSearchDomains": [],
      "environmentFiles": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "secretOptions": [],
        "options": {
          "awslogs-group": "KiltMacroMySysdigMacro-SysdigServerlessLogGroup-asDctJPbI8uU",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "TaskDefinition"
        }
      },
      "entryPoint": [
        "/opt/draios/bin/logwriter"
      ],
      "portMappings": [],
      "command": [],
      "linuxParameters": null,
      "cpu": 0,
      "environment": [],
      "resourceRequirements": null,
      "ulimits": [],
      "dnsServers": [],
      "mountPoints": [],
      "workingDirectory": null,
      "secrets": [],
      "dockerSecurityOptions": [],
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [],
      "stopTimeout": null,
      "image": "quay.io/sysdig/workload-agent:latest",
      "startTimeout": null,
      "firelensConfiguration": null,
      "dependsOn": null,
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": [],
      "hostname": null,
      "extraHosts": [],
      "pseudoTerminal": null,
      "user": null,
      "readonlyRootFilesystem": null,
      "dockerLabels": {},
      "systemControls": [],
      "privileged": null,
      "name": "SysdigInstrumentation"
    },
    {
      "dnsSearchDomains": [],
      "environmentFiles": [],
      "logConfiguration": {
        "logDriver": "awslogs",
        "secretOptions": [],
        "options": {
          "awslogs-group": "/ecs/event-generator",
          "awslogs-region": "us-east-1",
          "awslogs-stream-prefix": "app"
        }
      },
      "entryPoint": [
        "/opt/draios/bin/instrument"
      ],
      "portMappings": [],
      "command": [
        "/bin/event-generator",
        "run",
        "syscall",
        "--loop"
      ],
      "linuxParameters": {
        "capabilities": {
          "add": [
            "SYS_PTRACE"
          ],
          "drop": []
        },
        "sharedMemorySize": null,
        "tmpfs": [],
        "devices": [],
        "maxSwap": null,
        "swappiness": null,
        "initProcessEnabled": null
      },
      "cpu": 0,
      "environment": [
        {
          "name": "SYSDIG_ACCESS_KEY",
          "value": ""
        },
        {
          "name": "SYSDIG_COLLECTOR",
          "value": ""
        },
        {
          "name": "SYSDIG_COLLECTOR_PORT",
          "value": ""
        },
        {
          "name": "SYSDIG_LOGGING",
          "value": ""
        },
        {
          "name": "SYSDIG_ORCHESTRATOR",
          "value": "sysdi-Sysdi-1H9B3SZS3U6J-3ab0b532f7c361ef.elb.us-east-1.amazonaws.com"
        },
        {
          "name": "SYSDIG_ORCHESTRATOR_PORT",
          "value": "6667"
        }
      ],
      "resourceRequirements": null,
      "ulimits": [],
      "dnsServers": [],
      "mountPoints": [],
      "workingDirectory": null,
      "secrets": [],
      "dockerSecurityOptions": [],
      "memory": null,
      "memoryReservation": null,
      "volumesFrom": [
        {
          "sourceContainer": "SysdigInstrumentation",
          "readOnly": true
        }
      ],
      "stopTimeout": null,
      "image": "falcosecurity/event-generator:latest",
      "startTimeout": null,
      "firelensConfiguration": null,
      "dependsOn": null,
      "disableNetworking": null,
      "interactive": null,
      "healthCheck": null,
      "essential": true,
      "links": [],
      "hostname": null,
      "extraHosts": [],
      "pseudoTerminal": null,
      "user": null,
      "readonlyRootFilesystem": null,
      "dockerLabels": {},
      "systemControls": [],
      "privileged": null,
      "name": "event-generator"
    }
  ],
  "placementConstraints": [],
  "memory": "512",
  "taskRoleArn": "arn:aws:iam::************:role/task-role-for-event-generator",
  "compatibilities": [
    "EC2",
    "FARGATE"
  ],
  "taskDefinitionArn": "arn:aws:ecs:us-east-1:************:task-definition/event-generator:7",
  "family": "event-generator",
  "requiresAttributes": [
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.logging-driver.awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.execution-role-awslogs"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.19"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.17"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.task-iam-role"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.22"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "com.amazonaws.ecs.capability.docker-remote-api.1.18"
    },
    {
      "targetId": null,
      "targetType": null,
      "value": null,
      "name": "ecs.capability.task-eni"
    }
  ],
  "pidMode": null,
  "requiresCompatibilities": [
    "FARGATE"
  ],
  "networkMode": "awsvpc",
  "runtimePlatform": null,
  "cpu": "256",
  "revision": 7,
  "status": "ACTIVE",
  "inferenceAccelerators": null,
  "proxyConfiguration": null,
  "volumes": []
}

CloudFormation によるデプロイが完了すると、コンソールの ECS のページからサービスやタスクの状態を確認することができます。
実行中のタスクでは、サンプルアプリケーションのコンテナとは別に、Workload Agent のコンテナが稼働していることがわかります。

セキュリティイベントの検出にあたり、Sysdig のランタイムポリシはデフォルトで用意されているルールセットを有効化した状態としました。
デフォルトの設定では、ポリシに抵触するイベントが発生した場合、通知のみを行う(実行は制限しない)設定となっています。

Workload Agent が保護しているワークロードで検出されたセキュリティイベントは、Sysdig の Web コンソールの Events ページで確認することができます。

上の図では、サンプルアプリケーションが bash を起動したりディレクトリを作成したため、定義済みポリシに抵触して Notable Filesystem Changes や Unexpected Spawned Processes のイベントが検出されていることがわかります。

ECS Fargate 環境における Sysdig Capture

Sysdig Capture によるセキュリティイベントのキャプチャは、Sysdig の特徴的な機能のひとつです。
Sysdig はワークロードがポリシに準拠していることを評価するためにシステムコールをモニタリングし続けており、セキュリティイベントが発生した際には、その前後(最大 300 秒)のシステムコールやその他の OS イベントをひとつのキャプチャファイルにまとめ、後からダウンロードしたり分析ができるようになっています。
ECS Fargate 環境に限った話ではないのですが、Serverless Agent でもキャプチャが実行できるか試してみます。

キャプチャはポリシーの一部として構成するか、モジュールを使って手動で作成することができます。
今回は以下のポリシでイベントキャプチャを有効化して生成します。

サンプルアプリケーションで該当するイベントが検出されました。

生成されたキャプチャファイルは、端末にダウンロードするなどして参照することもできますが、手っ取り早く可視化する手段として Sysdig Inspect という分析ツールが提供されています。
Sysdig Inspect に Web コンソールからアクセスする場合は、メニューの Investigate ページにある Captures タブを開きます。

Sysdig Inspect の Web UI を使って、セキュリティイベント発生時のシステムコールやプロセスの状態を確認することができました。

補足 (1) Activity Audit の参照

Investigate ページの Activity Audit タブでは Sysdig が収集したアクティビティ情報を時系列で確認することができますが、今回、Serverless Agent を上記のようにセットアップした状態では何も表示されませんでした。
Serverless Agent が Fargate のホストにアクセスができないため Activity Audit がサポートされていないものと思われますが、もしかすると設定次第では何かしら表示できるものもあるのかもしれません。

ちなみに、EC2 に通常の Sysdig Agent をインストールした場合は、Activity Audit の情報が表示されました。

補足 (2) Kilt による Workload Agent と諸設定の自動挿入

公式ドキュメントにも記載されていますが、Workload Agent と諸設定の自動挿入を行う Kilt は、挿入の際に Entrypoint と Command の設定を変更します。
そのため、(プライベートなイメージの場合は特に)Entrypoint と Command を明示的にタスク定義に含めておきましょう。
パブリックなイメージの場合は Kilt が自動的にデフォルトの Entrypoint と Command を取得してくれますが、正しく取得できなかった場合は自動挿入に失敗します。

docs.sysdig.com

つづく

今回は ECS Fargate 環境に Sysdig Serverless Agent をセットアップし、ワークロードを保護するまでの流れをご紹介しました。
次回 は Serverless Agent がアプリケーションの性能に与える影響に触れたいと思います。

ECS Fargate ワークロードを Sysdig Serverless Agent で保護する (1)

Amazon Elastic Container Service (ECS) の実行環境に AWS Fargate を用いた構成(以下、ECS Fargate )は、AWS でコンテナの実行環境を整える際の強力な選択肢のひとつです。
Fargate を用いることで、ユーザが考慮すべきセキュリティリスクは( EC2 を用いるよりも)少なくなりますが、すべてがカバーされるものではありません。
今回は、ECS Fargate で実行されるワークロードを Sysdig の Serverless Agent を使って保護するための方法を連載記事でご紹介します。

  1. Sysdig と Serverless Agent の概要 (この記事)
  2. Serverless Agent のセットアップとイベントキャプチャ
  3. Serverless Agent がアプリケーションに与える性能影響

モチベーション

Fargate を利用することで、ECS / EKS のユーザはコンテナを実行するための仮想マシンを設定・運用する必要がなくなります。
そのため、仮想マシンの観点では、例えば セキュリティパッチ適用、マルウェア対策、アクセス制御などをユーザが行う必要はありません。
仮想マシンがユーザにとって透過的な存在となるため、セキュリティリスクを引き起こす脅威の一部を考慮する必要がなくなります。

一方、実行するコンテナのセキュリティ対策は依然としてユーザの責務です。
例えば、ECS Fargate の環境においても、以下のようなセキュリティリスクへの対応を検討する必要があります。

  • 正常なリクエストを装い、脆弱性を悪用して不正なコードを実行させる第三者からの攻撃
  • 悪意のあるコードを含むライブラリが混入し、意図せず外部へ機微情報を送信してしまう
  • SSH などでコンテナにアクセスする権限を持った者が不正にデータを持ち出す

近年、ECS Fargate のようなサーバレスコンピューティング環境においても、上記のようなリスクの顕在化を検知・抑止する目的で、コンテナセキュリティを専門とする各社から対策製品や機能が提供されるようになってきています。
ECS Fargate の環境をサポートしているコンテナセキュリティ製品の代表例としては、Sysdig, Prisma Cloud, Aqua などが挙げられます。

Sysdig の概要

Sysdig は CNCF のオープンソースプロジェクトのひとつである Falco 1 をベースとした、コンテナセキュリティのための SaaS です。
エージェントを対象の環境にインストールすることで収集したメトリクス・メタデータ・セキュリティイベントを SaaS バックエンド 2 に転送し、ポリシに違反したセキュリティイベントのアラートやコンプライアンスの準拠チェックなどを行うことができます。
ランタイムポリシは記述形式に Falco ルールを用いた Sysdig Secure Rules と呼ばれるルールセットで定義され 3 、エージェントはこのポリシに基づいてワークロードの挙動に対するイベント検出・キャプチャ・抑止などのアクションを行います。

以下は公式ドキュメントから引用した Sysdig のアーキテクチャ図です。

引用元

Sysdig では、保護するワークロードの実行環境に応じて 2 種類のエージェントが提供されています。

  • Sysdig Agent : ユーザが個別の仮想マシンの可視性を持つ EC2、オンプレミス、ベアメタル環境向けのエージェント(通常、Sysdig のエージェントと言うとこちらを指す)
  • Serverless Agent : サーバレスコンピューティング環境向けのエージェント(現在サポートされている環境は Fargate のみ)

エージェントが別々に用意されている理由には、Sysdig の Deep Instrumentation の仕組みが関係しています。
Sysdig Agent は、ホストにカーネルモジュールや eBPF プローブをインストールすることでシステムコールなどのカーネル内のイベントを収集します。
収集されたイベントはイベントバッファを通じてユーザランドのライブラリ ( libscap / libsinsp ) 4 に連携され、フィルタリングや加工を行ってから Sysdig のエンドポイントにデータを送ることで、ワークロードのセキュリティイベントを評価・可視化します。

引用元

一方、Fargate では同じこと(ホストにカーネルモジュールや eBPF プローブをインストール)ができません。
ものによっては CloudTrail や IDS / IPS の情報から検知できるセキュリティイベントもあるかと思いますが、より詳細で確実な分析を行うためにワークロードのシステムコールを観測することが必要となる場合があります。
このような背景から、Sysdig ではサーバレスコンピューティング環境向けの専用のエージェントが用意されています。

Serverless Agent では、ptrace を使ってシステムコールの観測を行う pdig 5 というライブラリが利用されています。
Fargate のプラットフォームバージョン v1.4.0 ( 2020 年 4 月リリース 6 ) が CAP_SYS_PTRACE の Linux capability をサポートしたことで、この実装が可能になりました。

Sysdig の Serverless Agent による ECS Fargate ワークロードの保護

Sysdig Serverless Agent を以下のように構成することで、ECS Fargate 環境のワークロードを保護することが可能です。

引用元

Sysdig Serverless Agent は 2 つのコンポーネントで構成されています。

  • Orchestrator Agent : (複数の) Workload Agent からセキュリティイベントを受信し、Sysdig Platform (SaaS エンドポイント等) に送信する。VPC 毎に必要
  • Workload Agent : 保護するアプリケーションと同じタスクにデプロイし、セキュリティイベントを収集、Orchestrator Agent に送信する。Fargate タスク定義毎に必要

Workload Agent は ptrace を用いてセキュリティイベントを収集するため、タスク定義にいくつかの設定を追加する必要が出てきます。
手動で設定することも可能ですが、アプリケーションの CloudFormation テンプレートに Workload Agent とその他必要な設定の一式を自動挿入してくれるマクロが Sysdig から提供されていますので、こちらを利用することも可能です。
マクロは AWS Lambda で実装されており、CloudFormation テンプレートの Transform セクションから呼び出すことで、アプリケーションのタスク定義に自動挿入する仕組み 7 となっています。

加えて、Sysdig Serverless Agent を利用するためには、X86_64 アーキテクチャを使用した Fargate タスクを定義する必要があります。
エージェントが ptrace に利用している pdig ライブラリのサポート対象は、現状 x86_64 のみとなっているためです。8
Fargate は、2021年11月のアップデートにより、従来から提供されてきた X86_64 アーキテクチャに加え、Graviton2 を使った ARM64 アーキテクチャ 9 も実行環境として利用可能になっています(タスク定義の runtimePlatform -> cpuArchitecture で指定、デフォルトは X86_64 )が、Sysdig を利用する際には選択肢が限定される点に留意しておく必要があります。

なお、Serverless Agent のコンテナイメージは Quay で公開されています。10
記事執筆時点の最新は、Orchestrator Agent と Workload Agent のいずれも 2.3.0 となっています。
直近数回のリリース履歴を見るに、3 ヶ月程度で新しいバージョンがリリースされているようです。

docs.sysdig.com

つづく

今回は Sysdig の概要と ECS Fargate のサポート状況について調べた情報をまとめました。
次回 は ECS Fargate に Serverless Agent をインストールする手順とイベント検出・キャプチャの様子をご紹介したいと思います。


  1. Falco は 2018 年に CNCF へ寄贈されたオープンソースのランタイムセキュリティプロジェクトです(現在 Incubating のステージ)

  2. 最近はオンプレに Sysdig Platform をデプロイするオプションもあるようですが、本記事は SaaS 版を前提とします

  3. Falco ルールをゼロから記述する負担を減らすため、ファストルールと呼ばれるブラックリストホワイトリスト形式の簡単な記述形式や、定義済みルールも提供されています

  4. Sysdig のライブラリやカーネルモジュールは、2021 年 2 月に CNCF へ寄贈されており、falcosecurityGitHub リポジトリで管理されています

  5. https://github.com/falcosecurity/pdig

  6. https://aws.amazon.com/jp/blogs/containers/aws-fargate-launches-platform-version-1-4/

  7. 自動挿入には、Falco の GitHub リポジトリで公開されている Kilt というコンポーネントが使われています

  8. https://github.com/falcosecurity/pdig#prerequisites

  9. https://aws.amazon.com/jp/blogs/news/announcing-aws-graviton2-support-for-aws-fargate-get-up-to-40-better-price-performance-for-your-serverless-containers/

  10. https://quay.io/repository/sysdig/workload-agent?tab=tags , https://quay.io/repository/sysdig/orchestrator-agent?tab=tags

Aurora PostgreSQL のメジャーバージョンをまたいだアップグレード

Amazon Aurora PostgreSQL 互換 (以下、Aurora PostgreSQL) の新機能として、複数のメジャーバージョンをまたいだアップグレードが可能になりました。
調べたことや検証したことを書き残しておきたいと思います。

メジャーバージョンのアップグレードに関するドキュメント

今回追加された機能に関するドキュメントは以下にあります。

docs.aws.amazon.com

ドキュメント履歴にも掲載されていました。

docs.aws.amazon.com

なお、この記事の執筆時点ではまだ日本語版のドキュメント履歴には載っていませんでした。

docs.aws.amazon.com

アップグレードの準備

Aurora PostgreSQL ではマイナーバージョンの自動アップグレードオプションが提供されており、ユーザとしては手動・自動の選択の余地があります。
一方、メジャーバージョンのアップグレードは自動アップグレードの対象ではないため、手動での対応が必要です。
理由はドキュメントにも記載されていますが、メジャーバージョンのアップグレードはバージョン間に破壊的な(後方互換性のない)変更が含まれる可能性があるためです。

このあたりは複数のメジャーバージョンにまたがったアップグレードにおいても変更はありません。

また、ドキュメントでは、メジャーバージョンのアップグレードに向けて以下のような観点で準備が必要と言及されています。

  • パラメータグループの準備
  • 拡張機能のアップグレードや削除
  • サポートされないデータ型の削除

パラメータグループの準備は、互換性のあるパラメータグループの選定、あるいは新規カスタムパラメータグループの作成などを行います。

拡張機能のアップグレードはメジャーバージョンのアップグレードに連動しないため、拡張機能ごとに手動でアップグレードの判断が必要です。
また、メジャーバージョンをアップグレードする前に最新版へアップグレードしておく必要のある拡張機能もあるため、作業順序を予め整理しておくと良いでしょう。

実際には、上記以外にも様々な変更影響が想定されるため、(開発用途などの)サービス提供に影響しない環境でアップグレードの影響を十分に事前調査したうえで実施することをおすすめします。

アップグレードにかかる時間

今回は Aurora PostgreSQL のメジャーバージョンアップにかかる時間を 2 つのパターンで計測してみたいと思います。
検証のために用意したクラスタは以下のとおりです。

  • Aurora PostgreSQL : db.r6g.large x 1 インスタンス
  • アップグレード前のバージョン : 11.13
  • アップグレード先のバージョン : 12.8, 13.4
  • キャパシティータイプ : Provisioned
  • Aurora レプリカの作成 : なし
  • リージョン : ap-northeast-1 (東京)

コンソールで確認してみると、11.3 からメジャーバージョンをまたいで 13.4 にアップグレード可能であることがわかります。

f:id:polar3130:20220308164104p:plain

メジャーバージョンをまたいだアップグレードと所要時間を比較するため、まずは 11.13 のクラスタを 12.8 のバージョンにアップグレードします。
今回は、できる限り単純にアップグレードにかかる時間のみを比較するため、クラスタ作成直後の(データが投入されていない)状態からアップグレードにかかる時間を計測してみました。

以下のように、コンソールに確認画面が表示されます。

f:id:polar3130:20220308152736p:plain

アップグレードを開始すると、経過を RDS のイベント履歴から参照できる旨のメッセージが表示されました。
以下はアップグレードの完了までに記録されたイベントの一覧を示しています。
アップグレードの開始直前に、スナップショットを取得していることなどがわかります。

f:id:polar3130:20220308152741p:plain

アップグレードの開始自体はイベントに記録されないようですが、最初の DB インスタンスのシャットダウンイベントから、アップグレードの完了イベントまでを所要時間とすると、おおよそ 845 秒 で完了しました。

次に、11.13 から 13.4 へのアップグレードを試します。 以下のように、コンソールに確認画面が表示されます。

f:id:polar3130:20220308164109p:plain

アップグレードの完了までに記録されたイベントの一覧は以下のとおりです。

f:id:polar3130:20220308165205p:plain

(先程の 11.13 → 12.8 と同様に)最初の DB インスタンスのシャットダウンイベントから、アップグレードの完了イベントまでを所要時間とすると、おおよそ 791 秒 で完了しました。

まとめ

Aurora PostgreSQL の新しい機能であるメジャーバージョンをまたいだアップグレードを試しました。
メジャーバージョンのアップグレードは破壊的変更を伴う場合があるため、事前に十分な検証は必要ですが、こうした手段があることは知っておくと Aurora をより便利に使えそうですね。

Amazon Aurora のリストアにかかる時間

Amazon Aurora は、AWS の RDS のひとつとして提供されている RDB のサービスです。
MySQL および PostgreSQL の互換エディションが提供されています。
今回は、Aurora をスナップショットから復元する際の所要時間が、データサイズや DB インスタンスの種類によってどのように変化するのかを調べてみました。

検証条件

検証に使う Aurora は以下の構成としました。

  • エディション : PorsgreSQL 13.5 互換
  • キャパシティータイプ : Provisioned
  • Aurora レプリカの作成 : なし
  • リージョン : ap-northeast-1 (東京)

スナップショットからのリストア(復元)にかかる時間は、データサイズまたはインスタンスクラスによって違いが出るのではと考え、以下の組み合わせで全 9 パターンをリストアすることとしました。

  • データサイズは 200GB / 400GB / 800GB の 3 パターン
  • DB インスタンスクラスは t4g.medium, r6g.large, r6g.8xlarge の3パターン

インスタンスクラスは、以下の観点で選定しました。

  • t4g.medium -> 選択可能な最も小さいもの
  • r6g.large -> 現時点でデフォルトのインスタンスクラス
  • r6g.8xlarge -> それなりの規模の商用環境などで使われる

計測にあたり、リストアにかかった時間の開始と終了は以下の定義としました。
RDS のイベントリストにはクラスタの作成開始が記録されないため、CloudTrail を利用しています。

  • リストアの開始 : CloudTrail に記録された、当該 DB インスタンスの CreateDBInstance イベントのタイムスタンプ
  • リストアの完了 : RDS のイベントに記録された、当該 DB インスタンスの "DB instance created" イベントのタイムスタンプ

検証データの作成

検証データの作成は こちら の stack overflow を参考にさせて頂きました。
1 テーブルあたり 13107200 行 (約 1,300 万行) のサンプルデータで、データサイズがほぼ 100 GB ぴったりになるため、きりの良い値で複数のデータサイズのスナップショットを用意することができました。

参考までに、下記の方のように pgbench でデータを投入し、CTAS でデータサイズをふくらませるという手段もありますね。

yohei-a.hatenablog.jp

検証結果

f:id:polar3130:20220304155614p:plain

最も所要時間が短かったのは 400GB x t4g.medium の 1,584 秒でした。
最も所要時間が長かったのは 800GB x r6g.8xlarge の 1,976 秒でした。
いずれの組み合わせも 1500 ~ 2000 秒の範囲に収まりました。

考察

検証結果から、200 ~ 800 GB程度のデータサイズでは、リストアの時間にデータサイズの大小がほぼ影響しないことがわかります。
追加で 1,200 GB x r6g.large も試しましたが、約 1,800 秒というところで他のパターンとの差はありませんでした。

また、インスタンスクラス毎の差はわずかにある(より大きいインスタンスクラスでリストアにより多くの時間を要している)ようにも見えますが、その差は大きくても 400 秒程度というところですので、実用上はこちらもほぼ差がないものと考えられます。

以上より、Aurora のリストアにかかる時間は、数百 GB 程度(おおよそ 1 TB 以下)のスナップショットであればインスタンスクラスに関わらず 30〜40 分程度で完了すると思っておいても良さそうです。

その他の気付き

参考までに、ひとつのスナップショットから複数のクラスタを一度にリストアしたところ、各クラスタの復元完了までの所要時間は上記検証の倍以上となりました。
おそらく、復元の際に行われるスナップショットからのデータ読み取りに関して I/O の制約があるものと思われます。

以下は Aurora MySQL 互換に関する AWS プレミアサポートのナレッジセンタに公開されている情報ですが、バックアップ作成時点でソース側に長時間実行されるトランザクションがある場合、リストアに通常よりも多くの時間を要することがあるようです。
商用の利用ではこうしたソースデータベース側の状態というのも影響要素になり得るかと思います。

aws.amazon.com

あくまで個人による簡易な検証結果ですが、何かのお役に立てば幸いです。

GKE 1.22 with Node Local DNS Cache で Alpine 3.13+ の名前解決に失敗する

先日、GKE の静的リリースに 1.22 がやってきました。
今回は、Node Local DNS Cache を有効化した GKE 1.22 において、Alpine を使用する際に注意しておいたほうが良い挙動とその検証結果をご紹介します。

以下の検証は、現時点の GKE 1.22 における最新の静的リリースである 1.22.6-gke.1000 を用いて行っています。

Node Local DNS Cache の設定

GKE の Node Local DNS Cache には、アップストリームに基づいた CoreDNS ベースのイメージが使われており、一応 ”-gke.” のようなサフィックスが付いていますがほぼ同じような挙動となっています。
設定は GKE 独自のものがコントロールプレーンのバージョンに応じて自動的に展開される仕組みとなっています。
ちなみに、GKE の Node Local DNS Cache は CoreDNS をベースとしている一方、GKE クラスタのデフォルト DNS は kube-dns となっています(最近は Cloud DNS を利用する選択肢も出てきていますね)。

cloud.google.com

github.com

GKE の Node Local DNS Cache の設定は、"kube-system" Namespace の "node-local-dns" という ConfigMap の "Corefile" の値として定義されています。
GKE 1.22 では、以下のように AAAA クエリ (IPv6) に関する設定が追加されました。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.3", GitCommit:"816c97ab8cff8a1c72eccca1026f7820e93e0d25", GitTreeState:"clean", BuildDate:"2022-01-25T21:25:17Z", GoVersion:"go1.17.6", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.6-gke.1000", GitCommit:"5595443086b60d8c5c62342fadc2d4fda9c793e8", GitTreeState:"clean", BuildDate:"2022-02-02T09:35:41Z", GoVersion:"go1.16.12b7", Compiler:"gc", Platform:"linux/amd64"}

$ kubectl get pods -n kube-system
NAME                                                           READY   STATUS    RESTARTS   AGE
event-exporter-gke-5877b595cd-6cw96                            2/2     Running   0          7m
fluentbit-gke-lpvn6                                            2/2     Running   0          5m10s
fluentbit-gke-nsdwj                                            2/2     Running   0          5m9s
fluentbit-gke-z7l9p                                            2/2     Running   0          5m9s
gke-metrics-agent-bmb52                                        1/1     Running   0          5m10s
gke-metrics-agent-fq5gw                                        1/1     Running   0          5m9s
gke-metrics-agent-nrqvv                                        1/1     Running   0          5m9s
konnectivity-agent-7dbd649949-6vfcp                            1/1     Running   0          5m2s
konnectivity-agent-7dbd649949-h864p                            1/1     Running   0          5m2s
konnectivity-agent-7dbd649949-s7lx9                            1/1     Running   0          6m50s
konnectivity-agent-autoscaler-698b6d8768-gq2dx                 1/1     Running   0          6m46s
kube-dns-6bb46c7474-4vfqf                                      4/4     Running   0          7m14s
kube-dns-6bb46c7474-wlj7p                                      4/4     Running   0          5m1s
kube-dns-autoscaler-f4d55555-btzmw                             1/1     Running   0          7m12s
kube-proxy-gke-cluster-1-22-clone-default-pool-058fd934-0pvn   1/1     Running   0          4m18s
kube-proxy-gke-cluster-1-22-clone-default-pool-058fd934-g67w   1/1     Running   0          4m38s
kube-proxy-gke-cluster-1-22-clone-default-pool-058fd934-nbrj   1/1     Running   0          4m20s
l7-default-backend-69fb9fd9f9-vsqf2                            1/1     Running   0          6m42s
metrics-server-v0.4.5-bbb794dcc-87ttk                          2/2     Running   0          4m44s
node-local-dns-8f2mf                                           1/1     Running   0          5m7s
node-local-dns-sf8l6                                           1/1     Running   0          5m7s
node-local-dns-t7v9x                                           1/1     Running   0          5m7s
pdcsi-node-tpxh6                                               2/2     Running   0          5m8s
pdcsi-node-wzgr8                                               2/2     Running   0          5m9s
pdcsi-node-z4r68                                               2/2     Running   0          5m10s

$ kubectl describe configmaps node-local-dns -n kube-system
Name:         node-local-dns
Namespace:    kube-system
Labels:       addonmanager.kubernetes.io/mode=Reconcile
Annotations:  app.kubernetes.io/created-by: kube-addon-manager

Data
====
Corefile:
----
cluster.local:53 {
    errors

    template ANY AAAA {
      rcode NXDOMAIN
    }

    cache {
            success 9984 30
            denial 9984 5
    }
    reload
    loop

    bind 169.254.20.10 10.120.0.10
    health 169.254.20.10:8080

    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
            expire 1s
    }
    prometheus :9253
    }
in-addr.arpa:53 {
    errors
    cache 30
    reload
    loop

    bind 169.254.20.10 10.120.0.10

    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
            expire 1s
    }
    prometheus :9253
    }
ip6.arpa:53 {
    errors
    cache 30
    reload
    loop

    bind 169.254.20.10 10.120.0.10

    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
            expire 1s
    }
    prometheus :9253
    }
.:53 {
    errors

    template ANY AAAA {
      rcode NXDOMAIN
    }

    cache 30
    reload
    loop

    bind 169.254.20.10 10.120.0.10

    forward . __PILLAR__UPSTREAM__SERVERS__ {
            force_tcp
    }
    prometheus :9253
    }


BinaryData
====

Events:  <none>

ひとつ前の GKE 1.21 (1.21.6-gke.1500) と比較してみるとわかりやすいかと思います。

$ kubectl version
Client Version: version.Info{Major:"1", Minor:"23", GitVersion:"v1.23.3", GitCommit:"816c97ab8cff8a1c72eccca1026f7820e93e0d25", GitTreeState:"clean", BuildDate:"2022-01-25T21:25:17Z", GoVersion:"go1.17.6", Compiler:"gc", Platform:"linux/amd64"}
Server Version: version.Info{Major:"1", Minor:"21", GitVersion:"v1.21.6-gke.1500", GitCommit:"7ce0f9f1939dfc1aee910732e84cba03840df91e", GitTreeState:"clean", BuildDate:"2021-11-17T09:30:26Z", GoVersion:"go1.16.9b7", Compiler:"gc", Platform:"linux/amd64"}
WARNING: version difference between client (1.23) and server (1.21) exceeds the supported minor version skew of +/-1

$ kubectl get pods -n kube-system
NAME                                                     READY   STATUS    RESTARTS   AGE
event-exporter-gke-5479fd58c8-w889w                      2/2     Running   0          5d6h
fluentbit-gke-8zj4h                                      2/2     Running   0          5d6h
fluentbit-gke-dwwjk                                      2/2     Running   0          5d6h
fluentbit-gke-h2wp2                                      2/2     Running   0          5d6h
gke-metrics-agent-72h26                                  1/1     Running   0          5d6h
gke-metrics-agent-dxsht                                  1/1     Running   0          5d6h
gke-metrics-agent-v27lf                                  1/1     Running   0          5d6h
konnectivity-agent-5b9bf44468-kttgm                      1/1     Running   0          5d6h
konnectivity-agent-5b9bf44468-l74sx                      1/1     Running   0          5d6h
konnectivity-agent-5b9bf44468-tmmzs                      1/1     Running   0          5d6h
konnectivity-agent-autoscaler-5c49cb58bb-cdlk8           1/1     Running   0          5d6h
kube-dns-697dc8fc8b-8v5nl                                4/4     Running   0          5d6h
kube-dns-697dc8fc8b-rzgcl                                4/4     Running   0          5d6h
kube-dns-autoscaler-844c9d9448-v6gx2                     1/1     Running   0          5d6h
kube-proxy-gke-cluster-1-21-default-pool-2a39355d-ghz4   1/1     Running   0          5d6h
kube-proxy-gke-cluster-1-21-default-pool-2a39355d-hw8r   1/1     Running   0          5d6h
kube-proxy-gke-cluster-1-21-default-pool-2a39355d-xl2n   1/1     Running   0          5d6h
l7-default-backend-69fb9fd9f9-wq98k                      1/1     Running   0          5d6h
metrics-server-v0.4.4-857776bc9c-v5296                   2/2     Running   0          5d6h
node-local-dns-j2r7v                                     1/1     Running   0          5d6h
node-local-dns-pzn75                                     1/1     Running   0          5d6h
node-local-dns-vcwr2                                     1/1     Running   0          5d6h
pdcsi-node-5d99x                                         2/2     Running   0          5d6h
pdcsi-node-g47xg                                         2/2     Running   0          5d6h
pdcsi-node-n6btn                                         2/2     Running   0          5d6h

$ kubectl describe configmaps node-local-dns -n kube-system
Name:         node-local-dns
Namespace:    kube-system
Labels:       addonmanager.kubernetes.io/mode=Reconcile
Annotations:  <none>

Data
====
Corefile:
----
cluster.local:53 {
    errors
    cache {
            success 9984 30
            denial 9984 5
    }
    reload
    loop

    bind 169.254.20.10 10.36.16.10
    health 169.254.20.10:8080

    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
            expire 1s
    }
    prometheus :9253
    }
in-addr.arpa:53 {
    errors
    cache 30
    reload
    loop

    bind 169.254.20.10 10.36.16.10

    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
            expire 1s
    }
    prometheus :9253
    }
ip6.arpa:53 {
    errors
    cache 30
    reload
    loop

    bind 169.254.20.10 10.36.16.10

    forward . __PILLAR__CLUSTER__DNS__ {
            force_tcp
            expire 1s
    }
    prometheus :9253
    }
.:53 {
    errors
    cache 30
    reload
    loop

    bind 169.254.20.10 10.36.16.10

    forward . __PILLAR__UPSTREAM__SERVERS__ {
            force_tcp
    }
    prometheus :9253
    }


BinaryData
====

Events:  <none>

設定追加の契機

上述のように GKE 1.22 の Node Local DNS Cache に設定が追加された背景には、アップストリームにおけるプラグインの追加が関係していると思われます。
GKE 1.21 と GKE 1.22 のそれぞれにおける Node Local DNS Cache のバージョンは以下のとおりです。

  • GKE 1.21 : 1.21.1-gke.0
  • GKE 1.22 : 1.21.4-gke.0

以下の URL から、実際に GitHub でこのバージョン間の差分を確認することができます。

github.com

ふたつのバージョンの差はパッチバージョンレベルのみとなっていますが、この差分に設定追加のきっかけと思われるコミットが含まれています。
たどってみると、差分に含まれる以下のコミットで Node Local DNS Cache に dns64 プラグインが追加されていることがわかります。
このアップストリームの変更をきっかけに、GKE 1.22 の Node Local DNS Cache に AAAA クエリ (IPv6) の設定が追加された可能性が考えられそうです。

github.com

CoreDNS の dns64 プラグインの説明はこちらにあります。

coredns.io

変更の影響

今回の変更が何をもたらすのでしょうか。

タイトルのとおりですが、 Node Local DNS Cache を有効化した GKE 1.22 において、Alpine 3.13+ のコンテナが名前解決に失敗するようになる という影響が生じます。

DNS クライアントの実装によっては、A + AAAA ペアクエリの一方で NXDOMAIN が返却された場合にクエリ全体を失敗とみなすことがあり、Alpine 3.13+ が名前解決に使用している musl libc 1.2 系はまさにこのような挙動を取ります。
Node Local DNS Cache を有効化した GKE 1.22 (1.22.6-gke.1000) における実際の挙動は次のようになります。

$ kubectl run alpine313 --image=alpine:3.13 --tty -i sh
If you don't see a command prompt, try pressing enter.

/ # nslookup www.google.com
Server:         10.120.0.10
Address:        10.120.0.10:53

** server can't find www.google.com: NXDOMAIN

Non-authoritative answer:
Name:   www.google.com
Address: 142.250.148.99
Name:   www.google.com
Address: 142.250.148.103
Name:   www.google.com
Address: 142.250.148.106
Name:   www.google.com
Address: 142.250.148.147
Name:   www.google.com
Address: 142.250.148.104
Name:   www.google.com
Address: 142.250.148.105

/ # wget www.google.com
wget: bad address 'www.google.com'

/ #

nslookup の結果から、AAAA クエリに対して NXDOMAIN が返却されていることがわかります。
これは上述の Node Local DNS Cache に加えられた設定変更によるものです。
Node Local DNS Cache はこの設定により、AAAA クエリに対して無条件に NXDOMAIN を返却しています。

wget の結果から、名前解決が失敗し、"bad address" のエラーとなっていることがわかります。
詳細は後述しますが、このとき musl libc は A + AAAA ペアクエリの一方 (ここでは AAAA クエリ) で NXDOMAIN が返却されたことにより、もう一方 (A クエリ) が成功する状況であってもクエリ全体を失敗とみなし、名前解決に失敗しています。

ちなみに、GKE 1.21 (+ Node Local DNS Cache) 、または Node Local DNS Cache なしの GKE 1.22 であれば、Alpine 3.13+ のコンテナも名前解決に成功します。
以下に Node Local DNS Cache なしの GKE 1.22 の環境における実行結果を示します。

$ kubectl run alpine313 --image=alpine:3.13 --tty -i sh
If you don't see a command prompt, try pressing enter.

/ # nslookup www.google.com
Server:         10.93.0.10
Address:        10.93.0.10:53

Non-authoritative answer:
Name:   www.google.com
Address: 173.194.197.104
Name:   www.google.com
Address: 173.194.197.147
Name:   www.google.com
Address: 173.194.197.106
Name:   www.google.com
Address: 173.194.197.99
Name:   www.google.com
Address: 173.194.197.103
Name:   www.google.com
Address: 173.194.197.105

Non-authoritative answer:
Name:   www.google.com
Address: 2607:f8b0:4001:c5a::69
Name:   www.google.com
Address: 2607:f8b0:4001:c5a::67
Name:   www.google.com
Address: 2607:f8b0:4001:c5a::6a
Name:   www.google.com
Address: 2607:f8b0:4001:c5a::63

/ # wget www.google.com
Connecting to www.google.com (173.194.192.104:80)
saving to 'index.html'
index.html           100% |**************************************************************************************************************************| 14119  0:00:00 ETA
'index.html' saved

/ #

NXDOMAIN

上述の挙動の差を生んでいる NXDOMAIN について少し掘り下げてみましょう。
NXDOMAIN の定義を詳細化している RFC 8020 によれば、NXDOMAIN は「その名前にいかなる型のレコードも存在しない」ことを意味します。
アドレスファミリが AF_UNSPEC の場合、名前解決のために A クエリと AAAA クエリで計 2 回のクエリが発行されることになりますが、どちらか 1 回目のクエリで NXDOMAIN によってその名前に対するレコードの非存在を確認できれば、クエリの試行回数を最適化することができます。

www.rfc-editor.org

一方、「その名前に他の型のレコード、もしくはサブドメインが存在し得る」という場合には、NXDOMAIN ではなく NODATA を返却する必要があります(NODATA は疑似 RCODE であり、実際には NOERROR + 空の Answer セクションの返却となります)。
これは RFC 8020 の 3.1. Updates to RFC 1034 で定義されています。

This document clarifies possible ambiguities in [RFC1034] that did not clearly distinguish Empty Non-Terminal (ENT) names ([RFC7719]) from nonexistent names, and it refers to subsequent documents that do. ENTs are nodes in the DNS that do not have resource record sets associated with them but have descendant nodes that do. The correct response to ENTs is NODATA (i.e., a response code of NOERROR and an empty answer section). Additional clarifying language on these points is provided in Section 7.16 of [RFC2136] and in Sections 2.2.2 and 2.2.3 of [RFC4592].

musl libc の作者であり現在も主要メンテナである Rich Felker 氏の下記 Issue のコメントでも、この挙動とセマンティクスの対応関係が言及されています。

gitlab.alpinelinux.org

したがって、musl libc 1.2 系の「A + AAAA ペアクエリの一方が成功する (Answer セクションで有効な IP アドレスの回答が得られる) 状況であったとしても、もう一方で NXDOMAIN が返却された場合にクエリ全体を失敗とみなす挙動」は、特殊なものではなく、DNS のセマンティクスに沿った挙動であると言えそうです。
一方、他の DNS クライアントでは、ペアクエリの一方で NXDOMAIN が返却されたとしても、もう一方のクエリで有効な IP アドレスが得られればこれを名前解決の結果とする実装もあるようです。

ライブラリによって異なる名前解決の挙動

Alpine は、軽量な Linux ディストリビューションとして GKE などのコンテナ実行環境でよく使われています。
しかし、Alpine の元々の用途は組み込み系であり、BusyBox と musl libc をベースとしているため、他の Linux ディストリビューションの多くが採用している glibc などとは実装が異なります。
musl libc, glibc はそれぞれのライブラリにネットワークの共通的な機能を備えており、どちらのライブラリを採用しているかで名前解決の際の挙動が変わってくるということになります。

musl libc, glibc のいずれでも構成可能な、Gentoo Linux を使って動作を確認してみます。

まずは Gentoo (5.10.90+) + musl libc 1.2.2 (musl-1.2.2-r7) の場合です。

$ kubectl run gentoo-musl --image=gentoo/stage3:amd64-musl-20220224 --tty -i sh
If you don't see a command prompt, try pressing enter.

sh-5.1# emerge --info
!!! Section 'gentoo' in repos.conf has location attribute set to nonexistent directory: '/var/db/repos/gentoo'
!!! Invalid Repository Location (not a dir): '/var/db/repos/gentoo'
WARNING: One or more repositories have missing repo_name entries:

        /var/db/repos/gentoo/profiles/repo_name

NOTE: Each repo_name entry should be a plain text file containing a
unique name for the repository on the first line.


!!! It seems /run is not mounted. Process management may malfunction.
Portage 3.0.30 (python 3.9.9-final-0, unavailable, gcc-11.2.0, musl-1.2.2-r7, 5.10.90+ x86_64)
=================================================================
System uname: Linux-5.10.90+-x86_64-Intel-R-_Xeon-R-_CPU_@_2.20GHz-with-libc
KiB Mem:     4026068 total,    131520 free
KiB Swap:          0 total,         0 free
sh bash 5.1_p16
ld GNU ld (Gentoo 2.37_p1 p2) 2.37
dev-lang/python:          3.9.9-r1::gentoo, 3.10.0_p1-r1::gentoo
sys-devel/autoconf:       2.71-r1::gentoo
sys-devel/automake:       1.16.4::gentoo
sys-devel/binutils:       2.37_p1-r2::gentoo
sys-devel/libtool:        2.4.6-r6::gentoo
sys-kernel/linux-headers: 5.15-r3::gentoo (virtual/os-headers)
Repositories:

ACCEPT_LICENSE="* -@EULA"
CFLAGS="-O2 -pipe"
CHOST="x86_64-gentoo-linux-musl"
CONFIG_PROTECT="/etc /usr/share/gnupg/qualified.txt"
CONFIG_PROTECT_MASK="/etc/ca-certificates.conf /etc/env.d /etc/gentoo-release /etc/sandbox.d /etc/terminfo"
CXXFLAGS="-O2 -pipe"
DISTDIR="/var/cache/distfiles"
FEATURES="assume-digests binpkg-docompress binpkg-dostrip binpkg-logs buildpkg-live config-protect-if-modified distlocks ebuild-locks fixlafiles ipc-sandbox merge-sync multilib-strict network-sandbox news parallel-fetch pid-sandbox preserve-libs protect-owned qa-unresolved-soname-deps sandbox sfperms strict unknown-features-warn unmerge-logs unmerge-orphans userfetch userpriv usersandbox usersync xattr"
GENTOO_MIRRORS="http://distfiles.gentoo.org"
PKGDIR="/var/cache/binpkgs"
PORTAGE_TMPDIR="/var/tmp"
USE=""
Unset:  ACCEPT_KEYWORDS, EMERGE_DEFAULT_OPTS, ENV_UNSET, PORTAGE_BINHOST, PORTAGE_BUNZIP2_COMMAND

sh-5.1# wget www.google.com
--2022-02-28 00:10:30--  http://www.google.com/
Resolving www.google.com... failed: Name does not resolve.
wget: unable to resolve host address 'www.google.com'

sh-5.1# nslookup www.google.com
sh: nslookup: command not found

sh-5.1# emerge --sync
!!! It seems /run is not mounted. Process management may malfunction.
>>> Syncing repository 'gentoo' into '/var/db/repos/gentoo'...
 * Using keys from /usr/share/openpgp-keys/gentoo-release.asc
 * Refreshing keys via WKD ...                                                                                                                                     [ ok ]
!!! getaddrinfo failed for 'rsync.gentoo.org': [Errno -2] Name does not resolve
>>> Starting rsync with rsync://rsync.gentoo.org/gentoo-portage...
rsync: getaddrinfo: rsync.gentoo.org 873: Name does not resolve
rsync error: error in socket IO (code 10) at clientserver.c(137) [Receiver=3.2.3]
>>> Retrying...
!!! Exhausted addresses for rsync.gentoo.org

 * IMPORTANT: 12 news items need reading for repository 'gentoo'.
 * Use eselect news read to view new items.


Action: sync for repo: gentoo, returned code = 1


sh-5.1#

Alpine のときと同様、wget に失敗します。
なお、nslookup はデフォルトでは含まれていないため、Portage を使ってインストールする必要がありますが、名前解決に失敗するため emerge コマンドが機能しません。

次は Gentoo (5.10.90+) + glibc (glibc-2.33-r7) の場合です。

$ kubectl run gentoo-glibc --image=gentoo/stage3:amd64-systemd-20220224 --tty -i sh
If you don't see a command prompt, try pressing enter.

sh-5.1# emerge --info
!!! Section 'gentoo' in repos.conf has location attribute set to nonexistent directory: '/var/db/repos/gentoo'
!!! Invalid Repository Location (not a dir): '/var/db/repos/gentoo'
WARNING: One or more repositories have missing repo_name entries:

        /var/db/repos/gentoo/profiles/repo_name

NOTE: Each repo_name entry should be a plain text file containing a
unique name for the repository on the first line.


!!! It seems /run is not mounted. Process management may malfunction.
Portage 3.0.30 (python 3.9.9-final-0, unavailable, gcc-11.2.0, glibc-2.33-r7, 5.10.90+ x86_64)
=================================================================
System uname: Linux-5.10.90+-x86_64-Intel-R-_Xeon-R-_CPU_@_2.20GHz-with-glibc2.33
KiB Mem:     4026068 total,    128684 free
KiB Swap:          0 total,         0 free
sh bash 5.1_p16
ld GNU ld (Gentoo 2.37_p1 p2) 2.37
dev-lang/python:          3.9.9-r1::gentoo, 3.10.0_p1-r1::gentoo
sys-devel/autoconf:       2.71-r1::gentoo
sys-devel/automake:       1.16.4::gentoo
sys-devel/binutils:       2.37_p1-r2::gentoo
sys-devel/libtool:        2.4.6-r6::gentoo
sys-kernel/linux-headers: 5.15-r3::gentoo (virtual/os-headers)
Repositories:

ACCEPT_LICENSE="* -@EULA"
CFLAGS="-O2 -pipe"
CONFIG_PROTECT="/etc /usr/share/gnupg/qualified.txt"
CONFIG_PROTECT_MASK="/etc/ca-certificates.conf /etc/env.d /etc/gentoo-release /etc/sandbox.d /etc/terminfo"
CXXFLAGS="-O2 -pipe"
DISTDIR="/var/cache/distfiles"
FEATURES="assume-digests binpkg-docompress binpkg-dostrip binpkg-logs buildpkg-live config-protect-if-modified distlocks ebuild-locks fixlafiles ipc-sandbox merge-sync multilib-strict network-sandbox news parallel-fetch pid-sandbox preserve-libs protect-owned qa-unresolved-soname-deps sandbox sfperms strict unknown-features-warn unmerge-logs unmerge-orphans userfetch userpriv usersandbox usersync xattr"
GENTOO_MIRRORS="http://distfiles.gentoo.org"
PKGDIR="/var/cache/binpkgs"
PORTAGE_TMPDIR="/var/tmp"
USE=""
Unset:  ACCEPT_KEYWORDS, CHOST, EMERGE_DEFAULT_OPTS, ENV_UNSET, PORTAGE_BINHOST, PORTAGE_BUNZIP2_COMMAND

sh-5.1# wget www.google.com
--2022-02-28 00:17:16--  http://www.google.com/
Resolving www.google.com... 108.177.111.106, 108.177.111.103, 108.177.111.105, ...
Connecting to www.google.com|108.177.111.106|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified [text/html]
Saving to: 'index.html'

index.html                                     [ <=>                                                                                   ]  13.91K  --.-KB/s    in 0s

2022-02-28 00:17:16 (36.6 MB/s) - 'index.html' saved [14243]

sh-5.1# emerge --sync
... (略) ...

sh-5.1# emerge net-dns/bind-tools -q
...

>>> Verifying ebuild manifests
>>> Emerging (1 of 1) net-dns/bind-tools-9.16.22::gentoo
>>> Installing (1 of 1) net-dns/bind-tools-9.16.22::gentoo
>>> Recording net-dns/bind-tools in "world" favorites file...

...

sh-5.1# nslookup www.google.com
Server:         10.120.0.10
Address:        10.120.0.10#53

Non-authoritative answer:
Name:   www.google.com
Address: 173.194.194.104
Name:   www.google.com
Address: 173.194.194.147
Name:   www.google.com
Address: 173.194.194.105
Name:   www.google.com
Address: 173.194.194.103
Name:   www.google.com
Address: 173.194.194.99
Name:   www.google.com
Address: 173.194.194.106
** server can't find www.google.com: NXDOMAIN

sh-5.1# 

こちらは wget に成功しています。
以上から、musl libc と glibc で名前解決に関する挙動が異なっていることがわかります。

バージョンが 3.12 までの Alpine であれば、GKE 1.22 + Node Local DNS Cache の環境であっても名前解決に成功します。
3.12 までの Alpine では musl libc 1.1 系が利用されており、1.2 系とは実装が異なるためです。
Alpine のリリースアナウンスでも 3.13 から musl libc 1.2 系 に変更されたことが記載されています。

alpinelinux.org

musl libc のコミットを追いかけてみると、1.2.1 で A + AAAA ペアクエリに対する挙動が変更されていることを確認できます。
この変更は、初版の 1.2.0 にはバックポートされなかったようです。

git.musl-libc.org

なお、musl libc では getaddrinfo のデフォルトのアドレスファミリが AF_UNSPEC となっているため、変更しない限りは A + AAAA のペアクエリになります。

git.musl-libc.org

回避手段

では GKE 1.22 において、Alpine 3.13+ のコンテナが名前解決を行えるようにするにはどうすればよいでしょうか。
以下のような手段が考えられます。

  1. クラスタの Node Local DNS Cache を無効化する
  2. 該当するコンテナの resolv.conf を書き換えて Node Local DNS Cache を迂回する
  3. Alpine の使用をやめる

いずれもクラスタやアプリケーションの構成変更が必要になるため、GKE ユーザとしては対処が悩ましいところですね。

1 はキャッシュが効かなくなるため性能面の影響が懸念されるでしょう。
2 は設定の変更対象が多いと大変ですし、その他の NW 要件との干渉で適切に迂回できない場合も考えられます。
3 はユーザアプリケーションなら検討の余地があるかもしれませんが、ミドルウェアのコンテナが Alpine 3.13+ を使用している場合には対処が困難です。

Node Local DNS Cache の Corefile の設定を書き換えることも可能ですが、kube-system Namespace 内のリソースはコントロールプレーンの一部として Google 側が管理・変更を行うため、手動で書き換えたとしても再び上書きされてしまう可能性があります。
GKE 1.22 の Node Local DNS Cache のデフォルト設定が、AAAA クエリに対して NODATA で応答する、もしくは kube-dns / Cloud DNS への再帰問い合わせを許可してくれるようになれば、根本的な解決となるかもしれません。

なお、GKE Autopilot では Node Local DNS Cache が強制的に有効化されるため、Autopilot 環境の Alpine ユーザは軒並みこの影響を受けることになると思われます。
こちらは resolv.conf で迂回するか、Alpine の使用をやめるくらいしか対応方法がなさそうですね。

まとめ

以下の相性問題により、GKE 1.22 で Node Local DNS Cache を有効化すると 3.13 以上の Alpine が名前解決に失敗することを確認しました。

  • GKE 1.22 の Node Local DNS Cache (1.21.4-gke.0) は AAAA に対して一律 NXDOMAIN を返す設定が追加されている
  • Alpine 3.13+ は musl libc 1.2.1+ を使用しており、A + AAAA ペアクエリのいずれかで NXDOMAIN が返るとクエリ全体が失敗したものとして扱われる

お読み頂いた方のお役に立てば幸いです。

参考

GKE ノードプールのアップグレードを中断(キャンセル)する

GKE ノードプールのアップグレードがキャンセルできるようになっていたので試してみました。
現状はベータ版として提供されている機能になります。
公式ドキュメントの該当箇所はこちら。

cloud.google.com

検証

GKE では、アップグレードに限らずクラスタに対するオペレーション(作成・削除・アップグレードなど)はその都度オペレーション ID が割り振られるようになっており、gcloud container operations list コマンドで確認することができます。

cloudshell:~ $ gcloud container operations list

...

NAME: operation-1642596393580-28fce564
TYPE: CREATE_CLUSTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T12:46:33.580352532Z
END_TIME: 2022-01-19T12:50:24.97742246Z

...

検証対象のクラスタ sample-cluster の作成オペレーションが完了していることがわかります。

今回は、コントロールプレーン・ノードプールともに 1.21.5-gke.1302 から 1.21.6-gke.1500 にアップグレードします。
ノードプールはノード数 3 の状態で開始します。
ノードプールのアップグレードを行う前に、まずはコントロールプレーンがノードプールのアップグレード先のバージョンをサポートできるようアップグレードしておきます。

f:id:polar3130:20220120120422p:plain

gcloud container operations list コマンドでオペレーションの状態を確認します。

cloudshell:~ $ gcloud container operations list

...

NAME: operation-1642596393580-28fce564
TYPE: CREATE_CLUSTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T12:46:33.580352532Z
END_TIME: 2022-01-19T12:50:24.97742246Z

NAME: operation-1642599713836-a095ee51
TYPE: UPGRADE_MASTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: RUNNING
START_TIME: 2022-01-19T13:41:53.836300031Z
END_TIME:

...

コントロールプレーンのアップグレードが開始されていることがわかります。
今回検証するキャンセルの機能はノードプールのアップグレードが対象とされていますが、試しにこの状態でもオペレーションのキャンセルをリクエストしてみます。

GKE クラスタに対するオペレーションのキャンセルはベータ機能であるため、gcloud beta コマンドを使います。
gcloud beta container operations cancel コマンドのリファレンスは以下で確認することができます。

cloud.google.com

cloudshell:~ $ gcloud beta container operations cancel operation-1642599713836-a095ee51
Are you sure you want to cancel operation operation-1642599713836-a095ee51?

Do you want to continue (Y/n)?  Y

ERROR: (gcloud.beta.container.operations.cancel) INVALID_ARGUMENT: Only node upgrade operations can be cancelled.

エラーになりました。
メッセージにもあるとおり、やはりノードプールのアップグレードのみがサポートされていることがわかります。

コントロールプレーンのアップグレードが完了したのち、ノードプールのアップグレードを行います。

f:id:polar3130:20220120120429p:plain

gcloud container operations list コマンドでオペレーションの状態を確認します。

cloudshell:~ $ gcloud container operations list

...

NAME: operation-1642596393580-28fce564
TYPE: CREATE_CLUSTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T12:46:33.580352532Z
END_TIME: 2022-01-19T12:50:24.97742246Z

NAME: operation-1642599713836-a095ee51
TYPE: UPGRADE_MASTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T13:41:53.836300031Z
END_TIME: 2022-01-19T14:06:04.341528233Z

NAME: operation-1642601615896-46119ad0
TYPE: UPGRADE_NODES
LOCATION: asia-northeast1
TARGET: default-pool
STATUS_MESSAGE:
STATUS: RUNNING
START_TIME: 2022-01-19T14:13:35.896076128Z
END_TIME:

...

ノードのアップグレードは正常に開始されているようです。
では、一部のノードのアップグレードが完了したところを見計らってキャンセルしてみます。
コンソールではノードプールが以下のような状態となっていました。

f:id:polar3130:20220120120437p:plain

ここで、先程と同様に gcloud beta container operations cancel コマンドを実行します。

cloudshell:~ $ gcloud beta container operations cancel operation-1642601615896-46119ad0
Are you sure you want to cancel operation operation-1642601615896-46119ad0?

Do you want to continue (Y/n)?  Y

Cancelation of operation operation-1642601615896-46119ad0 has been requested. Please use gcloud container operations describe operation-1642601615896-46119ad0 to check if the operation has been canceled successfully.
detail: 'Updating default-pool, done with 1 out of 3 nodes (33.3%): 1 being processed'
name: operation-1642601615896-46119ad0
operationType: UPGRADE_NODES
progress:
  metrics:
  - intValue: '3'
    name: NODES_TOTAL
  - intValue: '0'
    name: NODES_FAILED
  - intValue: '1'
    name: NODES_COMPLETE
  - intValue: '1'
    name: NODES_DONE
  - intValue: '0'
    name: NODE_PDB_DELAY_SECONDS
selfLink: https://container.googleapis.com/v1beta1/projects/************/locations/asia-northeast1/operations/operation-1642601615896-46119ad0
startTime: '2022-01-19T14:13:35.896076128Z'
status: ABORTING
targetLink: https://container.googleapis.com/v1beta1/projects/************/locations/asia-northeast1/clusters/sample-cluster/nodePools/default-pool
zone: asia-northeast1

キャンセルのリクエストが受け付けられました。
detail の内容はコンソールで確認したノードプールの状態と一致しています。
この時点では status が ABORTING となっており、キャンセルが成否を問わずリクエストの受付が完了した時点でコマンドへの応答が行われていることがわかります。

メッセージの指示にあるとおり、キャンセルの成否は別途 gcloud container operations describe コマンドで確認する必要があります。
キャンセルのリクエストをかけたのち間を置かずに確認しましたが、既にキャンセルのリクエストは成功していました。
結果は下記のように出力されました。

cloudshell:~ $ gcloud container operations describe operation-1642601615896-46119ad0
clusterConditions:
- canonicalCode: CANCELLED
  message: 'Operation was aborted: operation-1642601615896-46119ad0.'
detail: 'Operation was aborted: operation-1642601615896-46119ad0.'
endTime: '2022-01-19T14:21:01.520708481Z'
error:
  code: 1
  message: 'Operation was aborted: operation-1642601615896-46119ad0.'
name: operation-1642601615896-46119ad0
operationType: UPGRADE_NODES
progress:
  metrics:
  - intValue: '3'
    name: NODES_TOTAL
  - intValue: '0'
    name: NODES_FAILED
  - intValue: '2'
    name: NODES_COMPLETE
  - intValue: '2'
    name: NODES_DONE
  - intValue: '0'
    name: NODE_PDB_DELAY_SECONDS
selfLink: https://container.googleapis.com/v1/projects/************/locations/asia-northeast1/operations/operation-1642601615896-46119ad0
startTime: '2022-01-19T14:13:35.896076128Z'
status: DONE
statusMessage: 'Operation was aborted: operation-1642601615896-46119ad0.'
targetLink: https://container.googleapis.com/v1/projects/************/locations/asia-northeast1/clusters/sample-cluster/nodePools/default-pool
zone: asia-northeast1

gcloud beta container operations list コマンドでも確認してみます。

cloudshell:~ $ gcloud container operations list

...

NAME: operation-1642596393580-28fce564
TYPE: CREATE_CLUSTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T12:46:33.580352532Z
END_TIME: 2022-01-19T12:50:24.97742246Z

NAME: operation-1642599713836-a095ee51
TYPE: UPGRADE_MASTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T13:41:53.836300031Z
END_TIME: 2022-01-19T14:06:04.341528233Z

NAME: operation-1642601615896-46119ad0
TYPE: UPGRADE_NODES
LOCATION: asia-northeast1
TARGET: default-pool
STATUS_MESSAGE: Operation was aborted: operation-1642601615896-46119ad0.
STATUS: DONE
START_TIME: 2022-01-19T14:13:35.896076128Z
END_TIME: 2022-01-19T14:21:01.520708481Z

NAME: operation-1642602132989-39493995
TYPE: UPDATE_CLUSTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T14:22:12.989734846Z
END_TIME: 2022-01-19T14:22:13.741871596Z

...

キャンセルが完了していることが確認できました。
アップグレードは最後まで完了しなかったため、コンソール上の当該ノードプールはアップグレード前のバージョンとして表示されています。

f:id:polar3130:20220120120441p:plain

しかし、実際には一部のノードのアップグレードが完了していたため、ノードのバージョンが混在した状態となっています。

cloudshell:~ $ kubectl get node
NAME                                            STATUS                     ROLES    AGE    VERSION
gke-sample-cluster-default-pool-4a3a874a-0mqn   Ready                      <none>   16m    v1.21.6-gke.1500
gke-sample-cluster-default-pool-7a3668ce-5tb7   Ready                      <none>   100m   v1.21.5-gke.1302
gke-sample-cluster-default-pool-8005ab02-2iq1   Ready                      <none>   10m    v1.21.6-gke.1500
gke-sample-cluster-default-pool-8005ab02-79ff   Ready,SchedulingDisabled   <none>   101m   v1.21.5-gke.1302

コンソールでは以下のように見えています。

f:id:polar3130:20220120120447p:plain

ここで、元のノードプールのサイズは 3 でしたが、キャンセル後のノード数は 4 に増えています。
これはサージアップグレードを有効化(max surge: 1)していたためで、アップグレード中に新しいノードを追加し、そのセットアップが完了して古いノードを cordon したあたりでキャンセルが受け付けられたようです。

f:id:polar3130:20220120120454p:plain

ではこの状態でもう一度アップグレードをリクエストするとどうなるか試してみます。
先ほどと同じようにノードプールのアップグレードを開始します。 4 台のノードに対してアップグレードのプロセスを開始していることが確認できます。

f:id:polar3130:20220120120502p:plain

gcloud beta container operations list コマンドでも確認してみます。

cloudshell:~ $ gcloud container operations list

...

NAME: operation-1642596393580-28fce564
TYPE: CREATE_CLUSTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T12:46:33.580352532Z
END_TIME: 2022-01-19T12:50:24.97742246Z

NAME: operation-1642599713836-a095ee51
TYPE: UPGRADE_MASTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T13:41:53.836300031Z
END_TIME: 2022-01-19T14:06:04.341528233Z

NAME: operation-1642601615896-46119ad0
TYPE: UPGRADE_NODES
LOCATION: asia-northeast1
TARGET: default-pool
STATUS_MESSAGE: Operation was aborted: operation-1642601615896-46119ad0.
STATUS: DONE
START_TIME: 2022-01-19T14:13:35.896076128Z
END_TIME: 2022-01-19T14:21:01.520708481Z

NAME: operation-1642602132989-39493995
TYPE: UPDATE_CLUSTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T14:22:12.989734846Z
END_TIME: 2022-01-19T14:22:13.741871596Z

NAME: operation-1642603081158-8a2f2134
TYPE: UPGRADE_NODES
LOCATION: asia-northeast1
TARGET: default-pool
STATUS_MESSAGE:
STATUS: RUNNING
START_TIME: 2022-01-19T14:38:01.158573778Z
END_TIME:

...

その後、アップグレードが完了すると、コンソールでは以下のように表示されました。

f:id:polar3130:20220120120508p:plain

改めて gcloud beta container operations list コマンドでも確認してみます。

cloudshell:~ $ gcloud container operations list

NAME: operation-1642596393580-28fce564
TYPE: CREATE_CLUSTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T12:46:33.580352532Z
END_TIME: 2022-01-19T12:50:24.97742246Z

NAME: operation-1642599713836-a095ee51
TYPE: UPGRADE_MASTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T13:41:53.836300031Z
END_TIME: 2022-01-19T14:06:04.341528233Z

NAME: operation-1642601615896-46119ad0
TYPE: UPGRADE_NODES
LOCATION: asia-northeast1
TARGET: default-pool
STATUS_MESSAGE: Operation was aborted: operation-1642601615896-46119ad0.
STATUS: DONE
START_TIME: 2022-01-19T14:13:35.896076128Z
END_TIME: 2022-01-19T14:21:01.520708481Z

NAME: operation-1642602132989-39493995
TYPE: UPDATE_CLUSTER
LOCATION: asia-northeast1
TARGET: sample-cluster
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T14:22:12.989734846Z
END_TIME: 2022-01-19T14:22:13.741871596Z

NAME: operation-1642603081158-8a2f2134
TYPE: UPGRADE_NODES
LOCATION: asia-northeast1
TARGET: default-pool
STATUS_MESSAGE:
STATUS: DONE
START_TIME: 2022-01-19T14:38:01.158573778Z
END_TIME: 2022-01-19T14:44:55.142139747Z

...

kubectl get nodes コマンドの実行結果は以下のとおりです。

cloudshell:~ $ kubectl get nodes
NAME                                            STATUS   ROLES    AGE     VERSION
gke-sample-cluster-default-pool-4a3a874a-0mqn   Ready    <none>   31m     v1.21.6-gke.1500
gke-sample-cluster-default-pool-7a3668ce-yhk7   Ready    <none>   4m37s   v1.21.6-gke.1500
gke-sample-cluster-default-pool-8005ab02-2iq1   Ready    <none>   26m     v1.21.6-gke.1500

ノード数は 3 となっており、すべてのノードが v1.21.6-gke.1500 にアップグレードされたことがわかります。
ノードの作成時間からして、中断した 1 回目のアップグレードでアップグレードが完了したノードはそのまま使われる(再作成はしない)ようですね。
また、1 回目のアップグレードと 2 回目のアップグレードは独立したオペレーションなのでノードプールのサイズは 4 のままアップグレードが完了するのではと思ったのですが、実際には元のノードプールのサイズに収束したことが確認できました。

キャンセルの使い所

今回試したアップグレードのキャンセルの機能は、例えばノードプールのアップグレード中に障害が発生した場合などに活用できそうです。
従来、ノードプールのアップグレードが開始されるとクラスタアンタッチャブルな状態となってしまうため、アップグレード中に障害が発生するなどして特定のノードを切り離したいという状況下においてもユーザにはアップグレードの完了を待つしか選択肢がありませんでした。
アップグレードがキャンセルできるようになったことで、アップグレードが長時間化する状況下でも、ある程度短い待ち時間でクラスタへの操作権をユーザに戻すことができるようになりました。

まとめ

GKE におけるノードプールのアップグレードをキャンセルする機能について基本的な動作の確認をしました。
アップグレードが想定外に長期化するようなシチュエーションでも、「いざとなれば中断できる」という選択肢ができたことで安心感してアップグレードの完了を待てそうです。