Hvis du ønsker at kunne administrere webapplikationer effektivt i udvikling og produktion, er du nødt til at forstå miljøvariabler.
Det har ikke altid været tilfældet. For blot et par år siden var der næsten ingen, der konfigurerede deres Rails-apps med miljøvariabler. Men så kom Heroku.
Heroku introducerede udviklere til 12-faktor-app-tilgangen. I deres 12-faktor-app-manifest beskriver de en masse af deres bedste praksis for at skabe apps, der er nemme at implementere. Afsnittet om miljøvariabler har haft særlig stor indflydelse.
Den tolv-faktor-app gemmer konfigurationen i miljøvariabler (ofte forkortet til env vars eller env). Env vars er nemme at ændre mellem udrulninger uden at ændre kode; i modsætning til konfigurationsfiler er der lille chance for, at de bliver tjekket ind i kode-repo’en ved et uheld; og i modsætning til brugerdefinerede konfigurationsfiler eller andre konfigurationsmekanismer såsom Java System Properties er de en sprog- og OS-agnostisk standard.
Mere Rubyister bruger miljøvariabler end nogensinde. Men ofte er det på en cargo-culty måde. Vi bruger disse ting uden rigtig at forstå, hvordan de fungerer.
Dette indlæg vil vise dig, hvordan miljøvariabler virkelig fungerer – og måske endnu vigtigere, hvordan de IKKE fungerer. Vi vil også undersøge nogle af de mest almindelige måder at håndtere miljøvariabler på i dine Rails-apps. Lad os komme i gang!
BEMÆRK: Du kan læse om sikring af miljøvariabler her.
- Hver proces har sit eget sæt af miljøvariabler
- Miljøvariabler dør med deres proces
- En proces får sine miljøvariabler fra sin forælder
- Forældre kan tilpasse de miljøvariabler, der sendes til deres børn
- Børn kan ikke indstille deres forældres miljøvariabler
- Ændringer i miljøet synkroniseres ikke mellem kørende processer
- Din shell er blot en brugergrænseflade for systemet med miljøvariabler.
- Miljøvariabler er IKKE det samme som shell-variabler
- Håndtering af miljøvariabler i praksis
- Figaro
- Dotenv
- Secrets.yml?
- Plain old Linux
Hver proces har sit eget sæt af miljøvariabler
Hver eneste program, du kører på din server, har mindst én proces. Denne proces får sit eget sæt af miljøvariabler. Når den først har dem, kan intet uden for denne proces ændre dem.
En forståelig fejl, som begyndere begår, er at tro, at miljøvariabler på en eller anden måde er serverdækkende. Tjenester som Heroku får det helt sikkert til at se ud som om, at det at indstille miljøvariablerne svarer til at redigere en konfigurationsfil på disken. Men miljøvariabler er slet ikke som konfigurationsfiler.
Hvert program, du kører på din server, får sit eget sæt af miljøvariabler i det øjeblik, du starter det.
Hver proces har sit eget miljø.
Miljøvariabler dør med deres proces
Har du nogensinde indstillet en miljøvariabel, genstartet og opdaget, at den var væk? Da miljøvariabler hører til processer, betyder det, at når processen afsluttes, forsvinder din miljøvariabel.
Du kan se dette ved at indstille en miljøvariabel i en IRB-session, lukke den og forsøge at få adgang til variablen i en anden irb-session.
Når en proces lukker ned, går dens miljøvariabler tabt
Det er det samme princip, som gør, at du mister miljøvariabler, når din server genstartes, eller når du afslutter din shell. Hvis du vil have dem til at bestå på tværs af sessioner, skal du gemme dem i en slags konfigurationsfil som .bashrc
.
En proces får sine miljøvariabler fra sin forælder
Alle processer har en forælder. Det skyldes, at ethvert program skal startes af et andet program.
Hvis du bruger din bash-shell til at starte vim, så er vims forælder shell’en shell’en. Hvis din Rails-app bruger imagemagick til at identificere et billede, er identify
programmets forælder din Rails-app.
Underprocesser arver env vars fra deres forælder
I eksemplet nedenfor indstiller jeg værdien af miljøvariablen $MARCO i min IRB-proces. Derefter bruger jeg back-ticks til at sende en shell ud og ekko af værdien af denne variabel.
Da IRB er den overordnede proces for den shell, jeg lige har oprettet, får den en kopi af miljøvariablen $MARCO.
Miljøvariabler, der er indstillet i Ruby, arves af børneprocesser
Forældre kan tilpasse de miljøvariabler, der sendes til deres børn
Som standard får et barn kopier af alle de miljøvariabler, som dets forælder har. Men forældrene har kontrol over dette.
Fra kommandolinjen kan du bruge env-programmet. Og i bash er der en særlig syntaks til at sætte env vars på barnet uden at sætte dem på forælderen.
Brug env-kommandoen til at sætte miljøvariabler for et barn uden at sætte dem på forælderen
Hvis du sheller ud indefra Ruby kan du også give brugerdefinerede miljøvariabler til børneprocessen uden at fylde din ENV-hash op med affald. Du skal blot bruge følgende syntaks med system
-metoden:
Sådan overdrager du brugerdefinerede miljøvariabler til Rubys systemmetode
Børn kan ikke indstille deres forældres miljøvariabler
Da børn kun får kopier af deres forældres miljøvariabler, har ændringer foretaget af barnet ingen effekt på forælderen.
Miljøvariabler “overføres ved værdi” og ikke “ved reference”
Her bruger vi back-tick-syntaksen til at shell ud og forsøge at indstille en miljøvariabel. Mens variablen vil blive sat for barnet, bobler den nye værdi ikke op til forældrene.
Børneprocesser kan ikke ændre deres forældres env vars
Ændringer i miljøet synkroniseres ikke mellem kørende processer
I eksemplet nedenfor kører jeg to kopier af IRB side om side. Tilføjelse af en variabel til miljøet i den ene IRB-session har ingen effekt på den anden IRB-session.
Og tilføjelse af en miljøvariabel til en proces ændrer den ikke for andre processer
Din shell er blot en brugergrænseflade for systemet med miljøvariabler.
Systemet selv er en del af OS-kernen. Det betyder, at shell’en ikke har nogen magisk magt over miljøvariabler. Den skal følge de samme regler som alle andre programmer, du kører.
Miljøvariabler er IKKE det samme som shell-variabler
En af de største misforståelser sker, fordi shells tilbyder deres egne “lokale” shell-variable-systemer. Syntaksen for brug af lokale variabler er ofte den samme som for miljøvariabler. Og begyndere forveksler ofte de to.
Men lokale variabler kopieres ikke til børnene.
Miljøvariabler er ikke det samme som shell-variabler
Lad os tage et kig på et eksempel. Først sætter jeg en lokal shellvariabel ved navn MARCO. Da dette er en lokal variabel, kopieres den ikke til nogen underprocesser. Når jeg derfor forsøger at udskrive den via Ruby, virker det ikke.
Næste gang bruger jeg kommandoen export til at konvertere den lokale variabel til en miljøvariabel. Nu bliver den kopieret til hver ny proces, som denne shell opretter. Nu er miljøvariablen tilgængelig for Ruby.
Lokale variabler er ikke tilgængelige for underordnede processer. Eksport konverterer den lokale variabel til en miljøvariabel.
Håndtering af miljøvariabler i praksis
Hvordan fungerer alt dette i den virkelige verden? Lad os lave et eksempel:
Sæt, at du har to Rails-apps, der kører på en enkelt computer. Du bruger Honeybadger til at overvåge disse apps for undtagelser. Men du er stødt på et problem.
Du vil gerne gemme din Honeybadger API-nøgle i miljøvariablen $HONEYBADGER_API_KEY. Men dine to apps har to forskellige API-nøgler.
Hvordan kan en miljøvariabel have to forskellige værdier?
Nu håber jeg, at du kender svaret. Da env vars er per-proces, og mine to rails-apps køres i forskellige processer, er der ingen grund til, at de ikke kan have hver deres egen værdi for $HONEYBADGER_API_KEY.
Nu er det eneste spørgsmål, hvordan det skal sættes op. Heldigvis er der et par gems, der gør det rigtig nemt.
Figaro
Når du installerer Figaro gem’en i din Rails-app, vil alle værdier, som du indtaster i config/application.yml, blive indlæst i ruby ENV-hash ved opstart.
Du installerer bare gem’en:
# Gemfilegem "figaro"
Og begynder at tilføje elementer til application.yml. Det er meget vigtigt, at du tilføjer denne fil til din .gitignore, så du ikke ved et uheld committerer dine hemmeligheder.
# config/application.ymlHONEYBADGER_API_KEY: 12345
Dotenv
Dotenv-perlen minder meget om Figaro, bortset fra at den indlæser miljøvariabler fra .env, og den bruger ikke YAML.
Du skal bare installere perlen:
# Gemfilegem 'dotenv-rails'
Og tilføj dine konfigurationsværdier til .env – og sørg for at git ignorere filen, så du ikke ved et uheld udgiver den til github.
HONEYBADGER_API_KEY=12345
Du kan derefter få adgang til værdierne i din Ruby ENV hash
ENV
Du kan også køre kommandoer i shell’en med dit foruddefinerede sæt af env vars på følgende måde:
dotenv ./my_script.sh
Secrets.yml?
Sorry. Secrets.yml – selv om den er cool – sætter ikke miljøvariabler. Så det er ikke rigtig en erstatning for perler som Figaro og dotenv.
Plain old Linux
Det er også muligt at vedligeholde unikke sæt af miljøvariabler pr. app ved hjælp af grundlæggende linux-kommandoer. En tilgang er at lade hver app, der kører på din server, være ejet af en anden bruger. Du kan derefter bruge brugerens .bashrc til at gemme applikationsspecifikke værdier.