A step-by-step guide to automating build, code quality checks, Docker image push and Kubernetes deployment using Jenkins on AWS EC2."
tags: aws, devops, kubernetes, jenkins
Shipping code by hand is slow and easy to get wrong. So I built a complete CI/CD pipeline on AWS: every push to GitHub is compiled, scanned for code quality, packaged, containerized, pushed to Docker Hub and deployed to Kubernetes automatically.
vishalcloudjourney
🏗️ Architecture
GitHub → Jenkins (EC2) → Maven → SonarQube → Docker → Docker Hub → Kubernetes
Pipeline stages
- Declarative: Tool Install
- Git Checkout
- Code Compile
- SonarQube Analysis
- Maven Build
- Docker Build
- Docker Image Push
- Deploy to Kubernetes
- Declarative: Post Actions
Tools used
AWS EC2, Jenkins, Maven, SonarQube, Docker, Docker Hub, Kubernetes (kubectl), GitHub
Step 1: Launch EC2 instances
- Launch an Ubuntu EC2 instance (t2.medium or larger) for Jenkins and SonarQube.
- Launch instances for your Kubernetes cluster (or use EKS).
- Open these ports in the Security Group:
| Port | Purpose |
|---|---|
| 22 | SSH |
| 8080 | Jenkins |
| 9000 | SonarQube |
| 6443 | Kubernetes API (restrict to the Jenkins server IP) |
| 30000-32767 | NodePort range for your app |
💡 Avoid opening ports to
0.0.0.0/0unless you have to. Restrict them to your own IP wherever possible.
Step 2: Install Jenkins
sudo apt update sudo apt install openjdk-17-jdk -y # Add the Jenkins repository, then: sudo apt install jenkins -y sudo systemctl enable --now jenkins Open http://<EC2-Public-IP>:8080, unlock Jenkins with the initial admin password and install the suggested plugins.
Step 3: Install Docker, Maven and kubectl
sudo apt install docker.io maven -y sudo usermod -aG docker jenkins sudo systemctl restart jenkins # install kubectl as per the official Kubernetes docs Adding jenkins to the docker group lets pipeline builds run Docker commands.
Step 4: Set up SonarQube
docker run -d --name sonarqube -p 9000:9000 sonarqube:lts-community - Log in at
http://<EC2-IP>:9000. - Generate a token under My Account → Security.
- Create a project and note its project key.
Step 5: Configure Jenkins
Plugins: SonarQube Scanner, Docker Pipeline, Kubernetes CLI, Pipeline Stage View, Maven Integration.
Credentials (Manage Jenkins → Credentials):
-
dockerhub-cred: Docker Hub username and password -
sonar-token: the SonarQube token -
k8s-config: your kubeconfig file, as a Secret file
Tools (Manage Jenkins → Tools): add Maven and JDK.
SonarQube server (Manage Jenkins → System): add the server URL and token.
Step 6: Write the Jenkinsfile
pipeline { agent any tools { maven 'maven3' } environment { IMAGE = "your-dockerhub-user/your-app" TAG = "${BUILD_NUMBER}" } stages { stage('Git Checkout') { steps { git branch: 'main', url: 'https://github.com/<you>/<repo>.git' } } stage('Code Compile') { steps { sh 'mvn compile' } } stage('SonarQube Analysis') { steps { withSonarQubeEnv('sonar') { sh 'mvn sonar:sonar' } } } stage('Maven Build') { steps { sh 'mvn clean package -DskipTests' } } stage('Docker Build') { steps { sh "docker build -t $IMAGE:$TAG ." } } stage('Docker Image Push') { steps { withCredentials([usernamePassword(credentialsId: 'dockerhub-cred', usernameVariable: 'U', passwordVariable: 'P')]) { sh 'echo $P | docker login -u $U --password-stdin' sh "docker push $IMAGE:$TAG" } } } stage('Deploy to Kubernetes') { steps { withKubeConfig(credentialsId: 'k8s-config') { sh "sed -i 's|IMAGE_TAG|$TAG|g' k8s/deployment.yaml" sh 'kubectl apply -f k8s/deployment.yaml' sh 'kubectl apply -f k8s/service.yaml' } } } } } Step 7: Kubernetes manifests
Keep these in a k8s/ folder in your repo.
deployment.yaml
apiVersion: apps/v1 kind: Deployment metadata: name: my-app spec: replicas: 2 selector: matchLabels: app: my-app template: metadata: labels: app: my-app spec: containers: - name: my-app image: your-dockerhub-user/your-app:IMAGE_TAG ports: - containerPort: 8080 service.yaml
apiVersion: v1 kind: Service metadata: name: my-app-svc spec: type: NodePort selector: app: my-app ports: - port: 80 targetPort: 8080 nodePort: 30080 Step 8: Run the pipeline
Create a Pipeline job, point it at your repo's Jenkinsfile and click Build Now. The Stage View shows each stage turning green, and the SonarQube Quality Gate shows Passed when your code meets the quality rules.
🛠️ Troubleshooting: when "Deploy to Kubernetes" fails
My first builds (#25, #26 and #27) passed every stage until Deploy to Kubernetes, then failed. Build #25 failed in under a second, while #26 and #27 ran for about 3 minutes before failing. Build #28 passed, and the deploy stage took about 38 seconds.
Since everything before the deploy stage worked, the problem was between Jenkins and the cluster. A failure after about 3 minutes usually points to a connection timeout, and a near-instant failure usually points to a credential or config error. Things to check:
- Kubeconfig credential: is it uploaded correctly, and does the
withKubeConfigID match? - Network access: can the Jenkins server reach the cluster API on port 6443? Check the Security Group and the server IP in the kubeconfig.
- Manual test: SSH into the Jenkins server and run
kubectl get nodesas thejenkinsuser. - Manifest errors: check the YAML, image name and tag.
- Pod state: run
kubectl get podsandkubectl describe podto spot image pull errors.
📚 Key learnings
- Automating the path from commit to deployment removes manual mistakes.
- SonarQube's Quality Gate catches bad code before it ships.
- Tagging images with the build number makes every deployment traceable and easy to roll back.
- Most pipeline failures come from credentials, permissions and networking, so check those first.
🚀 What's next
- Add a GitHub webhook so builds trigger on every push.
- Move to Amazon EKS and Amazon ECR.
- Add Slack or SNS notifications for build status.
🤝 Connect with me
- LinkedIn: Vishal Patil
- GitHub: VishalPatil-DevOps-AWS
- dev.to: @vishal_patil_aws
If this helped you, drop a ❤️ and share what you're building in the comments!
