Skip to content
🗂️ 文章分类: 容器  
🏷️ 文章标签: Docker  
📝 文章创建时间: 2023-10-12
🔥 文章最后更新时间:2025-09-16

[toc]

Docker笔记2

Dockerfile

Dockerfile是用来构建docker镜像的脚本文件,文件内容包含了一条条构建镜像所需的指令和参数。

Dockerfile构建步骤

  1. 编写一个dockerfile文件
  2. docker build将dockerfile构建成为一个镜像
  3. docker run运行镜像
  4. docker push 发布镜像

编写dockerfile的注意事项:

  1. 每个保留关键字(指令)都是尽量是大写字母并且后面至少有一个参数。
  2. dockerfile中的指令是从上到下顺序执行的
  3. #表示注释
  4. 每一个指令都会创建一个新的镜像层并叠加到之前的镜像上。

docker执行dockerfile的流程如下

  1. docker从基础镜像上运行一个容器
  2. docker开始执行dockerfile上的指令,并根据执行对容器进行修改。
  3. 修改容器后,docker在执行docker commit命令,提交一个新的镜像层。
  4. docker基于刚提交的镜像,再运行一个新的容器。
  5. docker执行下一条指令。直到所有指令都执行完成。

dockerfile 文件中的常见指令

Dockerfile 的指令每执行一次都会在原有基础上新建一层新的镜像。所以过多的指令,会造成镜像膨胀过大。

FROM 指定基础镜像

FROM命令是用来指定你需要用哪个镜像作为基础镜像进行构建。

shell
# 写法如下
FROM ubuntu

RUN 用于执行后面的linux命令

RUN指令是用来执行linux命令的指令。

例如ubuntu系统下,需要更新软件包list,就需要执行apt-get update,之后会跟着执行apt-get install

shell
RUN有两种语法格式:
语法1:RUN <命令行命>
# <命令行命令> 等同于,在终端操作的 shell 命令。

语法2:RUN ["可执行文件", "参数1", "参数2"]
# RUN ["./test.php", "dev", "offline"] 等价于 RUN ./test.php dev offline

# 如下
RUN apt-get update -y
RUN apt-get install -y vim

# 一般情况下,多个连续执行的linux命令,都会用“\”做换行分割,再用“&&”将命令串联起来
RUN apt-get update -y \
    && apt-get install -y vim

在Dockerfile中,每写一个RUN指令就会在基础镜像上增加一层镜像,Docker对镜像的层数有限制,所以一般情况下,多个连续执行的linux命令,都会用“\”做换行分割,再用“&&”将命令串联起来。

MAINTAINER 指定创建镜像的维护者

shell
# 写法如下
MAINTAINER shuyx<xxxxxx@qq.com>        #镜像维护者信息

EXPOSE 声明端口

主要用于声明这个镜像需要暴露的端口,以方便配置映射。当使用随机端口映射时,也就是 docker run -P 时,会自动随机映射 EXPOSE 的端口。

shell
# 写法如下
EXPOSE <端口1> [<端口2>...]

WORKDIR 指定工作目录

指定在容器创建后,终端默认登录的工作目录。如该目录不存在,WORKDIR 会自动建立目录。

shell
# 写法如下
WORKDIR <工作目录路>

# 例如,当容器创建后,终端访问容器,会自动进入到/home/shuyx目录中。
WORKDIR /home/shuyx

USER 指定用户

用于指定执行后续命令的用户和用户组,或者切换后续命令执行的用户(用户和用户组必须提前已经存在)。默认为root用户。

shell
# 语法如下
USER <用户>[:<用户>]

# 例如
USER root
······
USER www
······

ENV 定义环境变量

定义了环境变量,那么在在后续的指令中,通过$符号就可以使用这个环境变量。

shell
# 语法如下
ENV <key> <value>
ENV <key1>=<value1> <key2>=<value2>...

# 示例:
ENV MY_PATH=/home/shuyx
ENV BASE_URL www.songpangpang.com

# 后续可以用$来使用之前指定的环境变量
WORKDIR $MY_PATH
RUN curl -SLO "https://$BASE_URL/"

VOLUME 定义容器数据卷

VOLUME 定义容器数据卷。在启动容器时会自动挂载容器数据卷。

shell
# 语法如下
VOLUME ["<宿主机路径>","<镜像路径>"]
VOLUME <>

# 例如
VOLUME ["/data"]

COPY 复制文件或目录到镜像中

将文件或目录复制到镜像中。

COPY可以复制文件,也可以复制目录,可以是相对路径,也可以是绝对路径,docker路径中尽量使用绝对路径,举例如下:

shell
# 写法如下
COPY <源路> <目标路>
# <源路径>:源文件或者源目录,这里可以是通配符表达式
# <目标路径>:容器内的指定路径。路径不存在的话,会自动创建。

# 例如
# 复制home的.env目录的文件到容器的.env目录中
COPY /home/.env /var/www/html/.env
......

# windows系统下
COPY f:/docker/ ./
······

ADD 拷贝文件到镜像中

将宿主机的文件拷贝进镜像中,并且会自动处理URL或解压tar压缩包。

shell
# 语法如下
ADD <源路> <目标路>
# ADD:若源文件为 tar 压缩文件的话,会自动复制并解压到 <目标路径>下。

# 例如
ADD /home/a.txt /var/www/html/

CMD 启动命令

类似于RUN指令,用于运行程序命令。但二者运行的时间点不同,CMD在docker run时运行,RUN是在 docker build时运行。

注意:

  • CMD命令用于启动容器时,需要执行的命令,多个CMD命令,仅最后一个生效
  • CMD指令指定的程序命令可以被 docker run 命令行参数中指定要运行的程序命令所覆盖。
shell
# 语法如下
CMD <shell命> 
CMD ["<可执行文件或命令>","<param1>","<param2>",...] 
CMD ["<param1>","<param2>",...]  # 该写法是为 ENTRYPOINT 指令指定的程序提供默认参数

ENTRYPOINT

类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖,而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序。

但是, 如果运行 docker run 时使用了 --entrypoint 选项,将覆盖 ENTRYPOINT 指令指定的程序。

shell
# 语法如下
ENTRYPOINT ["<可执行文件或命令>","<param1>","<param2>",...]

docker build 命令

当我们编写好Dockerfile文件,便可以通过docker build 命令把Dockerfile文件 创建为镜像。

docker build 命令的用法

shell
# 语法1如下 注意最后有个小点.
# 最后的那个点表示为当前路径下的Dockerfile文件
# 如果Dockerfile文件不在当前路径下,可以通过-f参数来指定Dockerfile文件路径。
docker build -t 新的镜像名称[:版本号] .

# 语法2如下,如果指定了文件路径,就不需要后面加上小点。
# -f 指定Dockerfile的文件路径。
# -t 指定镜像的名字
docker build -f /home/ -t 新的镜像名称[:版本号]

以下是docker build 命令的常用案例。

shell
# 1. 使用当前目录的 Dockerfile 创建镜像,标签为 shuyx/ubuntu:v1
docker build -t shuyx/ubuntu:v1 . 

# 2. 使用URL github.com/creack/docker-firefox 的 Dockerfile 创建镜像
docker build github.com/creack/docker-firefox -t shuyx/ubuntu:v1

# 3. 通过 -f 指定 Dockerfile 文件的位置
docker build -f /path/Dockerfile -t shuyx/ubuntu:v1

注意,在 Docker 守护进程执行 Dockerfile 中的指令前,首先会对 Dockerfile 进行语法检查,如果有语法错误时会返回下面代码。

shell
$ docker build -t shuyx/ubuntu:v1 . 
Sending build context to Docker daemon 2.048 kB
Error response from daemon: Unknown instruction: RUNCMD

dockerfile 基于centos7镜像安装jdk

  1. 下载jdk安装包

下载jdk-8u371-linux-x64.tar.gz,上传到/usr/local/jdk下,这个文件目录需要自己创建

  1. 创建Dockerfile文件,注意Dockerfile的D需要大写

在/usr/local/jdk下创建Dockerfile文件。

shell
# 创建dockerfile文件,并编写
cd /usr/local/jdk
touch Dockefile
vim Dockefile

Dockerfile文件内容如下

shell
# 基础镜像
FROM centos7
#维护者信息
MAINTAINER shuyx<xxxxx@qq.com>          

# 安装vim编辑器,net-tools工具查询ip
RUN yum -y install vim
RUN yum -y install net-tools
# 安装glibc.i686库
RUN yum -y install glibc.i686

# 安装java8
#新建目录
RUN mkdir /usr/local/jdk   
#指定工作目录          
WORKDIR /usr/local/jdk                 

# 将jdk文件拷贝到容器的/usr/local/jdk并解压 
ADD  jdk-8u371-linux-x64.tar.gz /usr/local/jdk
# 设置环境变量 
ENV JAVA_HOME /usr/local/jdk/jdk1.8.0_371
ENV JRE_HOME /usr/local/jdk/jdk1.8.0_371/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
  1. 进入到Dockerfile文件的存放目录下,开始构建镜像

注意要在 Dockerfile 文件的存放目录下,执行构建命令。

shell
# 执行当前目录的Dockerfile来构建镜像
# 镜像名为shuyx/jdk8-01:v1
docker build -t shuyx/jdk8-01:v1 . 

# 查看镜像文件是否构建成功 
docker images
  1. 根据新镜像创建容器并启动
shell
# 开始运行
docker run -d -it --name=jdk1.8 shuyx/jdk8-01:v1 /bin/bash

#进入容器查看JDK是否安装成功 
docker exec -it jdk1.8 /bin/bash 
> java -version

dockerfile 基于ubuntu镜像安装jdk

Dockerfile文件内容如下,其他步骤参考上面。

shell
# 以ubuntu为基础镜像
FROM ubuntu:18.04

# 指明该镜像的作者
MAINTAINER shuyx

# 更新apt源
RUN sed -i s/archive.ubuntu.com/mirrors.aliyun.com/g /etc/apt/sources.list \
    && sed -i s/security.ubuntu.com/mirrors.aliyun.com/g /etc/apt/sources.list 

# 复制linux版本jdk压缩包到镜像内
COPY /jdk-8u381-linux-x64.tar.gz /usr/java/jdk-8u381-linux-x64.tar.gz
# 解压缩jdk到指定目录
RUN mkdir /usr/java/jdk
RUN tar -zxvf /usr/java/jdk-8u381-linux-x64.tar.gz -C /usr/java/jdk

# 设置JAVA_HOME、CLASSPATH、PATH环境变量
ENV JAVA_HOME=/usr/java/jdk/jdk1.8.0_381
ENV CLASSPATH=.:$JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV PATH=$JAVA_HOME/bin:$PATH

RUN apt update
# 设置启动命令,打印jdk版本
CMD ["java", "-version"]

dockerfile 基于jdk镜像部署jar包

  1. 先将jar包上传到宿主机的某个目录中,并在该目录中创建Dockerfile文件

Dockerfile文件内容如下

shell
# 基础镜像
FROM java:8
#维护者信息
MAINTAINER shuyx<xxxxx@qq.com>          

# 将aaa.jar上传到容器中并更名为bbb.jar
ADD aaa.jar bbb.jar

# 运行bbb.jar包
RUN bash -c 'touch /bbb.jar'
ENTRYPOINT ["java","-jar","//bbb.jar"]

# 暴露端口
EXPOSE 8081
  1. 在与Dockerfile文件同目录的情况下,开始构建镜像
shell
# 开始构建镜像
docker build -t shuyx/aaa:v1 .

# 查询镜像
docker images

Docker 网络

Docker网络是什么?

Docker网络是Docker容器之间和容器与外部网络之间的通信和连接的一种机制。在Docker中,每个容器都可以有自己的网络接口、IP地址和网络配置等。

Docker网络能做什么?

在docker中,容器的每次重启都会导致容器的ip发生变化。这也意味着如果容器间使用ip地址来进行互相通信的话,一旦有容器重启,重启的容器将不再能被访问到。

而Docker网络可以实现容器之间的互联以及端口映射,并且容器IP变动的时候可以通过容器名直接网络通信而不受影响。

docker网络 相关的命令

查询docker中的所有网络

shell
# 语法
docker network ls 

# 例如
# 可以通过 docker network ls 命令来查询docker中的网络
qwe@ubuntu:~/Desktop$ docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
e806a6373a89   bridge    bridge    local
ea48da9dbf76   host      host      local
f3ab3fef5588   none      null      local

docker安装后,默认情况会存在上面三种网络,每个网络都有不同的用处。同时我们也可以自定义docker网络。

查询网络的具体信息

shell
# 语法
docker network inspect 网络名称  

# 例如,查询bridge网络的具体信息
docker network inspect bridge

docker_20231022194618.png

创建自定义网络

当不指定网络模式的情况下,创建的自定义网络,默认是bridge(桥接)模式。

shell
# 语法
docker network create 网络名称

# 例如,创建一个网络,名为my_network
docker network create my_network

# 创建网络时是可以添加一系列参数的:
# --driver:驱动程序类型
# --gateway:主子网的IPV4和IPV6的网关
# --subnet:代表网段的CIDR格式的子网
# my_network:自定义网络名称
docker network create --driver=bridge --gateway=192.168.137.1 --subnet=192.168.137.0/16 my_network

让容器连接某个docker网络

注意容器可以连接多个docker网络。

shell
# 语法
docker network connect 网络名称 容器名称

# 例如,mysql01容器连接my_network网络
docker network connect my_network mysql01

让容器断开某个docker网络

shell
# 语法
docker network disconnect 网络名称 容器名称

# 例如,mysql01容器断开my_network网络
docker network disconnect my_network mysql01

删除网络

shell
# 语法如下
docker network rm 网络名称1 网络名称2...

# 例如 删除my_network网络
docker network rm my_network

删除所有没有使用的网络

shell
# 语法如下
docker network prune

Docker 网络的四种模式

Docker 网络有4种模式:host,bridge,none,container。

当创建容器的时候,即docker run的时候,可以给容器设置网络模式。默认的网络模式是bridge。

网络模式命令指定方式介绍
bridge--network=bridge默认网络模式。此模式会为每一个容器单独分配网络、设置IP等,并将容器连接到docker0虚拟网桥。并通过docker0网桥与宿主机通信。
host--network=host容器不会创建自己的网卡,配置ip等,而是使用宿主机的 ip 和端口
none--network=none新创建的容器不会创建自己的网卡和配置自己的ip,而是和一个指定的容器共享ip、端口范围
container--network=container:容器名称或容器id新创建的容器不会创建自己的网卡和配置自己的ip,而是和一个指定的容器共享ip、端口范围
shell
# 例如
docker run mysql:5.7
docker run --network=bridge mysql:5.7
docker run --network=host mysql:5.7
docker run --network=none mysql:5.7
# 通过mysql:5.7镜像创建一个新容器,新容器与tomcat01容器共享网络
docker run --network=container:tomcat01 mysql:5.7
# -net是--network的简写
docker run -net=bridge mysql:5.7
# 例如用ubuntu容器创建容器,指定该容器连接host网络
docker run -it --network=host ubuntu /bin/bash

docker0虚拟网桥

Docker启动的时候会在宿主机上创建一个docker0虚拟网桥,每个容器在默认情况(bridge桥接模式)下会挂载到docker0虚拟网桥上。这样容器就可以和宿主机甚至是其他容器之间互相通讯了。

简而言之,在默认情况(bridge桥接模式)下每创建一个容器,docker0虚拟网桥会分配一个ip给该容器。容器会记录下网桥的ip。这样容器和网桥就会记住双方的ip,因此双方就可以进行通讯。

  • 桥接模式下宿主机和docker通讯是通过docker0虚拟网桥实现的。
  • 桥接模式下docker容器之间通信是通过docker0虚拟网桥中转的,因为docker0虚拟网桥会记录下所有运行的容器的ip。

bridge桥接模式

bridge桥接模式是默认的网络模式。此模式会为每一个容器单独分配网络、设置IP等,并将容器连接到docker0虚拟网桥。并通过docker0虚拟网桥与宿主机通信。

bridge桥接模式原理图 docker_20231022211535.png

如图所示,在容器中的网卡接口叫eth0,在虚拟网桥上的接口叫veth。docker0虚拟网桥上面的每个veth匹配某个容器实例内部的eth0,两两配对。

host主机模式

主机模式下,容器不创建任何网络接口,直接使用宿主机的 ip地址与外界进行通信,不再需要额外进行NAT转换。

host主机模式原理图 docker_20231022212916.png

如图所示,主机模式下容器网络是不需要docker0虚拟网桥的。而是直接使用宿主机的网络。这样的好处是宿主机与容器可以直接通信。

注意:docker run创建容器时,若指定--network=host或-net=host,如果还指定了-p映射端口,此时会警告端口无法使用。

shell
NARNING: Published ports are discarded when using host network mode

因为host主机模式下,通过-p设置的端口参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增。

container 容器模式

container 容器模式原理图 docker_20231022213535.png

如图所示,新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。

none模式

在none模式下,并不为Docker容器进行任何网络配置。即这个Docker容器没有网卡、ip、路由等信息,只有一个lo本地接口。

lo本地接口表示禁用网络功能,即:127.0.0.1,本地回环的意思

自定义网络

为什么使用自定义网络

在docker中,容器的每次重启都会导致容器的ip发生变化。这也意味着如果容器间使用ip地址来进行互相通信的话,一旦有容器重启,重启的容器将不再能被访问到。

而当我们自定义网络,便可以容器IP变动的时候。通过ping + 容器名来直接进行网络通信而不受影响IP变动的影响。

在实际开发中更推荐使用自定义的网络进行容器管理,以及启用容器名称到 IP 地址的自动 DNS 解析。

总结:自定义网络本身就维护了主机名和IP的对应关系(IP和域名都能通)

  1. 创建自定义网络
shell
docker network create my_network
  1. 将容器添加到自定义网络中
shell
# 把容器加入到自定义网络
docker network connect my_network mysql01
docker network connect my_network mysql02
  1. 开始ping+容器名
shell
# 进入到mysql01容器的终端中
docker exec -it mysql01 /bin/bash
> ping mysql02
PING mysql02 (172.19.0.3) 56(84) bytes of data.
64 bytes from mysql02.my_network (172.19.0.3): icmp_seq=1 ttl=64 time=0.269 ms
64 bytes from mysql02.my_network (172.19.0.3): icmp_seq=2 ttl=64 time=0.102 ms
64 bytes from mysql02.my_network (172.19.0.3): icmp_seq=3 ttl=64 time=0.094 ms
64 bytes from mysql02.my_network (172.19.0.3): icmp_seq=4 ttl=64 time=0.082 ms

# 进入到mysql02容器的终端中
docker exec -it mysql02 /bin/bash
> ping mysql01
PING mysql01 (172.19.0.4) 56(84) bytes of data.
64 bytes from mysql01.my_network (172.19.0.4): icmp_seq=1 ttl=64 time=0.132 ms
64 bytes from mysql01.my_network (172.19.0.4): icmp_seq=2 ttl=64 time=0.111 ms
64 bytes from mysql01.my_network (172.19.0.4): icmp_seq=3 ttl=64 time=0.222 ms
64 bytes from mysql01.my_network (172.19.0.4): icmp_seq=4 ttl=64 time=0.333 ms

Released under the MIT License.