# docker 进程的启动关闭
systemctl start|restart|stop docker
# 显示docker的版本信息
docker version
# 显示docker的系统信息,包括镜像和容器数量
docker info
# 万能命令
docker 命令 --help
帮助文档地址:https://docs.docker.com/engine/reference
查看所有本地主机上的镜像
[root@VM-4-17-centos ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
hello-world latest feb5d9fea6a5 5 months ago 13.3kB
# 解释
REPOSITORY 镜像的仓库源
TAG 镜像的标签
IMAGE ID 镜像的ID
CREATED 镜像的创建时间
SIZE 镜像的大小
# 可选项
-a, --all 列出所有镜像
-q, --quiet 只显示镜像的id
搜索镜像
[root@VM-4-17-centos ~]# docker search mysql
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 12265 [OK]
mariadb MariaDB Server is a high performing open sou… 4716 [OK]
# 可选项
-f, --filter 过滤
如:
[root@VM-4-17-centos ~]# docker search mysql -f=STARS=5000
NAME DESCRIPTION STARS OFFICIAL AUTOMATED
mysql MySQL is a widely used, open-source relation… 12265 [OK]
下载镜像
docker Docker官方镜像仓库
[root@VM-4-17-centos ~]# docker pull mysql
Using default tag: latest # 如果不写tag,默认就是latest
latest: Pulling from library/mysql
72a69066d2fe: Pull complete # 分层下载,docker image的核心 联合文件系统
93619dbc5b36: Pull complete
99da31dd6142: Pull complete
......
Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709 # 签名,防伪
Status: Downloaded newer image for mysql:latest
docker.io/library/mysql:latest # 真实地址 即docker pull mysql <==> docker pull docker.io/libray/mysql:latest
# 指定tag
[root@VM-4-17-centos ~]# docker pull mysql:5.7.37
5.7.37: Pulling from library/mysql
15115158dd02: Pull complete
... # 注意:当前面镜像中有这些文件中的某个,不会存在冲突,这里会显示Already exists
Digest: sha256:66d52e6baa8093820c09fec56992a5ee734f17e9fad8ef5ffc31597b231bd048
Status: Downloaded newer image for mysql:5.7.37
docker.io/library/mysql:5.7.37
# 注意:指定tag时需确定这个tag描述的镜像存在
** 删除镜像**
# 删除指定的容器
[root@VM-4-17-centos ~]# docker rmi -f 3218b38490ce
Untagged: mysql:latest
Untagged: mysql@sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709
Deleted: sha256:3218b38490cec8d31976a40b92e09d61377359eab878db49f025e5d464367f3b
...
# 删除指定的镜像
docker rmi -f 镜像ID | 镜像:Tag
# 删除多个镜像
docker rmi -f 镜像ID1 镜像ID2 镜像ID3
# 删除全部镜像。$(docker images -aq) 查询出所有镜像的id
docker rmi -f $(docker images -aq)
说明: 我们有了镜像才可以创建容器。这里我们下载一个centos镜像用来测试
[root@VM-4-17-centos ~]# docker pull centos:7
** 新建一个容器并启动**
# 完整语法: docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# Options参数说明
--name="name" 给容器指定一个容器名字。或者 --name custom_name
-d 使用后台方式运行
-it 使用交互方式运行(-i),并进入容器查看内容(-t)
-p 指定容器的端口 -p 8080:8080
-p ip:主机端口:容器端口
-p 主机端口:容器端口 (常用)
-p 容器端口
容器端口
-P 随机指定端口
# COMMAND
/bin/bash:在container中启动一个bash shell
# 启动并进入容器
[root@VM-4-17-centos ~]# docker run --name="centos7" -it centos:7 /bin/bash
[root@d259a3756db5 /]# ls # 查看容器内的centos。
anaconda-post.log dev home lib64 mnt proc run srv tmp var
bin etc lib media opt root sbin sys usr
# 退出容器
[root@d259a3756db5 /]# exit
exit
# 查看容器运行情况
[root@VM-4-17-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
** 列出所有运行的容器**
# docker ps [OPTIONS]
可选参数
默认列出当前正在运行的容器
-a 列出当前正在运行的+带出历史运行过的容器
-n=? 列出最近创建的容器 ?=1/2/3/...
-q 只显示容器id
** 退出容器**
exit # 直接停止容器并退出
ctrl + p + q # 容器仅退出,不停止
** 删除容器**
docker rm 容器id # 删除指定的容器, 不能删除正在运行的容器,如需强制删除加 -f
docker rm -f $(docker ps -aq) # 删除所有的容器
docker start 容器id
docker restart 容器id
docker stop 容器id # 停止当前正在运行的容器
docker kill 容器id # 强制停止指定容器
docker run -d 镜像 # 使用后台方式启动镜像
[root@VM-4-17-centos ~]# docker run -d centos:7
# 出现的问题: docker ps 发现centos:7停止了!!!
[root@VM-4-17-centos ~]# docker ps -a
CONTAINER ID IMAGE COMMAND STATUS
61773aaec794 centos:7 "/bin/bash" Exited (0) 20 seconds ago
# 常见的坑:
docker容器使用后台启动运行,必须要有一个前台进程。docker发现没有应用(没有对外提供服务),就会自动停止。
若想使用后台启动方式启动一个nginx容器,此时发现自己没有提供服务,就会立刻停止。
docker logs -tf --tail 10(行数) 容器
# 自己编写一段shell脚本,写入日志
[root@VM-4-17-centos ~]# docker run -d centos:7 /bin/bash -c "while true;do echo wguo;sleep 1;done"
[root@VM-4-17-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND
0d1d484242ed centos:7 "/bin/bash -c 'while…"
# 显示日志
-t 显示时间
-f 显示内容
--tail n 查询方式 及查询条数
[root@VM-4-17-centos ~]# docker logs -f -t --tail 50 0d1d484242ed
查看容器中进程信息
docker top 容器id
[root@VM-4-17-centos /]# docker run -it centos:7 /bin/bash
[root@f709a72995f2 /]# [root@VM-4-17-centos /]#
[root@VM-4-17-centos /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f709a72995f2 centos:7 "/bin/bash" 25 seconds ago Up 24 seconds dreamy_wozniak
# 查看容器中进程信息
[root@VM-4-17-centos /]# docker top f709a72995f2
UID PID PPID
root 4854 4833
查看容器的元数据
docker inspect 容器id
# 测试
[root@VM-4-17-centos /]# docker inspect f709a72995f2
...
进入当前正在运行的容器
我们的容器通常是使用后台部署的,往往需要进入容器修改一些配置
# 方式一 进入容器后开启了一个新的终端,可以在里面操作
docker exec -it 容器id /bin/bash
# 方式二 进入容器正在执行的终端,不会启动新的进程
docker attach 容器id
# 测试
[root@VM-4-17-centos /]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f709a72995f2 centos:7 "/bin/bash" 6 minutes ago Up 6 minutes dreamy_wozniak
# 方式一进入容器
[root@VM-4-17-centos /]# docker exec -it f709a72995f2 /bin/bash
[root@f709a72995f2 /]#
# 方式二进入容器
[root@VM-4-17-centos /]# docker attach f709a72995f2
# ...一些正在执行的代码(如果有的话)
从容器内拷贝文件到主机上
docker cp 容器id:容器内路径 目的地主机路径
# 进入已启动的容器
[root@VM-4-17-centos /]# docker attach f709a72995f2
# 进入容器的home目录
[root@f709a72995f2 /]# cd /home/
# 确保容器home目录下没有文件
[root@f709a72995f2 home]# ls
# 创建一个wguo.java文件
[root@f709a72995f2 home]# touch wguo.java
# 确保创建成功
[root@f709a72995f2 home]# ls
wguo.java
# ctrl + p + q 退出容器
[root@f709a72995f2 home]# read escape sequence
# 进入物理机,负责容器内容
[root@VM-4-17-centos /]# docker cp f709a72995f2:/home/wguo.java /home/
# 进入相应的文件夹,
[root@VM-4-17-centos /]# cd /home/
# 查看是否复制成功
[root@VM-4-17-centos home]# ls
lighthouse wguo.java
https://docs.docker.com/engine/reference/commandline/docker/
[root@VM-4-17-centos ~]# docker pull nginx:1.21
1.21: Pulling from library/nginx
a2abf6c4d29d: Pull complete
a9edb18cadd1: Pull complete
589b7251471a: Pull complete
186b1aaa4aa6: Pull complete
b4df32aa5a72: Pull complete
a0bcbecc962e: Pull complete
Digest: sha256:0d17b565c37bcbd895e9d92315a05c1c3c9a29f762b011a10c54a66cd53c9b31
Status: Downloaded newer image for nginx:1.21
docker.io/library/nginx:1.21
[root@VM-4-17-centos ~]# docker run --name mynginx -d -p 10086:80 nginx:1.21
657849785cd57c48f93bc9c8a8efd74cf902086d1fc04c9a7e3f45948f746d2c
注意:如果开启了防火墙,并在docker进程运行过程中修改了防护墙规则,且reload过firewall,此时需要重启docker进程。systemctl restart docker
[root@VM-4-17-centos ~]# curl localhost:10086
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
html { color-scheme: light dark; }
body { width: 35em; margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif; }
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>
<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>
<p><em>Thank you for using nginx.</em></p>
</body>
</html>
思考:
现阶段,我们每次改动nginx配置文件,都需要进入容器内部。十分麻烦,是否可以在容器外部提供一个映射路径,达到在容器外部修改文件,容器内部就可以自动修改?
-v 数据卷
见:第六章
注意: 使用docker run -xx 镜像 时,如果本地不存在,回去仓库下载,并执行
# 官方的使用示例:
docker run -it --rm tomcat:9.0
# 我们之前的测试启动都是后台运行,停止了容器之后,容器还是可以查到。
# docker run -it --rm 镜像 一般用来测试,用完即删除这个容器
安装运行过程:
# 下载最新版的tomcat
docker pull tomcat
# 开启服务器指定端口
firewall-cmd --permanent --add-port=58080
# 重载防护墙规则
firewall-cmd --reload
# 开启腾讯/阿里云服务器的防火墙规则
# 重启docker进程
systemctl restart docker
# 创建并后台运行容器。并将容器的tomcat端口8080 映射到服务器的指定端口,这里是58080
docker run -d -p 58080:8080 tomcat
测试过程
发现访问是没有问题的,只是tomcat没有首页信息。
这是因为docker安装的tomcat是阉割版的。即tomcat下的webapps目录下没有东西。
这是阿里云的镜像的原因。默认是最小的镜像,所有不必要的信息都剔除掉了。只保证最小的可运行环境
在容器中将tomcat默认文件从webapps.dist复制到webapps下
重新访问
思考:
现阶段,我们每次部署项目,都需要进入容器内部。十分麻烦,是否可以在容器外部提供一个映射路径webapps,我们每次部署就将项目放在外部的webapps中,然后自动同步到内部容器就好了。
比如:docker = tomcat + nginx + mysql + jdk + …
要是都把元数据信息都放在docker容器内部,那么要是有人想删库跑路,原来可能他只能删数据库,但现在只要删docker,就将所有的信息都删掉了。
ES 暴露的端口很多
ES 十分耗费内存
ES 的数据一般需要放置在安全目录! (挂载)
# 官方安装
# 这里的 --net somenetwork 是docker 网关配置。这里先不用
docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:tag
安装过程:
# 开启服务器防火墙,云防火墙,重载防火墙,重启docker
# 下载es镜像,
docker pull elasticsearch:7.5.2
# 并创建启动一个elasticsearch容器,将容器的9200端口和宿主机的59200端口映射
docker run -d --name elasticsearch -p 59200:9200 -p 59300:9300 -e "discovery.type=single-node" elasticsearch:7.5.2
# 发现启动后 linux就有点卡了(服务器内存大的话,可能不太明显)docker stats 查看cpu的状态
docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
0b61148f1ee6 elasticsearch 0.33% 4.23GiB / 7.638GiB 55.38% 8.32kB / 1.93kB 7.75MB / 877kB 58
# 可以看到,只启动了一个elasticsearch 就已经用了4个多G的内存。
增加内存限制,修改配置文件 -e 环境配置修改
ES_JAVA_OPTS
# -e ES_JAVA_OPTS 增加配置 初始内存64m,最大512m
docker run -d --name elasticsearch -p 59200:9200 -p 59300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.5.2
# 测试
[root@VM-4-17-centos ~]# docker run -d --name elasticsearch -p 59200:9200 -p 59300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.5.2
086d4f231ecc46f0859d217a04f767e49f3ac03ac5c940332c7fec9602448897
[root@VM-4-17-centos ~]# docker stats
CONTAINER ID NAME CPU % MEM USAGE / LIMIT MEM % NET I/O BLOCK I/O PIDS
086d4f231ecc elasticsearch 0.01% 183.8MiB / 7.638GiB 2.35% 516B / 0B 0B / 217kB 20
思考: kibana 和 elasticsearch直接如何通信?
安装Kibana
按照如上的步骤,选择的Kibana的版本(Kibana和Elasticsearch版本最好保持一致)
注意指定es的环境变量 ELASTICSEARCH_HOSTS
# 安装
docker pull kibana:7.5.2
# 创建容器并运行。注意:ELASTICSEARCH_HOSTS=自己的ES虚拟机IP地址:端口号
docker run --name kibana -e ELASTICSEARCH_HOSTS=http://xxx.xxx.xxx.xxx:59200 -p 55601:5601 \
-d kibana:7.5.2
** 测试 kibana**
注意:端口记得打开
访问 ip:55601
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需要的所有内容。包括代码、运行库、环境变量和配置文件。
所有的应用,直接打包位docker镜像,就可以直接跑起来。
UnionFS (联合文件系统)
Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交,来一层层的叠加。
同时可以将不同目录,挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。
Union文件系统是Docker镜像的基础。
镜像可以通过分层来进行继承,基于基础镜像(没有父镜像概念),可以制作各种具体的应用镜像。
我们下载镜像的时候,看到的一层层就是这个,如下:
[root@VM-4-17-centos ~]# docker pull redis
Using default tag: latest
latest: Pulling from library/redis
a2abf6c4d29d: Already exists # 此层已存在,就不再去下载
c7a4e4382001: Pull complete
4044b9ba67c9: Pull complete
c8388a79482f: Pull complete
413c8bb60be2: Pull complete
1abfd3011519: Pull complete
Digest: sha256:db485f2e245b5b3329fdc7eff4eb00f913e09d8feb9ca720788059fdc2ed8339
Status: Downloaded newer image for redis:latest
docker.io/library/redis:latest
总结:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
Docker 镜像加载原理
Docker镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS 分为两个部分:
即:系统启动时需要的引导加载,这个过程会需要一定时间。就是黑屏到开机之间的这么一个过程。电脑、虚拟机、Docker容器启动都需要的过程。在说回镜像,所以这一部分,无论是什么镜像都是公用的。
即:镜像启动之后的一个小的底层系统,这就是我们之前所说的,容器就是一个小的虚拟机环境,比如Ubuntu,Centos等,这个小的虚拟机环境就相当于rootfs。
平时我们安装进虚拟机的CentOS系统都是好几个G,为什么Docker这里才200M?
如下:
[root@192 /]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 300e315adb2f 3 months ago 209MB
对于一个精简的OS系统,rootfs可以很小,只需要包含最基本的命令、工具和程序库就可以了,因为底层直接用Host(宿主机)的kernel(也就是宿主机或者服务器的boosfs+内核),自己只需要提供rootfs就可以了。
由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs部分。
这就是我们之前说:虚拟机的启动是分钟级的,容器的启动是秒级的。
为什么Docker镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来,那么宿主机只需在磁盘上保留一份base镜像,同时内存中也只需要加载一份base镜像,这样就可以为所有的容器服务了,而且镜像的每一层都可以被共享。
查看镜像分层的方式可以通过docker image inspect +容器命令!
[root@VM-4-17-centos ~]# docker image inspect redis:latest
[
//...
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:2edcec3590a4ec7f40cf0743c15d78fb39d8326bc029073b41ef9727da6c851f",
"sha256:9b24afeb7c2f21e50a686ead025823cd2c6e9730c013ca77ad5f115c079b57cb",
"sha256:4b8e2801e0f956a4220c32e2c8b0a590e6f9bd2420ec65453685246b82766ea1",
"sha256:529cdb636f61e95ab91a62a51526a84fd7314d6aab0d414040796150b4522372",
"sha256:9975392591f2777d6bf4d9919ad1b2c9afa12f9a9b4d260f45025ec3cc9b18ed",
"sha256:8e5669d8329116b8444b9bbb1663dda568ede12d3dbcce950199b582f6e94952"
]
},
"Metadata": {
"LastTagTime": "0001-01-01T00:00:00Z"
}
//...
]
理解
所有的Docker镜像都起始于一个基础镜像层,当进行修改火增加新的内容时,就会在当前的镜像层之上,创建新的镜像层,比如:基于Ubuntu Linux 16.4 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。
该镜像当前已经包含3个镜像层,如下图所示:
在添加额外额镜像层的同时,镜像始终保持的是当前所有镜像的组合。如下图所示:每个镜像层包含三个文件,而镜像包含了来自两个镜像层的6个文件
下图展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有6个文件,这是因为最上层的文件7是文件5额一个更新版本
这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。
Docker通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。
Linux上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义,每种存储引擎都基于Linux中对应的文件系统或者块设备技术,并且每种存储引擎都有其独有的性能特点。
Docker在Windows上仅支持windowsfilter一种存储引擎,该引擎基于NTFS文件系统之上实现了分层和CoW[1].
下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
特点
Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!
这一层就是我们通常说的容器层(run),容器之下的都叫镜像层(远程pull)!
所有操作都是基于容器层
commit 镜像
docker commit 提交容器成为一个新的副本
docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名:[TAG]
测试:
# 启动一个tomcat容器
docker run -it --name tomcat -p 58080:8080 tomcat:latest /bin/bash
# 发现这个默认的tomcat镜像是没有webapps默认应用的。
# 将webapps.dist目录下的文件复制到webapps下
cp -r webapps.dist/* webapps
此时 要是想以后不用再在这个镜像启动的容器中进行上面那一步复制文件的操作。可以把修改后的容器打包成成镜像,以后需要就可以直接使用这个我们修改后的镜像。
将tomcat基础镜像层 +容器层(有我们修改过的内容) ==> 自定义tomcat基础镜像层
步骤:
# 将我们操作过的容器通过 commit 提交位一个新的镜像
[root@VM-4-17-centos ~]# docker commit -m="add webapps boot app" -a="wguo" fa0cd45987df tomcat-custom:1.0
sha256:c881f78a97bff1bcd85773190a5487eca78bcb8a16737f1a7f47ded84be5189c
# 查看打包后的镜像
[root@VM-4-17-centos ~]# docker images | grep tomcat
tomcat-custom 1.0 c881f78a97bf 50 seconds ago 684MB
tomcat latest fb5657adc892 2 months ago 680MB
docker理念回顾
将应用和环境打包成一个镜像
数据?数据都在容器中,那么我们容器删除,数据就会丢失! 需求:数据可以持久化
Mysql, 容器删了,数据全部丢失! 需求: Mysql数据可以存储在本地!
容器之间可以有一个数据共享技术!Docker 容器中产生数据,同步到本地!
这就是容器卷技术 ! 目录的挂载,将我们容器内的目录,挂载到Linux上面!
docker run -it -v 主机目录:容器内的目录
# 测试
# 本地有一个centos镜像
[root@VM-4-17-centos ~]# docker images | grep centos
centos 7 eeb6ee3f44bd 6 months ago 204MB
# 确保本地目录。 这里我们测试将本机的 /home/test 目录 映射到容器内的 /home目录
[root@VM-4-17-centos ~]# cd /home
[root@VM-4-17-centos home]# ls
lighthouse
# 创建并启动一个cengos容器。并将 /home/test 目录映射到容器的 /home目录下(将容器/home目录挂载到主机的/home/test)
[root@VM-4-17-centos /]# docker run -it -v /home/test:/home centos:7 /bin/bash
# ctrl + p + q 退出容器
# 查看容器详情
docker inspect a722f690950c
# 可以看到 容器的/home目录已经被挂载到主机/home/test上了
[
...
"Mounts": [ # 挂载信息 -v 卷
{
"Type": "bind",
"Source": "/home/test", # 主机地址
"Destination": "/home", # 容器地址
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],
]
测试:
**此时可以解决 每次修改配置文件时,都需要进入容器内部的麻烦 **
如: 部署nginx时 可以将nginx配置文件映射
** 安装**
# 获取mysql镜像
[root@VM-4-17-centos /]# docker pull mysql:5.7.37
5.7.37: Pulling from library/mysql
Digest: sha256:66d52e6baa8093820c09fec56992a5ee734f17e9fad8ef5ffc31597b231bd048
Status: Image is up to date for mysql:5.7.37
docker.io/library/mysql:5.7.37
# 官方样例 -e MYSQL_ROOT_PASSWORD 设置mysql初始密码
# docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 创建mysql容器,
# -d 后台运行
# --name 为容器命名
# 将主机的53306端口和容器的3306端口进行映射,
# 将主机的/home/mysql/conf 与 容器中的 /etc/mysql/conf.d 目录映射 配置挂载
# 将主机的/home/mysql/data 与 容器中的 /var/lib/mysql 进行映射 数据挂载
# 并设置相应的初始密码 -e MYSQL_ROOT_PASSWORD
docker run -d --name="mysql5.7" -p 53306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=root mysql:5.7.37
测试:
使用数据库连接工具连接docker运行的mysql容器
测试创建一个数据库
[root@VM-4-17-centos data]# cd /home/mysql/data/
[root@VM-4-17-centos data]# ls
... test_db
删除容器
[root@VM-4-17-centos data]# docker ps
CONTAINER ID IMAGE
8abfb37cf31b mysql:5.7.37
[root@VM-4-17-centos data]# docker stop 8abfb37cf31b
8abfb37cf31b
[root@VM-4-17-centos data]# docker rm 8abfb37cf31b
# 查看挂载的数据
[root@VM-4-17-centos data]# ls
...test_db
# 可以发现,挂载到本地的数据卷依旧没有丢失,这就实现了容器数据持久化功能。
匿名: -v 容器内路径
具名: -v 卷名:内路径
docker volume [命令] 操作卷相关内容
# 匿名挂载 -v 容器内路径
[root@VM-4-17-centos /]# docker run -d -p 10086:80 --name nginx01 -v /etc/nginx nginx:1.21
11a1c56c64b9e98a0b833477f72a42c15d9d1434cfc5789b20be8385eff3881a
# 查看所有的卷 docker volume ls
[root@VM-4-17-centos /]# docker volume ls
DRIVER VOLUME NAME
local 209e7abd6123be54122062b098a6acc52119e26d2f33df10283905066e8cf2da
# 发现:匿名挂载的卷名是一串唯一码
# 具名挂载 -v 卷名:容器内路径
# 查看卷详情
docker volume inspect 卷名
所有docker的容器内的卷,在没有指定主机目录的情况下,不论是具名挂载还是匿名挂载,卷都是存放在/var/lib/docker/volumes/xxx(卷名)/_data
我们通过具名挂载可以方便的找到一个我们的一个卷,大多数情况下,我们使用具名挂载
DockerFile 就是用来构建docker镜像的构建文件 ! 是一个命令脚本!
通过这个脚本可以生成镜像,镜像是一层一层的,脚本就是一个一个命令,每个命令都是一层。
回顾:镜像的构建方式
commit: docker commit -m=“msg” -a=“author” 容器id 目标镜像:Tag
[root@VM-4-17-centos ~]# cd /home/
[root@VM-4-17-centos home]# ls
lighthouse
# 1. 创建一个Dockerfile文件,并写入一些脚本信息
[root@VM-4-17-centos home]# vim Dockerfile
# 脚本内容如下
FROM centos:7 # 选择一个镜像
VOLUME ["volume01", "volume02"] # 将镜像的volume01和volume02挂载到主机上(匿名挂载)。注意目录在 /var/lib/docker/volumes/xxx/_data下
CMD echo "--------end---------" # cmd 执行echo 打印
CMD /bin/bash # cmd 执行/bin/bash 相当于在run 时加在后面的 /bin/bash参数
# 根据Dockerfile生成镜像
[root@VM-4-17-centos home]# docker build -f Dockerfile -t wguo/centos:1.0 . # 注意以 空格+. 结尾
Sending build context to Docker daemon 7.168kB
Step 1/4 : FROM centos:7 # 找到一个基准命令,给新镜像一个基础层
---> eeb6ee3f44bd
Step 2/4 : VOLUME ["volume01", "volume02"] # 挂载
---> Running in 5f5d6742f25b
Removing intermediate container 5f5d6742f25b
---> 23f2f1706f7c
Step 3/4 : CMD echo "--------end---------" # 执行命令
---> Running in f1a72e55dae1
Removing intermediate container f1a72e55dae1
---> 8cf20074a508
Step 4/4 : CMD /bin/bash # 给镜像生成 /bin/bash 镜像层
---> Running in 8b6b541abd76
Removing intermediate container 8b6b541abd76
---> f6abf1cd426b
Successfully built f6abf1cd426b
Successfully tagged wguo/centos:1.0
# 查看自己的镜像
root@VM-4-17-centos home]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
wguo/centos 1.0 f6abf1cd426b 3 minutes ago 204MB
# 运行自定义的镜像
[root@VM-4-17-centos home]# docker run -it wguo/centos:1.0 /bin/bash
[root@d44253763674 /]# ls
... volume01 volume02
# 发现我们脚本中挂载的目录,这就说明主机中存在了相应的目录。这里就是个匿名挂载
# 进入挂载目录,创建一个测试文件
[root@d44253763674 /]# cd volume01
[root@d44253763674 volume01]# touch helloworld.txt
[root@d44253763674 volume01]# ls
helloworld.txt
# ctrl + p + q 退出容器
[root@VM-4-17-centos home]# docker ps | grep wguo
d44253763674 wguo/centos:1.0 "/bin/bash"
# 查看容器详情,查看挂载
"Mounts": [
{
"Type": "volume",
"Name": "977e46c525e916ecd289c09e2259ed45f2a9d3fc11cec1f732607e337ed9c4d6",
"Source": "/var/lib/docker/volumes/977e46c525e916ecd289c09e2259ed45f2a9d3fc11cec1f732607e337ed9c4d6/_data",
"Destination": "volume02",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}, {
"Type": "volume",
"Name": "cf29e34f71e3a376d899eb196565bf83fe96fcc21cbfb3059f05011b2ae2a46b",
"Source": "/var/lib/docker/volumes/cf29e34f71e3a376d899eb196565bf83fe96fcc21cbfb3059f05011b2ae2a46b/_data",
"Destination": "volume01",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],
# 进入 /var/lib/docker/volumes/xxx(volume01对应的那个)/_data
[root@VM-4-17-centos home]# cd /var/lib/docker/volumes/cf29e34f71e3a376d899eb196565bf83fe96fcc21cbfb3059f05011b2ae2a46b/_data/
[root@VM-4-17-centos _data]# ls
helloworld.txt # 说明已正常挂载
测试
# 使用前面自定义的wguo/centos:1.0镜像启动一个容器 linux01
[root@VM-4-17-centos test]# docker run -it --name linux01 wguo/centos:1.0
# 可以看到volume01和volume02两个文件夹
[root@9589acb05968 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
# 退出 ctrl + p + q
# 在创建一个linux02容器 并将linux02 挂载到linux01
[root@VM-4-17-centos test]# docker run -it --name linux02 --volumes-from linux01 wguo/centos:1.0
# 可以看到,linux02 容器 下也有volume01和volume02 两个文件夹
[root@0ef9e3315ee0 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
# 测试linux01 和linux02 文件挂载
# 重启一个窗口,进入linux01的后台
[root@VM-4-17-centos ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
0ef9e3315ee0 wguo/centos:1.0 "/bin/sh -c /bin/bash" About a minute ago Up About a minute linux02
9589acb05968 wguo/centos:1.0 "/bin/sh -c /bin/bash" 6 minutes ago Up 6 minutes linux01
[root@VM-4-17-centos ~]# docker attach 9589acb05968
[root@9589acb05968 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
[root@9589acb05968 /]# cd volume01
[root@9589acb05968 volume01]# touch helloworld
[root@9589acb05968 volume01]# ls
helloworld
# 再在linux02的窗口查看相应的volume01文件夹下是否同步新建了helloworld文件
[root@0ef9e3315ee0 /]# cd volume01
[root@0ef9e3315ee0 volume01]# ls
helloworld
# 可以看到,linux01 和 linux02 两个容器此时已实现数据共享。
# linux02 挂载到 linux01 ,此时linux01 就叫做数据卷容器
在上面的例子中,linux02 挂载到 linux01 ,此时linux01 就叫做数据卷容器
此时,还可以创建其他多个容器,然后挂载到容器linux01/linux02,然后就可以实现容器数据共享.
# 创建linux03 将其挂载到linux02
root@VM-4-17-centos test]# docker run -it --name linux03 --volumes-from linux02 wguo/centos:1.0
[root@f39c052173a2 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
[root@f39c052173a2 /]# cd volume01
[root@f39c052173a2 volume01]# ls
helloworld
# 进入linux01 ,创建一个 文件
[root@9589acb05968 volume01]# touch linux03
[root@9589acb05968 volume01]# ls
helloworld linux03
# 此时,在linux03 容器,也能同步看到这个文件
[root@f39c052173a2 volume01]# ls
helloworld linux03
# 创建linux04 容器,将其挂载到linux01
root@VM-4-17-centos test]# docker run -it --name linux04 --volumes-from linux01 wguo/centos:1.0
[root@367c99eb4264 /]# ls
anaconda-post.log bin dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var volume01 volume02
[root@367c99eb4264 /]# cd volume01
# 能看到文件还是同步的
[root@367c99eb4264 volume01]# ls
helloworld linux03
# 进入与linux04 无直接关系的linxu02
root@VM-4-17-centos ~]# docker attach 0ef9e3315ee0
[root@0ef9e3315ee0 volume01]# ls
helloworld linux03
# 创建一个新文件
[root@0ef9e3315ee0 volume01]# touch linux04
# 切换会linux04 ,查看是否能同步到linux02容器的内容
[root@367c99eb4264 volume01]# ls
helloworld linux03 linux04
# 发现完全没有问题
# 此时,使用linux04 再创建一个文件,并将容器停止和删除
[root@367c99eb4264 volume01]# touch deletefile
[root@367c99eb4264 volume01]# ls
deletefile helloworld linux03 linux04
[root@VM-4-17-centos test]# docker rm -f 367c99eb4264
367c99eb4264
# 查看linux01、linux02、linux03 的内容是否还在及是否已同步最新的内容
# linux01
[root@9589acb05968 volume01]# ls
deletefile helloworld linux03 linux04
# linux02
[root@0ef9e3315ee0 volume01]# ls
deletefile helloworld linux03 linux04
# linux03
root@VM-4-17-centos test]# docker attach f39c052173a2
[root@f39c052173a2 volume01]# ls
deletefile helloworld linux03 linux04
# 发现文件已被同步,且linux04的删除没有影响
总结:容器件的数据共享是一个拷贝的概念。只要有一个容器还活着,数据就不会消失。
数据卷容器的作用
常用来做容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止
# 创建一个mysql容器
[root@VM-4-17-centos test]# docker run -d -p 53306:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=root --name mysql101 mysql:5.7.37
1bf6ba4102635e2986d7fd9beb37d2b4c5f9c20258b6305b35eb31ff840b6aa4
# 再创建一个mysql容器,并挂载到前面的容器中
[root@VM-4-17-centos test]# docker run -d -p 53307:3306 --volumes-from mysql101 -e MYSQL_ROOT_PASSWORD=root --name mysql102 mysql:5.7.37
0feac465c16262021289a108f88f989625c8167e37a6a835124c75f90d4669da
# 此时,两个数据库之间就能实现数据共享。再有多的数据库服务,也都类似操作
容器数据卷常用来做容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。但是一旦我们的数据持久化到了本地(主机),这个时候,即使容器都被删除了,本地的数据依然存在。
DockerFile 是用来构建docker镜像的文件。是命令参数脚本。
构建步骤:
可以发现,官方的镜像最终也是一个dockerfile文件。
Dockerfile是面向开发的,我们以后要发布项目,做镜像就需要编写Dockerfile文件,这个文件十分简单!
Dockerfile镜像逐渐成为企业交付标准
Dockerfile: 构建文件,定义了一切步骤、源代码
DockerImages: 通过Dockerfile 构建生成的镜像,最终发布和运行的产品
Docker容器: 容器就是镜像运行起来提供的服务器
FROM # 基础镜像,一切都从这里开始
MAINTAINER # 镜像是谁写的,一般姓名+邮箱
RUN # 镜像构建的时候需要运行的命令,一般用来再某些层安装一些其他软件 -r
ADD # 步骤,如centos镜像,往基础镜像里添加centos的tar压缩包并解压内容(会自动解压)
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录 如将指定的volume01目录挂载到主机 -v
EXPOST # 指定端口配置 -p
CMD # 指定这个容器启动时需要运行的命令,只有最后一个会生效(前面会被后面覆盖),可被替代
ENTRYPOINT # 指定这个容器启动时需要运行的命令,只有最后一个会生效,可追加(默认相当于用CMD追加ENTRYPOINT,但是也可以用--entrypoint覆盖)
ONBUILD # 当构建一个被继承 DockerFile 这个时候就会运行ONBUILD。
COPY #
ENV # 构建时需要设置的环境变量 -e
# 1. 编写Dockerfile文件
vim /home/test/Dockerfile
FROM centos:7
MAINTAINER wguo<[email protected]>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum -y install vim
RUN yum -y install net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "-------END------"
CMD /bin/bash
# 2. 构建镜像
docker build -f Dockerfile -t wguo/centos:2.0 .
Sending build context to Docker daemon 2.048kB
Step 1/11 : FROM centos:7
---> eeb6ee3f44bd
Step 2/11 : MAINTAINER wguo<[email protected]>
---> Running in d9d90f4112f9
Removing intermediate container d9d90f4112f9
---> aa215bb75207
Step 3/11 : ENV MYPATH /usr/local
---> Running in 95fec1163f06
Removing intermediate container 95fec1163f06
---> 3212b1d70d43
Step 4/11 : WORKDIR $MYPATH
---> Running in 2ead4fa2bba8
Removing intermediate container 2ead4fa2bba8
---> 5c24b83a263b
Step 5/11 : CMD echo "install others refrence soft..."
---> Running in 7032c41a5c2c
Removing intermediate container 7032c41a5c2c
---> 1c2850511830
Step 6/11 : RUN yum install -y vim
---> Running in fea7802e7dcb
...
Step 7/11 : RUN yum install -y net-tools
---> Running in 4ed47386202d
...
Step 8/11 : EXPOSE 80
---> Running in 03285e4ccc2d
Removing intermediate container 03285e4ccc2d
---> e5858f44f19d
Step 9/11 : CMD $MYPATH
---> Running in 57eaa27cd114
Removing intermediate container 57eaa27cd114
---> b281b3350938
Step 10/11 : CMD echo "-----------end----------------"
---> Running in a67bee745d8f
Removing intermediate container a67bee745d8f
---> 39a665a8a860
Step 11/11 : CMD /bin/bash
---> Running in dca0c8cc8eb3
Removing intermediate container dca0c8cc8eb3
---> 58a230dad200
Successfully built 58a230dad200
Successfully tagged wguo/centos:2.0
# 3. 测试
[root@VM-4-17-centos test]# docker images | grep wguo
wguo/centos 2.0 58a230dad200 2 minutes ago 580MB
[root@VM-4-17-centos test]# docker run -it 58a230dad200 /bin/bash
[root@beb19947968b local]# pwd
/usr/local # 证明WORKDIR设置成功了
[root@beb19947968b local]# echo $MYPATH
/usr/local
我们可以通过 docker history 镜像 来查看镜像的构建过程。如:
[root@VM-4-17-centos test]# docker history 58a230dad200
IMAGE CREATED CREATED BY SIZE COMMENT
58a230dad200 5 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "/bin… 0B
39a665a8a860 5 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
b281b3350938 5 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "$MYP… 0B
e5858f44f19d 5 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B
6d106e2e9c00 5 minutes ago /bin/sh -c yum install -y net-tools 161MB
33ed57b460aa 5 minutes ago /bin/sh -c yum install -y vim 216MB
1c2850511830 6 minutes ago /bin/sh -c #(nop) CMD ["/bin/sh" "-c" "echo… 0B
5c24b83a263b 6 minutes ago /bin/sh -c #(nop) WORKDIR /usr/local 0B
3212b1d70d43 6 minutes ago /bin/sh -c #(nop) ENV MYPATH=/usr/local 0B
aa215bb75207 6 minutes ago /bin/sh -c #(nop) MAINTAINER wguo
eeb6ee3f44bd 6 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B
<missing> 6 months ago /bin/sh -c #(nop) LABEL org.label-schema.sc… 0B
<missing> 6 months ago /bin/sh -c #(nop) ADD file:b3ebbe8bd304723d4… 204MB
# 查看nginx的构建过程
root@VM-4-17-centos test]# docker history 605c77e624dd
IMAGE CREATED CREATED BY SIZE COMMENT
605c77e624dd 2 months ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B
<missing> 2 months ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B
<missing> 2 months ago /bin/sh -c #(nop) EXPOSE 80 0B
<missing> 2 months ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B
<missing> 2 months ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 4.61kB
<missing> 2 months ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 1.04kB
<missing> 2 months ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 1.96kB
<missing> 2 months ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 1.2kB
<missing> 2 months ago /bin/sh -c set -x && addgroup --system -… 61.1MB
<missing> 2 months ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~bullseye 0B
<missing> 2 months ago /bin/sh -c #(nop) ENV NJS_VERSION=0.7.1 0B
<missing> 2 months ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.5 0B
<missing> 2 months ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B
<missing> 2 months ago /bin/sh -c #(nop) CMD ["bash"] 0B
<missing> 2 months ago /bin/sh -c #(nop) ADD file:09675d11695f65c55… 80.4MB
可以多看看官方的一些镜像是怎么做的。通过这种方式学习
从根本上说, ENTRYPOINT和CMD都是让用户指定一个可执行程序, 这个可执行程序在container启动后自动启动. 实际上, 如果你想让自己制作的镜像自动运行程序(不需要在docker run后面添加命令行指定运行的命令), 你必须在Dockerfile里面, 使用ENTRYPOINT或者CMD命令
比如执行运行一个没有调用ENTRYPOINT或者CMD的docker镜像, 一定返回错误
$ docker run alpine
FATA[0000] Error response from daemon: No command specified
大部分Linu发行版的基础镜像里面调用CMD命令, 指定容器启动后执行/bin/sh或/bin/bash. 这样镜像启动默认进入交互式的shell
3个不同的Linux镜像(ubuntu, busybox, debian)都在Dockerfile的最后调用 CMD ‘/bin/bash’
启动Linux发行版的基础container后, 默认调用shell程序, 符合大多数人的习惯.
但是, 作为开发者, 你希望在docker镜像启动后, 自动运行其他程序. 所以, 你需要用CMD或者ENTRYPOINT命令显式地指定具体的命令.
覆盖(Overrides)
在写Dockerfile时, ENTRYPOINT或者CMD命令会自动覆盖之前的ENTRYPOINT或者CMD命令.
在docker镜像运行时, 用户也可以在命令指定具体命令, 覆盖在Dockerfile里的命令.
比如, 我们写了一个这样的Dockerfile:
FROM centos:7
CMD ping localhost
如果根据这个Dockerfile构建一个新image, 名字叫mycentos,Tag2.0
[root@VM-4-17-centos test]# docker run -it mycentos:2.0
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.042 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.038 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.041 ms
64 bytes from localhost (127.0.0.1): icmp_seq=4 ttl=64 time=0.037 ms
可以看出ping命令在docker启动后自己执行, 但是我们可以在命令行启动docker镜像时, 执行其他命令行参数, 覆盖默认的CMD
$ docker run mycentos,Tag2.0 hostname
3f0deb223833
docker启动后, 并没有执行ping命令, 而是运行了hostname命令
和CMD类似, 默认的ENTRYPOINT也在docker run时, 也可以被覆盖. 在运行时, 用–entrypoint覆盖默认的ENTRYPOINT
vim dockerfile-test1
FROM centos:7
ENTRYPOINT ping localhost
docker build -f dockerfile-test1 -t mycentos:3.0 .
docker run -it mycentos:3.0
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.043 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.040 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.037 ms
64 bytes from localhost (127.0.0.1): icmp_seq=4 ttl=64 time=0.039 ms
^C
--- localhost ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 2999ms
rtt min/avg/max/mdev = 0.037/0.039/0.043/0.008 ms
# 覆盖
docker run -it --entrypoint hostname mycentos:3.0
c9cf2d591c9e
因为CMD命令很容易被docker run命令的方式覆盖, 所以, 如果你希望你的docker镜像的功能足够灵活, 建议在Dockerfile里调用CMD命令.
相反, ENTRYPOINT的作用不同, 如果你希望你的docker镜像只执行一个具体程序, 不希望用户在执行docker run的时候随意覆盖默认程序. 建议用ENTRYPOINT.
Shell vs. Exec
ENTRYPOINT和CMD指令支持2种不同的写法: shell表示法和exec表示法.
下面的例子使用了shell表示法:
CMD executable param1 param2
当使用shell表示法时, 命令行程序作为sh程序的子程序运行, docker用/bin/sh -c的语法调用. 如果我们用docker ps命令查看运行的docker, 就可以看出实际运行的是/bin/sh -c命令
root@VM-4-17-centos test]# docker run -d mycentos:3.0
bf7577f537e2c7f0ba8a4ddd3bea919eb6f300bf5f941d18594b7528d613ff4d
[root@VM-4-17-centos test]# docker ps -l
CONTAINER ID IMAGE COMMAND
bf7577f537e2 mycentos:3.0 "/bin/sh -c 'ping localhost'"
但是如果你想build一个超级小的docker镜像, 这个镜像甚至连shell程序都可以没有. shell的表示法没办法满足这个要求. 如果你的镜像里面没有/bin/sh, docker容器就不能运行.
A better option is to use the exec form of the ENTRYPOINT/CMD instructions which looks like this:
一个更好的选择是用exec表示法:
CMD ["executable","param1","param2"]
CMD指令后面用了类似于JSON的语法表示要执行的命令. 这种用法告诉docker不需要调用/bin/sh执行命令.
我们修改一下Dockerfile, 改用exec表示法:
FROM centos:7
CMD {"/bin/ping", "localhost"}
docker build -f dockerfile-test1 -t mycentos .
docker run -d mycentos
21fac4cb5ef1bfe2c5dc13792487752b6f8beb2f57adcce469f8078aea614bf3
docker ps -l
CONTAINER ID IMAGE COMMAND CREATED
21fac4cb5ef1 mycentos "/bin/ping localhost" 52 seconds ago
现在没有启动/bin/sh命令, 而是直接运行/bin/ping命令.
无论你用的是ENTRYPOINT还是CMD命令, 都强烈建议采用exec表示法
ENTRYPOINT 和 CMD组合使用
有时候,组合ENTRYPOINT和CMD能发挥更大的作用.
组合使用ENTRYPOINT和CMD, ENTRYPOINT指定默认的运行命令, CMD指定默认的运行参数. 例子如下:
FROM centos:7
ENTRYPOINT ["/bin/ping","-c","3"]
CMD ["localhost"]
根据上面的Dockerfile构建镜像, 不带任何参数运行docker run命令
docker build -f dockerfile-test1 -t mycentos .
docker run mycentos
PING localhost (127.0.0.1) 56(84) bytes of data.
64 bytes from localhost (127.0.0.1): icmp_seq=1 ttl=64 time=0.041 ms
64 bytes from localhost (127.0.0.1): icmp_seq=2 ttl=64 time=0.040 ms
64 bytes from localhost (127.0.0.1): icmp_seq=3 ttl=64 time=0.039 ms
--- localhost ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 1999ms
rtt min/avg/max/mdev = 0.039/0.
上面执行的命令是ENTRYPOINT和CMD指令拼接而成. ENTRYPOINT和CMD同时存在时, docker把CMD的命令拼接到ENTRYPOINT命令之后, 拼接后的命令才是最终执行的命令. 但是由于上文说docker run命令行执行时, 可以覆盖CMD指令的值. 如果你希望这个docker镜像启动后不是ping localhost, 而是ping其他服务器, 可以这样执行docker run:
docker run mycentos www.baidu.com
PING www.a.shifen.com (112.80.248.75) 56(84) bytes of data.
64 bytes from 112.80.248.75 (112.80.248.75): icmp_seq=1 ttl=52 time=11.9 ms
64 bytes from 112.80.248.75 (112.80.248.75): icmp_seq=2 ttl=52 time=11.8 ms
64 bytes from 112.80.248.75 (112.80.248.75): icmp_seq=3 ttl=52 time=11.8 ms
--- www.a.shifen.com ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2003ms
rtt min/avg/max/mdev = 11.801/11.858/11.964/0.116 ms
运行docker镜像, 感觉上和执行任何其他的程序没有区别 — 你指定要执行的程序(ping) 和 指定ping命令需要的参数.
注意到参数-c 3, 这个参数表示ping请求只发送3次, 这个参数包括在ENTRYPOINT里面, 相当于硬编码docker镜像中. 每次执行docker镜像都会带上这个参数, 并且也不能被CMD参数覆盖.
永远使用Exec表示法
组合使用ENTRYPOINT和CMD命令式, 确保你一定用的是Exec表示法. 如果用其中一个用的是Shell表示法, 或者一个是Shell表示法, 另一个是Exec表示法, 你永远得不到你预期的效果
说明:
build时默认的文件就是当前文件夹下的 Dockerfile,如果命名是这个,就不用手动置顶 -f 文件
一般也可以手动建一个readme.txt 说明文档
ADD 命令除了和COPY 一样把文件添加到指定位置,还会自动解压
vim Dockerfile
FROM centos:7
MAINTAINER wguo<[email protected]>
COPY readme.txt /usr/local/readme.txt
ADD jdk-8u311-linux-x64.tar.gz /usr/local
ADD apache-tomcat-8.5.77.tar.gz /usr/local
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME usr/local/jdk1.8.0_311
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.77
ENV CATALINA_BASH /usr/local/apache-tomcat-8.5.77
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
ENTRYPOINT ["catalina.sh", "run"]
# 注意: 如果只执行startup.sh的话,因为catalina.sh文件里多了一个 & 符号,有点问题。
docker build -t diytomcat .
将tomcat容器8080端口映射到主机58080,并将容器tomcat下webapps下的test和tomat的logs挂载到主机相应的目录
docker run -d -p 58080:8080 --name wguotomcat -v /home/wguo/build/tomcat/test:/usr/local/apache-tomcat-8.5.77/webapps/test -v /home/wguo/build/tomcat/logs:/usr/local/apache-tomcat-8.5.77/logs diytomcat
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Titletitle>
head>
<body>
<h1>Hello Worldh1>
body>
html>
1、地址https://hub.docker.com/
2、确保自己的用户名和密码能正确登陆
3、在我们自己的服务器上提交自己的镜像
相关命令
docker login -u 用户名 -p 密码
[root@VM-4-17-centos test]# docker login -u guowenjia
Password:
WARNING! Your password will be stored unencrypted in /root/.docker/config.json.
Configure a credential helper to remove this warning. See
https://docs.docker.com/engine/reference/commandline/login/#credentials-store
Login Succeeded
docker push 镜像:标签
注意:
DockerHub 要求在image的名字斜线前面部分必须是本人的用户名,所以一般现改名
docker tag diytomcat:latest guowenjia/diytomcat:1.0
docker push guowenjia/diytomcat:1.0
The push refers to repository [docker.io/wguo/diytomcat]
An image does not exist locally with the tag: wguo/diytomcat
[root@VM-4-17-centos test]# docker push diytomcat:latest
The push refers to repository [docker.io/library/diytomcat]
d928647aed5d: Preparing
c175769dff41: Preparing
a20556739524: Preparing
174f56854903: Preparing
denied: requested access to the resource is denied. # 被拒绝了
[root@VM-4-17-centos test]#
注意:如果有登陆状态,记得 docker logout
1、我们没启动一个docker容器,docekr就会给docker容器分配一个ip,我们只要安装了docker,就会有一个docker0桥接模式,使用的技术是evth-pair技术。
2、所有容器不指定网络的情况下,都是docker0路由的,docker会给我们的容器分配一个默认的可用ip。
Docker中所有网络接口都是虚拟的,虚拟的转发效率很高!(内网传递文件)
只要容器被删除,对应的网桥对就没有了
我们有一个微服务项目,部署到docker上,但是假如有一个模块重启了,这个模块的容器会给我们分配一个新的ip,那么微服务其他部分的配置都需要重新编写?
在容器之间是否可以做到通过某个固定名字访问其他容器?
–link技术
测试:
使用前面我们自定义的tomcat(或者去下载我的版本: docker pull guowenjia/tomcat:1.0-centos7-jdk8)
# 开启一个tomcat01容器
docker run -it --name tomcat01 tomcat:1.0-centos7-jdk8
# 开启另一个tomcat02 并 --link 链接到tomcat01
docker run -it --name tomcat02 --link tomcat01 tomcat:1.0-centos7-jdk8
# ctrl + p + q
# 重新进入tomcat02 ,并测试ping tomcat01
ocker exec -it tomcat02 ping tomcat01
PING tomcat01 (172.17.0.2) 56(84) bytes of data.
64 bytes from tomcat01 (172.17.0.2): icmp_seq=1 ttl=64 time=0.113 ms
64 bytes from tomcat01 (172.17.0.2): icmp_seq=2 ttl=64 time=0.108 ms
...
# 测试发现,tomcat02 现在可以直接ping通tomcat01了
那么tomcat01 现在是否能ping tomcat02?
docker exec -it tomcat01 ping tomcat02
ping: tomcat02: Name or service not known
测试发现,tomcat01 现在不能ping通tomcat02,为什么呢?
探究:
# 查看一下docker 的网关信息
docker network ls
NETWORK ID NAME DRIVER SCOPE
df2770cfdb7e bridge bridge local
7d2ccdcf4f69 host host local
4d6a1649701e none null local
# 进入bridge网关,查看其详情
docker network inspect df2770cfdb7e # 未发现什么特殊配置
# 进入tomcat02的容器详情,查看其配置
docker inspect tomcat02 # 未发现特殊配置
# 进入tomcat02 容器的 hosts文件,查看是否有特殊配置
docker exec -it tomcat02 cat /etc/hosts
172.17.0.2 tomcat01 c5b7e71b9979
# 发现: 在tomcat02 中配置了 tomcat01 映射到了tomat01 的内网ip
# 进入tomcat01,查看 hosts文件,证明此配置是否是 --link的原理
docker exec -it tomcat01 cat/hosts
总结
– link的原理就是修改了容器的 hosts文件。
网络模式
测试:
测试前,先停止所有docker容器,确保测试不被影响
# docekr run 命令默认加了 --net bridge
docker run -it --name tomcat01 tomcat:1.0-centos7-jdk8
docker run -it --name tomcat01 --net bridge tomcat:1.0-centos7-jdk8
# docker0的特点,默认;域名不能访问;--link可以打通
# 我们可以自定义一个网络,然后让我们的服务在自己的网络上启动
# 命令帮助:
docker network create --help
# 主要常用配置项。也是我们在windows上常配的 网络模式,子网掩码,网关。(ip一般可以让docker给我们安装子网自动分配一个)
#--driver(默认 bridge);
#--subnet(子网掩码,如192.168.0.0/16);
#--gateway(网关,如192.168.0.1)
# 上面的配置一般也是默认路由器的配置。
# 创建一个网关
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 tomcatnetwork
81549d8b1a74934c7527b40fc55ecc7d453173be4e9f63c04f57b8e56bac4334
# 查看网关
docker network ls
NETWORK ID NAME DRIVER SCOPE
df2770cfdb7e bridge bridge local
7d2ccdcf4f69 host host local
4d6a1649701e none null local
81549d8b1a74 tomcatnetwork bridge local
# 查看我们自己的网络详情
docker network inspect tomcatnetwork
# 使用,在我们自己的网络上部署容器
docker run -d --name tomcat01 --net tomcatnetwork tomcat:1.0-centos7-jdk8
docker run -d --name tomcat02 --net tomcatnetwork tomcat:1.0-centos7-jdk8
# 再次查看自定义网络详情
docker network inspect tomcatnetwork
...
"Containers": {
"89df51bba65bbf6b934e05ae7aeaa3bbf5e5d5cfefe5d41550f69a5b970f6b7e": {
"Name": "tomcat02",
"EndpointID": "cb886da0251b6031ea8bffa08adb778377842ffa33a35d94da10cd96dc1fbc21",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"98c66101704df7a6f645adc2b33e61ec659f12b7884473592db7c30dfa80f644": {
"Name": "tomcat01",
"EndpointID": "2fe10ecf0973fa7ac2b55086dca734610d420f98885f223c8e3fa988272eb16c",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
}
},
...
]
# 可以看到,网络上已经有我们部署的容器了
再次测试ping连接和ping名字(注意,此时没有使用–link)
# ping 连接
docker exec -it tomcat01 ping 192.168.0.3
PING 192.168.0.3 (192.168.0.3) 56(84) bytes of data.
64 bytes from 192.168.0.3: icmp_seq=1 ttl=64 time=0.105 ms
64 bytes from 192.168.0.3: icmp_seq=2 ttl=64 time=0.072 ms
# ping 名字
docker exec -it tomcat01 ping tomcat02
PING tomcat02 (192.168.0.3) 56(84) bytes of data.
64 bytes from tomcat02.tomcatnetwork (192.168.0.3): icmp_seq=1 ttl=64 time=0.065 ms
我们自定义的网络docker都已经帮我们维护好了对应的关系(而且没有修改我们的hosts文件)。
推荐平时实现容器间通信就使用这种方式
好处:
redis、mysql部署集群时,不同的集群使用不同的网络,保证集群时安全和健康的。
网络联通的核心命令
docker network connect 可以打通一个容器和另外一个网卡的联系
在默认bridge网络下再运行一个容器 10tacmot(docker默认网断)
docker run -d --name 10tacmot tomcat:1.0-centos7-jdk8
创建连接
# docker network connect [OPTIONS] NETWORK CONTAINER
docker network connect tomcatnetwork 10tacmot
测试
# ping 一下
docker exec -it 10tacmot ping tomcat01
PING tomcat01 (192.168.0.2) 56(84) bytes of data.
64 bytes from tomcat01.tomcatnetwork (192.168.0.2): icmp_seq=1 ttl=64 time=0.089 ms
# 查看tomcatnetwork网络详情
docker network inspect tomcatnetwork
...
"Containers": {
"89df51bba65bbf6b934e05ae7aeaa3bbf5e5d5cfefe5d41550f69a5b970f6b7e": {
"Name": "tomcat02",
"EndpointID": "cb886da0251b6031ea8bffa08adb778377842ffa33a35d94da10cd96dc1fbc21",
"MacAddress": "02:42:c0:a8:00:03",
"IPv4Address": "192.168.0.3/16",
"IPv6Address": ""
},
"98c66101704df7a6f645adc2b33e61ec659f12b7884473592db7c30dfa80f644": {
"Name": "tomcat01",
"EndpointID": "2fe10ecf0973fa7ac2b55086dca734610d420f98885f223c8e3fa988272eb16c",
"MacAddress": "02:42:c0:a8:00:02",
"IPv4Address": "192.168.0.2/16",
"IPv6Address": ""
},
"df706104bd07ef9bc49582de272b9459966076ee0f8f556216ef0b16cb526f09": {
"Name": "10tacmot",
"EndpointID": "d9c73ae5ecdd62fc76267c3f7a59ac5fac71f1ba1dbdd9f1476be9d54dc7cafe",
"MacAddress": "02:42:c0:a8:00:04",
"IPv4Address": "192.168.0.4/16",
"IPv6Address": ""
}
},
# 发现:docker已经将10tacmot加入到了tomcatnetwork网络中。这时可以去查看10tacmot的详情,能发现它有两个ip了
# 一个容器,两个ip
# 阿里、腾讯云服务。公网ip,私网ip
# 注意:如果bridge网络中的另一个容器,现在还是没有和tomcatnetwork联通的,如果需要联通,必须再去connect
总结:docker network connect 命令是需要联通的容器加入的了需要联通的网络中
分片+高可用+负载均衡
docker network create --driver bridge --subnet 172.16.0.0/16 --gateway 172.16.0.1 rediscluster
** 创建配置 **
# 通过脚本创建 6 个 redis 配置
for port in $(seq 1 6); \
do \
mkdir -p /home/mydata/redis/node-${port}/conf
touch /home/mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/home/mydata/redis/node-${port}/conf/redis.conf
port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.16.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
** 启动 redis **
# 批量启动
for port in $(seq 1 6); \
do \
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} \
-v /home/mydata/redis/node-${port}/data:/data \
-v /home/mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf \
-d --net rediscluster --ip 172.16.0.1${port} redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
done
# 单个启动服务
docker run -p 6371:6379 -p 16371:16379 --name redis-1 \
-v /home/mydata/redis/node-1/data:/data \
-v /home/mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf \
-d --net rediscluster --ip 172.16.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
# 查看状态,所有容器启动成功
docker ps
** 创建集群 **
# 进入任意一个redis
docker exec -it redis-1 /bin/sh
ls
appendonly.aof nodes.conf
# 创建集群
redis-cli --cluster create 172.16.0.11:6379 172.16.0.12:6379 172.16.0.13:6379 172.16.0.14:6379 172.16.0.15:6379 172.16.0.16:6379 --cluster-replicas 1
# 进入 redis 集群,查看集群信息
redis-cli -c
127.0.0.1:6379> cluster info
** 测试集群**
127.0.0.1:6379> set k helloworld
-> Redirected to slot [7629] located at 172.16.0.12:6379 # 存放到 主机 redis-2 中了
OK
172.16.0.12:6379> get k
"helloworld"
# 重新开一个窗口
# 停掉 redis-2 容器服务
docker stop redis-2
redis-2
# 重新查询 可以看到,redis-2 关闭后,可以从 从机 redis-6 中获取数据
127.0.0.1:6379> get k
-> Redirected to slot [7629] located at 172.16.0.16:6379
"helloworld"
# 重新查看 集群信息
cluster nodes
# 可以发现 172.16.0.12:6379@16379 master,fail
# 且 172.16.0.16:6379@16379 myself,master
mvn clean package -DskipTests
3、编写Dockerfile
注:可在idea中装一个docker插件,然后dockerfile文件就有高亮
FROM java:8
ADD demo-0.0.1-SNAPSHOT.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
CMD ["--server.port=8080"]
4、将jar包和Dockerfile传到服务器,然后build
ls
demo-0.0.1-SNAPSHOT.jar Dockerfile
# build
docker build -t hellodocker:1.0 .
# 镜像安装好后,启动
docker run -it -p 58080:8080 --name hello hellodocker:1.0