Continuous Delivery with Docker and Jenkins
- Velkommen
- Skal fortelle litt om hvordan vi bruker Docker og
Jenkins under utvikling, bygging og deployment
Docker
- Platform for developing, shipping, and running applications in a standardized way
- Separates applications from the underlying infrastructure by isolating them inside containers
- Can be installed on Linux, OS X, and Windows
- Docker gir oss en standardisert plattform for utvikling, distribusjon og
kjøring av applikasjoner
- Docker separerer applikasjonene fra den underliggende infrastrukturen ved å
isolere dem i containere
- Docker containere kan kjøres hvor som helst hvor Docker er installert
- Docker kan installeres og kjøres på Linux, OS X og Windows
- For øyeblikket må man ha Virtualbox på OS X og Windows, men har nettopp kommet
en betaversjon som gjør at du ikke trenger det lenger
Images and containers
- Docker containers are based on Docker images
- When starting up an image, you get a running container
- A process running inside the container is isolated from the underlying host
- En container er alltid basert på et image
- Veldig enkelt kan man si at et image kan sammenlignes med en kjørbar fil som
ligger på filsystemet
- Starter man et image, får man en kjørende container
- En prosess som kjører i containeren vet ingenting om den underliggende hosten
Docker Hub
- Man kan finne mange ferdige images på Docker Hub
- Hostet av Docker
- Alle kan pushe og pulle images herfra
- Noen images er offisielle
- Laget av et team som er sponset av Docker
- Anbefalt å bruke disse
- Blir jevnlig oppdatert med nye versjoner og sikkerhetspatcher
Dockerfile
FROM python:2.7
# Install Python packages
RUN pip install gunicorn Flask
# Add our own code to the image
ADD . /code
# Run the application
CMD ["bin/start.sh"]
- Oppskriften for hva som skal være med i et image defineres
i en såkalt Dockerfile
- Øverst definerer man et base image som ofte er hentet fra Docker hub
- Deretter en serie med instruksjoner, f.eks. installere avhengigheter
- Til slutt defineres hvilken kommando som skal kjøres når man starter
kontaineren
- Instruksjonene blir cachet, så hvis du har bygd imaget en gang, og bygger på
nytt senere, vil den hoppe over de stegene som allerede er gjort tidligere
- Hvis man i dette tilfellet har endret noe kode i stående katalog, og bygger
imaget på nytt, vil den f.eks. hoppe over installeringen av python-pakker
Docker compose
- Tool for container configuration and orchestration
- Containers are configured in a docker-compose.yml file
- Simplifies building and running multiple containers
- Docker har et verktøy som heter Docker Compose som lar deg konfigurere og
orkestrere containere
- Det gjøres via en docker-compose yaml fil
- Når man har en slik fil, kan man bygge images og kjøre opp containere med én
kommando
docker-compose.yml
myapi:
build: .
volumes:
- .:/code
ports:
- 8080:80
environment:
- DB_URL=postgresql://user:pw@database:5432/mydb
links:
- database
database:
image: postgres:9.4
environment:
- POSTGRES_USER=user
- POSTGRES_PASSWORD=pw
- Her er et eksempel på en docker-compose fil
- Vi har en api container (myapi) og en database container
- Vi ser at vi har definert build med et punktum
- Det betyr at docker-compose skal bygge et image basert på Dockerfile i
stående katalog - med mindre det allerede finnes et slikt image
- Vi ser også at vi har angitt at stående katalog skal mappes inn i
API-containeren, via volumes - dette vil man typisk gjøre når man utvikler
- Slik at hvis man endrer noen filer på hosten, vil dette også reflekteres i
containeren
- Man kan også mappe opp porter, f.eks. her sier vi at trafikk som går mot port
8080 på hosten skal rutes til port 80 i containeren
- Api containeren er linket mot database-containeren, slik at den kan
kommunisere med databasen
- Finnes mange muligheter for konfigurasjon her
Docker compose usage
# Build and start all containers
$ docker-compose up -d
# List running containers
$ docker-compose ps
Name Command State Ports
------------------------------------------------------------------
myapi_myapi_1 bin/start.sh Up 0.0.0.0:8080->80/tcp
myapi_db_1 postgres Up 5432/tcp
- Nå kan man starte alle containerne ved å skrive docker-compose up
- Man kan også få en oversikt over kjørende containere med docker-compose ps
- Man kan starte og stoppe containere hver for seg osv.
Projects
repositories
├── myapi
│ ├── docker-compose.yml
│ ├── Dockerfile
│ └── ...
└── mywebapp
├── docker-compose.yml
├── Dockerfile
└── ...
- I vårt eksempel her har vi to repositories
- Et API repository
- Et webapp repository
- Begge har hver sitt sett med docker-compose og Dockerfile
- Med disse kan man teste og kjøre opp
applikasjonene hver for seg
Environment configuration
repositories
├── myapi
│ └── ...
├── mywebapp
│ └── ...
└── myenvironments
├── local
│ └── docker-compose.yml
├── develop
│ └── docker-compose.yml
├── test
│ └── docker-compose.yml
└── production
└── docker-compose.yml
- For å kjøre opp både API og webapp samtidig, har
vi et ekstra Git repository som inneholder
konfigurasjon for alle miljøer
- Her har vi konfigurasjon for local, develop, test, og production
Local docker-compose.yml
myapi:
build: ../../myapi
...
mywebapp:
build: ../../mywebapp
...
database:
image: postgres:9.4
...
elasticsearch:
image: elasticsearch:2.3
...
nginx:
image: nginx:1.8
...
- Her er nedstrippet eksempel på docker-compose fil for kjøring lokalt når man
utvikler
- En stor fordel her er at alle utviklere kan kjøre opp hele systemet lokalt på
sin PC, og konfigurasjonen er lik for alle - veldig verdifullt, unngår
forskjeller i oppsett
- Alt kjører i isolerte kontainere, så man unngår at ulikheter i oppsett på
PCene påvirker systemet
- Ser at denne refererer til de andre repositoriene, slik at når man endrer noe
der, vil dette reflekteres i de kjørende containerne
Build pipeline
- Vi har så langt tatt for oss hvordan vi jobber med Docker lokalt
- Hvordan kan vi sette opp en build pipeline med Docker?
- Vi vil jo at det skal kjøres tester og bygges nye versjoner når ny kode
pushes til Git
Build pipeline
- Til dette kan man egentlig bruke en hvilkensomhelst byggeserver, f.eks.
Jenkins
- Vi kan sette opp Jenkins slik at når det pushes ny kode til en branch, blir
det trigget en byggejobb som bygger Docker images basert på Dockerfile i
prosjektet
- Vi vil i tillegg at det kjøres tester og at images blir tatt vare på hvis
testene er ok
- For å ta vare på images som er bygget, kan man sette opp et Docker registry
og la Jenkins pushe images dit
Setting up Jenkins and Docker registry
# docker-compose.yml
jenkins:
image: jenkins
links:
- registry
volumes:
- /home/jenkins/:/var/jenkins_home
ports:
- "8080:8080"
registry:
image: registry:2.0
volumes:
- /home/registry:/tmp/registry-dev
ports:
- "5000:5000"
- Det finnes Docker images for både Jenkins og Registry på Docker Hub
- Dermed veldig enkelt å sette opp både Jenkins og Registry med en
docker-compose fil
- Du vil typisk legge denne fila i et Git repository og klone repoet på en
server hvor Docker er installert
- Da er det bare å kjøre docker-compose up, så har du alt oppe å kjøre på få
sekunder
Build jobs
- Byggejobbene blir gjerne litt avhengig av hvilke branch strategi man har
- Hvis du følger Git flow, har du typisk en develop og en master branch, hvor
master branch alltid skal være klar for produksjon
- Da kan du sette opp ulike byggejobber for develop og master, og i tillegg en
generell byggejobb for feature-brancher
Build, test, and push
# Build script executed by Jenkins
# Build and run tests
docker-compose build
docker-compose run myapi nosetests
# Where to push image
IMAGE_LOCATION=localhost:5000/myapi:master-$BUILD_NUMBER
# Tag and push the image to our Docker registry
docker tag myapi:latest $IMAGE_LOCATION
docker push $IMAGE_LOCATION
- Jenkins har plugins for å bygge og publisere Docker images, men det er nesten
vel så enkelt å gjøre det med et script
- Ser at vi bruker branch og byggenummer som tag på imagene
- Her har vi hardkodet image og branch for hver jobb. For å gjøre det mer
generelt, kan man bruke navnet på byggejobben og byggenummer som tag. Da kan
man gjenbruke samme script for alle jobbene.
Deployment job
- Hvis vi oppretter en deployment jobb, så kan vi bruke registryet sitt API
til å hente ut en liste over alle tags, og legge listen i en nedtrekksliste
- Ved deployment kan man da velge miljø og versjon som skal deployes
- Denne jobben kan man også trigge automatisk rett etter at nye images har
blitt bygget
Choose environment and version
- Slik ser deployment-jobben ut i Jenkins
- Kan velge miljø og image tag fra nedtrekkslister
- Listen over image tags blir populert ved å gjøre et API-kall mot registry
The deployment approach
- Vi valgte en svært enkel strategi til å begynne med
- Visste at i starten kom vi til å ha kun en håndfull brukere, og det var
viktigst å levere noe kjørbart raskt
- Ønsket ikke å introdusere unødvendig kompleksitet
- Vi har oppsett for alle miljøene i docker-compose filer i et Git repo, som
beskrevet tidligere
- Klonet dette repoet på en server hvor Docker er installert
- Har en jenkins job hvor man kan velge hvilket miljø man vil deploye til,
samt hvilket image man vil deploye
- For hver deployment så tar deploy-jobben og:
- Kobler til serveren
- Setter inn ny image tag i docker-compose filen for valgt miljø
- Starter servicen på nytt
- Punkt 1, 2, og 3 har vi allerede vært igjennom
- Deretter oppdaterer Jenkins image tag i docker-compose filen for valgt miljø
og starter på nytt
- Ved restart hentes det nye imaget fra Registry
- Man kan sette Jenkins til å automatisk trigge deployment etter at image har
blitt pushet til registry - vi har gjort dette for develop-miljøet
What we have achieved
- Simple continuous delivery pipeline with little effort
- Isolated our applications from the infrastructure
- Can easily reproduce the entire system on any server or PC where Docker is installed
- As the system is containerized, it will be a lot easier to scale when that time comes
Still room for improvement
- Services are unavailable for a few seconds on deployment
- No multi-host scaling of containers
- No automatic failover if server crashes
But this is often "good enough" for non-critical systems, or during the initial project phase
Taking things further
- Docker Swarm
- Kubernetes (Google)
- Apache Mesos
- Cloud providers
- Etterhvert som systemet vokser og får mer trafikk, og man trenger skalering,
failover osv. så finnes det løsninger for det
- Docker Swarm er dockers egen løsning for clustering av containere. Her kan
man slå sammen flere docker hosts til et virtuelt cluster, og kommunisere
med én master node for å kjøre kommandoer.
Docker Control Panel
- Docker har også en egen løsning for deployment
- Da får du et web-gui hvor du kan deploye og administrere containere
- Må betale lisens for å bruke denne løsningen - er bl.a. slik Docker tjener
penger
Continuous Delivery with Docker and Jenkins
- Velkommen
- Skal fortelle litt om hvordan vi bruker Docker og
Jenkins under utvikling, bygging og deployment