WEB/DevOps

[Docker] Django + Gunicorn + Ngnix + MySQL 로 도커 세팅하기

김쿸후 2021. 7. 17. 22:17

1.  들어가기 전

도커 세팅은 언제 해야하는가. .많은 개발자들이 고민하는 문제일 것이다.

내가 몇 안되지만.. 주변에 자문을 구해본 결과 !! 대다수가 맨 처음에 다 세팅해두는게 편하다고 대답했다 

그래서 내가 지금 그러고 있는데 결과는 굉장히 만족!! 세팅에는 굉장히 삽질했지만 그래도 만족한다. 

서버 킬때 python run server 하던걸 그냥 docker-compose up 해주면 된다!

갱장히 편리~.~ 

 

본인 컴퓨터 : 맥 M1 칩

따라서 m1칩은 arm 베이스 이미지만 세팅이 되기때문에 몇개를 커스터마이징 해서 사용하였다. 

아래 자세히 설명할 예정 

 

2.  도커 세팅하기

2-1. 디렉토리 나누기

우선 나는 디렉토리를 나누어서 각각 도커 파일을 두었다. 

프로젝트 이름이 kuku 라고 하면

Kuku-backend

   - django

       - app

       - kuku <- 이거때문에 삽질 개많이했다..;; 프로젝트 이름이 여기에 들어감.,. 나도 왠지모름.. 

       - Dockerfile

       - requirements.txt

   - mysql

   - ngnix

       - Dockerfile

       - ngnix.conf

       - project.conf

   - docker-compose.yaml

 

 

2-2 Dockerfile 작성하기 

- Django Dockerfile

여기서 Expose 등은 docker-compose.yaml파일에서 해주기 때문에 여기서는 하지 않는다. 

(같은 이유로 CMD 명령어로 python runserver도 안한다.) 

# Dockerfile
# Django 최상위 루트에서 작성
FROM python:3.8
# 컨테이너 내에서 코드가 실행될 경로 설정
WORKDIR /app
# requirements.txt에 명시된 필요한 packages 설치
COPY ./django/requirements.txt requirements.txt
RUN pip install --upgrade pip
RUN pip install -r requirements.txt
# Project를 /usr/src/app으로 복사
COPY . .

- 예상 질문 ) 왜 그냥 도커허브에서 장고 이미지를 받지 않았나요?

      -  대답 ) 위에도 말했듯이 m1 칩이라 arm베이스로 만들어야되는데,

그게 없어서 그냥 requirements 에 장고를 넣고 직접 설치해줬습니다! 

#requirements.txt
Django==3.1.4
pymysql
Pillow
jsonify
requests
cryptography
gunicorn==20.0.4
djangorestframework
django-filter
asgiref
pytz
sqlparse
drf_yasg

 

- Ngnix Dockerfile

FROM nginx:1.15.8

RUN rm /etc/nginx/nginx.conf
COPY nginx.conf /etc/nginx/
RUN rm /etc/nginx/conf.d/default.conf
COPY project.conf /etc/nginx/conf.d/

- 엔지닉스는 평화롭게 도커 허브에서 공식 엔지닉스 가져왔당

 

2-3 ngnix 포트 연결하기

- 막간 설명 ) ngnix는 간단히 말해서 외부의 포트와 내부의 포트를 연결한다고 생각하면 된다. 

그냥 더 간단하게 말하면 포트끼리 이어주는 역할이다. 

 

- ngnix.conf

#ngnix.conf

#Define the user that will own and run the Nginx server
user  nginx;

# Define the number of worker processes; recommended value is the number of
# cores that are being used by your server
worker_processes  1;

# Define the location on the file system of the error log, plus the minimum
# severity to log messages for
error_log  /var/log/nginx/error.log warn;

# Define the file that will store the process ID of the main NGINX process
pid        /var/run/nginx.pid;


# events block defines the parameters that affect connection processing.
events {
    # Define the maximum number of simultaneous connections that can be opened by a worker process
    worker_connections  1024;
}


# http block defines the parameters for how NGINX should handle HTTP web traffic
http {
    # Include the file defining the list of file types that are supported by NGINX
    include       /etc/nginx/mime.types;

    # Define the default file type that is returned to the user
    default_type  text/html;

    # Define the format of log messages.
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    # Define the location of the log of access attempts to NGINX
    access_log  /var/log/nginx/access.log  main;

    # Define the parameters to optimize the delivery of static content
    sendfile        on;
    tcp_nopush     on;
    tcp_nodelay    on;

    # Define the timeout value for keep-alive connections with the client
    keepalive_timeout  65;

    # Define the usage of the gzip compression algorithm to reduce the amount of data to transmit
    #gzip  on;

    # Include additional parameters for virtual host(s)/server(s)
    include /etc/nginx/conf.d/*.conf;
}

 

- project.conf

server {

    listen 80;
    server_name localhost;

    location / {
        proxy_pass http://django_app:8000;

        # Do not change this
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    }

    location /static/ {
        alias /app/venti/static/;
    }
}

- 이게 완전 중요하다. 

- listen 80 : ngnix 서버가 80의 변화를 체크함

외부 포트 (ex : 8084) 가 ngnix로 정보를 주면 80으로 연결되어 변화를 감지한다. 

 

- location / proxy_pass http://django_app:8000;

80이 변하면 그걸 포트 8000으로 넘긴다. 

여기서 django_app은 내가 docker_compose.yaml에서 선언한 장고 이름!!!!!

이것도 삽질 개많이했다.. 이 글을 보시는 분은 제발 삽질하지 마시길 ㅋㅋㅋㅋ

 

- location /static/ 

 CSS 등 static file을 ngnix에 올려주는 애들이다. 

자신의 static 파일의 위치를 찾은 다음 alias 로 연결 해준다. 

이후 docker_compose.yaml 파일에 volumn에 올려야 한다.

 

2-4. 대망의 docker-compose.yaml파일 작성하기

version: '3.8'

services:
  nginx:
    container_name: nginx
    build: ./nginx
    ports:
      - "8084:80"
    volumes:
      - ./django/venti/nginx:/etc/nginx.conf.d
      - ./django/app:/app
      - ./django/venti/static:/static
    depends_on:
      - django_app

  mysql:
    image: mariadb:latest
    container_name: mysql
    expose:
      - "3306"
    environment:
      MYSQL_DATABASE: 디비이름
      MYSQL_USER: root
      MYSQL_PASSWORD: 디비비번
      MYSQL_ROOT_PASSWORD: 디비비번
    volumes:
      - ../db/data:/var/lib/mysql
      - ../db/data/conf.d:/etc/mysql/conf.d

  django_app:
    container_name: django_app
    build:
      context: .
      dockerfile: ./django/Dockerfile
    command: gunicorn kuku.wsgi:application --bind 0.0.0.0:8000
    expose:
      - "8000"
    depends_on:
      - mysql
    links:
      - mysql:mysql
    restart: always
    volumes:
      - ./django/app:/app
    env_file:
      - ./django/env.dev

networks:
  app-tier:
    driver: bridge

- version은 도커 버전이다. 

- services 안에 적힌 애들이 내가 도커에 올리고 싶은 애들이다. 

나는 ngnix mysql django 3개를 올렸다. 

여기서 django_app이 아까 위의 ngnix 프록시 설정을 해주었던 그 이름이다!

 

- ngnix

- ngnix 의 ports : 외부 포트, ngnix의 포트 

 : 다른 곳에서는 같은 포트 번호도 쓰던데 나는 자꾸 충돌나서 포트 번호를 바꿨다. (로컬에서 해서 그런가?

근데 포트 번호를 바꾸니 잘 된다. 

 

- ngnix 의 volumes

필요한 파일들이라고 생각하면 된다.

: 앞에 적힌게 현재 내 파일에서의 위치이고 : 뒤에 적힌게 도커가 올라갔을때 파일위치가 된다고 한다.  

static파일도 잊지말고 추가해주자 !

 

- ngnix 의 depends_on 

ngnix가 실행되면 장고 앱이 실행되어야 하므로 장고 앱을 넣어준다.

 

 

- django_app

- django 의 command : 

아까 내가 장고 도커파일을 선언할때 expose 를 도커 컴포즈 얌파일에서 한다고 했는데 그게 이 부분이다.

장고는 파이썬 기반이라 웹 서버가 이해하지 못하기때문에 WSGI 서버인 gunicorn 을 이용해서 ngnix 와 django를 연결해야된다. 

따라서 다음과 같이 bind 명령어를 사용하여 연결한다. 

    command: gunicorn kuku.wsgi:application --bind 0.0.0.0:8000 

이 명령어는 kuku(프로젝트 이름 : 아까 볼드 쳐둔 그거) 를 위스기 서버랑 연결해서 8000 번에 띄운다는 소리이다. 

 

- restart : always

핫 리로드라고도 불리는데 그냥 변경사항이 생겼을때마다 리스타트를 해라 이런거다

변경사항 생길때마다 리로드가 되는거 같기는 한데 나는 안되면 괜히 반영안된 탓해서 도커 끄고 다시 킨다 ㅋㅋㅋ 

잘못된 습관 

 

- links 

장고 안에 db와 연결할 때 mysql과 연결하는 것은 도커 파일의 mysql 과 연결한다는 뜻이다. 

이렇게 장고 안의 db와 연결하는 secret.json파일의 host 를 mysql로 해두면 연결이 된다. 

{
  "DB_SETTINGS": {
    "default" : {
      "ENGINE": "django.db.backends.mysql",
      "NAME": "디비이름",
      "USER": "root",
      "PASSWORD": "비번",
      "HOST": "mysql",
      "PORT": "3306"
    }
  }
}

 

- mysql

  mysql:
    image: mariadb:latest

mysql이라면서 웬 마리아디비? 라고 생각하는 사람이 있을 것이다. 

아까도 말했듯이 내가 m1칩인데 mysql디비는 arm 형식이 아니어서 mariadb이미지를 사용했다. 

mysql처럼 이용하면 된다. 

 

- mysql의 expose 

mysql을 띄울 포트 번호를 지정해주면 된다. 

 

이렇게 하면 도커 세팅이 끝난다. 

뭔가 빠진거 같긴한데..? 생각하면 채워놓겠다^^*

 

3. 도커 구동하기

이미지 빌드하기
docker-compose build

컨테이너 올리기
docker-compose up

빌드하고 올리기
docker-compose up --build

아까 나는 ngnix를 8084포트와 연결해놨으므로 

http://localhost:8084

로 접속하면 된다. 

 

- 끝 -


질문 있으시면 댓글 달아주시면 아는 선에서 대답해드릴게여