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