Docker 初探
一、容器(Container)
簡單來說,容器 是對應用程式及其依賴庫的一種封裝 。
乍看上去,容器就像一個輕量級的虛擬機器系統(VM),也封裝了一個作業系統例項用來執行某些應用程式。
相比於傳統的虛擬機器,容器的優勢主要在以下幾個方面:
-
容器與宿主機共享資源,使得其效率有了很大的提升。與主機中執行的應用程式相比,在容器中執行的應用程式幾乎沒有任何額外的開銷。
-
容器可移植 的特性可以解決由於執行環境的微小變化引發的一系列問題。
-
容器的輕量級特性意味著開發人員可以同時執行數十個容器,從而可以模擬出生產級別的分散式系統 。
-
對於不使用雲端應用的使用者和開發者,使用者可以節省下大量的安裝和配置時間,也不用擔心繫統的依賴衝突等問題。而開發者同時也可以避免由於使用者系統環境的差異導致的可用性問題。
總的來說,虛擬機器的基本目標,是完整 地虛擬出一個外部 (獨立)的系統環境,而容器是為了達到應用程式的可移植和自成一體。
而 Docker Engine 為執行容器提供了快捷方便的互動介面。
二、Docker 安裝(Linux)
Linux 系統上的 Docker 安裝,可以直接使用官方提供的安裝指令碼(ofollow,noindex">https://get.docker.com ),命令如下:
$ sudo wget -qO- https://get.docker.com/ | sh
或
$ sudo curl -sSL https://get.docker.com/ | sh
Mac OS 和 Windows 系統上的 Docker 安裝,可參考官方文件Docker for Mac 和Docker for Windows
Docker 官方映象訪問緩慢,可以使用阿里雲提供的加速 服務(參考映象加速器 )
Docker 需要特定的許可權才能執行,即普通使用者執行 docker 命令時需要加上 sudo 。
$ docker version Client: Version:18.06.1-ce API version:1.38 Go version:go1.10.3 Git commit:e68fc7a Built:Tue Aug 21 17:24:51 2018 OS/Arch:linux/amd64 Experimental:false Got permission denied while trying to connect to the Docker daemon socket at unix:///var/run/docker.sock: Get http://%2Fvar%2Frun%2Fdocker.sock/v1.38/version: dial unix /var/run/docker.sock: connect: permission denied
可以將本地使用者新增到docker 使用者組 中,後續使用 docker 命令時即無需加上 sudo 字首。
$ sudo usermod -aG docker <username>
$ docker version Client: Version:18.06.1-ce API version:1.38 Go version:go1.10.3 Git commit:e68fc7a Built:Tue Aug 21 17:24:51 2018 OS/Arch:linux/amd64 Experimental:false Server: Engine: Version:18.06.1-ce API version:1.38 (minimum version 1.12) Go version:go1.10.3 Git commit:e68fc7a Built:Tue Aug 21 17:23:15 2018 OS/Arch:linux/amd64 Experimental:false
三、操作入門
Hello World
$ docker run debian echo "Hello World" Unable to find image 'debian:latest' locally latest: Pulling from library/debian 05d1a5232b46: Pull complete Digest: sha256:07fe888a6090482fc6e930c1282d1edf67998a39a09a0b339242fbfa2b602fff Status: Downloaded newer image for debian:latest Hello World
docker run
命令用於載入容器並執行某個命令。上述命令中的debian
用於指定所使用的映象的名字。
如本地磁碟上沒有名為 debian 的映象檔案,則 Docker 會檢查線上的 Docker Hub 並將最新版本的 Debian 映象下載到本地。
之後將下載好的映象轉化成執行的容器,並在容器中執行指定的命令。
命令執行完畢後,輸出的結果傳送到標準輸出,容器停止執行。
互動式 Shell
$ docker run -i -t debian /bin/bash root@4af9c13b78d7:/# echo "Hello from Container" Hello from Container root@4af9c13b78d7:/# exit exit
上面的命令會在容器中開啟一個互動式 Shell (就像是 ssh 到了一個遠端主機上)。
-i
和-t
選項表示開啟一個已綁定了 tty 的互動式會話
。
當使用exit
命令退出 bash 後,執行中的容器也會停止。
可以使用docker start -i <Container_name>
命令回到互動式 Shell 中
列出 / 刪除容器
docker ps
命令可以列出當前正在執行
的容器及其相關資訊,如容器ID、使用的映象、執行的命令、建立時間、當前狀態和名稱
等,加上-a
選項則列出所有容器(包含已停止執行的容器)
$ docker ps CONTAINER IDIMAGECOMMANDCREATEDSTATUSPORTSNAMES $ $ docker ps -a CONTAINER IDIMAGECOMMANDCREATEDSTATUSPORTSNAMES f74ed03352d3debian"/bin/bash"9 minutes agoExited (0) 8 minutes agomystifying_beaver 45e9e3f3847bdebian"echo 'Hello World'"9 minutes agoExited (0) 9 minutes agoquirky_mirzakhani
可以使用docker rm <Container_name>
命令刪除已停止執行的容器。
$ docker rm mystifying_beaver mystifying_beaver $ docker ps -a CONTAINER IDIMAGECOMMANDCREATEDSTATUSPORTSNAMES 45e9e3f3847bdebian"echo 'Hello World'"13 minutes agoExited (0) 13 minutes agoquirky_mirzakhani
docker rm -v $(docker ps -aq -f status=exited)
命令可以刪除所有
已停止執行的容器
手動建立映象檔案
從 Docker Hub 上拉取的映象很多為初始的精簡系統,執行容器後,可以給容器內的系統安裝軟體並提交更改,做成新的映象供後期使用。
使用docker run
命令執行容器並安裝軟體
$ docker run -it --name cowsay --hostname cowsay debian /bin/bash root@cowsay:/# apt-get update ... root@cowsay:/# apt-get install -y cowsay fortune ... root@cowsay:/# /usr/games/fortune | /usr/games/cowsay ________________________ < Don't get to bragging. > ------------------------ \^__^ \(oo)\_______ (__)\)\/\ ||----w | ||||
其中--name
選項用於指定容器的名稱,--hostname
選項用於指定容器系統的主機名。
使用docker commit
命令將容器轉換成映象檔案。
$ docker commit cowsay test/cowsayimage sha256:2dcb2f4d09f824120db19f79d3bfbdb85c24d4888732bcc7eaae79f707d80e32 $ docker images REPOSITORYTAGIMAGE IDCREATEDSIZE test/cowsayimagelatest2dcb2f4d09f813 minutes ago159MB debianlatestf2aae6ff5d893 weeks ago101MB
docker images
命令用來檢視已在本地的映象檔案及其資訊。
使用生成的映象檔案
$ docker run test/cowsayimage /usr/games/fortune | /usr/games/cowsay _______________________________________ / Today is the first day of the rest of \ \ the mess./ --------------------------------------- \^__^ \(oo)\_______ (__)\)\/\ ||----w | ||||
使用 Dockerfile 建立映象檔案
Dockerfile 其實就是一個簡單的文字檔案,裡面包含了建立 Docker 映象的一系列步驟。相比於手動建立 Docker 映象,使用 Dockerfile 自動地建立映象檔案,省去了大量重複的操作,同時也便於分享給其他人。
編輯 Dockerfile
$ mkdir cowsay && cd cowsay $ vim Dockerfile
然後在新建的 Dockerfile 中填入以下內容:
FROM debian:wheezy RUN apt-get update && apt-get install -y cowsay fortune
其中FROM
用於指定構建時使用的初始映象檔案,RUN
用於指定在映象中執行的 Shell 命令
構建映象並測試
使用docker build
命令建立 Docker 映象:
$ docker build -t test/cowsay-dockerfile . Sending build context to Docker daemon2.048kB Step 1/2 : FROM debian:wheezy wheezy: Pulling from library/debian 703d6f3fb41c: Pull complete Digest: sha256:d00f167f8f2e70ecc2e0f5410a3cb74cd4ad720e33b9810da6a2dcfa81dccfc0 Status: Downloaded newer image for debian:wheezy ---> 94825a89630c Step 2/2 : RUN apt-get update && apt-get install -y cowsay fortune ---> Running in efcf246bde0f ... Setting up cowsay (3.03+dfsg1-4) ... Removing intermediate container efcf246bde0f ---> 88e9a0f834cd Successfully built 88e9a0f834cd Successfully tagged test/cowsay-dockerfile:latest
測試剛構建好的 Docker 映象:
$ docker run test/cowsay-dockerfile /usr/games/cowsay "Moo" _____ < Moo > ----- \^__^ \(oo)\_______ (__)\)\/\ ||----w | ||||
ENTRYPOINT
Dockerfile 中的 ENTRYPOINT 選項可以用來指定容器執行時自動執行的命令。如將 Dockerfile 改為如下內容並重新構建:
FROM debian:wheezy RUN apt-get update && apt-get install -y cowsay fortune ENTRYPOINT ["/usr/games/cowsay"]
$ docker build -t test/cowsay-dockerfile . ... $ docker run test/cowsay-dockerfile "Moo" _____ < Moo > ----- \^__^ \(oo)\_______ (__)\)\/\ ||----w | ||||
執行上述容器時則不需要再指定命令(/usr/games/cowsay
)而只輸入命令的引數即可。
可以在當前目錄下新建一個entrypoint.sh
指令碼:
#!/bin/bash if [ $# -eq 0 ]; then /usr/games/fortune | /usr/games/cowsay else /usr/games/cowsay "$@" fi
上述內容為 Shell 指令碼檔案,不作詳細解釋。作用是當docker run
沒有為容器中執行的命令提供引數時,執行fortune | cowsay
,如提供了引數,則執行cowsay <引數>
命令。
將 entrypoint.sh 檔案新增執行許可權:chmod +x entrypoint.sh
將 Dockerfile 改為如下版本:
FROM debian:wheezy RUN apt-get update && apt-get install -y cowsay fortune COPY entrypoint.sh / ENTRYPOINT ["/entrypoint.sh"]
其中COPY
選項用來將本地主機上的檔案複製到映象的檔案系統裡(類似於cp
命令)
最終效果如下:
$ docker build -t test/cowsay-dockerfile . ... $ docker run test/cowsay-dockerfile _________________________________________ / You don't become a failure until you're \ \ satisfied with being one./ ----------------------------------------- \^__^ \(oo)\_______ (__)\)\/\ ||----w | |||| $ docker run test/cowsay-dockerfile Hello Moo ___________ < Hello Moo > ----------- \^__^ \(oo)\_______ (__)\)\/\ ||----w | ||||
四、基本使用
埠轉發
假如容器中執行著一個 Web 伺服器,需要外部世界可以訪問。此時可以使用 docker 的-p
或-P
選項,將本地主機的埠轉發至容器內的埠。如:
$ docker run -d -p 8000:80 nginx Unable to find image 'nginx:latest' locally latest: Pulling from library/nginx ... Status: Downloaded newer image for nginx:latest be1364f343ae7aff9d5a6f6040b5d6ca69363a4480f04548988dd798ab04ab7b $ docker ps CONTAINER IDIMAGECOMMANDCREATEDSTATUSPORTSNAMES be1364f343aenginx"nginx -g 'daemon of…"8 seconds agoUp 7 seconds0.0.0.0:8000->80/tcpquirky_mclean $ curl localhost:8000 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ... $
其中docker run -d -p 8000:80 nginx
命令用於在後臺
(-d
選項)啟動一個由官方 nginx 映象建立的容器,並將本地主機的 8000 埠對映到容器的 80 埠(-p 8000:80
),即容器中 nginx 服務執行的埠。
可以看到,當使用 curl 命令訪問本地主機的 8000 埠時,等同於訪問了容器的 80 埠,即容器中的 nginx 服務。
而-P
選項可以自動選擇空閒的埠進行轉發,無需指定本地主機或容器的埠。可參考以下例項:
$ ID=$(docker run -d -P nginx) $ docker port $ID 80 0.0.0.0:32768 $ curl localhost:32768 <!DOCTYPE html> <html> <head> <title>Welcome to nginx!</title> ...
關聯容器
容器關聯可以允許同一個主機上的多個容器相互交換資料。當使用預設的網路模型時,這些關聯的容器通過其“內部” 網路傳輸資料,即關聯容器間的相互交流不會暴露給本地主機。
可參考以下例項:
$ docker run --name myredis -d redis be53967c42fd3292dfd59fd5d15e7025fa436816e46f6e7cbc3c47b06ddb0047 $ docker run --rm -it --link myredis:redis redis /bin/bash root@b27aca7d8c54:/data# redis-cli -h redis -p 6379 redis:6379> ping PONG redis:6379>
其中docker run --name myredis -d redis
命令用於在後臺啟動一個 redis 容器,並將其命名為 myredis 。同時返回該容器的 ID 到標準輸出。
docker run --rm -it --link myredis:redis redis /bin/bash
命令用於啟動另一個 redis 容器,並以互動的方式訪問其 Shell (-it /bin/bash
)。--rm
選項表示該容器退出後將自動刪除。
容器關聯的操作則由--link myredis:redis
選項實現。表示將新容器關聯至已存在的 "myredis" 容器,併為 "myredis" 容器設定別名為 "redis" ,即可以在當前的新容器中通過別名(redis)訪問。
該選項會在新容器的/etc/hosts
檔案中新增一條主機名為redis
的記錄,並將其指向 "myredis" 容器的 IP 地址。所以在當前容器的 Shell 中使用redis-cli
命令訪問 "myredis" 中的服務時,可以無需指定其 IP 地址,直接使用主機名 "redis" 即可。(redis-cli -h redis -P 6379
)
儲存卷
可以在使用docker run
時通過-v
選項指定儲存卷:
$ docker run -it --name container-test -h CONTAINER -v /data debian /bin/bash root@CONTAINER:/# cd /data ; touch test-file root@CONTAINER:/data# ls test-file root@CONTAINER:/data# exit exit
上面的命令會將容器中的/data
目錄變成一個儲存卷,該目錄下的任何檔案都會被複制到卷中。
可以使用docker inspect
命令檢視該儲存卷在本地主機中的位置:
$ docker inspect -f {{.Mounts}} container-test [{volume 7ae1... /var/lib/docker/volumes/7ae1.../_data /data localtrue }]
可以在儲存卷對應於本地主機的目錄(/var/lib/docker/volumes/7ae1.../_data
)中建立檔案,容器中對應目錄(/data
)下則會立即出現同樣的檔案。
$ sudo ls /var/lib/docker/volumes/7ae1.../_data test-file $ sudo touch /var/lib/docker/volumes/7ae1.../_data/test-file2 $ docker start -i container-test root@CONTAINER:/# ls /data test-filetest-file2 root@CONTAINER:/#
儲存卷還可以通過 Dockerfile 中的 VOLUME 選項指定。如:VOLUME /data
共享資料
可以使用-v HOST_DIR:CONTAINER_DIR
選項在本地主機和一(或多)個容器間共享資料。如:
$ docker run -v /home/starky/data:/data debian ls /data
該命令會將本地主機上的/home/starky/data
目錄掛載到容器中的/data
目錄下,所有已經存在於/home/starky/data
目錄下的檔案在容器中也同樣能夠被訪問。
但是原本存在於容器的/data
目錄下的檔案則被隱藏。
如某些配置檔案可以一直存放在本地主機上,並在需要時掛載到通用映象構建的容器中。
可以使用docker run
命令的--volumes-from CONTAINER
選項在容器間
共享資料。如:
$ docker run -it -h NEWCONTAINER --volumes-from container-test debian /bin/bash root@NEWCONTAINER:/# ls /data test-filetest-file2 root@NEWCONTAINER:/#
上面的命令建立了一個新的容器,且通過--volumes-from
選項,該容器可以訪問之前的 container-test 容器中的儲存卷(/data
)。
資料容器
資料容器是一種特殊的容器,其唯一目的是為了方便其他容器之間共享資料。如建立一個 PostgreSQL 的資料容器:
$ docker run --name dbdata postgres echo "Data-only container for postgres"
該命令用於從 postgres 映象建立一個容器,並初始化映象裡定義的儲存卷。
之後可以通過--volumes-from
選項使用資料容器裡的儲存卷:
$ docker run -d --volumes-from dbdata --name db1 postgres
參考資料
Using Docker: Developing and Deploying Software with Containers