Tujuan artikel ini adalah untuk menunjukkan salah satu pendekatan yang mungkin untuk mengatur penyebaran fleksibel dari bangku dev / test. Tunjukkan keuntungan apa yang diberikan pendekatan IaC dalam kombinasi dengan alat modern.
Latar Belakang
Ada beberapa singkatan untuk developer - devs, test, production. Versi baru komponen produk muncul beberapa kali sehari.
Alhasil, stand-stand yang ada sudah terisi, pengembang menganggur menunggu salah satu stand dikosongkan.
Pembuatan stan statis tambahan akan menyelesaikan masalah, tetapi akan menyebabkan kelebihan stan selama penurunan aktivitas pengembang dan, akibatnya, akan meningkatkan biaya infrastruktur perusahaan.
Sebuah tugas
Untuk memungkinkan pengembang menyebarkan dan menghapus stand mereka sendiri, berdasarkan kebutuhan saat ini.
Tumpukan
Gitlab CI, Terraform, Bash, semua awan pribadi / publik.
Kesulitan teknis:
File status terraform - di luar kotak kami tidak memiliki kemampuan untuk menggunakan variabel dalam nilai nama file negara. Anda perlu menemukan sesuatu atau menggunakan produk lain.
Subnet - setiap lingkungan baru harus dibuat di subnet yang terisolasi. Anda perlu mengontrol subnet bebas / sibuk, semacam analog DHCP, tetapi untuk subnet.
Algoritma
Gitlab CI menjalankan pipeline. Menyatukan semua komponen lainnya.
Terraform .
Configuration manager(CM) - .
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
env.sh
- (\).
:
DEPLOY_DIR
.
scripts/subnets.txt .
Terraform.
git .
-
scripts/subnets.txt
scripts/subnets.txt:
172.28.50.0
172.28.51.0
172.28.52.0
...
. CIDR scripts/create_env.sh
Kami mendapatkan fondasi yang memungkinkan kami menerapkan stand baru dengan menjalankan pipline di Gitlab CI.
Mengurangi biaya infrastruktur perusahaan kami.
Pengembang tidak menganggur dan dapat membuat serta menghapus stand saat mereka membutuhkannya.
Kami juga mendapat kemampuan untuk membuat mesin virtual di cloud apa pun dengan menulis modul Terraform baru dan sedikit memodifikasi skrip untuk membuat / menghapus lingkungan
Kita dapat bermain dengan pemicu Gitlab dan menerapkan stand baru dari pipeline tim pengembangan dengan mentransfer versi layanan.