From ce11d76a8e2d7b16a2ae4736574150f4fcb4307e Mon Sep 17 00:00:00 2001
From: Andrej Shadura <andrew.shadura@collabora.co.uk>
Date: Wed, 2 Jun 2021 15:22:55 +0200
Subject: [PATCH] Build Docker images
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Was:

Create a Docker image with a Debian package built from the current Git
source. This eliminates an extra round trip with a manual upload to OBS
and the package getting published and fetched from apt repos.
Unfortunately, doing this in a way compatible with what was previously
done requires some non-trivial hacks.

Since we want fairly recent OmniAuth gems, we install them from external
sources directly into the resulting Docker image.

ruby-faraday is used by the OAuth2 auth backend, but new versions
require newer Ruby than what stretch has, so we preinstall it from
packages to avoid pinning it.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora/sso
Gbp-Pq: Name Build-a-Docker-image-with-the-Web-UI-only.patch

Build Docker images in GitLab CI

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora/sso
Gbp-Pq: Name Build-Docker-images-in-GitLab-CI.patch

Split docker-entrypoint.sh into three separate files

Gbp-Pq: Topic collabora/sso
Gbp-Pq: Name Split-docker-entrypoint.sh-into-three-separate-files.patch

Move Docker-related files under docker/

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora/sso
Gbp-Pq: Name Move-Docker-related-files-under-docker.patch

Clean up stale pid files on start

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora
Gbp-Pq: Name Clean-up-stale-pid-files-on-start.patch

Install and configure mstmp

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora
Gbp-Pq: Name Install-and-configure-mstmp.patch

Allow changing the session lifetime

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora
Gbp-Pq: Name Allow-changing-the-session-lifetime.patch

Preinstall apt-transport-https ca-certificates build-essential

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Simplify the db config generation

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Remove MariaDB/MySQL from the API container

There’s no need to keep the database inside when it can be a separate
container.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Switch to an external memcached

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Drop Apache and TLS termination, use Puma instead of Passenger

When the container is deployed, it will have a real HTTP server in front
of it, so there is no need to have Apache and TLS inside.

Since Passenger (at least of the version in Debian stretch) cannot be
easily used without Apache, use the standard solution for such cases
which is Puma, and expose OBS_FRONTEND_WORKERS (default: 4) to allow
scaling it.

Drop no longer necessary supervisord.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Rename the frontend image to a more sensible name

A front-end is what it really is, and it’s also the name the upstream
uses for their container.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Generalise the build process to enable building multiple images

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Teach deb-arch-to-obs-arch to work with an arbitrary argument

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Build the backend Docker image

This Docker image installs obs-build and obs-productconverter from
packages and everything else directly from the sources.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

WIP: update Docker stuff

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Pass TAG argument

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Don't purge or install unnecessary packages

Don't update packages

Install puma

Wip wup

Support buster

tags

more deps

unify log names with upstream

add cloud uploader

ensure correct permissions for log/tmp/sphinx

init app before db so that permissions are all correct

db setup needs command line mysql client

init or migrate database

remove more gem cruft

pre-create run dir with correct perms

provide our custom docker-compose

move services confs into a subdir because the worker also needs some

fix frontend

fix scripts

build worker image

add worker again

Build Docker images

Was:

Create a Docker image with a Debian package built from the current Git
source. This eliminates an extra round trip with a manual upload to OBS
and the package getting published and fetched from apt repos.
Unfortunately, doing this in a way compatible with what was previously
done requires some non-trivial hacks.

Since we want fairly recent OmniAuth gems, we install them from external
sources directly into the resulting Docker image.

ruby-faraday is used by the OAuth2 auth backend, but new versions
require newer Ruby than what stretch has, so we preinstall it from
packages to avoid pinning it.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora/sso
Gbp-Pq: Name Build-a-Docker-image-with-the-Web-UI-only.patch

Build Docker images in GitLab CI

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora/sso
Gbp-Pq: Name Build-Docker-images-in-GitLab-CI.patch

Split docker-entrypoint.sh into three separate files

Gbp-Pq: Topic collabora/sso
Gbp-Pq: Name Split-docker-entrypoint.sh-into-three-separate-files.patch

Move Docker-related files under docker/

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora/sso
Gbp-Pq: Name Move-Docker-related-files-under-docker.patch

Clean up stale pid files on start

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora
Gbp-Pq: Name Clean-up-stale-pid-files-on-start.patch

Install and configure mstmp

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora
Gbp-Pq: Name Install-and-configure-mstmp.patch

Allow changing the session lifetime

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Gbp-Pq: Topic collabora
Gbp-Pq: Name Allow-changing-the-session-lifetime.patch

Preinstall apt-transport-https ca-certificates build-essential

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Simplify the db config generation

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Remove MariaDB/MySQL from the API container

There’s no need to keep the database inside when it can be a separate
container.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Switch to an external memcached

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Drop Apache and TLS termination, use Puma instead of Passenger

When the container is deployed, it will have a real HTTP server in front
of it, so there is no need to have Apache and TLS inside.

Since Passenger (at least of the version in Debian stretch) cannot be
easily used without Apache, use the standard solution for such cases
which is Puma, and expose OBS_FRONTEND_WORKERS (default: 4) to allow
scaling it.

Drop no longer necessary supervisord.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Rename the frontend image to a more sensible name

A front-end is what it really is, and it’s also the name the upstream
uses for their container.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Generalise the build process to enable building multiple images

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Teach deb-arch-to-obs-arch to work with an arbitrary argument

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Build the backend Docker image

This Docker image installs obs-build and obs-productconverter from
packages and everything else directly from the sources.

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

WIP: update Docker stuff

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Pass TAG argument

Signed-off-by: Andrej Shadura <andrew.shadura@collabora.co.uk>

Don't purge or install unnecessary packages

Don't update packages

Install puma

Wip wup

Support buster

tags

more deps

unify log names with upstream

add cloud uploader

ensure correct permissions for log/tmp/sphinx

init app before db so that permissions are all correct

db setup needs command line mysql client

init or migrate database

remove more gem cruft

pre-create run dir with correct perms

provide our custom docker-compose

move services confs into a subdir because the worker also needs some

fix frontend

fix scripts

build worker image

add worker again

depend on xml-structured

sphinxsearch is not in bullseye

adjust path
---
 .gitlab-ci.yml                                |  70 ++++++++++++
 Dockerfile.backend                            |  16 +++
 Dockerfile.backend-base                       |  53 ++++++++++
 Dockerfile.frontend                           |  49 +++++++++
 Dockerfile.frontend-base                      |  74 +++++++++++++
 Dockerfile.worker                             |  28 +++++
 docker-compose.yml                            | 100 +++++++++++-------
 docker/backend-docker-entrypoint.sh           |  48 +++++++++
 docker/configure-app.sh                       |  45 ++++++++
 docker/configure-backend-user.sh              |  22 ++++
 docker/configure-db.sh                        |  34 ++++++
 docker/configure-frontend-user.sh             |  12 +++
 docker/configure-sso.py                       |  38 +++++++
 docker/deb-arch-to-obs-arch                   |  24 +++++
 docker/frontend-docker-entrypoint.sh          |  16 +++
 .../backend/obsclouduploadserver.conf         |  11 ++
 .../backend/obsclouduploadworker.conf         |  11 ++
 docker/services/backend/obsdispatcher.conf    |  11 ++
 docker/services/backend/obsdodup.conf         |  11 ++
 docker/services/backend/obspublisher.conf     |  11 ++
 docker/services/backend/obsrepserver.conf     |  11 ++
 docker/services/backend/obsscheduler@.conf.in |  11 ++
 docker/services/backend/obsservice.conf       |  11 ++
 .../services/backend/obsservicedispatch.conf  |  11 ++
 docker/services/backend/obssrcserver.conf     |  11 ++
 docker/services/backend/obswarden.conf        |  11 ++
 docker/services/worker/obsworker.conf         |  13 +++
 docker/worker-docker-entrypoint.sh            |  72 +++++++++++++
 src/api/config/environments/production.rb     |   2 +-
 src/api/config/options.yml.example            |   2 +-
 .../20191011000000_create_allowbuilddeps.rb   |   2 +-
 31 files changed, 802 insertions(+), 39 deletions(-)
 create mode 100644 .gitlab-ci.yml
 create mode 100644 Dockerfile.backend
 create mode 100644 Dockerfile.backend-base
 create mode 100644 Dockerfile.frontend
 create mode 100644 Dockerfile.frontend-base
 create mode 100644 Dockerfile.worker
 create mode 100755 docker/backend-docker-entrypoint.sh
 create mode 100755 docker/configure-app.sh
 create mode 100755 docker/configure-backend-user.sh
 create mode 100755 docker/configure-db.sh
 create mode 100755 docker/configure-frontend-user.sh
 create mode 100755 docker/configure-sso.py
 create mode 100755 docker/deb-arch-to-obs-arch
 create mode 100755 docker/frontend-docker-entrypoint.sh
 create mode 100644 docker/services/backend/obsclouduploadserver.conf
 create mode 100644 docker/services/backend/obsclouduploadworker.conf
 create mode 100644 docker/services/backend/obsdispatcher.conf
 create mode 100644 docker/services/backend/obsdodup.conf
 create mode 100644 docker/services/backend/obspublisher.conf
 create mode 100644 docker/services/backend/obsrepserver.conf
 create mode 100644 docker/services/backend/obsscheduler@.conf.in
 create mode 100644 docker/services/backend/obsservice.conf
 create mode 100644 docker/services/backend/obsservicedispatch.conf
 create mode 100644 docker/services/backend/obssrcserver.conf
 create mode 100644 docker/services/backend/obswarden.conf
 create mode 100644 docker/services/worker/obsworker.conf
 create mode 100755 docker/worker-docker-entrypoint.sh

diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
new file mode 100644
index 0000000000..8cb609db38
--- /dev/null
+++ b/.gitlab-ci.yml
@@ -0,0 +1,70 @@
+stages:
+  - docker
+
+.build-docker-image:
+  stage: docker
+  image:
+    name: gcr.io/kaniko-project/executor:debug
+    entrypoint: [""]
+  script:
+    - |
+      cat << EOF > /kaniko/.docker/config.json
+      {
+        "auths":{
+          "$CI_REGISTRY": {
+            "username":"$CI_REGISTRY_USER",
+            "password":"$CI_REGISTRY_PASSWORD"
+          }
+        }
+      }
+      EOF
+    - >
+      /kaniko/executor
+      --context $CI_PROJECT_DIR
+      --dockerfile $CI_PROJECT_DIR/Dockerfile.$image
+      --destination $CI_REGISTRY_IMAGE/obs-$image:$CI_COMMIT_REF_SLUG
+      --build-arg REGISTRY=$CI_REGISTRY_IMAGE
+      --build-arg TAG=$CI_COMMIT_REF_SLUG
+      --single-snapshot
+    - echo Pushed $CI_REGISTRY_IMAGE/obs-$image:$CI_COMMIT_REF_SLUG
+
+frontend-base:
+  extends: .build-docker-image
+  only:
+    changes:
+      - Dockerfile.frontend-base
+      - src/api/Gemfile
+      - src/api/Gemfile.lock
+  variables:
+    image: frontend-base
+
+frontend:
+  extends: .build-docker-image
+  needs:
+    - frontend-base
+  variables:
+    image: frontend
+
+backend-base:
+  extends: .build-docker-image
+  only:
+    changes:
+      - Dockerfile.backend-base
+      - dist/**
+      - src/backend/**
+  variables:
+    image: backend-base
+
+backend:
+  extends: .build-docker-image
+  needs:
+    - backend-base
+  variables:
+    image: backend
+
+worker:
+  extends: .build-docker-image
+  needs:
+    - backend-base
+  variables:
+    image: worker
diff --git a/Dockerfile.backend b/Dockerfile.backend
new file mode 100644
index 0000000000..41bbf27328
--- /dev/null
+++ b/Dockerfile.backend
@@ -0,0 +1,16 @@
+ARG REGISTRY
+ARG TAG=latest
+FROM $REGISTRY/obs-backend-base:$TAG
+
+LABEL maintainer Andrej Shadura <andrew.shadura@collabora.co.uk>
+
+COPY docker/services/backend/*.conf /etc/supervisor/conf.d/
+COPY docker/ /opt/
+
+VOLUME /etc/obs
+
+RUN /opt/configure-backend-user.sh
+
+VOLUME /srv/obs
+
+ENTRYPOINT /opt/backend-docker-entrypoint.sh
diff --git a/Dockerfile.backend-base b/Dockerfile.backend-base
new file mode 100644
index 0000000000..f0ed5c44ec
--- /dev/null
+++ b/Dockerfile.backend-base
@@ -0,0 +1,53 @@
+FROM debian:bullseye-slim as server
+LABEL maintainer Andrej Shadura <andrew.shadura@collabora.co.uk>
+ENV LC_ALL=C.UTF-8
+ARG DEBIAN_FRONTEND=noninteractive
+ARG WORKDIR=/tmp/sources
+
+# Needs checking what’s actually needed
+RUN apt-get update \
+ && apt-get install -y \
+        apt-utils \
+        adduser \
+        ca-certificates \
+        curl \
+        diffutils \
+        dpkg-dev \
+        git \
+        locales \
+        libbssolv-perl \
+        libcompress-raw-zlib-perl \
+        libfile-sync-perl \
+        libio-compress-perl \
+        libjson-xs-perl \
+        libnet-ssleay-perl \
+        libsocket-msghdr-perl \
+        libtimedate-perl \
+        libxml-parser-perl \
+        libxml-simple-perl \
+        libxml-structured-perl \
+        libyaml-libyaml-perl \
+        make \
+        obs-build \
+        patch \
+        procps \
+        reprepro \
+        supervisor \
+        time \
+        tzdata \
+        zstd
+
+COPY . $WORKDIR
+
+RUN make -C $WORKDIR/dist install
+RUN make -C $WORKDIR/src/backend install
+
+RUN rm -rf $WORKDIR
+
+RUN mkdir -p /etc/obs
+RUN cp /usr/lib/obs/server/BSConfig.pm.template /etc/obs/BSConfig.pm
+
+RUN ln -sf /etc/obs/BSConfig.pm /usr/lib/obs/server/BSConfig.pm
+RUN ln -sf /usr/lib/obs-build /usr/lib/obs/server/build
+
+ENTRYPOINT /opt/backend-docker-entrypoint.sh
diff --git a/Dockerfile.frontend b/Dockerfile.frontend
new file mode 100644
index 0000000000..47248b2cd7
--- /dev/null
+++ b/Dockerfile.frontend
@@ -0,0 +1,49 @@
+ARG REGISTRY
+ARG TAG=latest
+FROM $REGISTRY/obs-frontend-base:$TAG as base
+
+ARG REGISTRY
+ARG TAG=latest
+FROM $REGISTRY/obs-frontend-base:$TAG
+ARG WORKDIR=/tmp/sources
+ARG INSTALLDIR=/obs
+
+ADD src/api/ $INSTALLDIR/src/api/
+
+COPY --from=base $INSTALLDIR/src/api/Gemfile* $INSTALLDIR/src/api/
+
+WORKDIR $INSTALLDIR/src/api
+
+RUN ls -la
+
+ARG BUNDLE_BUILD__SASSC=--disable-march-tune-native
+ARG NOKOGIRI_USE_SYSTEM_LIBRARIES=1
+
+RUN bundle install --jobs=$(nproc) --retry=3
+
+# Install extra gems for SSO
+RUN gem install omniauth omniauth-gitlab --no-doc
+RUN gem install omniauth-azure-oauth2 omniauth-azure-oauth2-v2 omniauth-phabricator --no-doc
+
+RUN rm -rf /var/lib/gems/*/cache
+
+ENV RAILS_ENV=production
+ENV RAILS_LOG_TO_STDOUT=true
+
+RUN echo nonce > config/secret.key \
+ && DATABASE_URL=mysql2://localhost/noncedb bundle exec rake assets:precompile RAILS_GROUPS=assets \
+ && rm config/secret.key
+
+RUN bundle config --local without test:assets:development
+
+RUN sed -i 's|^#!/usr/bin/ruby.ruby.*$|#!/usr/bin/ruby|' bin/* script/*
+RUN sed -i -e /mailcatcher:/d -e /web:/d Procfile
+
+COPY docker/ /opt/
+
+RUN /opt/configure-frontend-user.sh
+RUN mkdir -p log tmp db/sphinx \
+ && chown -R frontend /obs
+
+ENTRYPOINT /opt/frontend-docker-entrypoint.sh
+EXPOSE 3000
diff --git a/Dockerfile.frontend-base b/Dockerfile.frontend-base
new file mode 100644
index 0000000000..8c5ee5b323
--- /dev/null
+++ b/Dockerfile.frontend-base
@@ -0,0 +1,74 @@
+# FROM debian:buster-slim as frontend-base
+FROM debian:bookworm-slim as frontend-base
+ENV LC_ALL=C.UTF-8
+ARG DEBIAN_FRONTEND=noninteractive
+ARG INSTALLDIR=/obs
+
+RUN apt-get update \
+ && apt-get install -y \
+        apt-utils \
+        adduser \
+        ca-certificates \
+        curl \
+        diffutils \
+        dpkg-dev \
+        git \
+        locales \
+        make \
+        msmtp-mta \
+        mariadb-client \
+        npm \
+        pkgconf \
+        ruby \
+        ruby-dev \
+        ruby-bundler \
+        ruby-ffi \
+        ruby-foreman \
+        sphinxsearch \
+        patch \
+        supervisor \
+        time \
+        tzdata
+
+RUN apt-get update \
+ && apt-get install -y \
+        default-libmysqlclient-dev \
+        libldap2-dev \
+        libsasl2-dev \
+        libxml2-dev \
+        libxslt1-dev \
+        zlib1g-dev
+
+# Work around a bug in buster
+RUN [ $(readlink /usr/bin/ruby) != ruby2.5 ] \
+ || { ver=$(basename -s.gemspec $(echo /usr/share/rubygems-integration/all/specifications/rake-*.gemspec)); \
+      mkdir -p /usr/share/rubygems-integration/all/gems/$ver/exe; \
+      ln -s /usr/bin/rake /usr/share/rubygems-integration/all/gems/$ver/exe/rake; }
+
+RUN gem install --no-format-executable brakeman --version 5.0.2 --no-doc
+RUN gem install sassc --version 2.0.1 --no-doc
+
+ADD src/api/Gemfile* $INSTALLDIR/src/api/
+WORKDIR $INSTALLDIR/src/api/
+
+RUN sed -e "/gem 'puma'/d" Gemfile > Gemfile.new; \
+    echo "gem 'puma'" >> Gemfile.new; \
+    diff -u Gemfile Gemfile.new; \
+    mv Gemfile.new Gemfile
+
+RUN sed -e '/BUNDLED WITH/,+1 d' Gemfile.lock > Gemfile.lock.new; \
+    diff -u Gemfile.lock Gemfile.lock.new; \
+    mv Gemfile.lock.new Gemfile.lock
+
+ARG BUNDLE_BUILD__SASSC=--disable-march-tune-native
+ARG NOKOGIRI_USE_SYSTEM_LIBRARIES=1
+
+RUN bundle config --global without development:test
+
+RUN bundle install --jobs=$(nproc) --retry=3
+
+RUN rm -rf \
+    /var/lib/gems/*/cache/ \
+    /var/lib/gems/*/test/ \
+    /var/lib/gems/*/extensions/*/*/*/gem_make.out \
+    /var/lib/gems/*/extensions/*/*/*/*.log \
diff --git a/Dockerfile.worker b/Dockerfile.worker
new file mode 100644
index 0000000000..0bb994b36a
--- /dev/null
+++ b/Dockerfile.worker
@@ -0,0 +1,28 @@
+ARG REGISTRY
+ARG TAG=latest
+FROM $REGISTRY/obs-backend-base:$TAG
+
+LABEL maintainer Andrej Shadura <andrew.shadura@collabora.co.uk>
+ARG DEBIAN_FRONTEND=noninteractive
+
+# TODO: cleanup
+RUN apt-get update \
+ && apt-get install -y \
+        binutils \
+        cpio \
+        curl \
+        debootstrap \
+        fdisk \
+        libarchive-tools \
+        lsb-base \
+        lvm2 \
+        lzma \
+        psmisc \
+        rpm
+
+COPY docker/services/worker/*.conf /etc/supervisor/conf.d/
+COPY docker/ /opt/
+
+# RUN /opt/configure-worker-user.sh
+
+ENTRYPOINT /opt/worker-docker-entrypoint.sh
diff --git a/docker-compose.yml b/docker-compose.yml
index 90aa950244..69ebc276a2 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,44 +1,72 @@
-version: "2.1"
+---
+version: '2'
+
 services:
-  db:
-    image: registry.opensuse.org/obs/server/unstable/container/leap151/containers/openbuildservice/mariadb
-    ports:
-      - "3306:3306"
-    command: /usr/lib/mysql/mysql-systemd-helper start
+  obs-db:
+    image: mariadb:10.6
+    restart: unless-stopped
+    environment:
+      MARIADB_ROOT_PASSWORD: someobs
+      MARIADB_DATABASE: obsapi
+      MARIADB_USER: obs-api
+      MARIADB_PASSWORD: someobs
+
   cache:
-    image: registry.opensuse.org/obs/server/unstable/container/leap151/containers/openbuildservice/memcached
-    ports:
-      - "11211:11211"
-    command: /usr/sbin/memcached -u memcached
-  backend:
-    image: registry.opensuse.org/obs/server/unstable/container/leap151/containers/openbuildservice/backend
-    volumes:
-      - .:/obs
-      - ./dist/aws_credentials:/etc/obs/cloudupload/.aws/config
-      - ./dist/ec2utils.conf:/etc/obs/cloudupload/.ec2utils.conf
-      - ./dist/clouduploader.rb:/usr/bin/clouduploader
-    command: /obs/contrib/start_development_backend -d /obs
-  worker:
-    image: registry.opensuse.org/obs/server/unstable/container/leap151/containers/openbuildservice/backend
+    image: memcached:1.6-alpine
+
+  obs-backend:
+    image: obs/obs-backend
+    hostname: obs-server
+    restart: unless-stopped
     volumes:
-      - .:/obs
-    privileged: true 
+      - backend-storage:/srv/obs
+      - backend-logs:/srv/obs/log
+    environment:
+      OBS_FRONTEND_HOST: obs-api
+
+  obs-frontend:
+    image: obs/obs-frontend
     depends_on:
-      - backend
-    command: /obs/contrib/start_development_worker
-  frontend:
-    image: openbuildservice/frontend
-    command: foreman start -p 3000
-    build:
-      dockerfile: docker-files/Dockerfile
-      context: src/api
+      - obs-server
     volumes:
-      - .:/obs
+      - frontend-logs:/obs/src/api/log
+      - type: tmpfs
+        target: /tmp
+        tmpfs:
+          size: 4G
+    hostname: obs-api
+    restart: unless-stopped
+    environment:
+      DB_HOST: obs-db
+      DB_PORT: 3306
+      DB_ROOT_PASSWORD: someobs
+      DB_NAME: obsapi
+      DB_USER: obs-api
+      DB_PASSWORD: someobs
+      OBS_BACKEND_HOST: obs-server
+      OBS_FRONTEND_WORKERS: 4
     ports:
-      - "3000:3000"
-      - "1080:1080"
+      - "127.0.0.1:3000:3000"
     depends_on:
-      - db
+      - obs-db
       - cache
-      - backend
-      - worker
+
+  worker:
+    depends_on:
+        - obs-backend
+    image: obs/obs-worker
+    hostname: worker
+    restart: unless-stopped
+    privileged: true
+    volumes:
+      - worker-logs:/srv/obs/log
+    environment:
+      OBS_SRC_SERVER: obs-server:5352
+      OBS_REPO_SERVERS: obs-server:5252
+      OBS_WORKER_INSTANCES: 1
+
+volumes:
+  backend-storage:
+  backend-logs:
+  frontend-logs:
+  worker-logs:
diff --git a/docker/backend-docker-entrypoint.sh b/docker/backend-docker-entrypoint.sh
new file mode 100755
index 0000000000..eae9ba3725
--- /dev/null
+++ b/docker/backend-docker-entrypoint.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+if [ -z "$OBS_FRONTEND_HOST" ]; then
+    echo >&2 'error: OBS server frontend is unavailable and hostname option'
+    echo >&2 'is not specified '
+    echo >&2 '  You need to specify OBS_FRONTEND_HOST'
+    exit 1
+fi
+
+HOSTNAME=$(hostname)
+
+if [ "$(stat -c %U /srv/obs/run)" != obsrun ]
+then
+    echo "OBS files owned by the wrong user $(stat -c %U /srv/obs/run), re-owning..."
+    time chown obsrun:obsrun -R /srv/obs
+fi
+
+if [ ! -f /etc/obs/BSConfig.pm ]
+then
+    echo "OBS backend configuration not found, starting from scratch"
+    cp /usr/lib/obs/server/BSConfig.pm.template /etc/obs/BSConfig.pm
+fi
+
+echo "Configure OBS backend host: ${HOSTNAME}"
+sed -i "s/hostname = .*/hostname = '${HOSTNAME}';/g" /etc/obs/BSConfig.pm
+
+echo "Configure OBS frontend host: ${OBS_FRONTEND_HOST}"
+sed -i "s/frontend = undef/frontend = '${OBS_FRONTEND_HOST}'/g" /etc/obs/BSConfig.pm
+
+for arch in ${OBS_ARCHES:-amd64 i686 armhf arm64}
+do
+    obsarch=$(/opt/deb-arch-to-obs-arch "$arch")
+    if [ -z "$obsarch" ]
+    then
+        echo Failed to enable unsupported architecture $arch >&2
+        continue
+    fi
+    for template in /opt/services/backend/*@.conf.in
+    do
+        conf=$(echo $(basename $template) | sed -e "s|@|@$arch|" -e 's|.in$||')
+        sed -e "s|@ARCH@|$obsarch|g" $template > /etc/supervisor/conf.d/$conf
+    done
+done
+
+mkdir -p /srv/obs/log
+chmod ug=rwxt /srv/obs/run
+
+/usr/bin/supervisord -n
diff --git a/docker/configure-app.sh b/docker/configure-app.sh
new file mode 100755
index 0000000000..acaf3074f6
--- /dev/null
+++ b/docker/configure-app.sh
@@ -0,0 +1,45 @@
+#!/bin/sh -x
+
+if [ -z "$OBS_BACKEND_HOST" ]; then
+    echo >&2 'error: server backend is unavailable and hostname option is not specified '
+    echo >&2 '  You need to specify OBS_BACKEND_HOST'
+    exit 1
+fi
+
+if [ ! -z "$OBS_BACKEND_HOST" ]; then
+    sed -i s/"source_host: localhost"/"source_host: ${OBS_BACKEND_HOST}"/g config/options.yml
+fi
+
+for d in log tmp db/sphinx
+do
+    mkdir -p $d
+    chown -R frontend $d
+done
+
+# Allow overriding the secret key
+if [ -f /run/secrets/secretkey ]
+then
+    ln -sf /run/secrets/secretkey config/secret.key
+fi
+
+if [ ! -r config/secret.key ]
+then
+    bundle exec rake secret > config/secret.key
+fi
+
+for d in options.yml thinking_sphinx.yml
+do
+    [ -r config/$d ] || cp config/$d.example config/$d
+done
+
+# Set up msmtp if a configuration is supplied
+if [ -f /run/secrets/msmtprc ]
+then
+    ln -sf /run/secrets/msmtprc /etc/msmtprc
+fi
+
+# Set up SSO auth if a configuration is supplied
+if [ -f /run/secrets/ssoauth ]
+then
+    ln -sf /run/secrets/ssoauth config/auth.yml
+fi
diff --git a/docker/configure-backend-user.sh b/docker/configure-backend-user.sh
new file mode 100755
index 0000000000..5b434692aa
--- /dev/null
+++ b/docker/configure-backend-user.sh
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+if ! getent group obsrun > /dev/null; then
+    addgroup --system --gid 999 obsrun
+fi
+
+if ! getent passwd obsrun > /dev/null; then
+    adduser --system --uid 999 \
+        --ingroup obsrun --shell /bin/false \
+        --home /usr/lib/obs --no-create-home obsrun
+    usermod -c "User for build service backend" obsrun
+fi
+
+if ! getent passwd obsservicerun > /dev/null; then
+    adduser --system --uid 998 \
+        --ingroup obsrun --shell /bin/false \
+        --home /usr/lib/obs/server --no-create-home obsservicerun
+    usermod -c "User for obs source service server" obsservicerun
+fi
+
+mkdir -p /srv/obs/repos
+chown obsrun:obsrun /srv/obs/repos
diff --git a/docker/configure-db.sh b/docker/configure-db.sh
new file mode 100755
index 0000000000..39feb5ee22
--- /dev/null
+++ b/docker/configure-db.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+if [ -z "$DB_HOST" -o -z "$DB_ROOT_PASSWORD" -o -z "$DB_NAME" -o -z "$DB_USER" -o -z "$DB_PASSWORD" ]; then
+    echo >&2 'error: database is uninitialized and password option is not specified or OBS'
+    echo >&2 '  You need to specify DB_HOST, DB_ROOT_PASSWORD, DB_NAME, DB_USER and DB_PASSWORD'
+    exit 1
+fi
+
+cat > config/database.yml <<EOF
+production:
+  adapter: mysql2
+  host: $DB_HOST
+  port: 3306
+  database: $DB_NAME
+  username: $DB_USER
+  password: $DB_PASSWORD
+  encoding: utf8mb4
+  collation: utf8mb4_unicode_ci
+  timeout: 15
+  pool: 30
+EOF
+
+rake() {
+    runuser -u frontend -- bundle exec rake "$@"
+}
+
+if ! rake db:migrate:status
+then
+    rake db:create || true
+    rake db:setup
+    rake writeconfiguration
+else
+    rake db:migrate:with_data
+fi
diff --git a/docker/configure-frontend-user.sh b/docker/configure-frontend-user.sh
new file mode 100755
index 0000000000..cd28b6af8d
--- /dev/null
+++ b/docker/configure-frontend-user.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+if ! getent group frontend > /dev/null; then
+    addgroup --system --gid 999 frontend
+fi
+
+if ! getent passwd frontend > /dev/null; then
+    adduser --system --uid 999 \
+        --ingroup frontend --shell /bin/false \
+        --home /obs --no-create-home frontend
+    usermod -c "User for build service frontend" frontend
+fi
diff --git a/docker/configure-sso.py b/docker/configure-sso.py
new file mode 100755
index 0000000000..13b373fa58
--- /dev/null
+++ b/docker/configure-sso.py
@@ -0,0 +1,38 @@
+#!/usr/bin/python3
+
+import yaml
+import os
+
+CONFIG_LOCATION='config/auth.yml'
+
+def parse_method(method: str):
+    for k, v in os.environ.items():
+        prefix = 'OBS_SSO_' + method.upper().replace('-', '_') + '_'
+        if k.startswith(prefix):
+            opt = k.replace(prefix, '').lower()
+            yield opt, v
+
+def reorder_options(options: dict):
+    new_options = {}
+    client_options = {}
+    for k, v in options.items():
+        if k.startswith('client_options_'):
+            client_options[k.replace('client_options_', '')] = v
+        else:
+            new_options[k] = v
+    if client_options:
+        new_options['client_options'] = client_options
+    return new_options
+
+def generate_yaml():
+    methods = os.environ['OBS_SSO_METHODS'].split()
+    config = {}
+    for method in methods:
+        options = reorder_options(dict(parse_method(method)))
+        config[method] = options
+    with open(CONFIG_LOCATION, 'w') as f:
+        yaml.safe_dump(config, stream=f)
+
+if __name__ == "__main__":
+    if os.environ.get('OBS_SSO_ENABLED') == 'true':
+        generate_yaml()
diff --git a/docker/deb-arch-to-obs-arch b/docker/deb-arch-to-obs-arch
new file mode 100755
index 0000000000..0d9d948781
--- /dev/null
+++ b/docker/deb-arch-to-obs-arch
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+eval "$(dpkg-architecture ${1:+--host-arch $1} --print-set 2>/dev/null)"
+
+case "${DEB_HOST_ARCH:-$1}" in
+    (i?86)
+        echo "i586"
+        ;;
+
+    (armel)
+        echo "armv5el"
+        ;;
+
+    (armhf)
+        echo "armv7hl"
+        ;;
+
+    # add any more special cases here
+
+    (*)
+        echo "$DEB_HOST_GNU_CPU"
+        ;;
+esac
+
diff --git a/docker/frontend-docker-entrypoint.sh b/docker/frontend-docker-entrypoint.sh
new file mode 100755
index 0000000000..326e89ce10
--- /dev/null
+++ b/docker/frontend-docker-entrypoint.sh
@@ -0,0 +1,16 @@
+#!/bin/sh -e
+
+cd /obs/src/api
+
+# Make sure there are no stale files from previous runs
+rm -rfv tmp/pids/*
+chown -R frontend log tmp
+
+/opt/configure-app.sh
+/opt/configure-db.sh
+#/opt/configure-sso.py
+
+sed -i -e /web:/d Procfile
+echo "web: bundle exec puma -p 3000 -w ${OBS_FRONTEND_WORKERS:-4}" >> Procfile
+
+runuser -u frontend foreman start
diff --git a/docker/services/backend/obsclouduploadserver.conf b/docker/services/backend/obsclouduploadserver.conf
new file mode 100644
index 0000000000..e996e3a3d0
--- /dev/null
+++ b/docker/services/backend/obsclouduploadserver.conf
@@ -0,0 +1,11 @@
+[program:obsclouduploadserver]
+command=/usr/lib/obs/server/bs_clouduploadserver
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/clouduploadserver.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obsclouduploadworker.conf b/docker/services/backend/obsclouduploadworker.conf
new file mode 100644
index 0000000000..0cf718623a
--- /dev/null
+++ b/docker/services/backend/obsclouduploadworker.conf
@@ -0,0 +1,11 @@
+[program:obsclouduploadworker]
+command=/usr/lib/obs/server/bs_clouduploadworker
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/clouduploadworker.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obsdispatcher.conf b/docker/services/backend/obsdispatcher.conf
new file mode 100644
index 0000000000..e5db264923
--- /dev/null
+++ b/docker/services/backend/obsdispatcher.conf
@@ -0,0 +1,11 @@
+[program:obsdispatcher]
+command=/usr/lib/obs/server/bs_dispatch
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/dispatcher.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obsdodup.conf b/docker/services/backend/obsdodup.conf
new file mode 100644
index 0000000000..e515320dc9
--- /dev/null
+++ b/docker/services/backend/obsdodup.conf
@@ -0,0 +1,11 @@
+[program:obsdodup]
+command=/usr/lib/obs/server/bs_dodup
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/dodup.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obspublisher.conf b/docker/services/backend/obspublisher.conf
new file mode 100644
index 0000000000..6f4f13fff0
--- /dev/null
+++ b/docker/services/backend/obspublisher.conf
@@ -0,0 +1,11 @@
+[program:obspublisher]
+command=/usr/lib/obs/server/bs_publish
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/publisher.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obsrepserver.conf b/docker/services/backend/obsrepserver.conf
new file mode 100644
index 0000000000..5c3d9b8a03
--- /dev/null
+++ b/docker/services/backend/obsrepserver.conf
@@ -0,0 +1,11 @@
+[program:obsrepserver]
+command=/usr/lib/obs/server/bs_repserver
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/rep_server.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obsscheduler@.conf.in b/docker/services/backend/obsscheduler@.conf.in
new file mode 100644
index 0000000000..ee70976d20
--- /dev/null
+++ b/docker/services/backend/obsscheduler@.conf.in
@@ -0,0 +1,11 @@
+[program:obsscheduler@@ARCH@]
+command=/usr/lib/obs/server/bs_sched @ARCH@
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/scheduler_@ARCH@.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obsservice.conf b/docker/services/backend/obsservice.conf
new file mode 100644
index 0000000000..3ddbf9f48e
--- /dev/null
+++ b/docker/services/backend/obsservice.conf
@@ -0,0 +1,11 @@
+[program:obsservice]
+command=/usr/lib/obs/server/bs_service
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/src_service.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obsservicedispatch.conf b/docker/services/backend/obsservicedispatch.conf
new file mode 100644
index 0000000000..08fc6f673f
--- /dev/null
+++ b/docker/services/backend/obsservicedispatch.conf
@@ -0,0 +1,11 @@
+[program:obsservice]
+command=/usr/lib/obs/server/bs_servicedispatch
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/servicedispatch.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obssrcserver.conf b/docker/services/backend/obssrcserver.conf
new file mode 100644
index 0000000000..f67ac08195
--- /dev/null
+++ b/docker/services/backend/obssrcserver.conf
@@ -0,0 +1,11 @@
+[program:obssrcserver]
+command=/usr/lib/obs/server/bs_srcserver
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/obssrcserver.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/backend/obswarden.conf b/docker/services/backend/obswarden.conf
new file mode 100644
index 0000000000..e6cec6706e
--- /dev/null
+++ b/docker/services/backend/obswarden.conf
@@ -0,0 +1,11 @@
+[program:obswarden]
+command=/usr/lib/obs/server/bs_warden
+directory=/usr/lib/obs/server/
+stdout_logfile=/srv/obs/log/warden.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+
diff --git a/docker/services/worker/obsworker.conf b/docker/services/worker/obsworker.conf
new file mode 100644
index 0000000000..1129707f4e
--- /dev/null
+++ b/docker/services/worker/obsworker.conf
@@ -0,0 +1,13 @@
+[program:obsworker]
+command=%(ENV_OBS_WORKER_PATH)s/bs_worker --hardstatus --root /var/cache/build/root_%(process_num)d --statedir /var/cache/build/state_%(process_num)d --id %(ENV_OBS_WORKER_NAME)s:%(process_num)d %(ENV_OBS_WORKER_OPT)s
+process_name=%(program_name)s_%(process_num)d
+directory=%(ENV_OBS_WORKER_PATH)s
+stdout_logfile=/srv/obs/log/worker_%(process_num)d.log
+redirect_stderr=true
+autostart=True
+priority=1
+stopsignal=KILL
+killasgroup=true
+stopasgroup=true
+numprocs=%(ENV_OBS_WORKER_INSTANCES)s
+numprocs_start=1
diff --git a/docker/worker-docker-entrypoint.sh b/docker/worker-docker-entrypoint.sh
new file mode 100755
index 0000000000..47027e23af
--- /dev/null
+++ b/docker/worker-docker-entrypoint.sh
@@ -0,0 +1,72 @@
+#!/bin/sh -ex
+
+obsrundir=/run/obs
+workerdir=/var/cache/build
+workerbootdir="$workerdir/boot"
+obslogdir=/var/log/obs
+
+: mkdir -p "$obsrundir"
+
+: ${OBS_REPO_SERVERS:=obs-server:5252}
+
+repo_param=
+for i in $OBS_REPO_SERVERS
+do
+    repo_param="$REPO_PARAM --reposerver http://$i"
+    WORKER_CODE="http://$i"
+done
+
+: ${OBS_WORKER_NICE_LEVEL:=18}
+
+OBS_WORKER_OPT="--hardstatus $repo_param ${OBS_WORKER_JOBS:+--jobs $OBS_WORKER_JOBS}\
+ ${OBS_WORKER_CLEANUP_CHROOT:+--cleanup-chroot}\
+ ${OBS_WORKER_WIPE_AFTER_BUILD:+--wipeafterbuild}\
+ ${OBS_SRC_SERVER:+--srcserver $OBS_SRC_SERVER}\
+ ${OBS_ARCH:+--arch $OBS_ARCH} ${OBS_WORKER_OPT}"
+
+export OBS_WORKER_OPT
+
+: ${OBS_WORKER_NAME:=$(hostname)}
+
+export OBS_WORKER_NAME
+
+: ${OBS_WORKER_INSTANCES:=$(nproc)}
+
+export OBS_WORKER_INSTANCES
+
+OBS_WORKER_PATH=/usr/lib/obs/server
+
+update_worker() {
+    echo "Fetching initial worker code from $WORKER_CODE/getworkercode"
+    mkdir -p "$workerbootdir"
+    cd "$workerbootdir"
+    for retry in $(seq 10)
+    do
+        if curl -sS "$WORKER_CODE/getworkercode" | cpio --extract
+        then
+            ln -sfn . XML
+            chmod 755 bs_worker
+            return 0
+        fi
+        # we need to wait for rep server maybe
+        echo "WARNING: Could not reach rep server $WORKER_CODE. Trying again." >&2
+        sleep 10
+    done
+    echo "ERROR: Unable to reach rep server $WORKER_CODE!" >&2
+    return 1
+}
+
+if [ -n "$WORKER_CODE" ]
+then
+    update_worker
+    OBS_WORKER_PATH="$workerbootdir"
+fi
+
+export OBS_WORKER_PATH
+
+for i in $(seq 1 $OBS_WORKER_INSTANCES)
+do
+    mkdir -p $workerdir/root_$i $workerdir/state_$i
+done
+
+nice -n "$OBS_WORKER_NICE_LEVEL" /usr/bin/supervisord -n
diff --git a/src/api/config/environments/production.rb b/src/api/config/environments/production.rb
index bf5bc55254..72d510d209 100644
--- a/src/api/config/environments/production.rb
+++ b/src/api/config/environments/production.rb
@@ -37,7 +37,7 @@ OBSApi::Application.configure do
   config.action_controller.perform_caching = true
 
   # Disable Rails's static asset server (Apache or nginx will already do this)
-  config.public_file_server.enabled = false
+  config.public_file_server.enabled = true
 
   # Compress JavaScripts and CSS
   config.assets.compress = true
diff --git a/src/api/config/options.yml.example b/src/api/config/options.yml.example
index bc98f94f98..1a13f782fc 100644
--- a/src/api/config/options.yml.example
+++ b/src/api/config/options.yml.example
@@ -199,6 +199,7 @@ default: &default
 
 production:
   <<: *default
+  memcached_host: cache
 
 test:
   <<: *default
@@ -209,4 +210,3 @@ development:
   <<: *default
   source_host: backend
   memcached_host: cache
-
diff --git a/src/api/db/migrate/20191011000000_create_allowbuilddeps.rb b/src/api/db/migrate/20191011000000_create_allowbuilddeps.rb
index f4de579749..acf2f28a56 100644
--- a/src/api/db/migrate/20191011000000_create_allowbuilddeps.rb
+++ b/src/api/db/migrate/20191011000000_create_allowbuilddeps.rb
@@ -1,4 +1,4 @@
-class CreateAllowbuilddeps < ActiveRecord::Migration
+class CreateAllowbuilddeps < ActiveRecord::Migration[4.2]
   def self.up
     create_table :allowbuilddeps do |t|
       t.integer :db_project_id, :null => false
-- 
GitLab