一. 什麼是docker
Docker是一個開源項目,誕生於2013年初, 最初是 dotCloud 公司內部的一個業餘項目。 它基於Google公司推出的Go語言實現。項目後來加入了Linux基金會, 遵從了 Apache 2.0 協議,項目代碼在GitHub上進行維護。
Docker 自開源後受到廣泛的關注和討論, 以至於dotCloud公司後來都改名爲Docker Inc。 Redhat 已經在其RHEL6.5中集中支持Docker;Google也在其PaaS產品中廣泛應用。
Docker 項目的目標是實現輕量級的操作系統虛擬化解決方案。Docker的基礎是Linux容器(LXC) 等技術。在LXC的基礎上Docker進行了進一步的封裝,讓用戶不需要去關心容器的管理, 使得操作更爲簡便。 用戶操作Docker的容器就像操作一個快速輕量級的虛擬機一樣簡單。
docker有效地將單個操作系統管理的資源劃分到獨立地組中,以便更好地在各個獨立組直接平衡有衝突的資源使用需求。
下面的圖片比較了Docker和傳統虛擬化方式的不同之處, 可見容器是在操作系統層面上實現虛擬化, 直接複用本地主機的操作系統, 而傳統方式則是在硬件層面實現。
容器和 VM(虛擬機)的主要區別是,容器提供了基於進程的隔離,而虛擬機提供了資源的完全隔離。虛擬機可能需要一分鐘來啓動,而容器只需要一秒鐘或更短。容器使用宿主操作系統的內核,而虛擬機使用獨立的內核。
Docker 的侷限性之一是,它只能用在 64 位的操作系統上。
二. 爲什麼使用docker
作爲一種新興的虛擬化方式,Docker跟傳統的虛擬化方式相比具有衆多的優勢。
首先,秒級實現:Docker容器的啓動可以在秒級實現,這相比傳統的虛擬機方式要快得多。
其次,高效使用資源: Docker 對系統資源的利用率很高, 一臺主機上可以同時運行數千個Docker容器。容器除了運行其中應用外, 基本不消耗額外的系統資源, 使得應用的性能很高, 同時系統的開銷儘量小。
傳統虛擬機方式運行10個不同的應用就要起10個虛擬機, 而Docker只需要啓動10 個隔離的應用即可。
具體說來, Docker 在如下幾個方面具有較大的優勢:
2.1 更快速的交付和部署
對開發和運維(devop) 人員來說, 最希望的就是一次創建或配置, 可以在任意地方正常運行。開發者可以使用一個標準的鏡像來構建一套開發容器, 開發完成之後, 運維人員可以直接使用這個容器來部署代碼。Docker可以快速創建容器, 快速迭代應用程序, 並讓整個過程全程可見, 使團隊中的其他成員更容易理解應用程序是如何創建和工作的。Docker容器很輕很快!容器的啓動時間是秒級的, 大量地節約開發、測試、 部署的時間。
2.2 更高效的虛擬化
Docker 容器的運行不需要額外的hypervisor支持, 它是內核級的虛擬化, 因此可以實現更高的性能和效
率。
2.3 輕鬆的遷移和擴展
Docker 容器幾乎可以在任意的平臺上運行, 包括物理機、 虛擬機、 公有云、 私有云、 個人電腦、 服務器
等。 這種兼容性可以讓用戶把一個應用程序從一個平臺直接遷移到另外一個。
2.4 更簡單的管理
使用 Docker, 只需要小小的修改, 就可以替代以往大量的更新工作。 所有的修改都以增量的方式被分發和更新, 從而實現自動化並且高效的管理。
對比傳統虛擬機總結:
三. docker體系結構
docker使用C/S 架構,docker daemon作爲 server 端接受 client 的請求,並處理(創建、運行、分發容器),他們可以運行在一個機器上,也通過sockerts或者 RESTful API 通信。
Docker daemon 一般在宿主主機後臺運行,用戶使用 client 而直接跟 daemon 交互。Docker client以系統做 bin 命令的形式存在,用戶用 docker 命令來跟 docker daemon 交互。
3.1 Docker的內部組件
docker有三個內部組件
• docker images 鏡像
• docker registries 倉庫
• docker containers 容器
3.1.1 鏡像images
docker images就是一個只讀的模板。比如:一個image 可以包含一個ubuntu的操作系統,裏面安裝了apache或者你需要的應用程序。images可以用來創建docker containers,docker提供了一個很簡單的機制來創建images或者更新現有的images,你甚至可以直接從其他人那裏下載一個已經做好的images。
鏡像工作原理:
每個 docker 都有很多層次構成,docker使用union file systems 將這些不同的層結合到一個image中去。AUFS (AnotherUnionFS)是一種Union FS, 簡單來說就是支持將不同目錄掛載到同一個虛擬文件
系統下(unite several directories into a single virtual filesystem)的文件系統,更進一步的理解, AUFS支持爲每一個成員目錄(類似Git Branch)設定 readonly、readwrite和 whiteout-able 權限,同時 AUFS裏有一個類似分層的概念,對readonly 權限的 branch 可以邏輯上進行修改(增量地,不影響readonly部分的)。通常Union FS有兩個用途,一方面可以實現不借助LVM、RAID將多個disk 掛到同一個目錄下,另一個更常用的就是將一個 readonly 的branch 和一個 writeable 的 branch 聯合在一起,LiveCD 正是基於此方法可以允許在 OS image 不變的基礎上允許用戶在其上進行一些寫操作。Docker在AUFS上構建的 container image 也正是如此。
3.1.2 倉庫registries
倉庫是集中存放鏡像文件的場所。 有時候會把倉庫和倉庫註冊服務器(Registry) 混爲一談, 並不嚴格區分。實際上,倉庫註冊服務器上往往存放着多個倉庫, 每個倉庫中又包含了多個鏡像, 每個鏡像有不同的標籤(tag) 。
倉庫分爲公開倉庫(Public) 和私有倉庫(Private)兩種形式。
最大的公開倉庫是Docker Hub, 存放了數量龐大的鏡像供用戶下載。 國內的公開倉庫包括Docker Pool等, 可以提供大陸用戶更穩定快速的訪問。
當然, 用戶也可以在本地網絡內創建一個私有倉庫。當用戶創建了自己的鏡像之後就可以使用push命令將它上傳到公有或者私有倉庫,這樣下次在另外一臺機器上使用這個鏡像時候, 只需要從倉庫上pull下來就可以了。
*注:Docker倉庫的概念跟Git類似, 註冊服務器可以理解爲GitHub這樣的託管服務。
3.1.3 容器containers
Docker利用容器來運行應用。容器是從鏡像創建的運行實例。 它可以被啓動、 開始、 停止、刪除。 每個容器都是相互隔離的、 保證安全
的平臺。
可以把容器看做是一個簡易版的Linux環境( 包括root用戶權限、進程空間、 用戶空間和網絡空間等) 和運行在其中的應用程序。
*注:鏡像是隻讀的, 容器在啓動的時候創建一層可寫層作爲最上層。
3.1.4、容器工作原理
當我們運行 docker run -i -t ubuntu /bin/bash 命令時,docker 在後臺運行的操作如下:
- 拉取鏡像:如果本地有 ubuntu 這個 image 就從它創建容器,否則從公有倉庫下載
- 創建容器:從image 創建容器
- 掛載文件:分配一個文件系統,並在只讀的 image 層外面掛載一層可讀寫的層
- 網絡橋接:從宿主主機配置的網橋接口中橋接一個虛擬接口到容器中去
- 分配 IP:從地址池配置一個ip 地址給容器
- 執行命令:執行你指定的程序,在這裏啓動一個/bin/bash進程
- 輸入輸出: -i -t 指定標準輸入和輸出
3.2 Docker底層技術
docker 底層的2個核心技術分別是Namespaces和 Control groups
以下內容摘自 InfoQ Docker,自 1.20 版本開始 docker 已經拋開 lxc,不過下面的內容對於理解 docker還是有很大幫助。
1)pid namespace
不同用戶的進程就是通過 pid namespace 隔離開的,且不同 namespace 中可以有相同 pid。所有的LXC進程在docker 中的父進程爲 docker 進程,每個 lxc 進程具有不同的 namespace。同時由於允許嵌套,因此可以很方便的實現Docker in Docker。
2) net namespace
有了 pid namespace, 每個 namespace中的pid 能夠相互隔離,但是網絡端口還是共享 host 的端口。
網絡隔離是通過 net namespace實現的, 每個net namespace 有獨立的 network devices, IPaddresses, IP routing tables, /proc/net目錄。這樣每個container 的網絡就能隔離開來。docker默認採用veth的方式將 container 中的虛擬網卡同 host 上的一個 docker bridge: docker0 連接在一起。
3) ipc namespace
container 中進程交互還是採用linux常見的進程間交互方法(interprocess communication - IPC),包括常見的信號量、消息隊列和共享內存。然而同VM不同的是,container的進程間交互實際上還是host上具有相同 pid namespace 中的進程間交互,因此需要在IPC 資源申請時加入namespace 信息 -每個IPC 資源有一個唯一的 32 位 ID。
4) mnt namespace
類似 chroot,將一個進程放到一個特定的目錄執行。mnt namespace允許不同namespace 的進程看到的文件結構不同,這樣每個namespace中的進程所看到的文件目錄就被隔離開了。同chroot不同,每個namespace 中的 container 在/proc/mounts的信息只包含所在 namespace 的 mount point。
5) uts namespace
UTS("UNIX Time-sharing System") namespace 允許每個 container 擁有獨立的 hostname和domainname,使其在網絡上可以被視作一個獨立的節點而非Host上的一個進程。
6) user namespace
每個 container 可以有不同的 user 和 group id, 也就是說可以在 container 內部用 container內部的
用戶執行程序而非 Host 上的用戶。Control groups 主要用來隔離各個容器和宿主主機的資源利用。
四. docker安裝並運行spring boot
4.1 安裝
對於CentOS6,可以使用 EPEL庫安裝Docker,命令如下
$ sudo yum install http://mirrors.yun-idc.com/epel/6/i386/epel-release-6-8.noarch.rpm
$ sudo yum install docker-io
CentOS7系統CentOS-Extras 庫中已帶Docker,可以直接安裝:
- $ sudo yum install docker
4.2 啓動 Docker 服務
安裝之後啓動Docker 服務,並讓它隨系統啓動自動加載。
$ sudo service docker start
$ sudo chkconfig docker on
4.3 下載官方的 CentOS 鏡像到本地
必須先啓動docker才能下載:
docker pull centos
4.4 運行一個 Docker 容器:
# docker run -i -t centos /bin/bash
[root@dbf66395436d /]#
我們可以看到,CentOS 容器已經被啓動,並且我們得到了 bash 提示符。在 docker 命令中我們使用了 “-i 捕獲標準輸入輸出”和 “-t 分配一個終端或控制檯”選項。若要斷開與容器的連接,輸入 exit。
[root@cd05639b3f5c /]# cat /etc/redhat-release
CentOSLinux release 7.3.1611(Core)
[root@cd05639b3f5c /]#exit
exit
[root@localhost ~]#
4.5 利用Dockerfile來創建鏡像
我們安裝java8, 基礎鏡像是centos。新建一個目錄和一個Dockerfile
$ mkdir java8
$ cd java8
$ touch Dockerfile
Dockerfile 中每一條指令都創建鏡像的一層:
# Pull base image.
FROM centos
# 設置jdk 的環境變量。
ENV JAVA_HOME /usr/java8
# 複製/usr/java文件到鏡像中(jdk1.8.0_66 目錄和Dockerfile在同一目錄)
#ADD jdk1.8.0_66 /usr/java8
COPY jdk1.8.0_66 /usr/java8
# Define default command.
CMD ["bash"]
Dockerfile 基本的語法是
使用 # 來註釋
FROM 指令告訴Docker使用哪個鏡像作爲基礎
接着是維護者的信息
RUN 開頭的指令會在創建中運行。
Build構建,在當前目錄下也可以通過 -f Dockerfile 文件的位置:
docker build -t='java8' ./
或者$ docker build -f /path/to/a/Dockerfile .
然後查看鏡像:
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
java8 latest 2abc9f66aba0 4 minutes ago 484.9 MB
docker.io/centos latest 36540f359ca3 9 days ago 192.5 MB
運行已經構件好的鏡像:
docker run -it java8 /bin/bash
然後echo $JAVA_HOME 看看。
4.6 構建sping-boot
這個java jar 包就是turing-api-1.0-SNAPSHOT.jar 簡單http服務:
直接下載jar包:https://github.com/huangguisu/k8s.git
https://github.com/huangguisu/k8s/blob/master/docker/springboot/turing-api-1.0-SNAPSHOT.jar
Dockerfile:
#基礎鏡像:倉庫是java,標籤用java8
FROM java8
#當前鏡像的維護者和聯繫方式
MAINTAINER guisu [email protected]
#將打包好的spring程序拷貝到容器中的指定位置
ADD turing-api-1.0-SNAPSHOT.jar /opt/turing-api-1.0-SNAPSHOT.jar
#容器對外暴露8080端口
EXPOSE 8080
#容器啓動後需要執行的命令
CMD $JAVA_HOME/bin/java -jar /opt/turing-api-1.0-SNAPSHOT.jar
構建:
docker build-t="spingboot"
啓動:
本地80端口映射到容器的8081端口
sudo docker run-d -p 80:8080 spingboot
然後就可以訪問主機的80端口了。
五. 問題
1、具體錯誤:Error response from daemon: conflict: unable to delete b227a9dfe196 (must be forced) - image is being used by stopped container 7b080f1e1f17
錯誤解析:這是由於要刪除的目標鏡像中有容器存在,故無法刪除鏡像
解決辦法:先刪除鏡像中的容器,再刪除該鏡像。