docker 入門2 - 容器 【翻譯】

入門,第 2 部分:容器

先決條件

  • 安裝的 Docker 版本是 1.13 及以上。
  • 讀完 第一部分
  • 用下面的命令快速測試你的環境是否完備:html

     
    docker run hello-world

     

概述

如今開始用 Docker 的方式構建應用。咱們從這個應用的層次結構底部開始,也就是這裏講的容器。在容器層上面有第三部分講的 service 層,定義了生產中的容器的行爲方式。最頂層的是第五部分講的 stack 層,定義了全部 service 的交互。python

  • Stack
  • Services
  • Container (你在這裏)

新的部署環境

過去,若是要寫個 Python 應用,首先要在機器上安裝 Python 運行時。這就帶來了一個問題:要使應用按照預期運行,就須要機器上的環境完美適合應用程序,同時生產環境須要與開發環境徹底一致。 redis

經過 Docker,能夠將一個可移植的 Python 運行時做爲一個 image 鏡像獲取,無需安裝。 而後,構建時能夠將基礎 Python 鏡像與應用程序代碼一塊兒包括在內,確保應用程序,依賴項和運行時都一塊兒發佈。 經過 Dockerfile 定義可移植的鏡像。docker

用Dockerfile定義一個容器

Dockerfile 定義了容器中的環境包含哪些東西。對網絡接口、磁盤等資源的訪問被虛擬化到了這個環境內部,從而與系統的其餘部分隔離,所以必須映射端口到外部,而且指明須要把哪些文件複製到容器內部。這些完成後,經過這個 Dockerfile 對應用的構建在任何地方運行時都會有相同的表現。shell

Dockerfilejson

在本地計算機上建立空目錄。改變目錄 (cd) 進入到新目錄,建立名爲 Dockerfile 的文件,將如下內容複製並粘貼到該文件中,而後將其保存。請注意解釋新 Dockerfile 中每一個語句的註釋。flask

# 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
COPY . /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。接下來,讓咱們建立這些。瀏覽器

應用程序自己

再建立兩個文件,requirements.txt 和app.py,並將它們與 Dockerfile 放在同一文件夾中。這完成了咱們的應用程序,你能夠看到這是很簡單的。當上述 Dockerfile 內置到映像中時,因爲 Dockerfile 的 COPY 命令,app.pyrequirements.txt 存在,而且因爲 EXPOSE命令,能夠經過 HTTP 訪問來自app.py的輸出。服務器

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 自己),所以,咱們應預期在此處使用它的嘗試失敗並生成錯誤消息。

注意:在容器內訪問主機的名稱將檢索容器 ID,該 ID 相似於正在運行的可執行文件的進程 ID。

就是這樣!您不須要 Python 或任何requirements.txt 在您的系統上,也不構建或運行此映像安裝它們在您的系統上。看起來您並無真正使用 Python 和 Flask 創建了一個環境,但您已經設置了環境。

構建應用

咱們準備構建應用程序。請確保您仍在新目錄的頂層。如下是 ls 應顯示的內容:

 
$ ls
Dockerfile      app.py          requirements.txt

 

如今運行生成命令。這將建立一個 Docker 鏡像,咱們將使用 --tag 選項命名該鏡像。若是要使用較短的選項,請使用 -t。

 
docker build --tag=friendlyhello .

 

你的構建鏡像在哪裏?它位於計算機的本地 Docker 鏡像註冊表中:

 
$ docker image ls

REPOSITORY            TAG                 IMAGE ID
friendlyhello         latest              326387cea398

 

請注意標記如何默認爲最新標記。標記選項的完整語法相似於

--tag=friendlyhello:v0.0.1.

 

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 設置。您可使用 dns 密鑰在 /etc/docker/daeon.json 上編輯(或建立)配置文件,以下所示:

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

 

在上面的示例中,列表的第一個元素是 DNS 服務器的地址。第二個項目是谷歌的DNS,可使用時,第一個不可用。

在繼續以前,請保存daemon.json 並從新啓動 docker 服務。

 
sudo service docker restart

 

修復後,重試以運行build命令。

運行應用

運行應用,使用 -p 將計算機的端口 4000 映射到容器的已發佈端口 80:

 
docker run -p 4000:80 friendlyhello

 

您應該會看到一條消息,指出 Python 正在http://0.0.0.0:80爲你的應用提供服務。可是,該消息來自容器內部,該容器不知道您將該容器的端口 80 映射到 4000,從而正確的 URL 是 http://localhost:4000

轉到 Web 瀏覽器中的該 URL 以查看網頁上提供的顯示內容。

注意:若是您在 Windows 7 上使用 Docker Toolbox,請使用 Docker machine 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 中的 EXPOSE 與運行 docker run-p 時設置的發佈值之間的差別。在後面的步驟中,將主機上的端口 4000 映射到容器中的端口 80 並使用http://localhost

在終端中點擊 CTRL+C 以退出。

在 Windows 上,顯式中止容器

在 Windows 系統上,CTRL+C 不會中止容器。所以,首先鍵入 CTRL_C 以返回提示(或打開另外一個 shell),而後鍵入 docker 容器 ls 以列出正在運行的容器,而後鍵入 docker 容器中止 [容器名稱或 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

 

請注意,CONTAINER ID 與http://localhost:4000上的內容相匹配。

如今使用 docker container stop 來結束進程,使用 CONTAINER ID,以下所示:

 
docker container stop 1fa4ab2cf395

 

分享鏡像

爲了演示咱們剛剛建立的內容的可移植性,讓咱們上傳構建的鏡像並將其運行到其餘地方。畢竟,您須要知道如何在將容器部署到生產時推送到registries。

一個registry是repositories的集合, repository是鏡像的集合— 相似於 GitHub repository,但代碼已經生成。一個registry上的賬戶能夠建立許多repositories。默認狀況下,Docker CLI 使用 Docker 的公共registry。

注意:咱們在這裏使用 Docker 的公共registry,只是由於它是免費的,而且預先配置了,但有許多公共registry可供選擇,您甚至可使用 Docker 可信registry設置您本身的專用registry。

用 Docker ID 登陸

若是您沒有 Docker 賬戶,請hub.docker.com註冊。記下您的用戶名。

在本地機器上登陸 Docker 的公共 registry:

 
$ docker login

 

給鏡像打標籤(Tag)

將一個本地鏡像關聯到註冊處 registry 中的一個倉庫的符號是 username/repository:tag。標籤是可選的,可是建議使用,由於這是 registry 用來給 Docker 鏡像指定版本的機制。給倉庫和標籤起有意義的名字,例如 get-started:part2。這會把鏡像放入 get-started 倉庫,而且添加標籤 part2。

運行 docker tag image 命令,用本身的 username,repository 和 標籤名,這樣鏡像能夠上傳到指定的位置。命令的語法是:

 
docker tag image username/repository:tag

 

舉個例子:

 
docker tag friendlyhello john/get-started:part2

 

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

 
$ docker image ls
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
...

 

發佈鏡像

將標記的鏡像上載到repository:

 
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)

 

無論 docker run 在哪裏運行,Docker 會獲取你的鏡像並運行(這裏的鏡像安裝了 Python 和從 requirements.txt 文件指定的依賴,並會運行應用代碼)。全部的東西都在一個包裏,獲取到就能夠運行,不須要安裝其餘東西。

第二部分結論

本頁就到此。在下一節中,咱們將學習如何經過在service中運行此容器來擴展應用程序。

回顧和備忘單(可選)

下面是此頁面中的基本 Docker 命令的列表,若是您想在繼續以前先瀏覽一下,則提供一些相關命令。

 
docker build -t friendlyhello .  # Create image using this directory's Dockerfile
docker run -p 4000:80 friendlyhello  # Run "friendlyhello" 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
相關文章
相關標籤/搜索