Docker

Docker-related notes section

Dockerfile

Dockerfiles for several technologies

Golang

Optimized Dockerfile

# Build container
FROM golang:1.21.1-bookworm AS build

# Set build workdir
WORKDIR /app

# Copy app sources
COPY . .

# Build app
RUN go build -o app .

# ---
# Production container
FROM debian:bookworm-slim

# Set app workdir
WORKDIR /app

# Copy binary
COPY --from=build /app/app .

# Run app
CMD ["./app"]

Common Dockerfile

FROM golang:1.21.1-bookworm

# Set app workdir
WORKDIR /go/src/app

# Copy dependencies list
COPY go.mod go.sum ./

# Download dependencies
RUN go mod download

# Copy application sources
COPY . .

# Build app
RUN go build -o app .

# Run app
CMD ["./app"]

NodeJS

Static Site (Webpack)

Dockerfile

# Build container
FROM node:18.18.0-bookworm-slim AS build

# Set build workdir
WORKDIR /usr/src/app

# Copy dependencies list
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy app sources
COPY . .

# Run tests
RUN CI=true npm run test

# Build app
RUN npm run build

# ---
# Production container
FROM nginx:1.25.2-alpine

# Copy nginx.conf
COPY --from=build /usr/src/app/nginx.conf /etc/nginx/conf.d/default.conf

# Copy static files
COPY --from=build /usr/src/app/build/ /usr/share/nginx/html/

nginx.conf

server { 
    listen 80;
    server_name _ default_server;
    location / {
        root /usr/share/nginx/html;
        try_files $uri /index.html;
    }
}

Common Dockerfile

FROM node:18.18.0-bookworm-slim

# Set app workdir
WORKDIR /usr/src/app

# Copy dependencies list
COPY package*.json ./

# Install dependencies
RUN npm ci

# Copy app sources
COPY . .

# Run tests
RUN CI=true npm run test

# Run app
CMD ["npm", "run", "production"]

Python

Common Dockerfile

FROM python:3.9.18

# Stdout without buffer
ENV PYTHONUNBUFFERED=1

# Set app workdir
WORKDIR /usr/src/app

# Copy dependencies list
COPY ./requirements.txt .

# Install dependencies
RUN pip install --no-cache-dir -r requirements.txt

# Copy app sources
COPY . .

# Run app
CMD ["python", "app.py"]

Flutter

Flutter Web App

Dockerfile

# Build container
FROM cirrusci/flutter:2.8.1 AS build

# Set build workdir
WORKDIR /usr/src/app

# Copy app sources
COPY . .

# Build app
RUN flutter build web -t lib/main.dart

# ---
# Production container
FROM nginx:1.25.2-alpine

# Copy nginx.conf
COPY --from=build /usr/src/app/nginx.conf /etc/nginx/conf.d/default.conf

# Copy static files
COPY --from=build /usr/src/app/build/web/ /usr/share/nginx/html/

nginx.conf

server { 
    listen 80;
    server_name _ default_server;
    location / {
        root /usr/share/nginx/html;
        try_files $uri /index.html;
    }
}

MdBook

Dockerfile

# Build container
FROM debian:bullseye-slim AS build

# Set build workdir
WORKDIR /usr/src/app

# Install wget
RUN apt update && apt install -y wget

# Download mdBook
RUN wget -O mdbook.tar.gz -- https://github.com/rust-lang/mdBook/releases/download/v0.4.35/mdbook-v0.4.35-x86_64-unknown-linux-gnu.tar.gz && tar -xvf mdbook.tar.gz

# Copy sources
COPY . .

# Build book
RUN ./mdbook build

# ---
# Production container
FROM nginx:1.25.2-alpine

# Copy static files
COPY --from=build /usr/src/app/book/ /usr/share/nginx/html/

Docker-compose

Examples of compose files for several technologies

PostgreSQL

version: "3.3"

services:
  postgres:
    image: postgres:16.0
    restart: always
    environment:
      POSTGRES_DB: "dbname"
      POSTGRES_PASSWORD: "superpassword"
    ports:
      - "127.0.0.1:5432:5432"
    volumes:
      - "./data:/var/lib/postgresql/data"

*Replace dbname and superpassword with your values

MongoDB

version: "3.3"

services:
  mongo:
    image: mongo:4.4
    restart: always
    environment:
      MONGO_INITDB_DATABASE: "dbname"
      MONGO_INITDB_ROOT_USERNAME: "root"
      MONGO_INITDB_ROOT_PASSWORD: "superpassword"
    ports:
      - "127.0.0.1:27017:27017"
    volumes:
      - "./data:/data/db"

*Replace dbname and superpassword with your values

Redis

version: "3.3"

services:
  redis:
    image: redis:7.2.1
    restart: always
    ports:
      - "127.0.0.1:6379:6379"
    volumes:
      - "./data:/var/lib/redis"

RabbitMQ

docker-compose.yml

version: "3.3"
services:
  rabbitmq:
    image: rabbitmq:management-alpine
    ports:
      - 127.0.0.1:5672:5672
      - 127.0.0.1:15672:15672
    restart: always
    volumes:
      - rabbitmq_data:/var/lib/rabbitmq/
      - ./rabbitmq.conf:/etc/rabbitmq/conf.d/rabbitmq.conf


volumes:
  rabbitmq_data:

rabbitmq.conf Example

default_user = admin
default_pass = superpassword

*Replace admin and superpassword with your values

Gitlab Runner Installation

docker-compose.yml

version: "3.3"

services:
  runner:
    image: gitlab/gitlab-runner
    restart: always
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"
      - "./config.toml:/etc/gitlab-runner/config.toml"

config.toml Docs

[[runners]]
name = "Runner Name"
url = "https://gitlab.com/"
token = "supersecrettoken"
executor = "docker"
concurrent = 10
[runners.docker]
tls_verify = false
image = "docker"
privileged = true
disable_cache = false
volumes = ["/certs/client", "/cache"]
[runners.cache]
[runners.cache.s3]
[runners.cache.gcs]

*Replace supersecrettoken with your value

Gitea Installation

version: "3.3"

services:
  server:
    image: gitea/gitea
    container_name: gitea
    environment:
      - USER_UID=1001
      - USER_GID=1001
    restart: always
    volumes:
      - ./gitea:/data
      - /etc/timezone:/etc/timezone:ro
      - /etc/localtime:/etc/localtime:ro
      - /home/git/.ssh/:/data/git/.ssh
    ports:
      - "127.0.0.1:3000:3000"
      - "127.0.0.1:2222:22"

Drone CI Installation (Gitea)

version: "3.3"

services:
  drone:
    image: drone/drone
    restart: always
    environment:
      DRONE_GITEA_CLIENT_ID: "gitea_oauth_client_id"
      DRONE_GITEA_CLIENT_SECRET: "gitea_oauth_client_secret"
      DRONE_GITEA_SERVER: "https://gitea.example.com"
      DRONE_RPC_SECRET: "drone_rpc_secret"
      DRONE_SERVER_HOST: "drone.example.com"
      DRONE_SERVER_PROTO: "https"
      DRONE_USER_CREATE: "username:russia9,admin:true"
    ports:
      - "127.0.0.1:3001:80"
    volumes:
      - "./data:/data"

  drone-runner:
    image: drone/drone-runner-docker
    restart: always
    environment:
      DRONE_RUNNER_NAME: "runner-01"
      DRONE_RUNNER_CAPACITY: "10"
      DRONE_RPC_SECRET: "drone_rpc_secret"
      DRONE_RPC_HOST: "drone.example.com"
      DRONE_RPC_PROTO: "https"
    volumes:
      - "/var/run/docker.sock:/var/run/docker.sock"

*Replace drone_*, gitea_* and *.example.com with your values

GitLab

GitlabCI-related notes section

Build Docker image

stages:
  - build

services:
  - docker:20.10.12-dind

variables:
  TAG_LATEST: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:latest
  TAG_COMMIT: $CI_REGISTRY_IMAGE/$CI_COMMIT_REF_NAME:$CI_COMMIT_SHORT_SHA

build:
  image: docker:20.10.12
  stage: build

  before_script:
    # Login to GitLab Registry
    - echo "$CI_REGISTRY_PASSWORD" | docker login -u "$CI_REGISTRY_USER" --password-stdin $CI_REGISTRY
    # Pull last image to use cache
    - docker pull "$TAG_LATEST" || true
  script:
    # Build Image
    - docker build --cache-from "$TAG_LATEST" -t $TAG_COMMIT -t $TAG_LATEST .
    # Push to GitLab Registry
    - docker push $TAG_COMMIT
    - docker push $TAG_LATEST

Run SSH command

stages:
  # ...
  - deploy

deploy:
  stage: deploy
  before_script:
    - chmod 600 $SSH_KEY
    - apk update && apk add openssh-client
  script:
    - ssh -i $SSH_KEY -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST "command1"
    - ssh -i $SSH_KEY -o StrictHostKeyChecking=no $SSH_USER@$SSH_HOST "command2"
#  only:
#    - master
#  environment: production

CI Variables

SSH_KEY

Type: File
\n at the end of the file!

SSH_HOST

Type: Variable

SSH_USER

Type: Variable

Deploy to Kubernetes via Helm

stages:
  # ...
  - deploy

deploy:
  stage: deploy
  image: alpine/helm:3.7.2
  script:
    # Update Chart Version
    - "sed -i \"s/^appVersion:.*$/appVersion: $CI_COMMIT_SHORT_SHA/\" chart/Chart.yaml"
    # Deploy
    - helm upgrade $CI_PROJECT_NAME chart --install --namespace=$KUBERNETES_NAMESPACE --kubeconfig=$KUBERNETES_CONFIG
#  only:
#    - master
#  environment: production

CI Variables

KUBERNETES_CONFIG

Type: File

KUBERNETES_NAMESPACE

Type: Variable

Drone CI

Build Docker Image

kind: pipeline
type: docker
name: build

steps:
  - name: build
    image: plugins/docker
    settings:
      registry: registry.gitlab.com
      username:
        from_secret: docker_username
      password:
        from_secret: docker_password
      repo: registry.gitlab.com/USER/REPO/${DRONE_COMMIT_BRANCH}
      tags:
        - latest
        - ${DRONE_COMMIT_SHA}
      cache_from:
        - registry.gitlab.com/USER/REPO/${DRONE_COMMIT_BRANCH}:latest

*Replace USER and REPO by your values

CI Variables

docker_username

docker_password

Run SSH command

kind: pipeline
type: docker
name: deploy

clone:
  disable: true

steps:
  - name: deploy
    image: appleboy/drone-ssh
    settings:
      host:
        from_secret: ssh_address
      username:
        from_secret: ssh_username
      key:
        from_secret: ssh_key
      port: 22
      script_stop: true
      script:
        - command1
        - command2

trigger:
  branch:
    - master
  event:
    - push

CI Variables

ssh_key

\n at the end of the file!

ssh_address

ssh_username

Bitbucket

Bitbucket-related notes section

Run SSH command

image: atlassian/default-image:latest

pipelines:
  branches:
    master:
      - step:
          deployment: production
          clone:
            enabled: false
          script:
            - echo "command1" | ssh $SSH_USER@$SSH_HOST
            - echo "command2" | ssh $SSH_USER@$SSH_HOST

CI Variables

SSH_HOST

SSH_USER

Copy SSH pubkey to server (Repo Settings/Pipelines/SSH keys)

GitHub Actions

Actions-related notes section

Build Docker Image

name: Docker

on: [ push ]

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

jobs:
  build:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write

    steps:
      - name: Checkout repository
        uses: actions/checkout@v2

      - name: Log into registry ${{ env.REGISTRY }}
        if: github.event_name != 'pull_request'
        uses: docker/[email protected]
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract Docker metadata
        id: meta
        uses: docker/[email protected]
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

      - name: Build and push Docker image
        uses: docker/[email protected]
        with:
          context: .
          push: ${{ github.event_name != 'pull_request' }}
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}

Run SSH command

name: Docker

on: [ push ]

jobs:
  build:
    runs-on: ubuntu-latest

    steps:
      # ...
      
      - name: ssh-pipeline
        uses: cross-the-world/[email protected]
        with:
          host: ${{ secrets.SSH_HOST }}
          port: ${{ secrets.SSH_PORT }}
          user: ${{ secrets.SSH_USER }}
          key: ${{ secrets.SSH_KEY }}
          script: |
            command1
            command2

Secrets

  • SSH_HOST
  • SSH_PORT
  • SSH_USER
  • SSH_KEY

Deploy to GitHub Pages

name: Github Pages

on: [ push ]

jobs:
  pages:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/[email protected]

      - name: Build HTML
        run: |
          npm ci
          npm build

      - name: Deploy
        uses: JamesIves/[email protected]
        with:
          branch: gh-pages
          folder: build