突如任されたバックエンド開発にぼこぼこにされた話

概要

今までUnityやら組み込みやら比較的フロントエンドに近い開発を行ってきた私。

突如「簡単なAWS連携するバックエンドシステムの開発してくれない?」と。
いうて今までも未経験領域ばっかりだったし、いけるやろ!と思い、
「いいですよ!やってみますね~!」
そう答えた。

これが悪夢の始まりと知らずに─

リポジトリ

今回の説明用に作成したサンプル。
https://github.com/shinyshinpy/backend_sample

取り扱い内容

前段の茶番は置いておいて。

まじで安請け合いしてがっつりバックエンドにぼっこぼこにされてトラウマになったので
どんな構成で、どう組んだのかをまとめようと思います。

  • バックエンド初学者
  • フロントいけたしバックエンドいけるやろ!と考えた人
  • バックエンドってどう作るんか分からん

↑こんな人にピッタリな記事になると思うので、よかったら読んでくださいね~
トラウマを追体験しましょう!

※ AWSは今回は触れません。

構成

構成はこんな感じ

デバイスからデータが飛んできて、それをDBに保存。
外部からAPIでそのデータを吸い上げ。

”簡単”そうですね!
(実際簡単なシステムではある)

開発環境

ここからが本題。
python書いてSQL書くくらいなら簡単ですわ~

え?環境ごとノーヒントで作れ…?

ということで後任とか保守性とか分からないなりに考えた結果、
以下の環境で作ることに。
(会社なんだからサポートつけてくれ~~)

  • WSL2
    • Windows上でLinux使えて開発に便利!
  • devcontainer
    • 開発環境そのものがリポジトリに保存できて人依存の問題が減って便利!
    • VSCode上でしか使えない?けど開発環境統一できて便利!
    • 依存性の他、拡張機能もコンテナ毎に設定できて環境ずれが起きなくて便利!
  • docker compose
    • よく分からんがビルドする時に便利!
    • ビルドイメージとかビルド方法とか、ビルド関連の設定が色々できて便利!

一応VirtualBox上のUbuntuでDocker使ったことはあるが、
当然開発環境が出来上がった上で実装書いてたもので…

バックエンド開発ってみんな環境ごと作るの?すごいね…

事前準備

色々準備します。

WSL

WSLはWSL2です。
Linux用Windowsサブシステムを有効化して

ターミナルで wsl –install って打つだけです。

具体的な方法はいくらでも載ってるので適当に調べてください。

dockerの準備

本当はDockerDesktopを使う方が楽らしいんですけど、これ商用利用ライセンスが必要なんですよね。
大体の記事は皆個人開発なので↑使ってるんですけど、商用使えないの、困りますよねぇ…

ということで無償のDockerEngineを使います。

基本的には公式の手順に従ってコマンド打っていくだけです。
https://matsuand.github.io/docs.docker.jp.onthefly/engine/install/ubuntu

よっぽどリポジトリからのインストールで問題ないと思います。

$  sudo apt-get update
$  sudo apt-get install \
    ca-certificates \
    curl \
    gnupg \
    lsb-release

$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o 
/usr/share/keyrings/docker-archive-keyring.gpg

$ sudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin

docker -v でバージョンが出ればOKです。

環境構築

さて構築していきましょう。

作業フォルダに入る

まずWSLに入ります。

入れたら作業フォルダを選びます。

こうなっていればOK

devcontainerに入る

次にdevcontainerの準備をします。

ホスト側(Windows側)でもよいので以下の拡張機能を入れます。

Shit+Ctrl+Pでコマンドを開きます。

開発コンテナー:開発コンテナー構成ファイルを追加 を選択

今回はpythonで開発する環境が欲しいので、
pythonと入力して一番シンプルな python3 を選択。

pythonのバージョンはお好みで。今回は既定の3.12
実務ではpythonバージョンによって仕様が変わるので、関係者と相談して決めよう!(一敗)

今回はmysql使うことが確定しているのでこのタイミングで追加。

こうなればOK

この時点でdevcontainerの環境を作ることはできましたが、まだコンテナに入ってません。
左下の><に入っている環境が表示されています。
devcontainerに入ると、ここがコンテナ名になります。
今はWSLなので、まだWSL上でフォルダを開いているだけ、という状態です。

もう一度 Ctrl+Shit+P でコマンドを開き、
コンテナーでリビルドして再度開く を選択します。

開けない場合

以下のようなエラーが出て開けない場合。

[981117 ms] Start: Run in Host: docker version --format {{json .}}
[981208 ms] {"Client":{"Platform":{"Name":"Docker Engine - Community"},"Version":"28.3.3","ApiVersion":"1.51","DefaultAPIVersion":"1.51","GitCommit":"980b856","GoVersion":"go1.24.5","Os":"linux","Arch":"amd64","BuildTime":"Fri Jul 25 11:34:04 2025","Context":"default"},"Server":null}
[981209 ms] Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?

[981210 ms] Exit code 1

dockerが起動してないはずなので起動する。

$ sudo service docker status
 * Docker is not running

$ sudo service docker start
 * Starting Docker: docker                                                                                                                   [ OK ] 

こうなっていればOK
コンテナ名は .devcontainer.json の name に書かれている名前。

devcontainer設定

devcontainerは開く度に環境がリセットされます。

なのでこの状態でいつも通りタブから拡張機能入れたり、pip installしたりしても、
次開いたらそれらがなかったことになります。

しかも手動でやっちゃったら開く人によって環境変わっちゃうので
devcontainerの環境統一の恩恵が受けれません。

それら必要なものは全て .devcontainer.json に書くことで、
コンテナを開いた時(入った時)に自動的に全てインストールしてくれます。

{
	"name": "Python 3",

	"image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye",
	"features": {
		"ghcr.io/devcontainers-extra/features/mysql-homebrew:1": { }
	},
	"customizations": {
		"vscode": {
			"extensions": [
				"ms-python.python",
				"VisualStudioExptTeam.vscodeintellicode",
				"mhutchie.git-graph",
				"ms-python.black-formatter"
			]
		}
	},
	"postCreateCommand": "apt-get update && (apt-get install -y docker-ce-cli || apt-get install -y docker.io); pip install --no-cache-dir -r .devcontainer/requirements.txt"
}
fastapi
uvicorn
sqlalchemy
pymysql
pytest
numpy
freezegun
black

詳しく見ていきます。

拡張機能の設定

	"customizations": {
		"vscode": {
			"extensions": [
				"ms-python.python",
				"VisualStudioExptTeam.vscodeintellicode",
				"mhutchie.git-graph",
				"ms-python.black-formatter"
			]
		}
	},

extensionsの中には拡張機能IDを記載します。

欲しい拡張機能を右クリックして 拡張機能IDのコピー を押して、
jsonに貼り付けるだけです。

依存系の設定

pip installとかで入れる必要があるものを全て requirements.txt 内に記載します。
(ファイル名は自由です)

fastapi
uvicorn
sqlalchemy
pymysql
pytest
numpy
freezegun
black

これでdevcontainerを開いた時点で、コンテナ内にインストールさせることができます。

後述する個別のコンテナにも同様にインストールさせる必要があるので、
このようにファイルにまとめておくと便利です。

バージョン指定なども fastapi==0.45.0 など、通常のpip installと同様の記法が使えます。
未記載なら最新版が入ります。

インストール設定

前述のrequirements.txtに加え、開発環境自体に必要なライブラリを入れます。

"postCreateCommand": "apt-get update && (apt-get install -y docker-ce-cli || apt-get install -y docker.io); pip install --no-cache-dir -r .devcontainer/requirements.txt"

postCreateCommandを使用することで、引数のコマンドを実行されることができます。
ここでは、以下のことを行っています。

  • dockerEngineに必要な docker-ce-cliとdocker.ioの明示的なインストール
  • requirements.txt のインストール

本来、一つ目のものは不要のはずですが、
まれにdocker-ce-cliが無くてビルドできない、という状態に陥るので明示的に入れてます。(一敗)

インストール確認

構成(json)を変更したらリビルドして開き直しましょう。
ターミナルにログが残るので、正常に入ったかはターミナルを監視して確認しましょう。

Dockerコンテナ作成

ここまででdevcontainer上での開発環境構築が完了しました。

さて!開発するぞ~!

ど こ に ど う や っ て ??

ここに直接pythonファイル置いて動かすこともできるんですけど、
冒頭に示した構成をどう実現するのか?ってところが本題なんですよね。
(なのにその辺の記事がほとんどないという)

コンテナ作り

Dockerコンテナとして、アプリサービス用コンテナ、DB用コンテナの2種類に分けて開発することとします。

その際に、Dockerコマンドを使ってコンテナを立てるのではなく、
DockerFileに構成を記載し、docker composeコマンドで立てます。

こうすることで、コンテナの構成やビルドも統一することができ、
保守性が大きく向上します。

wikiとかreadmeにコマンド手順書けばよくね?

ありがちなんですけど、こんな風に思うと思います。
 docker composeいる?
 Dockerコマンド使って個々に作ってもらう方が速くね?

まぁ大体記載されたコマンドは間違っていたり、環境依存なことが大きいことが大半です。
つまり、

書いてある通りにやってもコンテナできなくて開発できない!!!!

って声が”自分以外の人から”やってきます。
困りますねぇ~

docker composeならコンテナ作りが1つのコマンドで済み、
更にどんな構成でコンテナ作ったかがファイルにまとまっているので管理しやすいです。

構成が変更されても、git管理するのでいつだれが変えたか分かるので保守しやすいです。

そんな訳で、docker composeで構成する方が良いです。
(ただ記事あんまりないので一人でやるの大分むずかしいです。)

コンテナ構成

docker-compose.ymlにコンテナ情報を記載
Dockerfileにビルド情報を記載

docker-compose.ymlをdevcontainer.jsonから呼び出すようにすれば
開いた時にコンテナ作成、マウントなどをやってくれる。

(始めたばかりすぎて合ってるかは不明…)

docker-compose.yml

services:
  mysql:
    image: mysql:8.4
    container_name: mysql_container
    environment:
      - MYSQL_ROOT_PASSWORD=root_password
      - MYSQL_DATABASE=my_database
      - MYSQL_USER=user
      - MYSQL_PASSWORD=user_password
    ports:
      - "3306:3306"
    volumes:
      - ./database:/docker-entrypoint-initdb.d
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      timeout: 20s
      retries: 10

  app:
    container_name: app_container
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: mysql_pymysql://user.user_password@mysql_container/my_database
    depends_on:
      mysql:
        condition: service_healthy
    build:
      context: ../
      dockerfile: .devcontainer/Dockerfile
    volumes:
      - ../:/app

volumes:
  mysql_data:

順番に見ていく

  mysql:
    image: mysql:8.4
    container_name: mysql_container
    environment:
      - MYSQL_ROOT_PASSWORD=root_password
      - MYSQL_DATABASE=my_database
      - MYSQL_USER=user
      - MYSQL_PASSWORD=user_password
    ports:
      - "3306:3306"
    volumes:
      - ./database:/docker-entrypoint-initdb.d
      - mysql_data:/var/lib/mysql
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
      timeout: 20s
      retries: 10

image: mysql 8.4を使用する。
mysql_container内にmysql8.4のイメージを展開するという意味。

environment: 環境変数。
ユーザーとかパスワードとかはここ以外でも設定できる。

volumes: マウント設定
一行目の./databaseにinit.sqlやら.envやら構成に必要なファイルを入れておく。
docker-entrypoint-initdb.dは魔法の言葉。ここにsqlとか置くと起動時に勝手にテーブル作ってくれる。
※ 既にテーブルがあると作成してくれないため、再作成時は削除してから。

healthcheck: ヘルスチェック
なくてもいいがあった方が良いやつ。
pingが通ればコンテナが作成されたことを示す。

  app:
    container_name: app_container
    ports:
      - "8000:8000"
    environment:
      DATABASE_URL: mysql_pymysql://user.user_password@mysql_container/my_database
    depends_on:
      mysql:
        condition: service_healthy
    build:
      context: ../
      dockerfile: .devcontainer/Dockerfile
    volumes:
      - ../:/app

environment: 環境変数。
使用するDBの依存設定。
デバッグ時などはここをデバッグ用DBに変えるといい。

depends_on: 起動順の設定
mysqlコンテナが健康状態で生成されてからappコンテナを生成する。

build: ビルド設定
context: ルート。.devcontainerに置いているためプロジェクトルートにする。
dockerfile: Dockerfileの在処

volumes: マウント設定
プロジェクトルートから全部appコンテナにマウントさせる
 → appコンテナ内で作業するための設定

Dockerfile

FROM python:3.12-slim

WORKDIR /app

COPY .devcontainer/requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

EXPOSE 8000

CMD [ "uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000" ]

python3.12-slimというコンテナを使用する。

最後コマンドでuvicornを実行している。
今回の構成がFastAPIを使用する構成のため、ここでサーバーを立ち上げてる。
(構成が違ったらここはなくてもいい)

.devcontainer.json
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/python
{
	"name": "Python 3",

	"dockerComposeFile": "./docker-compose.yml",
	"service": "app",
	"workspaceFolder": "/app",
	"features": {
		"ghcr.io/devcontainers/features/docker-outside-of-docker:1": {
			"moby": false
		}
	},
	"containerEnv": {
		"COMPOSE_FILE": ".devcontainer/docker-compose.yml"
	},
	"customizations": {
		"vscode": {
			"extensions": [
				"ms-python.python",
				"VisualStudioExptTeam.vscodeintellicode",
				"mhutchie.git-graph",
				"ms-python.black-formatter",
				"ms-azuretools.vscode-docker"
			]
		}
	},
	"postCreateCommand": "apt-get update && (apt-get install -y docker-ce-cli || apt-get install -y docker.io); pip install --no-cache-dir -r .devcontainer/requirements.txt"
}

image: コンテナイメージの設定
Dockerfileで設定するため削除。

dockerComposefile, service, workspaceFolder: appコンテナマウント設定
appじゃなくてもいいはず。
docker-compose.ymlの在処の設定。
imageがない場合はどこにマウントするか記載する必要があるためこれを書く。

features: 追加設定
mysqlは別コンテナにするため削除。
docker-outside-of-dockerでホスト側dockerコマンドの実行権を取得。
(なくても良い)

docker-outside-of-dockerについて

これがあると、コンテナ内でdockerコマンドが使用できる。
ただし、通常運用は危険らしいので非推奨。

というのも、せっかくコンテナ毎に分けて開発環境を分離したのに、
ホスト側操作をするdockerコマンドがコンテナ内で使用できると、
理解していない人が勝手に docker rmで削除したり、コンテナ追加したり、
意図しない操作が起きる可能性がある。

本来はターミナルを別で立ち上げ、
 ビルドはホストターミナル
 開発はコンテナに入ったVSCode
とするのが良いらしい。

関連ファイル作成

init.sql: DB初期化テーブル設定
 → .devcontainer/database/init.sql

CREATE TABLE sample(
    id VARCHAR(10) PRIMARY KEY,
    info VARCHAR(50)
)

今回はサンプル用。
ここに初期化時点で持っておきたいテーブルを記載する。

main.py: fastAPIエントリー
 → app/main.py

from fastapi import FastAPI


app = FastAPI()

fastAPIを使うため書いてる。使わなかったら不要。
ここにFastAPIのメソッドを書いていくと、URLアクセスでそのメソッドに応じた内容を返せる。

ビルド / 確認

ビルドは docker comopse build もしくはdevcontianerリビルドで可能。

docker compose buildした場合以下のようにビルドできる。
appコンテナにマウントしているため、mysqlコンテナのビルドはできない
mysqlコンテナのビルドは、devcontainerリビルドかホスト側でdocker composeする。

今回はappコンテナをポート8000で開いているため、
localhost:8000にアクセスすれば何かしら開く。
※ fastAPIメソッドの (“/”) がないためエラーメッセージが返る。

MySQLテーブルができたか確認。

MySQL workbenchを使う。
https://www.mysql.com/jp/products/workbench

⊕マーク押して追加。
username, passwordをdocker-compose.ymlに書いた内容に合わせて記載
※ 今回は user, user_password

TestConnectionでOKが出るか確認

OKで作成し、開くとテーブルがあることが分かる。

SQL打ったり、DB状態を見たりできるためMySQL使う分には便利。
(ちょっと操作しにくいけど…コマンドよりはこっちの方が好き)

ここまでできれば開発を開始できる。
やったね!

まとめ

え?これバックエンド見たこともない初心者にやらせるんですか??
フロントに帰りたい。

まぁ~~サンプルがなくて大変困った。
だってどのパラメータがどこに絡んでて中どうなっててとか分かんないんだもん!!!

最初ClaudeCodeに作らせて必要なファイルが何か理解して(当然動かし方分からないから動かない)
一つ一つ調べて理解してってやるの大変だった…

”簡単な”システムなのはコードの方であって、環境構築は難しいど。
車で10分の目的地に連れてって!(車は自作してね)って言われてる気分。
簡単なアプリ作ってね!(Unityとかフレームワークは自作してね)って言われてる気分。
お願いだから環境は知ってる人が作ってくれ~~~初学でやるの辛すぎるよ…

これインフラだろふざけんなって調べたら、環境構築もバックエンドの仕事らしい。
バックエンドの手広すぎだろ。

バックエンドエンジニアが見ればツッコミどころしかないだろうけど、
何も知らん!!!な人の足掛かりになればいいな。
(その前にそんな仕事振られたら逃げてくれって感じ)

バックエンドたのしいね!!!!(白目)

\ 最新情報をチェック /

コメント

PAGE TOP
タイトルとURLをコピーしました