Article
For Java teams embracing microservices and cloud-native development, achieving robust and reliable deployment processes is crucial. Spinnaker, Netflix's open-source continuous delivery platform, excels at deploying applications across multiple cloud providers with powerful concepts like deployment strategies and automated verification.
This guide walks through building effective Spinnaker pipelines specifically tailored for Java applications.
Core Concepts for Java Teams
Before building pipelines, understand these key Spinnaker concepts:
- Application: Represents your Java service (e.g.,
user-service,payment-api) - Pipeline: The automated deployment workflow
- Stage: A single step in the pipeline (e.g., Build, Deploy, Test)
- Cluster/Server Group: A set of instances running your Java application
- Load Balancer: Distributes traffic to your Java service instances
- Deployment Strategies: Blue/Green and Canary deployments made easy
Sample Pipeline Architecture for Java
Here's a typical pipeline flow for a Java microservice:
- Trigger: On new Docker image in registry
- Bake: Create machine image with the new Java application version
- Deploy to Dev: Deploy to development environment
- Integration Tests: Run automated tests against dev
- Manual Judgment: Approval for production deployment
- Deploy to Prod: Deploy to production using Blue/Green strategy
- Smoke Tests: Verify production deployment
- Cleanup: Remove old server groups
Building the Pipeline: Stage by Stage
Stage 1: Pipeline Trigger
Configure what starts your pipeline. For Java applications, this is typically when a new Docker image is pushed to your registry.
# Example trigger configuration (via UI or JSON)
"triggers": [
{
"type": "docker",
"account": "docker-hub-account",
"organization": "mycompany",
"repository": "user-service",
"tag": "^v[0-9]+\\.[0-9]+\\.[0-9]+$", # Matches semantic version tags
"enabled": true
}
]
Alternative Triggers:
- Jenkins: Trigger after your Java build completes
- Git: On push to specific branches (main, release/*)
- Cron: Scheduled deployments
- Webhook: From any external system
Stage 2: Bake AMI (Amazon Machine Image)
Create a machine image containing your Java application. This ensures consistent, versioned infrastructure.
"stages": [
{
"type": "bake",
"name": "Bake Amazon Linux with Java App",
"amiName": "user-service-${execution.id}",
"baseOs": "ubuntu",
"baseLabel": "RELEASE",
"package": "user-service:${trigger['tag']}",
"region": "us-west-2",
"storeType": "ebs",
"vmType": "hvm",
"roscoOptions": {
"templateFileName": "java-app-bake.json"
}
}
]
Sample Bake Template (java-app-bake.json):
{
"system_packages": ["openjdk-17-jre-headless"],
"user_data": "#!/bin/bash\nsystemctl daemon-reload\nsystemctl enable my-java-app\nsystemctl start my-java-app"
}
Stage 3: Deploy to Development
Deploy the baked AMI to your development environment.
{
"type": "deploy",
"name": "Deploy to Dev",
"clusters": [
{
"account": "aws-dev-account",
"application": "user-service",
"availabilityZones": {"us-west-2": ["us-west-2a", "us-west-2b"]},
"capacity": {"min": 2, "max": 4, "desired": 2},
"cloudProvider": "aws",
"freeFormDetails": "dev",
"loadBalancers": ["user-service-dev-lb"],
"securityGroups": ["sg-dev-user-service"],
"strategy": "redblack",
"instanceType": "t3.medium",
"stack": "api"
}
]
}
Stage 4: Automated Integration Tests
Run tests against your deployed Java application.
{
"type": "jenkins",
"name": "Run Integration Tests",
"master": "jenkins-master",
"job": "user-service-integration-tests",
"parameters": {
"SERVICE_URL": "http://user-service-dev-lb-123456789.us-west-2.elb.amazonaws.com"
}
}
Alternative: Use a webhook stage to trigger tests in your preferred testing framework.
Stage 5: Manual Judgment for Production
Get human approval before proceeding to production.
{
"type": "manualJudgment",
"name": "Promote to Production?",
"judgmentInputs": [
{
"value": "YES",
"description": "Deploy to production"
},
{
"value": "NO",
"description": "Cancel deployment"
}
],
"instructions": "Verify integration tests passed and check metrics in Grafana.",
"sendNotifications": true
}
Stage 6: Deploy to Production (Blue/Green)
Deploy to production using a blue/green strategy for zero-downtime deployments.
{
"type": "deploy",
"name": "Deploy to Prod (Blue/Green)",
"clusters": [
{
"account": "aws-prod-account",
"application": "user-service",
"availabilityZones": {"us-west-2": ["us-west-2a", "us-west-2b"]},
"capacity": {"min": 4, "max": 10, "desired": 4},
"cloudProvider": "aws",
"freeFormDetails": "prod",
"loadBalancers": ["user-service-prod-lb"],
"securityGroups": ["sg-prod-user-service"],
"strategy": "redblack",
"instanceType": "t3.large",
"stack": "api",
"trafficGuards": [
{
"stack": "api",
"detail": "prod",
"actionOnTimeout": "REJECT"
}
]
}
]
}
Stage 7: Smoke Tests
Verify the production deployment is healthy.
{
"type": "webhook",
"name": "Production Smoke Tests",
"url": "https://internal-tests.company.com/run-smoke-tests",
"method": "POST",
"payload": {
"service": "user-service",
"environment": "production",
"version": "${trigger['tag']}"
},
"customHeaders": {
"Authorization": "Bearer ${secret['SMOKE_TEST_TOKEN']}"
}
}
Stage 8: Cleanup Old Server Groups
Remove previous versions to save costs and reduce clutter.
{
"type": "cleanup",
"name": "Cleanup Old Versions",
"account": "aws-prod-account",
"regions": ["us-west-2"],
"cluster": "user-service-main",
"numberToKeep": 3,
"retainNewer": true
}
Java-Specific Pipeline Considerations
1. JVM Configuration and Monitoring
Add a stage to validate JVM performance after deployment:
{
"type": "webhook",
"name": "Validate JVM Metrics",
"url": "https://monitoring.company.com/validate-jvm",
"method": "POST",
"payload": {
"application": "user-service",
"expectedMaxHeap": "512m",
"expectedGcType": "G1GC"
}
}
2. Database Migrations
Handle schema changes for your Java application:
{
"type": "webhook",
"name": "Run Database Migrations",
"url": "https://migration-service.company.com/run-migrations",
"method": "POST",
"payload": {
"service": "user-service",
"targetVersion": "${trigger['tag']}",
"environment": "production"
},
"stageEnabled": {
"expression": "${#stage('Deploy to Prod (Blue/Green)')['status'] == 'SUCCEEDED'}",
"type": "expression"
}
}
3. Feature Flag Verification
Integrate with ConfigCat or LaunchDarkly to verify feature flag states:
{
"type": "webhook",
"name": "Verify Feature Flags",
"url": "https://configcat.company.com/verify-flags",
"method": "POST",
"payload": {
"service": "user-service",
"version": "${trigger['tag']}",
"expectedFlags": {
"newSearchEnabled": false,
"premiumFeatures": true
}
}
}
Best Practices for Java Teams
- Pipeline as Code: Store pipeline definitions in Git alongside your Java code
- Environment-Specific Configuration: Use different configuration files for dev/staging/prod
- Rollback Strategy: Always include automated rollback stages that trigger on failed health checks
- Canary Analysis: Integrate with Kayenta for automated canary analysis of your Java application metrics
- Security: Use Spinnaker's secret management for database passwords and API keys
- Monitoring Integration: Connect pipeline stages to your observability stack (Prometheus, Grafana, Datadog)
Example Complete Pipeline JSON
{
"name": "User-Service Deployment",
"application": "user-service",
"triggers": [
{
"type": "docker",
"account": "docker-hub",
"repository": "mycompany/user-service",
"tag": "^v[0-9]+\\.[0-9]+\\.[0-9]+$"
}
],
"stages": [
{
"type": "bake",
"name": "Bake Image",
"region": "us-west-2",
"package": "user-service:${trigger['tag']}"
},
{
"type": "deploy",
"name": "Deploy to Dev",
"clusters": [
{
"account": "aws-dev",
"strategy": "redblack",
"capacity": {"min": 2, "max": 4, "desired": 2}
}
]
},
{
"type": "webhook",
"name": "Integration Tests",
"url": "${parameter['testUrl']}/run-tests"
},
{
"type": "manualJudgment",
"name": "Production Approval"
},
{
"type": "deploy",
"name": "Deploy to Prod",
"clusters": [
{
"account": "aws-prod",
"strategy": "redblack",
"capacity": {"min": 4, "max": 10, "desired": 4}
}
]
}
],
"parameters": [
{
"name": "testUrl",
"label": "Test Service URL",
"description": "URL for integration test service",
"default": "https://tests.company.com",
"type": "string"
}
]
}
Conclusion
Spinnaker provides Java teams with a powerful, enterprise-grade platform for implementing sophisticated deployment strategies. By building pipelines that incorporate baking, blue/green deployments, automated testing, and proper cleanup, you can achieve highly reliable, zero-downtime deployments for your Java applications.
The key to success is starting with a simple pipeline and gradually adding complexity as your team becomes more comfortable with Spinnaker's capabilities and your deployment requirements evolve.