Docker入門,Part 2:容器

要求

  • 已安裝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:port

DNS設(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 container ls -a -q) # 刪除所有容器 docker image ls -a # 列出本機(jī)上所有的鏡像 docker image rm <image_id> # 從本機(jī)上刪除指定的鏡像 docker image 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
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容