[Docker-Compose] Django + PostgreSQL
여러 개의 컨테이너로 이루어진 어플리케이션을 도커를 이용해서 배포하려 하면, 한 Dockerfile로 해결이 안 되는 경우가 많습니다.
그래서 컨테이너들을 한 번에 관리하기 위해 docker compose를 사용합니다.
이번에 테스트할 어플리케이션은 Django로 작성된 간단한 웹사이트입니다.
데이터베이스는 PostgreSQL이며 마찬가지로 Docker 이용해서 설치됩니다.
그런데 일반적으로 DB는 도커를 이용해서 설치하는 게 권장되지 않습니다. DB는 일반적으로 독립적으로 계속 운용이 되면서, 어떤 앱에서든 자유롭게 접속할 수 있고 항상 정상적으로 응답을 하고 데이터도 계속 저장을 한 상태로 있어야 합니다. 만약에 도커를 이용해서 배포를 하면, 앱이 업데이트될 때마다 데이터 베이스도 업데이트가 되어 버립니다. 물론 데이터를 따로 저장소에 저장하며 도커를 운용하는 방법이 있긴 한데. 요즘 클라우드 서비스를 이용하면 보통 DB용 서비스를 따로 제공해주기 때문에, DB 따로 설치하는 경우는 좀 드뭅니다.
아무튼, 이번에 보여드리고 싶던 예제는, 한 컨테이너가 다른 컨테이너에 종속되어 있을 때 어떻게 멀티 컨테이너 배포를 하는지에 대한 방법입니다.
이번 예제에서 고민해야 될 부분은 Django 같은 경우에는 디비가 연결이 된 상태에서 이제 시작이 되어야 한다는 것입니다.
그러면 뭐가 문제지라고 생각할 수 있는데,
만약에 컨테이너들이 각자 이제 이미지를 다운받고 설치를 시작하고 런칭을 한다고 치면, 장고의 컨테이너가 PostgreSQL보다 먼저 시작을 하려고 할때, 데이터베이스를 찾을 수 없다는 에러를 뱉으면서 도커가 중지되는 경우가 발생할 수 있습니다.
그러면 약간 기다리는 Sleep 같은 걸 주면 되지 않을까?라고 생각할 수 있지만,
맞는 말이긴 하지만, 앱이 배포되는 환경이 다 다르기 때문에 딱 어떤 시간을 정해서 줄 수는 없습니다.
자 그럼, 지금부터 docker compose를 이용한 multi container 배포에 대해 알아보겠습니다!
샘플 어플리케이션 소개
위의 사진이 예제 어플리케이션인데, 그냥 새로고침 할 때마다 임의의 이미지를 화면아 카드 UI 형태로 뿌려주는 간단한 웹사이트입니다.
코드를 보면 쉽게 이해가 될 겁니다.
위에서 보이는 바와 같이, 유저가 index page에 접속할 때마다, random_picture함수가 불러지고, 그 함수 안에서는 image를 다운로드하고, base64로 인코딩 된 상태로 데이터베이스에 저장하는 방식입니다. 그리고 전체 포스팅을 불러와서 html에 뿌려주는 것으로 앱이 마무리가 됩니다.
간단하죠?
docker-compose.yml 작성하기
어느 정도 위의 코드를 이해하실 수 있다고 가정하고 진행하겠습니다.
위에 보면 두 개의 서비스 (컨테이너)가 있습니다.: ‘app’과 ‘postgres’.
‘app’ 컨테이너는 루트 디렉토리에 있는 Docker file을 이용해서 설치가 될 것이고, ‘postgres’ 컨테이너는 ‘postgres’ 폴더에 있는 Dockerfile을 이용해서 설치가 될 것입니다.
여기서 가장 중요한 파라미터는 ‘links’인데, app 컨테이너가 postgres컨테이너의 포트를 자유롭게 접속하려면, 이렇게 이 두 앱은 서로 연결이 되었다는 것을 명명해줘야 합니다. (물론, 그냥 포트를 콘테이너 밖으로 다 오픈해버리면 상관없긴 합니다만..)
이제 app 컨테이너가 사용하는 Dockerfile을 보도록 하겠습니다.
6번째 줄까지는 크게 특별한 내용이 없습니다.
도커를 써보셨던 분들이라면, 마지막에 python run 어쩌고 저쩌고 뭐 이렇게 끝나셨을 겁니다.
그런데 저희는 장고 앱을 완전 스크래치부터 빌드하다 보니까, 추가적인 작업을 좀 더 해줘야 합니다.
그래서 ‘entry_point.sh’ 파일에 필요한 명령어들을 모두 넣겠습니다.
여기서 가장 중요한 부분은 4번에서 7번 줄입니다.
이 루프 문은 python manage.py migrate가 정상적으로 완료될 때까지 계속 반복됩니다.
물론 PostgreSQL에 “health check”같은 요청을 보내서 확인하는 방법도 있습니다.
어떤 방법을 쓰든 가장 중요한 건 정상적으로 작동될 때까지, 중요한 길목에 있는 명령을 반복적으로 실행해주는 것입니다.
끝!
여기서 완성된 코드를 볼 수 있습니다.