YusukeKatoブログ

シェル芸オンラインジャッジ:システム全体をDockerコンテナ内で動かす

記事投稿日: 2026年2月1日(日)

最終更新日: 2026年2月1日(日)

目次

はじめに

シェル芸オンラインジャッジというウェブサイトを開発しています。 今回はフロントエンドとバックエンドをDockerコンテナ内で動かすように変更しました。 nginxもコンテナ内で動かしています。

シェル芸オンラインジャッジ

修正方法について

シェル芸オンラインジャッジのソースコードを丸ごとGeminiに渡して、 具体的に修正方法を聞いて修正しました。 今回はDockerfileやdocker-compose.yml、nginxのconfファイルなどについて教えてもらいました。

シェル芸オンラインジャッジのシステム構成図

Nano Banana Proに生成してもらった現状のシステム構成図です。

システム構成

Dockerfile

Frontend

下記がフロントエンドのDockerfileです。 主にフロントエンド側のビルドとnginxの設定・起動を行っています。 問題データのファイルもコンテナ内にコピーしています。

# --- Build Stage ---
FROM node:22 AS builder

WORKDIR /app

COPY frontend/package.json frontend/yarn.lock* ./
RUN yarn install --frozen-lockfile

COPY frontend/ ./
RUN yarn build

# --- Serve Stage ---
FROM nginx:alpine

# ビルド成果物をNginxの公開ディレクトリにコピー
COPY --from=builder /app/build /usr/share/nginx/html

# 問題データを公開ディレクトリにコピー
COPY problems/ /usr/share/nginx/html/

# Nginx設定ファイルをコピー
COPY frontend/nginx/default.conf /etc/nginx/conf.d/default.conf

EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]

nginxの設定ファイルはあらかじめ用意しておいてコンテナ内へコピーします。 一応80番ポートの設定を書いていますが、実際は443ポートだけ受け付けます。

server {
    listen 80;
    server_name shellgei-online-judge.com localhost;

    # HTTP -> HTTPS リダイレクト
    # location / {
    #     return 301 https://$host$request_uri;
    # }
}

server {
    listen 443 ssl;
    server_name shellgei-online-judge.com localhost;

    # SSL証明書の設定
    ssl_certificate /etc/letsencrypt/live/shellgei-online-judge.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/shellgei-online-judge.com/privkey.pem;
    ssl_session_cache shared:SSL:1m;
    ssl_session_timeout  10m;
    ssl_prefer_server_ciphers on;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

    root /usr/share/nginx/html;
    index index.html;

    # Docker内部のDNSサーバーを指定
    resolver 127.0.0.11 valid=30s;

    # フロントエンド (React)
    location / {
        try_files $uri $uri/ /index.html;
    }

    # バックエンド (FastAPI) へのプロキシ
    location /api {
        proxy_pass http://backend:8000;
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

Backend

下記がバックエンドのDockerfileです。 主にFastAPIの環境の作成を行っています。 こちらでも問題データのファイルをコンテナ内にコピーしています。

unixtime.txtやshellgei_id.txtというサーバ側のデータ管理用ファイルも作成しています(ここで作成していますが最終的にはdocker-compose.ymlのほうでホストからマウントしています)。 データ管理に関してはいずれデータベースをコンテナ内で動かす方針に移行したいです。

FROM python:3.12-slim

WORKDIR /app

# Poetryのインストール
RUN pip install poetry

# 依存関係ファイルのコピー (キャッシュ効率化のため先にコピー)
COPY pyproject.toml poetry.lock* ./

# 仮想環境を作成せずにシステムにインストール
RUN poetry config virtualenvs.create false \
    && poetry install --no-interaction --no-ansi --no-root

# ソースコードのコピー
COPY backend/ ./backend/

# 問題データのコピー
COPY problems/ ./backend/public/

# 作業ディレクトリをbackendに変更
WORKDIR /app/backend

RUN echo "0.0" > unixtime.txt && chmod 666 unixtime.txt \
    && echo "0" > shellgei_id.txt && chmod 666 shellgei_id.txt

# uvicornサーバーの起動
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]

docker-compose.yml

下記がシステム全体を起動するdocker-compose.ymlです。 バックエンドとフロントエンドのコンテナをそれぞれ起動しています。 バックエンド側ではコンテナ内からコンテナを操作するためにホスト側のDockerソケットをマウントしています。

フロントエンド側ではnginxの設定ファイルをマウントして使用しています。 また、ホスト側で用意したSSL証明書をマウントしています。

version: '3.8'

services:
  backend:
    build:
      context: .
      dockerfile: backend/Dockerfile
    container_name: soj-backend
    restart: always
    volumes:
      # ホストのDockerソケットをマウント (DooD)
      - /var/run/docker.sock:/var/run/docker.sock
      # データファイル
      - ./backend_data/shellgei_id.txt:/app/backend/shellgei_id.txt
      - ./backend_data/unixtime.txt:/app/backend/unixtime.txt
    environment:
      - TZ=Asia/Tokyo

  frontend:
    build:
      context: .
      dockerfile: frontend/Dockerfile
    container_name: soj-frontend
    restart: always
    ports:
      - "80:80"    # HTTP
      - "443:443"  # HTTPS
    volumes:
      - ./frontend/nginx/default.conf:/etc/nginx/conf.d/default.conf
      # ホスト側のSSL証明書のマウント
      - /etc/letsencrypt:/etc/letsencrypt:ro
    depends_on:
      - backend

バックエンドのデータ管理ファイル

実行するシェル芸の時刻管理やID付けのために使用するデータファイルを作成するbashファイルを用意しました。 運用としてスマートではないので、今後データベースを使用するなどで対応したいです。

mkdir -p backend_data
echo "0.0" > backend_data/unixtime.txt
echo "0" > backend_data/shellgei_id.txt
chmod 666 backend_data/unixtime.txt
chmod 666 backend_data/shellgei_id.txt

起動方法

シェル芸オンラインジャッジのリポジトリをダウンロードしてから下記コマンドを実行することで起動できます。 -dオプションを付けてバックグランドで実行しています。

cd /path/to/ShellgeiOnlineJudge/
bash create_backend_data.bash
docker compose up -d --build

以前より簡単に起動できるようになりました。 また、nginxの設定をしなくてよくなったことなど、ホスト側での準備が少なく済むようになって良かったです。

おわりに

今回はシェル芸オンラインジャッジのシステムをDockerコンテナ内で動かせるように変更を加えました。 Geminiに聞いたら何でも教えてくれるのでとても助かります(たまに解決できないこともありましたが仕方なしです)。 運用が少し楽になって良かったです。 この調子でどんどん改善できたらと思います。 それでは、また。

お知らせ

過去のお知らせ

シリーズ記事一覧

各シリーズの記事を下記にまとめてあります。

我が家のインコ「れもん&ぽぽ&ぐぐ&さん」の日記 我が家のインコ「れもん&ぽぽ&ぐぐ&さん」の日記24 : 2026年もよろしくお願いします 我が家のインコ「れもん&ぽぽ&ぐぐ&さん」の日記23 : 2025年の我が家のインコ 我が家のインコ「れもん&ぽぽ&ぐぐ&さん」の日記22 : 我が家にさんちゃんがやってきた 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記21 : 2ヶ月ぶりのインコ日記 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記20 : 久しぶりのインコ日記 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記19 : 鳥フェス浅草2025に行ってきました! 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記18 : 2024年に感謝&2025年もよろしくお願いします 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記17 : れもんもぐぐも平和な日々を送っています 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記16 : れもんが本に掲載されたり、愛鳥祭に行ったりなど 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記15 : 落花生、新聞紙、れもんとぐぐの邂逅 インコの飼い方と注意点(2024年版) 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記14 : 大人に近づくれもんと遊ぶ余裕が出てきたぐぐ 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記13 : 生後4ヶ月を迎えたれもんと我が家に慣れてきたぐぐ 我が家のインコ「れもん&ぽぽ&ぐぐ」の日記12 : ぽぽが亡くなりました、そしてぐぐがやってきました インコ仲間「れもん&ぽぽ」の日記11 : ズグロシロハラインコのぽぽ、我が家へ セキセイインコ「れもん」の日記10 : 生後100日のれもん、ついに喋る セキセイインコ「れもん」の日記9 : 換羽期のれもん セキセイインコ「れもん」の日記8 : 病院へ挑戦! セキセイインコ「れもん」の日記7 : 外出用ゲージにチャレンジ! セキセイインコ「れもん」の日記6 : 新宿ことり博に行ってきました! セキセイインコ「れもん」の日記5 : 3週間経って順調に成長している模様 セキセイインコ「れもん」の日記4 : 鳥フェス千葉2024に行ってきました! セキセイインコ「れもん」の日記3 : 2週間経って我が家にも慣れてきた模様 セキセイインコ「れもん」の日記:第2回 セキセイインコの「れもん」が我が家にやってきました!
AlpacaHackで始めるCTF入門 AlpacaHackで始めるCTF入門7:AlpacaHack Round 5 - XorshiftStreamに挑戦 AlpacaHackで始めるCTF入門6:AlpacaHack Round 4 - Simple Flag Checkerに挑戦 AlpacaHackで始めるCTF入門5:AlpacaHack Round 3 - qrimeに挑戦 AlpacaHackで始めるCTF入門4:AlpacaHack Round 2 - Simple Loginに挑戦 AlpacaHackで始めるCTF入門3:初めてのCTFに参加 AlpacaHackで始めるCTF入門2:DreamhackでCTF入門 AlpacaHackで始めるCTF入門1:初めてのCTF
シェル芸 シェル芸オンラインジャッジ:シェル芸実行用コンテナを常に待機させておく シェル芸オンラインジャッジ:システム全体をDockerコンテナ内で動かす Dockerコンテナのリソースの制限を設定【シェル芸オンラインジャッジ】 Google AI Studioで自作のウェブサイトの改善点を教えてもらった シェル芸オンラインジャッジの紹介 第66回シェル芸勉強会メモ 第64回シェル芸勉強会メモ ChatGPTでシェル芸勉強会の問題が解きたい シェル芸botで遊びたい(Bash)
シェル芸オンラインジャッジ シェル芸オンラインジャッジ:シェル芸実行用コンテナを常に待機させておく シェル芸オンラインジャッジ:システム全体をDockerコンテナ内で動かす Dockerコンテナのリソースの制限を設定【シェル芸オンラインジャッジ】 Google AI Studioで自作のウェブサイトの改善点を教えてもらった シェル芸オンラインジャッジの紹介
ROS 2 ROS 2シェル芸:URDFを置換してRVizで表示する 改訂新版『ROS 2ではじめよう』を読了&メモ書き ROS 2 Jazzy公式チュートリアル02: ROS 2のノードとトピックについて ROS 2 Jazzy公式チュートリアル01: ROS 2 Jazzyのインストールから動作確認まで ROS 2 Humble 公式チュートリアル 02: ROS 2 のノード関係のコマンド ROS 2 Humble 公式チュートリアル 01: 環境構築から turtlesim まで
VRChat 【VRChat】UnityとBlenderでアバターのパーツを作る VRChatのワールド「森の中のログハウス」をUnityで作成してアップロードするまで VRChatでワールドをパブリック(コミュニティーラボ)へアップロードする VRChatのアバターにGoGo Locoを導入して座ったり寝っ転がったりする VRChatのワールドでオブジェクトと変数の同期を行う VRChatのスマホアプリ(Android)用にアバターをアップロードする Modular Avatarを使ってVRChatのアバターのオブジェクトをワールドに固定する VRChatのアバターのオブジェクトにアニメーションを設定する 【VRChat】Modular Avatarを使ってアバターのオブジェクトの表示と非表示を切り替える 公開されているVRMモデルをVRChatで動かす
技術ネタ WSL2で開いたVisual Studio Codeで日本語入力が不安定になる問題の解決 TouchDesignerを使って曲の音声に合わせた動画を作成してみる Sonic Piで作曲してみる Visual Studio CodeでGitHub Copilot使用時にGitHubにサインインできない問題を解決 Unityでブラウザゲーム(Webアプリ)を作成してレンタルサーバで動かす方法 echoコマンドで"-n"をそのまま出力したい Ubuntu 24.04 LTSにおいてノートPCを電源に接続していない状態だと画面が暗くなる問題の解決方法 HTMLのvideoタグで貼った動画のサムネイルがSafariで表示されない問題を解決 WSL2上のUbuntuのVimで矩形選択するための設定 HTMLとCSSで画像のスライドショーを作成 GitHub Actionsでサーバ上のブログを自動更新 Windows と Ubuntu のデュアルブート
ELDEN RING BLOG ELDEN RING BLOG 10: DLCをクリアしました! ELDEN RING BLOG 9: DLCに挑戦した ELDEN RING BLOG 8: 最後のボスを倒した ELDEN RING BLOG 7: ラニのストーリーを進めた ELDEN RING BLOG 6: 四体目と五体目の大ボスを倒した ELDEN RING BLOG 5: 三体目の大ボスを倒した ELDEN RING BLOG 4: 二体目の大ボスを倒した ELDEN RING BLOG 3: 一体目の大ボスを倒した ELDEN RING BLOG 2: レベル上げと武器強化の旅 ELDEN RING BLOG 1: 今更ながら始める初見ELDEN RING冒険日記
ELDEN RING NIGHTREIGN BLOG ELDEN RING NIGHTREIGN BLOG 3: 喰らいつく顎を倒した ELDEN RING NIGHTREIGN BLOG 2: 最初の標的を倒した ELDEN RING NIGHTREIGN BLOG 1: マルチプレイ初戦
ポケポケブログ ポケポケブログ4:「超克の光」環境でもベトベトンデッキはまだ強い ポケポケブログ3:「時空の激闘」環境でもベトベトンデッキを使いたい ポケポケブログ2:「幻のいる島」環境でもベトベトンデッキが強いです ポケポケブログ1:ベトベトン+マタドガスデッキをおすすめする
読書日記 読書日記:山崎ナオコーラ『人のセックスを笑うな』(河出文庫) 読書日記:プジェ日記を読みました! 読書日記『本を読んだことがない32歳がはじめて本を読む』【ネタバレあり】 読書日記:近畿地方のある場所について【ネタバレあり】 改訂新版『ROS 2ではじめよう』を読了&メモ書き
映画日記 映画日記『ラストマイル』感想【ネタバレあり】
料理日記 料理日記:オムライス 料理日記:豆腐春巻き 料理日記:鶏肉とナスのほりにし炒め 料理日記:卵焼き 料理日記:鶏肉の照り焼き&小松菜のおひたし 料理日記:納豆蕎麦 料理日記:ローストビーフ 料理日記:バターガーリックチキン
その他 JR東日本公式のお忘れ物チャットにお世話になった話 『Omega Crafter』プレイ日記: チュートリアル編 自作ブログ大改造計画 HTML数式表示テスト 自作ブログ開始

YusukeKatoブログの著作物はCC BY-NC-ND 4.0で公開されています。
YusukeKatoブログのソフトウェアはApache License 2.0で公開されています。
About License: GitHub - YusukeKatoBlog/LICENSE
© 2023 YusukeKato All Rights Reserved.