2022. 4. 22. 11:01ㆍ작업일지
본 게시글은 https://github.com/f-lab-edu/double-shop에서 CI/CD를 구성하는 데 진행했던 과정을 정리한 내용입니다. CI/CD를 설계하는 데 필요한 방향을 찾는 데는 도움이 될 수 있겠지만, 구체적인 구현 기술에 대한 내용을 찾는 다면 이 게시글은 도움이 안 될 수 있습니다.
개요
저번 게시글에서는 CI/CD가 왜 필요한지에 대해 간단히 나의 생각을 정리해보았다. 내용을 요약해보자면, CI/CD와 같은 복잡하고 어려운 작업을 왜 해야 하는지 어떻게 해야 하는 지를 구체적으로 정의하는 과정이 필요하다는 것이었다. 물론 개인 프로젝트를 진행한다면, 이 과정은 필요 없을 것이다. 하지만, 보통 개발회사에서 한 명의 개발자가 하고 싶은 것을 마음대로 하다가 안되면 그냥 그만둬버리는 그런 경우는 없을 것이다. 따라서, 명확한 이유와 방향성을 제시하여, 주변 동료 개발자들에게 '동기 부여'를 하여, 해당 업무에 참여하는 동료 개발자들의 지구력(?)을 조금이라도 높이는 과정이 필요할 것 같아서 저번 게시글에서는 이유를 정의해야 하는 이유(?)를 정리하는 과정을 가졌다.
이번 게시글에서는 이제 CI/CD를 어떻게 구현하면 되는지를 알아가는 과정을 정리해보도록 하겠다.
Step 2. 'Fail Fast', 실패를 통해 배워보자
만약 이미 CI/CD에 대한 명확한 프로세스를 가지고 있는 '리더'가 있다면, 굳이 이 과정이 필요 없을 수 있다. 결국 '리더'가 하라는 데로 하고 같이 삽질(?)을 하면 결국 어떻게든 결과가 나올 수 있을 테니..(혹은 같이 망하거나..)
하지만, 그게 아니라면, 도와줄 사람은 없고 내가 알아서 찾아가며 해야 한다면 어떻게 해야 할까?
개인적으로는 우선 가장 무난한 방법은 시도하고 실패하는 것이라고 할 수 있겠다. 실패하는 것이 두려워서 계속 검토만 하게 된다면, 결국 아무것도 할 수 없을 것이고, 결과적으로는 문제를 해결하는 데 시간을 지연시킬 수 있기 때문이라 할 수 있겠다. 물론 오해하면 안 되는 게, 기본기에 대한 지식을 쌓는 것에 대해선, 절대로 시간을 아껴선 안된다는 게 내 입장이다. 만약, 애플리케이션을 CS의 관점에서 해석할 수 있는 시각(?)이 없다면, CI/CD에 대해선 잠시 잊고 관련 전공책을 읽는 게 더 유익할 수 있다. (근데, 이렇게 쓰는 나도 과연 CS를 제대로 파악하고 있는지는 확신을 못하겠다.. 아무리 해도 해도 부족하게 느껴지는 게 CS 다 보니..)
이번 Step 2. 에서는 이렇게 '실패'를 했던 작업들을 정리해보겠다.
Step 2.1 첫 번째 실패 : Github action과 GCR을 활용하여, CI/CD 구축하기
- github repository : https://github.com/wanniDev/testDocker
- 시나리오 :
- code push
- test & build
- gcloud에 서비스 계정 로그인
- GCE에 등록된 docker container에 빌드된 jar를 build & push
- gcloud를 통해 서버에 접속하여, 해당 docker 컨테이너 작동
- gcr.io의 메커니즘에 따라, gce를 재가동시켜서 애플리케이션 업데이트
우선.. 시나리오와 github repository에 등록된 workflow 스크립트를 본 사람은 알아챘을 것이다. 얼마나 내용이 두서없고, 특히 시나리오 부분을 보면 대체 이게 뭔 프로세스를 하려고 했는지 전혀 알 길이 없을 것이다.
설명 내용 자체도 애매한데, 왜냐면, 지금 생각해도 내가 뭘 하려고 했는지 알기 힘들 정도로, 당시에는 아는 것이 하나도 없었다. 사실 이러한 시도를 통해 알게 된 내용은 "아직은 CI/CD를 시도할 만큼 난 준비가 안되어있다."였다. 이번 시도를 통해 내가 공부해야 했던 부분을 알게 되었는데, 목록화하여 정리하자면 다음과 같다.
1. 운영체제 기본기 실전 적용 미흡 + 글로만 배웠던 리눅스 권한 체계
공룡 책을 보면, 운영체제에 대한 내용을 상세하게 알 수 있다. 그런데, 이걸 이론만 아는 것과 실전에 적용시키는 것은 조금 다른 문제였다. 서버라는 것은 결국 하나의 운영체제가 설치된 컴퓨터이고, 서비스를 제공하는 애플리케이션은 결국 소프트웨어.. 즉 실행 중인 하나의 프로세스이다. 따라서, 서비스를 업데이트를 하고 해당 내용을 반영하기 위해선 서버를 재가동시키는 과정이 필요하다.
처음에는 서버를 재가동시킨다는 것을 너무 정직하게 받아들여서 컴퓨터를 재시작해야 한다로 받아들였다. 그래서 복잡한 GCR을 적용하고 복잡하게 서비스 계정을 따로 만들어서, 원격으로 GCE를 재가동시키는 과정을 workflow에 포함하였다. 그 결과 불필요한 서버의 downtime을 증가시키고, gcp에 너무 의존적인 프로세스를 만들었다. 게다가 workflow 과정 하나하나의 결합도가 너무 높아서, workflow를 개선시키려고 하다가는 모든 과정을 뜯어고쳐야 하는 상황이 생긴다.
그리고 gcloud를 통해 gce 파일을 원격으로 접근하는 것만으로도 많은 삽질을 했는데, 원인은 간단했다. 리눅스의 권한 체계를 잊고 있었던 게 문제였다. 리눅스에서는 생성한 파일을 수정하려면, 관리자의 권한(root 권한)이 필요하다. 그러나, gcp에서 제공하는 기본적인 ssh 접속을 통해 얻는 권한은 생각보다 높지 않다. 따라서, gcloud를 통해 ssh 접속을 한다면, gcp 내에서 제공하는 기본 ssh 접속을 하는 것이고, 그 접속을 통해 허용되는 권한은 거의 일반 사용자 수준의 권한이므로, 만약 외부 접속을 통한 스크립트 실행을 활용하려면, 파일의 접근 권한을 조절하거나, 접속자의 권한을 높여야 한다. 그런데 이 과정에서는 그러한 권한 문제를 전혀 생각하지를 않고 진행을 하다 보니, 불필요한 삽질을 하게 되었다.
2. 외부 블로그에 나온 구현 내용에 의존하려고 했다.
뭔가 새로운 시도를 외부의 도움 없이 하려고 하니, 두려움이 생겼다. 그러나 보니, 명확한 레퍼런스를 그대로 따라 하려고 하다 보니, 외부 블로그에 의존하게 되었다. 그러한 내용의 게시글에 대해서 부정적으로 이야기할 생각은 없지만, 사실 그런 내용을 참고하는 게 과연 도움이 될지에 대해선 의문을 가져야 했다. 왜냐면, 대부분 블로그에서는 가장 일반적인 상황에서 어려운 문제가 발생할 수 있는 경우를 배제한 상태로 배포를 진행한다. 간혹 그러한 게시글을 쓰는 사람도 결국 다른 어떠한 블로그 내용을 그대로 가져다 쓰는 경우도 있거나, 검토를 진행하지 않고 정리한 내용을 정답인 것처럼 내용을 서술한 경우도 있다.
결론은, 새로운 시도에 대한 두려움 때문에 오히려 삽질을 더 오래 하게 되었다는 것이다.
3. 그렇다면, 다음엔, 어떻게 개선해볼까?
- 가장 먼저 해야 할 건 기본기에 대한 지식을 정리하는 것이다.
- 운영체제의 프로세스가 구체적으로 시스템에서 어떻게 돌아가는지, 리눅스에서 프로세스를 관리하기 위해선 어떻게 해야 하는지에 대한 구체적인 내용을 정리해야 한다.
- 결국엔 서버는 컴퓨터고, 애플리케이션은 프로그램이다. 프로그램의 내용을 업데이트하는데, 굳이 컴퓨터를 재시작하고, 프로그램을 삭제했다가 다시 설치하는 것이 좋은 방법일까?
- GCR과 같은 외부 api에 의존한 이유는 운영체제에서 프로세스 관리를 어떻게 하는지를 전혀 몰랐기 때문이다.
정리해보자...
2.1의 시행착오로 얻은 교훈은, '어려운 문제는 해결하기 어려우니까 어려운 문제다. 쉽게 쉽게 편법을 쓰는 것보다는 정공법으로 천천히 다가가는 게 오히려 빠를 수 있다'이다. 이번 시행착오를 통해, 한동안 프로젝트 진행을 멈추고, 전공책을 읽고, 프로젝트 코드를 architecture layer에 따라 세세하게 읽어보고, 만약 애플리케이션이 클라우드 환경에 올라갔을 경우, 네트워크 관점에 봤을 때, osi 7 layer와 연결시켜 생각해보고, MySQL과 같은 RDBMS를 통해 데이터베이스 자체에 대한 학습을 진행해보았다.
Step 2.2 두 번째 실패 : Github action과 docker를 활용하여, CI/CD 구축하기
- workflow : https://github.com/f-lab-edu/double-shop/actions/runs/2159328563/workflow
- 시나리오 :
- 특정 브랜치로부터 develop 브랜치로 PR이 끝난 후 merge를 한다.
- 코드 Test & Build
- 도커 로그인
- 빌드된 코드 파일을 도커라이징하여, 도커 허브에 푸시
- was 서버에 ssh 접속을 하여, 기존에 실행 중인 컨테이너 종료
- 기존의 이미지와 컨테이너 삭제
- 도커 허브로부터 새로 push된 이미지 pull
- pull이 완료된 이미지 컨테이너화하여 실행
Step 2.1 이후로 좀 더 학습을 진행한 이후로 시도했던 부분이라, 그래도 어느 정도 정리가 된 시나리오를 가질 수 있었다. 이 과정에서 도커를 활용하게 되었는데, 프로세스를 직접 백그라운드로 관리하는 것보다, 도커를 통해 관리하는 것이 훨씬 직관적이고 무엇보다 기존에 수정되기 전의 이미지들을 도커 허브에 저장해놓음으로써, 배포 과정에서 발생한 오류로 인한 롤백 작업을 좀 더 수월히 할 수 있을 거라고 생각했다.
하지만 이 방식도 사실상 실패라고 할 수 있다. 이유는 다음과 같다.
- 해당 workflow는 단일 인스턴스 환경에서만 유효하다.
- github-action 정책상 forked 된 리포지토리에는 secret 환경변수를 사용할 수 없다.
- 도커의 경우 외부 action 라이브러리에 의존하는 수 밖에 없는 구조이다.
1의 경우 운영하는 인스턴스의 만큼 스크립트를 반복할 수도 있고 혹은 matrix를 활용하는 등, 방법이 없는 것은 아니지만, 둘 다 너무 번거롭고, 복잡하다. 또한, 클라우드 환경에 접속하여 배포를 하는 과정에서는 민감한 정보가 오갈 수 있는데, github-action은 너무 public 하게 보여줄 수밖에 없는 구조로 되어있다. 물론 secret 환경변수를 활용할 수 있지만, github 정책상 forked 된 리포지토리에서는 해당 환경변수를 사용할 수 없는데, 이는 개발 프로세스의 제약을 주게 된다. 그리고 도커와 관련된 명령어를 직접 사용하지 못하고 외부 action 라이브러리를 사용할 수밖에 없는 구조인데, 이는 통합 배포 Task 구성을 너무 번거롭게 한다.
이번 실패를 통해 추후 개선점을 정리해보자면...
1. 코드 레벨에서 배포 시나리오 관련 스크립트 확인이 가능하면서, 코드에 큰 영향을 끼치지 않는 다른 방법을 생각해보자
사실 이 부분은 github action으로도 충분히 해결가능하다. 다만, 위에서 이야기한 3가지의 경우로 was 클러스터가 확장될수록, 점점 더 불편해진다. 프로세스 자동화가 일정한 퀄리티 보장과 업무 진행 효율인 점을 생각해보면, 아직은 반쪽짜리 프로세스다.
따라서, 이제 해결해야하는 점은 코드레 벨에서 배포 시나리오를 짐작해볼 수 있으면서, 민감한 정보는 숨길 수 있는 방법을 찾는 것이다.
2. 확장성 있는 배포 스크립트를 짤 수 있는 방법을 생각해보자
현재 workflow로는 관리하는 인스턴스가 많아질수록, 수작업이 많아진다. 당연히 관리 대상이 많아지면, 그만큼 운영비용이 올라가는 건 당연하다. 하지만, 그 증가하는 폭을 줄이기 위한 개선이 필요한 건 당연하다고 생각한다.
정리해봅시다...
이번 시도를 통해, 배포 시나리오를 구체적으로 구성하기 위한 방향은 어느 정도 잡힌 듯하다. 하지만, 문제는 역시 최적화다. 효율적인 프로세스를 위해서 시작한 일이라면, 그 과정이 힘들더라도, 결과물만큼은 그 힘든 과정을 보상해줄 만큼 효율적이고, 개발의 편의성을 제공해야 한다. 최적화를 할 수 있는 수단으로 우선 shell 스크립트를 학습해보기로 했다. 이유는...
- 컴파일러를 거치지 않고, 바로 시스템 콜을 호출하기 때문에, 리소스 효율이 나쁘지 않다.
- sh 파일에 권한을 조절하여, 인증된 사용자만 실행할 수 있도록 제어하여, 1차적으로 보안성을 유지할 수 있다.
- 또한 배포 과정에서 드러나는 민감한 정보들을 외부로부터 선택적으로 숨길 수 있다.
'작업일지' 카테고리의 다른 글
realstatelab.com | HTTP 로그로 행동패턴을 분석하던 중 발견한 이상한 요청들 (0) | 2025.03.18 |
---|---|
작업일지#15 그 동안의 회고 및 앞으로의 계획 (0) | 2022.09.02 |
[회고] 기능 구현부터 배포 자동화까지... -1- 왜 CI/CD가 필요할까? (0) | 2022.04.22 |
작업일지#14 Sync와 Async 속도차이 (0) | 2022.03.24 |
작업 일지#13 프로덕션 코드 리팩토링 - JPA로 이전 (0) | 2022.03.05 |