1. 基本概念
1.1. 主要組件
Docker有三個主要組件:
- 鏡像是Docker的構建組件,而且是定義應用程序操作系統的只讀模板
- 容器是Docker的運行組件,它是從鏡像創建的。容器可以運行、啟動、停止、移動和刪除
- 鏡像在注冊中心中存儲、共享和管理,並且是Docker的分發組件。Docker Store 是一個公開可用的注冊中心。https://hub.docker.com/
為了上這三個組件協同工作,Docker守護進程(或者叫Docker容器)運行在一個主機上,並負責構建、運行和分發Docker容器。此外,客戶端是一個Docker二進制文件,它接受來自用戶的命令並與引擎來回通信。
1.2. Docker Image
Docker鏡像是一個可以從其中啟動Docker容器的只讀模板。每個鏡像又一系列的層組成。(PS:現在發現,把“Image”翻譯成專業術語“鏡像”的話這里就感覺跟別扭。原文是“Each image consists of a series of layers”,如果按“Image”本來的意思“圖像”去理解就很好理解了,對PhotoShop有點兒了解的人都能理解這句話,“圖像由一系列圖層組成”,真是太形象了。)
Docker如此輕量級的原因之一就是這些層(圖層)。當你修改鏡像(例如,將應用程序更新到新版本)時,將構建一個新的層。因此,只添加或更新該層,而不是像使用虛擬機那樣替換整個映像或完全重建。現在,您不需要發布整個新圖像,只需要更新即可,從而使分發Docker鏡像更快、更簡單。(PS:越發覺得此處用“圖像”更好理解,加個新圖層或者在原先的圖層上做修改即可)
每個鏡像都是從一個基本鏡像開始的。你也可以使用自己的鏡像作為新鏡像的基礎。如果你有一個基本的Apache鏡像,那么你可以使用它作為所有web應用程序鏡像的基礎。
Docker使用一組稱為指令的簡單描述性步驟來構建鏡像。每條指令在鏡像中創建一個新層。
- 運行一條命令
- 添加一個文件或目錄
- 創建一個環境變量
- 當啟動一個容器時運行一個進程
這些指令被存儲在一個叫“Dockerfile”的文件中。當你請求構建鏡像時,Docker讀取這個Dockerfile文件,然后執行這些指令,並返回最終的鏡像。
(PS:關於鏡像,記住下面兩句話
- Each image consists of a series of layers.
- Each instruction creates a new layer in our image.
)
1.3. Docker Container
容器由操作系統、用戶添加的文件和元數據組成。正如我們所看到的,每個容器都是由一個鏡像構建的。鏡像告訴Docker容器持有什么、啟動容器時運行什么進程以及各種其他配置數據。鏡像是只讀的。當Docker從映像運行容器時,它會在鏡像之上添加一個讀寫層,然后你的應用程序就可以在其中運行了。
1.4. Docker Engine
Docker Host是在安裝Docker的時候創建的。一旦Docker Host被創建了,那么你就可以管理鏡像和容器了。例如,你可以下載鏡像、啟動或停止容器。
1.5. Docker Client
Docker Client與Docker Host通信,進而你就可以操作鏡像和容器了。
2. 構建一個鏡像
2.1. Dockerfile
Docker通過從Dockerfile文件中讀取指令來構建鏡像。Dockerfile是一個文本文檔,它包含用戶可以在命令行上調用的所有命令來組裝一個鏡像。docker image build命令會使用這個文件,並執行其中的所有命令。
build命令還傳遞一個在創建映像期間使用的上下文。這個上下文可以是本地文件系統上的路徑,也可以是Git存儲庫的URL。
關於Dockerfile中可以使用的命令,詳見 https://docs.docker.com/engine/reference/builder/
下面是一些常用的命令:
2.2. 創建你的第一個鏡像
首先,創建一個目錄hellodocker,然后在此目錄下創建一個名為Dockerfile的文本文件,編輯該文件,內容如下:
從以上兩行命令我們可以看到,該鏡像是以ubuntu作為基礎基礎,CMD命令定義了需要運行的命令。它提供了一個不同的入口/bin/echo,並給出了一個參數“hello world”。
2.3. 用Java創建你的第一個鏡像
補充:OpenJDK是Java平台標准版的一個開源實現,是Docker官方提供的鏡像
首先,讓我們創建一個java工程,然后打個jar包,接着創建並編輯Dockerfile
使用docker image build構建鏡像
使用docker container run啟動容器
其實,跟我們平常那一套沒多大區別,不過是把打好的jar包做成鏡像而已
2.4. 使用Docker Maven Plugin構建鏡像
利用Docker Maven Plugin插件我們可以使用Maven來管理Docker鏡像和容器。下面是一些預定義的目標:
詳見 https://github.com/fabric8io/docker-maven-plugin
補充:Maven中的生命周期、階段、目標
- 生命周期有三套:clean、default、site
- 生命周期由多個階段組成的,比如default生命周期的階段包括:clean、validate、compile、
- 每個階段由多個目標組成,也就是說目標才是定義具體行為的
- 插件是目標的具體實現
稍微留一下IDEA里面的Maven區域就不難理解了
言歸正傳,利用docker-maven-plugin來構建鏡像的方式有很多,比如,可以配置插件或屬性文件,還可以結合Dockerfile,都在這里:
https://github.com/fabric8io/docker-maven-plugin/tree/master/samples
此處,我們演示用屬性文件的方式,首先,定義一段profile配置,比如這樣:
1 <?xml version="1.0" encoding="UTF-8"?>
2 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4 <modelVersion>4.0.0</modelVersion>
5 <parent>
6 <groupId>org.springframework.boot</groupId>
7 <artifactId>spring-boot-starter-parent</artifactId>
8 <version>2.1.4.RELEASE</version>
9 <relativePath/> <!-- lookup parent from repository -->
10 </parent>
11 <groupId>com.cjs.example</groupId>
12 <artifactId>hello-demo</artifactId>
13 <version>0.0.1-SNAPSHOT</version>
14 <name>hello-demo</name>
15 <description>Demo project for Spring Boot</description>
16
17 <properties>
18 <java.version>1.8</java.version>
19 </properties>
20
21 <dependencies>
22 <dependency>
23 <groupId>org.springframework.boot</groupId>
24 <artifactId>spring-boot-starter-web</artifactId>
25 </dependency>
26
27 <dependency>
28 <groupId>org.springframework.boot</groupId>
29 <artifactId>spring-boot-starter-test</artifactId>
30 <scope>test</scope>
31 </dependency>
32 </dependencies>
33
34 <build>
35 <plugins>
36 <plugin>
37 <groupId>org.springframework.boot</groupId>
38 <artifactId>spring-boot-maven-plugin</artifactId>
39 </plugin>
40 </plugins>
41 </build>
42
43 <profiles>
44 <profile>
45 <id>docker</id>
46 <build>
47 <plugins>
48 <plugin>
49 <groupId>io.fabric8</groupId>
50 <artifactId>docker-maven-plugin</artifactId>
51 <version>0.30.0</version>
52 <configuration>
53 <images>
54 <image>
55 <name>hellodemo</name>
56 <build>
57 <from>openjdk:latest</from>
58 <assembly>
59 <descriptorRef>artifact</descriptorRef>
60 </assembly>
61 <cmd>java -jar maven/${project.name}-${project.version}.jar</cmd>
62 </build>
63 </image>
64 </images>
65 </configuration>
66 <executions>
67 <execution>
68 <id>docker:build</id>
69 <phase>package</phase>
70 <goals>
71 <goal>build</goal>
72 </goals>
73 </execution>
74 <execution>
75 <id>docker:start</id>
76 <phase>install</phase>
77 <goals>
78 <goal>run</goal>
79 <goal>logs</goal>
80 </goals>
81 </execution>
82 </executions>
83 </plugin>
84 </plugins>
85 </build>
86 </profile>
87 </profiles>
88 </project>
然后,在構建的時候指定使用docker這個profile即可
1 mvn clean package -Pdocker
2.5. Dockerfile命令(番外篇)
CMD 與 ENTRYPOINT 的區別
容器默認的入口點是 /bin/sh,這是默認的shell。
當你運行 docker container run -it ubuntu 的時候,啟動的是默認shell。
ENTRYPOINT 允許你覆蓋默認的入口點。例如:
這里默認的入口點被換成了/bin/cat
ADD 與 COPY 的區別
ADD有COPY所有的能力,而且還有一些額外的特性:
- 允許在鏡像中自動提取tar文件
- 允許從遠程URL下載文件
3. 運行一個Docker容器
3.1. 交互
以交互模式運行WildFly容器,如下:
1 docker container run -it jboss/wildfly
默認情況下,Docker在前台運行。-i允許與STDIN交互,-t將TTY附加到進程上。它們可以一起用作 -it
按Ctrl+C停止容器
3.2. 分離容器
1 docker container run -d jboss/wildfly
用-d選項代替-it,這樣容器就以分離模式運行
(PS:-it前台運行,-d后台運行)
3.3. 用默認端口
如果你想要容器接受輸入連接,則需要在調用docker run時提供特殊選項。
1 $ docker container ls
2 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3 93712e8e5233 jboss/wildfly "/opt/jboss/wildfly/…" 4 minutes ago Up 4 minutes 8080/tcp serene_margulis
4 02aa2ed22725 ubuntu "/bin/bash" 2 hours ago Up 2 hours frosty_bhabha
重啟容器
1 docker container stop `docker container ps | grep wildfly | awk '{print $1}'`
2 docker container run -d -P --name wildfly jboss/wildfly
-P選項將鏡像中的任何公開端口映射到Docker主機上的隨機端口。--name選項給這個容器起個名字。
1 $ docker container ls
2 CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3 3f2babcc1df7 jboss/wildfly "/opt/jboss/wildfly/…" 47 seconds ago Up 47 seconds 0.0.0.0:32768->8080/tcp wildfly
3.4. 用指定端口
1 docker container stop wildfly
2 docker container rm wildfly
或者你還可以用 docker container rm -f wildfly 來停止並刪除容器
1 docker container run -d -p 8080:8080 --name wildfly jboss/wildfly
格式是: -p hostPort:containerPort
此選項將主機上的端口映射到容器中的端口。這樣就使得我們可以通過主機上的特定的端口來訪問容器。
現在我們訪問http://localhost:8080/跟剛才http://localhost:32768/是一樣的
3.5. 停止容器
1 # 按id或name停止指定的容器
2 docker container stop <CONTAINER ID>
3 docker container stop <NAME>
4
5 # 停止所有容器
6 docker container stop $(docker container ps -q)
7
8 # 停止已經退出的容器
9 docker container ps -a -f "exited=-1"
3.6. 刪除容器
1 # 按id或name刪除指定的容器
2 docker container rm <CONTAINER ID>
3 docker container rm <NAME>
4
5 # 用正則表達式刪除匹配到的容器
6 docker container ps -a | grep wildfly | awk '{print $1}' | xargs docker container rm
7
8 # 刪除所有容器
9 docker container rm $(docker container ps -aq)
3.7. 查看端口映射
1 docker container port <CONTAINER ID> or <NAME>
4. 參考
https://github.com/docker/labs/tree/master/developer-tools/java/
https://github.com/fabric8io/docker-maven-plugin