許多組織使用Docker統一其跨機器的構建和測試環境,並提供一種用於部署應用程式的有效機制。從Pipeline 2.5及更高版本開始,Pipeline內置支持從內與Docker進行交互 Jenkinsfile。
雖然本節將介紹從a到Docker的使用基礎知識 Jenkinsfile,但不會涵蓋Docker的基礎知識,可以在Docker入門指南中進行閱讀 。
定製執行環境
Pipeline旨在輕鬆地將 Docker 映像用作單個Stage 或整個Pipeline 的執行環境 。這意味著用戶可以定義其管道所需的工具,而無需手動配置代理。幾乎任何可以 打包在Docker容器中的工具。只需對進行較小的修改即可輕鬆使用Jenkinsfile。
Jenkinsfile(聲明性管道)
pipeline { agent { docker { image 'node:7-alpine' } } stages { stage('Test') { steps { sh 'node --version' } } }}切換腳本管道 (高級)
當管道執行時,Jenkins將自動啟動指定的容器並在其中執行定義的步驟:
[Pipeline] stage[Pipeline] { (Test)[Pipeline] sh[guided-tour] Running shell script+ node --versionv7.4.0[Pipeline] }[Pipeline] // stage[Pipeline] }緩存容器數據
許多構建工具會下載外部依賴項並將其本地緩存以供將來重用。由於容器最初是使用「乾淨」文件系統創建的,因此這可能會導致管道運行速度變慢,因為它們可能無法利用後續管道運行之間的磁碟緩存。
管道支持添加傳遞給Docker的自定義參數,從而允許用戶指定 要安裝的自定義 Docker卷,可用於 在兩次管道運行之間在代理上緩存數據 。以下示例將~/.m2利用maven容器在兩次管道運行之間進行 緩存,從而避免為管道的後續運行重新下載依賴項。
Jenkinsfile(聲明性管道)
pipeline { agent { docker { image 'maven:3-alpine' args '-v $HOME/.m2:/root/.m2' } } stages { stage('Build') { steps { sh 'mvn -B' } } }}切換腳本管道 (高級)
使用多個容器
代碼庫依賴於多種不同的技術變得越來越普遍。例如,存儲庫可能同時具有基於Java的後端API實現和基於JavaScript的前端實現。結合使用Docker和Pipeline可以通過將指令與不同階段結合Jenkinsfile使用 多種技術agent {}。
Jenkinsfile(聲明性管道)
pipeline { agent none stages { stage('Back-end') { agent { docker { image 'maven:3-alpine' } } steps { sh 'mvn --version' } } stage('Front-end') { agent { docker { image 'node:7-alpine' } } steps { sh 'node --version' } } }}切換腳本管道 (高級)
使用Dockerfile
對於需要更多自定義執行環境的項目,Pipeline還支持從Dockerfile源存儲庫中構建和運行容器。與以前使用「現成」容器的方法相比,使用該agent { dockerfile true }語法Dockerfile將從Docker Hub而不是從 Docker Hub生成一個新映像。
重用上面的示例,並使用一個更自定義的示例Dockerfile:
Docker文件
FROM node:7-alpineRUN apk add -U subversion通過將其提交到源存儲庫的根目錄,Jenkinsfile可以將其更改為基於此構建容器Dockerfile,然後使用該容器運行定義的步驟:
Jenkinsfile(聲明性管道)
pipeline { agent { dockerfile true } stages { stage('Test') { steps { sh 'node --version' sh 'svn --version' } } }}該agent { dockerfile true }語法支持許多其他選項,這些選項將在「 管道語法」部分中詳細介紹 。
在Jenkins Pipeline中使用Dockerfile
指定Docker標籤
默認情況下,管道假定任何已配置的 代理都能夠運行基於Docker的管道。對於具有macOS,Windows或其他代理程序且無法運行Docker守護程序的Jenkins環境,此默認設置可能會出現問題。管道在「 管理Jenkins」頁面和「 文件夾」 級別提供了一個全局選項,用於指定要使用哪些代理(按 Label)來運行基於Docker的管道。
腳本管道的高級用法
運行「 sidecar」容器
在管道中使用Docker是運行構建或一組測試可能依賴的服務的有效方法。與sidecar模式類似 ,Docker Pipeline可以「在後臺」運行一個容器,而在另一個容器中執行工作。利用這種「邊車」方法,管道可以為每個管道運行配備一個「乾淨」的容器。
考慮一個假設的集成測試套件,該套件依賴於要運行的本地MySQL資料庫。使用withRun在Docker Pipeline插件對Scripted Pipeline的支持中實現的方法, Jenkinsfile可以將MySQL作為輔助工具運行:
node { checkout scm /* * In order to communicate with the MySQL server, this Pipeline explicitly * maps the port (`3306`) to a known port on the host machine. */ docker.image('mysql:5').withRun('-e "MYSQL_ROOT_PASSWORD=my-secret-pw" -p 3306:3306') { c -> /* Wait until mysql service is up */ sh 'while ! mysqladmin ping -h0.0.0.0 --silent; do sleep 1; done' /* Run some tests which require MySQL */ sh 'make check' }}可以進一步利用該示例,同時使用兩個容器。一個「邊車」運行MySQL,另一個通過Docker 容器連結提供執行環境。
node { checkout scm docker.image('mysql:5').withRun('-e "MYSQL_ROOT_PASSWORD=my-secret-pw"') { c -> docker.image('mysql:5').inside("--link ${c.id}:db") { /* Wait until mysql service is up */ sh 'while ! mysqladmin ping -hdb --silent; do sleep 1; done' } docker.image('centos:7').inside("--link ${c.id}:db") { /* * Run some tests which require MySQL, and assume that it is * available on the host name `db` */ sh 'make check' } }}上面的示例使用暴露的對象withRun,該對象具有通過id屬性提供的運行容器的ID 。通過使用容器的ID,管道可以通過將自定義Docker參數傳遞給inside()方法來創建連結 。
該id屬性對於在管道退出之前檢查正在運行的Docker容器中的日誌也很有用:
sh "docker logs ${c.id}"建築容器
為了創建Docker映像,Docker Pipeline 插件還提供了build()一種Dockerfile在Pipeline運行期間從存儲庫中的創建新映像的方法 。
使用該語法的一個主要好處docker.build("my-image-name")是,腳本化管道可以將返回值用於後續的Docker Pipeline調用,例如:
node { checkout scm def customImage = docker.build("my-image:${env.BUILD_ID}") customImage.inside { sh 'make test' }}返回值還可以用於通過以下方法將Docker映像發布到 Docker Hub或自定義註冊表,push()例如:
node { checkout scm def customImage = docker.build("my-image:${env.BUILD_ID}") customImage.push()}映像「標籤」的一種常見用法是為latest最新,經過驗證的Docker映像版本指定標籤。該push()方法接受一個可選tag參數,允許管道customImage使用不同的標籤推送,例如:
node { checkout scm def customImage = docker.build("my-image:${env.BUILD_ID}") customImage.push() customImage.push('latest')}該build()方法Dockerfile默認在當前目錄中構建。可以通過提供包含路徑Dockerfile作為方法的第二個參數的目錄路徑來覆蓋它build(),例如:
node { checkout scm def testImage = docker.build("test-image", "./dockerfiles/test") testImage.inside { sh 'make test' }}test-image根據位於的Dockerfile 構建./dockerfiles/test/Dockerfile。
通過將其他參數 添加到方法的第二個參數中,可以將其他參數傳遞給 docker buildbuild()。以這種方式傳遞參數時,該字符串中的最後一個值必須是docker文件的路徑,並且應以文件夾結尾作為構建上下文)
本示例Dockerfile通過傳遞-f 標誌來覆蓋默認值:
node { checkout scm def dockerfile = 'Dockerfile.test' def customImage = docker.build("my-image:${env.BUILD_ID}", "-f ${dockerfile} ./dockerfiles") }my-image:${env.BUILD_ID}根據位於的Dockerfile 構建./dockerfiles/Dockerfile.test。
使用遠程Docker伺服器
默認情況下,Docker Pipeline插件將與本地Docker守護進程通信,該守護進程通常通過訪問/var/run/docker.sock。
要選擇非默認的Docker伺服器,例如 Docker Swarm,withServer()應使用該方法。
通過使用以下方法將URI以及可選的Jenkins中預先配置的Docker伺服器證書身份驗證的憑據ID傳遞給方法:
node { checkout scm docker.withServer('tcp://swarm.example.com:2376', 'swarm-certs') { docker.image('mysql:5').withRun('-p 3306:3306') { /* do things */ } }}inside()並build()不會與碼頭工人群伺服器正常工作,開箱即用
為了inside()正常工作,Docker伺服器和Jenkins代理必須使用相同的文件系統,以便可以安裝工作空間。
當前,Jenkins插件和Docker CLI都不會自動檢測伺服器正在遠程運行的情況。典型的症狀是嵌套sh命令的錯誤,例如
cannot create /…@tmp/durable-…/pid: Directory nonexistent當詹金斯(Jenkins)檢測到代理本身在Docker容器中運行時,它將自動將--volumes-from參數傳遞給 inside容器,以確保它可以與代理共享工作區。
此外,某些版本的Docker Swarm不支持自定義註冊表。
使用自定義註冊表
默認情況下,Docker Pipeline集成假定Docker Hub為默認的Docker Registry 。
為了使用自定義Docker註冊表,腳本化管道的用戶可以使用withRegistry()方法包裝步驟,並傳入自定義註冊表URL,例如:
node { checkout scm docker.withRegistry('https://registry.example.com') { docker.image('my-custom-image').inside { sh 'make test' } }}對於需要身份驗證的Docker註冊表,請從Jenkins主頁添加「用戶名/密碼」憑據項,並將憑據ID作為第二個參數使用withRegistry():
node { checkout scm docker.withRegistry('https://registry.example.com', 'credentials-id') { def customImage = docker.build("my-image:${env.BUILD_ID}") /* Push the container to the custom Registry */ customImage.push() }}