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