一、關於Docker
1、什么是Docker?
(摘自Wikipedia)
Docker 是一個開放源代碼軟件,是一個開放平台,用於開發應用、交付(shipping)應用、運行應用。 Docker允許用戶將基礎設施(Infrastructure)中的應用單獨分割出來,形成更小的顆粒(容器),從而提高交付軟件的速度。[1]
Docker容器與虛擬機類似,但二者在原理上不同。容器是將操作系統層虛擬化,虛擬機則是虛擬化硬件,因此容器更具有便攜性、高效地利用服務器。 容器更多的用於表示 軟件的一個標准化單元。由於容器的標准化,因此它可以無視基礎設施(Infrastructure)的差異,部署到任何一個地方。另外,Docker也為容器提供更強的業界的隔離兼容。[2]
Docker 利用Linux核心中的資源分離機制,例如cgroups,以及Linux核心名字空間(namespaces),來創建獨立的容器(containers)。這可以在單一Linux實體下運作,避免啟動一個虛擬機造成的額外負擔[3]。Linux核心對名字空間的支持完全隔離了工作環境中應用程序的視野,包括行程樹、網絡、用戶ID與掛載文件系統,而核心的cgroup提供資源隔離,包括CPU、存儲器、block I/O與網絡。從0.9版本起,Dockers在使用抽象虛擬是經由libvirt的LXC與systemd - nspawn提供界面的基礎上,開始包括libcontainer庫做為以自己的方式開始直接使用由Linux核心提供的虛擬化的設施,
依據行業分析公司“451研究”:“Dockers是有能力打包應用程序及其虛擬容器,可以在任何Linux服務器上執行的依賴性工具,這有助於實現靈活性和便攜性,應用程序在任何地方都可以執行,無論是公用雲端服務器、私有雲端服務器、單機等。” [4]
2、為什么使用Docker?
Docker 對系統資源的利用率更高。無論是應用執行速度、內存損耗或者文件存儲速度,都要比傳統虛擬機技術更高效。因此,相比虛擬機技術,一個相同配置的主機,往往可以運行更多數量的應用。
Docker 容器應用,由於直接運行於宿主內核,無需啟動完整的操作系統,因此可以做到秒級、甚至毫秒級的啟動時間。大大的節約了開發、測試、部署的時間。
Docker 的鏡像提供了除內核外完整的運行時環境,確保了應用運行環境一致性,從而不會再出現 「這段代碼在我機器上沒問題啊」 這類問題。
Docker 可以通過定制應用鏡像來實現持續集成、持續交付、部署。開發人員可以通過 Dockerfile 來進行鏡像構建,並結合 持續集成(Continuous Integration) 系統進行集成測試,而運維人員則可以直接在生產環境中快速部署該鏡像,甚至結合 持續部署(Continuous Delivery/Deployment) 系統進行自動部署。
Dockerfile 使鏡像構建透明化,不僅僅開發團隊可以理解應用運行環境,也方便運維團隊理解應用運行所需條件,幫助更好的生產環境中部署該鏡像。
Docker 確保了執行環境的一致性,使得應用的遷移更加容易。Docker 可以在很多平台上運行,無論是物理機、虛擬機、公有雲、私有雲,甚至是筆記本,其運行結果是一致的。因此用戶可以很輕易的將在一個平台上運行的應用,遷移到另一個平台上,而不用擔心運行環境的變化導致應用無法正常運行的情況。
Docker 使用的分層存儲以及鏡像的技術,使得應用重復部分的復用更為容易,也使得應用的維護更新更加簡單,基於基礎鏡像進一步擴展鏡像也變得非常簡單。此外,Docker 團隊同各個開源項目團隊一起維護了一大批高質量的 官方鏡像,既可以直接在生產環境使用,又可以作為基礎進一步定制,大大的降低了應用服務的鏡像制作成本。
3、與傳統虛擬機的對比

二、Docker的安裝
Linux:
在未安裝過Docker的機器上,root權限執行如下命令即可一鍵安裝最新版Docker:
curl -s https://get.docker.com/ | sh
如果已經安裝過老版本Docker(且不是用這個一鍵安裝腳本安裝的),請先卸載Docker:
相關命令例如:
sudo apt purge --autoremove docker.io
如果不想使用這種方式安裝Docker,也可以使用系統自帶的包管理工具來安裝,比如在Ubuntu下:
sudo apt install docker.io
但包管理工具安裝的Docker版本一般較老。
輸入以下命令檢驗是否安裝成功:
docker info
docker version

此外,Docker 需要用戶具有 sudo 權限,為了避免每次命令都輸入sudo,可以把用戶加入 Docker 用戶組:
sudo usermod -aG docker $USER
Docker 是服務器----客戶端架構。命令行運行docker命令的時候,需要本機有 Docker 服務。如果這項服務沒有啟動,可以用下面的命令啟動。
# service 命令的用法
$ sudo service docker start
# 設置開機自動啟動
$ sudo systemctl enable docker
如果使用一鍵安裝工具安裝的docker,則docker會自動啟動。
Windows:
進入官網下載即可:
https://www.docker.com/products/docker-desktop

安裝完成后的圖形化界面:

利用Ubuntu20.04 LTS檢驗安裝結果:

三、鏡像
1、概述
2、獲取鏡像
鏡像是Docker運行容器的前提。
使用docker pull命令從網絡上下載鏡像。
命令格式:
docker pull NAME[:TAG]
如果不顯式地指定TAG,則默認選擇latest標簽,即下在倉庫中最新版本的鏡像。
示例:
從Docker Hub的 Ubuntu 倉庫下載一個最新的Ubuntu操作系統的鏡像:
docker pull ubuntu
docker pull ubuntu:14.04


也可以選在從其他注冊服務器的倉庫下載。此時需要在倉庫名稱前指定完整的倉庫注冊服務器地址:
docker pull xxx.com:port/ubuntu
3、查看鏡像信息
使用docker images 命令可以列出本地主機上已有的鏡像
命令:
docker images

圖中的字段信息:
- REPOSITORY:來自於哪個倉庫,比如ubuntu倉庫;
- TAG:鏡像的標簽信息,比如latest;
- IMAGE ID:鏡像的ID(唯一);
- CREATED:創建時間;
- SIZE:鏡像大小
使用docker tag 命令可以為本地鏡像添加新的標簽:
docker tag ubuntu:14.04 ubuntu:xiaojian

不難發現,不同標簽的鏡像的ID是完全一致的,說明它們實際上指向了同一個鏡像文件,只是別名不一樣。標簽可以理解為起到了引用或快捷方式的作用。
使用docker inspect命令可以獲取該鏡像的詳細信息:
docker inspect


docker inspect 命令返回的是一個JSON格式的下次,如果只需要查看其中一項內容,可以使用 -f 參數進行指定:
例如:獲取鏡像的Architecture信息:
命令:
docker inspect -f {{".Architecture"}} 13b66

4、搜尋鏡像
使用docker search 命令可以搜索遠端倉庫中共享的鏡像,默認搜索Docker Hub官方倉庫中的鏡像。
命令:
docker search TERM
參數如下:
- -- automated=false:僅顯示自動創建的鏡像;
- -- no-trunc=false:輸出信息不截斷顯示;
- -s, --stars=0:指定僅顯示評價為指定星級以上的鏡像;
例如,搜索帶有 mysql 關鍵字的鏡像:
返回信息中包括鏡像名字,描述,星級,是否官方創建,是否自動創建等字段信息。
默認的輸出結果將按照星級評價進行排序。
官方的鏡像說明是官方項目組創建和維護的,automated資源則允許用戶驗證鏡像的來源和內容。
5、刪除鏡像
命令:
docker rmi IMAGE [IMAGE...]
# IMAGE可以為標簽或ID
示例1:刪除 TAG 為 14.04 的鏡像:
docker rmi ubuntu

示例2:使用鏡像ID刪除鏡像:
當使用docker rmi 命令 + 鏡像ID時,會先嘗試刪除所有指向該鏡像的標簽,然后刪除該鏡像文件本身。
當有該鏡像創建的容器存在時,鏡像文件默認是無法刪除的,例如:
先利用 ubuntu 鏡像創建一個簡單的容器,輸出典中典的一句話“Hello World!”:
docker run ubuntu:14.04 echo 'Hello, World!'

使用docker ps -a 命令查看本機上存在的所有容器:
docker ps -a

此時則不允許刪除該鏡像。
若要強制刪除,可以使用 -f 參數:
在此之前如果沒有刪除該鏡像下的容器,則會導師查看本地鏡像列表時,出現一個標簽為<none>的臨時鏡像,原來被強制刪除的鏡像換成了新的ID繼續存在系統中。
6、創建鏡像
創建鏡像的方法
- 基於已有鏡像的容器創建;
- 基於本地模板導入;
- 基於Dockerfile創建;
此處介紹前兩種,第三種篇幅過長,后續將詳細介紹。
基於已有鏡像的容器創建
命令:
docker commit [OPTIONS] CONTAINER [REPOSTTORY[:TAG]]
參數:
- -a, --author="":作者信息;
- -m, --message="":提交消息;
- -p, --pause=true:提交時暫停容器運行;
示例:
首先啟動一個鏡像,並在其中進行修改,例如創建一個test文件,之后退出:
docker run -ti ubuntu:14.04 /bin/bash touch test exit

該容器ID為 d338e2f51b50。
此時該容器與原來的ubuntu:14.04鏡像相比,已經發生了改變,使用docker commit 命令提交提交為一個新的鏡像。提交時可以使用ID或名稱來指定容器:
docker commit -m "南風知我意,吹夢到西洲" -a "s1mpl3" d338e2f51b50 test
成功創建的話會返回新的鏡像的ID信息:

查看本地鏡像列表:

基於本地模板導入
比如使用OpenVZ提供的模板,
https://download.openvz.org/template/precreated/
命令:
cat ubuntu-14.04-x86_64-minamal.tar.gz | docker import -ubuntu:14.04
7、存出鏡像
命令:
docker save
示例:存出本地的ubuntu:14.04 鏡像為文件ubuntu_14.04.gz
docker save -o ubuntu_14.04.tar ubuntu:14.04

8、載入鏡像
命令:
docker load
從存出的本地文件中再導入本地鏡像庫。
示例:從ubuntu_14.04.tar 導入鏡像到本地鏡像列表
docker load --input ubuntu_14.04.tar 或 docker load < ubuntu_14.04.tar
四、容器
1、概念
容器是鏡像的一個運行實例。不同之處在於,它帶有額外的可寫文件層。
鏡像(Image)和容器(Container)的關系,就像是面向對象程序設計中的 類 和 實例 一樣,鏡像是靜態的定義,容器是鏡像運行時的實體。容器可以被創建、啟動、停止、刪除、暫停等。
容器的實質是進程,但與直接在宿主執行的進程不同,容器進程運行於屬於自己的獨立的 命名空間。因此容器可以擁有自己的 root 文件系統、自己的網絡配置、自己的進程空間,甚至自己的用戶 ID 空間。容器內的進程是運行在一個隔離的環境里,使用起來,就好像是在一個獨立於宿主的系統下操作一樣。這種特性使得容器封裝的應用比直接在宿主運行更加安全。也因為這種隔離的特性,很多人初學 Docker 時常常會混淆容器和虛擬機。
2、創建容器
(1)新建容器
命令:
docker create
示例:
docker create -it ubuntu:latest

使用該命令新建的容器處於停止狀態,使用docker start 命令啟動。
(2)新建並啟動容器
啟動容器的方式有兩種:
- 基於鏡像新建一個容器並啟動;
- 將在終止態(stopped)的容器重新啟動;
命令:
docker run # 等價於1.docker create 2.docker start
示例:以下命令輸出一個“Hello World”,之后容器自動終止:
docker run ubuntu:14.04 /bin/echo 'Hello World'

當利用 docker run 進行創建並啟動容器時,Docker在后台運行的標准操作如下:
- 檢查本地是否存在指定的鏡像,若不存在就從公有倉庫下載;
- 利用鏡像創建並啟動一個容器;
- 分配一個文件系統,並在只讀的鏡像層外面掛載一層可讀寫層;
- 從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中去;
- 從地址池配置一個IP地址給容器;
- 執行用戶指定的應用程序;
- 執行完畢后容器被終止。
以下命令啟動一個bash終端,允許用戶進行交互:
docker run -ti ubuntu:14.04 /bin/bash

其中,-t 選項允許Docker分配一個偽終端(pseudo-tty)並綁定容器的標准輸入上;
-i 選項則允許容器的標准輸入保持打開。
輸入 exit 或 Ctrl+d 退出容器,退出之后,該容器就自動處於終止狀態。(即當運行的應用退出之后,容器就沒有運行的必要了)
3、終止容器
命令:
docker stop [-t | --time[ =10 ]]
該命令首先向容器發送SIGTERM信號,等待一段時間后(默認為10s),再發送SIGKILL信號終止容器。
注:docker kill 命令會直接發送SIGKILL信號來強行終止容器。
使用docker ps -a -q 命令查看處於終止狀態的容器的ID信息:

處於終止狀態的容器,通過docker start 命令重啟:

使用docker restart 命令將一個運行態的容器終止,然后重啟:

4、進入容器
attach命令
docker attach 是Docker自帶的命令。
但當多個窗口同時attach 到同一個容器的時候,所有的窗口都會同步顯示。當某個窗口1因命令阻塞時,其他窗口也就無法執行操作了。
示例:
docker attach tender_mestorf

exec命令
exec工具可以直接在容器內運行命令。
示例:
命令:
docker exec -ti c9ce /bin/bash

5、刪除容器
命令:
docker rm [OPTIONS] CONTAINER [CONTAINER...]
參數:
- -f, --forece=false:強行終止並刪除一個運行中的容器;
- -l, --link=false:刪除容器的連接,但保留容器;
- -v, --volumes=false:刪除容器掛在的數據卷。
示例:
查看處於終止狀態的容器並刪除某容器:

6、導出容器
導出容器是指導出一個已經創建的容器到一個文件,無論此時是否處於運行狀態,都可以使用 docker export 命令。
命令:
docker export CONTAINER
示例:

7、導入容器
命令:
docker import - image_name
示例:


注:
既可以使用 docker load 命令來導入鏡像存儲文件到本地的鏡像庫,又可以使用 docker import 命令來導入一個容器的快照到本地鏡像庫。
二者的區別:
- 容器快照文件將丟棄所有的歷史記錄和元數據信息(即僅保存容器當時的快照狀態),而鏡像存儲文件將保存完整記錄,體積也更大;
- 從容器快照文件導入時可以重新指定標簽等元數據信息。
五、倉庫
1、概述
倉庫(Repository)是集中存放鏡像的地方。
容易與之混淆的概念是注冊服務器(Registry)。注冊服務器是存放倉庫的具體服務器,每個服務器上可以有多個倉庫,而每個倉庫下面有多個鏡像。可以說,倉庫被認為是一個具體的項目或目錄
倉庫又分公共倉庫和私有倉庫。
倉庫包括鏡像存儲系統和賬戶管理系統。前者通過 Docker 倉庫注冊服務(Registry)實現;而一個有實用價值的倉庫(如Docker Hub)都會有完善的賬戶管理系統。
使用者注冊成為用戶之后,可以通過Login 命令登錄到倉庫服務,命令如下:
docker login -u <username> -p <password> -e <email> <registry domain>
2、上傳鏡像
命令:
docker push IMAGES
六、Vulhub靶場搭建
1、安裝Docker-compose
命令:
pip install -U docker-compose
查看版本:
docker-compose --version

2、下載Vulhub
命令:
git clone https://github.com/vulhub/vulhub.git

3、啟動靶場環境
進入vulhub選擇要復現的漏洞,如/flask/ssti:
對靶場進行編譯:
docker-compose build
注:
該命令是可選的
docker-compose up -d運行后,會自動查找當前目錄下的配置文件。
如果配置文件中包含的環境均已經存在,則不會再次編譯;如果配置文件中包含的環境不存在,則會自動進行編譯。
所以,其實docker-compose up -d命令是包含了docker-compose build的。
如果更新了配置文件,你可以手工執行docker-compose build來重新編譯靶場環境。
運行靶場:
docker-compose up -d

4、查看Docker容器的IP
命令:
docker ps
ifconfig


5、訪問
172.17.0.1:8000即可
如果要在物理機訪問,需要對路由進行配置。
七、參考
https://vulhub.org/#/docs/install-docker-one-click/
https://yeasy.gitbook.io/docker_practice/
