<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>インフラ &#8211; エンジニア見習い</title>
	<atom:link href="https://otonan-syusyoku.work/archives/tag/%E3%82%A4%E3%83%B3%E3%83%95%E3%83%A9/feed" rel="self" type="application/rss+xml" />
	<link>https://otonan-syusyoku.work</link>
	<description>三流プログラマー</description>
	<lastBuildDate>Thu, 12 Feb 2026 08:49:23 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://otonan-syusyoku.work/wp-content/uploads/2023/10/cropped-名称未設定のデザイン-16-32x32.png</url>
	<title>インフラ &#8211; エンジニア見習い</title>
	<link>https://otonan-syusyoku.work</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>TerraformとCDK のロールバックについて</title>
		<link>https://otonan-syusyoku.work/archives/2193</link>
					<comments>https://otonan-syusyoku.work/archives/2193#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Thu, 12 Feb 2026 08:49:22 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[IaC]]></category>
		<category><![CDATA[ポエム]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2193</guid>

					<description><![CDATA[どうも、PHPerです。 先日、友人のWebアプリケーションエンジニアとIaC（Infrastructure as Code）について話す機会がありました。 話の発端は「Terraform と CDK、どっちを採用すべき [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>どうも、PHPerです。</p>



<p>先日、友人のWebアプリケーションエンジニアとIaC（Infrastructure as Code）について話す機会がありました。 話の発端は「Terraform と CDK、どっちを採用すべきか？」というよくあるテーマだったのですが、友人からこんな強烈な一言をもらいました。</p>



<p><strong>「CDK一択でしょ。だってCDK（CloudFormation）はデプロイ失敗時に自動ロールバックしてくれるけど、Terraformは作りかけで止まるでしょ？怖くて使えないよ」</strong></p>



<p>なるほど、確かにその視点はあります。DBのトランザクションに慣れ親しんだエンジニアからすれば、「失敗したらAtomicに元に戻る」挙動こそが正義でしょう。</p>



<p>しかし、普段Terraformを触っている身からすると「うーん、それは『ロールバック』の定義や運用思想が違うだけでは？」というモヤモヤが残りました。</p>



<p>今回は、この「IaCにおけるロールバックと復旧戦略」について、公式ドキュメント等の情報を交えながら整理してみたいと思います。</p>



<h2 class="wp-block-heading">そもそも「ロールバック」とは何を指しているのか？</h2>



<p>友人が言っていた「ロールバック」は、主に <strong>「デプロイ（Apply）が途中でコケた時の挙動」</strong> を指しています。</p>



<h3 class="wp-block-heading">CDK の場合</h3>



<p>CDKは、コンパイルされると最終的に <strong>AWS CloudFormation テンプレート</strong> になり、デプロイもCFnを通じて行われます。</p>



<ul class="wp-block-list">
<li><strong>挙動</strong><br>スタックの更新中にエラーが発生すると、CFnは <strong>「自動的に直前の正常な状態に戻そう」</strong> とします。</li>



<li><strong>ステータス</strong><br><code>UPDATE_ROLLBACK_IN_PROGRESS</code> → <code>UPDATE_ROLLBACK_COMPLETE</code></li>
</ul>



<p>これはCloudFormationの基本機能として提供されています。<br> <a href="https://www.google.com/search?q=https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-monitor-rollback.html" target="_blank" rel="noreferrer noopener">スタックの更新のロールバック &#8211; AWS CloudFormation</a></p>



<p><strong>メリット</strong><br>何もしなくても「壊れた状態」で放置されることが少ないです。<br>「オール・オア・ナッシング」に近い挙動をするため、整合性が保たれやすい安心感があります。</p>



<p><strong>デメリット</strong><br>ロールバックには時間がかかります。<br>リソース作成に30分かかって失敗した場合、ロールバックにも同等の時間がかかることがあります。 <br>また、ロールバック自体が失敗する<strong>UPDATE_ROLLBACK_FAILED</strong>という状態に陥ると、手動での介入が必要になり、復旧難易度が跳ね上がります。 <br><a href="https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/using-cfn-updating-stacks-continueupdaterollback.html" target="_blank" rel="noreferrer noopener">参考: 更新のロールバックに失敗したスタックを更新できるようにする &#8211; AWS CloudFormation</a></p>



<h3 class="wp-block-heading">Terraform の場合</h3>



<p>Terraformは、AWSのAPIを直接叩いてリソースを操作します。</p>



<ul class="wp-block-list">
<li><strong>挙動</strong><br><code>terraform apply</code> 中にエラーが発生すると、<strong>その時点で処理が停止します</strong>。</li>



<li><strong>ステータス</strong><br>成功したリソースは作成済み、失敗したリソースは未作成（または中途半端）な状態で残ります。</li>
</ul>



<p>Terraformには「自動ロールバック」という機能自体が存在しません。これはバグではなく仕様（思想）です。 <br><a href="https://developer.hashicorp.com/terraform/cli/commands/apply" target="_blank" rel="noreferrer noopener">HashiCorp Terraform: Command: apply</a></p>



<p><strong>メリット</strong><br>エラーの原因が明確で、<strong>「ここまでは成功した」という状態がStateファイルに保存されます</strong>。 <br>コードを修正して再実行（<code>terraform apply</code>）すれば、<strong>差分のみ</strong>を適用し直すため、復旧までの時間が圧倒的に短いです。</p>



<p><strong>デメリット</strong><br>「中途半端な状態」が一時的に発生するため、手動でのリカバリ（State操作やTaint）が必要になる場合があります。</p>



<h2 class="wp-block-heading">なぜTerraformには自動ロールバックがないのか？</h2>



<p>Terraform派の意見として、「自動ロールバックがないこと」は <strong>「Fail Fast（早く失敗して早く直す）」</strong> という設計思想に基づいています。</p>



<ol start="1" class="wp-block-list">
<li><strong>Stateの一貫性重視</strong><br>Terraformは「現在のインフラの状態」をtfstateファイルで厳密に管理します。<br>勝手にロールバック（過去の状態に戻すAPIコール）を行うと、ローカルのコードと現実のリソースの乖離が複雑化するリスクがあります。 <a href="https://developer.hashicorp.com/terraform/language/state" target="_blank" rel="noreferrer noopener">State &#8211; Terraform by HashiCorp</a></li>



<li><strong>Roll Forward（前進による修復）の推奨</strong><br>インフラ変更において、失敗した場合は「元に戻す」よりも「修正して適用し直す」方が早いケースが多いです。<br>Terraformは <code>plan</code> コマンドで事前に変更内容を詳細に確認できるため、実行時エラーは「権限不足」や「パラメータ不正」などが主であり、コードを直して再Applyすれば済むことがほとんどです。</li>
</ol>



<h2 class="wp-block-heading">ロールバックというバージョン切り戻し</h2>



<p>友人の言う「デプロイ失敗時の自動復旧」ではなく、<strong>「正常にデプロイしたけどアプリが動かないから昨日の構成に戻したい」</strong> という場合のロールバックはどうでしょうか。</p>



<ul class="wp-block-list">
<li><strong>CDK (CloudFormation)</strong>: Gitで以前のコードに戻し、再デプロイします。 ※ CFnの自動ロールバックはあくまで「デプロイエラー時」の挙動であり、論理的なバグまでは戻してくれません。</li>



<li><strong>Terraform</strong>: Gitで以前のコードに戻し、<code>terraform apply</code> します。</li>
</ul>



<p>つまり、<strong>「正常デプロイ後の切り戻し（Revert）」に関しては、両者の手間に大きな差はありません。</strong> どちらも「Gitを正」として、過去のコミットの状態へインフラを収束させる作業になります。</p>



<h2 class="wp-block-heading">結論：どっちが良いのか？</h2>



<p>友人の「CDKの方が優秀」という意見は、<strong>「デプロイ作業中の安全性」</strong> に重きを置いた意見と言えます。特にアプリケーションエンジニアにとって、デプロイ失敗時に環境が散らかったままになるのは恐怖でしょう。</p>



<p>一方でTerraformは、<strong>「状態の透明性と制御」</strong> に重きを置いています。「勝手に戻るより、どこで止まったか教えてくれ。俺が直すから」というスタンスです。</p>



<h3 class="wp-block-heading">まとめ</h3>



<ul class="wp-block-list">
<li><strong>CDK (CloudFormation)</strong>
<ul class="wp-block-list">
<li><strong>特徴</strong>: 失敗時は自動で元通り（トランザクション的）。</li>



<li><strong>向いているケース</strong><br>インフラの詳細なステータス管理より、デプロイパイプラインの安定性を重視したい場合。ロールバック待ち時間が許容できる場合。</li>
</ul>
</li>



<li><strong>Terraform</strong>
<ul class="wp-block-list">
<li><strong>特徴</strong><br>失敗時はそこでストップ（即時停止）。修正して再実行（ロールフォワード）。</li>



<li><strong>向いているケース</strong><br><code>terraform plan</code> で事前に結果を予測し、失敗時も自分の手ですぐに復旧させたい場合。APIコールの速さを求める場合。</li>
</ul>
</li>
</ul>



<p>「ロールバックがあるからCDK」ではなく、「失敗時のリカバリ戦略として、自動復帰（CDK）と即時停止（Terraform）のどちらが自分たちのチームに合っているか」で選ぶのが正解ではないでしょうか。</p>



<p>私は……やっぱり <code>terraform plan</code> で「何が起きるか完全に把握してから実行する」安心感が好きですね（笑）</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2193/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【CDK】LAMP環境を題材に考えるスタック間のリソース共有が難しい問題を解決する</title>
		<link>https://otonan-syusyoku.work/archives/2194</link>
					<comments>https://otonan-syusyoku.work/archives/2194#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Wed, 14 Jan 2026 06:26:23 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Typescript]]></category>
		<category><![CDATA[実務]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2194</guid>

					<description><![CDATA[こんちゃーす。最近 CDK と格闘している PHPer です。 CDK、楽しいですよね。Typescript でインフラがかけるなんて最高です。でも、学習を始めていくとある壁にぶつかりました。 ネット上の「CDKでECS [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p>こんちゃーす。最近 CDK と格闘している PHPer です。</p>



<p>CDK、楽しいですよね。Typescript でインフラがかけるなんて最高です。でも、学習を始めていくとある壁にぶつかりました。</p>



<p>ネット上の「CDKでECS構築してみた！」系の記事を参考にすると、大抵の場合 <strong>VPCもECSもRDSも全部一つの</strong> <code>lib/my-stack.ts</code> <strong>に処理が書かれている」</strong>のです。サンプルコードとしてはわかりやすいのですが、いざ実務でLMAP環境を作ろうとすると⋯…</p>



<ul class="wp-block-list">
<li>VPC は更新頻度が低いからスタックを分けたい</li>



<li>RDS はステートフルだから独立させたい</li>



<li>ECS はアプリケーションコードのデプロイで頻繁に更新が入るかも</li>
</ul>



<p>とスタックを分割したくなりませんか？<br>でも、分割した途端に「StackA で作ったVPCのIDをどうやってStackBにわたすの？」という問題が発生します。</p>



<p>当記事では、現時点での私の持論である <strong>スタック間のリソース共有</strong>を紹介します。</p>



<p></p>



<h2 class="wp-block-heading">IDではなくオブジェクトを渡す</h2>



<p>VPC スタックとECSスタックを分けるときに、ついやってしまいがちなのが <strong>VPC IDを Props で渡す</strong> という手法です。<br>Terraform に慣れていると、この発想になりがちですよね。</p>



<p>この手法では CDK の 型安全を活かすことが出来ないので、 <strong>VPCオブジェクトそのもの</strong> を渡すのがベストだと考えています。</p>



<p>私の構成案：<br>司令塔となる エントリーポイント <code>app</code> がバケツリレーのようにオブジェクトを渡していくイメージです。</p>



<ol class="wp-block-list">
<li>VPCStack： VPCを作る。 <code>public readonly vpc</code>でVPCオブジェクトを公開する</li>



<li>APP： VpcStack からVPCオブジェクトをもらい、 ECSStackに渡す</li>



<li>EcsStack： 受け取ったVPC オブジェクトを使ってクラスターを作る</li>
</ol>



<p>実際にコードを見ていきましょう。</p>



<h2 class="wp-block-heading">型安全の恩恵を受ける</h2>



<h3 class="wp-block-heading">VPCStack ⇒ 渡す側</h3>



<p>ここでは <code>this.vpc</code>をクラスのメンバ変数として公開しておきます。</p>



<pre class="wp-block-code"><code>// lib/vpc-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import { Construct } from 'constructs';

export class VpcStack extends cdk.Stack {
  // 外部に公開するプロパティ（これが重要！）
  public readonly vpc: ec2.IVpc;

  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    this.vpc = new ec2.Vpc(this, 'LampVpc', {
      maxAzs: 2,
    });
  }
}</code></pre>



<p></p>



<h3 class="wp-block-heading">EcsStack ⇒ 受け取る側</h3>



<p>ここがポイントデス。 <code>vpcId: string</code>ではなく、 <code>vpc: ec2.IVpc</code> というインターフェース型で受け取ります。</p>



<pre class="wp-block-code"><code>// lib/ecs-stack.ts
import * as cdk from 'aws-cdk-lib';
import * as ec2 from 'aws-cdk-lib/aws-ec2';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import { Construct } from 'constructs';

// Propsの定義：文字列ではなく「VPCの型」を指定する
interface EcsStackProps extends cdk.StackProps {
  vpc: ec2.IVpc;
}

export class EcsStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props: EcsStackProps) {
    super(scope, id, props);

    // 文字列から検索する必要なし！そのまま使える
    const cluster = new ecs.Cluster(this, 'LampCluster', {
      vpc: props.vpc, 
    });
  }
}</code></pre>



<p></p>



<h3 class="wp-block-heading">bin/app.ts ⇒ 繋ぐ場所</h3>



<p>最後にエントリーポイントで紐づけます。</p>



<pre class="wp-block-code"><code>// bin/app.ts
import * as cdk from 'aws-cdk-lib';
import { VpcStack } from '../lib/vpc-stack';
import { EcsStack } from '../lib/ecs-stack';

const app = new cdk.App();

// 1. VPCを作る
const vpcStack = new VpcStack(app, 'VpcStack');

// 2. VPCオブジェクトをECSスタックに渡す
const ecsStack = new EcsStack(app, 'EcsStack', {
  vpc: vpcStack.vpc, // ここでバケツリレー
});</code></pre>



<p></p>



<h3 class="wp-block-heading">この方法の何が良いの？</h3>



<p>この方法のメリットは2つあると思っています。<br>（一言で言うとプログラミング言語による抽象化です。）</p>



<ol class="wp-block-list">
<li>圧倒的な型安全性<br>もし間違えてS3のオブジェクトを渡そうとすると、エディタがその場でエラーを吐いてくれます。<br>「デプロイしてみたらIDが違ってコケた」という悲劇が未然に防げます</li>



<li>記述がメチャ楽<br>受け取る側で <code>ec2.Vpc.fromLookup()</code> のようなインポート処理を書く必要がありません。渡された瞬間から <code>props.vpc.addInterfaceEndpoint</code> のようにメソッドが使えます</li>
</ol>



<p></p>



<h2 class="wp-block-heading">議論したいポイント</h2>



<p>ここまで「これが正解だ」という顔で書いてきましたが、実はこの構成には明確なデミリットもあります。それは、<strong> スタック同士が密結合</strong> になることです。</p>



<p>Cloudformation の <code>Export/Import</code> 機能でガッチリ紐づいてしまうため、以下のような問題が置きます。</p>



<ul class="wp-block-list">
<li>スタック間の参照があるため、「VPCだけを作り直したい」が出来ない</li>
</ul>



<p>ただし、私は上記のような問題が発生したとしても、<br>LAMP 環境という一つのアプリケーションにおいて、VPCとECSは「運命共同体」にあたるため問題ないと考えています。</p>



<p>「ECSが生きているのにVPCだけ消したい」という状況は稀ではないでしょうか。そのためこの<strong>密結合はあえて受け入れるべき仕様</strong>だと割り切っています。</p>



<p>ただ、もし組織の基盤となる共通のVPCを作る場合は、ライフサイクルが異なるため、疎結合なID渡しにするのが正解なのかもしれません。</p>



<p>皆さんの構成おしてほしいっす。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2194/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【CDKで構築】AWS ECS/RDSの監視・オートスケール基盤を自動構築しSlack通知する方法</title>
		<link>https://otonan-syusyoku.work/archives/2168</link>
					<comments>https://otonan-syusyoku.work/archives/2168#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Wed, 19 Nov 2025 04:44:12 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[業務]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[ECS]]></category>
		<category><![CDATA[RDS]]></category>
		<category><![CDATA[監視]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2168</guid>

					<description><![CDATA[ECS FargateとRDSでPHPアプリケーションを運用する際、リソースの異常検知や負荷に応じたスケール管理は必須です。 しかし、これらの仕組みを個別に手動で設定するのは手間がかかります。 本記事では、AWS CDK [&#8230;]]]></description>
										<content:encoded><![CDATA[<p><span class="">ECS FargateとRDSでPHPアプリケーションを運用する際、</span><span class="">リソースの異常検知や負荷に応じたスケール管理は必須です。<br />
</span><span class="">しかし、</span><span class="">これらの仕組みを個別に手動で設定するのは手間がかかります。</span></p>
<p><span class="">本記事では、</span><b class="">AWS CDK</b><span class="">（Cloud Development Kit）を用いて、</span><span class="">ECSとRDSの</span><b class="">監視アラーム</b><span class="">、</span><span class="">ECSの</span><b class="">オートスケーリング</b><span class="">、</span><span class="">そしてそのイベントを</span><b class="">Slack</b><span class="">に集約する通知基盤を<strong>IaC</strong>として一括で構築する方法を解説します。</span></p>
<p>&nbsp;</p>
<h2>監視・通知アーキテクチャの全体像</h2>
<p>採用するアーキテクチャは以下の通りです。</p>
<div class="table-block-component"></div>
<div>
<table style="border-collapse: collapse; width: 100%;">
<tbody>
<tr style="background-color: #5aabe8;">
<td style="width: 33.3333%;"><span style="color: #ffffff;">機能</span></td>
<td style="width: 33.3333%;"><span style="color: #ffffff;">AWSサービス</span></td>
<td style="width: 33.3333%;"><span style="color: #ffffff;">役割</span></td>
</tr>
<tr>
<td style="width: 33.3333%;"><b>監視</b></td>
<td style="width: 33.3333%;">CloudWatch</td>
<td style="width: 33.3333%;">メトリクスを収集・アラーム発報</td>
</tr>
<tr>
<td style="width: 33.3333%;"><b>自動応答</b></td>
<td style="width: 33.3333%;">Application Auto Scaling</td>
<td style="width: 33.3333%;">ECSの負荷に応じてタスク数を自動調整</td>
</tr>
<tr>
<td style="width: 33.3333%;"><b>通知ルーティング</b></td>
<td style="width: 33.3333%;">Amazon SNS</td>
<td style="width: 33.3333%;">アラームやスケーリングイベントを中継</td>
</tr>
<tr>
<td style="width: 33.3333%;"><b>通知先</b></td>
<td style="width: 33.3333%;">AWS Chatbot <span class="math-inline" data-math="\rightarrow">$\rightarrow$</span> Slack</td>
<td style="width: 33.3333%;">SNSからの通知を整形しSlackへ送信</td>
</tr>
</tbody>
</table>
</div>
<div></div>
<h2>事前準備: AWS ChatbotとSlackの連携</h2>
<p>CDKデプロイの前に、AWSアカウントとSlackを連携させるための<b>AWS Chatbotクライアント</b>を作成しておく必要があります。</p>
<ol start="1">
<li>AWS Chatbotコンソールで、<b>Slackワークスペース</b>と<b>チャンネル</b>を連携させます。</li>
<li>この設定により、通知先の<b>SlackチャンネルID</b>と<b>ワークスペースID</b>が取得できます。これらはCDKコード内で使用します。</li>
</ol>
<div class="sc_toggle_box">
<div class="sc_toggle_title">CDKでAWS Chatbot連携が完結しない理由</div>
<div class="sc_toggle_content">
<h3>1. 外部サービスとの認証（OAuthプロセス）が必要</h3>
<p>AWS Chatbotは、AWSアカウントと外部サービス（SlackやMicrosoft Teams）の連携を提供します。この連携は、Slack側でAWS Chatbotアプリをインストールし、AWSアカウントへのアクセスを許可するOAuth認証プロセスをユーザー（管理者）がブラウザ上で行う必要があります。</p>
<p>CDKはAWSアカウント内のリソースをプログラマブルにデプロイしますが、Slackワークスペース側の認証フローをプログラムで自動実行することは、セキュリティ上の理由からできません。</p>
<h3>2. AWS Chatbot Clientの事前設定が必要</h3>
<p>CDKのaws-chatbotモジュールで定義できるのは、連携が完了したワークスペースを参照し、その上でチャンネル設定（どのSNSトピックの通知をどのSlackチャンネルに送るか）を行う部分のみです。</p>
<p>CDKでnew chatbot.SlackChannelConfiguration(&#8230;)を作成する前に、通知先のSlackワークスペースID (slackWorkspaceId) を特定する必要があります。このワークスペースIDは、手動でChatbotコンソールからSlackとの連携を完了させた際に初めてAWS側に登録される情報です。</p>
</div>
</div>
<p>&nbsp;</p>
<h2>監視とオートスケール基盤の構築</h2>
<p>今回は、ECSサービスとRDSインスタンスが既に存在することを前提に、その監視・スケール設定を定義するCDKスタックを作成します。</p>
<h3>CDKスタックのセットアップ</h3>
<p>必要なCDKライブラリをインポートし、スタックを定義します。</p>
<p>&nbsp;</p>
<div>
<pre class="line-numbers"><code class="language-js">// lib/monitoring-stack.ts

import * as cdk from 'aws-cdk-lib';
import { Construct } from 'constructs';
import * as sns from 'aws-cdk-lib/aws-sns';
import * as cloudwatch from 'aws-cdk-lib/aws-cloudwatch';
import * as actions from 'aws-cdk-lib/aws-cloudwatch-actions';
import * as events from 'aws-cdk-lib/aws-events';
import * as targets from 'aws-cdk-lib/aws-events-targets';
import * as autoscaling from 'aws-cdk-lib/aws-applicationautoscaling';
import * as ecs from 'aws-cdk-lib/aws-ecs';
import * as chatbot from 'aws-cdk-lib/aws-chatbot';

// 環境依存の定数を定義 (適宜置き換えてください)
const CLUSTER_NAME = 'your-ecs-cluster-name';
const SERVICE_NAME = 'your-ecs-service-name';
const RDS_IDENTIFIER = 'your-rds-instance-identifier';
const SLACK_CHANNEL_ID = 'C01234567'; // 取得したチャンネルID
const SLACK_WORKSPACE_ID = 'T01234567'; // 取得したワークスペースID

export class MonitoringStack extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    // 既存のECSサービスを参照
    const ecsService = ecs.FargateService.fromFargateServiceAttributes(this, 'ExistingEcsService', {
      serviceArn: cdk.Stack.of(this).formatArn({
        service: 'ecs',
        resource: 'service',
        resourceName: `${CLUSTER_NAME}/${SERVICE_NAME}`,
        sep: '/',
      }),
      cluster: ecs.Cluster.fromClusterAttributes(this, 'ExistingCluster', {
        clusterName: CLUSTER_NAME,
        vpc: ({} as any), // 参照のため空オブジェクト
        securityGroups: [],
      }),
    });
  }
}</code></pre>
<p>&nbsp;</p>
</div>
<h3>通知用SNSトピックとChatbotの設定</h3>
<p>アラームやイベントが通知を送るSNSトピックを作成し、それをAWS Chatbotに連携させます。</p>
<p><response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c519592647="" class="ng-tns-c519592647-2197 ng-star-inserted"></code-block></response-element></p>
<div _ngcontent-ng-c519592647="" class="code-block ng-tns-c519592647-2197 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_a2c461939f765a9a&quot;,&quot;c_86e1e1d55e25872e&quot;,null,&quot;rc_330331dc660dc0f2&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c519592647="" class="code-block-decoration header-formatted gds-title-s ng-tns-c519592647-2197 ng-star-inserted"><span _ngcontent-ng-c519592647="" class="ng-tns-c519592647-2197"></span></div>
</div>
<pre class="line-numbers"><code class="language-js">// 1. 通知用SNSトピックの作成
    const alarmTopic = new sns.Topic(this, 'AlarmTopic', {
      displayName: 'System-Monitoring-Notifications',
    });

    // 2. AWS ChatbotとSNSトピックの連携 (Slackへのルーティング)
    new chatbot.SlackChannelConfiguration(this, 'ChatbotConfig', {
      slackChannelConfigurationName: 'EcsRdsMonitoring',
      slackWorkspaceId: SLACK_WORKSPACE_ID,
      slackChannelId: SLACK_CHANNEL_ID,
      notificationTopics: [alarmTopic], // このトピックへの通知がSlackに送られる
    });</code></pre>
<h3>ECSとRDSの監視アラーム設定</h3>
<p>主要なメトリクスに対してCloudWatchアラームを作成し、アクションとして上記SNSトピックを設定します。</p>
<pre class="line-numbers"><code class="language-js">// --- ECS Fargate アラーム ---
    // ECS CPU利用率が80%を超えた場合にアラーム
    const ecsCpuAlarm = new cloudwatch.Alarm(this, 'EcsCpuHighAlarm', {
      metric: cloudwatch.Metric.fromNamespaceAndName('AWS/ECS', 'CPUUtilization').with({
        dimensionsMap: { ClusterName: CLUSTER_NAME, ServiceName: SERVICE_NAME },
      }),
      threshold: 80, 
      evaluationPeriods: 2,
      comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
      alarmDescription: 'ECS Service CPU utilization is too high.',
    });
    ecsCpuAlarm.addAlarmAction(new actions.SnsAction(alarmTopic));
    // ecsCpuAlarm.addOkAction(new actions.SnsAction(alarmTopic)); // 復旧時も通知したい場合は追加

    // --- RDS アラーム ---
    // RDS CPU利用率が70%を超えた場合にアラーム
    const rdsCpuAlarm = new cloudwatch.Alarm(this, 'RdsCpuHighAlarm', {
      metric: cloudwatch.Metric.fromNamespaceAndName('AWS/RDS', 'CPUUtilization').with({
        dimensionsMap: { DBInstanceIdentifier: RDS_IDENTIFIER },
      }),
      threshold: 70, 
      evaluationPeriods: 3,
      comparisonOperator: cloudwatch.ComparisonOperator.GREATER_THAN_OR_EQUAL_TO_THRESHOLD,
      alarmDescription: 'RDS CPU utilization is too high.',
    });
    rdsCpuAlarm.addAlarmAction(new actions.SnsAction(alarmTopic));</code></pre>
<h3>ECSサービスのオートスケーリング設定</h3>
<p>ECSサービスをCPU利用率$60%$を目標に自動スケーリングするように設定します。</p>
<p><response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c519592647="" class="ng-tns-c519592647-2199 ng-star-inserted"></code-block></response-element></p>
<div _ngcontent-ng-c519592647="" class="code-block ng-tns-c519592647-2199 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_a2c461939f765a9a&quot;,&quot;c_86e1e1d55e25872e&quot;,null,&quot;rc_330331dc660dc0f2&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c519592647="" class="code-block-decoration header-formatted gds-title-s ng-tns-c519592647-2199 ng-star-inserted">
<pre class="line-numbers"><code class="language-js">// 3. ECSサービスのオートスケーリング設定
    // サービスのDesired Countをスケーリング対象にする
    const scalableTarget = autoscaling.ScalableTarget.forEcsService(this, 'EcsScalableTarget', {
      service: ecsService,
      minCapacity: 1, // 最小タスク数
      maxCapacity: 5, // 最大タスク数
    });

    // 目標CPU利用率 60% を維持するように調整するターゲット追跡スケーリングポリシー
    scalableTarget.scaleToTrackMetric('CpuScalingPolicy', {
      metric: ecsService.metricCpuUtilization(), 
      targetValue: 60,
      scaleOutCooldown: cdk.Duration.seconds(60),
      scaleInCooldown: cdk.Duration.seconds(300),
    });</code></pre>
</div>
<h3>オートスケールイベントのSlack通知設定</h3>
<p>スケーリング操作の成功イベントをEventBridgeで捕捉し、Slackに通知します。</p>
<p><response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c519592647="" class="ng-tns-c519592647-2200 ng-star-inserted"></code-block></response-element></p>
<div _ngcontent-ng-c519592647="" class="code-block ng-tns-c519592647-2200 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_a2c461939f765a9a&quot;,&quot;c_86e1e1d55e25872e&quot;,null,&quot;rc_330331dc660dc0f2&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c519592647="" class="code-block-decoration header-formatted gds-title-s ng-tns-c519592647-2200 ng-star-inserted">
<pre class="line-numbers"><code class="language-js">// 4. オートスケールイベントを捕捉しSNSトピックへルーティング
    new events.Rule(this, 'AutoScalingNotificationRule', {
      description: 'Notify Slack on successful ECS Auto Scaling events.',
      eventPattern: {
        source: ['aws.application-autoscaling'],
        detailType: ['Application Auto Scaling Policy Execution'],
        detail: {
          // 成功したスケーリング操作のみを通知
          statusCode: ['Successful'], 
        },
      },
      // ターゲットは既存のSNSトピック
      targets: [new targets.SnsTopic(alarmTopic)],
    });</code></pre>
</div>
<h2>まとめ</h2>
<p>CDKを使うことで、<b>TypeScriptコード</b>で以下の堅牢な運用基盤を構築できました。</p>
<ol start="1">
<li>ECS/RDSの重要なメトリクス監視</li>
<li>CloudWatch AlarmsとChatbotによるSlackへの即時通知</li>
<li>ECSサービスの負荷に応じた自動スケーリング（スケールイン/アウト）</li>
<li>スケーリング操作完了時の通知</li>
</ol>
<p>インフラをコード化することで、再現性、バージョン管理、レビューが可能になり、システムの信頼性が大きく向上します。</p>
<div _ngcontent-ng-c519592647="" class="code-block-decoration header-formatted gds-title-s ng-tns-c519592647-2200 ng-star-inserted"></div>
</div>
</div>
<p>&nbsp;</p>
<div></div>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2168/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>unprivileged かつ distroless なイメージ について理解しようぜ</title>
		<link>https://otonan-syusyoku.work/archives/2156</link>
					<comments>https://otonan-syusyoku.work/archives/2156#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Wed, 19 Nov 2025 03:50:41 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[nginx]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2156</guid>

					<description><![CDATA[コンテナ技術は現代のアプリケーション開発・運用に欠かせないものとなりましたが、コンテナを運用する上で「セキュリティ」と「イメージサイズ」は常に大きな課題として立ちはだかります。 あなたのコンテナイメージは本当に安全で、そ [&#8230;]]]></description>
										<content:encoded><![CDATA[<div _ngcontent-ng-c2987940602="" inline-copy-host="" class="markdown markdown-main-panel stronger enable-updated-hr-color" id="model-response-message-contentr_c303978cc0204370" aria-live="polite" aria-busy="false" dir="ltr">
<p>コンテナ技術は現代のアプリケーション開発・運用に欠かせないものとなりましたが、コンテナを運用する上で「セキュリティ」と「イメージサイズ」は常に大きな課題として立ちはだかります。</p>
<p>あなたのコンテナイメージは本当に安全で、そして最小限に保たれていますか？</p>
<p>&nbsp;</p>
<p>多くの開発者は、手軽さからコンテナを<code>root</code>（管理者）権限で実行し、また開発やデバッグに不要なOSパッケージをそのまま含めてしまいがちです。</p>
<p>これが、<b>セキュリティリスクの増大</b>と、<b>イメージサイズの肥大化</b>を招く原因となります。</p>
<p>この記事では、この課題を解決するための <strong>Unprivileged（非特権ユーザー実行）</strong>と<strong>Distroless（ディストロレス）</strong>イメージについて、その概念から実践的な使い方、そして運用上の注意点までを解説します。</p>
<h2>そもそも「Unprivileged」と「Distroless」とは何か？</h2>
<h3>1. Unprivileged (非特権ユーザー実行) とは？</h3>
<p><b>Unprivileged実行</b>とは、「最小特権の原則」をコンテナで実践することです。</p>
<ul>
<li><b>定義<br />
</b>コンテナ内のメインプロセスを<code>root</code>ユーザー（UID 0）ではなく、一般ユーザー（非特権ユーザー）として実行すること。</li>
<li><b>メリット<br />
</b>コンテナが何らかの理由で侵害されたり、エスケープ（コンテナの外側にあるホストOSへの侵入）が試みられたりした場合でも、プロセスが持つ権限が最小限に抑えられます。<br />
これにより、ホストシステムへの影響範囲を劇的に制限できます。</li>
<li><b>実現方法<br />
</b>Dockerfile内で<code>USER</code>命令を使用して、コンテナの実行ユーザーを切り替えます。</li>
</ul>
<h3>2. Distroless (ディストロレス) イメージとは？</h3>
<p><b>Distrolessイメージ</b>とは、セキュリティとサイズを極限まで追求したベースイメージです。</p>
<ul>
<li><b>定義<br />
</b>OSディストリビューション（Linux）の要素、具体的には<code>bash</code>、<code>apt</code>、<code>ls</code>といった一般的なシェルやパッケージ管理ツール<b>を</b>意図的に含めない、アプリケーション実行に必要な最小限のランタイムとライブラリのみで構成されたコンテナイメージです。</li>
<li><b>「通常のイメージ」との違い<br />
</b>通常のイメージ（例: <code>ubuntu:latest</code>）には多くの汎用ツールが含まれていますが、Distrolessイメージにはそれらが一切含まれていません。デバッグツールさえないため、その名（Distro-less = ディストリビューションがない）が示す通り、極めて軽量です。</li>
</ul>
<p><a href="https://otonan-syusyoku.work/archives/1674/lets-go" rel="attachment wp-att-1680"><img fetchpriority="high" decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO.png" alt="Let&#96;s GO" width="1000" height="500" class="aligncenter size-full wp-image-1680" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO.png 1000w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO-300x150.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO-768x384.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></a></p>
<h2>Unprivileged &amp; Distroless を組み合わせるメリット</h2>
<p>この二つのアプローチを組み合わせることで、コンテナの運用は次のレベルに引き上げられます。</p>
<h3>1. 圧倒的なセキュリティ向上</h3>
<ul>
<li><b>攻撃対象領域の削減<br />
</b>Distroless化により、イメージに含まれるバイナリやライブラリの数が最小限になります。<br />
その結果、<b>既知の脆弱性（CVE）を持つ可能性のあるパッケージが激減</b>し、攻撃対象となる領域を大幅に縮小します。</li>
<li><b>権限昇格リスクの低減<br />
</b>Unprivileged実行により、仮にコンテナに侵入されても、攻撃者がホストシステムで<code>root</code>権限を得るのが極めて困難になります。</li>
</ul>
<h3>2. イメージサイズの劇的な削減</h3>
<ul>
<li>不要なOSレイヤーやツールがないため、イメージサイズは通常のベースイメージに比べて<b>数百MB単位で削減</b>されることがあるらしい</li>
<li>これにより、レジストリへのプッシュ/プル時間が短縮され、デプロイ時間の高速化、そしてストレージコストの削減につながります。</li>
</ul>
<h3>3. ビルド時間の短縮と効率化</h3>
<p>ベースイメージが小さくなることで、CI/CDパイプラインでのダウンロード時間が短縮され、コンテナのビルドプロセス全体が効率化されます。</p>
<h2>Unprivileged &amp; Distroless なコンテナの使い方</h2>
<p>Unprivileged &amp; Distrolessを実現する最も一般的な方法は、<b>マルチステージビルド</b>を利用することです。</p>
<h3>1. Distroless イメージの基本構造</h3>
<ol start="1">
<li><b>Build Stage<br />
</b>開発に必要なコンパイラ、パッケージマネージャなどを含む通常のイメージ（例: <code>golang:latest</code>, <code>node:latest</code>）を使い、アプリケーションのビルドや依存関係のインストールを行います。</li>
<li><b>Final Stage<br />
</b>Distrolessイメージをベースとし、Build Stageで生成された<b>最終的なバイナリ</b>や<b>必要なランタイムファイル</b>だけをコピーします。</li>
</ol>
<h3>2. Unprivileged実行の設定</h3>
<p>Distrolessイメージの多く（例: <code>gcr.io/distroless/...</code>）は、セキュリティのため、デフォルトで<code>非root</code>ユーザー（例: <code>nonroot</code>）が設定されています。</p>
<p>もしご自身でベースイメージから構築する場合、Dockerfileに以下のコマンドを追加してユーザーを切り替えます。</p>
<p><response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c519592647="" class="ng-tns-c519592647-2060 ng-star-inserted"></code-block></response-element></p>
<div _ngcontent-ng-c519592647="" class="code-block ng-tns-c519592647-2060 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_c303978cc0204370&quot;,&quot;c_2e6b4137d5b58b23&quot;,null,&quot;rc_9ed9dae3948441fd&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c519592647="" class="formatted-code-block-internal-container ng-tns-c519592647-2060">
<div _ngcontent-ng-c519592647="" class="animated-opacity ng-tns-c519592647-2060">
<pre _ngcontent-ng-c519592647="" class="ng-tns-c519592647-2060"># ユーザーを作成し、パーミッションを設定
RUN adduser --disabled-password --gecos "" appuser

# 実行ユーザーを一般ユーザーに切り替える
USER appuser</pre>
</div>
</div>
</div>
<p><a href="https://otonan-syusyoku.work/archives/1519/%e3%81%ae" rel="attachment wp-att-1540"><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2023/12/の.png" alt="title" width="1000" height="500" class="aligncenter size-full wp-image-1540" srcset="https://otonan-syusyoku.work/wp-content/uploads/2023/12/の.png 1000w, https://otonan-syusyoku.work/wp-content/uploads/2023/12/の-300x150.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2023/12/の-768x384.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></a></p>
<h2>運用上の注意点：非特権ユーザーとポート番号の壁</h2>
<p>UnprivilegedコンテナをECS（または任意のLinuxホスト）で運用する際、セキュリティの恩恵と引き換えに、ある<b>実用上の課題</b>に直面します。</p>
<h3>1. ポート1024未満のバインドはroot権限が必要</h3>
<p>Linux OSのセキュリティ原則により、Webサーバーなどで一般的に使われる<b>ポート番号1024未満</b>（ウェルノウンポート、例: HTTPの<code>80</code><b>、</b>HTTPSの<code>443</code><b>）にバインドするには、</b><code>root</code>権限（特権）が必要です。</p>
<p>あなたがECSでUnprivilegedかつDistrolessなNginxイメージを使った際、Nginxがデフォルトで<code>80</code>番ポートを使おうとすると、権限がないため「<b>Permission Denied</b>」といった<b>エラーが発生し、コンテナの起動に失敗</b>してしまいます。</p>
<h3>2. ECS/Fargate環境での具体的な解決策</h3>
<p>この問題を解決しつつ、Unprivilegedのセキュリティメリットを維持するには、以下の方法が最も推奨されます。</p>
<ol start="1">
<li>アプリケーション側のポート変更:Webサーバー（Nginxなど）の設定ファイルを変更し、1024番以上のポート（例: 8080、8081）で待ち受けるように設定を変更します。</li>
<li><b>ロードバランサー (ALB) でのポートマッピング:</b>
<ul>
<li>ECS（Fargate）の<b>タスク定義</b>で、コンテナが使用するポートを<code>8080</code>などに設定します。</li>
<li><b>Application Load Balancer (ALB)</b> を利用し、ALBは外部からのアクセスを<code>80</code>番（標準HTTP）で受けます。</li>
<li>ALBは、内部のECSタスクグループに対しては<code>8080</code>番でルーティングします。</li>
</ul>
</li>
</ol>
<p>これにより、コンテナ内部は非特権ユーザーで安全に運用され、外部ユーザーはポート番号を意識することなくWebサイトにアクセスできるようになります。</p>
<h2>次世代コンテナの標準へ</h2>
<p>UnprivilegedとDistrolessの組み合わせは、<b>コンテナのセキュリティ基準</b>と<b>デプロイ効率</b>を大きく引き上げる、次世代コンテナの標準パターンです。</p>
<ul>
<li><b>Distroless:</b> 攻撃対象領域とイメージサイズを最小化。</li>
<li><b>Unprivileged:</b> 権限昇格リスクを最小化。</li>
</ul>
<p>特にECSなどの本番環境で運用する際は、<b>ポート1024未満の制限</b>に注意を払い、<b>ロードバランサー</b>を活用して安全かつ効率的な構成を構築しましょう。</p>
<p>あなたのプロジェクトも、今日からこの強力なセキュリティパターンに移行してみませんか？</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2156/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>ホストゾーンを一応理解しておこう</title>
		<link>https://otonan-syusyoku.work/archives/2158</link>
					<comments>https://otonan-syusyoku.work/archives/2158#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Thu, 06 Nov 2025 02:37:11 +0000</pubDate>
				<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[インフラ]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2158</guid>

					<description><![CDATA[DNSの設定をしようとすると、必ず出てくる「ホストゾーン（Hosted Zone）」という言葉。 「ドメイン名はわかる。example.com のことだろ？ でもホストゾーンって何？ ドメイン名と何が違うの？」 わかりま [&#8230;]]]></description>
										<content:encoded><![CDATA[<div _ngcontent-ng-c1527710506="" inline-copy-host="" class="markdown markdown-main-panel stronger enable-updated-hr-color" id="model-response-message-contentr_eddbbc8edbcbe50f" aria-live="off" aria-busy="false" dir="ltr">
<p>DNSの設定をしようとすると、必ず出てくる「ホストゾーン（Hosted Zone）」という言葉。</p>
<p>「ドメイン名はわかる。<code>example.com</code> のことだろ？ でもホストゾーンって何？ ドメイン名と何が違うの？」</p>
<p>わかります。名前が似ているからこそ、ややこしいですよね。</p>
<p>大丈夫です。この2つは「表札」と「住所録」の関係だと考えれば、一瞬で理解できます。</p>
<h2>ドメイン名 ＝「表札」</h2>
<p>これは簡単ですね。</p>
<p>ドメイン名（DomainName）は、インターネット上の「家の表札」です。</p>
<ul>
<li><code>google.com</code></li>
<li><code>example.co.jp</code></li>
</ul>
<p>これがないと、誰もあなたのウェブサイトやブログにたどり着けません。</p>
<p>しかし、表札だけでは不十分です。</p>
<p>表札（<code>example.com</code>）があっても、「その家のどの部屋に行けばいいのか？」「郵便物（メール）はどのポストに入れればいいのか？」がわからないからです。</p>
<h2>ホストゾーン ＝「住所録」</h2>
<p>そこで登場するのが <b>ホストゾーン（Hosted Zone）</b> です。</p>
<p>ホストゾーンは、そのドメイン名（表札）に関する「指示書」や「住所録」をまとめておくファイル（コンテナ）のことです。</p>
</div>
<p>&nbsp;</p>
<p>一言で言うと<strong>ホストゾーンとは、「DNSレコードの集合体」です。</strong></p>
<div _ngcontent-ng-c1527710506="" inline-copy-host="" class="markdown markdown-main-panel stronger enable-updated-hr-color" id="model-response-message-contentr_eddbbc8edbcbe50f" aria-live="off" aria-busy="false" dir="ltr">
<p>example.com という表札（ドメイン）には、example.com のためのホストゾーン（住所録）が必ず1セットで存在します。</p>
<h2>「住所録」には何が書かれている？</h2>
<p>この「住所録」（ホストゾーン）の中には、具体的な指示書である「レコード」がたくさん入っています。</p>
<p>あなたが気になっていた A や CNAME は、このレコードの種類のことですね。</p>
<ul>
<li><b>Aレコード (Address Record):</b>
<ul>
<li><b>役割:</b> 「部屋番号」です。</li>
<li><b>指示内容:</b> 「<code>www.example.com</code> へのアクセスは、<code>192.0.2.1</code> というIPアドレス（サーバーの場所）に案内しろ」</li>
</ul>
</li>
<li><b>CNAME (Canonical Name Record):</b>
<ul>
<li><b>役割:</b> 「別名」や「転送先」の指定です。</li>
<li><b>指示内容:</b> 「<code>shop.example.com</code> へのアクセスは、<code>www.example.com</code> と同じ場所（Aレコード）を見ろ」</li>
</ul>
</li>
<li><b>TXT (Text Record):</b>
<ul>
<li><b>役割:</b> 「メモ書き」や「本人確認」欄です。</li>
<li><b>指示内容:</b> 「このドメインの持ち主は私です（GoogleやMicrosoftへの本人証明）」や「迷惑メールじゃない証拠（SPF）」を書いておきます。</li>
</ul>
</li>
<li><b>MXレコード (Mail Exchanger):</b>
<ul>
<li><b>役割:</b> 「郵便ポスト」の指定です。</li>
<li><b>指示内容:</b> 「<code>example.com</code> 宛のメールは、<code>mail.google.com</code> というメールサーバーに送れ」</li>
</ul>
</li>
</ul>
<h2>念の為これだけ覚えておこう</h2>
<ul>
<li><b>ドメイン名:</b>
<ul>
<li><code>example.com</code> という「名前」（表札）</li>
</ul>
</li>
<li><b>ホストゾーン:</b>
<ul>
<li><code>example.com</code> に関する「設定ファイルの入れ物」（住所録）</li>
<li>中身は <b>レコード（A, CNAME, TXT, MXなど）の集合体</b></li>
</ul>
</li>
</ul>
<p>DNSを設定するということは、ドメイン名（<code>example.com</code>）のホストゾーン（住所録）を開いて、「<code>www</code> のAレコード（部屋番号）を <code>1.2.3.4</code> に書き換える」といった作業をすることだったんですね。</p>
<p>これで「ホストゾーン」と聞いても、もう身構える必要はありません！</p>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2158/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Laravel+Inertia+ReactをECSで動かしたい！ NginxとPHP-FPMのコンテナ分離</title>
		<link>https://otonan-syusyoku.work/archives/2153</link>
					<comments>https://otonan-syusyoku.work/archives/2153#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Tue, 28 Oct 2025 05:24:52 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[インフラ]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[React]]></category>
		<category><![CDATA[ネットワーク]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2153</guid>

					<description><![CDATA[Laravel + Inertia (React/Vue) の組み合わせは、SPA（シングルページアプリケーション）の体験とサーバーサイド（Laravel）の書きやすさを両立できる、非常に強力な構成です。 しかし、ローカ [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>Laravel + Inertia (React/Vue) の組み合わせは、SPA（シングルページアプリケーション）の体験とサーバーサイド（Laravel）の書きやすさを両立できる、非常に強力な構成です。</p>
<p>しかし、ローカルのDocker Compose環境では問題なく動作しても、AWSのECS Fargateのような本番環境にデプロイしようとすると、いくつかの疑問に直面します。</p>
<ul>
<li>「NginxとPHP-FPMコンテナはどう連携させるのがベスト？」</li>
<li>「<code>fastcgi_pass</code> ってローカルと設定を変える必要ある？」</li>
<li>「InertiaでビルドしたReactのJS/CSSファイルは、結局どのコンテナに置くのが正解？」</li>
</ul>
<p>この記事では、<code>fastcgi_pass</code> の基本から、ECS Fargateでの最適なコンテナ構成、そしてInertia/Reactプロジェクト特有の「静的ファイルの配置場所」問題までを、順を追って解説します。</p>
<h2>fastcgi_pass とは？</h2>
<h3>NginxとPHP-FPMの「橋渡し」</h3>
<p>まず基本のおさらいです。Nginxは高性能なWebサーバーですが、それ自体はPHPコードを実行できません。<br />
一方、PHP-FPMはPHPコードを実行することに特化したプロセス（サーバー）です。</p>
<p>Nginxがクライアント（ブラウザ）から <code>.php</code> ファイルへのリクエストを受け取ったとき、そのリクエストをPHP-FPMに処理してもらう必要があります。</p>
<p>このとき、Nginxの設定ファイル（<code>nginx.conf</code>）で、<b>「どのPHP-FPMに処理を依頼するか」の宛先を指定する</b>のが <code>fastcgi_pass</code> ディレクティブです。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-470 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-470">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-470">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-470"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-470"><span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
    <span class="hljs-comment"># ...</span>
    <span class="hljs-comment"># ↓ この一行が「橋渡し」の指定</span>
    <span class="hljs-attribute">fastcgi_pass</span> php-fpm-server:<span class="hljs-number">9000</span>; 
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h2>ローカル開発 (Docker Compose) での構成</h2>
<p>ローカル開発では、「1コンテナ1責務」の原則に従い、NginxとPHP-FPMを別々のコンテナとして <code>docker-compose.yml</code> で定義するのが一般的です。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-471 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-471">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-471">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-471"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-471"><span class="hljs-comment"># docker-compose.yml (抜粋)</span>
<span class="hljs-attr">version:</span> <span class="hljs-string">'3'</span>
<span class="hljs-attr">services:</span>
  <span class="hljs-comment"># Nginxコンテナ</span>
  <span class="hljs-attr">nginx:</span>
    <span class="hljs-attr">image:</span> <span class="hljs-string">nginx:alpine</span>
    <span class="hljs-attr">ports:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">"80:80"</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./nginx.conf:/etc/nginx/conf.d/default.conf</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./laravel-project:/var/www/html</span>

  <span class="hljs-comment"># PHP-FPMコンテナ</span>
  <span class="hljs-attr">php:</span>
    <span class="hljs-attr">build:</span> <span class="hljs-string">.</span> <span class="hljs-comment"># PHP-FPMとLaravelコードを含むDockerfile</span>
    <span class="hljs-attr">volumes:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-string">./laravel-project:/var/www/html</span>
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p>この構成では、NginxコンテナとPHPコンテナは <b>別々のネットワーク空間</b> に存在します。Docker Composeが提供する内部ネットワークを介して、サービス名で通信します。</p>
<p>したがって、NginxコンテナからPHPコンテナへは、<code>php</code> というサービス名（ホスト名）を使ってアクセスします。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-472 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-472">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-472">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-472"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-472"><span class="hljs-comment"># nginx.conf (Docker Compose用)</span>
<span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
    <span class="hljs-comment"># ...</span>
    <span class="hljs-comment"># 'php' サービス（コンテナ）の 9000番ポートに転送</span>
    <span class="hljs-attribute">fastcgi_pass</span> php:<span class="hljs-number">9000</span>;
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p>（※ このTCP/IP通信によるパフォーマンスやセキュリティの懸念は、コンテナ間通信がDockerの内部ネットワークに限定されていれば、実用上ほとんど問題になりません。）</p>
<h2>【1タスク2コンテナ】本番 (ECS Fargate) での構成：</h2>
<p>さて、本題のECS Fargateです。<br />
ECSでは、<code>docker-compose.yml</code> に相当するものとして「タスク定義 (Task Definition)」を使います。</p>
<p>ここで重要なキーポイントは、<strong>「1つのタスク定義の中で、NginxコンテナとPHP-FPMコンテナの2つを定義する」</strong>ことです。</p>
<p><strong>これら2つのコンテナは、1セットで「Webアプリケーションサーバー」</strong>として機能します。</p>
<h3>ECS Fargateでのfastcgi_passはどうなる？</h3>
<p>ECSの同一タスク内で実行されるコンテナは、<b>同じネットワーク空間（ネットワークモード <code>awsvpc</code>）を共有します</b>。</p>
<p>これは、Nginxコンテナから見ると、PHP-FPMコンテナが「他人」ではなく、「自分自身（localhost）」として見えることを意味します。</p>
<p>したがって、ECS Fargate用のNginxイメージに含める <code>nginx.conf</code> の設定は、以下のようになります。</p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-473 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-473">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-473">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-473"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-473"><span class="hljs-comment"># nginx.conf (ECS Fargate用)</span>
<span class="hljs-attribute">location</span> <span class="hljs-regexp">~ \.php$</span> {
    <span class="hljs-comment"># ...</span>
    <span class="hljs-comment"># サービス名ではなく、localhost (127.0.0.1) を指定する</span>
    <span class="hljs-attribute">fastcgi_pass</span> <span class="hljs-number">127.0.0.1:9000</span>;
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h3>タスク定義のイメージ</h3>
<p>タスク定義では、以下のように設定します。</p>
<ul>
<li><b>コンテナ-A: <code>php-fpm-container</code></b>
<ul>
<li>イメージ: （Laravelコードを含むPHP-FPMイメージ）</li>
<li>ポートマッピング: <b>なし</b> (外部に公開する必要はないため)</li>
</ul>
</li>
<li><b>コンテナ-B: <code>nginx-container</code></b>
<ul>
<li>イメージ: （設定済みのnginx.confと静的ファイルを含むNginxイメージ）</li>
<li>ポートマッピング: <b><code>80:80</code></b> (ALBからのトラフィックを受け取るため)</li>
</ul>
</li>
</ul>
<p>ALB（ロードバランサー）からのトラフィックはNginxコンテナが受け取り、必要に応じて <code>localhost:9000</code> を介してPHP-FPMコンテナに処理を渡します。</p>
<h2>Inertia/Reactのビルドファイルはどこに置く？</h2>
<p>ここで、Inertia/React構成特有の問題に直面します。</p>
<p>「InertiaはLaravel（PHP）がビューを配信する仕組みだから、<code>npm run build</code> で生成された <code>public/build</code> フォルダは、PHP-FPMコンテナにだけ置けば良いのでは？」</p>
<p>これはよくある誤解ですが、<b>パフォーマンスと責務分離の観点から、ビルドした静的ファイル（JS/CSS）はNginxコンテナに配置するのが正解</b>です。</p>
<p>この理由を理解するために、Inertiaのページが読み込まれる2段階のフローを見てみましょう。</p>
<h3>ステップ1: HTMLシェルのリクエスト (PHP-FPMが処理)</h3>
<ol start="1">
<li>ブラウザが <code>https://example.com/dashboard</code> にアクセスします。</li>
<li>ALB → Nginxコンテナがリクエストを受け取ります。</li>
<li>NginxはこれがPHPのリクエストだと判断し、<code>fastcgi_pass 127.0.0.1:9000</code> を使って<b>PHP-FPMコンテナ</b>に転送します。</li>
<li>PHP (Laravel) が起動し、Inertiaは <code>app.blade.php</code> をレンダリングしようとします。</li>
<li>このとき、PHPは <b><code>public/build/manifest.json</code></b> を読み取り、HTMLに含めるべきJS/CSSのファイル名を特定します。</li>
<li>PHPは以下のようなHTMLの「ガワ」を<b>生成</b>し、Nginx経由でブラウザに返します。
<p><response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c535465467="" class="ng-tns-c535465467-474 ng-star-inserted"></p>
<div _ngcontent-ng-c535465467="" class="code-block ng-tns-c535465467-474 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression,attention;BardVeMetadataKey:[[&quot;r_4fea32b244968dc8&quot;,&quot;c_103da6cb53c10bc2&quot;,null,&quot;rc_8751fdc7f6beda59&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c535465467="" class="formatted-code-block-internal-container ng-tns-c535465467-474">
<div _ngcontent-ng-c535465467="" class="animated-opacity ng-tns-c535465467-474">
<pre _ngcontent-ng-c535465467="" class="ng-tns-c535465467-474"><code _ngcontent-ng-c535465467="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c535465467-474"><span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"/build/assets/app.12345.js"</span> <span class="hljs-attr">defer</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">"stylesheet"</span> <span class="hljs-attr">href</span>=<span class="hljs-string">"/build/assets/app.67890.css"</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"app"</span> <span class="hljs-attr">data-page</span>=<span class="hljs-string">"..."</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">div</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
</div>
</div>
</div>
<p></code-block></response-element></li>
</ol>
<h3>ステップ2: 静的アセット(JS/CSS)のリクエスト (Nginxが処理)</h3>
<ol start="1">
<li>ブラウザは、ステップ1で受け取ったHTMLを解析します。</li>
<li>「<code>/build/assets/app.12345.js</code> と <code>/build/assets/app.67890.css</code> が必要だ」と判断し、ブラウザは<b>別途2回のリクエスト</b>をサーバーに送信します。</li>
<li><b>このリクエストは、もはやPHPとは全く関係ありません。</b></li>
<li>ALB → <b>Nginxコンテナ</b>がこの2つのリクエストを受け取ります。</li>
<li>Nginxは「自分の管理下（<code>public</code> フォルダ）にそのファイルがあるか？」を探します。</li>
<li><b>Nginxコンテナに <code>public/build/</code> 以下の実体ファイルが存在するため</b>、NginxはPHP-FPMを起動することなく、それらの静的ファイルを<b>超高速で</b>ブラウザに直接返します。</li>
</ol>
<p>もし、NginxコンテナにJS/CSSファイルがなければ、このリクエストもPHP-FPMに転送されてしまい、「JSファイルを取得するためだけにLaravelを起動する」という深刻なパフォーマンスボトルネックが発生します。</p>
<h3>CI/CDでのベストプラクティス</h3>
<p>上記2ステップから、CI/CDパイプラインでDockerイメージをビルドする際は、以下の構成が最適です。</p>
<ol start="1">
<li><code>npm run build</code> を実行し、<code>public/build</code> ディレクトリを生成します。</li>
<li><b>PHP-FPMイメージのビルド:</b>
<ul>
<li>Laravelのコード（<code>app/</code>, <code>routes/</code> など）をコピーします。</li>
<li>ステップ1のために <b><code>public/build/manifest.json</code></b> をコピーします。（リンク生成に必要）</li>
</ul>
</li>
<li><b>Nginxイメージのビルド:</b>
<ul>
<li><code>nginx.conf</code> をコピーします。</li>
<li>ステップ2のために <b><code>public</code> フォルダ全体</b>（<code>index.php</code> と、<code>public/build</code> 以下の <b>全てのJS/CSSファイル</b> を含む）をコピーします。（静的アセットの配信用）</li>
</ul>
</li>
</ol>
<h3><b>結論</b></h3>
<p><code>manifest.json</code> はPHP-FPMコンテナに、JS/CSSの実体ファイルはNginxコンテナに必要です。</p>
<p>両方のイメージに <code>public</code> フォルダ（または <code>public/build</code>）全体をコピーするのが、最もシンプルで確実な方法です。</p>
<h2>まとめ</h2>
<p>ECS Fargateで Laravel + Inertia + React 構成を動かすための要点をまとめます。</p>
<ol start="1">
<li><b>構成</b>: 「1タスク2コンテナ（Nginx + PHP-FPM）」構成を採用します。</li>
<li><b><code>fastcgi_pass</code></b>: NginxからPHP-FPMへの通信は、同一タスク内のため <code>fastcgi_pass 127.0.0.1:9000;</code> を指定します。</li>
<li><b>静的ファイル</b>: ビルドしたJS/CSSは、パフォーマンス最適化のためNginxコンテナに配置し、Nginxから直接配信させます。</li>
<li><b><code>manifest.json</code></b>: HTMLシェル生成のため、PHP-FPMコンテナにも <code>manifest.json</code> が必要です。</li>
</ol>
<p>この構成により、PHP-FPMは動的処理に専念し、Nginxは静的ファイルの高速配信に専念するという、「1コンテナ1責務」のメリットを最大限に活かした、スケーラブルな本番環境を構築できます。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2153/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【AWS解説】ALBのHTTPSリダイレクト、コンテナはポート80でなぜOK？</title>
		<link>https://otonan-syusyoku.work/archives/2147</link>
					<comments>https://otonan-syusyoku.work/archives/2147#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Fri, 17 Oct 2025 01:45:08 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[HTTP]]></category>
		<category><![CDATA[セキュリティ]]></category>
		<category><![CDATA[ネットワーク]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2147</guid>

					<description><![CDATA[AWSでWebアプリケーションを構築する際、ALB (Application Load Balancer) を使ってHTTPS化するのはもはや常識です。しかし、多くの開発者が一度は疑問に思う点があります。 「ALBでHT [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>AWSでWebアプリケーションを構築する際、ALB (Application Load Balancer) を使ってHTTPS化するのはもはや常識です。しかし、多くの開発者が一度は疑問に思う点があります。</p>
<p><b>「ALBでHTTPS化したのに、なぜ後ろにいるECSコンテナはポート80（HTTP）のままで通信できるんだろう？」</b></p>
<p>この一見不思議な挙動は、ALBが持つ2つの重要な役割を理解することで、スッキリと解決します。今回は、このALBの「魔法」の仕組みを、セキュリティグループの設定から紐解いていきましょう。</p>
<h2>最終的な構成：安全な3層アーキテクチャ</h2>
<p>まず、私たちが目指すのは、セキュリティと可用性のベストプラクティスに基づいた、以下のような3層アーキテクチャです。</p>
<ul>
<li><b>Web層</b>: ALBがインターネットからのリクエストをすべて受け付けます。</li>
<li><b>AP層</b>: ECSコンテナがプライベートな領域でアプリケーションを動かします。</li>
<li><b>DB層</b>: RDSがさらに内側のプライベートな領域でデータを安全に保管します。</li>
</ul>
<p>この構成のセキュリティの肝は<strong>「ECSはALBからの通信しか受け付けない」</strong>というルールです。これを実現するのが、次に説明するセキュリティグループです。</p>
<p>&nbsp;</p>
<h2>門番の役割を担う「セキュリティグループ」</h2>
<p>セキュリティグループは、リソースのドアを守る「バーチャルな門番」です。今回は3人の門番を配置します。</p>
<ol start="1">
<li><b>ALBの門番</b>: インターネットからの訪問者（ポート80, 443）を全員受け入れます。</li>
<li><b>ECSの門番</b>: <b>ALBの門番から紹介された人</b>（ALBからのトラフィック）<b>だけ</b>を通します。それ以外の人は全員お断りです。</li>
<li><b>RDSの門番</b>: <b>ECSの門番から紹介された人</b>（ECSからのトラフィック）<b>だけ</b>を通します。</li>
</ol>
<p>この連携により、たとえ攻撃者がECSに直接アクセスしようとしても、門前払いされる仕組みが完成します。</p>
<h2>ALBがこなす2つの全く異なる仕事</h2>
<p>さて、ここからが本題です。ユーザーが<code>http://</code>でアクセスしてきた時に、ALBとコンテナの間で何が起きているのでしょうか。実は、これは<b>2段階の独立した処理</b>になっています。</p>
<p>ポイントは役割が2つ存在するということです。</p>
<h3>ブラウザへの「リダイレクト指示」</h3>
<p>最初のやり取りは、ユーザーのブラウザとALBの間だけで完結します。</p>
<ol start="1">
<li><b>ユーザー</b>: ブラウザで <code>http://example.com</code> (ポート80) にアクセス。</li>
<li><b>ALB (ポート80の受付係)</b>: リクエストを受け取ると、中身は見ずにこう応答します。「<b>すみません、こちらの窓口は安全ではありません。向かいにある安全な443番の窓口へ行き直してください</b>」。これが<b>HTTP 301リダイレクト</b>です。</li>
</ol>
<p><b>この時点では、リクエストはまだECSコンテナには一切届いていません。</b></p>
<h3>ステップ2：コンテナへの「リクエスト転送」</h3>
<p>ブラウザはALBからの指示に従い、今度は最初から安全な窓口へ向かいます。</p>
<ol start="1">
<li><b>ユーザー</b>: ブラウザが自動的に <code>https://example.com</code> (ポート443) に<b>新しいリクエスト</b>を送信します。</li>
<li><b>ALB (ポート443の受付係)</b>:
<ul>
<li>暗号化されたリクエストを受け取り、持っているSSL証明書を使って<b>通信を復号</b>します（SSL/TLS終端）。</li>
<li>安全な平文になったリクエストを、宛先である<b>ECSコンテナのポート80</b>へ転送（フォワード）します。</li>
</ul>
</li>
<li><b>ECSコンテナ (ポート80)</b>: ALBから来た通常のHTTPリクエストを受け取り、処理を実行します。</li>
</ol>
<p>このように、ALBは<strong>ユーザーを正しい入口へ案内する「案内係」</strong>の仕事と、<strong>リクエストを内部の担当者へ届ける「取次係」</strong>の仕事という、2つの全く異なる役割をこなしているのです。</p>
<p>コンテナからすれば、常にALBという信頼できる取次係から、暗号化が解かれた分かりやすいリクエストが届くだけなので、ポート80で待ち受けていれば良い、というわけです。</p>
<p>非常に便利ですね。。</p>
<h2>CDKの実装</h2>
<h3>SecurityGroup の実装</h3>
<pre class="line-numbers"><code class="language-php">// ALB用セキュリティグループ
const albSecurityGroup = new ec2.SecurityGroup(this, 'AlbSg', { vpc });
albSecurityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(80), 'Allow HTTP');
albSecurityGroup.addIngressRule(ec2.Peer.anyIpv4(), ec2.Port.tcp(443), 'Allow HTTPS');

// ECS用セキュリティグループ
const ecsSecurityGroup = new ec2.SecurityGroup(this, 'EcsSg', { vpc });
// ALBからの通信のみを、コンテナのポート80で許可
ecsSecurityGroup.addIngressRule(
    albSecurityGroup,
    ec2.Port.tcp(80),
    'Allow traffic only from ALB'
);</code></pre>
<h3>ALBリスナー</h3>
<pre class="line-numbers"><code class="language-js">// ALB本体を作成
const alb = new elbv2.ApplicationLoadBalancer(this, 'MyAlb', {
    vpc,
    internetFacing: true,
    securityGroup: albSecurityGroup,
});

// HTTPリスナー (ポート80): HTTPSへリダイレクトするだけ
alb.addListener('HttpListener', {
    port: 80,
    defaultAction: elbv2.ListenerAction.redirect({
        protocol: 'HTTPS',
        port: '443',
        permanent: true,
    }),
});

// HTTPSリスナー (ポート443): ECSへリクエストを転送する
alb.addListener('HttpsListener', {
    port: 443,
    certificates: [certificate], // ACMで発行した証明書
    defaultAction: elbv2.ListenerAction.forward([ecsTargetGroup]), // ECSのターゲットグループ
});</code></pre>
<h2>まとめ</h2>
<ul>
<li>ALBのHTTP→HTTPSリダイレクトは、「リダイレクト指示」<b>と</b>「リクエスト転送」の2段階で行われる。</li>
<li>SSL/TLSによる暗号化・復号の処理はALBがすべて肩代わりしてくれる（<b>SSL/TLS終端</b>）。</li>
<li>コンテナは、ALBから転送されてくる<b>暗号化されていないHTTPリクエスト</b>を受け取るだけで良い。</li>
<li><b>セキュリティグループ</b>を正しく設定することで、この安全な通信経路が完成する。</li>
</ul>
<p>役割が2つあることが分からなければ理解できなかったなぁ。</p>
<p>&nbsp;</p>
<h2>参考記事</h2>
<div class="awsui_grid-column_14yj0_16am7_186 awsui_colspan-12_14yj0_16am7_307">
<div class="awsui_restore-pointer-events_14yj0_16am7_357">
<ol>
<li><a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/introduction.html">What is an Application Load Balancer?</a></li>
<li><a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/load-balancer-listeners.html">Listeners for your Application Load Balancers</a></li>
</ol>
</div>
</div>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2147/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>クラウドメタデータ攻撃とは？ Nginx設定で防ぐサイバー脅威</title>
		<link>https://otonan-syusyoku.work/archives/2131</link>
					<comments>https://otonan-syusyoku.work/archives/2131#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Thu, 26 Jun 2025 04:38:30 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[nginx]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2131</guid>

					<description><![CDATA[近年、企業のITインフラをクラウドへ移行する動きが加速しています。AWS、GCP、Azureといったクラウドサービスは非常に便利ですが、同時に新たなセキュリティリスクも生み出しています。その一つが「クラウドメタデータ攻撃 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>近年、企業のITインフラをクラウドへ移行する動きが加速しています。AWS、GCP、Azureといったクラウドサービスは非常に便利ですが、同時に新たなセキュリティリスクも生み出しています。その一つが「<b>クラウドメタデータ攻撃</b>」です。</p>
<p>この記事では、この攻撃の仕組みと、<b>特にNginxを使っている場合にどのように対策すべきか</b>を分かりやすく解説します。</p>
<h2>そもそも「クラウドメタデータ」って何？</h2>
<p>クラウド環境でサーバー（インスタンス）を動かしていると、「<b>メタデータ</b>」というものが存在します。これは、そのサーバー自身の「身分証明書」のような情報です。</p>
<p>具体的には、以下のような情報が含まれます。</p>
<ul>
<li><b>インスタンスID:</b> そのサーバー固有の識別番号</li>
<li><b>ロール・認証情報:</b> そのサーバーが他のクラウドサービスにアクセスするための権限や秘密鍵など</li>
<li><b>ネットワーク情報:</b> サーバーのIPアドレスなど</li>
<li><b>起動スクリプト:</b> サーバーが起動時に実行するコマンド</li>
</ul>
<p>これらの情報は、サーバーが自分自身を識別したり、他のクラウドサービスと安全に連携したりするために非常に重要です。そして、これらのメタデータには通常、**<code>169.254.169.254</code>**という特別なIPアドレスを介して、サーバー内部からのみアクセスできるようになっています。</p>
<h3>169.254.169.254ってどんなIP？</h3>
<p>この<code>169.254.169.254</code>というIPアドレスは、「<b>リンクローカルアドレス</b>」と呼ばれる特殊な範囲のIPアドレスです。これは、インターネットのような外部ネットワークからはアクセスできない、サーバー自身の内部ネットワークでのみ有効なIPアドレスです。</p>
<p>つまり、通常は外部からこのIPアドレスにアクセスすることはできず、サーバー内部のアプリケーションだけが、自分自身の情報を安全に取得するために使います。</p>
<h2>クラウドメタデータ攻撃の仕組み</h2>
<p>「クラウドメタデータ攻撃」は、この本来アクセスできないはずのメタデータに、<b>不適切な設定を悪用してアクセスしようとする攻撃</b>です。</p>
<p>攻撃者が狙うのは、ウェブサーバーとしてよく使われる「<b>Nginx（エンジンエックス）</b>」の設定ミスです。</p>
<pre class="line-numbers"><code class="language-php">curl -H "X-aws-ec2-metadata-token: $(curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")" http://169.254.169.254/latest/meta-data/</code></pre>
<p>&nbsp;</p>
<h3>攻撃の手順</h3>
<ol start="1">
<li><b>Nginxの誤設定:</b> ウェブサーバーのNginxが、特定のルールなしに外部からのリクエストを内部のIPアドレスに転送してしまうような、<b>不適切な設定になっている</b>のが攻撃の前提です。</li>
<li><b>特別なリクエストの送信:</b> 攻撃者は、ウェブブラウザなどから、悪意のあるHTTPリクエストをNginxサーバーに送ります。このとき、リクエストの「<b>Hostヘッダー</b>」という部分に、先ほどのメタデータ用IPアドレスである<code>169.254.169.254</code>を意図的に含めます。</li>
<li><b>Nginxの転送:</b> 誤設定されたNginxは、この<code>Host</code>ヘッダーに<code>169.254.169.254</code>が含まれているリクエストを、本来ルーティング不可能なはずの内部メタデータサービスへ転送してしまいます。</li>
<li><b>メタデータの取得:</b> 結果として、外部の攻撃者が、サーバーの機密性の高いメタデータ（認証情報など）を不正に取得できてしまうのです。</li>
</ol>
<p>この攻撃が成功すると、攻撃者はサーバーになりすまして他のクラウドサービスへアクセスしたり、機密情報を盗み取ったりするなど、さらに深刻な被害につながる可能性があります。</p>
<h2>3. Nginxでクラウドメタデータ攻撃を防ぐ方法</h2>
<p>この攻撃を防ぐための最も効果的で直接的な方法は、<b>Nginxの設定を正しく行うこと</b>です。</p>
<p>Nginxの設定ファイル（通常は<code>/etc/nginx/nginx.conf</code>や<code>/etc/nginx/conf.d/</code>内のサイト設定ファイル）に、以下のルールを追加しましょう。</p>
<div _ngcontent-ng-c3273690373="" class="code-block ng-tns-c3273690373-284 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression;BardVeMetadataKey:[[&quot;r_a0b4d50376cf47d8&quot;,&quot;c_c62c50919a81b22d&quot;,null,&quot;rc_a2eeb02e335308f6&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c3273690373="" class="formatted-code-block-internal-container ng-tns-c3273690373-284">
<div _ngcontent-ng-c3273690373="" class="animated-opacity ng-tns-c3273690373-284">
<pre _ngcontent-ng-c3273690373="" class="ng-tns-c3273690373-284"><code _ngcontent-ng-c3273690373="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c3273690373-284"><span class="hljs-section">server</span> {
    <span class="hljs-attribute">listen</span> <span class="hljs-number">80</span>;
    <span class="hljs-attribute">listen</span> <span class="hljs-number">443</span> ssl; <span class="hljs-comment"># HTTPSを使用している場合</span>
    <span class="hljs-attribute">server_name</span> your-domain.com; <span class="hljs-comment"># 実際のドメイン名に置き換えてください</span>

    <span class="hljs-attribute">location</span> / {
        <span class="hljs-comment"># HostヘッダーがメタデータIPアドレス（169.254.169.254）の場合、</span>
        <span class="hljs-comment"># アクセスを拒否し、403 Forbiddenエラーを返す</span>
        <span class="hljs-attribute">if</span> ($http_host = <span class="hljs-string">"169.254.169.254"</span>) {
            <span class="hljs-attribute">return</span> <span class="hljs-number">403</span>;
        }
    }

    <span class="hljs-comment"># その他のNginx設定</span>
    <span class="hljs-comment"># ...</span>
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h3>設定のポイント</h3>
<ul>
<li><code>if ($http_host = "169.254.169.254")</code>: これが核心的な部分です。受信したリクエストの<code>Host</code>ヘッダーが<code>169.254.169.254</code>であるかをチェックします。</li>
<li><code>return 403;</code>: もし一致した場合、そのリクエストをサーバーの内部処理に進ませず、すぐに「403 Forbidden（アクセス禁止）」のエラーを返します。これにより、攻撃者がメタデータにアクセスするのを防ぎます。</li>
</ul>
<p>この設定を追加したら、必ずNginxの設定をテストし、再読み込み（または再起動）するのを忘れないでください。</p>
<div _ngcontent-ng-c3273690373="" class="code-block ng-tns-c3273690373-285 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression;BardVeMetadataKey:[[&quot;r_a0b4d50376cf47d8&quot;,&quot;c_c62c50919a81b22d&quot;,null,&quot;rc_a2eeb02e335308f6&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c3273690373="" class="formatted-code-block-internal-container ng-tns-c3273690373-285">
<div _ngcontent-ng-c3273690373="" class="animated-opacity ng-tns-c3273690373-285">
<pre _ngcontent-ng-c3273690373="" class="ng-tns-c3273690373-285"><code _ngcontent-ng-c3273690373="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c3273690373-285">sudo nginx -t <span class="hljs-comment"># 設定ファイルの文法チェック</span>
sudo systemctl reload nginx <span class="hljs-comment"># Nginxの設定を再読み込み</span>
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h2>まとめ</h2>
<p>クラウドメタデータ攻撃は、クラウド環境を利用する上で注意すべきサイバー脅威の一つです。しかし、適切なNginxの設定を行うことで、このリスクを効果的に軽減できます。</p>
<p>あなたのウェブサーバーが安全に稼働しているか、今一度Nginxの設定を見直してみてはいかがでしょうか？</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2131/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>Nginxの「upstream sent too big header」エラーを解決する：LaravelとPHP-FPM環境での対処法</title>
		<link>https://otonan-syusyoku.work/archives/2128</link>
					<comments>https://otonan-syusyoku.work/archives/2128#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Mon, 23 Jun 2025 15:04:51 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[nginx]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2128</guid>

					<description><![CDATA[環境 Ubuntu：24.04 PHP：8.2 Laravel：11.x 起こったこと NginxをWebサーバーとして、LaravelアプリケーションとPHP-FPMを連携させている環境で、ある日突然 upstream [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2 data-sourcepos="7:1-7:154">環境</h2>
<p>Ubuntu：24.04<br />
PHP：8.2<br />
Laravel：11.x</p>
<h2 data-sourcepos="5:1-5:183">起こったこと</h2>
<p data-sourcepos="5:1-5:183">NginxをWebサーバーとして、LaravelアプリケーションとPHP-FPMを連携させている環境で、ある日突然 <code>upstream sent too big header while reading response header from upstream</code> というエラーがNginxのエラーログに表示され、Webサイトが正常に表示されなくなリマした…</p>
<p data-sourcepos="7:1-7:154">調べていくと、このエラーは、<span class="sc_marker blue"><strong>PHP-FPM（アップストリームサーバー）がNginxに返そうとしたHTTPレスポンスヘッダーのサイズが、Nginxが受け入れられる設定値の上限を超えてしまった場合に発生</strong></span>します。</p>
<p data-sourcepos="7:1-7:154">特にLaravelのような動的なアプリケーションでは、セッション情報やクッキーの肥大化が原因で発生しやすい問題とのことでやんす。</p>
<h2 data-sourcepos="9:1-9:15">エラーが起きる原因は？</h2>
<p data-sourcepos="11:1-11:182">NginxがWebサーバーとしてクライアントからのリクエストを受け取り、それをバックエンドのPHP-FPM（FastCGIプロセス）に渡して処理してもらいます。</p>
<p data-sourcepos="11:1-11:182">PHP-FPMが処理を終えてNginxに応答を返す際、そのHTTPレスポンスにはヘッダー情報が含まれます。このヘッダー情報がNginxが設定しているバッファのサイズを超えると、エラーが発生してしまいます。</p>
<p data-sourcepos="13:1-13:24">主な原因として、以下のようなものが挙げられます。</p>
<ul data-sourcepos="15:3-18:0">
<li data-sourcepos="15:3-15:177"><strong>セッションデータの肥大化:</strong> Laravelなどのフレームワークでセッションをクッキーベースで管理している場合、ユーザーが多くの情報をセッションに保存したり、ショッピングカートに大量のアイテムを追加したりすると、クッキーのサイズが大きくなります。この大きなクッキー情報がレスポンスヘッダーに含まれることで、上限を超えてしまうことがあります。</li>
<li data-sourcepos="16:3-16:100"><strong>多数のクッキーやカスタムヘッダー:</strong> アプリケーションが非常に多くのクッキーを設定している、または独自のカスタムヘッダーを大量に追加している場合も、ヘッダー全体のサイズが大きくなります。</li>
<li data-sourcepos="17:3-18:0"><strong>リダイレクトループ:</strong> 不適切なリダイレクト設定により、ブラウザが何度もリダイレクトを繰り返す際、そのたびに新しいクッキーやヘッダーが付与され、最終的にヘッダーが大きくなってしまうケースも考えられます。</li>
</ul>
<h2 data-sourcepos="19:1-19:21">解決策：Nginxの設定を調整する</h2>
<p data-sourcepos="21:1-21:48">最も手軽で直接的な解決策は、Nginxが受け入れられるヘッダーバッファのサイズを増やすことです。</p>
<p data-sourcepos="23:1-23:162">Nginxの設定ファイル（通常は <code>/etc/nginx/nginx.conf</code> か、サイト固有の <code>.conf</code> ファイル）を開き、<code>http</code> ブロック内、または対象の <code>server</code> ブロック内に以下のディレクティブを追加または調整します。<code>http</code> ブロックに記述すると、すべてのバーチャルホストに適用されます。</p>
<div _ngcontent-ng-c1213168243="" class="code-block ng-tns-c1213168243-479 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression;BardVeMetadataKey:[[&quot;r_7b21b8e99d7c089f&quot;,&quot;c_6fface8cac75276a&quot;,null,&quot;rc_b9532f80be7d9863&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c1213168243="" class="formatted-code-block-internal-container ng-tns-c1213168243-479">
<div _ngcontent-ng-c1213168243="" class="animated-opacity ng-tns-c1213168243-479">
<pre _ngcontent-ng-c1213168243="" class="ng-tns-c1213168243-479"><code _ngcontent-ng-c1213168243="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c1213168243-479" data-sourcepos="25:1-42:1"><span class="hljs-section">http</span> {
    <span class="hljs-comment"># ... 他の http ブロック内の設定 ...</span>

    <span class="hljs-comment"># upstream sent too big header エラー対策</span>
    <span class="hljs-comment"># PHP-FPM（FastCGI）からのレスポンスヘッダーに対応するため、fastcgi_ のディレクティブを設定します。</span>
    <span class="hljs-comment"># 必要に応じて proxy_ のディレクティブも設定しますが、PHP-FPMの場合はfastcgi_を優先します。</span>
    <span class="hljs-attribute">fastcgi_buffer_size</span> <span class="hljs-number">128k</span>;   <span class="hljs-comment"># FastCGIヘッダーバッファの初期サイズ。デフォルトは4k/8k。</span>
    <span class="hljs-attribute">fastcgi_buffers</span>     <span class="hljs-number">4</span> <span class="hljs-number">256k</span>; <span class="hljs-comment"># 256KBのバッファを4つ用意。合計1MB。</span>

    <span class="hljs-comment"># 必要に応じて、もしプロキシとして使用している場合も考慮するなら、こちらも設定</span>
    <span class="hljs-comment"># proxy_buffer_size   128k;</span>
    <span class="hljs-comment"># proxy_buffers       4 256k;</span>
    <span class="hljs-comment"># proxy_busy_buffers_size 256k;</span>

    <span class="hljs-comment"># ... 他の http ブロック内の設定 ...</span>
}
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<h3 data-sourcepos="44:1-44:15"><strong>ディレクティブの解説</strong></h3>
<ul data-sourcepos="46:3-48:0">
<li data-sourcepos="46:3-46:150"><code>fastcgi_buffer_size</code>: NginxがFastCGIからのレスポンスヘッダーを読み込むための最初のバッファサイズを指定します。デフォルト値（通常は <code>4k</code> または <code>8k</code>）よりも大きな値（例：<code>128k</code>）を設定することで、大きなヘッダーにも対応できるようになります。</li>
<li data-sourcepos="47:3-48:0"><code>fastcgi_buffers</code>: ヘッダーを含むレスポンス全体をバッファリングするためのバッファの数とサイズを設定します。上記の例では、<code>256KB</code>のバッファを<code>4</code>つ使用し、合計で<code>1MB</code>のバッファスペースを確保します。</li>
</ul>
<p data-sourcepos="49:1-50:107"><strong>注意点:</strong> これらの値を必要以上に大きくしすぎると、Nginxが使用するメモリ量が増加し、サーバーリソースを圧迫する可能性があります。まずはエラーが出なくなる最小限の増加から試し、サーバーの負荷状況を見ながら調整してください。</p>
<h3 data-sourcepos="52:1-52:12">設定変更後の手順</h3>
<ol data-sourcepos="54:1-65:0">
<li data-sourcepos="54:1-54:46"><strong>Nginx設定ファイルを保存:</strong> 変更したNginx設定ファイルを保存します。</li>
<li data-sourcepos="55:1-59:60"><strong>設定ファイルのテスト:</strong> <response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c1213168243="" class="ng-tns-c1213168243-480 ng-star-inserted">
<div _ngcontent-ng-c1213168243="" class="code-block ng-tns-c1213168243-480 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression;BardVeMetadataKey:[[&quot;r_7b21b8e99d7c089f&quot;,&quot;c_6fface8cac75276a&quot;,null,&quot;rc_b9532f80be7d9863&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c1213168243="" class="code-block-decoration header-formatted gds-title-s ng-tns-c1213168243-480 ng-star-inserted"></div>
<div _ngcontent-ng-c1213168243="" class="formatted-code-block-internal-container ng-tns-c1213168243-480">
<div _ngcontent-ng-c1213168243="" class="animated-opacity ng-tns-c1213168243-480">
<pre _ngcontent-ng-c1213168243="" class="ng-tns-c1213168243-480"><code _ngcontent-ng-c1213168243="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c1213168243-480" data-sourcepos="56:5-58:17">sudo nginx -t</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p></code-block></response-element>このコマンドで構文エラーがないかを確認します。<code>test is successful</code> と表示されればOKです。</li>
<li data-sourcepos="60:1-65:0"><strong>Nginxのリロード:</strong> <response-element class="" ng-version="0.0.0-PLACEHOLDER"><code-block _nghost-ng-c1213168243="" class="ng-tns-c1213168243-481 ng-star-inserted">
<div _ngcontent-ng-c1213168243="" class="code-block ng-tns-c1213168243-481 ng-animate-disabled ng-trigger ng-trigger-codeBlockRevealAnimation" jslog="223238;track:impression;BardVeMetadataKey:[[&quot;r_7b21b8e99d7c089f&quot;,&quot;c_6fface8cac75276a&quot;,null,&quot;rc_b9532f80be7d9863&quot;,null,null,&quot;ja&quot;,null,1,null,null,1,0]]">
<div _ngcontent-ng-c1213168243="" class="formatted-code-block-internal-container ng-tns-c1213168243-481">
<div _ngcontent-ng-c1213168243="" class="animated-opacity ng-tns-c1213168243-481">
<pre _ngcontent-ng-c1213168243="" class="ng-tns-c1213168243-481"><code _ngcontent-ng-c1213168243="" role="text" data-test-id="code-content" class="code-container formatted ng-tns-c1213168243-481" data-sourcepos="61:5-63:31">sudo systemctl reload nginx
</code></pre>
</div>
</div>
</div>
<p>&nbsp;</p>
<p></code-block></response-element> Nginxサービスをリロードして、新しい設定を適用します。</li>
</ol>
<p data-sourcepos="66:1-66:87">これで、Nginxが大きなHTTPレスポンスヘッダーを適切に処理できるようになり、「<code>upstream sent too big header</code>」エラーは解消されるはずです。</p>
<h2>根本的な解決策：Laravelアプリケーション側の最適化</h2>
<p data-sourcepos="70:1-70:87">Nginxの設定でエラーを抑制できますが、<strong>Laravelアプリケーション側で不必要に大きなヘッダーが生成されている</strong>場合は、そちらの最適化も検討することをお勧めします。</p>
<ul data-sourcepos="72:3-78:0">
<li data-sourcepos="72:3-73:202"><strong>セッションデータの見直し:</strong>
<ul data-sourcepos="73:7-73:202">
<li data-sourcepos="73:7-73:202">クッキーに保存しているセッションデータが大きすぎる場合、データベースやRedisなどの<strong>サーバーサイドストレージにセッションを保存する</strong>ようにLaravelの設定を変更することを検討してください。これにより、クッキーにセッションIDのみが保存されるため、クッキーのサイズを大幅に削減できます。Laravelのセッション設定は <code>config/session.php</code> で変更可能です。</li>
</ul>
</li>
<li data-sourcepos="74:3-75:73"><strong>クッキーの管理:</strong>
<ul data-sourcepos="75:7-75:73">
<li data-sourcepos="75:7-75:73">アプリケーションが設定しているクッキーの数や、個々のクッキーのサイズを減らせないかレビューします。不要なクッキーは削除しましょう。</li>
</ul>
</li>
<li data-sourcepos="76:3-78:0"><strong>カスタムヘッダーの確認:</strong>
<ul data-sourcepos="77:7-78:0">
<li data-sourcepos="77:7-78:0">アプリケーションがレスポンスに含めているカスタムHTTPヘッダーが多すぎないか、または内容が大きすぎないかを確認します。</li>
</ul>
</li>
</ul>
<p data-sourcepos="79:1-79:84">Nginxの設定調整で当面のエラーは解消されますが、アプリケーションの効率性を高めるためにも、Laravel側のヘッダー生成ロジックを見直すことは良いプラクティスです。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2128/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>PHPer が SAA(C-03) に合格した話をしたい</title>
		<link>https://otonan-syusyoku.work/archives/2051</link>
					<comments>https://otonan-syusyoku.work/archives/2051#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sun, 15 Dec 2024 12:14:22 +0000</pubDate>
				<category><![CDATA[IT資格]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[SAA]]></category>
		<category><![CDATA[インフラ]]></category>
		<category><![CDATA[三流プログラマー]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2051</guid>

					<description><![CDATA[都内でPHPerとして従事している三流プログラマーです。 アプリケーションの外に興味を持ったことがキッカケでAWSの世界に足を踏み入れて1年ちょっとが経ちます。 そんなPHPerがAWS SAA(c03) に合格した話を [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>都内でPHPerとして従事している三流プログラマーです。</p>
<p>アプリケーションの外に興味を持ったことがキッカケでAWSの世界に足を踏み入れて1年ちょっとが経ちます。</p>
<p>そんなPHPerがAWS SAA(c03) に合格した話をしたいと思います〜</p>
<p><a href="https://otonan-syusyoku.work/archives/1674/lets-go" rel="attachment wp-att-1680"><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO.png" alt="Let&#96;s GO" width="1000" height="500" class="aligncenter size-full wp-image-1680" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO.png 1000w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO-300x150.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2024/02/Lets-GO-768x384.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></a></p>
<h2>SAA C03 とは</h2>
<p>AWS Certified Solutions Architect – Associate とは、 中級レベルに位置する AWS 認定資格です。</p>
<p>レベルとしては、4段階の下から2番目の資格となるため、難易度は高くはありません。</p>
<p>&nbsp;</p>
<blockquote><p>AWS 認定ソリューションアーキテクト &#8211; アソシエイトは、コストとパフォーマンスが最適化されたソリューションの設計に重点を置いています。これは、AWS クラウドまたはオンプレミス IT での経験が豊富な受験者にとって理想的な出発点です</p>
<p><cite class="blockquote_ref"> <a href="https://aws.amazon.com/jp/certification/certified-solutions-architect-associate/" target="_blank" rel="noopener noreferrer">AWS Certified Solutions Architect &#8211; Associate</a> </cite></p></blockquote>
<blockquote>
<table style="border-collapse: collapse; width: 100%;">
<tbody>
<tr>
<td style="width: 11.4623%;">カテゴリ</td>
<td style="width: 88.5377%;">Associate</td>
</tr>
<tr>
<td style="width: 11.4623%;">試験時間</td>
<td style="width: 88.5377%;">130分</td>
</tr>
<tr>
<td style="width: 11.4623%;">試験形式</td>
<td style="width: 88.5377%;"><span>65 個の問題 (複数選択または複数応答のいずれか)</span></td>
</tr>
<tr>
<td style="width: 11.4623%;">コスト</td>
<td style="width: 88.5377%;"><span>150 USD。 為替レートを含むコストに関する他の情報については、「</span><a href="https://aws.amazon.com/jp/certification/policies/before-testing/#Exam_pricing"><u>試験の料金</u></a><span>」にアクセスしてください</span></td>
</tr>
<tr>
<td style="width: 11.4623%;">受験オプション</td>
<td style="width: 88.5377%;"><span>Pearson VUE テストセンターまたはオンライン監督付き試験</span></td>
</tr>
<tr>
<td style="width: 11.4623%;">対象言語</td>
<td style="width: 88.5377%;"><span>英語、フランス語 (フランス)、ドイツ語、イタリア語、日本語、韓国語、ポルトガル語 (ブラジル)、中国語 (簡体字)、スペイン語 (ラテンアメリカ)、スペイン語 (スペイン)、中国語 (繁体字)</span></td>
</tr>
</tbody>
</table>
<p><cite class="blockquote_ref"> <a href="https://aws.amazon.com/jp/certification/certified-solutions-architect-associate/" target="_blank" rel="noopener noreferrer">AWS Certified Solutions Architect &#8211; Associate</a></cite></p></blockquote>
<h2>受験者の経歴</h2>
<ul>
<li>基本的にバックエンドを生業としているアプリケーションプログラマー</li>
<li>たまにサーバー構築/保守 のお仕事</li>
<li>AWS 歴は3年(素人レベルの仕事</li>
<li>受験前に触ったことのあるサービス（基本的にLAMPに関わるサービスのみ
<ul>
<li>EC2</li>
<li>ALB</li>
<li>ECS</li>
<li>Lambda</li>
<li>RDS</li>
<li>S3</li>
<li>Cloud Watch</li>
<li>SQS</li>
<li>SES</li>
</ul>
</li>
</ul>
<p>[getpost id=&#8221;1842&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;]</p>
<p>[getpost id=&#8221;1710&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;]</p>
<p>[getpost id=&#8221;1448&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;]</p>
<h2>合格するための勉強法</h2>
<h3>Ping-t</h3>
<p>Ping-t を中心に学習を進めました。</p>
<p>Ping-t は、1問1答型式で問題を解いていくことができ、分からないを解決しながら各種サービスの理解を深めていくことができる優れモノです。</p>
<p>各種サービスごとの問題集や試験レベルの問題を用意していくており、ひたらすら解いていくことで徐々に知識がついていきます。</p>
<p>SAA に関しては、完全無料で使うことができるため、 受験予定者はぜひ使っていただきたい神サービスです。（インフラ系の他資格の対策も練ることができるみたいなので、<a href="https://mondai.ping-t.com/">サイト</a>閲覧してみてえください〜</p>
<h3>AWS Black Belt</h3>
<p>問題を解くだけでは、受験するメリットが薄まってしまいそうだったため、PHPerとして業務に関わりそうな以下のサービスに関しては、<a href="https://aws.amazon.com/jp/events/aws-event-resource/archive/?cards.sort-by=item.additionalFields.SortDate&amp;cards.sort-order=desc&amp;awsf.tech-category=*all">AWS Black Belt</a> でお勉強をしました。</p>
<ul>
<li>EC2 + Auto Scaling</li>
<li>RDS/Aurora</li>
<li>DynamoDB</li>
<li>ElastiCache</li>
<li>S3/EFS</li>
<li>SQS/SNS</li>
</ul>
<p>あるべき姿を考えると試験範囲の全てのBlackBeltを読むべきだったのですが、残念ながら時間を確保することが難しかったため自分の業務に関わりそうなサービスだけ読んだ形になります。<br />
資格取得のために Ping-t をひたらすら解いたのですが、<span class="sc_marker blue"><strong>業務に活かすという観点からは 公式資料を熟読している方が有意義でした。</strong></span></p>
<h3>総勉強時間</h3>
<p>だいたい3週間ほど試験対策を行いました。</p>
<ul>
<li>7日間: Black Belt</li>
<li>14日間: Ping-t</li>
</ul>
<p>子育ての合間を縫っての勉強になったため結構な時間がかかってしまったのですが、 サーバー構築を1度でもやったことがある方だと、 Ping-t を3日間くらいやり込んだら取れるのではないかと思います。</p>
<p>なぜ、 サーバー構築を1度でもやったことがある人が3日間で取得可能かというと、サーバー構築をやるにあたって以下の知識が必要になってくるからです。<br />
※ 深い知見は必要ないです。 DNS とは ドメイン管理しているサービスです くらいが言えるレベル感を指しています。</p>
<ul>
<li>ネットワーク</li>
<li>DNS</li>
<li>Linux</li>
</ul>
<p>これらの基礎知識があれば、AWS の各種サービスについて学習していくだけで当たり前ですが合格できちゃいます。</p>
<p>逆にサーバー周りが何にも分からないって人は試験勉強の一環として一度だけLAMP環境を立ててみてほしいです。 <strong><span class="sc_marker blue">Ping-t の解説がなるほど〜</span> </strong>となったり、 <span class="sc_marker blue"><strong>AWS の提供しているマネージドサービスに感動する</strong></span>事ができます。</p>
<p>普通に勉強すればどんな人でも合格可能な資格ですので、皆さん頑張っていきましょう〜</p>
<h2>じゃーん</h2>
<p><a href="https://otonan-syusyoku.work/archives/2051/%e3%82%b9%e3%82%af%e3%83%aa%e3%83%bc%e3%83%b3%e3%82%b7%e3%83%a7%e3%83%83%e3%83%88-2024-12-15-20-35-04" rel="attachment wp-att-2053"><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/12/スクリーンショット-2024-12-15-20.35.04-300x191.png" alt="合格 SAA" width="300" height="191" class="aligncenter size-medium wp-image-2053" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/12/スクリーンショット-2024-12-15-20.35.04-300x191.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2024/12/スクリーンショット-2024-12-15-20.35.04.png 488w" sizes="(max-width: 300px) 100vw, 300px" /></a></p>
<h2>取得するとAWSの業務をこなせるようになるのか？</h2>
<p><span class="sc_marker blue"><strong>答えは No です。</strong></span></p>
<p>合格するとAWS の各種サービスについて理解がある状態になるのですが、業務に速攻で活きるということは絶対にありません。</p>
<p>今のインフラ構成がAWS のベストプラクティスに反していることが分かっていても、過去に何があって今の構成に至っているかを把握しながら</p>
<h2>受験したことで得た知識</h2>
<p>業務に活きそうな知識は以下のとおりです。</p>
<ul>
<li>Lambda
<ul>
<li>コールドスタート対策</li>
<li>イベント駆動型での活用(SNS/SQS)</li>
<li>VPC内への配置</li>
</ul>
</li>
<li>S3/EFS
<ul>
<li>コスト低下戦略</li>
<li>アップロードの最適化</li>
<li>Putイベント</li>
</ul>
</li>
<li>RDS/Aurora
<ul>
<li>マルチリージョン</li>
<li>RDS Proxy</li>
</ul>
</li>
<li>ALB/NLB
<ul>
<li>パフォーマンス最適化</li>
<li>マルチリージョン</li>
</ul>
</li>
<li>VPCに関わるネットワーク
<ul>
<li>ピアリング</li>
<li>マルチリージョン</li>
<li>オンプレとの通信</li>
</ul>
</li>
<li>オンプレとクラウドのハイブリット戦略
<ul>
<li>ストレージ間の同期
<ul>
<li>データ転送量を考慮した構成/サービス</li>
</ul>
</li>
<li>アプリケーションはオンプレ、データ基盤はクラウド</li>
<li>オンプレ配置されているデータセンターへのクラウド基盤の貸し出し</li>
</ul>
</li>
</ul>
<p>&nbsp;</p>
<p>番外編の知識としては、学習していく中でたまたま出会えた知見たちです。<br />
目からウロコのものもあったので、この辺りは勉強しておいて良かったなぁ〜と思っている所存です。</p>
<ul>
<li>DB インスタンスもセキュリティグループを絞る事を前提にパブリックサブネットに置いても良いんじゃないか説</li>
<li>SSH 鍵を不要とする戦略</li>
</ul>
<h2>PHPer としての SAA の次のステップ</h2>
<p>今のところ、SAAの上位資格の<a href="https://aws.amazon.com/jp/certification/certified-solutions-architect-professional/">SAP</a>を取得するつもりはありません。<br />
前述した通り、資格を取ったからといって業務に直ぐに活きるということがないため、AWSの世界にどっぷり入ることになったタイミングで Professional 資格に挑もうと思います。</p>
<p>これからの目標としては、PHP に直接関わりのある MySQL や nginx といったミドルウェアや DevOps として CI/CD , IaC について知見を深めることができるといいなぁと考えています。</p>
<h3>MySQL</h3>
<ul>
<li>Materialized View</li>
<li>Window関数, JSONの扱い</li>
<li>パフォーマンスチューニング</li>
</ul>
<h3><strong>nginx</strong></h3>
<ul>
<li>セキュリティに係るHTTPヘッダー</li>
<li>プロセス管理</li>
</ul>
<h3>DevOps</h3>
<ul>
<li>CI/CD （GitHub Actionis</li>
<li>IaC （Terraform</li>
</ul>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2051/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
