GitLab CI 기술

16782 단어 cicddevopsgitgitlab
Vdoo에서 CI 설정을 작성할 때 우리는 몇 가지 흥미로운 도전에 부딪혔다.이러한 문제들을 해결하고 상당한 시간을 사용한 후에 우리는 여러분과 공유하는 것이 가장 좋고 다른 사람들을 위해 비슷한 문제를 해결하기 위해 시간과 정력을 절약할 수 있기를 희망합니다.
물론 우리가 이미 대응한 도전에 대해 다른 해결 방안도 있고, 어떤 해결 방안은 우리보다 더 좋을 수도 있다.우리는 이러한 해결 방안을 듣고 우리 자신의 해결 방안을 개선하는 것을 매우 기쁘게 생각한다.
이 글의 코드는 우리의 CI에서 추출되었고 정리되었다.그래서 필요한 샘플이 부족하다.그것은 원래의 상태로 작동하지 않으며, CI에 적응하기 위한 작업이 필요합니다.즉, 해결 방안을 명확하게 열거해야 한다는 것이다.(당신은 그것을 슬라이드로 상상할 수 있다.)

LFS 검사


모든 과도는 흔들릴 수 있다.이진 파일을 일반적인git Blob로 저장하는 것에서 LFS로 저장하는 것까지의 변화는 이런 험난한 변화이다.
LFS와 관련된 모든 파일 및 패턴이 문서에 포함되어 있는지 확인합니다.gitattributes 파일이지만, LFS 환경을 정확하게 설정하는 데 시간이 좀 걸립니다.또한 LFS에서 제출해야 할 파일을 계속해서 가져와 병합 요청에서 일반 파일로 전송했습니다.
이러한 상황을 피하기 위해서, 우리는 모든 관련 파일이 LFS에 저장되어 있는지 확인하기 위해 CI 프로세스를 시작할 때 간단한 검사를 설정했다.
lfs-check:
  only:
    refs:
      - merge_requests
  script:
    - git lfs install
    - git add --renormalize -u
    - |
      if ! git diff --cached --name-only --exit-code ; then
        echo
        echo
        echo "=============================="
        echo "#  Please renormalize files  #"
        echo "=============================="
        echo
        echo "git add -u --renormalize"
        echo "git commit --amend"
        exit 1
      fi

사람들이 잘못된 방식으로 파일을 전송할 때 CI는 실패하고 정보 오류와 명령이 발생합니다.

제출해야 함


일정 시간마다 코드를 변경해서 변경 전의 코드를 실행할 수 없거나 상관없게 합니다.이런 상황이 발생한 원인은 매우 많다.다음은 몇 가지 예입니다.
  • CI에서 오류가 해결되었습니다.이것은 정확한 잠금 의존항 (귀속 의존항 포함) 을 잊어버릴 때 매우 흔하다
  • 아주 많은 시간을 소모하는 업데이트를 진행했다(ML모형을 재훈련한 사람이 있습니까?)
  • 이 변화는 의미가 크고 재정의 기반을 고통으로 만들 것이다
  • 중요한 버그를 복구하여 이전 버전의 테스트는 기본적으로 중요하지 않다
  • 코드를 이렇게 수정하면 사람들에게 알리고 주기 낭비를 멈추고 싶다.
    이 목표를 실현하기 위해서, 우리는 CI에 필요한 제출 메커니즘을 만들었다.CI 작업을 수행하려면 제출이 현재 제출된 선조여야 합니다.그렇지 않으면 CI가 실패하고 설명 오류와 문제 해결에 대한 설명이 나타납니다.
    새로운 필수 제출이 있으면, 모든 개발자에게 알리고, CI를 업데이트해서 일치하도록 합니다.이것은 개발자가 슬랙의 통지를 놓치더라도 CI는 그들에게 무엇을 해야 하는지 알게 할 것이다.
    솔루션에는 다음과 같은 간단한 CI 작업이 포함됩니다.
    required-commit:
      only:
        refs:
          - merge_requests
      script:
        - apt-get update
        - |
          if ! git merge-base --is-ancestor ${REQUIRED_COMMIT:-HEAD} HEAD ; then
            echo
            echo
            echo "============================="
            echo "#      Rebase Required      #"
            echo "============================="
            echo
            echo "Your base commit is out of date."
            echo "Please update to ${REQUIRED_COMMIT} or later."
            echo "The easiest fix is to rebase-onto or merge-from origin/main."
            echo
            echo
            exit 1
          fi
    
    
    CI 설정에 정의된 사용자 정의 변수(참조Create a custom variable in the UI:

    작업 Docker 이미지를 조건부로 구성


    일부 코드는 Docker 이미지를 통해 배치됩니다.따라서 CI가 이러한 이미지를 구축하고 테스트할 수 있기를 바랍니다.일부 테스트는 Docker 용기를 실행하고 통신해야 하지만, 일부 테스트 (특히 단원 테스트와 통합 테스트) 는 상기 용기에서 실행하기 쉽다.후자에 적응하기 위해서, 우리는 Docker 이미지를 CI 테스트 작업의 기초 이미지로 사용합니다.
    이것은 CI에서 쉽게 할 수 있다.그러나 우리의 예에서 Docker 이미지를 구축하는 데는 시간이 오래 걸린다.이 시간을 줄이기 위해 구축은 두 부분으로 나뉜다.첫 번째 단계-긴 컴파일 단계로 변경이 적은 코드를 구축한다.둘째, 빠르게 변화하는 파이썬 코드와 모든 관련 의존항을 설치합니다.
    우리가 구축한 신속한 변화 부분과 아주 적은 변화 부분 간의 차이를 알아차리고 우리는 이를 둘로 나누어 첫 번째 부분에 실제 변화가 발생할 때만 첫 번째 부분을 구축하기로 결정했다.
    그러나 이를 위해서는 상반부에 Docker 이미지를 조건부로 구축하고 하반부에 이전에 존재했던 상반부나 새로 구축된 상반부를 사용해야 한다.

    솔루션 - 구축, 에이전트, 프로모션


    우리의 해결 방안은 여러 개의 CI 작업으로 구성된 모델을 사용하여 서로 다른 부품을 처리한다.

  • 구축 작업-docker 이미지 구축을 책임진다.조건부로(제1부분) 또는 일치적으로(제2부분).

  • 프록시 작업 - 다음 작업에 Docker 이미지와 관련된 태그:latest 또는 현재 커밋을 제공하는 구성 작업을 처리하는 조건입니다.

  • 홍보 - 새로 만든 이미지를 :latest에 표시하고 추진하는 것을 책임진다.그들은 맨 마지막에 달렸다.
  • 우리의 용례에 대해 우리는 다음과 같은 설정을 사용했다.
  • 조건부 생성 작업, 변경이 적은 코드 생성에 사용

  • 관련 태그를 생성하는 프록시 작업

  • 빠르게 변화하는 코드를 구축하고 설치하는 데 사용되는 Build job

  • 테스트 작업, 새로 생성된 코드 테스트에 사용

  • 업그레이드 작업, 테스트가 통과된 경우
  • , 새로 생성된 이미지를 푸시:latest이를 실현하기 위해 저희는 다음과 같은 .yml 설정을 만들었습니다. 에이전트 업그레이드 모델을 구축하고 include:file 를 사용하여 저희.gitlab-ci.yml에 도입했습니다.
    # This file allows creating a prebuild-proxy-(build)-promote workflow with ease.  
    #  
    # The idea is that that in the prebuild step we build less-frequently-changed  
    # docker images than in the build step. This allows us to significantly speed  
    # up CI times.  
    #  
    # Setting Up  
    # ==========  
    #  
    # A basic setup consists of the following:  
    #  
    # 1\. Prebuild (extends .bpp:build)  - build docker images if relevant files changed  
    # 2\. Proxy (extends .bpp:proxy) - allows the rest of the CI to know whether Prebuild created new images or not  
    # 3\. Build [Optional] (extends .bpp:build) - builds extra, more-frequently-changing images.  
    #                                             This is not a conditional step!  
    # 4\. Use (custom step) - here we actually use the images we created!  
    # 5\. Promote (extends .bpp:promote) - if required, pushes the newly built images to the project's repository.  
    #  
    # These 5 steps should be in 5 different, consecutive stages for things to work.  
    # The prebuild step, being conditional, should not have any other step requiring it.  
    # All other steps (that need the prebuilt images) should require the proxy step instead,  
    # and use the ${PROXY_TAG} to as a label to the relevant docker images.
    
    
    .bpp:build:
      variables:
        # The names of all the docker images we want to pull from our registry
        TO_PULL: ""
        # The tag to use for pulling the images. This will usually be ${PROXY_TAG}
        PULL_TAG: ""
        # The name of the image and path of the dockerfile for building docker images.
        # The root path for the dockers will be the root of the project
        # Format the variable as follows:
        #
        #     >-
        #       "some_name the/relevant/path/Dockerfile"
        #       "some_other_name another/relevant/path/Dockerfile"
        #
        # Note that the quotes are significant!
        TO_BUILD: ""
        # The names of the images we want to push.
        # They will all be pushed with the ${CI_COMMIT_SHA} tag.
        TO_PUSH: ""
      script:
        - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY_IMAGE}
        - export DOCKER_BUILDKIT=1 # This cannot be in the `variables` field since users overwrite it.
        - |
          for IMAGE_NAME in ${TO_PULL}
          do
              echo "***********************************"
              echo "Pulling ${IMAGE_NAME}"
              echo "-----------------------------------"
              echo "docker pull ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${PULL_TAG}"
              echo "DOCKER_BUILDKIT=1 docker tag \
                    ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${PULL_TAG} \
                    ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:latest"
              echo "***********************************"
              docker pull ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${PULL_TAG}
              docker tag \
                  ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${PULL_TAG} \
                  ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:latest
          done
        - |
          eval "ARRAY=($TO_BUILD)"
          for ITEM in "${ARRAY[@]}"
          do
              MY_NAME=${ITEM% *}
              MY_PATH=${ITEM#* }
              echo "***********************************"
              echo "Building ${MY_NAME} from ${MY_PATH}"
              echo "-----------------------------------"
              echo "DOCKER_BUILDKIT=1 docker build \
                  --build-arg BUILDKIT_INLINE_CACHE=1 \
                  -t ${CI_REGISTRY_IMAGE}/${MY_NAME} \
                  -t ${CI_REGISTRY_IMAGE}/${MY_NAME}:${CI_COMMIT_SHA} \
                  -f ${MY_PATH} \
                  --label "commit_sha=${CI_COMMIT_SHA}" \
                  ."
              echo "***********************************"
              docker build \
                  --build-arg BUILDKIT_INLINE_CACHE=1 \
                  -t ${CI_REGISTRY_IMAGE}/${MY_NAME} \
                  -t ${CI_REGISTRY_IMAGE}/${MY_NAME}:${CI_COMMIT_SHA} \
                  -f ${MY_PATH} \
                  --label "commit_sha=${CI_COMMIT_SHA}" \
                  .
          done
        - |
          for IMAGE_NAME in $TO_PUSH
          do
              echo "***********************************"
              echo "Pushing ${IMAGE_NAME}"
              echo "-----------------------------------"
              echo "DOCKER_BUILDKIT=1 docker push ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${CI_COMMIT_SHA}"
              echo "***********************************"
              docker push ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${CI_COMMIT_SHA}
          done
    
    
    .bpp:proxy:
      variables:
        # The names of jobs we want to proxy - if any of them succeeded, we proxy.
        BUILD_JOBS: ""
      script:
        - PROXY_TAG=latest
        - apt-get -qq update
        - apt-get -qq install jq
        # Get the successful jobs for the current pipeline
        - >-
          curl
          --header "PRIVATE-TOKEN:${GITLAB_TOKEN}"
          "${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/pipelines/${CI_PIPELINE_ID}/jobs?scope[]=success"
          > jobs.json
        # Compare the job names from the pipeline with the provided job names
        - EXECUTED=$(comm -12 <(jq -r '.[].name' jobs.json | sort) <(echo ${BUILD_JOBS} | tr ' ' '\n' | sort))
        # If a build job was executed, we need to set the proxy tag to the current commit sha.
        - |
          if [ ! -z "$EXECUTED" ]
          then
              PROXY_TAG=${CI_COMMIT_SHA}
          fi
        - echo "PROXY_TAG=${PROXY_TAG}" >> deploy.env
        # Print out the proxy tag - for debug purposes
        - echo "PROXY_TAG=${PROXY_TAG}"
      artifacts:
        # To get the proxy tag, you need to get the artifacts from this job.
        # The proxy tag will be ${PROXY_TAG}
        reports:
          dotenv: deploy.env
    
    
    .bpp:promote:
      variables:
        # The names of the images you wish to promote.
        # They need to have been built & pushed by a previous build step.
        TO_PROMOTE: ""
      script:
        - |
          if [ "${PROXY_TAG}" = "latest" ]; then
              echo "Nothing to promote."
          else
              echo "Promoting docker image."
              docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} ${CI_REGISTRY_IMAGE}
    
              for IMAGE in ${TO_PROMOTE} ; do
                  docker pull ${CI_REGISTRY_IMAGE}/${IMAGE}:${CI_COMMIT_SHA}
                  docker tag ${CI_REGISTRY_IMAGE}/${IMAGE}:${CI_COMMIT_SHA} ${CI_REGISTRY_IMAGE}/${IMAGE}:latest
                  docker push ${CI_REGISTRY_IMAGE}/${IMAGE}:latest
              done
          fi
    

    좋은 웹페이지 즐겨찾기