はじめに
本記事では、FastAPI (Backend) と Next.js (Frontend) で構成されたWebアプリケーションを、Docker を使って Azure App Service へデプロイする手順を解説します。
今回使用するアプリケーションのソースコードはこちらです。
Githubリポジトリ:https://github.com/aripoyo14/practical_docker_deploy
デプロイするアプリの構成
通常、このアプリケーションをローカル(Dockerなし)で動かす場合は、Pythonの仮想環境を作って uvicorn を起動し、別途 Node.js で npm run dev を実行する必要があります。
本記事では、この Frontend と Backend が分離した構成 を Docker コンテナ化し、Azure 上で連携させて動かす「実践的なデプロイフロー」を構築します。
Dockerってそもそも何?という方はこちらの投稿から先にご覧ください。
Docker初心者によるDocker初心者のためのDocker解説
完成物のディレクトリ構造
本プロジェクトは モノレポ(Monorepo)構成 を採用しています。
1つのリポジトリの中に frontend と backend の両方のコードが含まれており、それぞれ個別に Docker コンテナとして動作します。
├── .github/workflows/ # CI/CD設定 (GitHub Actions)
│ ├── deploy-backend.yml
│ └── deploy-frontend.yml
├── backend/ # FastAPI (Python) アプリケーション
│ ├── db_control/ # DB操作・モデル定義などのモジュール
│ ├── app.py # アプリケーションのエントリーポイント
│ ├── Dockerfile # バックエンド用 Docker設定
│ ├── requirements.txt # Python依存パッケージリスト
│ └── .dockerignore # Dockerイメージに含めないファイルの設定
├── frontend/ # Next.js (App Router) アプリケーション
│ ├── src/app/ # 画面・ページコンポーネント
│ ├── Dockerfile # フロントエンド用 Docker設定
│ ├── package.json # Node.js依存パッケージリスト
│ └── .dockerignore # Dockerイメージに含めないファイルの設定
├── docker-compose.yml # ローカル開発用構成 (Frontend + Backend + MySQL)
└── README.md
1. Dockerfile を作成する
まずは、アプリケーションをコンテナ化するための設計図である Dockerfile を作成します。
Backend の Dockerfile
backend/Dockerfile を作成します。
FROM python:3.12-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]
解説
-
FROM python:3.12-slim- ベースイメージの指定です。Python 3.12 がインストールされたLinux環境を土台に使います。
-
slimは必要最小限のツールだけを含んだ軽量版イメージです。イメージサイズを小さく抑えることで、デプロイ時間の短縮やセキュリティリスクの低減につながります。
-
WORKDIR /app- 作業ディレクトリの設定です。
- コンテナ内部でのカレントディレクトリを
/appに切り替えます。これ以降のコマンド(COPYやRUNなど)は、全てこの/appディレクトリの中で実行されます。
-
COPY requirements.txt .- 依存定義ファイルのコピーです。
- まず最初に
requirements.txtだけをコンテナ内にコピーします。ソースコード全部をいきなりコピーしないのがポイントです。
-
RUN pip install --no-cache-dir -r requirements.txt- パッケージのインストールです。
-
pip installでライブラリをインストールします。 - なぜソースコードのコピーより先にやるかというと、Dockerのキャッシュ機能を効かせるためです。ソースコードを変更しても、
requirements.txtが変わっていなければ、この重いインストール処理はスキップ(キャッシュ利用)され、ビルドが爆速になります。 -
--no-cache-dirは pip 自体のキャッシュを残さないオプションで、イメージ容量を節約しています。
-
COPY . .- アプリケーションコードのコピーです。
- ここで初めて、手元の
backendディレクトリの中身をすべてコンテナの/appにコピーします。 -
app.pyやdb_controlフォルダなどがコンテナ内に配置されます。
-
EXPOSE 8000- ポート公開の宣言です。
- このコンテナは「8000番ポートで通信を受け付けますよ」ということを明示しています(ドキュメント的な意味合いが強いです)。
-
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]- 起動コマンドです。コンテナが立ち上がった瞬間に実行されるコマンドです。
-
uvicorn: FastAPI を動かすための高速なサーバーです。 -
app:app: 「app.pyファイルの中にあるappオブジェクト」を探して起動しなさい、という指示です。 -
--host 0.0.0.0: コンテナの外側からアクセスできるようにするための必須設定です。
Frontend の Dockerfile
frontend/Dockerfile を作成します。
FROM node:22.13.0-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["npm", "run", "dev"]
解説
-
FROM node:22.13.0-alpine- ベースイメージの指定です。Node.js 22.13.0 を使用します。
-
alpineは超軽量のLinuxディストリビューションです。一般的なLinuxイメージよりも圧倒的にサイズが小さいため、フロントエンドのコンテナ化では非常によく使われます。
-
WORKDIR /app- コンテナ内の作業場所を
/appに設定します。
- コンテナ内の作業場所を
-
COPY package*.json ./-
package.jsonとpackage-lock.jsonをコピーします。 - Pythonの時と同じく、依存関係のインストールを効率化するための手順です。
-
-
RUN npm install- 必要なライブラリ(
node_modules)をインストールします。
- 必要なライブラリ(
-
COPY . .- Next.js のソースコード一式をコンテナ内にコピーします。
-
RUN npm run build- ビルドの実行です。
- Next.js のアプリケーションを本番用に最適化(ビルド)します。これで
.nextフォルダなどが生成されます。
-
EXPOSE 3000- Next.js はデフォルトで 3000 番ポートを使うため、それを宣言しています。
-
CMD ["npm", "run", "dev"]- 起動コマンドです。
- 開発サーバー (
dev) を立ち上げています。 - ※補足:本番運用(Production)を厳密に意識する場合は
npm startを使うのが一般的ですが、今回は開発環境の設定をそのまま流用しやすくするためdevとしています。
2. docker-compose.yml を作成する
複数のコンテナ(Frontend, Backend, Database)をまとめて管理・起動するための docker-compose.yml を作成します。
version: '3.8'
services:
frontend:
build: ./frontend
ports:
- "3000:3000"
environment:
- NEXT_PUBLIC_API_ENDPOINT=http://localhost:8000
- API_ENDPOINT=http://backend:8000
depends_on:
- backend
backend:
build: ./backend
ports:
- "8000:8000"
environment:
- DB_USER=root
- DB_PASSWORD=${DB_PASSWORD}
- DB_HOST=mysql
- DB_PORT=3306
- DB_NAME=crm
depends_on:
mysql:
condition: service_healthy
mysql:
image: mysql:8.0
ports:
- "3307:3306"
environment:
- MYSQL_ROOT_PASSWORD=${MYSQL_ROOT_PASSWORD}
- MYSQL_DATABASE=crm
volumes:
- mydata:/var/lib/mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
interval: 10s
timeout: 5s
retries: 5
volumes:
mydata:
解説
version: '3.8'
Docker Compose ファイルの記述バージョンです。新しい機能を使うために比較的新しいバージョンを指定しています。
Service: frontend
-
build: ./frontend: 先ほど作ったfrontend/Dockerfileを使ってイメージを作ります。 -
ports: "3000:3000": パソコンのブラウザでhttp://localhost:3000にアクセスすると、コンテナの 3000番に繋がるようにします。 -
environment:-
NEXT_PUBLIC_API_ENDPOINT: ブラウザ側からAPIを叩くためのURL。 -
API_ENDPOINT: Next.jsサーバー側からAPIを叩くためのURL(コンテナ間通信)。
-
-
depends_on: - backend: バックエンドが起動してからフロントエンドを起動するよう制御します。
Service: backend
-
build: ./backend:backend/Dockerfileを使ってイメージを作ります。 -
ports: "8000:8000":http://localhost:8000でAPIにアクセスできるようにします。 -
environment: DB接続情報を渡します。-
DB_HOST=mysql: ここが重要です。Docker Compose内ではサービス名(mysql)がそのままホスト名として使えます。 -
${DB_PASSWORD}: パスワードをファイルに直接書くのは危険なので、.envファイル等から読み込む変数にしています。
-
-
depends_on:-
condition: service_healthy: 単にmysqlコンテナが起動するだけでなく、「MySQLが準備完了(healthy)」になるまで待ってから起動します。これで「起動直後のDB接続エラー」を防げます。
-
Service: mysql
-
image: mysql:8.0: Docker Hubにある公式の MySQL 8.0 イメージを使います。 -
ports: "3307:3306": ローカルPCの 3307番ポートを、コンテナの 3306番に繋ぎます(3306は自分のPCで既に使っていることが多いため、あえてずらしています)。 -
volumes: - mydata:/var/lib/mysql:-
データの永続化です。コンテナを削除してもデータが消えないように、
mydataという専用の保存領域にDBのデータを書き込みます。
-
データの永続化です。コンテナを削除してもデータが消えないように、
-
healthcheck:- MySQLが本当にクエリを受け付けられる状態になったか、定期的に
pingコマンドを打って確認する設定です。これのおかげで、Backend側が安心して接続開始できます。
- MySQLが本当にクエリを受け付けられる状態になったか、定期的に
3. Azureリソースグループを作成する
ここからはクラウド(Azure)側の設定です。まずはすべてのリソースを入れる「箱」を作ります。
- Azureポータルの検索窓で「リソースグループ」と検索し選択します。
- 「作成」ボタンをクリックします。
- サブスクリプション: 自分のものなどを選択。
-
リソースグループ名: 任意の名前(例:
rg-practical-docker)を入力。 -
リージョン: 日本に近い
Japan East(東日本) などを選択。 - 「確認と作成」→「作成」をクリックして完了です。
4. Azure Container Registry (ACR) を作成する
Dockerイメージを保存しておくための「倉庫」を作ります。
リソースグループの作成
参考資料:Azureのリソースグループとは?役割や作成方法・分け方
ACRの作成
-
「作成」をクリック。
-
リソースグループ: 先ほど作成したものを選択。
-
レジストリ名: 世界で一意の名前(例:
acrdeploytrial2024)を入力。※全て小文字である必要があります。 -
SKU: 学習用なら「Basic」で十分です。
Adminユーザーの有効化(重要)
デプロイ時にユーザー名とパスワードでログインできるように設定を変更します。
- 作成された ACR のリソース画面に移動します。
- 左メニューの「設定」>「アクセス キー」を選択します。
- 「管理者ユーザー」 を「有効 (Enabled)」に切り替えます。
- 表示された ユーザー名 と パスワード をメモしておきます(後で使います)。
5. ローカルのDockerイメージをACRに登録する
作成した ACR に、手元のパソコンで作ったDockerイメージをアップロード(Push)します。
Azure CLI でログイン
まず、コマンドラインツールで Azure にログインします。
参考資料:Azure CLI をインストールする方法
# Azureアカウントへのログイン(ブラウザが開きます)
az login
# ACRへのログイン
# ※ <ACR名> は先ほど作ったレジストリ名です
az acr login --name <ACR名>
「Login Succeeded」と表示されればOKです。
イメージにタグを付けてPushする
ACR にアップロードするためには、「これはどこのACRの、なんというイメージか」という名札(タグ)を付ける必要があります。
# === 1. タグ付け (Tagging) ===
# 形式: docker tag <元のイメージ名> <ACRログインサーバー>/<イメージ名>:<バージョン>
# フロントエンド
docker tag practical_docker_deploy-frontend <ACR名>.azurecr.io/frontend:v1
# バックエンド
docker tag practical_docker_deploy-backend <ACR名>.azurecr.io/backend:v1
# === 2. アップロード (Push) ===
# 作成したタグを指定してACRへ送信します
docker push <ACR名>.azurecr.io/frontend:v1
docker push <ACR名>.azurecr.io/backend:v1
これで、Azure上の倉庫(ACR)にイメージが格納されました。
成功しているとコンテナーレジストリにリポジトリが追加されます。

6. Azure App Service を作成する
いよいよアプリケーションを動かすサーバーを作ります。Frontend用とBackend用で2つ作成します。
作成手順
-
「作成」>「Web アプリ」を選択。
-
基本タブ:
- リソースグループ: 作成したものを選択。
-
名前: アプリのURLになります(例:
app-frontend-trial)。 - 公開: 「Docker コンテナー」を選択(重要!)。
- オペレーティングシステム: 「Linux」を選択。
-
プラン: テスト用なら
B1やF1(Free) を探して選択。
-
コンテナータブ:
- オプション: 「単一コンテナー」
- イメージソース: 「Azure Container Registry」
- レジストリ: 作成したACRを選択。
-
イメージ:
frontend(またはbackend) を選択。 -
タグ:
v1を選択。
-
「確認と作成」→「作成」をクリック。
※これを「フロントエンド用」と「バックエンド用」で2回繰り返してください。
7. GitHub Actions で自動デプロイを構築する
ここまでの手順だと、コードを修正するたびに手動でビルド・プッシュするのは大変です。
GitHub にコードをプッシュしたら、勝手に Azure までデプロイしてくれる仕組み(CI/CD)を作ります。
ワークフローファイルの作成
リポジトリの .github/workflows/ フォルダの中にYAMLファイルを作成します。
deploy-frontend.yml (フロントエンド用)
name: Deploy Frontend to Azure App Service
on:
push:
branches:
- main
paths:
- 'frontend/**' # frontendフォルダに変更があった時だけ動く
jobs:
build-and-deploy:
runs-on: ubuntu-latest # GitHub側のサーバー(Ubuntu)で実行
steps:
# 1. リポジトリのコードを取得
- name: Checkout repository
uses: actions/checkout@v3
# 2. Azureにログイン
- name: Log in to Azure
uses: azure/login@v1
with:
creds: ${{ secrets.AZURE_CREDENTIALS }}
# 3. ACRにログイン
- name: Log in to ACR
run: az acr login --name <あなたのACR名>
# 4. ビルドしてACRにPush
- name: Build and Push Docker image
run: |
# コミットハッシュ(github.sha)をタグに使ってユニークにする
docker build -t <あなたのACR名>.azurecr.io/frontend:${{ github.sha }} ./frontend
docker push <あなたのACR名>.azurecr.io/frontend:${{ github.sha }}
# 5. App Serviceへデプロイ
- name: Deploy to App Service
uses: azure/webapps-deploy@v2
with:
app-name: ${{ secrets.APP_SERVICE_FRONTEND }}
images: <あなたのACR名>.azurecr.io/frontend:${{ github.sha }}
解説
-
paths: - 'frontend/**':- これが重要です。「frontendフォルダの中身が変わった時だけ」この処理を走らせます。backendの修正時は無視することで、無駄なデプロイを防ぎます。
-
docker build ... ${{ github.sha }}:- タグに
v1などの固定値ではなく、${{ github.sha }}(その時点のコミットID)を使っています。これにより、「どのコミットのコードで動いているか」が明確になり、バージョン管理が確実になります。
- タグに
-
secrets.AZURE_CREDENTIALS:- ログイン情報などの機密情報はコードに直書きせず、GitHubの「Secrets」機能を使って隠蔽します。
※ バックエンド用の deploy-backend.yml も、パスを backend/** に、アプリ名をバックエンド用に変えて同様に作成します。
最後の仕上げ:GitHub Secrets の設定
GitHubリポジトリの「Settings」→「Secrets and variables」→「Actions」に、以下の変数を登録します。
| Secret名 | 設定する値 |
|---|---|
AZURE_CREDENTIALS |
サービスプリンシパルのJSON情報(後述のコマンドで取得) |
APP_SERVICE_FRONTEND |
作成したフロントエンド用App Serviceの名前 |
APP_SERVICE_BACKEND |
作成したバックエンド用App Serviceの名前 |
AZURE_CREDENTIALS 取得用コマンド:
Azure CLIで以下を実行し、出力されたJSON全体をコピーして登録してください。
az ad sp create-for-rbac --name "myAppDeploy" --role contributor \
--scopes /subscriptions/<サブスクリプションID>/resourceGroups/<リソースグループ名> \
--sdk-auth
デプロイ完了!
お疲れ様でした!これで以下のフローが完成しました。
- ローカルでコードを修正して
git push - GitHub Actions が変更を検知
- Dockerイメージを自動ビルドして ACR に保存
- Azure App Service が新しいイメージに更新される
これで、開発に集中できるモダンな開発環境の出来上がりです。








