diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000..6e426e3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,13 @@ +image: registry.amegaserver.com/images/docker +variables: + DOCKER_BASEIMAGE: ubuntu:18.04 + DOCKER_MAINTAINER: vasyakrg@gmail.com + BUILD_SCRIPT: ci/build-image/build.sh + GIT_SUBMODULE_STRATEGY: none + +build: + script: + - git submodule sync + - git submodule update --init --remote --force + - chmod +x "${BUILD_SCRIPT}" && "./${BUILD_SCRIPT}" + when: manual diff --git a/Dockerfile b/Dockerfile index 7ee00f5..20d9793 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,8 @@ -FROM ubuntu:18.04 +ARG DOCKER_BASEIMAGE +FROM ${DOCKER_BASEIMAGE} + +ARG DOCKER_MAINTAINER +LABEL maintainer="${DOCKER_MAINTAINER}" ARG PROJECT_NAME ARG DBTYPE diff --git a/ci/build-image/.gitignore b/ci/build-image/.gitignore new file mode 100644 index 0000000..13d4ac5 --- /dev/null +++ b/ci/build-image/.gitignore @@ -0,0 +1,2 @@ +.env +.idea/ \ No newline at end of file diff --git a/ci/build-image/README.md b/ci/build-image/README.md new file mode 100644 index 0000000..dc4bd54 --- /dev/null +++ b/ci/build-image/README.md @@ -0,0 +1,207 @@ +Проект содержит скрипты которые обеспечивают сборку контейнера docker и загрузку его в docker registry. + +Состав: +* build.sh - скрипт сборки образа +* import.sh - скрипт загрузки образа в docker registry +* .env - конфигурационный файл для запуска сборки в ручном режиме + +#### Сборка контейнера +* Контейнер собирается с использованием Dockerfile либо файла, определяемого переменной `DOCKER_FILE`, из корня проекта. При этом в него передаются параметры: + * Контекст сборки может быть определен переменной `DOCKER_BUILD_CONTEXT`, по умолчанию `.` + * Для успешной сборки образа, контейнер, в котором происходит сборка (директива `image:` в `.gitlab-ci.yml` файле) должен иметь установленный bash и git. + Для этих целей можно использовать образ `docker.ds.mlmsoft.cloud/images/docker`. + * Имя базового образа, определяется переменной `DOCKER_BASEIMAGE` + (по умолчанию используется `ubuntu:18.04`) + * Установка переменной `DOCKER_NO_PULL_BASEIMAGE` в непустое значение позволяет не осуществлять принудительный `docker pull` базового образа + * Префикс собираемого локально образа задается переменной `DOCKER_IMAGE_PREFIX` + (по умолчанию `mlmsoft`) + * Версия базового образа определяется (в порядке приоритета) либо переменной `VERSION`, либо содержимым файла `VERSION` из корня проекта, если файл отсутствует, берется версия `latest` + * Maintainer контейнера определяется переменной `DOCKER_MAINTAINER` + (по умолчанию указывается email пользователя в gitlab или service@ds.mlmsoft.cloud при локальном запуске, если иное не указано в Dockerfile) + * Имя собираемого образа оперделяется переменной `DOCKER_IMAGE_NAME`. По умолчанию используется короткое имя проекта в Gitlab (${CI_PROJECT_PATH#*/}) + * Тег собираемого образа определяется переменной `DOCKER_IMAGE_TAG`. При отсутствии переменной берется переменная `${VERSION}` и тд + * Для передачи произвольных аргументов в Dockerfile можно использовать набор переменных DOCKER_ARG0...DOCKER_ARG9 (соблюдение нумерации не обязательно), в секции `before_script:` .gitlab-ci.yml: + + `export DOCKER_ARG1="VERSION=1.0.0"` + + Переменная VERSION будет доступна в Dockerfile через конструкцию `ARG VERSION` + + Альтернативно можно передевать аргументы в сборку как параметры сборочного скрипта в формате `ARG=value`. + +* Контейнер выкладывается в docker registry `docker.ds.mlmsoft.cloud`, полный путь к контейнеру: + + `docker.ds.mlmsoft.cloud/${CI_PROJECT_NAME}:${VERSION}` если имя образа совпадает с именем проекта, либо + `docker.ds.mlmsoft.cloud/${CI_PROJECT_NAME}/${IMAGE_NAME}:${VERSION}` если имя образа отлично от имени проекта + + где: + - `${CI_PROJECT_NAME}` полный путь проекта вида `<группа>/имя проекта>` или `<пользователь>/имя проекта>` + - `${VERSION}` это версия образа или `latest` в случае ее отсутствия. + +#### Версионность образа +Правила формирования версии образа: +* Версия образа берется из файла VERSION, который должен располагаться в корне проекта +* В случае отсутствия файла, берется версия `:latest` + +Правила определения версии базового образа (DOCKER_BASEIMAGE): +* Если в DOCKER_BASEIMAGE есть тег образа, в качестве DOCKER_BASEIMAGE берется значение переменной ${DOCKER_BASEIMAGE} без изменений +* Если в DOCKER_BASEIMAGE отсутствует тег образа, то берется ${DOCKER_BASEIMAGE}:${VERSION} + +Правила определения версии итогового образа: +* Образ тегируется как ${IMAGE}:${VERSION} +* В случае импорта в регистри, образ тегируется как `docker.ds.mlmsoft.cloud/${CI_PROJECT_NAME}:${VERSION}` +* В случае, если сборка происходит не из ветки master, образ принудительно тегируется по имени ветки, из которой производится сборка. + Если происходит сборка не из ветки master и переменная `DOCKER_IMAGE_TAG` не пустая, то образ тегируется как `$DOCKER_IMAGE_TAG_$BRANCH`. + Даннове поведение можно отменить установив переменную `DOCKER_NO_BRANCH_TAG` в непустое значение. + +Дополнительно возможно указать переменную `DOCKER_TAG_AS_LATEST`, которая отвечает за дополнительное тегирование образа тегом `latest` в регистри. + +#### Подключение скриптов к проекту +Проект предполагает использование через механизм gitlab submodules или клонирование скриптов при сборке проекта. Примеры файлов и команды находятся в каталоге example/. + +##### Подключение через механизм git submodules +Доступно два варианта использования: + а) Использование фиксированной версии сборщика + б) Использование последней доступной версии + +Для настройки необходимо: +1) Однократно выполнить в проекте команду + +`git submodule add -b master ../../infra/ci-build-image.git ci/build-image` + +2) В корне проекта разместить `Dockerfile` вида: +``` +ARG DOCKER_BASEIMAGE +FROM ${DOCKER_BASEIMAGE} + +ARG DOCKER_MAINTAINER +LABEL maintainer="${DOCKER_MAINTAINER}" + +# Uncomment if you use assets/ +#COPY assets/ / +``` +3) а) Для использования фиксированной версии в `.gitlab-ci.yml` проекта прописать переменную `GIT_SUBMODULE_STRATEGY=recursive` + б) Для использования последней доступной версии в `.gitlab-ci.yml` проекта прописать переменную `GIT_SUBMODULE_STRATEGY=none` + +4) Осуществить вызов сборки в `.gitlab-ci.yml`: +а) Вариант фиксированной версии: +``` +image: docker.ds.mlmsoft.cloud/images/docker + +variables: + DOCKER_BASEIMAGE: ubuntu:18.04 +# DOCKER_IMAGE_NAME: ${CI_PROJECT_PATH#*/} +# DOCKER_IMAGE_TAG: latest + BUILD_SCRIPT: ci/build-image/build.sh + GIT_SUBMODULE_STRATEGY: recursive + +#before_script: +# - export DOCKER_ARG0="key=value" +# - export DOCKER_ARG1="key=value" + +stages: + - build + +build: + stage: build + script: + - chmod +x "${BUILD_SCRIPT}" && "./${BUILD_SCRIPT}" + when: manual +``` + +б) Вариант последней доступной версии: +``` +image: docker.ds.mlmsoft.cloud/images/docker + +variables: + DOCKER_BASEIMAGE: ubuntu:18.04 +# DOCKER_IMAGE_NAME: ${CI_PROJECT_PATH#*/} +# DOCKER_IMAGE_TAG: latest + BUILD_SCRIPT: ci/build-image/build.sh + GIT_SUBMODULE_STRATEGY: none + +#before_script: +# - export DOCKER_ARG0="key=value" +# - export DOCKER_ARG1="key=value" + +stages: + - build + +build: + stage: build + script: + - git submodule sync + - git submodule update --init --remote --force + - chmod +x "${BUILD_SCRIPT}" && "./${BUILD_SCRIPT}" + when: manual +``` + +5) Обновление submodule осуществляется командой: + +`git submodule sync && git submodule update --init --remote` + +##### Клонирование скриптов при сборке проекта + +1) В корне проекта разместить `Dockerfile` вида: +``` +ARG DOCKER_BASEIMAGE +FROM ${DOCKER_BASEIMAGE} + +ARG DOCKER_MAINTAINER +LABEL maintainer="${DOCKER_MAINTAINER}" + +# Uncomment if you use assets/ +#COPY assets/ / +``` + +2) Осуществить вызов сборки в `.gitlab-ci.yml`: +``` +image: docker.ds.mlmsoft.cloud/images/docker + +variables: + DOCKER_BASEIMAGE: ubuntu:18.04 +# DOCKER_IMAGE_NAME: ${CI_PROJECT_PATH#*/} +# DOCKER_IMAGE_TAG: latest + BUILD_SCRIPT: ci/build-image/build.sh + +#before_script: +# - export DOCKER_ARG0="key=value" +# - export DOCKER_ARG1="key=value" + +stages: + - build + +build: + stage: build + script: + - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.ds.mlmsoft.cloud/infra/ci-build-image.git ${BUILD_SCRIPT%/*} + - chmod +x "${BUILD_SCRIPT}" && "./${BUILD_SCRIPT}" + when: manual +``` + + +#### Ручной запуск +Возможен запуск сборки на локальной машине и выкладывание его в docker registry без внесения изменений в проект. Для этого рядом со сборочными скриптами в `/ci/build-images` должен находиться конфиг с именем `.env` (пример конфига см. в файле `sample.env`): +``` +# Your project target image name and tag +DOCKER_IMAGE_NAME=image +DOCKER_IMAGE_TAG=latest + +# Base image name and tag +DOCKER_BASEIMAGE=ubuntu:18.04 + +# Docker build context (leave it as is if not know that it is) +DOCKER_BUILD_CONTEXT=. +DOCKER_PROJECT=external/project +CI_COMMIT_REF_SLUG=master + +# Registry credentials (need only if docker registry upload required) +REGISTRY_USER=%YOUR_GITLAB_USERNAME% +REGISTRY_PASSWORD=%YOUR_GITLAB_PASSWORD% +REGISTRY_URL=docker.ds.mlmsoft.cloud +``` + +Запуск сборки из каталога проекта: `./ci/build-image/build.sh` + +При ручном запуске автоматическая загрузка в docker registry не происходит, необходимо использовать скрипт `import.sh`: + +`DOCKER_IMAGE=image ./import.sh` \ No newline at end of file diff --git a/ci/build-image/build.sh b/ci/build-image/build.sh new file mode 100644 index 0000000..ba10dbe --- /dev/null +++ b/ci/build-image/build.sh @@ -0,0 +1,76 @@ +#!/bin/bash + +set -e + +if [[ -n "${BUILD_SCRIPT}" ]]; then SCRIPTS_PATH=${BUILD_SCRIPT%/*}; else SCRIPTS_PATH=ci/build/image; fi +DOCKER_BASEIMAGE=${DOCKER_BASEIMAGE:-ubuntu:18.04} +DOCKER_MAINTAINER=${DOCKER_MAINTAINER:-$GITLAB_USER_EMAIL} +DOCKER_MAINTAINER=${DOCKER_MAINTAINER:-service@ds.mlmsoft.cloud} +DOCKER_IMAGE_NAME=${DOCKER_IMAGE_NAME:-${CI_PROJECT_PATH#*/}} +DOCKER_IMAGE_PREFIX=${DOCKER_IMAGE_PREFIX:-mlmsoft} +DOCKER_FILE=${DOCKER_FILE:-Dockerfile} + +if [[ -z "${VERSION}" ]]; then + if [[ -f "VERSION" ]]; then VERSION=$(cat VERSION); else VERSION=latest; fi +fi +DOCKER_IMAGE_TAG=${DOCKER_IMAGE_TAG:-$VERSION} +if [[ "${CI_COMMIT_REF_SLUG}" != "master" ]]; then + if [[ "${DOCKER_IMAGE_TAG}" == "latest" ]]; then TAG=''; else TAG="${DOCKER_IMAGE_TAG}_"; fi + echo TAG=${TAG} + if [[ -z "${DOCKER_NO_BRANCH_TAG}" ]]; then + DOCKER_IMAGE_TAG=${TAG}${CI_COMMIT_REF_SLUG} + echo Change DOCKER_IMAGE_TAG=${DOCKER_IMAGE_TAG} + fi +fi + +if [[ -n "${CI}" ]]; then + DOCKER_BUILD_CONTEXT=${DOCKER_BUILD_CONTEXT:-'.'} +elif [[ -f ${BASH_SOURCE[0]%/*}/.env ]]; then + source ${BASH_SOURCE[0]%/*}/.env +fi + +BUILD_ARGS=() +for var in $@; do + [[ "${var}" =~ ^[a-zA-Z0-9+_-]+=.+$ ]] && BUILD_ARGS+=( --build-arg ${var} ) +done + +for (( i=0; i<10; i++ )); do + arg=DOCKER_ARG$i + [[ -n ${!arg} ]] && BUILD_ARGS+=( --build-arg ${!arg} ) +done + +[[ ${DOCKER_BASEIMAGE} != *:* ]] && DOCKER_BASEIMAGE=${DOCKER_BASEIMAGE}:${VERSION} +#[[ ${DOCKER_BASEIMAGE} == *:latest ]] && docker pull ${DOCKER_BASEIMAGE} + +# Скачивание базового образа +if [[ -z ${DOCKER_NO_PULL_BASEIMAGE:+x} ]]; then + docker login -u gitlab-ci-token -p ${CI_JOB_TOKEN} ${CI_REGISTRY} + docker pull ${DOCKER_BASEIMAGE} +fi + +echo DOCKER_BASEIMAGE=${DOCKER_BASEIMAGE} +echo DOCKER_IMAGE_NAME=${DOCKER_IMAGE_NAME} +echo DOCKER_IMAGE_TAG=${DOCKER_IMAGE_TAG} +echo DOCKER_BUILD_CONTEXT=${DOCKER_BUILD_CONTEXT} +echo DOCKER_NO_BRANCH_TAG=${DOCKER_NO_BRANCH_TAG} +echo DOCKER_NO_PULL_BASEIMAGE=${DOCKER_NO_PULL_BASEIMAGE} +echo DOCKER_FILE=${DOCKER_FILE} +echo DOCKER_TAG_AS_LATEST=${DOCKER_TAG_AS_LATEST} +echo VERSION=${VERSION} +echo Building args: ${BUILD_ARGS[@]} + +DOCKER_IMAGE=${DOCKER_IMAGE_PREFIX}/${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG} +if ! docker build -f ${DOCKER_FILE} --build-arg DOCKER_BASEIMAGE=${DOCKER_BASEIMAGE} --build-arg DOCKER_MAINTAINER=${DOCKER_MAINTAINER} \ + --build-arg VERSION=${VERSION} ${BUILD_ARGS[@]} --no-cache --force-rm -t ${DOCKER_IMAGE} ${DOCKER_BUILD_CONTEXT} ; then + # Remove all orphaned images after unsuccessfull build + docker image prune -f > /dev/null 2>&1 + exit 1; +fi + +#docker tag ${DOCKER_IMAGE_NAME} ${DOCKER_IMAGE_NAME}:${DOCKER_IMAGE_TAG} + +if [[ -n "${CI}" ]]; then + chmod +x "${SCRIPTS_PATH}/import.sh" + DOCKER_IMAGE=${DOCKER_IMAGE} "${SCRIPTS_PATH}/import.sh" +fi + diff --git a/ci/build-image/example/.gitlab-ci.yml b/ci/build-image/example/.gitlab-ci.yml new file mode 100644 index 0000000..5f664f0 --- /dev/null +++ b/ci/build-image/example/.gitlab-ci.yml @@ -0,0 +1,24 @@ +image: docker.ds.mlmsoft.cloud/images/docker + +variables: + DOCKER_BASEIMAGE: ubuntu:18.04 +# DOCKER_IMAGE_NAME: ${CI_PROJECT_PATH#*/} +# DOCKER_IMAGE_TAG: latest + BUILD_SCRIPT: ci/build-image/build.sh +# GIT_SUBMODULE_STRATEGY: recursive + GIT_SUBMODULE_STRATEGY: none + +#before_script: +# - export DOCKER_ARG0="key=value" +# - export DOCKER_ARG1="key=value" + +stages: + - build + +build: + stage: build + script: + - git submodule sync + - git submodule update --init --remote --force + - chmod +x "${BUILD_SCRIPT}" && "./${BUILD_SCRIPT}" + when: manual \ No newline at end of file diff --git a/ci/build-image/example/.gitlab-ci_2.yml b/ci/build-image/example/.gitlab-ci_2.yml new file mode 100644 index 0000000..1751448 --- /dev/null +++ b/ci/build-image/example/.gitlab-ci_2.yml @@ -0,0 +1,21 @@ +image: docker.ds.mlmsoft.cloud/images/docker + +variables: + DOCKER_BASEIMAGE: ubuntu:18.04 +# DOCKER_IMAGE_NAME: ${CI_PROJECT_PATH#*/} +# DOCKER_IMAGE_TAG: latest + BUILD_SCRIPT: ci/build-image/build.sh + +#before_script: +# - export DOCKER_ARG0="key=value" +# - export DOCKER_ARG1="key=value" + +stages: + - build + +build: + stage: build + script: + - git clone https://gitlab-ci-token:${CI_JOB_TOKEN}@gitlab.ds.mlmsoft.cloud/infra/ci-build-image.git ${BUILD_SCRIPT%/*} + - chmod +x "${BUILD_SCRIPT}" && "./${BUILD_SCRIPT}" + when: manual \ No newline at end of file diff --git a/ci/build-image/example/Dockerfile b/ci/build-image/example/Dockerfile new file mode 100644 index 0000000..9b55876 --- /dev/null +++ b/ci/build-image/example/Dockerfile @@ -0,0 +1,8 @@ +ARG DOCKER_BASEIMAGE +FROM ${DOCKER_BASEIMAGE} + +ARG DOCKER_MAINTAINER +LABEL maintainer="${DOCKER_MAINTAINER}" + +# Uncomment if you use assets/ +#COPY assets/ / \ No newline at end of file diff --git a/ci/build-image/example/Makefile b/ci/build-image/example/Makefile new file mode 100644 index 0000000..0f2a412 --- /dev/null +++ b/ci/build-image/example/Makefile @@ -0,0 +1,17 @@ +image = image_name + +all: build + +clean: + @docker images -f "dangling=true" -q | docker ps -a -f "ancestor={}" -q | xargs --no-run-if-empty docker rm + @docker images -f "dangling=true" -q | xargs --no-run-if-empty docker rmi -f + + @docker ps --filter "ancestor=${image}" --format {{.ID}} | xargs --no-run-if-empty docker stop || true + @docker ps -a --filter "ancestor=${image}" --format {{.ID}} | xargs --no-run-if-empty docker rm || true + @docker images --filter "reference=${image}" --format {{.ID}} | xargs --no-run-if-empty docker rmi || true + +build: + @docker build --tag=${image} --no-cache . + +release: build + @docker build --tag=${image}:$(shell cat VERSION) . \ No newline at end of file diff --git a/ci/build-image/example/VERSION b/ci/build-image/example/VERSION new file mode 100644 index 0000000..afaf360 --- /dev/null +++ b/ci/build-image/example/VERSION @@ -0,0 +1 @@ +1.0.0 \ No newline at end of file diff --git a/ci/build-image/example/init.sh b/ci/build-image/example/init.sh new file mode 100644 index 0000000..095b92d --- /dev/null +++ b/ci/build-image/example/init.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +git submodule add -b master ../../infra/ci-build-image.git ci/build-image \ No newline at end of file diff --git a/ci/build-image/import.sh b/ci/build-image/import.sh new file mode 100644 index 0000000..32547e1 --- /dev/null +++ b/ci/build-image/import.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -e + +if [[ -n "${CI}" ]]; then + REGISTRY_USER=gitlab-ci-token +# REGISTRY_PASSWORD=${CI_JOB_TOKEN} + REGISTRY_URL=${CI_REGISTRY} + DOCKER_PROJECT=${CI_PROJECT_PATH} + REGISTRY_IMAGE=${DOCKER_IMAGE#*/} + [[ "${CI_PROJECT_PATH#*/}" == "${REGISTRY_IMAGE%:*}" ]] && DOCKER_PROJECT=${CI_PROJECT_PATH%/*} +elif [[ -f .env ]]; then + source ${BASH_SOURCE[0]%/*}/.env + CI_JOB_TOKEN=${REGISTRY_PASSWORD} +fi + +echo DOCKER_IMAGE=${DOCKER_IMAGE} +echo DOCKER_PROJECT=${DOCKER_PROJECT} + +docker login -u ${REGISTRY_USER} -p ${CI_JOB_TOKEN} ${REGISTRY_URL} + +docker tag ${DOCKER_IMAGE} ${REGISTRY_URL}/${DOCKER_PROJECT}/${REGISTRY_IMAGE} +docker push ${REGISTRY_URL}/${DOCKER_PROJECT}/${REGISTRY_IMAGE} + +if [[ -n "${DOCKER_TAG_AS_LATEST}" ]]; then + REGISTRY_IMAGE=${REGISTRY_IMAGE%:*}:latest + docker tag ${DOCKER_IMAGE} ${REGISTRY_URL}/${DOCKER_PROJECT}/${REGISTRY_IMAGE} + docker push ${REGISTRY_URL}/${DOCKER_PROJECT}/${REGISTRY_IMAGE} +fi \ No newline at end of file diff --git a/ci/build-image/sample.env b/ci/build-image/sample.env new file mode 100644 index 0000000..9513fcc --- /dev/null +++ b/ci/build-image/sample.env @@ -0,0 +1,16 @@ +# Your project target image name and tag +DOCKER_IMAGE_NAME=image +DOCKER_IMAGE_TAG=latest + +# Base image name and tag +DOCKER_BASEIMAGE=ubuntu:18.04 + +# Docker build context (leave it as is if not know that it is) +DOCKER_BUILD_CONTEXT=. +DOCKER_PROJECT=external/project +CI_COMMIT_REF_SLUG=master + +# Registry credentials (need only if docker registry upload required) +REGISTRY_USER=%YOUR_GITLAB_USERNAME% +REGISTRY_PASSWORD=%YOUR_GITLAB_PASSWORD% +REGISTRY_URL=docker.ds.mlmsoft.cloud