FLINTERS Engineer's Blog

FLINTERSのエンジニアによる技術ブログ

BottlerocketのManaged Node Groupでbootstrap containerを使う

この記事はFLINTERS Advent Calendar 202220日目の記事です。

こんにちは、永倉です。

私が所属しているチームでは多くのアプリケーションをKubernetes上で動かしています。Kubernetes ClusterはAmazon Elastic Kubernetes Service (EKS)を使って構築しています。
ワーカーノード(Amazon EC2 Instance)はManaged Node Groupによってプロビジョニングされており、Managed Node GroupのAMIにはBottlerocketを指定しています。

このKubernetes上で動かしているアプリケーションの事情により、ワーカーノードの起動時に特定の処理を実行させたいことがありました。 これにはBottlerocketのbootstrap containerの仕組みを使いました。

このBottlerocketのManaged Node Groupでbootstrap containerを使うという情報は調べた限りではほとんどなかったので紹介したいと思います。

なおこの記事ではBottlerocketやManaged Node Groupについての説明は特にはしていません。
BottlerocketについてはAWSページGitHubリポジトリ、Managed Node GroupについてはAWSドキュメントをそれぞれご確認ください。

bootstrap containerとは

bootstrap containerはBottlerocketのドキュメントによれば、ホストをブートストラップするために使われるコンテナです。このコンテナは、ECS AgentやKubernetes、Dockerが開始されるより前に実行されます。

AWSBlogにはbootstrap containerの使用用途の例として以下のようなものが書かれています。

ブートストラップコンテナは、さまざまな目的で使用できます。例えば、エフェメラルストレージのパーティション作成やフォーマット、実行可能な Pod の最大数の計算や設定、Kubernetes の追加ラベルの設定などに使用できます。可能性が広がりますね!

bootstrap containerには以下の設定ができます。詳細はこちらをご確認ください。

  • コンテナの実行に失敗した場合ブートプロセスを停止させるか
  • コンテナがいつ実行されるか(毎回のブート時、初回のブート時など)
  • 実行されるコンテナのイメージ
  • コンテナに対して渡すデータ(user data)

BottlerocketのManaged Node Groupでbootstrap containerの設定を行う方法

BottlerocketのManaged Node Groupでbootstrap containerの設定を行うにはLaunch Templateを使います。

Managed Node GroupではLaunch Templateがサポートされています。 Launch Templateはuser dataを指定でき、Bottlerocketではそのuser dataを使ってbootstrap containerなどBottlerocketに関する設定ができます。

BottlerocketのManaged Node Groupでbootstrap containerを使う手順

ということで、BottlerocketのManaged Node Groupで、bootstrap containerを使うためには以下のような手順を踏むことになります。

  • bootstrap containerで使用するコンテナイメージを用意する
  • Bottlerocketのbootstrap containerの設定を含むuser dataを指定したLaunch Templateを作成する
  • 作成されたLaunch Templateを指定したBottlerocketのManaged Node Groupを作成する

以下では上記の手順をコード例を含めて紹介します。
コード例の中で、Managed Node GroupやLaunch Templateの作成にはTerraformのAWS Provider(バージョン4.46.0)を使っています。

bootstrap containerで使用するコンテナイメージを用意する

bootstrap containerとして動かすコンテナ用にコンテナイメージを用意します。 既に動かしたいコンテナイメージが用意されていればこの手順は必要ないです。

今回は例としてbootstrap containerで使うコンテナイメージ用に簡単なDockerfileを用意します。

# syntax = docker/dockerfile:1.4
FROM alpine:3.17.0

COPY <<EOF /bootstrap-script
#!/usr/bin/env sh
# Transparent Huge Page を無効にする
# bootstrap containerでは`/.bottlerocket/rootfs`でホスト側のファイルシステムへアクセスができます
# この例はBottlerocketのissue(https://github.com/bottlerocket-os/bottlerocket/discussions/1989)を参考にしています。
echo never > /.bottlerocket/rootfs/sys/kernel/mm/transparent_hugepage/enabled

# 下でbootstrap containerのログを確認するので標準出力に適当な文言を出力させる
echo hello world
EOF

RUN chmod +x /bootstrap-script

ENTRYPOINT ["sh","/bootstrap-script"]

Dockerfileの例はAWSBlogやbootstrap containerのドキュメントにも書かれているので参考にしてみてください。

上記のDockerfileを元にしてコンテナイメージを作成します。 作成されたコンテナイメージは、DockerHubやAmazon ECRなどにPushしておきます。

Bottlerocketのbootstrap containerの設定を含むuser dataを指定したLaunch Templateを作成する

aws_launch_templateresourceを使いLaunch Templateを作成します。 Bottlerocketのuser dataはTOMLの形式で記述します。
bootstrap containerの設定はsettings.bootstrap-containers.<name>テーブル内に書きます。
この例ではbootstrap containerの設定しかしていませんが、他にもさまざまな設定項目があります。詳細はドキュメントをご確認ください。

locals {
  # launch templateに渡すuser data
  launch_template_user_data = <<USER_DATA
# bootstrap containerの設定
[settings.bootstrap-containers.example]
# bootstrap containerとして実行されるコンテナのイメージ
source = ${用意したコンテナイメージ}
# コンテナがいつ実行されるか(毎回のブート時、初回のブート時など)
mode = "always"
# コンテナの実行に失敗した場合ブートプロセスを停止させるか
essential = true
  USER_DATA
}

resource "aws_launch_template" "example" {
  name      = "example"
  user_data = base64encode(local.launch_template_user_data)
}

作成されたLaunch Templateを指定したManaged Node Groupを作成する

aws_eks_node_group resourceを使いManaged Node Grouopを作成します。(今回の説明に必要のない設定項目は除いています。)

resource "aws_eks_node_group" "example" {
  node_group_name = "example"
  # Bottlerocketを指定します
  ami_type       = "BOTTLEROCKET_x86_64"
  # 作成したLaunch Templateを指定します。
  launch_template {
    id      = aws_launch_template.example.id
    version = aws_launch_template.example.latest_version
  }
}

bootstrap containerが実行されたことを確認する

詳細は割愛しますが、BottlerocketではAWS Systems ManagerのSession Managerを使い、BottlerocketのEC2 Instance上でシェルを立ち上げることができます。*1

シェルを立ち上げた後以下のコマンドを実行します。
実行されたbootstrap containerが使っているコンテナイメージは、上で書いたDockerfileをもとに作成し、Amazon ECRにPushしたコンテナイメージです。

[ssm-user@control]$ enter-admin-container
Confirming admin container is enabled...
Waiting for admin container to start.......
Entering admin container
          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.

[root@admin]# sheltie

# bootstrap containerのログを確認する。参考: https://github.com/bottlerocket-os/bottlerocket/blob/1.11.x/sources/api/bootstrap-containers/README.md
# `example`の箇所は、bootstrap containerの設定`settings.bootstrap-containers.<name>`の`<name>`の値と同じです
bash-5.1# journalctl -u bootstrap-containers@example.service
Dec 16 08:46:15 10.0.3.25 systemd[1]: Starting bootstrap container example...
Dec 16 08:46:16 10.0.3.25 host-ctr[1101]: time="2022-12-16T08:46:16Z" level=info msg="pulling with Amazon ECR Resolver" ref="ecr.aws/arn:aws:ecr:ap-northeast-1:<aws_account_id>:repository/bottlerocket-bootstrap:latest"
Dec 16 08:46:16 10.0.3.25 host-ctr[1101]: time="2022-12-16T08:46:16Z" level=info msg="pulled image successfully" img="ecr.aws/arn:aws:ecr:ap-northeast-1:<aws_account_id>:repository/bottlerocket-bootstrap:latest"
Dec 16 08:46:16 10.0.3.25 host-ctr[1101]: time="2022-12-16T08:46:16Z" level=info msg="unpacking image..." img="ecr.aws/arn:aws:ecr:ap-northeast-1:<aws_account_id>:repository/bottlerocket-bootstrap:latest"
Dec 16 08:46:16 10.0.3.25 host-ctr[1101]: time="2022-12-16T08:46:16Z" level=info msg="tagging image" img="<aws_account_id>.dkr.ecr.ap-northeast-1.amazonaws.com/bottlerocket-bootstrap:latest"
Dec 16 08:46:16 10.0.3.25 host-ctr[1101]: time="2022-12-16T08:46:16Z" level=info msg="Container does not exist, proceeding to create it" ctr-id=boot.example
Dec 16 08:46:16 10.0.3.25 host-ctr[1101]: time="2022-12-16T08:46:16Z" level=info msg="container task does not exist, proceeding to create it" container-id=boot.example
Dec 16 08:46:17 10.0.3.25 host-ctr[1101]: hello world
Dec 16 08:46:17 10.0.3.25 host-ctr[1101]: time="2022-12-16T08:46:17Z" level=info msg="successfully started container task"
Dec 16 08:46:17 10.0.3.25 host-ctr[1101]: time="2022-12-16T08:46:17Z" level=info msg="container task exited" code=0
Dec 16 08:46:17 10.0.3.25 bootstrap-containers[1170]: 08:46:17 [INFO] bootstrap-containers started
Dec 16 08:46:17 10.0.3.25 bootstrap-containers[1170]: 08:46:17 [INFO] Mode for 'example' is 'always'
Dec 16 08:46:17 10.0.3.25 systemd[1]: Finished bootstrap container example.

# Transparent Huge Page を無効になっていることを確認する
bash-5.1# cat /sys/kernel/mm/transparent_hugepage/enabled
always madvise [never]

無事実行されていることが確認できました。

参考

最後に

明日は @rikuta_fl さんの記事が投稿されます。

*1:内部的にはcontrol-containerというコンテナが使われます。詳細はこちらをご確認ください。