Continuous Delivery with Docker and Jenkins – Docker – Local development



Continuous Delivery with Docker and Jenkins – Docker – Local development

0 1


bigone-docker

Presentation slides: Continuous Delivery with Docker and Jenkins

On Github mrodem / bigone-docker

Continuous Delivery with Docker and Jenkins

- Velkommen - Skal fortelle litt om hvordan vi bruker Docker og Jenkins under utvikling, bygging og deployment

Docker

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.

Local development

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

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

Questions?

Continuous Delivery with Docker and Jenkins - Velkommen - Skal fortelle litt om hvordan vi bruker Docker og Jenkins under utvikling, bygging og deployment