Poison

이준우·2023년 2월 1일
2

독초판별 시스템 프로젝트

이번에 2022년 실리콘벨리 부트캠프에서 진행한 프로젝트인데, 식물 사진을 넣으면 해당 식물이 독초인지 AI가 판별해주는 프로젝트이다.
나는 이 프로젝트에서 백엔드와 배포를 맡아서 작업을 하였다.

도메인 주소
https://posionsvb.com
git
https://github.com/SVTeamB-POISON

소프트웨어 아키텍쳐


우리 팀 시스템 아키텍쳐이다.

백엔드

WAS 서버

WS(WEB SERVER)의 일인 정적 데이터 처리를 하면서 동적인 처리도 해주는 서버이다.
WS 서버와 WAS 서버를 따로 두어 동적인 일을 WAS에게만 시키는 이유는 요청이 한쪽으로 쏠려 서버의 과부화를 막기 위함이다.

gunicorn

gunicron은 WSGI 서버로 쉽게 말해 파이썬 언어를 읽지를 못하는 브라우저들을 위한 통역기이다.
gunicorn은 멀티프로세스이며 동기로 일을 하고 마스터 프로세스가 각각의 자식 프로세스를 관리하는 형태이다. 또 각각의 자식 프로세스는 하나의 djanog 서버를 작동시킨다고 볼 수 있다.
gunicron에서 권장하는 worker 수는 2 * cpu_core + 1 이고, 각각의 worekr를 계속 켜두면 memory leak이 발생할 수 있기에 추가 옵션으로 어느정도의 요청을 받으면 worker를 재실행한다는 명령어를 주면 좋다.

gunicorn --bind 0:8000 config.wsgi:application --workers 3 --max-requests 100

  • --workers : worker의 수를 지정해준다
  • --max-requests : 해당 수 만큼 request를 받으면 gunicorn worekr를 재시작한다,

WISG vs ASGI
ASGI 서버는 WISG서버의 모든것을 계승하지만 싱글 프로세스에 비동기 처리라는 부분에서 업그레이드 된 서버이다. 예로는 uvicorn이 있다.

gunicorn + uvicorn
멀티 프로세스인 gunicorn을 이용해 싱글 프로세스인 uvicorn을 여러개 사용하는 방식이다.
따라서 멀티 프로세스 + 비동기의 일을한다.

Swagger

해당 view 메소드에 어노테이션을 달면 자동으로 api 명세서를 작성해준다.
이러한 api 명세서는 프론트엔드와의 소통을 더욱 편리하게 해주며, 실제 요청 형식과 반응 형식을 표현해 준다.

rabbitmq + celery + redis


celery는 파이썬의 비동기 처리를 도와주는 worker이다. celery의 worekr는 많은 수가 있다고 해도 cpu core 수 만큼 멀티 프로세스 작업을 한다.
rabbitmq는 task를 celery worekr에 넣어주는 메시지 브로커 역할과 큐 역할을 한다.
특징으로는 반드시 한번은 들어간다는 특징이있다. 즉 여러번 들어갈 수 도 있다는 뜻이다.
redis는 celery의 task가 반환하는 결과값을 저장하는 캐싱역할을 한다, 서버는 redis에서 task_id를 조회해 값을 가져온다.
여기서 말한 task_id란 task가 rabbitmq에 들어간 순간 바로 나오는 일종의 번호표 같은 개념이다. 이 task_id를 통해 우리는 값을 반환하거나 해당 task의 성공 여부, 실행 여부를 알 수있다.

redis는 입력과 출력이 많은 곳에서 사용하기 좋은 DB이고, Key : values 형식이다.
redis는 캐시와 같은 역할을 하고 또 서버가 다운되면 데이터가 날아가는 휘발성의 성격을 가지고 있다.
redis-cli --> 데이터베이스 내부 진입 명령어
keys {key_id pattern} --> 키 조회
get {key_id} --> 키에 해당하는 값 조회

우리는 celery를 통해 비교적 오래 걸리는 ai를 통한 독초 판별 서비스를 비동기로 처리하였다.
그 과정에서 프론트엔드와 polling 방식을 사용하였다. polling 방식이란 프론트 엔드가 백엔드에게 일정한 시간 간격을 두고 celery task가 종료 되었는지 묻는 것을 말한다.
polling 방식을 사용한 이유는 django는 파이썬 기반 프레임 워크 이므로 GIL(Global Interpreter Lock)이기 때문이다. 이 메커니즘은 인터프리터가 한 번에 하나의 바이크 코드명령만 실행하도록 하는 것을 말한다.
따라서 django는 한번에 하나의 request 밖에 처리를 못한는데, AI 처리는 최소 2~3초의 시간이 걸린다.
즉 우리 서비스에 빗대어 말하면, 우리 서버는 gunicorn worker 프로세스가 3개가 돌아가고 있는데, 만약 폴링방식을 지원하지 않았더라면, 4명의 사용자가 왔을 때 먼저 3명이 독초 판별을 하고 나머지 한명이 도감 서비스를 이용한다면, gunicorn worker는 무거운 ai 처리를 하느라 가벼운 request인 도감을 처리하지 못해 도감을 이용하려했던 클라이언트는 worker에 자리가 날 때까지 즉 독초 판별 서비스의 이용이 끝날때 까지 도감을 이용하지 못하게 된다.
따라서 polling 방식을 통해 gunicorn worker도 비동기로 만들어 줬다고 볼 수 있다.

MongoDB Atlas

우리의 서비스는 복잡하지 않은 구조를 가지고 있기 때문에 MongoDB를 사용하기로 하였다.
그리고 서버 내부의 최적화를 위해 서버 내부에 MongoDB를 두는것이 아닌 외부에 Cloud 형식을 두기로 결정하였다.
이는 최고의 선택이였고, docker로 인해 container가 꺼졌다 켜져도 데이터가 날라갈 걱정도 없고 또 손쉽게 데이터를 수정할 수 있다는 장점을 가지고 있었다.
또 Atals에서 지원해주는 검색엔진인 MongoDB Atlas Serach를 통해 도감 페이지에서 독초 검색엔진을 구현할 수 있었다. 검색엔진을 통해 우리는 유사어를 통한 독초 검색, 또 다중 검색이 가능해 졌다.

배포

Docker

이번에 처음으로 프로젝트를 진행해서 모든게 신기했었지만, 그중 제일 신기했던 스택을 꼽으라면 나는 Docker라고 말할 수 있겠다. Docker를 통해 다운로드된 스택들의 관리를 깔끔하게 할 수 있었고, 또 복잡하게 다운할 필요 없이 그냥 Docker 명령어를 통해 쉽게 사용할 수 있었다.
또한 배포 또한 개발환경 그대로 가져갈 수 있어, 진짜 편하고 신기했던것 같다.
Docker에 대해서는 쓸것이 너무 많으므로, 나중에 따로 작성하도록 하겠다.

Github action

CICD(Continuous Integration/Continuous Delivery) 지속적인 통합, 지속적인 배포라는 뜻이다. 어느 일정한 조건(push)를 만족했을 때, 자동으로 통합해주고, 나아가서 배포까지 해주는 스택이다. docker 다음으로 신기하게 봤던 기술 스택이다.

  • Workflow
    여러 Job으로 구성되고, Event에 의해 트리거될 수 있는 자동화된 프로세스
    최상위 개념
    Workflow 파일은 YAML으로 작성되고, Github Repository의 .github/workflows 폴더 아래에 저장됨
  • Event
    Workflow를 Trigger(실행)하는 특정 활동이나 규칙
    예를 들어 다음과 같은 상황을 사용할 수 있음
    특정 브랜치로 Push하거나
    특정 브랜치로 Pull Request하거나
    특정 시간대에 반복(Cron)
    Webhook을 사용해 외부 이벤트를 통해 실행
    자세한 내용은 Events that trigger workflows 참고
  • Job
    Job은 여러 Step으로 구성되고, 가상 환경의 인스턴스에서 실행됨
    다른 Job에 의존 관계를 가질 수 있고, 독립적으로 병렬 실행도 가능함
  • Step
    Task들의 집합으로, 커맨드를 날리거나 action을 실행할 수 있음
  • Action
    Workflow의 가장 작은 블럭(smallest portable building block)
    Job을 만들기 위해 Step들을 연결할 수 있음
    재사용이 가능한 컴포넌트
    개인적으로 만든 Action을 사용할 수도 있고, Marketplace에 있는 공용 Action을 사용할 수도 있음
    Github Marketplace와 Github Actions Repository에서 확인 가능
  • Runner
    Gitbub Action Runner 어플리케이션이 설치된 머신으로, Workflow가 실행될 인스턴스
    Github에서 호스팅해주는 Github-hosted runner와 직접 호스팅하는 Self-hosted runner로 나뉨
    Github-hosted runner는 Azure의 Standard_DS2_v2로 vCPU 2, 메모리 7GB, 임시 스토리지 14GB


위에 글(퍼온 글)을 내가 정리한 그림이다.

팀원과의 불화

팀원간의 불화라고 할 것이 없어다.
모두 다 처음이라 누가 옳다고 우기는 것이 아니라 누군가 의견을 제시하면 그 의견대로 수행하다가 막히면 다른 사람의 의견을 수용하는 방식으로 갔기 때문이다.
스스로에게 아쉬웠던 점은 내가 공부가 얕아 잘못된 정보를 옳다고 믿어서 팀원들에게 혼란을 빠뜨릴 뻔 했다는 것이다. 팀원 중 한명이 정정해줘서 망정이지 아니면 끝까지 옳다고 믿을뻔했다.
이 일을 통해 공부는 확실하게 해야하고 늘 내가 옳다는 생각이 아닌 다른 사람의 의견을 듣고 내 스스로 어느 부분이 틀렸는지에 대해 생각하는 겸손한 자세를 가져야겠다고 생각했다.


reference

github actions : https://zzsza.github.io/development/2020/06/06/github-action/
GIL: https://ssungkang.tistory.com/entry/python-GIL-Global-interpreter-Lock%EC%9D%80-%EB%AC%B4%EC%97%87%EC%9D%BC%EA%B9%8C

profile
잘 살고 싶은 사람

0개의 댓글