開発はキューやダウンタイムなしで成り立ちます

この記事の目的は、開発/テストベンチの柔軟な展開を整理するための可能なアプローチの1つを示すことです。IaCアプローチが最新のツールと組み合わせてどのような利点を提供するかを示してください。






バックグラウンド

開発者には、開発者、テスト、本番など、いくつかのスタンドがあります。製品コンポーネントの新しいバージョンが1日に数回表示されます。 





その結果、既存のスタンドが占有され、開発者はスタンドの1つが空になるのを待ってアイドル状態になります。





追加の静的スタンドを作成することで問題は解決しますが、開発者の活動が減少する間にそれらが過剰になり、その結果、会社のインフラストラクチャコストが増加します。





仕事

開発者が現在のニーズに基づいて、スタンドを独自に展開および削除できるようにするため。





スタック

Gitlab CI、Terraform、Bash、プライベート/パブリッククラウド。





技術的な問題: 





  1. Terraform状態ファイル-そのままでは、状態ファイル名の値に変数を使用することはできません。あなたは何かを発明するか、別の製品を使用する必要があります。





  2. サブネット-新しい環境はそれぞれ、分離されたサブネット上に作成する必要があります。DHCPアナログの一種である、空き/ビジーサブネットを制御する必要がありますが、サブネット用です。





アルゴリズム

  1. GitlabCIはパイプラインを実行します。他のすべてのコンポーネントを結び付けます。





  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. Gitlab CIでpiplineを実行することで、新しいスタンドをデプロイできる基盤を手に入れました。





  2. 当社のインフラストラクチャコストを削減しました。





  3. 開発者はアイドル状態ではなく、必要なときにスタンドを作成および削除できます。





  4. また、新しいTerraformモジュールを作成し、環境を作成/削除するためのスクリプトをわずかに変更することで、任意のクラウドで仮想マシンを作成できるようになりました。





  5.  サービスのバージョンを転送することで、Gitlabトリガー遊んだり、開発チームのパイプラインから新しいスタンドをデプロイしたりできます。








All Articles