1. Ubuntu에서 Django Docker 이미지 만들기

작업 디렉토리:

cd ~/jw_project/testpro

1-1. Dockerfile 작성

대략 이런 형태로 구성:

FROM python:3.12-slim

WORKDIR /app

# 시스템 패키지 (mysqlclient 빌드용)
RUN apt-get update && apt-get install -y \
    build-essential \
    default-libmysqlclient-dev \
    pkg-config \
    && rm -rf /var/lib/apt/lists/*

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

COPY . .

ENV PYTHONUNBUFFERED=1

EXPOSE 8000

CMD ["gunicorn", "mysite.wsgi:application", "--bind", "0.0.0.0:8000"]

1-2. 빌드 & 실행

# 이미지 빌드
sudo docker build -t jw_project:latest .

# 로컬 테스트 실행
sudo docker run --rm -p 8000:8000 --env-file .env jw_project:latest
  • gunicorn 이 없어서 한 번 에러 났고 → requirements.txt에 추가 후 다시 빌드
  • mysqlclient 빌드 시 pkg-config not found 에러 → pkg-config, default-libmysqlclient-dev 설치로 해결

curl http://127.0.0.1:8000 에서 HTML이 떨어지면, 컨테이너 안에서 Django + gunicorn 이 정상 동작하는 상태.


2. 이미지 파일로 뽑아서 Debian으로 옮기기

2-1. Docker 이미지를 tar로 저장

Ubuntu에서:

sudo docker save -o jw_project.tar jw_project:latest
ls -lh jw_project.tar    # 용량 확인 (약 800MB 정도)

2-2. SSH 키로 Debian에 scp

Ubuntu → Debian 간 접속을 위해 별도 SSH 키 생성:

ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519_debian -C "ubuntu-to-debian"
cat ~/.ssh/id_ed25519_debian.pub

이 공개키를 Debian 서버의 ~/.ssh/authorized_keys 에 등록 후, Ubuntu에서 테스트:

ssh -i ~/.ssh/id_ed25519_debian admin@13.209.77.43

정상 접속되면, tar와 .env 전송:

scp -i ~/.ssh/id_ed25519_debian jw_project.tar admin@13.209.77.43:~/
scp -i ~/.ssh/id_ed25519_debian .env admin@13.209.77.43:~/jw_project.env

2-3. Debian에서 Docker 설치 & 이미지 로드

Debian:

sudo apt update
sudo apt install docker.io -y
sudo systemctl enable --now docker

sudo docker load -i jw_project.tar
sudo docker images | grep jw_project

컨테이너 실행:

sudo docker run -d -p 8000:8000 --env-file jw_project.env --name jw_project jw_project:latest
sudo docker ps

AWS 보안 그룹에서 TCP 8000 포트 인바운드 허용 후, http://13.209.77.43:8000 접속 → Django 기본 화면 확인.


3. DB를 Docker 컨테이너로 분리 (MariaDB 컨테이너 도입)

처음에는 Debian의 앱 컨테이너에서 Ubuntu의 MariaDB로 직접 붙어보려 했지만, 포트/보안그룹/권한 문제로 접속이 꼬여서 최종적으로는:

앱 컨테이너(jw_project) + DB 컨테이너(jw-mysql) 구조로 정리

3-1. Ubuntu에서 기존 DB 덤프 떠오기

Ubuntu (MariaDB):

sudo mysqldump quiz_db_team > quiz_db_team.sql
ls -lh quiz_db_team.sql

덤프 파일을 Debian으로 전송:

scp -i ~/.ssh/id_ed25519_debian quiz_db_team.sql admin@13.209.77.43:~/

3-2. Debian에서 Docker 네트워크 + MariaDB 컨테이너

Debian:

# 네트워크 생성
sudo docker network create jw-net

# MariaDB 컨테이너
sudo docker run -d \
  --name jw-mysql \
  --network jw-net \
  -e MYSQL_ROOT_PASSWORD=Root_Quiz_2025! \
  -e MYSQL_DATABASE=quiz_db_team \
  -e MYSQL_USER=quiz \
  -e MYSQL_PASSWORD=Strong_Quiz_2025! \
  mariadb:11

덤프 파일 import:

sudo docker cp quiz_db_team.sql jw-mysql:/tmp/quiz_db_team.sql

sudo docker exec -i jw-mysql \
  mariadb -u root -pRoot_Quiz_2025! quiz_db_team < quiz_db_team.sql

3-3. Django에서 DB 컨테이너를 바라보도록 설정

Debian의 jw_project.env:

DATABASE_NAME=quiz_db_team
DATABASE_USER=quiz
DATABASE_PASSWORD=Strong_Quiz_2025!
DATABASE_HOST=jw-mysql
DATABASE_PORT=3306

앱 컨테이너를 같은 네트워크에 붙여서 재실행:

sudo docker stop jw_project
sudo docker rm jw_project

sudo docker run -d \
  --network jw-net \
  -p 8000:8000 \
  --env-file jw_project.env \
  --name jw_project \
  jw_project:latest

이제 앱 컨테이너는 컨테이너 이름 jw-mysql 로 DB에 접속한다. (이게 “도커 네트워크 안에서의 서비스 이름 기반 통신”)


4. CSS 깨짐 / 정적 파일 404 문제 해결

4-1. 증상

  • 페이지는 잘 뜨는데 CSS가 하나도 안 먹는다
  • DevTools Network 탭에서:

    • /static/quiz_app/styles.css → Status 404
    • 그리고 MIME 타입 경고:

      Refused to apply style from … because its MIME type (‘text/html’) is not a supported stylesheet MIME type…

= CSS 파일 URL로 요청했는데, HTML(404 페이지) 가 돌아오고 있다는 뜻.

4-2. 정적 파일 구조 확인

컨테이너 안:

sudo docker exec -it jw_project ls /app/static/quiz_app
# example_html.css, images, mp.css, munje_..., styles.css

그리고 collectstatic 이후:

sudo docker exec -it jw_project ls /app/staticfiles/quiz_app
# example_html.css, images, mp.css, munje_..., styles.css

파일 자체는 다 있다 → 문제는 “URL → 파일 매핑”.

4-3. settings.py 정리

mysite/settings.py:

STATIC_URL = "/static/"
STATICFILES_DIRS = [BASE_DIR / "static"]
STATIC_ROOT = BASE_DIR / "staticfiles"

그리고 컨테이너 안에서 한 번 collectstatic:

sudo docker exec -it jw_project python manage.py collectstatic --noinput

4-4. urls.py 에서 static 매핑 추가

핵심은 이 부분.

mysite/urls.py:

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path("admin/", admin.site.urls),
    path("", include("quiz_app.urls")),
    path("accounts/", include("accounts.urls")),
    path("accounts/", include("allauth.urls")),
    path("history/", include("history.urls")),
    path("community/", include("community.urls")),
]

if settings.DEBUG:
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)

처음에는 MEDIA만 매핑되어 있고 STATIC 매핑이 빠져 있어서, /static/... 요청이 전부 “일반 URL”로 흘러 들어가 404 HTML을 반환하고 있던 상태였다.

static 매핑 추가 후 컨테이너 재시작:

sudo docker restart jw_project

/static/quiz_app/styles.css 가 200 + text/css 로 떨어지고, 페이지 스타일 정상 복구


5. 오늘 배운 DevOps 포인트 정리

  1. 도커 이미지를 다른 서버로 옮기는 기본 패턴

    • docker builddocker savescpdocker loaddocker run
    • 애플리케이션 전체를 “이미지” 단위로 옮길 수 있어서 서버 갈아타는 부담이 줄어든다.
  2. 앱 컨테이너 / DB 컨테이너 분리

    • Django와 DB를 분리해서 각각 컨테이너로 띄우고
    • 도커 네트워크로 묶으면

      • 이사(서버 이전)
      • 스케일링(앱 컨테이너만 여러 개 늘리기) 가 훨씬 쉬워진다.
  3. 도커 내부에서의 “127.0.0.1” 의미

    • 컨테이너 안의 127.0.0.1 은 “호스트 서버”가 아니라 컨테이너 자기 자신
    • DB가 다른 서버/컨테이너에 있으면

      • 서버 IP or 컨테이너 이름을 DATABASE_HOST 로 써야 한다.
  4. 정적 파일은 코드 위치와 서비스 위치가 다르다

    • 코드: BASE_DIR/static/...
    • 서비스: STATIC_ROOT (예: BASE_DIR/staticfiles)
    • 배포 모드에서는 collectstatic 으로 모아놓고, urls.py + static() or Nginx로 /static/ 을 이 위치로 매핑해야 한다.
  5. 문제가 생겼을 때 보는 순서

    • 컨테이너 동작 여부: docker ps, docker logs
    • 내부 통신 확인: 컨테이너 안에서 curl, DB 클라이언트로 직접 접근
    • 정적 파일 깨짐:

      1. 파일이 실제로 있는지 (static / staticfiles)
      2. STATIC_URL, STATIC_ROOT, STATICFILES_DIRS
      3. urls.pystatic() 매핑 또는 Nginx 설정

마무리

오늘 작업 결과:

  • Ubuntu → Debian으로 Django 프로젝트 완전 이사 완료
  • 앱 컨테이너 + DB 컨테이너 구조로 정리
  • CSS 깨짐까지 해결해서 “도커 기반 실전 배포 1세트” 경험 완료

이제 이 기반 위에:

  • Nginx 리버스 프록시
  • HTTPS (Let’s Encrypt / Cloudflare)
  • Docker Compose / CI/CD

같은 것들을 하나씩 올려보면 될 것 같다 그리고 빌드 시간 단축하는 법도 연구해 보면 좋을 듯 하다