Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First version of Dockerfile & docker-compose #416

Merged
merged 32 commits into from Apr 25, 2022
Merged
Show file tree
Hide file tree
Changes from 27 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a973344
First version of Dockerfile & docker-compose, including a health chec…
nhoening Apr 10, 2022
9ed8213
fix inability to call flexmeasures command in container (had to use f…
nhoening Apr 11, 2022
0f3dc8d
load the instance directory as volume, which allows to pass a configu…
nhoening Apr 11, 2022
e303b88
document better & correctly how to mount config file into a FlexMeasu…
nhoening Apr 12, 2022
b45e805
copy less unneeded files into FlexMeasures image
nhoening Apr 12, 2022
d452d9f
run the FlexMeasures image via Gunicorn
nhoening Apr 12, 2022
382455f
install gunicorn early on
nhoening Apr 13, 2022
23f4a0e
add changelog entries
nhoening Apr 13, 2022
1689350
mention the Docker image on index and in toy tutorial section
nhoening Apr 13, 2022
9ca44ef
move the note about using Docker for the tutorial a little lower
nhoening Apr 13, 2022
fa71d31
install solver as well
nhoening Apr 14, 2022
6e6c1e2
document how at this point one can run tests inside a FlexMeasures co…
nhoening Apr 14, 2022
63bf9ad
Merge branch 'main' into 149-dockerfile-for-developmentdemo
nhoening Apr 14, 2022
cdb42e6
load sql extensions into postgres service
nhoening Apr 15, 2022
1fd6b68
add another note to the preliminary testing setup
nhoening Apr 15, 2022
dd03e7b
Merge branch 'main' into 149-dockerfile-for-developmentdemo
nhoening Apr 15, 2022
101224a
Run tests easily in Docker: add test-db service in compose stack, and…
nhoening Apr 15, 2022
9adc8b7
improve documentation, from review comments
nhoening Apr 16, 2022
73c4722
fix typo
nhoening Apr 17, 2022
79a99c0
lazy loading in SensorIdField (fixes flexmeasures add schedule)
nhoening Apr 17, 2022
8bdf811
docs: add FLASK_ENV=development to docker run call
nhoening Apr 17, 2022
5c65024
split docker and docker-compose docs (first is relevant for installin…
nhoening Apr 18, 2022
70a712d
fix regression in not attaching account to assets correctly in toy tu…
nhoening Apr 19, 2022
822d374
separate installation instructions for toy tutorial into Docker and n…
nhoening Apr 20, 2022
adbfe25
some text improvements
nhoening Apr 20, 2022
b695c0d
correct the link in coverage badge, to report on main branch
nhoening Apr 21, 2022
a2237df
switch to lfenergy/flexmeasures
nhoening Apr 22, 2022
89a9833
small comments from review
nhoening Apr 22, 2022
48e7c07
demanding the lowest docker compose version we know we need (we use s…
nhoening Apr 23, 2022
555dfd5
more attention-grabbing note about the tutorial happening inside the …
nhoening Apr 23, 2022
45b88cf
not only the API runs in the container
nhoening Apr 25, 2022
ad15a9d
better name for the Docker container in the compose tutorial & smalle…
nhoening Apr 25, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Expand Up @@ -16,6 +16,7 @@ venv
.env-flexmeasures/
flexmeasures.egg-info
flexmeasures.log*
prices-tomorrow.csv
nhoening marked this conversation as resolved.
Show resolved Hide resolved

.cache
__pycache__
Expand Down
42 changes: 42 additions & 0 deletions Dockerfile
@@ -0,0 +1,42 @@
FROM ubuntu:focal

# TODO: Cbc solver
# TODO: run gunicorn as entry command

ENV DEBIAN_FRONTEND noninteractive
ENV LC_ALL C.UTF-8
ENV LANG C.UTF-8

# pre-requisites
RUN apt-get update && apt-get install -y --upgrade python3 python3-pip git curl gunicorn coinor-cbc

WORKDIR /app
# requirements - doing this earlier, so we don't install them each time. Use --no-cache to refresh them.
COPY requirements /app/requirements

# py dev tooling
RUN python3 -m pip install --upgrade pip && python3 --version
RUN pip3 install --upgrade setuptools
RUN pip3 install -r requirements/app.txt -r requirements/dev.txt -r requirements/test.txt

# Copy code and meta/config data
COPY setup.* .flaskenv wsgi.py /app/
COPY flexmeasures/ /app/flexmeasures
RUN find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf
COPY .git/ /app/.git

RUN pip3 install .

EXPOSE 5000

CMD [ \
"gunicorn", \
"--bind", "0.0.0.0:5000", \
# This is set to /tmp by default, but this is part of the Docker overlay filesystem, and can cause stalls.
# http://docs.gunicorn.org/en/latest/faq.html#how-do-i-avoid-gunicorn-excessively-blocking-in-os-fchmod
"--worker-tmp-dir", "/dev/shm", \
# Ideally you'd want one worker per container, but we don't want to risk the health check timing out because
# another request is taking a long time to complete.
"--workers", "2", "--threads", "4", \
"wsgi:application" \
]
2 changes: 1 addition & 1 deletion Makefile
Expand Up @@ -18,7 +18,7 @@ update-docs:
@echo "Creating docs environment ..."
make install-docs-dependencies
@echo "Creating documentation ..."
cd documentation; make clean; make html; cd ..
cd documentation; make clean; make html SPHINXOPTS="-W --keep-going -n"; cd ..

update-docs-pdf:
@echo "NOTE: PDF documentation requires packages (on Debian: latexmk texlive-latex-recommended texlive-latex-extra texlive-fonts-recommended)"
Expand Down
4 changes: 2 additions & 2 deletions Readme.md
Expand Up @@ -6,7 +6,7 @@
[![](https://img.shields.io/badge/python-3.8+-blue.svg)](https://www.python.org/downloads/)
[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
[![Documentation Status](https://readthedocs.org/projects/flexmeasures/badge/?version=latest)](https://flexmeasures.readthedocs.io/en/latest/?badge=latest)
[![Coverage](https://coveralls.io/repos/github/FlexMeasures/flexmeasures/badge.svg?branch=coverage-in-ci)](https://coveralls.io/github/FlexMeasures/flexmeasures?branch=coverage-in-ci)
[![Coverage](https://coveralls.io/repos/github/FlexMeasures/flexmeasures/badge.svg?branch=coverage-in-ci)](https://coveralls.io/github/FlexMeasures/flexmeasures)
nhoening marked this conversation as resolved.
Show resolved Hide resolved

The *FlexMeasures Platform* is the intelligent EMS (energy management system) to support real-time energy flexibility apps, rapidly and scalable.

Expand Down Expand Up @@ -42,4 +42,4 @@ We made FlexMeasures freely available under the Apache2.0 licence and it is now

Within the FlexMeasures project, [we welcome contributions](https://github.com/FlexMeasures/tsc/blob/main/CONTRIBUTING.md). You can also [learn more about our governance](https://github.com/Flexmeasures/tsc/blob/main/GOVERNANCE.md).

You can connect with the community here on Github (e.g. by creating an issue), on [the mailing list](https://lists.lfenergy.org/g/flexmeasures), on [the FlexMeasures channel within the LF Energy Slack](https://slack.lfenergy.org/) or [by contacting the current maintainers](https://www.seita.nl/contact).
You can connect with the community here on Github (e.g. by creating an issue), on [the mailing list](https://lists.lfenergy.org/g/flexmeasures), on [the FlexMeasures channel within the LF Energy Slack](https://slack.lfenergy.org/) or [by contacting the current maintainers](https://www.seita.nl/contact).
2 changes: 2 additions & 0 deletions ci/load-psql-extensions.sql
@@ -0,0 +1,2 @@
CREATE EXTENSION IF NOT EXISTS cube;
CREATE EXTENSION IF NOT EXISTS earthdistance;
2 changes: 1 addition & 1 deletion ci/setup-postgres.sh
Expand Up @@ -27,4 +27,4 @@ while [[ true ]]; do
fi
done

psql -h $PGHOST -p $PGPORT -c "create extension if not exists cube; create extension if not exists earthdistance;" -U $PGUSER $PGDB;
psql -h $PGHOST -p $PGPORT --file ci/load-psql-extensions.sql -U $PGUSER $PGDB;
63 changes: 63 additions & 0 deletions docker-compose.yml
@@ -0,0 +1,63 @@
version: "3.8"

# -----------------------------------------------
# TODO:
# * Add redis service
# * Add a FlexMeasures worker service, maybe using https://docs.docker.com/compose/compose-file/#entrypoint
# -----------------------------------------------

services:
dev-db:
image: postgres
expose:
- 5432
restart: always
environment:
POSTGRES_DB: fm-dev-db
POSTGRES_USER: fm-dev-db-user
POSTGRES_PASSWORD: fm-dev-db-pass
volumes:
- ./ci/load-psql-extensions.sql:/docker-entrypoint-initdb.d/load-psql-extensions.sql
flexmeasures:
build:
context: .
dockerfile: Dockerfile
ports:
- 5000:5000
depends_on:
- dev-db
- test-db # use -e SQLALCHEMY_TEST_DATABASE_URI=... to exec pytest
restart: on-failure
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/api/v3_0/health/ready"]
start_period: 10s
interval: 20s
timeout: 10s
retries: 6
environment:
SQLALCHEMY_DATABASE_URI: "postgresql://fm-dev-db-user:fm-dev-db-pass@dev-db:5432/fm-dev-db"
SECRET_KEY: notsecret
FLASK_ENV: development
LOGGING_LEVEL: INFO
volumes:
# a place for config and plugin code - the mount point is for running the FlexMeasures CLI, the 2nd for gunicorn
- ./flexmeasures-instance/:/usr/var/flexmeasures-instance/:ro
- ./flexmeasures-instance/:/app/instance/:ro
command: >
bash -c "flexmeasures db upgrade
&& flexmeasures add toy-account --name 'Docker Toy Account'
&& gunicorn --bind 0.0.0.0:5000 --worker-tmp-dir /dev/shm --workers 2 --threads 4 wsgi:application"
test-db:
image: postgres
expose:
- 5432
restart: always
environment:
POSTGRES_DB: fm-test-db
POSTGRES_USER: fm-test-db-user
POSTGRES_PASSWORD: fm-test-db-pass
volumes:
- ./ci/load-psql-extensions.sql:/docker-entrypoint-initdb.d/load-psql-extensions.sql

volumes:
flexmeasures-instance:
3 changes: 3 additions & 0 deletions documentation/changelog.rst
Expand Up @@ -9,12 +9,14 @@ v0.10.0 | April XX, 2022
New features
-----------
* Improve legibility of chart axes [see `PR #413 <http://www.github.com/FlexMeasures/flexmeasures/pull/413>`_]
* API provides health readiness check at /api/v3_0/health/ready [see `PR #416 <http://www.github.com/FlexMeasures/flexmeasures/pull/416>`_]

Bugfixes
-----------

Infrastructure / Support
----------------------
* Dockerfile to run FlexMeasures API in container; also docker-compose file [see `PR #416 <http://www.github.com/FlexMeasures/flexmeasures/pull/416>`_]
nhoening marked this conversation as resolved.
Show resolved Hide resolved
* Unit conversion prefers shorter units in general [see `PR #415 <http://www.github.com/FlexMeasures/flexmeasures/pull/415>`_]
* Shorter CI builds in Github Actions by caching Python environment [see `PR #361 <http://www.github.com/FlexMeasures/flexmeasures/pull/361>`_]

Expand All @@ -36,6 +38,7 @@ Bugfixes
* Fix filter for selecting one deterministic belief per event, which was duplicating index levels [see `PR #414 <http://www.github.com/FlexMeasures/flexmeasures/pull/414>`_]



nhoening marked this conversation as resolved.
Show resolved Hide resolved
v0.9.1 | March 31, 2022
===========================

Expand Down
5 changes: 3 additions & 2 deletions documentation/conf.py
Expand Up @@ -48,6 +48,7 @@
"sphinx.ext.ifconfig",
"sphinx.ext.todo",
"sphinx_copybutton",
"sphinx_tabs.tabs",
"sphinx_fontawesome",
"sphinxcontrib.autohttp.flask",
"sphinxcontrib.autohttp.flaskqref",
Expand Down Expand Up @@ -77,7 +78,7 @@
# This pattern also affects html_static_path and html_extra_path .
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]

# Todo: these are not mature enough yet for release
# Todo: these are not mature enough yet for release, or should be removed
exclude_patterns.append("int/*.rst")
exclude_patterns.append("concepts/assets.rst")
exclude_patterns.append("concepts/markets.rst")
Expand Down Expand Up @@ -194,7 +195,7 @@
# -- Options for intersphinx extension ---------------------------------------

# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {"https://docs.python.org/": None}
intersphinx_mapping = {"https://docs.python.org/3/": None}

# -- Options for copybytton extension ---------------------------------------
copybutton_prompt_is_regexp = True
Expand Down
12 changes: 11 additions & 1 deletion documentation/configuration.rst
Expand Up @@ -6,7 +6,7 @@ Configuration
The following configurations are used by FlexMeasures.

Required settings (e.g. postgres db) are marked with a double star (**).
To enable easier quickstart tutorials, these settings can be set by environment variables.
To enable easier quickstart tutorials, these required settings can be set by environment variables.
Recommended settings (e.g. mail, redis) are marked by one star (*).

.. note:: FlexMeasures is best configured via a config file. The config file for FlexMeasures can be placed in one of two locations:
Expand Down Expand Up @@ -302,6 +302,16 @@ Default:
}


SQLALCHEMY_TEST_DATABASE_URI
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

When running tests (``make test``, which runs ``pytest``), the default database URI is set in ``utils.config_defaults.TestingConfig``.
You can use this setting to overwrite that URI and point the tests to an (empty) database of your choice.

.. note:: This setting is only supported as an environment variable, not in a config file, and only during testing.



Security
--------

Expand Down
14 changes: 13 additions & 1 deletion documentation/dev/ci.rst
@@ -1,4 +1,4 @@
.. continuous_integration:
.. _continuous_integration:

Continuous integration
======================
Expand Down Expand Up @@ -97,3 +97,15 @@ The last step, touching a wsgi.py file, is often used as a way to soft-restart t

echo "RESTARTING APPLICATION ..."
touch $PATH_TO_WSGI


A WSGI file can do various things, as well, but the simplest form is shown below.

.. code-block:: python

from flexmeasures.app import create as create_app

application = create_app()


The web server is told about the WSGI script, but also about the object which represents the application. For instance, if this script is called ``wsgi.py``, then the relevant argument to the gunicorn server is ``wsgi:application``.
80 changes: 80 additions & 0 deletions documentation/dev/docker-compose.rst
@@ -0,0 +1,80 @@
.. _docker-compose:

Running a complete stack with docker-compose
=============================================

Installing FlexMeasures and dependencies is some work, and can have unexpected hurdles, e.g. depending on the operating system. So, running the whole stack via `Docker compose <https://docs.docker.com/compose/>`_ can be a very useful option.
nhoening marked this conversation as resolved.
Show resolved Hide resolved

For this, we assume you are in the directory housing ``docker-compose.yml``.


Build the compose stack
------------------------

Run this:

.. code-block:: bash

docker-compose build
Flix6x marked this conversation as resolved.
Show resolved Hide resolved

This pulls the images you need, and re-builds the FlexMeasures one from code. If you change code, re-running this will re-build that image.

This compose script can also serve as an inspiration for using FlexMeasures in modern cloud environments (like Kubernetes). For instance, you might want to not build the FlexMeasures image from code, but simply pull the image from DockerHub.

.. todo:: This stack runs FlexMeasures, but misses the background worker aspect. For this, we'll add a redis node and one additional FlexMeasures node, which runs a worker as entry point instead (see `issue 418<https://github.com/FlexMeasures/flexmeasures/issues/418>`_).


Run the compose stack
----------------------

Start the stack like this:

.. code-block:: bash

docker-compose up

You can see log output in the terminal, but ``docker-compose logs`` is also available to you.

Check ``docker ps`` or ``docker-compose ps`` to see if your containers are running:


.. code-block:: console

± docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6105f6d1c91f flexmeasures_flexmeasures "bash -c 'flexmeasur…" 45 seconds ago Up 44 seconds (healthy) 0.0.0.0:5000->5000/tcp flexmeasures_flexmeasures_1
b48e4b9b113b postgres "docker-entrypoint.s…" 44 hours ago Up 45 seconds 5432/tcp flexmeasures_dev-db_1


The FlexMeasures container has a health check implemented, which is reflected in this output and you can see which ports are available on your machine to interact.

You can use ``docker-compose logs`` to look at output. ``docker inspect <container>`` and ``docker exec -it <container> bash`` can be quite useful to dive into details.

.. todo:: We should provide a way to test that this is working, e.g. a list of steps. Document this, but also include that in our tsc/Release list (as a test step to see if Dockerization still works, plus a publish step for the released version).


Configuration
---------------

You can pass in your own configuration (e.g. for MapBox access token, or db URI, see below) like we described above for running a container: put a file ``flexmeasures.cfg`` into a local folder called ``flexmeasures-instance``.


Data
-------

The postgres database is a test database with toy data filled in when the flexmeasures container starts.
You could also connect it to some other database, by setting a different `SQLALCHEMY_DATABASE_URI` in the config.


Running tests
---------------

You can run tests in the flexmeasures docker container, using the database service ``test-db`` in the compose file. Per default, we are using the ``dev-db`` database service.

After you've started the compose stack with ``docker-compose up``, run:

.. code-block:: console

docker exec -it -e SQLALCHEMY_TEST_DATABASE_URI="postgresql://fm-test-db-user:fm-test-db-pass@test-db:5432/fm-test-db" <flexmeasures-container-name> pytest
nhoening marked this conversation as resolved.
Show resolved Hide resolved

This rounds up the dev experience offered by running FlexMeasures in Docker. Now you can develop FlexMeasures and also run your tests. If you develop plugins, you could extend the command being used, e.g. ``bash -c "cd /path/to/my/plugin && pytest"``.
2 changes: 1 addition & 1 deletion documentation/host/data.rst
Expand Up @@ -33,7 +33,7 @@ On Unix:

.. code-block:: console

sudo apt-get install postgresql
sudo apt-get install postgresql-12
pip install psycopg2-binary


Expand Down
19 changes: 14 additions & 5 deletions documentation/host/deployment.rst
Expand Up @@ -5,11 +5,12 @@ How to deploy FlexMeasures

Here you can learn how to get FlexMeasures onto a server.

.. note:: FlexMeasures can be deployed via Docker. Read more at :ref:`docker-image`.

.. contents:: Table of contents
:local:
:depth: 1

.. todo:: It would be great to enable Dockerization of FlexMeasures, let us know if this matters to you.


WSGI configuration
Expand All @@ -22,17 +23,25 @@ On your own computer, ``flexmeasures run`` is a nice way to start FlexMeasures.
# This file contains the WSGI configuration required to serve up your
# web application.
# It works by setting the variable 'application' to a WSGI handler of some description.
# The crucial part are the last two lines. We add some ideas for possible other logic.

import os
from dotenv import load_dotenv

project_home = u'/path/to/your/code/flexmeasures'
# use this if you want to load your own ``.env`` file.
from dotenv import load_dotenv
load_dotenv(os.path.join(project_home, '.env'))
# use this if you run from source
if project_home not in sys.path:
sys.path = [project_home] + sys.path
# adapt PATH to find our LP solver if it is installed from source
os.environ["PATH"] = os.environ.get("PATH") + ":/home/seita/Cbc-2.9/bin"
nhoening marked this conversation as resolved.
Show resolved Hide resolved

# create flask app - need to call it "application" for WSGI to work
# create flask app - the name "application" has to be passed to the WSGI server
from flexmeasures.app import create as create_app
application = create_app()

The web server is told about the WSGI script, but also about the object which represents the application. For instance, if this script is called ``wsgi.py``, then the relevant argument to the gunicorn server is ``wsgi:application``.

Keep in mind that FlexMeasures is based on `Flask <https://flask.palletsprojects.com/>`_, so almost all knowledge on the web on how to deploy a Flask app also helps with deploying FlexMeasures.


Expand All @@ -52,7 +61,7 @@ You can install it on Debian like this:


If you can't use the package manager on your host, the solver has to be installed from source.
We provide `an example script <ci/install-cbc.sh>`_ to do that, where you can also
We provide an example script in ``ci/install-cbc-from-source.sh`` to do that, where you can also
pass a directory for the installation.

In case you want to install a later version, adapt the version in the script.
Expand Down