这篇文章主要记录了我学习 Docker 的过程,这是第二篇文章,主要包括 Docker 镜像仓库、数据卷、Dockerfile、网络模型等,希望可以帮助到大家~
目录
- Docker 入门实战,看这一篇就够了(一)
- Docker 入门实战,看这一篇就够了(二)
一、Docker 镜像仓库
Docker 的镜像仓库类似于代码仓库,用来存储和分发 Docker 镜像。
发布镜像到阿里云私有仓库
首先登录阿里云的 容器镜像服务控制台,创建私有镜像仓库,需要设置 命名空间 和 仓库名称,然后就可以进行镜像的发布和拉取操作了。
将镜像推送到 Registry
docker login --username=[阿里云账号全名] registry.cn-hangzhou.aliyuncs.com
docker tag [镜像ID] registry.cn-hangzhou.aliyuncs.com/[命名空间]/[仓库名称]:[镜像版本号]
docker push registry.cn-hangzhou.aliyuncs.com/[命名空间]/[仓库名称]:[镜像版本号]
从 Registry 中拉取镜像
docker pull registry.cn-hangzhou.aliyuncs.com/[命名空间]/[仓库名称]:[镜像版本号]
发布镜像到自建私有仓库
搭建私有 Registry
docker pull registry
docker run -d -p 5000:5000 -v /data/registry:/var/lib/registry --privileged=true registry
验证私服库是否安装成功
curl -XGET http://192.168.3.101:5000/v2/_catalog
{"repositories":[]}
将镜像推送到 Registry
docker tag [镜像ID] 192.168.3.101:5000/[仓库名称]:[镜像版本号]
docker push 192.168.3.101:5000/[仓库名称]:[镜像版本号]
执行 push 命令后可能会提示 Get "https://192.168.3.101:5000/v2/": http: server gave HTTP response to HTTPS client,需要修改 Docker 配置,使其支持 HTTP 请求。
vi /etc/docker/daemon.json
# 新增如下配置
"insecure-registries": ["192.168.3.101:5000"]
# 重启 Docker 服务
systemctl restart docker
重启 Docker 服务后自动启动 Registry
docker container update --restart=always f3be969c19db
验证是否推送成功
curl -XGET http://192.168.3.101:5000/v2/_catalog
{"repositories":["mycentos"]}
curl -XGET http://192.168.3.101:5000/v2/mycentos/tags/list
{"name":"mycentos","tags":["v1.0"]}
从 Registry 中拉取镜像
docker pull 192.168.3.101:5000/[仓库名称]:[镜像版本号]
二、Docker 数据卷
数据卷概述
数据卷就是目录或文件,存在于一个或多个容器中,由 Docker 挂载到容器,但不属于 UnionFS,因此能提供一些用于持续存储或共享数据的特性。
数据卷的设计目的就是数据的持久化,完全独立于容器的生命周期,因此 Docker 不会在容器删除时删除其挂载的数据卷。
数据卷主要有以下几个特点:
- 数据卷可在容器之间共享或重用数据。
- 数据卷中的更改可以直接实时生效。
- 数据卷中的更改不会包含在镜像的更新中。
- 数据卷的生命周期一直持续到没有容器使用它为止。
数据卷详解
数据卷的使用
docker run -d -p 5000:5000 -v /data/registry:/var/lib/registry --privileged=true registry
- -v hostDir/File:containerDir/File:挂载数据卷,左边是宿主机目录/文件,右边是容器目录/文件。
- --privileged=true:提升容器权限,使其具有宿主机 root 的操作权限。
数据卷的读写规则
# 读写模式
docker run -d -p 5000:5000 -v /data/registry:/var/lib/registry:rw --privileged=true registry
# 只读模式
docker run -d -p 5000:5000 -v /data/registry:/var/lib/registry:ro --privileged=true registry
- rw:读写模式,默认是读写模式
- ro:只读模式,容器实例被限制,只能读取不能写入
以 ro 模式启动 Registry 后,执行 push 操作失败,查看日志,提示 read-only file system,但是依然能够 pull 之前上传的镜像。
数据卷的继承和共享
# 启动容器 c1,进入 /tmp,写入文件 c1.txt
docker run -it --privileged=true -v /data/v1:/tmp --name c1 centos
cd /tmp && echo c1 > c1.txt
# 启动容器 c2,进入 /tmp,写入文件 c2.txt
docker run -it --privileged=true --volumes-from c1 --name c2 centos
cd /tmp && echo c2 > c2.txt
# 由于 c2 继承了 c1 的数据卷,所以两者的 /tmp 目录下都有 c1.txt 与 c2.txt 文件。
# 同时宿主机的 /data/v1 目录下也存在 c1.txt 与 c2.txt 文件。
ls /data/v1/
c1.txt c2.txt
- --volumes-from:继承指定容器的数据卷规则,相当于两个容器使用相同的
-v配置。
三、Dockerfile
Dockerfile 是用来构建 Docker 镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本。
Dockerfile 常用保留字指令
- FROM:基础镜像,指定一个已经存在的镜像作为模板,第一条必须是 FROM。
- LABEL:添加镜像元数据。
- RUN:容器构建时需要运行的命令。
- EXPOSE:当前容器对外暴露出的端口。
- WORKDIR:指定在创建容器后,终端默认登陆的进来工作目录。
- USER:指定该镜像以什么样的用户去执行,如果都不指定,默认是 root 用户。
- ENV:用来在构建镜像过程中设置环境变量。
- ADD:将宿主机目录下的文件拷贝进镜像且会自动处理 URL 和解压 TAR 压缩包。
- COPY:类似 ADD,拷贝文件和目录到镜像中。
- VOLUME:容器数据卷,用于数据保存和持久化工作。
- CMD:容器启动命令。
- ENTRYPOINT:也是用来指定一个容器启动时要运行的命令。
RUN
# shell 模式,RUN <命令行命令>
RUN yum -y install vim
# exec 模式,RUN ["可执行文件", "参数1", "参数2"]
RUN ["./test.php", "dev", "offline"]
ADD 和 COPY
ADD 和 COPY 指令功能类似,都是从外部往容器内添加文件。但是 COPY 指令只支持基本的文件和文件夹拷贝功能,ADD 则支持更多文件来源类型,比如自动提取 TAR 包,并且可以支持源文件为 URL 格式。
推荐使用 COPY 指令,因为 COPY 指令更加透明,仅支持本地文件向容器拷贝,而且使用 COPY 指令可以更好地利用构建缓存,有效减小镜像体积。
COPY SRC DEST
CMD 和 ENTRYPOINT
CMD 和 ENTRYPOINT 指令都是容器运行的命令入口,这两个指令使用中有很多相似的地方,但是也有一些区别。
这两个指令的相同之处:
- 都支持
shell 模式和exec 模式两种命令格式。推荐使用exec 模式,使用exec 模式启动容器时,容器的 1 号进程就是CMD/ENTRYPOINT中指定的命令。 - 如果存在多个
CMD指令,仅最后一个生效。如果存在多个ENTRYPOINT指令,也仅最后一个生效。
这两个指令的区别:
- 如果使用了
ENTRYPOINT指令,启动 Docker 容器时需要使用--entrypoint参数才能覆盖,而使用CMD设置的命令则可以被docker run后面的参数直接覆盖。 ENTRYPOINT指令可以结合CMD指令使用,也可以单独使用,而CMD指令只能单独使用。
# ENTRYPOINT 结合 CMD 使用案例
FROM nginx
ENTRYPOINT ["nginx", "-c"]
CMD ["/etc/nginx/nginx.conf"]
当指定了 ENTRYPOINT 后,CMD 的含义就发生了变化,不再是直接运行其命令而是将 CMD 的内容作为参数传递给 ENTRYPOINT 指令。
# 不传参运行
docker run nginx:test
# 容器内部执行
nginx -c /etc/nginx/nginx.conf
# 传参运行
docker run nginx:test -c /etc/nginx/new.conf
# 容器内部执行
nginx -c /etc/nginx/new.conf
Dockerfile 实战
基于一个简单的 SpringBoot 项目,进行 Dockerfile 的编写,然后打包镜像,推送镜像仓库,最后拉取镜像运行容器。
编写 Dockerfile 文件
# 基础镜像
FROM adoptopenjdk/openjdk8-openj9:alpine-slim
# 元数据-作者信息
LABEL maintainer="dyhtech <dyhtech@test.com>"
# 创建工作目录
RUN mkdir -p /data/app
# 设置工作目录
WORKDIR /data/app
# 设置数据卷
VOLUME /data/app/logs
# 暴露 8080 端口
EXPOSE 8080
# 拷贝文件到镜像中
COPY ./app.jar ./app.jar
# 容器启动命令
ENTRYPOINT ["java", "-Djava.security.egd=file:/dev/./urandom", "-jar", "app.jar"]
# 动态参数
CMD ["--spring.profiles.active=test"]
打包及推送镜像
# 打包镜像
docker build -t app:1.0.0 .
# 推送镜像
docker tag c5e990a052aa 192.168.3.101:5000/app:1.0.0
docker push 192.168.3.101:5000/app:1.0.0
拉取及运行镜像
# 拉取镜像
docker pull 192.168.3.101:5000/app:1.0.0
# 运行镜像
docker run -d -p 8080:8080 -v /data/app/logs:/data/app/logs --privileged=true 192.168.3.101:5000/app:1.0.0
四、Docker 网络模型
CNM
CNM (Container Network Model) 是 Docker 发布的容器网络标准,意在规范和指定容器网络发展标准,CNM 定义的网络标准包含三个重要元素。
- 沙箱(Sandbox):沙箱代表了一系列网络堆栈的配置,其中包含路由信息、网络接口等网络资源的管理,沙箱的实现通常是 Linux 的 Net Namespace。
- 接入点(Endpoint):接入点将沙箱连接到网络中,代表容器的网络接口,接入点的实现通常是 Linux 的 veth 设备对。
- 网络(Network):网络是一组可以互相通信的接入点,它将多接入点组成一个子网,并且多个接入点之间可以相互通信。
常见网络模式
- null 空网络模式:可以帮助我们构建一个没有网络接入的容器环境,以保障数据安全。
- bridge 桥接模式:可以打通容器与容器间网络通信的需求。
- host 主机网络模式:可以让容器内的进程共享主机网络,从而监听或修改主机网络。
- container 网络模式:可以将两个容器放在同一个网络命名空间内,让两个业务通过 localhost 即可实现访问。
null 空网络模式
使用 Docker 创建 null 空网络模式的容器时,容器拥有自己独立的 Net Namespace,但是此时的容器并没有任何网络配置。在这种模式下,Docker 除了为容器创建了 Net Namespace 外,没有创建任何网卡接口、IP 地址、路由等网络配置。
docker run --net=none -it centos
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
ip route
可以看到 ip addr 只有自带的 lo 网卡,ip route 也没有配置任何路由信息。
bridge 桥接模式
Docker 的 bridge 网络是启动容器时默认的网络模式,使用 bridge 网络可以实现容器与容器的互通,可以从一个容器直接通过容器 IP 访问到另外一个容器。同时使用 bridge 网络可以实现主机与容器的互通,我们在容器内启动的业务,可以从主机直接请求。
Linux veth
veth 是 Linux 中的虚拟设备接口,veth 都是成对出现的,它在容器中通常充当一个桥梁。veth 可以用来连接虚拟网络设备,例如 veth 可以用来连通两个 Net Namespace,从而使得两个 Net Namespace 之间可以互相访问。
Linux bridge
Linux bridge 是一个虚拟设备,是用来连接网络的设备,相当于物理网络环境中的交换机。Linux bridge 可以用来转发两个 Net Namespace 内的流量。
Docker 启动时会在主机上创建 docker0 网桥,docker0 网桥就相当于交换机,而 Docker 创建出的 brige 模式的容器则都会连接 docker0 上,从而实现网络互通。
# 启动两个 centos 容器
docker run -it centos
# 容器外部执行 ip addr
5: docker0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:b3:4e:b2:02 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
53: veth50f9bbc@if52: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether a6:41:4e:c3:7e:84 brd ff:ff:ff:ff:ff:ff link-netnsid 1
55: veth81a5fe4@if54: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue master docker0 state UP group default
link/ether de:94:9e:4e:70:78 brd ff:ff:ff:ff:ff:ff link-netnsid 2
# 两个容器内分别执行 ip addr
52: eth0@if53: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
54: eth0@if55: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:04 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.4/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
可以看到如上图所示结构,每个容器都有一对 veth 与 bridge 连接,最终实现网络互通。
host 主机网络模式
直接使用宿主机的 IP 地址与外界进行通信, 不再需要额外进行 NAT 转换。
- 容器将不会获得一个独立的 Network Namespace, 而是和宿主机共用一个 Network Namespace。
- 容器将不会虚拟出自己的网卡而是使用宿主机的 IP 和端口。
docker run -d --net=host tomcat
# 测试能否通过宿主机 IP 端口访问
curl localhost:8080
container 网络模式
container 网络模式允许一个容器共享另一个容器的网络命名空间。当两个容器需要共享网络,但其他资源仍然需要隔离时就可以使用 container 网络模式。
# 启动容器 1
docker run -it --name centos1 centos
# 查看容器 1 网络配置 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
58: eth0@if59: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
# 启动容器 2
docker run -it --net=container:centos1 --name=centos2 centos
# 查看容器 2 网络配置 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
58: eth0@if59: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default
link/ether 02:42:ac:11:00:03 brd ff:ff:ff:ff:ff:ff link-netnsid 0
inet 172.17.0.3/16 brd 172.17.255.255 scope global eth0
valid_lft forever preferred_lft forever
可以看到容器 1 与 容器 2 的网络配置完全一样。
Docker 网络常用命令
# 查看网络列表
docker network ls [OPTIONS]
# 查看网络详情
docker network inspect [OPTIONS] NETWORK [NETWORK...]
# 删除网络
docker network rm NETWORK [NETWORK...]
# 创建网络
docker network create [OPTIONS] NETWORK
详情访问官网:docker network ls、docker network inspect、docker network rm、docker network create
自定义网络
# 启动两个容器
docker run -it --name centos3 centos
docker run -it --name centos4 centos
# 通过 ip 互 ping,可以 ping 通
ping 172.17.0.2
ping 172.17.0.3
# 通过容器名互 ping,无法 ping 通
ping centos3
ping centos4
# 创建自定义桥接网络
docker network create --driver=bridge mynetwork
# 启动两个容器
docker run -it --name centos5 --network mynetwork centos
docker run -it --name centos6 --network mynetwork centos
# 通过 ip 互 ping,可以 ping 通
ping 172.19.0.2
ping 172.19.0.3
# 通过容器名互 ping,可以 ping 通
ping centos5
ping centos6