Au cours des dernières années, je suis devenu très friand de GitLab CI . Principalement pour sa simplicité et sa fonctionnalité. Il suffit de créer un fichier à la racine du référentiel .gitlab-ci.yml
, d'y ajouter quelques lignes de code, et le prochain commit démarrera le pipeline avec un ensemble de travaux qui exécuteront les commandes spécifiées.
Et si vous ajoutez les capacités d' inclusion et d' extension à cela , vous pouvez faire des choses assez intéressantes: créer des modèles de travaux et des pipelines, les déplacer dans des référentiels séparés et les réutiliser dans différents projets sans copier le code.
Mais malheureusement, tout n'est pas aussi rose que nous le souhaiterions. L'instruction script
dans GitLab CI est de très bas niveau. Il exécute simplement les commandes qui lui sont données sous forme de chaînes. Il n'est pas très pratique d'écrire de gros scripts dans YAML. Au fur et à mesure que la logique devient plus complexe, le nombre de scripts augmente, ils se mélangent avec YAML rendant les configurations illisibles et compliquant leur maintenance.
J'ai vraiment manqué un mécanisme qui simplifierait le développement de gros scripts. En conséquence, un microframework pour le développement de GitLab CI est né, dont je veux parler dans cet article (en utilisant l'exemple d'un simple pipeline pour construire des images docker).
Exemple: création d'images docker dans GitLab
Je souhaite examiner le processus de création d'un pipeline à l'aide d'un exemple de tâche simple que j'ai souvent rencontrée avec différentes équipes: créer des images docker de base dans GitLab pour les réutiliser.
Par exemple, une équipe écrit des microservices et souhaite utiliser sa propre image de base pour tous avec un ensemble d'utilitaires de débogage préinstallés.
Ou un autre exemple, une équipe écrit des tests et souhaite utiliser les services directement dans GitLab pour créer une base de données temporaire (ou une file d'attente, ou autre chose) pour eux en utilisant leur propre image.
, 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
:
:
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
}
:
( step2): https://gitlab.com/chakrygin/dockerfiles-example/-/tree/step2
: https://gitlab.com/chakrygin/dockerfiles-example/-/pipelines/303676828
gitlab-ci.sh
, .gitlab-ci.yml
. search_all_dockerfiles_task
search_changed_dockerfiles_task
DOCKERFILES
, build_and_push_dockerfiles_task
.
3:
, , . , , . , - , , .
. , , , - , . , .
, .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
}
:
( step3): https://gitlab.com/chakrygin/dockerfiles-example/-/tree/step3
( step3): https://gitlab.com/chakrygin/dockerfiles-example-ci/-/tree/step3
: https://gitlab.com/chakrygin/dockerfiles-example/-/pipelines/303702906
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
}
:
( step4): https://gitlab.com/chakrygin/dockerfiles-example/-/tree/step4
( step4): https://gitlab.com/chakrygin/dockerfiles-example-ci/-/tree/step4
: https://gitlab.com/chakrygin/dockerfiles-example/-/pipelines/303704359
GitLab CI bash GitLab CI Bootstrap.
. :
. .. , . GitLab CI.
. , Python C#.
. , .
, .
, 1.0. , . , .
Sources: peuvent être trouvées ici: https://gitlab.com/chakrygin/bootstrap
Dépôts avec des exemples ici: