init
This commit is contained in:
6
.dockerignore
Normal file
6
.dockerignore
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
Jenkinsfile
|
||||||
|
README.md
|
||||||
|
.env*
|
||||||
|
*.md
|
||||||
5
.env.example
Normal file
5
.env.example
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
# Copy to .env on deploy host (or set in Jenkins)
|
||||||
|
# These are passed to docker-compose during deployment
|
||||||
|
DOCKER_REGISTRY=docker.io
|
||||||
|
DOCKER_IMAGE=myorg/myapp
|
||||||
|
IMAGE_TAG=latest
|
||||||
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
.env
|
||||||
|
*.pyc
|
||||||
|
__pycache__/
|
||||||
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Simple example app - a minimal Python web server
|
||||||
|
FROM python:3.11-slim
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy application
|
||||||
|
COPY app.py .
|
||||||
|
|
||||||
|
EXPOSE 8080
|
||||||
|
|
||||||
|
CMD ["python", "-u", "app.py"]
|
||||||
87
Jenkinsfile
vendored
Normal file
87
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
pipeline {
|
||||||
|
agent any
|
||||||
|
|
||||||
|
environment {
|
||||||
|
// Configure these in Jenkins or as pipeline parameters
|
||||||
|
DOCKER_REGISTRY = 'docker.io'
|
||||||
|
DOCKER_IMAGE = 'myorg/myapp' // e.g., username/repo for Docker Hub
|
||||||
|
DEPLOY_HOST = 'deploy-server.example.com'
|
||||||
|
DEPLOY_USER = 'deploy'
|
||||||
|
DEPLOY_PATH = '/opt/myapp'
|
||||||
|
GIT_REPO_URL = 'https://github.com/myorg/jenkins-docker-deploy-example.git'
|
||||||
|
}
|
||||||
|
|
||||||
|
options {
|
||||||
|
buildDiscarder(logRotator(numToKeepStr: '10'))
|
||||||
|
timeout(time: 30, unit: 'MINUTES')
|
||||||
|
}
|
||||||
|
|
||||||
|
stages {
|
||||||
|
stage('Build Docker Image') {
|
||||||
|
steps {
|
||||||
|
script {
|
||||||
|
env.IMAGE_TAG = env.BRANCH_NAME == 'main' ? 'latest' : "${env.BUILD_NUMBER}-${env.GIT_COMMIT.take(7)}"
|
||||||
|
env.FULL_IMAGE = "${env.DOCKER_REGISTRY}/${env.DOCKER_IMAGE}:${env.IMAGE_TAG}"
|
||||||
|
}
|
||||||
|
echo "Building image: ${env.FULL_IMAGE}"
|
||||||
|
sh """
|
||||||
|
docker build -t ${env.FULL_IMAGE} .
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Push to Registry') {
|
||||||
|
steps {
|
||||||
|
withCredentials([usernamePassword(
|
||||||
|
credentialsId: 'docker-registry-credentials',
|
||||||
|
usernameVariable: 'DOCKER_USER',
|
||||||
|
passwordVariable: 'DOCKER_PASS'
|
||||||
|
)]) {
|
||||||
|
sh """
|
||||||
|
echo \$DOCKER_PASS | docker login -u \$DOCKER_USER --password-stdin ${env.DOCKER_REGISTRY}
|
||||||
|
docker push ${env.FULL_IMAGE}
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stage('Deploy via SSH') {
|
||||||
|
steps {
|
||||||
|
sshagent(credentials: ['deploy-ssh-key']) {
|
||||||
|
sh """
|
||||||
|
ssh -o StrictHostKeyChecking=no ${env.DEPLOY_USER}@${env.DEPLOY_HOST} << 'DEPLOY_EOF'
|
||||||
|
set -e
|
||||||
|
cd ${env.DEPLOY_PATH} || mkdir -p ${env.DEPLOY_PATH} && cd ${env.DEPLOY_PATH}
|
||||||
|
|
||||||
|
# Clone or pull the repo (contains docker-compose.yml)
|
||||||
|
if [ -d .git ]; then
|
||||||
|
git fetch origin
|
||||||
|
git reset --hard origin/${env.BRANCH_NAME}
|
||||||
|
else
|
||||||
|
git clone ${env.GIT_REPO_URL} .
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Set the image tag for this deployment
|
||||||
|
export IMAGE_TAG=${env.IMAGE_TAG}
|
||||||
|
export DOCKER_REGISTRY=${env.DOCKER_REGISTRY}
|
||||||
|
export DOCKER_IMAGE=${env.DOCKER_IMAGE}
|
||||||
|
|
||||||
|
# Pull latest image and deploy
|
||||||
|
docker compose pull
|
||||||
|
docker compose up -d
|
||||||
|
DEPLOY_EOF
|
||||||
|
"""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
success {
|
||||||
|
echo "Deployment successful! Image: ${env.FULL_IMAGE}"
|
||||||
|
}
|
||||||
|
failure {
|
||||||
|
echo "Deployment failed!"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
91
README.md
Normal file
91
README.md
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
# Jenkins Docker Deploy Example
|
||||||
|
|
||||||
|
Example repository demonstrating a Jenkins pipeline that:
|
||||||
|
|
||||||
|
1. **Builds** a Docker image
|
||||||
|
2. **Pushes** the image to a container registry (Docker Hub, etc.)
|
||||||
|
3. **SSHs** to a deployment machine
|
||||||
|
4. **Clones** (or pulls) this repo to get `docker-compose.yml`
|
||||||
|
5. **Deploys** with `docker compose up -d`
|
||||||
|
|
||||||
|
## Repository Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── Jenkinsfile # Pipeline definition
|
||||||
|
├── Dockerfile # Application image
|
||||||
|
├── docker-compose.yml # Deployment compose (pulled by deploy host)
|
||||||
|
├── app.py # Minimal Python web app
|
||||||
|
└── README.md
|
||||||
|
```
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
### Jenkins
|
||||||
|
|
||||||
|
- **Docker** installed and Jenkins user in `docker` group
|
||||||
|
- **Pipeline** and **SSH Agent** plugins
|
||||||
|
- **Git** for cloning
|
||||||
|
|
||||||
|
### Jenkins Credentials
|
||||||
|
|
||||||
|
Create these in **Manage Jenkins → Credentials**:
|
||||||
|
|
||||||
|
| ID | Type | Purpose |
|
||||||
|
|----|------|---------|
|
||||||
|
| `docker-registry-credentials` | Username/Password | Docker Hub or registry login |
|
||||||
|
| `deploy-ssh-key` | SSH Username with private key | SSH to deploy host |
|
||||||
|
|
||||||
|
### Deploy Host
|
||||||
|
|
||||||
|
- Docker and Docker Compose installed
|
||||||
|
- SSH access for the deploy user
|
||||||
|
- If using a **private registry**: run `docker login` on the deploy host (or add to the pipeline)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Edit the `environment` block in `Jenkinsfile`:
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
environment {
|
||||||
|
DOCKER_REGISTRY = 'docker.io' // or your registry (ghcr.io, etc.)
|
||||||
|
DOCKER_IMAGE = 'myorg/myapp' // e.g., username/repo
|
||||||
|
DEPLOY_HOST = 'deploy-server.example.com'
|
||||||
|
DEPLOY_USER = 'deploy'
|
||||||
|
DEPLOY_PATH = '/opt/myapp' // Where to clone & run compose
|
||||||
|
GIT_REPO_URL = 'https://github.com/myorg/jenkins-docker-deploy-example.git'
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Pipeline Stages
|
||||||
|
|
||||||
|
1. **Build** – `docker build` with tag from branch (e.g. `latest` for main, `123-abc1234` for others)
|
||||||
|
2. **Push** – `docker push` to registry using stored credentials
|
||||||
|
3. **Deploy** – SSH to host, clone/pull repo, run `docker compose up -d`
|
||||||
|
|
||||||
|
## First-Time Deploy Host Setup
|
||||||
|
|
||||||
|
On the deploy host:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create deploy directory
|
||||||
|
sudo mkdir -p /opt/myapp
|
||||||
|
sudo chown deploy:deploy /opt/myapp
|
||||||
|
|
||||||
|
# Ensure deploy user can run docker (add to docker group)
|
||||||
|
sudo usermod -aG docker deploy
|
||||||
|
```
|
||||||
|
|
||||||
|
## Manual Test
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build and run locally
|
||||||
|
docker build -t myapp:test .
|
||||||
|
docker run -p 8080:8080 myapp:test
|
||||||
|
# Visit http://localhost:8080
|
||||||
|
```
|
||||||
|
|
||||||
|
## Branch Behavior
|
||||||
|
|
||||||
|
- **main** → image tag `latest`
|
||||||
|
- **other branches** → image tag `{BUILD_NUMBER}-{GIT_SHA}`
|
||||||
26
app.py
Normal file
26
app.py
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Minimal web app for deployment example."""
|
||||||
|
import os
|
||||||
|
from http.server import HTTPServer, BaseHTTPRequestHandler
|
||||||
|
|
||||||
|
PORT = int(os.environ.get("PORT", 8080))
|
||||||
|
VERSION = os.environ.get("VERSION", "dev")
|
||||||
|
|
||||||
|
|
||||||
|
class Handler(BaseHTTPRequestHandler):
|
||||||
|
def do_GET(self):
|
||||||
|
self.send_response(200)
|
||||||
|
self.send_header("Content-type", "text/html")
|
||||||
|
self.end_headers()
|
||||||
|
self.wfile.write(
|
||||||
|
f"<h1>Hello from Docker!</h1><p>Version: {VERSION}</p>".encode()
|
||||||
|
)
|
||||||
|
|
||||||
|
def log_message(self, format, *args):
|
||||||
|
print(f"[{self.log_date_time_string()}] {format % args}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
server = HTTPServer(("", PORT), Handler)
|
||||||
|
print(f"Server running on port {PORT}")
|
||||||
|
server.serve_forever()
|
||||||
11
docker-compose.yml
Normal file
11
docker-compose.yml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# Docker Compose for deployment
|
||||||
|
# IMAGE_TAG is set by Jenkins during deployment (e.g., latest, or git commit SHA)
|
||||||
|
services:
|
||||||
|
app:
|
||||||
|
image: ${DOCKER_REGISTRY:-docker.io}/${DOCKER_IMAGE:-myapp}:${IMAGE_TAG:-latest}
|
||||||
|
container_name: jenkins-deploy-app
|
||||||
|
ports:
|
||||||
|
- "8080:8080"
|
||||||
|
environment:
|
||||||
|
- VERSION=${IMAGE_TAG:-latest}
|
||||||
|
restart: unless-stopped
|
||||||
Reference in New Issue
Block a user