1、Docker簡介
開源的應用容器引擎
打包環(huán)境和應用到一個輕量級,可移植的容器中,發(fā)布到任何 Linux 機器上,規(guī)避了軟件跨環(huán)境遷移的問題
沙箱機制,相互隔離
1.1 安裝
sudo apt install docker-ce
1.2 架構(gòu)

守護進程(daemon):后臺服務,管理一個 Docker 進程
鏡像(image):相當于一個 root 文件系統(tǒng),打包了環(huán)境和應用
容器(container):鏡像運行時的實體,可以被創(chuàng)建,啟動,暫停, 停止,刪除
倉庫(repository):保存鏡像文件
2、Docker命令
2.1 服務(守護進程)相關
- 啟動
sudo systemctl start docker
- 查看
sudo systemctl status docker
- 停止
sudo systemctl stop docker
- 重啟
sudo systemctl restart docker
2.2 鏡像相關
- 查看
sudo docker images
- 搜索
# 查看具體版本號需要去 hub.docker.com
sudo docker search ubuntu
- 拉取
# 配置阿里云加速:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
sudo vim /etc/docker/daemon.json
{
"registry-mirrors": ["加速地址"]
}
sudo systemctl daemon-reload
sudo systemctl restart docker
# 拉取鏡像
sudo docker pull ubuntu[:版本]
- 刪除
sudo docker rmi ubuntu[:版本]
2.3 容器相關
- 查看
# 要查看所有包括已停止運行的容器:加上參數(shù) -a
sudo docker ps
- 創(chuàng)建
# 通過指定鏡像版本(不指定默認 latest )創(chuàng)建容器
# -i:在容器上打開一個標準輸入接口, -t:創(chuàng)建一個偽終端,一般與 -i 連用來通過偽終端操作容器, 自定義啟動命令:如 bash,這會覆蓋鏡像定義的默認啟動命令
sudo docker run -it --name u1 ubuntu[:版本] [自定義啟動命令]
# -d:后臺創(chuàng)建并啟動容器,不會立即進入
# 如果沒有一個前臺運行的進程,容器是不會保持后臺運行的,所以不要隨便自定義啟動命令
sudo docker run -d --name u1 ubuntu[:版本] [自定義啟動命令]
- 進入
# 只能進入正在運行的容器
sudo docker exec -it u1 bash
- 啟動
sudo docker start u1
# 重啟
sudo docker restart u1
- 停止
sudo docker stop u1
- 刪除
# 不能刪除正在運行的容器
sudo docker rm u1
- 查看信息
# 查看容器定義信息
sudo docker inspect u1
# 查看容器運行日志
sudo docker logs u1
3、Docker數(shù)據(jù)卷
3.1 簡介
- 宿主機中的一個目錄(或文件),與容器內(nèi)目錄(文件)綁定后,同步它們的數(shù)據(jù)
- 可以被多個容器同時掛載,一個容器也可以掛載多個數(shù)據(jù)卷
- 數(shù)據(jù)持久化與數(shù)據(jù)交換
3.2 配置數(shù)據(jù)卷
# 創(chuàng)建啟動容器時,-v 參數(shù)設置
# 容器內(nèi)目錄必須是絕對路徑,宿主機目錄可以相對,但相對的是 /var/lib/docker/volumes/,并不是當前路徑
# 目錄不存在會自動創(chuàng)建,而文件不存在時會被當成目錄創(chuàng)建并掛載
# 可用多個 -v 掛載多個
sudo docker run ... -v 宿主目錄(文件):容器內(nèi)目錄(文件) ... --name u1 ubuntu[:版本]
3.3 配置數(shù)據(jù)卷容器
配置一個專門的容器來掛載數(shù)據(jù)卷,其他容器掛載這個容器(這時候相當于掛載同一個數(shù)據(jù)卷)來交換數(shù)據(jù)
sudo docker run ... -v 宿主目錄(文件):容器內(nèi)目錄(文件) ... --name u1 ubuntu[:版本]
# 其他容器掛載 u1
sudo docker run ... --volumes-from u1 --name u2 ubuntu[:版本]
sudo docker run ... --volumes-from u1 --name u3 ubuntu[:版本]
4、Docker應用部署
4.1 搜索并拉取鏡像
sudo docker search mysql
sudo docker pull mysql:5.7
4.2 MySQL部署
# 宿主機創(chuàng)建MySQL目錄用來存儲數(shù)據(jù)信息
mkdir mysql
cd mysql
# 端口映射 宿主機:容器
sudo docker run -d \
-p 3306:3306 \
-v $PWD/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=111 \
--name m1 mysql:5.7
4.3 Nginx部署
mkdir nginx
cd nginx
sudo docker run -d \
-p 80:80 \
-v $PWD/conf.d:/etc/nginx/conf.d \
-v $PWD/logs:/var/log/nginx \
--name n1 nginx
5、Docker鏡像制作
5.1 Docker鏡像原理

- Docker鏡像是由特殊的 文件系統(tǒng) 疊加 而成
- 最底端使用宿主機的bootfs
- 第二層是 rootfs,被稱為基礎鏡像
- 最頂層為對外鏡像名,下層鏡像稱為上層鏡像的父鏡像,下載時會把父鏡像一并下載
- 統(tǒng)一文件系統(tǒng)將不同層整合成了一個文件系統(tǒng),隱藏了多層的存在
- 鏡像是只讀的,從一個鏡像啟動容器時,會在頂層加載一個讀寫文件系統(tǒng)作為容器
5.2 方式一:容器轉(zhuǎn)鏡像
這種方式并不常用,因為打包后體積巨大不利于傳遞。
# 原容器中掛載的目錄(文件)不會被提交到鏡像
sudo docker commit 容器名(id) 鏡像名[:版本號]
# 鏡像打包
sudo docker save -o 文件名 鏡像名[:版本號]
# 鏡像還原
sudo docker load -i 文件名
5.3 方式二:Dockerfile
- 包含多條指令的文本文件
- 每條指令構(gòu)建一層,基于基礎鏡像
- 提供完全一致的環(huán)境的構(gòu)建方法
- 參照 DockerHub 上相關鏡像的寫法
- 構(gòu)建
# 最后的 . 表示當前路徑,是上下文目錄,構(gòu)建時會將路徑下所有文件發(fā)給Docker引擎供 COPY ADD 等命令調(diào)用
# 如果配置文件名不為 Dockerfile,可用 -f 指定文件名
sudo docker build -t 新鏡像名 .
5.4 Dockerfile常用關鍵字
- FROM:基于哪個基礎鏡像
- LABEL:鍵值對標簽,為鏡像添加元數(shù)據(jù)
- WORKDIR:指定運行目錄,為后續(xù)的 RUN、CMD、COPY、ADD 等命令配置目標目錄,默認 /
- COPY:將宿主機的文件拷貝到鏡像中去(原文件必須在build命令最后指定的路徑內(nèi))
- RUN:構(gòu)建鏡像時運行的shell命令,由于DockerFile每條指令都會堆疊一層鏡像,所以最好使用 \ 換行和 && 連接命令來減少體積
- CMD:容器運行時默認執(zhí)行的shell命令(多個CMD只有最后一個生效)
- VOLUME:聲明容器內(nèi)多個路徑為匿名卷,run容器時若沒有指定宿主機目錄與其關聯(lián),則自動在宿主機創(chuàng)建目錄與其關聯(lián),目的是防止容器運行時產(chǎn)生太多數(shù)據(jù),如 log
- EXPOSE:聲明容器運行時應該暴露的端口
- ENV:指定環(huán)境變量
- ADD:和COPY一樣,并且還可以解壓縮,或者從url拷貝文件到鏡像中
- USER:使用哪個用戶啟動,默認root
- ENTRYPOINT:和CMD一樣,但是可以追加(必須使用exec格式:ENTRYPOINT [./entrypoint.sh,參數(shù)1,參數(shù)2……])
- ONBUILD:當構(gòu)建一個父鏡像時,父鏡像被子鏡像繼承時,ONBUILD被觸發(fā)
6、服務編排(Docker Compose)
微服務架構(gòu)系統(tǒng)中一般包含多個微服務,每個微服務一般會部署多個實例,如果手動管理工作量就會很大:
- 要構(gòu)建或拉取鏡像
- 要創(chuàng)建多個容器
- 要管理這些容器
服務編排就是按照一定的規(guī)則批量管理容器的方法。Docker Compose 是一個編排多容器分布式部署的工具,提供命令集管理容器化應用的完整生命周期,使用步驟:
- 利用 DockerFile 定義運行環(huán)境鏡像
- 使用 docker-compose 配置文件定義組成應用的各服務
- 運行 docker-compose up 啟動應用
6.1 安裝
sudo apt install docker-compose
6.2 編排一個 Nginx+Flask+MySQL 項目
- 創(chuàng)建工作目錄
mkdir project
cd project
# 創(chuàng)建映射目錄
# Flask項目的代碼復制到myapp目錄下
mkdir -p nginx/conf.d
mkdir -p nginx/log
mkdir -p mysql/data
- 編寫配置文件 docker-compose.yml
version: '3'
services:
# 定義一個容器
mysql:
# 可以自定義容器名
container_name: myapp-mysql
image: mysql:8
volumes:
- ./mysql/data:/var/lib/mysql
environment:
MYSQL_ROOT_PASSWORD: qwerdfb
ports:
- "3306:3306"
myapp:
container_name: myapp-app
# 可以基于一份Dockerfile制作鏡像并創(chuàng)建容器,如果同時指定image會以它命名鏡像
build:
context: ./myapp
# 文件名不為Dockerfile時需要指定
# dockerfile: ./myapp/Dockerfile
# 暴露端口,并沒有與宿主機做映射,只允許被鏈接容器訪問
expose:
- "8000"
nginx:
container_name: myapp-nginx
image: nginx
volumes:
- ./nginx/conf.d:/etc/nginx/conf.d
- ./nginx/logs:/var/log/nginx
ports:
- "80:80"
- 編寫Nginx程序配置 ./nginx/conf.d/myapp.conf
server {
listen 80; # 監(jiān)聽80端口來自外部的請求
server_name _; # 如果映射了域名,可以代替_
# 為HTTP規(guī)則 / 設置轉(zhuǎn)發(fā)
location / {
proxy_pass http://myapp:8000; # 轉(zhuǎn)發(fā)到本地端口
# 重寫一些請求首部
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
- 啟動
# 如果配置文件名不為 docker-compose.yml,可用 -f 指定文件名
# -d: 后臺創(chuàng)建并啟動所有容器
sudo docker-compose up
- 查看所有容器狀態(tài)
sudo docker-compose ps
- 停止容器
# 可通過 sudo docker-compose start 再次啟動
sudo docker-compose stop
# 刪除所有停止狀態(tài)的容器
sudo docker-compose rm
# 停止并刪除容器,網(wǎng)絡
# --rmi all/local (刪除compose文件中定義的所有鏡像)/(刪除鏡像名為空的鏡像)
# -v 刪除已經(jīng)在compose文件中定義的和匿名的掛載在容器上的數(shù)據(jù)卷
sudo docker-compose down
7、Docker網(wǎng)絡模式
7.1 網(wǎng)絡模式及簡單使用
安裝完 Docker 后,系統(tǒng)自動創(chuàng)建了一個 docker0 網(wǎng)卡以及三種默認網(wǎng)絡模式

Docker 安裝完成之后,會在宿主機虛擬一個網(wǎng)卡 docker0,容器在默認情況下啟動時會從此網(wǎng)段分配一個IP地址,同時 Docker0 是每個容器的默認網(wǎng)關,這樣容器之間就能夠通過容器的 container-ip 直接通信。
但由于 Docker0 網(wǎng)卡是虛擬出來的,外部網(wǎng)絡無法直接進行訪問,只能通過端口映射來訪問容器,即 docker run 創(chuàng)建容器時候通過 -p 或 -P 參數(shù)來啟用
上面三種網(wǎng)絡模式可以在 docker run 時使用 --net 指定,它們的區(qū)別主要是:
- host 模式:直接使用宿主機網(wǎng)卡,可以為容器提供最好的網(wǎng)絡性能,但不可避免的有網(wǎng)絡資源沖突問題的(如 ip 端口號)
- none 模式:這種模式下的容器與世隔絕,只有自己的環(huán)回網(wǎng)卡lo
- bridge 模式:默認模式,容器連接到同一塊網(wǎng)卡(默認是 docker0),它們之間可以通過 ip 通信,指定
--link 容器名后就可以使用容器名通信。同時,可以使用docker network create 網(wǎng)絡名來創(chuàng)建另一個橋接網(wǎng)絡,這時ifconfig可以看到除 docker0 外多出了一個虛擬網(wǎng)卡,同時docker network ls可以看到新創(chuàng)建的模式為 bridge 的新網(wǎng)絡,之后可以 --net 指定新的橋接網(wǎng)絡來實現(xiàn)不同的網(wǎng)絡隔離
7.2 docker-compose中指定網(wǎng)絡模式
docker-compose中指定網(wǎng)絡模式主要有以下幾種場景:
- 未顯式聲明網(wǎng)絡
默認情況下,docker-compose 會為我們的應用創(chuàng)建一個網(wǎng)絡(網(wǎng)絡名稱為 docker-compose.yml 所在文件夾名稱_default),應用的每個容器都會加入該網(wǎng)絡中。這樣,容器就可被該網(wǎng)絡中的其他容器訪問,不僅如此,該容器還能以容器名作為 hostname 被其他容器訪問(相當于默認實現(xiàn)了 link)
- 自定義網(wǎng)絡
以下文件為項目創(chuàng)建了兩個 docker 網(wǎng)絡,假如文件在 myapp 目錄下,它們實際的名字分別為 myapp_front 和 myapp_back,這種情況下實現(xiàn)了 nginx 和 db 的網(wǎng)絡隔離,它們只能通過 app 通信
version: '3'
services:
nginx:
networks:
- front
app:
networks:
- front
- back
db:
networks:
- back
networks:
front:
driver: bridge
back:
- 配置默認網(wǎng)絡
其實就是設置 services 中的容器未指定 networks 時的默認網(wǎng)絡,以下文件效果和未顯示聲明網(wǎng)絡相同(使用默認網(wǎng)絡 myapp_default)
version: '3'
services:
web:
db:
networks:
default:
driver: bridge
- 使用已存在的網(wǎng)絡
如應用容器已經(jīng)啟動,需要單獨部署 nginx 服務,可以將它加入到已經(jīng)存在的應用網(wǎng)絡以獲得訪問應用時更好的網(wǎng)絡性能
version: '3'
services:
nginx:
networks:
- myapp_default
networks:
myapp_default:
external: true
8、Docker倉庫
將自己的應用制作好鏡像后,可以上傳到 Docker 倉庫保存,后面 pull 下來就直接可以使用,Docker 官方倉庫 Docker Hub 就提供了鏡像托管服務,國內(nèi)阿里云也有相同服務,此外,我們也可以自己搭建私有倉庫來管理團隊內(nèi)部使用的鏡像。
8.1 Docker Hub
首先,需要擁有一個 Docker Hub 賬戶,沒有的話先去注冊一個吧。進入 Repositories --> Create 來創(chuàng)建一個倉庫,一般一個倉庫存放一個鏡像的所有版本,倉庫命名一般為鏡像名就好。
# 操作之前需要先登錄
sudo docker login --username 用戶名
# 打標簽將鏡像歸為某一倉庫,版本默認為latest
sudo docker tag 鏡像名或id 用戶名/倉庫名[:版本]
# 上傳鏡像
sudo docker push 用戶名/倉庫名[:版本]
# 拉取鏡像
# 如果上一步成功且創(chuàng)建的是公開倉庫的話這時候應該能搜索到自己的鏡像了,當然因為網(wǎng)絡情況一般情況下是不成功的
sudo docker pull 用戶名/倉庫名[:版本]
8.2 阿里云
由于國內(nèi)網(wǎng)絡環(huán)境的種種限制,阿里云提供的Docker倉庫是一個更好的選擇,同樣要先有一個阿里云賬戶,然后去 容器鏡像服務 創(chuàng)建倉庫,地域選擇離自己近的,設置好命名空間,值得一提的是,DockerHub默認以用戶名作為命名空間,而阿里云可以創(chuàng)建三個。代碼源選擇本地倉庫,創(chuàng)建好后點擊倉庫名進入就會有操作指南。
# 同樣要先登錄先登錄,不同的是要加上阿里云站點,注意倉庫地域不同站點也不一樣
sudo docker login --username 用戶名 registry.cn-shanghai.aliyuncs.com
# 打標簽
sudo docker tag 鏡像名或id registry.cn-shanghai.aliyuncs.com/命名空間/倉庫名[:版本]
# 上傳鏡像
sudo docker push registry.cn-shanghai.aliyuncs.com/命名空間/倉庫名[:版本]
# 拉取鏡像
sudo docker pull registry.cn-shanghai.aliyuncs.com/命名空間/倉庫名[:版本]
8.3 私有倉庫
私有倉庫其實就是把倉庫的服務端部署在自己的服務器上,一般用于一個團隊內(nèi)部使用。
私有倉庫它首先也是一個鏡像,我們根據(jù)這個鏡像啟動一個容器,然后外部訪問這個容器就像前面訪問 Docker Hub 或者阿里云的倉庫服務一樣。
sudo docker pull registry
# 啟動容器,容器內(nèi)對外暴露的端口為 5000
sudo docker run -id --name registry1 -p 5000:5000 registry
# 在 daemon 配置文件內(nèi)添加一條,讓Docker信任此私有倉庫
# 如果私有倉庫部署在本機,ip為本地地址 127.0.0.1
sudo vim /etc/docker/daemon.json
{"insecure-registries":["部署私有倉庫的服務器ip:5000"]}
# 重啟docker服務
sudo systemctl restart docker
# 如果私有倉庫部署在本機,還需要重啟私有倉庫容器
sudo docker start registry1
# 接下來使用步驟就和 DockerHub,阿里云大同小異,由于是私有倉庫,也就不再需要登錄和命名空間了
# 打標簽
sudo docker tag 鏡像名或id 私有倉庫服務器ip:5000/倉庫名[:版本]
# 上傳鏡像
sudo docker push 私有倉庫服務器ip:5000/倉庫名[:版本]
# 拉取鏡像
sudo docker pull 私有倉庫服務器ip:5000/倉庫名[:版本]