要求
- 已安裝Dockers1.13或更高版本
- 完成Part 1的閱讀
- 在機(jī)器環(huán)境上快速啟動運(yùn)行一個容器保證所有都配置正確
docker run hello-world
介紹
是時候開始使用Docker方式來構(gòu)建一個應(yīng)用了。在本節(jié)我們將基于一個應(yīng)用垂直結(jié)構(gòu)的底層開始構(gòu)建容器。在這之上是服務(wù),我們將在Part3第三節(jié)介紹基于容器的服務(wù)是如何在生產(chǎn)環(huán)境運(yùn)行的。最后,在最頂層是棧,定義了所有服務(wù)的交互行為,我們將在Part5第五節(jié)進(jìn)行介紹說明。
- Stack棧
- Services服務(wù)
- Container容器(本節(jié)的介紹內(nèi)容)
新的開發(fā)環(huán)境
在以往,如果我們要開始著手寫一個Python應(yīng)用,要做的第一件事就是在機(jī)器上安裝Python的運(yùn)行環(huán)境。但是,這樣會造成一個問題,就是我們搭建的環(huán)境要完美的適配應(yīng)用程序的需求,同時也要匹配將來生產(chǎn)環(huán)境的要求。
使用Docker,我們僅僅需要做的就是構(gòu)建一個便捷的Python運(yùn)行環(huán)境鏡像,不再需要而外安裝其他東西。基于一個通用的鏡像我們將代碼和運(yùn)行環(huán)境一起構(gòu)建為一個專用鏡像,保證所有的依賴、運(yùn)行環(huán)境、代碼全都在一起。
通過使用Dockerfile我們能夠定義這些非常便捷的鏡像。
使用Dockerfile定義容器
Dockerfile定義了容器是如何運(yùn)轉(zhuǎn)的。類似于網(wǎng)絡(luò)接口和數(shù)據(jù)硬盤驅(qū)動等這些資源的使用在環(huán)境中都是虛擬化的,并且與系統(tǒng)其他程序是相互隔離的,因此我們需要將端口映射到外部,確定文件是如何來實現(xiàn)共通。我們期望通過Dockerfile定義配置的應(yīng)用容器能夠準(zhǔn)確無誤在任何地方運(yùn)行。
Dockerfile
- 創(chuàng)建一個空目錄
- 切換工作路徑到這個新目錄
- 在目錄下新建Dockerfile,將下文中的內(nèi)容復(fù)制到Dockerfile中,然后保存文件(請詳細(xì)查看每個語句的注釋部分,解釋了每個語句具體的含義)
# 使用官方的Python運(yùn)行環(huán)境作為基礎(chǔ)鏡像
FROM python:2.7-slim
# 設(shè)置工作路徑為/app
WORKDIR /app
# 復(fù)制當(dāng)前目錄下的內(nèi)容到容器的/app下
ADD . /app
# 安裝在文件requirements.txt指定的包
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# 將80端口進(jìn)行對外開放,這樣就能從容器外部通過映射訪問容器內(nèi)部的80端口
EXPOSE 80
# 定義環(huán)境變量
ENV NAME World
# 容器啟動后運(yùn)行 app.py程序
CMD ["python", "app.py"]
Dockerfile中提到了有幾個我們還沒有創(chuàng)建的文件,例如app.py和requirements.txt。我們接下來進(jìn)行逐一的創(chuàng)建
應(yīng)用程序
我們再創(chuàng)建兩個文件,分別為requirements.txt和app.py,并將他們保存到跟Dockerfile一致的目錄。這樣我們就非常簡單的完成了應(yīng)用。當(dāng)上文提到的Dockerfile構(gòu)建為一個鏡像后,app.py和requirements.txt也會在鏡像中,因為我們使用了Dockerfile的ADD命令,并且因為使用的EXPOSE命令我們能夠使用http協(xié)議訪問到內(nèi)部的服務(wù)
requirements.txt
Flask
Redis
app.py
from flask import Flask
from redis import Redis, RedisError
import os
import socket
# Connect to Redis
redis = Redis(host="redis", db=0, socket_connect_timeout=2, socket_timeout=2)
app = Flask(__name__)
@app.route("/")
def hello():
try:
visits = redis.incr("counter")
except RedisError:
visits = "<i>cannot connect to Redis, counter disabled</i>"
html = "<h3>Hello {name}!</h3>" \
"<b>Hostname:</b> {hostname}<br/>" \
"<b>Visits:</b> {visits}"
return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname(), visits=visits)
if __name__ == "__main__":
app.run(host='0.0.0.0', port=80)
現(xiàn)在我們可以看到pip install -r requirements.txt將按照Flask和Redis包,在應(yīng)用程序中會將環(huán)境變量NAME進(jìn)行打印輸出,同時還會輸出socket.gethostname()的內(nèi)容。最后應(yīng)為Redis并沒有運(yùn)行(因為我們并沒有啟用任何的Redis服務(wù)),因此在程序中嘗試使用Redistribution會出錯同時也會輸出相關(guān)的錯誤信息。
注意:在容器中獲取機(jī)器的名稱會返回一個類似于程序進(jìn)程ID一樣的容器ID
如此簡單,不需要在機(jī)器上安裝Python和相關(guān)的依賴,也不需要在機(jī)器上構(gòu)建安裝這些鏡像。看上去就像我們沒有配置Python和Flask,一樣,實際上我們已經(jīng)配置好了。
構(gòu)建應(yīng)用程序
通過上面的配置,我們已經(jīng)準(zhǔn)備好構(gòu)建需要的所有。確保你的工作目錄是處于之前創(chuàng)建的新目錄。通過ls命令我們可以看到
$ ls
Dockerfile app.py requirements.txt
運(yùn)行構(gòu)建命令。將會新建一個Docker鏡像,我們可以通過使用-t參數(shù)為鏡像取一個友好的名字
docker build -t friendlyhello .
我們構(gòu)建的鏡像在哪里呢?它位于機(jī)器上的本地Docker鏡像倉庫中,
docker image ls
REPOSITORY TAG IMAGE ID
friendlyhello latest ******
Linux 用戶可能遇到的問題
代理服務(wù)設(shè)置
代理服務(wù)在啟動運(yùn)行時能夠阻止(修改)網(wǎng)絡(luò)連接。如果你的機(jī)器上運(yùn)行了一個代理服務(wù)器,需要將下面的相關(guān)指令添加到Dockerfile,通過使用ENV指令能夠指定代理服務(wù)的主機(jī)和端口# 配置代理服務(wù),修改host:port為你自己機(jī)器上的對應(yīng)值 EVN http_proxy host:port EVN https_proxy host:portDNS設(shè)置
錯誤的DNS設(shè)置會給pip的使用帶來問題。為確保pip的正常使用,需要設(shè)置DNS服務(wù)地址。我們可以通過修改Docker守護(hù)進(jìn)程里的DNS信息。通過在編輯(或者新建)配置文件(/etc/docker/daemon.json)添加dns信息{ "dns": ["你的DNS地址", "8.8.8.8"] } 保存對daemon.json文件的修改,重啟docker服務(wù)`sudo service docker restart`問題修復(fù)后,重新使用
build進(jìn)行構(gòu)建
運(yùn)行應(yīng)用
使用-p參數(shù)進(jìn)行端口映射將主機(jī)的4000端口映射為容器對外發(fā)布的80端口,docker run -p 4000:80 friendlyhello
通過控制臺我們可以看到容器啟動的相關(guān)信息,可以看到Python監(jiān)聽了80端口啟動了一個HTTP服務(wù)。但是這個信息是來自于容器內(nèi)容,因此80端口也是指的容器內(nèi)的端口,在運(yùn)行啟動時我們通過端口映射將主機(jī)的4000端口映射到容器內(nèi)的80,因此我們可以在本機(jī)通過http://localhost:4000來訪問服務(wù)。如果一起啟動正常我們可以在瀏覽器看到相關(guān)的信息
注意
如果是在windows7上通過Docker Toolbox來運(yùn)行容器復(fù)蘇的話,需要將localhost替換為Docker Machine的IP??梢酝ㄟ^docker machine ip來查看Docker Machine的IP地址
在終端中使用CTRL+C,可以退出
在windows操作系統(tǒng)中,需要明確停止容器
在windows操作系統(tǒng)中,使用CTRL+C不會停止容器,只會退出命令終端。因此我們需要通過CTRL+C退出命令終端(此時通過docker container ls查看還有哪些容器在運(yùn)行),然后使用docker container stop <Container NAME od ID>來停止容器
接下來,我們需要應(yīng)用在后臺運(yùn)行,以靜默的方式在后臺運(yùn)行
docker run -d -p 4000:80 friendlyhello
使用-d參數(shù),可以讓容器在后臺運(yùn)行,通過docker container ls查看正在運(yùn)行的容器和相關(guān)信息,在后臺運(yùn)行的的容器可以使用docker container stop <Container NAME od ID>來停止容器
發(fā)布共享鏡像
為演示我們剛剛運(yùn)行的應(yīng)用的可移植性,我們可以上傳構(gòu)建的鏡像然后就可以在任意地方運(yùn)行了。在部署容器到其他環(huán)境(生產(chǎn)環(huán)境、測試環(huán)境)前,需要知道如何將鏡像推送上傳到登記注冊處。
一個登記注冊處是倉庫的集合,倉庫是鏡像的集合——就像GitHub的倉庫一樣。登記注冊處的一個賬號可以創(chuàng)建多個倉庫。docker命令默認(rèn)使用Dockers的公共注冊處
注意
使用默認(rèn)的注冊處是因為它已經(jīng)做好了默認(rèn)的配置而且是免費(fèi)的。當(dāng)然還有很多其他的公共注冊處提供選擇,我們也可以自己創(chuàng)建私有的注冊處
使用Docker賬號登陸
如果還沒有Docker賬號,可以在http://hub.docker.com進(jìn)行注冊。在本機(jī)登陸公共的Docker登記注冊處docker login,根據(jù)提示信息輸入賬號密碼,完成登陸
為鏡像打標(biāo)簽
倉庫上的鏡像與本地鏡像的關(guān)聯(lián)格式是username/repository:tag,tag是可選的,但是建議每次都附上tag,通過tag我們可以了解到docker鏡像的版本。通過鏡像功能和相關(guān)信息為repository:tag選擇有意義的名稱,例如get-started:part2
使用我們自己的username,repository和tag運(yùn)行docker tag image,命令的語法格式為:
docker tag image username/repository:tag
例如
docker tag friendlyhello gordon/get-started:part2
運(yùn)行docker image ls查看剛剛打上tag的鏡像
推送發(fā)布鏡像
上傳打上標(biāo)簽的鏡像到倉庫
docker push username/repository:tag,例如docker push gordon/get-started:part2
上傳完成后,這個鏡像就成為公共的,如果我們登陸Docker Hub,就可以看到剛剛上傳的鏡像
從遠(yuǎn)端拉取鏡像并運(yùn)行
至此,可以通過使用 docker run在任何其他支持docker的機(jī)器上運(yùn)行應(yīng)用
docker run -p 4000:80 username/repository:tag
如果這個鏡像在本機(jī)沒有檢測到,Docker就會從遠(yuǎn)端倉庫哦查找獲取這個鏡像。無論docker run在哪里運(yùn)行,他都會獲取到我們上傳的鏡像,在鏡像中包含有Python、相關(guān)的依賴和運(yùn)行的代碼。它把所有需要的代碼依賴組件等打包在一起,而在本地機(jī)器上我們不需要任何安裝而外的東西就能夠運(yùn)行整個應(yīng)用。
本節(jié)總結(jié)
本節(jié)通一個實際的案例講解了Dockefile和Docker Hub的相關(guān)知識,在下一節(jié),將會學(xué)習(xí)如何以服務(wù)的方式運(yùn)行容器來擴(kuò)展我們的應(yīng)用
溫故知新
下面的終端記錄小視頻記錄了本節(jié)的所涉及的相關(guān)操作
下面列出了本節(jié)使用到的一些命令
docker build -t friendlyhello # 使用當(dāng)前目錄下的Dockerfile新建鏡像
docker run -p 4000:80 friendlyhello # 運(yùn)行“friendlyhello”,并將本機(jī)的4000端口映射到容器的80端口
docker run -d -p 4000:80 friendlyhello # 以后臺靜默方式運(yùn)行容器,并將本機(jī)的4000端口映射到容器的80端口
docker container ls # 列出所有正在運(yùn)行的容器
docker container ls -a # 列出所有容器,包括已停止的
docker container stop <hash> # 友好的停止指定的容器
docker contailer kill <hash> # 強(qiáng)制停止指定的容器
docker container rm <hash> # 從機(jī)器上刪除指定的容器
docker container rm(docker image ls -a -q) # 刪除本機(jī)上所有的鏡像
docker login # 使用Docker賬號進(jìn)行登陸
docker tag <image> username/repository:tag # 為待上傳的鏡像打標(biāo)簽
docker push username/repository:tag # 上傳鏡像到倉庫
docker run username/repository:tag # 運(yùn)行鏡像(如果本機(jī)沒有該鏡像,就會從倉庫下載鏡像并允許)
了解更多
- 微信公眾號搜索“懶得糊涂個人工作室”了解更多
- 博客網(wǎng)站地址:http://www.403studio.com