# 🚀 I Built a Jenkins CI/CD Pipeline From Scratch — Here's Every Bug I Hit (and How I Fixed Them)

2026年8月13日1 次浏览来源:Dev.to阅读原文

A learning-in-public story about Flask, Jenkins, AWS EC2, systemd, and finally shipping a live demo on Vercel. 🎯 TL;DR I built PyPulse, a tiny Flask app, and wired it up to a full CI/CD pipeline: push to GitHub → Jenkins builds → tests → deploys to AWS EC2 → auto-triggered via webhook → managed by systemd.

Along the way I broke almost every piece of it at least once, and fixed each one.

I also deployed a permanent live demo on Vercel, since my EC2 instance is running on the AWS free trial and won't live forever. 🔗 Live demo: pypulse-pi.vercel.app 🔗 Live demo (health check): pypulse-pi.vercel.app/health If you're learning DevOps and want to see what the real, messy version of "just set up a CI/CD pipeline" looks like — not the polished tutorial version — this is that. 🧰 The Stack Piece Tool Job App Flask + pytest + gunicorn The actual web app and its tests CI/CD Jenkins (on EC2, Ubuntu 22.04) Build → Test → Deploy automation Source control GitHub Single source of truth Trigger GitHub Webhook Auto-runs the pipeline on every push Process management systemd Keeps the app alive on reboot/crash Permanent demo Vercel Live URL that survives EC2 termination 🏗️ The App: PyPulse Nothing fancy on purpose — the whole point of this project was the pipeline, not the app. python app.py from flask import Flask, jsonify from datetime import datetime, timezone app = Flask(name) @app.route("/") def home(): return jsonify({ "message": "Hello from PyPulse", "time": datetime.now(timezone.utc).isoformat() }) @app.route("/health") def health(): return jsonify({"status": "ok"}), 200 if name == "main": app.run(host="0.0.0.0", port=5000) Two routes.

Two tests.

That's it.

Small enough that when something broke, I knew it wasn't the app — it was the plumbing around it.

That turned out to be the right call, because the plumbing broke a lot. 😅 ⚙️ The Pipeline: Build → Test → Deploy Here's the mental model I ended up with for a Jenkinsfile: Each stage is a gate.

If Build fails, Test never runs.

If Test fails, Deploy never runs.

That ordering is the entire value of CI/CD — automation with checkpoints, not just automation. 🔨 Build groovy stage('Build') { steps { sh ''' python3 -m venv venv . venv/bin/activate pip install -r requirements.txt ''' } } Runs on the Jenkins machine itself.

Fresh virtual environment, install dependencies.

If a dependency is broken or missing, this fails immediately — before wasting time testing or deploying broken code. ✅ Test groovy stage('Test') { steps { sh ''' . venv/bin/activate pytest ''' } } Runs the two tests against / and /health.

This is the safety net: if I break a route later, this stage fails and the pipeline stops before broken code ever reaches the live server. 🚀 Deploy groovy stage('Deploy') { steps { sshagent(credentials: ['pypulse-ec2-ssh']) { sh ''' set -e timeout 60 ssh -o StrictHostKeyChecking=no ${EC2_USER}@${EC2_IP} " set -e if [ ! -d ${APP_DIR} ]; then git clone https://github.com/IrfanPasha05/pypulse.git ${APP_DIR} fi cd ${APP_DIR} git pull origin main source venv/bin/activate pip install -r requirements.txt sudo systemctl restart pypulse " ''' } } } Only runs if Build and Test both passed.

SSHes into EC2, pulls the latest code, reinstalls dependencies, and restarts a systemd service (more on why, below). 🐛 The Debugging Journey (the actually useful part) Anyone can copy a working Jenkinsfile.

Here's what happened when mine didn't work — because this is the part that actually teaches you something.

Bug #1: "ensurepip is not available" The virtual environment was not created successfully because ensurepip is not available.

On Debian/Ubuntu systems, you need to install the python3-venv package What happened: Jenkins runs on the same machine as the OS, and that machine didn't have the python3-venv package installed for its Python version.

Fix: bash sudo apt install python3.14-venv -y Lesson: the Jenkins host machine needs every tool your build script assumes exists.

It's easy to forget Jenkins isn't magi

分享
Baike.dev

baike.dev helps you discover great languages, frameworks, databases, DevOps and cloud-native tools.

Quick links

About

Contribute

Found a great developer tool? Share it with the community.

Submit a tool
© 2026 baike.dev Developer EncyclopediaUpdated daily · Discover great developer tools