Overview
The adventuretube-microservice project operates in two environments:
1. Development Environment (Local Mac)
┌─────────────────────────────────────────────────────────────────┐
│ LOCAL MAC │
│ │
│ AdventureTube Services (Java Runtime - NO Docker) │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────────┐ │
│ │ auth │ │ member │ │ web │ │ geospatial │ │
│ │ service │ │ service │ │ service │ │ service │ │
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ └──────────────┴──────────────┴───────────────┘ │
│ │ │
│ │ connects to │
└──────────────────────────────┼──────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ RASPBERRY PI (192.168.1.105) │
│ │
│ Cloud Infrastructure (Docker containers) │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ eureka │ │ config │ │ gateway │ │
│ │ :8761 │ │ :9297 │ │ :8030 │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ │
│ Storages (Docker containers) │
│ ┌────────────┐ ┌────────────┐ │
│ │ PostgreSQL │ │ MongoDB │ │
│ │ :5432 │ │ :27017 │ │
│ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────────┘
2. Production Environment (After Git Push)
Code Push → Git → Jenkins Trigger → Deploy to Pi2
┌─────────────────────────────────────────────────────────────────┐
│ RASPBERRY PI (192.168.1.105) - ALL Docker containers │
│ │
│ AdventureTube Services (Docker) │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ ┌─────────────┐ │
│ │ auth │ │ member │ │ web │ │ geospatial │ │
│ │ service │ │ service │ │ service │ │ service │ │
│ └─────┬──────┘ └─────┬──────┘ └─────┬──────┘ └──────┬──────┘ │
│ │ │ │ │ │
│ └──────────────┴──────┬───────┴───────────────┘ │
│ │ │
│ ▼ │
│ Cloud Infrastructure (Docker) │
│ ┌────────────┐ ┌────────────┐ ┌────────────┐ │
│ │ eureka │ │ config │ │ gateway │ │
│ │ :8761 │ │ :9297 │ │ :8030 │ │
│ └────────────┘ └────────────┘ └────────────┘ │
│ │
│ Storages (Docker) │
│ ┌────────────┐ ┌────────────┐ │
│ │ PostgreSQL │ │ MongoDB │ │
│ │ :5432 │ │ :27017 │ │
│ └────────────┘ └────────────┘ │
└─────────────────────────────────────────────────────────────────┘
Summary
| Environment | AdventureTube Services | Cloud Infrastructure | Location |
|---|---|---|---|
| Development | Java Runtime (NO Docker) | Docker on Pi2 | Local Mac → Pi2 |
| Production | Docker (via Jenkins) | Docker on Pi2 | All on Pi2 |
Docker Compose Files
docker-compose-clouds.yml→ Cloud infrastructure (eureka, config, gateway)docker-compose-adventuretubes.yml→ AdventureTube services (auth, member, web, geospatial)docker-compose-storages.yml→ Databases (PostgreSQL, MongoDB)docker-compose-kafka.yml→ Kafka infrastructure
CRITICAL: Environment Variable Flow
This is the most important concept to understand in the entire build/deploy process.
Environment variables flow through multiple layers, each serving a different purpose:
┌─────────────────────────────────────────────────────────────────┐
│ 1. JENKINSFILE (Source of Truth) │
│ └── Injects env.pi, env.pi2 from Jenkins credentials │
└──────────────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 2. SHELL SCRIPT (adventuretube-cloud-redeploy.sh) │
│ └── source env.pi2 (loads variables into shell session) │
│ └── docker compose --env-file $ENV_FILE ... │
└──────────────────────────────┬──────────────────────────────────┘
▼
┌─────────────────────────────────────────────────────────────────┐
│ 3. DOCKER COMPOSE (docker-compose-clouds.yml) │
│ Variables split into TWO paths: │
│ │
│ ┌─────────────────────┐ ┌─────────────────────────┐ │
│ │ BUILD TIME (args:) │ │ RUN TIME (environment:)│ │
│ │ For: docker build │ │ For: container start │ │
│ └──────────┬──────────┘ └────────────┬────────────┘ │
│ │ │ │
└────────────────┼──────────────────────────────┼─────────────────┘
│ │
▼ ▼
┌────────────────────────────┐ ┌────────────────────────────────┐
│ 4a. DOCKERFILE.PI │ │ 4b. RUNNING CONTAINER │
│ (Image Creation) │ │ (Spring Boot App) │
│ │ │ │
│ ARG BASE_IMAGE │ │ System.getenv("ENV_TARGET") │
│ ARG MODULE_NAME │ │ System.getenv("CONFIG_SERVER")│
│ FROM ${BASE_IMAGE} │ │ │
│ COPY ${MODULE_NAME}/*.jar │ │ DotenvEnvironmentPostProcessor│
│ EXPOSE ${APP_PORT} │ │ └── loads env.pi2 file │
└────────────────────────────┘ └────────────────────────────────┘
BUILD vs RUN: The Key Difference
| Phase | Docker Compose Key | When Used | Purpose | Example Variables |
|---|---|---|---|---|
| BUILD | args: |
docker compose build |
Baked into image | BASE_IMAGE, MODULE_NAME, APP_PORT |
| RUN | environment: |
docker compose up |
Injected at container start | ENV_TARGET, CONFIG_SERVER_URL, SPRING_PROFILES_ACTIVE |
Why This Matters
- BUILD args – Determine WHAT gets built (which base image, which module, which ports to expose)
- RUN environment – Determine HOW the app behaves (which config to load, which profile to use)
Example from docker-compose-clouds.yml
auth-service:
build:
args: # ← BUILD TIME
BASE_IMAGE: ${BASE_IMAGE} # arm64v8/openjdk:17
MODULE_NAME: auth-service # which JAR to copy
APP_PORT: ${AUTH_SERVICE_PORT} # 8010
environment: # ← RUN TIME
- SPRING_PROFILES_ACTIVE=${SPRING_PROFILES_ACTIVE} # pi2
- ENV_TARGET=${ENV_TARGET} # pi2 → loads env.pi2
- CONFIG_SERVER_URL=${CONFIG_SERVER_URL} # http://192.168.1.105:9297
Jenkins Build System Architecture
Jenkins Master (PI1:8443)
URL: jenkins.travel-tube.com
Jobs
| Job | Agent | What It Deploys | Target |
|---|---|---|---|
| Adventuretube | – | Main app (iOS) | – |
| adventuretube-clouds | jenkins-agent2 | Eureka + Config + Gateway | PI2 |
| adventuretube-microservice | jenkins-agent2 | Auth + Member + Web + Geospatial | PI2 |
| GarminConnector-Yehwan | jenkins-agent3 | Garmin integration | PI3 |
Jenkins Agents
| Agent | Host | Executors | Status |
|---|---|---|---|
| jenkins-agent | PI1 | – | Offline |
| jenkins-agent2 | PI2 (shares Docker runtime) | 0/2 | Active |
| jenkins-agent3 | PI3 (192.168.1.141) | 0/1 | Active |
Deploy Flow
┌─────────────────────────────────────────────────────────────────────────────────┐
│ DEPLOYMENT PIPELINE │
│ │
│ ┌───────┐ git push ┌──────────┐ webhook ┌─────────────────────┐ │
│ │ Mac │───────────────>│ GitHub │─────────────>│ Jenkins Master │ │
│ │ (dev) │ │ strider73│ │ (PI1:8443) │ │
│ └───────┘ └──────────┘ └──────────┬──────────┘ │
│ │ │
│ ┌─────────────────────┼────────────┐ │
│ │ dispatches to │ │ │
│ │ agent by job v │ │
│ │ ┌──────────────┐ │ │
│ │ │jenkins-agent2│ │ │
│ │ │ (PI2) │ │ │
│ │ └──────┬───────┘ │ │
│ │ │ │ │
│ ┌────────────────────────────────────────────────────┼──────┐ │ │
│ │ ON PI2 (agent2 shares PI2's Docker runtime) │ │ │ │
│ │ v │ │ │
│ │ ┌──────────────────────────────────────────────────────┐ │ │ │
│ │ │ 1. git pull origin main │ │ │ │
│ │ │ 2. Copy env.pi + env.pi2 from Jenkins Credentials │ │ │ │
│ │ │ 3. mvn clean package -DskipTests │ │ │ │
│ │ │ 4. docker compose build (Dockerfile.pi bakes env) │ │ │ │
│ │ │ 5. docker compose up -d │ │ │ │
│ │ └──────────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ ┌───────────┴───────────┐ │ │ │
│ │ v v │ │ │
│ │ ┌──────────────────┐ ┌────────────────────┐ │ │ │
│ │ │ Jenkinsfile-cloud│ │Jenkinsfile- │ │ │ │
│ │ │ │ │adventuretubes │ │ │ │
│ │ │ eureka-server │ │ │ │ │ │
│ │ │ config-service │ │ auth-service │ │ │ │
│ │ │ gateway-service │ │ member-service │ │ │ │
│ │ │ │ │ web-service │ │ │ │
│ │ │ (start first!) │ │ geospatial-service │ │ │ │
│ │ └──────────────────┘ └────────────────────┘ │ │ │
│ └──────────────────────────────────────────────────────────┘ │ │
│ │ │
│ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ KAFKA (manual restart after Jenkins deploy) │ │ │
│ │ │ │ │
│ │ PI1$ docker compose --env-file env.pi │ │ │
│ │ -f docker-compose-kafka.yml up -d │ │ │
│ │ → kafka1 (broker+controller) + kafdrop │ │ │
│ │ │ │ │
│ │ PI2$ docker compose --env-file env.pi2 │ │ │
│ │ -f docker-compose-kafka.yml up -d │ │ │
│ │ → kafka2 (broker) │ │ │
│ └──────────────────────────────────────────────────────────┘ │ │
└─────────────────────────────────────────────────────────────────────────────────┘
Startup order: Kafka (PI1→PI2) → Clouds (eureka→config→gateway) → Adventuretubes
Jenkinsfiles
| File | Compose File | Services |
|---|---|---|
Jenkinsfile-cloud |
docker-compose-clouds.yml |
eureka-server, config-service, gateway-service |
Jenkinsfile-adventuretubes |
docker-compose-adventuretubes.yml |
auth, member, web, geospatial services |
Env File Flow (IMPORTANT)
┌─────────────────────────────────────────────────────────────────────────────────┐
│ ENV FILE LIFECYCLE │
│ │
│ Env files are NOT in GitHub. They live in Jenkins Credential Store. │
│ │
│ ┌──────────────────────────┐ │
│ │ Jenkins Credential Store │ │
│ │ │ │
│ │ env.pi (Secret file) │ │
│ │ env.pi2 (Secret file) │ │
│ └────────────┬─────────────┘ │
│ │ withCredentials() copies to workspace │
│ v │
│ ┌──────────────────────────┐ │
│ │ Jenkins Workspace (PI2) │ │
│ │ │ │
│ │ env.pi ──┐ │ │
│ │ env.pi2 ──┤ │ │
│ │ v │ │
│ │ ┌────────────────────┐ │ │
│ │ │ Dockerfile.pi │ │ │
│ │ │ │ │ │
│ │ │ COPY env.pi ./ │ │ env files baked INTO the Docker image │
│ │ │ COPY env.pi2 ./ │ │ │
│ │ └─────────┬──────────┘ │ │
│ └────────────┼─────────────┘ │
│ v │
│ ┌──────────────────────────────────────────────────────────────────┐ │
│ │ Docker Container at Runtime │ │
│ │ │ │
│ │ ENV_TARGET=pi2 (from docker-compose environment:) │ │
│ │ │ │ │
│ │ v │ │
│ │ ┌──────────────────────────────┐ │ │
│ │ │ DotenvEnvironmentPostProcessor│ │ │
│ │ │ │ │ │
│ │ │ reads ENV_TARGET → "pi2" │ │ │
│ │ │ loads env.pi2 from image │ │ │
│ │ │ injects all vars into │ │ │
│ │ │ Spring Environment │ │ │
│ │ └──────────────────────────────┘ │ │
│ │ │ │
│ │ App sees: KAFKA_BOOTSTRAP_SERVERS, POSTGRES_HOST, │ │
│ │ JWT_SECRET, GOOGLE_CLIENT_ID, etc. │ │
│ └──────────────────────────────────────────────────────────────────┘ │
│ │
│ To update env vars: │
│ Jenkins → Manage Jenkins → Credentials → env.pi/env.pi2 │
│ → check "Replace" → upload new file → rebuild pipeline │
│ │
│ docker-compose environment: section only passes: │
│ SPRING_PROFILES_ACTIVE, ENV_TARGET, CONFIG_SERVER_URL, debug ports │
│ Everything else comes from the baked-in env file. │
└─────────────────────────────────────────────────────────────────────────────────┘
Config Server Gotcha
- Config server on PI2 uses
default-label: ${GIT_BRANCH}fromenv.pi2 - If
GIT_BRANCHinenv.pi2points to wrong branch, config server serves stale config - Fix: update
env.pi2in Jenkins Credentials, then rebuild - Config server URL pattern:
http://192.168.1.105:9297/{service}/{profile}/{label}
Where Does Everything Run?
All processes run inside the Jenkins agent machine (Raspberry Pi).
The Jenkins agent (jenkins-agent2) itself runs as a Docker container on the Raspberry Pi. When it executes docker compose commands, it communicates with the Docker daemon on the host machine through Docker Socket mounting.
┌─────────────────────────────────────────────────────────────────┐
│ Raspberry Pi (192.168.1.105) - HOST MACHINE │
│ │
│ ┌─────────────────────────────────────────────────────────┐ │
│ │ Docker Daemon (dockerd) │ │
│ │ │ │
│ │ ┌──────────────────┐ ┌──────────────────┐ │ │
│ │ │ jenkins-agent2 │ │ eureka-server │ │ │
│ │ │ (container) │ │ (container) │ │ │
│ │ │ │ └──────────────────┘ │ │
│ │ │ runs: │ ┌──────────────────┐ │ │
│ │ │ docker compose │ │ config-service │ │ │
│ │ │ ───────────────→ │ (container) │ │ │
│ │ │ │ └──────────────────┘ │ │
│ │ │ │ ┌──────────────────┐ │ │
│ │ │ │ │ gateway-service │ │ │
│ │ └──────────────────┘ │ (container) │ │ │
│ │ └──────────────────┘ │ │
│ └─────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
The “Magic”: Docker Socket Mounting
The Jenkins agent container has access to /var/run/docker.sock (the Docker socket), which allows it to communicate with the Docker daemon running on the host.
Key Points:
- Jenkins agent runs inside a Docker container
docker composecommands talk to the host’s Docker daemon- New containers are created as siblings (not nested inside Jenkins)
- All containers share the same network (
adventuretube-jenkins-network) - Services communicate via container names
- Built images and running containers are visible on the host
