在Docker容器中運行Jenkins
【編者的話】這篇博客是Riot的Docker實踐系列博客的第二篇,主要內容是:基于Cloudbees鏡像創建新的Dockerfile,設置了一個日志目錄,并介紹了如何使用docker exec命令查看日志文件。
當我一年前開始學習Docker的時候,發現很難找到好的文檔和實例,即使是今天,也只能找到一些簡單的用例,完全不能用作真正的產品。使用Docker容器來產品化應用,需要適應容器的短暫性和單一進程的特性。這對于需要數據持久化和多進程的應用來說,是一個挑戰。
就像我 上一篇博客 中提到的,我們在Jenkins這樣一個開源軟件的基礎上構建自己的自動化系統。Jenkins也是一種容器化應用的一種方式。我們使用以下的體系結構組件來部署Jenkins:
- Jenkins主服務器(Java進程)
- Jenkins master data (插件,任務定義等)
- NGINX web代理(使用SSL證書)
- 構建slave代理(可以通過SSH、JNLP或Jenkins Master連接)
這是一個很好的開端。通過這一系列博客,我們考慮將上述提到的組件容器化,并使用Docker容器來構建從節點(slaves)。對于初學者,我們將在Docker容器中創建Jenkins主服務器,然后轉向處理數據持久化,并通過NGINX添加web代理。
整個系列博客將涉及以下幾個Docker概念:
- 創建你自己的Dockerfiles
- 最小化對于公共鏡像的依賴
- 創建和使用數據卷(Data-Volumes),包括備份
- 利用容器創建容器化的“構建環境”
- 使用鏡像和Jenkins來處理“機密”數據
如果你還沒有看過Cloudbees的Jenkins Docker鏡像,就從它開始吧,這是一個相當不錯的開端。我剛開始就參考了它,在Docker容器中運行Jenkins,對于很多人來說,這就足夠了。你可以找到相關的 文檔 和 Git倉庫 。
這篇博客分為了兩課,每一課需要30分鐘來完成。第一課是準備好你的開發環境,并學習如何使用Cloudbees提供的Jenkins Docker容器。第二課是奠定基礎,定義自己的Dockerfile,并更優雅地控制鏡像。這些課程是設計來起步的,尤其是當你從未用過Docker, 盡管我們是假設你已經熟悉Jenkins的用法。如果你已經有Docker的使用經驗,那么第一課中的某些內容,你可能已經了解了。
第一課:構建并運行你的第一個鏡像
準備你的開發環境
讓我們從準備環境開始。在Riot,我們在Windows、Mac OSX和Linux上運行Docker和Jenkins。盡管1.6版本之后,Docker就可以完美地運行在Windows上了,但是,我個人比較喜歡 在OSX上運行Docker。無論哪一種方式,你的Docker宿主機服務器都是運行在一臺Linux服務器上的。我最喜歡的一個工具Docker- Compose也不兼容Windows(Docker 1.8和Compose 1.4)。在每個方式(OSX或Windows)中,你都需要安裝Docker Toolbox(之前叫Boot2Docker)。
順便說一下,微軟正在計劃在2016年發布一個支持原生Docker的Windows服務器,我們非常期待Windows Docker容器。現在,我們將集中在現有的技術上。
要求
- 你需要Windows 7(或更高版本)或者Mac OSX 10.7(或更高版本)。
- 你需要開啟BIOS中的虛擬化選項來運行VirtualBox。
- 如果你已經安裝了Virtualbox和Docker Toolbox,你就可以跳過步驟1。這篇博客使用的是Docker Toolbox 1.8和Virtualbox 5.0。
- 請注意:如果你事先安裝了Virtualbox,那么在安裝Docker Toolbox時可能會遇到一些有趣的問題。當我這么做時,Virtualbox并沒有完全升級,現有的鏡像配置信息也遭到了破壞。我建議先卸載 Virtualbox,并將之前的鏡像導出為OVA文件或ISO文件。 * 如果你在老版本的boot2docker的基礎上安裝新版本的Docker Toolbox,也會有問題。因此,最好首先卸載boot2docker和ISO鏡像。
KITEMATIC簡介
在Docker 1.8和Docker Toolbox中,已經包含了“Kitematic”,它是一個漂亮的GUI工具,來管理和可視化Docker鏡像和容器。這篇教程大部分專注在使用命令 行來控制Docker。這是為了更好地暴露給讀者底層的機制。同樣地,后續博客將涉及Compose的用法,同時開啟和關閉多個容器。然 而,Kitematic的確是一個很酷的工具,未來我會專門寫一篇博客介紹如何在開發生命周期中使用它。
步驟1:安裝Docker Toolbox
- 點擊 https://www.docker.com/toolbox
- 下載和安裝Docker Toolbox。請注意,在此過程中將安裝VirtualBox和一些其他工具(Compose, Kitematic, Docker Machine)。
- 按照步驟安裝。
- 運行“Docker Quickstart Terminal”,來驗證安裝是否成功。在Windows上,在桌面上有一個快捷方式;在Max OSX上,它在你的Docker文件夾中。然后,運行以下命令,并確保沒有發生任何錯誤。
docker ps
docker info
步驟2:查詢你的IP地址
我們需要訪問Jenkins web服務器(最終是一個NGINX服務器)。最簡單的方式,就是在瀏覽器中直接輸入Docker宿主機的IP地址。
你可以使用ifconfig或ipconfig來查詢IP地址,但是Docker Toolbox通過docker-machine提供了一個方便的命令:
docker-machine ip default
這就是你的宿主機的IP地址,你的web服務就監聽在上面。當然,這個IP是在你本地的機器上,并不能被外部訪問。如果你希望從外部訪問你的機 器,你需要在Virtualbox上設置端口轉發(port forwarding)。我把這個留給你作為一個練習,可以參考Virtualbox的文檔。
步驟3:Pull并運行Cloudbees的Jenkins容器
- 登錄到Docker的終端窗口。
- 從公共倉庫中pull Jenkins鏡像:
docker pull jenkins
docker run -p 8080:8080 --name=jenkins-master jenkins - 打開瀏覽器,并訪問http://yourdockermachineiphere:8080。
如果Jenkins沒有顯示在瀏覽器中,但是容器正在運行,那么請再次確認是否正確執行了步驟2,并獲得了正確的IP地址。
請注意,你可能注意到我使用了--name這個參數,并將這個容器命名為jenkins-master。命名容器是一個最佳實踐,有三個優點:
1. 更容易記憶
2. Docker不允許兩個容器使用同一個名字,可以防止開啟兩個同名容器。
3. 很多通用的Docker工具(如docker-compose)需要使用特定的容器名字,因此命名容器是一個最佳實踐。
步驟4:更實用一點
之前的步驟是以最基本的參數來啟動Jenkins。就像Riot一樣,你是不太可能直接用默認配置來運行Jenkins的。讓我們加上一些有用的參數,并解釋為什么這么加。
步驟4A: 守護進程
你可能不希望直接將Jenkins的日志打到標準輸出中。因此使用Docker的daemon標志(-d)來開啟容器。
- 用Ctrl-c關閉你的容器
- 運行以下命令:
docker rm jenkins-master
docker run -p 8080:8080 --name=jenkins-master -d jenkins
現在你應該可以獲得一個hash字符串。如果你是剛接觸Docker的話,那個hash值就是你的容器的唯一ID。
步驟4B: 內存設置
在Riot,我們需要一些健壯的設置來運行Jenkins。運行以下命令:
docker stop jenkins-masterdocker rm jenkins-master
docker run -p 8080:8080 --name=jenkins-master -d --env JAVA_OPTS="-Xmx8192m" jenkins
這就會給Jenkins 8GB的內存空間來做GC(garbage collection)。請注意,如果你使用的是Java 1.7或者更早的版本,請使用一下指令:
docker run -p 8080:8080 --name=jenkins-master -d --env JAVA_OPTS=”-Xmx8192m -XX:PermSize=256m -XX:MaxPermSize=1024m” jenkinsCloudbees的容器使用的是Java 1.8,我們不需要設置PermSize,因為Java 1.8沒有PermGen。
步驟4C: 升級連接池(Connection Pool)
STEP 4C: UPPING THE CONNECTION POOL
在Riot,大量的流量發往Jenkins服務器,因此我們需要給Jenkins設置更大的連接池。運行以下命令:
docker stop jenkins-masterdocker rm jenkins-master
docker run -p 8080:8080 --name=jenkins-master -d --env JAVA_OPTS="-Xmx8192m" --env JENKINS_OPTS="--handlerCountStartup=100 --handlerCountMax=300" jenkins
你已經學會了如何使用JAVA OPTS和JENKINS OPTS作為環境變量。請注意,這是因為Cloudbees方便地組織了Dockerfile。
步驟5: 將這些組合起來
我把這些學到的內容放到一個簡單的makefile中,你就可以用make命令來控制運行Jenkins Docker容器。你可以在這里找到:
你可以運行以下命令:
- make build - 下拉Jenkins鏡像
- make run - 運行容器
- make stop - 關閉容器
- make clean - 關閉和刪除已有容器
你不是必須使用makefile,只是使用它可以不用輸入太多命令。你可以將這些命令放到一個腳本中。
COMMENTS
希望你能看到運行Docker和Jenkins是多么容易。我已經提供了一些基本的選項,來運行默認的Cloudbees Jenkins容器,并使其更實用。Cloudbees提供了很多有用的建議,例如如何預安裝插件和存儲Jenkins數據。
這個鏡像也有一些缺點:沒有一致的日志記錄、沒有持久化、沒有web服務器代理、不能保證使用正確版本的Jenkins。這帶來了一個問題:如果你想使用一個老版本,怎么辦?或者你想使用最新版本?
在下一課中,我將介紹如何增強容器的魯棒性。特別是:
- 以Cloudbees為基礎,創建自己的Dockerfile
- 將部分環境變量移到這個新的鏡像中
- 創建一個日志目錄,設置權限和其他有用的Jenkins目錄,以及如何獲得Jenkins實時的運行日志
第2課 - 基于Jenkins的鏡像
在前面的課程中,我們討論了如何準備開發環境,來運行Docker和Cloudbees提供的Jenkins鏡像。我們發現這很簡單,也易于使用,也有很多不錯的特性。為了進一步進行優化,在本課程中將涉及以下幾個概念:
- 創建自己的Dockerfile
- 在Dockerfile中設置環境變量
- 在Dockerfile中創建目錄,并設置權限
- 使用docker exec執行命令
創建基礎Dockerfile
我們希望改變Jenkins啟動的默認行為。在上一篇博客中,我們的方法是通過makefile傳遞了一些參數作為環境變量。考慮到我們每次啟動容器都需要這么做,因此我們可以把這些環境變量放到Dockerfile中。另外,我們自己的Dockerfile可以鎖定Jenkins的版本,以防我們還沒準備好升級Jenkins版本。
我們通過以下四步完成這一點:
- 創建工作路徑。
- 使用你最喜愛的文本編輯器,創建一個新文件“Dockerfile”。
- 添加以下內容,并保存。
FROM jenkins:1.609.1
MAINTAINER yourname - 在命令行中,輸入以下命令:
docker build -t myjenkins .
我們所做的是,從公共的Docker倉庫中下拉一個特定版本的Jenkins鏡像。你可以找到所有可用的版本:
你總是可以使用FROM語句來設置任意版本的可用鏡像。然而,你卻不能設置成Jenkins的任意版本。因為這個版本實際上指的是鏡像版本 (“tag”或“lable”),Cloudbees很友善地將它設置為Jenkins版本。但是Cloudbees并沒有為每個Jenkins版本構建 鏡像,只支持長期穩定版本。如果你希望使用一個特定版本,那么請關注后續博客,我將介紹不使用Cloudbees鏡像,創建自己的鏡像。
測試新的Dockerfile
我們可以運行以下命令,很容易地切換至新鏡像:
docker run -p 8080:8080 --name=jenkins-master -d --env JAVA_OPTS="-Xmx8192m" --env JENKINS_OPTS="--handlerCountStartup=100 --handlerCountMax=300" myjenkins我們可以將那些環境變量放在Dockerfile中。
添加環境變量到Dockerfile中
添加像環境變量這樣的默認配置,是很容易的。這也提供了一種自文檔化(self-documentation)的方式。而且,你總是可以在運行Docker容器的時候重寫這些環境變量。
- 在Dockerfile的“MAINTAINER”一行下,添加以下內容:
ENV JAVA_OPTS="-Xmx8192m"
ENV JENKINS_OPTS="--handlerCountStartup=100 --handlerCountMax=300" - 保存并重建鏡像:
docker build -t myjenkins .
相當簡單!你可以輸入以下三條命令來測試鏡像是否成功:
docker stop jenkins-masterdocker rm jenkins-master
docker run -p 8080:8080 --name=jenkins-master -d myjenkins
你的鏡像應該可以立即啟動!但是如何知道那些環境變量是否生效了呢?這很簡單:可以使用ps命令來查看Jenkins的啟動參數,即使在Windows中這也適用。
在容器中運行簡單的命令
為了驗證我們設置的Java和Jenkins選項,我們可以運行Docker exec來查看Jenkins進程的運行情況:
docker exec jenkins-master ps -ef | grep java你應該可以看到類似的輸出:
jenkins 1 0 99 21:28 ? 00:00:35 java -Xmx8192m -jar /usr/share/jenkins/jenkins.war --handlerCountStartup=100 --handlerCountMax=300
你可以看到我們的設置已經生效。docker exec是一個在容器中執行shell命令的簡單方式。這個方法甚至在Windows中也適用,原因是“exec”執行的命令是運行在容器中的,它是由容器使用的鏡像決定的。
創建日志目錄
在上一篇博客中,當我們使用守護進程參數(-d)運行容器時,我們看不到Jenkins的日志。我們想要使用Jenkins的內置特性來設置一個日志目錄。我們需要在Dockerfile中創建它,并以參數形式傳遞給Jenkins。
讓我們再次編輯Dockerfile,在“MAINTAINER”和第一行ENV命令之間,加入以下內容:
RUN mkdir /var/log/jenkins之所以將這條指令放在這個位置,是遵循了最佳實踐。相對于創建的目錄而言,我們更有可能修改環境變量。Dockerfile中的每一行都是鏡像的一層,因此,把經常變化的指令盡可能放在文件尾部,可以最大程度地重用這些層。
再次創建鏡像:
docker build -t myjenkins .你將會遇到類似的錯誤:
---> Running in 0b5ac2bce13bmkdir: cannot create directory ‘/var/log/jenkins’: Permission denied
不用擔心,這是因為Cloudbees默認容器使用了“Jenkins”用戶。你可以從Dockerfile( https://github.com/ Jenkinsci/docker/blob/master/Dockerfile)的底部看到:
USER jenkins這對于創建/var/log/jenkins是不方便的,通常情況下,需要使用SUDO或者其他方式來創建目錄(/var/log是root用戶所有的)。幸運的是,Docker運行我們切換用戶。在Dockerfile中添加以下內容:
- 在RUN mkdir line之前添加:
USER root - 在RUN mkdir line之后添加:
RUN chown -R jenkins:jenkins /var/log/jenkins - 在RUN chown line之后添加:
USER jenkins
注意:我們需要添加chown命令,因為我們想要Jenkins用戶寫這個目錄。然后,我們將用戶重置為Jenkins,從而限制Dockerfile的行為。
再次構建鏡像:
docker build -t myjenkins .
之前的錯誤消失了。
通過設置日志目錄(請注意:你可以設置成任意目錄,為了一致性,我們選擇了/var/log),我們可以修改JENKINS_OPTS環境變量,讓Jenkins將日志寫入該目錄。
在Dockerfile中,設置JENKINS_OPTS環境變量:
ENV JENKINS_OPTS="--handlerCountStartup=100 --handlerCountMax=300 --logfile=/var/log/jenkins/jenkins.log"再次構建鏡像:
docker build -t myjenkins .讓我們測試新鏡像,來查看日志文件。運行以下命令:
docker stop jenkins-masterdocker rm jenkins-master
docker run -p 8080:8080 --name=jenkins-master -d myjenkins
我們可以查看日志文件:
docker exec jenkins-master tail -f /var/log/jenkins/jenkins.log
Jenkin崩潰后的日志恢復
bonus時間到了。如果Jenkins崩潰了,那么容器將停止,docker exec將停止工作。那么怎么辦呢?
之后,我們將提供幾種高級方法來持久化日志文件。現在,因為容器停止了,我們可以使用docker cp命令將文件拷貝出來。讓我們通過停止容器來模擬一次崩潰,然后恢復日志:
- ctrl-c終止日志文件的查看
- 運行以下命令:
docker stop jenkins-master
docker cp jenkins-master:/var/log/jenkins/jenkins.log jenkins.log
cat jenkins.log
總結
你可以找到這篇教程中所有的代碼:
https://github.com/maxfields2000/docker Jenkins_tutorial/tree/master/tutorial_02為了更為簡單一些,我們基于Cloudbees鏡像創建了自己的Dockerfile。我們設置了一個更合適的目錄來存儲日志文件,并學習了如何使用docker exec命令查看它們。我們將默認設置放進Dockerfile中,從而可以將其納入源碼控制中。
我們仍然還面臨著數據持久化的挑戰。我們已經學會了如何從已停止的容器(Jenkins崩潰)中獲取日志。但是,一旦容器停止,我們就失去了所有的工作。因此,如果沒有持久化,那么這個Jenkins鏡像就只能作為本地的開發和測試。
通過Dockerfile,我們可以解決持久化問題。在下一篇博客中,我們將討論以下問題:
- 保存Jenkins的任務和插件數據(Plugin data)
- 通過卷(Volumes)實現數據持久化
- 制作一個數據卷容器(Data-Volume container)
- 與其他容器分享卷中的數據
原文鏈接: PUTTING JENKINS IN A DOCKER CONTAINER (翻譯:夏彬 校對:李穎杰)