Docker的使用
# 基本组成
# 镜像(image)
Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很 多容器。 就好似 Java 中的 类和对象,类就是镜像,容器就是对象!
# 容器(container)
Docker 利用容器(Container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。
它可以被启动、开始、停止、删除。每个容器都是相互隔离的,保证安全的平台。
可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等) 和运行在其中的应用程序。
容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
2
3
4
# 仓库(repository)
仓库(Repository)是集中存放镜像文件的场所。
仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓 库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。
仓库分为公开仓库(Public)和私有仓库(Private)两种形式。
最大的公开仓库是 Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。
国内的公开仓库包括阿里云 、网易云 等 12345678
2
3
4
5
- Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个 可交付的运行环境,这个打包好的运行环境就似乎 image 镜像文件。只有通过这个镜像文件才能生 成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。 同一个 image 文件,可以生成多个同时运行的容器实例。
- image 文件生成的容器实例,本身也是一个文件,称为镜像文件。
- 一个容器运行一种服务,当我们需要的时候,就可以通过 docker 客户端创建一个对应的运行实例, 也就是我们的容器
- 至于仓库,就是放了一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候从仓库中拉下来 就可以了。
# 安装
官网手册:https://docs.docker.com/engine/install/centos/ (opens new window)
# 1.安装 gcc 相关环境
yum -y install gcc
yum -y install gcc-c++
2
# 2.卸载旧版本
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
2
3
4
5
6
7
8
# 3.安装需要的软件包
# step 1: 安装必要的一些系统工具
sudo apt-get update
sudo apt-get -y install apt-transport-https ca-certificates curl software-properties-common
# step 2: 安装GPG证书
curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# Step 3: 写入软件源信息
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# Step 4: 更新并安装 Docker-CE
sudo apt-get -y update
sudo apt-get -y install docker-ce
# 注意:其他注意事项在下面的注释中
# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# apt-cache madison docker-ce
# docker-ce | 17.03.1~ce-0~ubuntu-xenial | http://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
# docker-ce | 17.03.0~ce-0~ubuntu-xenial | http://mirrors.aliyun.com/docker-ce/linux/ubuntu xenial/stable amd64 Packages
# Step 2: 安装指定版本的Docker-CE: (VERSION 例如上面的 17.03.1~ce-0~ubuntu-xenial)
# sudo apt-get -y install docker-ce=[VERSION]
# 通过经典网络、VPC网络内网安装时,用以下命令替换Step 2、Step 3中的命令
# 经典网络:
# curl -fsSL http://mirrors.aliyuncs.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyuncs.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
# VPC网络:
# curl -fsSL http://mirrors.cloud.aliyuncs.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
# sudo add-apt-repository "deb [arch=amd64] http://mirrors.cloud.aliyuncs.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
```
</code-block>
<code-block title="CentOS">
```bash
# 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
# 注意:其他注意事项在下面的注释中
# 官方软件源默认启用了最新的软件,您可以通过编辑软件源的方式获取各个版本的软件包。例如官方并没有将测试版本的软件源置为可用,你可以通过以下方式开启。同理可以开启各种测试版本等。
# vim /etc/yum.repos.d/docker-ce.repo
# 将 [docker-ce-test] 下方的 enabled=0 修改为 enabled=1
#
# 安装指定版本的Docker-CE:
# Step 1: 查找Docker-CE的版本:
# yum list docker-ce.x86_64 --showduplicates | sort -r
# Loading mirror speeds from cached hostfile
# Loaded plugins: branch, fastestmirror, langpacks
# docker-ce.x86_64 17.03.1.ce-1.el7.centos docker-ce-stable
# docker-ce.x86_64 17.03.1.ce-1.el7.centos @docker-ce-stable
# docker-ce.x86_64 17.03.0.ce-1.el7.centos docker-ce-stable
# Available Packages
# Step2 : 安装指定版本的Docker-CE: (VERSION 例如上面的 17.03.0.ce.1-1.el7.centos)
# sudo yum -y install docker-ce-[VERSION]
# 注意:在某些版本之后,docker-ce安装出现了其他依赖包,如果安装失败的话请关注错误信息。例如 docker-ce 17.03 之后,需要先安装 docker-ce-selinux。
# yum list docker-ce-selinux- --showduplicates | sort -r
# sudo yum -y install docker-ce-selinux-[VERSION]
# 通过经典网络、VPC网络内网安装时,用以下命令替换Step 2中的命令
# 经典网络:
# sudo yum-config-manager --add-repo http://mirrors.aliyuncs.com/docker-ce/linux/centos/docker-ce.repo
# VPC网络:
# sudo yum-config-manager --add-repo http://mirrors.could.aliyuncs.com/docker-ce/linux/centos/docker-ce.repo
```
</code-block>
</code-group>
### 4.启动测试
```sh
# 启动
systemctl start docker
# 测试
docker version
docker run hello-world
docker images
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# 5.卸载
systemctl stop docker
yum -y remove docker-ce docker-ce-cli containerd.io
rm -rf /var/lib/docker
2
3
# Docker 常用命令
# 帮助命令
# 显示 Docker 版本信息。
docker version
# 显示 Docker 系统信息,包括镜像和容器数。
docker info
# 帮助
docker --help
2
3
4
5
6
# 镜像命令
# docker images (列出本地主机上的镜像)
[root@mrcdh ~]# docker images
#或者
[root@mrcdh ~]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest bf756fb1ae65 4 months ago 13.3kB
# 解释
REPOSITORY 镜像的仓库源
TAG 镜像的标签
IMAGE ID 镜像的ID
CREATED 镜像创建时间
SIZE 镜像大小
# 同一个仓库源可以有多个 TAG,代表这个仓库源的不同版本,我们使用REPOSITORY:TAG 定义不同 的镜像,如果你不定义镜像的标签版本,docker将默认使用 lastest 镜像!
# 可选项
-a: 列出本地所有镜像
-q: 只显示镜像id
--digests: 显示镜像的摘要信息
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# docker image ls
docker image ls -f since=mongo:3.2 # 望看到在 mongo:3.2 之后建立的镜像
docker image ls -f before=mongo:3.2 # 望看到在 mongo:3.2 之前建立的镜像
docker image ls -f label=com.example.version=0.1 # 如果镜像构建时,定义了 LABEL ,还可以通过 LABEL 来过滤
docker image ls --digests # 查看镜像摘要
docker image ls -q # 列出镜像id
# 删除所有仓库名为 redis 的镜像
docker image rm $(docker image ls -q redis)
# 删除所有在 mongo:3.2 之前的镜像
docker image rm $(docker image ls -q -f before=mongo:3.2)
2
3
4
5
6
7
8
9
10
11
# docker search (搜索镜像)
[root@mrcdh ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL
mysql MySQL is a widely used, open-source relation… 9484 [OK]
# docker search 某个镜像的名称 对应DockerHub仓库中的镜像
# 可选项
--filter=stars=50 : 列出收藏数不小于指定值的镜像。
2
3
4
5
6
# docker pull (下载镜像)
# 下载镜像
[root@mrcdh ~]# docker pull mysql
Using default tag: latest # 不写tag,默认是latest
latest: Pulling from library/mysql
54fec2fa59d0: Already exists # 分层下载
bcc6c6145912: Already exists
951c3d959c9d: Already exists
05de4d0e206e: Already exists
319f0394ef42: Already exists
d9185034607b: Already exists
013a9c64dadc: Already exists
42f3f7d10903: Pull complete
c4a3851d9207: Pull complete
82a1cc65c182: Pull complete
a0a6b01efa55: Pull complete
bca5ce71f9ea: Pull complete
Digest:
sha256:61a2a33f4b8b4bc93b7b6b9e65e64044aaec594809f818aeffbff69a893d1944 # 签名
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实位置
# 指定版本下载
[root@mrcdh ~]# docker pull mysql:5.7
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# docker rmi (删除镜像)
docker rmi -f 镜像id # 删除单个
docker rmi -f 镜像名:tag 镜像名:tag # 删除多个
docker rmi -f $(docker images -qa) # 删除全部
2
3
# 容器命令
下载个镜像做测试 docker pull centos
# 新建容器并启动
docker run [OPTIONS] IMAGE [COMMAND][ARG...]
# 常用参数说明
--name="Name" # 给容器指定一个名字
-d # 后台方式运行容器,并返回容器的id!
-i # 以交互模式运行容器,通过和 -t 一起使用
-t # 给容器重新分配一个终端,通常和 -i 一起使用
-P # 随机端口映射(大写)
-p # 指定端口映射(小结),一般可以有四种写法
ip:hostPort:containerPort
ip::containerPort
hostPort:containerPort (常用)
containerPort
--rm # 容器退出后随之将其删除
2
3
4
5
6
7
8
9
10
11
12
13
# 测试
[root@mrcdh ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 470671670cac 3 months ago 237MB
# 使用centos进行用交互模式启动容器,在容器内执行/bin/bash命令!
[root@mrcdh ~]# docker run -it centos /bin/bash
[root@dc8f24dd06d0 /]# ls # 注意地址,已经切换到容器内部了!
bin etc lib lost+found mnt proc run srv tmp var
dev home lib64 media opt root sbin sys usr
[root@dc8f24dd06d0 /]# exit # 使用 exit 退出容器
exit
[root@mrcdh ~]#
2
3
4
5
6
7
8
9
10
11
12
13
14
# 列出所有运行的容器
# 命令
docker ps [OPTIONS]
# 常用参数说明
-a # 列出当前所有正在运行的容器 + 历史运行过的容器
-l # 显示最近创建的容器
-n=? # 显示最近n个创建的容器
-q # 静默模式,只显示容器编号。
2
3
4
5
6
7
8
# 退出容器
exit # 容器停止退出
ctrl+P+Q # 容器不停止退出
2
# 启动停止容器
docker start (容器id or 容器名) # 启动容器
docker restart (容器id or 容器名) # 重启容器
docker stop (容器id or 容器名) # 停止容器
docker kill (容器id or 容器名) # 强制停止容器
2
3
4
# 删除容器
docker rm 容器id # 删除指定容器
docker rm -f $(docker ps -a -q) # 删除所有容器
docker ps -a -q|xargs docker rm # 删除所有容器
2
3
# 查看日志
# 命令
docker logs -f -t --tail 容器id
# 例子:我们启动 centos,并编写一段脚本来测试玩玩!最后查看日志
[root@mrcdh ~]# docker run -d centos /bin/sh -c "while true;do echo mrcdh;sleep 1;done"
[root@mrcdh ~]# docker ps
CONTAINER ID IMAGE
c8530dbbe3b4 centos
# -t 显示时间戳
# -f 打印最新的日志
# --tail 数字 显示多少条!
[root@mrcdh ~]# docker logs -tf --tail 10 c8530dbbe3b4
2020-05-11T08:46:40.656901941Z mrcdh
2020-05-11T08:46:41.658765018Z mrcdh
2020-05-11T08:46:42.661015375Z mrcdh
2020-05-11T08:46:43.662865628Z mrcdh
2020-05-11T08:46:44.664571547Z mrcdh
2020-05-11T08:46:45.666718583Z mrcdh
2020-05-11T08:46:46.668556725Z mrcdh
2020-05-11T08:46:47.670424699Z mrcdh
2020-05-11T08:46:48.672324512Z mrcdh
2020-05-11T08:46:49.674092766Z mrcdh
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 查看容器中运行的进程信息,支持 ps 命令参数
# 命令
docker top 容器id
# 测试
[root@mrcdh ~]# docker top c8530dbbe3b4
UID PID PPID C STIME TTY TIME CMD
root 27437 27421 0 16:43 ? 00:00:00 /bin/sh -c ....
2
3
4
5
6
7
# 查看容器/镜像的元数据
# 命令
docker inspect 容器id
# 测试
[root@mrcdh ~]# docker inspect c8530dbbe3b4
[
{ # 完整的id,有意思啊,这里上面的容器id,就是截取的这个id前几位!
"Id": "c8530dbbe3b44a0c873f2566442df6543ed653c1319753e34b400efa05f77cf8",
"Created": "2020-05-11T08:43:45.096892382Z",
"Path": "/bin/sh",
"Args": [
"-c",
"while true;do echo mrcdh;sleep 1;done"
],
# 状态
"State": {
"Status": "running",
"Running": true,
"Paused": false,
"Restarting": false,
"OOMKilled": false,
"Dead": false,
"Pid": 27437,
"ExitCode": 0,
"Error": "",
"StartedAt": "2020-05-11T08:43:45.324474622Z",
"FinishedAt": "0001-01-01T00:00:00Z"
},
]
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 进入正在运行的容器
# 命令1
docker exec -it 容器id bashShell
# 测试1
[root@mrcdh ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c8530dbbe3b4 centos "/bin/sh -c 'while t…" 12 minutes ago Up 12 minutes happy_chaum
[root@mrcdh ~]# docker exec -it c8530dbbe3b4 /bin/bash
[root@c8530dbbe3b4 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:43 ? 00:00:00 /bin/sh -c while true;do echo mrcdh;sleep
root 751 0 0 08:56 pts/0 00:00:00 /bin/bash
root 769 1 0 08:56 ? 00:00:00 /usr/bin/coreutils -- coreutils-prog-shebang=s
root 770 751 0 08:56 pts/0 00:00:00 ps -ef
# 命令2
docker attach 容器id
# 测试2
[root@mrcdh ~]# docker exec -it c8530dbbe3b4 /bin/bash
[root@c8530dbbe3b4 /]# ps -ef
UID PID PPID C STIME TTY TIME CMD
root 1 0 0 08:43 ? 00:00:00 /bin/sh -c while true;do echo mrcdh;sleep
root 856 0 0 08:57 pts/0 00:00:00 /bin/bash
root 874 1 0 08:57 ? 00:00:00 /usr/bin/coreutils -- coreutils-prog-shebang=s
root 875 856 0 08:57 pts/0 00:00:00 ps -ef
# 区别
# exec 是在容器中打开新的终端,并且可以启动新的进程
# attach 直接进入容器启动命令的终端,不会启动新的进程
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 从容器内拷贝文件到主机上
# 命令
docker cp 容器id:容器内路径 目的主机路径
# 测试
# 容器内执行,创建一个文件测试
[root@c8530dbbe3b4 /]# cd /home
[root@c8530dbbe3b4 home]# touch f1
[root@c8530dbbe3b4 home]# ls
f1
[root@c8530dbbe3b4 home]# exit exit
# linux复制查看,是否复制成功
[root@mrcdh ~]# docker cp c8530dbbe3b4:/home/f1 /home
[root@mrcdh ~]# cd /home
[root@mrcdh home]# ls
f1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 常用命令
attach
# 当前 shell 下attach
连接指定运行镜像build
# 通过 Dockerfile 定 制镜像commit
# 提交当前容器为新的镜像cp
#从容器中拷贝指定文件或者目录到宿主机中create
# 创建一个新的容器,同 run,但不启动容器diff
# 查看 docker 容器变化events
# 从 docker 服务获取容 器实时事件exec
# 在已存在的容器上运行命令export
# 导出容器的内 容流作为一个 tar 归档文件[对应 import ]history
# 展示一个镜像形成历史docker history 镜像名
images
# 列出系统当前镜像import
# 从 tar 包中的内容创建一个新的文件系统映像[对应 export]info
# 显示系统相关信息inspect
# 查看容器详细信息kill
# kill 指定 docker 容器load
# 从一个 tar 包中加载一 个镜像[对应 save]login
# 注册或者登陆一个 docker 源服务器logout
# 从当前 Docker registry 退出logs
# 输出当前容器日志信息port
# 查看映射端口对应的容器内部源端口pause
# 暂停容器ps
# 列出容器列表pull
# 从 docker 镜像源服务器拉取指定镜像或者库镜像push
# 推送指定镜像或者库镜像至 docker 源服务器restart
# 重启运行的容器rm
# 移除一个或者多个容器rmi
# 移除一个或多个镜像[无容器使用该 镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]run
# 创建一个新的容器并运行 一个命令save
# 保存一个镜像为一个 tar 包[对应 load]search
# 在 docker hub 中搜 索镜像start
# 启动容器stop
# 停止容器stats
# 查看容器的 cpu 内存和网络状态docker stats 容器id
tag
# 给源中镜像打标签top
# 查看容器中运行的进程信息unpause
# 取消暂停容器version
# 查看 docker 版本号wait
# 截取容 器停止时的退出状态值
# 查看镜像、容器、数据卷所占用的空间
docker system df
2
# 镜像(Commit)
docker commit 从容器创建一个新的镜像。
docker commit 提交容器副本使之成为一个新的镜像!
# 语法
docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]
2
3
测试
# 1、从Docker Hub 下载tomcat镜像到本地并运行 -it 交互终端 -p 端口映射
docker run -it -p 8080 :8080 tomcat
# 注意:坑爹:docker启动官方tomcat镜像的容器,发现 404 是因为使用了加速器,而加速器里的tomcat的webapps下没有root等文件!
# 下载tomcat官方镜像,就是这个镜像(阿里云里的tomcat的webapps下没有任何文件)
# 进入tomcat查看cd到webapps下发现全部空的,反而有个webapps.dist里有对应文件,cp -r到webapps下!
root@aba865b53114:/usr/local/tomcat# cp -r webapps.dist/* webapps
# 2、删除上一步镜像产生的tomcat容器的文档
docker ps # 查看容器id
docker exec -it 容器id /bin/bash
/usr/local/tomcat # ce webapps/
/usr/local/tomcat/webapps # ls -l # 查看是否存在 docs文件夹
/usr/local/tomcat/webapps # curl localhost:8080/docs/ # 可以看到 docs 返回的内容
/usr/local/tomcat/webapps # rm -rf docs # 删除它
/usr/local/tomcat/webapps # curl localhost:8080/docs/ # 再次访问返回 404
# 3、当前运行的tomcat实例就是一个没有docs的容器,我们使用它为模板commit一个没有docs的tomcat新镜像, tomcat02
docker ps -l # 查看容器的id
# 注意:commit的时候,容器的名字不能有大写,否则报错:invalid reference format
docker commit -a="kuangshen" -m="no tomcat docs" 1e98a2f815b0 tomcat02:1.1
sha256:cdccd4674f93ad34bf73d9db577a20f027a6d03fd1944dc0e628ee4bf17ec748
[root@kuangshen /]# docker images # 查看,我们自己提交的镜像已经OK了!
REPOSITORY TAG IMAGE ID CREATED
SIZE
tomcat02 1 .1 cdccd4674f93 About a minute
ago 649MB
redis latest f9b990972689 9 days ago
104MB
tomcat latest 927899a31456 2 weeks ago
647MB
centos latest 470671670cac 3 months ago
237MB
# 4、这个时候,我们的镜像都是可以使用的,大家可以启动原来的tomcat,和我们新的tomcat02来 测试看看!
[root@kuangshen ~]# docker run -it -p 8080:8080 tomcat02:1.1
# 如果你想要保存你当前的状态,可以通过commit,来提交镜像,方便使用,类似于 VM 中的快照!
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# 使用数据卷
# 挂载
# 命令
docker run -it -v 宿主机绝对路径目录:容器内目录 镜像名
# 测试
[root@kuangshen ~]# docker run -it -v /home/ceshi:/home centos /bin/bash
2
3
4
5
# 自定义网络
# 查看所有网络
[root@kuangshen ~]# docker network ls
NETWORK ID NAME DRIVER SCOPE
4eb2182ac4b2 bridge bridge local
ae2b6209c2ab host host local
c037f7ec7e57 none null local
2
3
4
5
# 网络模式
网络模式 | 配置 | 说明 |
---|---|---|
bridge | --net=bridge | 默认值,在 Docker 网桥 docker0 上为容器创建新的网络栈 |
none | --net=none | 不配置网络,用户可以稍后进入容器自行配置 |
container | --net=container:name/id | 容器和另外一个容器共享 Network namespace。k8s 中的 pod 就是多个容器共享一个 Network namespace |
host | --net=host | 容器和宿主机共享 Network namespace |
用户自定义 | --net=自定义网络 | 用户自己使用 network 相关命令定义网络,创建容器的时候可以指定为自己定义的网络 |
# 查看一个具体的网络的详细信息
# 命令
[root@kuangshen ~]# docker network inspect 4eb2182ac4b2
2
# Dockerfile
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
2
# FROM 指定基础镜像
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 nginx 镜像的容器,再进行修改一样,基础镜像是必须指定的。而FROM 就是指定基础镜像,因此一个 Dockerfile 中 FROM 是必备的指令,并且必须是第一条指令。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 scratch
。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
FROM scratch
...
2
如果你以 scratch 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,比如 swarm
、 coreos/etcd
。对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 FROM scratch
会让镜像体积更加小巧。使用 Go
语言 开发的应用很多会使用这种方式来制作镜像,这也是为什么有人认为 Go
是特别适合容器微服务架构的语言的原因之一。
# RUN 执行命令
# COPY
格式:
COPY [--chown=<user>:<group>] <源路径>... <目标路径>
COPY [--chown=<user>:<group>] ["<源路径1>",... "<目标路径>"]
# 复制上下文(context)目录下的 package.json 到 /app/ 目录下
COPY ./package.json /app/
# 通配符
COPY hom* /mydir/
COPY hom*.txt /mydir/
# 修改所属用户/组
COPY --chown=55:mygroup files* /mydir/
COPY --chown=bin files* /mydir/
COPY --chown=1 files* /mydir/
COPY --chown=10:11 files* /mydir/
2
3
4
5
6
7
8
9
10
# ADD
ADD
指令和 COPY
的格式和性质基本一致。但是在 COPY
基础上增加了一些功能。
比如 <源路径>
可以是一个 URL
,这种情况下,Docke
r 引擎会试图去下载这个链接的文件放到 <目标路径>
去。下载后的文件权限自动设置为 600
,如果这并不是想要的权限,那么还需要增加额外的一层 RUN
进行权限调整,另外,如果下载的是个压缩包,需要解压缩,也一样还需要额外的一层 RUN
指令进行解压缩。所以不如直接使用 RUN
指令,然后使用 wget
或者 curl
工具下载,处理权限、解压缩、然后清理无用文件更合理。因此,这个功能其实并不实用,而且不推荐使用。
如果 <源路径>
为一个 tar
压缩文件的话,压缩格式为 gzip
, bzip2
以及
xz
的情况下, ADD
指令将会自动解压缩这个压缩文件到 <目标路径>
去。
在 COPY
和 ADD
指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 COPY
指令,仅在需要自动解压缩的场合使用 ADD
。
# CMD 容器启动命令
CMD
指令的格式和 RUN
相似,也是两种格式:
shell
格式:CMD <命令>
exec
格式:CMD ["可执行文件", "参数1", "参数2"...]
- 参数列表格式:
CMD ["参数1", "参数2"...]
。在指定了ENTRYPOINT
指令后,用CMD
指定具体的参数。
# shell 格式
CMD echo $HOME
# 实际执行中会变为
CMD [ "sh", "-c", "echo $HOME" ]
2
3
4
在指令格式上,一般推荐使用 exec
格式,这类格式在解析时会被解析为 JSON
数组,因此一定要使用双引号 "
,而不要使用单引号。
# ENTRYPOINT 入口点
ENTRYPOINT
的格式和 RUN
指令格式一样,分为 exec
格式和 shell
格式。
当指定了 ENTRYPOINT
后, CMD
的含义就发生了改变,不再是直接的运行其命令,而是将 CMD
的内容作为参数传给 ENTRYPOINT
指令,换句话说实际执行时,将变为:
<ENTRYPOINT> "<CMD>"
# 之前
docker run myip curl -s http://ip.cn -i
# 使用后
docker run myip -i
ENTRYPOINT ["curl", "-s", "http://ip.cn"]
2
3
4
5
6
7
# ENV 设置环境变量
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...
ENV NODE_VERSION 7.2.0
RUN curl -SLO "https://nodejs.org/dist/v$NODE_VERSION/node-v$NOD
E_VERSION-linux-x64.tar.xz"
2
3
# ARG 构建参数
格式: ARG <参数名>[=<默认值>]
构建参数和 ENV
的效果一样,都是设置环境变量。所不同的是, ARG
所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。
Dockerfile
中的 ARG
指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 docker build
中用 --build-arg <参数名>=<值>
来覆盖。
# VOLUME 定义匿名卷
VOLUME ["<路径1>", "<路径2>"...]
VOLUME <路径>
# 这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。
VOLUME /data
# 当然,运行时可以覆盖这个挂载设置,在这行命令中,就使用了 mydata 这个命名卷挂到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置。
docker run -d -v mydata:/data xxxx
2
3
4
# EXPOSE 声明端口
格式为 EXPOSE <端口1> [<端口2>...]
要将 EXPOSE
和在运行时使用 -p <宿主端口>:<容器端口>
区分开来。 -p
是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而 EXPOSE
仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射
# WORKDIR 指定工作目录
格式为 WORKDIR <工作目录路径>
# USER 指定当前用户
格式: USER <用户名>[:<用户组>]
USER
指令和 WORKDIR
相似,都是改变环境状态并影响以后的层。 WORKDIR
是改变工作目录, USER
则是改变之后层的执行 RUN
, CMD
以及 ENTRYPOINT
这类命令的身份
# HEALTHCHECK 健康检查
HEALTHCHECK [选项] CMD <命令>
:设置检查容器健康状况的命令HEALTHCHECK NONE
:如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令
HEALTHCHECK
支持下列选项:
--interval=<间隔>
:两次健康检查的间隔,默认为 30 秒;--timeout=<时长>
:健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒;--retries=<次数>
:当连续失败指定次数后,则将容器状态视为unhealthy
,默认 3 次。
在 HEALTHCHECK [选项] CMD
后面的命令,格式和 ENTRYPOINT
一样,分为 shell
格式,和 exec
格式。命令的返回值决定了该次健康检查的成功与否: 0 :成功
; 1 :失败
; 2 :保留
,不要使用这个值
和 CMD
, ENTRYPOINT
一样, HEALTHCHECK
只可以出现一次,如果写了多个,只有最后一个生效。
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib
/apt/lists/*
HEALTHCHECK --interval=5s --timeout=3s \
CMD curl -fs http://localhost/ || exit 1
2
3
4
5
这里我们设置了每 5 秒检查一次(这里为了试验所以间隔非常短,实际应该相对较长),如果健康检查命令超过 3 秒没响应就视为失败,并且使用 curl -fs http://localhost/ || exit 1
作为健康检查命令
# ONBUILD 为他人做嫁衣裳
格式: ONBUILD <其它指令>
ONBUILD
是一个特殊的指令,它后面跟的是其它指令,比如 RUN
, COPY
等,而这些指令,在当前镜像构建时并不会被执行。只有当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行。
Dockerfile
中的其它指令都是为了定制当前镜像而准备的,唯有 ONBUILD
是为了帮助别人定制自己而准备的
# 基础镜像,命名为my-node,ONBUILD不会执行
FROM node:slim
RUN mkdir /app
WORKDIR /app
ONBUILD COPY ./package.json /app
ONBUILD RUN [ "npm", "install" ]
ONBUILD COPY . /app/
CMD [ "npm", "start" ]
# 原始镜像
FROM my-node # 只需这一行,上方三行ONBUILD就会执行
2
3
4
5
6
7
8
9
10
11
# 构建镜像
在 Dockerfile 文件所在目录执行:
# 默认使用Dockerfile文件
docker build -t nginx:v3 .
# 指定Dockerfile文件
docker build -t nginx:v3 . -f Dockerfile.build
2
3
4
# 使用git仓库构建
docker build https://github.com/twang2218/gitlab-ce-zh.git#:8.14
这行命令指定了构建所需的 Git repo,并且指定默认的 master 分支,构建目录为 /8.14/ ,然后 Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建。
# 用给定tar压缩包构建
docker build http://server/context.tar.gz
如果所给出的 URL 不是个 Git repo,而是个 tar 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。
// Make sure to add code blocks to your code group