Webアプリをデプロイしよう
進め方の全体像
さて、いよいよアプリのデプロイまで進めていこうと思います。
ゴールとしては
- Linux
- Docker
- SQLite以外のDB
- Django ※PythonのWebフレームワーク
を使ったアプリケーションを作っていきます。
アプリケーションそのもの(Djangoそのもの)はあまり作りこまず、こうやればWebアプリが公開できるというところまで理解することを目的とします。
全体がイメージできれば、後は各技術を勉強していけばよいので「何を学べばいいかわからない」「結局プログラミングって何ができるの?」を脱却できると思います。
それでは1つずつ進めていきましょう
注意
なるべく細かく記載していくつもりですが、おそらく作っていて気になるところをすべては記述しきれません。
適宜ChatGPTとかも駆使しながら1ステップずつ進んでいただければと思います。
なんやかんや、そういう「調べて何とかする」技術・姿勢がエンジニアとしての資質になると思いますので...(言い訳)
注意点2
最終的に「Render」というサービスにアプリをデプロイします。
無料で使えるサービスですが、DBのほうは無料期間に限りがあり、それを過ぎると動かなくなるはずですので実際に運用し続けるアプリのデプロイには向かないこと、ご了承ください
Linuxの準備
まずはLinuxとは?というところからですが、これはWindowsやMacなどと同じOSの一種です。
一般に利用するPCで使うことはあまりないと思いますが、Webアプリの世界ではむしろLinuxがスタンダートです。
Linuxが無料で使えるオープンソースのOSであったために、サーバーのOSとして爆発的に広がり、今や各種サーバーの周辺ツールはLinuxであることがデフォルトになっています。
そんなLinuxですが、新たにPCを買ったりせずともWindows上に仮想のLinuxを作成することができ、それをWSL2(Windows Subsystem for Linux 2)と言います。
まずはこれを使って自PCの中でアプリを作成し、それを後からRenderにデプロイします
WSL2の設定
コマンドラインにて、下記を実行します
wsl --install
wsl --install -d Ubuntu
これらコマンドで、WSL2を有効化したうえで、UbuntuというLinuxの一種をWidows上で使えるようになりました。
ここまで来たら一度再起動し、
wsl -l -v
でUbuntuがちゃんとインストールされているか確認してください。
その後、
sudo apt update
sudo apt upgrade
で今後動かすコマンドラインの機能を最新化しておきます。
パスワードを求められたら、Windowsにログインするときと同じパスワードでOKなはずです。
Do you want to continue? [Y/n]
みたいに「アップデートする?(意訳)」を求められたら、「y」を入力してエンターで処理が実行されます。
ここまで出来たら、「\wsl$\Ubuntu\home」をエクスプローラーで開き、ユーザー名のフォルダが作成されていることを確認してください。
今後、そのフォルダの中を基準に各種操作を行います。
Pythonの設定
さて、Windows PCと今回準備したWSL2のUbuntuは独立しているので、WindowsにPythonが入っていてもUbuntu側でPythonの準備が必要になることがあります。
wsl
を実行し、インストールしたWSL2を起動します。

起動出来たら、下記を実行しPythonの有無を確認してください
python3 --version
私は最初からインストールされていました。
インストールされていなければ
sudo apt install python3 python3-venv python3-pip
で必要な機能をインストールします。
最初から入っていた人も、Pythonの仮想環境を作る機能はインストールされていないことがあるので
sudo apt install python3-venv
にてインストールを行いましょう。
仮想環境とは?
Pythonは豊富なライブラリがあり、それが人気言語である理由でもあるのですが、それゆえにちゃんと整理しないとライブラリがぐっちゃぐちゃになります。
そこで、整理用の箱を作って、その中にライブラリを入れていくことで整理しやすくしながら後続の作業を進めていきます。
この整理用の箱を「仮想環境」と言います。
Djangoプロジェクトの準備
専用フォルダの準備
いよいよPythonのフレームワーク、Djangoを使ってアプリを作っていきます。
まずは、「\wsl$\Ubuntu\home<ユーザー名>」のフォルダにプロジェクト用のフォルダを作りましょう。
エクスプローラで開いているのであれば、いつもWindowsでフォルダを作るように操作してもらえればフォルダ作成することができます。
※もちろんコマンドラインで作成いただいても構いません。
私はmyporjectという名称で作成しています。

準備ができたら
cd ~/myproject
で、作成したフォルダにコマンドラインの基準を移動する。
続いて、
python3 -m venv venv
でPythonの仮想環境を作成し
source venv/bin/activate
で作成した仮想環境を有効にします。
下図のように、(venv) ...という表記になればOKです

Djangoのインストール・アプリ作成
pip install django psycopg2-binary dj-database-url gunicorn
上記にて、仮想環境にDjangoと必要なライブラリをインストールします。
- psycopg2-binary:PostgreSQL用ドライバ
- dj-database-url:DATABASE_URLの読み込みを簡単にする
- gunicorn:本番用のWSGIサーバ
※wsgiとは、Python(Django)を実行するAPサーバーの役割と理解するといいと思います。
django-admin startproject mysite
これで、mysiteというDjangoのアプリが出来上がります。
作成出来たら
cd mysite
python manage.py migrate
python manage.py runserver
を実行し、Djangoアプリを起動します。
PHPでやったときの、ローカルサーバーを立ち上げた状態になるのでブラウザから へアクセスし、Djangoのデフォルト画面が表示されればOKです
migrateについて
Djangoは、直接SQLを記述しなくともDBへのアクセスを可能にする機能があります。
その1種として、Pythonで記述したモデルをもとにテーブルを作成することができるのですが、この「モデルをもとにDBへテーブル情報を連携する」のがmigrateの役割です。
デフォルトでもいくつかモデルが用意されているので、まずはmigrateを行いその情報をDBへ反映します。
※この時、DBはmysite内に作成されたdb.sqlite3が該当します
簡易アプリの作成
デプロイが目的なので詳細を作りこんだりはしませんが、少なくともDBを使うようなアプリケーションにはしたいので、
- ユーザー登録
- ログイン
- (ログイン後に)ユーザー名を表示する
機能を作ろうと思います。
Djangoには、複数のアプリを組み合わせて1つのプロジェクトとする概念が存在しているので
- createUser
- loginApp
- user
という3つのアプリを、下記コマンドにて作成します
python manage.py startapp <appname>
※appnameに各アプリ名を記述してください
ちなみに、usersテーブルがデフォルトで存在しているので、テーブルを準備する必要はありません。
createUser
Djangoそのものについてはこの記事では詳しく触れません。
基礎はいろんなところに転がっているので、気になる人は先に見てもいいかもです。
ググったら出てきた↓
各種コードの中身を記載していきます。
まず、エクスプローラーでcreateUserフォルダを開き、適当なファイルを開きます。

おそらく上記のようなメッセージが出るので、「Allow」をクリックしてコードを開きます。
※Windowsと準備したWSL2は別環境として扱われているので、ざっくりいうと「なんか知らんPCのフォルダ開こうとしてるけどホントに開いていい?」と聞かれています。ドラッグアンドドロップだと上記制限で開けないこともあるので注意。
さて、まずはurls.pyを開き、下記を記述してください
from django.urls import path
from . import views
urlpatterns = [
path('register/', views.register, name='register'),
]
※上側にコメントで使い方が記述されていると思いますが、これは残しても残さなくてもいいです。
register/ っていうリンクがクリックされたらviewsファイルのregisterを実行するよというコードです。また、そのリンクにregisterという名前を付けています。
続いて、views.pyを開き
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth import login
def register(request):
if request.method == "POST":
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
login(request, user)
return redirect('profile')
else:
return render(request, 'createUser/register.html', {'form': form})
else:
form = UserCreationForm()
return render(request, 'createUser/register.html', {'form':form})
を記述。
データがPOSTされていれば有効性を確認してユーザー登録。
有効でない、またはPOSTではないときはformの情報を渡してregister.htmlへ遷移するというコードになっています。
そのregister.htmlは、createUserのフォルダの中にtemplates\createUser\register.htmlという階層で作成します。

<head>
<meta charset="utf-8" />
<meta lang="ja" />
<title>ユーザー登録</title>
</head>
<body>
<h2>ユーザー登録</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p}}
<button type="submit">登録する</button>
</form>
</body>
csrf_tokenとはDjangoデフォルトのセキュリティ機能として必須なトークン
form.as_pは、registerメソッドから渡されたformの情報を表示しています。
loginApp
同様にloginAppとuserのほうも作成を進めていきます。
urls.py
from django.urls import path, include
from . import views
urlpatterns = [
path('login/', views.login_page, name='login')
]
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login, logout
from django.contrib import messages
def login_page(request):
if request.user.is_authenticated:
return redirect('profile')
if request.method == 'POST':
form = AuthenticationForm(request, data=request.POST)
if form.is_valid():
user = form.get_user()
login(request, user)
return redirect('profile') # プロフィールページへリダイレクト
else:
messages.error(request, 'ユーザー名かパスワードが間違っています。')
else:
form = AuthenticationForm()
return render(request, 'loginApp/login.html', {'form': form})
def logout_view(request):
logout(request)
return redirect('login')
templates\loginApp\login.html
<head>
<meta charset="utf-8" />
<meta lang="ja" />
<title>ログインページ</title>
</head>
<body>
<h2>ログイン</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">ログイン</button>
</form>
{% if messages %}
{% for message in messages %}
<p style="color:red">{{ message }}</p>
{% endfor %}
{% endif %}
</body>
user
urls.py
from django.urls import path
from . import views
urlpatterns = [
path('profile/', views.profile, name='profile'),
]
views.py
from django.shortcuts import render
from django.contrib.auth.decorators import login_required
@login_required
def profile(request):
return render(request, 'user/profile.html')
※@login_requiredとは、文字通り「ログイン状態じゃなきゃ動かせないよ」という記述です。
templates\user\profile.html
<head>
<meta charset="utf-8" />
<meta lang="ja" />
<title>プロフィール</title>
</head>
<body>
<h2>プロフィール</h2>
<p>ようこそ、{{ request.user.username }} さん!</p>
</body>
mysite
さて、ここまで作ってきた3つの機能を統合します。
mysiteフォルダにはもう1つmysiteというフォルダがあると思うので、そこに入ると、プロジェクト全体の設定等があるのでそこを整理していきます。
urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('createUser/', include('createUser.urls')),
path('user/', include('user.urls')),
path('login/', include('loginApp.urls')),
]
includeは、各アプリの入り口だけ設定しておき、後はアプリ側のurls.pyの設定に従うというイメージです。
例えば、createUser/registerにアクセスされると、createUser/urls.pyに記述した/registerの挙動をします。
※このあたりは動かしてみるとイメージがつかみやすいと思います。
settings.py
ここのINSTALLED_APPSに、今回作ったcreateUser, loginApp, userを追加
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'createUser',
'user',
'loginApp',
]
また、未ログイン時にログイン必須ページへアクセスしたときの情報もsettings.pyに追記します
# 未ログイン時
LOGIN_URL = '/login/login'
挙動の確認
python manage.py runserver
にてローカルサーバーを起動し、
が動くか確認してみましょう。
Renderの準備
さて、ここまでsqlite3をDBとして扱ってきましたが、実際の開発ではDBは外部サービスを使うことも多いです。
※AWSのRDSなど
そこで今回は無料で使える(期間制限あり)DBサービスの1つ、Render postgresを使っていきます。
GitHubのアカウント作成
後続対応で、GitHubにアップロードしたDjangoアプリをRenderでビルド・デプロイする...という流れをとるので、RenderにはGitHubアカウントでサインアップ・ログインしたいです。
まず、GitHubアカウントを持っていない人はアカウントを作成しましょう
GitHubアカウント作成・ユーザーID確認マニュアル
作成出来たら
へアクセスして、GitHubでユーザー登録をします

Render postgresへの接続
ログインできたら、「Create new project」から新しいプロジェクトを作成。
プロジェクトに入り、右上にある+NewからPostgresを選択する

Nameはお好みで、Projectでは先ほど作成したプロジェクトを選択。
Instance TypeをFreeにして、DBを作成します。
※プロジェクト作成時やDB作成時に地域(Region)選択が必要なところがありますが、特にこだわりがなければ物理的に位置の近いSingaporeでいいと思います。
例えばDjango-sampleという名前で作成すると、プロジェクト内に下記のようにDBが作成されます

※もう1つ、地球マークのDjango-sampleはデプロイしたWebアプリになるので、現時点では存在しないはずです。
このリンクの中に入り、Connectionsの欄に接続情報が記載されています。
- Hostname
- Port
- Database
- Username
- Password
を確認のうえ、Djangoのsettings.pyを書き換えていきます
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME'),
'USER': os.getenv('DB_USER'),
'PASSWORD': os.getenv('DB_PASSWORD'),
'HOST': os.getenv('DB_HOST'),
'PORT': os.getenv('DB_PORT', '5432'),
'OPTIONS': {
'sslmode': 'require',
},
}
}
それぞれ、オープンにしていい情報ではないので、このコードではos.getenvとして環境変数から情報を取得するようにしています。(後ほど解説します)
自PCで動かすうちはべた書きで問題ないかと思いますので、NAME~PORTまでRenderで確認した情報を記述してください。
各種準備が完了したら
python manage.py migrate
を実行し、デフォルトのテーブル情報をRender postgresに連携します。
その後、
python manage.py runserver
でローカルサーバーを起動し、同じように動くか確認してください
注意
HOSTは、Renderで表示されたHostnameをそのまま記述してもつながりません。
シンガポールにDBを作成したなら
.singapore-postgres.render.com
をHostnameに追加した値をセットしてください
余談:クラウドとは
今回利用したRenderがまさにクラウドのイメージです。
従来であれば、DB専用のコンピューターを購入し、そこへPostgreSQLをインストール。
その後、そのコンピューターへ接続できるようにアプリを設定するという対応が必要だったのですが、そんなめんどくさいことしなくても、こうやって画面でポチポチすればDBやサーバーを利用できるようになったのがクラウドが主流になっていった理由です。
※その分ちゃんと使うと利用料がかかりますが...それでも、サーバールームとかを用意しなくてもよいなどメリットのほうが大きいと判断されています。
Dockerの準備
自PCでの準備、最後のステップです。
Dockerそのものの解説はここではスキップします。
ものすごくざっくりいうと、開発環境の再現を簡単にする技術です。
今回例えばPythonとDjangoの準備をして、postgresへの接続ライブラリもpipダウンロードして...と行ってきましたが、本来であればこれは実際にアプリをデプロイするサーバーでも必要な作業になります。
この位簡易なアプリであればさほど大変ではないですが、これが大規模アプリになって、さらにテスト環境が必要で...とかになってくると設定がかなり大変になります。
それでいて本番環境とテスト環境にずれが生じると、テストで動いていたところが本番でバグになったり...
めんどくさくてやってられないので、環境を持ち運べるようにするイメージで「コンテナ」技術と呼ばれたりします。
ググったら出てきた↓ YouTubeのvideoIDが不正です
Dockerfileの準備
さて、コンテナを作るための情報をこのDockerfileに記述していきます
Dockerファイルは、myproject直下に作成してください。
# ベースイメージを指定(Python 3.12.3の軽量版を使用)
FROM python:3.12.3-slim
# コンテナ内の作業ディレクトリを設定(以降のコマンドはこの場所で実行される)
WORKDIR /app/mysite
# ホストのrequirements.txtをコンテナ内にコピー(Pythonパッケージのインストール用)
COPY requirements.txt /app/mysite
# 依存パッケージをインストール(キャッシュを使わずすっきりインストール)
RUN pip install --no-cache-dir -r requirements.txt
# プロジェクトの全ファイルをコンテナ内にコピー
COPY mysite/ /app/mysite
# Pythonの標準出力をバッファリングしない設定(ログがリアルタイムに見える)
ENV PYTHONUNBUFFERED=1
# コンテナの8000番ポートを開放(Djangoのデフォルトポート)
EXPOSE 8000
# コンテナ起動時にDjango開発サーバーを起動
CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
さらにDjangoなど、インストールが必要なものはrequirement.txtにまとめます。
これもmyproject直下に作成してください
Django==5.2.8
bcrypt==3.2.2
cryptography==41.0.7
PyJWT==2.7.0
requests==2.31.0
pytz==2024.1
setuptools==68.1.2
wheel==0.42.0
gunicorn==20.1.0
python-dotenv
psycopg2-binary
.envの作成
さて、ここでさらに.envというファイルを用意します。(これもmyproject直下に)
例えばDBの接続情報とか、settings.pyの中に記述されているSECRET_KEYとか、そのままGitHubに上げるとヤバそうな情報があります。
これを.envというファイルにまとめ、そこから読み込む形に変えておきます。
実際にはRender側に環境変数というものを準備し、そこを読み込むのですが、開発時にはそれと同じ挙動を再現するために.envに必要情報を記述していきます。
※つまり、実際には.envファイルもGitHubにはアップロードしません。
SECRET_KEY='django-xxx'
DB_NAME=xxx
DB_USER=xxx
DB_PASSWORD=xxx
DB_HOST=xxx.singapore-postgres.render.com
DB_PORT=5432
SECRET_KEYには、もともとsettings.pyに記述されていた値を入力してください。
DBの情報は、一度settings.pyにべた書きしたものをこちらに記述してください。
そのうえで、settings.pyを書き換えていきます。
# 一番上に
from dotenv import load_dotenv
import os
load_dotenv()
# 中略
SECRET_KEY = os.getenv('SECRET_KEY')
# 中略
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': os.getenv('DB_NAME'),
'USER': os.getenv('DB_USER'),
'PASSWORD': os.getenv('DB_PASSWORD'),
'HOST': os.getenv('DB_HOST'),
'PORT': os.getenv('DB_PORT', '5432'),
'OPTIONS': {
'sslmode': 'require',
},
}
}
Docker Desktopのインストール
Windowsにこのツールをインストールし、WSL2で相乗りするかたちでDockerを利用します。
こちらにアクセスし、Windows用インストーラをダウンロードしてください。
インストールまで完了したら、Docker Desktopを起動します。

図のように、WSL2でDockerが使えるように設定を変更、Docker Desktopを立ち上げなおします。
docker --version
でバージョン情報が返ってきたら準備OKです。
※Docker Desktopに相乗りしている形なので、Windows側でDocker Desktopが起動していないと実行できません。
cd ~/myproject
でmyproject直下へ移動、下記でDockerイメージを作成します。
docker build -t render_project .
イメージの作成が完了したら、
docker run -d --name render_project -p 8000:8000 --env-file .env render_project
で.envを読み込みながら、作成したDjangoアプリがDockerのコンテナとして起動します。
この時点で、ローカルサーバーが立ち上がっている状態なので、 等にアクセスして、問題なく動作するか確認してください
動かなかったとき
※各種エラーが起き、その原因を修正したのちのことを想定しています。
修正が完了したら、再度docker buildコマンドを実行してイメージを作り直します。
そして、エラーを起こしているコンテナが残ったままになっているので
docker stop render_project
docker rm render_project
でコンテナを削除し、
docker run -d --name render_project -p 8000:8000 --env-file .env render_project
でコンテナを作成しなおして下さい。
GitHubへアップロードし、Render上でデプロイする
Dockerfileの修正
一部開発用の機能になっている部分を修正します
# CMD ["python", "manage.py", "runserver", "0.0.0.0:8000"]
CMD ["gunicorn", "mysite.wsgi:application", "--bind", "0.0.0.0:8000"]
runserverではなく、wsgiを使った方法へ切り替えています。
WSL2へGitのインストール
sudo apt install -y git
インストールされたかどうかは下記で確認できます
git --version
基本的な情報をconfigに登録します
git config --global user.name "あなたのGitHubユーザー名"
git config --global user.email "あなたのGitHub登録メールアドレス"
認証
次にGitHubへデータをアップロードする(push)ための設定ですが、現在(25/11)時点でGitHubはパスワードを使ったpushを認めていないようなので、パスワード以外の認証設定をします。
SSHキーを作成
ssh-keygen -t ed25519 -C "your_email@example.com"
※何度か確認があるがエンターキー連打でOK
公開鍵の情報を取得
cat ~/.ssh/id_ed25519.pub
GitHub右上のプロフィール → Settings → SSH and GPG keys → New SSH key
→ さきほどの内容を貼り付け



接続確認
ssh -T git@github.com
GitHubへpush
myprojectをGitHubへアップロードしていきます。
Gitリポジトリを作成
cd ~/myproject
git init
.gitignore を設定
echo ".env" >> .gitignore
echo "__pycache__/" >> .gitignore
echo "*.pyc" >> .gitignore
echo "*.sqlite3" >> .gitignore
echo "venv/" >> .gitignore
これで、.envを含めてGitに上げないファイルが設定されます。
初回コミット
git add .
git commit -m "Initial commit"
GitHub上で新しいリポジトリを作成
GitHubにログインし、Dashboardを開くと左上にNewがあるのでそこからリポジトリを作成

push
git remote set-url origin git@github.com:<ユーザー名>/<リポジトリ名>.git
git push
これでGitHub上にアップロード(push)は完了です。
Renderで新しい「Web Service」を作成
postgresを作成したとき同様に、プロジェクト内の+Newから「Web Service」を選択。
先ほどpushまで完了したGitリポジトリを選択します。
- Nameはお好みで設定
- Projectはpostgresの時に作成したプロジェクトを選択
- LanguageはDocke
-
Instance TypeをFree
に設定します。
そして、下部にEnvironment Variablesを入力するところがあるので、ここに.envを同じ情報を追加していきます。
そしてDeploy Web Serviceをクリック。
settings.pyの修正
これで完了...と思いきや、最後に1つ修正すべきところがあります。
上記まで完了してようやくリンクが確定するので、ALLOWED_HOSTSに
your-app-name-xxx.onrender.com
の部分を追加して、
git add .
git commit -m "settings.py修正"
git push
でpushしなおします。
Render側で再度自動でビルドしなおすので、それが完了次第、できたページが問題なく動作することを完了し、Webサービスのデプロイは完成です🎉
Discussion