Docker - an unexpected journey

150

Mirko Friedenhagen

Agenda

  • What is Docker (in a nutshell)?

  • Why would you use Docker (as a developer)?

  • Why would you use Docker (for continuous integration)?

  • Why would you use Docker (in production)?

  • Docker and Security

What is Docker (in a nutshell)? - VM vs Container

VM vs Container

What is Docker (in a nutshell)? - VM vs Container II

WhatContainerVM

Isolation

-

+

Density

+

-

Inspectibility

+

-

What is Docker (in a nutshell)? - Docker I

Docker’s used interfaces

Docker’s used interfaces

What is Docker (in a nutshell)? - Docker II

  • Linux-only technique for containerization based on cgroups and namespaces

  • Tooling

What is Docker (in a nutshell)? - Alternatives

Why would you use Docker (as a developer)?

  • Easy way to setup complex build environments. (Android development)

  • Easy way to setup complex test environments.

  • Easy way to tear down complex test environments.

Docker on your workstation - pure docker

docker run -d --name redis redis
docker run -d --name postgres --link redis -e POSTGRES_PASSWORD=123 -e POSTGRES_USER=postgres postgres:9.6.1
docker exec -t postgres ping -c1 redis

Docker on your workstation - docker-compose

version: "2.0"
services:
  redis:
    image: redis
  postgres:
    image: postgres:9.6.1
    environment:
      POSTGRES_PASSWORD: 123
      POSTGRES_USER: postgres
    depends_on:
      - redis
    links:
      - redis:redis

Docker and Maven

<image>
        <name>gitlab/gitlab-ce:latest</name>
        <alias>gitlab</alias>
        <run>
                <ports>
                        <port>gitlab.port:80</port>
                </ports>
                <volumes>
                        <bind>
                                <volume>
                                        ${user.dir}/src/test/conf/gitlab/gitlab.rb:/etc/gitlab/gitlab.rb
                                </volume>
                        </bind>
                </volumes>
                <wait>
                        <http>
                                <url>http://${docker.host.address}:${gitlab.port}/api/v3/projects</url>
                                <method>GET</method>
                                <status>401</status>
                        </http>
                        <time>${docker-maven-plugin.timeOut}</time>
                </wait>
        </run>
</image>

Docker and Maven - simple sample

git clone https://github.com/mfriedenhagen/java-gitlab-api
git checkout docker
mvn -Pdocker-gitlab docker:start # Wait 2 minutes for download of image and another 2 minutes for startup!
mvn -Pdocker-gitlab docker:logs
mvn -Pdocker-gitlab docker:stop
mvn -Pdocker-gitlab clean verify # Starts/stops gitlab for "system" tests with failsafe
mvn -Pdocker-gitlab,docker-ide docker:start # Starts with fixed ports so you may adapt run tests in the IDE.

Docker on your workstation - Dockerfile

FROM openjdk:8-jdk

ENV MAVEN_VERSION 3.4.0-SNAPSHOT
ENV MAVEN_VERSION_TIMESTAMP 20161226.182921-307
ENV MAVEN_DOWNLOAD_LOG_LEVEL INFO
ENV MAVEN_OPTS "-Dorg.slf4j.simpleLogger.log.org.apache.maven.cli.transfer.Slf4jMavenTransferListener=${MAVEN_DOWNLOAD_LOG_LEVEL} -Dorg.slf4j.simpleLogger.showDateTime=true -Djava.awt.headless=true"

RUN mkdir -p /usr/share/maven \
  && curl -fsSL https://repository.apache.org/content/repositories/snapshots/org/apache/maven/apache-maven/$MAVEN_VERSION/apache-maven-3.4.0-${MAVEN_VERSION_TIMESTAMP}-bin.tar.gz \
    | tar -xzC /usr/share/maven --strip-components=1 \
  && ln -s /usr/share/maven/bin/mvn /usr/bin/mvn \
  && useradd --create-home user \
  && mkdir -p /home/user/.m2/repository /cache \
  && chown -R user:user /home/user/ /cache

ENV MAVEN_HOME /usr/share/maven
USER user
CMD ["mvn", "--version"]

Docker on your workstation - Dockerfile II

  • Each directive is a layer.

  • Layers of an image

Docker on your workstation - Dockerfile III

FROM fedora

MAINTAINER Guillaume Scheibel <guillaume.scheibel@gmail.com>

ENV JAVA_HOME /jdk1.8.0_112
ENV PATH $PATH:$JAVA_HOME/bin:/fopub/bin
ENV BACKENDS /asciidoctor-backends
ENV GVM_AUTO_ANSWER true
ENV ASCIIDOCTOR_VERSION "1.5.5"

RUN dnf install -y tar \
    make \
    gcc \
    ruby \
    ruby-devel \
    rubygems \
    graphviz \
    rubygem-nokogiri \
    unzip \
    findutils \
    which \
    wget \
    python-devel \
    zlib-devel \
    libjpeg-devel \
    redhat-rpm-config \
    patch \
  && dnf clean packages \
  && (curl -s -k -L -C - -b "oraclelicense=accept-securebackup-cookie" http://download.oracle.com/otn-pub/java/jdk/8u112-b15/jdk-8u112-linux-x64.tar.gz | tar xfz -) \
  && mkdir /fopub \
  && curl -L https://api.github.com/repos/asciidoctor/asciidoctor-fopub/tarball | tar xzf - -C /fopub/ --strip-components=1 \
  && touch /tmp/empty.xml \
  && fopub /tmp/empty.xml \
  && rm /tmp/empty.xml \
  && gem install --no-ri --no-rdoc asciidoctor --version $ASCIIDOCTOR_VERSION \
  && gem install --no-ri --no-rdoc asciidoctor-diagram \
  && gem install --no-ri --no-rdoc asciidoctor-epub3 --version 1.5.0.alpha.6 \
  && gem install --no-ri --no-rdoc asciidoctor-pdf --version 1.5.0.alpha.13 \
  && gem install --no-ri --no-rdoc asciidoctor-confluence \
  && gem install --no-ri --no-rdoc rouge coderay pygments.rb thread_safe epubcheck kindlegen \
  && gem install --no-ri --no-rdoc slim \
  && gem install --no-ri --no-rdoc haml tilt \
  && mkdir $BACKENDS \
  && (curl -LkSs https://api.github.com/repos/asciidoctor/asciidoctor-backends/tarball | tar xfz - -C $BACKENDS --strip-components=1) \
  && wget https://bitbucket.org/pypa/setuptools/raw/bootstrap/ez_setup.py -O - | python \
  && easy_install "blockdiag[pdf]" \
  && easy_install seqdiag \
  && easy_install actdiag \
  && easy_install nwdiag \
  && (curl -s get.sdkman.io | bash) \
  && /bin/bash -c "source /root/.sdkman/bin/sdkman-init.sh" \
  && /bin/bash -c "echo sdkman_auto_answer=true > ~/.sdkman/etc/config" \
  && /bin/bash -c -l "sdk install lazybones"

WORKDIR /documents
VOLUME /documents

CMD ["/bin/bash"]

Why would you use Docker (for continuous integration)?

  • Better isolation of build processes (but caching? ~/.m2/repository/)

  • Better isolation of integration tests

  • Enhanced security (strong chroot)

Sample usages with GitLab - this presentation

image: asciidoctor/docker-asciidoctor

build_slide:
  stage: build
  script:
    - ./convert
  only:
    - master@mfriedenhagen/dockers-presentation
  artifacts:
    paths:
      - "target/*"

build_doc:
  stage: build
  script:
    - ./convert_overview
  only:
    - master@mfriedenhagen/dockers-presentation
  artifacts:
    paths:
      - "target/*"

pages:
  image: alpine
  stage: deploy
  script:
    - mv target public
  dependencies:
    - build_slide
    - build_doc
  artifacts:
    paths:
    - public
  only:
    - master@mfriedenhagen/dockers-presentation

Sample usages with GitLab - services

test:
  image: mfriedenhagen/docker-maven:XXX
  services:
    # Available via DNS as gitlab-gitlab-ce for Maven
    - gitlab/gitlab-ce:latest
  script:
    - mvn verify

Why would you use Docker (in production)?

  • Do not use Docker!

    • unless you have only stateless services

    • cattle vs pets

    • unless you have a good idea where your data is stored

    • Ephemeral storage

Is Docker enough?

  • Depends

  • Only one server (AKA node) docker-compose or Ansible:

- docker_container:
    name: db_test
    image: "postgres:latest"
    volumes:
      - /postgres-data:/var/lib/postgres/data
- docker_container:
    name: sleeper
    image: ubuntu:14.04
    links:
      - db_test:postgres

Orchestration engines (Kubernetes, Docker Swarm)

  • Multiple nodes?

  • Myriads of services?

  • Automated load-balancing?

  • Automated up- and down-scaling?

Orchestration engines - Kubernetes

  • Form a cluster with multiple nodes.

  • Spawn containers/pods on different nodes - single network

  • Group containers/pods by label to form a service

  • Expose service to the world using load balancer.

  • Information is redundantly stored in an etcd cluster.

  • Works with docker and rkt

Orchestration engines - K8S manual

# Create two running nginx instances, which are deployed to listen on port 80
kubectl run mirkos-nginx --image=nginx:latest --replicas=2 --port=80
# Existing objects
kubectl get pods -o wide -Lrun -l run=mirkos-nginx
kubectl get replicasets -o wide -Lrun -l run=mirkos-nginx
kubectl get deployments -o wide -Lrun -l run=mirkos-nginx
# Create service
kubectl expose deployment mirkos-nginx --target-port=80 --type=NodePort
kubectl describe pods,deployments,services -l run=mirkos-nginx
# Shutdown!
kubectl delete deployments,services,pods -l run=mirkos-nginx

Orchestration engines - K8S files

apiVersion: v1
kind: Service
metadata:
  creationTimestamp: 2016-12-08T20:06:23Z
  labels:
    run: mirkos-nginx
  name: mirkos-nginx
  namespace: default
  resourceVersion: "24582"
  selfLink: /api/v1/namespaces/default/services/mirkos-nginx
  uid: cb0235e9-bd81-11e6-b5e1-86de67ab1283
spec:
  clusterIP: 10.0.0.78
  ports:
  - nodePort: 31076
    port: 80
    protocol: TCP
    targetPort: 80
  selector:
    run: mirkos-nginx
  sessionAffinity: None
  type: NodePort
status:
  loadBalancer: {}

Orchestration engines - K8S other objects

  • secrets - store your secrets in Kubernetes

  • configmap - storing configurations for reusage

  • volumes - allows nfs, hostPath, cephfs and others

Orchestration engines - Swarm

  • Quite new (since summer 2016)

  • Allows alternative mechanisms for registries, i.e.:

    • etcd

    • consul

    • zookeeper

  • Simpler but less powerful

Docker and Security

  • dockerd runs as root

  • Anyone with access to the socket (unix or tcp) has complete access to the node.

  • Restrictions via user namespace (but may be overridden using docker run --privileged)

  • Use apparmor or selinux.

Docker and Security II

  • Developers now have more responisibility for updates!

  • New Tomcat? New JRE? New openssl?

  • Rebuild of all docker images needed!

  • But: this has to be tested anyway!

Docker and Security III

  • Think twice about your base images.

    • Use trusted base images.

    • Build your own base images.

  • Scanning of existing images needed.