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

PoC of dockerable solution #399

Closed
wants to merge 17 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ build-backend = "flit_core.buildapi"

[project]
name = "ansys-templates"
version = "1.8.dev0"
version = "1.8.dev4"
description = "Creates Python projects according to PyAnsys guidelines"
readme = "README.rst"
requires-python = ">=3.8,<4"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
"CONTRIBUTING.md",
"CONTRIBUTORS.md",
"LICENSE.rst",
"Dockerfile",
"docker-compose.yaml",
"poetry.lock",
"pyproject.toml",
"README.rst",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,16 @@
"port": 5724
},
"justMyCode": false
},
{
"name": "Python: Remote Attach Dash",
"type": "python",
"request": "attach",
"connect": {
"host": "localhost",
"port": 5725
},
"justMyCode": false
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# === Base Stage ===
FROM python:3.10-alpine as base

# credentials will only be available during image built
ARG SOLUTIONS_PRIVATE_PYPI_PAT

# TODO: get poetry version from pyproject file?
RUN pip install --upgrade pip && \
pip install poetry==1.7.1
RUN poetry config http-basic.solutions-private-pypi PAT ${SOLUTIONS_PRIVATE_PYPI_PAT}

COPY pyproject.toml poetry.lock /app/
COPY src/ /app/src/
COPY README.rst /app/

WORKDIR /app

# TODO: include documentation in the package?

RUN poetry install --only build -vv && \
poetry build --format wheel && \
poetry export --without-hashes --format requirements.txt --output requirements_api.txt
{% if cookiecutter.with_dash_ui == "yes" %}
RUN poetry export --only ui --without-hashes --format requirements.txt --output requirements_ui.txt
{% endif %}

# === API Stage ===
FROM python:3.10-alpine as solution_api

# credentials will only be available during image built
ARG SOLUTIONS_PRIVATE_PYPI_PAT

COPY --from=base /app/dist/*.whl /dist/
COPY --from=base /app/requirements_api.txt /dist/

# required by psutil for the psutil._psutil_linux extension
# TODO: how can we skip it and use a pre-built psutil that already includes it?
RUN apk add --no-cache build-base linux-headers

RUN pip install --upgrade pip && \
pip install /dist/*.whl -r /dist/requirements_api.txt --extra-index-url "https://PAT:${SOLUTIONS_PRIVATE_PYPI_PAT}@pkgs.dev.azure.com/pyansys/_packaging/ansys-solutions/pypi/simple/"
RUN apk del build-base linux-headers && \
rm -rf /var/cache/apk/* /root/.cache/pip/* /dist

# Launch API server directly with uvicorn
ENTRYPOINT ["uvicorn", "ansys.saf.glow.api:app"]

{% if cookiecutter.with_dash_ui == "yes" %}
# === UI Stage ===
# Currently, UI is designed as an extra of the API server, that's why it's built over the API image, including its dependencies.
FROM solution_api as solution_ui

COPY --from=base /app/requirements_ui.txt /dist/

RUN pip install -r /dist/requirements_ui.txt --extra-index-url "https://PAT:${SOLUTIONS_PRIVATE_PYPI_PAT}@pkgs.dev.azure.com/pyansys/_packaging/ansys-solutions/pypi/simple/"
RUN rm -rf /root/.cache/pip/* /dist

# Exposing UI app to be directly called with uvicorn is not available yet. Fallback to glow_engine CLI.
ENTRYPOINT ["glow_engine", "ui"]
{% endif %}
Original file line number Diff line number Diff line change
Expand Up @@ -68,19 +68,26 @@ Installation

cd {{ cookiecutter.__project_name_slug }}

3. Install ``toml`` and ``packaging`` on your system environment:

Start the solution with a local installation
============================================

To start the solution using a desktop orchestrator run the following commands in the root of the project:


1. Install ``toml`` and ``packaging`` on your system environment:

.. code:: bash

pip install toml packaging

4. Install the production dependencies:
2. Install the production and desktop dependencies:

.. code:: bash

python setup_environment.py -d run
python setup_environment.py -d run desktop

5. Activate the virtual environment:
3. Activate the virtual environment:

* For Windows CMD:

Expand All @@ -94,17 +101,48 @@ Installation

.venv\Scripts\Activate.ps1

From now on, all the commands must be executed within the virtual environment.
4. Launch the solution:

.. code:: bash

Start the solution
==================
saf run

Start the solution using Docker compose
=======================================

To start the solution run the following command anywhere in the project:
To start the solution using Docker compose run the following commands in the root of the project:

1. Launch the solution

.. code:: bash

saf run
docker compose up

This command will build the required docker images and start the containers using docker compose as an orchestrator.

Build the Docker images
========================

To build the solution docker images (UI and API) run the following commands in the root of the project:


1. Build docker images:
.. tabs::

.. code-tab:: bash
:caption: PowerShell

docker build --build-arg SOLUTIONS_PRIVATE_PYPI_PAT=$env:SOLUTIONS_PRIVATE_PYPI_PAT --target solution_api -t {{cookiecutter.__project_name_slug}}-api:{{cookiecutter.__version}} .
{% if cookiecutter.with_dash_ui == "yes" %}
docker build --build-arg SOLUTIONS_PRIVATE_PYPI_PAT=$env:SOLUTIONS_PRIVATE_PYPI_PAT --target solution_ui -t {{cookiecutter.__project_name_slug}}-ui:{{cookiecutter.__version}} .
{% endif %}
.. code-tab:: bash
:caption: CMD

docker build --build-arg SOLUTIONS_PRIVATE_PYPI_PAT=$SOLUTIONS_PRIVATE_PYPI_PAT --target solution_api -t {{cookiecutter.__project_name_slug}}-api:{{cookiecutter.__version}} .
{% if cookiecutter.with_dash_ui == "yes" %}
docker build --build-arg SOLUTIONS_PRIVATE_PYPI_PAT=$SOLUTIONS_PRIVATE_PYPI_PAT --target solution_ui -t {{cookiecutter.__project_name_slug}}-ui:{{cookiecutter.__version}} .
{% endif %}


Documentation
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
version: '3'

services:

postgresql:
image: postgres:16.0
container_name: {{cookiecutter.__project_name_slug}}-database-container
networks:
- {{cookiecutter.__project_name_slug}}-internal
environment:
- POSTGRES_USER=glow
- POSTGRES_PASSWORD=glow
# don't expose ports, consumed internally by another container
volumes:
- glow-database:/var/lib/postgresql/data
healthcheck:
test: ["CMD", "pg_isready", "-U", "glow"]
interval: 5s
retries: 20

glow-api:
#image: {{cookiecutter.__project_name_slug}}-api:{{cookiecutter.__version}}
build:
context: .
dockerfile: ./Dockerfile
target: solution_api
args:
- SOLUTIONS_PRIVATE_PYPI_PAT=${SOLUTIONS_PRIVATE_PYPI_PAT}
container_name: {{cookiecutter.__project_name_slug}}-api-container
depends_on:
postgresql:
condition: service_healthy
networks:
- {{cookiecutter.__project_name_slug}}-external
- {{cookiecutter.__project_name_slug}}-internal
environment:
- GLOW_DEPLOYMENT=DockerCompose
# Following ones are optional
- GLOW_PROJECT_FILES_DIRECTORY=/projects
- GLOW_METHOD_EXECUTION_DIRECTORY=/transactions
- GLOW_API_HOST=0.0.0.0
- GLOW_API_PORT=50000
- GLOW_DEBUG=1
- GLOW_DATABASE_TYPE=postgresql
- GLOW_DATABASE_LOCATION=postgresql://glow:glow@{{cookiecutter.__project_name_slug}}-database-container:5432
ports:
- 50000:50000
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:50000/health"]
interval: 5s
retries: 20
volumes:
# All are optional
- glow-api-projects-data:/projects # Should match GLOW_PROJECT_FILES_DIRECTORY
- glow-api-transactions-data:/transactions # Should match GLOW_METHOD_EXECUTION_DIRECTORY
# uvicorn does not consume GLOW_API_HOST and GLOW_API_PORT.
command: "--host 0.0.0.0 --port 50000"
{% if cookiecutter.with_dash_ui == "yes" %}
glow-ui:
#image: {{cookiecutter.__project_name_slug}}-ui:{{cookiecutter.__version}}
build:
context: .
dockerfile: ./Dockerfile
target: solution_ui
args:
- SOLUTIONS_PRIVATE_PYPI_PAT=${SOLUTIONS_PRIVATE_PYPI_PAT}
networks:
- {{cookiecutter.__project_name_slug}}-external
environment:
- GLOW_DEPLOYMENT=DockerCompose
# Following ones are optional
- GLOW_DEBUG=1
- GLOW_UI_HOST=0.0.0.0
- GLOW_UI_PORT=50001
- GLOW_API_URL=http://{{cookiecutter.__project_name_slug}}-api-container:50000
depends_on:
glow-api:
condition: service_healthy
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:50001/health"]
interval: 5s
retries: 20
ports:
- 50001:50001
{% endif %}
volumes:
glow-database:
glow-api-projects-data:
glow-api-transactions-data:
networks:
{{cookiecutter.__project_name_slug}}-internal:
internal: true
{{cookiecutter.__project_name_slug}}-external:
driver: bridge
Loading