Prepare the infrastructure

Requirements

  • CI system;

  • Linux host to run the CI jobs, featuring:

Setting up the build environment with Buildah

(For Ubuntu 23.10 and later) on the GitLab Runner host run:

{ echo "kernel.apparmor_restrict_unprivileged_userns = 0" && echo "kernel.apparmor_restrict_unprivileged_unconfined = 0";} | sudo tee -a /etc/sysctl.d/20-apparmor-donotrestrict.conf && sudo sysctl -p /etc/sysctl.d/20-apparmor-donotrestrict.conf

Configuring the Runner

Create the werf volume on the host where CI jobs are run:

docker volume create werf

Configure your CI system’s Runner so that the containers you create have the following parameters:

  • --security-opt seccomp:unconfined;

  • --security-opt apparmor:unconfined;

  • --volume werf:/home/build/.werf.

If the host to run CI jobs has Linux kernel version 5.12 or lower, install fuse on the host and configure Runner so that the containers you create have the optional parameter --device /dev/fuse.

Configuring the container registry

Enable garbage collection for your container registry.

Preparing the system for cross-platform building (optional)

This step only needed to build images for platforms other than host platform running werf.

Register emulators on your system using qemu-user-static:

docker run --restart=always --name=qemu-user-static -d --privileged --entrypoint=/bin/sh multiarch/qemu-user-static -c "/register --reset -p yes && tail -f /dev/null"

Configure the project

Configuring CI/CD of the project

This is how the repository that uses werf for build and deploy might look:

.helm
app
pseudo-ci-cd-config.yaml
werf.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: app
spec:
  selector:
    matchLabels:
      app: app
  template:
    metadata:
      labels:
        app: app
    spec:
      containers:
      - name: app
        image: {{ .Values.werf.image.app }}

apiVersion: v1
kind: Service
metadata:
  name: app
spec:
  selector:
    app: app
  ports:
  - name: app
    port: 80

FROM node

WORKDIR /app
COPY . .
RUN npm ci

CMD ["node", "server.js"]

{
  "name": "app",
  "version": "1.0.0",
  "lockfileVersion": 2,
  "requires": true,
  "packages": {
    "": {
      "name": "app",
      "version": "1.0.0"
    }
  }
}

{
  "name": "app",
  "version": "1.0.0",
  "main": "server.js",
  "scripts": {
    "start": "node server.js"
  }
}

const http = require('http');

const hostname = '127.0.0.1';
const port = 80;

const server = http.createServer((req, res) => {
  res.statusCode = 200;
  res.setHeader('Content-Type', 'text/plain');
  res.end('Hello World');
});

server.listen(port, hostname, () => {
  console.log(`Server running at http://${hostname}:${port}/`);
});

image: "registry.werf.io/werf/werf:2-stable"
image_pull_policy: always

environment_variables:
  WERF_REPO: registry.example.org/myrepo
  WERF_ENV: "${CI_ENVIRONMENT}"
  WERF_ENABLE_PROCESS_EXTERMINATOR: "1"

before_every_job:
  - werf cr login -u "${REGISTRY_USER:?}" -p "${REGISTRY_PASSWORD:?}" "${WERF_REPO:?}"

jobs:
  prod:
    commands:
      - werf converge
    environment: prod
    on: master
    how: manually

  images:cleanup:
    commands:
      - werf cleanup
    on: master
    how: daily

configVersion: 1
project: myproject
---
image: app
dockerfile: Dockerfile
context: ./app

Extras:

  • Add authorization options for werf cleanup in the container registry by following instructions.