Continuous Integration

+

Dockerization

Continuous Integration (CI)

In software engineering, continuous integration (CI) is the practice of merging all developers' working copies to a shared mainline several times a day. Grady Booch first proposed the term CI in his 1991 method, although he did not advocate integrating several times a day. Extreme programming (XP) adopted the concept of CI and did advocate integrating more than once per day – perhaps as many as tens of times per day.

source: wikipedia

Integration

Integration

  • compilation
  • dependencies
  • stability
  • quality
  • artifacts
  • deployment

Continous

  • during the stone age, a team was sending their piece of work to an assembler responsible to merge it and solve conflicts
  • automation
  • every commit on every branch and merge
  • quick verification
  • easy solving of conflict

Solution

  • Hosted (Jenkins)
  • Cloud (Travis / CircleCI / Gitlab / Github Actions)

Features

  • Automation
  • Notifications
  • Parallelization
  • Pipelines
  • Integrations

Pipelines

Examples

build_steps: &build_steps
  working_directory: ~/repo
  steps:
    - checkout
    - run: bundle install
    - run: bundle exec rake

version: 2
jobs:
  test_ruby-2.7:
    <<: *build_steps
    docker:
      - image: circleci/ruby:2.7
  test_ruby-3.0:
    <<: *build_steps
    docker:
      - image: circleci/ruby:3.0

workflows:
  version: 2
  test_and_release:
    jobs:
      - test_ruby-2.7
      - test_ruby-3.0
name: Rails

on:
  push:
jobs:
  build:
    name: Build & test
    runs-on: ubuntu-latest

    services:
      posgres:
        image: postgres:12

    steps:
      - uses: actions/checkout@v2
      - name: Set up Ruby
        uses: ruby/setup-ruby@v1
        with:
          bundler-cache: false
      - name: Install dependencies
        run: |
          gem install bundler
          bundle config path vendor/bundle
          bundle install --jobs 4 --retry 3
      - name: Setup test database
        env:
          RAILS_ENV: test
        run: bundle exec rake db:drop db:create db:migrate
      - name: Run tests
        env:
          RAILS_ENV: test
        run: bundle exec rspec
      - name: Run linter
        run: bundle exec rubocop -c .rubocop.yml

Links

Homework

  • Add linters check
  • Run as much as possible in parallel steps
  • Build docker image
  • Automatically deploy an app to Heroku on successful integration on master / main branch

Advanced

  • Create a simple setup in CircleCI or GH Actions that builds an app and run tests

Dockerization

Containerization

OS-level virtualization is an operating system paradigm in which the kernel allows the existence of multiple isolated user space instances. Such instances, called containers (LXC, Solaris containers, Docker), Zones (Solaris containers), virtual private servers (OpenVZ), partitions, virtual environments (VEs), virtual kernels (DragonFly BSD), or jails (FreeBSD jail or chroot jail), may look like real computers from the point of view of programs running in them. A computer program running on an ordinary operating system can see all resources (connected devices, files and folders, network shares, CPU power, quantifiable hardware capabilities) of that computer. However, programs running inside of a container can only see the container's contents and devices assigned to the container.

Source: wikipedia

Problem #1

  • dev vs dev vs production machine
  • different OS
  • different versions of dependencies

Solution #1

Vagrant.configure("2") do |config|
  config.vm.define "ops01" do |ops|
    ops.vm.box = "bento/centos-7.2"
    ops.vm.network "forwarded_port", guest: 22, host: 2222, id: "ssh"
    ops.vm.network "private_network", ip: "10.1.0.22"

    ops.vm.provider "virtualbox" do |vb|
      vb.gui = false
      vb.memory = "256"
    end
  end

  config.vm.define "ops02" do |ops|
    ops.vm.box = "bento/centos-7.2"
    ops.vm.network "forwarded_port", guest: 22, host: 2223, id: "ssh"
    ops.vm.network "private_network", ip: "10.1.0.23"

    ops.vm.provider "virtualbox" do |vb|
      vb.gui = false
      vb.memory = "256"
    end
  end
end

Problem #2

  • performance
  • resources

Solution #2

  • containers

Problem #3

  • hard to use

Solution #3

  • docker

What is docker?

  • company (Docker inc)
  • tool (docker cli) & server
  • specification format (Dockerfile)

Docker ecosystem

  • docker hub
  • docker-compose
  • docker-swarm
  • kubernetes
  • AWS Fargate
  • nomad

Docker ecosystem

  • docker hub
  • docker-compose
  • docker-swarm
  • kubernetes
  • AWS Fargate
  • nomad

Examples

FROM ruby:3.0.1-alpine3.12

RUN apk add --no-cache build-base=0.5-r2\
                       postgresql=12.7-r0\
                       postgresql-dev=12.7-r0\
                       git=2.26.3-r0\
                       shared-mime-info=1.15-r0

RUN mkdir -p /srv/www/app
WORKDIR /srv/www/app

ENV RAILS_LOG_TO_STDOUT 1
ENV RAILS_ENV production

RUN bundle config set deployment 'true' && bundle config set without 'development test'

# gem caching
COPY Gemfile .
COPY Gemfile.lock .

RUN bundle install --jobs=3 --retry=3

# copy app
COPY app ./app
COPY bin ./bin
COPY config ./config
COPY config.ru Procfile Rakefile ./
COPY db ./db
COPY lib ./lib
COPY vendor ./vendor

EXPOSE 3000

CMD ["bundle", "exec", "rails", "server"]
# https://docs.docker.com/engine/reference/commandline/build/
$ docker build . -t railsapp
# https://docs.docker.com/engine/reference/run/
$ docker run -it --rm --name railsapp railsapp
# https://docs.docker.com/engine/reference/commandline/exec/
$ docker exec railsapp db:create db:migrate

Problem #4

  • multiple services

Solution #4

  • docker-compose
version: "3.1"
services:
  psql:
    container_name: tc-psql
    image: postgres:9.6.3
    restart: always
    ports:
      - 7121:5432
  redis:
    container_name: tc-redis
    image: redis:4
    restart: always
    ports:
      - 7122:6379
  mailcatcher:
    container_name: tc-mailcatcher
    image: schickling/mailcatcher
    restart: always
    ports:
      - 7125:1025
      - 7180:108
$ docker-compose up

Links

Homework

  • Create simple Dockerfile
  • Create docker-compose.yml

Advanced

  • Build docker image on CI and publish in registry (Docker Hub, Github Packages, AWS Container Registry)
  • Configure Heroku to run with docker
Made with Slides.com