뒤로가기

Common

프로젝트 CI/CD 개선

김준우 2021.12.31.

소개

안녕하세요. 이번 포스팅은 개발부터 운영 배포까지에 개선점 찾아, 개선하는 방법에 대해 알아볼 예정입니다. 다나와에서는 사내 내부에서 형상관리 도구는 SVN, gitlab이 있고, 오픈소스는 외부 서비스인 github을 통해 관리하고 있습니다. 저희 팀은 gitlab, github 만 사용하기 때문에 해당 프로젝트에 코드검토, 서버 배포 절차에 알아보겠습니다. 그리고 다나와의 오픈소스 운영관리 플랫폼을 사용한다는 가정하에 작성합니다.

기존 방식

문제점

  • 하나의 프로젝트에 저장소 3개 존재
  • 마스터 브랜치에 병합을 하여 충돌 문제
  • 브랜치마다 도커 이미지 이름이 상이함
  • 브랜치 증가할때 gitlab-ci.yml job 증가

배포 구성도

소스 수정이 필요할때 새 브랜치를 생성하여 코드를 수정합니다. 개발이 끝나면 마스터로 MR요청을 보냅니다. 코드검토자는 검토하고 팀장님께 검토완료 보고를 합니다. 운영 배포 승인이 되면 운영 서버 배포 프로젝트에서 다시 소스 프로젝트를 빌드 후 배포를 하게 됩니다.

Untitled

개선 방식

  • 소스 프로젝트만 유지
    • gitlab의 경우 소스 프로젝트 CI/CD 구성
    • 배포 프로젝트 제거
  • 팀내 git flow 브랜치 전략 사용
  • 도커 이미지 이름 변경 <프로젝트명>:<브랜치 or="" 버전="">

개선 구조도

기본 베이스는 git flow 브랜치방식을 접목하였습니다. 충돌의 최소화를 하고, 컨테이너 이미지 빌드시 태그 구분으로 관리가 수월해집니다.

Untitled

Gitlab 프로젝트 개선

위 구성대로 gitlab 프로젝트의 CI/CD를 구성해보았습니다. 다나와에서 사용 중인 gitlab 버전은 12.8.1 입니다. 버전에 종속적인 방식으로 구성하였고, 메이븐 프로젝트를 예로 들겠습니다.

Dockerfile 내용

컨테이너에서는 빌드 결과파일만 추가하여 실행합니다. 범용적인 사용을 하기위해 PROJECT_NAME, VERSION는 빌드시 아규먼트로 넣어 줍니다.

FROM openjdk:8

ENV TZ=Asia/Seoul
 
WORKDIR /app
 
ARG PROJECT_NAME
ARG VERSION
 
COPY target/${PROJECT_NAME}-${VERSION}.jar ${PROJECT_NAME}-${VERSION}.jar
 
CMD ["java", "-jar", "${PROJECT_NAME}-${VERSION}.jar"]

gitlab-ci.yml 내용

주석을 통해 설명하였습니다. 테스트서버 배포 및 운영 배포는 운영관리플랫폼 서비스로 진행합니다. API 호출을 통해 자동으로 교체된 이미지로 컨테이너를 재시작합니다.

stages:
- build    # 소스 빌드 단계
- docker   # 바이너리파일을 도커라이징 단계
- test     # 테스트 배포 단계
- tagging  # 태그 변경 단계
- deploy   # 운영 배포 단계
  
cache:
  paths:
    # gitlab 12.8.1은 job 마다 파일 전달 기능이 없어 캐싱으로 전달합니다.
    # strcutre) $CI_PROJECT_DIR/envs/<branch>/target
  - $CI_PROJECT_DIR/envs
    # 메이븐 캐싱
  - $CI_PROJECT_DIR/.m2/repository
  
variables:
    # 메이븐 옵션
  MAVEN_OPTS: -Dmaven.repo.local=$CI_PROJECT_DIR/.m2/repository
    # 캐시 경로 변수
  CACHE_ENV_PATH: $CI_PROJECT_DIR/envs
	  # 컨테이너 이미지 이름
  REGISTRY_IMAGE: $REGISTRY_URL/$CI_PROJECT_NAME
    # 도커파일명
  DOCKER_FILE: Dockerfile
 
before_script:
    # 버전 추출
    - DEV_VERSION="$(echo $CI_BUILD_REF_NAME|cut -d '/' -f 2)"
    - STAGING_VERSION="$(echo $CI_BUILD_REF_NAME|cut -d '/' -f 2)-RC"
    - SOURCE_VERSION="$(cat pom.xml | grep "^    <version>.*</version>$" | awk -F'[><]' '{print $3}')"
    # 캐싱할 디렉토리 생성
    - mkdir -p $CACHE_ENV_PATH/$DEV_VERSION
    - mkdir -p $CACHE_ENV_PATH/$STAGING_VERSION
 
 
##################################################################
###################### 릴리즈 CI/CD 시작 라인 ######################
##################################################################
 
Source-Build:
  image: openjdk:8
  stage: build
  script:
    - echo "1. 메이븐 빌드&패키지"
    - echo "2. target 캐싱 (save)"
    - sh mvnw package
    - cp -R target $CACHE_ENV_PATH/$STAGING_VERSION/ # 캐싱 경로로 결과파일 복사
  rules: # 브랜치에 따라 실행 여부를 지정합니다.
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: always
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: always
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: never
 
Docker-Build:
  image: docker:latest
  stage: docker
  services:
    - docker:dind
  needs:
    - Source-Build
  script:
    - echo "1. target 캐싱 (restore)"
    - echo "2. 이미지 빌드"
    - echo "3. 저장소 업로드"
    - mv $CACHE_ENV_PATH/$STAGING_VERSION/target .
    - docker build -f $DOCKER_FILE -t $REGISTRY_IMAGE:$STAGING_VERSION --build-arg PROJECT_NAME=$CI_PROJECT_NAME --build-arg VERSION=$SOURCE_VERSION .
    - docker push  $REGISTRY_IMAGE:$STAGING_VERSION
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: always
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: always
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: never
     
Staging#elk2-dev:
  image: docker:latest
  stage: test
  services:
    - docker:dind
  needs:
    - Source-Build
    - Docker-Build
  script:
    - echo "1. 스테이징 서버 업데이트 호출"
    - echo "curl http://service-mangement.danawa.io/restart"
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: never
 
 
Tagging:
  image: docker:latest
  stage: tagging
  services:
    - docker:dind
  needs:
    - Source-Build
    - Docker-Build
    - Staging#elk2-dev
  script:
    - echo "1. 운영버전 태그 (release, latest)"
    - echo "2. 저장소 업로드"
    - docker tag $REGISTRY_IMAGE:$STAGING_VERSION $REGISTRY_IMAGE:$SOURCE_VERSION
    - docker push $REGISTRY_IMAGE:$SOURCE_VERSION
    - docker tag $REGISTRY_IMAGE:$STAGING_VERSION $REGISTRY_IMAGE:latest
    - docker push $REGISTRY_IMAGE:latest
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: never
 
 
 
Deploy#esapi1:
  image: docker:latest
  stage: deploy
  services:
    - docker:dind
  needs:
    - Source-Build
    - Docker-Build
    - Staging#elk2-dev
    - Tagging
  script:
    - echo "1. 운영서버 배포 호출"
    - echo "curl http://service-mangement.danawa.io/esapi1/restart"
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: never
  
Deploy#esapi2:
  image: docker:latest
  stage: deploy
  services:
    - docker:dind
  needs:
    - Source-Build
    - Docker-Build
    - Staging#elk2-dev
    - Tagging
  script:
    - echo "1. 운영서버 배포 호출"
    - echo "curl http://service-mangement.danawa.io/esapi2/restart"
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: never
  
Deploy#esapi3:
  image: docker:latest
  stage: deploy
  services:
    - docker:dind
  needs:
    - Source-Build
    - Docker-Build
    - Staging#elk2-dev
    - Tagging
  script:
    - echo "1. 운영서버 배포 호출"
    - echo "curl http://service-mangement.danawa.io/esapi3/restart"
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: never
 
 
 
##################################################################
#################### 릴리즈 CI/CD 마지막 라인 ######################
##################################################################
 
 
 
##################################################################
####################### 개발 CI/CD 시작 라인 #######################
##################################################################
source-build:
  image: openjdk:8
  stage: build
  script:
    - echo "1. [테스트] 메이븐 빌드&패키지"
    - echo "2. [테스트] target 캐싱 (save)"
    - sh mvnw package -Dskip.test=true
    - mkdir -p $CACHE_ENV_PATH/$DEV_VERSION
    - cp -R target $CACHE_ENV_PATH/$DEV_VERSION/
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: manual
 
docker-build:
  image: docker:latest
  stage: docker
  needs:
    - source-build
  services:
    - docker:dind
  script:
    - echo "1. [테스트] target 캐싱 (restore)"
    - echo "2. [테스트] 이미지 빌드"
    - echo "3. [테스트] 저장소 업로드"
    - mv $CACHE_ENV_PATH/$DEV_VERSION/target .
    - docker build -f $DOCKER_FILE -t $REGISTRY_IMAGE:$DEV_VERSION --build-arg PROJECT_NAME=$CI_PROJECT_NAME --build-arg VERSION=$SOURCE_VERSION .
    - docker push $REGISTRY_IMAGE:$DEV_VERSION
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: manual
 
Deploy#elk2-dev:
  image: docker:latest
  stage: test
  needs:
    - source-build
    - docker-build
  script:
    - echo "1. [테스트] 테스트 서버 배포 호출"
    - echo "curl http://service-management.danawa.io/elk2-dev/restart~~~"
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^master/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^release/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^hotfix/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^develop$/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^feature/'
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^bugfix/'
      when: manual
 
##################################################################
##################### 개발 CI/CD 마지막 라인 #######################
##################################################################

릴리즈 / 핫픽스 파이프라인

needs 키워드를 통해 DAO 아치텍쳐로 구현하였습니다.

Untitled

피쳐 / 디벨롭 파이프라인

릴리즈보다 간소화하였습니다.

Untitled

Github 프로젝트 개선

gitlab에서는 gitlab-ci.yml 파일기반으로 CI/CD가 가능했습니다. github에서는 기본적으로 오픈소스에 대해서는 actions 기능을 통해 CI/CD를 제공하고 있습니다. 사용법은 이전 포스팅을 확인해주세요.

dsearch-server의 Dockerfile

도커파일은 gitlab, github 큰 차이는 없습니다. 추 후 도커파일도 고도화할 부분이 있어 보입니다.

FROM openjdk:8

ENV TZ=Asia/Seoul

RUN mkdir -p /data/indexFile
RUN apt-get update -y
RUN apt-get install rsync -y

ARG PROJECT_NAME
ARG VERSION

WORKDIR /app

CMD ["java", "-jar", "$PROJECT_NAME-$VERSION.jar"]

릴리즈 actions

릴리즈 영역에서의 workflow 입니다. 푸시 또는 PR 이벤트가 발생하면 릴리즈 브랜치의 태그로 컨테이너 이미지를 생성하게 됩니다. 아직 정식 버전이 아니기 때문에 접두사로 -rc 가 붙어 있습니다. 이미지는 github의 package에 저장하게 됩니다.

name: docker-release-ci

on:
  push:
    branches: [ release/**, hotfix/** ]
  pull_request:
    branches: [ release/**, hotfix/** ]

env:
  CONTAINER_REGISTRY: ghcr.io
  DOCKER_FILE: Dockerfile.flow
  TAG_SUFFIX: -rc

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
#     소스 체크아웃
    - uses: actions/checkout@v2
    
#     저장소이름 추출
    - name: PROJECT_NAME Exctract
      run: echo "PROJECT_NAME=$(echo $GITHUB_REPOSITORY|cut -d '/' -f 2)" >> $GITHUB_ENV

#     브랜치 추출
    - name: BRANCH_VERSION Exctract
      run: echo "BRANCH_VERSION=$(echo $GITHUB_REF_NAME|cut -d '/' -f 2)$TAG_SUFFIX" >> $GITHUB_ENV

#     소스 버전 추출
    - name: SOURCE_VERSION Exctract
      run: echo "SOURCE_VERSION=$(grep -oPm2 "(?<=<version>)[^<]+" pom.xml | sed -n 2p)" >> $GITHUB_ENV

#     자바 활성화
    - name: Setup Java JDK
      uses: actions/setup-java@v2.5.0
      with:
        java-version: 8.0.312+7
        distribution: adopt

#     메이븐 캐싱
    - name: Maven Cache (restore)
      uses: skjolber/maven-cache-github-action@v1.1
      with:
        step: restore

#     메이븐 패키징
    - name: Maven Package
      run: mvn package
      
#     메이븐 캐싱
    - name: Maven Cache (save)
      uses: skjolber/maven-cache-github-action@v1.1
      with:
        step: save

#     도커 로그인
    - name: Docker Login
      uses: docker/login-action@v1.12.0
      with:
        registry: $
        username: joonwoo8888
        password: $

#     도커 빌드/푸시
    - name: Docker Build & Push Action
      uses: mr-smithers-excellent/docker-build-push@v5.6
      with:
        registry: $
        dockerfile: $
        buildArgs: VERSION=$,PROJECT_NAME=$
        image: $
        tags: $

마스터 action

릴리즈와 유사하지만 실제 정식버전의 이미지로 빌드를 하게 됩니다. 버전은 소스버전으로 태그가 됩니다.

name: docker-master-build

on:
  push:
    branches: [ master ]
  pull_request:
    branches: [ master ]

env:
  CONTAINER_REGISTRY: ghcr.io
  DOCKER_FILE: Dockerfile.flow

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
#     소스 체크아웃
    - uses: actions/checkout@v2
    
#     저장소이름 추출
    - name: PROJECT_NAME Exctract
      run: echo "PROJECT_NAME=$(echo $GITHUB_REPOSITORY|cut -d '/' -f 2)" >> $GITHUB_ENV

#     브랜치 추출
    - name: BRANCH_VERSION Exctract
      run: echo "BRANCH_VERSION=$(echo $GITHUB_REF_NAME|cut -d '/' -f 2)" >> $GITHUB_ENV

#     소스 버전 추출
    - name: SOURCE_VERSION Exctract
      run: echo "SOURCE_VERSION=$(grep -oPm2 "(?<=<version>)[^<]+" pom.xml | sed -n 2p)" >> $GITHUB_ENV

#     자바 활성화
    - name: Setup Java JDK
      uses: actions/setup-java@v2.5.0
      with:
        java-version: 8.0.312+7
        distribution: adopt

#     메이븐 캐싱
    - name: Maven Cache (restore)
      uses: skjolber/maven-cache-github-action@v1.1
      with:
        step: restore

#     메이븐 패키징
    - name: Maven Package
      run: mvn package
      
#     메이븐 캐싱
    - name: Maven Cache (save)
      uses: skjolber/maven-cache-github-action@v1.1
      with:
        step: save

#     도커 로그인
    - name: Docker Login
      uses: docker/login-action@v1.12.0
      with:
        registry: $
#         danawalab 계정에선 토큰이 안보임..
        username: joonwoo8888
        password: $

#     도커 빌드/푸시
    - name: Docker Build & Push Action
      uses: mr-smithers-excellent/docker-build-push@v5.6
      with:
        registry: $
        dockerfile: $
        buildArgs: VERSION=$,PROJECT_NAME=$
        image: $
        tags: $

개발 action

브랜치명 그대로 컨테이너 이미지를 생성하게 됩니다. develop은 지속적으로 오버라이딩됩니다.

name: docker-dev-ci

on:
  push:
    branches: [ develop, feature/**, bugfix/** ]
  pull_request:
    branches: [ develop, feature/**, bugfix/** ]

env:
  CONTAINER_REGISTRY: ghcr.io
  DOCKER_FILE: Dockerfile.flow

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
#     소스 체크아웃
    - uses: actions/checkout@v2
    
#     저장소이름 추출
    - name: PROJECT_NAME Exctract
      run: echo "PROJECT_NAME=$(echo $GITHUB_REPOSITORY|cut -d '/' -f 2)" >> $GITHUB_ENV

#     브랜치 추출
    - name: BRANCH_VERSION Exctract
      run: echo "BRANCH_VERSION=$(echo $GITHUB_REF_NAME|cut -d '/' -f 2)" >> $GITHUB_ENV

#     소스 버전 추출
    - name: SOURCE_VERSION Exctract
      run: echo "SOURCE_VERSION=$(grep -oPm2 "(?<=<version>)[^<]+" pom.xml | sed -n 2p)" >> $GITHUB_ENV

#     자바 활성화
    - name: Setup Java JDK
      uses: actions/setup-java@v2.5.0
      with:
        java-version: 8.0.312+7
        distribution: adopt

#     메이븐 캐싱
    - name: Maven Cache (restore)
      uses: skjolber/maven-cache-github-action@v1.1
      with:
        step: restore

#     메이븐 패키징
    - name: Maven Package
      run: mvn package
      
#     메이븐 캐싱
    - name: Maven Cache (save)
      uses: skjolber/maven-cache-github-action@v1.1
      with:
        step: save

#     도커 로그인
    - name: Docker Login
      uses: docker/login-action@v1.12.0
      with:
        registry: $
#         danawalab 계정에선 토큰이 안보임..
        username: joonwoo8888
        password: $

#     도커 빌드/푸시
    - name: Docker Build & Push Action
      uses: mr-smithers-excellent/docker-build-push@v5.6
      with:
        registry: $
        dockerfile: $
        buildArgs: VERSION=$,PROJECT_NAME=$
        image: $
        tags: $

actions

아래와 같이 3개의 workflow로 구성되게 됩니다. 하위 액션에서는 build만 존재하며 배포는 gitlab CI/CD를 사용하게 됩니다.

Untitled

배포를 위한 gitlab CI/CD 사용

이미지는 github에서 지속적으로 빌드가 되게 됩니다. 배포 프로젝트를 제거하기위해 현재 작업을 하지만 github에서 사내 서버로 배포를 할 순 없기 때문에 제한적으로 배포 프로젝트를 구성해야됩니다. 테스트 배포, 운영 배포에서는 아래 조건에 해당할때 배포 프로젝트를 구성하여 사용해야합니다.

배포 프로젝트 구성 조건

  • 배포 서버가 많을때
  • 배포 전 선행, 후행 작업이 필요할때

gitlab-ci.yml

배포 프로젝트는 production, develop 브랜치로 배포 서버를 구분하며, 사용할 이미지 태그는 운영관리 플랫폼에서 업데이트 진행합니다.

image: alpine/curl   # 운영관리 플랫폼 API 호출만하기 때문에 최소한의 이미지 사용

stages:
- trigger      # 시작 버튼 클릭시 서버 하나씩 배포 시작
- deploy#1     # 순차적으로 배포 진행
- deploy#2
- deploy#3

Start:
  stage: trigger
  script: 
    - echo "start"
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^production/' # 운영 릴리즈 배포시 
      when: manual
    - if: '$CI_BUILD_REF_SLUG =~ /^develop/'    # 개발 배포시
      when: manual
  
Deploy#1:
  stage: deploy#1
  needs: 
    - Start
  script:
     # 운영관리 플랫폼 업데이트 호출
    - echo `curl -w "%{http_code}" -o /dev/null -s -H "Content-Type:application/json" http://manager.danawa.io/esapi1`
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^production/'
      when: on_success
    - if: '$CI_BUILD_REF_SLUG =~ /^develop/'
      when: never

Deploy#2:
  stage: deploy#2
  needs:
    - Start
    - Deploy#1
  script:
     # 운영관리 플랫폼 업데이트 호출
    - echo `curl -w "%{http_code}" -o /dev/null -s -H "Content-Type:application/json" http://manager.danawa.io/esapi2`
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^production/'
      when: on_success
    - if: '$CI_BUILD_REF_SLUG =~ /^develop/'
      when: never
    
Deploy#3:
  stage: deploy#3
  needs:
    - Start
    - Deploy#1
    - Deploy#2
  script:
     # 운영관리 플랫폼 업데이트 호출
    - echo `curl -w "%{http_code}" -o /dev/null -s -H "Content-Type:application/json" http://manager.danawa.io/esapi3`
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^production/'
      when: on_success
    - if: '$CI_BUILD_REF_SLUG =~ /^develop/'
      when: never

TEST Deploy#1:   # 테스트 서버 배포
  stage: deploy#1
  needs: 
    - Start
  script:
    - echo `curl -w "%{http_code}" -o /dev/null -s -H "Content-Type:application/json" http://manager.danawa.io/esapi1`
  rules:
    - if: '$CI_BUILD_REF_SLUG =~ /^production/'
      when: never
    - if: '$CI_BUILD_REF_SLUG =~ /^develop/'
      when: on_success # develop 브랜치에서만 표시

파이프라인 생성

배포를할땐 운영인지, 개발인지 브랜치를 선택합니다.

Untitled

production 일 경우 아래 처럼 파이프라인이 구성되고, Start 버튼을 클릭하게 되면 Deploy#1 ~ n 순차적으로 롤링 배포가 이뤄지도록 됩니다.

Untitled

develop 브랜치에서는 Start 버튼 클릭시 테스트 서버로 배포가 됩니다.

Untitled

마무리

프로젝트 관리에 있어서 개선점을 찾아 개선방법에 대해 알아보았습니다. 아직 팀에서 바로 적용하기에는 추가적인 개선사항이 있을 수있고, 실제 사용을 해보지 않아 검증이 안된 부분도 있어 보입니다. 소스 충돌로 시작해 개선 방안을 고민하면서 CI/CD까지 개선해보는 좋은 경험이 된거 같습니다. 위와 유사한 고민하는 분들에게 도움이 되었으면 좋겠습니다. 감사합니다.