Amazon Web Services ブログ

Bottlerocket: 特定目的のコンテナオペレーティングシステム

2020 年 3 月 10 日、Linux コンテナをホストするために設計された新しい特定目的オペレーティングシステムである Bottlerocket を導入しました。この記事では、当社が掲げたいくつかの目標、その過程で行ったエンジニアリングの選択、OS が今後も進化し続けるというビジョンについて説明します。

2014 年に、Linux コンテナのオーケストレーションサービスである Amazon Elastic Container Service (ECS) を開始しました。サービスとともに、事前設定済みでコンテナをホストするためにすぐに使用できるオペレーティングシステム、Amazon ECS に最適化された AMI を立ち上げました。この AMI は、ECS 向けに 2 つの方法で最適化されました。まず、ECS で Docker コンテナを実行するために必要なすべてのソフトウェアがインストールされており、起動するとすぐに準備が整います。そして 2 つ目は、Amazon Linux AMI のやや簡略化されたバージョンに基づいており、維持する必要のある不要なソフトウェアを削減し、ディスク領域を節約することを目的としています。ただし、この AMI は依然として、コンテナの外で従来のソフトウェアアプリケーションを実行するために設計された汎用オペレーティングシステムに基づいていました。

2017 年に Amazon Elastic Kubernetes Service (EKS) を立ち上げたとき、当社は同じことを行いました。Kubernetes ポッドをホストするための事前設定済みですぐに使用できるオペレーティングシステムとして、Amazon EKS に最適化された AMI がそれです。Amazon ECS に最適化された AMI と同様に、Amazon EKS に最適化された AMI には、EKS でポッドを実行するために必要なすべてのソフトウェアがインストールされていました。また、Amazon ECS に最適化された AMI と同様に、この AMI は、従来のソフトウェアアプリケーションをコンテナの外部で実行するために設計された汎用オペレーティングシステムに基づいていました。

Bottlerocket プロジェクトは、Amazon で長年に渡って大規模な本番環境サービスを実行して学んだ教訓の結果として始まり、過去 6 年間にコンテナの実行方法について学んだ教訓を活かして色分けされています。Amazon の内部経験の蓄積とエンジニアからのフィードバックに加えて、お客様からは、ECS に最適化された AMI、EKS に最適化された AMI、およびその他のコンテナ中心のオペレーティングシステムに関するコンテナ固有の幅広いご意見をいただきました。セキュリティを強化し、クラスター内のインスタンスが同一であるようにし、優れた運用動作とツールを用意するといった際立つテーマがいくつかあり、それらを結集して後に Bottlerocket となるものを構築することになりました。AWS は、Bottlerocket が上記の各状況を改善すると信じており、将来的にはさらに改善することを目指しています!

また大事な点として、Bottlerocket がこのような方向に舵取りした最初のオペレーティングシステムというわけではありません。多くの新しいソフトウェアプロジェクトと同様に、Bottlerocket はこれまでのプロジェクトを土台にしています。Bottlerocket を設計および構築する際は、従来の汎用 Linux ディストリビューションに加えて、CoreOS Container Linux、Rancher OS、Project Atomic などのコンテナ中心のオペレーティングシステムにも触発されました。当社が行ったエンジニアリングの選択の中にはこれらのオペレーティングシステムに類似しているものもありますが、うまく機能したものとうまく機能できる可能性があるものの両方を独自の設計に組み込むことを試みました。

技術的な詳細に入る前に、コンテナが通常どのように使用されるのか、なぜこれらのテーマについて一貫したフィードバックが寄せられるのかについてお話したいと思います。どのような環境でも、コンピュータの起動にはしばらく時間がかかります。しかし、起動よりも難しいのは、ランダムなアプリケーションをそのコンピュータにデプロイし、確実に実行することです。コンテナを使用すると、このプロセスがはるかに簡単になります。コンテナイメージは、動的にリンクされたライブラリ、呼び出す他のプログラム、アセットなど、アプリケーションのローカル依存関係のセットをパッケージ化するための信頼性が高く反復可能なメカニズムを提供します。cgroup や名前空間など、コンテナを駆動する Linux カーネルの基本機能は、ある程度のリソースと可視性の分離を提供します。また、コンテナはコンピュータ全体よりもはるかに速く起動します。このようなプロパティにより、各アプリケーションは、実行中の唯一のアプリケーションであるかのように見せかけ、大きなコンピュータを小さな部分に分割することができます。これにより、これらのアプリケーションの多くが競合なしで一緒に実行できるようにします。さらに、1 台のコンピュータで複数のアプリケーションを実行したり、コンピュータのクラスターでこれらのアプリケーションの多くのコピーを実行したりするのにうってつけです。

コンテナオーケストレーションのより大きなエコシステムにより、ソフトウェアシステムをデプロイおよび操作するための強力なプロパティが実現可能になります。コンテナオーケストレーターは、同じコンピュータのセットでアプリケーションの多くのコピーと多くの異なるアプリケーションを管理するためのツールとメカニズムを提供します。オーケストレーターは、サービスディスカバリ、ネットワークポリシー管理、ロードバランシング、アプリケーショントレースなどのメカニズムと機能も提供します。これらはすべて、マイクロサービスベースのアーキテクチャの人気のある部分です。ただし、多くのコンピュータにまたがるより大規模なコンテナの実行は、そのようなコンピュータに一貫性があり、予測可能で、安全であることにも左右されます。Bottlerocket では、コンテナの優れた点を活かし、コンテナをホストするオペレーティングシステムにその点を導入したいと考えています。

コンテナエコシステムは、オープンソースコミュニティの拡大を受けて、成長し、繁栄してきました。Docker、containerd、Kubernetes、Linux 自体など、コンテナを開発、実行、および操作するためのコアコンポーネントの多くはオープンソースです。当社は Bottlerocket をコンテナエコシステムにうまく適合させたいと考えており、これをオープンソースプロジェクトとして開発しています。参加方法については、この記事の最後をご覧ください。

Bottlerocket の設計

セキュリティ、一貫性、操作性に関する目標をサポートするために行ったエンジニアリング上の選択をいくつか詳しく見ていきたいと思います。当社が行った選択の多くは複数の目標をサポートしているため、各目標によって選択を分類することは簡単ではありません。けれども、これらの選択を、それらがサポートする主要な目標に沿って大まかに整理してみようと思います。

セキュリティ

セキュリティから始めましょう。ここでの大きな概念は、攻撃対象領域の縮小、ソフトウェアの検証、権限の制限の強制です。

Bottlerocket に含まれるソフトウェアは少なく、特に予想されるいくつかのコンポーネントを排除しています。Bottlerocket には、SSH、Python などのインタープリター、またはシェルさえありません。当社は、Bottlerocket がほとんどの場合「ハンドオフ」(無干渉) になることを期待しており、このようにコンポーネントを削除することで、攻撃者がシステムに足場を固めることが難しくなると考えています。(そして、以下で触れるトラブルシューティングとデバッグのためのメカニズムがあります)。 ソフトウェアの削除に加えて、Bottlerocket は、位置に依存しない実行可能ファイル (PIE) の構築、再配置読み取り専用 (RELRO) リンクの使用、Rust や Go といったメモリセーフ言語によるファーストパーティソフトウェアの構築といったソフトウェア強化技術を適用することにより、オペレーティングシステムの攻撃対象領域を縮小しています。

Bottlerocket は暗号化で自分自身を検証します。オペレーティングシステムは、ブート時に dm-verity で検証されるディスクイメージで設定されています。ディスクイメージの内容が予期せず変更されると、オペレーティングシステムが起動しなくなります。Bottlerocket は、より一般的な Linux パッケージマネージャーではなく、独自のソフトウェアアップデーターを使用しています。Bottlerocket の更新は、Update Framework (TUF) 仕様に準拠したリポジトリから提供されます。TUF は、従来のパッケージマネージャーシステムに存在するソフトウェアリポジトリに対する一般的なクラスの攻撃を軽減します。

Bottlerocket は、強制モードで SELinux を使用して、Bottlerocket 自体への変更も特権コンテナからの変更に制限しています。SELinux は、Linux カーネルによって強制される強制アクセス制御 (MAC) を実装したものであり、プロセスが実行できる一連のアクションを制限します。現在、Bottlerocket の SELinux ポリシーは、オーケストレーションされたコンテナがオペレーティングシステムに望ましくない予期しない変更を引き起こすことを制限することを目指しています。今後、このポリシーを拡張して、すべてのカテゴリの持続的な脅威に適用したいと考えています。

一貫性

当社は、Bottlerocket がお客様の環境に一貫性を持たせるのを助けたいと考えています。コンピュータのクラスターを実行してコンテナを実行する場合、どのコンテナでも同じワークロードを実行できるようにする必要があります。Bottlerocket は、主に 3 つのアプローチ (イメージベースの更新、読み取り専用のルートファイルシステム、API 駆動型の設定) によって一貫性を補強します。

最も一般的に使用される汎用の Linux ディストリビューションには、ソフトウェアをインストールおよび更新するための統合パッケージ管理システムがあります。これにより、ディストリビューションは非常に柔軟になります。そしてそのことから、さまざまな異なるワークロードを実行できます。ただし、多数のホストを管理する場合、この柔軟性はマイナス面になる可能性があります。異なるパッケージと異なるバージョンのパッケージが各ホストにインストールされ、相互に矛盾する可能性があるからです。パッケージマネージャーで利用できる多種多様なパッケージも課題の原因となる可能性があります。インストールするパッケージの組み合わせは、一緒にテストされたことがないかもしれません。Bottlerocket はこの点が異なります。インストールするソフトウェアを幅広く選択できるパッケージマネージャはありません。パッケージマネージャではなく、Bottlerocket は、オペレーティングシステム用のソフトウェアを含む事前に作成されたイメージを使用して、コンテナ内の診断ツールや観測ツールなどの他のソフトウェアを簡単に実行できるようにしています。更新が利用できるようになると、Bottlerocket は新しいディスクイメージ全体をダウンロードし、再起動するだけで更新を適用できます。イメージベースのデプロイは一貫性を保証します。フリートのすべての Bottlerocket ホストはまったく同じソフトウェアを実行でき、Bottlerocket イメージに含まれる各コンポーネントの特定のバージョンが一緒にテストされることが保証されます。

Bottlerocket はコンテナを実行するように設計されており、一貫性を確保するためにイメージベースのデプロイを採用しています。ただし、実行中のコンテナのすべてのユースケースに対して、ソフトウェアと設定のセットがすべて揃っているわけではないことは認識しています。現在、Bottlerocket は AWS の Kubernetes クラスターでノードとして実行することをサポートしています。けれども、Bottlerocket をさまざまな場所 (Raspberry Pi など) で、さまざまなオーケストレーター (Amazon ECS など) で実行できるようにしたいと考えています。Bottlerocket は、さまざまなユースケースに合わせて異なるイメージを使用する「バリアント」システムを通じて、この要件の違いに対応しています。リリース時に利用できるバリアントは、Kubernetes 1.15 で使用するために AWS が公開したもので、aws-k8s-1.15 と呼ばれています。Amazon ECS のバリアントに加えて、Amazon EKS で提供する際に他のバージョンの Kubernetes のバリアントも追加で公開する予定です。Bottlerocket には、独自のニーズがあるときに独自のバリアントを構築するためのツールも含まれています。

従来の Linux ディストリビューションとは異なり、Bottlerocket オペレーティングシステムは読み取り専用のルートファイルシステムで設定されています。これは、一貫性を確保してドリフトを減らすためのもう 1 つのメカニズムです。アプリケーションはディスクイメージを変更できず、あるホストから別のホストに変更を導入できません。Bottlerocket が更新をダウンロードしてインストールする準備ができると、更新はセカンダリパーティションに書き込まれます。再起動すると、Bottlerocket のブートローダーは正しいパーティションで起動する方法を理解し、プライマリを変更して、古いバージョンのイメージをセカンダリとして使用できるようにします。更新で問題が発生した場合、これと同じメカニズムを使用して迅速にロールバックできます。Bottlerocket には、コンテナイメージやボリュームなどの永続的なユーザーデータ用に設計された、ファイルシステムの個別の書き込み可能な部分も備えています。

Linux のソフトウェア設定を /etc ディレクトリに保存することは比較的一般的です。Bottlerocket には互換性のために /etc がありますが、ブートごとに再生成されるメモリバックアップの一時ファイルシステムとして公開されています。そこで設定を永続化し、アプリケーションが Bottlerocket の設定を変更できるようにするのではなく、Bottlerocket は、構造化設定、トランザクション、および自動移行に関する豊富なセマンティクスをサポートする API を公開して設定の変更を行えるようにしています。API は、対話型の変更のために AWS Systems Manager を介して Bottlerocket の「制御」コンテナからアクセスできますが、プログラムで設定することもできます。EC2 で Bottlerocket を使用している場合は、TOML 形式のユーザーデータを使用して設定を行うこともできます。

設定の中には、Bottlerocket が自ら生成する方法を知っているものもいくつかあります。起動プロセスの早い段階で、Bottlerocket は、ホスト名やネットワーク設定など、起動まで不明なデータを使用して自らを設定します。Bottlerocket の aws-k8s-1.15 バリアントを使用すると、ヘルパープログラムが実行され、クラスター DNS 設定や一時停止コンテナイメージの名前などの Kubernetes 固有の設定を行います。API を使用するか、EC2 で Bottlerocket を使用している場合は、TOML 形式のユーザーデータを使用して、この設定を上書きできます。

操作性

今日お話しする最後の目標は、操作性です。Bottlerocket は他の Linux ベースのオペレーティングシステムとは異なりますが、ソフトウェアの更新やトラブルシューティングなどの通常の操作のための機能を備えています。Bottlerocket は明確に定義された方法で動作し、その動作を変更するための設定があります。これには、調整されたノードのコード化とドレインによる中断を減らすための Kubernetes との統合など、自動ソフトウェア更新を実行するためのメカニズムがあります。設定の変更やソフトウェアアップデートの手動インストールなどの定期的な管理タスク用のツールがありますが、追加機能が本当に必要なときには緊急シナリオ用のツールも用意しています。

Bottlerocket の更新機能は、いくつかの異なるコンポーネントによって促進されます。まず、TUF ベースのリポジトリには、更新されたイメージと、イメージの完全性とリポジトリ自体の完全性をカバーする署名が含まれています。次に、リポジトリと対話して更新を取得するための、updog と呼ばれる Bottlerocket のオンホストツールがあります。Updog には、更新をクエリして、Bottlerocket に更新をすぐに適用する機能があります。ただし、updog はデフォルトで「ウェーブ」ベースのアップデート戦略を使用します。ウェーブは、すべてのホストが更新をすぐに確認するのではなく、クラスター内のさまざまなホストが異なるタイミングで更新を利用できるようにするメカニズムを提供します。これにより、すべてのホストが同時に更新しようとする可能性 (これはコンテナベースのワークロードが中断される原因です) が減り、問題が発生した場合は更新を停止することができます。各ホストは起動時にランダムなウェーブに割り当てられますが、これは設定で変更可能です。

Bottlerocket の更新機能は、コンテナオーケストレーターと統合することもできます。プレビュー版のローンチの一環として、Bottlerocket には Kubernetes オペレーターが付属しており、クラスターにデプロイして updog を使用して更新を実行できます。オペレーターは、クラスター内の一度に 1 つのホストのみが更新されるようにし、更新が適用される前に、ホストからのポッドの接続とドレインを処理します。アップデーターは開発のかなり早い段階にあります。そのため、その機能をどのように拡張するかについてのご意見があれば、是非お寄せください。

Bottlerocket には SSH がインストールされていないため、オペレーティングシステムの制御、API との対話、および管理モードへの「ブレークグラス」の実行には別のメカニズムが必要です。Bottlerocket にはこのためのツールが 2 つあります。設定の変更など、予想される一般的なメンテナンスタスク用の「制御」コンテナと、緊急用の「管理」コンテナです。制御コンテナは起動時に立ち上げられ、Amazon SSM エージェントが含まれます。 AWS Systems Manager API を使用してそれと対話できます。この制御コンテナには、Bottlerocket API との対話を容易にする apiclient と呼ばれるプログラムと、緊急管理コンテナを開始するために必要な API 呼び出しを自動化する enable-admin-container と呼ばれる小さなヘルパープログラムがあります。

管理コンテナは緊急用です。完全な特権で起動され、SELinux プロファイルが適用されている場合を除き、制約はありません。管理コンテナは Amazon Linux 2 コンテナイメージに基づいており、汎用の Linux ディストリビューションで期待されるツールを備えています。管理コンテナには SSH がインストールされ、実行されています。インスタンスの起動時に指定された SSH キーを使用して、Bottlerocket のプライマリネットワークインターフェース経由で SSH に接続できます。また、作業コンテキスト (Linux 名前空間) をホストのコンテキストに移行するための sheltie と呼ばれるツールもあります。これにより、管理コンテナ内からホストを操作できます。管理コンテナはデフォルトでは有効になっていません。Bottlerocket の本番環境のデプロイでは無効にしておくことをお勧めします。

Bottlerocket は、オーケストレーターが管理するコンテナと、「ホストコンテナ」と呼ばれるローカル操作用のコンテナを実行します。 このホストコンテナには、上記の制御コンテナと管理コンテナが含まれています。Bottlerocket は、2 つの異なるコンテナランタイムを使用してこれらを実行します。2 つの異なる containerd のコピーです。これには 3 つの理由があります。第 1 に、オーケストレーションされたコンテナとホストコンテナは、個別の SELinux プロファイルによって強制される個別のセキュリティ要件を持つことができます。次に、オーケストレーションされたコンテナは、ホストコンテナとは異なるランタイム (Docker や CRI-O など) で起動できます。3 番目に、オーケストレーションされたコンテナとホストコンテナは、コンテナランタイムでの設定の変更または障害のために別々のフォールトドメインを持つことができます。制御コンテナはデフォルトで含まれており、必要に応じて管理コンテナを追加できますが、ホストコンテナシステムを使用して、Bottlerocket で独自の診断、運用、管理ツールを実行することもできます。

今後の展望

Bottlerocket は現在プレビュー段階にあり、一般公開する前に、引き続き多くの機能強化に取り組んでいます。公開ロードマップがありますが、ここでは特に強調したい詳細な点をいくつか取り上げたいと思います。

Bottlerocket が一般公開される前と将来の両方で大切になってくる主要なテーマに、セキュリティがあります。これまでに Bottlerocket で行ってきたことに満足していますが、常に改善し続ける機会があります。Bottlerocket が一般公開される前に、SELinux ポリシーが完了します。当社はオーケストレーターの containerd のコピーを別のマウント名前空間で実行する可能性など、通常のオーケストレーションコンテナへのファイルシステムアクセスのレベルを下げる方法を模索しています。また、高度な分離を必要とするユースケースのために Firecracker を備えた microVM 内部で実行するなど、コンテナ化されたワークロードを実行する別の方法も検討しています。

現在、Bottlerocket は Kubernetes をサポートしていますが、Bottlerocket は Kubernetes 専用のオペレーティングシステムというわけではありません。ロードマップには、Bottlerocket に Amazon ECS のサポートを追加したり、無停止更新のような操作を Amazon ECS クラスターに統合したりする計画があります。Bottlerocket で見たいオーケストレーターが他にある場合は、是非ご参加ください!

ご参加ください!

Bottlerocket は完全にオープンソースのオペレーティングシステムです。オペレーティングシステムは、Linux カーネルや約 50 のパッケージなどの既存のオープンソースコンポーネントと、Bottlerocket (主に Rust 言語と Go 言語) 向けに特別に作成された新しいコンポーネントで設定されています。Bottlerocket が使用する既存のオープンソースコンポーネントは、独自のオリジナルライセンスに基づいてライセンスされますが、Bottlerocket 固有のすべてのコンポーネントは、Rust 言語と同様に、ご希望の Apache 2.0 ライセンスまたは MIT ライセンスに基づいてライセンスされます。

Bottlerocket のコンポーネントは、ロードマップと同様に、オープンソースです。当社の意図は、Bottlerocket が共同のコミュニティプロジェクトになるようにすることです。そのため、直接貢献して、独自のカスタマイズバージョンを作成することができます。Amazon EKS や (将来的には) Amazon ECS といったサポートされている統合の公式イメージとアップデートのセットを作成します。ただし、当社の公式イメージでは予期できない、またはサポートできないニーズがあると予想しています。そこで、当社が使用しているのと同じツールセットを使用して、お客様が独自のイメージとアップデートを構築できるようになればと考えています。

Bottlerocket へのご参加をお待ちしております! 当社の GitHub リポジトリをチェックして、問題に関する議論やプルリクエストによる貢献をご確認ください。また、AWS Developer Slack で非公式なやり取りを行うための #bottlerocket チャネルもご用意しております。チャネルへは、こちらからサインアップできます

まとめ

Bottlerocket は、従来の汎用 Linux ディストリビューションとは非常に異なるオペレーティングシステムですが、変更によりセキュリティと運用が長期的に改善されると考えています。そして Bottlerocket に組み込んだツール (管理コンテナのような「ブレークグラス」メカニズムなど) により、移行が容易になればと願っています。

Bottlerocket のプレビュー版を今すぐお試しいただければ幸いです。ご意見やご感想をお待ちいたしております。