部分内容来自狂神的视频。
安装
Centos 7之后可以直接通过yum命令安装,先查看系统是否安装docker:
yum list installed | grep docker
没有安装就运行yum install docker -y,安装成功后使用docker --version查看版本。
卸载:
yum remove docker -y
![](https://i-blog.csdnimg.cn/blog_migrate/0b892d48c7484d48315f5d2508384ee3.png)
安装成功后,可以通过systemctl start docker开启docker,然后运行一下hello world查看是否可以正常运行:
![](https://i-blog.csdnimg.cn/blog_migrate/411ed3ee8f0eb4aa13ca559fa8b47af0.png)
但是1.13的版本太老了,已经是17年的了,所以还得卸了,现在是分为CE版本和EE(收费)版本的。
首先安装一些必要的系统工具:
yum install -y yum-utils device-mapper-persistent-data lvm2
然后添加软件源信息,这里用阿里云的镜像:
yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
然后安装最新稳定版本:
yum -y install docker-ce
![](https://i-blog.csdnimg.cn/blog_migrate/1e4402d251a4e32b18281643304df86e.png)
启动方式还是一样systemctl start docker:
![](https://i-blog.csdnimg.cn/blog_migrate/3e9047a4757a798e66b1a07bd4ff8bf0.png)
然后改成国内源,不然后面拉镜像很慢甚至可能timeout,vim /etc/docker/daemon.json
{
"registry-mirrors":["https://6kx4zyno.mirror.aliyuncs.com"]
}
然后重启docker:systemctl restart docker
docker info可以查看docker的信息,可以看到已经换源成功:
![](https://i-blog.csdnimg.cn/blog_migrate/7c49f241b68344d5cb67ae5d4ac3e899.png)
简单使用
# 下载镜像
docker pull image_name
# 运行镜像
docker run -d(后台运行) image_name
这里就以tomcat为例了:
![](https://i-blog.csdnimg.cn/blog_migrate/89f4c11ac35c3556e0e632a4690f7ee7.png)
在windows中用浏览器访问看能不能看到tomcat的欢迎界面:
![](https://i-blog.csdnimg.cn/blog_migrate/8d966d857a15c5eeec57da9bf6f0993c.png)
显然是访问不到的,因为8080是linux的端口号,而容器内tomcat启动的tomcat端口号8080并没有映射到linux的8080,所以是访问不到的。需要通过**-p linux端口:容器端口**,来做一个映射,这样就可以访问到了。首先把tomcat停掉:
![](https://i-blog.csdnimg.cn/blog_migrate/004dc00369a4b17c7411cffdaf450bb0.png)
然后再重新启动,加上-p后再从浏览器进行访问:
![](https://i-blog.csdnimg.cn/blog_migrate/7088f08a8d8ed2e20fa058924466b0cc.png)
tomcat是能访问到的,但是这个版本的tomcat webapps目录下没东西,不像之前有欢迎界面了… 很无语。
进入docker容器
docker exec -it container_id(name) bash
i表示交互式的,也就是保持标准输入输出流打开。t表示虚拟控制台,分配到一个虚拟控制台。
![](https://i-blog.csdnimg.cn/blog_migrate/a32d326b0d8842ced7b90277851c26ff.png)
退出也很简单,直接exit就退出容器了。
但是每次都用ID很不方便,需要提前ps看ID。可以在运行的时候通过–name指定容器名字,这样后续的指令都可以用容器名字代替ID:
![](https://i-blog.csdnimg.cn/blog_migrate/73db991396c87e6bd011cf2adce169ac.png)
–name="my_tomcat"和–name my_tomcat都是可以的。
Docker核心组件
Docker使用客户端-服务器(C/S)架构模式,使用远程API来管理和创建Docker容器。
Docker容器通过Docker镜像来创建。镜像和容器的关系类似于面向对象中类和对象的关系:镜像就是类,容器就是对象。
Docker三个核心要素:镜像(Image)、容器(Container)、仓库(Repository)
镜像
Docker镜像就是一个只读的模板,用来创建Docker容器。
镜像是由许多层的文件系统叠加构成的:
最下面是一个引导文件系统bootfs,主要包含bootloader和kernel,这一层和linux系统是一样的,bootloader来加载内核。
第二层是一个root文件系统rootfs,rootfs是某种操作系统比如ubuntu centos,包含linux系统中的/dev /proc /bin等目录和文件。但是可以发现centos的镜像才200多M,因为它只需要包含最基本的命令(命令都是阉割过的 比如ll vim都没有)、工具和库就可以了,底层的kernel是可以直接用host的。
root文件系统之上有很多层文件系统,这些文件系统叠加在一起,构成docker中的镜像。
运行、进入、停止上面已经提过了。删除镜像是docker rmi image_name,注意不是rm,rm是删除容器。
容器
容器是从镜像创建的运行实例,可以被启动、停止、删除。每个容器都是相互隔离的、保证平台安全。可以看做一个简易版的linux环境,包括root用户权限、进程空间、用户空间、网络空间和运行在其中的应用程序。
Docker利用容器来运行应用,镜像是只读的,容器在启动的时候创建一层可写层作为最上层。
docker ps是查看正在运行中的容器,docker ps -a是查看所有的容器,包括之前已经stop的容器:
![](https://i-blog.csdnimg.cn/blog_migrate/9e7b17da7bd8fd15f574606da531f589.png)
每次run都会新建一个容器,所以可以看到新建了四个tomcat容器,而且都是被stop了。现在通过start就可以将之前停掉的容器重新启动:
![](https://i-blog.csdnimg.cn/blog_migrate/47862a18801b943c21d9da5b15a9b75e.png)
如果觉得开的容器太多了就可以通过docker rm container_id(name)来删除容器,删除的时候必须是停止状态。
查看容器更多信息:
docker inspect container_id(name)
![](https://i-blog.csdnimg.cn/blog_migrate/134d5401edad439f9a12a44d9c4a4de7.png)
只显示正在运行容器的ID:
docker ps -q
![](https://i-blog.csdnimg.cn/blog_migrate/29ffda6756f479b0f2e3cda6f3f86385.png)
停用全部正在运行中的容器:
docker stop $(docker ps -q)
删除全部容器:
docker rm $(docker ps -aq)
然后两条命令也可以通过 & 拼在一起,实现一条命令停用并删除所有容器:
docker stop $(docker ps -q) & docker rm $(docker ps -aq)
![](https://i-blog.csdnimg.cn/blog_migrate/e3254993c46d8e7d5a0bcceef021e8e4.png)
commit镜像
通过上面的描述,可以了解到容器是在镜像之上增加了一个可写层,那如果希望把这个容器“打包”,让别人可以直接pull到这个已经修改过的镜像,就需要通过commit命令,和git是很相似的。
docker commit -m="提交的描述信息" -a="作者" 容器ID 目标镜像名:[TAG]
比如之前的tomcat里webapps目录下是空的,但是webapps.dist目录下有,我们可以手动把下面的东西拷到webapps目录下,这时再通过浏览器就可以访问了:
![](https://i-blog.csdnimg.cn/blog_migrate/78907a39df3992dbdb4ff30d75e716ea.png)
但是这个容器一旦被rm了,下次再启动一个新容器,还得手动拷贝,那就可以把这个容器提交了,下次用这个提交的镜像来新建容器,就不用再手动拷贝了。
![](https://i-blog.csdnimg.cn/blog_migrate/9f9b2fecb4f39c75a8ca3af5d0a83c6c.png)
这样下次通过自己commit的镜像新建容器,就不用手动拷贝东西了,后续还可以把这个镜像发布到阿里云仓库里,这样即使换了虚拟机也可以pull下来。
仓库
仓库是集中存放镜像文件的场所,有时候会把仓库和仓库注册服务器看做同一个事物,并不严格区分。实际上,仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像都有不同的标签(tag)。
仓库分为公开仓库和私有仓库。最大的公开仓库是Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。
用户也可以在本地网络创建一个私有仓库,之后就可以使用push命令将自己创建的镜像传到共有或者私有仓库。
部署nginx
先docker pull nginx,然后运行。在linux中新建一个test.html用来测试。先看一下nginx的html文件夹在哪个位置:
![](https://i-blog.csdnimg.cn/blog_migrate/45b6e759ca4c7b3d3e2cfd0eef1cfa4d.png)
现在需要把linux中的test.html传到容器中的/usr/share/nginx/html文件夹下,命令为:docker cp。具体的格式可以通过–help查看:
![](https://i-blog.csdnimg.cn/blog_migrate/c041e9330914fb41f874a4a10ec918a4.png)
![](https://i-blog.csdnimg.cn/blog_migrate/78cab896ed12694fcf772de4a5173b40.png)
然后在浏览器中访问:
![](https://i-blog.csdnimg.cn/blog_migrate/25c0dd8c480a6b55e181e9bbf271e295.png)
容器数据卷
现在有一个需要解决的问题,就是容器中的数据怎么办,一旦容器删了,数据就没了。需要一种技术可以把容器中的数据同步到本地,而不是靠人工手动拷贝。
这就是容器数据卷的作用:将容器内的目录挂载到linux上。
![](https://i-blog.csdnimg.cn/blog_migrate/c68ff3eadff4985376276ea8874bfb32.png)
总结下来:容器的持久化和同步操作,容器间也是可以数据共享的。
用法一:bind-mount 目录映射
docker run -v 主机目录:容器目录 ......
之后不论是在容器内还是容器外修改目录下的文件,都是同步的,应该是linux的硬链接,也就是文件只有一份。比如数据库就可以把data文件夹挂载到宿主机的目录下。
用法二:volume(匿名/具名挂载)
方法一是指定了一个主机目录,方法二则是不指定(匿名)/只指定一个名字(具名)。匿名的就不演示了,基本不会用,因为生成的名字是随机的。这里一定要区分,指定的名字开头没有 / ,这不代表是相对地址。
![](https://i-blog.csdnimg.cn/blog_migrate/d357ecb8908a94f9630935acce276eb4.png)
使用方法二的,挂载点都在/var/lib/docker/volumes下,如果是匿名的那么文件夹名字就是随机生成的,具名的就是自己指定的名字:
![](https://i-blog.csdnimg.cn/blog_migrate/2c84dc211c09dc2a1c6e31ff3ee5a14d.png)
-v 容器内路径 # 匿名挂载,docker给生成卷名
-v 卷名:容器内路径 # 具名挂载,路径为/var/lib/docker/volumes/卷名
-v /宿主机路径:容器内路径 # 指定路径挂载
容器路径后可以接上 :ro/rw ro表示只读,rw表示可读可写。一旦限定了ro,那么容器就无法对这个文件夹进行写操作了,只能在宿主机进行修改。
Docker自定义镜像
Dockerfile用于构建Docker镜像,Dockerfile文件是由一行行命令语句组成,基于这些命令可以构建一个镜像。
一般Dockerfile分为四个部分:基础镜像信息;维护者信息;镜像操作指令;容器启动时执行指令。
Dockerfile指令:
FROM
格式为FROM <image> 或者 FROM <image>:<tag>。Dockerfile文件的第一条指令必须为FROM指令。并且如果在一个Dockerfile中创建多个镜像时,可以使用多个FROM指令(每个镜像一次)。
MAINTAINER
格式为 MAINTAINER <name>,指定维护者信息。
VOLUME
格式为 VOLUME [“volume_name01”, “volume_name02” …],会在创建容器时自动帮你挂载两个文件夹,容器内的文件夹名称就是指定的名字,宿主机内则还是/var/lib/docker/volumes/随机生成,也就是匿名挂载。
ENV
格式为 ENV <key> <value>,指定一个环境变量,会被后续RUN指令使用,并在容器运行时保持。
ADD
格式为 ADD <src> <dest>,复制指定的<src>到容器中的<dest>。
EXPOSE
格式为 EXPOSE <port> [<port> …],告诉Docker服务端暴露的端口号,供互联系统使用,在启动容器时需要通过 -p映射端口,Docker主机会自动分配一个端口转发到指定的端口。
RUN
格式为 RUN <command>,RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像,命令比较长的时候可以用 \ 来换行。
CMD
指定启动容器时执行的命令,每个Dockerfile只能有一条CMD命令。如果指定了多条CMD命令,只有最后一条会被执行。如果用户启动容器时指定了运行的命令,则会覆盖掉CMD指定的命令。
自定义centos镜像
vim mydockerfile
FROM centos
MAINTAINER youkee<xdutao@foxmail.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
RUN yum install -y vim
RUN yum install -y net-tools
EXPOSE 80
CMD echo $MYPATH
CMD echo "---end---"
CMD /bin/bash
然后在当前目录下:
docker build -f mydockerfile -t mycentos:0.1 .
最后一个 . 不要忘了表示在当前目录下。如果不想通过-f指定文件的路径,只要确保名字叫Dockerfile就可以了。
![](https://i-blog.csdnimg.cn/blog_migrate/6d13939f0874f466108fc7bdb562fba6.png)
![](https://i-blog.csdnimg.cn/blog_migrate/6190c7141201b63a06168ccec9f19310.png)
CMD和ENTRYPOINT区别
CMD:指定这个容器启动时要运行的命令,只有最后一个会生效,可被替代
ENTRYPOINT:指定这个容器启动时要运行的命令,可以追加命令
linux的ls命令可以追加多个参数,比如ls -a只列出来名字,再加个l参数 ls -al就会列举出详细信息:比如读写权限,大小,创建时间等。
现在假如Dockerfile是这么写的:
FROM centos
CMD ["ls", "-a"]
那么build成功后运行会列出工作目录下的所有文件名,如果在run的后面添加一个 -l,就会报错,提醒你 “-l不是一个可执行命令“,因为这里 -l 把 ls -a命令覆盖掉了。
如果Dockerfile是这么写的:
FROM centos
ENTRYPOINT ["ls", "-a"]
那么,run的时候在后面添加 -l,就会列出工作目录下所有文件的详细信息,也就是命令追加成了 ls -al。
自定义Tomcat镜像
需要准备好jdk和tomcat的压缩包
FROM centos
MAINTAINER youkee<xdutao@foxmail.com>
ADD jdk-11.0.10_linux-x64_bin.tar.gz /usr/local/
ADD apache-tomcat-9.0.44.tar.gz /usr/local/
RUN yum install -y vim
ENV MYPATH /usr/local
WORKDIR $MYPATH
ENV JAVA_HOME /usr/local/jdk-11.0.10
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.44
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.44
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
EXPOSE 8080
CMD /usr/local/apache-tomcat-9.0.44/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.44/bin/logs/catalina.out
之后:
docker build -t diytomcat:1.0 .
![](https://i-blog.csdnimg.cn/blog_migrate/541719b7be2826185cb30f68902b7ebd.png)
然后把该镜像运行起来:
docker run -d -p 8080:8080 diytomcat:1.0
浏览器中访问正常:
![](https://i-blog.csdnimg.cn/blog_migrate/01a30b083d737f774dbf9da5fb20781c.png)
这里一定要注意,用jdk 11。因为下的tomcat太新了,虽然官方文档上写的tomcat 9.x只要8以上的就可以,但是用jdk8的时候java -version一直提示没文件,tomcat启动不了。换成11才没问题。
发布镜像到阿里云
首先注册阿里云账号,用户名一定用英文的!不然到时候在控制台登录的时候打中文非常尴尬。
![](https://i-blog.csdnimg.cn/blog_migrate/c1cf68001da45987cb764cc0f87e3fdc.png)
然后创建个人版实例,会让你设置registry密码,可以看到用户名就是阿里云账户名,如果是中文的就得在控制台输中文用户名,密码可以和登录密码一样也可以不一样,随意。
![](https://i-blog.csdnimg.cn/blog_migrate/a10a99534b3054c53ae2f5c1813e3c11.png)
然后分别创建命名空间和镜像仓库,代码源可以关联github的仓库,也可以直接用本地仓库:
![](https://i-blog.csdnimg.cn/blog_migrate/1480ade08293217ea35a06055eaca383.png)
点进去就可以看到已经有文档告诉你怎么发布镜像了,按着提示来就可以:
![](https://i-blog.csdnimg.cn/blog_migrate/27ffb7d8370d1c4a1a2a910c2f72b815.png)
这里就用helloword做测试了,别的镜像都太大了。
![](https://i-blog.csdnimg.cn/blog_migrate/b812b7dfb9f4ebbf4562a9bf608e7f6f.png)
这时可以看控制台的镜像仓库已经有东西了:
![](https://i-blog.csdnimg.cn/blog_migrate/cee6ecf5d4eeb33cf495bd2e0cae8cdf.png)
![](https://i-blog.csdnimg.cn/blog_migrate/c417853d7f848c52599cd4a95761940b.png)
退出登录docker logout就可以。
![](https://i-blog.csdnimg.cn/blog_migrate/a516c435f87bfc3d229056570ef8370f.png)
Docker网络
先用ip addr命令网络:
![](https://i-blog.csdnimg.cn/blog_migrate/2c7f01c0c4959e25fc239d92ec7d9e95.png)
分别是本地回环地址、腾讯云内网地址和docker0地址。
下面运行一个tomcat,然后看一下在容器内部,它的ip地址是多少:
[root@VM-8-10-centos ~]# docker run -d -P --name tomcat01 tomcat
[root@VM-8-10-centos ~]# docker exec -it tomcat01 ip addr
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
82: eth0@if83: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:02 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.2/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
然后**在linux中ping容器内部的这个ip,显然是可以ping通的**,可以发现172.17.0.2是和docker0在同一个网段内的:
![](https://i-blog.csdnimg.cn/blog_migrate/68795d319581e9e5e629a9aeea0cf146.png)
-
每启动一个容器,docker就会给docker容器分配一个ip,只要安装了docker,就会有一个网卡docker0桥接模式,使用的是veth-pair技术。
-
这时,我们运行着tomcat,再次ip addr,可以发现多了一个网卡:
而且可以看到,网卡的开头83: veth510cdfd@if82和刚刚在容器内看ip地址时的网卡名惊人地相似82: eth0@if83。
然后,再运行一个tomcat,查看它的网络信息,发现还是成对出现的:
在tomcat02中ping tomcat01,可以ping通,按理说容器之间是互相隔离的,但是这里却ping通了。
这里的原理大致为:
但是这样还有一个缺陷,就是tomcat01是无法直接ping tomcat02的,必须ping ip地址,这个缺陷可以通过 --link参数解决:
![](https://i-blog.csdnimg.cn/blog_migrate/e606941275f105cd4ddfeb9e7f1f4aab.png)
其实–link的原理也很简单,就是在hosts文件中加了一行
![](https://i-blog.csdnimg.cn/blog_migrate/576aad8ba859216903960c853ce26d3d.png)
这样配一两个容器还可以通过–link解决,但是如果要部署redis集群,需要很多容器,一个个link就太麻烦了,所以可以通过自定义网络来解决这个问题。
自定义网络
在创建容器时,默认使用的网络是bridge,我们可以自定义一个网络,然后创建容器时通过–net networkname显示指定使用的网络,自定义的网络相当于自带DNS,可以直接通过容器名ping通,在部署集群时非常方便。
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
然后通过命令查看现在的网络:
![](https://i-blog.csdnimg.cn/blog_migrate/f4c37abf39ba4a360091760a44a01c7d.png)
![](https://i-blog.csdnimg.cn/blog_migrate/94d168b7fb0ba079564d1230b262c264.png)
现在我们再新建两个tomcat容器,通过–net mynet显示指定网络,而不用–link进行连接。这时再inspect我们新建的网络,可以看到划分的网段就是我们定义的子网:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-d4u4xKeL-1617802449327)(C:\Users\Youkee\AppData\Roaming\Typora\typora-user-images\image-20210407181119131.png)]
这时通过容器名相互ping都是通的:
![](https://i-blog.csdnimg.cn/blog_migrate/34eb18d3d240800337a91697b24f4eda.png)
现在tomcat01和tomcat02用的是docker0,tomcat03和tomcat04用的是mynet,在不同的网段,是肯定ping不通的:
![](https://i-blog.csdnimg.cn/blog_migrate/be97749d8c8d25bf1efca989ee8bc3df.png)
![](https://i-blog.csdnimg.cn/blog_migrate/aff2b217605ed73fa5085384178ac32f.png)
但是docker提供了connect命令,可以打通容器和network:
![](https://i-blog.csdnimg.cn/blog_migrate/7c70a2d798fc81681cd8381174da99aa.png)
docker network connect mynet tomcat01
这时再inspect一下mynet,会发现实现也很暴力,就是给tomcat01又分配了一个地址:
![](https://i-blog.csdnimg.cn/blog_migrate/6f427e40915af8ac5b2154b7e34f1b8d.png)
这时进入tomcat01,查看ip addr,会发现它比之前多了一个网卡:
![](https://i-blog.csdnimg.cn/blog_migrate/c1128cc08718a221ec8819409d631b55.png)
然后ping mynet内的任何容器都是可以通的:
![](https://i-blog.csdnimg.cn/blog_migrate/baf7d946d55b7a4d8aab566334128f98.png)
部署springboot项目
准备一个springboot的hello world,本地调试好打成jar包,然后编写Dockerfile
FROM java:8
COPY *.jar /app.jar
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app.jar"]
将jar包和Dockerfile放在一个文件夹下
![](https://i-blog.csdnimg.cn/blog_migrate/21ce0162aa595861e7c6c99f49810f2b.png)
然后:
docker build -t boot .
下java 8比较慢,需要等一会,build成功后直接run就行了:
docker run -d -p 8080:8080 boot
浏览器访问:
![](https://i-blog.csdnimg.cn/blog_migrate/66e6968b0c0c1f04ae34d7dd4b2da234.png)