기존 앱을 리뉴얼하면서 아예 스크래치부터 프로젝트가 시작됐다. 서버를 배포하기 위해 AWS를 사용하면서 EC2 세팅, ALB 설정, ACM 사용 및 https 연결 등의 과정에서 여럿 우여곡절이 있었는데, 이 전체적인 그림이 머리에서 휘발되기 전에 남겨놓고자 한다. 자세한 방법보다는 과정 중심의 일기 정도가 되겠다. 장고를 사용했기 때문에 uwsgi와 nginx도 세팅해야 했는데, 서버를 구성하고 배포하면서 오랜만에 매일 하던 것이 아닌 지식을 활용해본 것 같다. 익숙하지 않은 기술의 러닝 커브가 어느정도 되는지 파악해볼 수 있었다. 그리고 새삼 웹 배포플랫폼이 얼마나 잘 되어 있고 편리한지 느낄 수 있었다. 프론트 좋아..
로컬에 있는 서버를 띄우기 위해 EC2를 구성했다. 처음에는 Amazon Linux로 시작했는데, Ubuntu보다 자료가 없어 환경으로 인한 에러가 많았다. Ubuntu 기준의 참고자료가 많아서 Linux와 패키지가 다른 경우 하나씩 지원하는 패키지를 찾아 설치하는 경우도 굉장히 많았다. 귀찮아서 중간에 Ubuntu로 갈아탈까 했지만 이미 너무 많이 와버렸더라... Docker를 띄우면 편했겠지만, 이건 나중에 구성하기로 하고 django, postgreSQL부터 세팅했다.
Amazon RDS for postgreSQL을 쓸까 하다가, 로컬에 DB 세팅하는게 큰 작업은 아니니 로컬과 환경을 비슷하게 만들고 싶어 (돈을 절약하고자..) 굳이 사용하지 않았다. 일단 dev 돌리는 것부터 해야 하니 python 가상환경에서 runserver가 되기 위한 세팅을 해줘야 한다. deps가 잘 설치되었다는 가정 하에 (여기서 생기는 문제는 보통 버전을 조정하거나 deps를 확인하면 해결된다.) 환경 별 DB와 user를 생성해주고, 기본으로 열려있는 SSH를 제외한 localhost http 및 DB port를 열어준다. DB 세팅이 잘못 된 경우 runserver 과정에서 DB를 읽어오지 못해 터지는데, 프리티어를 사용하면 이 작은 실수가 재부팅까지 이어질 수 있기 때문에 로컬에서 잘 테스트해줘야 한다. 무조건 로컬에서 잘 도는 것 확인하고 EC2에 적용하기... ㅎ 난 귀찮 아서 EC2에서 바로 작업하다 재부팅을 여럿 해야 했다. 아주 조심히 살살 다뤄줘야 함 ㅋ
그렇게 포트까지 잘 열어주고 postgreSQL에 요청이 잘 들어오는 걸 확인한 뒤, 본격적으로 django 프로덕션 배포를 위한 설정을 만져준다. 그냥 node 쓸 걸 암튼... 장고는 프로덕션 운영을 위한 웹 서버가 아니기 때문에 (보안, 안정성 등의 측면) 따로 프로덕션용 웹 서버를 띄워야 한다. 그렇게 nginx를 세팅하고 중간 다리로 uwsgi를 놓으면서 (multiple process worker) django-uwsgi-nginx 연결을 구성했다. 일단 uwsgi 통신을 http으로 기본 세팅을 해놓고, nginx와 맞춘 포트를 열어주면 정상적으로 서빙된다. settings.py 에서 설정한 static file 경로를 nginx에서 동일하게 세팅해주면 static files도 잘 읽히고, 로컬에서 보던 화면이 이제 EC2에서 띄운 인스턴스에서도 잘 서빙하는 것이 확인된다.
추가적으로 uwsgi를 쓰기 때문에 통신을 uwsgi-socket으로 하도록 바꿔줬는데, 이 과정에서 디버깅이 많았다. 잘 되던 서버가 500 Internal Server 에러 밖에 안 뱉는데 달래주느라 좀 힘들었다. uwsgi와 nginx를 daemonize 로 백그라운드에서 실행되도록 했는데, 매번 nginx log를 깔 때마다 Host is closed
가 떴다. 결국 socket 통신이 안 되고 있다는 건데... 아무리 chmod로 소켓 권한을 바꿔도 되지 않았다. 소켓도 잘 만들어졌고, 소유자 및 권한도 잘 설정했는데 결국 원인은 기본적으로 생성되어 있는 /tmp 에 socket이 있을 때 Linux의 경우 nginx에서 이 소켓에 접근하지 못하는 것이었다. (Ubuntu는 된다. ㅋ) Linux에서 해결한 방법은 간단하게 새로 디렉토리를 만들어서 socket, pid의 경로를 바꿔준 것이었다.
근데 결론을 먼저 말하자면 이 인스턴스는 종료했다. (;) postgreSQL 프로그램을 돌리다가 인스턴스가 터져서 재부팅도 해보고 시스템 로그 보면서 디버깅도 했지만 심폐소생술이 안 먹히길래... 그냥 죽였다. 미안..
다시 처음부터 세팅할 생각에 좀 토할 것 같았는데 이왕 새로 파는거 자꾸 귀찮게 하던 yum을 떠나 apt-get
를 쓸 생각을 하니 좀 설렜다. 그렇게 Ubuntu로 환경 구성을 바꾸고 이전에 귀찮았던 것들을 싹 다 바꿨다. (레포지토리 디렉토리라든가... 시스템 유저라든가... 그 사이에 성장했네)
기존에 했던 세팅을 다시 설정하고 마지막으로 socket 통신을 위해 임의로 디렉토리를 만들었던 것도 싹 다 다시 /tmp로 바꾸면서 설정을 좀 깔끔하게 다듬고 정리했다.
이제 로컬이 아닌 외부에서 접속할 수 있도록 (인터넷, 퍼블릭 웹 연결) 퍼블릭 IP를 해당 인스턴스에 할당해주고, 해당 IP에서 잘 서빙되는 것을 확인한 후 이전에 구입해놓았던 도메인에 연결해준다. 퍼블릭 IP를 할당해주지 않으면 인스턴스를 재부팅 하는 등의 상태 변경이 있을 때마다 IP가 재할당된다. 그럼 매번 config를 바꿔줘야 하니 불필요한 작업을 줄이기 위해 퍼블릭 IP를 할당해주는 것이 편하다. 우리는 가비아에서 도메인을 구입했기 때문에, 가비아 네임 서버를 AWS가 제공해주는 EC2 네임서버로 변경해준다. 그리고 EC2에 할당된 퍼블릭 IP로 DNS를 설정해준다. 그럼 이제 http로 해당 도메인에 접근할 수 있게 된다. 그 다음 외부에서 접근 시 https로 리디렉션하도록 추가 설정을 해준다. https이기 때문에 인증서를 설정해줘야 하는데, 무료 SSL을 이용하려다가 그냥 AWS에서 모두 해결하는게 덜 복잡해 ACM을 사용해 인증서를 생성해줬다. 일단 prerequisites는 모두 완료했으니, EC2에 들어오는 요청을 나누고 관리하기 위해 ALB를 설정해준다. ALB 설정 시 http와 https 케이스를 나누어 조건 처리를 하는데, https의 경우 바로 대상 그룹 (http를 통해 접근하는 대상이 되는 그룹) 으로 EC2 인스턴스로 설정하면 된다. 이 때 새로 https를 위한 포트를 열어주고 퍼블릭 IP에서 https 포트로 접근 시 대상 그룹을 보도록 하면 되고, 여기에 ACM으로 생성한 인증서를 할당해주면 된다. http로 접근하는 경우 https로 보내야 하기 때문에 redirection rule을 최우선순위로 설정하고, 기본값을 타깃 그룹을 보도록 하면 된다. 우리는 nginx를 사용하기 때문에 ALB의 https 설정 뿐만 아니라 nginx에서도 리디렉션 처리를 해주어 둘 간의 싱크를 맞춰줘야 한다.
이 과정에서 굉장히 쓸데없는 삽질을 했는데, 대상 그룹으로 연결되는 경우 http 포트로만 접근하도록 해야하는데, https를 설정하면서 해당 포트로 연결되는 대상 그룹을 하나 더 만들어버렸다. 그래서 자꾸 1/2의 확률로 새로고침 시마다 200과 500 상태 코드를 오갔다. ALB에서 밸런싱 규칙을 RR로 설정했기 때문에 1/n의 확률로 대상 그룹을 바꿔가며 읽는데, 이 실수를 발견하기 위해 ALB access log를 보기 위해 S3 생성 및 연결을 거치고 로그를 까보며 handshake가 실패하고 있다는 것을 확인해야 했다. ALB의 경우 자세한 access log를 기본적으로 제공하지 않기 때문에, 로그 옵션을 키고 이 내용을 S3에 저장해 파악해야 한다. 로컬에 access 또는 error log를 주긴 하지만, 자세하지 않기 때문에 디버깅을 하기에는 역부족이다.
잘못 생성한 https 포트 대상 그룹을 삭제하면서 생각했던 대로 도메인에 접근이 잘 되고, 모든 것이 잘 세팅된 것을 확인했다. 이후 REST API 통신을 위해 django의 경우 rest_framework을 설정해준다. 그렇게 api 응답이 잘 오는 것도 확인하면서, 우리가 사용할 서버 구성 및 배포를 마쳤다.
기존에는 static file을 로컬에 만들어 읽도록 했으나, access log를 저장하기 위한 S3를 만들면서 이 부분도 갈아엎었다. S3를 통해 static을 읽으면 CDN을 태우고 컨테이너 용량도 훨씬 줄일 수 있으니 과금 외의 단점 보다 장점이 컸다. nginx에서 설정한 static 경로 및 django의 STATIC, MEDIA 설정을 바꿔주고, S3의 권한 세팅을 바꿔주면 읽기까지 큰 어려움은 없다. 다만 GET 뿐만 아니라 POST도 해야하기 때문에 권한 설정 시 IAM을 잘 할당해야 한다.
이렇게 정리하니 간결해보이는데, 중간중간 꽤 많은 삽질 과정이 있어 빼먹은 부분이 있을지 모르겠다. 오랜만에 리눅스도 사용하고, 인터넷 연결 전반의 과정을 경험하니 학부 시절 과제를 하는 느낌이었다. 새로운 지식을 다량 얻고 활용하는 느낌.. 배우는 것에 열망이 큰지 몰랐는데, 난 새로운 것을 습득하고자 하는 열망이 큰 아이인 것 같다. 자아실현을 하고 있는 요즘 많이 행복하다 ㅋㅋ 그 어떠한 핑계도 댈 수 없고 불평불만을 해결할 수 있는 주체가 나 밖에 없는 환경이 굉장히 간결하다. 안 되면 내가 되게 하는 것 밖에 답이 없기 때문에 누구의 탓도 하지 않고 그냥 내가 한다. 그러면 해결되고 진행된다. 내가 바랐던 걸 충족할 수 있는 환경에서 창조에 대한 욕구와 생산성의 욕구까지 충족하면 정말 행복할 것 같다. 요즘엔 프론트 코드를 갈아엎고 있는데 익숙한 것을 하니 이리저리 부딪히며 서버를 만질 때와 다르게 속도감도 느끼고 있어 더 재밌다. 내 머리 속에 있는 걸 빠르게 실행해 외부에 내놓고 증명할 수 있는 역량을 갖는 것 만큼 스스로의 가치를 높이는 것이 있을까? 끊임없이 유저 보이스가 들리고, 우리가 갈 방향성과 가치를 얘기하며 다시 동기부여를 하고, 내가 좋아하는 분야를 개발하며 이 생태계에 대한 이해도를 높이는 등 취미와 일이 동일시 된 것이 생각보다 나쁘지 않다. 원래 2번째로 좋아하는 걸 일로 하라던데, 약간 그런 느낌이다. 알차게 가꿔야지!