Skip to content

Commit

Permalink
Add docker image that uses nginx unit instead of gunicorn
Browse files Browse the repository at this point in the history
🦄🔫
  • Loading branch information
frankh committed Sep 21, 2023
1 parent b14be47 commit 88ccf4e
Show file tree
Hide file tree
Showing 5 changed files with 251 additions and 1 deletion.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@
!plugin-server/.prettierrc
!share/GeoLite2-City.mmdb
!hogvm/python
!unit.json
12 changes: 11 additions & 1 deletion .github/workflows/container-images-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,17 @@ jobs:
with:
buildx-fallback: false # the fallback is so slow it's better to just fail
push: true
tags: posthog/posthog:${{github.sha}},posthog/posthog:latest,${{ steps.aws-ecr.outputs.registry }}/posthog-cloud:master
tags: posthog/posthog:${{ github.sha }},posthog/posthog:latest,${{ steps.aws-ecr.outputs.registry }}/posthog-cloud:master
platforms: linux/arm64,linux/amd64

- name: Build and push unit container image
id: build
uses: depot/build-push-action@v1
with:
buildx-fallback: false # the fallback is so slow it's better to just fail
push: true
file: production-unit.Dockerfile
tags: ${{ steps.aws-ecr.outputs.registry }}/posthog-cloud:unit
platforms: linux/arm64,linux/amd64

- name: get deployer token
Expand Down
13 changes: 13 additions & 0 deletions bin/docker-server-unit
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#!/bin/bash
set -e

# To ensure we are able to expose metrics from multiple processes, we need to
# provide a directory for `prometheus_client` to store a shared registry.
export PROMETHEUS_MULTIPROC_DIR=$(mktemp -d)
chmod -R 777 $PROMETHEUS_MULTIPROC_DIR
trap 'rm -rf "$PROMETHEUS_MULTIPROC_DIR"' EXIT

export PROMETHEUS_METRICS_EXPORT_PORT=8001
export STATSD_PORT=${STATSD_PORT:-8125}

exec /usr/local/bin/docker-entrypoint.sh unitd --no-daemon
210 changes: 210 additions & 0 deletions production-unit.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#
# This Dockerfile is used for self-hosted production builds.
#
# PostHog has sunset support for self-hosted K8s deployments.
# See: https://posthog.com/blog/sunsetting-helm-support-posthog
#
# Note: for PostHog Cloud remember to update ‘Dockerfile.cloud’ as appropriate.
#
# The stages are used to:
#
# - frontend-build: build the frontend (static assets)
# - plugin-server-build: build plugin-server (Node.js app) & fetch its runtime dependencies
# - posthog-build: fetch PostHog (Django app) dependencies & build Django collectstatic
# - fetch-geoip-db: fetch the GeoIP database
#
# In the last stage, we import the artifacts from the previous
# stages, add some runtime dependencies and build the final image.
#


#
# ---------------------------------------------------------
#
FROM node:18.12.1-bullseye-slim AS frontend-build
WORKDIR /code
SHELL ["/bin/bash", "-o", "pipefail", "-c"]

COPY package.json pnpm-lock.yaml ./
RUN corepack enable && pnpm --version && \
mkdir /tmp/pnpm-store && \
pnpm install --frozen-lockfile --store-dir /tmp/pnpm-store --prod && \
rm -rf /tmp/pnpm-store

COPY frontend/ frontend/
COPY ./bin/ ./bin/
COPY babel.config.js tsconfig.json webpack.config.js ./
RUN pnpm build


#
# ---------------------------------------------------------
#
FROM node:18.12.1-bullseye-slim AS plugin-server-build
WORKDIR /code/plugin-server
SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# Compile and install Node.js dependencies.
COPY ./plugin-server/package.json ./plugin-server/pnpm-lock.yaml ./plugin-server/tsconfig.json ./
COPY ./plugin-server/patches/ ./patches/
RUN apt-get update && \

Check warning on line 50 in production-unit.Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
apt-get install -y --no-install-recommends \
"make" \
"g++" \
"gcc" \
"python3" \
"libssl-dev" \
"zlib1g-dev" \
&& \
rm -rf /var/lib/apt/lists/* && \
corepack enable && \
mkdir /tmp/pnpm-store && \
pnpm install --frozen-lockfile --store-dir /tmp/pnpm-store && \
rm -rf /tmp/pnpm-store

# Build the plugin server.
#
# Note: we run the build as a separate action to increase
# the cache hit ratio of the layers above.
COPY ./plugin-server/src/ ./src/
RUN pnpm build

# As the plugin-server is now built, let’s keep
# only prod dependencies in the node_module folder
# as we will copy it to the last image.
RUN corepack enable && \
mkdir /tmp/pnpm-store && \
pnpm install --frozen-lockfile --store-dir /tmp/pnpm-store --prod && \
rm -rf /tmp/pnpm-store


#
# ---------------------------------------------------------
#
FROM python:3.10.10-slim-bullseye AS posthog-build
WORKDIR /code
SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# Compile and install Python dependencies.
# We install those dependencies on a custom folder that we will
# then copy to the last image.
COPY requirements.txt ./
RUN apt-get update && \

Check warning on line 92 in production-unit.Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
apt-get install -y --no-install-recommends \
"build-essential" \
"git" \
"libpq-dev" \
"libxmlsec1" \
"libxmlsec1-dev" \
"libffi-dev" \
"pkg-config" \
&& \
rm -rf /var/lib/apt/lists/* && \
pip install -r requirements.txt --compile --no-cache-dir --target=/python-runtime

ENV PATH=/python-runtime/bin:$PATH \
PYTHONPATH=/python-runtime

# Add in Django deps and generate Django's static files.
COPY manage.py manage.py
COPY posthog posthog/
COPY ee ee/
COPY --from=frontend-build /code/frontend/dist /code/frontend/dist
RUN SKIP_SERVICE_VERSION_REQUIREMENTS=1 SECRET_KEY='unsafe secret key for collectstatic only' DATABASE_URL='postgres:///' REDIS_URL='redis:///' python manage.py collectstatic --noinput


#
# ---------------------------------------------------------
#
FROM debian:bullseye-slim AS fetch-geoip-db
WORKDIR /code
SHELL ["/bin/bash", "-o", "pipefail", "-c"]

# Fetch the GeoLite2-City database that will be used for IP geolocation within Django.
RUN apt-get update && \

Check warning on line 124 in production-unit.Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
apt-get install -y --no-install-recommends \
"ca-certificates" \
"curl" \
"brotli" \
&& \
rm -rf /var/lib/apt/lists/* && \
mkdir share && \
( curl -s -L "https://mmdbcdn.posthog.net/" | brotli --decompress --output=./share/GeoLite2-City.mmdb ) && \
chmod -R 755 ./share/GeoLite2-City.mmdb


#
# ---------------------------------------------------------
#
FROM nginx/unit:1.28.0-python3.10
WORKDIR /code
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
ENV PYTHONUNBUFFERED 1

# Install OS runtime dependencies.
# Note: please add in this stage runtime dependences only!
RUN apt-get update && \

Check warning on line 146 in production-unit.Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`

Check notice on line 146 in production-unit.Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Delete the apt-get lists after installing something
apt-get install -y --no-install-recommends \
"chromium" \
"chromium-driver" \
"libpq-dev" \
"libxmlsec1" \
"libxmlsec1-dev" \
"libxml2"

# Install NodeJS 18.
RUN apt-get install -y --no-install-recommends \

Check warning on line 156 in production-unit.Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Pin versions in apt get install. Instead of `apt-get install <package>` use `apt-get install <package>=<version>`
"curl" \
&& \
curl -fsSL https://deb.nodesource.com/setup_18.x | bash - && \
apt-get install -y --no-install-recommends \
"nodejs" \
&& \
rm -rf /var/lib/apt/lists/*

# Install and use a non-root user.
RUN groupadd -g 1000 posthog && \
useradd -u 999 -r -g posthog posthog && \
chown posthog:posthog /code
USER posthog

# Add in the compiled plugin-server & its runtime dependencies from the plugin-server-build stage.
COPY --from=plugin-server-build --chown=posthog:posthog /code/plugin-server/dist /code/plugin-server/dist
COPY --from=plugin-server-build --chown=posthog:posthog /code/plugin-server/node_modules /code/plugin-server/node_modules
COPY --from=plugin-server-build --chown=posthog:posthog /code/plugin-server/package.json /code/plugin-server/package.json

# Copy the Python dependencies and Django staticfiles from the posthog-build stage.
COPY --from=posthog-build --chown=posthog:posthog /code/staticfiles /code/staticfiles
COPY --from=posthog-build --chown=posthog:posthog /python-runtime /python-runtime
ENV PATH=/python-runtime/bin:$PATH \
PYTHONPATH=/python-runtime

# Copy the frontend assets from the frontend-build stage.
# TODO: this copy should not be necessary, we should remove it once we verify everything still works.
COPY --from=frontend-build --chown=posthog:posthog /code/frontend/dist /code/frontend/dist

# Copy the GeoLite2-City database from the fetch-geoip-db stage.
COPY --from=fetch-geoip-db --chown=posthog:posthog /code/share/GeoLite2-City.mmdb /code/share/GeoLite2-City.mmdb

# Add in the Gunicorn config, custom bin files and Django deps.
COPY --chown=posthog:posthog gunicorn.config.py ./
COPY --chown=posthog:posthog ./bin ./bin/
COPY --chown=posthog:posthog manage.py manage.py
COPY --chown=posthog:posthog posthog posthog/
COPY --chown=posthog:posthog ee ee/
COPY --chown=posthog:posthog hogvm hogvm/

# Setup ENV.
ENV NODE_ENV=production \
CHROME_BIN=/usr/bin/chromium \
CHROME_PATH=/usr/lib/chromium/ \
CHROMEDRIVER_BIN=/usr/bin/chromedriver

# Expose container port and run entry point script.
EXPOSE 8000

# Expose the port from which we serve OpenMetrics data.
EXPOSE 8001
COPY unit.json /docker-entrypoint.d/unit.json
USER root

Check warning on line 209 in production-unit.Dockerfile

View workflow job for this annotation

GitHub Actions / Lint changed Dockerfiles

Last USER should not be root
CMD ["./bin/docker"]
16 changes: 16 additions & 0 deletions unit.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"listeners": {
"*:8000": {
"pass": "applications/posthog"
}
},
"applications": {
"posthog": {
"type": "python 3.10",
"processes": 1,
"working_directory": "/code",
"path": ".",
"module": "posthog.wsgi"
}
}
}

0 comments on commit 88ccf4e

Please sign in to comment.