Skip to content

feat: refactor deployment workflow, enhance service configurations, a… #86

feat: refactor deployment workflow, enhance service configurations, a…

feat: refactor deployment workflow, enhance service configurations, a… #86

Workflow file for this run

name: Build and Deploy to VM
on:
push:
branches: [ main ]
workflow_dispatch:
jobs:
build-and-deploy:
runs-on: ubuntu-latest
env:
ORACLE_VM_IP: ${{ secrets.ORACLE_VM_IP }}
DOCKER_REGISTRY: ${{ secrets.OCI_REGISTRY }}
OCI_USERNAME: ${{ secrets.OCI_USERNAME }}
OCI_AUTH_TOKEN: ${{ secrets.OCI_AUTH_TOKEN }}
SSH_KEY_PATH: ~/.ssh/id_rsa
steps:
- name: Checkout code
uses: actions/checkout@v3
- name: Set up JDK 8
uses: actions/setup-java@v3
with:
java-version: '8'
distribution: 'temurin'
cache: 'maven'
- name: Configure Maven settings
run: |
mkdir -p ~/.m2
cat > ~/.m2/settings.xml << EOF
<settings>
<mirrors>
<mirror>
<id>google-maven-central</id>
<name>Google Maven Central</name>
<url>https://maven-central.storage-download.googleapis.com/maven2/</url>
<mirrorOf>central</mirrorOf>
</mirror>
</mirrors>
</settings>
EOF
- name: Build with Maven (with retries)
run: |
max_attempts=3
attempt=1
while [ $attempt -le $max_attempts ]; do
echo "Build attempt $attempt of $max_attempts"
if mvn -B clean package -DskipTests; then
echo "Build succeeded"
break
else
echo "Build failed, retrying..."
attempt=$((attempt+1))
sleep 30
fi
done
if [ $attempt -gt $max_attempts ]; then
echo "Build failed after $max_attempts attempts"
exit 1
fi
- name: Verify build artifacts
run: |
echo "Checking JAR files..."
for service in eureka-server api-gateway recommendation-service statistics-service user-tracking-service; do
jar_files=$(find $service/target -type f -name "$service*.jar" | grep -v original || echo "")
if [ -z "$jar_files" ]; then
echo "ERROR: JAR file for $service not found!"
find $service/target -type f -name "*.jar" || echo "No JARs found"
exit 1
fi
done
- name: List target directory contents
run: |
echo "Listing all target directories contents..."
for service in eureka-server api-gateway recommendation-service statistics-service user-tracking-service; do
echo "=== $service/target contents: ==="
ls -la $service/target/ || echo "No target directory found"
done
- name: Prepare Docker build context
run: |
# Create directories under vm-deploy for each service
mkdir -p vm-deploy/{eureka-server,api-gateway,recommendation-service,statistics-service,user-tracking-service}
mkdir -p vm-deploy/config/{eureka-server,api-gateway,recommendation-service,statistics-service,user-tracking-service}
# Copy the pre-built JAR files directly into the vm-deploy directories with simplified names
cp $(find eureka-server/target -type f -name "eureka-server*.jar" | grep -v original) vm-deploy/eureka-server/eureka-server.jar
cp $(find api-gateway/target -type f -name "api-gateway*.jar" | grep -v original) vm-deploy/api-gateway/api-gateway.jar
cp $(find recommendation-service/target -type f -name "recommendation-service*.jar" | grep -v original) vm-deploy/recommendation-service/recommendation-service.jar
cp $(find statistics-service/target -type f -name "statistics-service*.jar" | grep -v original) vm-deploy/statistics-service/statistics-service.jar
cp $(find user-tracking-service/target -type f -name "user-tracking-service*.jar" | grep -v original) vm-deploy/user-tracking-service/user-tracking-service.jar
# Copy configuration files
cp -r vm-deploy/config/* vm-deploy/config/
# Use the simplified Dockerfiles in the vm-deploy directories
cat > vm-deploy/eureka-server/Dockerfile << 'EOF'
FROM openjdk:8-jre-slim
WORKDIR /app
COPY eureka-server.jar app.jar
EXPOSE 8761
ENTRYPOINT ["java", "-jar", "app.jar"]
EOF
cat > vm-deploy/api-gateway/Dockerfile << 'EOF'
FROM openjdk:8-jre-slim
WORKDIR /app
COPY api-gateway.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "app.jar"]
EOF
cat > vm-deploy/recommendation-service/Dockerfile << 'EOF'
FROM openjdk:8-jre-slim
WORKDIR /app
COPY recommendation-service.jar app.jar
EXPOSE 8082
ENTRYPOINT ["java", "-jar", "app.jar"]
EOF
cat > vm-deploy/statistics-service/Dockerfile << 'EOF'
FROM openjdk:8-jre-slim
WORKDIR /app
COPY statistics-service.jar app.jar
EXPOSE 8083
ENTRYPOINT ["java", "-jar", "app.jar"]
EOF
cat > vm-deploy/user-tracking-service/Dockerfile << 'EOF'
FROM openjdk:8-jre-slim
WORKDIR /app
COPY user-tracking-service.jar app.jar
EXPOSE 8084
ENTRYPOINT ["java", "-jar", "app.jar"]
EOF
# Create application.yml configuration files for each service
cat > vm-deploy/config/eureka-server/application.yml << 'EOF'
server:
port: 8761
spring:
application:
name: eureka-server
eureka:
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://eureka-server:8761/eureka/
server:
enableSelfPreservation: false
waitTimeInMsWhenSyncEmpty: 0
instance:
hostname: eureka-server
EOF
cat > vm-deploy/config/api-gateway/application.yml << 'EOF'
server:
port: 8080
spring:
application:
name: api-gateway
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://eureka-server:8761/eureka/
instance:
preferIpAddress: true
hostname: api-gateway
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 20
instanceId: ${spring.application.name}:${server.port}
EOF
cat > vm-deploy/config/recommendation-service/application.yml << 'EOF'
server:
port: 8082
spring:
application:
name: recommendation-service
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://eureka-server:8761/eureka/
instance:
preferIpAddress: true
hostname: recommendation-service
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 20
instanceId: ${spring.application.name}:${server.port}
EOF
cat > vm-deploy/config/statistics-service/application.yml << 'EOF'
server:
port: 8083
spring:
application:
name: statistics-service
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://eureka-server:8761/eureka/
instance:
preferIpAddress: true
hostname: statistics-service
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 20
instanceId: ${spring.application.name}:${server.port}
EOF
cat > vm-deploy/config/user-tracking-service/application.yml << 'EOF'
server:
port: 8084
spring:
application:
name: user-tracking-service
eureka:
client:
registerWithEureka: true
fetchRegistry: true
serviceUrl:
defaultZone: http://eureka-server:8761/eureka/
instance:
preferIpAddress: true
hostname: user-tracking-service
leaseRenewalIntervalInSeconds: 10
leaseExpirationDurationInSeconds: 20
instanceId: ${spring.application.name}:${server.port}
EOF
echo "Verifying copied files:"
ls -la vm-deploy/*/
ls -la vm-deploy/config/*/
- name: Debug environment variables
run: |
echo "Checking environment variables (sensitive data masked):"
echo "ORACLE_VM_IP is set: ${{ secrets.ORACLE_VM_IP != '' }}"
echo "DOCKER_REGISTRY is set: ${{ secrets.OCI_REGISTRY != '' }}"
echo "OCI_USERNAME is set: ${{ secrets.OCI_USERNAME != '' }}"
echo "OCI_AUTH_TOKEN is set: ${{ secrets.OCI_AUTH_TOKEN != '' }}"
- name: Set up SSH key
run: |
mkdir -p $HOME/.ssh
chmod 700 $HOME/.ssh
ls -la $HOME/.ssh || echo "Unable to list .ssh directory"
if [ -z "${{ secrets.SSH_PRIVATE_KEY }}" ]; then
echo "ERROR: SSH_PRIVATE_KEY secret is not set!"
exit 1
fi
echo "${{ secrets.SSH_PRIVATE_KEY }}" > $HOME/.ssh/id_rsa
ls -la $HOME/.ssh/id_rsa || { echo "ERROR: Failed to create SSH key file!"; exit 1; }
chmod 600 $HOME/.ssh/id_rsa || { echo "ERROR: Failed to set permissions on SSH key file!"; exit 1; }
key_size=$(stat -c %s $HOME/.ssh/id_rsa)
echo "SSH key file size: $key_size bytes"
if [ -z "${{ secrets.ORACLE_VM_IP }}" ]; then
echo "ERROR: ORACLE_VM_IP secret is not set!"
exit 1
fi
touch $HOME/.ssh/known_hosts
ssh-keyscan -H ${{ secrets.ORACLE_VM_IP }} >> $HOME/.ssh/known_hosts
echo "Testing SSH connection..."
ssh -o BatchMode=yes -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa opc@${{ secrets.ORACLE_VM_IP }} "echo SSH connection successful" || {
ls -la $HOME/.ssh/
head -1 $HOME/.ssh/id_rsa
exit 1
}
- name: Prepare environment file
run: |
cat > vm-deploy/.env << EOF
DOCKER_REGISTRY=${{ secrets.OCI_REGISTRY }}
OCI_USERNAME=${{ secrets.OCI_USERNAME }}
OCI_AUTH_TOKEN=${{ secrets.OCI_AUTH_TOKEN }}
EOF
echo "Created .env file for docker-compose"
echo "DOCKER_REGISTRY, OCI_USERNAME, OCI_AUTH_TOKEN are set"
- name: Deploy to Oracle VM
run: |
# Create the base directory on the remote VM
ssh -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa opc@${{ secrets.ORACLE_VM_IP }} "mkdir -p ~/music-analytics/vm-deploy ~/music-analytics/vm-deploy/config/{eureka-server,api-gateway,recommendation-service,statistics-service,user-tracking-service}"
# Create a new docker-compose.yml file locally
cat > vm-deploy/docker-compose.yml << 'EOF'
version: '3.8'
networks:
music-analytics-network:
driver: bridge
services:
eureka-server:
build: ./eureka-server
container_name: eureka-server
hostname: eureka-server
ports:
- "8761:8761"
networks:
- music-analytics-network
volumes:
- ./config/eureka-server:/config
environment:
- SPRING_CONFIG_LOCATION=file:/config/application.yml
- SPRING_APPLICATION_NAME=eureka-server
- EUREKA_INSTANCE_HOSTNAME=eureka-server
- EUREKA_CLIENT_REGISTERWITHEUREKASERVER=false
- EUREKA_CLIENT_FETCHREGISTRY=false
- EUREKA_SERVER_ENABLESELFREPLICATION=false
- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
- EUREKA_INSTANCE_INSTANCE_ID=eureka-server:8761
- EUREKA_INSTANCE_LEASE_RENEWAL_INTERVAL_IN_SECONDS=10
- EUREKA_INSTANCE_LEASE_EXPIRATION_DURATION_IN_SECONDS=20
healthcheck:
test: ["CMD", "curl", "-f", "http://eureka-server:8761/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
api-gateway:
build: ./api-gateway
container_name: api-gateway
hostname: api-gateway
ports:
- "8080:8080"
networks:
- music-analytics-network
volumes:
- ./config/api-gateway:/config
depends_on:
eureka-server:
condition: service_healthy
environment:
- SPRING_CONFIG_LOCATION=file:/config/application.yml
- SPRING_APPLICATION_NAME=api-gateway
- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
- EUREKA_INSTANCE_PREFERIPADDRESS=true
- SPRING_CLOUD_DISCOVERY_ENABLED=true
- SPRING_CLOUD_SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED=true
- EUREKA_INSTANCE_INSTANCE_ID=api-gateway:8080
- EUREKA_INSTANCE_LEASE_RENEWAL_INTERVAL_IN_SECONDS=10
- EUREKA_INSTANCE_LEASE_EXPIRATION_DURATION_IN_SECONDS=20
- EUREKA_CLIENT_REGISTER_WITH_EUREKA=true
- EUREKA_CLIENT_FETCH_REGISTRY=true
- CORS_ALLOWED_ORIGINS=https://musicanalytics.netlify.app,https://music-analytics.abenezeranglo.uk
healthcheck:
test: ["CMD", "curl", "-f", "http://api-gateway:8080/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
recommendation-service:
build: ./recommendation-service
container_name: recommendation-service
hostname: recommendation-service
ports:
- "8082:8082"
networks:
- music-analytics-network
volumes:
- ./config/recommendation-service:/config
- recommendation-data:/data
depends_on:
eureka-server:
condition: service_healthy
environment:
- SPRING_CONFIG_LOCATION=file:/config/application.yml
- SPRING_APPLICATION_NAME=recommendation-service
- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
- EUREKA_INSTANCE_PREFERIPADDRESS=true
- SPRING_CLOUD_DISCOVERY_ENABLED=true
- SPRING_CLOUD_SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED=true
- EUREKA_INSTANCE_INSTANCE_ID=recommendation-service:8082
- EUREKA_INSTANCE_LEASE_RENEWAL_INTERVAL_IN_SECONDS=10
- EUREKA_INSTANCE_LEASE_EXPIRATION_DURATION_IN_SECONDS=20
- EUREKA_CLIENT_REGISTER_WITH_EUREKA=true
- EUREKA_CLIENT_FETCH_REGISTRY=true
healthcheck:
test: ["CMD", "curl", "-f", "http://recommendation-service:8082/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
statistics-service:
build: ./statistics-service
container_name: statistics-service
hostname: statistics-service
ports:
- "8083:8083"
networks:
- music-analytics-network
volumes:
- ./config/statistics-service:/config
- statistics-data:/data
depends_on:
eureka-server:
condition: service_healthy
environment:
- SPRING_CONFIG_LOCATION=file:/config/application.yml
- SPRING_APPLICATION_NAME=statistics-service
- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
- EUREKA_INSTANCE_PREFERIPADDRESS=true
- SPRING_CLOUD_DISCOVERY_ENABLED=true
- SPRING_CLOUD_SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED=true
- EUREKA_INSTANCE_INSTANCE_ID=statistics-service:8083
- EUREKA_INSTANCE_LEASE_RENEWAL_INTERVAL_IN_SECONDS=10
- EUREKA_INSTANCE_LEASE_EXPIRATION_DURATION_IN_SECONDS=20
- EUREKA_CLIENT_REGISTER_WITH_EUREKA=true
- EUREKA_CLIENT_FETCH_REGISTRY=true
healthcheck:
test: ["CMD", "curl", "-f", "http://statistics-service:8083/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
user-tracking-service:
build: ./user-tracking-service
container_name: user-tracking-service
hostname: user-tracking-service
ports:
- "8084:8084"
networks:
- music-analytics-network
volumes:
- ./config/user-tracking-service:/config
- user-tracking-data:/data
depends_on:
eureka-server:
condition: service_healthy
environment:
- SPRING_CONFIG_LOCATION=file:/config/application.yml
- SPRING_APPLICATION_NAME=user-tracking-service
- EUREKA_CLIENT_SERVICEURL_DEFAULTZONE=http://eureka-server:8761/eureka/
- EUREKA_INSTANCE_PREFERIPADDRESS=true
- SPRING_CLOUD_DISCOVERY_ENABLED=true
- SPRING_CLOUD_SERVICE_REGISTRY_AUTO_REGISTRATION_ENABLED=true
- EUREKA_INSTANCE_INSTANCE_ID=user-tracking-service:8084
- EUREKA_INSTANCE_LEASE_RENEWAL_INTERVAL_IN_SECONDS=10
- EUREKA_INSTANCE_LEASE_EXPIRATION_DURATION_IN_SECONDS=20
- EUREKA_CLIENT_REGISTER_WITH_EUREKA=true
- EUREKA_CLIENT_FETCH_REGISTRY=true
healthcheck:
test: ["CMD", "curl", "-f", "http://user-tracking-service:8084/actuator/health"]
interval: 30s
timeout: 10s
retries: 3
volumes:
recommendation-data:
statistics-data:
user-tracking-data:
EOF
# Recursively copy each service directory to the remote VM with the destination path quoted
scp -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa -r vm-deploy/eureka-server opc@${{ secrets.ORACLE_VM_IP }}:"~/music-analytics/vm-deploy/"
scp -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa -r vm-deploy/api-gateway opc@${{ secrets.ORACLE_VM_IP }}:"~/music-analytics/vm-deploy/"
scp -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa -r vm-deploy/recommendation-service opc@${{ secrets.ORACLE_VM_IP }}:"~/music-analytics/vm-deploy/"
scp -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa -r vm-deploy/statistics-service opc@${{ secrets.ORACLE_VM_IP }}:"~/music-analytics/vm-deploy/"
scp -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa -r vm-deploy/user-tracking-service opc@${{ secrets.ORACLE_VM_IP }}:"~/music-analytics/vm-deploy/"
# Copy the configuration files to the remote VM
scp -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa -r vm-deploy/config/* opc@${{ secrets.ORACLE_VM_IP }}:"~/music-analytics/vm-deploy/config/"
# Copy the docker-compose file and environment file to the remote VM
scp -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa vm-deploy/docker-compose.yml opc@${{ secrets.ORACLE_VM_IP }}:"~/music-analytics/vm-deploy/docker-compose.yml"
scp -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa vm-deploy/.env opc@${{ secrets.ORACLE_VM_IP }}:"~/music-analytics/vm-deploy/.env"
# On the remote VM: log in to the Docker registry and deploy containers
ssh -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa opc@${{ secrets.ORACLE_VM_IP }} "cd ~/music-analytics/vm-deploy && \
echo 'Logging in to Docker registry...' && \
echo \"${{ secrets.OCI_AUTH_TOKEN }}\" | docker login ${{ secrets.OCI_REGISTRY }} -u ${{ secrets.OCI_USERNAME }} --password-stdin && \
echo 'Stopping and removing existing containers if any...' && \
docker-compose down || true && \
echo 'Building Docker images locally...' && \
docker-compose build --no-cache && \
echo 'Starting new containers with docker-compose...' && \
docker-compose up -d && \
echo 'Container status:' && docker-compose ps && \
echo 'Waiting 60 seconds for containers to initialize...' && \
sleep 60 && \
echo 'Checking Eureka registration status:' && \
docker exec vm-deploy-api-gateway-1 curl -s http://eureka-server:8761/eureka/apps | grep -A 3 '<app>' || echo 'No services registered yet'"
- name: Debug on failure
if: failure()
run: |
if [ -f "$HOME/.ssh/id_rsa" ]; then
ssh -o StrictHostKeyChecking=no -i $HOME/.ssh/id_rsa opc@${{ secrets.ORACLE_VM_IP }} '
echo "==== ENVIRONMENT VARIABLES ====" &&
env | grep DOCKER || echo "No Docker environment variables found" &&
echo "==== DOCKER-COMPOSE FILE CONTENT ====" &&
cat ~/music-analytics/vm-deploy/docker-compose.yml || echo "No docker-compose file found" &&
echo "==== CONFIG FILES ====" &&
for service in eureka-server api-gateway recommendation-service statistics-service user-tracking-service; do
echo "==== $service CONFIG CONTENT ====" &&
cat ~/music-analytics/vm-deploy/config/$service/application.yml || echo "No config file found for $service"
done &&
echo "==== DIRECTORY CONTENT ====" &&
ls -la ~/music-analytics/vm-deploy/ &&
for service in eureka-server api-gateway recommendation-service statistics-service user-tracking-service; do
echo "==== $service DIRECTORY CONTENT ====" &&
ls -la ~/music-analytics/vm-deploy/$service/
done &&
echo "==== EUREKA SERVER LOGS ====" &&
docker logs $(docker ps -q -f name=eureka-server) || echo "No eureka-server logs available" &&
echo "==== API GATEWAY LOGS ====" &&
docker logs $(docker ps -q -f name=api-gateway) || echo "No api-gateway logs available" &&
echo "==== ALL CONTAINERS ====" &&
docker ps -a || echo "No containers found"
'
else
echo "SSH key file not found, cannot retrieve logs"
fi