Docker-不仅仅是运行环境
学习目标
- 命令 ✔️
- 镜像 ✔️
- 容器数据卷 ✔️
- DockerFile ✔️
- Docker网络原理 ✔️
- SpringBoot整合Docker ✔️
- 集群Docker Compose ✖️
- Docker Swarm 简化-k8s ✖️
Docker
应用更快速的交付和部署
更便捷的升级和扩缩容
更简单的系统运维,开发测试环境都是高度一致的
更高效的计算资源利用----即使内存就2g都可以跑几十个tomcat
docker容器与传统虚拟机的比较:
都什么年代还在用传统虚拟机技术?
- 传统虚拟机是自身虚拟出一个完整的操作系统然后运行起来,然后在这个系统上安装和运行软件,十分麻烦
,并且浪费硬盘空间、内存空间。
- 容器没有像传统虚拟机那样的一个内核,也没有虚拟硬件,它的应用直接应用在宿主机的内核上,因为那个Kernel的复用性强,不必重复安装好多个。
- 每个容器是互相隔离的,每个容器内都有一个属于自己的文件系统,互不影响。
docker的基本组成
镜像:
可以理解为一个模版,可以通过这个模版来创建容器服务。
容器:
利用容器技术,独立运行一个活着一个组应用,通过镜像来创建。
可以理解为一个简易的linux系统。
仓库:
存放镜像的地方。
DockerHub…
命令
docker run ------- 在本机寻找这个镜像 ------ 有镜像:使用这个镜像来运行
------ 无:上DockerHub下载到本地------运行
Docker是怎么工作的
Docker是一个C-S结构的系统,Docker的守护进程运行在主机上,通过Socket从客户端访问。
DockerServer接收到Client的指令,然后执行这条指令。
Docker为什么比VM快
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rokUKQgj-1671432973335)(https://ts1.cn.mm.bing.net/th/id/R-C.d68ffd189931aaed580ae407ded21ead?rik=stQ89ljaHTBgxg&riu=http%3a%2f%2ffavorites.ren%2fassets%2fimages%2f2018%2fdocker%2fdocker_vs_vm.png&ehk=KaRMZJ9Vqmljxt%2fpYhkN%2fWtOMuOOPPvy01Xcfptoc%2f8%3d&risl=&pid=ImgRaw&r=0)]
- docker有着比虚拟机更少的抽象层。
- docker利用的是宿主机的内核,vm需要Guest Kernel
在新建一个容器是,docker不需要像虚拟机一样重新加载一个操作系统内核KErnel,避免引导。虚拟机这样搞太慢了,是分钟级别的,docker则省略了复杂的过程,是秒级的。
帮助命令
docker info
docker version
docker --help
镜像命令
docker images
# options
-a, --all 列出所有信息
-q, --quiet 只显示名字
# 搜索命令
docker search
# 删除命令
docker rmi -f IMAGESID 指定ID删除
docker rmi -f $(docker images -aq) 删除全部容器
容器命令
docker run [可选参数] image
# options
--name 容器名字
-d 后台方式运行 like nohup
-it 使用交互方式运行,进入容器查看内容
-p 指定端口 主机端口:容器端口
-P 随机指定端口
# 启动并进入容器
ubuntu@VM-4-17-ubuntu:~$ sudo docker run --name learn -it ubuntu:20.04 /bin/bash
root@26f55927fbe6:/# ls
bin boot dev etc home lib lib32 lib64 libx32 media mnt opt proc root run sbin srv sys tmp usr var
root@26f55927fbe6:/#
#exit 即可退出,但会停止容器
Ctrl+P+Q 容器不停止并且退出
docker ps
#options
-a 列出当前正在运行的容器+历史运行过的容器
-n=? 显示最近运行过的容器
#删除容器
docker rm 容器ID
docker rm -f
#启动和停止容器
docker start 容器ID
docker stop 容器ID
常用的其他命令
#查看日志
docker logs -tf --tail 显示条数
#查看容器内进程信息
docker top 容器ID
#查看镜像的元数据
docker inspect 容器ID
进入当前正在运行的容器
docker exec -it 容器ID /bin/bash # 进入容器后开启一个新的终端,可以在里面操作 ·常用
docker attach 容器ID /bin/bash # 进入容器正在执行的终端,不会启动新的进程。
从容器内拷贝文件到主机上
docker cp 容器ID:容器内路径 主机路径
具体实际操作可见我的另一篇笔记《用docker挂载方式部署Nginx》
可视化
- portainer
图形化界面管理工具,提供一个后台面板供我们操作
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true portainer/portainer
还挺帅。
狠狠学镜像
轻量级、可执行的独立软件包。所有的应用,直接打包docker镜像,就可以直接跑起来。
自己制作一个镜像------DockerFile
联合文件系统 UnionFS
是一种分层、轻量级并且高性能的文件系统。支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂在到同一个虚拟文件系统下。这个系统是DOcker镜像的基础。镜像可以通过分层来继承,基于基础镜像,可以制作各种具体的应用镜像。
镜像加载原理
Docker镜像的最底层是bootfs,这一层和典型Linux系统是一样的,包含boot加载器和内核。当boot加载完成后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核。
----即电脑开机加载的过程。
rootfs,在bootfs之上,包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不用的操作系统发行版,比如CentOS和Ubuntu等。。。
分层理解
docker镜像默认都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部。
这一层就是所谓的容器层,我们只能在这一层上面进行操作。
一旦run了,就新增了一层了。我们自己写的容器打包之后成为镜像之后,会把我们自己的容器层打包到一个新的镜像中。
docker commit -m="提交的描述信息" -a="作者" 容器ID 目标镜像名:TAG
容器数据卷
数据都在容器中,一般删除容器,数据就会丢失。因此需要:数据可以持久化。
比如:MySQL,容器出问题了,就完蛋了。需要把数据存储在本地。
容器数据卷:容器之间数据共享的技术。
=目录的挂载
使用方式
方式一:使用 -v
docker run -it -v 主机目录:容器内部目录
具体用例可见我的另一篇笔记《使用Docker通过挂载的方式配置Nginx方式》。
可以在docker inspect 容器ID 获取的信息中,查看Mounts的值,获取挂载的信息。
*不管容器是否运行,变化也会同步。但也会占用两倍的存储。
MySQL持久化
首先要记得配置密码:
-e MYSQL_ROOT_PASSWORD=********
# -e 环境配置
运行时就要挂载。
容器内mysql的配置文件在 /etc/mysql/conf.d
数据在 /var/lib/mysql
具名和匿名挂载
匿名挂载
只写-v 和容器内路径,就是匿名挂载。
docker volume ls 查看所有的卷的情况
docker volume inspect volumeID 查看卷的详细信息
我们在-v时没写容器外的路径,就会形成以乱码为名字的匿名卷。
具名挂载
-v 卷名:容器内路径 ,这样就给卷命名了,形成具名挂载。
- 所有docker容器内的卷,在没有指定目录的情况下,都是在
/var/lib/docker/volumes/xxxxx/_data/
文件下。
通过具名挂载可以方便的找到一个卷,大多数情况使用具名挂载。
在容器内路径后面加:ro或者rw,表示只读或者读写。
方式二:用dockerfile VOLUME匿名挂载。
是构建Docker镜像的文件,命令脚本。
docker build
-f dockerfile路径
-t 镜像的名字及标签,通常 name:tag 或者 name 格式
. 单独一个点代表生成在当前目录下
使用dockerfile的命令VOLUME 来挂载数据卷,情况是这样的:在镜像里面就设置好了挂载,每当生成一个容器,可以通过inspect命令查看详细信息,去查看Mounts信息,可以发现已经为每个新容器都匿名挂载了【VOLUME挂载时数据卷数量】个数据卷。
数据卷容器
如果容器之间需要数据同步。
--volumes-from 让不同容器间数据同步。
#类似于 Java中的son extend father
即使父容器没了,子容器中的数据不会有改变。
数据是以双向拷贝的形式进行共享的,虽然会占用多倍空间,但是可以真正实现持久化。灾备的重要性远高于硬盘!
数据卷容器的生命周期一直持续到没有容器使用为止。
但是一旦持久化到了本地,本地的那一份是一定不会被删除的,呦西!
并且如果是3—2—1链式挂载的形式,即使2断了,1和3也不会有任何影响。
DockerFile
----命令参数脚本
每一个指令都会创建提交一个新的镜像层并且提交。
FROM # 基础镜像 ubuntu centos
MAINTAINER #维护者+邮箱 WZL
RUN #在命令前加个RUN即可
ADD # 本地已有的镜像,用ADD添加压缩包
WORKDIR #镜像的工作目录 / 或者 /bin/bash 等等
VOLUME #挂载的目录
EXPOSE #暴露端口配置
CMD #指定容器启动时 需要运行的脚本命令 !!!!!但只有最后一个会生效,可被替代!!!!!
ENTRYPOINT #指定容器启动时 需要运行的脚本命令,可以追加命令
ONBUILD #触发指令,在构建一个被继承DockerFile时,就会运行这个命令
COPY #类似ADD
ENV #构建时设置环境变量
以前都是使用别的人的,现在就可以自己写镜像了。
docker hub 中99%的镜像都是从scratch基础镜像构造起来的。
如下:centos的官方dockerfile
FROM scratch
ADD centos-7-x86_64-docker.tar.xz /
LABEL \
org.label-schema.schema-version="1.0" \
org.label-schema.name="CentOS Base Image" \
org.label-schema.vendor="CentOS" \
org.label-schema.license="GPLv2" \
org.label-schema.build-date="20201113" \
org.opencontainers.image.title="CentOS Base Image" \
org.opencontainers.image.vendor="CentOS" \
org.opencontainers.image.licenses="GPL-2.0-only" \
org.opencontainers.image.created="2020-11-13 00:00:00+00:00"
CMD ["/bin/bash"]
发布到DockerHub
首先注册好自己的账号
#先登录
docker login
-p Password
-u username
docker push IMAGEID
#修改镜像名字或版本号
docker tag IMAGEID newID:newVersion
Docker网络
Docker0
ubuntu@VM-4-17-ubuntu:~$ 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
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP group default qlen 1000
link/ether 52:54:00:c4:f8:29 brd ff:ff:ff:ff:ff:ff
inet 10.0.4.17/22 brd 10.0.7.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 fe80::5054:ff:fec4:f829/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:48:c4:bb:b9 brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
inet6 fe80::42:48ff:fec4:bbb9/64 scope link
valid_lft forever preferred_lft forever
- lo:本机回环地址
- Eth0: 服务器内网地址
- Docker0 :docker自己生成的网卡
分表代表三种不同环境
内部容器之间是怎么互相连接访问的?
容器外可以ping通容器内吗?
测试一下,比如查看一个tomcat容器内部的网卡
#首先进入到容器内部,我这边是进入了/bin/bash
docker exec -it tomcat /bin/bash
#如果下面ip的命令运行报错,那么应该是容器内部阉割版的Linux命令不够,装一个iproute2就行,运行下面两个命令即可
#### apt update && apt install -y iproute2
root@fd9cbbe589e4:/usr/local/tomcat# 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
40: eth0@if41: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:06 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.6/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
eth0@if41?
这是容器启动时,docker分配的ip地址。然后ping一下这个172.17.0.6
PING 172.17.0.6 (172.17.0.6) 56(84) bytes of data.
64 bytes from 172.17.0.6: icmp_seq=1 ttl=64 time=0.037 ms
64 bytes from 172.17.0.6: icmp_seq=2 ttl=64 time=0.047 ms
64 bytes from 172.17.0.6: icmp_seq=3 ttl=64 time=0.044 ms
答案是可以的。
那么,容器之间可以ping通吗?
我们每启动一个docker 容器,docker就会给容器分配一个IP,只要电脑安装了Docker,就会有一个网卡叫docker0 。
是桥接模式,使用的技术是evth-pair技术。
这时候再去主机的ip addr里面找,就能找到新创建的这个tomcat的网卡了,
41: veth9a89274@if40: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether 3e:65:41:ec:f2:f5 brd ff:ff:ff:ff:ff:ff link-netnsid 4
inet6 fe80::3c65:41ff:feec:f2f5/64 scope link
valid_lft forever preferred_lft forever
和上面容器内的网卡是一对的(40/41)
这就是evth-pair技术
是一对的虚拟设备接口,都是成对出现的,一端连着协议,一端彼此相连,可以通信。
正因为有这个特性,这个技术就可以当做一个桥梁来使用,来连接各种虚拟网络设备的。
openstack、docker容器之间的连接,OVS的连接,都是使用这个技术。
ok,现在来试试不同容器间能否ping通吧
sudo docker run -d --name tomcat-pair -p 8089:8080 tomcat:8.5.81
root@15d4dfa764ee:/usr/local/tomcat# 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
44: eth0@if45: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:07 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.7/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 44/45 ok 开始ping测试
#。。。。。。无语,连net-tools都没有,还是需要自己 apt install net-tools && apt install iputils-ping
root@15d4dfa764ee:/usr/local/tomcat# ping 127.17.0.6
PING 127.17.0.6 (127.17.0.6) 56(84) bytes of data.
64 bytes from 127.17.0.6: icmp_seq=1 ttl=64 time=0.015 ms
64 bytes from 127.17.0.6: icmp_seq=2 ttl=64 time=0.031 ms
答案是可以ping通的~
docker0类似于一个交换机,会给容器分配一个默认的可用IP。
Docker中的所有网络接口都是虚拟的,因为虚拟的是最快的。
细节的东西我也不懂了,我还没学计算机网络原理,所以就先学到这边吧。
–link
两个容器之间不用通过地址来ping通,想要只用名字来通信。
ubuntu@VM-4-17-ubuntu:~$ sudo docker exec -it tomcat-pair ping tomcat
ping: tomcat: Name or service not known
无法ping通,遗憾离场
这时候就需要–link来了,在run容器的时候加入命令 --link 【需要链接的容器名字】 之后就可以在这个容器里ping通link的容器了。但是反向之后就不行了,所以这是一个单项网络。
SpringBoot打包Docker镜像
首先可以在IDEA里面下载Docker的插件,然后在项目根目录下创建Dockerfile,就能使用到插件带来的高亮提示和自动补齐dockerfile语句的功能了,以下给出一个示例
FROM java:8
MAINTAINER "luke <348358584@qq.com>"
COPY *.jar /app.jar
CMD ["--server.port=8080"]
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]
然后上传到服务器时就把jar包和Dockerfile上传就行了,在服务器上build之后run一下,直接起飞。