본문으로 바로가기
반응형


기존에는 로컬에서 그냥 작업을 했는데 실제 배포등 여러 이슈들이 발생하여 도커를 사용하기로 했다.

간단하게 하나의 이미지를 띄울때는 기본 docker를 사용하면 되는데

여러개의 이미지를 띄우고 각 이미지를 연동시키려면 docker-compose를 사용하여

하나로 묶어주는 작업이 필요하다.

먼저 docker, docker-compose는 설치가 되어있다고 가정한다.

아래의 명령어로 nginx, python, redis, mongodb를 설치한다.


docker pull nginx:latest

docker pull mongo:later

docker pull redis:latest

docker pull python:3.6.5


여기서 파이썬 3.6.5버전을 사용한 이유가 궁금할수도 있겠다.

이전 포스팅을 보면 알겠지만, 추후 celery&redis를 플라스크와 연동해주는 작업을 진행할텐데

celery가 파이썬 3.7버전을 지원하지 않는다.

다음으로 test라는 폴더를 만든다. (폴더명은 마음대로 해도 된다)

그리고 docker-compose.yml 파일을 생성한다.

다음으로 아래의 내용을 채워넣는다.


version: '3.7'

services:
nginx:
build:
context: .
dockerfile: docker/nginx/dockerfile
container_name: nginx
hostname: nginx-dev
ports:
- '80:80'
networks:
- backend

mongodb:
build:
context: .
dockerfile: docker/mongodb/dockerfile
container_name: mongodb
hostname: mongodb-dev
ports:
- '27017:27017'
networks:
- backend

web_project:
build:
context: .
dockerfile: docker/web/dockerfile
container_name: web_project
hostname: web_project_dev
ports:
- '5000:5000'
networks:
- backend
tty: true
depends_on:
- mongodb
links:
- mongodb

redis:
image: redis:latest
container_name: redis
hostname: redis_dev

networks:
backend:
driver: 'bridge'

간단하게 설명을 해보자면 최상단에 있는 version을 3.7로 준것은 현재 docker의 버전에 따라 결정되는 것이므로

본인의 docker버전에 따라 설정해주면 된다.

그리고 services내부에 여러개의 이미지들을 각 선언해주는데 이렇게 선언된 이미지들이 하나로 묶여서 돌아가는 형태이다.

나는 docker-compose로 각 파일들을 묶어줄 때 직접 이미지를 지정하지 않고 각 이미지 별 dockerfile을 참조하는 형태로 만들었고

내부에서 실행되어야 할 명령어등 세부 옵션들은 각 dockerfile에 정의해놨다.

(사실 이건 개인의 취향차이인 것 같다)

처음 도커를 접하는 사람은 위 내용이 무엇인지 잘 모를텐데(나도 그랬음) docs를 참조하는게 내가 설명하는것보다 빠를 것 같으므로

자세한 설명은 생략한다.

참고로 말하자면, 위처럼 dockerfile을 따로 빼놓는 형태로 사용할때는 build하위에 dockerfile의 경로를 써줘야하는데

context도 무조건 써줘야한다. 아니면 에러가 발생한다.

그리고 web_project, python flask 웹 서버는 mongodb가 실행된 이후에 실행이 되어야 하므로 depends_on에 mongodb를 걸어줬고

연결시키기 위하여 links 또한 mongodb에 걸어줬다.


다음으로 docker라는 폴더를 하나 생성하고 내부에 nginx, mongodb, web이라는 4개의 폴더를 생성한다.

먼저 mongodb폴더로 들어가여 dockerfile을 생성하고 아래의 내용을 채워넣는다.


FROM mongo:latest

EXPOSE 27017
CMD mongod --bind_ip_all

FROM부분에 기존에 설치한 몽고 이미지를 넣어놨고 EXPOSE를 통해 포트를 지정해줬다.

그리고 mongod로 몽고를 실행하되, --bind_ip_all옵션을 통해 모든 아이피에 대한 허용을 정의했다.

이제 nginx폴더에 dockerfile을 생성하고 아래의 내용을 채워넣는다.


FROM nginx:latest

이쪽은 따로 실행할 명령어나 설정해줄 옵션이 없으므로 기존에 다운받은 nginx이미지를 사용하도록 명시했다.

다음으로 web폴더안에 dockerfile을 생성하고 아래의 내용을 채워넣는다.


FROM python:3.6.5

COPY . ./home
WORKDIR home
RUN pip install -r app/requirements.txt
CMD python app.py run

마찬가지로 파이썬 3.6.5을 사용한다고 명시해놨고,

현재 폴더의 전부를 /home폴더로 복사하기 위해 COPY를 사용했다.

또한 작업 디렉토리를 home으로 이동했다. (리눅스의 cd /home과 같음)

그리고 파이썬에서 필요한 라이브러리를 설치하기 위해 RUN으로 명령어를 실행시켰고

manage.py를 실행시킨 모습이다.

여기까지 작성한 이후 docker-compose build를 통해 빌드 이후

docker-compose up을 입력하면 4개의 서비스가 돌기 시작한다.


여기까지 간단하게 도커 설정부분이었고 이제 파이썬 코드쪽을 살펴봐야 한다.

먼저 app폴더를 하나 생성하고 내부에 manage.py파일과 requirements.txt파일을 생성한다.

그리고 requirements.txt에 아래의 내용을 넣는다.


flask
celery[redis]
flask-restplus
flask-mongoengine
pipenv
flask-testing
flask-script
marshmallow

(자신이 사용할 라이브러리를 채워넣으면 된다)


다음으로 app.py를 아래의 내용으로 채워넣는다.


from flask import Flask


app = Flask(__name__)


if __name__ == '__main__':
app.run(host='0.0.0.0', debug=True)

(이또한 간단하게 테스트 용도로 작성한 것이므로 수정해도 된다)


여기서 이제 몽고디비를 연동시켜야 하는데 살짝 헤맸었다.

먼저 위쪽에서 몽고를 --bind_ip_all 옵션을 줘서 실행했으므로 모든 아이피에서 접근이 가능하다.

디비 세팅쪽을 살펴봐야 하는데, 기존에 로컬에서 돌릴때는 

host명을 localhost:27017로 줬었다.

하지만 우리는 docker-compose.yml에 python(flask 웹서버)부분에 mongodb를 link시켜줬으므로

host명을 mongodb:27017로 줘야한다.

위 내용때문에 한참 헤맸다.

이렇게까지 하면 flask-mongodb까지 연동이 완료된다.


다음으로 Nginx와 flask를 연동해줘야 한다.

보통 flask를 배포할때는 단독으로 배포하지 않고 웹 서버와 붙여서 배포한다.

나는 웹서버로 nginx를 선택했고 uwsgi를 통해 nginx <-> uwsgi <-> flask 의 통신 구조로 만들예정이다.

먼저 최상위 디렉토리에서 config폴더를 생성하고 아래 3개의 파일을 생성한다.


nginx.conf

uwsgi_params

uwsgi.ini


nginx.conf를 아래의 내용으로 채워넣는다.


server {
listen 80;
server_name localhost;

location / {
proxy_set_header Host $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;
proxy_set_header Host $http_host;

proxy_pass http://web_project:5000;
}
}

/ url 80포트로 들어오면 어떻게 처리할지에 대해 정의하는 부분이다.

가장 아래쪽에 proxy_pass 부분에 http://docker-compose.yml에 정의한 웹서버이름:포트명 으로 적으면 된다.

우리는 web_project라고 이름을 줬고 5000포트이므로 위처럼 작성했다.

다음으로 uwsgi_params에 아래 내용을 채워넣는다.


uwsgi_param  QUERY_STRING       $query_string;

uwsgi_param  REQUEST_METHOD     $request_method;

uwsgi_param  CONTENT_TYPE       $content_type;

uwsgi_param  CONTENT_LENGTH     $content_length;


uwsgi_param  REQUEST_URI        $request_uri;

uwsgi_param  PATH_INFO          $document_uri;

uwsgi_param  DOCUMENT_ROOT      $document_root;

uwsgi_param  SERVER_PROTOCOL    $server_protocol;

uwsgi_param  REQUEST_SCHEME     $scheme;

uwsgi_param  HTTPS              $https if_not_empty;


uwsgi_param  REMOTE_ADDR        $remote_addr;

uwsgi_param  REMOTE_PORT        $remote_port;

uwsgi_param  SERVER_PORT        $server_port;

uwsgi_param  SERVER_NAME        $server_name;


마지막으로 uwsgi.ini에 아래 내용을 채워넣는다.


[uwsgi]
chdir = /home/app
socket = 127.0.0.1:5000
chmod-socker = 777
logto = /home/web.log
master = true
process = 2
daemonize = /home/uwsgi.log

chdir로 실행시킬 디렉토리를 지정해줬다.

또한 logto를 통해 /home/web.log에 로그를 남기도록 했다.

추가적으로 잠시 헤맸던 부분이 있는데 가장 아래쪽에 있는 daemonize부분이다.

이 부분을 작성해주지 않으면 docker-compose build로 빌드할 때 uwsgi이후에 넘어가지 않는다.

그래서 백그라운드로 돌리는 방법을 찾아보던 중 daemonize를 설정해주면 된다고 하여 작성했다.


다음으로 /docker/nginx/dockerfile의 내용을 아래와 같이 수정한다.


FROM nginx:latest

COPY . ./home
WORKDIR home
RUN apt-get update && apt-get install -y uwsgi && apt-get install -y uwsgi-plugin-python3 && apt-get install -y vim
RUN rm /etc/nginx/conf.d/default.conf
COPY ./config/nginx.conf /etc/nginx/conf.d/default.conf
RUN ls
RUN uwsgi --ini config/uwsgi.ini

우리가 위에서 설정한 nginx 설정파일을 복사하고 최종적으로 uwsgi를 실행시키는 모습이다.

또한 /docker/web/dockerfile의 내용도 아래와 같이 수정한다.

마지막으로 nginx와 web어플리케이션이 독립적으로 떠있으므로

docker-compose.yml로 들어가서 nginx부분에 links로 web_project를 연결해준다.


nginx:
build:
context: .
dockerfile: docker/nginx/dockerfile
container_name: nginx
hostname: nginx-dev
ports:
- '80:80'
networks:
- backend
links:
- web_project

build하고 up하면 80포트로도 접속할 수 있다.

반응형