<?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>AWS &#8211; エンジニア見習い</title>
	<atom:link href="https://otonan-syusyoku.work/archives/tag/aws/feed" rel="self" type="application/rss+xml" />
	<link>https://otonan-syusyoku.work</link>
	<description>三流プログラマー</description>
	<lastBuildDate>Thu, 12 Feb 2026 06:29:36 +0000</lastBuildDate>
	<language>ja</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=7.0</generator>

<image>
	<url>https://otonan-syusyoku.work/wp-content/uploads/2023/10/cropped-名称未設定のデザイン-16-32x32.png</url>
	<title>AWS &#8211; エンジニア見習い</title>
	<link>https://otonan-syusyoku.work</link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>CDKで管理しているRDSを暗号化したい</title>
		<link>https://otonan-syusyoku.work/archives/2198</link>
					<comments>https://otonan-syusyoku.work/archives/2198#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Thu, 12 Feb 2026 06:29:36 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[CDK]]></category>
		<category><![CDATA[DevOps]]></category>
		<category><![CDATA[RDS]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2198</guid>

					<description><![CDATA[どうも、PHPerです。 先日、業務にて「既存のRDSのストレージを暗号化したい」という、シンプルかつ重たい要件が降ってきました。 これの何が困るかというと、皆様ご存知の通りRDSの仕様です。 RDSは作成後の暗号化ステ [&#8230;]]]></description>
										<content:encoded><![CDATA[
<p class="wp-block-paragraph">どうも、PHPerです。</p>



<p class="wp-block-paragraph">先日、業務にて<strong>「既存のRDSのストレージを暗号化したい」</strong>という、シンプルかつ重たい要件が降ってきました。</p>



<p class="wp-block-paragraph">これの何が困るかというと、皆様ご存知の通りRDSの仕様です。 RDSは<strong>作成後の暗号化ステータスの変更ができません</strong>。つまり、<strong>既存のRDSを暗号化するためには、「暗号化有効」設定で作り直す（再作成する）必要</strong>があります。 </p>



<p class="wp-block-paragraph"><a href="https://docs.aws.amazon.com/ja_jp/prescriptive-guidance/latest/patterns/automatically-remediate-unencrypted-amazon-rds-db-instances-and-clusters.html" target="_blank" rel="noreferrer noopener">参考: Amazon RDS DB インスタンスおよびクラスターの暗号化 &#8211; AWS Prescriptive Guidance</a></p>



<p class="wp-block-paragraph">さらに今回はインフラを <strong>AWS CDK</strong> で管理しています。<br> 単純にCDKのコードを書き換えてデプロイすると、CloudFormationの仕様により「リソースの置換」が発生し、予期せぬダウンタイムやデータ損失のリスクがあります。<br>また、よくある「スナップショットから復元して差し替え」という手法も、CDKのState管理との兼ね合いで今回は採用できませんでした。</p>



<p class="wp-block-paragraph">様々な制約がある中、「既存データを維持しつつ」「CDK管理のまま」暗号化リソースへ移行できたので、その手法を共有します。</p>



<h2 class="wp-block-heading">今回の要件と制約</h2>



<p class="wp-block-paragraph">今回のミッションにおける前提条件は以下の通りです。</p>



<ul class="wp-block-list">
<li><strong>既存RDSの暗号化</strong>（最重要）</li>



<li>既存データをそのまま移行できること</li>



<li>アプリ側の変更を避けるため、<strong>エンドポイント（またはDNS）の変更がないこと</strong></li>



<li><strong>ダウンタイムは許容</strong>（ALBでメンテナンス画面を挟む前提）</li>



<li>バッチ処理が稼働していない夜間帯での作業</li>



<li><strong>CDKコードの変更は最小限</strong>に留め、今後もCDKで管理し続けること</li>
</ul>



<h2 class="wp-block-heading">結論：CDKデプロイを3回行う</h2>



<p class="wp-block-paragraph">結論から言うと、<strong>「削除ポリシーを変更して既存RDSをCDK管理から切り離し、新規で暗号化RDSを作成する」</strong>という力技で解決しました。</p>



<p class="wp-block-paragraph">具体的には以下の3ステップでデプロイを行います。</p>



<ol start="1" class="wp-block-list">
<li><code>RemovalPolicy.RETAIN</code> を設定（既存保護）</li>



<li><code>instanceIdentifier</code> を変更（既存切り離し）</li>



<li><code>storageEncrypted: true</code> を指定（新規作成）</li>
</ol>



<h3 class="wp-block-heading">なぜこの手順が必要なのか？（ハマりポイント）</h3>



<p class="wp-block-paragraph">通常、CDKで管理されているリソースに対し、<code>instanceIdentifier</code>（物理ID）を固定したまま <code>storageEncrypted: true</code> に変更してデプロイしようとすると、以下のエラーが発生して失敗します。</p>



<blockquote class="wp-block-quote is-layout-flow wp-block-quote-is-layout-flow">
<p class="wp-block-paragraph"><strong>CloudFormation cannot update a stack when a custom-named resource requires replacing</strong></p>
</blockquote>



<p class="wp-block-paragraph">これは、「カスタム名（明示的な名前）がついているリソースを置換（削除＆作成）することはできない」というCloudFormationの安全装置です。これを回避するために、あえて識別子を変更して別リソースとして認識させる必要があります。</p>



<hr class="wp-block-separator has-alpha-channel-opacity"/>



<h2 class="wp-block-heading">手順詳細</h2>



<h3 class="wp-block-heading">1. 削除ポリシーの変更</h3>



<p class="wp-block-paragraph">まず、既存のRDSがCDKのスタック操作によって削除されないようにします。 これをやらないと、次の手順で「既存RDSの削除」が走ってしまい、データが消えます（超重要）。</p>



<p class="wp-block-paragraph">コード スニペット</p>



<pre class="wp-block-code"><code>new rds.DatabaseInstance(this, 'RdsInstance', {
  instanceIdentifier: 'my-prod-db', // 現在の識別子
  // ...
  removalPolicy: cdk.RemovalPolicy.RETAIN, // ここを追加！
});
</code></pre>



<p class="wp-block-paragraph">この状態で一度 <strong><code>cdk deploy</code></strong> します。 これで、このRDSはスタックから削除される際も、AWS上にはリソースが残るようになります。</p>



<h3 class="wp-block-heading">2. instanceIdentifier の変更</h3>



<p class="wp-block-paragraph">次に、既存のRDSをCDKの管理下から外す（Orphanにする）作業です。 CDK上の識別子を、既存のものとは別の名前に書き換えます。</p>



<p class="wp-block-paragraph">コード スニペット</p>



<pre class="wp-block-code"><code>new rds.DatabaseInstance(this, 'RdsInstance', {
  instanceIdentifier: 'my-prod-db-temp', // 3と被らない一時的な名前に変更
  // ...
  removalPolicy: cdk.RemovalPolicy.RETAIN,
});
</code></pre>



<p class="wp-block-paragraph">この状態で <strong><code>cdk deploy</code></strong> します。</p>



<p class="wp-block-paragraph"><strong>何が起きるか：</strong><br> CloudFormationは「<code>my-prod-db</code> を管理から外し、新しく <code>my-prod-db-temp</code> を作る」という挙動をします。 ステップ1で <code>RETAIN</code> を設定しているため、<strong>旧RDS（データ入り）は削除されずにそのまま残ります。</strong> <br>※この時点でアプリはまだ旧RDSを見ています。</p>



<h3 class="wp-block-heading">3. いよいよ暗号化RDSの構築</h3>



<p class="wp-block-paragraph">最後に、本命の「暗号化されたRDS」を作成します。</p>



<p class="wp-block-paragraph">コード スニペット</p>



<pre class="wp-block-code"><code>new rds.DatabaseInstance(this, 'RdsInstance', {
  instanceIdentifier: 'my-prod-db', // 元の名前に戻す（ここが変わるとエンドポイント変わるので注意）
  storageEncrypted: true,           // 暗号化をON
  removalPolicy: cdk.RemovalPolicy.RETAIN,
  // ...
});
</code></pre>



<p class="wp-block-paragraph">この状態で <strong><code>cdk deploy</code></strong> します。</p>



<p class="wp-block-paragraph"><strong>注意点：</strong><br>もし <code>instanceIdentifier</code> を手順1と同じ名前（<code>my-prod-db</code>）に戻したい場合、手順2の時点でAWS上に古い <code>my-prod-db</code> が残っているため、名前重複でエラーになる可能性があります。<br>その場合は、<strong>AWSマネジメントコンソールまたはCLIから、古いRDS（手順2で切り離されたもの）の識別子を手動で <code>my-prod-db-old</code> などにリネーム</strong> してからデプロイしてください。</p>



<p class="wp-block-paragraph">これで、暗号化された空っぽのRDSが立ち上がりました。</p>



<h2 class="wp-block-heading">データ移行</h2>



<p class="wp-block-paragraph">新旧2つのRDSが存在している状態になりました。</p>



<ul class="wp-block-list">
<li><strong>旧RDS</strong>
<ul class="wp-block-list">
<li>データあり</li>



<li>非暗号化</li>



<li>CDK管理外</li>
</ul>
</li>



<li><strong>新RDS</strong>
<ul class="wp-block-list">
<li>データなし</li>



<li>暗号化済み</li>



<li>CDK管理下</li>
</ul>
</li>
</ul>



<p class="wp-block-paragraph">データ量がそれほど多くない（数GB〜数十GB程度）ため、今回はシンプルに <code>mysqldump</code> で移行を行いました。TB級の場合は AWS DMS (Database Migration Service) の利用を検討してください。</p>



<pre class="wp-block-code"><code># SSM Session Manager 等で踏み台サーバーに接続

# 1. 旧RDSからデータをエクスポート（パイプで圧縮して転送時間を短縮）
mysqldump -h &#91;旧RDSエンドポイント] -u &#91;ユーザー名] -p \
  --single-transaction --routines --triggers \
  --databases &#91;対象のスキーマ] \
  | gzip &gt; dump.sql.gz

# 2. 新RDSへインポート
zcat dump.sql.gz | mysqldump -h &#91;新RDSエンドポイント] -u &#91;ユーザー名] -p
</code></pre>



<p class="wp-block-paragraph">データの移行が完了したら、アプリケーションの接続先が新RDSに向いていることを確認し、メンテナンスを解除します。</p>



<h2 class="wp-block-heading">後片付け</h2>



<p class="wp-block-paragraph">無事稼働確認が取れたら、不要なリソースを削除してお財布を守りましょう。</p>



<ol start="1" class="wp-block-list">
<li><strong>手順2で立ち上がった一時的なRDS</strong>（CDKの管理からは外れているはずですが、念の為確認して削除）</li>



<li><strong>手順1以前から存在していた旧RDS</strong>（リネームした <code>my-prod-db-old</code> など）</li>
</ol>



<p class="wp-block-paragraph">スナップショットが取れていることを確認した上で、マネジメントコンソールから削除しました。</p>



<h2 class="wp-block-heading">(おまけ) 試したけどうまくいかなかったこと</h2>



<h3 class="wp-block-heading"><code>cdk import</code> での既存リソース取り込み</h3>



<p class="wp-block-paragraph">既存のリソースをリネームして、新しいスタックに取り込もうと <code>cdk import</code> を試みましたが、以下のようなメッセージが出てスキップされました。</p>



<pre class="wp-block-code"><code>TmpStack: no new resources compared to the currently deployed stack, skipping import.
</code></pre>



<p class="wp-block-paragraph">また、定義と実リソースのプロパティ（暗号化の有無）が食い違っているため、インポート自体が整合性エラーになる可能性が高いです。</p>



<h3 class="wp-block-heading"><code>DatabaseInstanceFromSnapshot</code> の利用</h3>



<p class="wp-block-paragraph">スナップショットから復元するCDKのクラスですが、これをメインの定義にしてしまうと、常に「スナップショットから作られた状態」が正となり、パラメータグループやバージョン更新などの運用時にドリフト（設定乖離）の管理が面倒になると判断し、今回は見送りました。</p>



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



<p class="wp-block-paragraph">CDKで管理しているステートフルなリソース（RDSなど）の「再作成必須な変更」は非常に神経を使います。 <strong>「Retainで保護」→「Identifier変更で管理から切り離し」→「新規作成」</strong> というフローは、RDSに限らず他のリソースでも応用が効くテクニックなので、覚えておいて損はないはずです。</p>



<p class="wp-block-paragraph">なにはともあれですが、RDSは最初から暗号化しましょう。これだけは覚えて頂けると。。。</p>



<p class="wp-block-paragraph">同じ悩みを抱えるPHPer（およびAWS使い）の助けになれば幸いです！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2198/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 class="wp-block-paragraph">こんちゃーす。最近 CDK と格闘している PHPer です。</p>



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



<p class="wp-block-paragraph">ネット上の「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 class="wp-block-paragraph">とスタックを分割したくなりませんか？<br>でも、分割した途端に「StackA で作ったVPCのIDをどうやってStackBにわたすの？」という問題が発生します。</p>



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



<p class="wp-block-paragraph"></p>



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



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



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



<p class="wp-block-paragraph">私の構成案：<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 class="wp-block-paragraph">実際にコードを見ていきましょう。</p>



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



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



<p class="wp-block-paragraph">ここでは <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 class="wp-block-paragraph"></p>



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



<p class="wp-block-paragraph">ここがポイントデス。 <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 class="wp-block-paragraph"></p>



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



<p class="wp-block-paragraph">最後にエントリーポイントで紐づけます。</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 class="wp-block-paragraph"></p>



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



<p class="wp-block-paragraph">この方法のメリットは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 class="wp-block-paragraph"></p>



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



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



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



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



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



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



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



<p class="wp-block-paragraph">皆さんの構成おしてほしいっす。</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>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>【実録】AWS ALBのデフォルトルールが招いた悲劇 &#8211; DoS攻撃からアプリを守るには？</title>
		<link>https://otonan-syusyoku.work/archives/2083</link>
					<comments>https://otonan-syusyoku.work/archives/2083#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sat, 24 May 2025 03:31:25 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[ALB]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[EC2]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=2083</guid>

					<description><![CDATA[こんにちは、比嘉です！以前は自社開発企業で開発をしていましたが、現在は一時的にニートライフを満喫しています。 今日は、僕がAWS環境で構築したWEBアプリケーションがDoS攻撃を受け、サーバーダウンに至った際の障害対応に [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>こんにちは、比嘉です！以前は自社開発企業で開発をしていましたが、現在は一時的にニートライフを満喫しています。</p>
<p>今日は、僕がAWS環境で構築したWEBアプリケーションがDoS攻撃を受け、サーバーダウンに至った際の障害対応について、その全貌を語りたいと思います。</p>
<p>特に、ALB（Application Load Balancer）の思わぬ落とし穴が原因だったという、衝撃の事実を皆さんに共有します。<br />
同じような問題で悩んでいる方、これからAWS環境を構築する方は、ぜひ参考にしてください。</p>
<p>&nbsp;</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>障害内容</h2>
<p>ある日突然、ALB配下のアプリケーションサーバーがDoS攻撃によりダウンしてしまいました。</p>
<p>緊急事態発生です！確認すると、アクセスのあったIPはまさかのオレゴンリージョンから。</p>
<p>僕が管理しているアプリケーションサーバーは東京リージョンにあるため、なぜ遠く離れたオレゴンから大量のアクセスが来ているのか、この時点では全く見当がつきませんでした。</p>
<div class="sc_frame_wrap inline orange">
<div class="sc_frame no-title shadow">
<ul>
<li>ALB配下のアプリケーションサーバーがDos攻撃によりサーバーダウン。</li>
<li>アクセスのあったIPの出どころはオレゴンリージョン</li>
<li>私の管理しているアプリケーションサーバーは東京リージョン。</li>
</ul>
</div>
</div>
<h2>調査内容</h2>
<p>まずは手がかりを求めて、nginxやアプリケーションのアクセスログ、エラーログを徹底的に漁りました。<br />
しかし、一向に決定的な手がかりが見つかりません。</p>
<p>そんな中、アクセスログを注意深く見ていたところ、ある異変に気づきました。<br />
通常、僕たちが意図的に公開している <strong>EIP</strong> で抑えているグローバルIPとは全く関係ないIPアドレスから、アプリケーションサーバーにアクセスが到達しているのです。</p>
<p>そこで、すかさずdigコマンドを実行してみると、やはり先ほどのEIPとは無関係のIPアドレスが確認できました。<br />
しかし、「なぜ、僕たちに関わりのないIPがアプリケーションへアクセスを流しているんだ？」</p>
<p>この疑問が頭から離れません。ルーティングテーブルや他のAWSリソースを調べても問題は見当たらず、「うーん、わからない…」という状況が3時間ほど続きました。</p>
<p>&nbsp;</p>
<p>精神的に疲弊し始めたその時、ふとALB（Application Load Balancer）のルールに問題があるのではないかという考えがよぎりました。そして、確認してみると…ビンゴです！</p>
<p>&nbsp;</p>
<p><strong>ALBのデフォルトルールが、アプリケーションサーバーが所属するターゲットグループにトラフィックを転送する設定</strong>になっていたのです。</p>
<p>そして、<strong>Route 53のAレコードにALBのエイリアスを指定していることで、ALBに紐づくサブネットのグローバルIPでもアクセスが可能になっている仕様</strong>もここで理解しました。</p>
<p>つまり、<span class="sc_marker blue"><strong>ALBのグローバルIPに直接アクセスが入ると、意図せずアプリケーションサーバーへ流れてしまう状態になっていた</strong></span>のです。</p>
<p>この瞬間、「これで勝ち確定！」と心の中で叫びましたね。</p>
<h2>対策</h2>
<p>今回の障害を受けて、最も効果的な対策はALBのデフォルトルールを適切に設定することです。意図しないアクセスをアプリケーションサーバーに流さないために、以下のように設定することをおすすめします。</p>
<ul>
<li>デフォルトルールで固定レスポンスを返す:レスポンスコード: 503 Service Unavailable</li>
<li>本文: 許可されていないリクエストです</li>
</ul>
<p>これにより、正規のドメインを通さないALBへの直接アクセスはアプリケーションサーバーに到達せず、DoS攻撃のリスクを大幅に低減できます。</p>
<p>もし、プロジェクトの制約上、固定レスポンスを返すのが難しい場合は、少なくともアプリケーションサーバーへリクエストを流さない設定にすることが重要です。例えば、別のダミーのターゲットグループを指定するか、アクセスを拒否するアクションを設定することも検討してください。</p>
<h2>調査に使ったもの</h2>
<h3>nginx のアクセスログ</h3>
<p>該当のリクエストがあった時間帯のアクセスを dig ることで出どころのIP等がわかります。</p>
<p>IPでどの辺りの地域かが判別できたり、content-type を元に BOT からのリクエストなのかがわかったりします。一旦、アクセスログを覗いてみましょう。</p>
<p>2025年に入って、生成AIからのリクエストが頻繁に来ている事を確認できているのでアクセスログを覗いてみると面白いですよ。</p>
<h3><span>VPC フローログ</span></h3>
<p>AWS のサービスです。VPC内の監視ができるのでかなり有用です。。使っていないプロジェクトは導入オススメします。</p>
<blockquote><p>VPC、サブネット、またはネットワークインターフェイスのフローログを作成できます。サブネットまたは VPC のフローログを作成する場合、そのサブネットまたは VPC 内の各ネットワークインターフェイスが監視されます。</p>
<p>監視されるネットワークインターフェイスのフローログデータは、フローログレコードとして記録されます。これは、トラフィックフローについて説明するフィールドで構成されるログイベントです。詳細については、「フローログレコード」を参照してください</p>
<p><cite class="blockquote_ref"> <a href="https://docs.aws.amazon.com/ja_jp/vpc/latest/userguide/flow-logs-basics.html" target="_blank" rel="noopener noreferrer">フローログの基礎</a> </cite></p></blockquote>
<h3>コマンド類</h3>
<h4 data-sourcepos="7:1-7:114">nslookup</h4>
<p>DNS が正しく設定されているか、IPがEIPと同値かに使用した、</p>
<h4>dig</h4>
<p data-sourcepos="7:1-7:114">ドメイン名に関する詳細な情報を取得するためのツールです。今回はDNSの設定が正しいか検証するために使用しました。</p>
<h4 data-sourcepos="7:1-7:114">tail</h4>
<p>アクセスログを掘り起こすのに使用しました。リアルタイム監視を後輩に見せたら尊敬されたのでドヤりたい人使ってください。（後輩が可愛いだけ</p>
<p>&nbsp;</p>
<h2>まとめ</h2>
<p>誰でも簡単に作れるようになっているのがAWSの特徴ですが、結果として穴ができてしまうことが多々あるので皆さんの環境も見てみましょう。</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/2083/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>
