4. Gitlab Docker container registry. CI скрипт для роботи docker images

Відео версія
(YouTube - для зворотнього зв'язку)

Продовження попереднього гайда. Контейнер з PHP завжди модифікується, ми ставимо розширення для фреймворку, залежностей і так далі. Тому Dockerfile з php сервісом існує завжди.

Збирати ж php контейнер кожен раз як на сервері, так і на машинах розробників не найкраща ідея.

По-перше, і це головне, деплой та прогін тестів на сервері може проходити безліч разів на день, кожен раз збирати контейнер завантажувати та встановлювати одні й ті ж залежності не раціонально й довго.

По-друге, консистентність - на продакшені, стейжі, тестах завжди буде однакове середовище розробки, без поправок на версії будь-яких залежностей

Для Gitlab створюю новий проєкт в підгрупі infrastructure з назвою swoole

.gitlab-ci.yml
  stages:
    - build

  default:
    image: docker:26.1.3-alpine3.19
    services:
      - docker:26.1.3-dind
    before_script:
      - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY

  variables:
    IMAGE_NAME: 'swoole/core'
    IMAGE_NAME_DEV: $CI_REGISTRY_IMAGE/dev:${CI_COMMIT_TAG}
    IMAGE_NAME_PROD: $CI_REGISTRY_IMAGE/prod:${CI_COMMIT_TAG}

  build-image:
    stage: build
    script:
      - docker build -t $IMAGE_NAME -f Dockerfile .
      - docker build -t $IMAGE_NAME_DEV -f dev.Dockerfile . --build-arg SOURCE=$IMAGE_NAME
      - docker build -t $IMAGE_NAME_PROD -f prod.Dockerfile . --build-arg SOURCE=$IMAGE_NAME
      - docker push $IMAGE_NAME_DEV
      - docker push $IMAGE_NAME_PROD
    rules:
      - if: '$CI_COMMIT_TAG =~ /^v(\d+\.)?(\d+\.)?(\*|\d+)$/'

Тут треба звернути увагу на

  • $CI_COMMIT_TAG - назва тегу. Будь-який реліз робимо тегами.
  • docker login - команда для авторизації в реєстрі Docker.
  • $CI_REGISTRY_USER, $CI_REGISTRY_PASSWORD, $CI_REGISTRY - gitlab змінні.
  • docker build -t $IMAGE_NAME -f Dockerfile . - збираємо базовий імейдж. На його основі збираємо прод, дев та будь-яку іншу версію.
  • docker push - пушимо зображення в реєстр
  • if: '$CI_COMMIT_TAG =~ /^v(\d+\.)?(\d+\.)?(\*|\d+)$/' - умова по якій запускається крок розробки. У цьому випадку запуститься лише для тегів з назвою з літерою v та звичайна семантична версійність.
dev.Dockerfile
ARG SOURCE

FROM ${SOURCE}

RUN apk add --no-cache nodejs npm git

ARG SOURCE FROM ${SOURCE} - змінна, яку передаємо командою --build-arg SOURCE= у нашому випадку - swoole/core зображення.

Логіка роботи з проєктом:

  • Всі глобальні зміни додаємо в Dockerfile. Зазвичай туди йдуть php.ini файли, всі php extension.
  • До файлу dev.Dockerfile станом на зараз додається node, npm та git. За необхідності туди ж встановлюють xdebug, XhProf і тд. Важливо не вмикайте їх для проду. Вони дуже й дуже навантажують сервер. Я б і для dev не вмикав, краще винести їх в окремий контейнер.
  • To prod.Dockerfile go all production settings, opcache, turn off display of errors, etc.

І звісно ж все це описуємо в README.md

README.md
### Local work
0) Build core image
```sh
docker build -t local/swoole-core .
```
1) Build dev image
```sh
docker build -t local/swoole-dev -f dev.Dockerfile . --build-arg SOURCE='local/swoole-core'
```
2) Build prod image
```sh
docker build -t local/swoole-prod -f prod.Dockerfile . --build-arg SOURCE='local/swoole-core'
```
3) Use `local/swoole-prod` or `local/swoole-dev` in your projects


### Deploy

0) Commit your changes
1) Merge to main
2) Switch to main branch
3) Make tag with {version} (using semantic rules https://semver.org/) 
should increment [last tags](https://gitlab.com/bandheart.com/back/infrastructure/swoole/container_registry) and match the expression
```regexp
/^v(\d+\.)?(\d+\.)?(\*|\d+)$/
```
3) Push your tag
```
git push origin {version}
```
It will create 2 tagged versions:
- `registry.gitlab.com/bandheart.com/back/infrastructure/swoole/dev:{version}` - for development
- `registry.gitlab.com/bandheart.com/back/infrastructure/swoole/prod:{version}` - for production

Також не забуваємо про .dockerignore

.dockerignore
**.git
.idea
README.md
.gitlab-ci.yml
dev.Dockerfile
Dockerfile
prod.Dockerfile

Also, don't forget about .dockerignore