🐙

상황에 맞는 서버 배포 운영방식 🐱🐙🐳

Created
2021/02/21 05:00
Tags
AWS
Docker
GIT
Subtitle
배포 자동화가 모든 문제를 해결해주지 않는다.
회사에서 총 세개의 서비스를 운영중에 있다.
다만 서비스를 시작한 시기나 아키텍처를 구성한 맴버들이 다 달라서, 서버의 운영방식이 다르다.
예전에는 자동화 하는 방식으로 전부 통일해야 한다고 생각해서 이런 저런 시도를 했었는데, 요즘은 생각이 많이 바뀌었다.
요즘은 기술이 조직보다 우선시 되면 안된다는 생각을 항상 한다. 조직에 잘 스며들지도 않을 뿐더러, 모든 기술은 장단점이 있어서 지금까지 잘 동작했다면 새로운 기술의 장점보다 단점이 부각되어 보이는 것 같다.
이러한 연유에서 운영방식이 다른 각각의 서비스들 장단점을 비교해보고, 정말 변화가 필요없는지 정리해보는 시간을 가졌다.

EC2에서 직접 버전 반영을 하는 Django서버

서비스 초창기 Django로 개발된 서비스는 이미지와 같은 구조를 가진다. ( Load Balancer앞에 Route53이나 Proxy 서버등은 생략했다. )
브랜치 반영 후에 EC2에 SSH로 접속해서 Git 명령어를 이용하여 코드를 반영한다.
이러한 방식은 다음과 같은 장단점을 가진다.
장점
빠른 배포가 가능하다.
프로세스가 단순하다. 그렇기 때문에 모든 팀원이 해당 배포방식을 이해하고 사용하고 있다.
단점
모든 인스턴스에 접속하여 코드를 반영해주어야 한다.
서버 환경(환경변수, CGI설정 등) 적용 역시 모든 인스턴스에 적용해야 한다.
사람이 적용하기 때문에 모든 인스턴스가 일관성있는 상태라 보장받지 못한다.
autoscaling이 필요할 때, 플랫폼이나 환경등을 고려해야 해서 특히 복잡해진다.
이전에는 위처럼 장점보다 단점이 많아서 위 구조를 바꾸어야 한다고 생각했다.
하지만 막상 바꾸고 나니 우리의 오래된 초기 서버에는 맞지 않는 옷이였다.
오래된 초기 서버는 하드코딩된 레이아웃들, 자잘한 버그들이 많아서 잦은 hotfix가 이루어 진다.
뿐만 아니라 모든 개발자들이 필요할 때, 바로바로 수정된 코드를 적용하길 원했다.
해당 업데이트는 배포는 더럽게 느려졌고, 과정은 더럽게 복잡해졌다.
결국 레거시 서버는 EC2로 직접 반영하는것으로 지금까지 잘 사용중에 있다.

배포 자동화를 통해 신기능이 추가되는 Node 서버

신기능이 추가되는 Node서버는 Codepipeline과 ElasticBeansTalk를 통해서 배포된다.
초창기 서버와 달리 신기능이 추가되는 서버는 레이아웃등 대부분을 기능화시켜서 HTML의 텍스트 한줄을 3시간안에 반영해야 하는 등의 요청이 발생하지 않는다.
이러한 배포 전략은 다음과 같은 장단점을 가져왔다.
장점
CI/CD 자동화가 가져다 주는 장점
도커 사용으로 어플리케이션 단위의 버전관리로 모든 플랫폼에 동일한 결과를 얻음
AutoScaling이 쉬워짐
배포 스크립트를 짜는 대신 Codepipeline를 사용하여 문서 제공으로 통한 기술 공유 가능
배포 서버와 같은 플랫폼 및 자원 설정을 통한 빌드 타임에 테스트 코드 실행을 통해 사전에 이슈를 방지
단점
빌드 타임 및 여러 배포 프로세스로 인해 코드 배포 후 반영까지 많은 시간이 걸림.
Docker AWS Github Action등의 기술에 대한 팀원들의 러닝커브
러닝커브로 인해 이슈 발생시에 문제를 해결할 사람이 한정됨
배포 자동화를 통해서 코드 반영시에 복잡하게 신경써야 할 것들이 줄어들게 된다.
다만 CI/CD에 이슈가 발생했을 때, 아키텍처를 이해하는 소수의 인원들만 문제를 해결 할 수 있게 된다.
물론 전문 데브옵스나 팀장급 개발자가 회사에 있다면 해피엔딩이겠지만 우리는 전자도 후자도 없어서, 결국 업무에 영향을 줄 정도로 업무 강도가 언밸런스해져버린다.
이 때문에, 깊게는 아니더라도 기본적인 아키텍처는 팀원들과 공유가 필수적인 부분이다.
뿐만 아니라, 매 배포마다 빌드를 여러번 해야 하기 때문에 번들 사이즈등을 끊임없이 고민하지 않으면 반영까지 아주 오랜 시간이 걸릴 수 있다.
우리 프로젝트 역시 덩치가 꽤 커져버려서... 반영되는데 한 세월 걸리는 느낌이다.

CodePipeline 사용 전 Script를 통한 배포 자동화

CodePipeline은 처음부터 사용한 서비스는 아니다.
배포자동화 초기에는 배포 스크립트를 짜서 ECR에 이미지를 올리고 EB에서 Dockerrun파일을 읽어 ECR에 Pull 받아 사용하는 방식이였다.
세 번째 서버는 역시 위의 Node서버와 거의 유사한 배포 구조를 가진다.
다만 지금 사용하는 방식보다 예전 방식으로 비교하는게 더 볼거리가 많아서 소개하게 되었다.
위의 CodePipeline을 이용한 배포방식과 비교해서 느낀바는 다음과 같다.
Script 사용시 장점
스크립트만 읽을 수 있다면 어떻게 배포가 진행되는지 이해하기 쉽다. ( Code Commit 웹 훅등은 모르면 어떻게 돌아가는지 알 수 없음 )
배포 프로세스 과정이 단순하다.
Script 사용시 단점
복잡한 스크립트라면 이해하기 힘들다. 하지만 Codepipeline은 문서화되어 있기 때문에 읽으면 누구나 사용할 수 있다.
배포되는 서버의 플랫폼 및 자원과 가장 유사하게 build 및 test를 실행할 수 있다.
ecr은 가지고 있는 어플리케이션 버전 수가 s3보다 제한되어 있다.
나열하고 보니, 확실히 CodePipeline을 사용하는데 더 적합해 보인다.
아주 복잡해지는거에 비해서 build나 test를 확실히 하고, 팀 특성상 문서화를 많이 하지 않는데 Codepipeline에서 문서를 제공하고 있기 때문이다.
그래도 Codepipeline이 말썽일때는 가끔... 생각나더라 ㅎㅎ.

결론

CI/CD는 무조건 좋은 것도 아니고, 사람 마다 기술에 대한 생각도 다 다른걸 겪고 나니 아무리 좋은 기술이라도 조직의 상황에 맞게 적용해야 하는것 같다.
위의 방법 역시 지금은 우리팀에 맞는 방식이라고 해도 서비스가 더 커지고 팀 멤버가 바뀌면 기존의 방식이 변화해야 할 수도 있고 혹은 멤버들이 기술에 맞춰가야 할 수도 있을꺼 같다.
React Maintainer인 Dan Abramov의 Goodbye, Clean Code 포스트도 같은 맥락에서 볼 수 있을 것 같다.
클린 코드도 좋은 방법론 Best Practice일 뿐이지 모든 상황에 정답은 아니다. 조직과 상황을 바라보고 환경에 맞게 적용하는 것이 더욱 중요하다는 것이다.
가령 클린 코드의 “2. 의미 있는 이름(Meaningful name)” 파트에서는 약어를 사용하지 말라. 라는 내용이 있다지만 React.js의 많은 부분에서는 constructorctor라고 사용하고 있다. 하지만 React.js를 누가 나쁜 코드라고 말하겠는가.
function checkClassInstance(instance: any, ctor: any, newProps: any) { if (__DEV__) { const name = getComponentNameFromType(ctor) || 'Component'; const renderPresent = instance.render; if (!renderPresent) { if (ctor.prototype && typeof ctor.prototype.render === 'function') { console.error( '%s(...): No `render` method found on the returned component ' + 'instance: did you accidentally return an object from the constructor?', name,
JavaScript
복사
< React.js/FizzClassComponent.js 예시 >
배포 뿐만이 아니라 어떤 기술이든 조직과의 커뮤니케이션이 가장 중요하다는 말을 꼭 전하고 싶다.
근데 문제는 이미 합의도 안보고 쑤셔 넣은 기술들이 넘무 많다는 것이다
정말 ... 기술을 배우는것도 어렵지만 조직에 기술을 적용시키는게 더 어려운거 같다.