Имеется проект на Azure, которым я занимался большую часть последнего года (с 20-го марта 2016).
Пост Azure: почему никогда писался под впечатлением работы как раз на нём (да и большая часть рубрики Azure – тоже).
За год мы перенесли приложение на Umbraco CMS с MS SQL базами от другого агентства к нам (и с Ажуры на Ажур), пачку небольших WPMU сайтов (см. тут>>>) и другие сервисы – в основном на PHP, и… с переменными на немецком. Дебаг этого кода при миграции доставлял неимоверно. Весь процесс миграции должен закончится большим феерверком – запуском новой версии основного сайта производителя алкогольной продукции (завтра, 25.-го).
Текущий деплой Umbraco CMS (legacy-сайт) описан в посте Azure: GoCD и MSDeploy – деплой UmbracoCMS в Azure WebServices.
Ниже кратко описана новая инфрастуктура с некоторыми примерами. Увы – не было возможности более подробно описать все сложности, с которыми столкнулся в процессе – но несколько интересных примеров будет.
Содержание
Описание проекта
Основой проекта является веб-сайт, который раньше работал на Umbraco CMS, а сейчас мигрирует на NodeJS (а с ним ещё ~90 доменов на Azure DNS).
Новая версия приложения включает в себя 6 контейнеров, один из которых – jm-website-proxy – представляет собой локальный NGINX с реверс-прокси для реврайтов и редиректов. Кроме того – есть ещё и внешний прокси, который обслуживает все домены и приложения проекта.
Рабочее окружение включает в себя три (Dev, QA – две) ресурс-группы – в одной находятся CDN и Traffic Manager, в двух других (Blue и Green) – VMSS с Docker-Swarm:
Pusblish и Preview TM – используются для доступа к актуальной версии (Publish) и версии для редакторов (Preview). Бекенд – Contentful.
Больше всего сложностей во всём этом (и у девелоперов тоже хватило внезапных проблем) оказался Azure CDN (Verizon Premium). Из-за того, что Verizon CDN не позволяет привязать домен иначе, как через CNAME
(который мы не могли использовать, т.к. на домене имеется множество субдоменов, почта и т.д., подробнее см. RFC) – пришлось выстраивать целую цепочку редиректов и SSL-терминаторов.
В результате была построена такая схема. Речь о Publish сервисе, т.к. он основной:
- Пользователь приходит на http://domain.tld
- domain.tld – направлен на внешний прокси-сервис проекта (NGINX), который выполняет переадресацию на https://www.domain.tld
- Субдомен www.domain.tld через
CNAME
является алиасом ендпоинта CDN – jm-website-production-cdn-endpoint.azureedge.net - У CDN в качестве Origin – указан URL Traffic Manager – jm-website-production-publish.trafficmanager.net
- У Traffic Manager – имеется два endpoint-а – Green и Blue. Green направлен на URL publish-green.domain.tld, Blue – на publish-blue.domain.tld.
В свою очередь publish-green.domain.tld и blue черезCNAME
направлены на Load Balancer-ы в Green и Blue ресурс-группах, за которым находятся Swarm-ноды в VMSS. В один момент времени – только один из ендпоинтов ТМ может быть активен (тут мы и делаем Blue/Green deployment).
Если кратко – то так.
С SSL тоже оказалось не всё гладко.
Сам Azure при добавлении к CDN-профайлу CustomDomain
и активации HTTPS для него – выдаёт для этого домена сертифкат от DigiCert (через письмо на admin-c
контакт домена). Ожидалось – что на этом мы закрываем SSL-сессию, и дальше работаем с CDN-ендпоинтами по HTTP. Оказалось, что для работы самого CDN по HTTPS – необходимо выстроить вторую SSL-сессию – от edge location серверов Verizon до наших VMSS с приложением.
Сначала решили делать это через добавление Application Gateway, но потом всё-равно пришлось добавлять локальный NGINX как сервис в общий стек для реврайтов старых URI в новые (на внешнем NGINX это было бы сложнее, т.к. много доменов/конфигов), поэтому SSL подняли там.
SSL:
- Первый сертификат, который встречает пользователь – выдан DigiCert для домена(ов), которые подключены к Azure CDN как CustomDomains. SSL termination выполняется на CDN Edge-location сервере.
- Между Azure CDN и VMSS с нодами – второе HTTPS соединение, с самоподписанным сертификатом – CDN вполне с ними работает. SSL termination выполняется на локальном прокси-сервисе (jm-website-proxy) непосредственно на нодах.
Jenkins
Рабочее окружение Jenkins
Развёртывание новых окружений, сборка новых образов и их деплой выполняются Jenkins.
Сам Jenkins работает в Docker-контейнере, ноды с билдами запускаются тоже в контейнерах.
Jenkins работает на отдельной VM, а все его данные ($JENKINS_HOME
) – хранятся на отдельном data-диске, который подключается к VM во время провижена группы.
Выглядит группа так:
(data-диск – Managed, поэтому тут не виден)
Сам шаблон можно посмотреть тут>>>.
Из интересных примеров в нём – подключение data-диска с workspaces Jenkins-a:
... { "apiVersion": "2016-04-30-preview", "type": "Microsoft.Compute/virtualMachines", "name": "[variables('vmName')]", ... "osDisk": { "createOption": "FromImage" }, "dataDisks": [ { "lun": 0, "createOption": "Attach", "caching": "None", "diskSizeGB": 100, "managedDisk": { "id": "/subscriptions/0a4f2b9c-***-40b17ef8c3ab/resourceGroups/europe-jm/providers/Microsoft.Compute/disks/jenkinsworkspaces" } } ] },
И вызов скрипта для установки и запуска самого Jenkins – jenkins_provision.sh
с помощью CustomScript extention для VM Azure:
... { "type": "Microsoft.Compute/virtualMachines/extensions", "name": "[concat(variables('vmName'),'/DockerInstall')]", "apiVersion": "2015-05-01-preview", "location": "[resourceGroup().location]", "dependsOn": [ "[concat('Microsoft.Compute/virtualMachines/', variables('vmName'))]" ], "properties": { "publisher": "Microsoft.Azure.Extensions", "type": "CustomScript", "typeHandlerVersion": "2.0", "autoUpgradeMinorVersion": true, "settings": { "fileUris": [ "https://utils.blob.core.windows.net/scripts/jenkins_provision.sh" ], "commandToExecute": "bash jenkins_provision.sh" }, "protectedSettings": { "storageAccountName": "utils", "storageAccountKey": "tOk***6Bw==" } } } ...
Сам скрипт jenkins_provision.sh
:
#!/usr/bin/env bash curl https://get.docker.com/ | bash usermod -aG docker jmadmin mkdir /jenkins mount /dev/sdc1 /jenkins/ useradd jenkins usermod -a -G docker jenkins docker run -u 0 -tid -p 80:8080 \ -v /jenkins:/var/jenkins_home \ -v /var/run/docker.sock:/var/run/docker.sock \ -v /usr/bin/docker:/usr/bin/docker \ -v /etc/passwd:/etc/passwd \ -e JENKINS_HOME='/var/jenkins_home' --name jenkins jenkins
В строке:
... mount /dev/sdc1 /jenkins/ ...
выполняется монтирование внешнего дата-диска “Microsoft.Compute/disks/jenkinsworkspaces“.
Вместе – шаблон и скрипт создают новую виртуальную машину, устанавливают туда Docker, подключают диск с данными Jenkins-а и запускают последнюю версию Jenkins.
Подробнее – в постах Azure: подключение дополнительного диска к VM и миграция Jenkins и Azure: Azure Resource Manager provisioning и Jenkins в Docker.
Билды Jenkins
Основная задача Jenkins – собирать образы с NodeJS приложением при изменениях в репозитории. Создание Github-вебхука описано тут>>>.
Билды описаны в Groovy-скриптах:
[simterm]
$ ls -l total 76 -rw-r--r-- 1 setevoy setevoy 254 Mar 2 17:44 build.groovy drwxr-xr-x 2 setevoy setevoy 4096 Mar 2 13:34 configs -rwxr-xr-x 1 setevoy setevoy 2362 Mar 2 11:13 deploy.sh -rw-r--r-- 1 setevoy setevoy 6447 Mar 11 13:38 docker-compose_v3.yml -rw-r--r-- 1 setevoy setevoy 2492 Mar 7 17:15 docker-compose.yml -rw-r--r-- 1 setevoy setevoy 1236 Mar 3 11:37 docker-compose.yml.origin -rw-r--r-- 1 setevoy setevoy 93 Mar 3 10:41 Dockerfile -rw-r--r-- 1 setevoy setevoy 30 Feb 21 11:20 hooktest.groovy -rw-r--r-- 1 setevoy setevoy 2319 Mar 8 14:01 jm-build.groovy -rw-r--r-- 1 setevoy setevoy 568 Mar 8 14:02 jm-cms-transform-build.groovy -rw-r--r-- 1 setevoy setevoy 394 Feb 24 16:02 jm-cms-transform-DEV-deploy.groovy -rw-r--r-- 1 setevoy setevoy 1452 Apr 7 14:31 jm-provision.groovy -rw-r--r-- 1 setevoy setevoy 779 Apr 7 15:51 jm-sw-dev-provision.groovy -rw-r--r-- 1 setevoy setevoy 881 Apr 7 14:32 jm-sw-production-provision.groovy -rw-r--r-- 1 setevoy setevoy 875 Apr 7 17:33 jm-sw-staging-provision.groovy -rw-r--r-- 1 setevoy setevoy 539 Mar 8 14:02 jm-website-build.groovy -rw-r--r-- 1 setevoy setevoy 390 Feb 24 16:02 jm-website-DEV-deploy.groovy -rw-r--r-- 1 setevoy setevoy 12 Feb 21 10:33 README.md
[/simterm]
По аналогии с сетапом из поста AWS: билд Java + Maven + Docker + Packer + Terraform – имеется один общий скрипт, который содержит функции и несколько скриптов – для каждого из приложений (все на NodeJS, все собираются практически одинаково).
Скрипт с функциями:
#!/usr/bin/env groovy def dockerBuild (imgName='1') { stage ('Docker build') { withDockerRegistry(registry: [credentialsId: 'jm-docker-hub-jmautomation']){ sh 'git log -n 1 > version.html' def appimage = docker.build("jmrakqa/${imgName}:${TAG}", "--build-arg NPM_TOKEN=${NPM_TOKEN} .") appimage.push() appimage.push('latest') } } } def dockerDeploy (tag='1', host='2') { git branch: "${BRANCH}", credentialsId: 'jm-github', url: 'https://github.com/jm/website-prototype.git' stage ('Docker deploy') { sh "echo -e \"Deploying to the DEV: ${host}\nWith TAG=${tag}\"" sh "./buildscripts/deploy.sh ${host} ${tag}" } } def cleanup() { sh 'echo -e "\nWiping workdir $(pwd).\n"' deleteDir() } return this
И скрипт сборки одного из приложений:
#!/usr/bin/env groovy node { ENV='dev' TAG = "${BRANCH}-${env.BUILD_NUMBER}" REPOURL = 'https://github.com/jm/jm-website.git' dir('buildscripts') { git branch: 'master', credentialsId: 'jm-github', url: 'https://github.com/jm/jm-jenkins.git' } git branch: "${BRANCH}", credentialsId: 'jm-github', url: "${REPOURL}" def website = load 'buildscripts/jm-build.groovy' website.dockerBuild('website-frontend') website.cleanup() }
Скрипты из репозитория ‘https://github.com/jm/jm-jenkins.git‘ загружаются в директорию buildscripts
, в а корень билд-директории – загружается код из репозитория с кодом – REPOURL = ‘https://github.com/jm/jm-website.git’.
В каждом репозитории с кодом имеется Dockerfile
, в котором и описана дальнейшая сборка сервиса:
FROM node:7.5.0 ENV NODE_ENV production ARG NPM_TOKEN="${NPM_TOKEN}" RUN useradd --user-group --create-home --shell /bin/false app RUN apt-get update && apt-get -y install rsync ENV HOME=/home/app COPY . $HOME # https://github.com/docker/docker/issues/30110 RUN chown -R app:app $HOME/ USER app WORKDIR $HOME RUN pwd && ls -l RUN npm config set //registry.npmjs.org/:_authToken=${NPM_TOKEN} RUN npm install --production=false RUN npm run build:production && npm prune --production CMD ["npm", "run", "start:production"]
Передача параметров (токен для Node репозитория, например) выполняется через --build-args
.
Деплой собранного образа выполняется из другого скрипта другой Jenkins-джобой:
#!/usr/bin/env groovy node { git branch: "${BRANCH}", credentialsId: 'jm-github', url: 'https://github.com/jm/jm-website.git' dir('buildscripts') { git branch: 'master', credentialsId: 'jm-github', url: 'https://github.com/jm/jm-jenkins.git' } def website = load 'buildscripts/jm-build.groovy' website.dockerDeploy("${IMAGE_TAG}", "$DEV_HOST") }
Собранные образы загружаются в приватный репозиторий проекта на DockerHub.
Деплой
Деплой новых образов выполняется bash
-скриптом, который обновляет compose
-файл на сервере и пересоздаёт стек:
#!/usr/bin/env bash HOST="$1" USER="jmadmin" RSA_KEY="buildscripts/.ssh/jmadmin" # compose to be deployed to Swarm Master COMPOSE="buildscripts/docker-compose.yml" # Version for the :tag for image + $TRAVIS_JOB_NUMBER # resulted vesrion will be ~ 1.0.3-44.1 # later, use ${BRANCH}-${env.BUILD_NUMBER} now # VERSION=$(cat package.json | grep version | cut -d":" -f 2 | sed 's/"//g' | cut -d "," -f 1) > /dev/null TAG=$2 # 1) generate new compose with sed docker-compose.yml; # 2) upload new Compose file docker-compose.yml to $HOST; # 3) stop Compose; # 4) start Compose; me="$(basename "$(test -L "$0" && readlink "$0" || echo "$0")")" [[ -z $HOST ]] && { echo -e "\nERROR: HOST must be specified as a first argument. Exit.\n"; exit 1; } [[ -z $TAG ]] && { echo -e "\nERROR: TAG must be specified as a second argument. Exit.\n"; exit 1; } [[ -e $RSA_KEY ]] && chmod 400 $RSA_KEY || { echo -e "\nERROR: RSA key $RSA_KEY not found. Exit.\n"; exit 1; } # no need atm as using LATEST tag compose_generate () { # sed -e s/CHANGEME/"$1"/g scripts/docker-compose.yml.erb > scripts/docker-compose.yml sed -i "s/BUILDTAG/$TAG/" $COMPOSE } # no need atm as using LATEST tag compose_copy () { scp -P 2200 -o StrictHostKeyChecking=no -i $RSA_KEY $COMPOSE $USER@$1:/home/$USER/ } compose_stop () { ssh -p 2200 -t -t -o StrictHostKeyChecking=no -i "$RSA_KEY" "$USER@$1" "bash -c ' export DOCKER_HOST=:2375 sudo docker-compose down --rmi all -v '" } compose_start () { ssh -p 2200 -t -t -o StrictHostKeyChecking=no -i "$RSA_KEY" "$USER@$1" "bash -c ' docker-compose up -d '" } echo -e "\n[$me] $TAG deployment started at $(date) to the $HOST\n" # no need atm as using LATEST tag #if compose_generate $VERSION-$TRAVIS_JOB_NUMBER; then # echo -e "New Compose created:\n" # cat $COMPOSE ##else # echo -e "\nERROR: can not create $COMPOSE. Exit.\n" # exit 1 #fi if compose_copy $HOST; then echo -e "\n$COMPOSE copied to the $HOST.\n" else echo -e "\nERROR: can not copy $COMPOSE to the $HOST. Exit.\n" exit 1 fi if compose_stop $HOST; then echo -e "Compose stopped.\n" else echo -e "\nERROR: can not stop Compose. Exit.\n" exit 1 fi if compose_start $HOST; then echo -e "Compose started.\n" else echo -e "\nERROR: can not start Compose. Exit.\n" exit 1 fi
На самом деле деплой выглядит немного иначе, и скрипт другой – это старая версия, но суть та же – на одном из мастеров обновляется файл docker-compose.yml
, который содержит актуальную версию образов (или просто тег `latest`), после чего на Traffic Manager-е проверяется текущая активная VMSS-нода (Blue или Green) и переключается ендпоинт. Может добавлю отдельным постом по B/G деплою на Azure с помощью Traffic Manager.
Сам docker-compose
выглядит примерно так:
version: '3' services: proxy: image: "jmakqa/jm-website-proxy:latest" ports: - "80:80" - "443:443" logging: driver: json-file options: max-size: "10m" max-file: "3" depends_on: - web web: environment: - JM_enableTrustProxy=true - JM_trustProxyIPsCSV=uniquelocal - JM_basicAuthEnabled=true - JM_contentEndpoint=http://transform:3003/get - JM_contentServer=http://transform:3003 - JM_domainPattern=publish.jm-website-sw-staging.domain.tld - JM_internalPort=8008 - JM_pollIntervalMs=30000 - JM_publicPort=8008 - JM_purgeCdn=true image: "jmakqa/jm-website:jm-website-version" ports: - "8008:8008" logging: driver: json-file options: max-size: "10m" max-file: "3" depends_on: - transform ...
Инфрастуктура
Описание
Далее я буду рассматривать Production, т.к. он имеет полный набор всего, что пришлось тут строить.
Как уже говорилось – рабочее окружение включает в себя три группы ресурсов – Switcher, Green и Blue.
Green и Blue – одинаковы, и включают в себя:
- виртуальную сеть с двумя подсетями – для master и nodes VMSS
- две VMSS – для master и nodes
- два Load Balancer – master и nodes
- две группы безопасности и два публичных IP-адреса
Switcher-группа – включает в себя:
- CDN-профайл
- его CDN-ендпоинт
- Preview Traffic Manger profile – для доступа к Preview-сервисам (с двумя ендпоинтами – Green и Blue)
- Publish Traffic Manger profile – для доступа к Publish-сервисам (с двумя ендпоинтами – Green и Blue)
CDN-ендпоинт имеет несколько добавленных CustomDomain
, к которым Azure через DigiCert сам выдаёт сертификат при добавлении домена:
В качестве Origin, как говорилось – указан Publish Traffic Manager, через который CDN обращается к бекендам (Swarm-nodes VMSS с приложением) для загрузки данных.
Для доступа по HTTPS – на бекендах пришлось добавлять свои сертификаты (jm-website-proxy). Т.е. между пользователем и CDN – используется сертификат от DigiCert, а между серверами CDN и нашим VMSS – самоподписанный сертификат с нашего NGINX-а.
Развёртывание окружений
Как и в случае с Jenkins – группы ресурсов для окружений вписаны в ARM-шаблоны:
[simterm]
$ ls -l azure-infrastructure/jm-website/ total 16 drwxr-xr-x 3 setevoy setevoy 4096 Apr 7 17:12 CDN drwxr-xr-x 3 setevoy setevoy 4096 Apr 21 14:17 ENV drwxr-xr-x 2 setevoy setevoy 4096 Apr 7 16:12 NSG drwxr-xr-x 3 setevoy setevoy 4096 Apr 3 14:43 obsolete
[/simterm]
CDN
– шаблоны для рес. групп switchersENV
– шаблоны непосредственно рабочих окружений с Docker SwarmNSG
– шаблоны групп безопасности (см. ниже)
Например:
[simterm]
$ ls -l azure-infrastructure/jm-website/ENV/ total 52 -rw-r--r-- 1 setevoy setevoy 1319 Apr 7 11:23 jm-website-sw-dev.parameters.json -rw-r--r-- 1 setevoy setevoy 24604 Apr 21 13:05 jm-website-sw.json -rw-r--r-- 1 setevoy setevoy 1338 Apr 3 14:43 jm-website-sw-production-blue.parameters.json -rw-r--r-- 1 setevoy setevoy 1340 Apr 7 13:02 jm-website-sw-production-green.parameters.json -rw-r--r-- 1 setevoy setevoy 1345 Apr 20 17:08 jm-website-sw-staging-blue.parameters.json -rw-r--r-- 1 setevoy setevoy 1346 Apr 20 17:08 jm-website-sw-staging-green.parameters.json -rw-r--r-- 1 root root 1322 Apr 20 17:17 jm-website-sw-test.parameters.json
[/simterm]
Тут jm-website-sw.json
– сам шаблон, jm-website-sw-dev.parameters.json
– файл параметров.
Для CDN – картина немного другая:
[simterm]
$ ls -l azure-infrastructure/jm-website/CDN/ total 24 -rw-r--r-- 1 setevoy setevoy 297 Apr 3 14:43 jm-website-cdn-dev.parameters.json -rw-r--r-- 1 setevoy setevoy 2243 Apr 6 16:29 jm-website-cdn.json -rw-r--r-- 1 setevoy setevoy 6522 Apr 7 12:49 jm-website-cdn-tm.json -rw-r--r-- 1 setevoy setevoy 895 Apr 3 14:43 jm-website-cdn-tm-production.parameters.json -rw-r--r-- 1 setevoy setevoy 943 Apr 7 17:12 jm-website-cdn-tm-staging.parameters.json
[/simterm]
Файл jm-website-cdn.json
содержит только CDN, а файл jm-website-cdn-tm.json
– CDN и Traffic Manager профайлы, для Staging и Production (на Dev и QA TM не используются, т.к. у них нет B/G деплоя).
Шаблоны можно посмотреть тут>>> и тут>>>.
Из интересного в этих шаблонах.
Во первых – сам провижен Docker Swarm. Пришлось лепить костыли, и писать свой скрипт, который разворачивает всю ферму (шаблон ACS с Docker Swarm от самого Azure оказался непригоден из-за невозможности подключить свою группу безопасности (!)).
Вызывается он так же из CustomScript extention:
... "extensionProfile": { "extensions": [ { "name": "MasterSwarmInstall", "properties": { "publisher": "Microsoft.Azure.Extensions", "type": "CustomScript", "typeHandlerVersion": "2.0", "autoUpgradeMinorVersion": false, "settings": { "fileUris": [ "https://utils.blob.core.windows.net/scripts/swarm_master_provision.sh", "https://utils.blob.core.windows.net/scripts/docker_cleanup.sh" ], "commandToExecute": "[concat('bash swarm_master_provision.sh ', '10.0.0.4 ', parameters('environment'))]" }, "protectedSettings": { "storageAccountName": "[parameters('storageAccountName')]", "storageAccountKey": "[parameters('storageAccountKey')]" } } } ] } ...
Причём IP первого мастера (‘10.0.0.4 ‘) пришлось хардкодить, т.к. вытащить IP из VMSS во время провижена оказалось… Долго, криво и муторно. Вот тут>>> парень из Azure Product team-ы пытался подсказать – но уже не было времени реализовывать.
Собственно, скрипты провижена можно посмотреть тут>>> (master) и тут>>> (nodes).
Кроме того, каждое окружение (Dev/QA/etc) имеет свой шаблон группы безопасности для нод (мастер-группа у всех одна с одним правилом – разрешить доступ по SSH).
Создаются NSG с помощью ресурса Microsoft.Resources/deployments
, который загружает шаблон группы из Storage Account-а:
... { "apiVersion": "2015-01-01", "name": "[variables('NodesNSGname')]", "type": "Microsoft.Resources/deployments", "properties": { "mode": "incremental", "templateLink": { "uri": "[concat('https://utils.blob.core.windows.net/templates/jm-website-nsg-', parameters('environment'), '.json', parameters('SAStoken'))]", "contentVersion": "1.0.0.0" }, "parameters": { "NodesNSGname":{"value": "[variables('NodesNSGname')]"} } } }, ...
Подробнее – в посте Azure: ARM – подключение вложенного шаблона.
Развёртывание и обновление групп ресурсов выполняются тем же Jenkins, через Azure CLI, часть параметров – в файлах, часть – в переменных самого Jenkins.
Основной скрипт – jm-provision.groovy
:
#!/usr/bin/env groovy def templateValidate(infraUrl='1', env='2', resGroup='3', templateFile='4', paramFile='5') { docker.image('microsoft/azure-cli').inside('-v /var/run/docker.sock:/var/run/docker.sock') { git branch: "${BRANCH}", credentialsId: 'jm-github', url: "${infraUrl}" stage('Temlate validate') { sh "azure login -v -u ${AZURUSER} -p ${AZUREPASS}" sh "azure account set -v ${SUBSCRIPTION}" sh "azure group template validate -g ${resGroup} -f jm-website/ENV/${templateFile} -e jm-website/ENV/${paramFile}" } } } def environmentUpdate(infraUrl='1', env='2', resGroup='3', templateFile='4', paramFile='5', tag='6') { docker.image('microsoft/azure-cli').inside('-v /var/run/docker.sock:/var/run/docker.sock') { git branch: "${BRANCH}", credentialsId: 'jm-github', url: "${infraUrl}" stage('Environment update') { sh "azure login -v -u ${AZURUSER} -p ${AZUREPASS}" sh "azure account set -v ${SUBSCRIPTION}" // for new environment // sh "azure group create -l westeurope -n ${resGroup} -f jm-website/${templateFile} -e jm-website/${paramFile}" sh "azure group deployment create -n ${tag} -g ${resGroup} -f jm-website/ENV/${templateFile} -e jm-website/ENV/${paramFile}" } } } def verify(msg='1') { stage 'Verify' input id: 'Deploy', message: "${msg}", ok: 'Deploy!' } return this
И скрипт апдейта Production:
#!/usr/bin/env groovy node { ENV='production' TAG = "${env.BUILD_TAG}" INFRAURL = 'https://github.com/jm/azure-infrastructure.git' RESGROUP = "${RESGROUP}" TMPL = "${TMPL}" PARAMS = "${PARAMS}" dir('buildscripts') { git branch: 'master', credentialsId: 'jm-github', url: 'https://github.com/jm/jm-jenkins.git' } git branch: "${BRANCH}", credentialsId: 'jm-github', url: "${INFRAURL}" def provision = load 'buildscripts/jm-provision.groovy' provision.verify("WARNING: you are going to update PRODUCTION environment. Are you sure?") provision.templateValidate("${INFRAURL}", "${ENV}", "${RESGROUP}", "${TMPL}", "${PARAMS}") provision.verify("Is Verify OK? Proceed with an environment deployment?") provision.environmentUpdate("${INFRAURL}", "${ENV}", "${RESGROUP}", "${TMPL}", "${PARAMS}", "${TAG}") }
В целом – это всё, попозже может быть добавлю ещё пару связанных постов, ибо проект (:cry:) ещё ни разу не закончен.