Docker在英雄聯盟游戲中的實踐探索(四)
這篇博客是Riot的Docker實踐系列博客的第四篇,主要討論了如何添加一個基于Nginx的代理容器,以及如何用Compose來管理多容器應用。
背景
如果你剛加入我們,可以先從 這篇介紹的文章開始,了解我們是如何完成英雄聯盟的持續發布,以及我們是如何發現這個技術棧可以很好地解決我們的問題。在我們的 第一篇文章中,我們介紹了如何把Jenkins放在Docker容器中。 第二篇文章中, 我們介紹了如何使用Docker數據卷容器來創建持久化層。我們創建了一個容器,來保存Jenkins相關的文件,以持久化插件、任務和其他的 Jenkins核心數據。我們討論了數據卷容器和宿主機掛載卷之間的區別。最后,為了不持久化Jenkins war文件,我們介紹了如何從Jenkins主目錄中移除war文件。
在第二篇文章的末尾,我們已經有了一個功能完備的、可以保存數據的Jenkins鏡像。然而,由于若干原因,它還不完美。本篇文章將解決其中一個 問題:在Jenkins之前缺少一個web代理。同時,我們將運行3個容器來構建Jenkins環境。本文將分為兩個部分:一是如何添加一個代理容器,二 是如何使用Compose(一種方便的Docker工具)來管理多容器應用。
讀完本文,你將會完成一個全棧(full stack)的Jenkins主服務器。
第一部分: 代理容器
在Riot,我們使用Nginx作為代理,因為它可以容易地重定向至HTTPS,并使Jenkins監聽在 8080端口,web服務器監聽在80端口。這里不會介紹如何配置Nginx的SSL和HTTPS(互聯網上可以找到文檔和實例);相反,我將介紹如何在 容器中運行Nginx代理服務器,并代理Jenkins服務器。這一部分將涉及以下幾點:
- 創建簡單的Nginx容器
- 學習如何從本地目錄中添加文件到鏡像中,比如Nginx配置文件
- 使用Docker容器鏈接(link),連接Nginx和Jenkins
- 配置Nginx來代理Jenkins </ul>
更換OS
在Riot,我們并不常用Debian;然而,Cloudbees的Jenkins鏡像使用Debian作為默認 OS,繼承自Java 8鏡像。但是,Docker的其中一個強大之處在于,我們可以使用任意的OS,因為宿主機并不在乎。這也展示了容器的“混合模式”。這意味著,如果應用在 多個容器之間運行,它們并不需要是同一個OS。如果某個特定的進程需要使用某個特定的Linux發行版的庫或模塊,這種做法就很有價值了。至于應用在 Debian/Centos/Ubuntu上的擴展性(spread)是否是個好主意,你們可以自行判斷。你們可以將鏡像轉換成Ubuntu、Debian,或者任意一個OS。我們將使用CentOS 7。在本文的第四部分,我將會具體地討論如何更換Jenkins鏡像中的默認OS,并移除對于外部鏡像的依賴性。需要考慮的是,如果你更換了OS,那么需 要更改很多命令和配置,來確保Nginx正常工作。
創建Nginx Dockerfile
在你的項目根目錄中,創建一個新目錄jenkins-nginx,來保存另一個Dockerfile。現在,你應該有三個目錄了(如果你讀過之前的文章):1、設置OS基礎鏡像:
FROM centos:centos7 MAINTAINER yourname
2、使用yum安裝Nginx:
RUN yum -y update; yum clean all RUN yum -y install http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm; yum -y makecache RUN yum -y install nginx-1.8.0
注意我們使用的Nginx版本是1.8.0。這是一個最佳實踐:總是鎖定版本,避免鏡像的重新構建使用未經測試的版本。
3、清除不需要的默認Nginx配置:
RUN rm /etc/nginx/conf.d/default.conf RUN rm /etc/nginx/conf.d/example_ssl.conf
4、添加配置文件:
COPY conf/jenkins.conf /etc/nginx/conf.d/jenkins.conf COPY conf/nginx.conf /etc/nginx/nginx.conf
這是我們第一次使用COPY命令。ADD命令與COPY命令十分類似。如果想透徹地了解兩者的區別,我推薦你閱讀以下鏈接:
- http://stackoverflow.com/quest ... s-add
- https://docs.docker.com/articl ... -copy </ul>
針對我們的場景,COPY是最好的選擇。就像以上文章中推薦的,我們只是拷貝單個的文件,并不需要ADD命令提供的特性(解壓tarball,基于URL的獲取等)。我們會更新默認的nginx.conf和Jenkins的配置文件。
5、我們希望Nginx監聽80端口:
EXPOSE 80
6、啟動Nginx:
CMD ["nginx"]
保存文件,但不要構建它。因為Dockerfile中有兩個COPY命令,我們需要首先創建這些文件。否則,如果這些文件不存在,構建將會失敗。
創建Nginx配置文件
以下的nginx.conf是一個默認配置,然后再修改特定的配置。daemon off; user nginx; worker_processes 2;error_log /var/log/nginx/error.log warn; pid /var/run/nginx.pid;
events { worker_connections 1024; use epoll; accept_mutex off; }
http { include /etc/nginx/mime.types; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on; #tcp_nopush on;
keepalive_timeout 65;
client_max_body_size 300m; client_body_buffer_size 128k;
gzip on; gzip_http_version 1.0; gzip_comp_level 6; gzip_min_length 0; gzip_buffers 16 8k; gzip_proxied any; gzip_types text/plain text/css text/xml text/javascript application/xml application/xml+rss application/javascript application/json; gzip_disable "MSIE [1-6]."; gzip_vary on;
include /etc/nginx/conf.d/*.conf; }</pre>
現在來修改默認配置:
1、使Nginx不以daemon方式運行:daemon off;
這是因為命令行中調用nginx,Nginx將以daemon方式運行在后臺。這會返回exit 0,Docker會認為進程已經退出,然后停止容器。你會發現這種現象經常發生。對于Nginx來說,只要簡單地修改下配置就可以解決這個問題。
2、將Nginx的worker數目提升為2:
worker_processes 2;
這是我每次設置Nginx時必定做的事。當然,你可以選擇保持該配置為1。Nginx調優可以單獨寫一篇文章。我不能告訴你什么是對的。粗略地說,該配置 指定了多少個單獨的Nginx進程。CPU數目是一個不錯的參考值,當然,很多NGINX專家會說情況遠比這個要復雜。
3、事件調優(Event tuning):
use epoll; accept_mutex off;
打開epolling可以使用高效的連接模型。為了加速,我們關閉了accept_mutex,因為我們不在乎較低的連接請求數造成的資源浪費。
4、設置代理頭:
proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
除了關閉daemon模式之外,這是第二個必須的Jenkins代理配置。只有這樣,Jenkins才能正確地處理請求,否則會出現一些警告。
5、客戶端大小:
client_max_body_size 300m; client_body_buffer_size 128k;
你可能需要這些配置,也可能不需要。不可否認的是,300MB是一個很大的body大小。然而,我們的用戶上傳文件到Jenkins服務器,其中一些是HPI插件,一些是真實文件。
6、打開GZIP:
gzip on; gzip_http_version 1.0; gzip_comp_level 6; gzip_min_length 0; gzip_buffers 16 8k; gzip_proxied any; gzip_types text/plain text/css text/xml text/javascript application/xml application/xml+rss application/javascript application/json; gzip_disable "MSIE [1-6]\."; gzip_vary on;
為了加速,我們打開了gzip壓縮。
保存文件為conf/nginx.conf。下一步就是為Jenkins添加特定的配置文件。
針對Jenkins的NGINX配置
就像上一章那樣,我會先提供一份完整的配置文件,然后再修改特定的配置。你會發現大多數內容可以在Jenkins的 官方文檔找到。
server { listen 80; server_name "";access_log off;
location / { proxy_pass http://jenkins-master:8080;
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-Proto http; proxy_max_temp_file_size 0;
proxy_connect_timeout 150; proxy_send_timeout 100; proxy_read_timeout 100;
proxy_buffer_size 8k; proxy_buffers 4 32k; proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k;
}
}</pre>
只有一個配置,是真正關于代理的:proxy_pass http://jenkins-master:8080;
這個配置需要域名jenkins-master存在,這個可以通過容器連接來保證(稍后會講到)。如果你還沒使用容器連接的話,那么需要將映射Jenkins容器的IP/hostname。
然而,你不能把它設置為localhost。這是因為每個Docker容器都有自己的localhost, 將代理指向了Nginx容器本身,這里并沒有運行在8080端口的Jenkins。為了避免使用容器連接,需要執行Docker Host(應該是你的Desktop或laptop)的IP地址。盡管你是知道這個信息的,但是請想象一下,如果你的Jenkins容器是運行在 Dockerhost集群中的任意一臺。你需要寫一個自動腳本,來獲取IP地址,然后編輯配置文件。這是可以做到的,但是非常麻煩。容器連接可以簡化這一 過程。
構建Nginx鏡像,并連接到Jenkins鏡像
現在,我們已經創建了Nginx和Jenkins的配置文件。請確保你是在頂層目錄中。
docker build -t myjenkinsnginx jenkins-nginx/.
構建完成之后,我們可以啟動它,并連接到jenkins-master鏡像,使代理發生作用。首先,請確保jenkins-data和jenkins-master正在運行。
docker run --name=jenkins-data myjenkinsdata
如果發生了錯誤,不用緊張,這說明容器已經存在了。這是一個好事,因為這意味著我們沒有覆蓋原來的數據。
docker stop jenkins-master docker rm jenkins-master docker run -p 8080:8080 -p 50000:50000 --name=jenkins-master --volumes-from=jenkins-data -d myjenkins
現在,我們終于可以啟動Nginx容器,并連接jenkins-master:
docker run -p 80:80 --name=jenkins-nginx --link jenkins-master:jenkins-master -d myjenkinsnginx
請注意--link參數。你可以在 Docker官方網站上找到相關文檔。需要確保域名"jenkins-master"在NGINX容器中存在,指向jenkins-master容器的內部Docker網絡IP。
請注意Nginx容器必須在jenkins-master容器之后啟動。這意味著,如果要停止和重啟jenkins-master容器,那么也需要重啟Nginx容器。
測試一切是否正常很容易。只要在瀏覽器中輸入IP地址,一切應該正常工作了。
如果出了問題,那么一定有什么東西阻塞了80端口(這更可能發生在OSX上)。請確保防火墻已經關閉,或者至少可以接受80端口的流量。如果由于某種原因,你不能清除80端口,請關閉并刪除jenkins-nginx容器,以-p 8000:80參數重啟它。然后,訪問 http://yourdockermachineip:8000,看看一切是否正常。
Jenkins鏡像清理
我們已經讓Nginx監聽在80端口了,就不需要Jenkins鏡像暴露8080端口了。現在我們需要刪除這個端口配置。停止并重啟Jenkins容器,同時Nginx容器也需要重啟,因為兩者是連接在一起的。每次重啟時,它們都會連接在一起。
docker stop jenkins-nginx docker stop jenkins-master docker rm jenkins-nginx docker rm jenkins-master docker run -p 50000:50000 --name=jenkins-master --volumes-from=jenkins-data -d myjenkins docker run -p 80:80 --name=jenkins-nginx --link jenkins-master:jenkins-master -d myjenkinsnginx
刷新瀏覽器 http://yourdockermachineiphere。
已經不能訪問8080端口了,相反,可以通過Nginx代理訪問它了。
與往常一樣,代碼和示例都可以在GitHub上找到,地址是 https://github.com/maxfields20 ... al_04。你會注意到makefile更新了,添加了Nginx容器和Jenkins容器的啟動順序。
Docker Compose和Jenkins
我們現在運行了3個容器,一個是Nginx代理容器,一個是Jenkins應用容器和一個保存Jenkins數據的數據卷容器。我們已經發現,因為數據卷和容器連接,這3個容器之間有啟動順序和依賴。本文將介紹Compose來處理這些。
本小節將涉及:
- 使用Compose來管理多容器應用 </ul>
什么是Compose
Compose是從另一個工具Fig發展而來的。Docker將它定義為“運行復雜應用的工具”。你可以從 https://docs.docker.com/compose/查看相關文檔。當運行應用時,Compose可以幫我們構建鏡像,決定停止和啟動哪些容器。例如,如果我希望運行3個容器應用,重新構建Jenkins容器,重新運行應用-可能是升級Jenkins版本。請運行以下命令:
docker stop jenkins-nginx docker stop jenkins-master docker rm jenkins-nginx docker rm jenkins-master docker build -t myjenkins jenkins-master/. docker run --name=jenkins-master --volumes-from=jenkins-data -d myjenkins docker run -p 80:80 --name=jenkins-nginx --link jenkins-master:jenkins-master -d myjenkinsnginx
正確配置之后,我們可以運行Compose:
docker-compose stop docker-compose build docker-compose up -d
這和makefile的行為很類似。使用Compose的取舍在于,你必須額外再維護一個配置文件。
本小節單獨成章,是因為使用Docker-Compose是一個個人選擇。然而,如果你有很強的Windows開發背景,那么Compose可能不是一個很好的選擇。
需求
- 如果你在OSX上使用Docker Toolbox, Compose是默認安裝的。
- 如果你還沒安裝Docker Toolbox,或者使用Linux,那么請參考https://docs.docker.com/compose/install/安裝Compose
- OSX 或者 Linux </ul>
- 在你的項目根目錄中,創建一個文件docker-compose.yml </ul>
請注意,Compose還不能在Windows上與Windows版本的Docker客戶端一起運行。如果你使用的是Windows和 Docker Toolbox,情況會有所不同。我的建議是暫時使用makefile。Compose的開發團隊正在開發Windows兼容版本,但是1.4版本尚不支 持。
第一步:創建Compos配置文件
Compose使用YAML配置文件。我們為每一個需要Compose管理的鏡像添加一個條目。你完全可以使用另外一個名字,但是默認情況下,Compose將首先查找這個名字。
第二步: Jenkins數據容器
編輯docker-compose.yml,添加以下內容(由于它是yaml,所以需要保持縮進):jenkinsdata: build: jenkins-data
以上是創建了一個容器的條目,叫做“jenkinsdata”。Compose不支持名字中的特殊字符,如“-”。然后,我們添加了一個構建目錄,名字是Dockerfile所在的目錄名,如“jenkins-data”。
檢查一切是否正常:
- 保存文件。
- 執行docker-compose build。 </ol>
- 在Jenkins實例中,創建一條測試任務
docker-compose stop
</li> - 簡單地編輯一下Jenkins主Dockerfile,例如更改MAINTAINER。
docker-compose build docker-compose up -d
</li> - 回到Jenkins實例,查看測試任務是否已經存在。 </ul>
- 完全控制所有的鏡像
- 備份Jenkins鏡像
- 構建從容器 </ol>
Docker-Compose會找到jenkins-data目錄,構建Dockerfile,就像執行了docker build jenkins-data/一樣。你會注意到,鏡像的名字是不同的。Compose使用的命名轉換是“projectname_composecontainername”。默認情況下,項目名稱是父目錄的名字。
這種命名標準是相當重要的。這是產品環境中的容器命名方式。請確保父目錄的名字是合理的,或者使用-p來制定鏡像名稱。你也可以使用-p來區別產品環境和開發環境。
第三步: Jenkins主鏡像
繼續編輯docker-compose.xml,添加以下內容:jenkinsmaster: build: jenkins-master volumes_from: - jenkinsdata ports: - “50000:50000”
就像Jenkins數據鏡像一樣,我們也有一個條目,來命名容器,并定義構建目錄。我們也加了一條volumes_from語句,與命令行中的--volumes-from=的作用相同。但是,請注意Compose使用的容器名并不是真正的名字。這是Compose的一個方便的特性,可以讓我們引用這些名字,提高可讀性。Compose足夠聰明,可以把它們組合在一起,來構建容器。
另一個優勢是Compose知道jenkinsmaster依賴于jenkinsdata,因此會以正確的順序來啟動它們。你可以在Compose文件中以任意順序列舉它們。
最后,我們使用ports指令,來處理端口映射。為了JNLP從連接,我們需要確保Jenkins主容器做50000端口映射。
第四步:Nginx鏡像
jenkinsnginx: build: jenkins-nginx ports: - "80:80" links: - jenkinsmaster:jenkins-master
將像其他兩個條目,它也有一個名字(jenkinsnginx)和一個構建目錄。但是,我們添加了一條links指令,就像命令行中的--link。
第五步:將所有這些組合在一些
完整的docker-compose.yml:jenkinsdata: build: jenkins-data jenkinsmaster: build: jenkins-master volumes_from: - jenkinsdata ports: - "50000:50000" jenkinsnginx: build: jenkins-nginx ports: - "80:80" links: - jenkinsmaster:jenkins-master
我們需要構建所有這些。首先,需要確保沒有之前的容器的痕跡。如果你已經清理了,你可以跳過這一步:
docker stop jenkins-nginx docker rm jenkins-nginx docker stop jenkins-master docker rm jenkins-master docker rm jenkins-data
注意:遷移到新模型,我們只能丟掉數據容器,這很討厭。在未來的文章中,我將會討論如何備份數據。但是如果你需要備份這些數據的話,你可以先用 第三篇的docker up來備份數據。
docker-compose build docker-compose up -d
注意-d使得Docker-Compose以daemon方式運行容器,就像Docker的參數-d一樣。如果你想知道哪些容器正在運行,Docker-Compose也有相類似的特性:
docker-compose ps
第六步:使用Compose維護
Compose足夠聰明到了解數據卷,并持久化。Docker Compose會啟動數據容器,并重新創建Nginx容器和主容器。
Compose有一個簡單的方式來清理所有的東西:
docker-compose rm
這條命令也會刪除你的數據容器。如果你不愿意刪除數據容器的話,也很簡單。
docker-compose rm jenkinsmaster jenkinsgninx
總結
你可以在 https://github.com/maxfields20 ... al_05上找到代碼和實例。我們了解到Compose可以簡化多鏡像應用的管理,只需要多加一個配置文件。這個文件用來自描述容器之間的關系。
Compose是一個不錯的、可操作的工具。可能的一個缺點是,容器名是基于父目錄而定的,總是需要你指定一個項目名稱。Compose可以使用PS和RM等工具。
我們也了解到Compose還不能在Windows上運行。你是否使用Compose取決于你是否需要Windows支持,或者你是否喜歡 Compose的命名方式。我個人很喜歡docker-compose.yml的自描述方式。你會注意到我仍然提供了makefile,因為我不需要記住 所有容器的名字。
至此,基礎教程結束了。之后的文章將涉及更高級的內容。
下一步
我們還有三個高級話題:備份,構建從節點和Docker鏡像的完全控制。我將會討論如何完全制作你自己的Jenkins 鏡像,而不需要依賴公共倉庫。主要是因為依賴管理,或者是因為你不喜歡基于Debian的容器,更喜歡Ubuntu或者CentOS。因此,之后我們會從 頭創建自己的Dockerfiles,來構建從容器。接下來的內容是:下次再會!
原文鏈接:JENKINS, DOCKER, PROXIES, AND COMPOSE(翻譯:夏彬 校對:李穎杰)
來自:http://dockone.io/article/820