Continuous Delivery -putkia kirjoittavat ihmiset jakautuvat yleensä kahteen leiriin: Apache Groovy- ja YAML-leiriin. Kumpi on parempi?
Markus Suonto
Cloud Architect
Markus is a Cloud Architect based in Helsinki. He likes to apply the latest open-source technologies to enterprises and occasionally talks about it.
Groovy-putket hallitsivat alaa jonkin aikaa, mutta viime aikoina YAML-ratkaisut ovat saaneet tuulta alleen. Tämä vertailu perustuu kokemukseeni työskentelystä sekä Groovyn että YAMLin parissa sekä keskusteluihin muiden kanssa.
Luettavuus
YAML-putket näyttävät yleensä hyvältä. Katso esimerkiksi GitLab CI:n esimerkkiputkea:
image: "ruby:2.5"
before_script:
- apt-get update -qq && apt-get-install -y -qq sqlite3 libsqlite3-dev nodejs
- ruby -v
- which ruby
- gem install bundler --no-document
- bundle install --jobs $(nproc) "${FLAGS[@]}"
rspec:
script:
- bundle exec rspec
rubocop:
script:
- bundle exec rspecTämä esimerkki on varmasti selkeä ja ymmärrettävä. Koska useimmat esimerkit ovat tällaisia, ensireaktio putkien määrittelyyn YAMLilla on usein hyvin myönteinen: ”Totta kai määrittelemme ne YAMLilla. Katsokaa, kuinka kaunis lopputulos on!”
Groovy ei näytä yhtä hyvältä tai helppolukuiselta, koska sen syntaksi on monimutkaisempi:
node('docker'){
checkout scm
stage('Build'){
docker.image('node:6.3').inside{
sh 'npm --version'
}
}
} Groovy voi tuntua hieman pelottavalta. Erityisesti ihmisillä, jotka eivät ohjelmoi Javalla tai Scalalla, on yleensä vahvan kielteisiä tunteita Groovyä kohtaan. Java-ohjelmoijat taas tyypillisesti rakastavat Groovyä ensisilmäyksellä. Java- ja Scala-ohjelmoijat eivät kuitenkaan ole enemmistö ohjelmistoalalla – ainakaan GitHubissa – joten siirtymä YAML-pohjaisiin putkiin on helppo ymmärtää. On jopa Jenkins-lisäosia ja blogikirjoituksia Jenkins-putkien kirjoittamisesta YAMLilla!
Käytettävyys
YAML oli alun perin lyhenne sanoista ”Yet Another Markup Language”, mutta Wikipedian mukaan se on ”ihmisluettava tiedon sarjallistamiskieli”. Joka tapauksessa YAML on itse asiassa JSONin ylijoukko.
Sekä YAMLin että JSONin käyttötarkoitus on tiedon sarjallistaminen. YAML pyrkii tekemään lopputuloksesta ihmiselle helpommin luettavan korvaamalla sulkeet sisennyksillä ja rivinvaihtokäytännöillä. Olennaista kuitenkin on, että YAML ei ole skriptauskieli. Putkien kannalta tämä tarkoittaa, ettei YAMLilla voi ilmaista logiikkaa. YAMLissa ei ole if-lauseita, silmukoita eikä muuttujia.
Monet YAML-pohjaiset CI-moottorit tarjoavat oman kehyksensä tai käytäntönsä logiikan ilmaisemiseen. Otetaan esimerkiksi nämä GitLab-säännöt:
workflow:
rules:
- if: $CI_COMMIT_REF_NAME =~ /-wip$/
when: never
- if: $CI_COMMIT_TAG
when: never
- when: alwaysKun putkiin lisätään monimutkaista logiikkaa, useimmat eivät enää pidä niitä yksinkertaisina ja helposti luettavina. Päinvastoin, useimmista alkaa tuntua, että putkissa ilmenevien ongelmien ratkaisemiseksi on otettava yhteyttä omaan suosikki-”DevOps-tyyppiin”. Tähän on kaksi syytä:
Kynnys ymmärtää täysin jonkun muun kirjoittama putki nousee nopeasti. Ihmisillä on taipumus hakea turvaa tietämättömyydestä.
Kaikki tämä on täysin ymmärrettävää. Jos satut olemaan se ”DevOps-tyyppi”, yritä ymmärtää ”tavallisten” käyttäjien turhautumista ja tarjoa heille kannustusta ja empatiaa. Ja välipaloja. Erityisesti silloin, kun he tekevät hyvää työtä omin voimin. Välipalat auttavat aina.
Toinen seuraus siitä, ettei YAML ole skriptauskieli, on se, että valtaosa YAML-putkista joko viittaa skripteihin tai sisältää upotettuja skriptilohkoja. Nämä lohkot kirjoitetaan yleensä Bashilla tai Pythonilla. Katso nopeasti tätä esimerkkiä:
upload:
image: alpine
stage: upload
script:
- for f in $(find . -name '*.yml' -o -name '*.yaml'); do
bname=${f#./};
if [[ "$bnam" == ".gitlab-ci-yml" ]]; then
continue;
fi;
if [[ "$CI_COMMI_REF_SLUG" == "edge" ]]; then
sed -i.bak 's/s-latest/latest/g' $bname;
fi
url="$NEXUS_PIPELINES/$CI_COMMIT_REF_SLUG/$bname";
curl --verbose --show-error --fail ----upload-file $bname url;;
doneTällaiset lohkot hävittävät nopeasti kaikki jäljellä olevat myönteiset tunteet, joita ”tavallisilla” käyttäjillä vielä on ”yksinkertaisia YAML-putkia” kohtaan.
Groovy on varsinainen ohjelmointikieli. Siksi se tarjoaa kaikki logiikan ilmaisemiseen tarvittavat ominaisuudet. Hyvä puoli on, että Groovy-putket sisältävät harvoin muuta kuin Groovyä ja yksirivisiä shell-komentoja.
Huono puoli on, että käyttäjien, jotka eivät tunne syntaksia, on opeteltava melko monimutkainen DSL ennen kuin putkien käyttö tuntuu luontevalta. Katso esimerkiksi tätä ”yksinkertaista” Groovy-funktiota, joka sisältää closure-rakenteen:
def withImageStagingSelector(Closure selector) {
this.imageselector = selector ?: { img -> img }
this
}Useimmat ovat luultavasti samaa mieltä siitä, ettei tätä ole helppo selittää. Groovy-putkissa on melko tavallista, että monimutkainen logiikka on suoraan putkitiedostoissa. Silloin on vaikea ymmärtää, mitä tapahtuu.
Toisaalta Groovyssä on helppo tuoda kirjasto käyttöön ja kutsua funktiota parametreilla. Esimerkiksi:
mycorplib.deploy(‘myapp’, ‘production’)YAML ei sen sijaan tue funktioita lainkaan. Mahdollisuus välittää parametreja funktiokutsuille on Groovyn merkittävä etu YAMLiin verrattuna.
Yhteenveto
Palataan hetkeksi perusteisiin. Putket koostuvat yleensä build-, testaus- ja käyttöönottotoiminnoista. Kaikki nämä toiminnot perustuvat pohjimmiltaan logiikkaan. Käyttöönottoskripti on loppujen lopuksi vain skriptattu määritelmä siitä, miten käyttöönotto tarkalleen toimii. Toisin sanoen se on logiikan ilmaisu.
Näiden tietojen perusteella voimme turvallisesti päätellä, ettei YAML yksin riitä putkiin. Sen rinnalla tarvitaan aina varsinainen skriptauskieli tai kehys, joka tulkitsee YAML-datan logiikaksi.
Olisi kuitenkin harkitsematonta päätellä, että Groovy on aina oikea valinta. Mielestäni kaikissa putkissa on parasta pitää ne yksinkertaisina ja erottaa suurin osa logiikasta uudelleenkäytettäviin funktioihin tai malleihin. Lisäksi on helppo huomata, milloin YAML-putken logiikka ylittää hyväksyttävän monimutkaisuustason, sillä YAML ei yksinkertaisesti pysty toteuttamaan monimutkaista logiikkaa.
Lopullinen mielipiteeni on, että useimmissa tapauksissa YAML on parempi putkien kirjoittamiseen, koska se kannustaa luonnollisesti pitämään monimutkaisen logiikan putkitiedostojen ulkopuolella. Lisäksi YAML-putket ovat luettavampia, kun monimutkainen logiikka on abstrahoitu pois.
En kuitenkaan yritä hävittää kaikkea Groovyn käyttöä maailmankaikkeudesta. Groovy toimii paremmin import-lauseiden kanssa ja on erinomainen valinta, kun organisaatiosi pääasiallinen ohjelmointikieli on Java tai Scala.
Valitsitpa kumman tahansa, on tärkeää ymmärtää, että kieli on todellisuudessa toissijainen ja kontekstista riippuva valinta. Siihen ei ole olemassa yhtä yleispätevää parasta käytäntöä. Putkille sen sijaan on joitakin yleispäteviä parhaita käytäntöjä, mutta yksikään niistä ei koske Groovyn tai YAML:n valintaa.
- DevOps
- CI/CD
Subscribe to our newsletter
Related blogs