Skip to content

Commit

Permalink
Add Waitress support for production
Browse files Browse the repository at this point in the history
Add resource metrics
Add psutil memory info metrics
  • Loading branch information
sbrunner committed Nov 23, 2024
1 parent 6f78625 commit 053ed00
Show file tree
Hide file tree
Showing 52 changed files with 1,606 additions and 32 deletions.
3 changes: 0 additions & 3 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,6 @@ repos:
rev: 1.1.2
hooks:
- id: copyright
- id: poetry-check
additional_dependencies:
- poetry==1.8.4 # pypi
- id: poetry-lock
additional_dependencies:
- poetry==1.8.4 # pypi
Expand Down
11 changes: 9 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,15 @@ build_docker_test:
docker build --tag=$(DOCKER_BASE):tests --target=tests .

.PHONY: build_test_app
build_test_app: build_docker
docker build --tag=$(DOCKER_BASE)_test_app --build-arg="GIT_HASH=$(GIT_HASH)" acceptance_tests/app
build_test_app: build_test_app_gunicorn build_test_app_waitress

.PHONY: build_test_app_gunicorn
build_test_app_gunicorn: build_docker
docker build --tag=$(DOCKER_BASE)_test_app --build-arg="GIT_HASH=$(GIT_HASH)" acceptance_tests/gunicorn_app

.PHONY: build_test_app_waitress
build_test_app_waitress: build_docker
docker build --tag=$(DOCKER_BASE)_test_app_waitress --build-arg="GIT_HASH=$(GIT_HASH)" acceptance_tests/waitress_app

.PHONY: checks
checks: prospector ## Run the checks
Expand Down
File renamed without changes.
File renamed without changes.
70 changes: 70 additions & 0 deletions acceptance_tests/gunicorn_app/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
FROM camptocamp/c2cwsgiutils as base-all
LABEL maintainer Camptocamp "info@camptocamp.com"
SHELL ["/bin/bash", "-o", "pipefail", "-cux"]

# Used to convert the locked packages by poetry to pip requirements format
# We don't directly use `poetry install` because it force to use a virtual environment.
FROM base-all as poetry

RUN --mount=type=cache,target=/var/lib/apt/lists \
--mount=type=cache,target=/var/cache,sharing=locked \
apt-get update \
&& apt-get install --assume-yes --no-install-recommends python-is-python3

# Install Poetry
WORKDIR /tmp
COPY requirements.txt ./
RUN python3 -m pip install --disable-pip-version-check --requirement=requirements.txt

# Do the conversion
COPY poetry.lock pyproject.toml ./
RUN poetry export --output=requirements.txt \
&& poetry export --with=dev --output=requirements-dev.txt

# Base, the biggest thing is to install the Python packages
FROM base-all as base

WORKDIR /app

EXPOSE 8080
RUN --mount=type=cache,target=/root/.cache \
--mount=type=bind,from=poetry,source=/tmp,target=/poetry \
python3 -m pip install --disable-pip-version-check --no-deps --requirement=/poetry/requirements.txt

COPY . /app

ARG GIT_HASH

RUN --mount=type=cache,target=/root/.cache \
python3 -m pip install --disable-pip-version-check --no-deps --editable=. \
&& python3 -m pip freeze > /requirements.txt
RUN ./models_graph.py > models.dot \
&& ./models_graph.py Hello > models-hello.dot \
&& c2cwsgiutils-genversion $GIT_HASH \
&& python3 -m compileall -q .

ENV \
DOCKER_RUN=1 \
DEVELOPMENT=0 \
SQLALCHEMY_POOL_RECYCLE=30 \
SQLALCHEMY_POOL_SIZE=5 \
SQLALCHEMY_MAX_OVERFLOW=25 \
SQLALCHEMY_SLAVE_POOL_RECYCLE=30 \
SQLALCHEMY_SLAVE_POOL_SIZE=5 \
SQLALCHEMY_SLAVE_MAX_OVERFLOW=25 \
LOG_TYPE=console \
OTHER_LOG_LEVEL=WARNING \
GUNICORN_LOG_LEVEL=WARNING \
SQL_LOG_LEVEL=WARNING \
C2CWSGIUTILS_LOG_LEVEL=WARNING \
LOG_LEVEL=INFO \
VISIBLE_ENTRY_POINT=/

RUN mkdir -p /prometheus-metrics \
&& chmod a+rwx /prometheus-metrics
ENV PROMETHEUS_MULTIPROC_DIR=/prometheus-metrics

# www-data
USER 33

CMD ["/venv/bin/gunicorn", "--paste=/app/application.ini"]
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
###
# app configuration
# Application configuration
# http://docs.pylonsproject.org/projects/pyramid/en/1.6-branch/narr/environment.html
# this file should be used by gunicorn.
###

[app:app]
use = egg:c2cwsgiutils_app
filter-with = proxy-prefix

pyramid.reload_templates = %(DEVELOPMENT)s
pyramid.debug_authorization = %(DEVELOPMENT)s
Expand All @@ -27,15 +28,23 @@ sqlalchemy_slave.max_overflow = %(SQLALCHEMY_SLAVE_MAX_OVERFLOW)s
c2c.sql_request_id = True
c2c.requests_default_timeout = 2

[filter:proxy-prefix]
use = egg:PasteDeploy#prefix
prefix = %(VISIBLE_ENTRY_POINT)s

[filter:translogger]
use = egg:Paste#translogger
setup_console_handler = False

[pipeline:main]
pipeline = egg:c2cwsgiutils#client_info egg:c2cwsgiutils#sentry app
pipeline = egg:c2cwsgiutils#client_info egg:c2cwsgiutils#sentry translogger app

[server:main]
use = egg:waitress#main
listen = *:8080

###
# logging configuration
# Logging configuration
# http://docs.pylonsproject.org/projects/pyramid/en/latest/narr/logging.html
###

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
from c2cwsgiutils_app import models
from pyramid.config import Configurator
from pyramid.httpexceptions import HTTPInternalServerError

import c2cwsgiutils.pyramid
from c2cwsgiutils import broadcast, db
from c2cwsgiutils.health_check import HealthCheck, JsonCheckException

from c2cwsgiutils_app import models


def _failure(_request):
raise HTTPInternalServerError("failing check")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@

import psycopg2
import transaction
from c2cwsgiutils_app import models

import c2cwsgiutils.db
import c2cwsgiutils.setup_process

from c2cwsgiutils_app import models


def _fill_db():
for db, value in (("db", "master"), ("db_slave", "slave")):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import prometheus_client
import requests
from c2cwsgiutils_app import models
from pyramid.httpexceptions import (
HTTPBadRequest,
HTTPForbidden,
Expand All @@ -13,6 +12,8 @@

from c2cwsgiutils import sentry, services

from c2cwsgiutils_app import models

_PROMETHEUS_TEST_COUNTER = prometheus_client.Counter("test_counter", "Test counter")
_PROMETHEUS_TEST_GAUGE = prometheus_client.Gauge("test_gauge", "Test gauge", ["value", "toto"])
_PROMETHEUS_TEST_SUMMARY = prometheus_client.Summary("test_summary", "Test summary")
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/usr/bin/env python3
from c2cwsgiutils_app import models

from c2cwsgiutils.models_graph import generate_model_graph

from c2cwsgiutils_app import models


def main():
generate_model_graph(models)
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env python3

# Copyright (c) 2023, Camptocamp SA
# Copyright (c) 2023-2024, Camptocamp SA
# All rights reserved.

# Redistribution and use in source and binary forms, with or without
Expand Down
12 changes: 10 additions & 2 deletions acceptance_tests/tests/docker-compose.override.sample.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,28 @@ services:
app:
# Uncomment to use pserve
# command:
# - pserve
# - /venv/bin/pserve
# - --reload
# - c2c:///app/production.ini
# - c2c:///app/application.ini
volumes:
# This mounts the local filesystem inside the container so that
# the views are automatically reloaded when a file change
- ../app/c2cwsgiutils_app/:/app/c2cwsgiutils_app/:ro
- ../../c2cwsgiutils/:/opt/c2cwsgiutils/c2cwsgiutils/:ro
environment:
- DEVELOPMENT=TRUE
ports:
- 9090:9090

app2:
# command:
# - /venv/bin/pserve
# - --reload
# - c2c:///app/application.ini
ports:
- 9092:9090
environment:
- DEVELOPMENT=TRUE

db:
ports:
Expand Down
6 changes: 1 addition & 5 deletions acceptance_tests/tests/docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ services:
# Test problematic environment variable (values contains % and duplicated with different cass)
- TEST='%1'
- test='%2'
- PROMETHEUS_MULTIPROC_DIR=/prometheus-metrics
- C2C_PROMETHEUS_PORT=9090
- C2C_PROMETHEUS_APPLICATION_PACKAGE=c2cwsgiutils_app
links:
Expand All @@ -46,6 +45,7 @@ services:

app2:
<<: *app
image: camptocamp/c2cwsgiutils_test_app_waitress
# Same as app but with 2 workers (and different Redis DB 2, broadcast_prefix, ports, and JSON log format)
environment:
- SQLALCHEMY_URL
Expand All @@ -61,7 +61,6 @@ services:
- C2C_PROFILER_PATH=/api_profiler
- C2C_PROFILER_MODULES=c2cwsgiutils c2cwsgiutils_app sqlalchemy request
- C2C_ENABLE_EXCEPTION_HANDLING=1
- GUNICORN_CMD_ARGS="--reload" # don't use this in production
- C2CWSGIUTILS_LOG_LEVEL=DEBUG
- SQL_LOG_LEVEL=DEBUG
- OTHER_LOG_LEVEL=INFO
Expand All @@ -75,12 +74,9 @@ services:
- C2C_BROADCAST_PREFIX=app2
- PYTHONMALLOC=debug
- DEBUG_LOGCONFIG
- GUNICORN_WORKERS=2
- GUNICORN_THREADS=10
# Test problematic environment variable (values contains % and duplicated with different cass)
- TEST='%1'
- test='%2'
- PROMETHEUS_MULTIPROC_DIR=/prometheus-metrics
- C2C_PROMETHEUS_PORT=9090
- C2C_PROMETHEUS_APPLICATION_PACKAGES=c2cwsgiutils_app
ports:
Expand Down
17 changes: 14 additions & 3 deletions acceptance_tests/tests/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@

_BASE_URL = "http://app:8080/api/"
_BASE_URL_APP2 = "http://app2:8080/api/"
_PROMETHEUS_URL = "http://app2:9090/metrics"
_PROMETHEUS_URL_1 = "http://app:9090/metrics"
_PROMETHEUS_URL_2 = "http://app2:9090/metrics"
_PROMETHEUS_TEST_URL = "http://run_test:9090/metrics"
_PROMETHEUS_STATS_DB_URL = "http://stats_db:9090/metrics"
_LOG = logging.getLogger(__name__)
Expand Down Expand Up @@ -44,13 +45,23 @@ def app2_connection(composition):


@pytest.fixture
def prometheus_connection(composition):
def prometheus_1_connection(composition):
"""
Fixture that returns a connection to a running batch container.
"""

del composition
return Connection(base_url=_PROMETHEUS_URL, origin="http://example.com/")
return Connection(base_url=_PROMETHEUS_URL_1, origin="http://example.com/")


@pytest.fixture
def prometheus_2_connection(composition):
"""
Fixture that returns a connection to a running batch container.
"""

del composition
return Connection(base_url=_PROMETHEUS_URL_2, origin="http://example.com/")


@pytest.fixture
Expand Down
13 changes: 11 additions & 2 deletions acceptance_tests/tests/tests/test_prometheus_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,17 @@
_PID_RE = re.compile(r',pid="([0-9]+)"')


def test_prometheus(prometheus_connection):
def test_prometheus_1(prometheus_1_connection):
# One for the root process, one for each workers
assert (
len(set(_PID_RE.findall(prometheus_connection.get("metrics", cache_expected=False, cors=False)))) == 3
len(set(_PID_RE.findall(prometheus_1_connection.get("metrics", cache_expected=False, cors=False))))
== 3
)


def test_prometheus_2(prometheus_2_connection):
# One for the root process, one for each workers
assert (
len(set(_PID_RE.findall(prometheus_2_connection.get("metrics", cache_expected=False, cors=False))))
== 3
)
9 changes: 9 additions & 0 deletions acceptance_tests/waitress_app/.coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[run]
source=
/opt/c2cwsgiutils/c2cwsgiutils
/app/c2cwsgiutils_app
branch=True
omit=
/opt/c2cwsgiutils/c2cwsgiutils/acceptance/*
/opt/c2cwsgiutils/c2cwsgiutils/models_graph.py
/opt/c2cwsgiutils/c2cwsgiutils/coverage_setup.py
3 changes: 3 additions & 0 deletions acceptance_tests/waitress_app/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
**/__pycache__
Dockerfile
c2cwsgiutils_app.egg-info
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,17 @@ ENV \
SQLALCHEMY_MAX_OVERFLOW=25 \
SQLALCHEMY_SLAVE_POOL_RECYCLE=30 \
SQLALCHEMY_SLAVE_POOL_SIZE=5 \
SQLALCHEMY_SLAVE_MAX_OVERFLOW=25 LOG_TYPE=console \
SQLALCHEMY_SLAVE_MAX_OVERFLOW=25 \
LOG_TYPE=console \
OTHER_LOG_LEVEL=WARNING \
GUNICORN_LOG_LEVEL=WARNING \
WAITRESS_LOG_LEVEL=WARNING \
SQL_LOG_LEVEL=WARNING \
C2CWSGIUTILS_LOG_LEVEL=WARNING \
LOG_LEVEL=INFO
LOG_LEVEL=INFO \
WAITRESS_THREADS=10 \
VISIBLE_ENTRY_POINT=/

# www-data
USER 33

CMD ["/venv/bin/pserve", "c2c:///app/application.ini"]
8 changes: 8 additions & 0 deletions acceptance_tests/waitress_app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# A sample application

This is a sample application shipped as a Docker Image.

It's used for testing the library. Because we want to test the currently
checked out version of the library, it is not in the requirements.txt
file. The root Makefile copies the library inline with the application
before building the Docker image.
Loading

0 comments on commit 053ed00

Please sign in to comment.