Azure: provisioning с Resource Manager, Jenkins и Groovy

Автор: | 19/09/2017
 

Описание сетапа развёртывания группы ресурсов Azure с одной виртуальной машиной для будущего мониторинга проекта.

В этой части – создание Azure Resource Manager шаблона, настройка Jenkins и примеры Groovy скриптов.

Далее – провижен NGINX и Prometheus с Ansbile. Ещё позже – Grafana.

Все файлы и скрипты хранятся в Github.

Подготовка

Используем готовый шаблон от Azure отсюда>>>.

Переименовываем файлы:

[simterm]

$ mv azuredeploy.json jm-monitoring.json
$ mv azuredeploy.parameters.json jm-monitoring.parameters.json

[/simterm]

Добавляем RSA ключ:

[simterm]

$ mkdir .ssh
$ cp ~/.ssh/setevoy_main_pub_openssh .ssh/jm-monitoring.pub                                                                           
$ cp ~/.ssh/setevoy_main_priv_openssh .ssh/jm-monitoring

[/simterm]

Логинимся:

[simterm]

$ azure login

[/simterm]

Создаём группу ресурсов:

[simterm]

$ azure group create -n jm-monitoring-dev -l westeurope
...
info:    group create command OK

[/simterm]

Обновление ARM шаблона

Сначала обновляем параметры.

vmSize из variables  переносим в parameters, что бы иметь возможность менять тип интанса для Dev и Prod (хотя можно было бы использовать какой-то mapping, по аналогии с AWS CloudFormation – но тратить на это время желания и необходимости нет):

...
    "vmSize": {
      "type": "string",
      "defaultValue": "Standard_DS1",
      "metadata": {
        "description": "SSH rsa public key file as a string."
      }
    }
...

Обновляем переменные. Мне не нравятся имена, генерируемые Azure – имя ресурса, включающее  имя группы выглядит приятнее и понятнее.

Т.е. используем функцию concat() и подставляем имя группы ресурсов и тип ресурса: "[concat(resourceGroup().name,'-RESOURCETYPE')]".

Тут же меняем ОС с Ubuntu 14 на Ubuntu 16:

...
  "variables": {
    "vmName": "[concat(resourceGroup().name,'-vm')]",
    "ubuntuOSVersion": "16.04.0-LTS",
    "uniqueDnsLabelPrefix": "[concat(resourceGroup().name,'-ip')]",
    ...
    "addressPrefix": "10.0.0.0/16",
    "subnet1Name": "[concat(resourceGroup().name,'-subnet')]",
    ...
    "nicName": "[concat(resourceGroup().name,'-nic')]",
    "publicIPAddressName": "[concat(resourceGroup().name,'-ip')]",
    ...
    "virtualNetworkName": "[concat(resourceGroup().name,'-vnet')]",
    "networkSecurityGroupName": "[concat(resourceGroup().name,'-nsg')]",
    ...
  },
...

Обновляем ресурс “Microsoft.Compute/virtualMachines”vmSize был в переменных, теперь в параметрах – "[parameters('vmSize')]":

...
    {
      "apiVersion": "2016-04-30-preview",
      "type": "Microsoft.Compute/virtualMachines",
      "name": "[variables('vmName')]",
      "location": "[variables('location')]",
      "dependsOn": [
        "[concat('Microsoft.Network/networkInterfaces/', variables('nicName'))]"
      ],
      "properties": {
        "hardwareProfile": {
          "vmSize": "[parameters('vmSize')]"
        },
...

Запускаем тестовый деплой группы:

[simterm]

$ azure group deployment create --resource-group jm-monitoring-dev \
> --name init \
> --template-file jm-monitoring.json \
> --parameters-file jm-monitoring.parameters.json
info:    Executing command group deployment create
+ Initializing template configurations and parameters
+ Creating a deployment
info:    Created template deployment "init"
+ Waiting for deployment to complete
...
info:    group deployment create command OK

[/simterm]

Хорошо, всё готово:

Логинимся, проверяем:

[simterm]

$ ssh -i .ssh/jm-monitoring jmadmin@13.***.***.62
...
Are you sure you want to continue connecting (yes/no)? yes
...
jmadmin@jm-monitoring-dev-vm:~$

[/simterm]

Отлично.

Network security group

Обновим NSG, разрешим доступ по SSH только с одного IP и добавим доступ по 80 и 443, тоже с одного IP.

Возвращаемся к шаблону, обновляем ресурс “Microsoft.Network/networkSecurityGroups”:

...
    {
      "apiVersion": "[variables('apiVersion')]",
      "type": "Microsoft.Network/networkSecurityGroups",
      "name": "[variables('networkSecurityGroupName')]",
      "location": "[variables('location')]",
      "properties": {
        "securityRules": [
          {
            "name": "SSH_Work_Allow",
            "properties": {
              "description": "Locks inbound down to ssh default port 22.",
              "protocol": "Tcp",
              "sourcePortRange": "*",
              "destinationPortRange": "22",
              "sourceAddressPrefix": "194.***.***.45",
              "destinationAddressPrefix": "*",
              "access": "Allow",
              "priority": 120,
              "direction": "Inbound"
            }
          },
          {
            "name": "HTTP_Work_Allow",
            "properties": {
              "description": "Locks inbound down to HTTP default port 80.",
              "protocol": "Tcp",
              "sourcePortRange": "*",
              "destinationPortRange": "80",
              "sourceAddressPrefix": "194.***.***.45",
              "destinationAddressPrefix": "*",
              "access": "Allow",
              "priority": 130,
              "direction": "Inbound"
            }
          },
          {
            "name": "HTTPS_Work_Allow",
            "properties": {
              "description": "Locks inbound down to HTTPS default port 443.",
              "protocol": "Tcp",
              "sourcePortRange": "*",
              "destinationPortRange": "443",
              "sourceAddressPrefix": "194.***.***.45",
              "destinationAddressPrefix": "*",
              "access": "Allow",
              "priority": 140,
              "direction": "Inbound"
            }
          }
        ]
      }
    },
...

Сохраняем, повторяем деплой (не уверен, что ARM разрешит обновить так правила в NSG):

[simterm]

$ azure group deployment create --resource-group jm-monitoring-dev --name NSG_Update --template-file jm-monitoring.json --parameters-file jm-monitoring.parameters.json

[/simterm]

Гуд – всё обновилось.

Jenkins

Для Jenkins используем Jenkins Pipeline плагин и Groovy скрипты.

Основной скрипт jm-monitoring-dev-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-monitoring/${templateFile} -e jm-monitoring/${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}"
            sh "azure group deployment create -n ${tag} -g ${resGroup} -f jm-monitoring/${templateFile} -e jm-monitoring/${paramFile}"
        }
    }
}

def verify(msg='1') {

    stage 'Verify'
    input id: 'Deploy', message: "${msg}", ok: 'Deploy!'

}
return this

(не забываем return this)

И скрипт провижена самого окружения:

#!/usr/bin/env groovy

node {

    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-monitoring-provision.groovy'

    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}")
}

Параметры в пайплайне Jenkins-а выглядят так:

Собственно – по Jenkins-у тут всё.

Запускаем, повторяем деплой, убеждаемся что всё работает:

Готово.