Docker 入門 第二部分: 容器

Docker 入門 第二部分: 容器

先決條件

  • 安裝 Docker 1.13 或更高版本
  • 閱讀第一部分
  • 給你的環境作一個快速運行測試,以確保你已經準備好了python

    docker run hello-world

介紹

如今到了以docker的方式去構建一個應用了。咱們從整個應用的架構的底層開始,這裏是一個容器,咱們會在本頁介紹它。在此層級之上是一個服務,它定義了容器在生產環境中會如何工做,咱們會在第三部分介紹。最後,最頂層的是堆棧,定義全部服務的交互行爲,咱們會在第五部分介紹。web

  • Stack 堆棧
  • Services 服務
  • Container 容器 (如今你在這裏)

你的新開發環境

在過去,若是你要寫一個ptyhon應用,首先你要在你的機器上安裝一套ptyhon的運行環境。可是,這就產生了一種狀況,爲了讓你的應用按照預期運行,你機器上的環境須要精確部署,並且還須要和你的生產環境相匹配。
使用Dcoker,你只須要將一個便攜的python運行時(就是一套可執行的python)做爲鏡像,而沒有安裝的必要。而後,你構建的鏡像能夠包括基本的Python鏡像和你的應用代碼。以確保你的應用、應用的依賴和運行環境所有在一塊兒。
這些由某些內容定義的便攜的鏡像叫作 Dockerfileredis

使用 Dockerfile 定義一個容器

Dockerfile 定義容器內環境中的內容。對注入網絡接口和磁盤驅動的訪問都在這個環境中被虛擬化。以和系統的其他部分分離開來,所以你須要將端口映射到外部,並具體指明你想要將什麼文件「複製到」該環境。docker

對諸如網絡接口和磁盤驅動器等資源的訪問在這個環境中被虛擬化, 這與系統的其他部分隔離開來, 所以您須要將端口映射到外部世界, 並具體要 "複製" 哪些文件到該環境。無論怎樣,能夠保證在這個 Dockerfile中構建定義的應用能夠在任何地方均可以同樣的運行。shell

Dockerfile

建立一個空目錄。進入該目錄,並建立一個名爲 Dockerfile 的文件,把如下內容複製粘貼到該文件中,並保存。注意dockerfile文件中解釋每一條語句的註釋內容。json

# 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"]

這個 Dockerfile 文件引用了咱們尚未建立的文件,app.pyrequirements.txt。 稍後咱們會建立它們。flask

應用自己

再建立兩個文件,app.pyrequirements.txt,並把他們放入和 Dcokerfile 相同的目錄裏。這樣咱們就完成了應用,你能夠看到,這很簡單。當上面的 Dcokerfile 被構建到一個鏡像中時,app.pyrequirements.txt 也會存在於這個鏡像,這是由於 DockerfileADD 命令,而且由於有 EXPOSE 命令, app.py 的輸出能夠經過 HTTP 訪問。windows

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 爲 Python 安裝了Flask 和Redis庫,應用程序打印了環境變量NAME,以及調用socket.gethostname()後的輸出。最終,由於Redis沒有運行(咱們只安裝了Python庫,沒有安裝redis),正常的話,咱們能夠看到嘗試使用redis失敗,產生的錯誤信息。瀏覽器

注意:當在容器內部檢索容器ID時,訪問的主機名就像一個正在運行的進程的ID

就是這樣,你的系統中不須要 python 或 requirements.txt 中的任何內容,構建和運行這個鏡像時也不須要在你的系統上安裝它們。看起來你並無安裝Python和Flask的環境,但你已經有了。

構建應用

咱們已經準備好了去構建應用。確保你一直都在新建目錄的最上級。你能夠看到以下內容:

$ ls
Dockerfile      app.py          requirements.txt

如今運行構建命令,這會建立一個Docker鏡像,咱們可使用 -t 參數給它指定一個友好的名字。

docker build -t friendlyhello .

構建的鏡像保存在哪裏呢?鏡像會保存在你機器上本地的Docker鏡像registry裏:

$ docker image ls

REPOSITORY            TAG                 IMAGE ID
friendlyhello         latest              326387cea398

Linux用戶故障排查
代理服務器設置
代理服務器啓動運行後,能夠阻塞與web應用的連接。若是你使用了代理服務器,把下面的內容添加到你的Dockerfile,使用ENV命令指定你的代理服務器的主機和端口:

# Set proxy server, replace host:port with values for your servers
ENV http_proxy host:port
ENV https_proxy host:port

DNS設置
DNS配置錯誤能夠致使使用pip時出現問題。你須要設置你本身的DNS服務器地址來pip正常工做。可能你想修改 Docker守護進程的DNS設置,你能夠編輯(或建立)配置文件/etc/docker/daemon.json,設置 dns關鍵字,內容以下:

{
  "dns": ["your_dns_address", "8.8.8.8"]
}

上面的例子中,列表的第一個元素是DNS服務器的地址,第二個是Google的DNS,當第一個地址沒法使用時,會使用第二個。
在繼續以前,先保存 daemon.json 並重啓docker服務。

sudo service docker restart

問題修復後,從新嘗試build命令

運行應用

運行應用,使用-p參數將你的機器的4000端口映射到容器的發佈端口80上。

docker run -p 4000:80 friendlyhello

你會在http://0.0.0.0:80看到一個Pyhton正常爲你的應用服務的消息。可是這個消息來自容器內部,它不知道你將該容器的80端口映射到4000端口,須要輸入正確的URL http://localhost:4000

在瀏覽器中訪問正確的URL,以查看網頁中顯示的內容。

圖片名稱

注意:若是你在Windows7中使用Docker Toolbox,要使用Docker機器的IP替換localhost。例如,http://192.168.99.100:4000/。使用 docker-machine ip 命令能夠找到這個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 演示了和 DockerfiileEXPOSE的不一樣之處,並且在運行 docker run -p 時設置了發佈的值。在後面的步驟中,映射了主機的4000端口到容器的80端口,並使用了 http://localhost

在你的終端中使用 CTRL+C 來退出。

在Windows中,顯示的關閉容器
在windows系統,CTRL+C 不會關閉容器。因此,首先輸入 CTRL+C 返回提示符(或者新打開一個shell),而後輸入 docker container ls 來列出運行中的容器,隨後經過 docker container stop <Container NAME or ID>來關閉容器。不然,在稍後的步驟中,當你嘗試從新運行容器會獲得一個來自守護進程錯誤的響應。

如今咱們以分離模式在後臺運行應用:

docker run -d -p 4000:80 friendlyhello

你會得到應用容器的長ID並返回終端,容器會在後臺運行,你還能夠榮國 docker container ls 查看縮寫的容器ID()

$ docker container ls
CONTAINER ID        IMAGE               COMMAND             CREATED
1fa4ab2cf395        friendlyhello       "python app.py"     28 seconds ago

注意 容器IDhttp://localhost:4000上的內容相匹配。
如今使用 docker container stop 指定 容器ID 來結束進程,以下:

docker container stop 1fa4ab2cf395

共享你的鏡像

爲了證實咱們剛剛建立的鏡像的可移植性,咱們能夠上傳構建的鏡像並在任何地方運行它。畢竟,當你想把容器部署到生產環境中時,你須要知道如何推送到 registries。

一個registry 是一個倉庫的集合,一個倉庫是一個鏡像的集合——相似於GitHub的倉庫。只是代碼已經構建好了。registry上的一個用戶能夠建立多個倉庫。docker CLI默認使用Docker的公共registry。

注意: 這裏咱們使用Docker的公共registry是由於它是免費的和預置的,不過咱們仍是有不少公共的registry能夠選擇,甚至你可使用 Docker Trusted Registry來搭建你本身私有的registry。

使用你的docker ID進行登陸

若是你沒有docker用戶,能夠在hub.docker.com註冊一個。記下你的用戶名。在你的本地機器上登陸到DOcker公共的registry。

$ docker login

爲鏡像加標籤

關聯本地鏡像與registry倉庫的方法是 username/repository:tag 。標籤是可選的,但建議設置上,由於這是registry爲docekr鏡像提供版本的機制,根據上下文給倉庫和標籤起一個有意義的名字,例如:get-started:part2。這裏咱們將鏡像放在 get-started倉庫,標籤名字爲 part2
如今,把它們放在一塊兒來標記鏡像。使用in的用戶名、倉庫和標籤名來運行 docker tag image,以便將鏡像上傳到你想要上傳的位置,命令語法以下:

docker tag image username/repository:tag

例如:

docker tag friendlyhello gordon/get-started:part2

運行docker image ls來查看你信打標籤的鏡像:

$ docker image ls

REPOSITORY               TAG                 IMAGE ID            CREATED             SIZE
friendlyhello            latest              d9e555c53008        3 minutes ago       195MB
gordon/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 Hub看到最新的鏡像。

從遠程倉庫拉取並運行鏡像

從如今開始,你能夠在在任何機器上使用docker run運行你的應用:

docker run -p 4000:80 username/repository:tag

若是鏡像在本地機器上不可用,Docker會從遠程倉庫上拉取鏡像:

$ docker run -p 4000:80 gordon/get-started:part2
Unable to find image 'gordon/get-started:part2' locally
part2: Pulling from gordon/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 gordon/get-started:part2
 * Running on http://0.0.0.0:80/ (Press CTRL+C to quit)

如今不管你在哪裏運行docker run,他會拉取鏡像,Pyhton、requirements.txt中指定的依賴和你的代碼 會在一塊兒運行,他們所有打包在一個小包裹裏(鏡像),你不須要在你的機器上安裝任何東西,Docker 就能夠運行它了。

第二部分總結

這就是本頁全部內容,下一節,咱們會經過在服務中運行這個容器來學習如何擴展咱們的應用程序。

回顧和備忘【可選】

官方在這裏提供了一段視頻,但我這裏觀看有問題,我就找把視頻錄製下來放到了B站,地址以下:

官方視頻

B站的「分享視頻」中給的代碼放在博客園沒法播放,只能在這裏寫一個地址了。

下面是本頁中關於docker的基本命令列表,以及一些相關的命令,在繼續後面的學習前能夠研究一下。

docker build -t friendlyhello .  # Create image using this directory's Dockerfile
docker run -p 4000:80 friendlyhello  # Run "friendlyname" mapping port 4000 to 80
docker run -d -p 4000:80 friendlyhello         # Same thing, but in detached mode
docker container ls                                # List all running containers
docker container ls -a             # List all containers, even those not running
docker container stop <hash>           # Gracefully stop the specified container
docker container kill <hash>         # Force shutdown of the specified container
docker container rm <hash>        # Remove specified container from this machine
docker container rm $(docker container ls -a -q)         # Remove all containers
docker image ls -a                             # List all images on this machine
docker image rm <image id>            # Remove specified image from this machine
docker image rm $(docker image ls -a -q)   # Remove all images from this machine
docker login             # Log in this CLI session using your Docker credentials
docker tag <image> username/repository:tag  # Tag <image> for upload to registry
docker push username/repository:tag            # Upload tagged image to registry
docker run username/repository:tag                   # Run image from a registry
相關文章
相關標籤/搜索