mirror of
https://github.com/getlago/lago.git
synced 2025-07-04 07:18:04 +07:00
feat(docker): Single docker image (#464)
* 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
This commit is contained in:
116
.github/workflows/release-docker-image.yml
vendored
Normal file
116
.github/workflows/release-docker-image.yml
vendored
Normal file
@ -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
docker/Dockerfile
Normal file
67
docker/Dockerfile
Normal file
@ -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
docker/Procfile
Normal file
3
docker/Procfile
Normal file
@ -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
docker/README.md
Normal file
37
docker/README.md
Normal file
@ -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
docker/nginx.conf
Normal file
12
docker/nginx.conf
Normal file
@ -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
docker/redis.conf
Normal file
75
docker/redis.conf
Normal file
@ -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
docker/runner.sh
Executable file
72
docker/runner.sh
Executable file
@ -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
scripts/bootstrap.sh
Executable file
7
scripts/bootstrap.sh
Executable file
@ -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
|
7
scripts/pg-init-scripts/bootstrap.sh
Normal file
7
scripts/pg-init-scripts/bootstrap.sh
Normal file
@ -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 build-essential nodejs npm
|
Reference in New Issue
Block a user