AdventureTube Cloud Services – CI/CD Pipeline & Environment Variables

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

  1. BUILD args – Determine WHAT gets built (which base image, which module, which ports to expose)
  2. 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} from env.pi2
  • If GIT_BRANCH in env.pi2 points to wrong branch, config server serves stale config
  • Fix: update env.pi2 in 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 compose commands 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

Leave a Comment

Your email address will not be published. Required fields are marked *

Scroll to Top