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:
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.
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
Gitlab CI ejecuta la canalización. Vincula todos los demás componentes.
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
Obtuvimos la base que nos permite implementar un nuevo soporte ejecutando pipline en Gitlab CI.
Reducción de los costos de infraestructura de nuestra empresa.
Los desarrolladores no están inactivos y pueden crear y eliminar stands cuando lo necesiten.
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.
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.