安裝了1.13或者更高版本的Dockerhtml
閱讀了Part1中的定位(我沒寫)python
是時候用Docker構建一個app了。咱們會從構建這樣一個app的最底層開始,容器——咱們這節所介紹的內容。在這層之上是服務,服務定義了容器們的在生產中的行爲,在第3章介紹。最上層的是堆,定義了服務的交互行爲,在第5章介紹。web
堆(Stack)redis
服務(Services)docker
容器(container)shell
使用docker,您能夠直接獲取一個可移植的Python運行時做爲映像。而後,您的構建能夠在應用程序代碼旁邊包含基本的Python映像,確保應用程序、它的依賴項和運行時環境一塊兒運行。flask
Dockerfile
定義一個容器Dockerfile將定義容器內環境的內容。訪問像網絡接口和磁盤驅動器之類的資源在這個環境中是虛擬化的,這與系統的其餘部分是隔離的,所以您必須將端口映射到外部世界,並具體地說明您想要「複製」到該環境中的文件。然而,在作了這些以後,您能夠指望在這個Dockerfile中定義的應用程序的構建在運行的任何地方都是徹底相同的。小程序
建立一個空目錄。將目錄(cd)更改成新目錄,建立一個名爲Dockerfile的文件,將如下內容複製粘貼到該文件中,並保存它。注意在新Dockerfile中解釋每一個語句的註釋。瀏覽器
# Use an official Python runtime as a parent image FROM python:2.7-slim # Set the working directory to /app WORKDIR /app # Copy the current directory contents into the container at /app ADD . /app # Install any needed packages specified in requirements.txt RUN pip install --trusted-host pypi.python.org -r requirements.txt # Make port 80 available to the world outside this container EXPOSE 80 # Define environment variable ENV NAME World # Run app.py when the container launches CMD ["python", "app.py"]
您是否使用了代理服務器?
代理服務器一旦啓動並運行,就能夠阻塞鏈接到您的web應用程序。若是您在代理服務器後面,請在Dockerfile中添加如下幾行,使用ENV命令爲您的代理服務器指定主機和端口:服務器
# Set proxy server, replace host:port with values for your servers ENV http_proxy host:port ENV https_proxy host:port
這個Dockerfile和咱們尚未建立的兩個文件有聯繫,即app.py和requirements.txt。接下來讓咱們建立這些。
建立兩個更多的文件,requirements.txt和app.py,並將它們放在與Dockerfile相同的文件夾中。這就完成了咱們的應用程序,正如您所看到的很是簡單。當上述Dockerfile被構建成一個Image,因爲Dockerfile的ADD命令會添加requirements.txt和app.py,感謝EXPOSE使app.py的輸出能夠經過HTTP訪問。
Flask Redis
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)
如今咱們看到pip install -r requirements.txt
爲Python安裝Flask和Redis庫,應用程序打印環境變量NAME
,以及調用socket.gethostname()的輸出。最後,由於Redis沒有運行(由於咱們只安裝了Python庫,而不是Redis自己),咱們應該指望在這裏使用它的嘗試會失敗併產生錯誤消息。
注意:當容器內檢索容器ID時訪問主機名,這就像運行可執行文件的進程ID
就是這樣!您不須要Python或requirements.txt
中的任何東西在你的系統上,也不會在你的系統上安裝或運行這個映像。看起來你並無真正創建一個包含Python和Flask的環境,可是你有。
咱們已經準備好構建應用程序,確保您仍然處於新目錄的頂層。下面是ls應該展現的:
$ ls Dockerfile app.py requirements.txt
如今運行build命令。這建立了一個Docker映像,咱們將使用- t標記它,所以它有一個友好的名稱。
docker build -t friendlyhello .
你的構建的Image在哪裏?就在你的機器的本地Docker圖像註冊表中:
$ docker images REPOSITORY TAG IMAGE ID friendlyhello latest 326387cea398
提示:您可使用命令
docker images
或較新的docker image ls
查看全部的鏡像。它們給出相同的輸出。
運行應用程序,使用-p
,將您的機器的端口4000映射到容器已經發布的端口80:
docker run -p 4000:80 friendlyhello
您應該看到一條消息,Python正在以http://0.0.0.0:80爲您的應用服務。可是這個消息來自容器內部,它不知道您將該容器的80端口映射到4000,從而使正確的URL http://localhost:4000。
在web瀏覽器中訪問該URL,能夠看到web頁面上顯示的顯示內容,包括「Hello World」文本、容器ID和Redis錯誤消息。
注意:若是您在Windows 7上使用Docker工具,使用Docker機器IP而不是localhost。例如,http://192.168.99.100:4000。要查看IP地址,請使用命令docker -machine IP。
您還能夠在shell中使用curl命令來查看相同的內容。
$ curl http://localhost:4000 <h3>Hello World!</h3><b>Hostname:</b> 8fc990912a14<br/><b>Visits:</b> <i>cannot connect to Redis, counter disabled</i>
這個端口映射4000:80是爲了演示在Dockerfile中公開的內容和使用docker run - p發佈的內容之間的區別。在後面的步驟中,咱們將把端口80映射到容器中的80端口,並使用http://localhost。
在你的終端按CTRL + C退出。
如今讓咱們在後臺運行應用程序,在分離的模式下:
docker run -d -p 4000:80 friendlyhello
你獲得了你的應用程序的容器長ID,而後被離開你的終端。您的容器將在後臺運行。您還能夠看到縮寫容器ID經過docker container ls
(在運行命令時,這兩個工做均可以互換):
$ docker container ls CONTAINER ID IMAGE COMMAND CREATED 1fa4ab2cf395 friendlyhello "python app.py" 28 seconds ago
您將看到匹配http://localhost:4000上的CONTAINER ID
如今使用docker容器中止進程,使用容器ID,例如:
docker container stop 1fa4ab2cf395
爲了演示咱們剛剛建立的可移植性,讓咱們上傳咱們構建的映像並在其餘地方運行它。畢竟,當您想要將容器部署到生產時,您須要學習如何推進註冊。
註冊表是存儲庫的集合,而存儲庫是鏡像的集合——有點像GitHub庫,只是代碼已經構建好了。註冊表上的賬戶能夠建立許多存儲庫。docker CLI在默認狀況下使用docker的公共註冊表。
注意:咱們將在這裏使用Docker的公共註冊表,由於它是免費和預配置的,可是有許多公共註冊中心可供選擇,並且您甚至可使用Docker可信註冊表創建您本身的私有註冊表。
若是你沒有Docker賬戶,在cloud.docker.com註冊一個。記下你的用戶名。
登陸到本地機器上的Docker公共註冊表。
$ docker login
將本地映像與註冊表中的存儲庫關聯的符號是username/repository:tag
。標籤是可選的,但推薦,由於它是註冊中心用來給Docker映像提供一個版本的機制。提供存儲庫併爲上下文標記有意義的名稱,例如get - started:part2。這將把鏡像放到get-started存儲庫中,並將其標記爲part2。
docker tag image username/repository:tag
例如:
docker tag image username/repository:tag
運行docker image
以查看新標記的圖像。(您也可使用docker image ls
)
$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE friendlyhello latest d9e555c53008 3 minutes ago 195MB john/get-started part2 d9e555c53008 3 minutes ago 195MB python 2.7-slim 1c7128a655f6 5 days ago 183MB ...
將您的標記鏡像上載到存儲庫
docker push username/repository:tag
一旦完成,這個上傳的結果是公開的。若是您登陸到Docker Hub,您將在那裏看到新的圖像,使用它的pull命令。
從如今開始,你可使用docker run
在任何機器上運行你的應用程序:
docker run -p 4000:80 username/repository:tag
若是映像在機器上沒有本地可用,Docker將從存儲庫中提取該映像。
$ docker run -p 4000:80 john/get-started:part2 Unable to find image 'john/get-started:part2' locally part2: Pulling from john/get-started 10a267c67f42: Already exists f68a39a6a5e4: Already exists 9beaffc0cf19: Already exists 3c1fe835fb6b: Already exists 4c9f1fa8fcb8: Already exists ee7d8f576a14: Already exists fbccdcced46e: Already exists Digest: sha256:0601c866aab2adcc6498200efd0f754037e909e5fd42069adeff72d1e2439068 Status: Downloaded newer image for john/get-started:part2 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)
注意:若是您沒有指定這些命令的標記部分
:tag
,那麼將會假定您在構建和運行映像時都將使用最新的標記:latest
。Docker將使用沒有指定標記的圖像的最後一個版本(不必定是最近的圖像)。
不管docker運行在哪裏,它都會拉出您的映像,以及Python以及來自需求的全部依賴項requirements.txt
,並運行您的代碼。它在一個整潔的小程序包中一塊兒運行,而主機不須要安裝任何東西,而是安裝Docker來運行它。