コンテナ実行に特化したAWS製オープンソースOS「Bottlerocket」がGAになりました!

衝撃の発表 (ハマコー氏談) から半年、いよいよ「Bottlerocket」が正式リリースとなりました。
2020.09.01

みなさん、こんにちは!
AWS事業本部の青柳@福岡オフィスです。

今年3月、AWSからコンテナ実行に特化したOS「Bottlerocket」が発表されました。

Announcing the General Availability of Bottlerocket, a new open source Linux-based operating system purpose-built to run containers

発表と同時にパブリックプレビューとして試すことができる状態でしたが、このたび正式版としてリリースされました!

※ なお、Bottlerocketは「EKS」および「ECS」のホストOSとして利用可能ですが、今回GAになったのはEKS向けの利用についてのみですので、ご注意ください。

Bottlerocketとは

パブリックプレビューとして発表された際に、弊社ハマコーがブログ記事を執筆しています。
是非こちらを参照してください。

現時点における制約事項など

今回正式リリースされた時点では、Bottlerocketには以下のような制約があります。

  • 「x86」「ARM」の各プロセッサーアーキテクチャに対応しています。
    ただし、「GPUインスタンス」「Inferentiaインスタンス」には未対応です。
  • EKSをサポートする各リージョンで利用できます。
    ただし、以下のリージョンにはデプロイできません。
    • 中国(北京)リージョン (cn-north-1)
    • 中国(寧夏)リージョン (cn-northwest-1)
    • AWS GovCloud(米国東部) (us-gov-east-1)
    • AWS GovCloud(米国西部) (us-gov-west-1)
  • EKSの「マネージドノード」には対応していません。現時点ではアンマネージドなノードとしてデプロイする必要があります。

詳細は、AWSドキュメントを参照してください。
Launching self-managed Bottlerocket nodes - Amazon EKS

試してみた

AWS Blogで公開されている「Getting Started」に沿って、実際に試してみました。

Getting Started with Bottlerocket and Certified AWS Partners | AWS Partner Network (APN) Blog

前提条件

Bottlerocket環境の構築方法にはいくつか手段がありますが、Getting Startedでは「eksctl」コマンドを使用しています。

eksctlはバージョン「0.26.0」以上でBottlerocketに対応しているため、バージョンが古い場合はアップデートしましょう。

ついでに、「kubectl」など関連するツールについても、最新バージョンにしておきます。

$ eksctl version
0.26.0

$ kubectl version --client --short
Client Version: v1.18.8

$ aws --version
aws-cli/2.0.44 Python/3.7.3 Linux/4.19.104-microsoft-standard exe/x86_64.ubuntu.18

※ kubectlは現在「1.19.0」がリリースされていますが、作成するEKSクラスターのバージョンが「1.17」であるため、マイナーバージョンが2つ以上離れる「1.19.xx」は利用できません。「1.18.xx」を利用しましょう。

Bottlerocketベースのワーカーノードを含むEKSクラスターを作成

パブリックプレビュー時点では、Bottlerocket環境を構築するためには以下の手順が必要でした:

  1. 通常の (=Amazon Linux 2ベースの) ワーカーノードグループを含むEKSクラスターを作成する
  2. 作成したEKSクラスターから各種情報 (コントロールプレーン接続のための認証情報など) を取得する
  3. 作成したノードグループから各種情報 (セキュリティグループ、IAMインスタンスプロファイルなど) を取得する
  4. BottlerocketのAMIを検索する
  5. 4.で検索したAMIからEC2インスタンスを起動して、2.および3.で取得した情報を設定する
  6. 起動した「Bottlerocket」インスタンスがEKSクラスターのワーカーノードとして組み込まれることを確認する

(もっと細かな手順があったかもです)

しかし、GAとなった現在は、もっとシンプルに、Amazon Linux 2ベースのノードグループの場合とほぼ同様の手順で、Bottlerocketベースのノードグループを含んだEKSクラスターを作成することが可能です。

それでは作成していきましょう。

まず、以下のような設定ファイルを作成します。

bottlerocket-cluster.yaml

---
apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: bottlerocket
  region: ap-northeast-1
  version: '1.17'

nodeGroups:
  - name: ng-bottlerocket
    instanceType: m5.large
    desiredCapacity: 3
    amiFamily: Bottlerocket
    iam:
       attachPolicyARNs:
          - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
          - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
          - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
          - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore
    ssh:
        allow: true
        publicKeyName: <キーペア名>
    bottlerocket:
      settings:
        motd: "Hello from eksctl!"

ポイントは以下の通りです:

  • 10行目: アンマネージドなノードグループとなるためnodeGroups:と記述します。(managedNodeGroups:と書かないようにしてください)
  • 14行目: amiFamilyBottlerocketを指定します。
  • 15~20行目: これら4つのIAMポリシーは全て必要になりますので、必ず指定します。
  • 21~23行目: 後述する「Admin Container」を有効にするために、ssh:の設定を記述します
  • 24~26行目: この部分は必須ではありませんが、Bottlerocketワーカーノードに対して設定したい場合は、ここに記述します。(ここでは/etc/motdへメッセージを書き込む設定を行っています)

この設定ファイルを指定して、eksctl create clusterコマンドを実行します。

$ eksctl create cluster --config-file ./bottlerocket-cluster.yaml

約20分程度でEKSクラスターおよびBottlerocketベースのノードグループが作成されます。

作成されたノードグループを確認してみましょう。

$ kubectl get nodes -o wide
NAME                                                STATUS   ROLES    AGE    VERSION   INTERNAL-IP      EXTERNAL-IP   OS-IMAGE                KERNEL-VERSION   CONTAINER-RUNTIME
ip-192-168-38-40.ap-northeast-1.compute.internal    Ready    <none>   111s   v1.17.9   192.168.38.40    18.xx.xx.xx   Bottlerocket OS 1.0.0   5.4.50           containerd://1.3.7+unknown
ip-192-168-4-210.ap-northeast-1.compute.internal    Ready    <none>   111s   v1.17.9   192.168.4.210    18.xx.xx.xx   Bottlerocket OS 1.0.0   5.4.50           containerd://1.3.7+unknown
ip-192-168-91-121.ap-northeast-1.compute.internal   Ready    <none>   111s   v1.17.9   192.168.91.121   52.xx.xx.xx   Bottlerocket OS 1.0.0   5.4.50           containerd://1.3.7+unknown

各ノードの「OS-IMAGE」の表記が「Bottlerocket OS 1.0.0」となっています。

このように、驚くほどアッサリとBottlerocketベースのノードグループを含んだEKSクラスターを作成することができました。

もちろん、Amazon Linux 2ベースのノードグループと同じように、ノード上でポッドを実行することができます。
(適当なコンテナイメージで試してみてください)

Bottlerocketワーカーノードを管理するための2つの「ツール」

Amazon Linux 2ワーカーノードと変わらない手順で作成することができたBottlerocketワーカーノードですが、Amazon Linux 2ワーカーノードとは大きく異なる点があります。

それは「ワーカーノード (ホストインスタンス) へSSHでログインすることができない」という点です。

(「さっきSSHの設定を書いたやん!」とツッコミたくなったアナタ、もう少し待ってくださいね)

Bottlerocketでは、セキュリティ観点から、sshdサービスが搭載されていません。

しかし、何か問題が発生した時のトラブルシューティングの際など、ノードへ直接ログインすることができないと困りますよね?

そこで、Bottlerocketでは以下の2つの管理用ツールが用意されています。

  • Control Container
  • Admin Container

これらはいずれも、Bottlerocketワーカーノード上でコンテナとして動作するツールです。
また、これらのコンテナは、EKSコントロールプレーンの管理下にはなく、独立して動作します。

これらのツールについて、順に説明します。

Control Container

Control Containerの主な機能は以下の通りです:

  • SSM Session ManagerやSSM Run Commandを使ってControl Containerへ接続することができる
  • Bottlerocketワーカーノード上で動作するAPIサーバーに対して、Control ContainerからAPIを発行することができる

SSM Session ManagerでControl Containerへ接続する

細かな説明よりも、実際に使ってみましょう。

作成したBottlerocketワーカーノード3台のうちの1つに対して、SSM Session Managerで接続してみます。

Bottlerocketノードグループを作成する際にデフォルトでControl Containerは有効になっていますし、また、EKSクラスター&Bottlerocketノードグループの構築時にIAMポリシーAmazonSSMManagedInstanceCoreを指定しているため、特に準備をしなくてもSSM Session Managerで接続できるようになっています。

接続に使用するのはAWSマネジメントコンソール、AWS CLIいずれでも構いませんが、今回はAWS CLIを使ってみます。
予め「Session Manager Plugin for the AWS CLI」をインストールしておいてください。
(Optional) Install the Session Manager Plugin for the AWS CLI - AWS Systems Manager

接続対象のBottlerocketワーカーノードのインスタンスIDを確認して、以下のコマンドを実行します。

$ aws ssm start-session --target i-XXXXXXXXXXXXXXXXX

接続に成功すると、以下のようにコンソール表示されます。

Welcome to Bottlerocket's control container!

This container gives you access to the Bottlerocket API, which in turn lets you
inspect and configure the system.  You'll probably want to use the `apiclient`
tool for that; for example, to inspect the system:

   apiclient -u /settings

You can run `apiclient --help` for usage details, and check the main
Bottlerocket documentation for descriptions of all settings and examples of
changing them.

If you need to debug the system further, you can enable the admin container.
This enables SSH access to the system using the key you specified when you
launched the instance.  This environment has more debugging tools installed,
and allows you to get root access to the host.

To enable the admin container, run:

   enable-admin-container

[ssm-user@ip-192-168-4-210 /]$

インスタンスIDを指定して接続しましたが、実際に接続された先はControl Containerとなっています。

APIサーバーへアクセスしてみる

では次に、接続したControl ContainerからBottlerocketワーカーノードのAPIサーバーへアクセスしてみましょう。

APIサーバーへアクセスするためのapiclientコマンドが用意されています。

$ apiclient
Usage: apiclient
            (-u | --uri) URI
            [ (-X | -m | --method) METHOD ]
            [ (-d | --data) DATA ]
            [ (-s | --socket-path) PATH ]
            [ -v | --verbose ... ]

    Method defaults to GET
    Socket path defaults to /run/api.sock

試しに、以下のようにコマンドを実行してみます。

$ apiclient -u /os

以下のように結果が返ってきます。(見やすいように整形しています)

{
  "pretty_name": "Bottlerocket OS 1.0.0",
  "variant_id": "aws-k8s-1.17",
  "version_id": "1.0.0",
  "build_id": "b0e2bc22",
  "arch": "x86_64"
}

Bottlerocketの設定を参照することもできます。

$ apiclient -u /settings
{
  "motd": "Hello from eksctl!",
  "kubernetes": {
    "cluster-name": "bottlerocket",
    "cluster-certificate": "XXXX",
    "api-server": "https://XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.gr7.ap-northeast-1.eks.amazonaws.com",
    "node-labels": {
      "alpha.eksctl.io/cluster-name": "bottlerocket",
      "alpha.eksctl.io/nodegroup-name": "ng-bottlerocket"
    },
    "max-pods": 29,
    "cluster-dns-ip": "10.100.0.10",
    "cluster-domain": "cluster.local",
    "node-ip": "192.168.4.210",
    "pod-infra-container-image": "602401143452.dkr.ecr.ap-northeast-1.amazonaws.com/eks/pause-amd64:3.1"
  },
  "updates": {
    "metadata-base-url": "https://updates.bottlerocket.aws/2020-07-07/aws-k8s-1.17/x86_64/",
    "targets-base-url": "https://updates.bottlerocket.aws/targets/",
    "seed": 915,
    "version-lock": "latest",
    "ignore-waves": false
  },
  "host-containers": {
    "admin": {
      "source": "328549459982.dkr.ecr.ap-northeast-1.amazonaws.com/bottlerocket-admin:v0.5.2",
      "enabled": true,
      "superpowered": true
    },
    "control": {
      "source": "328549459982.dkr.ecr.ap-northeast-1.amazonaws.com/bottlerocket-control:v0.4.1",
      "enabled": true,
      "superpowered": false
    }
  },
  "ntp": {
    "time-servers": [
      "169.254.169.123",
      "2.amazon.pool.ntp.org"
    ]
  },
  "aws": {
    "region": "ap-northeast-1"
  }
}

参照だけではなく、設定を変更することもできます。

さきほど出力した設定内容のうち、「ntp」の設定内容を変更してみましょう。
設定を変更するにはHTTPの「PATCH」メソッドを使用します。

$ apiclient -v -u /settings -m PATCH -d '{"ntp": {"time-servers": ["169.254.169.123", "ntp.nict.jp"]}}'
204 No Content

(「204 No Content」は「200 OK」の特殊なパターンで、リクエストは正常に受理されたがレスポンスすべき内容が無い場合のステータスコードです)

PATCHメソッドの実行のみでは完了しておらず、「コミット」を行う必要があります。
コミットは、特定のURLに対して「POST」メソッドをリクエストすることで行います。

$ apiclient -v -u /tx/commit_and_apply -m POST
200 OK

これで、設定の変更が反映されました。

さきほどの「参照」のAPIをリクエストして、「ntp」の設定内容を確認してみましょう。

{
  ・・・
  "ntp": {
    "time-servers": [
      "169.254.169.123",
      "ntp.nict.jp"
    ]
  },
  ・・・
}

ちゃんと変更されたことが確認できました。

SSM Run Commandを使ってControl Containerを操作する

Control Containerに対してSSM Run Commandを使うこともできます。

手元のPCから以下のコマンドを実行します。

$ INSTANCE_ID=i-XXXXXXXXXXXXXXXXX
$ COMMAND_ID=$(aws ssm send-command \
    --instance-ids $INSTANCE_ID \
    --document-name "AWS-RunShellScript" \
    --comment "Bottlerocket API Settings" \
    --parameters commands="apiclient -u /settings" \
    --output text \
    --query "Command.CommandId")

これでコマンドが投入されました。
コマンドIDを確認します。

$ echo $COMMAND_ID
XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX

コマンドIDを指定して、コマンドの実行結果を抽出します。

$ aws ssm list-command-invocations \
    --command-id $COMMAND_ID \
    --details \
    --query 'CommandInvocations[*].CommandPlugins[*][Output]'

さきほどSSM Session Managerで実行した時と同様の結果が返ってきたと思います。

{
  "motd": "Hello from eksctl!",
  "kubernetes": {
    "cluster-name": "bottlerocket",
    "cluster-certificate": "XXXX",
    "api-server": "https://XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.gr7.ap-northeast-1.eks.amazonaws.com",
(以下略)

Admin Container

Admin Containerの機能は以下の通りです:

  • SSHを使ってAdmin Containerへ接続することができる
  • Admin Containerを介して、Bottlerocketワーカーノードに対するshellアクセスを行うことができる

Admin Containerは、デフォルトでは有効になっていません。

場合によってはセキュリティホールになる危険性もあるため、開発やトラブルシューティングなど必要な場合・状況に限定してAdmin Containerを有効にすることが推奨されます。

SSHでAdmin Containerへ接続する

今回は、Bottlerocketノードグループの作成時にAdmin Containerを有効に設定したため、接続が行えるようになっています。

以下のように、構築時に指定したキーペアを使ってSSHで接続します。

$ ssh -i ~/.ssh/<キーペアのpemファイル> ec2-user@<BottlerocketワーカーノードのグローバルIPアドレス>

接続に成功すると、以下のようにコンソール表示されます。

Welcome to Bottlerocket's admin container!

This container provides access to the Bottlerocket host filesystems (see
/.bottlerocket/rootfs) and contains common tools for inspection and
troubleshooting.  It is based on Amazon Linux 2, and most things are in the
same places you would find them on an AL2 host.

To permit more intrusive troubleshooting, including actions that mutate the
running state of the Bottlerocket host, we provide a tool called "sheltie"
(`sudo sheltie`).  When run, this tool drops you into a root shell in the
Bottlerocket host's root filesystem.
[ec2-user@ip-192-168-4-210 ~]$

BottlerocketワーカーノードのIPアドレスを指定して接続しましたが、実際に接続された先はAdmin Containerとなっています。

Admin ContainerからBottlerocketワーカーノードに対してroot権限でshellアクセスするには、以下のようにsudo sheltieコマンドを実行します。

$ sudo sheltie
bash-5.0#

プロンプトが「#」となり、root権限で各種の管理用コマンドが実行できるようになりました。

ちなみに余談ですが、「sheltie」とは「シェットランド・シープドッグ」という犬の種類の愛称だそうです。
この後に出てくる管理用コマンドは「~dog」というコマンド名で統一されていますので、それにかけたのでしょうかね?

話を元に戻して、いくつかの管理用コマンドを実行してみましょう。

BottlerocketワーカーノードのOSアップデート

Bottlerocketは、パッケージ・ソフトウェア単位ではなくOS全体をまとめてアップデートできることが特徴の一つです。

OSのアップデートを行う流れは以下の通りです。
まずは、アップデートの有無をチェックします。

# updog check-update
No update available

う~ん、リリースされたばかりのAMIから起動したためか、「アップデートは無い」という結果になってしまいました。

アップデートが見つかった場合には、以下のコマンドでアップデートを実行することができます。

# updog update

詳しいアップデートの手順については、別途調べてみたいと思います。

Bottlerocketワーカーノードのログ採取

Bottlerocketワーカーノードの各種ログを一括して採取することができます。

# logdog
Running: exec containerd-config containerd --config /etc/containerd/config.toml config dump
Running: exec containerd-config-host containerd --config /etc/host-containerd/config.toml config dump
Running: exec df df -h
Running: exec df-inodes df -hi
Running: exec dmesg dmesg --color=never --nopager
Running: exec iptables-filter iptables -nvL -t filter
Running: exec iptables-nat iptables -nvL -t nat
Running: exec journalctl-boots journalctl --list-boots --no-pager
Running: exec journalctl.errors journalctl -p err -a --no-pager
Running: exec journalctl.log journalctl -a --no-pager
Running: exec proc-mounts cat /proc/mounts
Running: exec settings.json apiclient --method GET --uri /
Running: exec signpost signpost status
Running: exec wicked wicked show all
Running: file os-release /etc/os-release
Running: exec kube-status systemctl status kube* -l --no-pager
logs are at: /tmp/bottlerocket-logs.tar.gz

様々なログが集められて/tmp/bottlerocket-logs.tar.gzファイルにアーカイブされました。

これをローカルPCに持ってきて解析したいところですが、SCPは使えないため、以下のようにしてローカルPCへコピーします。

$ ssh -i ~/.ssh/<キーペアのpemファイル> ec2-user@<BottlerocketワーカーノードのグローバルIPアドレス> "cat /.bottlerocket/rootfs/tmp/bottlerocket-logs.tar.gz" > bottlerocket-logs.tar.gz

あとは、ローカルPCにコピーされたbottlerocket-logs.tar.gzファイルを解凍すれば、採取したログを参照・解析することができますね。

おわりに

Bottlerocketには、今回紹介したものの他にも、様々な機能や利用方法があります。
それらについても、おいおい試してみてご紹介したいと思います。

今回ご紹介したように、BottlerocketはこれまでのAmazon Linux 2と同じような手順でデプロイすることができ、既存のシステムを少ない手間でよりセキュアなコンテナ実行環境へ移行できることが期待されます。

また、Bottlerocketはリリース時点で既に多くのサードパーティ (APNパートナー) がサポートを提供しています。
例えば、以下のようなベンダー・製品がBottlerocketをサポートしています。

  • 「Datadog」「New Relic」「Splunk」などのロギング・モニタリング製品
  • 「Aqua Security」「Trend Micro」などのセキュリティ製品
  • 「GitLab」などのDevOps関連製品

(これらはサポートベンダーの一部です。全てのサポートベンダーのリストは、今回参考にしたAWSブログの最後の方に掲載されています)
Announcing the General Availability of Bottlerocket, a new open source Linux-based operating system purpose-built to run containers

このように、将来性が非常に期待できる「Bottlerocket」がいよいよ正式リリースされました。
EKSの新機能・新サービスの中では、比較的簡単に試せるかな?という印象なので(笑)、皆さんも是非試してみてくださいね。