Docker stack на примере системы в несколько сервисов

Задача заключается в создании приложения из нескольких сервисов с автономными контейнерами в одной сети ос связкой с Mysql Percona Cluster в среде DEV. Итак приступим.

Наш итоговый рабочий конфиг из трех сервисов, выглядит следующим образом:

version: '3'

volumes:
  smetacloud_web:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.98.110,rw,nolock,soft
      device: ":/nfs/docker/cloud/cloud"
  smetacloud_yii:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.98.110,rw,nolock,soft
      device: ":/nfs/docker/cloud/framework"
  smetacloud_nginx_ssl:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.98.110,rw,nolock,soft
      device: ":/nfs/docker/cloud/nginx/ssl"

services:

  cloud_php:
    image: repo.dmz:8081/dev/phpfpm56withexts:v6

    volumes:
       - smetacloud_web:/var/www/cloud.smeta.ru
       - smetacloud_yii:/var/www/framework
       - /nfs/docker/cloud/php/cloud_php.ini:/usr/local/etc/php/php.ini

    environment:
        - MAIN_URL=https://devcloud.smeta.ru
        - MAINSERVICEURL=https://cloudservice.smeta.ru
        - IDDOMAIN=https://auth.smeta.ru
        - BACKURL=https://devcloud.smeta.ru/site/success
        - ADMINURL=https://devcloud.smeta.ru/Cloudadmin
        - CITRIXINSTALLERPATH=/var/www/cloud.smeta.ru/installers/
        - SITEFRAMEWORKCONTRACT=https://cloudservice.smeta.ru/Service/StroySoft.SmetaCloud.Service.SiteFrameworkContract.svc?singleWsdl
        - SITECONTRACT=https://smetaru.slcloud.ru/Service/StroySoft.SmetaCloud.Service.SiteContract.svc?singleWsdl
        - MAILATTACHMENTURL=https://cloud.smeta.ru
        - DB_HOST=192.168.98.111
        - DB1_PORT=3306
        - DB1_BASENAME=cloudsmetaru
        - DB1_LOGIN=root
        - DB1_PASS=rootpass
        - DB2_BASENAME=idsmeta
        - DB2_PORT=7001
        - DB3_BASENAME=osticket
        - DB4_BASENAME=servicedb
        - DB2_PASS=rootpass2
        - DB5_BASENAME=statistics
        - DB6_BASENAME=vk

    networks:
       smetarudev:
       proxy:

    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.environment == dev]

  cloud_nginx:
    image: repo.dmz:8081/dev/nginx:v1

    volumes:
      - /nfs/docker/cloud/nginx/cloud.conf:/etc/nginx/conf.d/default.conf
      - smetacloud_nginx_ssl:/etc/nginx/ssl
      - smetacloud_web:/var/www/cloud.smeta.ru
      - smetacloud_yii:/var/www/framework

    networks:
       smetarudev:
       proxy:

    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.environment == dev]
      labels:
        - traefik.enable=true
        - traefik.port=80
        - traefik.ssl.port=443
        - traefik.frontend.rule=Host:devcloud.smeta.ru
        - traefik.docker.network:proxy 
        - traefik.frontend.redirect.entryPoint=https

  cloud_memcached:
    image: repo.dmz:8081/dev/memcached:v1.1

    networks:
       smetarudev:
 
    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.environment == dev]

networks:
  smetarudev:
     driver: overlay
     external: true
  proxy:
     driver: overlay
     external: true

Разберем его детально и расскажем как собирался наш стек.
Секция с volumes обозначает общую сетевую nfs-папку c swarm dev, откуда мы будем брать статичные настройки для нашего приложения, например:
smetacloud_web:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.98.110,rw,nolock,soft
      device: ":/nfs/docker/cloud/cloud"

в этой подпапке находятся все скрипты сайта devcloud.smeta.ru.
Далее:
smetacloud_yii:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.98.110,rw,nolock,soft
      device: ":/nfs/docker/cloud/framework"

Подпапка с web-движком для сайта devcloud.smeta.ru
Еще в одной nfs-папке находятся все необходимые сертификаты для web-сервера nginx:
smetacloud_nginx_ssl:
    driver: local
    driver_opts:
      type: nfs
      o: addr=192.168.98.110,rw,nolock,soft
      device: ":/nfs/docker/cloud/nginx/ssl"

Далее идет перечисление сервисов в секции «services» со всеми необходимыми параметрами.
У нас здесь 3 сервиса, связанных между собой в одной виртуальной сети:
1) cloud_php — сервис с phpfpm — непосредственно сам сайт devcloud.smeta.ru
Далее опишем параметры внутри первого сервиса.
image: repo.dmz:8081/dev/phpfpm56withexts:v6

здесь мы подключаем отдельный универсальный образ с phpfpm на основе alpine в котором уже установлены все необхоимые php-модули
для работы большинства наших сайтов с версией php 5.6
volumes:
       - smetacloud_web:/var/www/cloud.smeta.ru
       - smetacloud_yii:/var/www/framework
       - /nfs/docker/cloud/php/cloud_php.ini:/usr/local/etc/php/php.ini

Далее в секции «volumes» монтируем сначала две отдельные папки со скриптами сайта и php-движком на yii2,
а в третьей строке подключаем конфиг php — обратите внимание, чтобы указать отдельный файл с nfs, его нужно прописать полностью:
- /nfs/docker/cloud/php/cloud_php.ini:/usr/local/etc/php/php.ini

Далее идет длинный список переменных окружения в секции «environment»:
- MAIN_URL=https://devcloud.smeta.ru
        - MAINSERVICEURL=https://cloudservice.smeta.ru
        - IDDOMAIN=https://auth.smeta.ru
        - BACKURL=https://devcloud.smeta.ru/site/success
        - ADMINURL=https://devcloud.smeta.ru/Cloudadmin
        - CITRIXINSTALLERPATH=/var/www/cloud.smeta.ru/installers/
        - SITEFRAMEWORKCONTRACT=https://cloudservice.smeta.ru/Service/StroySoft.SmetaCloud.Service.SiteFrameworkContract.svc?singleWsdl
        - SITECONTRACT=https://smetaru.slcloud.ru/Service/StroySoft.SmetaCloud.Service.SiteContract.svc?singleWsdl
        - MAILATTACHMENTURL=https://cloud.smeta.ru
        - DB_HOST=192.168.98.111
        - DB1_PORT=3306
        - DB1_BASENAME=cloudsmetaru
        - DB1_LOGIN=root
        - DB1_PASS=rootpass
        - DB2_BASENAME=idsmeta
        - DB2_PORT=7001
        - DB3_BASENAME=osticket
        - DB4_BASENAME=servicedb
        - DB2_PASS=rootpass2
        - DB5_BASENAME=statistics
        - DB6_BASENAME=vk

в нем мы указали необходимые ссылки и параметры подключения к percona cluster и другого отдельного mysql-сервера.

Непосредственно вышеуказанные переменные в случае с php можно подключать в скриптах таким образом в конфиге сайта:
'connectionString' => 'mysql:host='.getenv(DB_HOST).';port='.getenv(DB1_PORT).';dbname='.getenv(DB1_BASENAME)


Ниже идет перечисление виртуальных сетей в секции «networks» необходимых для работы приложения:
networks:
       smetarudev:
       proxy:

Здесь smetarudev — это отдельная виртуальная сеть трех наших сервисов,
а proxy — сеть Прокси-сервера Traefik
Завершающая секция «deploy» в нашем первом сервисе стека
показывает нам, что реплика будет только одна и подниматься на одной из трех (в настоящий момент)
нод в сварме DEV:
deploy:
      replicas: 1
      placement:
        constraints: [node.labels.environment == dev]

2) сервис cloud_nginx — веб-сервис nginx, транслирующий наш сайт.
Разберем его также детально.
image: repo.dmz:8081/dev/nginx:v1

Подключаем первую сборку nginx без дополнительных модулей — все необходимое уже встроено в этот докер.
volumes:
      - /nfs/docker/cloud/nginx/cloud.conf:/etc/nginx/conf.d/default.conf
      - smetacloud_nginx_ssl:/etc/nginx/ssl
      - smetacloud_web:/var/www/cloud.smeta.ru

Первой строкой мы напрямую монтируем конфиг сайта devcloud.smeta.ru для nginx.
Следующие две — путь к сертификатам и исходникам сайта в nfs-папке.
Обратите внимание, что одни и те же nfs-папки для нужно указывать и для сервиса с phpfpm и для сервиса с nginx.
networks:
       smetarudev:
       proxy:

Здесь как выше было сказано, также подключаем 2 виртуальные сетки.
deploy:
      replicas: 1
      placement:
        constraints: [node.labels.environment == dev]

Также одна реплика и сварм dev.
labels:
        - traefik.enable=true
        - traefik.port=80
        - traefik.ssl.port=443
        - traefik.frontend.rule=Host:devcloud.smeta.ru
        - traefik.docker.network:proxy 
        - traefik.frontend.redirect.entryPoint=https

Здесь уже интересней. Перед нами новая секция labels, в которой мы указываем прокси-серверу Traefik параметры подключения с принудительным редиректом на защищенное по протоколу https.
3) сервис cloud_memcached — сервер для кеширования статичных данных, к-е редко меняются — нужен для оптимизации и более быстрой загрузки нашего сайта.
Это стандартный докер с минимальными параметрами из двух секций, о которых мы уже говорили:
networks:
       smetarudev:

    deploy:
      replicas: 1
      placement:
        constraints: [node.labels.environment == dev]

После раздела с тремя нашими сервисами завершающим этапом обозначаем 2 виртуальные сетки:
networks:
  smetarudev:
     driver: overlay
     external: true
  proxy:
     driver: overlay
     external: true

Теперь расскажем об особенностях сборки образов до формирования окончательного стека.
С Memcached все предельно ясно — это готовый докер, не требующий более никаких кастомных настроек — просто используем дефолтный образ.
В nginx нас интересует только конфиг сайта, к-й мы подключали выше; разберем в нем только один ключевой нестандартный момент:

server {

    listen   *:80;
    listen   *:443;

    error_log  /var/log/nginx/errorus.log;

    set $host_path "/var/www/cloud.smeta.ru";
    server_name devcloud.smeta.ru;
    root   $host_path;

 location / {
        set $yii_bootstrap "index.php";
        charset utf-8;
        index /index.php;
        try_files $uri $uri/ /$yii_bootstrap?$args;
    }


        location ~ ^/(protected|themes/\w+/views) {
        deny  all;
    }

        location ~ \.php$ {


                                fastcgi_split_path_info  ^(.+\.php)(.*)$;
                                set $fsn /$yii_bootstrap;
                                if (-f $document_root$fastcgi_script_name){
                                        set $fsn $fastcgi_script_name;
                                }

                include /etc/nginx/fastcgi_params;
                fastcgi_pass  cloud_php:9000;
                fastcgi_param REMOTE_ADDR $http_x_real_ip;
                fastcgi_param  SCRIPT_FILENAME  $document_root$fsn;
                fastcgi_param  PATH_INFO        $fastcgi_path_info;
                fastcgi_param  PATH_TRANSLATED  $document_root$fsn;

   }
   location ~* ^.+.(gadget)$ {
        root /var/www/cloud.smeta.ru;
    }
        error_page  404  http://cloud.smeta.ru;
}

В секции подключения к phpfpm есть одна интересная строка:
fastcgi_pass  cloud_php:9000;

Обратите внимание, что этот хост «cloud_php» не указан нигде в докере, а информацию о его местонахождении на конечном этапе уже берет на себя стек, который смотрит все сервисы в одной отмеченной виртуальной сети (в данном случае smetarudev) и связывает их между собой.
Проверить видит ли система в докере nginx этот хост можно довольно просто ping, входящий в пакет iputils-ping:

Как видите этот хост пингуется со стороны нашего веб-сервера nginx- значит в конфиге этот хост указан верно.
Самым трудоемким получился этап кастомной сборки образа с php версии 5.6
В итоге получился довольно внушительный Dockerfile:
FROM repo.dmz:8081/universal/php:5.6-fpm-alpine

RUN apk update && apk add libxml2-dev

RUN apk add curl-dev
RUN apk add mariadb-dev autoconf gcc g++ make git memcached curl-dev

COPY docker-php-ext-* docker-php-entrypoint /usr/local/bin/

RUN docker-php-ext-install xml && \
docker-php-ext-install soap && \
docker-php-ext-install curl && \
docker-php-ext-install pdo && \
docker-php-ext-install mysqli && \
docker-php-ext-install mysql

RUN apk add ca-certificates && \
apk --no-cache add openssl && \
apk --no-cache add openssl wget

#ставим старенький memcache для нашего старенького php 5.6
RUN apk add memcached
RUN apk add libmemcached-dev cyrus-sasl-dev && \
git clone -b 2.2.0 https://github.com/php-memcached-dev/php-memcached /usr/src/php/ext/memcached && \
cd /usr/src/php/ext/memcached && \
docker-php-ext-configure memcached && \
docker-php-ext-install memcached

RUN docker-php-ext-install pdo_mysql

RUN apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev

RUN docker-php-ext-configure gd \
        --with-gd \
        --with-freetype-dir=/usr/include/ \
        --with-png-dir=/usr/include/ \
        --with-jpeg-dir=/usr/include/ && \
    NPROC=$(grep -c ^processor /proc/cpuinfo 2>/dev/null || 1) && \
    docker-php-ext-install -j${NPROC} gd && \
    apk del --no-cache freetype-dev libpng-dev libjpeg-turbo-dev

RUN wait
RUN apk add php5-pear
RUN apk add php5-dev
RUN pecl install timezonedb
RUN echo "extension=timezonedb.so;" >> /etc/php5/php.ini
RUN wait
RUN apk add tzdata nginx mc nano
RUN wait
RUN cp /usr/share/zoneinfo/Europe/Moscow /etc/localtime
RUN echo "Europe/Moscow" >  /etc/timezone

COPY ./php.ini /usr/local/etc/php/php.ini

CMD ["php-fpm"]

Собираем его локально командой
docker build -t phpfpm56withexts .

Тагетируем полученный образ с хранилищем Harbor
docker tag phpfpm56withexts:latest repo.dmz:8081/dev/phpfpm56withexts:v6

и загружаем его в хранилище
docker push repo.dmz:8081/dev/phpfpm56withexts:v6

После этого кастомный образ c phpfpm и всеми нужными плагинами можно будет использовать в стеке.
Подытожим теперь весь регламент работ по созданию нашего приложения из 3 сервисов для сварма от А до Я:
1) настраиваем локально площадку из трех нод для сварма с одной общей сетевой nfs-папкой
2) подключаем внешний репозиторий Harbor к нашему локальному докер-сварму.
3) выносим конфиги сервисов в сетевую папку, чтобы не менять переменные окружения внутри контейнеров и лишний раз не перезапускать их.
4) при необходимости собираем кастомные образы со всеми необходимыми плагинами и сохраняем их в репозитории учитывая инкрементацию версий, например phpfpm56withexts:v4, phpfpm56withexts:v5, phpfpm56withexts:v6
5) Деплоим стек локально и проверяем на наличие ошибок, например:
docker stack deploy -c stack.yml devcloud
6) если все верно, модернизируем конфиг стека с учетом среды DEV; в нем однозначно нужно будет поменять настройки для монтирования NFS, указать другую внутреннюю виртуальную сеть и другие переменные окружения подключения к кластеру и внешним серверам. П.ч. локально к кластеру можно достучаться по одному ip 192.168.100.72, а в условиях dev-среды уже адрес уже другой: 192.168.98.111
7) запускаем стек через portainer и смотрим ошибки если они возникли:

При необходимости заходим в консоль через портаинер

и убеждаемся что нужный нам сервис (в данном случае phpfpm) запущен

В конечном итоге мы должны увидеть поднятую реплику для каждого из 3 наших сервисов

Итак, что мы имеем в конечном итоге:
Рабочее приложение в dev-среде из 3 автономных сервисов, связанных между собой виртуальной сетью, с вынесенными в отдельную общую папку конфигами и скриптами сайта, работающее c Mysql Percona Cluster и по https с прокси-сервером Traefik.

0 комментариев

Автор топика запретил добавлять комментарии