2025.08.26
オンプレからAWSへの移行で変化した運用方針
結論
- AWS Organizationsを導入して、管理アカウントのrootはMFAとサインイン検知で保護する
- Step Functionsでバッチジョブを任意のタイミングで実行する
- EKSをインプレースデプロイメントでアップグレードする
はじめに
こんにちは。次世代システム研究室のT.Tです。
現在、開発運用に携わっているWebサービスのシステム基盤のAWSへの移行を進めていて、全5フェーズ中の第3フェーズとなるバッチの移行まで完了しました。このフェーズの完了によりWebサービスの主要な機能の移行は完了したため、AWSでの運用が本格化してきていて、運用面での課題と向き合いながら運用しています。
本記事では、移行元のオンプレと異なる方針が必要となった運用上の課題について、その内容と運用方針についてご紹介します。
1.オンプレとAWSのシステム構成
オンプレのシステム構成
オンプレでは、踏み台サーバー経由でワークロード用のサーバー群にアクセス出来るようにしています。バッチジョブはLinuxのcronジョブでPHPアプリケーションを定期的に実行しています。WebサービスのフロントエンドはKubernetesクラスター内でPHPのアプリケーションコンテナを稼働していて、Kubernetesクラスターはrkeにより自前で環境を構築しています。
AWSのシステム構成
AWSでは、Organizationsによりマルチアカウント構成にしていて、一つのメンバーアカウントに本番環境やSTG環境等の環境を一つ割り当てています。バッチジョブはECS/EventBridgeの構成でPHPアプリケーションのコンテナを定期的に実行する仕組みです。WebサービスのフロントエンドはEKSクラスター内でPHPのアプリケーションコンテナを稼働していて、EKSクラスターはCDKを利用してaws-cdk-lib/aws-eksのCfnClusterで構築しています。
2.rootユーザーの保護
オンプレとAWSのrootユーザーの対比
オンプレでは、踏み台サーバーや各ワークロード用のサーバーのrootユーザーを保護の対象にしています。AWSではOrganizationsを利用しているため、管理アカウントとメンバーアカウントのrootユーザーを保護の対象にしています。オンプレとAWSではrootユーザーの仕組み自体が根本的に異なりますが、この項目ではこの両者について比較してみます。
オンプレでのrootユーザーの保護
オンプレでは、rootユーザーはsshでのログインとsuを禁止することでrootユーザーを保護しています。
AWSでのrootユーザーの保護
AWSでは以下の方針でrootユーザーを保護しています。AWSアカウントのルートユーザーのベストプラクティスを参考にしています。
- メンバーアカウントのrootユーザーは資格情報を削除してサインイン不可にする
- 管理アカウントのrootユーザーはMFAを設定してサインイン時に通知する
rootユーザーでのサインイン時の通知は、AWS ルートユーザーでのサインインをAWS Chatbotを使ってSlackに通知してみたで紹介されている内容で設定出来ます。
3.バッチジョブの実行
オンプレでのバッチジョブの実行
オンプレではバッチジョブはLinuxのcronジョブでPHPアプリケーションを定期的に実行する仕組みになっていて、任意のタイミングでバッチを実行したい場合は、Jenkins経由でバッチのPHPアプリケーションを実行しています。
AWSでのバッチジョブの実行
定期的に実行するバッチジョブは、ECSとEventBridgeにより実行していて、PHPのcronバッチをAWSに移行する方法でご紹介した構成で構築しています。任意のタイミングでバッチジョブを実行する場合は、Step Functionsを利用しています。実行したいバッチジョブを予めCDKで登録しておき、バッチジョブを実行する際はAWSコンソールにサインインしてStep Functionsを実行します。
CDKのStep Functionsのスタックは以下のようになっています。
export class SampleBatchJobStack extends cdk.Stack { constructor(scope: Construct, id: string, props: SampleBatchJobStackProps) { const taskDefinitionProps: ecs.CfnTaskDefinitionProps = { containerDefinitions: [ { essential: true, image: 'php-app', entryPoint: ['php'], name: 'sample-batch-job', ... }, ], }; const taskDefinition = new ecs.CfnTaskDefinition(this, 'sample-batch-job-td', taskDefinitionProps); taskDefinition.addDependency(ecsTaskExecutionRole); const stepFnDefinition = { "StartAt": "ValidateArgs", "States": { "ValidateArgs": { ... }, "MissingArgs" : { ... }, "RunEcsTask": { "Type": "Task", "Resource": "arn:aws:states:::ecs:runTask.sync", "QueryLanguage": "JSONata", "Arguments": { "Cluster": 'sample-batch-cluster', "TaskDefinition": taskDefinition.attrTaskDefinitionArn, ... } ... } } }; new stepfunctions.CfnStateMachine(this, 'run-sample-batch-job', { definitionString: JSON.stringify(stepFnDefinition), stateMachineType: 'STANDARD', stateMachineName: 'sample-batch-job', ... }); } }
実行画面は以下のようになります。
4.EKSのアップグレード
オンプレでのアップグレード
オンプレではKubernetesをブルーグリーンデプロイメントによりアップグレードしていました。rkeを利用していたため、ローリングアップデートによるサービス無停止更新も出来る環境ではありましたが、事前検証のために手軽に何度も検証環境を再構築する仕組みを用意するのが困難でした。一方、検証環境と本番環境にそれぞれ待機系を用意していたため、アップグレード前に稼働環境を待機系に切り替えて、現用系をアップグレードして正常稼働が確認出来るまで切り離しておけるブルーグリーンデプロイメントが環境的に実現し易かったのが採用していた理由です。
AWSでのアップグレード
AWSではインプレースデプロイメントによるアップグレードを採用しています。以下の手順によりアップグレードすることでサービスを無停止で更新することが出来ます。
- コントロールプレーンのアップグレード
- EKSアドオンのアップグレード
- EKSマネージドノードグループのアップグレード
コントロールプレーンのアップグレードはブルーグリーンデプロイメント、EKSマネージドノードグループのアップグレードは各ノードのローリングアップデートによる更新で無停止で更新されます。
AWSでは、CDKによりEKSクラスターを構築しているため、検証環境で更新元のバージョンを用意してインプレースデプロイメントによるアップグレードを容易に検証出来て、サービスの無停止更新を十分に検証して本番環境用の更新手順を事前に用意出来るため、インプレースデプロイメントを採用しました。AWS EKS Upgrade Checklistにバージョンごとの互換性情報等が事前にチェックする内容がまとまっているので、こちらの内容を確認してからアップグレードを進めます。
CDKのEKSクラスターとEKSアドオンのスタックは以下のようになっています。
export class EksClusterStack extends cdk.Stack { constructor(scope: Construct, id: string, props: EksClusterProps) { ... const eksCluster = new eks.CfnCluster(this, 'eks-cluster', { ... version: '1.XX', ... }); const eksClusterAddonCni = new eks.CfnAddon(this, `${prefixResource}-vpc-cni-addon`, { addonName: 'vpc-cni', addonVersion: 'v1.XX.X-eksbuild.X', ... }); eksClusterAddonCni.addDependency(eksCluster); const eksClusterAddonKubeProxy = new eks.CfnAddon(this, `${prefixResource}-kube-proxy-addon`, { addonName: 'kube-proxy', addonVersion: 'v1.XX.X-eksbuild.X', ... }); eksClusterAddonKubeProxy.addDependency(eksCluster); ... } }
コントロールプレーンをアップグレードする場合は、EKSクラスターのバージョンだけ更新してCDKを実行します。
export class EksClusterStack extends cdk.Stack { constructor(scope: Construct, id: string, props: EksClusterProps) { ... const eksCluster = new eks.CfnCluster(this, 'eks-cluster', { ... version: '1.YY', ... }); const eksClusterAddonCni = new eks.CfnAddon(this, `${prefixResource}-vpc-cni-addon`, { addonName: 'vpc-cni', addonVersion: 'v1.XX.X-eksbuild.X', ... }); eksClusterAddonCni.addDependency(eksCluster); const eksClusterAddonKubeProxy = new eks.CfnAddon(this, `${prefixResource}-kube-proxy-addon`, { addonName: 'kube-proxy', addonVersion: 'v1.XX.X-eksbuild.X', ... }); eksClusterAddonKubeProxy.addDependency(eksCluster); ... } }
$ npm run cdk:prod -- --profile prod deploy eks-cluster-stack
コントロールプレーンのアップグレード後に、EKSアドオンのバージョンを変更してCDKを実行してEKSアドオンをアップグレードします。その後、amazon-eks-ami/CHANGELOGからEKSのバージョンに最適化したAMIイメージの情報を取得して、EKSマネージドノードグループもアップグレードします。これで、EKSクラスターの更新が完了になります。
5.まとめ
今回は運用面で発生した課題についてご紹介しました。AWSで運用しながら残りの第4フェーズ以降の計画を立てながら設計開発を進めています。今後発生する課題への取り組みについても共有して行きたいと思います。
次世代システム研究室では、アプリケーション開発や設計を行うアーキテクトを募集しています。アプリケーション開発者の方、次世代システム研究室にご興味を持って頂ける方がいらっしゃいましたら、ぜひ 募集職種一覧 からご応募をお願いします。
皆さんのご応募をお待ちしています。
グループ研究開発本部の最新情報をTwitterで配信中です。ぜひフォローください。
Follow @GMO_RD