gitlab runner with testconatiner - g-market/b-shop-backend GitHub Wiki

CI/CD ํŒŒ์ดํ”„๋ผ์ธ ์„ค๊ณ„ ๋ฐ ๊ตฌํ˜„

  • Gitlab-CI ๋กœ ํ…Œ์ŠคํŠธ ์ž๋™ํ™” ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•
  • ํ…Œ์ŠคํŠธ ์ดํ›„ CI
  • CD ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•
    • Spring boot ํ”„๋กœ์ ํŠธ docker image๋กœ ๋ฐฐํฌ
    • docker image gitlab container registry์— ์—…๋กœ๋“œ
    • ์„œ๋ฒ„ VM์—์„œ ํ•ด๋‹น ์ด๋ฏธ์ง€ ๋‚ด๋ ค๋ฐ›๊ณ  ์‹คํ–‰

CI ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ฒดํ™”

CI ํŒŒ์ดํ”„๋ผ์ธ์€ ํ•ด๋‹น ๊ณผ์ •์—์„œ ๋นŒ๋“œ, ํ…Œ์ŠคํŠธ, ๊ทธ๋ฆฌ๊ณ  ํ…Œ์ŠคํŠธ์ปค๋ฒ„๋ฆฌ์ง€์— ๋Œ€ํ•œ ์‹œ๊ฐํ™”์ž‘์—…(jacoco)๋ฅผ ์ˆ˜ํ–‰ํ•œ๋‹ค. ํ˜„์žฌ ์ปค๋ฒ„๋ฆฌ์ง€์— ๋Œ€ํ•œ ๋ช…ํ™•ํ•œ ์ •์˜๋ฅผ ๋‚ด๋ฆฌ์ง€ ์•Š์•„ ๋นŒ๋“œ๊ณผ์ •์—์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ปค๋ฒ„๋ฆฌ์ง€์— ๋Œ€ํ•œ ์„ค์ •์€ ๋ง‰์•„๋†“์€ ์ƒํƒœ์ด๋‹ค.

  • coverage๋ฅผ 5% ์ •๋„๋กœ ๋‚ฎ์ถ”๊ณ  ์‹คํ–‰ํ–ˆ์„ ๋•Œ ์ปค๋ฒ„๋ฆฌ์ง€๊ฐ€ ๋ณด์ด๋Š”์ง€ ํ™•์ธํ•„์š” <- ์•ˆ๋จ..

ํ•ด๋‹น ํŒŒ์ดํ”„๋ผ์ธ์€ ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•œ ํ†ตํ•ฉ, ๋‹จ์œ„ ํ…Œ์ŠคํŠธ๋ฅผ develop, main ๋ธŒ๋žœ์น˜๋กœ Merge request๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ์ˆ˜ํ–‰์ด ๋œ๋‹ค.
ํ…Œ์ŠคํŠธ ์ปจํ…Œ์ด๋„ˆ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด DinD(docker in dockder) ํ™˜๊ฒฝ์„ ๊ตฌ์„ฑํ–ˆ๋‹ค. build๊ณผ test๋‹จ๊ณ„์˜ ์—๋Ÿฌ๋ฅผ ๊ตฌ๋ถ„ํ•˜๊ธฐ ์œ„ํ•ด ๋นŒ๋“œ๋ฅผ ํ…Œ์ŠคํŠธ๋ฅผ ์ œ์™ธํ•˜๊ณ  ์ˆ˜ํ–‰ํ•˜๋„๋ก ์„ค์ •ํ–ˆ๋‹ค. -> (./gradlew build -x test)
๋‹ค์Œ์€ gitlab-ci.yml ์Šคํฌ๋ฆฝํŠธ์ด๋‹ค.

build:
  rules:
    -  if : '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
  image: openjdk:17-alpine
  stage: build
  before_script:
    - echo "$APPLICATION_YAML" > "src/main/resources/application.yml"
  script:
    - ./gradlew clean
    - ./gradlew build -x test
  artifacts:
    paths:
      - build/libs/*.jar
    expire_in: 10 min

test:
  rules:
    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
  variables:
    GRADLE_OPTS: "-Dorg.gradle.daemon=false"
    # Instruct Testcontainers to use the daemon of DinD.
    DOCKER_HOST: "tcp://docker:2375"
    # Improve performance with overlayfs.
    DOCKER_DRIVER: overlay2
    DOCKER_TLS_CERTDIR: ""

  image: openjdk:17-alpine
  stage: test
  tags:
    - gitlab-runner
  services:
    - name: docker:dind
  script:
    - ./gradlew test
  coverage: '/    - Instruction Coverage: ([0-9.]+)%/'
  artifacts:
    paths:
      - build/reports/jacoco/test/jacoco*.xml
    reports:
      junit: build/test-results/test/**/TEST-*.xml

CD

๋ฐฐํฌ๋ฅผ ์œ„ํ•œ ์„ ํ–‰๋‹จ๊ณ„๋กœ dockerfile๋กœ ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค๊ณ  ํ•ด๋‹น ์ด๋ฏธ์ง€๋ฅผ ์ปจํ…Œ์ด๋„ˆ ๋ ˆ์ง€์ŠคํŠธ๋ฆฌ๋กœ ์—…๋กœ๋“œ ํ•˜๋Š” ์ž‘์—…์ด ํ•„์š”ํ•˜๋‹ค. ํ˜„์žฌ ํ”„๋กœ์ ํŠธ ํ™˜๊ฒฝ๋ณ€์ˆ˜(application.yml)์— ํ•„์š”ํ•œ ์ •๋ณด๋“ค์ด ์™ธ๋ถ€์—์„œ ์ฃผ์ž…๋ฐ›๋„๋ก ์„ค์ •๋˜์–ด์žˆ์–ด ํ™˜๊ฒฝ๋ณ€์ˆ˜๋ฅผ ์–ด๋–ป๊ฒŒ ์ ์šฉํ•ด์•ผํ•˜๋Š”์ง€์—๋Œ€ํ•œ ๊ณ ๋ฏผ์ด ํ•„์š”ํ•˜๋‹ค.

docker run -d \
  --name b-shop-test \
  -p 8080:8080 \
  -e ACCESS-EXPIRED-TIME=3600000 \
  -e ACCESSTOKEN-URL=https://api.hiworks.com/open/auth/accesstoken \
  -e ACTIVE-PROFILE=local \
  -e CART-EXPIRED-TIME=86400000 \
  -e CLIENT-ID=g9weeb6q1rsgirhtgcxsmyj4wt7ktcer6045c8d8d357e7.34276869.open.apps \
  -e CLIENT-SECRET=9a1n7i7p1qz8ih2knpy3jgrpcbcedbyf \
  -e DB-PASSWORD=1234 \
  -e DB-URL=โ€œjdbc:mysql://b-shop-database:3306/bshop_dbโ€ \
  -e DB-USERNAME=root \
  -e DDL-AUTO=create \
  -e FETCH-SIZE=100 \
  -e LOCAL-DOMAIN=http://b-shop.com \
  -e LOG-LEVEL=debug \
  -e USER-URL=https://api.hiworks.com/user/v2/me \
  -e TOKEN-SECRET=localSecretKey123456789876543210 \
  -e SERVER-PORT=8080 \
  -e PROD-DOMAIN=https://b-shop.com \
  -e PROD-URL=https://139.150.73.182 \
  -e REDIS-HOST=172.22.0.2 \
  -e REDIS-PORT=6379 \
  -e REFRESH-EXPIRED-TIME=1209600000 \
  -e MINIO-ENDPOINT=http://localhost:9000 \
  -e MINIO_BUCKET=images \
  -e MINIO_PASSWORD=minio1234 \
  -e MINIO_USER=minio \
  -e MINIO_NO_IMAGE_URL=http://localhost:9000/images/No_Image.jpg \
  --network test_net \
  test

stages:
  - build
  - test
  - visualization
  - package
  - deploy

build:
  rules:
    -  if : '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
    image: openjdk:17-alpine
    stage: build
    before_script:
      - echo "$APPLICATION_YAML" > "src/main/resources/application.yml"
    script:
      - ./gradlew clean
      - ./gradlew build -x test
    artifacts:
      paths:
        - build/libs/*.jar
      expire_in: 10 min

test:
  rules:
    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
    variables:
      GRADLE_OPTS: "-Dorg.gradle.daemon=false"
      # Instruct Testcontainers to use the daemon of DinD.
      DOCKER_HOST: "tcp://docker:2375"
      # Improve performance with overlayfs.
      DOCKER_DRIVER: overlay2
      DOCKER_TLS_CERTDIR: ""

    image: openjdk:17-alpine
    stage: test
    tags:
      - gitlab-runner
    services:
      - name: docker:dind
    script:
      - ./gradlew test
    coverage: '/ - Instruction Coverage: ([0-9.]+)%/'
    artifacts:
      paths:
        - build/reports/jacoco/test/jacoco*.xml
      reports:
        junit: build/test-results/test/**/TEST-*.xml

#https://docs.gitlab.com/ee/ci/testing/test_coverage_visualization.html
visualization:
  rules:
    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main" || $CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "develop"'
    stage: visualization
    image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.7
    script:
      - python /opt/cover2cover.py build/reports/jacoco/test/jacoco*.xml $CI_PROJECT_DIR/src/main/java/ > build/cobertura.xml
    needs: ["test"]
    artifacts:
      reports:
        coverage_report:
          coverage_format: cobertura
          path: build/cobertura.xml

package-and-push:
  rules:
    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"'
  stage: package
  needs: ["test"]
  image: docker:latest
  services:
    - name: docker:dind
  before_script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
  script:
    - docker pull $IMAGE_NAME:latest || true
    - docker build --cache-from $IMAGE_NAME:latest --tag $IMAGE_NAME:$CI_COMMIT_SHA --tag $IMAGE_NAME:latest .
    - docker push $IMAGE_NAME:$CI_COMMIT_SHA
    - docker push $IMAGE_NAME:latest
  after_script:
    - docker logout

deploy:
  rules:
    - if: '$CI_MERGE_REQUEST_TARGET_BRANCH_NAME == "main"'
  stage: deploy
  needs: ["test"]
  tags:
    - deployer
  before_script:
    ## Run ssh-agent (inside the build environment)
    - eval $(ssh-agent -s)

    ## ADD ssh-key to ssh-agent
    - chmod 400 "$SSH_KEY"
    - ssh-add "$SSH_KEY"

    ## ADD ssh known hosts
    - mkdir -p ~/.ssh touch /.ssh/known_hosts
    - cp "$SSH_KNOWN_HOSTS" ~/.ssh/known_hosts

    ## change permission
    - chmod 644 ~/.ssh/known_hosts
    - chmod 700 ~/.ssh
  script:
    ## SSH CONNECTION
    - ssh root@IP_ADDRESS
    - cd deploy
    ## PULL IMAGE
    - docker pull $IMAGE_NAME:latest
    - echo "$DEPLOY_ENV" >> .env
    ## BLUE_DEPLOY
    - echo "$BLUE_DEPLOY_YAML" >> blue_deploy.yml
    - docker-compose -f blue-deploy.yml down -v
    - docker-compose -f blue-deploy.yml --env-file .env up -d
    ## GREEN_DEPLOY TODO
    # - echo "$GREEN_DEPLOY_YAML" >> green_deploy.yml
    # - docker-compose -f green_deploy.yml down -v
    # - docker-compose -f green_deploy.yml --env-file .env up -d
  after_script:
    - exit

์ฐธ๊ณ 

https://gitlab.com/gitlab-org/gitlab-runner/-/issues/3251

https://cha-vi.tistory.com/entry/Docker-Docker-%EC%9D%B4%EB%AF%B8%EC%A7%80-%EC%83%9D%EC%84%B1-%EB%B0%8F-%EC%9E%90%EB%8F%99-%EB%B0%B0%ED%8F%ACFastAPI-%EC%99%80-Gitlab-CICD%EB%A5%BC-%EA%B3%81%EB%93%A4%EC%9D%B8 https://velog.io/@jsb100800/Spring-boot-Test

https://medium.com/monday-9-pm/ci-cd-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EA%B5%AC%EC%B6%95%EA%B8%B0-2-f96b1217279e

https://nearhome.tistory.com/141

https://velog.io/@jsb100800/Spring-boot-Test

https://aidanbae.github.io/code/docker/dinddood/

https://nearhome.tistory.com/141

https://mns010.tistory.com/25

https://filip5114.github.io/GitLab-CI-Pipeline-SSH/

โš ๏ธ **GitHub.com Fallback** โš ๏ธ