Skip to content

Commit 3da77c2

Browse files
authored
Merge pull request #298 from DevKor-github/codexd/private-rds-deploy-workflow
[codex] Align deploy workflow with private RDS
2 parents d1b9462 + 9a78ff6 commit 3da77c2

4 files changed

Lines changed: 65 additions & 25 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ jobs:
8787
BACKEND_IMAGE=${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
8888
BACKEND_CONTAINER_NAME=ontime-container
8989
BACKEND_HTTP_PORT=${{ secrets.BACKEND_HTTP_PORT || '8080' }}
90+
BACKEND_MEMORY_LIMIT=${{ secrets.BACKEND_MEMORY_LIMIT || '768m' }}
91+
BACKEND_CPU_LIMIT=${{ secrets.BACKEND_CPU_LIMIT || '1.0' }}
9092
SERVER_PORT=8080
9193
SPRING_PROFILES_ACTIVE=prod
9294
JAVA_TOOL_OPTIONS=-XX:InitialRAMPercentage=50.0 -XX:MaxRAMPercentage=75.0 -Djava.security.egd=file:/dev/./urandom
@@ -154,14 +156,33 @@ jobs:
154156
FLYWAY_BASELINE="$(get_env_value SPRING_FLYWAY_BASELINE_ON_MIGRATE)"
155157
NORMALIZED_DB_URL="$(printf '%s' "$DB_URL" | tr '[:upper:]' '[:lower:]')"
156158
NORMALIZED_DB_USERNAME="$(printf '%s' "$DB_USERNAME" | tr '[:upper:]' '[:lower:]')"
159+
DB_URL_NO_PREFIX="${DB_URL#jdbc:mysql://}"
160+
[ "$DB_URL_NO_PREFIX" != "$DB_URL" ] || fail_deploy "SPRING_DATASOURCE_URL must start with jdbc:mysql://."
161+
DB_ADDRESS="${DB_URL_NO_PREFIX%%/*}"
162+
DB_HOST="${DB_ADDRESS%%:*}"
163+
DB_PORT="${DB_ADDRESS#*:}"
164+
[ "$DB_PORT" != "$DB_ADDRESS" ] || DB_PORT="3306"
165+
DB_NAME_AND_QUERY="${DB_URL_NO_PREFIX#*/}"
166+
DB_NAME="$(printf '%s' "$DB_NAME_AND_QUERY" | sed 's/[?;].*$//')"
157167
158168
[ -n "$DB_URL" ] || fail_deploy "SPRING_DATASOURCE_URL is required."
159169
[ -n "$DB_USERNAME" ] || fail_deploy "SPRING_DATASOURCE_USERNAME is required."
160170
[ -n "$DB_PASSWORD" ] || fail_deploy "SPRING_DATASOURCE_PASSWORD is required."
171+
[ -n "$DB_HOST" ] || fail_deploy "SPRING_DATASOURCE_URL must include an RDS host."
172+
[ "$DB_NAME" = "ontime_prod" ] || fail_deploy "SPRING_DATASOURCE_URL must use the ontime_prod database."
161173
[ "$NORMALIZED_DB_USERNAME" != "root" ] || fail_deploy "SPRING_DATASOURCE_USERNAME must not be root."
162174
[ "$DDL_AUTO" = "validate" ] || fail_deploy "SPRING_JPA_HIBERNATE_DDL_AUTO must be validate."
163175
[ "$FLYWAY_BASELINE" = "false" ] || fail_deploy "SPRING_FLYWAY_BASELINE_ON_MIGRATE must be false."
164176
177+
case "$NORMALIZED_DB_URL" in
178+
*localhost*|*127.0.0.1*|*host.docker.internal*) fail_deploy "SPRING_DATASOURCE_URL must point to private RDS, not a local database." ;;
179+
esac
180+
181+
case "$(printf '%s' "$DB_HOST" | tr '[:upper:]' '[:lower:]')" in
182+
*.rds.amazonaws.com) ;;
183+
*) fail_deploy "SPRING_DATASOURCE_URL host must be an RDS endpoint." ;;
184+
esac
185+
165186
case "$NORMALIZED_DB_URL" in
166187
*allowpublickeyretrieval=true*) fail_deploy "SPRING_DATASOURCE_URL must not enable allowPublicKeyRetrieval." ;;
167188
esac
@@ -175,10 +196,17 @@ jobs:
175196
esac
176197
177198
case "$NORMALIZED_DB_URL" in
178-
*sslmode=required*|*sslmode=verify_ca*|*sslmode=verify_identity*) ;;
179-
*) fail_deploy "SPRING_DATASOURCE_URL must declare sslMode=REQUIRED, VERIFY_CA, or VERIFY_IDENTITY." ;;
199+
*sslmode=required*|*sslmode=verify_ca*|*sslmode=verify_identity*|*usessl=true*) ;;
200+
*) fail_deploy "SPRING_DATASOURCE_URL must declare useSSL=true, sslMode=REQUIRED, VERIFY_CA, or VERIFY_IDENTITY." ;;
180201
esac
181202
203+
echo "Checking EC2-to-RDS TCP connectivity for $DB_HOST:$DB_PORT..."
204+
if command -v nc >/dev/null 2>&1; then
205+
nc -zv "$DB_HOST" "$DB_PORT"
206+
else
207+
timeout 5 bash -c "</dev/tcp/$DB_HOST/$DB_PORT"
208+
fi
209+
182210
echo "${{ secrets.GHCR_READ_TOKEN }}" | sudo docker login ghcr.io -u "${{ secrets.GHCR_USERNAME }}" --password-stdin
183211
184212
if sudo docker compose version >/dev/null 2>&1; then

docs/deployment.md

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,24 @@ Deployment access:
1515
Runtime image and port:
1616

1717
- `BACKEND_HTTP_PORT` (optional, defaults to `8080`)
18+
- `BACKEND_MEMORY_LIMIT` (optional, defaults to `768m`; use `640m` if the EC2 instance is memory constrained)
19+
- `BACKEND_CPU_LIMIT` (optional, defaults to `1.0`)
1820

1921
Spring and database:
2022

2123
- `SPRING_APPLICATION_NAME`
22-
- `SPRING_DATASOURCE_URL`
23-
- `SPRING_DATASOURCE_USERNAME`
24+
- `SPRING_DATASOURCE_URL` (`jdbc:mysql://ontime-prod.cpoeguokwaq5.ap-northeast-2.rds.amazonaws.com:3306/ontime_prod?useSSL=true&serverTimezone=Asia/Seoul&characterEncoding=UTF-8`)
25+
- `SPRING_DATASOURCE_USERNAME` (`ontimeadmin`)
2426
- `SPRING_DATASOURCE_PASSWORD`
25-
- `SPRING_DATASOURCE_DRIVER_CLASS_NAME`
26-
- `SPRING_JPA_HIBERNATE_DDL_AUTO`
27-
- `SPRING_FLYWAY_URL`
28-
- `SPRING_FLYWAY_USER`
29-
- `SPRING_FLYWAY_PASSWORD`
27+
28+
The deploy workflow hardcodes safe production defaults for the datasource driver, JPA DDL mode, and Flyway:
29+
30+
- `SPRING_DATASOURCE_DRIVER_CLASS_NAME=com.mysql.cj.jdbc.Driver`
31+
- `SPRING_JPA_HIBERNATE_DDL_AUTO=validate`
32+
- `SPRING_FLYWAY_ENABLED=true`
33+
- `SPRING_FLYWAY_BASELINE_ON_MIGRATE=false`
34+
35+
It also fails before restart if the datasource URL does not point to an RDS MySQL endpoint using the `ontime_prod` database, or if EC2 cannot reach RDS on `3306`.
3036

3137
Authentication and OAuth:
3238

@@ -97,8 +103,9 @@ The workflow:
97103
- `ghcr.io/devkor-github/ontime-back:deploy-latest`
98104
3. Uploads `docker-compose.yml` to `/home/ubuntu/OnTime-back`.
99105
4. Writes `/home/ubuntu/OnTime-back/.env` from GitHub secrets.
100-
5. Runs `docker compose pull && docker compose up -d --remove-orphans`.
101-
6. Waits until the `ontime-container` Docker health status is `healthy`.
106+
5. Verifies EC2 can reach private RDS on `3306`.
107+
6. Runs `docker compose pull && docker compose up -d --remove-orphans`.
108+
7. Waits until the `ontime-container` Docker health status is `healthy`.
102109

103110
## Health Verification
104111

@@ -115,6 +122,7 @@ cd /home/ubuntu/OnTime-back
115122
sudo docker compose ps
116123
sudo docker inspect -f '{{.State.Health.Status}}' ontime-container
117124
curl -fsS http://localhost:8080/actuator/health/readiness
125+
nc -zv ontime-prod.cpoeguokwaq5.ap-northeast-2.rds.amazonaws.com 3306
118126
```
119127

120128
## Rollback

ontime-back/docker-compose.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ services:
1414
stop_grace_period: 30s
1515
mem_limit: "${BACKEND_MEMORY_LIMIT:-768m}"
1616
cpus: "${BACKEND_CPU_LIMIT:-1.0}"
17+
logging:
18+
driver: json-file
19+
options:
20+
max-size: "10m"
21+
max-file: "3"
1722
healthcheck:
1823
test: ["CMD-SHELL", "curl -fsS http://localhost:${SERVER_PORT:-8080}/actuator/health/readiness || exit 1"]
1924
interval: 30s

ontime-back/docs/deployment/ec2.md

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ This service deploys to Amazon EC2 through `.github/workflows/deploy.yml`.
88
2. Add the required GitHub Actions secrets listed below.
99
3. Run the `Deploy` workflow manually from GitHub Actions, or push to the `deploy` branch.
1010

11-
The workflow builds the Spring Boot jar, creates deploy-only config files from GitHub Secrets, uploads them to `/home/ubuntu/OnTime-back`, and restarts Docker Compose on the EC2 instance.
11+
The workflow builds a Docker image, pushes it to GHCR, uploads `docker-compose.yml` to `/home/ubuntu/OnTime-back`, writes a production `.env` from GitHub Secrets, verifies private RDS connectivity, and restarts Docker Compose on the EC2 instance.
1212

1313
## Required EC2 Secrets
1414

@@ -22,8 +22,6 @@ The workflow builds the Spring Boot jar, creates deploy-only config files from G
2222
- `SPRING_DATASOURCE_URL`
2323
- `SPRING_DATASOURCE_USERNAME`
2424
- `SPRING_DATASOURCE_PASSWORD`
25-
- `SPRING_DATASOURCE_DRIVER_CLASS_NAME`
26-
- `SPRING_JPA_HIBERNATE_DDL_AUTO`
2725
- `JWT_SECRETKEY`
2826
- `JWT_ACCESS_EXPIRATION`
2927
- `JWT_REFRESH_EXPIRATION`
@@ -34,27 +32,28 @@ The workflow builds the Spring Boot jar, creates deploy-only config files from G
3432
- `APPLE_CLIENT_ID`
3533
- `APPLE_LOGIN_KEY`
3634
- `APPLE_TEAM_ID`
37-
- `AUTHKEY_743M7R5W3W`
38-
- `SPRING_FLYWAY_URL`
39-
- `SPRING_FLYWAY_USER`
40-
- `SPRING_FLYWAY_PASSWORD`
41-
- `ONTIME_PUSH_FIREBASE_ADMINSDK`
35+
- `APPLE_PRIVATE_KEY_BASE64`
36+
- `FIREBASE_CREDENTIALS_BASE64`
4237

4338
## Optional Secrets
4439

45-
- `SPRING_JPA_DATABASE_PLATFORM` defaults to `org.hibernate.dialect.MySQL8Dialect`.
40+
- `BACKEND_HTTP_PORT` defaults to `8080`.
41+
- `BACKEND_MEMORY_LIMIT` defaults to `768m`; use `640m` if the EC2 instance is memory constrained.
42+
- `BACKEND_CPU_LIMIT` defaults to `1.0`.
4643
- `FEATURE_APPLE_LOGIN_ENABLED` defaults to `true`.
4744
- Google and Kakao OAuth provider/registration secrets are included by the workflow when configured.
4845

4946
## Runtime Files on EC2
5047

5148
The deploy workflow writes these files under `/home/ubuntu/OnTime-back`:
5249

53-
- `project.jar`
54-
- `Dockerfile`
5550
- `docker-compose.yml`
56-
- `config/application.properties`
57-
- `secrets/firebase-adminsdk.json`
58-
- `secrets/AuthKey_743M7R5W3W.p8`
51+
- `.env`
52+
53+
Production uses the private RDS instance:
54+
55+
```text
56+
ontime-prod.cpoeguokwaq5.ap-northeast-2.rds.amazonaws.com:3306/ontime_prod
57+
```
5958

6059
Do not commit local `application.properties`, Firebase service account JSON, Apple `.p8` keys, or `.env` files.

0 commit comments

Comments
 (0)