Automatizando pipeline de atualização de template vSphere com GitLab CI/CD, Packer e Ansible

Numa época em que imutabilidade de infraestrutura é uma característica e técnica sendo bastante adotada, versionar imagens e atualizar é tarefa continua/repetitiva, no caso de VMs torna ainda mais desafiador poder fazer isso de forma automática.

No caso de VM, imagina sempre ter que converter o template manualmente para atualizar o OS ou qualquer outra config como hardening da imagem. Pois é, chato eu diria e infelizmente esse é o cenário que eu tenho no meu ambiente no momento.

E podem dizer mais ok, por exemplo porque não atualiza a VM logo apos o deployment por um script? Eu mantenho todas VMs com mesmo nível ou versão de pacotes daquele mês, efectuando update no momento de deploy criará uma espécie de configuration drift (a VM instanciada terá pacotes mais atualizados que as antigas). E como manter registo de todas mudanças feitas na imagem? Como automatizar esse processo?

Para automatizar o processo irei usar pipeline CI/CD (GitLab) e ferramentas IaC e Configuration Management (Packer e Ansible).

Requirements & Tools

ESXi 7;

vCenter 7;

GitLab 14.1.1;

packer 1.7.8;

ansible core 2.12.0;

gitlab runner 14.4.0 “shell executor”;

vSphere Automation Python SDK/pyVmomi on the runner.

Pipeline

O pipeline terá 3 stages ou estagios e cada stage contendo 1 job.

Validate: validar o code IaC do Packer e sintax do Ansible YAML file.

Build: Packer é executado para criar a nova imagem.

Reorganize: Renomeia o antigo template para TMP-date e novo template para TMP-CentOS8.

Code repo

https://gitlab.com/manuh-L/vsphere_template_update

https://github.com/manuh-L/vsphere_template_update

.gitlab-ci.yml

stages:
  - validate
  - build
  - reorganize

IaC:
  stage: validate
  script:
    - packer --version
    - ansible --version
    - packer validate vsphere.pkr.hcl
    - ansible-playbook default-config.yml --syntax-check
    - ansible-playbook template-rename.yml --syntax-check 
  rules:
    - exists:
        - vsphere.pkr.hcl
        - template-rename.yml

create-template:
  stage: build
  tags:
    - local
  script:
    - packer build vsphere.pkr.hcl
  needs:
    - IaC

reorganize-templates:
  stage: reorganize
  tags:
    - local
  script:
    - ansible-playbook template-rename.yml
  needs:
    - create-template

vsphere.pkr.hcl

# "timestamp" template function replacement
locals { timestamp = regex_replace(timestamp(), "[- TZ:]", "") }

# https://www.packer.io/docs/templates/hcl_templates/blocks/source
source "vsphere-clone" "MGlobal" {
  communicator        = "ssh"
  host                = "192.168.100.100"
  insecure_connection = "true"
  password            = "Password1!"
  template            = "TMP-CentOS8"
  username            = "administrator@vsphere.local"
  vcenter_server      = "vcsa-01.lab.com"
  cluster             = "Tanzu-Cluster"
  vm_name             = "TMP-latest"
  notes               = "Template created on ${local.timestamp}"
  convert_to_template = "true"
  ssh_username        = "root"
  ssh_password        = "Password1"
}

# https://www.packer.io/docs/templates/hcl_templates/blocks/build
build {
  sources = ["source.vsphere-clone.MGlobal"]

#https://www.packer.io/docs/provisioners
  provisioner "shell-local" {
    inline = ["ip a", "hostname"]
  }

  provisioner "ansible" {
    playbook_file = "./default-config.yml"
      extra_arguments = ["-v"]

    }
}

default-config.yml

---

- hosts: all
  become: true
  gather_facts: no
  vars_files:
    - input.yml

  tasks:
    - name: Update OS
      yum:
        name: '*'
        state: latest

    - name: Create groups
      ansible.builtin.group:
        name: "{{ item.name }}"
        state: present
      with_items: "{{ groups_to_be_created }}"
  
    - name: Create local Users
      user:
        name: "{{ item.name }}"
        groups: "{{ item.groups }}"
        shell:  "{{ item.shell }}"
        comment: "{{ item.comment }}"
        password: "{{ 'Password1' | password_hash('sha512') }}"
        update_password: on_create
        append: yes
      with_items: "{{ users_to_be_created }}"

template-rename.yml

---
- name: Rename virtual machine from old name to new name using UUID
  gather_facts: yes
  vars_files:
    - input.yml
  vars:
    vc_user: "{{ lookup('env','VC_USER') }}"
    vc_passwd: "{{ lookup('env','VC_PASSWD') }}"
  hosts: localhost
  tasks:
    - set_fact:
        new_vm_name: "TMP-{{ ansible_date_time.date }}-{{ ansible_date_time.time }}"

    - name: Get Template "{{ template_name }}" uuid
      vmware_guest_facts:
        hostname: "{{ vcenter }}"
        username: "{{ vc_user }}"
        password: "{{ vc_passwd }}"
        validate_certs: False
        datacenter: "{{ datacenter }}"
        folder: "/{{datacenter}}/vm"
        name: "{{ template_name }}"
      register: vm_facts

    - name: Rename "{{ template_name }}" to "{{ new_vm_name }}"
      vmware_guest:
        hostname: "{{ vcenter }}"
        username: "{{ vc_user }}"
        password: "{{ vc_passwd }}"
        validate_certs: False
        datacenter: "{{ datacenter }}"
        cluster: "{{ cluster_name }}"
        uuid: "{{ vm_facts.instance.hw_product_uuid }}"
        name: "{{ new_vm_name }}"
        is_template: yes

##############Set new Template ##################
    - name: Get new Template "{{ vm_name }}" uuid
      vmware_guest_facts:
        hostname: "{{ vcenter }}"
        username: "{{ vc_user }}"
        password: "{{ vc_passwd }}"
        validate_certs: False
        datacenter: "{{ datacenter }}"
        folder: "/{{datacenter}}/vm"
        name: "{{ vm_name }}"
      register: vm_facts

    - name: Rename "{{ vm_name }}" to "{{ template_name }}"
      vmware_guest:
        hostname: "{{ vcenter }}"
        username: "{{ vc_user }}"
        password: "{{ vc_passwd }}"
        validate_certs: False
        datacenter: "{{ datacenter }}"
        cluster: "{{ cluster_name }}"
        uuid: "{{ vm_facts.instance.hw_product_uuid }}"
        name: "{{ template_name }}"
        is_template: yes

input.yml

---

####################################OS #####################################
update: "yes"

groups_to_be_created:
- {name: local}
- {name: admins}

users_to_be_created:
- {name: app-01, groups: local, comment: "local for app-01", shell: /sbin/nologin}
- {name: Dandy, groups: admins, comment: "1 of Admins", shell: /bin/bash}
- {name: master-01, groups: admins, comment: "master", shell: /bin/bash}


####################################VCENTER #####################################
template_name: "TMP-CentOS8"

vm_name: "TMP-latest"
datacenter: "MGlobal"
cluster_name: "Tanzu-Cluster"
vcenter: "vcsa-01.lab.com"

Aqui temos o pipeline depois de executado

Schedule

Para criar o schedule:

abrir o repo no gitlab, ir para CI/CD -> Schedules-> New Schedule

Em seguida preencher dados do cron job e indicar o taget branch

O pipeline vai ser executado no primeiro dia do mês pelas 5 horas

Wrap

Espero que o post ajude e para quem deseja 1 pipeline um pouco mais complexo ou que englobe merge request e continuous deployment, pode dar uma vista de olhos no https://virtualclusterit.net/2021/02/25/gitops-gitlab-packer-ansible-terraform-aws/

Deixe uma Resposta

Preencha os seus detalhes abaixo ou clique num ícone para iniciar sessão:

Logótipo da WordPress.com

Está a comentar usando a sua conta WordPress.com Terminar Sessão /  Alterar )

Imagem do Twitter

Está a comentar usando a sua conta Twitter Terminar Sessão /  Alterar )

Facebook photo

Está a comentar usando a sua conta Facebook Terminar Sessão /  Alterar )

Connecting to %s