<?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>LAMP &#8211; エンジニア見習い</title>
	<atom:link href="https://otonan-syusyoku.work/archives/tag/lamp/feed" rel="self" type="application/rss+xml" />
	<link>https://otonan-syusyoku.work</link>
	<description>三流プログラマー</description>
	<lastBuildDate>Wed, 14 Jan 2026 06:26:24 +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>LAMP &#8211; エンジニア見習い</title>
	<link>https://otonan-syusyoku.work</link>
	<width>32</width>
	<height>32</height>
</image> 
	<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>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>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】S3への通信量削減の手法</title>
		<link>https://otonan-syusyoku.work/archives/1842</link>
					<comments>https://otonan-syusyoku.work/archives/1842#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sun, 15 Sep 2024 07:31:51 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[CakePHP]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[サーバー]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1842</guid>

					<description><![CDATA[便利なストレージサービスであるS3を皆さん使っていますか…!? 静的コンテツの配信サーバーの役割をこなしたり、イレブンナインの耐久性を持ったストレージサービスといった点からAWSの中でも大好きなサービスです。 便利が故に [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>便利なストレージサービスであるS3を皆さん使っていますか…!?</p>
<p>静的コンテツの配信サーバーの役割をこなしたり、イレブンナインの耐久性を持ったストレージサービスといった点からAWSの中でも大好きなサービスです。</p>
<p>便利が故にコスト面でネックになってしまう状況が多々あるかと思いましたので、個人的に経験したコストダウンの設計手法をご紹介したいと思いますー！</p>
<div class="sc_frame_wrap inline orange">
<div class="sc_frame_title">話すこと</div>
<div class="sc_frame">
<p>今回は設計について話します。<br />
実装については話しません。</p>
</div>
</div>
<h2>手始めに</h2>
<p>まず初めにS3に関わる費用のざっくりとした内訳を確認しておきましょう。</p>
<ul>
<li>ストレージ</li>
<li>データ転送（IN/OUT）</li>
<li>セキュリティコントロール</li>
<li>レプリケーション</li>
<li>etc&#8230;</li>
</ul>
<p>一般的な運用を想定すると <strong>ストレージ </strong>と <strong>データ転送 </strong>が費用の大部分を占めるはずです。</p>
<p>その中でストレージに関しては、<br />
不必要なデータを置かない, データのライフサイクルを短くする, ストレージクラスを最適なものに設定するなどはありますが、<span class="sc_marker blue"><strong>S3にどのようなデータを置くかは各システムで要件が異なってくるため、費用の削減はあまり期待できません。</strong></span></p>
<p>そうなってくるとデータ転送の部分でコストを最適化するのが必要になってきます。</p>
<h2>通信量削減の取り組み</h2>
<h3>イメージ図</h3>
<table style="border-collapse: collapse; width: 100%;">
<tbody>
<tr>
<td style="width: 50%;"><a href="https://otonan-syusyoku.work/archives/1842/flow-drawio" rel="attachment wp-att-1883"><img fetchpriority="high" decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/09/flow.drawio.png" alt="" width="722" height="514" class="aligncenter size-full wp-image-1883" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/09/flow.drawio.png 722w, https://otonan-syusyoku.work/wp-content/uploads/2024/09/flow.drawio-300x214.png 300w" sizes="(max-width: 722px) 100vw, 722px" /></a></td>
<td style="width: 50%;"><a href="https://otonan-syusyoku.work/archives/1842/aws-s3-request-cosst" rel="attachment wp-att-1880"><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2024/09/aws-s3-request-cosst.png" alt="" width="632" height="308" class="aligncenter size-full wp-image-1880" srcset="https://otonan-syusyoku.work/wp-content/uploads/2024/09/aws-s3-request-cosst.png 632w, https://otonan-syusyoku.work/wp-content/uploads/2024/09/aws-s3-request-cosst-300x146.png 300w" sizes="(max-width: 632px) 100vw, 632px" /></a></td>
</tr>
</tbody>
</table>
<h3>やりたいこと</h3>
<p><span class="sc_marker blue"><strong>S3に゙保管しているコンテンツを毎回S3からダウンロードすることを辞めるための設計</strong></span>です。</p>
<p>これにより、キャッシュを用いてS3からの転送量を大幅に減らすことができます。（サイトへのアクセスが多ければ多いほど今回の設計は意味を増してくると思います。</p>
<p>&nbsp;</p>
<p>添付した画像のフローを改めて文字に起こします。</p>
<ol>
<li><strong>ページアクセス</strong>後に、必要なViewやCSS、画像などがリクエストされます。</li>
<li>まずアプリケーションサーバー（EC2）にコンテンツがキャッシュされているか確認します。</li>
<li>キャッシュに存在する場合は、そのままレスポンスを返します（転送料金が発生しません）。</li>
<li>キャッシュに存在しない場合は、S3からコンテンツを取得し、その後キャッシュします（S3へのアクセスを減らす）。</li>
</ol>
<h3>ChatGPT に計算してもらった</h3>
<pre class="line-numbers"><code class="language-php">### 東京リージョン（Asia Pacific (Tokyo)）でのS3のデータ転送料金

2023年9月現在、東京リージョンでのS3のデータ転送料金は次の通りです：

- 最初の1GB: **無料**
- 1GB～10TBまでのデータ転送: **$0.114/GB**

これに基づいて、東京リージョンでの転送料金削減を計算します。

### 仮定
- 1回のアクセスでS3からダウンロードされるデータの量：**1MB**
- 1日のアクセス数：**10,000回**
- キャッシュ成功率：**80%**
- 東京リージョンでのS3データ転送料金：**$0.114/GB**

### 計算式（東京リージョン）
1MBのデータが10,000回ダウンロードされる場合：
```
1MB × 10,000 = 10,000MB = 10GB
```

#### S3へのアクセスがない場合のコスト（全アクセスがS3の場合）
```
10GB × 0.114 = $1.14
```

#### キャッシュ導入後のコスト
キャッシュ成功率80%の場合、S3へのアクセスが20%：
```
10,000 × 0.2 = 2,000回
```

2,000回分のダウンロードデータ量：
```
1MB × 2,000 = 2,000MB = 2GB
```

これに対する転送料金：
```
2GB × 0.114 = $0.228
```

#### 削減額
S3にすべてのリクエストを送る場合のコストは**$1.14**、キャッシュを利用した場合のコストは**$0.228**。
したがって、削減できる金額は：
```
$1.14 - $0.228 = $0.912
```

### 結論
キャッシュを使うことで、東京リージョンの場合は、
1日あたり約**$0.912**の転送料金削減が見込めます。

これを1ヶ月（30日）分に換算すると、約**$27.36**の削減が期待できます。

もちろん、アクセス数やキャッシュ成功率、ダウンロードデータのサイズによって結果は変わりますが、
このざっくりとした計算での削減効果は**80%**程度です。
</code></pre>
<h3>注意点①</h3>
<p><span class="sc_marker blue"><strong>キャッシュクリアの方法を適切に設定しない場合、全てのコンテンツリクエストがキャッシュデータを返却してしまいます。</strong></span></p>
<p>各方面から以下のようなお叱りが届いてしまうので、今回ご紹介した設計を取る場合は、しっかりとキャッシュクリアの戦略を練りましょう。。。</p>
<div class="voice left">
<div class="icon">
<p><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2020/10/名称未設定のデザイン-69.png" /></p>
<div class="name">エンドユーザー</div>
</div>
<div class="text sc-inner-content sc_balloon left blue">
<p>ユーザーから保存したデータが古いままなんだけどどうなってるの!?</p>
</div>
</div>
<div class="sc_frame_wrap inline orange">
<div class="sc_frame_title">キャッシュクリア</div>
<div class="sc_frame">
<p>&#8211; バッチ処理にて定期的に削除する（1日1回、6時間毎に&#8230;<br />
&#8211; データの書き換えのタイミングで該当のキャッシュを削除</p>
</div>
</div>
<h3>注意点②</h3>
<p><span class="sc_marker blue"><strong>アプリケーションサーバーにコンテンツを配置するので、アプリケーションサーバー自体のストレージを圧迫する可能性が大いにあります。</strong></span><br />
アプリケーションサーバーのストレージを外部に移す目的でS3を導入している場合は、今回の手法は取れないので、CDNサービス等を用いてキャッシュさせるのも一つの手になってくると思います。</p>
<h3>注意点③</h3>
<p>注意点②と同様にアプリケーションサーバーにコンテンツを配置するが故の問題として、センシティブなデータをアプリケーションサーバーに配置する可能性があります。<br />
例： 個人情報の類のコンテンツ（運転免許証、何らかの明細）</p>
<p>これらを一時的にではありますが、アプリケーションサーバーに配置するのがシステム要件的にNGの場合は、絶対に今回の手法は取れません！</p>
<h2>最後に</h2>
<p>ちょいとした工夫でコストダウンを見込めますので、皆さんも試してみて欲しいですー！</p>
<p>システムの要件等で導入できなければごめんさい！！</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1842/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【今日の覚書】static メソッドは適切に</title>
		<link>https://otonan-syusyoku.work/archives/1817</link>
					<comments>https://otonan-syusyoku.work/archives/1817#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Thu, 12 Sep 2024 07:50:30 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[コーディング]]></category>
		<category><![CDATA[脱3流プログラマー]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1817</guid>

					<description><![CDATA[static おじさんになりかけたので、備忘録として学習の記録を残します。 I`m PHPer!!!!! 便利なstatic PHPの static メソッドは、インスタンスを生成せずにクラスから直接呼び出せるメソッドで [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>static おじさんになりかけたので、備忘録として学習の記録を残します。</p>
<p>I`m PHPer!!!!!</p>
<h2>便利なstatic</h2>
<p>PHPの <strong>static メソッド</strong>は、インスタンスを生成せずにクラスから直接呼び出せるメソッドです。</p>
<h2>使うべきタイミング</h2>
<p><a href="https://otonan-syusyoku.work/archives/1095/php" rel="attachment wp-att-1098"><img decoding="async" src="https://otonan-syusyoku.work/wp-content/uploads/2022/03/PHP.png" alt="PHP" width="1000" height="500" class="aligncenter size-full wp-image-1098" srcset="https://otonan-syusyoku.work/wp-content/uploads/2022/03/PHP.png 1000w, https://otonan-syusyoku.work/wp-content/uploads/2022/03/PHP-300x150.png 300w, https://otonan-syusyoku.work/wp-content/uploads/2022/03/PHP-768x384.png 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></a></p>
<h3>状態を持たない処理</h3>
<p>インスタンスの状態に依存しない、独立した処理を行う場合に適しています。例えば、ユーティリティ関数やヘルパー関数などがこれに該当します。</p>
<pre class="line-numbers"><code class="language-php">class MathHelper {
  public static function add($a, $b) {
    return $a + $b;
  }
}

echo MathHelper::add(3, 4); // 出力: 7
</code></pre>
<p>&nbsp;</p>
<h3>ファクトリーメソッド</h3>
<p>クラスのインスタンスを生成するメソッド（ファクトリーメソッド）として使用されることがあります。</p>
<p>この場合、<strong>`new`キーワード</strong>を隠蔽して、インスタンス化の過程を制御できます。</p>
<pre class="line-numbers"><code class="language-php">class User {
  public $name;

  private function __construct($name) {
    $this-&gt;name = $name;
  }

  public static function create($name) {
    return new self($name);
  }
}

$user = User::create('John Doe');
</code></pre>
<p>&nbsp;</p>
<p>あまり使いたくないね。。。</p>
<p>&nbsp;</p>
<h2>シングルトンパターン</h2>
<p>特定のクラスのインスタンスが一つだけ存在することを保証するシングルトンパターンの実装に利用されます。</p>
<pre class="line-numbers"><code class="language-php">&lt;?php

class Singleton
{
    private static $instance;

    private function __construct()
    {
// プライベートコンストラクタ
    }

    public static function getInstance()
    {
        if (self::$instance === null) {
            self::$instance = new self();
        }
        return self::$instance;
    }
}

$singleton = Singleton::getInstance();
</code></pre>
<p>&nbsp;</p>
<h2>だめな使い方</h2>
<h3>インスタンスの状態を操作する処理</h3>
<p>インスタンスの状態に依存するメソッドに`static`を使うべきではありません。<code>static</code>メソッドはクラスレベルで動作するため、インスタンス固有の状態を保持することができません。</p>
<pre class="line-numbers"><code class="language-php">class User {
  private $name;

  public static function setName($name) {
    $this-&gt;name = $name; // エラー: $thisを使えない
  }
}
</code></pre>
<p>&nbsp;</p>
<h3>過度な使用</h3>
<p>全てのメソッドを`static`にすることは避けるべきです。</p>
<p><code>static</code>メソッドを多用すると、オブジェクト指向プログラミングの基本原則である<code>カプセル化</code>や<code>多態性</code>が損なわれる可能性があります。<br />
特に、後からメソッドをオーバーライドする必要が出てきた場合に柔軟性を失います。</p>
<h3>依存性注入の回避</h3>
<p><code>static</code>メソッドは依存性注入と相性が悪く、テストやメンテナンスが難しくなる場合があります。テスト可能なコードを維持するためには、インスタンスメソッドを使用し、必要な依存関係をコンストラクタやセッターで注入する方が望ましいです。</p>
<h2>まとめ</h2>
<p><code>static</code> メソッドは、クラス固有の処理やユーティリティ関数として適切に使用することで、コードの構造を明確にし、インスタンス化を不要にするメリットがあります。ただし、過度な使用はオブジェクト指向の原則を損なう可能性があるため、状況に応じて適切に使い分けることが重要です。</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1817/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>AmazonLinux2023はPHP7系が使えません</title>
		<link>https://otonan-syusyoku.work/archives/1845</link>
					<comments>https://otonan-syusyoku.work/archives/1845#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Mon, 19 Aug 2024 22:29:45 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[仕事の独り言]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[EC2]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[PHP]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1845</guid>

					<description><![CDATA[とある日、サーバー移行のタスクが一端のプログラマーである私に降ってきました。 セキュリティサポートが2022年末に終了して久しいPHP7系で動いているシステムが乗っかているサーバーの移行タスクです。 &#160; 事の発 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>とある日、サーバー移行のタスクが一端のプログラマーである私に降ってきました。</p>
<p>セキュリティサポートが2022年末に終了して久しいPHP7系で動いているシステムが乗っかているサーバーの移行タスクです。</p>
<p>&nbsp;</p>
<h2>事の発端</h2>
<p><span>CentOS 7のサポート終了がきっかけです。</span></p>
<p>DevOps系のお仕事に携わっている人なら理解してくれると思うのですが、サポートが切れた技術を継続して使用していくことの恐ろしさったら中々のものです。</p>
<p>まぁそんな事情があったので、CentOSを別のOSに移行していこうー！というノリで始まりました。</p>
<h2>困ったことに</h2>
<p>クラウドサービスはAWSを選択しているので、AWSが提供しているOSであるAmazonLinux2023を移行先のOSとして決定しました。</p>
<p>単純なLAMP環境を構築することが今回のタスクだったので、作業をガシガシ進めていくと一つの問題にぶち当たりました。</p>
<p><strong>PHP7系がインストールできない…</strong></p>
<h3>Remiリポジトリ, EPELが使えない</h3>
<p>そもそもの問題としてPHP7系は2022年末にサポートが切れているので、AmazonLinuxがサポートしている訳がありません。<br />
普通に考えてみれば至極真っ当です。</p>
<p>今回のタスクでは、PHP7系を使うことは外せない要件として上がっていたので、どうしてもPHP7系が必要です。</p>
<p>こうなったらRemiリポジトリから持ってくるしかないなーと思っていた矢先、Remiリポジトリも使えないとのこと。（EPELがそもそも使えないため</p>
<p><a href="https://docs.aws.amazon.com/ja_jp/linux/al2023/ug/relationship-to-fedora.html">AWS ドキュメント</a><br />
<a href="https://docs.aws.amazon.com/ja_jp/linux/al2023/ug/compare-with-al2.html#epel">EPEL</a><br />
<a href="https://qiita.com/charon/items/6d34ae798e9b05e8bd0a">CentOSなどで使う、RemiRepositoryってなんだ？</a></p>
<p>&nbsp;</p>
<p>今回の要件（PHP7系 install）を達成する事は、Amazonlinux2023 では不可能だと知った瞬間でした…</p>
<h2>OS選定</h2>
<p>CentOS の移行先で検索すると以下のOSたちがよくヒットしました。</p>
<ul>
<li>RedHatEnterpriseLinux</li>
<li>AlmaLinux</li>
<li>RockyLinux</li>
</ul>
<p><strong>PHP7系 が使えること</strong> 及び <strong>互換性があること , </strong><strong>ある程度の認知度 </strong>があるOSを選択したかったので、 RockyLinux で行くことに決定しました。</p>
<p>最終的にはRockyLinux上でRemiリポジトリを使用して目的の環境を作成することができたよーん。</p>
<p>&nbsp;</p>
<h2>感謝を捧げる</h2>
<p>振り回された感が拭えないタスクだったのですが、誰が悪いとかはないなーという所感です。</p>
<p>CentOSのサポートが切れるのも、Amazonlinux2023 がPHP7 をサポートしていないのも、PHP7を使って未だに運用しているシステムも誰も悪くないです。<br />
（PHP7に゙関しては早くバージョンアップしてほしい…</p>
<p>最終的には何とかなったので、 RockeyLinux のコントリビューターに感謝を捧げます</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1845/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>自分で作ったサービスで売上を上げるのめちゃくちゃ難しいやんけ</title>
		<link>https://otonan-syusyoku.work/archives/1613</link>
					<comments>https://otonan-syusyoku.work/archives/1613#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sat, 27 Jan 2024 01:55:46 +0000</pubDate>
				<category><![CDATA[仕事の独り言]]></category>
		<category><![CDATA[日々の独り言]]></category>
		<category><![CDATA[生涯独学]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[Docker]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[Laravel]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[個人開発]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1613</guid>

					<description><![CDATA[こんにちは。都内でPHPerとして勤務している三流プログラマーです。 本業とは別に取り組んでいた個人開発でマネタイズができず、サービスを終了させてしまったのでここで供養させてください。 RIP My Service&#8 [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>こんにちは。都内でPHPerとして勤務している三流プログラマーです。</p>
<p>本業とは別に取り組んでいた個人開発でマネタイズができず、サービスを終了させてしまったのでここで供養させてください。</p>
<p>RIP My Service&#8230;</p>
<h2>作ったもの</h2>
<p>HP 兼 予約サイト 兼 ECサイトなるシステムを開発しました。</p>
<p>僕が沖縄県出身ということもあり、沖縄の個人経営を営んでいる小さな企業さんを相手に営業の肩代わりをすることを目的にシステムを開発しました。</p>
<p>というのも沖縄県は観光地として有名でありながらもHPすら持たず、地元の口コミのみで経営を行っているお店が多々ある状態でした。</p>
<p>せっかくの観光地でありながらお客様を呼び込めないのはかなりもったいないという思いから作った次第でございます。</p>
<h2>技術構成</h2>
<table style="border-collapse: collapse; width: 100%;">
<tbody>
<tr>
<td style="width: 50%;">デザイン</td>
<td style="width: 50%;">Tailwind + DaisyUI</td>
</tr>
<tr>
<td style="width: 50%;">フロントエンド</td>
<td style="width: 50%;">React</td>
</tr>
<tr>
<td style="width: 50%;">バックエンド</td>
<td style="width: 50%;">Laravel10</td>
</tr>
<tr>
<td style="width: 50%;">DB</td>
<td style="width: 50%;">RDS(MySQL8.x)</td>
</tr>
<tr>
<td style="width: 50%;">決済</td>
<td style="width: 50%;">Stripe</td>
</tr>
<tr>
<td style="width: 50%;">インフラ</td>
<td style="width: 50%;">サーバー：EC2<br />
DB：RDS<br />
ファイルサーバー：S3</td>
</tr>
<tr>
<td style="width: 50%;">その他</td>
<td style="width: 50%;">Docker</td>
</tr>
</tbody>
</table>
<h2>マネタイズポイント</h2>
<h3>物販購入</h3>
<p>沖縄ならではの商品が展開されるEC機能を開発しました。</p>
<p>1商品当たり個別に x％ の手数料をいただく契約になります。</p>
<h3>予約</h3>
<p>美容院やレジャー施設の予約を受け付ける機能を開発しました。</p>
<p>こちらもEC同様に1予約あたり、x円の手数料 or オプション契約 のどちらかを契約する想定でした。</p>
<h2>売り方</h2>
<p>沖縄出身ではあるものの都内で勤務しているということもあり現地での営業活動ができないという状況でした。<br />
そのため、現地で営業活動を行ってもらうために地元の友だちに「俺はこれから月100万円稼ぐシステムを開発するから営業をしてくれ」と<strong>大きく誇張した言い分</strong>を持って営業活動をしてもらいました。</p>
<p>その友人は面白そうだからという理由で快く引き受けてくれ、以下のマーケティング手法で営業を行ってもらいました。</p>
<ul>
<li>SNS
<ul>
<li>Instagram</li>
<li>Twitter</li>
</ul>
</li>
<li>ブログ</li>
</ul>
<p>先に結論から入るのですが、期間としては6ヶ月間行ってもらい、皆さんの想像通り契約は1件も入らなかったです。</p>
<h2>なぜ売れなかったのか</h2>
<h3>知名度</h3>
<p>これは誰しもが考えることだと思うのですが <span class="sc_marker blue"><strong>認知されていないサービスにお金を払う</strong></span> ことは避けたいはずです。</p>
<p>尚更、登記もしていない<span class="sc_marker blue"><strong>個人が開発したシステムに対して自分の命であるお金を注ぐことはあり得ない</strong></span>です。</p>
<p>考えられる手法としては以下の手法が取れるはずです。</p>
<ul>
<li>正しい営業をする</li>
<li>受託等で知名度を得る</li>
<li>その他のサービスで知名度を得る（知名度が無いことによる無限ループが発生する…）</li>
</ul>
<p>お客様が安心してこのサービスにはお金を投資できると胸を張って言えるようなシステムを構築するのは難しいですね。。。</p>
<h3>効果を得られるか不安</h3>
<p>知名度がないという理由にも近しいですが、当システムを使用することで利益を得られることに対する不安を拭い去る事はできませんでした。</p>
<p>なぜなら<span class="sc_marker blue"><strong>契約が1件もないということは実績が無いということですからお客様からすると不安しかありません</strong></span>。</p>
<p>世の中のスタートアップはどうやって利益を得ているんですか？？</p>
<h3>県民性</h3>
<p>こちらは確固たるデータがある訳ではないんですが、ハードに仕事をしないという県民性を見誤っていた可能性がある事を感じています。</p>
<p>沖縄は「のんかーな性格（のんきな性格）」と呼ばれる人が多い印象です。</p>
<p>事実として僕の知っている<span class="sc_marker blue"><strong>沖縄の経営者では食べていける分を稼げて楽しい毎日を送ることが大切</strong></span>という人が多いです。（4人だけしか知りませんが…）</p>
<p>そういった考えを持っている人に対して業務を効率化して売上を伸ばしましょうなんて言っても「忙しくなるなら現状のままで」といった答えが返ってくるに決まっています。</p>
<p>そのあたりをしっかりと調査する or ターゲットを先に見つける事が大切かなぁという印象です。</p>
<h2>反省</h2>
<p>僕はゴミを作ってしましました。</p>
<p>誰にも使われないシステムなんてゴミです。ただのゴミクズです。</p>
<p>今回は僕一人と頭の悪い友人の時間だけが溶けただけで済んで本当に良かったです。（実際には5万ほど運用費としてお金を使いました。）</p>
<p>仮に起業という形でサービスを開発していた場合は廃業に大きく近づいていたので、個人開発の一環でここまでの勉強ができたのは僕の才能かもしれません。笑</p>
<p>まだまだ作りたいものはあるので引き続き頑張っていきたいですねぇ〜</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1613/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>UbuntuにてMySQLコマンドだけ使いたい場合の備忘録</title>
		<link>https://otonan-syusyoku.work/archives/1446</link>
					<comments>https://otonan-syusyoku.work/archives/1446#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Sun, 17 Dec 2023 11:56:09 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[AWS]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[MySQL]]></category>
		<category><![CDATA[RDS]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1446</guid>

					<description><![CDATA[ワシはRDSに繋げたいだけなんじゃ！ 先日 RDS を構築した際に EC2 と接続ができなくて泣きたくなった。 原因はシンプルで EC2 に MySQL クライアントが存在しませんよという内容です。 クラウド先（RDS） [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>ワシはRDSに繋げたいだけなんじゃ！</h2>
<p>先日 RDS を構築した際に EC2 と接続ができなくて泣きたくなった。</p>
<p>原因はシンプルで <span class="sc_marker blue"><strong>EC2 に MySQL クライアントが存在しませんよ</strong></span>という内容です。</p>
<p>クラウド先（RDS）には MySQL が存在しているのに、わざわざ EC2 に MySQL 本体をインストールするのもなぁと思っていたのですが、ちょっと工夫するだけで EC2 から接続できるようにすることができることを知ったので共有ですぅぅぅ〜</p>
<p>[getpost id=&#8221;1426&#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>
<ul>
<li>とりあえずアプデ
<pre class="line-numbers"><code class="language-other">sudo apt update
</code></pre>
</li>
<li>mysql-client のパッケージを検索<br />
なんかいい感じのやつ出てくるよ</p>
<pre class="line-numbers"><code class="language-other">apt search mysql-clien</code></pre>
</li>
<li>最小を入れたい<br />
(お使いのバージョンに合わせたものを指定してね<br />
(client を入れれば MySQL コマンドが使えるよ</p>
<pre class="line-numbers"><code class="language-other">apt install mysql-client-core-8.x</code></pre>
</li>
<li>MySQL に接続
<pre class="line-numbers"><code class="language-other">mysql -h rds.amazonaws.com -P 3306 -u rds -p</code></pre>
</li>
</ul>
<h2>失敗時のエラー</h2>
<h3>権限不足</h3>
<pre class="line-numbers"><code class="language-other">Permission denied</code></pre>
<ul>
<li>Ubuntu で入ったから何となく行けてんのかと思いきや…</li>
<li>sudo しなさいよ</li>
</ul>
<h3>接続失敗</h3>
<pre class="line-numbers"><code class="language-other">ERROR 2003 (HY000): Can't connect to MySQL server on</code></pre>
<ul>
<li>RDS の設定漏れ</li>
<li>EC2 からの接続許可</li>
</ul>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1446/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>【セキュリティ】ufw と waf について</title>
		<link>https://otonan-syusyoku.work/archives/1422</link>
					<comments>https://otonan-syusyoku.work/archives/1422#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Mon, 09 Oct 2023 04:04:41 +0000</pubDate>
				<category><![CDATA[インフラ]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[セキュリティ]]></category>
		<category><![CDATA[設計]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1422</guid>

					<description><![CDATA[サーバー構築を任されたとき、ufwとwaf の違いがいまいちピンとこなかったので、ちょっと勉強してみました。 両者の違いをわからないままノリで進めていくと大恥かくので先に学んでおく事をおすすめします。 UFW（Uncom [&#8230;]]]></description>
										<content:encoded><![CDATA[<p>サーバー構築を任されたとき、ufwとwaf の違いがいまいちピンとこなかったので、ちょっと勉強してみました。</p>
<p>両者の違いをわからないままノリで進めていくと大恥かくので先に学んでおく事をおすすめします。</p>
<h2>UFW（Uncomplicated Firewall）とは</h2>
<p>UFWは、<span class="sc_marker blue"><strong>Linuxベースのシステムでファイアウォールを設定するためのツール</strong></span>です。</p>
<p>UFWは、iptablesと呼ばれるより高度なファイアウォール設定を簡略化したもので、コマンドラインを通じて簡単に設定できます。</p>
<p>主な特徴は以下です</p>
<ul>
<li>ポートベースのファイアウォール設定をサポート
<ul>
<li>特定のポートへのアクセスを制御</li>
<li>IPアドレス、サブネット、アプリケーションなどの設定が可能。</li>
</ul>
</li>
<li>シンプルな構文
<ul>
<li>ufwは非常に簡単な構文を持っており、コマンドラインで簡単に設定できる</li>
<li>ufwはシステム全体のファイアウォールとして機能する。</li>
<li>ネットワークトラフィックを制御するために使用される。</li>
<li>特にWebアプリケーションのセキュリティには直接関係なし。</li>
</ul>
</li>
</ul>
<h3>使用例</h3>
<p>サーバーに対してセキュリティに関わる制限を設ける。</p>
<pre class="line-numbers"><code class="language-other">#HTTPS の port 開放 
sudo ufw allow 443

#MySQL の port 開放 
sudo ufw allow 3306

#sshの port 閉鎖 
sudo ufw deny 22</code></pre>
<p>&nbsp;</p>
<p>ufw にてポートの開閉を明示的に指示ができる。</p>
<div class="sc_frame_wrap inline red">
<div class="sc_frame_title"><span class="sc_frame_icon"><i class="fa fa-check-square-o" aria-hidden="true"><span>fa-check-square-o</span></i></span>ssh のポート開閉設定には注意</div>
<div class="sc_frame">
<p>ufw にて ssh のポートを閉じた場合、<span class="sc_marker blue"><strong>当たり前ですが ssh を用いてサーバーに接続することができなくなります</strong></span>。</p>
<p>当時の私の権限では、コントロールパネルから操作する権限を与えられていないアカウントで作業を行っていたため、構築作業が何もできない状況に陥りました。</p>
<p>サーバー管理者に申請を出す時間がないくらい案件を掛け持ちしていたため、サーバーを削除して最初から構築するという事態に陥りました。</p>
<p>ssh 周りの設定は注意しながら進めましょう。笑</p>
</div>
</div>
<h2>WAF（Web Application Firewall）とは</h2>
<p>WAFは、<span class="sc_marker blue"><strong>Webアプリケーションのセキュリティを強化するためのセキュリティツール</strong></span>です。</p>
<p>WAFは、Webトラフィックに対する特定のセキュリティルールを適用し、Webアプリケーション攻撃（例：SQLインジェクション、クロスサイトスクリプティングなど）から保護します。</p>
<p>主な特徴は以下です</p>
<ul>
<li>Webアプリケーションに対するトラフィックフィルタリング
<ul>
<li>WAFは、HTTPおよびHTTPSトラフィックを対象として、不正なリクエストや攻撃を検出し、遮断します。</li>
<li>パターンマッチング：WAFは特定の攻撃パターンや脆弱性に対する署名またはルールを使用して攻撃を検出します。</li>
<li>ロギングと監視：WAFは攻撃試行をログに記録し、セキュリティエンジニアがそれを監視および対応できるようにします。</li>
</ul>
</li>
</ul>
<p>WAFはWebサーバーとWebアプリケーションの間に配置され、攻撃をブロックしてWebアプリケーションを保護します。</p>
<h3>WAFは販売されている</h3>
<p>今回の勉強をするまでは、WAFの防御する内容が SQLインジェクションや XSS などであったため、自分で記述するプログラムにて防御するものだと思っていました。（自分で書くべきプログラムの総称と思っていた…</p>
<p>内容を深掘っていくとWAFはかなりの数が外反されており、運用を楽にするためにたくさんの企業が導入しているということがわかりました。</p>
<p>独自のルールを付加させる事のできるAWS WAF はその内勉強しないといけないなぁという印象です…</p>
<h2>用途の異なるセキュリティツール</h2>
<p>UFW（Uncomplicated Firewall）とWAF（Web Application Firewall）は、異なるセキュリティ関連のツールであり、異なる用途と機能を持っています。</p>
<p>簡単にまとめると以下のとおりですー。</p>
<ul>
<li>UFWはシステムのファイアウォール設定を簡単に行うためのツール</li>
<li>WAFはWebアプリケーションのセキュリティを向上させるための特化したセキュリティツール</li>
</ul>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1422/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
		<item>
		<title>[Apache2]オラのセキュリティ対策</title>
		<link>https://otonan-syusyoku.work/archives/1289</link>
					<comments>https://otonan-syusyoku.work/archives/1289#respond</comments>
		
		<dc:creator><![CDATA[hrokig2]]></dc:creator>
		<pubDate>Mon, 03 Apr 2023 23:50:10 +0000</pubDate>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[絶対に必要なIT基礎知識]]></category>
		<category><![CDATA[LAMP]]></category>
		<category><![CDATA[セキュリティ]]></category>
		<guid isPermaLink="false">https://otonan-syusyoku.work/?p=1289</guid>

					<description><![CDATA[Apache2の設定ファイルについて はじめにApache2の設定ファイルについて理解しておきます。 &#160; WEB検索にてApacheのセキュリティ対策と検索するとApache導入後に自動生成される「/etc/a [&#8230;]]]></description>
										<content:encoded><![CDATA[<h2>Apache2の設定ファイルについて</h2>
<p>はじめにApache2の設定ファイルについて理解しておきます。</p>
<p>&nbsp;</p>
<p>WEB検索にてApacheのセキュリティ対策と検索するとApache導入後に自動生成される「/etc/apache2/apache2.conf」に定義してと書いてあります。</p>
<p>私のようにSSL化に伴い、設定ファイルを作成した場合は作成したファイルにセキュリティ対策を定義します。</p>
<p>/etc/apache2/sites-available/ドメイン名.conf</p>
<p>&nbsp;</p>
<p>「/etc/apache2/apache2.conf」と「/etc/apache2/sites-available/ドメイン名.conf」の違いとしては、</p>
<ul>
<li>全般的な設定は/etc/apache2/apache2.confに定義してね</li>
<li>サイトごとの設定をしたい場合は作成した設定ファイルに定義してね</li>
</ul>
<p>って感じになります。</p>
<p>&nbsp;</p>
<p>基本的に1サーバー1ドメインって形になるので、自動生成ファイルに記述していくことになるかと思います。</p>
<h2>エラーが出る場合</h2>
<p>これから紹介する対策を設定ファイルに定義した際にエラーが出る場合は下記のコマンドを実行してくださーい。</p>
<h3>Invalid command &#8216;Header&#8217;, perhaps misspelled or defined by a module not included in the server configuration</h3>
<h4>エラー内容</h4>
<p>Apache内でHTTPヘッダーの操作が許可されていませんよー。という内容</p>
<h4>対策</h4>
<p>下記コマンドでHTTPヘッダの有効化。</p>
<pre class="line-numbers"><code class="language-other">sudo a2enmod headers</code></pre>
<h2>クリックジャギング</h2>
<h3>クリックジャギングとは</h3>
<p>悪意のあるウェブページや広告をユーザーが踏むことで、意図しない操作を実行してしまう攻撃です。</p>
<p>具体的にはiframeタグやリンクなどを挿入し、既存のコンテンツの上に透過した悪意のあるコンテンツを重ねることで悪意のあるコードを実行させてしまう手法です。<br />
ユーザーからすると通常のボタンを押しただけなのに個人情報が盗まれてしまうという事象が発生します。</p>
<h3>対策</h3>
<p>HTTPヘッダーによる対策を実施します。</p>
<p>X-Frame-Options とはブラウザが特定のタグを許可するかどうかの設定です。</p>
<ul>
<li>DENY：どのサイトからも埋め込まれないようにする。</li>
<li>SAMEORIGIN：同じオリジン（同じドメイン・プロトコル・ポート番号）のサイトからのみ埋め込まれるようにする。</li>
<li>ALLOW-FROM：特定のURLからのみ埋め込まれるようにする。<br />
<span style="font-size: 10pt;">※今は非推奨らしい</span></li>
</ul>
<p>個人的には複数のサーバー(ロードバランサー)等で運用することが多いと思うので、SAMEORIGINを設定するケースが多いと思いますー。</p>
<p>&nbsp;</p>
<p>Apacheの場合は以下のファイルに追記します。</p>
<pre class="line-numbers"><code class="language-php">X-Frame-Options: DENY</code></pre>
<p>&nbsp;</p>
<p>HTMLページに設定する場合は以下のように設定します。<br />
<span style="font-size: 10pt;">※WEBサーバーに設定するのが推奨とのこと。</span></p>
<pre class="line-numbers"><code class="language-markup">&lt;meta http-equiv="X-FRAME-OPTIONS" content="DENY" /&gt;</code></pre>
<p>&nbsp;</p>
<h2>XSS</h2>
<h3>XSSとは</h3>
<p>悪意のあるスクリプトを注入し、ユーザーのブラウザで実行させることで、クッキー情報やセッションIDなどの個人情報を盗んだり、偽のWebページを表示してユーザーの意図しない行為を実施することができます。</p>
<p>&nbsp;</p>
<p>具体的にはGETパラーメーターに悪意のあるウェブページのリンクを入れたりすることで実現できます。</p>
<h3>考え方</h3>
<p>基本的にアプリケーション側で文字列のエスケープ処理やサニタイズ処理を実施します。</p>
<h3>Apacheでの対策</h3>
<p>XSSフィルター機能を設定します。</p>
<p>XSSフィルター機能とはブラウザの機能であり、ウェブページに含まれるリクエストパラーメーターやHTMLタグなどの入力値を検査し、悪意のあるスクリプトがあれば削除/エスケープするなどの対策を実施します。</p>
<pre class="line-numbers"><code class="language-other">Header set X-XSS-Protection "1; mode=block"</code></pre>
<h2>CSP</h2>
<h3>CSPとは</h3>
<p>Webページに埋め込まれる外部リソースの取り扱いを制限することを指します。</p>
<h3>対策</h3>
<pre class="line-numbers"><code class="language-other">Header set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; object-src 'none'; frame-ancestors 'none'"
</code></pre>
<p>&nbsp;</p>
<p>CSPの設定後に以下のエラーが出る方はドメインも含む形で &#8216;unsafe-eval&#8217;を設定してください。</p>
<p>ご自身のJSファイルが正規のものと判断させるために必要です。</p>
<p>Uncaught EvalError: Refused to evaluate a string as JavaScript because &#8216;unsafe-eval&#8217; is not an allowed source of script in the following Content Security Policy directive: &#8220;script-src &#8216;self&#8217; &#8216;unsafe-inline'&#8221;.</p>
<p>&nbsp;</p>
<p>ただし、Eval関数は文字列をJSの構文として扱うことが出来るので、推奨されません。</p>
<pre class="line-numbers"><code class="language-js">#実行されちゃう
let alert = alert('aiueo');
eval( "alert")</code></pre>
<p>そのため、他にも追加で対策してください。<br />
追加の対策として以下が考えられます。(N重にすることである程度は担保できるはずです。)</p>
<ul>
<li>XSSフィルター(サーバー側)</li>
<li>入力データの検証(アプリケーション側)</li>
</ul>
<h2>DOS/Ddos</h2>
<h3>DOS/DDOS</h3>
<p>システムに対して大量のリクエストを送ることで正常なリクエストを中止/失敗させサービスを正常に運用させなくする攻撃です。</p>
<h3>対策</h3>
<p>mod_evasiveというモジュールを使用します。</p>
<p>Linuxのディストリビューションやバージョンによって記載するファイルが異なります。</p>
<pre class="line-numbers"><code class="language-other">&lt;IfModule mod_evasive20.c&gt;
   DOSHashTableSize 3097
   DOSPageCount 10
   DOSSiteCount 50
   DOSPageInterval 1
   DOSSiteInterval 1
   DOSBlockingPeriod 10
   DOSLogDir "/var/log/mod_evasive"
   DOSEmailNotify admin@yourdomain.com
   DOSWhitelist 127.0.0.1
&lt;/IfModule&gt;</code></pre>
<p>&nbsp;</p>
<h2>画像の直リンク</h2>
<h3>直リンクされて困ること</h3>
<p>案件によっては、画像やPDFなどに情報を詰め込んだデータを扱う事があると思います。(保険の明細や履歴書など)</p>
<p>そういったデータをバイナリ化してDBへ登録するのではなく、特定ディレクトリに保管する場合に関しては、そのディレクトリに直接アクセスされてしまうと個人情報が公開されてしまいます。</p>
<p>そのための対策ですよん。</p>
<h3>対策</h3>
<p>リファラを使って直アクセスの対策を実施。</p>
<pre class="line-numbers"><code class="language-other">RewriteEngine On
RewriteCond %{HTTP_REFERER} !^$
RewriteCond %{HTTP_REFERER} !^http(s)?://(www\.)?ドメイン.com [NC]
RewriteRule \.(jpg|jpeg|png|gif)$ - [NC,F,L]</code></pre>
<p>一応、Apacheならではの.htaccessを使用したアクセス禁止方法もあります。</p>
<p>[getpost id=&#8221;1215&#8243; cat_name=&#8221;1&#8243; date=&#8221;0&#8243;]</p>
<h2>バージョン情報の非表示</h2>
<p>バージョンを表示することで対象のバージョンに合わせた攻撃を実施される/バージョンのセキュリティホールを見抜くなどの脅威があります。</p>
<p>サーバーにてコマンドを打てばバージョンを確認できるので、バージョンは非表示にしましょう。</p>
<h3>Apacheのバージョン非表示</h3>
<pre class="line-numbers"><code class="language-other">ServerTokens Prod</code></pre>
<h3>PHPのバージョン非表示</h3>
<p>/etc/php/{PHPのバージョン}/apache2/php.iniを編集。</p>
<pre class="line-numbers"><span style="color: #222222; font-family: monospace;"><span style="background-color: #e9ebec;">expose_php=off</span></span></pre>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
]]></content:encoded>
					
					<wfw:commentRss>https://otonan-syusyoku.work/archives/1289/feed</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
