diff --git a/api/app/urls.py b/api/app/urls.py index a110c7d111c0..c58409d0f3f3 100644 --- a/api/app/urls.py +++ b/api/app/urls.py @@ -15,6 +15,9 @@ re_path(r"^api/v2/", include("api.urls.v2", namespace="api-v2")), re_path(r"^admin/", admin.site.urls), re_path(r"^health", include("health_check.urls", namespace="health")), + # Aptible health checks must be on /healthcheck and cannot redirect + # see https://www.aptible.com/docs/core-concepts/apps/connecting-to-apps/app-endpoints/https-endpoints/health-checks + path("healthcheck", include("health_check.urls", namespace="health")), re_path(r"^version", views.version_info, name="version-info"), re_path( r"^sales-dashboard/", diff --git a/api/core/management/commands/waitfordb.py b/api/core/management/commands/waitfordb.py index 91c37468bc26..811af752994e 100644 --- a/api/core/management/commands/waitfordb.py +++ b/api/core/management/commands/waitfordb.py @@ -45,6 +45,7 @@ def handle( database: str, **options: Any, ) -> None: + start = time.monotonic() wait_between_checks = 0.25 diff --git a/api/scripts/run-docker.sh b/api/scripts/run-docker.sh index 939a339b504b..88ae045c98f5 100755 --- a/api/scripts/run-docker.sh +++ b/api/scripts/run-docker.sh @@ -1,8 +1,14 @@ #!/bin/sh set -e +function waitfordb() { + if [ -z "${SKIP_WAIT_FOR_DB}" ]; then + python manage.py waitfordb "$@" + fi +} + function migrate () { - python manage.py waitfordb && python manage.py migrate && python manage.py createcachetable + waitfordb && python manage.py migrate && python manage.py createcachetable } function serve() { # configuration parameters for statsd. Docs can be found here: @@ -10,7 +16,7 @@ function serve() { export STATSD_PORT=${STATSD_PORT:-8125} export STATSD_PREFIX=${STATSD_PREFIX:-flagsmith.api} - python manage.py waitfordb + waitfordb exec gunicorn --bind 0.0.0.0:8000 \ --worker-tmp-dir /dev/shm \ @@ -26,9 +32,9 @@ function serve() { app.wsgi } function run_task_processor() { - python manage.py waitfordb --waitfor 30 --migrations + waitfordb --waitfor 30 --migrations if [[ -n "$ANALYTICS_DATABASE_URL" || -n "$DJANGO_DB_NAME_ANALYTICS" ]]; then - python manage.py waitfordb --waitfor 30 --migrations --database analytics + waitfordb --waitfor 30 --migrations --database analytics fi RUN_BY_PROCESSOR=1 exec python manage.py runprocessor \ --sleepintervalms ${TASK_PROCESSOR_SLEEP_INTERVAL:-500} \ diff --git a/docs/docs/deployment/hosting/aptible.md b/docs/docs/deployment/hosting/aptible.md new file mode 100644 index 000000000000..92c0c135dd52 --- /dev/null +++ b/docs/docs/deployment/hosting/aptible.md @@ -0,0 +1,75 @@ +--- +title: Aptible +--- + +## Prerequisites + +The options and health check routes described in this document are available from Flagsmith 2.130.0. + +## Configuration + +Running Flagsmith on Aptible requires some configuration tweaks because of how Aptible's application lifecycle works: + +- Don't wait for the database to be available before the Flagsmith API starts. You can do this by setting the + `SKIP_WAIT_FOR_DB` environment variable. +- Add `containers` as an allowed host to comply with Aptible's + [strict health checks](https://www.aptible.com/docs/core-concepts/apps/connecting-to-apps/app-endpoints/https-endpoints/health-checks#strict-health-checks). +- Use the `before_release` tasks from `.aptible.yml` to run database migrations +- Use a Procfile to only start the API and not perform database migrations on startup + +This configuration can be applied by adding the Procfile and `.aptible.yml` configuration files to a +[Docker image](https://www.aptible.com/docs/core-concepts/apps/deploying-apps/image/deploying-with-docker-image/overview#how-do-i-deploy-from-docker-image) +that you build starting from a Flagsmith base image: + +```text title="Procfile" +cmd: serve +``` + +```yaml title=".aptible.yml" +before_release: + - migrate + - bootstrap +``` + +```dockerfile title="Dockerfile" +# Use flagsmith/flagsmith-private-cloud for the Enterprise image +FROM --platform=linux/amd64 flagsmith/flagsmith + +# Don't wait for the database to be available during startup for health checks to succeed +ENV SKIP_WAIT_FOR_DB=1 + +# Use root user to add Aptible files to the container +USER root +RUN mkdir /.aptible/ +ADD Procfile /.aptible/Procfile +ADD .aptible.yml /.aptible/.aptible.yml + +# Use non-root user at runtime +USER nobody +``` + +Before deploying, set the environment variables for your database URL and allowed hosts from the Aptible dashboard, or +using the Aptible CLI: + +```shell +aptible config:set --app flagsmith \ + DATABASE_URL=postgresql://aptible:...@...:23532/db \ + DJANGO_ALLOWED_HOSTS='containers,YOUR_APTIBLE_HOSTNAME' +``` + +## Deployment + +After your image is built and pushed to a container registry that Aptible can access, you can deploy it using the +Aptible CLI as you would any other application: + +```shell +aptible deploy --app flagsmith --docker-image example/my-flagsmith-aptible-image +``` + +Once Flagsmith is running in Aptible, make sure to create the first admin user by visiting `/api/v1/users/config/init/`. + +## Limitations + +The steps described in this document do not deploy the +[asynchronous task processor](/deployment/configuration/task-processor), which may affect performance in production +workloads.