本文旨在讓讀者能大體瞭解docker整個體系,而且對於docker運行機制有必定了解。如要深刻,請先深刻了解linux。html
Docker是開源的,基於Linux容器技術的引擎,統一了被隔離的應用程序訪問系統核心的API。試圖解決開發者的世紀難題在個人機器上能夠跑
。前端
前端同窗能夠視鏡像爲npm包,倉庫爲npm倉庫。這樣更方便理解。node
Docker是一種相似虛擬機技術的縮減版,因爲虛擬機啓動過程過於漫長與虛擬化以後的硬件在運行程序的時候,並不能很好的契合物理機,比較典型的例子就是移動端開發,啓動虛擬系統的時候,過程十分的漫長。linux
咱們常常開啓一個虛擬機僅僅是須要隔離一個應用,可是虛擬機建立佔用了一套完整的系統資源(guest os),存在着大材小用的問題,成本也息息相關。nginx
而Docker隨着Linux功能的更新出現了,Docker本質僅隔離應用程序,共享當前系統核心。git
下圖爲虛擬機與Docker架構對比:github
下圖爲容器虛擬機功能對比:web
這樣的話,Docker就能夠進行秒啓動,由於Docker跳過了系統初始化(kernel init),直接使用了當前系統核心。可是這個也是有弊病的,好比 虛擬機熱遷移 這個功能,Docker就作的不是很好。redis
使用Docker能夠快速的搭建配置應用環境,簡化操做,確保運行環境一致性「一次編譯 處處運行」,應用級隔離,彈性伸縮,快速拓展。docker
鏡像是一個特殊的文件系統,除了提供容器運行時所需的程序、庫、資源、配置等文件外,還包含了一些爲運行時準備的一些配置參數(如匿名卷、環境變量、用戶等)。 鏡像不包含任何動態數據,其內容在構建以後也不會被改變。
鏡像利用(union file system)提供應用運行的只讀模板,它能夠只提供一個功能,也能夠由多個鏡像疊加建立多個功能服務。
鏡像僅僅是定義隔離應用運行所須要的東西,容器則是運行這些鏡像的進程。在容器內部,提供了完整的 文件系統、網絡、進程空間等等。徹底隔離於外部環境,不會受到其餘應用的侵擾。
容器的讀寫必須使用**Volume**
,或者宿主的存儲環境,容器在重啓或者關閉以後,存在於運行容器內部的數據將會丟失,每次啓動容器,都是經過鏡像建立一個新的容器。
Docker
倉庫是集中存放鏡像文件的場所。鏡像構建完成後,能夠很容易的在當前宿主上運行,可是, 若是須要在其它服務器上使用這個鏡像,咱們就須要一個集中的存儲、分發鏡像的服務,Docker Registry
(倉庫註冊服務器)就是這樣的服務。有時候會把倉庫 (Repository)
和倉庫註冊服務器 (Registry)
混爲一談,並不嚴格區分。Docker
倉庫的概念跟 Git
相似,註冊服務器能夠理解爲 GitHub
這樣的託管服務。實際上,一個 Docker Registry
中能夠包含多個倉庫 (Repository)
,每一個倉庫能夠包含多個標籤 (Tag)
,每一個標籤對應着一個鏡像。因此說,鏡像倉庫是 Docker
用來集中存放鏡像文件的地方相似於咱們以前經常使用的代碼倉庫。
一般,一個倉庫會包含同一個軟件不一樣版本的鏡像,而標籤就經常使用於對應該軟件的各個版本 。咱們能夠經過<倉庫名>:<標籤>
的格式來指定具體是這個軟件哪一個版本的鏡像。若是不給出標籤,將以 latest
做爲默認標籤.。
倉庫又能夠分爲兩種形式:
public
(公有倉庫)private
(私有倉庫)Docker client是一個泛稱,用來向指定的Docker Engine發起請求,執行相應的容器管理操做.它既能夠是Docker命令行工具,也能夠是任何遵循了Docker API的客戶端.目前, 社區中維護着的Docker client種類很是豐富,涵蓋了包括C#(支持 Windows)、Java、Go、Ruby、JavaScript等經常使用語言,甚至還有使用Angular庫編寫的WebU格式的客戶端,足以知足大多數用戶的需求。
Docker Engine是Docker最核心的後臺進程,它負責響應來自Docker client的請求,而後將這些請求翻譯成系統調用完成容器管理操做。該進程會在後臺啓動一個API Server,負責接收由Docker client發送的請求;接收到的請求將經過Docker Engine內部的一個路由分發調度,再由具體的函數來執行請求。
##2.1 安裝Docker
本文全部環境均運行在centos7
下。
首先,移除全部老版本的Docker。
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
複製代碼
若是,是一個全新環境,那能夠跳過這一步。
爲國內一些緣由,因此按照官網安裝docker-ce大機率是裝不上的,因此,咱們須要國內鏡像來加速安裝。下面咱們經過aliyun加速安裝。
# step 1: 安裝必要的一些系統工具
sudo yum install -y yum-utils device-mapper-persistent-data lvm2
# Step 2: 添加軟件源信息
sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# Step 3: 更新並安裝 Docker-CE
sudo yum makecache fast
sudo yum -y install docker-ce
# Step 4: 開啓Docker服務
sudo service docker start
複製代碼
安裝完成以後能夠運行docker version
檢查是否安裝成功。
➜ ~ docker version
Client: Docker Engine - Community
Version: 19.03.3
API version: 1.40
Go version: go1.12.10
Git commit: a872fc2f86
Built: Tue Oct 8 00:58:10 2019
OS/Arch: linux/amd64
Experimental: false
Server: Docker Engine - Community
Engine:
Version: 19.03.3
API version: 1.40 (minimum version 1.12)
Go version: go1.12.10
Git commit: a872fc2f86
Built: Tue Oct 8 00:56:46 2019
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.2.10
GitCommit: b34a5c8af56e510852c35414db4c1f4fa6172339
runc:
Version: 1.0.0-rc8+dev
GitCommit: 3e425f80a8c931f88e6d94a8c831b9d5aa481657
docker-init:
Version: 0.18.0
GitCommit: fec3683
複製代碼
##2.2 獲取一個鏡像
如今咱們須要拉取一個nginx
鏡像,部署一個nginx
應用。
➜ ~ docker pull nginx
Using default tag: latest
latest: Pulling from library/nginx
68ced04f60ab: Pull complete
28252775b295: Pull complete
a616aa3b0bf2: Pull complete
Digest: sha256:2539d4344dd18e1df02be842ffc435f8e1f699cfc55516e2cf2cb16b7a9aea0b
Status: Downloaded newer image for nginx:latest
docker.io/library/nginx:latest
複製代碼
拉取完成以後,使用docker image ls
查看當前docker本地鏡像列表。
➜ ~ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest 6678c7c2e56c 13 hours ago 127MB
複製代碼
從新運行相同的命令docker pull nginx
會更新本地鏡像。
##2.3 運行一個Docker容器
建立一個shell
腳本文件,寫入如下內容:
docker run \
# 指定容器中止後的重啓策略:
# no:容器退出時不重啓
# on-failure:容器故障退出(返回值非零)時重啓
# always:容器退出時老是重啓
--restart=always \
# 指定docker運行在後臺,若是不加-d,在執行完這條命令以後
# 你退出命令行也會將這個docker容器退掉
-d \
# 將宿主機端口號綁定至容器端口上
-p 8080:80 \
# 指定容器暴露的端口,即修改鏡像的暴露端口
--expose=80 \
# 映射宿主目錄至
-v /wwwroot:/usr/share/nginx/html \
# 指定容器名字,後續能夠經過名字進行容器管理,links特性須要使用名字
--name=testdocker \
# 用哪一個鏡像初始化這個容器
nginx:lastest
複製代碼
咱們要明確一點docker的容器
網絡是與宿主機隔離的,除非指定容器網絡模式依託宿主機,不然是沒法直接訪問的。
如今咱們運行這個腳本,接着打開瀏覽器,輸入http://ip:8080
就能夠看到使用nginx
鏡像運行的應用程序了。
更多命令,請參考:docs.docker.com/engine/refe…
Usage: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
02.
03. -d, --detach=false 指定容器運行於前臺仍是後臺,默認爲false
04. -i, --interactive=false 打開STDIN,用於控制檯交互
05. -t, --tty=false 分配tty設備,該能夠支持終端登陸,默認爲false
06. -u, --user="" 指定容器的用戶
07. -a, --attach=[] 登陸容器(必須是以docker run -d啓動的容器)
08. -w, --workdir="" 指定容器的工做目錄
09. -c, --cpu-shares=0 設置容器CPU權重,在CPU共享場景使用
10. -e, --env=[] 指定環境變量,容器中可使用該環境變量
11. -m, --memory="" 指定容器的內存上限
12. -P, --publish-all=false 指定容器暴露的端口
13. -p, --publish=[] 指定容器暴露的端口
14. -h, --hostname="" 指定容器的主機名
15. -v, --volume=[] 給容器掛載存儲卷,掛載到容器的某個目錄
16. --volumes-from=[] 給容器掛載其餘容器上的卷,掛載到容器的某個目錄
17. --cap-add=[] 添加權限,權限清單詳見:http://linux.die.net/man/7/capabilities
18. --cap-drop=[] 刪除權限,權限清單詳見:http://linux.die.net/man/7/capabilities
19. --cidfile="" 運行容器後,在指定文件中寫入容器PID值,一種典型的監控系統用法
20. --cpuset="" 設置容器可使用哪些CPU,此參數能夠用來容器獨佔CPU
21. --device=[] 添加主機設備給容器,至關於設備直通
22. --dns=[] 指定容器的dns服務器
23. --dns-search=[] 指定容器的dns搜索域名,寫入到容器的/etc/resolv.conf文件
24. --entrypoint="" 覆蓋image的入口點
25. --env-file=[] 指定環境變量文件,文件格式爲每行一個環境變量
26. --expose=[] 指定容器暴露的端口,即修改鏡像的暴露端口
27. --link=[] 指定容器間的關聯,使用其餘容器的IP、env等信息
28. --lxc-conf=[] 指定容器的配置文件,只有在指定--exec-driver=lxc時使用
29. --name="" 指定容器名字,後續能夠經過名字進行容器管理,links特性須要使用名字
30. --net="bridge" 容器網絡設置:
31. bridge 使用docker daemon指定的網橋
32. host //容器使用主機的網絡
33. container:NAME_or_ID >//使用其餘容器的網路,共享IP和PORT等網絡資源
34. none 容器使用本身的網絡(相似--net=bridge),可是不進行配置
35. --privileged=false 指定容器是否爲特權容器,特權容器擁有全部的capabilities
36. --restart="no" 指定容器中止後的重啓策略:
37. no:容器退出時不重啓
38. on-failure:容器故障退出(返回值非零)時重啓
39. always:容器退出時老是重啓
40. --rm=false 指定容器中止後自動刪除容器(不支持以docker run -d啓動的容器)
41. --sig-proxy=true 設置由代理接受並處理信號,可是SIGCHLD、SIGSTOP和SIGKILL不能被代理
複製代碼
咱們可使用docker exec -it [docker container id] /bin/bash
來進入正在運行的容器。
而要退出容器,有兩種方式:
exit
就退出ctrl+P Q
也會退出以上兩種方式都可從容器中退出,而且保持容器在後臺運行。
##2.5 自定義一個鏡像Dockerfile
Dockerfile 分爲四部分:基礎鏡像信息、維護者信息、鏡像操做指令和容器啓動時執行指令。
這裏我使用了一份簡單的node啓動開發環境的Dockerfile。
# 1. 設置來源的基礎鏡像
FROM node:12.0
# 指定後續 RUN、CMD、ENTRYPOINT 指令的工做目錄
WORKDIR /workspace # 在上一次經過WORKDIR指定的目錄運行 RUN 後續命令
RUN npm install --registry=https://registry.npm.taobao.org # 初始化暴露 8080 8001 8800端口號
# 如下端口號 也能夠在docker run時暴露出去
EXPOSE 8080
EXPOSE 8001
EXPOSE 8800
# 默認執行的命令,若是在宿主機經過docker run -it /bin/bash進入時,如下命令不會被執行
# 徹底不被覆蓋的指令爲 ENTRYPOINT
CMD ["npm","run","dev-server"] 複製代碼
保存退出編輯,執行docker build -t nodeapp:v1.0 .
注意 最後一個.
表示當前目錄。
在運行完成以後,使用docker image ls
查看是否有已經編譯完成的鏡像便可。
這個時候,可能有些人有個提問,就是每次都須要npm install安裝文件嗎?
其實,若是你的node應用包不會變化,而你這個鏡像又是專爲此應用開發的,能夠考慮使用ADD
指令,將node_modules
追加至docker鏡像中。(現實基本不會如此處理,由於外部若是映射了數據捲進來會覆蓋目錄,此處只是爲了演示像鏡像追加文件。)
更多指令集,請查閱 :
中文版:yeasy.gitbooks.io/docker_prac…
官方英文版:docs.docker.com/engine/refe…
##2.6 多容器啓動:Docker-compose
Docker-compose 須要單獨安裝。
咱們來假設一個場景,咱們啓動了一個前端項目。須要啓動nginx
運行前臺項目,啓動一個數據庫來記錄數據,保證整個應用的完整。那麼這就是docker-compose的用武之地了。
首先要知道,docker-compose由如下兩種類型組成:
service
):一個應用容器,實際上能夠運行多個相同鏡像的實例。project
):由一組關聯的應用容器組成的一個完整業務單元。咱們回到以前建立Dockerfile
的目錄中,編寫一個docker-compose.yml
文件,配置多容器。
version: '1'
services:
web:
build: .
ports:
- "8080:80"
volumes:
- /wwwroot:/usr/share/nginx/html
redis:
image: "redis:alpine"
複製代碼
運行命令docker-compose up
以後,咱們經過docker stats
能夠看到兩個(web、redis)docker已經啓動了。
訪問http://ip:8080
就能夠看到跟以前同樣的網頁了。
更多可參考:docs.docker.com/compose/
因爲應有隔離,你沒法在外網直接訪問到宿主機上的docker容器。因此咱們須要將宿主機上的端口綁定到容器上。
2.3已經介紹瞭如何綁定端口與導出容器端口。咱們這裏瞭解一下容器互聯。
# 運行命令建立一個docker網絡
$ docker network create -d bridge my-net
# 建立兩個容器 加入my-net網絡
$ docker run -it --rm --name busybox1 --network my-net busybox sh
$ docker run -it --rm --name busybox2 --network my-net busybox sh
# 接着咱們進入busybox1
$ docker exec -it busybox1 /bin/bash
# ping 另一個容器,能夠看到他的IP信息
$ root@busybox1:ping busybox2
PING busybox2 (172.19.0.3): 56 data bytes
64 bytes from 172.19.0.3: seq=0 ttl=64 time=0.072 ms
64 bytes from 172.19.0.3: seq=1 ttl=64 time=0.118 ms
複製代碼
Docker使用Go語言編寫,而且使用了一系列Linux內核提供的特性來實現其功能。
一個能執行Docker的系統分爲兩大部分:
Docker使用的Linux核心模塊功能包括下列各項:
使用虛擬機運行Linux,而後在Linux中運行Docker Engine。在本機運行Docker client。
前端同窗關注點
爲何不能用CMD ['node','app.js']
做爲默認啓動,由於在 Node.js 的官方最佳實踐裏有寫到 "Node.js was not designed to run as PID 1 which leads to unexpected behaviour when running inside of Docker."。下圖來自 github.com/nodejs/dock… 。
這個問題涉及linux運行機制,簡單的說,就是 linux pid爲1的進程是系統守護的進程,將會接收全部孤兒進程。而且在適當的時候發送關閉信號給這些進程。
可是,docker中pid 1的進程爲node,而node並無作回收孤兒進程的事情。因此,若是你的應用跑相似爬蟲之類的應用,執行完畢以後將進程掛到pid 1 上,慢慢的容器就會BOOM。
解決方案:
1. 用`/bin/bash`啓動。
2. 在`docker run`後面追加`--init`用於初始化一個docker的進程爲pid 1。docker提供的進程能夠回收全部孤兒進程。
複製代碼
具體能夠參考這個案例:juejin.im/post/5e0002…
hujb2000.gitbooks.io/docker-flow…
《docker從入門到實踐》legacy.gitbook.com/book/yeasy/…
K8S部署方案:github.com/opsnull/fol…