Saramin 형상관리 전환과정을 설명합니다.
SSG TF 소개
사람인 서비스의 형상관리 전환을 위해 IT연구소 내부에서 자발적 지원으로 구성된 TF팀이며 Saramin SVN to Git 이라는 의미를 담고 있습니다. 저희 SSG 팀에서 SVN 에서 GIT으로의 형상관리 전환과정에 대해 공유드리고자 이 글을 작성합니다.
배경
사람인의 주요서비스 중 일부는 지난 10여년간 SVN (Subversion)을 사용해왔습니다. 회사가 성장해가면서 개발인력과 여러 프로젝트가 나날히 늘어갔고 사람인의 SVN을 통한 개발 프로세스는 여러가지 문제점들이 있었습니다.
기존 개발 프로세스의 문제점
개발환경 구성
SVN 을 통한 기존의 사람인 개발 프로세스는 하나의 유지보수 브랜치 (maintenance) 와 trunk 를 두고 모든 개발 인력이 유지보수 브랜치에서 작업을 하거나 필요시에는 신규 브랜치를 생성하여 개발해 왔습니다.
이 과정에서 신규 브랜치 및 웹서버 구성을 담당 개발자가 직접구성하였고 계속되는 불편함을 느낀탓에 자동 구성 스크립트를 작성하거나 Jenkins CI 를 통한 구성을 해보기도 했습니다. 하지만 SVN 의 느린속도와 구성에 적지않은 시간이 소요되는 것은 마찬가지였습니다.
소스 프리징
사람인 서비스는 정기적인 배포일정에 맞춰서 배포를 진행합니다. 위에서 말한 유지보수 브랜치 또는 프로젝트 브랜치에서 개발과 QA가 완료된 프로젝트들은 배포일정에 따라 trunk 에서 통합테스트를 진행 후 배포를 해왔습니다.
언뜻 보기엔 무리없어 보이는 개발흐름 이지만 대규모 프로젝트 및 장기 프로젝트는 통합테스트 기간이 길어 질 수 밖에 없었습니다. 이로 인해 테스트 기간동안 정기적인 배포일정은 중단됩니다. 이것을 사람인에서는 소스 프리징이라 불러왔습니다.
소스프리징 기간 중에는 배포가 나가기 어려운 상태였기에 다른 프로젝트들의 런칭에 일부 영향을 주게되었습니다.
merge 의 잦은 충돌 및 불편함
SVN 에서 브랜치의 작업내역을 trunk 로 merge 하는 과정은 프로젝트가 크고 장기적일수록 어렵고 힘들었습니다. revision 이 누락되면 바로 충돌이 나기 십상이고, 충돌해결을 위해 작업자들이 다 달라붙어 해결해야했습니다. 작업내용이 크고 많을때는 merge 에만 반나절 넘게 소요된 적도 있었습니다.
전환의 시작
가장 먼저 해야할 것은 브랜치 전략을 확립하는 것이었습니다. 기존의 프로세스의 문제점들을 보완하면서 크게 복잡하지 않으면서도 사용과 이해가 쉬운 전략을 도출해야 했습니다. 소규모 조직이 사용할 브랜치 전략이 아닌 70여명의 개발자들이 함께 사용해야 할 브랜치 전략이었기에 저희 SSG는 토의하고 또 토의했습니다.
브랜치 전략 도출
Git 기반의 대표적인 work flow 에는 git-flow, gitLab-flow, gitHub-flow 가 있습니다. 해당 flow 들에 대한 개념을 이해해보고 각각의 특징과 장단점 들을 확인해보게 되었습니다.
하지만 그 무엇도 사람인의 환경에 부합하는 flow 는 없었습니다. 이에 저희 SSG는 각 flow 들의 장점들을 참고하여 사람인의 개발 프로세스에 최적화 된 독자적인 flow를 구성해 나가기 시작했습니다.
사람인 특징
사람인 개발 프로세스의 특징을 살펴보면 아래와 같습니다.
- 지속적으로 변하는 웹 어플리케이션
- 모놀리틱 서비스 (일부 서비스 제외)
- 하나의 저장소에서 수많은 개발자들이 작업 진행
- 크고 작은 많은 프로젝트들이 동시에 진행
- 정기적인 배포 체계
- VOC 및 운영에 대한 빠른 대처가 필요
이런 특징들을 바탕으로 세 가지의 사항들을 중점적으로 고려했습니다.
프로젝트별 별도의 개발환경 구성필요
사람인의 서비스는 일부 서비스를 제외하면 모놀리틱(Monolithic) 아키텍쳐 서비스이고 하나의 저장소에서 수십명의 개발자들이 수많은 프로젝트를 동시에 진행하고 있습니다. 이렇다보니 하나의 유지보수 브랜치와 규모에 따라 몇몇의 브랜치로 나누어 작업하던 기존의 형태는 많은 문제가 있었습니다. 히스토리의 추적이 어렵고 merge 의 복잡도가 증가하게 되었습니다.
이러한 문제 해결을 위해 git 의 branch 와 Gitlab CI/CD 연동을 통해 개발환경들을 자동으로 구성할 수 있도록 설계하였습니다. 각 프로젝트들은 release
브랜치로부터 파생된 feature
브랜치를 생성하여 관련된 개발자 및 작업자들이 해당 브랜치에서 모든 개발과 코드리뷰, 테스트 및 QA를 진행합니다. QA 가 완료된 feature
들은 배포일정에 맞춰 release
브랜치로 merge 되어 통합테스트를 진행하게 됩니다.
통합테스트가 가능한 환경필요
사람인은 정기적인 배포 체계를 가지고 있습니다. 해당하는 배포시점에는 여러 프로젝트들이 함께 배포가 될 수도 있었기에 연관된 이슈가 없는지 배포전 최종 통합테스트가 이루어져야 합니다. 이러한 통합테스트는 master
브랜치로부터 파생된 release
브랜치를 유지브랜치로 두고 통합테스트 환경을 구성했습니다. release
브랜치에서 통합테스트가 완료된 배포건들은 정기 배포 일정에 따라 master
로 merge 되어 배포됩니다.
언제든지 운영배포가 가능한 환경필요
앞서 얘기한 “소스프리징” 문제 의 이유로 항시 배포가 가능상태의 stable 한 브랜치가 필요했습니다. 소스프리징 기간동안 다른 배포를 위해 trunk 를 일괄 롤백하는의 문제를 피하기 위해 master
브랜치를 항상 배포가능한 유지브랜치로 두었습니다.
소스프리징 기간에 긴급 배포건 발생시 master
브랜치에서 파생된 hotfix
브랜치를 생성하여 오류수정 및 검증 후 다시 master
브랜치로 merge 하도록 구성하였습니다. 배포가 완료된 hotfix
건은 다시 release
브랜치로 merge 되어 동일한 형상을 유지하게끔 하였습니다.
사람인 Flow
이렇게 구성한 사람인의 브랜치 흐름을 도식화하면 아래와 같습니다.
- master
- 유지 브랜치
- 항시 배포가능한 상태를 유지
- Protected branch 이며 권한자에 의해 관리 (Maintainer)
- release
- 유지 브랜치
- 통합테스트 브랜치
- 배포 대상인
feature
브랜치들이 모여 통합테스트 진행 master
브랜치로부터 파생됨
- feature
- 보조 브랜치
- 기능개발 브랜치
- 배포 일정에 맞춰
release
로 merge release
브랜치로부터 파생됨- 각
feature
브랜치는 주기적으로release
변경 내역을 가져와서 반영 - 배포 완료 후 해당
feature
브랜치 삭제
- hotfix
- 보조 브랜치
- 운영 간 긴급 오류 해결을 위한 브랜치
master
브랜치로부터 파생됨- 배포 완료 후
release
브랜치로 merge 및 해당hotfix
브랜치 삭제
개발흐름
사람인의 기본적인 개발흐름을 도식표로 나타내었습니다.
flow 실습
최종적으로 구성된 flow 를 실제로 실습해보며 검증하는 단계는 중요합니다. 개발환경과 동일한 테스트용 GIT 저장소를 구성하고 다양한 commit & push, MR(Merge Request) 등을 생성해보며 자유롭게 테스트를 진행합니다. 구성된 flow 가 적합한지, 누락사항 및 다른 문제는 없는지를 파악하고 수정할 부분들을 수정해가며 재구성합니다.
CI/CD 구성
사람인은 다양한 서비스 제공을 위해 수많은 개발자들이 서비스 개선 및 신규 프로젝트를 진행하고 있습니다. 이 과정에서 자유롭고 빠른 개발환경의 구성이 필요했습니다. GIT 의 핵심적인 기능중 하나인 branch 와 Gitlab 의 CI/CD 를 통해 원하는 환경구성을 할 수 있었습니다.
Gitlab runner
Gitlab runner 는 Gitlab CI/CD 와 함께 작동하여 파이프라인 상에서 명세한 작업을 수행하고 결과를 피드백해주는 오픈소스 프로젝트 입니다. Gitlab runner 가 설치된 환경에서 Gitlab 저장소의 runner 를 등록합니다.
runner 등록 방법은 간단합니다.
# gitlab-runner register
해당 명령을 통해 대화식으로 등록가능하고 등록옵션을 통해서도 가능합니다.
Gitlab Runner Docs
.gitlab-ci.yml
.gitlab-ci.yml 은 runner 가 수행해야할 작업들을 명시합니다. Gitlab 저장소의 최상위에 위치하게되며 해당 yml 에 명시된 작업들을 runner 가 수행하게 됩니다. stage 를 정의하고 각각의 stage 단계에 정의된 job 들을 수행하게 됩니다.
구성예시
stage:
- build
- test
- deploy
...
cache:
key: $CI_COMMIT_REF_NAME
paths:
- vendor
...
build:
stage: build
script:
- composer install --prefer-dist
...
eslint:
stage: test
...
deploy:
stage: deploy
script:
- echo "run to deploy"
...
아래와 같이 특정 브랜치를 지정하여 해당 브랜치의 이벤트만 수행하거나
only
- release
변수를 설정할 수 도 있으며
variables:
HOST_NAME: "staging"
SERVER_NAME: $staging
특정 job 의 alias 를 선언/사용할 수도 있고
.sync: &sync
stage : deploy
script : echo " run script ... "
....
staging:
<<: *sync
....
또한, 정규표현식을 지원하며 아래와 같이 rule 구성도 가능합니다. when: manual 옵션으로 특정 job을 수동으로 처리할 수 도 있습니다.
rules:
- if: '$CI_COMMIT_BEFORE_SHA !~ /0{40}/ && $CI_COMMIT_REF_NAME != "master" && $CI_COMMIT_REF_NAME != "release"'
when: manual
allow_failure: true
이 밖에도 다양한 job 키워드들을 제공하니 Gitlab 레퍼런스 문서를 참고하시면 됩니다.
.gitlab-ci.yml reference
사람인의 CI/CD 구성
최종 구성된 사람인의 CI/CD 흐름입니다.
마찬가지로, 구성한 CI/CD 를 검증하는 단계가 필요합니다. flow 의 흐름과 맞는지, CI/CD 의 수행은 오류없이 정상동작 하는지 반복적인 테스트를 통해 완성됩니다.
전환전 준비
이렇게 브랜치 전략과 정책, CI/CD 구성을 완료 후 전환 시나리오를 구성하기 시작하였고, 시나리오 구성에 앞서 몇가지 준비사항들이 있었습니다.
SVN 히스토리
사람인의 경우 약 10여년 간의 누적된 히스토리가 존재했습니다. 최초 svn2git 을 통해 모든 히스토리를 가져오려 시도했으나, 20만여건의 히스토리로 인해 많은 시간이 소요되었습니다.
(svn2git 은 내부적으로 git-svn
명령들을 사용합니다.)
또한 이렇게 히스토리를 포함한 저장소는 기존 사람인의 배포를 위한 통합테스트 디렉토리 (현 master 디렉토리)와 교체를 해야했는데, 이로인해 모든 파일들을 배포해야하는 이슈가 있었습니다.
이러한 문제들로 인해 통합테스트 디렉토리를 기준으로 git init
하여 저장소를 신규로 구성하고 history 는 기존 정책서 및 svn 서버를 당분간 남겨두어 확인하기로 하였습니다.
일정
전환 일정은 전환과정에 있어 중요한 사항이었습니다. 프로젝트들의 개발기간 및 QA, 배포 일정에 영향을 줄 수 있는 부분이었기에, 유관부서에 공유하여 진행중인 프로젝트의 일정들을 취합후 가장 적절한 시점을 정했습니다.
이관 방안 검토
기존 형상의 저장소 변경으로 인한 작업내역 이관 방안도 고려해야했습니다. 이미 SVN 형상에 커밋한 리비전들 및 파일들을 오롯이 신규 Git 저장소로 옮겨와야 했기에 두 가지의 방안을 검토했습니다.
IDE 의 patch file 추출을 통해 리비전 이관
TortoiseSVN 의 revision history file export 하여 이관
Git Version UP
CentOS 7 환경의 개발환경들은 기본적으로 1.8.3.1 의 git version 이 설치된 상태였고 이로인해 앞서 Gitlab CI/CD 구성간 한 가지 문제가 있었습니다.
CI/CD 수행간 Shallow clone 설정을 해두었으나 아래와 같은 오류메세지로 정상적으로 Gitlab Build 가 되지 않는 문제가 있었습니다. (Shallow clone 은 저장소 히스토리의 일부만 받아오는 것으로, 파이프라인 실행속도를 높이기 위한 설정입니다.)
fatal: git fetch-pack: expected shallow list
fatal: The remote end hung up unexpectedly
해당 오류를 검색해본 결과 낮은 git version 으로 인한 문제가 확인되어 runner 서버 및 개발 환경들의 일괄적인 git version up 을 진행했습니다.
관련링크
배포 환경 구성
사람인의 배포환경은 배포를 위한 특정 디렉토리가 존재합니다(현 master 디렉토리). 전환 전에는 배포서버에 접근하여 rsync 명령을 통해 앞서 말한 배포 디렉토리와 동기화를 시키는 형태로 진행했습니다. 서버에 매번 접근하여 실행하는 것이 비효율적이라 생각되어 Gitlab CI/CD 를 통해 build - master sync - dry-run - deploy
단계를 거쳐 배포하는 형태로 파이프라인을 구성하였습니다.
유관부서 공유
기존의 유지보수 브랜치에서 대부분의 QA 및 테스트를 진행하던 부분이 개별 프로젝트 브랜치로 변경이 되면서 기획 및 QA 등 유관부서에 전환 후 변경사항들을 공유하였습니다.
가이드 작성
이렇게 구성한 브랜치 전략과 개발 흐름에 대해 개발자들을 이해시키고 공유하는 것은 중요합니다. 전환의 기본적인 안내와 전환 후의 장점을 부각시키고 브랜치 전략과 전반적인 개발흐름을 가이드로 작성하여 제공하였습니다.
전환 단계
모의테스트
전환 수행 시 순차적으로 각 단계를 나누는 시나리오를 구성했습니다. 이렇게 구성한 시나리오는 별도의 테스트 환경에서 반복적으로 모의테스트를 진행하며 이슈는 없는지, 누락사항은 없는지 확인하였습니다.
svn 제거
master 가 될 디렉토리는 svn 연결이 해제되어야 했기에, 일괄적으로 아래 명령을 통해 제거하였습니다.
find . -type d -name .svn -exec rm -rf {} \;
.gitignore 파일 수정
형상관리가 불필요한 파일들의 경로를 추가하여 형상관리에서 제외하여 저장소의 용량을 최소화 합니다. 제외파일에는 로그 및 캐시파일, hidden 파일 등을 포함합니다.
.gitattributes 파일 추가
사람인 개발자들의 대부분의 환경은 windows 였기에, 개행문자 자동변환을 위해 .gitattributes 파일을 생성하여 추가합니다.
* text=auto
*.html text diff=html
*.css text
*.js text
...
저장소 생성과 git init / remote 연결 / push
이제 저장소를 생성하고 init 합니다. 생성된 remote 를 git remote add
를 통해 연결하고 파일들을 일괄 추가하고 앞서 구성한 .gitignore 및 .gitattributes 파일들도 함께 추가합니다. 추가된 파일들을 master 로 push 합니다.
.gitkeep 파일 추가
형상관리를 해야할 빈 디렉토리가 있다면 (캐시 및 log 디렉토리 등) .gitkeep 파일을 추가하여 관리합니다.
.gitlab-ci.yml 작성
저장소 구성까지 완료되었다면 이제 CI/CD 를 구성할 차례입니다. 앞서 미리 구성해둔 .gitlab-ci.yml 을 추가합니다. CI Lint 를 통해 문법에 이상은 없는지와 각 stage 별 실행 job 들을 다시한번 확인합니다.
release branch 생성 및 branch 설정
master 를 기준으로 release branch 를 생성합니다. master 는 protect branch 가 되어야하고 release branch 는 default branch 가 됩니다. 이후 모든 feature branch 들은 이 release branch 를 기준으로 생성되고 관리 됩니다.
CI/CD 구성 및 점검
저장소의 CI/CD 설정을 점검하고 runner 서버에서 해당 저장소의 runner 를 등록합니다. 등록 후 runner 가 정상적으로 online 상태가 되었는지 확인합니다.
등록이 되었다면 이제 앞서 CI/CD 구성간 검증한 내용들이 올바르게 작업을 수행하는지 확인합니다. 파이프라인들을 모니터링 하며 failed 상태의 job 을 확인하여 오류를 수정합니다.
작업내역 이관
이제 전환의 막바지에 이르렀습니다. 앞서 제공한 이관가이드를 참고하여 개발자별로 진행중인 작업에 대해 feature branch 를 생성하고 작업내역들을 옮겨오게 됩니다. 이 과정에서 아래와 같이 주의할 점이 있습니다.
IDE patch 파일 이용시
리비전 단위로 patch 를 하므로 apply 순서에 유의해야함
Tortoise SVN history file export 후 이관시
본인 혹은 협업자가 작업한 부분인지 반드시 확인후 이관
배포 점검
구성한 배포 프로세스 점검을 위해 일부 테스트 파일을 최종 배포단계까지 반영해보는 점검을 진행했습니다. feature 를 생성하고 release 로 merge 및 master 까지 반영 후 구성해둔 배포 파이프라인 각 단계별로 실행하며 점검합니다. 최종적으로 배포까지 이상없이 수행하면 점검은 일단락됩니다.
전환 후 업무지원
전환 후 1 ~ 2 주간은 파이프라인에 대한 모니터링을 진행하고 지원이 필요로 하는 부서 혹은 동료를 지원하여 작업 내역 이관 및 git 사용에 대한 이해를 도왔습니다. 모든 파이프라인이 failed 없이 passed 상태인지를 확인하고 모든 개발자가 작업 이관이 완료 되었는지를 확인합니다.
후기
전환작업에 있어 가장 유념했던 부분은 전환으로 인해 운영 서비스에 영향이 없도록 해야했습니다. 단계별 시나리오를 구성하여 반복적인 모의테스트로 영향도는 없는지 시나리오에는 문제가 없는지를 판단했습니다. 다소 아쉬운 부분들은 남아있지만 노후화된 형상의 교체로 개발 효율을 높여 부서에 기여할 수 있었습니다.
많은 도움과 지원해주신 SSG TF와 IT 연구소분들께 감사드립니다.
SSG TF
사람인IT 연구소 김종성, 조성창, 이현재, 엄기화
출처 및 참고사이트