プラットフォームエンジニアリングと内部開発者プラットフォーム:概念と応用
1. 概要
サービスの数が増えるにつれ、各チームは自力でCI/CD、インフラストラクチャ、ログ、セキュリティ、ドキュメント管理をしなければなりません。彼らは機能の実装ではなく、繰り返しのタスクに多くの時間を費やしています。プラットフォームエンジニアリングはこの問題を解決します。これらのタスクを**内部開発者プラットフォーム(IDP)**にまとめ、開発者がセルフサービスで利用できるようにします。
この記事を読み終わると、その概念と関心が高まっている理由を理解できます。Spring Bootスターター、サービスカタログ、シンプルなポリシーチェックを使用して、小さな「ゴールデンパス」を構築することもできます。記事の後半では、プラットフォムに投資すべきでない場合についても説明します。
2. プラットフォームエンジニアリングとは?
プラットフォームエンジニアリングは、専任チーム(プラットフォームチーム)がツール、プロセス、内部サービスのセットを構築・運用するものです。プロダクトチーム(ストリーム配置チーム)はこのセットを使用して、下層のインフラストラクチャの詳細を理解することなく、サービスを構築、デプロイ、運用できます。
2.1. 内部開発者プラットフォーム
IDPはプラットフォームチームが作成する成果物です。通常、単一のツールではありません。既存のものを統合するレイヤーです:Git、CI、Kubernetes、クラウド、シークレット管理、可観測性。開発者はポータル、CLI、テンプレート、またはリポジトリ内の設定ファイルを通じてこのレイヤーと対話します。
2.2. 3つのコア概念
- ゴールデンパス(舗装道): 一般的なタスク(例:「新しいRESTサービスを作成する」)を行うための推奨され、サポートされているパスです。このパスに従うと、速く安全です。別のパスを選ぶこともできますが、チームは責任を負います。
- セルフサービス: 開発者は誰かを待ったりチケットを開くことなく、サービス、データベース、またはテスト環境を自分で作成できます。
- プラットフォームアズプロダクト: 内部開発者が顧客です。プラットフォームチームは彼らにインタビューし、使用レベルを測定し、バックログを優先順位付けする必要があります。真の製品チームのようなものです。
2.3. DevOpsとの違いは?
DevOpsは文化です:コードを書くチームも運用を担当します(「you build it, you run it」)。プラットフォームエンジニアリングはDevOpsに取って代わりません。それは大規模でDevOpsを実現する方法です。各チームをKubernetes、Terraform、IAMの専門家にするのではなく、プラットフォームチームはその知識を使いやすいabstractionにパッケージします。
3. このトピックが注目されている理由
特定のツールに依存しない実際の理由があります:
- 認知負荷の増加: モダンなサービスはDockerfile、Kubernetesマニフェスト、パイプライン、ダッシュボード、アラート、セキュリティポリシー、シークレット設定が必要です。それぞれに多くの選択肢があります。
- チームごとに異なる方式: 共通の基準がないと、各サービスが異なる形式でログを記録し、メトリクスに異なる名前を付けます。インシデント発生時、オンコール担当者はそのサービスがどのチームに属するかを調べるのに苦労します。
- セキュリティとコンプライアンス要件: 依存関係のスキャン、アーティファクトへの署名、アクセス制御。ゴールデンパスに組み込まれていれば、これらは一貫して適用しやすいです。
- 遅いオンボーディング: 新入者は「サービスをどこに作成するか、デプロイ方法、ログを見る場所」を知る必要があります。優れたプラットフォームはこれらの質問に一か所で答えます。
4. IDPの一般的なコンポーネント
| コンポーネント | 役割 |
|---|---|
| サービスカタログ | サービス、オーナー、ドキュメント、依存関係のリスト |
| ソフトウェアテンプレート | 数個のパラメータで標準に従うプロジェクトを作成 |
| 共有CI/CD | ビルド、テスト、スキャン、デプロイを持つパイプラインが組み込み |
| インフラセルフサービス | 宣言的な設定でデータベース、キュー、バケットを作成 |
| デフォルト可観測性 | サービス起動時にログ、メトリクス、トレースが組み込み |
| ガードレール / ポリシー | 必須ルールを自動チェック |
最初から全てを揃える必要はありません。以下の節では3つの小さなピース(Spring Bootスターターによるゴールデンパス、カタログディスクリプタ、ポリシーチェック)を示します。
5. Spring Bootスターターによるゴールデンパス
Javaサービスを標準化する最も安い方法は、内部スターターをリリースすることです。このスターターを追加するサービスは、自動的に標準メタデータを取得します:名前、担当チーム、重要度。メタデータはアクチュエータを通じて公開されます。
5.1. 依存関係
内部スターターに必要な依存関係:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-actuator</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
アクチュエータはoptionalにマークされます。アクチュエータを使用しないサービスはまだスターターを使用できます。
5.2. プロパティとディスクリプタ
package dev.nonla.platform;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties("platform.service")
public record PlatformServiceProperties(String name, String owner, String tier) {
public PlatformServiceProperties {
if (name == null || name.isBlank()) {
throw new IllegalArgumentException("platform.service.name is required");
}
if (owner == null || owner.isBlank()) {
throw new IllegalArgumentException("platform.service.owner is required");
}
if (tier == null || tier.isBlank()) {
tier = "standard";
}
}
}
package dev.nonla.platform;
public record ServiceDescriptor(String name, String owner, String tier) {
}
Recordのコンパクトコンストラクタは2つのことを行います:
nameとownerの宣言を必須にします。どちらか一方を欠くと、アプリケーションは起動できません。これはガードレール:「孤児」サービスがありません。- 宣言されない場合、
tierにデフォルト値standardを割り当てます。
5.3. オートコンフィグレーション
package dev.nonla.platform;
import java.util.Map;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@AutoConfiguration
@EnableConfigurationProperties(PlatformServiceProperties.class)
public class PlatformServiceAutoConfiguration {
@Bean
@ConditionalOnMissingBean
ServiceDescriptor serviceDescriptor(PlatformServiceProperties props) {
return new ServiceDescriptor(props.name(), props.owner(), props.tier());
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(InfoContributor.class)
static class ActuatorConfiguration {
@Bean
InfoContributor platformInfoContributor(ServiceDescriptor descriptor) {
return builder -> builder.withDetail("platform", Map.of(
"name", descriptor.name(),
"owner", descriptor.owner(),
"tier", descriptor.tier()));
}
}
}
各部分の説明:
@ConditionalOnMissingBeanはサービスが独自のServiceDescriptorを定義できるようにします。これはゴールデンパスから「逃げ道」です。本当に必要な場合に使用します。- ネストされたクラス
ActuatorConfigurationは、InfoContributorがclasspathにある場合のみロードされます。こうすることで、アクチュエータを使用しないサービスはClassNotFoundExceptionに遭遇しません。
Spring Boot 3がこのオートコンフィグレーションを見つけるには、src/main/resources/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.importsに登録します:
dev.nonla.platform.PlatformServiceAutoConfiguration
サービス側、例えばorder-serviceは、スターターと数行の設定を追加するだけです:
platform.service.name=order-service
platform.service.owner=team-checkout
platform.service.tier=critical
management.endpoints.web.exposure.include=health,info
management.info.env.enabled=false
GET /actuator/infoを呼ぶと以下が返されます:
{"platform":{"name":"order-service","owner":"team-checkout","tier":"critical"}}
ここから、監視ツールまたはカタログはこのエンドポイントをスキャンして、誰がどのサービスを所有しているかを知ることができます。
5.4. スターターの動作をテストする
プラットフォームチームはスターターを徹底的にテストする必要があります。ここでのバグは、それを使用しているすべてのサービスに影響を与えます。ApplicationContextRunnerは、Webサーバー全体を起動することなく、オートコンフィグレーションを素早くテストするのに役立ちます:
package dev.nonla.platform;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.jupiter.api.Test;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.boot.autoconfigure.AutoConfigurations;
import org.springframework.boot.test.context.runner.ApplicationContextRunner;
class PlatformServiceAutoConfigurationTest {
private final ApplicationContextRunner runner = new ApplicationContextRunner()
.withConfiguration(AutoConfigurations.of(PlatformServiceAutoConfiguration.class));
@Test
void givenNameAndOwner_whenStarting_thenDescriptorUsesDefaultTier() {
runner.withPropertyValues(
"platform.service.name=order-service",
"platform.service.owner=team-checkout")
.run(ctx -> {
assertThat(ctx).hasSingleBean(ServiceDescriptor.class)
.hasSingleBean(InfoContributor.class);
assertThat(ctx.getBean(ServiceDescriptor.class).tier())
.isEqualTo("standard");
});
}
@Test
void givenMissingOwner_whenStarting_thenContextFails() {
runner.withPropertyValues("platform.service.name=order-service")
.run(ctx -> {
assertThat(ctx).hasFailed();
assertThat(ctx.getStartupFailure())
.rootCause()
.hasMessageContaining("platform.service.owner");
});
}
}
最初のテストはデフォルト値とInfoContributorの登録を確認します。2番目のテストはガードレールを確認します:ownerが欠けるとコンテキストは起動しません。エラーメッセージは欠落した属性を明確に示します。
6. サービスカタログとガードレール
6.1. リポジトリ内のカタログディスクリプタ
ほとんどのIDPはサービスのメタデータをリポジトリ内、コードのすぐそばに保存します。以下は、オープンソースソースコードポータルで一般的に見られるタイプのディスクリプタです:
apiVersion: backstage.io/v1alpha1
kind: Component
metadata:
name: order-service
description: 注文と支払い処理
tags:
- java
- spring-boot
spec:
type: service
lifecycle: production
owner: team-checkout
system: checkout
dependsOn:
- resource:orders-db
providesApis:
- order-api
このファイルをGitに保存することは2つの利点があります。メタデータはコードと一緒にレビューされ、忘れられたWikiページのように現実から外れることはありません。ポータルはこのファイルを読んで、オーナー、依存関係、およびサービスのAPIを表示するだけです。
6.2. シンプルなポリシーチェック
ガードレールは複雑なポリシーエンジンである必要はありません。スタートでは、CI内で実行されるコードの一部で、不正な設定をブロックするのに十分です:
package dev.nonla.platform.policy;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class ServicePolicy {
private static final Set<String> ALLOWED_TIERS = Set.of("critical", "standard", "experimental");
public record ServiceSpec(String name, String owner, String tier, int replicas) {
}
public List<String> violations(ServiceSpec spec) {
List<String> errors = new ArrayList<>();
if (!spec.name().matches("[a-z][a-z0-9-]{2,40}")) {
errors.add("name must be kebab-case, 3-41 chars");
}
if (!spec.owner().startsWith("team-")) {
errors.add("owner must be a team, not a person");
}
if (!ALLOWED_TIERS.contains(spec.tier())) {
errors.add("unknown tier: " + spec.tier());
}
if ("critical".equals(spec.tier()) && spec.replicas() < 2) {
errors.add("critical services need at least 2 replicas");
}
return errors;
}
}
入力new ServiceSpec("Order_Service", "alice", "critical", 1)の場合、関数は以下を返します:
[name must be kebab-case, 3-41 chars, owner must be a team, not a person, critical services need at least 2 replicas]
重要な点は、エラーメッセージが何を修正する必要があるかを明確に示す必要があります。良いガードレールは開発者をエラーの修正に導き、単に彼らをブロックするだけではありません。
7. 実践的な応用:どこから始めるか?
- 開発者にインタビューする。 彼らが最も時間を費やす場所を聞きます:新しいサービスの作成、環境のプロビジョニング待ち、またはパイプラインのデバッグ。最も興味深いツールではなく、最も痛い問題を優先します。
- 「最薄実行可能プラットフォーム」から始める。 しばしば必要なのは、テンプレートリポジトリ、共有パイプライン、ドキュメントページだけです。ポータルは後で来ることができます。
- 単一のゴールデンパスを選ぶ。 例えば「Spring BootのRESTサービスがKubernetesにデプロイ」。このパスをうまくいかせてから、拡張します。
- 測定する。 プラットフォームを使用するチーム数、リポジトリ作成から最初のデプロイまでの時間、および定期的な開発者フィードバックを追跡します。誰も自発的に使用しない場合、それは製品がまだ準備ができていないというシグナルです。
- 逃げ道を保つ。 ゴールデンパスは最も簡単なパスであり、唯一の必須パスではありません。セクション5.3のように、
@ConditionalOnMissingBeanはこの原則の小さな例です。 - バージョニングと廃止を明確に。 スターター、テンプレート、共有パイプラインはAPIです。互換性を破る変更は通知され、アップグレードロードマップが必要です。
8. よくある間違いと使用すべきでない時
よくある間違い:
- コンテンツの前にポータルを構築する。 美しいポータルでも、カタログが空でテンプレートが古いと、すぐに忘れられます。
- opsチームの名前をプラットフォームチームに変更する。 開発者がまだチケットを開いて待つ必要があるなら、それはセルフサービスではありません。
- abstractionが厚すぎる。 Kubernetesを完全に非表示にすると、問題が発生したときに開発者はデバッグできません。プラットフォームは認知負荷を減らすべきですが、下のレイヤーを見る能力を消してはいけません。
- ルールの強制。 実行する必要がある場合、人々はそれを使用しています。つまり、プラットフォームはまだ本当の問題を解決していません。
- 「すべてをする」スターター。 スターターが多くの依存関係と暗黙的な設定を引き込む場合、バージョンの競合を引き起こし、アップグレードが難しくなります。
注意: プラットフォームエンジニアリングには大きな固定コストがあります。プラットフォーム自体を構築、運用、サポートする人が必要です。
IDPに投資すべきでない場合:
- 小さな企業、少数のチーム、少数のサービス。良いREADMEと共有パイプラインで十分なことがあります。
- 製品がまだ製品市場フィットを探していて、アーキテクチャが頻繁に変わる。早すぎる標準化は、すぐに変わるべき選択肢を固定します。
- クラウドの管理サービスは既にニーズを十分に満たしている。同じレイヤーを再構築するだけで、メンテナンスが増えるだけです。
- プラットフォームのプロダクトオーナーになる意思のある人がいない。開発者の話を聞く人がいなければ、プラットフォームは誰も使いたくないツール集になりやすいです。
9. 結論
プラットフォームエンジニアリングは新しいツールではありません。それは組織の方法です:チームが内部開発者の経験を製品として見て、インフラ知識をゴールデンパスとセルフサービスにパッケージします。あなたは3つの小さなピースを見ました:メタデータを標準化するSpring Bootスターター(テスト付き)、リポジトリ内のカタログディスクリプタ、およびCI内で実行されるポリシーチェック。
小さく始め、実際に測定し、開発者が実際に使用する時だけ拡張します。組織がまだ小さい場合、優れたテンプレートと共有パイプラインが、完全なプラットフォームなしでほとんどの利点をもたらすことができます。