はじめに
AWSのECSについて学習するため、フロントエンドとバックエンドの環境を構築することにしました。これまで実務ではNestJSやRailsを扱ってきましたが、ちょうどPythonにも興味を持っていたため、今回はPythonを用いて開発を進めてみることにします。
また、インフラの理解を深める上で、Dockerの知識が不十分だと感じていたため、この機会にしっかりと学び直そうと考えました。加えて、GitHub ActionsやTerraformを活用しながらCI/CDやIaCについても学び、体系的に整理していく予定です。
この学習の過程や得た知識を記録しながら、ブログとしてまとめていきます。拙い部分もあるかもしれませんが、インフラ学習の備忘録として役立つ内容を目指し、できるだけ分かりやすく整理していきたいと思います。同じように学びたい方の参考になれば嬉しいです。
なお、学習しながらブログを書いているため、説明が雑もしくはあまり良くない部分があるかもしれませんが、その点は多めに見ていただけると幸いです。動くものは作れるようになるというところを目標に頑張ります。
作成したリポジトリ
前提
- docker周りのセットアップが済んでいる
- vscodeの拡張機能 Dev Containers をインストールしている
1. Dockerまわりのファイル作成
作業したい位置に移動し、以下のコマンドを叩く。
mkdir .devcontainer && touch .devcontainer/devcontainer.json && touch .devcontainer/Dockerfile && touch docker-compose.yml
devcontainer.jsonに以下を追記
{
"name": "Django Development",
"dockerComposeFile": "../docker-compose.yml",
"service": "api",
"workspaceFolder": "/usr/src/app",
"shutdownAction": "stopCompose",
"customizations": {
"vscode": {
"extensions": [
"ms-python.python",
"ms-python.vscode-pylance",
"ms-azuretools.vscode-docker",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"aaron-bond.better-comments",
"janisdd.vscode-edit-csv",
"usernamehw.errorlens",
"vscode.git",
"mhutchie.git-graph",
"donjayamanne.githistory",
"gitHub.vscode-pull-request-github",
"eamodio.gitlens",
"oderwat.indent-rainbow",
"ms-ceintl.vscode-language-pack-ja",
"christian-kohler.npm-intellisense",
"faissaloux.package-manager-intellisense",
"christian-kohler.path-intellisense",
"mechatroner.rainbow-csv",
"chrmarti.regex",
"bradlc.vscode-tailwindcss",
"gruntfuggly.todo-tree",
"vscode-icons-team.vscode-icons"
],
"settings": {
"editor.fontSize": 16,
"editor.tabSize": 2,
"editor.wordWrap": "on",
"notebook.output.wordWrap": true,
"git.enableSmartCommit": true,
"editor.renderWhitespace": "all",
"terminal.integrated.scrollback": 6000,
"editor.minimap.size": "fit",
"files.trimTrailingWhitespace": true,
"githubPullRequests.pullBranch": "never",
"workbench.editorAssociations": {
"*.csv": "default"
},
"files.autoSave": "afterDelay",
"editor.formatOnSave": true,
"workbench.tree.enableStickyScroll": false,
"workbench.tree.stickyScrollMaxItemCount": 1,
"editor.stickyScroll.maxLineCount": 1,
"editor.stickyScroll.enabled": false,
"hediet.vscode-drawio.resizeImages": null,
"workbench.iconTheme": "vscode-icons",
"better-comments.multilineComments": true,
"better-comments.highlightPlainText": false,
"better-comments.tags": [
{
"tag": "!",
"color": "#FF2D00",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "?",
"color": "#3498DB",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "//",
"color": "#474747",
"strikethrough": true,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "todo",
"color": "#FF8C00",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": false,
"italic": false
},
{
"tag": "*",
"color": "#98C379",
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": true,
"italic": true
},
{
"tag": "NOTE",
"color": "#FFD700", // ゴールド色
"strikethrough": false,
"underline": true,
"backgroundColor": "transparent",
"bold": true,
"italic": false
},
{
"tag": "DEBUG",
"color": "#FF1493", // ディープピンク色
"strikethrough": false,
"underline": false,
"backgroundColor": "#1E1E1E", // 暗い背景色
"bold": true,
"italic": true
},
{
"tag": "SECTION",
"color": "#1E90FF", // ドジャーブルー
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": true,
"italic": false
},
{
"tag": "CHAPTER",
"color": "#32CD32", // ライムグリーン
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": true,
"italic": false
},
{
"tag": "=",
"color": "#1E90FF", // ドジャーブルー(SECTIONと同じ色)
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": true,
"italic": false
},
{
"tag": "-",
"color": "#32CD32", // ライムグリーン(CHAPTERと同じ色)
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": true,
"italic": false
},
{
"tag": "END",
"color": "#FF4500", // オレンジレッド
"strikethrough": false,
"underline": false,
"backgroundColor": "transparent",
"bold": true,
"italic": false
}
],
"git.ignoreRebaseWarning": true,
"yaml.customTags": [
"!And",
"!And sequence",
"!If",
"!If sequence",
"!Not",
"!Not sequence",
"!Equals",
"!Equals sequence",
"!Or",
"!Or sequence",
"!FindInMap sequence",
"!Base64",
"!Base64 mapping",
"!Cidr",
"!Cidr sequence",
"!Ref",
"!Sub",
"!GetAtt",
"!GetAZs",
"!ImportValue",
"!Select",
"!Select sequence",
"!Split",
"!Split sequence",
"!Join sequence"
],
"diffEditor.hideUnchangedRegions.enabled": true,
"workbench.colorTheme": "Night Owl"
}
}
}
}
Dockerfileに以下を追記
# ベースとなるDockerイメージを指定
# ここではPythonのバージョン3.13.0を使用
FROM python:3.13.0
# コンテナ内で作業するディレクトリを指定
# ここでは `/usr/src/app` を作業ディレクトリとして設定
WORKDIR /usr/src/app
# 必要なシステムパッケージをインストール
# - `apt-get update`: パッケージリストを更新
# - `libpq-dev`: PostgreSQLとの接続に必要なライブラリをインストール
# - `rm -rf /var/lib/apt/lists/*`: 不要なキャッシュを削除してコンテナのサイズを削減
RUN apt-get update \
&& apt-get install -y libpq-dev
# Pythonパッケージの依存関係をインストール
# - `COPY backend/requirements.txt .` : `requirements.txt` をコンテナ内にコピー
# - `pip install --upgrade pip`: `pip` を最新バージョンに更新
# - `pip install -r requirements.txt`: `requirements.txt` に記載されたパッケージをインストール
COPY backend/requirements.txt .
RUN pip install --upgrade pip \
&& pip install -r requirements.txt
# コンテナのポート8000番を開放
# Djangoの開発サーバーはデフォルトで8000番ポートを使用するため
EXPOSE 8000
CMD ["sh", "-c", "python manage.py migrate && python manage.py runserver 0.0.0.0:8000"]
docker-compose.ymlに以下を追記
services:
api:
build:
context: . # 基準となるパス。docker-compose.ymlがあるカレントディレクトリにいてDockerfileなどを探しに行く。
dockerfile: .devcontainer/Dockerfile # 使用するDockerfileのパス
container_name: django_api # コンテナの名前を指定
command: python manage.py runserver 0.0.0.0:8000 # Django の開発サーバーを起動するコマンド
volumes: # バインドマウント (ファイルを同期する) (ホストPCのフォルダとコンテナ内のフォルダをリアルタイムで同期)
- ./backend:/usr/src/app # ホスト(PC)の ./backend/ フォルダ を コンテナの /usr/src/app に接続(マウント)
ports:
- "8000:8000" # ホストのポート8000とコンテナのポート8000を紐付ける。Docker コンテナは仮想環境の中で動いているので、デフォルトでは外部(ホストPC)からアクセスできません。ports: を設定すると、ホストPCのポートとコンテナのポートを紐づけ(ポートフォワード)、ホストからアクセスできるようになります。
depends_on:
- db # このコンテナを起動する前に db(PostgreSQL)コンテナを起動する
env_file:
- .env # 環境変数を記載した .env ファイルを読み込む
db:
image: postgres:16
container_name: postgres_db
restart: always # コンテナが停止した場合に自動で再起動
environment: # 環境変数を設定
POSTGRES_USER: ${POSTGRES_USER} # ユーザー名(.env ファイルから読み込む)
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} # パスワード(.env ファイルから読み込む)
POSTGRES_DB: ${POSTGRES_DB} # 作成するデータベース名(.env ファイルから読み込む)
volumes:
- postgres_data:/var/lib/postgresql/data # データを永続化するためのボリュームをマウント
ports:
- "5432:5432" # ホストのポート5432とコンテナのポート5432を紐づける(PostgreSQL のデフォルトポート)
volumes:
postgres_data: # PostgreSQL のデータを永続化するためのボリューム
.envを作成して追記
echo 'POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=dev
' > .env
backend/requirements.txt
Django と PostgreSQL 用のパッケージをインストールします。
mkdir backend && touch backend/requirements.txt
requirements.txtに以下を追記
Django>=5.1
psycopg2>=2.9
Django プロジェクトを新規作成
-
コンテナ内に入る
docker compose run api bash
root@702045f8a4bd:/usr/src/app# django-admin startproject myapi .
exit
現在のツリーはこちらになります
.
├── .devcontainer
│ ├── Dockerfile
│ └── devcontainer.json
├── .env
├── .gitignore
├── backend
│ ├── .env
│ ├── db.sqlite3
│ ├── manage.py
│ ├── myapi
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-310.pyc
│ │ │ ├── __init__.cpython-313.pyc
│ │ │ ├── settings.cpython-310.pyc
│ │ │ ├── settings.cpython-313.pyc
│ │ │ ├── urls.cpython-310.pyc
│ │ │ ├── urls.cpython-313.pyc
│ │ │ ├── wsgi.cpython-310.pyc
│ │ │ └── wsgi.cpython-313.pyc
│ │ ├── asgi.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ └── requirements.txt
└── docker-compose.yml
35 directories, 50 files
backend
│ ├── .env
backendフォルダにも.envファイルを作成する
echo 'POSTGRES_USER=postgres
POSTGRES_PASSWORD=postgres
POSTGRES_DB=dev
' > backend/.env
コンテナを起動し、DB操作まで
http://localhost:8000/ にアクセス
postgresを繋ぐ
pip install python-dotenv
settings.pyを変更
DATABASES = {
'default': {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": "dev", #ご自身が作成したデータベース名
"USER": "postgres", #ご自身が設定したユーザー名
"PASSWORD": "postgres", #ご自身が設定したパスワード
"HOST": "postgres_db",
"PORT": "5432",
}
}
python manage.py makemigrations
python manage.py migrate
envから読み取る方法は以下
requirements.tsx
Django>=5.1
psycopg2>=2.9
python-dotenv
settings.py
"""
Django settings for myapi project.
Generated by 'django-admin startproject' using Django 5.1.5.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
import os
from pathlib import Path
from os.path import join, dirname
from dotenv import load_dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(verbose=True)
dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
'default': {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": os.getenv("POSTGRES_DB"),
"USER": os.getenv("POSTGRES_USER"),
"PASSWORD": os.getenv("POSTGRES_PASSWORD"),
"HOST": "postgres_db",
"PORT": "5432",
}
}
Postテーブルを作りたい
myapi/models.pyを作成し、
以下を記述
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
コマンドを実行してテーブルを作成する
models.py で定義されたモデルの変更を検出し、それを「マイグレーションファイル」として保存します。
python manage.py makemigrations
作成されたマイグレーションファイルを元に、実際にデータベースを更新します。
python manage.py migrate
Django シェルを使い、テーブルができたか確認。
python manage.py shell
以下を実行
from myapi.models import Post
# 新しい投稿を作成
post = Post.objects.create(
title="初めての投稿",
content="これはDjangoのPostモデルを使った最初の投稿です!"
)
# 作成したデータを確認
print(post.id, post.title, post.content)
作成されたデータを確認するには、以下のコマンドを実行してください。
print(Post.objects.all()) # すべてのデータを表示
うまくDB操作できたと思います。
control + Dでコンソールを抜けることができる。
http://localhost:8000/adminに辿り着くまでに途中に起きたエラー集
サーバーが正しく起動しているのに このサイトにアクセスできません
myapi/settings.py を開いて、以下のように修正。
ALLOWED_HOSTS = ['0.0.0.0', '127.0.0.1', 'localhost']
または
ALLOWED_HOSTS = ['*']
エラー文 ModuleNotFoundError: No module named 'myapi.wsgi'
このエラーは Django が myapi.wsgi を見つけられない ことが原因です。
これは、プロジェクトの構造が正しくない、settings.py の WSGI_APPLICATION の設定が間違っている などが原因で発生します。
myapi/wsgi.py がない場合は作成してください。
myapi/wsgi.py の内容
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "myapi.settings")
application = get_wsgi_application()
settings.py の WSGI_APPLICATION を確認
WSGI_APPLICATION = 'myapi.wsgi.application'
最終的には以下になる
"""
Django settings for myapi project.
Generated by 'django-admin startproject' using Django 5.1.5.
For more information on this file, see
https://docs.djangoproject.com/en/5.1/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.1/ref/settings/
"""
import os
from pathlib import Path
from os.path import join, dirname
from dotenv import load_dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
load_dotenv(verbose=True)
dotenv_path = join(dirname(__file__), '.env')
load_dotenv(dotenv_path)
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-j)ejxqmwc@t647)uc$r^hp2%66so_-pzkgws(^wz4w!85!gcq@'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['0.0.0.0', '127.0.0.1', 'localhost']
# Application definition
INSTALLED_APPS = [
'myapi',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'myapi.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'myapi.wsgi.application'
# Database
# https://docs.djangoproject.com/en/5.1/ref/settings/#databases
DATABASES = {
'default': {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": os.getenv("POSTGRES_DB"),
"USER": os.getenv("POSTGRES_USER"),
"PASSWORD": os.getenv("POSTGRES_PASSWORD"), #
"HOST": "postgres_db",
"PORT": "5432",
}
}
# Password validation
# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/5.1/topics/i18n/
LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = 'static/'
# Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
localohost:8000/admin にアクセスできない
superuser を作成(管理者アカウント)
python manage.py createsuperuser
プロンプトが出るので、管理者情報を入力してください。
Username (leave blank to use 'your-user'): admin
Email address: admin@example.com
Password:
Password (again):
パスワードは 8文字以上で英数字を含む 必要があります。
管理画面で Post モデルを管理できるようにするには、admin.py に登録します。なかったら作成。
myapp/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
これで、管理画面の「Post」からデータを追加・編集できるようになります。
まとめ
Django に触れるのは初めてで、最初は 設定やエラー対応に苦戦(4〜5時間かかった) しましたが、
なんとなく動く状態には持っていけました。
次は API の作成やプロジェクトの整理 をしながら、より理解を深めていきたいと思います。
まだまだ学ぶことは多いですが、まずは動かしてみることが大事!
引き続き、Django に挑戦していきます!
(AWSを学ぶために始めてるのにDjangoでかなりハマったので選んだのを後悔しています。。)
最終的なディレクトリ構成はこちら
.
├── .devcontainer
│ ├── Dockerfile
│ └── devcontainer.json
├── .env
├── .gitignore
├── backend
│ ├── .env
│ ├── db.sqlite3
│ ├── manage.py
│ ├── myapi
│ │ ├── __init__.py
│ │ ├── __pycache__
│ │ │ ├── __init__.cpython-313.pyc
│ │ │ ├── admin.cpython-313.pyc
│ │ │ ├── models.cpython-313.pyc
│ │ │ ├── settings.cpython-313.pyc
│ │ │ ├── urls.cpython-313.pyc
│ │ │ └── wsgi.cpython-313.pyc
│ │ ├── admin.py
│ │ ├── asgi.py
│ │ ├── migrations
│ │ │ ├── 0001_initial.py
│ │ │ ├── __init__.py
│ │ │ └── __pycache__
│ │ │ ├── 0001_initial.cpython-313.pyc
│ │ │ └── __init__.cpython-313.pyc
│ │ ├── models.py
│ │ ├── settings.py
│ │ ├── urls.py
│ │ └── wsgi.py
│ └── requirements.txt
└── docker-compose.yml