CI/CD

프로젝트 최상위에 .gitlab-ci.yaml 파일을 생성하고, 아래와 같이 설정한다.

stages 생성

사용할 스테이지를 전부 만들어준다.

stages:
  - build
  - testing
  - dev
  - release
  - qa
  - prod

파이프라인에서 사용할 변수 선언

variables:
  IMAGE_NAME: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}
  LATEST_IMAGE_NAME: ${CI_REGISTRY_IMAGE}:latest

job 생성

run_job:
  stage: dev
  image: alpine:3.8
  before_script:
    - echo pre
  script:
    - echo hello world
  after_script:
    - echo post
unit_test_job:
  stage: dev
  image: alpine:3.8
  script:
    - echo unit test

run_job을 테스트로 생성햇고 unit_test_job도 생성해보았다.

둘다 stage가 dev인것을 확인할수 있다.

run_e2e_tests:
  stage: testing
  image: alpine:3.8
  before_script:
    - echo pre
  script:
    - echo hello world
  after_script:
    - echo post

testing stage 에 job을 하나 추가햇다.

docker build

echo "hello world" > index.html
vi Dockerfile
FROM nginx:1.20-alpine
COPY index.html /usr/share/nginx/html/index.html
EXPOSE 80
CMD ["/bin/sh", "-c", "exec nginx -g 'daemon off;';"]
WORKDIR /usr/share/nginx/html

.gitlab-ci.yaml 에 추가하자.

build stage 에 job을 하나 추가햇다.

build:
  stage: build
  image: docker:20.10
  services:
    - docker:20.10-dind
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker build -f Dockerfile -t ${IMAGE_NAME} -t ${LATEST_IMAGE_NAME} .
    - docker push ${IMAGE_NAME}
    - docker push ${LATEST_IMAGE_NAME}
  after_script:
    - docker logout

${xxx}는 variable에 있는 변수를 사용한다.

CI_REGISTRY_USER, CI_REGISTRY_PASSWORD, CI_REGISTRY 를 사용한다.

그림처럼 생성해서 넣어줘야한다.

혹시 여러사람이 공유하면 그룹 단위로 적용하는게 키를 보호하는데 도움이 될듯 싶다.

change log

  • $CI_COMMIT_TAG => 태그를 붙이는 순간만 동작을 한다.

changelog:
  stage: release
  image: docker:git
  rules:
    - if: $CI_COMMIT_TAG
  script:
    - git log $(git describe --tags --abbrev=0 HEAD^)..HEAD --pretty=format:'At %ci, %cN committed %h - %s' --decorate --graph >release.md
  artifacts:
    paths: [release.md]

git log를 사용하여 바로 직전 태그 이후로부터 지금까지 git log를 가져오고, release.md로 저장한다.

release.md가 artifactory로 생성이 되서 ui에서 다운받을수 있다.

release

  • $CI_COMMIT_TAG => 태그를 붙이는 순간만 동작을 한다.

바로 직전에 생긴 artifact가 자동으로 다운이 된다. 그러므로 cat해서 릴리즈에 넣으면 된다.

needs를 이용하면 디펜던시가 걸린 작업이 먼저 끝난후 이 잡을 실행한다. changelog가 끝나면 release가 된다.

release:
  stage: release
  needs: ['changelog']
  image: registry.gitlab.com/gitlab-org/release-cli:latest
  rules:
    - if: $CI_COMMIT_TAG
  script:
    - echo "running release_job for $TAG"
  release:
    tag_name: '$CI_COMMIT_TAG'
    name: 'Release $CI_COMMIT_TAG'
    description: '$(cat release.md)'

이제 태그를 붙이고 push를 하면 릴리즈 페이지가 된다.

staging 배포

stage를 staging으로 구성햇음.

argocd repo를 업데이트 하는것으로 배포가 마무리 된다. 깃을 클론해서 다운로드한후 docker image tag만 바꿔주고 다시 커밋하면된다.

staging-deploy:
  stage: staging
  image: alpine:3.8
  rules:
    - if: $CI_COMMIT_TAG
  before_script:
    - apk add --no-cache git curl bash
    - git config --global user.email "gitlab@gitlab.com"
    - git config --global user.name "GitLab CI/CD"
  script:
    - git clone https://${CI_USER}:${CI_PUSH_TOKEN}@gitlab.com/teamsmiley/staging.git
    - cd k8s-c1/apps/default/sample-www-internal
    - sed "s/:latest/:${CI_COMMIT_SHORT_SHA}/g" deploy.origin > deploy.yaml
    - git commit -am "change docker tag"
    - git push

deploy.origin 파일

---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: www
  labels:
    app: www
spec:
  replicas: 1
  revisionHistoryLimit: 1
  selector:
    matchLabels:
      app: www
  template:
    metadata:
      labels:
        app: www
    spec:
      containers:
        - name: www
          image: registry.gitlab.com/teamsmiley/sample:latest
          ports:
            - containerPort: 80
              name: http
      imagePullSecrets:
        - name: gitlab-regcred

production 배포(job template)

스테이징 코드를 복사해서 만들면 된다. 그런데 코드의 중복이 생긴다.

여기서 job template라는 개념이 생긴다.

job template는 사용한곳보다는 yaml파일에 상단에 위치해야한다. yaml에 추가하자.

jobname이 .으로 시작한다. 이건 실제로 동작하지는 않는 작업이다.

.deploy-template: &template
  rules:
    - if: $CI_COMMIT_TAG
  before_script:
    - apk add --no-cache git curl bash
    - echo ${CI_COMMIT_SHORT_SHA}
    - git config --global user.email "gitlab@gitlab.com"
    - git config --global user.name "GitLab CI/CD"
  script:
    - git clone https://${CI_USER}:${CI_PUSH_TOKEN}@gitlab.com/teamsmiley/staging.git
    - cd staging/apps/default/sample-www-internal
    - sed "s/:latest/:${CI_COMMIT_SHORT_SHA}/g" deploy.origin > deploy.yaml
    - git commit -am "change docker tag"
    - git push

yaml에 anchor기능을 이용한다.

&template 처럼 &하고 이름을 적는다.

이제 사용할 곳에서 이름을 적어서 사용한다. <<: * 다음에 이름을 써서 사용한다.

staging-deploy:
  stage: staging
  image: alpine:3.8
  rules:
    - if: $CI_COMMIT_TAG
  <<: *template

이렇게 하면 위 내용을 가져다가 붙여준다. 코드 중복을 줄일수 있다.

production과 staging을 하다보니 git repo의 경로가 다르다.

staging.git / production.git 이다. 어떻게 처리해야할까?

- git clone https://${CI_USER}:${CI_PUSH_TOKEN}@gitlab.com/teamsmiley/staging.git
- cd staging/apps/default/sample-www-internal

이부분이 문제인데 이건 간단하게 variable을 사용하면된다.

.deploy-template: &template
  rules:
    - if: $CI_COMMIT_TAG
  before_script:
    - apk add --no-cache git curl bash
    - echo ${CI_COMMIT_SHORT_SHA}
    - git config --global user.email "gitlab@gitlab.com"
    - git config --global user.name "GitLab CI/CD"
  script:
    - git clone https://${CI_USER}:${CI_PUSH_TOKEN}@gitlab.com/teamsmiley/$gitrepo_name.git # $gitrepo_name은 variable을 사용하여 정해준다.
    - cd $gitrepo_name/apps/default/sample-www-internal # $gitrepo_name은 variable을 사용하여 정해준다.
    - sed "s/:latest/:${CI_COMMIT_SHORT_SHA}/g" deploy.origin > deploy.yaml
    - git commit -am "change docker tag"
    - git push
staging-deploy:
  stage: staging
  image: alpine:3.8
  rules:
    - if: $CI_COMMIT_TAG
  # 이부분을 추가한다.
  variables:
    gitrepo_name: staging
  <<: *template

production-deploy:
  stage: production
  image: alpine:3.8
  rules:
    - if: $CI_COMMIT_TAG
  # 이부분을 추가한다.
  variables:
    gitrepo_name: production
  <<: *template

이렇게 하면 variable을 사용하여 staging은 staging.git / production은 production.git 이렇게 정해준다.

코드의 중복을 전부 job template를 이용하여 처리할수 있다.

Approval

environment에서 approval을 사용하면 된다.

production-deploy:
  stage: production
  image: alpine:3.8
  # 이부분을 추가한다.
  environment:
    name: prod
    url: https://www.aaa.com
  rules:
    - if: $CI_COMMIT_TAG
  variables:
    gitrepo_name: production
  <<: *template

ci/cd를 실행하면 environment가 생성된다.

setting >> ci/cd >> protect an environment

이제 cicd를 실행해보면 프로덕션에서 멈춰있는것을 알수 있다.

environment 에 가서 productin을 선택하고 들어가면 컨펌을 할수가 있다.

approval을 할수가 있다.

approval을 하면 다음 프로세스가 진행되는것을 알수 있다.

전체 ci/cd 프로세스

stages:
  - build
  - testing
  - dev
  - release
  - qa
  - prod

variables:
  IMAGE_NAME: ${CI_REGISTRY_IMAGE}:${CI_COMMIT_SHORT_SHA}
  LATEST_IMAGE_NAME: ${CI_REGISTRY_IMAGE}:latest

run_job:
  stage: dev
  image: alpine:3.8
  before_script:
    - echo pre
  script:
    - echo hello world
  after_script:
    - echo post
unit_test_job:
  stage: dev
  image: alpine:3.8
  script:
    - echo unit test

run_e2e_tests:
  stage: testing
  image: alpine:3.8
  before_script:
    - echo pre
  script:
    - echo hello world
  after_script:
    - echo post

build:
  stage: build
  image: docker:20.10
  services:
    - docker:20.10-dind
  before_script:
    - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY}
  script:
    - docker build -f Dockerfile -t ${IMAGE_NAME} -t ${LATEST_IMAGE_NAME} .
    - docker push ${IMAGE_NAME}
    - docker push ${LATEST_IMAGE_NAME}
  after_script:
    - docker logout

changelog:
  stage: release
  image: docker:git
  rules:
    - if: $CI_COMMIT_TAG
  script:
    - git log $(git describe --tags --abbrev=0 HEAD^)..HEAD --pretty=format:'At %ci, %cN committed %h - %s' --decorate --graph >release.md
  artifacts:
    paths: [release.md]

release:
  stage: release
  needs: ['changelog']
  image: registry.gitlab.com/gitlab-org/release-cli:latest
  rules:
    - if: $CI_COMMIT_TAG
  script:
    - echo "running release_job for $TAG"
  release:
    tag_name: '$CI_COMMIT_TAG'
    name: 'Release $CI_COMMIT_TAG'
    description: '$(cat release.md)'

.deploy-template: &template
  image: alpine:3.8
  rules:
    - if: $CI_COMMIT_TAG
  before_script:
    - apk add --no-cache git curl bash
    - echo ${CI_COMMIT_SHORT_SHA}
    - git config --global user.email "gitlab@gitlab.com"
    - git config --global user.name "GitLab CI/CD"
  script:
    - git clone https://${CI_USER}:${CI_PUSH_TOKEN}@gitlab.com/teamsmiley/staging.git
    - cd staging/apps/default/sample-www-internal
    - sed "s/:latest/:${CI_COMMIT_SHORT_SHA}/g" deploy.origin > deploy.yaml
    - git commit -am "change docker tag"
    - git push

staging-deploy:
  stage: staging
  # 이부분을 추가한다.
  variables:
    gitrepo_name: staging
  <<: *template

production-deploy:
  stage: production
  # 이부분을 추가한다.
  variables:
    gitrepo_name: production
  <<: *template

jira ticket create for rtp with release.md

unit_test_job:
  stage: qa
  image: alpine:3.8
  script:
    - curl xxxxxxxxxx (jira api with api key)

this will create ma ticket

when we click argocd sync button

deploy

  • update jira ticket status to start

  • deploy

  • update jira ticket status to done

rollback

  • update jira ticket status to rollback-start

  • deploy

  • update jira ticket status to rollback-done

todo

  • approval email

Last updated

Was this helpful?