テスト1
# EC2(AlmaLinux)にApache+PHPをゼロから構築してポートフォリオAPIをデプロイした話
## はじめに
ポートフォリオのブログAPIをEC2にデプロイした。OSはAlmaLinux 10、WebサーバーはApache、言語はPHPだ。
会社でもまったく同じ構成(AlmaLinux + Apache + PHP on EC2)を使っている。なのに、「ローカルのDockerで書いたコードがどういう経路を経て本番サーバーで動くのか」がずっとブラックボックスだった。Dockerが何を自動でやってくれているのか、本番のLinuxサーバーの上では実際に何が起きているのか、全然わかっていなかった。
今回の目的は、その間を埋めることだ。Dockerをあえて使わず、EC2に直接Apacheを入れてPHPを動かすことで、「Dockerが隠してくれていたすべて」を一つ一つ自分の手でやる。
この記事では、AlmaLinuxとは何かという背景知識から、EC2の立ち上げ・SSH接続・Apache+PHPの構築・コードのデプロイまでを全部書く。詰まったところも含めて。
---
## Linuxとは何か:カーネルとディストリビューションの違い
まず「Linux」という言葉の定義から整理する。これが曖昧なままだと後の話が全部ぼやける。
### Linuxカーネルとは
「Linux」を厳密に言うと、それは「Linuxカーネル」のことだ。カーネルとはOSの中核コンポーネントで、コンピュータのハードウェア(CPU・メモリ・ストレージ・ネットワークカード)とその上で動くソフトウェアの間に介在し、ハードウェアリソースを管理・抽象化する。
カーネルが担う主な仕事は4つある。
メモリ管理:各プロセス(動いているプログラム)に「仮想メモリ」という抽象化されたメモリ空間を割り当てる。プロセスAからはプロセスBのメモリが見えないよう隔離する(プロセス分離)。これにより、あるプログラムがバグで暴走しても他のプログラムのメモリを破壊できない。
プロセス管理:複数のプロセスが1つのCPUコアを共有できるよう、タイムスライス(ミリ秒単位の実行時間)を各プロセスに割り当てて高速に切り替える。人間には「複数のプログラムが同時に動いている」ように見える。
デバイス管理:アプリケーションがNVMe SSDだろうがSATA HDDだろうが同じAPIでファイルを読み書きできるよう、ハードウェアの差異を吸収する。この抽象化のおかげで、アプリはストレージの物理的な種類を意識しなくていい。
システムコール:ユーザースペースのアプリがハードウェアリソースに触れる必要がある操作(ファイルのオープン・ネットワーク通信・プロセスの生成)を、カーネルに依頼するための仕組み。アプリが直接ハードウェアを操作することはできない設計になっている。
#### なぜアプリは直接ハードウェアを操作できないのか
現代のプロセッサには「保護リング」という権限分離の仕組みがある。
- Ring 0(カーネルスペース):カーネルが動作する。ハードウェアへの無制限のアクセス権を持つ
- Ring 3(ユーザースペース):一般のアプリが動作する。ハードウェアに直接触れることはできない
アプリがファイルを読みたいとき、直接ディスクを読むのではなく「open()」などのシステムコールをカーネルに投げる。カーネルが安全性を検証した上でハードウェアにアクセスし、結果を返す。このメカニズムにより、悪意のあるプログラムがハードウェアを直接操作してシステム全体を壊すことを防いでいる。
### Linuxディストリビューションとは
カーネル単体だけでは実用的なOSとして機能しないvmlinuzというカーネルのバイナリファイルを起動しても、コマンドを入力する画面すら表示されない。
カーネルの周りに必要なソフトウェアを詰め込み、インストール可能な形にパッケージ化したものが「Linuxディストリビューション(通称:ディストロ)」だ。
ディストリビューションに含まれる主なコンポーネント:
- 初期化システム(systemd):OS起動直後にカーネルから制御を受け取り、各種サービス(Webサーバー・SSHサーバーなど)のライフサイクルを管理する
- シェル(bash/zsh):コマンドを入力するインターフェース。ターミナルで打ったコマンドを解釈して実行する
- GNUユーティリティlscpgrepなどの基本コマンド群
- パッケージ管理システム:ソフトウェアのインストール・更新・依存関係の解決・削除を統合的に管理する仕組み。**ここがディストリビューションのアイデンティティを決定づける最重要コンポーネント**
「Linux」という共通のカーネルを使っていても、ディストリビューションが違えばパッケージ管理の手法もセキュリティポリシーも設定ファイルの場所も全部異なる。
---
## 主要なLinuxディストリビューションの系譜
数百種類のディストリビューションが存在するが、エンタープライズサーバーの世界では大きく2つの系譜が市場を二分している。
### Debian系(Ubuntu・Debian)
1993年に創設されたDebianを祖とする系譜。パッケージフォーマット.debを使いAPT(Advanced Package Tool)でインストールと依存関係解決を行う。
Debianプロジェクト自体は特定企業に依存しない完全コミュニティ主導で、「比類なき安定性」が強み。新しいソフトウェアの採用に極めて保守的で、数年単位のテストを経たものだけを「安定版」としてリリースする。
Ubuntuはこの系譜の代表で、Canonical社が商用サポートを提供しつつ開発を主導。クラウドネイティブな開発環境・コンテナ・AI/MLワークロードで事実上の標準になっている。
### Red Hat系(RHEL・AlmaLinux・Rocky Linux)
Red Hat社(2019年にIBMが買収)が主導する、エンタープライズ市場に特化した系譜。パッケージフォーマット.rpmを使いDNF(Dandified YUM)でインストールを行う。
この系譜の大きな特徴が**SELinux(Security-Enhanced Linux)**の標準搭載だ。NSAと共同開発した強制アクセス制御システムで、金融機関・政府機関が要求する厳格なセキュリティ基準を満たす。会社がAlmaLinuxを採用しているのも、このエンタープライズグレードの信頼性と実績が評価されているからだ。
| 比較項目 | Debian系 | Red Hat系 |
|----------|----------|-----------|
| パッケージ形式 | .deb | .rpm |
| パッケージ管理 | apt / dpkg | dnf / rpm |
| デフォルトのMACモジュール | AppArmor | SELinux |
| 主な用途 | クラウドネイティブ・コンテナ・開発環境 | 基幹業務・政府機関・金融・オンプレミス |
---
## AlmaLinuxとは何か:CentOS終焉から生まれた経緯
AlmaLinuxを使う理由を本当に理解するには、**2020年のCentOS終焉騒動**を知る必要がある。
### Red HatとCentOSの歴史的関係
「RHEL(Red Hat Enterprise Linux)」はRed Hatの有償エンタープライズOS。商用サポート・ISV(独立系ソフトウェアベンダー)の動作認定・10年間の長期サポートが付いている。
RHELはオープンソースライセンスに基づき、ソースコードを公開する義務があった。このソースコードからRed Hatの商標だけを取り除き、RHELとバイナリレベルで完全互換になるよう再コンパイルした**無料OS**が「CentOS」だ。
「無料で使えるエンタープライズ品質のOS」として爆発的に普及した。企業はローカル開発環境にCentOS、本番環境にRHELという構成で、同じ動作を保証できた。
### 2020年12月:CentOS終焉の発表
2020年12月8日、Red HatはCentOSの方針転換を突然発表した。
> 「従来のCentOS Linuxの開発を完全に終了し、新たな『CentOS Stream』へ移行する」
さらに衝撃的だったのが、**2029年まで10年間サポートが約束されていたCentOS 8のサポート終了を、わずか1年後の2021年12月末に前倒し**したことだ。
この変化の本質は、ソフトウェア開発パイプラインの**構造的逆転**にある:
```
【従来のモデル】
Fedora(実験場) → RHEL(安定版) → CentOS Linux(RHELの完全コピー・下流)
【新モデル】
Fedora(実験場) → CentOS Stream(RHELの直前プレビュー・上流) → RHEL(安定版)
```
CentOS Streamは「次期RHELのテストベッド」という位置づけになった。本番環境で10年間変わらない安定したOSを必要としていた企業にとって、これは「常にアップデートが入り続ける不安定なOS」への強制移行を意味した。コミュニティからは「信頼への裏切り」という激しい非難が巻き起こった。
### AlmaLinuxとRocky Linuxの誕生
巨大な市場の空白を埋めるため、2021年初頭に2つのプロジェクトが立ち上がった。
Rocky Linux:かつてのCentOS共同創設者であるGregory Kurtzer氏が、亡き共同創設者Rocky McGaugh氏の名前を冠して発表。CIQ社が主要スポンサー。ただし最終的な意思決定権は実質的に創設者個人に帰属する構造で、「将来また誰かに買収・方針変更されるリスク」がある。
AlmaLinux:ホスティング事業者向けOSを提供してきたCloudLinux社が立ち上げ。年間100万ドルのスポンサーシップで開発を加速しつつ、米国法に基づく**501(c)(6)非営利団体「AlmaLinux OS Foundation」**を設立し、OSの商標・知的財産・ガバナンスをすべて財団に移譲した。
この財団モデルがAlmaLinuxの決定的な強みだ。特定の企業や個人に意思決定が左右されず、選挙で選ばれたコミュニティのボードが運営する。「CentOSがRed Hatに買収されて方針を変えられたあのリスク」が、法的な構造として排除されている。
### 2023年:RHELソースコード非公開化とABI互換へのシフト
2023年6月、Red HatはさらにRHELクローンへの制限を強化した。RHELのソースコードを有償顧客専用のポータルに移動し、一般公開を停止。しかも利用規約には「取得したソースコードを使ってクローンOSを作って配布することを禁止する」という条項が含まれていた。
これによりソースコードをそのままコピーしてクローンを作る手法が事実上不可能になった。
Rocky LinuxはRHELとの「Bug-for-Bug互換(バグも含めた完全同一の挙動)」に固執し、迂回手段でソースコードを入手し続けた。
一方AlmaLinuxは、「**ABI(Application Binary Interface)互換**」への戦略転換を選んだ。
#### ABIとは何か
APIがソースコードレベルの「文法の約束」だとすれば、ABIはコンパイル後のバイナリコードレベルの「呼び出し規約の約束」だ。関数の呼び出し規約・構造体のメモリレイアウト・共有ライブラリのシンボルバージョンなどがこれに当たる。
「ABI互換性が保たれている」という意味は、「RHELのバイナリをAlmaLinux上で再コンパイルなしに完全動作させられる」ということだ。ソースコードが1ビットも同じである必要はない。
この転換が生んだ決定的な優位性がある。従来のBug-for-Bug制約下では、RHELに既知のバグがあってもAlmaLinuxは独自に修正できず、Red Hatがパッチをリリースするまで待たなければならなかった。ABI互換に移行したことで、**AlmaLinuxがRHELより先にバグを修正・パッチ配信できるようになった**。
---
## なぜポートフォリオでAlmaLinuxを選んだのか
理由は一つだ。**会社と同じ構成を自分の手で再現することで、「ローカルDockerと本番EC2のギャップ」というブラックボックスを解体したかった。**
会社では「ローカルはDocker、本番はAlmaLinux EC2に直接Apache+PHPをインストール」という構成になっている。ローカルdocker compose upすれば一瞬で動くのに、本番での「Apache+PHPをOSに直接入れる」という作業の全体像が見えていなかった。
自分でEC2を立てて一から構築することで、「Dockerが裏側で自動的にやってくれていたこと」の全体像が見えるはずだと考えた。
---
## 実際のデプロイ手順
### 前提:ローカルの構成
ポートフォリオのAPIは以下の構成だった。
```
apps/api/
├── Dockerfile
├── docker-compose.yml
└── src/
├── index.php
├── login.php
├── auth.php
├── get_articles.php
├── save_article.php
├── delete_article.php
├── upload_image.php
├── composer.json
└── composer.lock
```
Dockerを使えばこのまま動くが、EC2への直接デプロイではDockerなしで同じ環境を手動で再現する必要がある。
#### なぜVanilla PHPでURLルーティングが動くのか
このAPIはLaravelなどのフレームワークを使っておらず、PHPファイルをベタ置きした「Vanilla PHP」構成だ。RouterもControllerも存在しない。
フレームワークを使う場合、すべてのリクエストindex.phpという「ルーター」で受け取り、そこから各コントローラーへ振り分ける。しかしApacheには、「URLのパス」と「Linuxのファイルシステム上のパス」を直接紐付ける機能がある。
```
https://api.shogomorisawa.me/login.php にアクセス
↓
Apacheが「login.phpというファイルを探して実行」
↓
DocumentRootの中にあるlogin.phpを実行
↓
JSONを返す
```
これがWebの原点だ。「なぜ世の中にルーターやコントローラー(MVCアーキテクチャ)が生まれたのか」という理由は、このシンプルな構成を経験するとよくわかる。PHPファイルが50個・100個になると管理が地獄になるからだ。今回はAPIが7ファイルしかないので、このシンプルな構成で十分に機能する。
---
### 1. EC2インスタンスの作成
AWSコンソールからEC2インスタンスを起動する。
#### AMI(Amazon Machine Image)とは
AMIとはEC2インスタンスの「OS込みの雛形イメージ」だ。どのOSをインストールした状態から始めるかを選ぶ。AWS Marketplaceで「AlmaLinux 10」を検索し、今回AlmaLinux 10 Server by ProComputers(AlmaLinux-10.1-Minimal-20260504-8GiB)を選択した。「Minimal」構成なので余計なソフトウェアが入っておらず、Apacheなどを一から自分で入れる今回の学習目的にちょうどいい。
#### インスタンスタイプの選択
インスタンスタイプはEC2の「スペック(CPU・メモリの組み合わせ)」を選ぶ設定だt2.microまたt3.microはAWS無料利用枠の対象なので、開発・学習用途ならこれを選ぶ。
#### キーペアの作成(超重要)
EC2への接続にはSSH公開鍵認証を使う。その際に必要な「鍵ペア」を作成する。
- 名前portfolio-api
- タイプ:RSA
- 形式.pem
ダウンロードし.pemファイルは**絶対に紛失・公開しない**。これがないとEC2に入れなくなる~/.ssh/に保存しておく。
##### SSH公開鍵認証とは何か
通常のパスワード認証は「知識(パスワード)」で本人確認する。公開鍵認証は「数学的に対応する2つの鍵の組み合わせ(公開鍵と秘密鍵)」を使う。
- 秘密鍵(Private Key).pemファイルのこと。自分のMacだけに保管し、絶対に誰にも渡さない
- 公開鍵(Public Key):EC2サーバー上~/.ssh/authorized_keysに登録される。誰に見られても安全
接続時の仕組みは以下だ:
1. クライアント(Mac)がサーバーに接続要求を送る
2. サーバーが「チャレンジ(乱数)」を生成し、登録された公開鍵で暗号化してクライアントに送る
3. クライアントが手元の秘密鍵で復号し、サーバーに返す
4. サーバーが「正しく復号できた=正しい秘密鍵を持っている=本人だ」と認証する
秘密鍵はネットワーク上を流れない。これがパスワード認証より安全な理由だ。
#### セキュリティグループの設定(ファイアウォールの穴開け)
セキュリティグループはEC2の仮想ファイアウォールだ。インターネットからのどの通信を許可するかをポート番号単位で制御する。
今回設定したルール:
| ルール | プロトコル | ポート | 送信元 |
|--------|-----------|--------|--------|
| SSH | TCP | 22 | 自分のIPのみ113.39.xx.xx/32) |
| HTTP | TCP | 80 | 任意の場所0.0.0.0/0) |
| HTTPS | TCP | 443 | 任意の場所0.0.0.0/0) |
SSHの送信元を「自分のIPのみ」に絞ることが最重要0.0.0.0/0(全IP許可)にすると、世界中のボットからSSHへのパスワード総当たり攻撃(ブルートフォース攻撃)を受け続ける/32はそのIPアドレス1つだけを意味するCIDR表記だ。
HTTP(80番)とHTTPS(443番)をすべてのIPに開放しているのは、Vercel上のブログフロントエンドからAPIを叩くために必要だからだ。
---
### 2. SSH接続
インスタンスの状態が「実行中(Running)」になったら接続する。
```bash
# 鍵の権限を「自分だけが読める」に変更
chmod 400 ~/.ssh/portfolio-api.pem
# SSH接続(パブリックIPアドレスはAWSコンソールで確認)
ssh -i ~/.ssh/portfolio-api.pem ec2-user@54.248.14.179
```
#### chmod 400とは何か
chmodはファイルのパーミッション(権限)を変更するコマンドだ。Linuxのパーミッションは「所有者・グループ・その他」の3者に対してそれぞれ「読み取り(r=4)・書き込み(w=2)・実行(x=1)」の3種類の権限を設定する。
400は8進数で以下を意味する:
```
4 (所有者):r-- (読み取りのみ)
0 (グループ):--- (何もなし)
0 (その他):--- (何もなし)
```
SSHクライアントは秘密鍵ファイルを読み込む際、パーミッションを確認する。もし「他のユーザーも読める状態」になっていると、「この鍵は安全に管理されていない」と判断して接続を拒否する仕様になっているchmod 400で「所有者のみ読み取り可能」に絞る。
#### 初回接続時の警告
```
The authenticity of host '54.248.14.179 (54.248.14.179)' can't be established.
ED25519 key fingerprint is SHA256:bSMMz4ONjrnptrgkfrQw0hW9U8/+guSpU3OZc1ABJnA.
Are you sure you want to continue connecting (yes/no/[fingerprint])?
```
「このサーバー、初めて接続するけど信頼していいか?」というセキュリティチェックだyesと入力すると、Macがこのサーバーの指紋(フィンガープリント)~/.ssh/known_hostsに記録する。次回から同じサーバーに接続するとき、記録されたフィンガープリントと一致するかを確認し、一致しなければ(サーバーが入れ替えられた可能性を示す)警告を出す仕組みだ。
yesを入力すると接続成功:
```
Welcome to AlmaLinux 10.1 x86_64 MINIMAL - 20260504
[ec2-user@ip-172-31-38-248 ~]$
```
---
### 3. root権限を取得する
Apache・PHPのインストールなどシステム全体に関わる操作はroot権限が必要だ。
```bash
sudo su -
```
sudoは「superuser do」の略で、一時的に管理者権限でコマンドを実行する仕組みsu -は「rootユーザーに切り替える」コマンドで-をつけることでrootのログイン環境(環境変数など)も引き継ぐ。
プロンプトの末尾$か#に変わればroot権限の取得成功。
```
[root@ip-172-31-38-248 ~]#
```
---
### 4. OSのアップデートとApacheのインストール
```bash
# パッケージの最新化(システムを最新・安全な状態にする)
dnf update -y
```
dnf(Dandified YUM)はRed Hat系のパッケージマネージャだ。Debian系aptに相当するupdate -yはすべてのインストール済みパッケージを最新バージョンに更新し-yは確認プロンプトに自動でYes)、セキュリティパッチも適用する。
```bash
# Apacheのインストール
dnf install httpd -y
```
AlmaLinuxでapache2ではなhttpdというパッケージ名を使う(Red Hat系の命名規則)。
```bash
# Apacheの起動
systemctl start httpd
# サーバー再起動後も自動起動するよう設定
systemctl enable httpd
```
#### systemctlsystemdとは何か
systemdはLinuxの「初期化システム(init system)」だ。OS起動時にカーネルから最初に制御を受け取り、各種サービス(デーモン)を起動・管理する。
systemctlsystemdを操作するコマンドだ。
- systemctl start httpd:httpdサービスを**今すぐ**起動する
- systemctl enable httpd:次回OS起動時に**自動起動**するよう登録するstartとは別の操作)
- systemctl stop httpd:サービスを停止する
- systemctl restart httpd:サービスを再起動する
- systemctl status httpd:サービスの動作状態を確認する
startenableは独立した操作なので、両方実行する必要があるenableだけだと今すぐは起動しないstartだけだと次回再起動で自動起動しない。
#### デーモンとは何か
「デーモン(daemon)」とはバックグラウンドで常時稼働するプロセスのことだ。Apacheはリクエストが来るたびに起動するのではなく、常時バックグラウンドで動き続けてリクエストを待ち受ける。Unixの世界では慣習的にデーモンのプログラム名末尾に「d」をつけるhttpdの「d」はdaemonのdだsshdも同様)。
この時点でブラウザかhttp://54.248.14.179にアクセスすると、Apacheのデフォルトページが表示される。サーバーが動いていることの確認だ。
---
### 5. PHPのインストール
```bash
# PHP本体 + 各種モジュール + ツール群
dnf install php php-cli php-pgsql php-mbstring php-xml unzip git -y
```
各パッケージの役割:
| パッケージ | 役割 |
|-----------|------|
| php | PHP本体(Apache連携用のモジュールも含む) |
| php-cli | コマンドラインからPHPを実行するためのCLI版 |
| php-pgsql | PostgreSQL(Neon)との接続に必要な拡張モジュール |
| php-mbstring | マルチバイト文字列(日本語など)を扱うための拡張 |
| php-xml | XML処理に必要な拡張 |
| unzip | zipファイルを解凍するツール |
| git | GitHubからコードをクローンするためのツール |
```bash
# PHPのインストールをApacheに認識させるため再起動
systemctl restart httpd
# バージョン確認
php -v
```
```
PHP 8.3.29 (cli) (built: Dec 16 2025 14:32:42) (NTS gcc x86_64)
Copyright (c) The PHP Group
Zend Engine v4.3.29, Copyright (c) Zend Technologies
with Zend OPcache v8.3.29, Copyright (c), by Zend Technologies
```
PHP 8.3.29が入った。
---
### 6. Composerのインストール
ComposerはPHPのパッケージ管理ツールだnpmのPHP版と思えばいいfirebase/php-jwt(JWT認証ライブラリ)aws/aws-sdk-php(S3操作)などの外部ライブラリを管理する。
```bash
# インストーラーをダウンロード
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
# インストール実行
php composer-setup.php
```
```
All settings correct for using Composer
Downloading...
Composer (version 2.9.7) successfully installed to: /root/composer.phar
```
```bash
# インストーラーを削除
php -r "unlink('composer-setup.php');"
# どこからでcomposerコマンドを使えるよう/usr/local/bin/に移動
mv composer.phar /usr/local/bin/composer
```
/usr/local/bin/はシステム全体で使えるコマンドを置く場所だ。ここに置くことcomposerとだけ打てば実行できる。
---
### 7. GitHubからコードをクローン
Apacheが公開するディレクトリ/var/www/html)にリポジトリをクローンする。
```bash
cd /var/www
# 既存のhtmlディレクトリを削除(Apacheのデフォルトファイルが入っている)
rm -rf html
# GitHubからクローン(html という名前のディレクトリとして)
git clone https://github.com/ShogoMorisawa/portfolio.git html
```
```
Cloning into 'html'...
remote: Enumerating objects: 1649, done.
remote: Total 1649 (delta 159), reused 263 (delta 147), pack-reused 1363
Receiving objects: 100% (1649/1649), 93.07 MiB | 24.74 MiB/s, done.
```
#### /var/www/htmlとは
Apacheのデフォルトのドキュメントルート(公開ディレクトリの起点)だ。ブラウザからサーバーへのリクエストが来たとき、Apacheはこのディレクトリからファイルを探して返す。
```bash
# APIのディレクトリに移動
cd /var/www/html/apps/api
composer install
```
ここで詰まった:
```
Composer could not find a composer.json file in /var/www/html/apps/api
To initialize a project, please create a composer.json file.
```
#### トラブルcomposer.jsonapi/ではなsrc/の中にあった
ローカルdocker-compose.ymlを見ると:
```yaml
volumes:
- ./src:/var/www/html
```
src/ディレクトリがそのままコンテナ/var/www/html(Apacheのドキュメントルート)に直接マウントされていた。だかcomposer.jsonsrc/の中に置いていた。一般的なPHPプロジェクトでcomposer.jsonはプロジェクトのルートに置くが、この構成ではDockerのマウント設計に合わせsrc/の中に入れていたのだ。
Claude Codeで確認したら「意図的な構成で、誤りではない」との回答。辻褄が合った。
src/に移動してからインストール:
```bash
cd /var/www/html/apps/api/src
composer install
```
```
Package operations: 15 installs, 0 updates, 0 removals
- Installing firebase/php-jwt (v7.0.5): Extracting archive
- Installing aws/aws-sdk-php (3.379.7): Extracting archive
...
Generating autoload files
```
成功vendor/ディレクトリが作成された。
---
### 8. Apacheの設定(VirtualHost)
Apacheにどのディレクトリをドキュメントルートにするかを教える設定ファイルを作る。
```bash
vi /etc/httpd/conf.d/api.conf
```
iキーで入力モードに入り、以下を貼り付けるEsc → :wq → Enterで保存):
```apache
<VirtualHost *:80>
# PHPファイルが置いてあるsrc/を公開ディレクトリとして指定
DocumentRoot /var/www/html/apps/api/src
# 環境変数(DB接続情報・JWTシークレット・AWS認証情報)
SetEnv DB_HOST "ep-blue-silence-xxx.neon.tech"
SetEnv DB_NAME "neondb"
SetEnv DB_USER "username"
SetEnv DB_PASS "password"
SetEnv JWT_SECRET "xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
SetEnv AWS_BUCKET_NAME "bucket-name"
SetEnv AWS_REGION "ap-northeast-1"
SetEnv AWS_ACCESS_KEY_ID "AKIAXXXXXXXXXXXXXXXX"
SetEnv AWS_SECRET_ACCESS_KEY "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
<Directory /var/www/html/apps/api/src>
AllowOverride All
Require all granted
</Directory>
</VirtualHost>
```
#### 各ディレクティブの意味
*<VirtualHost :80>*:80番ポートへのすべてのリクエストに対してこの設定を適用する。VirtualHostはApacheが1台のサーバーで複数のドメイン・設定を扱うための仕組みだ*はIPアドレスを問わないことを意味する。
*DocumentRoot**:Apacheがファイルを探す起点ディレクトリ。ブラウザhttp://54.248.14.179/login.phpにアクセスすると/var/www/html/apps/api/src/login.phpを実行する。Dockervolumes: ./src:/var/www/htmlと同じことを、設定ファイルで実現している。
*SetEnv**:Apache経由で動くPHPプロセスに環境変数を渡す。PHPコードの中getenv('DB_HOST')として読み取れる。DB接続情報やJWTシークレットなどの秘密情報をコードに直書きせず、環境変数として管理する。Dockerでdocker-compose.ymlenvironmentセクションに書いていたものだ。
*AllowOverride All**:ディレクトリ内.htaccessファイルによる設定の上書きを許可する。PHPのURLルーティング.htaccessを使う場合に必要。
*Require all granted**:このディレクトリへのアクセスをすべて許可するRequire all deniedにすれば全拒否になる。
---
### 9. 権限設定とApache再起動
```bash
# ファイルの所有者をApacheユーザーに変更
chown -R apache:apache /var/www/html
# 設定を反映
systemctl restart httpd
```
#### chown -R apache:apacheとは何か
chownは「change owner」の略で、ファイル・ディレクトリの所有者を変更するコマンドだ。
chown -R apache:apache /var/www/htmlの意味:
- -R:Recursive(再帰的)/var/www/html以下のすべてのファイル・ディレクトリに対して適用する
- apache:apache:所有ユーザーapache、所有グループapacheに変更する
なぜこれが必要か。Apacheのサービスapacheというシステムユーザーの権限で動作する(セキュリティ上、rootで動かさない)。ファイルの所有者rootのままだとapacheユーザーがファイルを読めず権限エラーになる。
---
### 10. 動作確認
ブラウザhttp://54.248.14.179/index.phpにアクセス:
```json
{
"status": "success",
"message": "blog API is running!",
"database": "PostgreSQL connected seccessful!"
}
```
デプロイ成功。
---
## まとめ
今回の作業を整理すると、Dockerが「ファイルを1行書くだけで自動でやってくれていたこと」を一つ一つ手動で再現したことになる。
| Dockerでの操作 | EC2での対応操作 |
|---------------|----------------|
| FROM php:8.3-apache | dnf install httpd php ... |
| volumes: ./src:/var/www/html | DocumentRoot /var/www/html/apps/api/src |
| environment: DB_HOST=... | SetEnv DB_HOST "..." |
| RUN composer install | cd /var/www/html/apps/api/src && composer install |
| コンテナ起動 | systemctl start httpd && systemctl enable httpd |
この対応関係が見えたことで、「Dockerが何を抽象化していたのか」が初めてはっきりわかった。
次回の記事では、この構成で発生した**SELinuxがNeon(PostgreSQL)への通信を弾く問題**と、その背景にあるLinuxのセキュリティモデルの深層を解説する。