Criação de pipelines reutilizáveis ​​para GitLab CI no bash

Nos últimos anos, passei a gostar muito do GitLab CI . Principalmente por sua simplicidade e funcionalidade. Basta criar um arquivo na raiz do repositório .gitlab-ci.yml



, adicionar algumas linhas de código lá e o próximo commit iniciará o pipeline com um conjunto de jobs que executarão os comandos especificados.





E se você adicionar a inclusão e estender recursos para isso , você pode fazer coisas bastante interessantes: criar trabalhos de modelo e pipelines, movê-los para repositórios separados e reutilizá-los em projetos diferentes sem copiar o código.





Mas, infelizmente, nem tudo é tão róseo como gostaríamos. A instrução script



no GitLab CI é de nível muito baixo. Ele simplesmente executa os comandos que lhe são fornecidos na forma de strings. Não é muito conveniente escrever scripts grandes dentro de YAML. Conforme a lógica se torna mais complexa, o número de scripts aumenta, eles se misturam com YAML tornando as configurações ilegíveis e complicando sua manutenção.





Eu realmente senti falta de algum mecanismo que simplificasse o desenvolvimento de grandes scripts. Como resultado, nasceu um microframework para o desenvolvimento do GitLab CI, sobre o qual quero falar neste artigo (usando o exemplo de um pipeline simples para construir imagens docker).





Exemplo: construir imagens docker no GitLab

Quero considerar o processo de criação de um pipeline usando um exemplo de uma tarefa simples que sempre encontrei com diferentes equipes: a criação de imagens docker básicas no GitLab para reutilização.





Por exemplo, uma equipe escreve microsserviços e deseja usar sua própria imagem de base para todos eles com um conjunto de utilitários de depuração pré-instalados.





Ou outro exemplo, uma equipe escreve testes e deseja usar serviços diretamente no GitLab para criar um banco de dados temporário (ou fila, ou outra coisa) para eles usando sua própria imagem.





, docker- GitLab . .gitlab-ci.yml



:





services:
  - docker:dind

Build:
  image: docker
  script:
    - |
      docker login "$CI_REGISTRY" \
        --username "$CI_REGISTRY_USER" \
        --password "$CI_REGISTRY_PASSWORD"

      docker build \
        --file "Dockerfile" \
        --tag "$CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_REF_SLUG" .

      docker push "$CI_REGISTRY/$CI_PROJECT_PATH:$CI_COMMIT_REF_SLUG"
      
      



Build



, GitLab Container Registry, . , ( , ).





, , :





  • , dockerfiles



    .





  • :





    • Build All



      - dockerfiles



      . ( ).





    • Build Changed



      - dockerfiles



      , . ( ) .





. . , .





:









, .NET.





:









docker-, Container Registry :





, .





1: " "

. .gitlab-ci.yml



. :





services:
  - docker:dind

stages:
  - Build

Build All:
  stage: Build
  image: docker
  when: manual
  script:
    - |
      dockerfiles=$(find "dockerfiles" -name "*.Dockerfile" -type f)

      docker login "$CI_REGISTRY" \
        --username "$CI_REGISTRY_USER" \
        --password "$CI_REGISTRY_PASSWORD"

      for dockerfile in $dockerfiles; do
        path=$(echo "$dockerfile" | sed 's/^dockerfiles\///' | sed 's/\.Dockerfile$//')
        tag="$CI_REGISTRY/$CI_PROJECT_PATH/$path:$CI_COMMIT_REF_SLUG"

        echo "Building $dockerfile..."
        docker build --file "$dockerfile" --tag "$tag" .

        echo "Pushing $tag..."
        docker push "$tag"
      done

Build Changed:
  stage: Build
  image: docker
  only:
    changes:
      - 'dockerfiles/*.Dockerfile'
      - 'dockerfiles/**/*.Dockerfile'
  script:
    - |
      apk update
      apk add git #  ,     ,    ...

      dockerfiles=$(git diff --name-only HEAD HEAD~1 -- 'dockerfiles/***.Dockerfile')

      docker login "$CI_REGISTRY" \
        --username "$CI_REGISTRY_USER" \
        --password "$CI_REGISTRY_PASSWORD"

      for dockerfile in $dockerfiles; do
        path=$(echo "$dockerfile" | sed 's/^dockerfiles\///' | sed 's/\.Dockerfile$//')
        tag="$CI_REGISTRY/$CI_PROJECT_PATH/$path:$CI_COMMIT_REF_SLUG"

        echo "Building $dockerfile..."
        docker build --file "$dockerfile" --tag "$tag" .

        echo "Pushing $tag..."
        docker push "$tag"
      done
      
      



:





  • ( step1)









:





  • Build All



    , :





    • , .. when: manual



      .





    • :





      • find "dockerfiles" -name "*.Dockerfile" -type f







  • Build Changed



    , :





    • , .. only:changes



      .





    • , :





      • git diff --name-only HEAD HEAD~1 -- 'dockerfiles/***.Dockerfile'







  • , , , dockerfiles/



    .Dockerfile



    , GitLab Container Registry.





:





  • ( )





  • , .





GitLab CI .





GitLab CI Bootstrap

GitLab CI Bootstrap - GitLab CI. :





  • ( .gitlab-ci.yml



    ) (bash shell), YAML .





  • , .





  • ( ) , .





GitLab CI Bootstrap bootstrap.gitlab-ci.yml, .gitlab-ci.yml



include. . include:local:





include:
  - local: 'bootstrap.gitlab-ci.yml'
      
      



.bootstrap



, :





.bootstrap:
  before_script:
    - |
     	...
      
      



.bootstrap



, extends:





example:
  extends: '.bootstrap'
  script:
    - '...'
      
      



( ), GitLab.





. . , , .





bash shell. git, . .bootstrap



docker- .





, , docker-.





2:

, .bootstrap



.gitlab-ci.sh



. , .





, , .gitlab-ci.yml



:





.gitlab-ci.yml



:





include:
  - project: '$CI_PROJECT_NAMESPACE/bootstrap'
    ref: 'master'
    file: 'bootstrap.gitlab-ci.yml'

services:
  - docker:dind

stages:
  - Build

Build All:
  stage: Build
  image: docker
  extends: .bootstrap
  when: manual
  script:
    - search_all_dockerfiles_task
    - build_and_push_dockerfiles_task

Build Changed:
  stage: Build
  image: docker
  extends: .bootstrap
  only:
    changes:
      - 'dockerfiles/*.Dockerfile'
      - 'dockerfiles/**/*.Dockerfile'
  script:
    - install_git_task
    - search_changed_dockerfiles_task
    - build_and_push_dockerfiles_task
      
      



.gitlab-ci.sh



:





DOCKERFILES=""

function search_all_dockerfiles_task() {
    DOCKERFILES=$(find "dockerfiles" -name "*.Dockerfile" -type f)
}

function search_changed_dockerfiles_task() {
    DOCKERFILES=$(git diff --name-only HEAD HEAD~1 -- 'dockerfiles/***.Dockerfile')
}

function install_git_task() {
    #  ,     ,    ...
    apk update
    apk add git
}

function build_and_push_dockerfiles_task() {
    docker login "$CI_REGISTRY" \
        --username "$CI_REGISTRY_USER" \
        --password "$CI_REGISTRY_PASSWORD"

    for dockerfile in $DOCKERFILES; do
        path=$(echo "$dockerfile" | sed 's/^dockerfiles\///' | sed 's/\.Dockerfile$//')
        tag="$CI_REGISTRY/$CI_PROJECT_PATH/$path:$CI_COMMIT_REF_SLUG"

        echo "Building $dockerfile..."
        docker build --file "$dockerfile" --tag "$tag" .

        echo "Pushing $tag..."
        docker push "$tag"
    done
}
      
      



:





gitlab-ci.sh



, .gitlab-ci.yml



. search_all_dockerfiles_task



search_changed_dockerfiles_task



DOCKERFILES



, build_and_push_dockerfiles_task



.





3:

, , . , , . , - , , .





include.





. , , , - , . , .





, .bootstrap



CI_IMPORT



, : CI_{module}_PROJECT



, CI_{module}_REF



CI_{module}_FILE



, {module}



- CI_IMPORT



( ).





, .gitlab-ci.sh



, .





:





.gitlab-ci.yml



:





include:
  - project: '$CI_PROJECT_NAMESPACE/dockerfiles-example-ci'
    ref: 'step3'
    file: 'dockerfiles.gitlab-ci.yml'
      
      



:





dockerfiles.gitlab-ci.yml



( .gitlab-ci.yml



):





include:
  - project: '$CI_PROJECT_NAMESPACE/bootstrap'
    ref: 'master'
    file: 'bootstrap.gitlab-ci.yml'

services:
  - docker:dind

stages:
  - Build

variables:
  CI_DOCKERFILES_PROJECT: '$CI_PROJECT_NAMESPACE/dockerfiles-example-ci'
  CI_DOCKERFILES_REF: 'step3'
  CI_DOCKERFILES_FILE: 'dockerfiles.gitlab-ci.sh'

Build All:
  stage: Build
  image: docker
  extends: .bootstrap
  variables:
    CI_IMPORT: dockerfiles
  when: manual
  script:
    - search_all_dockerfiles_task
    - build_and_push_dockerfiles_task

Build Changed:
  stage: Build
  image: docker
  extends: .bootstrap
  variables:
    CI_IMPORT: dockerfiles
  only:
    changes:
      - 'dockerfiles/*.Dockerfile'
      - 'dockerfiles/**/*.Dockerfile'
  script:
    - search_changed_dockerfiles_task
    - build_and_push_dockerfiles_task
      
      



dockerfiles.gitlab-ci.sh



( .gitlab-ci.sh



):





DOCKERFILES=""

function search_all_dockerfiles_task() {
    DOCKERFILES=$(find "dockerfiles" -name "*.Dockerfile" -type f)
}

function search_changed_dockerfiles_task() {
    DOCKERFILES=$(git diff --name-only HEAD HEAD~1 -- 'dockerfiles/***.Dockerfile')
}

function build_and_push_dockerfiles_task() {
    docker login "$CI_REGISTRY" \
        --username "$CI_REGISTRY_USER" \
        --password "$CI_REGISTRY_PASSWORD"

    for dockerfile in $DOCKERFILES; do
        path=$(echo "$dockerfile" | sed 's/^dockerfiles\///' | sed 's/\.Dockerfile$//')
        tag="$CI_REGISTRY/$CI_PROJECT_PATH/$path:$CI_COMMIT_REF_SLUG"

        echo "Building $dockerfile..."
        docker build --file "$dockerfile" --tag "$tag" .

        echo "Pushing $tag..."
        docker push "$tag"
    done
}
      
      



:





Build All



Build Changed



CI_IMPORT



dockerfiles



. : CI_DOCKERFILES_PROJECT



, CI_DOCKERFILES_REF



CI_DOCKERFILES_FILE



, .





, dockerfiles.gitlab-ci.sh



, $CI_PROJECT_NAMESPACE/dockerfiles-example-ci



, step3



.





, install_git_task



. git , .. git git .





4:

. , .





dockerfiles.gitlab-ci.sh



. , .





, include



, , . , . include



, .





, , include



.





dockerfiles.gitlab-ci.sh



:





dockerfiles.gitlab-ci.sh



:





include "tasks/search.sh"
include "tasks/docker.sh"
      
      



tasks/search.sh



:





DOCKERFILES=""

function search_all_dockerfiles_task() {
    DOCKERFILES=$(find "dockerfiles" -name "*.Dockerfile" -type f)
}

function search_changed_dockerfiles_task() {
    DOCKERFILES=$(git diff --name-only HEAD HEAD~1 -- 'dockerfiles/***.Dockerfile')
}
      
      



tasks/docker.sh



:





function build_and_push_dockerfiles_task() {
    docker login "$CI_REGISTRY" \
        --username "$CI_REGISTRY_USER" \
        --password "$CI_REGISTRY_PASSWORD"

    for dockerfile in $DOCKERFILES; do
        path=$(echo "$dockerfile" | sed 's/^dockerfiles\///' | sed 's/\.Dockerfile$//')
        tag="$CI_REGISTRY/$CI_PROJECT_PATH/$path:$CI_COMMIT_REF_SLUG"

        echo "Building $dockerfile..."
        docker build --file "$dockerfile" --tag "$tag" .

        echo "Pushing $tag..."
        docker push "$tag"
    done
}
      
      



:





GitLab CI bash GitLab CI Bootstrap.





. :





  • . .. , . GitLab CI.





  • . , Python C#.





  • . , .





, .





, 1.0. , . , .





Fontes: podem ser encontradas aqui: https://gitlab.com/chakrygin/bootstrap





Repositórios com exemplos aqui:








All Articles