Комментарии 39
Поправте плз текст ссылки

С инструкцией по установке GitLab CI Runner можно ознакомиться на сайте docs.docker.com.
Актуальная тема! Спасибо!

Скажите, используете ли вы проверку кода (CodeSniffer)? Как вы считаете, уместно ли проверять код именно тут. Ведь на сколько я понимаю, если код не прошел проверку, то его просто вообще нельзя запушить на сервер.
Мы используем, но у нас это не автоматизировано: есть свой coding style, основанный на PSR-2, всем «рекомендуется» настраивать свою IDE так чтобы оно подсвечвало неправильно оформленный код. Ну и на этом всё, да и не прижилась у нас такая проверка.

Проверку на coding style можно было бы запускать до этапа build и после этапа deps. Параллельно с phpunit, например, было бы самое оно. Ну и этап можно было бы переименовать: вместо test сделать qa, например. Вообще ещё много чего можно добавить в pipeline! Например, этапа lint нет. Ещё нужно проверять а запустятся ли контейнеры после сборки до того, как их выложим на удалённый сервер и как они будут работать (вот тут я не знаю каким инструментом можно воспользоваться, кстати). Может быть ещё что-нибудь.

Ещё я не согласен с тем, что непрошедший проверку на coding style код, не может быть запушен на сервер! Если такую проверку и вводить, то этот код таки будет запушен, но результат работы не дойдёт до тестового сайта разработчика (не говоря уже про staging и production), что не позволит сдать результат работы. И если такая проверка (как и phpunit-тестирование, кстати) не позволяет выкладывать код на удалённый сервер, разработчик должен будет исправить недочёты, объединить коммиты и запушить --force свой код ещё раз.
мне кажется, что лучше на job-ах deps:php-composer и test:phpunit лучше использовать реальный php контейнер. Ведь если мы добавим в composer.json зависимость, которому будет необходим какой то экстеншн то тесты на зависимость уже не пройдут. Та же ситуация с phpunit, но тут еще и настройки php добавляются.
и возможно, такой pipeline возможно тогда будет лучше

build — создаем контейнеры и пушим в Registry
deps — используем созданные контейнеры из Registry
test — используем созданные контейнеры из Registry

Кстати, ведь для тестов вероятно нужны будут зависимости, но так как это другая джоба, то зависимостей не этапе теста нет. Тут наверное нужно объеденить deps и test в одну джобу?

На самом деле в скелете приложения задача deps:php-composer не особо и нужна. Она заполняет кэш composer, а использует этот кэш только задача test:phpunit. Задача test:phpunit запускает composer require phpunit/phpunit:* --dev что, также как и в deps, заполняет кэш composer. Я оставил deps в шаблоне "на всякий пожарный", чтобы в случае, когда параллельно с phpunit будут выполнятся какие-то другие задачи, — этот кэш не надо было наполнять в конкурентной борьбе компоузеров.


И если останется только одна задача на этапе test, то deps и test можно будет объединить. А если появится третья, например, phpcs — то весь кэш нужно будет загружать в deps так, чтобы он был доступен и для phpunit и для phpcs, т.е. объединять не нужно. Пусть пока будут разъединёнными — а потом либо надо будет удалить deps, либо на "потенциальном" этапе qa что-нибудь появится такое, что будет полезным всем.

На сколько я знаю, кеширование папок делается вот так
cache:
  paths:
    - vendor/

А каким образом это происходит у вас? ведь наполненый кеш композера и заполненая папка vendors это все таки разные вещи. И для исполнения скриптов нужно чтоб были фалы в vendors, а не в кеше композера.

Таким образом можно закэшировать только директории внутри рабочей директории.


В Dockerfile образа для php домашняя директория composer установлена в /composer/home. Кэш composer находится в /composer/home/cache. Эта директория находится вне рабочей директории, с которой может работать GitLab Runner.


И поэтому я сначала формирую кэш composer, чтобы много раз запускать composer install или composer require.

О, теперь ясно, спасибо! Но ИМХО, это не правильно, базовый образ «критично» связан с gitlab-ci. И это не явно. Можно легко все нечаянно сломать

Весь процесс будет работать и без кэширования этой директории. Но с кэшированием через gitlab ci runner оно просто быстрее работает. Но, вообще, я согласен про возможность "нечаянно сломать".

у себя я это сделал через кеширование папки vendors и работает оно быстро, ведь нужно только сравнить composer.lock и папку vendors
А сейчас так и есть: на всех этапах в качастве основы уже используется один и тот же образ для php: covex/php7.1-fpm:1.0. См. в репозитории файлы: Dockerfile (для образа приложения для удалённых серверах), Dockerfile-dev (для контейнера в локальном окружении), .gitlab-ci.yml — описание задач в pipeline. Для «скелета приложения» образ — именно такой. А для реальных задач мой образ всё равно будет не очень безопасно использовать — нужно будет делать свой, копируя и изменяя Dockerfile моего образа для контейнера с php.
Не уверен что я правильно вас понял.
Вы описали так джоб, где композер инсталирует зависимости
deps:php-composer:
    stage: deps
    image: covex/php7.1-fpm:1.0
    script:
      - echo ...
      - composer install --prefer-dist --no-scripts --no-autoloader --no-interaction

Но это не боевой контейнер, а «covex/php7.1-fpm:1.0», вы ведь когда будете изменять php docker image (тут) образы могут быть разные. Вы тестируете не тем образом что будет в продакшене

Это не инсталирование зависимостей, а заполнение кэша composer, чтобы этот кэш использовали следущие задачи. Кэш composer хранится в volume у gitlab-ci-runner и доступен для всех задач, которые запускаются этим runner-ом.


Окружение на этапе test, когда работает phpunit, совпадает с тем, что в production — там один и тот же образ. Но в production (да и везде внутри контейнеров, запущенных через docker-compose) — есть база данных, а на этапе test, для phpunit этой БД ещё нет.


Для юнит-тестов этой группы подключение БД не нужно, а юнит-тестов, использующих БД ещё и не существует. Наверное, эту задачу нужно будет сделать вместе с "интеграционными тестами". Например: (1) запускаем docker-compose внутри docker (2) проверяем запущены ли все контейнеры (3) наполняем БД данными для тестирования (4) запускаем тесты, использующие БД.

Андрей. спасибо за такие подробные ответы, но я хотел бы уточнить.

Вы тестируете образом «covex/php7.1-fpm:1.0», а на продакшене образ от этого файла который наследуется от «covex/php7.1-fpm:1.0».

Если вы будете менять образ для прода, вы ведь в этот же файл планируете вносить изминения, а не в базовый образ? Если так, то получается два разных образа.

Если вдруг будет нужно установить новое расширение или ещё какое-нибудь ПО внутрь образа с php, я сделаю новый образ на основе Dockerfile для образа covex/php7.1-fpm:1.0 и буду использовать новый образ как "базовый" для проекта на всех стадиях.

А какой тогда смысл в локальном php Dockerfile, если вы изминения будете вносить в базовый образ?

Расширения php для образа на основе alpine очень долго устанавливаются. Для создания образа я использую репозиторий на gitlab.com — там сборка образа занимает 3-4 минуты. Локально у меня на ноуте это длится в разы дольше. Если очень часто запускать docker-compose down -v и затем docker-compose up -d то, можно весь день только этим и заниматься =)

=) но вопрос остался тем же, зачем локальный Dockerfile если мы фактически вручную вносим изминения в родительский образ? Зачем тогда вообще что то собирать? Сделали docker pull чего нам надо, и юзаем

Локально используется файл Dockerfile-dev — это "базовый образ" плюс включение расширения xdebug.


Но, видимо, тут какое-то недопонимание есть. Локально весь код находится на хосте. Внутри контейнера код доступен в директории /srv. Плюс локально нужно вручную запускать phing после запуска docker-compose up


На удалённом сервере создаётся новый образ. Это "базовый образ" плюс копирование кода в внутрь контейнера, также в папку /srv; плюс запускается composer install; плюс запускается phing (для вызова cache:warmup и assets:install).


Таким образом "базовый образ" — это основа для всех окружений. От проекта к проекту основа может отличаться. Расширениями, например.


"Базовый образ" для скелета приложения — дефолтный covex/php7.1-fpm:1.0. Для другого проекта — он может быть другим, и я его сначала подготовлю, а потом буду использовать также как дефолтный.

правильно ли я понял, что мы не можем тут провести тесты, которым требуется DataBase?

На этапе test базы данных не существует — там только php. БД появляется в docker-compose после этапа build.


Основной репозиторий "скелета приложения" хранится у нас внутри GitLab. Репозиторий на GitHub — это его копия. И для репозитория на GitHub я дополнительно подключил travis-ci для тестирования "как система будет запускаться с нуля". Вот файл .travis.yml, и вот задача внутри travis.


И вот в Travis CI тесты запускатся внутри docker-compose! Таким же самым образом можно сделать "тестирование-с-базой-данных" на этапе между build и deploy. Но это сейчас не реализовано, да.

Не могли бы вы детальнее расказать, для чего для каждого разработчика свой репозиторий? Что заставило прийти к такому подходу?

Чтобы иметь возможность безболезненно менять историю коммитов. Ну и чтобы изменения одного разработчика никак не влияли на результат работы другого.


До docker у нас был один репозиторий, доступный для всех разработчиков, на проект. Для деплоя на тестовый сайт проекта, мы используем ещё один "служебный и скрытый ото всех" репозиторий, в который jenkins заливает изменения после push в ветку задачи разработчика. Вобщем, у нас Система. И эта система ломается, когда кто-то из добрых и хороших побуждений решает сделать git push --force — приходится чистить и приводить репозитории к работоспособному виду.


А иногда разработчики правят один и тот же файл в одном и том же месте! Тут опять приходится вмешиваться, потому что и тут система оказывается сломаной =) И хорошо ещё, что репозитории находятся внутри GitLab (он у нас какой-то там совсем старой версии), так что ветки master и production у нас защищены от изменений.


Ещё иногда случается, что локально без изменения коллеги всё работает, а на тестовом сайте, со слитыми изменениями всех разработчиков, что-то идёт не так.


Сейчас же у разработчика есть и свобода действий с историей коммитов с одной стороны, и строгие рамки в виде ветки origin/stable — все изменения должны идти строго после неё.

Перемудрено сильно, по-моему. Три ветки (у нас было мастер, препрод и дев) защищенные по полной, для каждой фичи или разработчика отдельная ветка, которая средствами CI/CD гитлаба и докера разворачивается на отдельный домен типа cool-feature-branch.dev.example.com, ветка препрод идёт на preprod.example.com, а мастер на example.com. Локально смотреть можно на cool-feature-branch.example.com.localhost (по дефолту, а в принципе через env переменные передаётся).

Я исходил из имеющихся у меня инструментов. Например, я не знаю как сделать новый домен, выделить свободный IP в подсети docker, и плюс ещё настроить «внешний» nginx чтобы тот проксировал HTTP-запрос в нужный внутренний ip:port. Наверное поэтому у меня так и получилось: операции с настройкой внешнего nginx — вручную, а всё остальное — автоматом. А про «локально» я не понял.

Локально — на машине разработчика разворачивается система, по возможности без необходимости ребилдить при каждом изменении исходников через монтирование volumes


Автоматическая привязка создается примерно так:


  • контейнер с прокси (nginx, haproxy, ...), привязывается к 80/443/… порту хоста, слушает события докера и при наличии у стартующего контейнера специальных меток (имя домена, порт, протокол и т. п.) реконфигурирует прокси для нового контейнера. Есть минимум два готовіх решения — docker-flow и nginx-proxy. Я первое выбрал, поскольку в целом swarm используем.
  • внешний днс сервер настраивается на вилд-кард для домена, типа *.dev.example.com, резолвя его на хост с контейнером прокси.
А может быть вы напишете тут, на хабре, про контейнер-с-прокси, про docker-flow и docker swarm?
Я кстати зарезолвил всю зону *.dev на 127.0.0.1 через dnsmasq для локальной разработки

Использовал подобное решение для резолвинга *.localhost, пока dnsmasq был штатным для ubuntu.

Сейчас начал писать пост о разработке микросервисной системы, в основе большинства сервисов которой на Symfony 3+ с DDD, ES и т. п… Не знаю успею ли закончить до 1 сентября, но постараюсь и этот момент отразить. Если не успею, то не знаю когда закончить смогу.


Основная проблема, внезапно, сначала придумать, а потом реализовать предметную область. Вроде придумал одну, но как-то она полезла в теорию графов внезапно. Вот сижу думаю стоит ли зарываться, реализовать "в лоб", а-ля MVP или переключиться на классические "бложики-тудушечки".

На сколько я понял этот подход ветвления называется forking-workflow и в atlassian считают ее очень хорошим. Правда они приводят другие преимущества.
Я вспомнил, что был на этой странице, когда изучал Git и выдумывал нашу предыдущую «систему». Видимо, тогда мне этот подход понравился, но со старым GitLab и Jenkins не получилось сделать удобный и понятный workflow, а тут — оно как-то само вспомнилось и я возомнил, что я это сам придумал =) Я стремился сделать чтобы было похоже на GitHub, где есть один общедоступный репозиторий и много contributors. В итоге так и получилось, но со своей спецификой: это всё таки не для open source проектов.
before_script:
      - eval $(ssh-agent -s)
      - ssh-add <(echo "$SSH_PRIVATE_KEY")


ssh-add <
очепятка?

Да, непонятно. Мой образ — это alpine + bash + openssh-client + git + настройка ssh и git. Практически такой же как docker:git только без докер =)

мой рабочий вариант такой
deploy_dev:
  stage: deploy
  script:
    - eval $(ssh-agent -s)
    - echo "$SSH_PRIVATE_KEY" > /tmp/id_rsa && chmod 600 /tmp/id_rsa && ssh-add /tmp/id_rsa
    - ssh -o StrictHostKeyChecking=no -p $SSH_PORT $SSH_USER@$SSH_HOST
  environment:
    name: dev
  only:
    - dev
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.