deployment refactoring

- update CI to correctly build docker images
- add requirement.txt
- update Dockerfile to correctly build the app
This commit is contained in:
Nicola Tarocco 2024-07-26 18:04:53 +02:00 committed by lrdossan
parent 66a5944489
commit 41ea92cac2
12 changed files with 263 additions and 243 deletions

1
.gitignore vendored
View file

@ -3,6 +3,7 @@ __pycache__
*.egg-info
*.DS_Store
*.pyc
dist
# Editor stuff
*.swp

View file

@ -1,7 +1,7 @@
stages:
- test
- docker-build
- oc-tag
- deploy
# Use the acc-py-devtools templates found at
# https://gitlab.cern.ch/-/ide/project/acc-co/devops/python/acc-py-devtools/blob/master/-/acc_py_devtools/templates/gitlab-ci/python.yml.
@ -18,6 +18,8 @@ variables:
.test-base:
image: registry.cern.ch/docker.io/library/python:${PY_VERSION}
stage: test
except:
- live/caimira-test # do not run tests on live/caimira-test branch
.test-run:
extends:
@ -62,11 +64,7 @@ test-cern-caimira-py39:
.test_openshift_config:
stage: test
rules:
- if: '$OC_TOKEN && $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME == $BRANCH'
allow_failure: true # The branch must represent what is deployed. FIXME: change to true because of a diff between ConfigMaps
- if: '$OC_TOKEN && $CI_MERGE_REQUEST_EVENT_TYPE != "detached"'
allow_failure: true # Anything other than the branch may fail without blocking the pipeline.
allow_failure: true
image: registry.cern.ch/docker.io/mambaorg/micromamba
before_script:
- micromamba create --yes -p $HOME/env python=3.9 ruamel.yaml wget -c conda-forge
@ -86,6 +84,9 @@ test-cern-caimira-py39:
paths:
- ./app-config/openshift/${CAIMIRA_INSTANCE}/actual
- ./app-config/openshift/${CAIMIRA_INSTANCE}/expected
only:
- master
- live/caimira-test # do not run tests on live/caimira-test branch
check_openshift_config_test:
extends: .test_openshift_config
@ -108,6 +109,7 @@ check_openshift_config_test:
# ###################################################################################################
# Build docker images
# base
.docker-build:
stage: docker-build
image:
@ -116,6 +118,7 @@ check_openshift_config_test:
name: gcr.io/kaniko-project/executor:debug
entrypoint: [""]
script:
- echo "Building image for ${CI_COMMIT_REF_NAME} branch with tag ${IMAGE_TAG}"
# Prepare Kaniko configuration file
- echo "{\"auths\":{\"$CI_REGISTRY\":{\"username\":\"$CI_REGISTRY_USER\",\"password\":\"$CI_REGISTRY_PASSWORD\"}}}" > /kaniko/.docker/config.json
- echo "Building ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:latest Docker image..."
@ -124,67 +127,95 @@ check_openshift_config_test:
# Print the full registry path of the pushed image
- echo "Image pushed successfully to ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG}"
.docker-build-live-test:
variables:
IMAGE_TAG: caimira-test-latest
extends: .docker-build
before_script:
- echo "Branch is $CI_COMMIT_REF_NAME"
- echo "Building image for live/caimira-test branch with tag ${IMAGE_TAG}"
only:
- live/caimira-test
.docker-build-release:
extends: .docker-build
before_script:
- echo "Tag is $CI_COMMIT_REF_NAME"
# Extract version number without 'v' prefix as IMAGE_TAG
- IMAGE_TAG=$(echo "$CI_COMMIT_REF_NAME" | sed 's/^v//')
- echo "Version is $IMAGE_TAG"
only:
- tags
build-auth-service-image:
extends:
- .docker-build-live-test
- .docker-build-release
.docker-build-auth-service:
variables:
IMAGE_NAME: auth-service
DOCKERFILE_DIRECTORY: app-config/auth-service
DOCKER_CONTEXT_DIRECTORY: app-config/auth-service
extends: .docker-build
build-calculator-app-image:
extends:
- .docker-build-live-test
- .docker-build-release
.docker-build-calculator-app:
variables:
IMAGE_NAME: calculator-app
DOCKERFILE_DIRECTORY: app-config/calculator-app
DOCKER_CONTEXT_DIRECTORY: ""
extends: .docker-build
# on push to live/caimira-test
.docker-build-test:
variables:
IMAGE_TAG: caimira-test-latest
docker-build-auth-service-test:
extends:
- .docker-build-test
- .docker-build-auth-service
only:
- live/caimira-test
docker-build-calculator-app-test:
extends:
- .docker-build-test
- .docker-build-calculator-app
only:
- live/caimira-test
# on release
.docker-build-release:
before_script:
# Extract version number without 'v' prefix as IMAGE_TAG
- IMAGE_TAG=$(echo "$CI_COMMIT_REF_NAME" | sed 's/^v//')
- echo "Version is $IMAGE_TAG"
docker-build-auth-service-release:
extends:
- .docker-build-release
- .docker-build-auth-service
only:
- tag
docker-build-calculator-app-release:
extends:
- .docker-build-release
- .docker-build-calculator-app
only:
- tag
# ###################################################################################################
# Link build Docker images OpenShift <-> GitLab registry
.link_docker_images_with_gitlab_registry:
stage: oc-tag
image: gitlab-registry.cern.ch/paas-tools/openshift-client:latest
# Deploy to OpenShift
.deploy:
stage: deploy
image: gitlab-registry.cern.ch/paas-tools/openshift-client
variables:
OC_PROJECT: "caimira-test"
OC_TOKEN: ${OPENSHIFT_CAIMIRA_TEST_DEPLOY_TOKEN}
IMAGE_TAG: caimira-test-latest
OPENSHIFT_SERVER: https://api.paas.okd.cern.ch
OPENSHIFT_PROJECT: caimira-test
script:
- oc tag --source=docker ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG} ${IMAGE_NAME}:latest --token ${OC_TOKEN} --server=https://api.paas.okd.cern.ch -n ${OC_PROJECT}
- echo "Deploying ${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG} to OpenShift"
- oc login $OPENSHIFT_SERVER --token=$OPENSHIFT_CAIMIRA_TEST_DEPLOY_TOKEN
- oc project $OPENSHIFT_PROJECT
- oc set image dc/$OPENSHIFT_DEPLOYMENT $OPENSHIFT_CONTAINER_NAME=${CI_REGISTRY_IMAGE}/${IMAGE_NAME}:${IMAGE_TAG}
- oc rollout status dc/$OPENSHIFT_DEPLOYMENT
only:
- live/caimira-test # for prod, we want to manually deploy the tag that we need
- live/caimira-test
link_auth-service_with_gitlab_registry:
extends:
- .link_docker_images_with_gitlab_registry
deploy-auth-service-test:
extends: .deploy
variables:
IMAGE_NAME: auth-service
OPENSHIFT_DEPLOYMENT: auth-service
OPENSHIFT_CONTAINER_NAME: auth-service
link_calculator-app_with_gitlab_registry:
extends:
- .link_docker_images_with_gitlab_registry
deploy-calculator-app-test:
extends: .deploy
variables:
IMAGE_NAME: calculator-app
OPENSHIFT_DEPLOYMENT: calculator-app
OPENSHIFT_CONTAINER_NAME: calculator-app
deploy-calculator-open-app-test:
extends: .deploy
variables:
IMAGE_NAME: calculator-app
OPENSHIFT_DEPLOYMENT: calculator-open-app
OPENSHIFT_CONTAINER_NAME: calculator-open-app

View file

@ -103,25 +103,25 @@ pip install -e . # At the root of the repository
### Running the Calculator app in development mode
```
python -m ui.apps.calculator
python -m cern_caimira.apps.calculator
```
To run with a specific template theme created:
```
python -m ui.apps.calculator --theme=ui/apps/templates/{theme}
python -m cern_caimira.apps.calculator --theme=ui/apps/templates/{theme}
```
To run the entire app in a different `APPLICATION_ROOT` path:
```
python -m ui.apps.calculator --app_root=/myroot
python -m cern_caimira.apps.calculator --app_root=/myroot
```
To run the calculator on a different URL path:
```
python -m ui.apps.calculator --prefix=/mycalc
python -m cern_caimira.apps.calculator --prefix=/mycalc
```
Each of these commands will start a local version of CAiMIRA, which can be visited at http://localhost:8080/.

View file

@ -1,22 +1,33 @@
FROM registry.cern.ch/docker.io/condaforge/mambaforge as conda
FROM registry.cern.ch/docker.io/condaforge/mambaforge AS conda
ARG PYTHON_VERSION=3.12
RUN mamba create --yes -p /opt/app python=${PYTHON_VERSION}
RUN mamba create --yes -p /opt/app python=3.9
COPY . /opt/app-source
RUN cd /opt/app-source && conda run -p /opt/app python -m pip install -r ./requirements.txt .[app]
WORKDIR /opt/app-source
# install Python deps
RUN cd cern_caimira \
&& conda run -p /opt/app python -m pip install -r requirements.txt
RUN cd caimira \
&& conda run -p /opt/app python -m pip install .
RUN cd cern_caimira \
&& conda run -p /opt/app python -m pip install .
COPY app-config/calculator-app/app.sh /opt/app/bin/calculator-app.sh
RUN cd /opt/app \
&& find -name '*.a' -delete \
&& rm -rf /opt/app/conda-meta \
&& rm -rf /opt/app/include \
&& find -name '__pycache__' -type d -exec rm -rf '{}' '+' \
&& rm -rf /opt/app/lib/python*/site-packages/pip /opt/app/lib/python*/idlelib /opt/app/lib/python*/ensurepip \
/opt/app/bin/x86_64-conda-linux-gnu-ld \
/opt/app/bin/sqlite3 \
/opt/app/bin/openssl \
/opt/app/share/terminfo \
&& find /opt/app/lib/ -name 'tests' -type d -exec rm -rf '{}' '+' \
&& find /opt/app/lib -name '*.pyx' -delete \
;
&& find -name '*.a' -delete \
&& rm -rf /opt/app/conda-meta \
&& rm -rf /opt/app/include \
&& find -name '__pycache__' -type d -exec rm -rf '{}' '+' \
&& rm -rf /opt/app/lib/python*/site-packages/pip /opt/app/lib/python*/idlelib /opt/app/lib/python*/ensurepip \
/opt/app/bin/x86_64-conda-linux-gnu-ld \
/opt/app/bin/sqlite3 \
/opt/app/bin/openssl \
/opt/app/share/terminfo \
&& find /opt/app/lib/ -name 'tests' -type d -exec rm -rf '{}' '+' \
&& find /opt/app/lib -name '*.pyx' -delete \
;
FROM registry.cern.ch/docker.io/library/debian
@ -25,12 +36,10 @@ ENV PATH=/opt/app/bin/:$PATH
# Make a convenient location to the installed CAiMIRA package (i.e. a directory called caimira in the CWD).
# It is important that this directory is also writable by a non-root user.
RUN mkdir -p /scratch \
&& chmod a+wx /scratch
&& chmod a+wx /scratch
# Set the HOME directory to something that anybody can write to (to support non root users, such as on openshift).
ENV HOME=/scratch
WORKDIR /scratch
RUN CAIMIRA_INIT_FILE=$(/opt/app/bin/python -c "import caimira; print(caimira.__file__)") \
&& ln -s $(dirname ${CAIMIRA_INIT_FILE}) /scratch/caimira
CMD [ \
"calculator-app.sh" \
]
RUN CERN_CAIMIRA_INIT_FILE=$(python -c "import cern_caimira; print(cern_caimira.__file__)") \
&& ln -s $(dirname ${CERN_CAIMIRA_INIT_FILE}) /scratch/cern_caimira
CMD [ "calculator-app.sh" ]

View file

@ -26,8 +26,8 @@ if [[ "$APP_NAME" == "calculator-app" ]]; then
export "DATA_SERVICE_ENABLED"="${DATA_SERVICE_ENABLED:=0}"
export "CAIMIRA_PROFILER_ENABLED"="${CAIMIRA_PROFILER_ENABLED:=0}"
echo "Starting the caimira webservice with: python -m ui.apps.calculator ${args[@]}"
python -m ui.apps.calculator "${args[@]}"
echo "Starting the caimira webservice with: python -m cern_caimira.apps.calculator ${args[@]}"
python -m cern_caimira.apps.calculator "${args[@]}"
else
echo "No APP_NAME specified"

View file

@ -1,103 +1,82 @@
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
tcp_nopush on;
tcp_nodelay on;
types_hash_max_size 2048;
include /usr/share/nginx/modules/*.conf;
server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name _;
root /opt/app-root/src;
events {
worker_connections 1024;
}
# Load configuration files for the default server block.
include /opt/app-root/etc/nginx.default.d/*.conf;
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
large_client_header_buffers 4 16k;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
error_page 404 /404.html;
location = /40x.html {
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
server {
listen 8080 default_server;
listen [::]:8080 default_server;
server_name _;
root /opt/app-root/src;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
# Load configuration files for the default server block.
include /opt/app-root/etc/nginx.default.d/*.conf;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
large_client_header_buffers 4 16k;
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_read_timeout 86400;
location /auth {
proxy_pass_request_body off;
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Content-Length "";
proxy_set_header If-None-Match "";
proxy_pass http://auth-service:8080;
}
location /auth {
proxy_pass_request_body off;
location @error401 {
# Store the request_uri (complete with args) to be redirected to
# when we hit /auth/complete.
add_header Set-Cookie "POST_AUTH_REDIRECT=$request_uri;";
return 302 /auth/login;
}
proxy_set_header Host $http_host;
proxy_set_header Content-Length "";
proxy_set_header If-None-Match "";
proxy_pass http://auth-service:8080;
}
location @proxy_404_error_handler {
# Pass the request on to the webservice. Most likely the URI won't
# exist so we get a 404 from that service instead (good as the 404
# pages are consistent).
proxy_pass http://calculator-app:8080/$request_uri;
}
location @error401 {
# Store the request_uri (complete with args) to be redirected to
# when we hit /auth/complete.
add_header Set-Cookie "POST_AUTH_REDIRECT=$request_uri;";
return 302 /auth/login;
}
# Redirect URLs to the new scheme.
absolute_redirect off;
location @proxy_404_error_handler {
# Pass the request on to the webservice. Most likely the URI won't
# exist so we get a 404 from that service instead (good as the 404
# pages are consistent).
proxy_pass http://calculator-app:8080/$request_uri;
}
location / {
# By default we have no authentication.
proxy_pass http://calculator-app:8080;
}
# Redirect URLs to the new scheme.
absolute_redirect off;
location /calculator {
return 302 /calculator-cern$is_args$args;
}
location / {
# By default we have no authentication.
proxy_pass http://calculator-app:8080;
}
location /calculator-cern {
# CERN calculator is authenticated.
auth_request /auth/probe;
error_page 401 = @error401;
location /calculator {
return 302 /calculator-cern$is_args$args;
}
# calculator-app is the name of the tornado server (for the calculator)
# in each of docker-compose, caimira-test.web.cern.ch and caimira.web.cern.ch.
proxy_pass http://calculator-app:8080/calculator-cern;
}
location /calculator-cern {
# CERN calculator is authenticated.
auth_request /auth/probe;
error_page 401 = @error401;
# calculator-app is the name of the tornado server (for the calculator)
# in each of docker-compose, caimira-test.web.cern.ch and caimira.web.cern.ch.
proxy_pass http://calculator-app:8080/calculator-cern;
}
location /calculator-open {
# Public open calculator
proxy_pass http://calculator-open-app:8080/calculator-open;
}
location /calculator-open {
# Public open calculator
proxy_pass http://calculator-open-app:8080/calculator-open;
}
}

View file

@ -56,9 +56,8 @@ doc = [
[project.urls]
Homepage = "https://github.com/cern/caimira"
[tool.setuptools]
packages = ["caimira"]
package-dir = {"" = "src"}
[tool.setuptools.package-data]
caimira = ["**/*"]
[tool.pytest.ini_options]
addopts = "--mypy"

View file

@ -56,9 +56,8 @@ doc = [
[project.urls]
Homepage = "https://cern.ch/caimira"
[tool.setuptools]
packages = ["cern_caimira"]
package-dir = {"" = "src"}
[tool.setuptools.package-data]
cern_caimira = ["**/*"]
[tool.pytest.ini_options]
addopts = "--mypy"

View file

@ -1,9 +1,8 @@
# Created by installing the caimira[core] extra and running:
# echo '.[app]' > requirements.txt
# pip list --format freeze | grep -vi caimira | grep -v pip | grep -v setuptools >> requirements.txt
.[app]
anyio==4.2.0
# Created by:
# 1. installing the caimira and cern_caimira
# 2. running `pip freeze > requirements.txt`
# 3. removing the local caimira and cern_caimira apps from the list
anyio==4.4.0
appnope==0.1.4
argon2-cffi==23.1.0
argon2-cffi-bindings==21.2.0
@ -11,114 +10,117 @@ arrow==1.3.0
asttokens==2.4.1
async-lru==2.0.4
attrs==23.2.0
Babel==2.14.0
Babel==2.15.0
beautifulsoup4==4.12.3
bleach==6.1.0
certifi==2024.2.2
certifi==2024.7.4
cffi==1.16.0
charset-normalizer==3.3.2
cloudpickle==3.0.0
comm==0.2.1
contourpy==1.2.0
comm==0.2.2
contourpy==1.2.1
cycler==0.12.1
debugpy==1.8.1
debugpy==1.8.2
decorator==5.1.1
defusedxml==0.7.1
exceptiongroup==1.2.0
executing==2.0.1
fastjsonschema==2.19.1
fonttools==4.49.0
fastjsonschema==2.20.0
fonttools==4.53.1
fqdn==1.5.1
h11==0.14.0
h3==3.7.6
httpcore==1.0.3
httpx==0.26.0
idna==3.6
importlib-metadata==7.0.1
importlib-resources==6.1.1
ipykernel==6.29.2
ipympl==0.9.3
ipython==8.18.1
h3==3.7.7
httpcore==1.0.5
httpx==0.27.0
idna==3.7
ipykernel==6.29.5
ipympl==0.9.4
ipython==8.26.0
ipython-genutils==0.2.0
ipywidgets==7.8.1
ipywidgets==7.8.2
isoduration==20.11.0
jedi==0.19.1
Jinja2==3.1.3
joblib==1.3.2
json5==0.9.14
jsonpointer==2.4
jsonschema==4.21.1
Jinja2==3.1.4
joblib==1.4.2
json5==0.9.25
jsonpointer==3.0.0
jsonschema==4.23.0
jsonschema-specifications==2023.12.1
jupyter-events==0.10.0
jupyter-lsp==2.2.5
jupyter_client==8.6.2
jupyter_core==5.7.2
jupyter_server==2.14.2
jupyter_server_terminals==0.5.3
jupyterlab==4.2.4
jupyterlab_pygments==0.3.0
jupyterlab_server==2.27.3
jupyterlab_widgets==1.1.8
kiwisolver==1.4.5
loky==3.4.1
MarkupSafe==2.1.5
matplotlib==3.8.3
matplotlib-inline==0.1.6
matplotlib==3.9.1
matplotlib-inline==0.1.7
memoization==0.4.0
mistune==3.0.2
nbclient==0.7.4
nbconvert==7.16.0
nbformat==5.9.2
nbclient==0.10.0
nbconvert==7.16.4
nbformat==5.10.4
nest-asyncio==1.6.0
notebook==7.1.0
notebook==7.2.1
notebook_shim==0.2.4
numpy==1.26.4
numpy==2.0.1
overrides==7.7.0
packaging==23.2
pandas==2.2.0
packaging==24.1
pandas==2.2.2
pandocfilters==1.5.1
parso==0.8.3
parso==0.8.4
pexpect==4.9.0
pillow==10.2.0
platformdirs==4.2.0
pillow==10.4.0
platformdirs==4.2.2
prometheus_client==0.20.0
prompt-toolkit==3.0.43
psutil==5.9.8
prompt_toolkit==3.0.47
psutil==6.0.0
ptyprocess==0.7.0
pure-eval==0.2.2
pure_eval==0.2.3
py==1.11.0
pycparser==2.21
Pygments==2.17.2
pycparser==2.22
Pygments==2.18.0
pyinstrument==4.6.2
PyJWT==2.8.0
pyparsing==3.1.1
python-dateutil==2.8.2
pyparsing==3.1.2
python-dateutil==2.9.0.post0
python-json-logger==2.0.7
pytz==2024.1
PyYAML==6.0.1
pyzmq==25.1.2
referencing==0.33.0
requests==2.31.0
pyzmq==26.0.3
referencing==0.35.1
requests==2.32.3
retry==0.9.2
rfc3339-validator==0.1.4
rfc3986-validator==0.1.1
rpds-py==0.18.0
rpds-py==0.19.1
ruptures==1.1.9
scikit-learn==1.4.1.post1
scipy==1.12.0
Send2Trash==1.8.2
scikit-learn==1.5.1
scipy==1.14.0
Send2Trash==1.8.3
setuptools==71.1.0
six==1.16.0
sniffio==1.3.0
sniffio==1.3.1
soupsieve==2.5
stack-data==0.6.3
terminado==0.18.0
threadpoolctl==3.3.0
timezonefinder==6.4.1
tinycss2==1.2.1
tomli==2.0.1
tornado==6.4
traitlets==5.14.1
types-python-dateutil==2.8.19.20240106
tabulate==0.9.0
terminado==0.18.1
threadpoolctl==3.5.0
timezonefinder==6.5.2
tinycss2==1.3.0
tornado==6.4.1
traitlets==5.14.3
types-python-dateutil==2.9.0.20240316
types-retry==0.9.9.4
typing_extensions==4.9.0
tzdata==2024.1
uri-template==1.3.0
urllib3==2.2.0
urllib3==2.2.2
wcwidth==0.2.13
webcolors==1.13
webcolors==24.6.0
webencodings==0.5.1
websocket-client==1.7.0
websockets==12.0
wheel==0.41.3
widgetsnbextension==3.6.6
zipp==3.17.0
websocket-client==1.8.0
widgetsnbextension==3.6.7

View file

@ -0,0 +1,3 @@
import importlib.metadata
__version__ = importlib.metadata.version(__package__ or __name__)

View file

@ -1,3 +0,0 @@
import importlib.metadata
__version__ = importlib.metadata.version(__package__ or __name__)