Gitlab 和 Terraform都是很流行的DevOps工具,下面简单的记录一下如何使用他们在Azure上部署一个服务。在这个例子里面,Gitlab是作为我们的Git Repo,同时也是作为 CICD Pipeline来使用的。
准备工作
首先是准备工作,我们需要配置一个Service Principal可以允许从第三方的程序访问Azure,我们还需要配置一个Storage Account来保存我们的Terraform的状态文件。
1. App 注册
如果配置过任何的Azure SSO的项目,或者利用API访问过Azure,会知道这个步骤对于所有的第三方程序来访问Azure都是必须的。
登录到 Azure Admin Portal - > App Registration - > New Registration  
创建之后,去certificate& secrets里面创建一个client secret
记录下来,稍后会用。
然后来到overview的界面,记录下application id和tenant id
2. 分配IAM 权限
这一步需要给app授权。一般授权可以在IAM里面做,如果要精细规划,可以考虑在graph api里面配置。
Home -> Subscription ->IAM -> Add Role assignment
给我们刚刚注册的app 分配 contributor的权限
3. 创建一个storage account
Home – Storage Account - Create

然后去 Data Storage -> Containers -> Create a new blob container

Gitlab的配置
1. 首先需要配置变量
把下面的变量信息准备好,我们才能从Gitlab访问Azure的资源。另外注意如果要在terraform直接调用变量,定义需要使用下面这种形式 TF_VAR_变量名
Settings -> CI/CD -> Variables
•   ARM_ACCESS_KEY: Storage account的 key
•   ARM_CLIENT_ID: App 的 id
•   ARM_CLIENT_SECRET: 我们在app里面创建的 client secret
•   ARM_SUBSCRIPTION_ID: 账号subscrption的 id
•   ARM_TENANT_ID: Azure AD tenant的ID

2. CICD 文件
gitlab的CICD配置文件,他会自动找一个可用的runner来启动。
.gitlab-ci.yaml
image:
  name: hashicorp/terraform:latest
  entrypoint:
    - '/usr/bin/env'
    - 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin'
before_script:
  - rm -rf .terraform
  - terraform --version
  - terraform init
stages:
  - validate
  - plan
  - apply
validate:
  stage: validate
  script:
    - terraform validate
plan:
  stage: plan
  script:
    - terraform plan -out "planfile"
  dependencies:
    - validate
  artifacts:
    paths:
      - ./planfile
apply:
  stage: apply
  script:
    - terraform apply -input=false "planfile"
  dependencies:
    - plan3. Terraform的文件
azure-user-data.sh 这个文件是我的user data的脚本,虚拟机启动的时候,自动执行,安装docker,docker-compose,并注册一个gitlab的 runner
注意我这里传入的${runner_token}的变量,来自于main.tf里面的template_file, 而 template_file里面的变量 var.runner_token又是在 variable.tf里面定义的,然后默认值是从 gitlab CI的 Variable里面赋值的
#!/bin/bash
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get install -y docker-ce
sudo curl -L https://github.com/docker/compose/releases/download/1.18.0/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
cd /root
sudo mkdir config
sudo cd config
cat << EOT >> docker-compose.yml
version: '3.3'
services:
  gitlab-runner:
    image: 'gitlab/gitlab-runner:latest'
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./config:/etc/gitlab-runner
    restart: unless-stopped
EOT
docker-compose up -d
docker-compose exec gitlab-runner gitlab-runner register --non-interactive --url https://gitlab.com/ --registration-token ${runner_token} --executor docker --docker-image python3.9 --docker-image dockermain.tf
terraform {
  required_version = ">=0.12"
  required_providers {
    azurerm = {
      source  = "hashicorp/azurerm"
      version = "~>2.0"
    }
  }
}
provider "azurerm" {
  features {}
}
resource "azurerm_resource_group" "vmss" {
  name     = var.resource_group_name
  location = var.location
  tags     = var.tags
}
resource "random_string" "fqdn" {
  length  = 6
  special = false
  upper   = false
  numeric = false
}
resource "azurerm_virtual_network" "vmss" {
  name                = "vmss-vnet"
  address_space       = ["10.0.0.0/16"]
  location            = var.location
  resource_group_name = azurerm_resource_group.vmss.name
  tags                = var.tags
}
resource "azurerm_subnet" "vmss" {
  name                 = "vmss-subnet"
  resource_group_name  = azurerm_resource_group.vmss.name
  virtual_network_name = azurerm_virtual_network.vmss.name
  address_prefixes     = ["10.0.2.0/24"]
}
resource "azurerm_virtual_machine_scale_set" "vmss" {
  name                = "vmscaleset"
  location            = var.location
  resource_group_name = azurerm_resource_group.vmss.name
  upgrade_policy_mode = "Manual"
  sku {
    name     = "Standard_DS1_v2"
    tier     = "Standard"
    capacity = 2
  }
  storage_profile_image_reference {
    publisher = "Canonical"
    offer     = "0001-com-ubuntu-server-focal"
    sku       = "20_04-lts-gen2"
    version   = "latest"
  }
  storage_profile_os_disk {
    name              = ""
    caching           = "ReadWrite"
    create_option     = "FromImage"
    managed_disk_type = "Standard_LRS"
  }
  storage_profile_data_disk {
    lun           = 0
    caching       = "ReadWrite"
    create_option = "Empty"
    disk_size_gb  = 10
  }
  os_profile {
    computer_name_prefix = "vmlab"
    admin_username       = var.admin_user
    admin_password       = var.admin_password
    custom_data          = data.template_file.linux-vm-cloud-init.rendered
  }
  os_profile_linux_config {
    disable_password_authentication = false
  }
  network_profile {
    name    = "terraformnetworkprofile"
    primary = true
    ip_configuration {
      name      = "IPConfiguration"
      primary   = true
      subnet_id = azurerm_subnet.vmss.id
    }
  }
  tags = var.tags
}
# Data template Bash bootstrapping file
data "template_file" "linux-vm-cloud-init" {
  template = file("azure-user-data.sh")
  vars = {
    runner_token = "${var.runner_token}"
  }
}
provider.tf
# State Backend
terraform {
  backend "azurerm" {
    resource_group_name   = "rg1"
    storage_account_name  = "gitlabstoragetest"
    container_name        = "terraform-state-file"
    key                   = "test.terraform.tfstate"
  }
}variable.tf
variable "resource_group_name" {
   description = "Name of the resource group in which the resources will be created"
   default     = "myResourceGroup"
}
variable "location" {
   default = "eastasia"
   description = "Location where resources will be created"
}
variable "tags" {
   description = "Map of the tags to use for the resources that are deployed"
   type        = map(string)
   default = {
      environment = "codelab"
   }
}
variable "application_port" {
   description = "Port that you want to expose to the external load balancer"
   default     = 80
}
variable "admin_user" {
   description = "User name to use as the admin account on the VMs that will be part of the VM scale set"
}
variable "admin_password" {
   description = "Default password for admin account"
}
variable "runner_token" {
  description = "project gitlab runner token "
}
效果图












