Linux를 배제하고 Docker, CI/CD 가능?

Jeeho Park (aquashdw)·2024년 10월 16일
0

이 글은 강의하면서 만난, 몇몇 Linux는 하고싶지 않으면서 CI/CD, Docker 등은 하고 싶어하는 몇몇 개발자 지망생들을 위한 글이다.

개요

개발자 시장은 빠르게 커졌고, 반대급부로 정말 급하게 다시 작아져 버렸다. 경기가 한창일때는 정말 손쉽게 개발자로 취업이 되었지만, 코로나 특수 이후로는 많이 잦아져 들어간게 현실.

다만 개발자 지망생은 생각보다 줄지 않은것 같다. 애초에 그 시장으로 먹고사는 나만 보더라도 일거리는 어찌됬건 생긴다. 이전과 같은 환상(6개월만 죽은척 하면 너도 연봉 6천!)은 조금 줄어들긴 했어도, 어쨋든 중소기업 위주로 개발자 인력은 여전히 부족하고. 다른 영역도 취업이 안되는건 마찬가지여서 그런가? 상대적으로 연봉도 안챙겨주기 힘들고...

그래서 내가 이 일을 처음 시작할때랑 비교해보면 바뀐 분위기가 한가지 보이는데, 이전에는 CRUD를 큰 문제없이 잘하기만 해도 괜찮게 취업이 됐다고 본다. 물론 완전한 개발자는 아니어도 초급 개발자는 일하면서 배우는게 더 많으니까. 근데 지금은 그걸로는 안된다고 많이들 생각하며, 그 결과 개발자 지망생들이 요구하는 교육도 달라진다. 좀더 많이, 좀더 어려운, 좀더 힙(?)한 기술을 공부하고 싶어하며, 그런걸 가르쳐주는 교육을 받고싶어 한다. 이 현상 자체가 문제라고 생각하지는 않는다.

문제는 그 이야기를 하는 개발자 지망생 본인이 그걸 할 수 있는지에 대한 고려가 되었느냐의 문제가 있다고 생각한다. 다양한 기술을 공부하고 싶은 지망생은 아직 지망생이다. 아직 알고리즘도 잘 풀지 못하고, 간단한 서비스도 만드는데 시간이 꽤나 소요된다. 테스트 코드 작성에도 익숙하지 않다. 그런데 TDD는 언제 배우냐 물어본다. Git을 쓰는데 add와 commit의 차이를 설명 못한다. 그런데 오픈소스 기여해보고 싶다 한다. AWS에 회원가입도 안해봤다. 그런데 CI/CD를 알려고 한다.

아기가 자라는 과정을 생각해보면, 걷기 전에 일어서는걸 연습하고, 일어서기 전에 기어다니며, 기어다니기 전에 뒤집는다. 그 전에는 그냥 예쁘고, 울고, 움직이는 감자이다. 그런데 왜 개발자는 그렇지 않을거라 생각하는가? 아직 내가 하는일이 무엇인지 감이 잡히지 않는데, 더 멀리 뛰는 방법을 알고자 하는 것인가?

기술이 워낙 빠르게 발전해서 모를 수도 있겠지만, 컴퓨터라는 분야도 다른 많은 분야처럼, 수십년간 수 많은 선대의 개발자들의 피와 땀, 그리고 무엇보다 삽질 위에 올라선 거대한 산이다. "이거하면 편하다, 저거하면 편하다" 등의 말에 속지 말아라. 그걸 쓰면 편한 이유는, 쓰지 않았을 때의 불편함을 알기 때문에 편한 것이다. 그리고 그 불편함을 알기 때문에 도구를 편하게 쓸 수 있다는 것을 반드시 알아야 한다.

이번 글은 그중에서도 Docker와 CI/CD에 대한 이야기다. 둘다 현재 개발자들에게 많은 관심을 받는 기술이다. 이들은 현대 개발에 있어서 빠질 수 없는 중요한 기술인 것은 사실이다. 하지만 개발자 지망생이 이들을 얼른 배우려는 이유는? 놀랍게도 다음과 같은 답변을 생각보다 자주 들었다.

서버에서 작업하는게 어려운데, 이거 배우면 쉽게 할 수 있데요.

Linux를 하기 어려우니까, 그걸 건너뛰기 위해서 Linux를 기반으로 하는 기술을 배우고 싶단다.

Docker, CI/CD, 그 외에 서버에서 사용하는 다양한 기술들이 있다. 이들을 잘 배우기 위해선, 당연히 서버, 적어도 그중 현재 가장 대중적으로 쓰이는 Linux의 이해가 선행되어야 한다고 생각한다. 엄청 잘할 필요도 없다. 적어도 Linux가 지금 당신이 사용하는 Windows나 macOS와 비슷한 OS임을 이해하면 된다. CLI에 작성하는 명령어가 Explorer나 Finder를 사용하는 것과 사실상 큰 차이가 없음을 알기만 하면 된다. 그리고 우리가 평소에 사용하던 OS와의 차이점을 알아보면 된다.

Linux를 모르면 Docker나 CI/CD를 못한다는 의미가 아니다. 세상에 얼마나 좋은 글이 많은가. Docker도 글 몇가지 찾아보면 내 컴퓨터에 수 많은 데이터베이스와 미들웨어 설치해서 편하게 사용한다. 내가 만든 프로젝트를 이미지로 만들 수 있다. CI/CD도 만들어진 템플릿 따라가다 보면, 전혀 지식 없이도 내 프로젝트가 배포가 된다.

하지만 결국 이들을 내 입맛대로 다룰 줄 아는게 중요하다. 그게 되지 않으면, 개발이라는 분야의 무한에 가까운 문제들 중 하나만 풀 수 있게 되는거니까. 그걸 위해서라도, Linux의 가장 기본적인 부분, 적어도 명령어를 가지고 컴퓨터를 사용하는, CLI를 이용하는 방법을 조금은 익혀서 가기를 바란다.

Docker 하기전에 배포 해봐라

"Docker란 무엇일까?"를 부트캠프 막 끝낸 개발자 지망생에게 물어보자. 십중팔구 다음과 같이 말하게 된다.

Docker는 컨테이너 기반의 가상화 시스템입니다.

보통의 개발자는 "컨테이너 기반"이라는 말에 초점을 맞추게 된다. 왜냐면 이미 "가상화"를 알고 있으니까. 하지만 개발자 지망생은 "가상화"에 초점을 맞춰야 한다. 왜냐하면, 가상화가 필요한 상황을 겪어보지 못했으니까.

가상화 이전에는?

국비지원을 받는 온라인 부트캠프의 참여하기 위해 필요한 장비로 기재되는 경우, 보통 8GB 메모리를 가진 화상채팅이 가능한 노트북이 기재되어 있다. 여기에는 "MacBook이어야 합니다, 그램이어야 합니다, 갤럭시북이어야 합니다"이런 이야기는 전혀 없다. 그렇기 때문에 지참하게 되는 컴퓨터는 전부 제각각이다. 강의를 하는 입장에선 고역이 따로 없다. 각 컴퓨터에 맞게끔 설정하는 방법을 준비해주어야 하기 때문에. Windows 11 나오고서는 우클릭 메뉴도 달라져서 더 힘들어졌다. 그나마 OS만 따지면 되니까 다행이라고 해야할까.

우리가 일반적으로 "배포"라는 큰 단어를 사용하지만, 웹 서비스 배포의 실체는 "24시간 켜두는 서버에 내 프로젝트 실행해두기"라고 볼 수 있다. 여기서 말하는 서버도 일종의 컴퓨터이다. 이게 무슨 말이냐? 앞서 이야기한 "서로 다른 컴퓨터에 맞는 방법"을, 서버 컴퓨터를 기준으로도 준비해 주어야 한다는 뜻이다. 게다가 서버에 주로 사용하는 OS는 보통 Linux를 사용하게 되는데, 아직 공부하는 입장이라면 Linux는 AWS EC2가 아니라면 접할 기회도 별로 없다. 그러다 보면 IDE로 개발을 할때는 마주하지 못한 상황들을 만나게 되는데, 이 상황의 대부분은 간단히 설명하면, "내 컴퓨터에선 되는데요 서버에선 안되요"라고 하는 상황이다.

이건 우리처럼 공부를 하는 입장에서보다 예전부터 오래된 서비스를 운영하는 입장에서 더 큰 문제가 된다. 오래된 기업은 클라우드 서비스가 대중화되기 이전에 직접 물리적 서버 컴퓨터를 구비하고 사용해왔다. 서비스가 커지고 확장하면, 추가로 서버를 구비했을 것이다. 근데 문제는 컴퓨터 기술이 그대로 정체되어 있지 않는다는 것이다. 이전에 사용하던 서버에서 동작하던 방식이 새로 산 서버에서 잘 동작할지 누가 보장해줄 것인가? 게다가 이전 서버에서 어떤일들이 있었는지 정확하게 기록하기도 어려운 와중에, 어떤 작업을 해줘야 새로운 서버의 환경이 이전 서버의 환경과 동일해지는지, 최선을 다해 조정하려고 해도 문제는 항상 발생하게 된다.

가상화의 목표

새로운 기술은 불편함과 귀찮음을 해소하려고 만들어진다고 나는 생각한다.

앞서 설명했듯 세상의 모든 컴퓨터는 완전히 같을 수 없기 때문에, 배포를 비롯한 서비스 운영 과정에서 환경으로 인한 문제는 언제든 발생할 수 있다. 기술적으로도, 인적으로도 완벽히 동일한 환경을 구축하는 것은 불가능하기 때문에. 가상화란 기술은 "수 많은 컴퓨터의 환경이 다르다"는 불편함을 해소하려고 만들어진 기술이다. 어떻게? 서로 다른 컴퓨터에 "가상 환경"을 만듦으로서.

예시의 편의를 위해 RAM과 하드디스크만 가지고 예를 들어보자. 만약 내가 가진 컴퓨터의 RAM이 16GB, 디스크가 128GB인데, 일상에서 사용할 때는 그 절반만 사용한다면? 평소에 남는 공간은 다음과 같을 것이다.

  • 8GB RAM
  • 64GB 디스크 용량

내가 아무리 컴퓨터를 열심히 사용하더라도 이 이상의 자원을 사용할 수 없다면, 이를 놀리기 보단 아예 새로운 컴퓨터를 하나 더 만들어 사용하는 것이 나을지도 모른다. 이때 사용하는 기술이 가상화 기술이다. 가상화 기술은, "컴퓨터 자원을 가상화하여 더 작은 단위의 자원으로 나누고, 그 자원에 또다른 컴퓨터를 만드는" 기술을 총칭한다.

여기서 말하는 또다른 컴퓨터는 굳이 내 컴퓨터와 동일한 OS를 가질 필요는 없다. Virtual Box나 VMWare 같은 프로그램으로 Linux를 공부해 봤는가? 분명 내 컴퓨터는 Windows지만, 컴퓨터를 켜둔 채로 Linux를 사용할 수 있었을 것이다. 즉, 내 컴퓨터는 자원만 제공하고, 그 자원 위의 환경은 원하는데로 구성할 수 있게 해준다는게 핵심이다. 그래서 어떤 형태의 컴퓨터든 간에, 내가 요구하는 환경을 구성해 놓는다면 문제없이 프로그램을 실행할 수 있다는 의미가 된다.

Docker도 가상화 도구이고, 그 목적도 크게 다르지 않다. 즉, 여러 환경의 컴퓨터에 내가 원하는 환경의 컴퓨터를 실행하기 위해서. 하지만 이전 기술들에 비해 Docker가 인기를 끈 이유는

  1. 기술적 특징으로 인해 이전의 기술보다 훨씬 가볍다.
  2. 개발자가 배우고 활용하기 가장 편하다.

그러다 보니 자연스럽게 개발자들도 Docker를 공부하고, 자신의 프로젝트에 접목하기 시작한것이다.

Docker = 가상화라면?

Docker는 가상화 기술이다. 가상화 기술은 프로젝트를 동일한 환경에서 배포하기 위해 만들어졌다. 그렇다면 Docker의 목표에는 배포를 편하게 하기 위해가 포함되어 있다고 볼 수 있다.

문제는 Docker가 환경을 제공한다는 뜻이, 개발자 생활에 익숙해지기 전에 그러듯 마우스로 화면의 아이콘을 클릭하는 환경을 제공하는게 아니다. Docker는 일반적으로 서비스를 배포하기 위해 사용하는 환경, 즉 Linux 기반의 환경을 제공한다. 기술적으로는 좀더 복잡한 설명이 들어가겠지만, 실질적인 개발자의 입장에선 그러하다.

Docker를 이용해 내 프로젝트를 배포하고 싶다면 다음 내용을 이해해야 하는데,

  • Docker는 실행중인 컴퓨터와 상관 없이 자신만의 환경에서 프로세스를 실행하는 컨테이너(container)를 만들 수 있다.
  • 컨테이너를 만들기 위해선, 해당 컨테이너를 만드는 방법이 작성된 이미지(image)가 필요하다.
  • 이미지를 만들기 위해선 Dockerfile이 필요하다.
postman runner Docker가 우리 컴퓨터 위를 항해하고, 그 위에 컨테이너를 실어주며, 그 컨테이너를 태평양이든, 대서양이든 대신 건나가준다.

결국, Docker를 이용해 내가 만든 서비스를 배포하고 싶다면, Dockerfile이라는 파일을 만들어야 된다. 예시 파일을 하나 살펴보자.

FROM eclipse-temurin:17

WORKDIR app
COPY . .

RUN <<EOF
./gradlew bootJar
mv build/libs/*.jar app.jar
EOF

CMD ["java", "-jar", "app.jar"]
EXPOSE 8080

이건 평범한 Spring Boot 프로젝트를 빌드하고 실행하는 컨테이너를 만들기 위해 필요한 이미지, 그 이미지를 만들기 위한 최소한의 Dockerfile이다. Java와 Spring Boot만 막 배운 입장에서 이 내용을 전부 이해할 수 있을까? 아마 쉽지 않을거다. 하나씩 그 내용을 파악해보면 다음과 같이 설명해볼 수 있다.

  1. temurin 배포판 JDK를 설치한 컴퓨터(이미지)를 사용한다.
  2. app이라고 하는 폴더를 컴퓨터에 만든다.
  3. 현재 폴더(Docker를 실행한 폴더)의 내용을 전부 이미지 내부로 복사한다.
  4. gradle Wrapper를 사용해 프로젝트를 빌드하고, 현재 폴더에 app.jar라는 이름을 붙여서 저장한다.
  5. java -jar app.jar를 실행한다.
  6. 컴퓨터(이미지를 바탕으로 만든 컨테이너)의 8080 포트로 들어오는 요청을 허용한다.

정확한 워딩은 아니지만 컴퓨터로 비유해서 작성해봤다. 어떤가? 만약 EC2와 같은 서버에 직접 프로젝트를 배포 해봤다면 어느정도는 익숙한 과정으로 보일지도 모르겠다. 물론 내 컴퓨터에서 빌드해서 서버로 옮기느냐, 서버 안에서 직접 빌드까지 하느냐, 어떤 Java를 사용하느냐 등의 차이는 있을 수 있다. 하지만 본질적으로는 이미지라는 곳에 내 Java 프로젝트를 배포하고 있다는 느낌을 받을 수 있다.


어차피 이미지를 만드는 과정은 프로젝트를 배포하는 과정과 비슷한데, 그럼 왜 굳이 이미지로 만드느냐? 다시 이 파트의 제목을 보고 와라. Docker는 가상화 도구이다. 즉 어떤 컴퓨터에서든 내가 만든 프로젝트를 같은 환경에서 실행하기 위한 기술이다. 이게 무슨말이냐고? 이미지로 만들면, 이제 어떤 컴퓨터에서든 Docker만 설치가 된다면, 내 프로젝트가 동일하게 동작하게끔 실행해 준다는 것이다. 그래서 첫 문단에서 말한것 처럼 배포가 편하게 된다. 새로운 컴퓨터가 생길때마다 반복하던 작업을 줄여주고, 그 과정에서 생기는 문제를 최소화 해주니까.

즉, Docker를 제대로 원하는데로 사용하고 싶다면, Dockerfile을 작성할 줄 알아야 한다. Dockerfile을 내 프로젝트의 요구사항에 맞게 잘 작성하고 싶다면, 내 프로젝트가 정상적으로 실행되는 환경을 구성할 줄 알아야 한다. 어디에? Linux 서버 컴퓨터에!

CI/CD가 결국 뭐하는 거냐

이제 CI/CD로 넘어가보자. 개인적으로는 DevOps라는 분야에서 가장 먼저 떠오르는 기술인것 같다.

옛날에 DevOps라는 말이 처음 퍼질 때는 나를 포함해 정확히 이해하지 못한 사람이 꽤 있었던거 같다. 정확히는 백엔드와 DevOps를 정확히 분리해내지 못했다고 해야할까? 물론 지금은 프런트가 있고, 백이 있고, 데이터가 있고, Ai가 있듯, DevOps도 아예 별도의 분야로 생각하는 경향이 좀더 강해졌다.

좀더 얘기하면, 개발(Development)에서 운영(Operations)으로 넘어가는 과정에 전반적으로 관여하는 개발 분야라고 할 수 있겠다. Docker에서도 이야기 했듯, 프로젝트를 사용자가 도달할 수 있는 곳에 배포하는 과정은 생각보다 더 많은 일이 발생한다. 앞에도 언급했지만,

새로운 기술은 불편함과 귀찮음을 해소하려고 만들어진다고 나는 생각한다.

이 관점에서 봤을 때, DevOps는 운영으로 넘어가는 과정에서 발생하는 불편함을 해소하기 위한 분야라고 생각한다. 그러한 불편함을 해소하기 가장 좋은 방법은, 컨베이어 벨트 위의 재료들이 자동으로 조립되듯, 내 코드가 자동으로 조립되어 사용자에게 전달되게 하는 것 아닐까? 그래서 DevOps 하면 CI/CD가 먼저 떠오르는 것 같다.

CI/CD가 없었을 땐?

시작은 개발자가 작성한 코드 부터이다. Spring Boot를 비롯한 웹 프레임워크로 작업을 하고있다 가정해보자. Repository 부터 Controller 까지 연결하고, 해당 내용을 테스트 한다고 가정하자. 물론 단위 테스트도 작성하고, 통합 테스트도 작성해볼 수 있다. 최종적으로는 Postman으로 요청을 보내보는 것 까지 해봐야, 내가 만든 기능이 의도대로 작동하는지 확신을 가질 수 있을것이다.

postman runner 그래서 Postman은 Runner 기능도 제공한다.

그런데 이제 많은 개발자가 모여서 자신이 만든것을 본 코드에 추가하려고 할때, 문제가 발생하기 시작한다. 코드를 통합하는 것은 서로의 존재를 모르던 것들이 앞으로 상호작용 해야 한다는 뜻이고, 내 코드만 따로 동작하는 상황에선 발생하지 않던 문제들이 발생하게 된다. 그래서 테스트 코드가 많이 보급되긴 했지만, 결국 사람이 직접 코드를 합친 다음 테스트 코드가 문제 없다는걸 확인해야 한다. 결국, 개발자가 일일히 직접 코드를 통합하는 것은 많은 인력과 시간을 소모하게 된다. 그렇다고 통합하는 단계를 미루다보면? 더 큰, 이제는 해결하기 더 어려운 문제가 되어 개발자를 덥치게 된다.

postman runner Conflict는 Git을 쓰면서 코드를 통합할 때 만나는 짜증나는 상황 중 하나이다.

CI의 목표가 여기서 나온다고 생각할 수 있다. 이름부터가 Continuous Integration, 지속된 통합이다. 즉 도구를 이용해 코드를 통합하는 과정을 좀더 유연하게 진행하자는 의미이다. 테스트 코드를 실행하고 결과를 정리하는 것은 방법만 안다면 주니어 개발자도 할 수 있을 정도의 루틴한 작업이다. 그렇다면 그 과정을 컴퓨터한테 맡기는 것도 가능할 것이다. CI의 첫 목표는, 이런 루틴한 작업을 코드를 통해 자동화 함으로서, 통합을 하는 주기를 줄이는 것이라 볼 수 있다.

기능을 만들고 한곳에 합쳤다. 이걸로 끝인가? 새로운 기능은 당연히 사용자에게 도달해야된다. 여기서 등장하는게 CD가 된다. 여기서는 두가지를 지칭하는 말이라 조금 햇갈릴 수 있는데, 하나는 Continuous Delivery, 다른 하나는 Continuous Deployment이다. 여러 글들을 찾아보면 결국 Delivery는 실제로 사용자에게 전달되는 배포 단계"는" 자동으로 하지 않는다 정도의 이야기를 찾을 수 있다. 어쨋든 핵심은, CI를 통해 통합된 코드를, 자동으로 배포(Deploy) 하거나, 배포 가능한 상태(Deliver)로 만들자는 이야기이다.

즉 CI/CD는 일반적으로 개발자들이 서비스 운영을 위해 손으로 진행하던 내용을 자동으로 하자는 이야기다. 잘 구축된 CI/CD 파이프라인은, 배포 과정에서 개발자 본인을 거의 배제할 수 있다. 개발자는 자신의 코드 자체에만 집중하고, 통합되었을 때의 문제를 보고받아 작업할 수 있다. 통합된 코드가 문제가 없다면, 개발자가 신경쓰지 않아도 사용자에게 자동으로 전달되는 것이다!

CI/CD를 위한 도구는?

정말 지금은 상상도 하기 어려운 이야기일지 모르겠다. 면접에 들어간지도 꽤 되었으니까. 하지만 실제로 있었던 이야기이다. 당시에 면접에서 꼭 언급하는 질문이 있었다. "Git 쓸 줄 아나요"? 그러면 정말 신기한 대답이 들어오는 경우가 한번씩 있었다.

Git은 안써봤는데, GitHub은 써봤어요.

도대체 무슨 소리였을까? 아마 "못한다고 하기는 그렇지만, 맨날 'GitHub'에 프로젝트 코드를 올렸고 'Git'이란 이름이랑 비슷하니까, 뭔진 기억안나도 비슷한걸 할 줄 안다고 어필해보자"라고 그 자리에서 임기응변으로 대응했을 것이다. 물론 Git을 써본 개발자 입장에서는 이게 오히려 익숙하지 않음을 어필하게 될것을 쉽게 예상할 수 있을것이다...

지금은 상상하기 힘들지만, Git이 없던 시절도 있다. Git의 역사 자체를 찾아보는 것도 재밌을 것이다. 그때는 가장 대중적인게 SVN이었고, Git의 개발자가 Git 개발 전에 사용하던건 BitKeeper였다. 이들의 목표는 "프로젝트의 버전 관리"였다. 당연하지만 이들은 서로 다른 소프트웨어고, 사용법도 전혀 달랐다. 하지만 어쨋든 "프로젝트 버전 관리"라는 주제가 있으니, 그걸 바탕으로 이해하려고 노력해볼 수는 있다. 그렇다면 차라리 앞의 질문에 대한 답변은 이게 나을지도 모르겠다.

Git은 안써봤는데, SVN은 써봤어요.

적어도 각 기술과 용어의 목적은 잘 이해하고 있다는 의미니까.


CI/CD는 상황이 조금 다르긴 하다. 하지만 비슷한 부분도 있다.

CI/CD라는 말을 생각해보면, "Git" 보다는 "버전 관리"와 유사한 위치에 있다. 즉, CI/CD는 방법론이고, CI/CD를 할 수 있게 해주는 여러 도구들이 존재한다는 뜻이다. 대표적으로 Jenkins, TravisCI, GitLab CI/CD, GitHub Actions 등이 있다. 당연한 이야기지만, 이들을 사용하는 방법도 전부 다르다.

⚠️ 아래 문단들은 필자의 주관이 상대적으로 많이 반영되어 있다.

DevOps의 범주에 CI/CD만 있는게 아니라는 사실은 배제하고 생각해보자. 좋은 DevOps 개발자가 되는게 위에 나열한 수많은 도구들을 실수 없이 잘 사용하는 것일까? Git과 SVN만 하더라도, 경력이 많은 선대 개발자 분들은 여전히 Git의 방식이 어렵게 느껴지는 분들이 있다. 이런 분들은 전부 무의미한 경력만 찬 개발자인가?

아까 CI/CD와 Git의 상황은 조금 다르다고 했는데, 이 다른 부분이 오히려 더 큰 문제이다. 무엇이 다르냐면, CI/CD는 아직 Git과 같은 절대적인 인기의 도구가 없다고 생각한다. 그런게 없다면, "모든 도구를 골고루 잘하는 것"보다, "하나의 도구를 잘 사용하고", "그걸 나의 상황에 맞게 구성"하는 능력이 중요하지 않을까?

궁금해서 레딧에서 사람들 의견도 찾아봤다. Which CI/CD learn first?

뭘 기준으로 자동화를 하는가?

CI/CD도 Docker와 마찬가지다. Docker는 이전의 배포 과정의 어려움을 해소해준다. CI/CD도 비슷한, 배포 과정에서 생기는 통합의 어려움을 다른 각도에서, 자동화라는 방법으로 해소해준다. 그리고 자동화라는 것은 자동화되기 전의 방식이 있었다는 것이다. 그말은 결국, CI/CD 도구도 전부 내가 직접 하던걸 대신 해주는 도구이며, 잘 사용하기 위해선 내가 무엇을 해야하는지를 정확히 알아야 된다는 것이다.

우리가 코드를 통합하고, 배포하는 과정은 어떻게 될까? 편의를 위해 일단 Docker를 제외하고 생각해보자. Spring Boot를 기준으로

  1. 프로젝트 코드를 한곳(Git이라면 한 브랜치)에 모은다.
  2. 코드의 테스트 코드를 실행하여 기능에 문제가 없는지 확인한다.
  3. 코드를 빌드하여 실행 가능한 형태, 예를들어 JAR 파일로 만든다.
  4. 환경이 준비된 컴퓨터로 JAR 파일을 옮긴다.
  5. 운영용 설정을 전달하여 JAR 파일을 실행한다.

의 작업을 진행해야 한다. 물론 세부 상황은 어떤 클라우드, 어떤 기술을 사용하느냐에 따라 다를 것이다.

이중 1 ~ 2를 GitHub Actions로 실행하기 위한 설정 파일을 만들어 보았다. 즉, 모인 코드에 대하여 CI를 진행하는 설정이다.

name: Java CI with Gradle

on:
  push:
    branches: [ "main" ]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v4
    - name: Set up JDK 17
      uses: actions/setup-java@v4
      with:
        java-version: '17'
        distribution: 'temurin'
    - name: Setup Gradle
      uses: gradle/actions/setup-gradle@v3.1.0

    - name: Test with Gradle Wrapper
      run: ./gradlew test

이게 무엇을 하는건지 이해가 되는가? 몇가지 설정을 살펴보면,

  • on: 언제 작업들을 실행하는지 설정한다.
    • .push.branches: ["main"]: 어떤 특정 브랜치(여기선 main)에 커밋이 작성되면 작업을 실행하도록 설정한다.
  • jobs.<job>: 각 작업들을 구성한다. 여기선 test 작업을 구성한다.
    • runs-on: ubuntu-latest: 최신 Ubuntu 버전에서 진행한다.
    • steps: 하나의 작업의 세부 단계를 구성한다. 여기선 다음 순서로 진행하는데,
      1. 소스코드를 가져온다.
      2. temurin 배포판 JDK 17버전을 사용한다.
      3. Gradle을 사용하도록 설정한다.
      4. Gradle을 사용해 test 작업을 진행한다.

...어찌보면 배포하는 것보다 더 복잡한 느낌이다. 겨우 테스트 코드 한번 돌리는데! 한번 실행하고 이미지를 만드는 Docker와 다르게, 여기서는 코드를 가져오기, 빌드하기, Docker로 만들기 등의 모든 작업들을 자동화 할 수 있기 때문이다. 그래서 가능한 세부적으로 설정을 전달할 수 있도록 만들어져서, Dockerfile 이상으로 더 많은 내용을 작성해주어야 하는 것이다. 만약 여기에서 코드를 빌드해서 JAR로 만드는 과정을 더한다면,

jobs:
  test:
    # ...
  boot-jar:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Set up JDK 17
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'
      - name: Setup Gradle
        uses: gradle/actions/setup-gradle@v3.1.0
        
      - name: Build JAR with Gradle Wrapper
        run: ./gradlew bootJar
      - name: Upload JAR
        uses: actions/upload-artifact@v4
        with:
          name: artifact
          path: build/libs/*.jar

....위처럼 만들 수 있다. 거의 비슷한데, 마지막 몇단계만 바뀌었다. Upload JAR가 추가되었는데, 이는 다른 곳에서 사용할 수 있도록 JAR를 만드는 과정이다.

여기서 testboot-jar의 설정이 비슷해 보일 수 있는데, 여러 Job은 병렬적으로 서로 다른 컴퓨터에서 동작할 수 있어서 설정의 앞부분을 일단 복붙한 것이라 그렇다.

이 다음은? JAR 파일을 서버로 보내고, 서버에서 원래 실행중인 JAR 파일을 종료하고 새로 다운받은 JAR 파일을 실행하도록 조정해야한다......만약 AWS를 사용한다면 여기서 CodeDeploy라는 서비스를 추가로 사용하기도 한다.

지금은 JAR로 진행하지만 원한다면 Docker 이미지로 만드는 방법도 있다. 이미지를 TAR로 추출하거나, Docker Hub에 올릴 수도 있을것이다. 그 외에도 구성할 수 있는 방법이 많을것이다.


위의 예시는 GitHub Actions를 사용한 경우이다. GitHub Actions를 사용한 이유는 GitHub이 무료로 제공하며, 설정이 복잡하지 않기 때문이다. 그렇다면 다른 CI/CD 도구들은? GitLab CI는 GitHub Actions와 비슷하다. 대신 GitHub이 아닌 GitLab을 주로 사용한다. Jenkins는 아예 이 작업을 자동으로 실행해줄 서버를 구성해야 한다(고 알고 있다). 그 외에도 수많은 CI/CD용 도구들이 있고, 그들은 제각각 다른 방법으로 사용해야 한다.

굉장히 많은 CI/CD 도구가 있고, 이름을 들어본적 있다면 대부분 어느정도 수요가 있다. 언젠간 하나를 전문적으로 배워야 할 수 있겠지만, 내가 CI/CD에 입문하는데 중요한게 어떤 도구를 사용하느냐 일까? DevOps와 CI/CD는 안정적이고 지속적인 서비스의 전달을 목표로한다. 그말은 단순히 코드를 다루는 개발도, 테스트 코드의 작성도, 배포를 위한 지식도 사실상 기본 선수지식이라는 이야기이다. 그만큼 어렵고 복잡한 분야이다. 그런데 아직 Linux도 써보지 않은 입장에서 CI/CD를 찾는 이유는 도대체 무엇일까?

반대로 어떤 도구를 쓰던간에 그 도구가 이해할 수 있는 형태로 설정을 전달하게 된다. 이 설정들은, 내가 자동화 하고 싶은 작업을 묘사하는, 도구가 없었다면 개발자가 직접 했을 작업들을 묘사한다. 그렇다면 내가 그 과정을 정확히 이해하고 묘사하는 능력이 오히려 CI/CD를 익히는데 중요한 지식 아닐까?

결론

이해는 한다. 매일 모니터에서 나오는 화려한 영상들을 보고 있으면, 검은 화면에 깜빡이는 커서가 컴퓨터의 본질이라는 사실을 외면하고 싶을 것이다. 마우스를 사용해서 클릭하던 습관이 쉽게 사라지는 것도 아니다. 그래서 서버에서 직접 작업을 하게 해주는 기능들, IDE와 비슷한 환경을 제공하는 기능들도 계속 등장하게 된다.

하지만 그들의 본질은 결국 Linux OS를 사용하는 컴퓨터라는 것이다. 그래서 Docker를 사용하던, CI/CD를 사용하던, 결국 Linux를 다루는 방법과 특징을 익힐 필요가 생기게 된다. Docker Compose, 무중단 배포같은 더 복잡한 기능을 구현하고 싶을때 더욱 그렇다.

플라톤이 소크라테스, 파이드로스와 나눈 이야기를 다루는 파이드로스에는 이집트 신화 중 지혜의 신 토트(Thoth)와 상이집트의 왕 타무스(Thamus)의 이야기가 등장한다.

토트는 지혜의 신으로서 문자를 발명하였고, 이를 타무스에게 널리 퍼뜨리라고 전했다. 토트는 문자가 사람들의 기억을 돕고 지혜를 향상시킬 것이라고 이야기했다.

그에 대한 타무스의 답변은 이러했다.

...당신의 발명은 학습자들의 영혼에 망각을 만들어낼 것입니다. 왜냐하면 그들은 기억을 사용하지 않을 것이기 때문입니다. 대신 그들은 외부에 쓰여진 문자에만 의지하여 스스로 기억하려고 하지 않을 것입니다.
...for this discovery of yours will create forgetfulness in the learners' souls, because they will not use their memories; they will trust to the external written characters and not remember of themselves.

Docker와 CI/CD는 현대 개발에서 빠질 수 없는 중요한 요소이다. 개발자라면 물론 당연히 시도해봐야 한다. 하지만 그 기술이 탄생한 배경, 사용하는 환경이야말로 중요한 내용이다. 그걸 알지 못한다면, 문자로 인해 기억을 잃듯이, 적어둔 설정 없이는 내가 해야할 일을 찾아내지 못하는 반쪽짜리 기술만 다루는 개발자가 될것이다.

Linux를 비롯한 서버 작업의 마스터가 되자는 이야기가 아니다. 적어도 알아야 하는 만큼은, 완전 배제하지 않고 진행하길 바라는 이야기이다. 중간 과정을 건너뛰고 원하는 목적지에 도달하려고 한다면, 알 수 없는 곳에 조난당할 것이다. 망망대해 한가운데 있는 섬에 서있으려고 하지 말고, 그 섬까지 가는 길을 아는 방법을 찾아가는 자세를 가지도록 하자. 그러면 내가 가다가 길을 잃어도 돌아가는 방법 정도는 배울 수 있을 것이다.

P.S. velog 작성할 때 어떤 HTML은 출판된 글에도 적용되고, 어떤 HTML은 적용 안된다....슬프다.

0개의 댓글

관련 채용 정보