sachin1801 commited on
Commit
da11987
·
1 Parent(s): 489e004

feat: add Docker containerization and CI/CD pipeline

Browse files

- Add multi-stage Dockerfile with Python 3.10 and ViennaRNA
- Add docker-compose.yml for local development
- Add docker-compose.prod.yml for production deployment
- Add GitHub Actions workflows for CI, test deploy, and prod deploy
- Add Apache reverse proxy configuration with WebSocket support
- Add server setup script for Ubuntu 24.04
- Add comprehensive deployment documentation

.dockerignore ADDED
@@ -0,0 +1,42 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Git
2
+ .git
3
+ .gitignore
4
+
5
+ # Python
6
+ __pycache__
7
+ *.py[cod]
8
+ *$py.class
9
+ *.so
10
+ .Python
11
+ venv*/
12
+ .venv
13
+ pip-log.txt
14
+
15
+ # IDE
16
+ .vscode
17
+ .idea
18
+ *.swp
19
+ *.swo
20
+
21
+ # Testing
22
+ .pytest_cache
23
+ .coverage
24
+ htmlcov/
25
+
26
+ # Environment
27
+ .env
28
+ .env.*
29
+ *.local
30
+
31
+ # Build artifacts
32
+ *.egg-info
33
+ dist/
34
+ build/
35
+
36
+ # Documentation
37
+ *.md
38
+ !webapp/TODO.md
39
+
40
+ # OS files
41
+ .DS_Store
42
+ Thumbs.db
.github/workflows/ci.yml ADDED
@@ -0,0 +1,57 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main, develop]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ jobs:
10
+ test:
11
+ runs-on: ubuntu-latest
12
+ steps:
13
+ - uses: actions/checkout@v4
14
+
15
+ - name: Set up Python 3.10
16
+ uses: actions/setup-python@v5
17
+ with:
18
+ python-version: '3.10'
19
+
20
+ - name: Install ViennaRNA
21
+ run: |
22
+ sudo apt-get update
23
+ sudo apt-get install -y vienna-rna
24
+
25
+ - name: Install dependencies
26
+ run: |
27
+ python -m pip install --upgrade pip
28
+ pip install -r webapp/requirements.txt
29
+
30
+ - name: Run tests
31
+ run: |
32
+ cd webapp && python -m pytest tests/ -v
33
+
34
+ build:
35
+ runs-on: ubuntu-latest
36
+ needs: test
37
+ steps:
38
+ - uses: actions/checkout@v4
39
+
40
+ - name: Set up Docker Buildx
41
+ uses: docker/setup-buildx-action@v3
42
+
43
+ - name: Build Docker image
44
+ uses: docker/build-push-action@v5
45
+ with:
46
+ context: .
47
+ push: false
48
+ tags: splicing-model:${{ github.sha }}
49
+ cache-from: type=gha
50
+ cache-to: type=gha,mode=max
51
+
52
+ - name: Test Docker image
53
+ run: |
54
+ docker run -d --name test-container -p 8000:8000 splicing-model:${{ github.sha }}
55
+ sleep 10
56
+ curl -f http://localhost:8000/api/health || exit 1
57
+ docker stop test-container
.github/workflows/deploy-prod.yml ADDED
@@ -0,0 +1,56 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Deploy to Production
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - 'v*.*.*'
7
+
8
+ env:
9
+ REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
10
+ IMAGE_NAME: splicing-model
11
+
12
+ jobs:
13
+ promote-and-deploy:
14
+ runs-on: ubuntu-latest
15
+ environment: production
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Login to Container Registry
20
+ uses: docker/login-action@v3
21
+ with:
22
+ registry: ${{ secrets.REGISTRY_URL }}
23
+ username: ${{ secrets.REGISTRY_USERNAME }}
24
+ password: ${{ secrets.REGISTRY_PASSWORD }}
25
+
26
+ - name: Get version tag
27
+ id: version
28
+ run: echo "tag=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
29
+
30
+ - name: Pull and retag test image for production
31
+ run: |
32
+ # Pull the test-latest image (already validated)
33
+ docker pull ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:test-latest
34
+
35
+ # Tag for production
36
+ docker tag ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:test-latest \
37
+ ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
38
+ docker tag ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:test-latest \
39
+ ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:prod-latest
40
+
41
+ # Push production tags
42
+ docker push ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:${{ steps.version.outputs.tag }}
43
+ docker push ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}:prod-latest
44
+
45
+ - name: Deploy to Production Server
46
+ uses: appleboy/ssh-action@v1.0.0
47
+ with:
48
+ host: ${{ secrets.PROD_SERVER_HOST }}
49
+ username: ${{ secrets.PROD_SERVER_USER }}
50
+ key: ${{ secrets.PROD_SERVER_SSH_KEY }}
51
+ script: |
52
+ cd /opt/splicing-model
53
+ docker pull ${{ secrets.REGISTRY_URL }}/splicing-model:prod-latest
54
+ docker-compose -f docker-compose.prod.yml down
55
+ IMAGE_TAG=prod-latest docker-compose -f docker-compose.prod.yml up -d
56
+ docker system prune -f
.github/workflows/deploy-test.yml ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Deploy to Test
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+
7
+ env:
8
+ REGISTRY_URL: ${{ secrets.REGISTRY_URL }}
9
+ IMAGE_NAME: splicing-model
10
+
11
+ jobs:
12
+ build-and-push:
13
+ runs-on: ubuntu-latest
14
+ outputs:
15
+ image_tag: ${{ steps.meta.outputs.tags }}
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+
19
+ - name: Set up Docker Buildx
20
+ uses: docker/setup-buildx-action@v3
21
+
22
+ - name: Login to Container Registry
23
+ uses: docker/login-action@v3
24
+ with:
25
+ registry: ${{ secrets.REGISTRY_URL }}
26
+ username: ${{ secrets.REGISTRY_USERNAME }}
27
+ password: ${{ secrets.REGISTRY_PASSWORD }}
28
+
29
+ - name: Extract metadata
30
+ id: meta
31
+ uses: docker/metadata-action@v5
32
+ with:
33
+ images: ${{ env.REGISTRY_URL }}/${{ env.IMAGE_NAME }}
34
+ tags: |
35
+ type=sha,prefix=
36
+ type=raw,value=test-latest
37
+
38
+ - name: Build and push
39
+ uses: docker/build-push-action@v5
40
+ with:
41
+ context: .
42
+ push: true
43
+ tags: ${{ steps.meta.outputs.tags }}
44
+ cache-from: type=gha
45
+ cache-to: type=gha,mode=max
46
+
47
+ deploy:
48
+ runs-on: ubuntu-latest
49
+ needs: build-and-push
50
+ environment: test
51
+ steps:
52
+ - name: Deploy to Test Server
53
+ uses: appleboy/ssh-action@v1.0.0
54
+ with:
55
+ host: ${{ secrets.TEST_SERVER_HOST }}
56
+ username: ${{ secrets.TEST_SERVER_USER }}
57
+ key: ${{ secrets.TEST_SERVER_SSH_KEY }}
58
+ script: |
59
+ cd /opt/splicing-model
60
+ docker pull ${{ secrets.REGISTRY_URL }}/splicing-model:test-latest
61
+ docker-compose -f docker-compose.prod.yml down
62
+ IMAGE_TAG=test-latest docker-compose -f docker-compose.prod.yml up -d
63
+ docker system prune -f
DEPLOYMENT.md ADDED
@@ -0,0 +1,877 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Deployment Documentation
2
+
3
+ This document describes the Docker containerization and CI/CD pipeline setup for the Interpretable Splicing Model webapp.
4
+
5
+ ---
6
+
7
+ ## Table of Contents
8
+
9
+ 1. [Overview](#overview)
10
+ 2. [Architecture Decisions](#architecture-decisions)
11
+ 3. [Files Created](#files-created)
12
+ 4. [How Each Component Works](#how-each-component-works)
13
+ 5. [Deployment Flow](#deployment-flow)
14
+ 6. [Configuration Reference](#configuration-reference)
15
+ 7. [Common Modifications](#common-modifications)
16
+ 8. [Next Steps](#next-steps)
17
+ 9. [Troubleshooting](#troubleshooting)
18
+
19
+ ---
20
+
21
+ ## Overview
22
+
23
+ The webapp is containerized using Docker for deployment on Ubuntu 24.04 VMs with Apache as a reverse proxy. The CI/CD pipeline uses GitHub Actions to automatically:
24
+
25
+ - Run tests on every push/PR
26
+ - Build and push Docker images
27
+ - Deploy to test environment on `main` branch pushes
28
+ - Deploy to production on version tags (e.g., `v1.0.0`)
29
+
30
+ ### Key Points
31
+
32
+ - **No GPU required**: The TensorFlow model runs on CPU only
33
+ - **SQLite database**: Persisted via Docker volumes
34
+ - **Image promotion**: Test images are promoted to production (not rebuilt)
35
+ - **WebSocket support**: Apache configured for PyShiny interactive visualizations
36
+
37
+ ---
38
+
39
+ ## Architecture Decisions
40
+
41
+ | Decision | Choice | Rationale |
42
+ |----------|--------|-----------|
43
+ | CI/CD Platform | GitHub Actions | Native GitHub integration, free for public repos |
44
+ | Container Registry | Self-hosted (configurable) | Flexibility for on-premise or cloud registries |
45
+ | Web Server | Apache Reverse Proxy | Required by deployment environment (Ubuntu VMs) |
46
+ | Python Version | 3.10 | Required for TensorFlow 2.15 (Keras 2 compatibility) |
47
+ | Base Image | `python:3.10-slim-bookworm` | Debian-based, includes ViennaRNA package |
48
+ | Branch Strategy | `main` → test, tags → prod | Clear separation of environments |
49
+ | Secrets Management | GitHub Secrets + env files | Secure CI/CD + runtime configuration |
50
+ | Database Persistence | Docker named volumes | Survives container restarts/updates |
51
+
52
+ ---
53
+
54
+ ## Files Created
55
+
56
+ ### Docker Files
57
+
58
+ | File | Purpose |
59
+ |------|---------|
60
+ | `Dockerfile` | Multi-stage build for the webapp container |
61
+ | `.dockerignore` | Excludes unnecessary files from build context |
62
+ | `docker-compose.yml` | Local development configuration |
63
+ | `docker-compose.prod.yml` | Production deployment template |
64
+
65
+ ### GitHub Actions Workflows
66
+
67
+ | File | Purpose |
68
+ |------|---------|
69
+ | `.github/workflows/ci.yml` | Runs tests and builds Docker image on push/PR |
70
+ | `.github/workflows/deploy-test.yml` | Deploys to test server on `main` push |
71
+ | `.github/workflows/deploy-prod.yml` | Deploys to production on version tags |
72
+
73
+ ### Deployment Configuration
74
+
75
+ | File | Purpose |
76
+ |------|---------|
77
+ | `deploy/apache-site.conf` | Apache virtual host configuration |
78
+ | `deploy/server-setup.sh` | Server initialization script for Ubuntu 24.04 |
79
+
80
+ ---
81
+
82
+ ## How Each Component Works
83
+
84
+ ### 1. Dockerfile
85
+
86
+ ```
87
+ Location: /Dockerfile
88
+ ```
89
+
90
+ **What it does:**
91
+ - Uses multi-stage build to minimize final image size
92
+ - Stage 1 (builder): Compiles Python wheels from requirements
93
+ - Stage 2 (production): Installs ViennaRNA and pre-built wheels
94
+
95
+ **Key sections:**
96
+
97
+ ```dockerfile
98
+ # Stage 1: Build wheels
99
+ FROM python:3.10-slim-bookworm AS builder
100
+ # Installs build-essential for compiling C extensions
101
+ # Creates wheels in /app/wheels
102
+
103
+ # Stage 2: Production image
104
+ FROM python:3.10-slim-bookworm
105
+ # Installs vienna-rna system package
106
+ # Copies and installs pre-built wheels (faster, smaller)
107
+ # Copies application code: webapp/, figures/, output/, data/
108
+ # Creates non-root user 'appuser' for security
109
+ # Exposes port 8000
110
+ # Configures health check against /api/health
111
+ # Runs uvicorn on 0.0.0.0:8000
112
+ ```
113
+
114
+ **What to change if:**
115
+
116
+ | Scenario | What to modify |
117
+ |----------|----------------|
118
+ | Add system dependency | Add to `apt-get install` in Stage 2 |
119
+ | Add Python dependency | Add to `webapp/requirements.txt` (wheels auto-built) |
120
+ | Change port | Modify `EXPOSE` and `CMD` uvicorn port |
121
+ | Add more app directories | Add additional `COPY` statements |
122
+ | Increase health check timeout | Modify `HEALTHCHECK` parameters |
123
+
124
+ ---
125
+
126
+ ### 2. .dockerignore
127
+
128
+ ```
129
+ Location: /.dockerignore
130
+ ```
131
+
132
+ **What it does:**
133
+ - Excludes files from Docker build context to speed up builds and reduce image size
134
+ - Prevents sensitive files (`.env`, credentials) from being included
135
+
136
+ **What to change if:**
137
+
138
+ | Scenario | What to modify |
139
+ |----------|----------------|
140
+ | Include a previously ignored file | Remove from `.dockerignore` |
141
+ | Exclude additional files | Add pattern to `.dockerignore` |
142
+ | Include specific markdown file | Add exception like `!filename.md` |
143
+
144
+ ---
145
+
146
+ ### 3. docker-compose.yml (Local Development)
147
+
148
+ ```
149
+ Location: /docker-compose.yml
150
+ ```
151
+
152
+ **What it does:**
153
+ - Builds the Docker image locally
154
+ - Mounts `./webapp` as read-only volume for code changes
155
+ - Exposes port 8000 on localhost
156
+ - Creates `db-data` volume for database persistence
157
+
158
+ **Usage:**
159
+ ```bash
160
+ # Build and start
161
+ docker-compose up --build
162
+
163
+ # Start in background
164
+ docker-compose up -d
165
+
166
+ # Stop
167
+ docker-compose down
168
+
169
+ # View logs
170
+ docker-compose logs -f
171
+ ```
172
+
173
+ **What to change if:**
174
+
175
+ | Scenario | What to modify |
176
+ |----------|----------------|
177
+ | Change local port | Modify `ports: - "8000:8000"` to `"NEW_PORT:8000"` |
178
+ | Add environment variable | Add under `environment:` section |
179
+ | Mount additional directory | Add under `volumes:` section |
180
+ | Enable debug mode | Set `DEBUG=true` in environment |
181
+
182
+ ---
183
+
184
+ ### 4. docker-compose.prod.yml (Production)
185
+
186
+ ```
187
+ Location: /docker-compose.prod.yml
188
+ ```
189
+
190
+ **What it does:**
191
+ - Pulls image from container registry (not builds locally)
192
+ - Binds only to localhost (Apache proxies external traffic)
193
+ - Uses external `splicing-db` volume for database
194
+ - Loads secrets from `.env.production` file
195
+ - Configures log rotation (10MB max, 3 files)
196
+ - Auto-restarts on failure (`unless-stopped`)
197
+
198
+ **Key differences from development:**
199
+ - Uses pre-built image from registry
200
+ - Port bound to `127.0.0.1` only (not exposed externally)
201
+ - Uses external volume (must be created manually)
202
+ - Loads production environment file
203
+
204
+ **Environment variables required in `.env.production`:**
205
+ ```bash
206
+ # Create on server at /opt/splicing-model/.env.production
207
+ DEBUG=false
208
+ # Add any other production-specific settings
209
+ ```
210
+
211
+ **What to change if:**
212
+
213
+ | Scenario | What to modify |
214
+ |----------|----------------|
215
+ | Change registry URL | Set `REGISTRY_URL` environment variable |
216
+ | Change image tag | Set `IMAGE_TAG` environment variable |
217
+ | Add production secret | Add to `.env.production` file on server |
218
+ | Change log retention | Modify `logging.options` section |
219
+ | Change volume mount path | Modify `volumes:` section |
220
+
221
+ ---
222
+
223
+ ### 5. CI Workflow (.github/workflows/ci.yml)
224
+
225
+ ```
226
+ Location: /.github/workflows/ci.yml
227
+ ```
228
+
229
+ **What it does:**
230
+ - Triggers on push to `main`/`develop` and PRs to `main`
231
+ - **Test job**: Installs dependencies, runs pytest
232
+ - **Build job**: Builds Docker image, runs container health check
233
+
234
+ **Workflow steps:**
235
+
236
+ ```
237
+ Push/PR → Test Job → Build Job
238
+ ↓ ↓
239
+ Run pytest Build image
240
+
241
+ Start container
242
+
243
+ Health check
244
+ ```
245
+
246
+ **What to change if:**
247
+
248
+ | Scenario | What to modify |
249
+ |----------|----------------|
250
+ | Add branch to CI | Add to `branches:` list under `on.push` |
251
+ | Add test command | Modify the `Run tests` step |
252
+ | Change Python version | Modify `python-version` in setup-python |
253
+ | Add CI environment variable | Add `env:` section to job or step |
254
+ | Skip tests for certain paths | Add `paths-ignore:` under `on.push` |
255
+
256
+ ---
257
+
258
+ ### 6. Test Deployment Workflow (.github/workflows/deploy-test.yml)
259
+
260
+ ```
261
+ Location: /.github/workflows/deploy-test.yml
262
+ ```
263
+
264
+ **What it does:**
265
+ - Triggers on push to `main` branch only
266
+ - Builds Docker image and pushes to registry with tags:
267
+ - `<commit-sha>` (for traceability)
268
+ - `test-latest` (for deployment)
269
+ - SSHs into test server and deploys new image
270
+
271
+ **Workflow steps:**
272
+
273
+ ```
274
+ Push to main → Build & Push → Deploy to Test
275
+ ↓ ↓
276
+ Push to registry SSH to server
277
+ ↓ ↓
278
+ Tags: sha, docker pull
279
+ test-latest docker-compose up
280
+ ```
281
+
282
+ **Required GitHub Secrets:**
283
+ - `REGISTRY_URL` - Container registry URL
284
+ - `REGISTRY_USERNAME` - Registry login username
285
+ - `REGISTRY_PASSWORD` - Registry login password
286
+ - `TEST_SERVER_HOST` - Test server hostname/IP
287
+ - `TEST_SERVER_USER` - SSH username
288
+ - `TEST_SERVER_SSH_KEY` - SSH private key
289
+
290
+ **What to change if:**
291
+
292
+ | Scenario | What to modify |
293
+ |----------|----------------|
294
+ | Change deployment directory | Modify `cd /opt/splicing-model` in SSH script |
295
+ | Add pre-deployment step | Add commands before `docker pull` in script |
296
+ | Change image name | Modify `IMAGE_NAME` env variable |
297
+ | Add deployment notification | Add step after deploy (e.g., Slack webhook) |
298
+ | Skip deployment for certain files | Add `paths-ignore:` under `on.push` |
299
+
300
+ ---
301
+
302
+ ### 7. Production Deployment Workflow (.github/workflows/deploy-prod.yml)
303
+
304
+ ```
305
+ Location: /.github/workflows/deploy-prod.yml
306
+ ```
307
+
308
+ **What it does:**
309
+ - Triggers on version tags (e.g., `v1.0.0`, `v2.3.1`)
310
+ - **Does NOT rebuild** - promotes test-latest image to production
311
+ - Tags image with version number and `prod-latest`
312
+ - SSHs into production server and deploys
313
+
314
+ **Image promotion flow:**
315
+
316
+ ```
317
+ test-latest → v1.0.0 (versioned)
318
+ → prod-latest (deployment tag)
319
+ ```
320
+
321
+ **Why image promotion?**
322
+ - Ensures production runs exact same image as tested
323
+ - Faster deployments (no rebuild)
324
+ - Version tags provide rollback points
325
+
326
+ **Required GitHub Secrets:**
327
+ - Same registry secrets as test deployment
328
+ - `PROD_SERVER_HOST` - Production server hostname/IP
329
+ - `PROD_SERVER_USER` - SSH username
330
+ - `PROD_SERVER_SSH_KEY` - SSH private key
331
+
332
+ **What to change if:**
333
+
334
+ | Scenario | What to modify |
335
+ |----------|----------------|
336
+ | Change version tag pattern | Modify `tags:` pattern under `on.push` |
337
+ | Add manual approval | Already uses `environment: production` (configure in GitHub) |
338
+ | Rebuild instead of promote | Replace retag steps with build-push-action |
339
+ | Add rollback capability | Store previous IMAGE_TAG before updating |
340
+
341
+ ---
342
+
343
+ ### 8. Apache Configuration (deploy/apache-site.conf)
344
+
345
+ ```
346
+ Location: /deploy/apache-site.conf
347
+ ```
348
+
349
+ **What it does:**
350
+ - Configures Apache as reverse proxy to the Docker container
351
+ - Forwards HTTP requests to `127.0.0.1:8000`
352
+ - Handles WebSocket upgrades for PyShiny
353
+ - Logs to `/var/log/apache2/splicing-*.log`
354
+
355
+ **Key sections:**
356
+
357
+ ```apache
358
+ # Proxy all requests to Docker container
359
+ ProxyPass / http://127.0.0.1:8000/
360
+ ProxyPassReverse / http://127.0.0.1:8000/
361
+
362
+ # WebSocket support (required for PyShiny)
363
+ RewriteCond %{HTTP:Upgrade} websocket [NC]
364
+ RewriteRule ^/?(.*) "ws://127.0.0.1:8000/$1" [P,L]
365
+ ```
366
+
367
+ **Installation on server:**
368
+ ```bash
369
+ # Copy config
370
+ sudo cp deploy/apache-site.conf /etc/apache2/sites-available/splicing.conf
371
+
372
+ # Edit ServerName
373
+ sudo nano /etc/apache2/sites-available/splicing.conf
374
+ # Change ${SERVER_NAME} to your actual domain
375
+
376
+ # Enable site
377
+ sudo a2ensite splicing
378
+ sudo systemctl reload apache2
379
+ ```
380
+
381
+ **What to change if:**
382
+
383
+ | Scenario | What to modify |
384
+ |----------|----------------|
385
+ | Change domain name | Replace `${SERVER_NAME}` with actual domain |
386
+ | Enable HTTPS | Uncomment the SSL VirtualHost section |
387
+ | Change container port | Update `127.0.0.1:8000` to new port |
388
+ | Add custom headers | Add `Header set` directives |
389
+ | Serve at subpath (e.g., /app) | Modify ProxyPass paths |
390
+
391
+ ---
392
+
393
+ ### 9. Server Setup Script (deploy/server-setup.sh)
394
+
395
+ ```
396
+ Location: /deploy/server-setup.sh
397
+ ```
398
+
399
+ **What it does:**
400
+ - Installs Docker using official install script
401
+ - Installs Docker Compose plugin
402
+ - Installs Apache with required modules
403
+ - Creates `/opt/splicing-model` directory
404
+ - Creates `splicing-db` Docker volume
405
+
406
+ **Run on server:**
407
+ ```bash
408
+ # Copy script to server
409
+ scp deploy/server-setup.sh user@server:/tmp/
410
+
411
+ # SSH to server and run
412
+ ssh user@server
413
+ bash /tmp/server-setup.sh
414
+
415
+ # Log out and back in (for docker group)
416
+ exit
417
+ ssh user@server
418
+ ```
419
+
420
+ **What to change if:**
421
+
422
+ | Scenario | What to modify |
423
+ |----------|----------------|
424
+ | Change app directory | Modify `/opt/splicing-model` paths |
425
+ | Change volume name | Modify `docker volume create` command |
426
+ | Add additional packages | Add `apt-get install` commands |
427
+ | Skip Apache installation | Remove Apache section |
428
+
429
+ ---
430
+
431
+ ### 10. Health Endpoint (webapp/app/api/routes.py)
432
+
433
+ ```
434
+ Location: /webapp/app/api/routes.py:55-77
435
+ ```
436
+
437
+ **What it does:**
438
+ - Provides `/api/health` endpoint for container orchestration
439
+ - Checks if TensorFlow model is loaded
440
+ - Checks if database connection works
441
+ - Returns `healthy` or `degraded` status
442
+
443
+ **Response format:**
444
+ ```json
445
+ {
446
+ "status": "healthy",
447
+ "version": "1.0.0",
448
+ "model_loaded": true,
449
+ "database_connected": true
450
+ }
451
+ ```
452
+
453
+ **Used by:**
454
+ - Docker HEALTHCHECK command
455
+ - Load balancers
456
+ - Monitoring systems
457
+
458
+ **What to change if:**
459
+
460
+ | Scenario | What to modify |
461
+ |----------|----------------|
462
+ | Add health check | Add check in `health_check()` function |
463
+ | Change response fields | Modify `HealthResponse` schema |
464
+ | Add external service check | Add try/except block similar to db check |
465
+
466
+ ---
467
+
468
+ ## Deployment Flow
469
+
470
+ ```
471
+ ┌─────────────────────────────────────────────────────────────────┐
472
+ │ DEVELOPMENT │
473
+ ├─────────────────────────────────────────────────────────────────┤
474
+ │ Developer pushes code to feature branch │
475
+ │ ↓ │
476
+ │ Creates Pull Request to main │
477
+ │ ↓ │
478
+ │ CI runs: tests + Docker build (ci.yml) │
479
+ │ ↓ │
480
+ │ PR merged to main │
481
+ └─────────────────────────────────────────────────────────────────┘
482
+
483
+ ┌─────────────────────────────────────────────────────────────────┐
484
+ │ TEST DEPLOYMENT │
485
+ ├─────────────────────────────��───────────────────────────────────┤
486
+ │ Push to main triggers deploy-test.yml │
487
+ │ ↓ │
488
+ │ Build Docker image │
489
+ │ ↓ │
490
+ │ Push to registry: <sha>, test-latest │
491
+ │ ↓ │
492
+ │ SSH to test server │
493
+ │ ↓ │
494
+ │ docker pull → docker-compose up │
495
+ │ ↓ │
496
+ │ Manual QA testing on test environment │
497
+ └─────────────────────────────────────────────────────────────────┘
498
+
499
+ ┌─────────────────────────────────────────────────────────────────┐
500
+ │ PRODUCTION DEPLOYMENT │
501
+ ├─────────────────────────────────────────────────────────────────┤
502
+ │ Developer creates version tag: git tag v1.0.0 │
503
+ │ ↓ │
504
+ │ Push tag: git push origin v1.0.0 │
505
+ │ ↓ │
506
+ │ deploy-prod.yml triggered │
507
+ │ ↓ │
508
+ │ Pull test-latest, retag as v1.0.0 + prod-latest │
509
+ │ ↓ │
510
+ │ Push production tags to registry │
511
+ │ ↓ │
512
+ │ SSH to production server │
513
+ │ ↓ │
514
+ │ docker pull → docker-compose up │
515
+ └─────────────────────────────────────────────────────────────────┘
516
+ ```
517
+
518
+ ---
519
+
520
+ ## Configuration Reference
521
+
522
+ ### GitHub Secrets Required
523
+
524
+ Configure in: Repository → Settings → Secrets and variables → Actions
525
+
526
+ | Secret | Description | Example |
527
+ |--------|-------------|---------|
528
+ | `REGISTRY_URL` | Container registry URL | `registry.example.com` |
529
+ | `REGISTRY_USERNAME` | Registry login username | `deploy-user` |
530
+ | `REGISTRY_PASSWORD` | Registry login password | `***` |
531
+ | `TEST_SERVER_HOST` | Test server IP/hostname | `test.example.com` |
532
+ | `TEST_SERVER_USER` | SSH username for test | `deploy` |
533
+ | `TEST_SERVER_SSH_KEY` | SSH private key for test | `-----BEGIN OPENSSH...` |
534
+ | `PROD_SERVER_HOST` | Production server IP/hostname | `prod.example.com` |
535
+ | `PROD_SERVER_USER` | SSH username for prod | `deploy` |
536
+ | `PROD_SERVER_SSH_KEY` | SSH private key for prod | `-----BEGIN OPENSSH...` |
537
+
538
+ ### GitHub Environments (Optional)
539
+
540
+ Configure in: Repository → Settings → Environments
541
+
542
+ **test environment:**
543
+ - No special configuration needed
544
+ - Deploys automatically on main push
545
+
546
+ **production environment:**
547
+ - Enable "Required reviewers" for manual approval
548
+ - Add deployment protection rules as needed
549
+
550
+ ### Server Directory Structure
551
+
552
+ ```
553
+ /opt/splicing-model/
554
+ ├── docker-compose.prod.yml # Copied from repo
555
+ ├── .env.production # Created manually with secrets
556
+ └── (container runs here)
557
+
558
+ Docker volumes:
559
+ └── splicing-db # SQLite database persistence
560
+ ```
561
+
562
+ ---
563
+
564
+ ## Common Modifications
565
+
566
+ ### Adding a New Environment Variable
567
+
568
+ 1. **For local development:**
569
+ ```yaml
570
+ # docker-compose.yml
571
+ environment:
572
+ - NEW_VAR=value
573
+ ```
574
+
575
+ 2. **For production:**
576
+ ```bash
577
+ # On server: /opt/splicing-model/.env.production
578
+ NEW_VAR=production_value
579
+ ```
580
+
581
+ 3. **For CI/CD:**
582
+ ```yaml
583
+ # .github/workflows/*.yml
584
+ env:
585
+ NEW_VAR: ${{ secrets.NEW_VAR }}
586
+ ```
587
+
588
+ ### Changing the Port
589
+
590
+ 1. **Dockerfile:**
591
+ ```dockerfile
592
+ EXPOSE 9000
593
+ CMD ["python", "-m", "uvicorn", "...", "--port", "9000"]
594
+ ```
595
+
596
+ 2. **docker-compose.yml:**
597
+ ```yaml
598
+ ports:
599
+ - "9000:9000"
600
+ ```
601
+
602
+ 3. **docker-compose.prod.yml:**
603
+ ```yaml
604
+ ports:
605
+ - "127.0.0.1:9000:9000"
606
+ ```
607
+
608
+ 4. **Apache config:**
609
+ ```apache
610
+ ProxyPass / http://127.0.0.1:9000/
611
+ ```
612
+
613
+ 5. **Health check in Dockerfile:**
614
+ ```dockerfile
615
+ CMD python -c "import httpx; httpx.get('http://localhost:9000/api/health')"
616
+ ```
617
+
618
+ ### Adding a New Python Dependency
619
+
620
+ 1. Add to `webapp/requirements.txt`
621
+ 2. Rebuild Docker image: `docker-compose build`
622
+ 3. Commit and push - CI/CD will build new image
623
+
624
+ ### Adding a System Dependency
625
+
626
+ 1. **Dockerfile (Stage 2):**
627
+ ```dockerfile
628
+ RUN apt-get update && apt-get install -y --no-install-recommends \
629
+ vienna-rna \
630
+ new-package \
631
+ && rm -rf /var/lib/apt/lists/*
632
+ ```
633
+
634
+ ### Changing the Container Registry
635
+
636
+ 1. **Update GitHub Secrets:**
637
+ - `REGISTRY_URL` → new registry URL
638
+ - `REGISTRY_USERNAME` → new credentials
639
+ - `REGISTRY_PASSWORD` → new credentials
640
+
641
+ 2. **Update server `.env.production`:**
642
+ ```bash
643
+ REGISTRY_URL=new-registry.example.com
644
+ ```
645
+
646
+ ### Adding SSL/HTTPS
647
+
648
+ 1. **Obtain SSL certificate** (e.g., Let's Encrypt)
649
+
650
+ 2. **Update Apache config:**
651
+ ```apache
652
+ # Uncomment the HTTPS VirtualHost section in apache-site.conf
653
+ # Update certificate paths
654
+ SSLCertificateFile /etc/ssl/certs/your-domain.crt
655
+ SSLCertificateKeyFile /etc/ssl/private/your-domain.key
656
+ ```
657
+
658
+ 3. **Enable SSL module:**
659
+ ```bash
660
+ sudo a2enmod ssl
661
+ sudo systemctl reload apache2
662
+ ```
663
+
664
+ ### Rolling Back Production
665
+
666
+ ```bash
667
+ # SSH to production server
668
+ ssh deploy@prod.example.com
669
+
670
+ # Pull previous version
671
+ docker pull registry.example.com/splicing-model:v1.0.0
672
+
673
+ # Deploy previous version
674
+ cd /opt/splicing-model
675
+ IMAGE_TAG=v1.0.0 docker-compose -f docker-compose.prod.yml up -d
676
+ ```
677
+
678
+ ---
679
+
680
+ ## Next Steps
681
+
682
+ ### 1. Configure GitHub Secrets
683
+
684
+ **Priority: Required before first deployment**
685
+
686
+ Go to your GitHub repository → Settings → Secrets and variables → Actions, and add all the secrets listed in [Configuration Reference](#github-secrets-required).
687
+
688
+ For SSH keys, generate a dedicated deployment key:
689
+ ```bash
690
+ ssh-keygen -t ed25519 -C "github-deploy" -f deploy_key
691
+ # Add deploy_key.pub to server's ~/.ssh/authorized_keys
692
+ # Add deploy_key (private) as GitHub secret
693
+ ```
694
+
695
+ ### 2. Set Up Container Registry
696
+
697
+ **Priority: Required before first deployment**
698
+
699
+ Options:
700
+ - **GitHub Container Registry (ghcr.io)**: Free for public repos
701
+ - **Docker Hub**: Free tier available
702
+ - **Self-hosted**: Harbor, GitLab Registry, etc.
703
+
704
+ Update `REGISTRY_URL` secret accordingly.
705
+
706
+ ### 3. Prepare Test Server
707
+
708
+ **Priority: Required for test deployments**
709
+
710
+ ```bash
711
+ # SSH to test server
712
+ ssh user@test.example.com
713
+
714
+ # Run setup script
715
+ bash /path/to/server-setup.sh
716
+
717
+ # Copy production compose file
718
+ scp docker-compose.prod.yml user@test.example.com:/opt/splicing-model/
719
+
720
+ # Create environment file
721
+ ssh user@test.example.com
722
+ echo "DEBUG=false" > /opt/splicing-model/.env.production
723
+
724
+ # Configure Apache
725
+ sudo cp /path/to/apache-site.conf /etc/apache2/sites-available/splicing.conf
726
+ sudo nano /etc/apache2/sites-available/splicing.conf # Set ServerName
727
+ sudo a2ensite splicing
728
+ sudo systemctl reload apache2
729
+ ```
730
+
731
+ ### 4. Prepare Production Server
732
+
733
+ **Priority: Required for production deployments**
734
+
735
+ Same steps as test server, on the production machine.
736
+
737
+ ### 5. Configure GitHub Environments (Optional)
738
+
739
+ **Priority: Recommended for production safety**
740
+
741
+ Go to Repository → Settings → Environments:
742
+ - Create `test` environment (auto-deploy)
743
+ - Create `production` environment with required reviewers
744
+
745
+ ### 6. Test the Pipeline
746
+
747
+ **Priority: Verify everything works**
748
+
749
+ ```bash
750
+ # Test local Docker build
751
+ docker-compose up --build
752
+ curl http://localhost:8000/api/health
753
+
754
+ # Test CI pipeline
755
+ git checkout -b test-ci
756
+ git commit --allow-empty -m "Test CI pipeline"
757
+ git push origin test-ci
758
+ # Create PR and verify CI passes
759
+
760
+ # Test deployment
761
+ git checkout main
762
+ git merge test-ci
763
+ git push origin main
764
+ # Verify test deployment
765
+
766
+ # Test production deployment
767
+ git tag v0.1.0
768
+ git push origin v0.1.0
769
+ # Verify production deployment
770
+ ```
771
+
772
+ ### 7. Set Up Monitoring (Optional)
773
+
774
+ **Priority: Recommended for production**
775
+
776
+ - Configure uptime monitoring for `/api/health`
777
+ - Set up log aggregation (e.g., Loki, ELK)
778
+ - Add alerting for failed deployments
779
+
780
+ ### 8. Configure SSL/HTTPS
781
+
782
+ **Priority: Required for production**
783
+
784
+ See [Adding SSL/HTTPS](#adding-sslhttps) in Common Modifications.
785
+
786
+ ---
787
+
788
+ ## Troubleshooting
789
+
790
+ ### Container won't start
791
+
792
+ ```bash
793
+ # Check container logs
794
+ docker-compose logs webapp
795
+
796
+ # Check if port is in use
797
+ sudo lsof -i :8000
798
+
799
+ # Check container status
800
+ docker ps -a
801
+ ```
802
+
803
+ ### Health check failing
804
+
805
+ ```bash
806
+ # Test health endpoint manually
807
+ docker exec <container_id> python -c "import httpx; print(httpx.get('http://localhost:8000/api/health').json())"
808
+
809
+ # Check if model loaded
810
+ docker logs <container_id> | grep -i model
811
+ ```
812
+
813
+ ### CI pipeline failing
814
+
815
+ 1. Check GitHub Actions logs for specific error
816
+ 2. Common issues:
817
+ - Missing secrets
818
+ - ViennaRNA installation failure
819
+ - Test failures
820
+
821
+ ### Deployment SSH failing
822
+
823
+ ```bash
824
+ # Test SSH connection manually
825
+ ssh -i /path/to/key deploy@server.example.com
826
+
827
+ # Check key permissions
828
+ chmod 600 /path/to/key
829
+
830
+ # Verify key is in authorized_keys on server
831
+ ```
832
+
833
+ ### Apache proxy not working
834
+
835
+ ```bash
836
+ # Check Apache status
837
+ sudo systemctl status apache2
838
+
839
+ # Check Apache error logs
840
+ sudo tail -f /var/log/apache2/splicing-error.log
841
+
842
+ # Verify modules enabled
843
+ sudo apache2ctl -M | grep -E "proxy|rewrite"
844
+
845
+ # Test local connection
846
+ curl http://127.0.0.1:8000/api/health
847
+ ```
848
+
849
+ ### Database not persisting
850
+
851
+ ```bash
852
+ # Check volume exists
853
+ docker volume ls | grep splicing
854
+
855
+ # Inspect volume
856
+ docker volume inspect splicing-db
857
+
858
+ # Verify mount in container
859
+ docker inspect <container_id> | grep -A5 Mounts
860
+ ```
861
+
862
+ ---
863
+
864
+ ## File Quick Reference
865
+
866
+ | Need to... | Edit this file |
867
+ |------------|----------------|
868
+ | Change Python dependencies | `webapp/requirements.txt` |
869
+ | Change system packages | `Dockerfile` (Stage 2 apt-get) |
870
+ | Change local dev settings | `docker-compose.yml` |
871
+ | Change production settings | `docker-compose.prod.yml` + `.env.production` |
872
+ | Change CI test commands | `.github/workflows/ci.yml` |
873
+ | Change test deploy process | `.github/workflows/deploy-test.yml` |
874
+ | Change prod deploy process | `.github/workflows/deploy-prod.yml` |
875
+ | Change Apache/web server | `deploy/apache-site.conf` |
876
+ | Change server setup | `deploy/server-setup.sh` |
877
+ | Change health check logic | `webapp/app/api/routes.py` |
Dockerfile ADDED
@@ -0,0 +1,47 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Multi-stage build for smaller image
2
+ FROM python:3.10-slim-bookworm AS builder
3
+
4
+ WORKDIR /app
5
+
6
+ # Install build dependencies
7
+ RUN apt-get update && apt-get install -y --no-install-recommends \
8
+ build-essential \
9
+ && rm -rf /var/lib/apt/lists/*
10
+
11
+ # Install Python dependencies
12
+ COPY webapp/requirements.txt .
13
+ RUN pip wheel --no-cache-dir --no-deps --wheel-dir /app/wheels -r requirements.txt
14
+
15
+ # Production image
16
+ FROM python:3.10-slim-bookworm
17
+
18
+ WORKDIR /app
19
+
20
+ # Install system dependencies including ViennaRNA
21
+ RUN apt-get update && apt-get install -y --no-install-recommends \
22
+ vienna-rna \
23
+ && rm -rf /var/lib/apt/lists/*
24
+
25
+ # Copy wheels and install
26
+ COPY --from=builder /app/wheels /wheels
27
+ RUN pip install --no-cache /wheels/*
28
+
29
+ # Copy application code
30
+ COPY webapp/ ./webapp/
31
+ COPY figures/ ./figures/
32
+ COPY output/ ./output/
33
+ COPY data/ ./data/
34
+
35
+ # Create non-root user
36
+ RUN useradd -m -u 1000 appuser && chown -R appuser:appuser /app
37
+ USER appuser
38
+
39
+ # Expose port
40
+ EXPOSE 8000
41
+
42
+ # Health check
43
+ HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
44
+ CMD python -c "import httpx; httpx.get('http://localhost:8000/api/health')" || exit 1
45
+
46
+ # Run with Uvicorn
47
+ CMD ["python", "-m", "uvicorn", "webapp.app.main:app", "--host", "0.0.0.0", "--port", "8000"]
deploy/apache-site.conf ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <VirtualHost *:80>
2
+ ServerName ${SERVER_NAME}
3
+
4
+ # Redirect to HTTPS (uncomment when SSL is configured)
5
+ # Redirect permanent / https://${SERVER_NAME}/
6
+
7
+ ProxyPreserveHost On
8
+ ProxyPass / http://127.0.0.1:8000/
9
+ ProxyPassReverse / http://127.0.0.1:8000/
10
+
11
+ # WebSocket support for PyShiny
12
+ RewriteEngine On
13
+ RewriteCond %{HTTP:Upgrade} websocket [NC]
14
+ RewriteCond %{HTTP:Connection} upgrade [NC]
15
+ RewriteRule ^/?(.*) "ws://127.0.0.1:8000/$1" [P,L]
16
+
17
+ ErrorLog ${APACHE_LOG_DIR}/splicing-error.log
18
+ CustomLog ${APACHE_LOG_DIR}/splicing-access.log combined
19
+ </VirtualHost>
20
+
21
+ # HTTPS configuration (uncomment when SSL is ready)
22
+ # <VirtualHost *:443>
23
+ # ServerName ${SERVER_NAME}
24
+ #
25
+ # SSLEngine on
26
+ # SSLCertificateFile /etc/ssl/certs/${SERVER_NAME}.crt
27
+ # SSLCertificateKeyFile /etc/ssl/private/${SERVER_NAME}.key
28
+ #
29
+ # ProxyPreserveHost On
30
+ # ProxyPass / http://127.0.0.1:8000/
31
+ # ProxyPassReverse / http://127.0.0.1:8000/
32
+ #
33
+ # RewriteEngine On
34
+ # RewriteCond %{HTTP:Upgrade} websocket [NC]
35
+ # RewriteCond %{HTTP:Connection} upgrade [NC]
36
+ # RewriteRule ^/?(.*) "ws://127.0.0.1:8000/$1" [P,L]
37
+ #
38
+ # ErrorLog ${APACHE_LOG_DIR}/splicing-error.log
39
+ # CustomLog ${APACHE_LOG_DIR}/splicing-access.log combined
40
+ # </VirtualHost>
deploy/server-setup.sh ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/bin/bash
2
+ # Run this on the Ubuntu 24.04 VM to prepare for deployment
3
+
4
+ set -e
5
+
6
+ echo "=== Installing Docker ==="
7
+ curl -fsSL https://get.docker.com | sh
8
+ sudo usermod -aG docker $USER
9
+
10
+ echo "=== Installing Docker Compose ==="
11
+ sudo apt-get update
12
+ sudo apt-get install -y docker-compose-plugin
13
+
14
+ echo "=== Installing Apache ==="
15
+ sudo apt-get install -y apache2
16
+ sudo a2enmod proxy proxy_http proxy_wstunnel rewrite headers
17
+
18
+ echo "=== Creating application directory ==="
19
+ sudo mkdir -p /opt/splicing-model
20
+ sudo chown $USER:$USER /opt/splicing-model
21
+
22
+ echo "=== Creating Docker volume for database ==="
23
+ docker volume create splicing-db
24
+
25
+ echo "=== Setup complete ==="
26
+ echo "Next steps:"
27
+ echo "1. Copy docker-compose.prod.yml to /opt/splicing-model/"
28
+ echo "2. Create .env.production file with secrets"
29
+ echo "3. Configure Apache site: sudo cp apache-site.conf /etc/apache2/sites-available/splicing.conf"
30
+ echo "4. Enable site: sudo a2ensite splicing && sudo systemctl reload apache2"
31
+ echo "5. Add GitHub Secrets for deployment"
docker-compose.prod.yml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ webapp:
5
+ image: ${REGISTRY_URL}/splicing-model:${IMAGE_TAG:-latest}
6
+ restart: unless-stopped
7
+ ports:
8
+ - "127.0.0.1:8000:8000" # Only localhost, Apache proxies
9
+ volumes:
10
+ - splicing-db:/app/data/db
11
+ environment:
12
+ - DEBUG=false
13
+ env_file:
14
+ - .env.production
15
+ healthcheck:
16
+ test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8000/api/health')"]
17
+ interval: 30s
18
+ timeout: 10s
19
+ retries: 3
20
+ start_period: 10s
21
+ logging:
22
+ driver: "json-file"
23
+ options:
24
+ max-size: "10m"
25
+ max-file: "3"
26
+
27
+ volumes:
28
+ splicing-db:
29
+ external: true # Create manually: docker volume create splicing-db
docker-compose.yml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: '3.8'
2
+
3
+ services:
4
+ webapp:
5
+ build:
6
+ context: .
7
+ dockerfile: Dockerfile
8
+ ports:
9
+ - "8000:8000"
10
+ volumes:
11
+ - ./webapp:/app/webapp:ro # Hot reload for development
12
+ - db-data:/app/webapp # Database persistence
13
+ environment:
14
+ - DEBUG=true
15
+ healthcheck:
16
+ test: ["CMD", "python", "-c", "import httpx; httpx.get('http://localhost:8000/api/health')"]
17
+ interval: 30s
18
+ timeout: 10s
19
+ retries: 3
20
+ start_period: 10s
21
+
22
+ volumes:
23
+ db-data: