Ansible Tower kommer med en stor prislapp, speciellt för ett mindre företag eller en privatperson. AWX å andra sidan är helt fritt, men kräver numera Kubernetes. För den som vill automatisera sina Ansible Playbooks går det dock bra att använda Jenkins som en ersättning för Tower och AWX.

Jenkins

Jenkins är ett CI/CD-verktyg (continuous integration/continuous delivery) som är helt fritt och har öppen källkod. Det är ett verktyg för att automatisera hela cykeln av mjukvaru-utveckling; från kompilering, testning och paketering till publicering. Men i själva verket går det att använda Jenkins till så mycket mer – det är ett allt-i-allo-verktyg. Här kommer vi att använda Jenkins för att exekvera Ansible Playbooks.

Ett Jenkins-jobb går att trigga på en rad olika sätt, exempelvis via ett API, en förändring på filsystemet eller schemalagt på en specifik tidpunkt. När ett jobb har körts kan vi se exakt vad som hände i loggen. Det finns även ett plugin för Ansible till Jenkins. Med detta får vi stöd för bland annat Ansible Vault. Lösenord för Ansible Vault-filer kan vi spara i Jenkins under Credentials. Jag använder själv Jenkins på detta sättet, bland annat för att uppdatera system och förnya Let’s Encrypt-certifikat bakom en failover.

Installationen av Jenkins kommer inte att behandlas i denna artikeln då det finns många sätt att installera det på. Det går att installera Jenkins via operativsystemets pakethanterare, via en .war-fil eller via en Docker-avbild. Det finns också färdiga molnavbilder för Jenkins hos de stora molnleverantörerna. I denna guiden utgår vi från att Jenkins har installerats via operativsystemets pakethanterare på en Debian- eller Ubuntu-dator.

Installera Ansible på servern

För att Jenkins ska kunna använda Ansible behöver vi först installera det på datorn eller servern som kör Jenkins. Här kommer vi att installera Ansible som ett PIP-paket i en viruell Pythonmiljö. Fördelen med detta är att vi får en isolerad Ansiblemiljö som är nyare än den som finns i operativsystemetes pakethanterare. En annan fördel är att vi kan ha flera Ansible-installationer om det skulle behövas.

För att kunna skapa virtuella Pythonmiljöer behöver vi installera paketen python3-pip och python3-venv i Debian och Ubuntu. På Fedora och CentOS räcker det att installera python3-pip; paketet för virtuella miljöer ingår redan i Python-paketet.

Jag väljer att placera allt Ansible-material under katalogen /home/jenkins-stuff/ansible och sätter användaren och gruppen jenkins som ägare.

#> mkdir -p /home/jenkins-stuff/ansible
#> chown -R jenkins:jenkins /home/jenkins-stuff

Därefter byter jag användare till jenkins (denna skapas autoamtiskt vid installationen av Jenkins):

ANNONS FÖR VÅRA EGNA BÖCKER Demonerna på internet

#> su jenkins
$> 

Nu kan vi installera Ansible:

$> cd /home/jenkins-stuff/ansible
$> python3 -m venv env
$> source env/bin/activate
(env) $> pip install wheel
[...]
(env) $> pip install ansible
[...]

Nu kan vi testa så att installationen fungerar:

(env) $> ansible --version
ansible [core 2.12.3]
[...]

Installera och konfigurera Ansible-pluginet i Jenkins

När vi nu har en fungerande Ansible-installation behöver vi installera ett plugin för Ansible i Jenkins.

I Jenkins webb-GUI välj Dashboard → Manage Jenkins. Välj sedan Manage Plugins. Under fliken Available söker vi efter ett plugin som heter Ansible plugin. När vi hittat det väljer vi att installera pluginet. Eventuellt kan du behöva starta om Jenkins efter att du installerat det.

Vi behöver också tala om för Jenkins var Ansible-pluginet kan hitta den Ansible-installation vi gjorde tidigare. Gå till Dashboard → Manage Jenkins igen och därefter Global Tool Configuration. Långt ner på sidan hittar vi Ansible. Klicka på Ansible installations…. Klicka sedan på Add Ansible. Fyll i ett namn för denna Ansible-installationen, exempelvis Ansible eller MinAnsible. Under Path to ansible executables directory skriver vi den kompletta sökvägen till Ansible. I mitt fall blir det /home/jenkins-stuff/ansible/env/bin.

Det är allt vi behöver konfigurera för att själv pluginet ska fungera.

Skapa en playbook, inventarie och tillhörande filer

Nästan steg blir att skapa en playbook på vår Jenkins-server och alla de tillhörande filer som behövs, så som en inventariefil, grupp- och värdinställningar med mera.

Vi kommer att placera alla Ansible-filer, så som playbooks, inventarie-filer, katalogerna group_vars och host_vars med mera under /home/jenkins-stuff/ansible.

I detta exemplet använder vi en enkel playbook som uppdaterar Debian- och RedHat-baserade system. Vi kommer att använda en lösenordsskyddad SSH-nyckel för att logga in på servrarna som ska uppdateras. sudo och root-lösenorden kommer vi att spara krypterat med Ansible Vault i host_vars- och group_vars-katalogerna. Vi börjar med filerna för playbooken.

Filerna för vår playbook

Dessa filerna är för själva playbooken. Vi placerar dem under /home/jenkins-stuff/ansible.

upgrade.yml

Detta är vår playbook, det är den vi kommer att exekvera från Jenkins. Jag använder här serial: 1 för att bara uppdatera en server i taget om något skulle gå snett.

# Playbook for upgrading Debian/Ubuntu systems and reboot it if 
# there were updates applied.

- hosts: ankeborg
  become: true
  serial: 1

  tasks:
    - import_tasks: upgrade-apt.yml
    - import_tasks: upgrade-dnf.yml

  handlers:
    - import_tasks: reboot-linux.yml

upgrade-apt.yml

# Tasks for upgrading Debian-based systems and reboot if 
# updates were applied.
- name: Update Debian-based systems
  when: ansible_os_family == 'Debian'
  apt:
    update_cache: yes
    upgrade: dist
  notify: Reboot machine

upgrade-dnf.yml

# Tasks for upgrading RedHat/CentOS/Fedora systems reboot if 
# updates were applied.
- name: Upgrade RedHat-based systems
  when: ansible_os_family == 'RedHat'
  dnf:
    name:  "*"
    state: latest
    update_cache: yes
    update_only: yes
  notify: Reboot machine

reboot-linux.yml

- name: Reboot machine
  reboot:
    msg: "Reboot initiated by Ansible"
    connect_timeout: 5
    reboot_timeout: 600
    pre_reboot_delay: 0
    post_reboot_delay: 30
    test_command: whoami

Inventariefilen

Vi behöver också en inventariefil så att Ansible vet vilka värdar vi ska uppdatera. Här har jag döpt gruppen till ankeborg och värdarna efter invånarna i Ankeborg. Anpassa filen efter dina egna värdar. Även denna filen ska placeras under /home/jenkins-stuff/ansible.

hosts

[ankeborg]
knatte.nixnet.jke ansible_host=192.168.0.29
fnatte.nixnet.jke ansible_host=192.168.0.24
tjatte.nixnet.jke ansible_host=192.168.0.42
joakim.nixnet.jke ansible_host=192.168.0.47

Grupp- och värdinställningar

Vissa av mina värdar använder su med en riktig root-användare och vissa andra använder sudo för att utföra uppgifter som root. De flesta använder sudo med samma lösenord för alla värdarna, därför konfigurerar jag detta för hela gruppen. Därefter specificerar jag de avvikande värdarna var för sig.

Om du inte redan har katalogerna group_vars och host_vars i /home/jenkins-stuff/ansible behöver du skapa dem först.

group_vars/ankeborg.yml

ansible_user: jake
ansible_become_pass: peak-airspeed
ansible_become_method: sudo

Eftersom vi lagrar lösenordet i filen är det säkrast om vi krypterar den med ansible-vault. Här väljer jag lösenordet popcorn-pants för filen.

(env) $> pwd
/home/jenkins-stuff/ansible
(env) $> ansible-vault encrypt group_vars/ankeborg.yml --ask-vault-password
Vault password:

host_vars/joakim.nixnet.jke.yml

ansible_become_method: su
ansible_become_pass: speech-fargo

Även denna filen krypterar vi ansible-vault med samma lösenord:

(env) $> ansible-vault encrypt host_vars/joakim.nixnet.jke.yml --ask-vault-password
Vault password:

host_vars/knatte.nixnet.jke.yml

ansible_become_method: su
ansible_become_pass: speech-fargo

Vi gör samma sak här – krypterar filen med ansible-vault med samma lösenord:

(env) $> ansible-vault encrypt host_vars/knatte.nixnet.jke.yml --ask-vault-password
Vault password:

Acceptera värdarnas SSH-fingeravtryck

För att undvika att SSH-anslutningarna stannar vid att acceptera värdarnas SSH-fingeravtryck är det enklast om vi manuellt ansluter till dem en gång. Vi behöver inte logga in nu, utan bara acceptera deras fingeravtryck.

(env) $> ssh knatte.nixnet.jke
The authenticity of host 'knatte (192.168.0.29)' can't be established.
ECDSA key fingerprint is SHA256:yYw0JoqD9u07ikSGa14tIl2yXVEf77jG1caooBnnK88.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added 'knatte' (ECDSA) to the list of known hosts.
^D

Upprepa proceduren för samtliga värdar.

Konfigurera Jenkins att köra vår playbook

Nu behöver vi konfigurera allting i Jenkins, exempelvis SSH-nyckeln till värdarna, lösenordet för ansible-vault, sökvägen till vår playbook och några andra saker.

SSH-nyckeln

Det första vi behöver göra är att lägga in SSH-nyckeln i Jenkins som används för att logga in på värdarna. Här förutsätter jag att du redan har en nyckel och har kopierat den publika delen till värdarna.

I mitt exempel använder jag det globala credentials-utrymmet eftersom jag är ensam användare i Jenkins. Har du en mer avancerad uppsättning av Jenkins placerar du den där det passar bäst.

I Jenkins GUI välj Dashboard → Manage Jenkins → Manage Credentials. Under Store väljer du Jenkins och sedan Global credentials (unrestricted). Här kan vi lägga till SSH-nyckeln genom att klicka på Add Credentials.

Under Kind väljer vi SSH Username with private key. I fältet Username skriver vi in användarnamnet som används för att logga in på värdarna. I mitt fall är detta jake. I ID-fältet skriver vi något som identifierar denna nyckeln, exempelvis användarnamnet. Under Private Key välj Enter directly, klicka på Add och klistra in nyckeln i textfältet. Om nyckeln är skyddad av ett lösenord skriver du in det i fältet Passphrase. Spara sedan genom att klicka på Save.

Lösenordet för Ansible Vault

Vi behöver skapa ytterligare en credentials, nämligen lösenordet för de filer som vi krypterade med ansible-vault.

Fortfarande under Dashboard → Manage Jenkins → Manage Credentials → Store → Jenkins → Global credentials (unrestricted) väljer vi Add Credentials igen. Denna gången väljer vi typen Secret text. I fältet Secret skriver vi in lösenordet för valven vi skapde. I mitt fall är detta popcorn-pants. Under ID väljer jag det beskrivande namnet ankeborgs-valv.

Skapa ett Jenkins-jobb för vår playbook

Nu är det dags att skapa själva jobbet som kommer att köra vår playbook.

Gå till Dashboard och välj New Item. Skriv ett namn för jobbet, exempelvis Uppdatera Ankeborg. Välj sedan Freestyle project och klicka på OK.

Nu kan vi välj hur jobbet ska triggas. Under Build Triggers väljer jag Build periodically för att uppdatera servrarna regelbundet. I textrutan Schedule skriver jag in 15 04 28 * *. Detta kommer att köra jobbet klockan 04:15 den 28:e varje månad.

Under Build ska vi nu lägga till två byggsteg. Det första blir att sätta några miljövariabler för Ansible, och det andra blir blir att köra vår Ansible Playbook. Vi börjar därför med att klicka på Add build-step och väljer Inject Environment variables. I textrutan Properties Content skriver vi in följande:

PYTHONPATH=/home/jenkins-stuff/ansible/env/lib
ANSIBLE_HOME=/home/jenkins-stuff/ansible

Klicka sedan på Add build-step igen. Denna gången väljer vi Invoke Ansible Playbook. I rutan Playbook path anger vi sökvägen till playbooken, i vårt fall blir det /home/jenkins-stuff/ansible/upgrade.yml.

Under Inventory väljer vi File or host list och anger sökvägen /home/jenkins-stuff/ansible/hosts.

Under Credentials väljer vi den användare och SSH-nyckel som ska användas för att logga in på värdarna. I mitt fall är detta jake.

Under Vault Credentials väljer vi lösenordet för valven vi skapade. I mitt fall är detta ankeborgs-valv.

Spara allting genom att klicka på Save.

Testköra

Nu kan vi testköra jobbet genom att gå till Dashboard och klicka på jobbet Uppdatera Ankeborg. Här kan vi starta jobbet manuellt genom att klicka på Build Now.

Under Build History dyker jobbet upp efter några sekunder. Det går att följa vad som händer i jobbet genom att klicka på byggjobbet, exempelvis #1, och sedan Console Output. När jobbet har avslutats utan några fel ändras statusen till en grön bock. Annars avslutas det med ett rött kryss.

Avslutning

Detta är bara ett exempel på hur det går att köra Ansible Playbooks via Jenkins. Det går att göra betydligt mer avancerade uppsättningar. Exempelvis går det att trigga bygget från ett API, hämta playbook-filerna genom Git, eller exekvera playbooks från andra Jenkins-noder och mycket mer.


Nyhetsbrev
Nyhetsuppdateringar från tidningen direkt till din inkorg, helt kostnadsfritt. Avsluta när du vill.

Kommentarer

Kommentarsfältet är modererat. Det innebär att alla kommentarer granskas av ansvarig utgivare före publicering.

Du väljer själv om du vill ange ditt riktiga namn, en pseudonym eller vara helt anonym. Ingen registrering behövs.