1 容器数据卷
1.1 卷技术介绍
关键字:持久化、同步、数据共享。
通过前面Docker入门的学习,我们可以知道docker的理念就是把应用和环境打包成镜像,这样可以更加方便的去运行容器。
但是现在有一个问题,例如我的容器中的mysql内存储了若干条数据,如果此时容器被删除,那我的数据是不是就丢了呢?(也就是我们常说的删库)这在开发中肯定是不允许存在的!
于是我们就多了这样一个需求:将数据存储在容器以外的地方(例如本地),然后将容器中产生的数据同步到本地,这里就出现了我们接下来要学习的卷技术。
卷技术:说白了就是目录的挂载,将容器中的目录挂在到Linux上,学习过vue的,我们可以将其简单理解成为v-model(双向绑定),其作用是相同的。如图,将容器里的/usr/mysql
挂载到外部linux下的/home/mysql
。现在不仅mysql容器可以使用,其他的2个容器同样可以使用,实现了数据共享。
1.2 使用数据卷
使用命令-v进行挂载(docker run -it -
v
主机目录:容器目录
)
docker run -it -v /home/test:/home centos /bin/bash
这时候用docker inspect
命令来查看容器,可以看到挂载情况。
[root@zlk home]# docker inspect 6a190e62a77b
# 找到Mounts,可以看到绑定的具体详情。
接下来我们停止容器,如果容器停止了,我在外面把文件都删掉了,当再次启动容器后进去查看,发现也是同步的。
经过以上练习操作,相信你也能够体会到容器卷的作用,它为我们带来了极大的便利。这里简单总结一下,如果容器里涉及到需要修改配置的,那么我们采用容器卷技术将容器目录挂在到主机目录后,就不需要进入容器修改了,直接在容器外修改,容器内自动同步!
1.3 实战:安装mysql
mysql的数据持久化问题:
# 获取镜像
[root@zlk home]# docker pull mysql:5.7
# 运行容器, 需要做数据挂载!
# 安装启动mysql,需要配置密码(注意)
# 官方测试, docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
# 启动我们自己的镜像
-d # 后台运行
-p # 端口隐射
-v # 卷挂载
-e # 环境配置
--name # 容器的名字
[root@zlk home]# docker run -d -p 3344:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
9552bf4eb2b69a2ccd344b5ba5965da4d97b19f2e1a78626ac1f2f8d276fc2ba
# 启动成功之后,我们在本地使用navicat链接测试一下
# navicat链接到服务器的3344 --- 3344 和 容器的3306映射,这个时候我们就可以连接上mysql了!
# 在本地测试创建一个数据库,查看下我们的路径是否有效!
此时我们将刚刚创建的容器删除,查看本地数据是否存在?
[root@zlk /]# docker rm -f mysql01
mysql01
[root@zlk /]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e5fe938049b2 tomcat "catalina.sh run" About an hour ago Up About an hour 0.0.0.0:3355->8080/tcp, :::3355->8080/tcp tomcat
[root@zlk mysql]# cd /home/
[root@zlk home]# ll
总用量 0
drwxr-xr-x. 4 root root 30 5月 20 00:26 mysql
[root@zlk home]# cd mysql/
[root@zlk mysql]# ll
总用量 4
drwxr-xr-x. 2 root root 6 5月 20 00:26 conf
drwxr-xr-x. 5 polkitd root 4096 5月 20 00:26 data
通过上述测试,我们发现,挂载到本地的数据卷依然存在,这就实现了容器数据持久化功能。
1.4 匿名和具名挂载
# 匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx # -P 随机指定端口
# 查看所有volume的情况
[root@zlk ~]# docker volume ls
DRIVER VOLUME NAME
local 282edef9101142af114e111818216d05bef294a99c309ecd36170300393d230a
local 447b4b50f4a2551107a62649cdf3b4f944a7ba2872094b2daf5f3bda61924489
# 这里发现,这种情况就是匿名挂载,我们在-v 后面只写了容器内的路径,没有写容器外的路径!
# 具名挂载
[root@zlk ~]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
26da1ec7d4994c76e80134d24d82403a254a4e1d84ec65d5f286000105c3da17
[root@zlk ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
26da1ec7d499 nginx "/docker-entrypoint.…" 3 seconds ago Up 2 seconds 0.0.0.0:32769->80/tcp nginx02
486de1da03cb nginx "/docker-entrypoint.…" 3 minutes ago Up 3 minutes 0.0.0.0:32768->80/tcp nginx01
[root@zlk ~]# docker volume ls
DRIVER VOLUME NAME
local 561b81a03506f31d45ada3f9fb7bd8d7c9b5e0f826c877221a17e45d4c80e096
local 36083fb6ca083005094cbd49572a0bffeec6daadfbc5ce772909bb00be760882
local juming-nginx
# 通过-v 卷名:容器内的路径
# 查看一下这个卷
# docker volume inspect juming-nginx
[root@zlk ~]# docker volume inspect juming-nginx
[
{
"CreatedAt": "2020-08-08T18:15:21+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/juming-nginx/_data",
"Name": "juming-nginx",
"Options": null,
"Scope": "local"
}
]
可以发现docker容器内的所有卷,在没有指定目录的情况下都是存放在/var/lib/docker/volumes/xxxxx/_data
目录下。
通过具名挂载可以方便的找到我们的一个卷,在大多数情况下我们使用的都是具名挂载。
# 如何确定是具名挂载还是匿名挂载,还是指定路径挂载!
-v 容器内路径 # 匿名挂载
-v 卷名:容器内路径 # 具名挂载
-v /主机路径:容器内路径 # 指定路径挂载
知识扩展: -v 卷名:容器内容路径:ro rw 改变读写权限.
ro readonly # 只读
rw readwrite # 可读可写
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:ro nginx
docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx:rw nginx
# ro 只要看到ro就说明这个路径只能通过宿主机来操作,容器内容无法操作。
2 DockerFile
2.1 初识DockerFile
DockerFile就是用来构建Docker镜像的文件!是一个命令脚本!通过这个脚本可以生成镜像,镜像是一层一层的,脚本是一个一个的命令,每个命令都是一层。
创建一个DockerFile文件,名字可以随意(推荐使用dockerfile)。
# 文件中的内容 指令(大写)、参数
FROM centos
VOLUME ["volume01", "volume02"]
CMD echo "----end----"
CMD /bin/bash
# 这里的每一个命令都属于镜像的一层!
启动上述我们自己构建的镜像!
这个卷和外部一定有一个同步的目录!我们来查看一下卷挂载的路径!
docker inspect 容器id,找到Mounts,Source对应的就是数据卷外部挂载路径。
测试一下,看容器内和容器外的文件是否能够进行同步!
这种方式我们在以后的实际应用中会经常用到,因为我们经常会构建自己的镜像。如果在构建镜像的时候没有挂载卷,我们可以手动操作使用具名挂载来实现数据卷的挂载( -v 卷名:容器内路径)!
2.2 数据卷容器
多个mysql同步数据!
启动2个容器,通过我们刚才自己写的镜像启动:
删除容器docker01,查看一下docker02是否可以访问这个文件!
核心代码:
[root@zlk home]# docker run -d -p 3344:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
[root@zlk home]# docker run -d -p 3344:3306 -v /etc/mysql/conf.d -v /var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
小结:
- 容器之间配置信息的传递,数据卷容器的生命周期一直持续到没有容器使用为止。
- 一旦持久化到了本地,本地的数据是不会删除的。
2.3 DockerFile的构建过程
构建步骤:
编写一个dockerFile文件
docker build 构建成为一个镜像
docker run 运行镜像
docker push 发布镜像(DockerHub、阿里云镜像)
基础知识:
- 每个保留关键字(指令)都必须是大写字母
- 执行顺序是从上到下
- #表示注释
- 每一个指令都会创建一个新的镜像层并提交
dockerfile是面向开发的,以后发布项目,制做镜像,就需要编写dockerfile 文件,这个文件的编写相对来说比较简单!
步骤:
- 开发,部署,运维…缺一不可。
- DockerFile:构建文件,定义一切的步骤,也就是源代码。
- DokcerImages:通过dockerFile构建生成的镜像,最终发布和运行产品。
- Dokcer容器:容器就是镜像运行起来,提供服务器。
2.4 DockerFile指令说明
FROM # 基础镜像,一切从这里开始构建
MAINTAINER # 镜像是谁写的,姓名+邮箱
RUN # 镜像构建的是需要运行的命令
ADD # 步骤:tomcat镜像,这个tomcat压缩包!添加内容
WORKDIR # 镜像的工作目录
VOLUME # 挂载的目录
EXPOSE # 保留端口配置
CMD # 指定容器启动时要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT # 指定容器启动时要运行的命令,可以追加命令
ONBUILD # 当构建一个被继承 DockerFile时,就会运行这个指令,触发指令
COPY # 类似ADD,将文件拷贝到镜像中
ENV # 构建的时候,设置环境变量
2.5 创建一个自己的centos
[root@zlk home]# cd /home
[root@zlk home]# mkdir dockerfile
[root@zlk home]# cd dockerfile
[root@zlk dockerfile]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
centos latest 5d0da3dc9764 8 months ago 231MB
[root@k3 dockerfile]# docker run -it centos /bin/bash
[root@ff53460fa928 /]# pwd
/
[root@ff53460fa928 /]# vim #此时的基础镜像不能使用该命令
bash: vim: command not found
[root@ff53460fa928 /]# ifconfig
bash: ifconfig: command not found
[root@ff53460fa928 /]# [root@zlk dockerfile]# #返回到宿主机
# 1. 编写Dockerfile的文件
[root@zlk dockerfile]# vim mydockerfile-centos
[root@zlk dockerfile]# cat mydockerfile-centos
FROM centos
MAINTAINER 星悦糖<1205441442@qq.com>
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 镜像名:[tag] .
[root@zlk dockerfile]# docker build -f mydockerfile-centos -t mycentos:0.1 .
Successfully built d2d9f0ea8cb2
Successfully tagged mycentos:0.1
2.6 CMD 和 ENTRYPOINT 区别
- CMD :指定容器启动时要运行的命令,只有最后一个会生效,可被替代
- ENTRYPOINT :指定容器启动时要运行的命令,可以追加命令
测试CMD:
# 1. 编写dockerfile文件:dockerfile-cmd-test
[root@zlk dockerfile]# vim dockerfile-cmd-test
FROM centos
CMD ["ls", "-a"]
# 2. 构建镜像
[root@zlk dockerfile]# docker build -f dockerfile-cmd-test -t cmdtest .
# 3. run运行, 发现我们的ls -a 命令生效
[root@zlk dockerfile]# docker run -it cmdtest
.
..
.dockerenv
bin
dev
etc
home
lib
lib64
# 想追加一个命令 -l 变成 ls -al
[root@zlk dockerfile]# docker run -it cmdtest -l
docker: Error response from daemon: OCI runtime create failed: container_linux.go:349: starting container process caused "exec: \"-l\": executable file not found in $PATH": unknown.
# cmd的情况下 -l替换了CMD["ls", "-a"]命令, 而-l不是命令,所以报错了
测试ENTRYPOINT:
# 1. 编写dockerfile文件
[root@zlk dockerfile]# vim dockerfile-entrypoint-test
FROM centos
ENTRYPOINT ["ls", "-a"]
# 2. 构建文件
[root@zlk dockerfile]# docker build -f dockerfile-entrypoint-test -t entrypoint-test .
# 3. run运行 发现我们的ls -a 命令同样生效
[root@zlk dockerfile]# docker run -it entrypoint-test
.
..
.dockerenv
bin
dev
etc
home
lib
# 4. 追加命令, 是直接拼接到ENTRYPOINT命令的后面的!
[root@zlk dockerfile]# docker run -it entrypoint-test -l
total 56
drwxr-xr-x 1 root root 4096 Aug 13 07:52 .
drwxr-xr-x 1 root root 4096 Aug 13 07:52 ..
-rwxr-xr-x 1 root root 0 Aug 13 07:52 .dockerenv
lrwxrwxrwx 1 root root 7 May 11 2019 bin -> usr/bin
drwxr-xr-x 5 root root 340 Aug 13 07:52 dev
drwxr-xr-x 1 root root 4096 Aug 13 07:52 etc
drwxr-xr-x 2 root root 4096 May 11 2019 home
lrwxrwxrwx 1 root root 7 May 11 2019 lib -> usr/lib
lrwxrwxrwx 1 root root 9 May 11 2019 lib64 -> usr/lib64
drwx------ 2 root root 4096 Aug 9 21:40 lost+found
# 等价于
[root@zlk dockerfile]# docker run -it entrypoint-test ls -al
3 DockerFile制作Tomcat镜像
3.1 制作tomcat镜像
(1)准备镜像文件 tomcat压缩包,jdk的压缩包!下载xxxxx.tar.gz
Tomcat下载地址:Apache Tomcat® - Apache Tomcat 9 Software Downloads
jdk下载地址:Java Downloads | Oracle
(2)编写Dockerfile文件,官方命名Dockerfile
, build会自动寻找这个文件,就不需要-f指定了
[root@zlk tomcat]# touch readme.txt
[root@zlk tomcat]# vim Dockerfile
[root@zlk tomcat]# cat Dockerfile
FROM centos
MAINTAINER 星悦糖<1205441442@qq.com>
COPY readme.txt /usr/local/readme.txt
ADD apache-tomcat-9.0.65.tar.gz /usr/local/
ADD jdk-8u341-linux-aarch64.tar.gz /usr/local/
# RUN yum -y install vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk1.8.0_341
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.65
ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.65
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8090
CMD /usr/local/apache-tomcat-9.0.65/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.65/bin/logs/catalina.out
(3)构建镜像
[root@zlk tomcat]# docker build -t diytomcat .
Sending build context to Docker daemon 86.09MB
Step 1/14 : FROM centos
---> 5d0da3dc9764
Step 2/14 : MAINTAINER 星悦糖<1205441442@qq.com>
---> Running in 0548f4af1d0f
Removing intermediate container 0548f4af1d0f
---> 75168f3a6e36
Step 3/14 : COPY readme.txt /usr/local/readme.txt
---> be5bab596409
Step 4/14 : ADD apache-tomcat-9.0.65.tar.gz /usr/local/
---> 3a52186d9792
Step 5/14 : ADD jdk-8u341-linux-aarch64.tar.gz /usr/local/
---> 442a346ad766
Step 6/14 : ENV MYPATH /usr/local
---> Running in 04874f9e7f8b
Removing intermediate container 04874f9e7f8b
---> 8a7245c1109b
Step 7/14 : WORKDIR $MYPATH
---> Running in a15142842f2d
Removing intermediate container a15142842f2d
---> f706a36bca35
Step 8/14 : ENV JAVA_HOME /usr/local/jdk1.8.0_341
---> Running in 7b3467ea903a
Removing intermediate container 7b3467ea903a
---> a70a47ddb13c
Step 9/14 : ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
---> Running in c94957f9a965
Removing intermediate container c94957f9a965
---> 39e62227e18c
Step 10/14 : ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.65
---> Running in 50f9131f62d9
Removing intermediate container 50f9131f62d9
---> e1d71111db27
Step 11/14 : ENV CATALINA_BASE /usr/local/apache-tomcat-9.0.65
---> Running in 1915bbb4eecd
Removing intermediate container 1915bbb4eecd
---> 4a74cc721189
Step 12/14 : ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
---> Running in 68029b61d358
Removing intermediate container 68029b61d358
---> c8e395876410
Step 13/14 : EXPOSE 8090
---> Running in 219fc101fda1
Removing intermediate container 219fc101fda1
---> 0957b73bddc4
Step 14/14 : CMD /usr/local/apache-tomcat-9.0.65/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.65/bin/logs/catalina.out
---> Running in 5e38c56eca8f
Removing intermediate container 5e38c56eca8f
---> b4e29f89b02f
Successfully built b4e29f89b02f
Successfully tagged diytomcat:latest
(4)启动镜像
docker run -d -p 9090:8090 --name zlktomcat01 -v /home/zlk/build/tomcat/test:/usr/local/apache-tomcat-9.0.65/webapps/test -v /home/zlk/build/tomcat/tomcatlogs:/usr/local/apache-tomcat-9.0.65/logs diytomcat
# 进入容器
[root@zlk tomcat]# docker exec -it 7475bf858f8b /bin/bash
[root@7475bf858f8b local]# ls -l
total 52
drwxr-xr-x 1 root root 4096 Aug 9 08:14 apache-tomcat-9.0.65
drwxr-xr-x 2 root root 4096 Nov 3 2020 bin
drwxr-xr-x 2 root root 4096 Nov 3 2020 etc
drwxr-xr-x 2 root root 4096 Nov 3 2020 games
drwxr-xr-x 2 root root 4096 Nov 3 2020 include
drwxr-xr-x 8 root root 4096 Aug 9 08:14 jdk1.8.0_341
drwxr-xr-x 2 root root 4096 Nov 3 2020 lib
drwxr-xr-x 3 root root 4096 Sep 15 2021 lib64
drwxr-xr-x 2 root root 4096 Nov 3 2020 libexec
-rw-r--r-- 1 root root 0 Aug 9 08:05 readme.txt
drwxr-xr-x 2 root root 4096 Nov 3 2020 sbin
drwxr-xr-x 5 root root 4096 Sep 15 2021 share
drwxr-xr-x 2 root root 4096 Nov 3 2020 src
[root@7475bf858f8b local]#
(5)访问测试
在Linux上测试:curl localhost:9090
浏览器上测试:阿里云ip地址:9090
项目部署成功, 可以直接访问!我们以后开发的步骤:需要掌握Dockerfile的编写! 我们之后的一切都是使用docker进行来发布运行的!
(6)发布项目(由于做了卷挂载,我们直接在本地编写项目就可以发布了)
在本地tomcat/test
文件夹下创建WEB-INF
文件夹,在WEB-INF
下编写web.xml
文,在/test
下编写index.jsp
文件,再访问http://服务器ip:9090/test/
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
</web-app>
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>hello. xiaofan</title>
</head>
<body>
Hello World!<br/>
<%
System.out.println("-----my test web logs------");
%>
</body>
</html>
3.2 发布自己的镜像到Docker Hub
- 地址:在Docker Hub上注册自己的账号
- 确定这个账号可以登录
# 先登录我们自己的Docker Hub
docker login -u friggly
- 在我们的服务器上提交镜像,提交的时候也是按照镜像的层级一层层push的!
# push到我们的服务器上
[root@zlk test]# docker push diytomcat
Using default tag: latest
The push refers to repository [docker.io/library/diytomcat]
d8fd1ae46933: Preparing
7bb4b739625f: Preparing
8945a6e83d53: Preparing
74ddd0ec08fa: Preparing
denied: requested access to the resource is denied #拒绝
# push 镜像的问题,说明已经存在
[root@zlk test]# docker push zlk/diytomcat:1.0
The push refers to repository [docker.io/zlk/diytomcat]
An image does not exist locally with the tag: zlk/diytomcat
# 解决,增加一个tag,更换名字
docker tag [IMAGE ID] zlk/tomcat:1.0
# 查看镜像
docker images
# 提交自己的镜像,提交成功(自己发布的镜像尽量带上版本号)
docker push zlk/tomcat:1.0
3.3 发布到阿里云镜像服务上(参考官方地址即可)
- 登录阿里云
- 找到容器镜像服务(以我自己的服务器为例)
- 创建命名空间
- 创建容器镜像
点击仓库名称,查看详细信息:(按照下面步骤操作即可)