En los últimos años, me he encariñado mucho con GitLab CI . Principalmente por su sencillez y funcionalidad. Basta con crear un archivo en la raíz del repositorio .gitlab-ci.yml
, agregar algunas líneas de código allí y la siguiente confirmación iniciará la canalización con un conjunto de trabajos que ejecutarán los comandos especificados.
Y si agrega las capacidades de inclusión y extensión a esto , puede hacer cosas bastante interesantes: crear trabajos de plantilla y canalizaciones, moverlos a repositorios separados y reutilizarlos en diferentes proyectos sin copiar el código.
Pero, lamentablemente, no todo es tan optimista como nos gustaría. La instrucción script
en GitLab CI es de muy bajo nivel. Simplemente ejecuta los comandos que se le dan en forma de cadenas. No es muy conveniente escribir scripts grandes dentro de YAML. A medida que la lógica se vuelve más compleja, aumenta la cantidad de scripts, se mezclan con YAML haciendo que las configuraciones sean ilegibles y complicando su mantenimiento.
Realmente me perdí algún mecanismo que simplificaría el desarrollo de scripts grandes. Como resultado, nació un microframework para el desarrollo de GitLab CI, del que quiero hablar en este artículo (usando el ejemplo de una canalización simple para construir imágenes de Docker).
Ejemplo: compilar imágenes de Docker en GitLab
Quiero considerar el proceso de creación de una canalización usando un ejemplo de una tarea simple que a menudo me encontré con diferentes equipos: crear imágenes de docker básicas en GitLab para su reutilización.
Por ejemplo, un equipo escribe microservicios y quiere usar su propia imagen base para todos ellos con un conjunto de utilidades de depuración preinstaladas.
U otro ejemplo, un equipo escribe pruebas y quiere usar servicios directamente en GitLab para crear una base de datos temporal (o cola, o algo más) para ellos usando su propia imagen.
, 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. , . , .
Fuentes: se pueden encontrar aquí: https://gitlab.com/chakrygin/bootstrap
Repositorios con ejemplos aquí: