Hvordan bygge et image for OKD
Hvorfor trenger vi en Containerfile?
For at OKD skal kunne kjøre applikasjonen din, trenger den et såkalt container image. Dette er en selvstendig pakke som inneholder alt applikasjonen din trenger for å kjøre: kode, avhengigheter og konfigurasjon.
Verktøyet som er anbefalt på UiO for å bygge disse container image-ene er Podman. Men Podman trenger en detaljert oppskrift for å vite nøyaktig hvordan image-et skal bygges, fra start til slutt. Denne oppskriften er din Containerfile. Den definerer hvert steg i byggeprosessen, fra hvilket base-image som skal brukes, til hvilke kommandoer som skal kjøres.
Det er god praksis å bruke en såkalt multi-stage build. Dette innebærer å dele opp prosessen i flere separate steg for å skape et lite, sikkert og optimalisert sluttprodukt. Den vanligste varianten har to steg: en byggeprosess og en kjøre-prosess, men du kan ha så mange steg som nødvendig.
Eksempelet under viser en Containerfile for en applikasjon bygget i Next.js. Dette er et populært rammeverk for å lage React-applikasjoner ved UiO, og det kjører på Node.js.
Node.js er et kjøremiljø som gjør det mulig å kjøre JavaScript-kode på en server, altså utenfor nettleseren. For rammeverk som Next.js fungerer Node.js som motoren som både bygger selve nettsiden (omgjør React-kode til ferdige filer) og kjører web-serveren som leverer siden til sluttbrukerne.
Steg 1: Bygging av applikasjonen (build stage)
I det første steget setter vi opp et midlertidig miljø med alle verktøyene som trengs for å bygge applikasjonen. Dette steget produserer de ferdige filene, men selve miljøet blir forkastet.
Eksempel
# Steg 1: Bruk et fullt Node.js-image som inneholder alle byggeverktøy
FROM registry.access.redhat.com/ubi9/nodejs-22 AS build
# Sett en arbeidsmappe inne i containeren
WORKDIR /usr/src/app
# Kopier kun avhengighetsfilene og installer dem
COPY package.json package-lock.json* ./
RUN npm install
# Kopier resten av kildekoden, inkludert konfigurasjonsfilen
COPY . .
# Last inn variabler fra config.env og kjør byggekommandoen
RUN export $(grep -v '^#' config.env 2>/dev/null | xargs -r) && npm run build
Hva skjer i dette steget?
FROM ... AS build: Vi starter med å hente et fullt Node.js-image som har alle nødvendige byggeverktøy. Vi navngir dette stegetbuildfor å kunne referere til det senere.WORKDIR: Vi oppretter og bytter til mappen/usr/src/appinne i containeren.COPYogRUN npm install: Først kopierer vi kun filene som definerer avhengigheter og installerer dem. Dette er en optimalisering som gjør at Podman kan gjenbruke dette laget så lenge avhengighetene ikke endres.COPY . .: Deretter kopieres resten av applikasjonens kildekode inn.RUN export ... && npm run build: Dette er en sammensatt kommando:- Først leser
export $(grep ...)innholdet iconfig.env, ignorerer kommentarlinjer (de som starter med#), og gjør variabelene i filen tilgjengelige som miljøvariabler under bygging. Dette er nyttig dersom vi trenger variablene når vi bygger imaget, ikke bare når det kjører. Next.js krever ofte dette. Dette krever også at filen config.env enten finnes i applikasjonen, eller blir lest inn av containerfilen fra et annet sted. - Deretter, når variablene er lastet inn, kjøres
npm run build. Dette bygger så applikasjonen med node package manager som har et regelsett det følger ved byggingen. Det er lurt å teste dette lokalt før du prøver å bygge i OKD.
- Først leser
Steg 2: Klargjøring av kjøremiljø (runtime stage)
Nå bygger vi det endelige imaget som OKD skal bruke. Dette imaget skal kun inneholde det absolutt nødvendige for å kjøre applikasjonen.
Eksempel
# Steg 2: Start på nytt med et minimalt image optimalisert for kjøring
FROM registry.access.redhat.com/ubi9/nodejs-22-minimal
ENV NODE_ENV=production
WORKDIR /usr/src/app
# Sett automatisk utløp for imaget (3 uker)
LABEL quay.expires-after=3w
# Kopier kun de nødvendige filene fra bygge-steget
COPY --from=build /usr/src/app/package.json ./
COPY --from=build /usr/src/app/node_modules ./node_modules
COPY --from=build /usr/src/app/.next ./.next
COPY --from=build /usr/src/app/public ./public
COPY --from=build /usr/src/app/config.env ./
# Informer OKD om hvilken port applikasjonen lytter på
EXPOSE 3000
# Last inn variabler fra config.env og start applikasjonen
CMD ["sh", "-c", "export $(grep -v '^#' config.env 2>/dev/null | xargs -r) && exec npm start"]
Hva skjer i dette steget?
FROM ...: Vi starter med blanke ark i hvert steg, denne gangen velger vi et minimalt base-image som kun inneholder det som trengs for å kjøre en Node.js-applikasjon (ikke bygge).COPY --from=build ...: Her får vi gevinsten av en multi-stage build. Vi kopierer kun de ferdigbygde filene og produksjonsavhengighetene frabuild-steget over til vårt nye, rene image. All kildekode og byggeverktøy blir liggende igjen og forkastet.EXPOSE 3000: Vi dokumenterer at applikasjonen vil lytte på port 3000 inne i containeren, dette må matche det som er satt i appen din.CMD ["sh", "-c", "..."]: Dette er den endelige kommandoen som kjøres når image-et startes som en container.- Den starter en shell (
sh -c) som først laster inn variablene fraconfig.env, på samme måte som i bygge-steget. Kun nødvendig dersom vi hentet data i byggefasen og trenger de ved kjøring av applikasjonen. - Deretter bruker den
exec npm start. Bruken avexecer viktig fordi den erstatter shell-prosessen med applikasjonsprosessen (npm start). Dette sikrer at appen mottar signaler korrekt fra OKD, for eksempel ved en kontrollert nedstengning.
- Den starter en shell (
Eksempel-fil
Her er et eksempel på hvordan en Containerfile kan se ut.
# Steg 1: Bruk et fullt Node.js-image som inneholder alle byggeverktøy
FROM registry.access.redhat.com/ubi9/nodejs-22 AS build
# Sett en arbeidsmappe inne i containeren
WORKDIR /usr/src/app
# Kopier kun avhengighetsfilene og installer dem
COPY package.json package-lock.json* ./
RUN npm install
# Kopier resten av kildekoden, inkludert konfigurasjonsfilen
COPY . .
# Last inn variabler fra config.env og kjør byggekommandoen
RUN export $(grep -v '^#' config.env 2>/dev/null | xargs -r) && npm run build
# Steg 2: Start på nytt med et minimalt image optimalisert for kjøring
FROM registry.access.redhat.com/ubi9/nodejs-22-minimal
ENV NODE_ENV=production
WORKDIR /usr/src/app
# Sett automatisk utløp for imaget (3 uker)
LABEL quay.expires-after=3w
# Kopier kun de nødvendige filene fra bygge-steget
COPY --from=build /usr/src/app/package.json ./
COPY --from=build /usr/src/app/node_modules ./node_modules
COPY --from=build /usr/src/app/.next ./.next
COPY --from=build /usr/src/app/public ./public
COPY --from=build /usr/src/app/config.env ./
# Informer OKD om hvilken port applikasjonen lytter på
EXPOSE 3000
# Last inn variabler fra config.env og start applikasjonen
CMD ["sh", "-c", "export $(grep -v '^#' config.env 2>/dev/null | xargs -r) && exec npm start"]