在Docker容器中運行Jenkins

jopen 9年前發布 | 47K 次閱讀 Docker


【編者的話】這篇博客是Riot的Docker實踐系列博客的第二篇,主要內容是:基于Cloudbees鏡像創建新的Dockerfile,設置了一個日志目錄,并介紹了如何使用docker exec命令查看日志文件。

在Docker容器中運行Jenkins

當我一年前開始學習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

  1. 點擊 https://www.docker.com/toolbox
  2. 下載和安裝Docker Toolbox。請注意,在此過程中將安裝VirtualBox和一些其他工具(Compose, Kitematic, Docker Machine)。
  3. 按照步驟安裝。
  4. 運行“Docker Quickstart Terminal”,來驗證安裝是否成功。在Windows上,在桌面上有一個快捷方式;在Max OSX上,它在你的Docker文件夾中。然后,運行以下命令,并確保沒有發生任何錯誤。
    docker ps
    docker info

在Docker容器中運行Jenkins

步驟2:查詢你的IP地址

我們需要訪問Jenkins web服務器(最終是一個NGINX服務器)。最簡單的方式,就是在瀏覽器中直接輸入Docker宿主機的IP地址。

你可以使用ifconfig或ipconfig來查詢IP地址,但是Docker Toolbox通過docker-machine提供了一個方便的命令:

docker-machine ip default

在Docker容器中運行Jenkins

這就是你的宿主機的IP地址,你的web服務就監聽在上面。當然,這個IP是在你本地的機器上,并不能被外部訪問。如果你希望從外部訪問你的機 器,你需要在Virtualbox上設置端口轉發(port forwarding)。我把這個留給你作為一個練習,可以參考Virtualbox的文檔。

步驟3:Pull并運行Cloudbees的Jenkins容器

  1. 登錄到Docker的終端窗口。
  2. 從公共倉庫中pull Jenkins鏡像:
    docker pull jenkins
    docker run -p 8080:8080 --name=jenkins-master jenkins
  3. 打開瀏覽器,并訪問http://yourdockermachineiphere:8080。

在Docker容器中運行Jenkins

如果Jenkins沒有顯示在瀏覽器中,但是容器正在運行,那么請再次確認是否正確執行了步驟2,并獲得了正確的IP地址。

在Docker容器中運行Jenkins

請注意,你可能注意到我使用了--name這個參數,并將這個容器命名為jenkins-master。命名容器是一個最佳實踐,有三個優點:

1. 更容易記憶

2. Docker不允許兩個容器使用同一個名字,可以防止開啟兩個同名容器。

3. 很多通用的Docker工具(如docker-compose)需要使用特定的容器名字,因此命名容器是一個最佳實踐。

步驟4:更實用一點

之前的步驟是以最基本的參數來啟動Jenkins。就像Riot一樣,你是不太可能直接用默認配置來運行Jenkins的。讓我們加上一些有用的參數,并解釋為什么這么加。

步驟4A: 守護進程

你可能不希望直接將Jenkins的日志打到標準輸出中。因此使用Docker的daemon標志(-d)來開啟容器。

  1. 用Ctrl-c關閉你的容器
  2. 運行以下命令:
    docker rm jenkins-master
    docker run -p 8080:8080 --name=jenkins-master -d jenkins

在Docker容器中運行Jenkins

現在你應該可以獲得一個hash字符串。如果你是剛接觸Docker的話,那個hash值就是你的容器的唯一ID。

步驟4B: 內存設置

在Riot,我們需要一些健壯的設置來運行Jenkins。運行以下命令:

docker stop jenkins-master
docker 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” jenkins

Cloudbees的容器使用的是Java 1.8,我們不需要設置PermSize,因為Java 1.8沒有PermGen。

步驟4C: 升級連接池(Connection Pool)

STEP 4C: UPPING THE CONNECTION POOL

在Riot,大量的流量發往Jenkins服務器,因此我們需要給Jenkins設置更大的連接池。運行以下命令:

docker stop jenkins-master
docker 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版本。

我們通過以下四步完成這一點:

  1. 創建工作路徑。
  2. 使用你最喜愛的文本編輯器,創建一個新文件“Dockerfile”。
  3. 添加以下內容,并保存。
    FROM jenkins:1.609.1
    MAINTAINER yourname
  4. 在命令行中,輸入以下命令:
    docker build -t myjenkins .

在Docker容器中運行Jenkins

我們所做的是,從公共的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容器的時候重寫這些環境變量。

  1. 在Dockerfile的“MAINTAINER”一行下,添加以下內容:
    ENV JAVA_OPTS="-Xmx8192m"
    ENV JENKINS_OPTS="--handlerCountStartup=100 --handlerCountMax=300"
  2. 保存并重建鏡像:
    docker build -t myjenkins .

相當簡單!你可以輸入以下三條命令來測試鏡像是否成功:

docker stop jenkins-master
docker rm jenkins-master
docker run -p 8080:8080 --name=jenkins-master -d myjenkins

在Docker容器中運行Jenkins

你的鏡像應該可以立即啟動!但是如何知道那些環境變量是否生效了呢?這很簡單:可以使用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容器中運行Jenkins

你可以看到我們的設置已經生效。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 0b5ac2bce13b
mkdir: cannot create directory ‘/var/log/jenkins’: Permission denied

在Docker容器中運行Jenkins

不用擔心,這是因為Cloudbees默認容器使用了“Jenkins”用戶。你可以從Dockerfile( https://github.com/ Jenkinsci/docker/blob/master/Dockerfile)的底部看到:

USER jenkins

這對于創建/var/log/jenkins是不方便的,通常情況下,需要使用SUDO或者其他方式來創建目錄(/var/log是root用戶所有的)。幸運的是,Docker運行我們切換用戶。在Dockerfile中添加以下內容:

  1. 在RUN mkdir line之前添加:
    USER root
  2. 在RUN mkdir line之后添加:
    RUN chown -R jenkins:jenkins /var/log/jenkins
  3. 在RUN chown line之后添加:
    USER jenkins

注意:我們需要添加chown命令,因為我們想要Jenkins用戶寫這個目錄。然后,我們將用戶重置為Jenkins,從而限制Dockerfile的行為。

再次構建鏡像:

docker build -t myjenkins .

在Docker容器中運行Jenkins

之前的錯誤消失了。

通過設置日志目錄(請注意:你可以設置成任意目錄,為了一致性,我們選擇了/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-master
docker rm jenkins-master
docker run -p 8080:8080 --name=jenkins-master -d myjenkins

我們可以查看日志文件:

docker exec jenkins-master tail -f /var/log/jenkins/jenkins.log

在Docker容器中運行Jenkins

Jenkin崩潰后的日志恢復

bonus時間到了。如果Jenkins崩潰了,那么容器將停止,docker exec將停止工作。那么怎么辦呢?

之后,我們將提供幾種高級方法來持久化日志文件。現在,因為容器停止了,我們可以使用docker cp命令將文件拷貝出來。讓我們通過停止容器來模擬一次崩潰,然后恢復日志:

  1. ctrl-c終止日志文件的查看
  2. 運行以下命令:
    docker stop jenkins-master
    docker cp jenkins-master:/var/log/jenkins/jenkins.log jenkins.log
    cat jenkins.log

在Docker容器中運行Jenkins

總結

你可以找到這篇教程中所有的代碼:

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 (翻譯:夏彬 校對:李穎杰)

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!