From 64404c27ed376260c6e98674a0e750adb8321a9e Mon Sep 17 00:00:00 2001
From: th-kim0823
Date: Sat, 25 Apr 2026 19:35:44 +0900
Subject: [PATCH] =?UTF-8?q?feat:=20Docker=20=ED=8C=A8=ED=82=A4=EC=A7=95=20?=
=?UTF-8?q?(=EB=A1=9C=EC=BB=AC=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20+=20?=
=?UTF-8?q?=ED=99=88=EC=84=9C=EB=B2=84=20=EB=B0=B0=ED=8F=AC)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- Dockerfile: python:3.12-slim 베이스, headless streamlit
- docker-compose.yml: ADMIN_TOKEN 환경변수, votes.db 영속 볼륨, participants.json read-only mount
- .dockerignore: 빌드 컨텍스트 최소화
테스트:
- docker compose build OK
- 컨테이너 실행 후 http://localhost:8501 200 OK 확인
Co-Authored-By: Claude Opus 4.7 (1M context)
---
.dockerignore | 14 ++++++++++++++
Dockerfile | 20 ++++++++++++++++++++
README.md | 33 +++++++++++++++++++++++----------
docker-compose.yml | 20 ++++++++++++++++++++
4 files changed, 77 insertions(+), 10 deletions(-)
create mode 100644 .dockerignore
create mode 100644 Dockerfile
create mode 100644 docker-compose.yml
diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..86e2991
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,14 @@
+.git
+.gitignore
+*.db
+*.sqlite
+*.pyc
+__pycache__/
+.venv/
+venv/
+.env
+README.md
+docker-compose.yml
+Dockerfile
+.dockerignore
+teams.md
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..7e765d1
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,20 @@
+FROM python:3.12-slim
+
+WORKDIR /app
+
+COPY requirements.txt ./
+RUN pip install --no-cache-dir -r requirements.txt
+
+COPY app.py assign_teams.py ./
+COPY participants.json ./
+
+EXPOSE 8501
+
+ENV VOTE_DB=/data/votes.db \
+ PARTICIPANTS=/app/participants.json
+
+CMD ["streamlit", "run", "app.py", \
+ "--server.address=0.0.0.0", \
+ "--server.port=8501", \
+ "--server.headless=true", \
+ "--browser.gatherUsageStats=false"]
diff --git a/README.md b/README.md
index 59e7ed2..4dcf293 100644
--- a/README.md
+++ b/README.md
@@ -8,22 +8,35 @@
2. `app.py` 실행 → 참가자가 본인 이름 선택 → 자동 본인 팀 매핑 → 다른 6팀에 3분야 투표
3. 어드민 페이지에서 분야별 1위와 2위 차이만 공개 (하위 표수는 expander 내부)
-## 실행
+## 실행 — Docker (권장)
```bash
-# 1. 의존성 설치
-pip install -r requirements.txt
-
-# 2. 팀 배정 (시드 고정 = 재현 가능)
+# 1. 팀 배정 (호스트에서 1회, participants.json 생성)
python3 assign_teams.py
-# → participants.json 저장됨
-# 3. 환경변수 (선택)
+# 2. 컨테이너 실행
export ADMIN_TOKEN="강한-토큰-아무거나"
-export VOTE_DB="votes.db"
-export PARTICIPANTS="participants.json"
+docker compose up -d --build
-# 4. 홈서버 실행 (외부 접속 허용)
+# 로그
+docker compose logs -f
+
+# 종료
+docker compose down
+
+# DB 영속 데이터까지 삭제
+docker compose down -v
+```
+
+- 투표 DB는 docker volume `vote-data`에 영속 → 컨테이너 재시작해도 유지
+- `participants.json`은 호스트→컨테이너 read-only mount → 재배정 시 호스트에서 변경하고 컨테이너만 재시작
+
+## 실행 — 로컬 (Docker 없이)
+
+```bash
+pip install -r requirements.txt
+python3 assign_teams.py
+export ADMIN_TOKEN="강한-토큰-아무거나"
streamlit run app.py --server.address 0.0.0.0 --server.port 8501
```
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..af9b6d0
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,20 @@
+services:
+ vote:
+ build: .
+ image: hackathon-vote:latest
+ container_name: hackathon-vote
+ ports:
+ - "${PORT:-8501}:8501"
+ environment:
+ ADMIN_TOKEN: ${ADMIN_TOKEN:-change-me}
+ VOTE_DB: /data/votes.db
+ PARTICIPANTS: /app/participants.json
+ volumes:
+ # 호스트의 participants.json 변경 즉시 반영 (재배정 시)
+ - ./participants.json:/app/participants.json:ro
+ # 투표 DB는 호스트에 영속 (컨테이너 재시작해도 유지)
+ - vote-data:/data
+ restart: unless-stopped
+
+volumes:
+ vote-data: