1. やりたいこと
git cloneしてdocker compose upしたらデプロイできるようにしたい
2. どうやって実現するか
以下をフォルダに置いてdocker compose upするだけです
・ソースコード
・Dockerfile
・docker-compose.yml
3. 試行環境
以下の記事で作った環境で実行しています
ホスト: Windows10
実行環境: WSL2 Ubuntu-22.04
4. 前提知識
4-1. Dockerfileの書き方
Dockerfileはdockerイメージ1つ分のビルド指示を書くファイルです
以下のコマンドでdockerイメージをビルドするときの指示を書けます
FROM: ベースとするdockerイメージとバージョンを指定します (ubuntu:latestなど)
WORKDIR: RUNなどのコマンドを実行するワーキングディレクトリを指定します
RUN: dockerイメージビルド時にdockerのterminalからコマンドを実行します
COPY: dockerイメージビルド時にホストからdockerにフォルダやファイルをコピーします
ADD: dockerイメージビルド時にホストからdockerにフォルダやファイルをコピーします
VOLUME: ホストのフォルダやファイルをdockerイメージにマウントします
EXPOSE: コンテナがアクセスを許可するポートを書きます
ENV: 環境変数を登録します
ENTRYPOINT: コンテナを起動する際に実行するコマンドを指定します
CMD: コンテナを起動する際に実行するデフォルト引数を指定します
FROMで指定するdockerイメージはdocker hubにある公式のイメージを利用することが多いと思います。COPYとADDは似てますがADDは圧縮ファイルを解凍してからコピーするのに対し、COPYはそのままコピーします。ENTRYPOINTとCMDも似てますが、CMDはdocker runするときに引数を指定することで変更されるのに対して、ENTRYPOINTは必ず実行されるコマンドであるという違いがあるようです
Dockerfileについての公式ドキュメントは以下にあります
4-2. docker-compose.ymlの書き方
docker-compose.ymlにはdockerコンテナを起動するときの条件を書いておきます。複数Dockerfileを指定して複数のコンテナを同時に起動することもできますのでフロントエンド、バックエンド、DBとまとめて起動する場合も1ファイルにまとめて書いて、docker compose upするだけでまとめて起動することができます
services:
コンテナ名:
image: "イメージ名"
ports:
- "ホスト側のポート:コンテナのポート"
volumes:
- ホスト側のパス:コンテナのパス
environment:
環境変数名: 環境変数値
コンテナ名:
build: Dockerfileのパス
ports:
- "ホスト側のポート:コンテナのポート"
volumes:
- ホスト側のパス:コンテナのパス
environment:
環境変数名: 環境変数値
depends_on:
- コンテナ名
portsの書き方についてはこの記事が詳しいです
4-3. 起動する方法
docker-compose.ymlの内容を実行するにはdocker compose upします
docker compose up
--buildオプションを付ければ既にイメージがある状態でもビルドからやり直してくれます
docker compose up --build
4-4. 起動中のdockerコンテナへの接続
以下のコマンドで起動しているコンテナのterminalにアクセスできます
docker exec -i -t CONTAINER_ID /bin/bash
コンテナIDは以下で確認できます
docker container ls
上手く動かないときにはこれでterminalに入って確認すると良いと思います
5. 静的サイトをデプロイする
5-1. Dockerfile
静的サイトなのでベースイメージにはhttpd:latestを使いたいと思います
htmlをhtml名のフォルダに入れる想定で、COPY欄にフォルダ内の内容をドキュメントルートにコピーするように設定しました
FROM httpd:latest
COPY ./html/ /usr/local/apache2/htdocs/
5-2. docker-compose.yml
docker-compose.ymlでは上記Dockerfileをbuildして、ポートはdockerコンテナ内で80番のポートを80番のポートとするように設定しました
version: "1"
services:
web:
build: .
ports:
- "80:80"
後は表示したいhtmlファイルを書いたら完成です
<html>
<head>
<title>ページ</title>
<link rel="icon" href="data:,">
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width">
</head>
<body>
ほげほげ
</body>
</html>
5-3. 起動
あとはこのフォルダでdocker compose upすれば起動できます
$ docker compose up
[+] Running 1/0
✔ Container test2-web-1 Recreated 0.0s
Attaching to web-1
web-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.2. Set the 'ServerName' directive globally to suppress this message
web-1 | AH00558: httpd: Could not reliably determine the server's fully qualified domain name, using 172.18.0.2. Set the 'ServerName' directive globally to suppress this message
web-1 | [Sun Jan 21 14:56:31.806008 2024] [mpm_event:notice] [pid 1:tid 140235577243520] AH00489: Apache/2.4.58 (Unix) configured -- resuming normal operations
web-1 | [Sun Jan 21 14:56:31.806069 2024] [core:notice] [pid 1:tid 140235577243520] AH00094: Command line: 'httpd -D FOREGROUND'
web-1 | 172.18.0.1 - - [21/Jan/2024:14:56:34 +0000] "GET / HTTP/1.1" 200 357
web-1 | 172.18.0.1 - - [21/Jan/2024:14:57:26 +0000] "-" 408 -
ブラウザでlocalhostを開くと表示されている筈です

6. ビルドしたSPAをデプロイする
VueやReactで作ったSPAアプリはビルドしてあれば静的サイトと同じなので、デプロイ時にやることも静的サイトと同じです
6-1. Vueプロジェクトを準備する
node.jsが入っている環境で以下を実行するとVueのプロジェクトが作成されます
npm install vue
npm init vue@latest
以下でビルドします
npm run build
6-2. ファイル構成と手順
Dockerfileとdocker-compose.ymlを追加してフォルダ構成は以下のようにしました

6-3. Dockerfile
FROM httpd:latest
COPY ./myapp/dist/ /usr/local/apache2/htdocs/
6-4. docker-compose.yml
services:
web:
build: .
ports:
- "80:80"
6-5. 起動する
あとはこのフォルダでdocker compose upすれば
http://localhost にVueアプリが待機します

7. Djangoで作った動的サイトをデプロイする
次はDjangoでつくった動的サイトをデプロイしてみましょう
7-1. Djangoプロジェクトをつくる
Python + Djangoが入っている環境で以下を実行すればプロジェクトの最小構成が作成されます
django-admin startproject mysite
requirement.txtを以下で作成します
以下で書き出す場合は必要なパッケージがすべてpipで入っている必要がありますので、condaなどを併用してる方はpipreqsを使うのが良いようです
python -m pip freeze > requirements.txt
7-2. ファイル構成と手順
Dockerfileとdocker-compose.ymlを追加してフォルダ構成は以下のようにしました

やらなければならない手順は以下になります
- apache2をインストールする
- mod-wsgiをインストールする
- pythonをインストールする
- djangoをインストールする
- apache2の設定ファイルにdjango, mod-wsgiの設定を追加する
- ソースコードをマウントする
7-3. apache2の設定ファイル
apache2の設定に追加する内容は以下のようにしました。Django公式のドキュメント通りにpythonのパスやDjangoプロジェクトのパスを指示してやります
ServerName localhost:80
WSGIScriptAlias / /var/web/mysite/html/mysite/wsgi.py
WSGIPythonPath /var/web/mysite/html
<Directory /var/web/mysite/html/mysite>
<Files wsgi.py>
Require all granted
</Files>
</Directory>
Alias /static/ /var/web/mysite/html/static/
<Directory /var/web/mysite/html/static>
Require all granted
</Directory>
7-4. Dockerfile
Djangoの公式イメージがdeprecatedになって、代替イメージとしてUbuntuを使うように書かれていますのでUbuntuでやってみたいと思います
FROM ubuntu
# アップデート
RUN apt-get update
# apache2, python, mod-wsgiをインストールする
RUN apt-get install -y apt-utils vim curl apache2 apache2-utils apache2-dev
RUN apt-get -y install python3 libapache2-mod-wsgi-py3
RUN ln /usr/bin/python3 /usr/bin/python
RUN apt-get -y install python3-pip
# pythonにdjangoをインストールする
RUN pip install --upgrade pip
RUN pip install django ptvsd
# pythonにmod-wsgiをインストールする
RUN pip install mod_wsgi mod-wsgi-httpd
# apacheの設定ファイルにmod-wsgiの設定を追加
COPY ./apache_django.conf /etc/apache2/sites-available/apache_django.conf
RUN a2ensite apache_django.conf
# Djangoソースコードをマウント
VOLUME ./web/mysite ./var/web/mysite
EXPOSE 80
CMD ["apache2ctl", "-D", "FOREGROUND"]
7-5. docker-compose.yml
あまりビルドし直したくないのでvolumesでソースコードをマウントしていますが、毎回docker compost up --buildするのであればDockerfileのCOPYでコピーするのでも良いと思います
services:
django-apache2:
build: .
container_name: django-apache2
ports:
- '80:80'
volumes:
- ./web/mysite:/var/web/mysite/html
7-6. 起動する
あとはこのフォルダでdocker compose upすれば
http://localhost にDjangoウェブアプリが待機します

まとめ
フォルダでdocker compose upするだけでデプロイできるようにしました。フォルダの内容をGithubにpushしておけば、git cloneしてdocker compose upするだけでデプロイできる筈です