如何在Jenkinsfile中实现Docker容器持久运行并替换旧容器
Great question! I’ve run into this exact issue with Jenkins’ Docker Pipeline plugin before—withRun is perfect for short-lived tasks like running tests or one-off scripts, but it’s designed to clean up containers as soon as the block finishes. To get persistent containers that stick around after the pipeline runs, plus handle stopping/restarting old instances, here’s a solid solution:
First, let’s break down why your current approach falls short: the withRun method creates a container, executes the code inside its block (in your case, just fetching logs), then automatically stops and deletes the container once the block completes. To maintain control over the container’s lifecycle, we’ll use direct docker commands instead.
Here’s the revised Deploy stage that handles persistent containers and cleans up old instances:
stage('Deploy') { steps { script { // Define a consistent name for your container to easily reference it later def containerName = "${IMAGE_NAME}-service" // Step 1: Clean up existing container (if it exists) sh """ // Stop the container if it's currently running docker stop ${containerName} || true // Remove the container completely (even if it was stopped) docker rm ${containerName} || true """ // Step 2: Start the new container in detached mode (so it runs in the background) sh """ docker run -d \ --name ${containerName} \ -e 'IMAGE=${IMAGE_NAME}:${BUILD_ID}' \ -e 'CNAME=${IMAGE_NAME}' \ -p ${PORT_1}:80 \ -p ${PORT_2}:443 \ deployscript:latest """ // Step 3: Verify the container is running and check startup logs sh "docker logs ${containerName}" sh "docker ps --filter name=${containerName}" } } }
Key Changes & Explanations:
- Replaced
withRunwith directdocker run: Adding the-dflag runs the container in detached mode, so it stays running after the Jenkins pipeline finishes executing. - Consistent Container Naming: Using a fixed name (
${IMAGE_NAME}-service) makes it easy to target the container for future stops, restarts, or deletions. - Safe Cleanup: The
|| trueat the end ofdocker stopanddocker rmensures the pipeline doesn’t fail if the container doesn’t exist yet (like on the first deployment). - Fixed Port Mapping: I swapped your
-e(environment variable) flags for ports with-p—Docker uses-pto map host ports to container ports, which was likely a typo in your original code that would’ve broken port routing.
Optional Enhancements:
- Auto-Restart Policy: Add
--restart unless-stoppedto thedocker runcommand if you want the container to automatically restart on failure or system reboot. - Health Checks: Verify your app is healthy before finishing the pipeline with these flags:
--health-cmd "curl -f http://localhost:80/health || exit 1" \ --health-interval 10s \ --health-timeout 5s \ --health-retries 3 - Stream Startup Logs: Instead of just fetching logs once, stream them for a short time to confirm the app boots correctly:
sh "docker logs -f ${containerName} --tail 100 & sleep 10; pkill -f 'docker logs'"
This approach gives you full control over your container’s lifecycle, ensuring it runs persistently after the pipeline completes, and handles updates to existing containers cleanly.
内容的提问来源于stack exchange,提问作者Adam Zarn




