Stand de desarrollo sin colas ni tiempo de inactividad

El propósito del artículo es mostrar uno de los posibles enfoques para organizar la implementación flexible de bancos de desarrollo / prueba. Muestre las ventajas que nos brinda el enfoque IaC en combinación con herramientas modernas.






Fondo

Hay varios soportes para desarrolladores: desarrolladores, pruebas, producción. Las nuevas versiones de los componentes del producto aparecen varias veces al día. 





Como resultado, los stands existentes están ocupados, los desarrolladores están inactivos esperando que uno de los stands sea desocupado.





La creación de stands estáticos adicionales resolverá el problema, pero conducirá a una sobreabundancia de ellos durante una disminución en la actividad de los desarrolladores y, como resultado, aumentará los costos de infraestructura de la empresa.





Una tarea

Permitir que los desarrolladores implementen y eliminen soportes por su cuenta, en función de las necesidades actuales.





Apilar

Gitlab CI, Terraform, Bash, cualquier nube privada / pública.





Dificultades técnicas: 





  1. Archivo de estado de Terraform: no tenemos la capacidad de usar una variable en el valor del nombre del archivo de estado. Necesitas inventar algo o usar otro producto.





  2. Subredes: cada nuevo entorno debe crearse en una subred aislada. Necesita controlar subredes libres / ocupadas, una especie de DHCP analógico, pero para subredes.





Algoritmo

  1. Gitlab CI ejecuta la canalización. Vincula todos los demás componentes.





  2. Terraform .





  3. Configuration manager(CM) - .





  4. Bash .





development-infrastructure/
    deploy/
        env1/
            main.tf
            backend.tf
            ansible-vars.json 
            subnets.txt 
        env2/
        ...
    cm/
        ...
    modules/
        azure/
            main.tf
            variables.tf
    scripts/
        env.sh
        subnets.txt 
    .gitlab-ci.yml
      
      



  • deploy - - terraform CM, .





  • cm - , Ansible .





  • modules - terraform





  • scripts - bash





.gitlab-ci.yml:





stages: 
  - create environment 
  - terraform apply 
  - cm 
  - destroy environment 

.template: 
  variables: 
    ENV: $NAME_ENV 
  when: manual 
  tags: [cloudRunner01] 
  only: 
    refs: 
      - triggers 

Create environment: 
  stage: create environment 
  extends: .template 
  script: 
    - ./scripts/create_env.sh -e $ENV -a create 
  artifacts: 
    paths: 
      - deploy/${ENV}/backend.tf 
      - deploy/${ENV}/main.tf 
      - deploy/${ENV}/vars.json 

Create instances: 
  stage: terraform apply 
  extends: .template 
  script: 
    - cd ./deploy/$ENV 
    - terraform init -input=false 
    - terraform validate 
    - terraform plan -input=false -out=tf_plan_$ENV 
    - terraform apply -input=false tf_plan_$ENV 

Deploy applications: 
  stage: cm 
  extends: .template 
  script: 
    - #          CM 
    - #   ,    $ENV  , 
    - #      .. 
    - #       terraform 

Destroy instances and environment: 
  stage: destroy environment 
  extends: .template 
  script: 
    - cd ./deploy/$ENV 
    - terraform init -input=false 
    - terraform destroy -auto-approve 
    - ./scripts/delete_env.sh -e $ENV -a delete 
      
      



:





  • Create environment - , NAME_ENV, , git .





  • Create instances - ( ) , .





  • Deploy applications - Configuration Manager.





  • Destroy instances and environment - bash , . scripts/subnets.txt.





NAME_ENV, :





Git pipeline.





modules/base/main.tf:





#           
provider "azurerm" { 
  version = "=1.39.0" 
} 


#    ,   Azure.   ,         
resource "azurerm_resource_group" "product_group" { 
  name     = "${var.env_name}" 
  location = "East Europe" 
} 

#   
resource "azurerm_virtual_network" "vnet" { 
  name                = "product-vnet" 
  resource_group_name = azurerm_resource_group.product_group.name 
  location            = azurerm_resource_group.product_group.location 
  address_space       = [var.vnet_address] 
} 

#      bash  
resource "azurerm_subnet" "subnet" { 
  name                 = "product-subnet" 
  resource_group_name  = azurerm_resource_group.product_group.name 
  virtual_network_name = azurerm_virtual_network.vnet.name 
  address_prefix       = var.subnet_address 
} 

#     
resource "azurerm_virtual_machine" "product_vm" { 
  name                  = "main-instance" 
  location              = azurerm_resource_group.product_group.location 
  resource_group_name   = azurerm_resource_group.product_group.name 
  network_interface_ids = [azurerm_network_interface.main_nic.id] 
  … 
} 

#     ... 
      
      



, , , .





, , , .





scripts/env.sh:





#!/bin/bash 

set -eu 

CIDR="24" 
DEPLOY_DIR="./deploy" 
SCRIPT_DIR=$(dirname "$0") 

usage() { 
     echo "Usage: $0 -e [ENV_NAME] -a [create/delete]" 
     echo "  -e: Environment name" 
     echo "  -a: Create or delete" 
     echo "  -h: Help message" 
     echo "Examples:" 
     echo "  $0 -e dev-stand-1 -a create" 
     echo "  $0 -e issue-1533 -a delete" 
} 

while getopts 'he:a:' opt; do 
    case "${opt}" in 
        e) ENV_NAME=$OPTARG ;; 
        a) ACTION=$OPTARG ;; 
        h) usage; exit 0 ;; 
        *) echo "Unknown parameter"; usage; exit 1;; 
    esac 
done 

if [ -z "${ENV_NAME:-}" ] && [ -z "${ACTION:-}" ]; then 
    usage 
    exit 1 
fi 

#       
ENV_NAME="${ENV_NAME,,}" 

git_push() { 
    git add ../"${ENV_NAME}" 
    case ${1:-} in 
        create) 
            git commit -am "${ENV_NAME} environment was created" 
            git push origin HEAD:"$CI_COMMIT_REF_NAME" -o ci.skip 
            echo "Environment ${ENV_NAME} was created.";; 
        delete) 
            git commit -am "${ENV_NAME} environment was deleted" 
            git push origin HEAD:"$CI_COMMIT_REF_NAME" -o ci.skip 
            echo "Environment ${ENV_NAME} was deleted.";; 
    esac 
} 

create_env() { 
    #      
    if [ -d "${DEPLOY_DIR}/${ENV_NAME}" ]; then 
        echo "Environment ${ENV_NAME} exists..." 
        exit 0 
    else 
        mkdir -p ${DEPLOY_DIR}/"${ENV_NAME}" 
    fi 

    #    
    NET=$(sed -e 'a$!d' "${SCRIPT_DIR}"/subnets.txt) 
    sed -i /"$NET"/d "${SCRIPT_DIR}"/subnets.txt 
    echo "$NET" > ${DEPLOY_DIR}/"${ENV_NAME}"/subnets.txt 
    if [ -n "$NET" ] && [ "$NET" != "" ]; then 
        echo "Subnet: $NET" 
        SUBNET="${NET}/${CIDR}" 
    else 
        echo "There are no free subnets..." 
        rm -r "./${DEPLOY_DIR}/${ENV_NAME}" 
        exit 1 
    fi 

    pushd "${DEPLOY_DIR}/${ENV_NAME}" || exit 1 

    #  main.tf terraform        
cat > main.tf << END 
module "base" { 
source = "../../modules/azure" 
env_name = "${ENV_NAME}" 
vnet_address = "${SUBNET}" 
subnet_address = "${SUBNET}" 
} 
END 

    # C backend.tf terraform  ,      state  
cat > backend.tf << END 
terraform { 
    backend "azurerm" { 
        storage_account_name = "terraform-user" 
        container_name = "environments" 
        key = "${ENV_NAME}.tfstate" 
    } 
} 
END 
} 

delete_env() { 
    #       
    if [ -d "${DEPLOY_DIR}/${ENV_NAME}" ]; then 
        NET=$(sed -e '$!d' ./${DEPLOY_DIR}/"${ENV_NAME}"/subnets.txt) 
        echo "Release subnet: ${NET}" 
        echo "$NET" >> ./"${SCRIPT_DIR}"/subnets.txt 
        pushd ./${DEPLOY_DIR}/"${ENV_NAME}" || exit 1 
        popd || exit 1 
        rm -r ./${DEPLOY_DIR}/"${ENV_NAME}" 
    else 
        echo "Environment ${ENV_NAME} does not exist..." 
        exit 1 
    fi 
} 

case "${ACTION}" in 
    create) 
        create_env 
        git_push "${ACTION}" 
        ;; 
    delete) 
        delete_env 

        git_push "${ACTION}" 
        ;; 
    *) 
        usage; exit 1;; 
esac 

      
      



  1. env.sh



    - (\).





  2. :





  3. DEPLOY_DIR



    .





  4. scripts/subnets.txt .





  5. Terraform.





  6. git .





  7. scripts/subnets.txt







scripts/subnets.txt:





172.28.50.0
172.28.51.0
172.28.52.0
...
      
      



. CIDR  scripts/create_env.sh







  1. Obtuvimos la base que nos permite implementar un nuevo soporte ejecutando pipline en Gitlab CI.





  2. Reducción de los costos de infraestructura de nuestra empresa.





  3. Los desarrolladores no están inactivos y pueden crear y eliminar stands cuando lo necesiten.





  4. También obtuvimos la capacidad de crear máquinas virtuales en cualquier nube escribiendo un nuevo módulo de Terraform y modificando ligeramente el script para crear / eliminar entornos.





  5. Podemos jugar con los activadores de Gitlab  y desplegar nuevos soportes de la cartera de equipos de desarrollo mediante la transferencia de versiones de servicios.








All Articles