Skip to content

Commit 48dd214

Browse files
authored
Initial commit
0 parents  commit 48dd214

23 files changed

+1022
-0
lines changed

.flake8

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[flake8]
2+
exclude = .git,__pycache__,venv,.venv
3+
max-line-length = 120

.github/workflows/workflow.yml

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
# Documentation: https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsuses
2+
name: github_workflow
3+
run-name: GitHub Workflow
4+
5+
env:
6+
## Common environment variables
7+
# Service name (must be lowercase and not contain any spaces)
8+
SERVICE_NAME: ${{ vars.SERVICE_NAME }}
9+
# Path to the model
10+
MODEL_PATH: ${{ vars.MODEL_PATH }}
11+
# S3 access key ID
12+
S3_ACCESS_KEY_ID: ${{ secrets.S3_ACCESS_KEY_ID }}
13+
# S3 secret access key
14+
S3_SECRET_ACCESS_KEY: ${{ secrets.S3_SECRET_ACCESS_KEY }}
15+
16+
## Development environment variables
17+
# The URLs of the Core Engine to which the service should connect
18+
DEV_CORE_ENGINE_URLS: ${{ vars.DEV_CORE_ENGINE_URLS }}
19+
# The URL that the service (dev) should be accessible at
20+
DEV_SERVICE_URL: ${{ vars.DEV_SERVICE_URL }}
21+
# The Kubernetes namespace that the service should be deployed to
22+
DEV_NAMESPACE: ${{ vars.DEV_NAMESPACE }}
23+
# Maximum number of tasks the service can accept
24+
DEV_MAX_TASKS: ${{ vars.DEV_MAX_TASKS }}
25+
# Number of retries on the Engine for announcement
26+
DEV_ENGINE_ANNOUNCE_RETRIES: ${{ vars.DEV_ENGINE_ANNOUNCE_RETRIES }}
27+
# Delay between each retry
28+
DEV_ENGINE_ANNOUNCE_RETRY_DELAY: ${{ vars.DEV_ENGINE_ANNOUNCE_RETRY_DELAY }}
29+
# Logging level
30+
DEV_LOG_LEVEL: ${{ vars.DEV_LOG_LEVEL }}
31+
# Kube configuration
32+
DEV_KUBE_CONFIG: ${{ secrets.DEV_KUBE_CONFIG }}
33+
34+
## Production environment variables
35+
# The URLs of the Core Engine to which the service should connect
36+
PROD_CORE_ENGINE_URLS: ${{ vars.PROD_CORE_ENGINE_URLS }}
37+
# The URL that the service (dev) should be accessible at
38+
PROD_SERVICE_URL: ${{ vars.PROD_SERVICE_URL }}
39+
# The Kubernetes namespace that the service should be deployed to
40+
PROD_NAMESPACE: ${{ vars.PROD_NAMESPACE }}
41+
# Maximum number of tasks the service can accept
42+
PROD_MAX_TASKS: ${{ vars.PROD_MAX_TASKS }}
43+
# Number of retries on the Engine for announcement
44+
PROD_ENGINE_ANNOUNCE_RETRIES: ${{ vars.PROD_ENGINE_ANNOUNCE_RETRIES }}
45+
# Delay between each retry
46+
PROD_ENGINE_ANNOUNCE_RETRY_DELAY: ${{ vars.PROD_ENGINE_ANNOUNCE_RETRY_DELAY }}
47+
# Logging level
48+
PROD_LOG_LEVEL: ${{ vars.PROD_LOG_LEVEL }}
49+
# Kube configuration
50+
PROD_KUBE_CONFIG: ${{ secrets.PROD_KUBE_CONFIG }}
51+
52+
# Allow one concurrent deployment
53+
concurrency:
54+
group: github_workflow
55+
cancel-in-progress: true
56+
57+
on:
58+
push:
59+
branches:
60+
- main
61+
- prod
62+
63+
pull_request:
64+
65+
# Allows you to run this workflow manually from the Actions tab
66+
workflow_dispatch:
67+
68+
jobs:
69+
review:
70+
runs-on: ubuntu-latest
71+
if: ${{ vars.RUN_CICD == 'true' }}
72+
steps:
73+
- name: Clone repository
74+
uses: actions/checkout@v4
75+
76+
- name: Lint Python app
77+
uses: swiss-ai-center/common-code/.github/actions/lint-python-app@main
78+
with:
79+
python-app-path: ./model-serving
80+
81+
train:
82+
needs: review
83+
runs-on: ubuntu-latest
84+
if: ${{ vars.RUN_CICD == 'true' }}
85+
steps:
86+
- name: Clone repository
87+
uses: actions/checkout@v4
88+
89+
- name: Run ML experiment with DVC
90+
uses: swiss-ai-center/common-code/.github/actions/run-ml-experiment-with-dvc@main
91+
with:
92+
ml-experiment-path: ./model-creation
93+
s3-access-key-id: ${{ env.S3_ACCESS_KEY_ID }}
94+
s3-secret-access-key: ${{ env.S3_SECRET_ACCESS_KEY }}
95+
96+
- name: Upload model
97+
uses: actions/upload-artifact@v4
98+
with:
99+
name: model
100+
path: ${{ env.MODEL_PATH }}
101+
retention-days: 5
102+
103+
test:
104+
needs: train
105+
runs-on: ubuntu-latest
106+
if: ${{ vars.RUN_CICD == 'true' }}
107+
steps:
108+
- name: Clone repository
109+
uses: actions/checkout@v4
110+
111+
- name: Download model
112+
uses: actions/download-artifact@v3
113+
with:
114+
name: model
115+
path: ./model-serving
116+
117+
- name: Test Python app
118+
uses: swiss-ai-center/common-code/.github/actions/test-python-app@main
119+
with:
120+
python-app-path: ./model-serving
121+
token: ${{ secrets.GITHUB_TOKEN }}
122+
123+
release:
124+
needs: test
125+
runs-on: ubuntu-latest
126+
if: ${{ vars.RUN_CICD == 'true' && success() && github.ref == 'refs/heads/main' && (vars.DEPLOY_DEV == 'true' || vars.DEPLOY_PROD == 'true') }}
127+
steps:
128+
- name: Clone repository
129+
uses: actions/checkout@v4
130+
131+
- name: Download model
132+
uses: actions/download-artifact@v4
133+
with:
134+
name: model
135+
path: ./model-serving
136+
137+
- name: Build and push Docker image to GitHub
138+
id: build-and-push-docker-image-to-github
139+
uses: swiss-ai-center/common-code/.github/actions/build-and-push-docker-image-to-github@main
140+
with:
141+
docker-registry-username: ${{ github.actor }}
142+
docker-registry-password: ${{ secrets.GITHUB_TOKEN }}
143+
docker-image-name: ${{ github.repository }}
144+
docker-image-context: ./model-serving
145+
outputs:
146+
docker-image-tags: ${{ steps.build-and-push-docker-image-to-github.outputs.docker-image-tags }}
147+
148+
deploy-dev:
149+
needs: release
150+
runs-on: ubuntu-latest
151+
if: ${{ vars.RUN_CICD == 'true' && success() && github.ref == 'refs/heads/main' && vars.DEPLOY_DEV == 'true' }}
152+
steps:
153+
- name: Clone repository
154+
uses: actions/checkout@v4
155+
156+
- name: Get service Docker image SHA tag
157+
shell: bash
158+
run: |
159+
docker_image_tags=(${{ needs.release.outputs.docker-image-tags }})
160+
docker_image_sha_tag="${docker_image_tags[1]}"
161+
echo "SERVICE_DOCKER_IMAGE_SHA_TAG=$docker_image_sha_tag" >> "$GITHUB_ENV"
162+
163+
- name: Prepare configuration files
164+
uses: swiss-ai-center/common-code/.github/actions/prepare-kubernetes-configuration-files@main
165+
with:
166+
service-name: ${{ env.SERVICE_NAME }}
167+
service-url: ${{ env.DEV_SERVICE_URL }}
168+
service-docker-image-tag: ${{ env.SERVICE_DOCKER_IMAGE_SHA_TAG }}
169+
configuration-files-location: ./model-serving/kubernetes
170+
environment: development
171+
log-level: ${{ env.DEV_LOG_LEVEL }}
172+
engine-urls: ${{ env.DEV_CORE_ENGINE_URLS }}
173+
max-tasks: ${{ env.DEV_MAX_TASKS }}
174+
engine-announce-retries: ${{ env.DEV_ENGINE_ANNOUNCE_RETRIES }}
175+
engine-announce-retry-delay: ${{ env.DEV_ENGINE_ANNOUNCE_RETRY_DELAY }}
176+
177+
- name: Remove unnecessary keys from configuration files
178+
uses: swiss-ai-center/common-code/.github/actions/remove-unnecessary-keys-from-kubernetes-configuration-files@main
179+
with:
180+
configuration-files-location: ./kubernetes
181+
182+
- name: Deploy service on the Kubernetes cluster
183+
uses: swiss-ai-center/common-code/.github/actions/execute-command-on-kubernetes-cluster@main
184+
with:
185+
kube-config: ${{ env.DEV_KUBE_CONFIG }}
186+
kube-namespace: ${{ env.DEV_NAMESPACE }}
187+
kubectl-context: ./model-serving/kubernetes
188+
kubectl-args: |
189+
apply \
190+
-f config-map.yml \
191+
-f stateful.yml \
192+
-f service.yml \
193+
-f ingress.yml
194+
195+
deploy-prod:
196+
needs: release
197+
runs-on: ubuntu-latest
198+
if: ${{ vars.RUN_CICD == 'true' && success() && github.ref == 'refs/heads/main' && vars.DEPLOY_PROD == 'true' }}
199+
steps:
200+
- name: Clone repository
201+
uses: actions/checkout@v4
202+
203+
- name: Get service Docker image SHA tag
204+
shell: bash
205+
run: |
206+
docker_image_tags=(${{ needs.release.outputs.docker-image-tags }})
207+
docker_image_sha_tag="${docker_image_tags[1]}"
208+
echo "SERVICE_DOCKER_IMAGE_SHA_TAG=$docker_image_sha_tag" >> "$GITHUB_ENV"
209+
210+
- name: Prepare configuration files
211+
uses: swiss-ai-center/common-code/.github/actions/prepare-kubernetes-configuration-files@main
212+
with:
213+
service-name: ${{ env.SERVICE_NAME }}
214+
service-url: ${{ env.PROD_SERVICE_URL }}
215+
service-docker-image-tag: ${{ env.SERVICE_DOCKER_IMAGE_SHA_TAG }}
216+
configuration-files-location: ./model-serving/kubernetes
217+
environment: production
218+
log-level: ${{ env.PROD_LOG_LEVEL }}
219+
engine-urls: ${{ env.PROD_CORE_ENGINE_URLS }}
220+
max-tasks: ${{ env.PROD_MAX_TASKS }}
221+
engine-announce-retries: ${{ env.PROD_ENGINE_ANNOUNCE_RETRIES }}
222+
engine-announce-retry-delay: ${{ env.PROD_ENGINE_ANNOUNCE_RETRY_DELAY }}
223+
224+
- name: Deploy service on the Kubernetes cluster
225+
uses: swiss-ai-center/common-code/.github/actions/execute-command-on-kubernetes-cluster@main
226+
with:
227+
kube-config: ${{ env.PROD_KUBE_CONFIG }}
228+
kube-namespace: ${{ env.PROD_NAMESPACE }}
229+
kubectl-context: ./model-serving/kubernetes
230+
kubectl-args: |
231+
apply \
232+
-f config-map.yml \
233+
-f stateful.yml \
234+
-f service.yml \
235+
-f ingress.yml

.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
## Python
2+
3+
# Environments
4+
.venv
5+
venv
6+
7+
# Byte-compiled / optimized / DLL files
8+
__pycache__/
9+
10+
# Pytest cache
11+
.pytest_cache
12+
13+
# Pytest Coverage
14+
.coverage
15+
16+
## IntelliJ's IDEs
17+
18+
.idea
19+
20+
## Visual Studio Code
21+
22+
.vscode
23+
24+
## macOS
25+
26+
.DS_Store

README.md

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
# Create a new service (model from scratch) template
2+
3+
This repository contains the Python + FastAPI template to create a service with
4+
a model built from scratch compatible with the Core engine.
5+
6+
Please read the documentation at
7+
<https://docs.swiss-ai-center.ch/how-to-guides/how-to-create-a-new-service> to
8+
understand how to use this template.
9+
10+
## Guidelines
11+
12+
TODO: Add instructions on how to edit this template.
13+
14+
### Publishing and deploying using a CI/CD pipeline
15+
16+
This is the recommended way to publish and deploy your service if you have
17+
access to GitHub Actions or GitLab CI.
18+
19+
TODO
20+
21+
### Publishing and deploying manually
22+
23+
This is the recommended way to publish and deploy your service if you do not
24+
have access to GitHub Actions or GitLab CI or do not want to use these services.
25+
26+
TODO
27+
28+
## Checklist
29+
30+
These checklists allow you to ensure everything is set up correctly.
31+
32+
### Common tasks
33+
34+
- [ ] Rename the project in the [`pyproject.toml`](./pyproject.toml) file
35+
- [x] Add files that must be ignored to the [`.gitignore`](.gitignore) configuration file
36+
- [ ] TODO
37+
38+
### Publishing and deploying using a CI/CD pipeline
39+
40+
> [!NOTE]
41+
> This checklist is specific to the _Publishing and deploying using a CI/CD
42+
> pipeline_ section.
43+
44+
- [x] Add the environment variables
45+
- [ ] TODO
46+
47+
### Publishing and deploying manually
48+
49+
> [!NOTE]
50+
> This checklist is specific to the _Publishing and deploying manually_ section.
51+
52+
- [x] Edit the [`.env`](.env) configuration file
53+
- [ ] TODO

model-creation/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# model-creation
2+
3+
<!-- TODO 1: Update the description of the service and the link to the documentation. -->
4+
_Check the [related documentation](https://docs.swiss-ai-center.ch/reference/core-concepts/service/) for more information._

model-serving/.env

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Log level
2+
LOG_LEVEL=debug
3+
4+
# Environment
5+
ENVIRONMENT=development
6+
7+
# The Core engine URLs
8+
ENGINE_URLS=["http://localhost:8080"]
9+
10+
# The Service Port
11+
SERVICE_PORT=9090
12+
13+
# The Service URL+Port
14+
SERVICE_URL="http://host.docker.internal:${SERVICE_PORT}"
15+
16+
# The maximum of tasks the service can process
17+
MAX_TASKS=50
18+
19+
# The number of times the service tries to announce itself to the engine
20+
ENGINE_ANNOUNCE_RETRIES=5
21+
22+
# The number of seconds between each retry
23+
ENGINE_ANNOUNCE_RETRY_DELAY=3

model-serving/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Check parent `.gitignore` file for complete ignored files

model-serving/Dockerfile

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Base image
2+
FROM python:3.11
3+
4+
# Install all required packages to run the model
5+
# TODO: 1. Add any additional packages required to run your model
6+
# RUN apt update && apt install --yes package1 package2 ...
7+
8+
# Work directory
9+
WORKDIR /app
10+
11+
# Copy requirements file
12+
COPY ./requirements.txt .
13+
COPY ./requirements-all.txt .
14+
15+
# Install dependencies
16+
RUN pip install --requirement requirements.txt --requirement requirements-all.txt
17+
18+
# Copy sources
19+
COPY src src
20+
21+
# Copy model
22+
# TODO: 2. Change the name of the model file to match the name of your model file
23+
COPY mnist_model.h5 .
24+
25+
# Environment variables
26+
ENV ENVIRONMENT=${ENVIRONMENT}
27+
ENV LOG_LEVEL=${LOG_LEVEL}
28+
ENV ENGINE_URL=${ENGINE_URL}
29+
ENV MAX_TASKS=${MAX_TASKS}
30+
ENV ENGINE_ANNOUNCE_RETRIES=${ENGINE_ANNOUNCE_RETRIES}
31+
ENV ENGINE_ANNOUNCE_RETRY_DELAY=${ENGINE_ANNOUNCE_RETRY_DELAY}
32+
33+
# Exposed ports
34+
EXPOSE 80
35+
36+
# Switch to src directory
37+
WORKDIR "/app/src"
38+
39+
# Command to run on start
40+
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]

model-serving/README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# model-serving
2+
3+
<!-- TODO 1: Update the description of the service and the link to the documentation. -->
4+
_Check the [related documentation](https://docs.swiss-ai-center.ch/reference/core-concepts/service/) for more information._

0 commit comments

Comments
 (0)