Kildomaten

Kildomaten er en tjeneste for å automatisere overgangen fra kildedata til inndata. Tjenesten lar statistikkere kjøre sine egne skript automatisk på alle nye filer i kildedatabøtta og skrive resultatet til produktbøtta. Formålet med tjenesten er minimere behovet for tilgang til kildedata samtidig som teamet selv bestemmer hvordan transformasjonn til inndata skal foregå. Statistikkproduksjon kan da starte i en tilstand der dataminimering og pseudonymisering allerede er gjennomført.

Prosessering som skal skje i overgangen fra kildedata til inndata har SSB definert til å være:

  • Dataminimering:
    Fjerne alle felter som ikke er strengt nødvendig for å produsere statistikk.
  • Pseudonymisering:
    Pseudonymisering av personidentifiserende data.
  • Kodeverk:
    Legge på standard kodeverk fra for eksempel Klass.
  • Standardisering:
    Tegnsett, datoformat, etc. endres til SSBs standardformat.

Under forklarer vi nærmere hvordan man bruker tjenesten. Da forutsetter vi at du har et Dapla-team med tjenesten er aktivert. les mer om hvordan du aktiverer tjenester her (lenker her).

Forberedelser

Før et Dapla-team kan ta i bruk Kildomaten må man tjenesten aktivert for teamet. Som standard får alle statistikkteam dette skrudd på i prod-miljøet som opprettes for teamet. Ønsker du å aktivere Kildomaten i test-miljøet kan dette gjøres selvbetjent som en feature. Alternativt kan man opprette en Kundeservice-sak og be om å hjelp til dette.

Sette opp tjenesten

I denne delen bryter vi ned prosessen med å sette opp Kildomaten i de stegene vi mener det er hensiktsmessig å gjøre det når den settes opp for første gang på en enkeltkilde. Senere i kapitlet forklarer vi hvordan man setter den opp flere kilder og hvordan man vedlikeholder tjenesten.

Husk at alle på teamet kan gjøre det meste av arbeidet her, men det er data-admins som må godkjenne at tjenesten rulles ut1.

Klone IaC-repoet

Oppsett av Kildomaten gjøres i teamets IaC-repo2. Når vi skal sette opp Kildomaten-kilde må vi gjøre gjøre endringer i teamets IaC-repo. Man finner teamets IaC-repo ved gå inn på SSBs GitHub-organisasjon og søke etter repoet som heter <teamnavn>-iac. Når du har funnet repoet så kan du gjøre følgende:

  1. Klon ned ditt teams Iac-repo git clone <repo-url>
  2. Opprett en ny branch: git checkout -b add-kildomaten-source

Mappestruktur i IaC-repo

For at Kildomaten skal fungere så må det opprettes en bestemt mappestruktur i IaC-repoet til teamet. Når et team blir opprettet vil den grunnleggende mappestrukturen i IaC-repoet allerede være opprettet for prod-miljøet til teamet. F.eks. vil mappestrukturen se slik ut for team dapla-example:

github.com/statisticsnorway/dapla-example-iac
dapla-example-iac
├── automation/
│   └── source-data/
│       ├── dapla-example-prod/
│           └── README.md

│...           

Skal du sette opp Kildomaten i prod-miljøet så kan du følge oppskriften som kommer senere i kapitlet uten å gjøre noe mer enda.

Skal du også bruke Kildomaten i test-miljøet så må opprette en ny mappe og lage en PR i IaC-repoet til teamet. Da vil strukturen se slik ut:

github.com/statisticsnorway/dapla-example-iac
dapla-example-iac
├── automation/
│   └── source-data/
│       ├── dapla-example-prod/
│       │    └── README.md
│       ├── dapla-example-test/ 
│...           

I mappestrukturen over så har vi klargjort den grunnleggende mappestrukturen for å ta i bruk Kildomaten i prod- og test-miljøet. Neste steg blir å legge de ulike kildene som egne mapper under dapla-example-prod og dapla-example-test. Det viser vi i neste avsnitt.

Flere kilder

Kildomaten lar deg prosessere ulike filstier i kildebøtta med ulike python-script. Dette refereres til som at Kildomaten har flere kilder. For å sette opp en kilde så må man følge en definert mappestruktur i IaC-repoet der alle kildene ligger rett under <teamnavn>-prod- eller <teamnavn>-test-mappen. Du kan ikke ha undermapper under en kilde. Du velger selv navnet på kildene/mappene i IaC-repoet, og det vil være navnet på kildene i Kildomaten. Senere i kapitlet ser vi at vi må bruke navnet for trigge re-kjøring av kilder.

Under er et eksempel på hvordan det kan se ut for eksempel-teamet dapla-example:

github.com/statisticsnorway/dapla-example-iac
dapla-example-iac
├── automation/
│   └── source-data/
│       ├── dapla-example-prod/
│       │    └── altinn
│       │    └── ameld
│       ├── dapla-example-test/
│       │    └── altinn
│       │    └── ameld
│       │    └── nudb
│...           

I eksempelet over ser vi at det er opprettet kildene altinn og ameld for både test- og prod-miljøet. I tillegg er det i test-miljøet kjørende en annen kilde som heter nudb. Hver av disse kildene kan kjøre et eget Pyton-script på alle filer som skrives til en gitt filsti som man definerer selv.

Alle kilder på samme nivå

Et team kan sette opp mange kilder som skal prosesseres med ulike skript. Men Kildomaten tillater bare et nivå under automation/source-data-<teamnavn>-<miljø>/. Det vil si at du ikke kan ha undermapper under en kilde. Det er også slik at man alltid må opprette en mappe for en kilde, selv om du kun har en kilde. F.eks. kan man ikke droppe mappen altinn i eksempelet over.

Konfigurasjon og skript

Når mappestrukturen er opprettet kan man legge til filene som beskriver hvilke filer som skal prosesseres, og python-skriptet med koden som skal prosessere filene. Det er to filer som må eksistere for at tjenesten skal fungere:

  1. En konfigurasjonsfil
  2. Et Python-skript

Når du har opprettet de skal de ligge på denne måten i IaC-repoet:

github.com/statisticsnorway/dapla-example-iac
dapla-example-iac
├── automation/
│   └── source-data/
│       ├── dapla-example-prod/
│           └── altinn/
│               ├── config.yaml
│               └── process_source_data.py
│...           

Under forklares hvordan skriver innholdet i de to filene.

Konfigurasjonsfil

Kildomaten trigges ved at det oppstår nye filer i kildebøtta til teamet. Hvorvidt den skal trigges på alle filer, eller kun filer i en gitt undermappe, bestemmer brukeren ved å konfigurere tjenesten i config.yaml. Her kan du også angi hvor mye ressurser prosesseringen skal få.

Hvis vi fortsetter eksempelet vårt fra tidligere med dapla-example, så kan vi tenkes oss at teamet ønsker å Kildomaten skal trigges på alle filer som oppstår i kildebøtta under filstien:
ssb-dapla-example-data-kilde-prod/ledstill/altinn/.

For å konfigurere tjenesten i Kildomaten må vi legge til en fil som heter config.yaml under automation/source-data/dapla-example-prod/altinn/, slik som vist i forrige kapitel. I dette tilfellet vil den ha innholdet som vist til venstre under:

config.yaml
folder_prefix: ledstill/altinn
memory_size: 1  # GiB
ssb-dapla-example-data-kilde-prod/
├── ledstill/
│   └── altinn/
│   └── aordningen/
├── sykefra/
│   └── altinn/
│   └── freg/
│...
       

Mappestrukturen til høyre over viser hvordan vi mappestrukturen ser ut i kildebøtta, mens config.yaml-fila til venstre viser hvordan vi angir at Kildomaten kun skal trigges på nye filer som oppstår i undermappen ssb-dapla-example-data-kilde-prod/ledstill/altinn/. Vi bruker nøkkelen folder_prefix for å angi hvilken sti i kildebøtta som tjenesten skal trigges på. Nøkkelen memory_size lar brukeren angi hvor mye minne og CPU hver prosessering skal få.

Hvor mye minne og CPU er mulig?

Som standard så får hver prosessering med Kildomaten 1 CPU-kjerne og 512MB minne/RAM. Hvis man skal gjøre mye prossesering, eller filene som prosesseres er veldig store, kan man sette memory_size opp til max 32GB minne. Antall CPU-kjerner blir satt implisitt ut i fra valg av memory_size iht til begrensningene som Google setter for Cloud Run. Se de nøyaktige verdiene som blir satt her.

Python-skript

Husk dette når du skriver skriptet ditt

Når du skal skrive et Python-skript for Kildomaten er det spesielt viktig å huske på 2 ting:

  1. Skriptet ditt kommer til å bli kjørt på en-og-en fil.
  2. Skriptet ditt må skrive ut et unikt navn på filen som skal skrives til produktbøtta, ellers risikerer du at filer blir overskrevet.
  3. Python-pakkene som kan benyttes er definert her. Ved behov for andre pakker, ta kontakt med Kundeservice.

Python-skriptet er der teamet kan angi hva slags kode som skal kjøre på hver fil som dukker opp i den angitte mappen i kildebøtta. For at dette skal være mulig må koden følge disse reglene:

  1. Koden må ligge i en fil som heter process_source_data.py.
  2. Koden må pakkes inn i en funksjon som heter main().

main() er en funksjon med ett parameter som heter source_file som du alltid får av Kildomaten når en fil blir prosessert. Når du skriver koden kan man derfor anta at dette parameteret blir gitt til funksjonen, og du kan benytte det inne i main-funksjonen.

source_file-parameteret gir main() en ferdig definert filsti til filen som skal prosesseres. For eksempel så kan source_file ha verdien
gs://ssb-dapla-example-data-kilde-prod/ledstill/altinn/test20231010.csv hvis filen test20231010.csv dukker opp i ssb-dapla-example-data-kilde-prod/ledstill/altinn/.

Hvis vi fortsetter eksempelet fra tidligere så ser mappen i IaC-repoet vårt slik ut nå:

github.com/statisticsnorway/dapla-example-iac
├── automation/
│   └── source-data/
│       ├── dapla-example-prod/
│           └── altinn/
│               ├── config.yaml
│               └── process_source_data.py

│...         

Vi ser nå at filene config.yaml og process_source_data.py ligger i mappen
automation/source-data/dapla-example-prod/ledstill/altinn/. Senere, når vi har rullet ut tjenesten, vil en ny fil i gs://ssb-dapla-exmaple-data-kilde-prod/ledstill/altinn/ trigge Kildomaten og kjøre koden i process_source_data.py på filen.

Under ser du et eksempel på hvordan en vanlig kodesnutt kan konverteres til å kjøre i Kildomaten:

Vanlig kode
import dapla as dp

# Stien til filen
source_file = "gs://ssb-dapla-example-data-kilde-prod/ledstill/altinn/test20231010.csv"

df = dp.read_pandas(source_file, file_type="csv")

# Dataminimerer ved å velge kolonner
df2 = df[['col1', 'col2', 'col3']]

# Skriver ut fil til produktbøtta
dp.write_pandas(df2, 
            "gs://ssb-dapla-example-data-produkt-prod/test20231010.parquet")
Kode i Kildomaten
import dapla as dp

def main(source_file):
    df = dp.read_pandas(source_file, file_type="csv")

# Dataminimerer ved å velge kolonner
    df2 = df[['col1', 'col2', 'col3']]

# Bytter ut "kilde" med "produkt" i filstien
    new_path = source_file.replace("kilde", "produkt")

# Bytter ut ".csv" med ".parquet" i filstien
    new_path2 = new_path.replace(".csv", ".parquet")

# Skriver ut fil til produktbøtta
    dp.write_pandas(df2, new_path2)

De to kodesnuttene over gir det samme resultatet, bare at koden til venstre kjøres som vanlig python-kode, mens koden til høyre kjøres i Kildomaten. Som vi ser av koden til høyre så trenger vi aldri å hardkode inn filstier i Kildomaten. Tjenesten gir oss filstien, og vi kan bruke den til å skrive ut filen til produktbøtta.

Strukturen på filene som skrives bør tenkes nøye gjennom når man automatiseres prosessering av data. Hvis vi ikke har unike filnavn eller stier så kan du risikere at filer blir overskrevet. Typisk er dette allerede tenkt på når filer skrives til kildebøtta, så hvis man kopierer den strukturen, slik vi gjorde i eksempelet over, så vil det ikke være noe problem.

Hvilke Python-biblioteker kan jeg bruke?

Du kan kun bruke biblioteker som er forhåndsinstallert i Kildomaten. Du kan se en liste over disse bibliotekene her. Ønsker du andre biblioteker så må du ta kontakt med Kunderservice.

Test koden

Tilgang til kildedata

Tilgang til kildedata i prod-miljøet er det kun gruppen data-admins som kan aktivere ved å bruke tilgangsstyringsløsningen Just-in-Time Access (JIT). Les mer om hvordan JIT-løsningen fungerer her. Ønsker man å kunne liste ut innhold fra bøtta må man aktivere rollen ssb.buckets.list. Ønsker man i tillegg å lese/skrive til bøtta må man også aktivere ssb.bucket.write. Tilgang til kildebøtta i test-miljøet krever ikke JIT-tilgang.

Før man ruller ut koden i tjenesten er det greit å teste at alt fungerer som det skal i Jupyterlab. Hvis vi tar utgangspunkt i eksempelet over så kan vi teste koden ved å kjøre følgende nederst i skriptet:

process_source_data.py
main("gs://ssb-dapla-example-data-kilde-prod/ledstill/altinn/test20231010.csv")

Når tjenesten er rullet ut så vil det være dette som kjøres når en fil dukker opp i gs://ssb-dapla-example-data-kilde-prod/ledstill/altinn/. Ved å kjøre det manuelt på denne måten får vi sett at ting fungerer som det skal.

Husk å fjerne kjøringen av koden før du ruller ut tjenesten.

Pseudonymisering kan ikke kjøres fra en IDE i prod-miljøet

Du kan teste kode fra Jupyter og andre IDE-er i prod-miljøet på Dapla. Men hvis prosesseringen innebærer bruk av pseudonymisering, så vil den ikke kunne kalles fra programmeringsmiljøer som Jupyter. Grunnen til dette er at det ikke er ønskelig å gjøre det lett å se upseudonymisert og pseudonymisert data samtidig. Hvis du ønsker å teste prosesseringen av pseudo-tjenesten, så kan du gjøre med testdata i test-miljøet.

Rull ut tjenesten

For å rulle ut tjenesten gjør du følgende;

  1. Push branchen til GitHub og opprette en pull request.
  2. Pull request må godkjennes av en data-admins på teamet.
  1. Når pull request er godkjent så sjekker du om alle tester, planer og utrullinger var vellykket, slik som vist i Figur 1.
  2. Hvis alt er vellykket så kan du merge branchen inn i main-branchen.
Suksessfulle tester på GitHub
Figur 1: Suksessfulle tester på GitHub

Etter at du har merget inn i main kan du følge med på utrullingen under Actions-fanen i repoet. Når den siste jobben lyser grønt er Kildomaten rullet ut og klar til bruk.

Vanlige feil ved utrulling

For å gi raskt tilbakemelding på noen mulige feilsituasjoner, så kjøres det enkel validering på config.yaml og process_source_data.py når en Pull request er opprettet. Følgende validering gjennomføres:

  • Er det et python-script kalt process_source_data.py?
  • Er det en config.yaml med en gyldig folder_prefix: definert?
  • Bruker process_source_data.py biblioteker som ikke er installert i Kildomaten?
  • Har koden i process_source_data.py feil iht Pyflakes?

Det kan også forekomme at Atlantis, verktøyet for å rulle ut endringer fra IaC-repoet til GCP, feiler. Da kan du prøve å skrive atlantis plan i kommentarfeltet til pull request’en, og testene vil kjøre på nytt. Hvis det fortsatt ikke fungerer så kontakter man Dapla kundeservice.

Test tjenesten

Når du har rullet ut tjenesten kan teste tjenesten ved at data-admins flytter en fil til en den filstien Kildomaten er satt opp for. I eksempelet vi har brukt tidligere, gjør du følgende: 1. Aktiverer JIT-tilgangen til kildedata (som beskrevet over). 2. Kopier en fil over til filstien:
gs://ssb-dapla-example-data-kilde-prod/ledstill/altinn/ 3. Sjekk om filen ble skrevet til ønsket mappe i produktbøtta.

Du kan også sjekke logger og monitorere Kildomaten. Les mer i neste avsnitt.

Monitorering og logging

Når en kilde i Kildomaten er satt opp og ruller ut, kan du monitere tjenesten i Google Cloud Console (GCC) ved å gjøre følgende:

  1. Logg deg inn med SSB-bruker på GCC.
  2. Velg standardprosjektet i prosjektvelgeren3.
  3. Søk opp Cloud Run i søkefeltet på toppen av siden og gå inn på siden.

På siden til Cloud Run vil du se en oversikt over alle kilder teamet har kjørende i Kildomaten. De har formen source-<kildenavn>-processor. I eksempelet med team dapla-example tidligere, vil man da se en kilde som heter source-altinn-processor, siden mappen de opprettet under
automation/source-data-dapla-example-prod/ i IaC-repoet heter altinn.

Trykker man seg inn på hver enkelt kilde vil man kunne monitorere aktiviteten i kilden, og man kan trykke seg videre for å se loggene.

Sjekke logger

Det er anbefalt å se på Kildomaten-loggene i Logs Explorer. Det kan man enkelt gjøre ved å trykke på “View in Logs Explorer” som vist på bildet under:

Bilde som viser hvordan man åpner Logs Explorer

Åpne Kildomaten-loggene i Logs Explorer

Skalering

Kildomaten er satt opp for å kunne prosessere hver kilde i 5 parallelle intanser samtidig. F.eks. hvis det oppstår 10 nye filer i en mappe som trigger en Kildomaten-kilde, så kan det prosesseres 5 filer samtidig. Ta kontakt med Kundeservice hvis man har behov for ytterlige skalering.

Varsling på e-post

Kildomaten tilbyr e-postvarsling til teamet når tjenesten feiler. Opprett en Kundeservice-sak for å få satt opp e-postvarsling for teamet ditt.

Flere kilder

Man kan sette opp så mange kilder man ønsker. Men når man setter det opp er det viktig å huske at alle kildene må spesifiseres rett under automation/source-data/<teamnavn>-<miljø>/. Her er et eksempel på hvordan team dapla-example har to kilder i Kildomaten:

github.com/statisticsnorway/dapla-example-iac
dapla-example-iac
├── automation/
│   └── source-data/
│       ├── dapla-example-prod/
│           └── altinn/
│               ├── config.yaml
│               └── process_source_data.py
│           └── ledstill/
│               ├── config.yaml
│               └── process_source_data.py
│...           

I eksempelet over ser vi det er to kilder: altinn og ledstill. Hver kilde trigges på ulike filstier i kildebøtta, og python-koden som kjøres kan være ulik mellom kilder.

Test-miljø

I eksempelet som er brukt i dette kapitlet er Kildomaten satt opp i prodmiljøet til teamet. Mens egentlig burde man teste ut nye kilder i teamets test-miljø. Kildomaten er ikke satt opp i test-miljøet som standard, og derfor må det skrus på før man kan anvende det. Teamet kan gjøre det selv ved å følge denne beskrivelsen, eller de kan ta kontakt med Kundeservice og få hjelp til dette.

En av de store fordelene med å sette opp Kildomaten-kilder i test-miljøet før man gjør det i prod-miljøet, er at tilgangsstyringen til data er mye mindre streng. Det gjør det lettere for alle i teamet å utvikle koden som skal benyttes.

Når man skal sette opp Kildomaten i test-miljøet så følger det samme oppskrift som vi har vist for prod-miljøet over. Den store forskjellen er at test-kilder skal legges under en egen mappe under automation/source-data/ i teamets IaC-repo. Eksempelet under med team dapla-example viser hvordan man kan sette opp kildene altinn og ledstill for både prod- og test-testmiljøet:

github.com/statisticsnorway/dapla-example-iac
dapla-example-iac
├── automation/
│   └── source-data/
│       ├── dapla-example-prod/
│       │       ├── altinn/
│       │       │       ├── config.yaml
│       │       │       └── process_source_data.py
│       │       └── ledstill/
│       │               ├── config.yaml
│       │               └── process_source_data.py
│       ├── dapla-example-test/
│               ├── altinn/
│               │       ├── config.yaml
│               │       └── process_source_data.py
│               └── ledstill/
│                       ├── config.yaml
│                       └── process_source_data.py
│...           

Som vi ser av mappestrukturen over så er det to mapper under
automation/source-data:

  • dapla-example-prod
  • dapla-example-test

Disse to mappene angir om det er henholdsvis prod- eller test-miljøet vi setter opp kilder for.

Trigge kilde manuelt

Kildomaten er bygget for å trigge på nye filer som oppstår i en gitt filsti. Men noen ganger er det nødvendig å trigge kjøring av alle filer på nytt. Noen ganger ønsker man kanskje å kun trigge noen filer for en gitt kilde. Dette kan gjøres med en funksjon i Python-pakken dapla-toolbelt.

Før du kan gjøre dette trenger du følgende informasjon:

  1. project-id for standardprosjektet. Slik finner du prosjekt-id. Merk at dette ikke er kilde-prosjektet!
  2. folder_prefix som du ønsker at koden skal trigges på. Dette fungerer likt som tidligere forklart for config.yaml, men her har du også mulighet til å kunne trigge prosesseringen på et undermappe av stien som var satt i kildens config.yaml.
  3. source_name er navnet på kilden. Navnet på kilden i eksempelet med team dapla-example var altinn.

Her er et kodeeksempel som viser hvordan man kan trigge prossesering av alle filer for kilde

notebook
from dapla import trigger_source_data_processing

project_id = "dapla-example-p"
source_name = "altinn"
folder_prefix = "ledstill/altinn/ra0678"

trigger_source_data_processing(project_id, source_name, folder_prefix, kuben=True)          

I eksempelet over trigger vi scriptet som er satt opp for kilden altinn til å kjøre på alle undermapper av
ssb-dapla-example-data-kilde-prod/ledstill/altinn/ra0678/.

Vedlikehold

Når tjenesten er rullet ut så vil den kjøre automatisk på alle filer som dukker opp i filsti i kildebøtta. Etter hvert vil det være behov for å endre på skript, endre filstier som skal trigge tjenesten, eller prosessere alle filer på nytt. I denne delen forklarer vi hvordan du går frem for å gjøre dette.

Endre skript

Alle på team kan endre på skriptet, men det er data-admins som må godkjenne endringene før de blir rullet ut. For å endre skriptet gjør du følgende:

  1. Klon repoet.
  2. Gjør endringene du ønsker i en branch.
  3. Push opp endringene til GitHub og opprett en pull request.
  4. Få en data-admins på teamet til å godkjenne endringene.
  5. Når endringene er godkjent så kan du merge inn i main-branchen.

En endring i Python-skriptet krever ikke at tjenesten rulles ut på nytt. Derfor er det ikke like mange tester og kjøringer som gjøres som når man oppretter en helt ny kilde.

Endre config.yaml

Alle på teamet kan gjøre endringer i config.yaml, men det er data-admins som må godkjenne endringene før de blir rullet ut. For å endre config.yaml gjør du følgende:

  1. Klon repoet.
  2. Gjør endringene du ønsker i en branch.
  3. Push opp endringene til GitHub og opprett en pull request.
  4. Få en data-admins på teamet til å godkjenne endringene.
  5. Når endringene er godkjent så kan du merge inn i main-branchen.

En endring i config.yaml krever at tjenesten rulles ut på nytt og tar derfor litt mer tid enn en endring i Python-skriptet.

Fotnoter

  1. I tillegg er det data-admins som må teste tjenesten manuelt hvis det gjøres på skarpe data, siden det kun er data-admins som kan få tilgang til de dataene.↩︎

  2. Infrastructure-as-Code (IaC) er repo som definerer alle ressursene til teamet på Dapla. Alle Dapla-team har et eget IaC-repo på GiHub og du finner det ved å søke etter repoet -iac under statisticsnorway.↩︎

  3. Standardprosjektet har navnestrukturen <teamnavn>-p↩︎