Submodule CI/CD 통합하기

endmoseung·2024년 5월 5일
2
post-thumbnail

최근 사내 웹 프론트엔드에서 앱과 웹에서 사용할 수 있는 모듈을 사용하기 위해
Submodule을 사용하고 있는데, 배포 과정에서 문제가 생겼었습니다.
그래서 이 글은 약간의 Submodule에 대한 제 생각과, 당시 자동화과정에서 생긴 문제를 해결한 과정을 작성한 글입니다.

1. Why Submodule?

해당 모듈은 현재 사내 웹가 앱에 전반적으로 사용되는 비즈니스로직이 포함돼있으며, 각 서비스에서 Submodule형태로 App과 Web에 전반적으로 사용중에 있습니다.
이를 Submodule형태로 레포지토리를 분리한 이유는 다음과 같습니다.

  1. 같은 소스코드를 앱과 웹에서 공통적으로 사용
  2. 복잡한 비즈니스 로직을 따로 설계하여 아키텍처 분리(추후 웹과 앱에 동시에 적용 가능)
  3. 중요한 Config Key값을 따로 Private 레포지토리에서 관리

간단하게 Submodule과, Subtree를 비교해보겠습니다.

Submodule

  1. 외부 리포지토리의 특정 커밋을 참조한다.(아래 사진 참조)
  2. 외부 리포지토리는 독립적으로 관리되며, 수동으로 업데이트해야 한다.
  3. 프로젝트의 디렉토리 내에 실제 파일 대신 메타데이터를 저장한다.

    위의 내용처럼 해당 레포지토리를 수동으로 업데이트 해야하며 만약 프로젝트를 처음 받는 상황에서 Submodule을 사용하는 프로젝트를 일반적으로 clone하면 Submodule은 파일의 메타데이터만 가지고 있을분 소스코드를 다운로드 받진 않습니다. 그래서 clone하거나 이후에 소스코드를 지속적으로 통합하기 위해서는 추가적인 작업이 필요합니다.

Subtree

Submodule과 유사한 방법으로 Subtree가 있습니다. Subtree의 특징은 다음과 같습니다.

  1. 외부 리포지토리의 코드를 현재 리포지토리의 서브디렉토리에 복사한다.
  2. 외부 리포지토리와의 통합 및 관리가 상대적으로 간단하다.
  3. 프로젝트 내에서 직접 수정된 외부 코드를 외부 리포지토리로 쉽게 반영할 수 있다.

Submodule과는 다르게 Subtree를 사용하게되면 참조한 외부 레포지토리의 코드를 그대로 가져옵니다. 그래서 해당 소스코드를 clone하게되면 소스코드가 같이 clone하게 됩니다. Submodule과는 달리 소스코드를 직접 사용하기 떄문에 해당 코드에 대한 변경사항에 대한 내용을 직접적으로 git으로 추적이 가능합니다.

2. 사용법

아래의 명령어를 사용하여 submodule을 업데이트 하고 소스코드를 작업 로컬환경으로 가져옵니다. 이러면 내가 가져온 submodule의 최신 Head값으로 참조가 바뀌게 되고 이 변경 이력을 깃 형상관리에 추가해줍니다. → 이를 해주지 않으면 해당 소스코드를 받은 사람들은 참조가 바뀐지 알 수 없게 됩니다.
즉 Submodule의 레포지토리를 작업하는 개발자가 소스코드를 추가적으로 배포하면 해당 코드를 사용하는 레포지토리에서는 마지막 커밋을 바라보게 됩니다.

// subModule에 있는 코드까지 clone 받는 방법
git clone --recurse-submodules <repository_URL>
// 지속적으로 Submodule의 변경점을 update하는 방법
git submodule update --init --recursive

3. 자동화

저는 웹을 개발했고, 이 과정에서 자동화부분을 데브옵스 개발자님과 같이 신경썼습니다. 이미 CI/CD가 적용돼있어서 깃허브에서 코드 푸시이벤트를 구독하여 Jenkins에서 배포하고 있었습니다. 프로세스는 다음과 같습니다.

  1. 해당 Jenkins에서 DockerFile에 적힌 명령어를 따라 Docker Build를 진행
  2. 배포 결과물을 ECR에 등록
  3. EKS 환경에 배포 진행

이 과정에서 아무런 문제가 없었으면 제가 이 글을 쓰지 않았을겁니다. 이 부분에 대해서 자동화에 대한 자세한 과정은 이번 글에서 다루지 않기에 넘어가겠습니다.

문제점

웹 배포를 Jenkins 환경에서 배포할때 DokcerFile의 명령어에 따라 다음과 같은 프로세스로 진행합니다.

  1. 사용할 이미지를 선택({nodeversion}:{baseImage})
    ex: 18-Alpine
  2. 의존성 lock파일을 참조하여 변경점이 있다면 의존성 설치
  3. 현재 StandAlone으로 배포중이기에 app폴더 내에 있는 모든 소스코드를 Github에서 Copy
  4. 현재 브랜치에 맞춰 빌드 및 배포
  5. 실제 배포가 되면 서버환경에서 next 서버 실행

문제는 4번에서 생겼습니다. Submodule로 받아온 파일의 경로를 읽을 수 없다는것이었습니다.
이를 해결하기 위해 2가지 실험을 했습니다.

  1. 실제로 돌아가고 있는 서버의 소스코드에 Submodule로 받아온 코드가 있는지
  2. 로컬에서 새로 우리 서비스를 clone 했을때 Submodule 코드가 있는지

2가지 경우에서 모두 소스코드가 존재하지 않았고 로컬환경에서 추가적으로 위 사용법의 코드를 입력했을때 소스코드를 받아 올 수 있었습니다.

하나의 추가적으로 의문이었던점은 로컬에선 DockerBuild가 가능했다는것인데, 이 소스코드를 불러오는 과정을 CI과정에서 추가해주면 해결될거라는 확신을 얻었습니다.

4. 문제 해결

그래서 이 문제를 해결하기 위해 Docker환경에서 직접 Submodule을 받아올 수 있도록 했지만 이는 잘 되지 않았습니다. Docker환경에서는 .git 파일이 없기 때문입니다. 그리고 원래 Docker환경에 git을 올리는 행위가 보안적으로 좋은 방법은 아니였습니다.
그래서 다른 방법을 모색하여 Jenkins에서 코드를 Clone할때 git submodule update --init --recursive 명령어를 한번 더 실행하여 문제를 해결했습니다.

결론

Jenkins + Docker환경에서 CI/CD를 진행중이며 이 과정에서 Submodule과 호환이 원활하지 않아 Jenkins 설정에 직접 Submodule을 받아오는 코드를 추가하여 해결.

Submodule? Subtree?

서브모듈을 사용하면서 얻은 장점은 명확합니다. 우선 앱과 웹에 필요한 소스코드를 동시에 작성하지 않아도 하나의 레포지토리에서 개발해도 되기에 이를 사용하는 곳에선 해당 참조를 최신화해주기만 하면 됩니다.
그리고 Subtree와는 다르게 소스코드를 직접 수정하여 배포하는게 불가능한데 만약 공통으로 사용하는 모듈을 각 서비스에서 로직이 수정돼야하고 각각의 버젼으로 디벨롭돼야한다면 Submodule보다는 Subtree를 추천드립니다.

profile
Walk with me

0개의 댓글