一萍悴、容器(Container)
簡單來說,容器是對應(yīng)用程序及其依賴庫的一種封裝。
乍看上去谨垃,容器就像一個(gè)輕量級(jí)的虛擬機(jī)系統(tǒng)(VM),也封裝了一個(gè)操作系統(tǒng)實(shí)例用來運(yùn)行某些應(yīng)用程序撵儿。
相比于傳統(tǒng)的虛擬機(jī)乘客,容器的優(yōu)勢主要在以下幾個(gè)方面:
容器與宿主機(jī)共享資源,使得其效率有了很大的提升淀歇。與主機(jī)中運(yùn)行的應(yīng)用程序相比易核,在容器中運(yùn)行的應(yīng)用程序幾乎沒有任何額外的開銷。
容器可移植的特性可以解決由于運(yùn)行環(huán)境的微小變化引發(fā)的一系列問題浪默。
容器的輕量級(jí)特性意味著開發(fā)人員可以同時(shí)運(yùn)行數(shù)十個(gè)容器牡直,從而可以模擬出生產(chǎn)級(jí)別的分布式系統(tǒng)。
對于不使用云端應(yīng)用的用戶和開發(fā)者纳决,用戶可以節(jié)省下大量的安裝和配置時(shí)間碰逸,也不用擔(dān)心系統(tǒng)的依賴沖突等問題。而開發(fā)者同時(shí)也可以避免由于用戶系統(tǒng)環(huán)境的差異導(dǎo)致的可用性問題阔加。
總的來說饵史,虛擬機(jī)的基本目標(biāo),是完整地虛擬出一個(gè)外部(獨(dú)立)的系統(tǒng)環(huán)境,而容器是為了達(dá)到應(yīng)用程序的可移植和自成一體胳喷。
而 Docker Engine 為運(yùn)行容器提供了快捷方便的交互接口湃番。
二、Docker 安裝(Linux)
Linux 系統(tǒng)上的 Docker 安裝吭露,可以直接使用官方提供的安裝腳本(https://get.docker.com)吠撮,命令如下:
$ sudo wget -qO- https://get.docker.com/ | sh
或
$ sudo curl -sSL https://get.docker.com/ | sh
Mac OS 和 Windows 系統(tǒng)上的 Docker 安裝,可參考官方文檔 Docker for Mac 和 Docker for Windows
Docker 官方鏡像訪問緩慢讲竿,可以使用阿里云提供的加速服務(wù)(參考 鏡像加速器)
Docker 需要特定的權(quán)限才能運(yùn)行泥兰,即普通用戶執(zhí)行 docker 命令時(shí)需要加上 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 用戶組中题禀,后續(xù)使用 docker 命令時(shí)即無需加上 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
命令用于加載容器并執(zhí)行某個(gè)命令投剥。上述命令中的 debian 用于指定所使用的鏡像的名字师脂。
如本地磁盤上沒有名為 debian 的鏡像文件,則 Docker 會(huì)檢查在線的 Docker Hub 并將最新版本的 Debian 鏡像下載到本地江锨。
之后將下載好的鏡像轉(zhuǎn)化成運(yùn)行的容器吃警,并在容器中執(zhí)行指定的命令。
命令執(zhí)行完畢后啄育,輸出的結(jié)果傳送到標(biāo)準(zhǔn)輸出酌心,容器停止運(yùn)行。
交互式 Shell
$ docker run -i -t debian /bin/bash
root@4af9c13b78d7:/# echo "Hello from Container"
Hello from Container
root@4af9c13b78d7:/# exit
exit
上面的命令會(huì)在容器中打開一個(gè)交互式 Shell (就像是 ssh 到了一個(gè)遠(yuǎn)程主機(jī)上)挑豌。
-i
和 -t
選項(xiàng)表示打開一個(gè)已綁定了 tty 的交互式會(huì)話安券。
當(dāng)使用 exit
命令退出 bash 后,運(yùn)行中的容器也會(huì)停止氓英。
可以使用
docker start -i <Container_name>
命令回到交互式 Shell 中
列出 / 刪除容器
docker ps
命令可以列出當(dāng)前正在運(yùn)行的容器及其相關(guān)信息侯勉,如容器ID、使用的鏡像铝阐、執(zhí)行的命令址貌、創(chuàng)建時(shí)間、當(dāng)前狀態(tài)和名稱等徘键,加上 -a
選項(xiàng)則列出所有容器(包含已停止運(yùn)行的容器)
$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
$
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f74ed03352d3 debian "/bin/bash" 9 minutes ago Exited (0) 8 minutes ago mystifying_beaver
45e9e3f3847b debian "echo 'Hello World'" 9 minutes ago Exited (0) 9 minutes ago quirky_mirzakhani
可以使用 docker rm <Container_name>
命令刪除已停止運(yùn)行的容器练对。
$ docker rm mystifying_beaver
mystifying_beaver
$ docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
45e9e3f3847b debian "echo 'Hello World'" 13 minutes ago Exited (0) 13 minutes ago quirky_mirzakhani
docker rm -v $(docker ps -aq -f status=exited)
命令可以刪除所有已停止運(yùn)行的容器
手動(dòng)創(chuàng)建鏡像文件
從 Docker Hub 上拉取的鏡像很多為初始的精簡系統(tǒng),運(yùn)行容器后吹害,可以給容器內(nèi)的系統(tǒng)安裝軟件并提交更改螟凭,做成新的鏡像供后期使用。
使用 docker run
命令運(yùn)行容器并安裝軟件
$ 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
選項(xiàng)用于指定容器的名稱它呀,--hostname
選項(xiàng)用于指定容器系統(tǒng)的主機(jī)名螺男。
使用 docker commit
命令將容器轉(zhuǎn)換成鏡像文件棒厘。
$ docker commit cowsay test/cowsayimage
sha256:2dcb2f4d09f824120db19f79d3bfbdb85c24d4888732bcc7eaae79f707d80e32
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
test/cowsayimage latest 2dcb2f4d09f8 13 minutes ago 159MB
debian latest f2aae6ff5d89 3 weeks ago 101MB
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 創(chuàng)建鏡像文件
Dockerfile 其實(shí)就是一個(gè)簡單的文本文件下隧,里面包含了創(chuàng)建 Docker 鏡像的一系列步驟绊谭。相比于手動(dòng)創(chuàng)建 Docker 鏡像,使用 Dockerfile 自動(dòng)地創(chuàng)建鏡像文件汪拥,省去了大量重復(fù)的操作,同時(shí)也便于分享給其他人篙耗。
編輯 Dockerfile
$ mkdir cowsay && cd cowsay
$ vim Dockerfile
然后在新建的 Dockerfile 中填入以下內(nèi)容:
FROM debian:wheezy
RUN apt-get update && apt-get install -y cowsay fortune
其中 FROM
用于指定構(gòu)建時(shí)使用的初始鏡像文件迫筑,RUN
用于指定在鏡像中執(zhí)行的 Shell 命令
構(gòu)建鏡像并測試
使用 docker build
命令創(chuàng)建 Docker 鏡像:
$ docker build -t test/cowsay-dockerfile .
Sending build context to Docker daemon 2.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
測試剛構(gòu)建好的 Docker 鏡像:
$ docker run test/cowsay-dockerfile /usr/games/cowsay "Moo"
_____
< Moo >
-----
\ ^__^
\ (oo)\_______
(__)\ )\/\
||----w |
|| ||
ENTRYPOINT
Dockerfile 中的 ENTRYPOINT 選項(xiàng)可以用來指定容器運(yùn)行時(shí)自動(dòng)執(zhí)行的命令。如將 Dockerfile 改為如下內(nèi)容并重新構(gòu)建:
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 |
|| ||
運(yùn)行上述容器時(shí)則不需要再指定命令(/usr/games/cowsay
)而只輸入命令的參數(shù)即可宗弯。
可以在當(dāng)前目錄下新建一個(gè) entrypoint.sh
腳本:
#!/bin/bash
if [ $# -eq 0 ]; then
/usr/games/fortune | /usr/games/cowsay
else
/usr/games/cowsay "$@"
fi
上述內(nèi)容為 Shell 腳本文件脯燃,不作詳細(xì)解釋。作用是當(dāng)
docker run
沒有為容器中執(zhí)行的命令提供參數(shù)時(shí)蒙保,執(zhí)行fortune | cowsay
辕棚,如提供了參數(shù),則執(zhí)行cowsay <參數(shù)>
命令邓厕。
將 entrypoint.sh 文件添加執(zhí)行權(quán)限: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
選項(xiàng)用來將本地主機(jī)上的文件復(fù)制到鏡像的文件系統(tǒng)里(類似于 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 |
|| ||
四逝嚎、基本使用
端口轉(zhuǎn)發(fā)
假如容器中運(yùn)行著一個(gè) Web 服務(wù)器,需要外部世界可以訪問详恼。此時(shí)可以使用 docker 的 -p
或 -P
選項(xiàng)补君,將本地主機(jī)的端口轉(zhuǎn)發(fā)至容器內(nèi)的端口。如:
$ 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 ID IMAGE COMMAND CREATED STATUS PORTS NAMES
be1364f343ae nginx "nginx -g 'daemon of…" 8 seconds ago Up 7 seconds 0.0.0.0:8000->80/tcp quirky_mclean
$ curl localhost:8000
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
...
$
其中 docker run -d -p 8000:80 nginx
命令用于在后臺(tái)(-d
選項(xiàng))啟動(dòng)一個(gè)由官方 nginx 鏡像創(chuàng)建的容器昧互,并將本地主機(jī)的 8000 端口映射到容器的 80 端口(-p 8000:80
)挽铁,即容器中 nginx 服務(wù)運(yùn)行的端口。
可以看到敞掘,當(dāng)使用 curl 命令訪問本地主機(jī)的 8000 端口時(shí)叽掘,等同于訪問了容器的 80 端口,即容器中的 nginx 服務(wù)玖雁。
而 -P
選項(xiàng)可以自動(dòng)選擇空閑的端口進(jìn)行轉(zhuǎn)發(fā)更扁,無需指定本地主機(jī)或容器的端口∏丫眨可參考以下實(shí)例:
$ 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>
...
關(guān)聯(lián)容器
容器關(guān)聯(lián)可以允許同一個(gè)主機(jī)上的多個(gè)容器相互交換數(shù)據(jù)疯潭。當(dāng)使用默認(rèn)的網(wǎng)絡(luò)模型時(shí),這些關(guān)聯(lián)的容器通過其“內(nèi)部”網(wǎng)絡(luò)傳輸數(shù)據(jù)面殖,即關(guān)聯(lián)容器間的相互交流不會(huì)暴露給本地主機(jī)竖哩。
可參考以下實(shí)例:
$ 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
命令用于在后臺(tái)啟動(dòng)一個(gè) redis 容器,并將其命名為 myredis 脊僚。同時(shí)返回該容器的 ID 到標(biāo)準(zhǔn)輸出相叁。
docker run --rm -it --link myredis:redis redis /bin/bash
命令用于啟動(dòng)另一個(gè) redis 容器遵绰,并以交互的方式訪問其 Shell (-it /bin/bash
)。--rm
選項(xiàng)表示該容器退出后將自動(dòng)刪除增淹。
容器關(guān)聯(lián)的操作則由 --link myredis:redis
選項(xiàng)實(shí)現(xiàn)椿访。表示將新容器關(guān)聯(lián)至已存在的 "myredis" 容器,并為 "myredis" 容器設(shè)置別名為 "redis" 虑润,即可以在當(dāng)前的新容器中通過別名(redis)訪問成玫。
該選項(xiàng)會(huì)在新容器的 /etc/hosts
文件中添加一條主機(jī)名為 redis
的記錄,并將其指向 "myredis" 容器的 IP 地址拳喻。所以在當(dāng)前容器的 Shell 中使用 redis-cli
命令訪問 "myredis" 中的服務(wù)時(shí)哭当,可以無需指定其 IP 地址,直接使用主機(jī)名 "redis" 即可冗澈。(redis-cli -h redis -P 6379
)
存儲(chǔ)卷
可以在使用 docker run
時(shí)通過 -v
選項(xiàng)指定存儲(chǔ)卷:
$ 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
上面的命令會(huì)將容器中的 /data
目錄變成一個(gè)存儲(chǔ)卷钦勘,該目錄下的任何文件都會(huì)被復(fù)制到卷中。
可以使用 docker inspect
命令查看該存儲(chǔ)卷在本地主機(jī)中的位置:
$ docker inspect -f {{.Mounts}} container-test
[{volume 7ae1... /var/lib/docker/volumes/7ae1.../_data /data local true }]
可以在存儲(chǔ)卷對應(yīng)于本地主機(jī)的目錄(/var/lib/docker/volumes/7ae1.../_data
)中創(chuàng)建文件亚亲,容器中對應(yīng)目錄(/data
)下則會(huì)立即出現(xiàn)同樣的文件彻采。
$ 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-file test-file2
root@CONTAINER:/#
存儲(chǔ)卷還可以通過 Dockerfile 中的 VOLUME 選項(xiàng)指定。如:VOLUME /data
共享數(shù)據(jù)
可以使用 -v HOST_DIR:CONTAINER_DIR
選項(xiàng)在本地主機(jī)和一(或多)個(gè)容器間共享數(shù)據(jù)捌归。如:
$ docker run -v /home/starky/data:/data debian ls /data
該命令會(huì)將本地主機(jī)上的 /home/starky/data
目錄掛載到容器中的 /data
目錄下肛响,所有已經(jīng)存在于 /home/starky/data
目錄下的文件在容器中也同樣能夠被訪問。
但是原本存在于容器的 /data
目錄下的文件則被隱藏惜索。
如某些配置文件可以一直存放在本地主機(jī)上终惑,并在需要時(shí)掛載到通用鏡像構(gòu)建的容器中。
可以使用 docker run
命令的 --volumes-from CONTAINER
選項(xiàng)在容器間共享數(shù)據(jù)门扇。如:
$ docker run -it -h NEWCONTAINER --volumes-from container-test debian /bin/bash
root@NEWCONTAINER:/# ls /data
test-file test-file2
root@NEWCONTAINER:/#
上面的命令創(chuàng)建了一個(gè)新的容器雹有,且通過 --volumes-from
選項(xiàng),該容器可以訪問之前的 container-test 容器中的存儲(chǔ)卷(/data
)臼寄。
數(shù)據(jù)容器
數(shù)據(jù)容器是一種特殊的容器霸奕,其唯一目的是為了方便其他容器之間共享數(shù)據(jù)。如創(chuàng)建一個(gè) PostgreSQL 的數(shù)據(jù)容器:
$ docker run --name dbdata postgres echo "Data-only container for postgres"
該命令用于從 postgres 鏡像創(chuàng)建一個(gè)容器吉拳,并初始化鏡像里定義的存儲(chǔ)卷质帅。
之后可以通過 --volumes-from
選項(xiàng)使用數(shù)據(jù)容器里的存儲(chǔ)卷:
$ docker run -d --volumes-from dbdata --name db1 postgres
參考資料
Using Docker: Developing and Deploying Software with Containers