Barcelonaのアーキテクチャ
Tweetはじめに
この記事は、Barcelonaを紹介するシリーズの一つです。
- Kubernetes VS Barcelona
- Barcelonaのアーキテクチャ(この記事)
- Barcelonaチュートリアル1(ローカルDBでまず使ってみる)
- Barcelonaチュートリアル2(運用編)
- Barcelonaチュートリアル3(AWSにインストール)
- Barcelonaチュートリアル4 (Mastodonを動かす)
- Barcelonaの今後
Barcelonaの構成要素
Barcelonaの構成要素は以下の通りです。
- Barcelona-API: REST形式のAPIを提供するメインのプロセス
- Barcelona-cli: REST APIへの簡単なアクセスを提供するcliコマンド
- PostgreSQL DB: Barcelonaのリソースを格納するデータベース
この3つにより、仮想的なPaaSを構成するリソースを提供します。
このリソースが、CloudFormationを通して、実際のAWSのリソースと同期することで、AWS内にDocker用のPaaSが構築されます。
これにより、以下のようなサービスを実現します。
- アプリ開発者には、シンプルな仮想PaaSを操作しているように見えます
- 運用者には、プレーンなAWSのリソースが見えていて、AWSのWeb Console/CLIを通して運用できます
- 両者は、CloudFormationによって同期します。
類似のツール、特に Kubernetes + 構築ツールと比べて、以下のような特色があると思います。
- ツール独自のリソースとAWSリソースの関係がわかりやすく、管理しやすい
- AWSのリソースは全て、CloudFormation のテンプレートから構築されるので、このテンプレートを見ることで詳細を把握できる
- 検証しやすいセキュリティモデル(後述)
- CloudWatch, CloudTrail などとの連携が容易になる
Barcelona-API (-> repository)
Barcelonaの本体は、Ruby on Rails で構築された、Webアプリです。DB上のリソースに対するCRUD処理が基本となっていますので、全体の構成が把握しやすいと思います。
更新系処理は、ほとんどの場合、以下のように行われます。
- ユーザの認証、認可
- パラメータの検証
- DB上のリソース更新
- CloudFormation のテンプレートをJSON形式で生成
- テンプレートを適用
つまり、このようになっています。
- PaaSの状態は、DB上に保持されている
- REST APIでDB上の状態が更新されると、関連するテンプレートを生成し適用する
- テンプレートと実際のAWS上のリソースの不一致があると、(不一致の部分だけ)CloudFormation が対応するAWSリソースの生成、更新、削除を行う
そして、Barcelonaの機能の追加、更新は、生成されるJSON形式のテンプレートに対する項目の追加や、値の生成方法の変更となります。
CloudFormationを経由しない処理もいくつかありますが、その場合も、task defenitionや IAM Policy などのJSONデータを生成して処理を一括で依頼するAWSのAPIを使っています。
運用中のAWSリソースに対する変更は、とてもクリティカルな処理ですが、Barcelonaはそこに直接タッチせず、難しいところをCloudFormation に任せているというかたちになります。Barcelonaの役割は、基本的にJSON形式のテンプレートを作るところまでなので、テストや検証を確実に行うことができます。
Barcelona-CLI ( -> repository)
Go言語で記述されたCLIで、これがユーザインターフェースとなります。
実行例
$ bcn app show hello
Name: hello
Image Name: docker.io/essa/barcelona-sample-hello
Image Tag : latest
Version: 2
Before Deploy: None
Token: 9fd04e74-8ee2-4272-a7fa-e169cac87842
Scheduled Tasks:
Environment Variables
bcnには、他に次のような機能があります。
- 認証用トークンの管理
- コンテナインスタンスへのログイン
- テスト用コンテナの起動とログイン
Barcelonaが管理する仮想リソース
bcn
コマンドでユーザが直接作成するBarcelonaの主要なリソースは、以下の3種類になります。
- District: 仮想PaaS=アプリケーションを deploy する場所
- Heritage: 仮想PaaSにDeployされるアプリケーション
- EndPoint: アプリケーションを外部に公開するためのリソース
District
DistrictはBarcelonaの基本となるリソースで、次のようなコマンドで作成します。
$ bcn district create --region=us-west-2 --nat-type=managed_gateway demo1
つまり、Districtの名前(この例ではdemo1
)、以外には、二つのパラメータを指定するだけで作られますが、これにより以下のAWSリソースが作られます。
- VPC
- DMZ用サブネット × 2
- プライベートサブネット × 2
- インターネットゲートウェイ
- bastionサーバ(踏み台サーバ)
- ECS Cluster
- ECSInstanceRole
- ECSServiceRole
- Auto Scaling Group
- Launch Configuration
- Container Insntance
これらのリソースを必要に応じて、何組でも作ることができます。PaaSを分割することが必要となるケースは次のような場合です。
- staging test用のPaaSを本番用と別に用意する
- セキュリティや負荷に対する要件が違うアプリケーションを別の(専用の)PaaSで動かしたい
- チームごとにAWSアカウントが分かれているので、PaaSもそれに合わせたい
また、Districtごとに、プラグインを導入して、PaaSを拡張したり、カスタマイズしたりできるようになっています。現在、次のようなプラグインが開発されています。
- newrelic plugin: コンテナインスタンスに NewRelic のエージェントを組み込む
- logentries plugin: コンテナの標準出力を Logentries に出力する
- pcidss plugin: クラスタ内にWazhu(OSSEC)の管理サーバを起動し、コンテナインスタンスにエージェントを組み込む
デジカでは、本番用 District を3つ作成して、次のように分けています。
- EC用アプリケーションは、負荷的なスパイクが多いので、他のアプリと分ける
- 決済用アプリケーションは、PCIDSSのため以下の点で他と違う運用になる
- logentriesはPCIDSSに準拠していないので、これだけログの管理をCloudWatchで行う
- OSSECサーバ必須なので、このDistrictだけ、Wazuh(OSSEC)を導入している
- セキュリティを強化するため、専用のAWSアカウントで運用している
- 上記二つ以外のアプリケーションは全て、既定の District で運用する
このように、Districtごとにきめ細かく要件に合わせて運用することができます。
また、プラグインの処理は、いくつかのフックを利用して、JSONテンプレートの生成方法を変えるというものです。生成されたJSONの内容をチェックすればいいだけなので、この種のツールとしては、テストや検証が比較的容易だと思います。
Heritage
Heritageは、DiscrictにDeployされるアプリケーションを表すリソースです。
Barcelonaで作成する仮想PaaSは、Dockerを前提としていますので、「Deployされるアプリケーション」とは基本的には、Dockerイメージになります。
つまり、Barcelonaにおけるアプリケーションの登録は次のようになります。
- アプリケーションをDockerイメージとして作成、Dockerでテスト
- どこかの Docker レポジトリにDockerイメージを登録
- 特定のDistrict内に、Dockerレポジトリのパスを指定してHeritageを作成
- HeritageとDockerレポジトリのタグを指定して Deploy
- Heritageに対応した ECS Service内で、Dockerイメージが稼働する
その後、アプリケーションを修正するたびに、次の作業を繰り返すことになります。
- アプリケーションを修正し、新しいDockerイメージを作成、登録
- Heritageとタグを指定して、Deploy
- Heritageに対応した ECS Service 内で、ECSのDeployment処理が行われる
最初の登録の時には、パラメータも多く、考えることも多いですが、稼働してから新しいバージョンを deploy するには、以下のようなコマンドを発行するだけです。
$ bcn deploy -e production --tag=latest
barcelona.yml
Barcelonaでは、一つのアプリケーションが、本番環境とテスト環境のように、複数の環境で動作することを前提としています。つまり、一つのアプリケーションが複数のHeritageで稼働することになります。
そこで、複数の環境の設定を一つファイルで管理できるように、barcelona.yml
というファイルで、Heritageを管理します。barcelona.yml
は次のようなYAML形式のファイルです。
environments:
production:
name: hello-production
image_name: docker.io/essa/barcelona-sample-hello
services:
- name: web
service_type: web
public: true
cpu: 128
memory: 256
command: ruby hello.rb
listeners:
- endpoint: test-endpoint
health_check_interval: 10
health_check_path: /
rule_conditions:
- type: path-pattern
value: '*'
staging:
name: hello-staging
image_name: docker.io/essa/barcelona-sample-hello
services:
- name: web
...
これらは次のような関係になります。
- アプリケーションのリポジトリに一つづつ
barcelona.yml
が作られる barcelona.yml
の中に複数のenvironments:
エントリがある(productionとstaging)envinronments:
エントリの数だけ、Heritageが作成される(hello-productionとhello-staging)- production用のHeritageとstaging用のHeritageでは、異なるパラメータが使用される
従って、上記のbarcelona.yml
を、main-production
とmain-staging
の二つのDistrictにDeployするとしたら、次のコマンドを使用します。
$ bcn create -e staging -d main-staging
これによって、barcelona.yml
の中の staging:
エントリの内容で Heritage が一つ作成され、それが、main-staging
District と関係づけられます。
次に、次のコマンドで本番用のHeritageを作成します。
$ bcn create -e production -d main-production
これによって、barcelona.yml
の中の production:
エントリの内容で Heritage が一つ作成され、それが、main-production
District と関係づけられます。
service と scheduled_tasks
一般的に、Webアプリケーションは、複数のプロセスで構成されています。Barcelonaでは、このプロセスは、barcelona.yml
の中のservices:
というエントリに記述します。
このserviceのエントリが、ECS Serviceに対応します。
また、常時稼働するのではなく、特定時刻や定周期で起動するバッチ処理は、scheduled_tasks:
というエントリに記述します。
services:
とscheduled_tasks:
はそれぞれ複数記述することができて、環境変数や後述するEndPointとの結びつきを変えることができます。これらは、ECSを直接使用する場合は、task definitionに対応するものですが、実際には、多くのパラメータを共有するケースが多いので、Barcelonaでは、Dockerイメージのパスなどの共通部分を Heritage として共有し、各サービスごとの記述は、最低限の異なる部分だけ記述すればいいようになっています。
結局、個別の項目はほとんど task definition内の項目と一対一に対応しているのですが、共通部分を複数回記述する必要がないので、最低限の記述で稼働できる合理的なものになっていると思います。
また、サービスごとのプロセス数の管理は、ECS Serviceに関連する Web Consoleや AWS CLI で行うことができます。
たとえば、負荷が高くなった時に、稼働状況を見たりプロセス数を増やしたりする処理は、AWS Web Consoleから行います。
ECS上でのサービス名称は、”(Heritage名)-(サービス名)-(postfix)” となるので、対応に迷うことはなく、ECSのドキュメントを見ながら行えます。
このアーキテクチャは、以下のように、アプリ開発者とインフラ担当双方にとって使いやすいものになっていると思います。
- アプリ開発者は、Heritage(=Dockerイメージを共有する範囲)で、一括して Deploy できる
- インフラ担当者は、ECS Serviceごとに、稼働状態に合わせたきめの細かい運用ができる
EndPoint
EndPointは、AWSのALBに対応するリソースで、サービスを外部に公開する時にしようします。次のコマンドで作成します。
$ bcn endpoint create --district=(District名) --public
そして、barcelona.yml
内のlisteners:
エントリの記述により、Deploy時に、ECS Serviceと結びつけられます。
services:
- name: web
...
listeners:
- endpoint: test-endpoint
health_check_interval: 10
health_check_path: /
rule_conditions:
- type: path-pattern
value: '*'
ほとんどのパラメータは、ALB の同名のパラメータに対応しており、CloudFormationのテンプレートとして、ALBと同期します。
ここは、AWSから直接ALBを使う場合と同じですが、違いは、このパラメータをHeritage単位で記述し管理できることです。
health_check_path
とかルーティング方法などのパラメータは、インフラというよりはアプリケーションに近いパラメータだと思いますので、このほうが自然だと思います。
Barcelonaのセキュリティモデル
BarcelonaのAPIサービスは、多くの場合、インターネットに公開するサービスとして運用されるので、セキュリティ面の安全性が重要です。これについて説明したいと思います。
ユーザの認証、認可
ユーザの認証、認可については、Barcelona自体ではユーザ管理をしないで、外部のサービスにまかせるようになっていて、現在のところ、github による認証、認可に対応しています。これは次のような単純なモデルです。
- githubで権限最小のアクセストークンを作成し、それでユーザを認証する
- District関連の処理は、Admin Team として指定したチームに所属しているメンバーのみに許可する
- それ以外の処理は、Developer Team として指定したチームに所属しているメンバーのみに許可する
bcn deploy
の処理だけは、Heritage単位のトークンによる認証でも許可する
多くの場合、Deployの処理は CI に組み込まれるので、外部のCIサービスにそのためのトークンを登録する必要があると思います。その場合に、ユーザ単位のトークンを使用すると、必要のない権限を渡してしまうことになるので、Barcelonaでは Heritage Token という仕組みを容易にしています。
このトークンでは、deploy 以外の処理はできないので、仮に漏れたとしても、AWSの環境を壊されたり情報を盗まれたりすることはありません。
Hashicorp Vault による認証、認可
githubの他に、HashicorpのVaultによる認証、認可の機能も開発しています。これはコードはほぼ完成していて、デジカ社内での評価の後に近々リリースされると思います。
- Vaultで作成したトークンによって認証を行う
- APIのパスとメソッド単位で、Vault内で使用を許可する
- Barcelonaは、実行時に、APIのパスとメソッド及びユーザのトークンを使って、Vaultに可否を問い合わせる
この方法だと、Districtごとに別の管理者グループを設定したり、Heritageごとユーザごとの使用許可ができて、キメの細かい運用が可能になります。
ssh セッションのタイムアウト管理
Barcelonaには、次の二つの ssh セッションを開設する機能があります。
- コンテナインスタンスへの ssh セッション
- Heritageの Docker イメージで新しいコンテナを起動して、そのコンテナ上でシェルなどの CLI ツールを起動する
特に、Rails APP を運用している場合は、後者の機能で、様々な Rakeタスクを実行できるので、かなり便利な機能です。
しかし、開発者のマシンにあるssh秘密鍵が盗まれてしまうと、これらの機能を不正に利用されてしまうので、非常に危険です。
そのため、以下のような方式で、不正利用を防いでいます。
- bcn のログイン処理で、専用の ssh キーペアを生成する
- 上記の ユーザの認証、認可の処理で確認した上で、SSHユーザー証明書を発行する
- sshdがセッション開設前に署名の有効期限を確認する
「有効期限つきの署名による認可」という機能は、sshdが標準で持っているもので、Barcelonaはこれを活用しています。
この仕組みにより、仮に秘密鍵が流出しても、githubやvault上でそのユーザのトークンを無効化すれば、最後に使用してから10分後にその秘密鍵が使えなくなります。
多重防護
仮に、Barcelonaや、それが使っているライブラリなどの脆弱性があり、Barcelona API がクラックされてしまった場合は、ある程度の被害は仕方ないですが、被害を最小限に止める工夫が必要です。
これについて、Barcelonaは次のようなモデルで設計されています。
- AWS Access Key などの credential は保存しない
- District ごとに Role を作成する
- AWS リソースの更新は、その Role の権限で(Assume Roleで)実行する
従って、仮にAPIサービスがクラックされたとしても、被害の範囲を限定できるようになっています。
- AWS Access Key などの credential が外部へ流出する危険性はない
- 攻撃者が、Assume Role を行わなければ、被害はかなり限定できる
- 仮に、Barcelonaのアーキテクチャを理解した攻撃者だった場合にも、被害をDistrictのRoleの範囲内に限定できる
Districtの作成、修正の場合には、 ほぼ全権を持つ credential を与える必要がありますが、その処理が終わった後には、credentialは捨てて、Roleを使用して処理を行います。
ということで、アーキテクチャ自体は、多重防護の考えに基づいた安全性の高いものだと思いますが、ソフトウエアそのものの実績は多くありませんので、使用の際はご自分で評価した上で、自己責任でお使いください。
また、デジカでは万が一の危険性を考えて、barcelonaのエンドポイントは、VPN経由でないとアクセスできないようにして運用しています。
シークレットの管理
Barcelonaでは、パスワードなどのシークレット情報は、環境変数経由でアプリケーションに引き渡しますが、このシークレット情報を、DBなどに保存することはありません。
シークレット情報は、以下の AWS サービスによって管理します。
- Systems Manager のパラメータストア
- AWS Secret Manager
いずれの場合も、シークレット情報のパスやARNを登録するようにして、値そのものは実行時に API で取得します。
パラメータストアの場合は、特定のパスへの権限の登録まで行いますので、この命名規則に従って運用すれば、IAM Policy などの変更も不要で、最小限の手間で安全にシークレットを管理することができます。
なお、上記サービスがリリースされる前は、KMSとS3を用いた独自のシークレット管理も行なっていました。まだ、互換性のためにこの機能も残っていますが、将来は、上記二つに統合されます。