Skip to content

Commit

Permalink
feat(docker): Single docker image (#464)
Browse files Browse the repository at this point in the history
* add a new docker image

* add api

* add pg, redis and foreman

* add ga

* tag with latest

* update api

* right tag on submodules

* use one run
  • Loading branch information
jdenquin authored Feb 12, 2025
1 parent cc70c2e commit 52ab3b3
Show file tree
Hide file tree
Showing 9 changed files with 396 additions and 0 deletions.
116 changes: 116 additions & 0 deletions .github/workflows/release-docker-image.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
name: "Release Single Docker Image"
on:
release:
types: [released]
workflow_dispatch:
inputs:
version:
description: Version
required: true
env:
REGISTRY_IMAGE: getlago/lago
jobs:
build-single-docker-image:
strategy:
matrix:
platform:
- version: linux/amd64
runner: linux/amd64
- version: linux/arm64
runner: linux-arm64
name: Build ${{ matrix.platform.version }} Image
runs-on: ${{ matrix.platform.runner }}
steps:
- name: Prepare
run: |
platform=${{ matrix.platform.version }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- uses: actions/checkout@v4
- name: Docker Meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
tags: |
type=raw,value=${{ github.event_name == 'release' && github.event.release.tag_name || github.event.inputs.version }}
type=raw,value=latest
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
with:
version: latest
- name: Log In to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Add version into docker image
id: add_version
run: |
echo "${{ github.event_name == 'release' && github.event.release.tag_name || github.event.inputs.version }}" > LAGO_VERSION
- name: Build and push Docker image
uses: docker/build-push-action@v6
id: build
with:
context: .
file: ./docker/Dockerfile
platforms: ${{ matrix.platform.version }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,name=${{ env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
build-args: |
SEGMENT_WRITE_KEY=${{ secrets.SEGMENT_WRITE_KEY }}
- name: Export Digest
run: |
mkdir -p ./_tmp/${{ github.run_id }}/${{ github.run_attempt }}/digests
digest="${{ steps.build.outputs.digest }}"
touch "./_tmp/${{ github.run_id }}/${{ github.run_attempt }}/digests/${digest#sha256:}"
- name: Upload Digest
uses: actions/upload-artifact@v4
with:
name: digests-${{ env.PLATFORM_PAIR }}
path: ./_tmp/${{ github.run_id }}/${{ github.run_attempt }}/digests/*
if-no-files-found: error
retention-days: 1
- name: Clean up
if: always()
run: |
[ -e ./_tmp/${{ github.run_id }}/${{ github.run_attempt }}/digests ] && \
rm -rf ./_tmp/${{ github.run_id }}/${{ github.run_attempt }}/digests
merge:
name: Merge Images
runs-on: lago-runner
needs: [build-images]
steps:
- name: Download Digests
uses: actions/download-artifact@v4
with:
path: ./_tmp/${{ github.run_id}}/${{ github.run_attempt }}/digests
pattern: digests-*
merge-multiple: true
- name: Docker meta
id: meta
uses: docker/metadata-action@v5
with:
images: ${{ env.REGISTRY_IMAGE }}
tags: |
type=raw,value=${{ github.event_name == 'release' && github.event.release.tag_name || github.event.inputs.version }}
type=raw,value=latest
- name: Set up Docker buildx
uses: docker/setup-buildx-action@v3
- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}
- name: Create manifest and push
working-directory: ./_tmp/${{ github.run_id }}/${{ github.run_attempt}}/digests
run: |
docker buildx imagetools create $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \
$(printf '${{ env.REGISTRY_IMAGE }}@sha256:%s ' *)
- name: Inspect Image
run: |
docker buildx imagetools inspect ${{ env.REGISTRY_IMAGE }}:${{ steps.meta.outputs.version }}
- name: Clean up
if: always()
run: |
[ -e ./_tmp/${{ github.run_id }}/${{ github.run_attempt }}/digests ] && \
rm -rf ./_tmp/${{ github.run_id }}/${{ github.run_attempt }}/digests
67 changes: 67 additions & 0 deletions docker/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
ARG NODE_VERSION=20
ARG RUBY_VERSION=3.3.6

# Front Build
FROM node:$NODE_VERSION-alpine AS front_build

WORKDIR /app

COPY ./front/ .

RUN apk add python3 build-base
RUN yarn && yarn build && npm prune --omit=dev

# API Build
FROM ruby:$RUBY_VERSION-slim AS api_build

ENV BUNDLER_VERSION='2.5.5'
ENV PATH="$PATH:/root/.cargo/bin/"

WORKDIR /app

RUN apt-get update && apt-get upgrade -y && \
apt-get install nodejs curl build-essential git pkg-config libpq-dev libclang-dev curl -y && \
curl https://sh.rustup.rs -sSf | bash -s -- -y

COPY ./api/Gemfile /app/Gemfile
COPY ./api/Gemfile.lock /app/Gemfile.lock

RUN gem install bundler --no-document -v '2.5.5' && \
gem install foreman && \
bundle config build.nokogiri --use-system-libraries &&\
bundle install --jobs=3 --retry=3 --without development test

# Final Image
FROM ruby:$RUBY_VERSION-slim

WORKDIR /app

RUN apt-get update && apt-get upgrade -y && \
apt-get install curl ca-certificates gnupg software-properties-common -y && \
curl -fsSL https://postgresql.org/media/keys/ACCC4CF8.asc | gpg --dearmor | tee /usr/share/keyrings/postgresql-archive-keyring.gpg > /dev/null && \
echo deb [arch=amd64,arm64,ppc64e1 signed-by=/usr/share/keyrings/postgresql.gpg] http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main | tee /etc/ap && \
curl -fsSL https://packages.redis.io/gpg | gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg && \
chmod 644 /usr/share/keyrings/redis-archive-keyring.gpg &&\
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb $(lsb_release -cs) main" | tee /etc/apt/sources.list.d/redis.list && \
apt-get update && \
apt-get install nginx xz-utils git libpq-dev postgresql-15 redis -y && \
apt-get remove software-properties-common apt-transport-https -y

COPY ./docker/nginx.conf /etc/nginx/sites-enabled/default
COPY ./docker/redis.conf /etc/redis/redis.conf

COPY --from=front_build /app/dist /app/front
COPY --from=api_build /usr/local/bundle/ /usr/local/bundle

COPY ./front/.env.sh ./front/.env.sh
COPY ./api ./api
COPY ./docker/Procfile ./api/Procfile
COPY ./docker/runner.sh ./runner.sh

ENV SEGMENT_WRITE_KEY=$SEGMENT_WRITE_KEY

EXPOSE 80
EXPOSE 3000
VOLUME /data

ENTRYPOINT ["./runner.sh"]
3 changes: 3 additions & 0 deletions docker/Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
web: bundle exec rails s -b :: -p 3000
worker: bundle exec sidekiq -C config/sidekiq/sidekiq.yml
clock: bundle exec clockwork ./clock.rb
37 changes: 37 additions & 0 deletions docker/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# Lago Docker Image

This is the official one docker image for Lago.
We do not recommend to use it in production for heavy usage, please check the `docker-compose` file in the root of the repository
or our [helm chart](https://github.com/getlago/lago-helm-charts) for a more robust deployment.

## Features

This docker image embed everything to run Lago with just one command line to ease the deployment.
Here are the services that are running into the container :
- PostgreSQL
- Redis
- Lago UI
- Lago API
- Lago Worker
- Lago Clock

## Get Started

```bash
docker run -d --name lago-p 80:80 -p 3000:3000 getlago/lago:latest
```

## Storage

The container is using a volume to store the data, you can mount it to your host to keep the data safe.
You can find many folders for each services in the `/data` folder.

## Logs

Database Logs (creation, migration) are stored in the `/data/db.log` file.
Applicative logs are streamed to the standard output.

## Contributing

This docker image is a work in progress, this does not provide a lot of features yet (ei: external database configuration).
Feel free to open issues or PRs to improve it or ask for new features.
12 changes: 12 additions & 0 deletions docker/nginx.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
server {
listen 80;
listen [::]:80;

location / {
root /app/front;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;
}

include /etc/nginx/extra-conf.d/*.conf;
}
75 changes: 75 additions & 0 deletions docker/redis.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
bind 127.0.0.1 -::1
protected-mode yes
port 6379
tcp-backlog 511
timeout 0
tcp-keepalive 300
daemonize yes
supervised auto
pidfile /run/redis/redis-server.pid
loglevel notice
logfile /var/log/redis/redis-server.log
databases 16
always-show-logo no
set-proc-title yes
proc-title-template "{title} {listen-addr} {server-mode}"
locale-collate ""
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
dbfilename dump.rdb
rdb-del-sync-files no
dir /data/redis
replica-serve-stale-data yes
replica-read-only yes
repl-diskless-sync yes
repl-diskless-sync-delay 5
repl-diskless-sync-max-replicas 0
repl-diskless-load disabled
repl-disable-tcp-nodelay no
replica-priority 100
acllog-max-len 128
lazyfree-lazy-eviction no
lazyfree-lazy-expire no
lazyfree-lazy-server-del no
replica-lazy-flush no
lazyfree-lazy-user-del no
lazyfree-lazy-user-flush no
oom-score-adj no
oom-score-adj-values 0 200 800
disable-thp yes
appendonly yes
appendfilename "appendonly.aof"
appenddirname "appendonlydir"
appendfsync everysec
no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
aof-timestamp-enabled no
slowlog-log-slower-than 10000
slowlog-max-len 128
latency-monitor-threshold 0
notify-keyspace-events ""
hash-max-listpack-entries 512
hash-max-listpack-value 64
list-max-listpack-size -2
list-compress-depth 0
set-max-intset-entries 512
set-max-listpack-entries 128
set-max-listpack-value 64
zset-max-listpack-entries 128
zset-max-listpack-value 64
hll-sparse-max-bytes 3000
stream-node-max-bytes 4096
stream-node-max-entries 100
activerehashing yes
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit replica 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
dynamic-hz yes
aof-rewrite-incremental-fsync yes
rdb-save-incremental-fsync yes
jemalloc-bg-thread yes
72 changes: 72 additions & 0 deletions docker/runner.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/bin/bash

declare -A ENV_VARS=(
[POSTGRES_PASSWORD]=$(openssl rand -hex 16)
[SECRET_KEY_BASE]=$(openssl rand -base64 16)
[LAGO_RSA_PRIVATE_KEY]=$(openssl genrsa 2048 | base64 | tr -d '\n')
[REDIS_URL]="redis://localhost:6379/0"
[LAGO_FRONT_URL]="http://localhost"
[LAGO_API_URL]="http://localhost:3000"
[API_URL]="http://localhost:3000"
[APP_ENV]="production"
)

if [ -f "/data/.env" ]; then
for LINE in $(cat /data/.env); do export $LINE; done
fi

# Configure data directories
if [ -z "${DATA_DIR}" ]; then
export DATA_DIR=/data
mkdir -p ${DATA_DIR}
mkdir -p ${DATA_DIR}/redis
chown redis:redis ${DATA_DIR}/redis
mkdir -p ${DATA_DIR}/postgresql
touch ${DATA_DIR}/db.log
touch ${DATA_DIR}/.env
echo "DATA_DIR=${DATA_DIR}" >> ${DATA_DIR}/.env
fi

# Configure Redis
sed -i "s#DATA_DIR#${DATA_DIR}#g" /etc/redis/redis.conf

# Configure PG
export PGDATA="${DATA_DIR}/postgresql"
export PGPORT=5432

# Start Redis, PG and Nginx
service redis-server restart >> /dev/null
service postgresql restart >> /dev/null
service nginx restart >> /dev/null

# Prepare Environment
# Defaulting values
for VAR in "${!ENV_VARS[@]}"; do
if [ -z "${!VAR}" ]; then
export $VAR=${ENV_VARS[$VAR]}
echo "$VAR=${ENV_VARS[$VAR]}" >> ${DATA_DIR}/.env
fi
done

if [ -z "${DATABASE_URL}" ]; then
export DATABASE_URL=postgresql://lago:$POSTGRES_PASSWORD@localhost:5432/lago
echo "DATABASE_URL=${DATABASE_URL}" >> ${DATA_DIR}/.env
fi

# Prepare Front Environment
cd ./front
bash -c ./.env.sh
cd ..

export RAILS_ENV=production

# Create DB User
su -c "psql -tc \"SELECT 1 FROM pg_user WHERE usename = 'lago';\" | grep -q 1 || psql -c \"CREATE ROLE lago PASSWORD '${POSTGRES_PASSWORD}' CREATEDB LOGIN;\"" postgres >> ${DATA_DIR}/db.log

# Launch BE Services
cd ./api
bundle exec rake db:create >> ${DATA_DIR}/db.log
bundle exec rake db:migrate >> ${DATA_DIR}/db.log
bundle exec rails signup:seed_organization >> ${DATA_DIR}/db.log
rm -f ./tmp/pids/server.pid
foreman start
7 changes: 7 additions & 0 deletions scripts/bootstrap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#! /bin/sh

apt update
apt install -y git curl
curl -sL https://deb.nodesource.com/setup_20.x | sh -
apt update
apt install nodejs npm
Loading

0 comments on commit 52ab3b3

Please sign in to comment.