Puppet Forge on Puppetlabsin tarjoama repo Puppet-moduuleille. Forge-moduuleja voi asentaa "puppet module install"-komennolla, mutta niihin voi viitata myös r10k:n tai librarian-puppetin Puppetfilessä. Nykyisin Puppet Forge on ensimmäinen paikka, josta katson, onko joku muu jo ratkaissut käsilläni olevan ongelman Puppetilla - pyörää kun on toisinaan turha keksiä uudelleen.

Olen itse käyttänyt vuosia Git Submoduleja, koska niiden avulla Puppet-moduuleihin ulkopuolelta tulevat muutokset on helppo tarkistaa Gitin lokeista ja diffeistä. Niiden käyttö menee kuitenkin kankeaksi kun moduulien määrä kasvaa suureksi: sekä päärepo että submodulet vaativat jatkuvaa paapomista. Tuloksena on turhanpäiväisiä, samankaltaisina toistuvia commit-viestejä kuten "Update Git submodule pointers", joilla ei ole mitään muuta käytännön merkitystä kuin Gitin pitäminen tyytyväisenä.

Moduulien säilömisestä Forgessa on eräs etu käytettäessä Puppetfileä: librarian-puppet osaa ladata moduulien metadata.json:issa määritetyt riippuvuudet automaattisesti, mutta vain jos riippuvuudet löytyvät Forgesta. Tai kääntäen: Gitissä olevia riippuvuuksia ei ladata automaattisesti, vaan ne pitää määritellä yrityksen ja erehdyksen kautta käsin. Tämä paisuttaa Puppetfileä huomattavasti ja tekee sen ylläpidosta työlästä. Kyseessä on Puppetin oma version riippuvuushelvetistä. Valitettavasti r10k ei osaa ladata riippuvuuksia automaattisesti - mitään selkeää syytä tähän ei ole.

Moduulien saaminen Puppet Forgeen ei ole kovin vaikeaa. Ensin luodaan käyttäjätili Puppet Forgeen. Käyttäjätunnuksen ja moduulin metadata.json:in "name"-kohdan tulisi täsmätä. Esimerkiksi jos metadata.json sisältää

"name": "puppetfinland-os",

niin Puppet Forge-käyttäjätunnuksen tulisi olla "puppetfinland". Ilmeisesti tämä ei ole ehdoton vaatimus, mutta sen noudattaminen lienee selkeyden takia järkevää.

Forgeen voi viedä moduuleja käsin, mutta se on erittäin työlästä kun moduuleja on paljon. Seuraava askel onkin puppet-blacksmithin asennus. Tässä vaiheessa haasteeksi tulee se, mihin Gem-polkuun puppet-blacksmith asennetaan. Itse suosin asentamista /opt/puppetlabs/puppet -hakemiston alle:

$ /opt/puppetlabs/puppet/bin/gem install puppet-blacksmith

Sitten luodaan Blacksmithin konfiguraatiotiedosto, ~/.puppetforge.yml, johon laitetaan Puppet Forgen käyttäjätunnus ja salasana tähän tapaan:

---
 url: https://forgeapi.puppetlabs.com
 username: myuser
 password: mypassword
 

Tämän jälkeen voidaanin siirtyä jo itse moduuliin muokkaamiseen paremmin Forgen kanssa yhteensopivaksi. Tämä kannattaa tehdä Puppetin moduulikehitystyökaluilla eli Puppet PDK:lla, joka asennetaan näitä ohjeita seuraamalla. Vaikka virallista Debian GNU/Linux -tukea ei olekaan, PDK:n Ubuntu-versio asentuu myös Debianiin. Olemassa olevaan moduuliin saadaan PDK:lla lisättyä tukku "best practices"-läskiä helposti:

$ cd <moduulin-hakemisto>
 $ pdk convert

PDK:n muunnoskomento lisää moduuliin mm. rspec-testejä sekä Travis CI- ja Appveyor -integraatioita

Seuraavaksi PDK:n luomaan Rakefileen lisätään riippuvuus puppet-blacksmithin:

require 'puppet_blacksmith/rake_tasks'

Tämän jälkeen rakella (Rubyn "make") voidaan ajaa puppet-blacksmithin komentoja, jotka on hyvin esitelty sen virallisesta dokumentaatiosta.

Mikäli moduuliin tehdyt muutokset halutaan testata automaattisesti Travis CI:llä (mikä on ehdottoman järkevää), pitää Gemfileen lisäksi tehdä pieni lisäys:

group :development do
 --- snip ---
 gem "puppet-blacksmith"
 end

Ilman tätä muutosta Travis-ajot epäonnistuvat ("Build failed"), koska Rakefileen yllä määriteltyä puppet-blacksmith riippuvuutta ei löydy:

LoadError: cannot load such file -- puppet_blacksmith/rake_tasks

Traviksen kanssa kannattaa myös varmistua siitä, että metadata.jsonissa määritelty ohjelmistolisenssin nimi vastaa sen virallista tunnistetta ("Identifier"), jonka voi etsiä esimerkiksi täältä. Muutoin Traviksen ajama metadata_lint -testi epäonnistuu:

(WARN) license: License identifier BSD license is not in the SPDX list: http://spdx.org/licenses/
 Warnings found in metadata.json
 
 The command "bundle exec rake $CHECK" exited with 1.

Tässä vaiheessa kaikki Git-repossa olevat muutokset kannattaa kommitoida.

Nyt puppet-blacksmithin pitäisi olla jo toimintakykyinen. On kuitenkin järkevää ensin tarkistaa, että Traviksen ajamat testit menevät läpi pushaamalla muutokset Gitiin:

$ git push

Jos testit näyttävät vihreää, voidaan moduulista julkaista uusi versio. Teoriassa komennon "rake module:release" pitäisi hoitaa koko julkaisuprosessi, mutta siinä olevan bugin takia prosessi on valitettavasti vähemmän suoraviivainen. Ensin muutetaan moduulin versio metadata.jsonissa ja kommitoidaan muutokset:

$ rake module:bump_commit:patch
 Bumping version from 0.1.5 to 0.1.6

Sitten luodaan metadata.jsonin versiota vastaava Git tag ja pushataan se:

$ rake module:tag
 $ git push --tags

Lopuksi julkaistaan moduuli Puppet Forgessa:

$ rake module:clean
 $ rake module:push
 Uploading to Puppet Forge puppetfinland/systemd

Komento "module:clean" varmistaa, että Forgeen ladataan uusin versio.

Mikäli muutosten tekeminen Puppet-koodiin ja Hieraan suoraan puppetmasterilla tuntuu liian kevyeltä ja helpolta, voi Puppet-työnkulusta tehdä asteittain vaikeammaksi oman sietokyvyn mukaan lisäämällä palettiin lisää komponentteja. Esimerkiksi ns. control repon ja r10k:n käyttö Puppetin environmentien ylläpitoon lisää heti useita vaiheita aiemmin yksinkertaiseen työnkulkuun. Toki tällä saavutetaan myös ihan oikeita etuja, joista lisää tuonnempana.

Control repo - tästä lähtien osin suomennettuna "hallintarepo" - on käytännössä Git repository, jonka jokaisesta haarasta (branch) luodaan (tai ainakin voidaan luoda) dynaamisesti Puppet masterille vastaava Puppet-environment. Hallintarepon oletushaaran nimi, "production", on tästä syystä sama, kuin Puppet agenttien oletuksena käyttämä environment. Hallintarepon haaroissa on pitkälti sama sisältö kuin tavanomaisessa /etc/puppetlabs/code/environments/production -kansiossakin olisi:

$ ls control-repo
 data
 environment.conf
 hiera.yaml
 Puppetfile
 README.md
 site
 site.pp

Puppetfileä käsiteltiin jo aiemmassa artikkelista, jossa luotiin Puppet-koodiin pohjautuvia Kafo-installereja. Tiivistetysti Puppetfile sisältää viittaukset niihin Puppet-moduuleihin, jotka environmentin moduulikansiossa halutaan olevan:

# Forgen osoite
 forge "https://forgeapi.puppetlabs.com"
 
 # Moduuli Puppet Forgesta
 mod 'puppetlabs/stdlib'
 
 # Moduuli Gitistä
 mod 'puppet-mymodule',
 :git => 'git@gitlab.com:myorg/puppet-mymodule.git'

Tiedosto site.pp on näinä aikoina usein äärimmäisen pelkistetty, koska sitä tulee usein käytettyä lähinnä luokkien lataamiseen Hieran "classes"-listasta:

# Oletusarvo [] estää Puppet-ajojen epäonnistumisen siinä
 # tapauksessa, että jollakin koneella ei ole classes-listassa yhtään
 # luokkaa.
 lookup('classes', {merge => unique, default_value => []}).include

Tiedosto environment.conf on sekin varsin pelkistetty:

# Environmentin manifesti (ks. yllä)
 manifest = site.pp
 
 # Puppet-moduulien latauspolku. Alla käytetään globaaleja,
 # environmentien ulkopuolisia moduuleja kansiosta
 # /etc/puppetlabs/code/modules, sitten etsitään environmentin
 # moduulikansiosta, sitten environmentin site-kansiosta.
 # Site-kansion moduulien tiedostojen oletetaan olevan suoraan
 # hallintarepossa, ts. niihin ei viitata Puppetfilessä.
 modulepath = $basemodulepath:modules:site
 
 # Varmistetaan, että noodit käyttävät aina tuoreinta koodia. Suurella
 # noodimäärällä tämän parametrin arvoa voi joutua miettimään
 # uudelleen. 
 environment_timeout = 0

Tiedosto hiera.yaml on environment-kohtainen Hieran-asetustiedosto. Aiemmista versioista poiketen versio 5 hiera.yaml:ista sallii kolmitasoisen hierarkian, johon kuuluu globaali taso, environment-kohtainen taso ja moduulikohtainen taso. Viimeksi mainittu vaikuttaa suunnitellun vaihtoehdoksi hieman ikääntyneelle params.pp -patternille. Näillä seikoilla ei ole hallintarepon kannalta muuta merkitystä kuin se, että hallintarepon jokaisessa haarassa on hiera.yaml-tiedosto - esimerkiksi tällainen:

---
 version: 5
 defaults:
 datadir: data
 data_hash: yaml_data
 
 hierarchy:
 - name: "Per-node data"
 path: "nodes/%{trusted.certname}.yaml"
 - name: "Common data"
 path: "common.yaml"

Kansio site sisältää hallintarepossa suoraan ylläpidettävät Puppet-moduulit, kuten yllä site.pp:n kommenteissa mainittiinkin. Näin säästytään Puppetfilen ja erillisten moduulirepojen ylläpidolta.

Kansio data sisältää Hieran hierarkian, eli yllä olevaa hiera.yml-tiedostoa käytettäessä tiedoston common.yaml sekä kansion nodes, jossa on noodeille niiden omat yaml-tiedostot.

Artikkelisarjan muut osat:

Edellisessä blogipostauksessa esiteltiin tyypillinen hallintarepon rakenne. Tässä artikkelissa käsitellään r10k:ta, jolla hallintarepon haaroista muodostetaan Puppet-palvelimelle vastaavat Puppet-environmentit. R10k:n asennus Puppet 4:een tai uudempaan on suoraviivaista Puppet-pakettien mukana tulevalla gem-ohjelmalla:

$ /opt/puppetlabs/puppet/bin/gem install r10k
 

Tämän jälkeen luodaan r10k:lle asetustiedosto /etc/puppetlabs/r10k/r10k.yaml:

---
 :cachedir: /opt/puppetlabs/puppet/cache/r10k
 :sources:
 control:
 basedir: /etc/puppetlabs/code/environments
 prefix: false
 remote: git@gitlab.com:myorg/control-repo.git

Yllä oletetaan, että hallintarepo ("control repo") on jo olemassa GitLabissa tai vastaavassa, ja että SSH-avaimet on luotu ja lisätty hallintarepoon sekä mahdollisiin yksityisiin Puppet-moduulirepoihin, joihin Puppetfilessä viitataan.

Mikäli hallintarepo ei ole julkinen - mikä on erittäin todennäköistä - pitää r10k:lle osoittaa, mitä SSH-avainta sen tulisi käyttää hallintarepon itsensä ja yksityisten Puppet-moduulien lataamiseen. Tämä onnistuu muokkaamalla pääkäyttäjän SSH-asetuksia eli tiedostoa /root/.ssh/config:

Host gitlab.com
 StrictHostKeyChecking no
 RSAAuthentication yes
 IdentityFile /etc/puppetlabs/r10k/ssh/id_rsa
 User git

SSH-avaimen yksityisen osan (esim. id_rsa) pitää toki olla yllä mainitussa polussa (/etc/puppetlabs/r10k/ssh/id_rsa) ja sen tiedosto-oikeuksien pitää olla riittävän rajatut (esim. 0400).

Lopuksi - jos kaikki meni hyvin, voidaan hallintarepon kaikki haarat muuntaa Puppet environmenteiksi r10k:lla:

$ r10k deploy environment -v --puppetfile
 

Parametri --puppetfile (alias -p) aiheuttaa sen, että r10k poistaa ensin environmentit ja luo ne sen jälkeen uudestaan tyhjästä. Sivuvaikutuksena tässä on se, että kaikki Puppetfilessä mainitut Puppet-moduulit päivitetään uusimpiin versioihin, ellei niiden versiota ole erikseen määritetty.

Ajan säästämiseksi on myös mahdollista päivittää vain yksittäinen environment:

$ r10k deploy environment -v my_feature

Yllä ei myöskään käytetä parametria --puppetfile, joten olemassa olevat environmentit ainoastaan päivitetään sen sijaan, että ne jyrättäisiin sileäksi. Tarkempia ohjeita löytyy r10k:n virallisesta dokumentaatiosta.

Artikkelisarjan muut osat:

Edellisissä artikkeleissa käsiteltiin hallintarepoa ja r10k:ta. Vaikka mikään ei estäkään hallintarepon pitämistä Puppetmaster-koneella ja r10k.yaml:n osoittamista siihen, menetettäisiin silloin monta työvaihetta eikä ratkaisu siten sopisi tämän artikkelisarjan teemaan. Viimeinen puuttuva komponentti työnkulun läskiyttämisessä onkin GitLab tai vastaava, kuten GitHub tai Bitbucket. Näistä ensimmäinen soveltuu parhaiten tilanteeseen, jossa halutaan luoda paljon yksityisiä Git-repoja, joista ei haluta maksaa, eikä GitLabin edistyneemmille ominaisuuksille ole käyttöä. GitHubissa jokainen yksityinen repository maksaa, joten se on kallis ratkaisu yksityisten puppet-moduulien säilömiseen. Bitbucket sallii GitLabin tavoin rajoittamattomasti yksityisiä repositoryjä, mutta rajoittaa käyttäjien määrän tällä hetkellä viiteen, eikä tarjoa merkittävästi etuja, paitsi ehkä Trello-integraation, jos Kanbania haluaa käytellä. GitLabilla on puolellaan vielä se etu, että se on avointa lähdekoodia, joten sen voi asentaa myös omaan sisäverkkoon ja käyttää sieltä.

Työnkulku on GitLabin ja sen kaltaisten järjestelmien kanssa jokseenkin sama. Lisäksi valmisteluvaihe on jokseenkin identtinen:

Edellisessä artikkelissa kerrottiin, miten r10k konfiguroidaan käyttämään GitLabin kanssa oikeaa SSH-avainta eli "deploy keytä". Kun kaikki yllä mainitut valmistelut on tehty, voidaankin siirtyä varsinaiseen työnkulkuun, joka menee tyypillisesti näin.

Ensin käyttäjä päivättä oman hallintarepo-klooninsa production-haaran:

$ git branch
 * production
 $ git pull

luo paikalliseen hallintarepon klooniinsa uuden haaran (ns. "feature branch"):

$ git branch feature_a
 $ git checkout feature_a

Seuraavaksi hän tekee tarvittavat muokkaukset ja commitoi ("commit") muutokset:

$ git add <file>
 $ git commit -m "Add feature a"

Sitten hän julkaisee muutokset GitLabissa:

$ git push origin feature_a

Muutokset siis löytyvät nyt GitLab-reposityn haarasta feature_a.

Seuraavaksi käyttäjä tekee "Merge requestin" GitLabin käyttöliittymällä. Yleensä GitLab osaa valita oikean lähdehaaran automaattisesti, mutta jos näin ei ole, voi sen valita myös käsin. Merge requestin kohteeksi valitaan tyypillisesti production-haara, mutta mikään ei estä erillisen, pitkäikäisen testing tai staging-haaran käyttöä ennen production-haaraan siirtämistä.

Jonkun toisen käyttäjän olisi hyvä seuraavaksi tarkistaa Merge requestin sisältämät muutokset niiden laadun varmistamiseksi. Tässä vaiheessa on myös mahdollista ja jopa järkevää ajaa r10k Puppetmasterilla, jolloin se luo Puppet environmentin feature_a:

$ r10k deploy environment
 

Nyt uutta ominaisuutta ("feature_a") voi testata oikeilla Puppetiin liitetyillä koneilla tähän tapaan:

$ puppet agent -tv --environment=feature_a --noop

Testikoneilla komento voidaan turvallisesti ajaa myös ilman --noop -vipua.

Jos muutokset näyttävät hyviltä ja toimivat odotetulla tavalla, voi tarkistava käyttäjä liittää ("merge") ne hallintarepoon, jolloin ne päätyvät sen production-haaraan.

Seuraava ja viimeinen vaihe on uuden production-haaran sisällön vienti oikeasti tuotantoon r10k:lla:

$ r10k deploy environment
 

Sama prosessi toistetaan aina muutoksia tehdessä.

Jos (ja vain jos) Puppetia käyttää ainostaan yksi henkilö, voi merge requestit halutessaan unohtaa ja tarkistaa muutokset pelkästään Gitin omilla työkaluilla eli lähinnä "git diff"-komennolla. Erillisten feature-haarojen käyttö on kuitenkin perusteltua, koska se helpottaa uuden koodin testausta r10k:lla. Testien jälkeen muutokset voi liittää ("merge") production-haaraan ja julkaista suoraan:

$ git branch
 * feature_a
 production
 $ git push origin feature_a
 $ r10k deploy environment
 --- testaus ---
 $ git checkout production
 $ git merge feature_a
 $ git push

Artikkelisarjan muut osat:

menucross-circle