Docker 入门实战,看这一篇就够了(二)

2,132 阅读12分钟

这篇文章主要记录了我学习 Docker 的过程,这是第二篇文章,主要包括 Docker 镜像仓库、数据卷、Dockerfile、网络模型等,希望可以帮助到大家~

目录

一、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

ADDCOPY 指令功能类似,都是从外部往容器内添加文件。但是 COPY 指令只支持基本的文件和文件夹拷贝功能,ADD 则支持更多文件来源类型,比如自动提取 TAR 包,并且可以支持源文件为 URL 格式。

推荐使用 COPY 指令,因为 COPY 指令更加透明,仅支持本地文件向容器拷贝,而且使用 COPY 指令可以更好地利用构建缓存,有效减小镜像体积。

COPY SRC DEST

CMD 和 ENTRYPOINT

CMDENTRYPOINT 指令都是容器运行的命令入口,这两个指令使用中有很多相似的地方,但是也有一些区别。

这两个指令的相同之处:

  • 都支持 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 内的流量。

03-Docker-bridge桥接模式.png

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 lsdocker network inspectdocker network rmdocker 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