1、创建Docker Image步骤
使用Dockerfile创建自己的Docker Image,可以概括为以下几个步骤:
- 创建一个
Dockerfile
,其中包含设置应用程序的所有指令。 - 使用
docker build
命令,指定Dockerfile和镜像的标记名称,构建本地镜像。 - 使用
docker push
命令,将本地镜像推送到公共Docker Hub注册表。
2、Dockerfile指令和参数解释
Dockerfile
是一个文本文件,采用特定格式编写,包含以下元素:
- 指令:如
FROM
,RUN
,COPY
,ENTRYPOINT
等,指示Docker在创建镜像时执行的操作。 - 参数:对指令的具体化,如安装的软件包、复制的文件路径等。
Dockerfile中的指令和参数是构建Docker镜像的基础。下面是对这些指令及其参数的详细解释:
指令(Instruction)
-
FROM - 指定基础镜像。所有Docker镜像都是从一个基础镜像开始构建的。例如,
FROM ubuntu:18.04
表示使用Ubuntu 18.04作为镜像的基础。 -
RUN - 执行命令。通常用于安装软件包、更新系统或运行任何所需的命令。例如,
RUN apt-get update && apt-get install -y curl
会更新软件包列表并安装curl。 -
COPY - 将文件或目录从构建上下文(通常是Dockerfile所在的目录)复制到镜像中。例如,
COPY . /app
会将当前目录(.
)复制到镜像中的/app
目录。 -
ENTRYPOINT - 配置容器启动时执行的命令。它允许您定义容器的运行行为。例如,
ENTRYPOINT ["python", "app.py"]
定义了容器启动时运行python app.py
。 -
EXPOSE - 声明容器运行时监听的端口,不用于端口映射,但用于文档化。例如,
EXPOSE 80
表示容器将监听80端口。 -
ENV - 设置环境变量。这些变量在后续的RUN指令中可用。例如,
ENV PATH /usr/local/bin:$PATH
会更新PATH环境变量。 -
WORKDIR - 设置工作目录。这是RUN、CMD、ENTRYPOINT等指令的执行环境。例如,
WORKDIR /app
会将工作目录设置为/app
。 -
VOLUME - 创建一个可以从本地主机或其他容器挂载的挂载点。例如,
VOLUME /data
创建一个挂载点/data
。 -
CMD - 提供容器启动时的默认执行命令。如果用户指定了命令,则会覆盖这个默认值。例如,
CMD ["python", "app.py"]
。
参数(Argument)
参数是指令的具体化,它们告诉Docker如何执行指令:
- 软件包名称:在
RUN
指令中,参数可以是软件包的名称,如RUN apt-get install -y nginx
中的nginx
。 - 文件路径:在
COPY
指令中,参数可以是文件或目录的路径,如COPY . /app
中的.
和/app
。 - 命令和参数:在
ENTRYPOINT
和CMD
指令中,参数是容器启动时要执行的命令和其参数,如ENTRYPOINT ["python", "app.py"]
中的python
和app.py
。 - 端口号:在
EXPOSE
指令中,参数是容器要监听的端口号,如EXPOSE 80
中的80
。
参数的正确使用对于确保Docker镜像按照预期构建和运行至关重要。通过精确指定参数,您可以控制镜像的内容和行为,从而满足应用程序的需求。
3、Docker 镜像的分层结构
Docker镜像采用分层结构构建,每条指令创建一个新的层,仅包含相对于前一层的更改。这种结构的优势包括:
- 大小优化:由于每层只存储更改,因此镜像大小得以优化。
- 构建效率:在构建过程中,Docker会利用缓存,提高构建速度。
- 易于维护:如果构建失败或需要添加新步骤,可以只针对特定层进行修改。
4、Dockerfile练习
4.1 创建Dockerfile
由于下载的ubuntu:22.04镜像(最小化安装版本)缺省必要的软件,所以重新构建Docker image,下载必须的软件,方便使用。
root@host1:~# docker --version
Docker version 27.1.1, build 6312585root@host1:~#
root@host1:~# cat Dockerfile
FROM ubuntu:22.04
RUN apt-get -qq update \&& apt-get -qq install vim -y \&& apt-get -qq install iproute2 -y \&& apt-get -qq install iputils-ping -y \&& apt-get -qq install openssh-server -y \&& rm -rf /var/lib/apt/lists/*
RUN mkdir /var/run/sshd
RUN echo "root:openstack" | chpasswd
RUN echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
CMD ["/usr/sbin/sshd", "-D"]
root@host1:~#
这段Dockerfile定义了一个基于Ubuntu 22.04操作系统的镜像,并且安装了Vim编辑器、网络工具、SSH服务,并配置了SSH服务以允许root用户登录。以下是对这段Dockerfile的逐行解释:
基础镜像指定:
FROM ubuntu:22.04
:指定基础镜像为Ubuntu 22.04版本。更新软件包列表并安装软件:
RUN apt-get -qq update
:静默模式下更新软件包列表(&& apt-get -qq install vim -y
:静默模式下安装Vim编辑器,并使用-y
参数自动接受安装过程中的提示。&& apt-get -qq install iproute2 -y
:安装iproute2
,包含ip
命令,用于网络配置。&& apt-get -qq install iputils-ping -y
:安装iputils-ping
,提供ping
命令。&& apt-get -qq install openssh-server -y
:安装SSH服务。&& rm -rf /var/lib/apt/lists/*
:清理apt
缓存,减少镜像大小。创建SSH服务运行目录:
RUN mkdir /var/run/sshd
:创建SSH服务需要的运行目录。设置root用户的密码:
RUN echo "root:openstack" | chpasswd
:设置root用户的密码为openstack
。配置SSH服务允许root用户登录:
RUN echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
:向SSH配置文件追加一行,允许root用户通过SSH登录。定义容器启动命令:
CMD ["/usr/sbin/sshd", "-D"]
:定义容器启动时执行的命令,这里启动SSH服务,并以守护进程模式运行(-D
参数)。注意事项:
- 使用
&&
连接各个命令,确保只有在前一个命令成功执行后,才会执行下一个命令。rm -rf /var/lib/apt/lists/*
这一步是为了清理apt缓存,避免镜像变得过大。但请注意,这可能会影响后续需要依赖这些缓存的构建步骤。PermitRootLogin yes
配置允许root用户登录,这在生产环境中可能带来安全风险,应根据实际情况谨慎配置。CMD
指令定义了容器的默认执行命令,如果需要覆盖默认命令,可以在运行容器时指定。这个Dockerfile构建的镜像将提供一个具有SSH服务的Ubuntu环境,允许root用户登录并使用Vim编辑器和网络工具。
4.2 构建本地镜像
root@host1:~# docker build -t ub-test:22.04 .
[+] Building 0.0s (9/9) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 452B 0.0s=> [internal] load metadata for docker.io/library/ubuntu:22.04 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> [1/5] FROM docker.io/library/ubuntu:22.04 0.0s=> CACHED [2/5] RUN apt-get -qq update && apt-get -qq install vim -y && apt-get -qq install iproute2 -y && apt-get -qq install iputils-ping -y && apt-get -qq install openssh-server -y && rm -rf /var/lib/apt/ 0.0s=> CACHED [3/5] RUN mkdir /var/run/sshd 0.0s=> CACHED [4/5] RUN echo "root:openstack" | chpasswd 0.0s=> CACHED [5/5] RUN echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config 0.0s=> exporting to image 0.0s=> => exporting layers 0.0s=> => writing image sha256:627c68158e9d5cfb1a1ded9d214df939afc65158538683e027170cc5babaf634 0.0s=> => naming to docker.io/library/ub-test:22.04 0.0s
root@host1:~# root@host1:~# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ub-test 22.04 627c68158e9d 26 hours ago 234MB
ubtest 22.04 627c68158e9d 26 hours ago 234MB
ubuntu 22.04 8a3cdc4d1ad3 6 weeks ago 77.9MB
hello-world latest d2c94e258dcb 15 months ago 13.3kB注:
ubtest:22.04和ub-test:22.04采用相同的Dockerfile构建,由于ub-test:22.04是后面创建的,所以采用缓存构建。
这段输出是使用Docker构建镜像时的日志。以下是对日志的逐步解释:
构建命令:
docker build -t ub-test:22.04 .
:这是执行的Docker构建命令。-t
选项用于标记镜像名称和标签,这里设置为ub-test:22.04
。.
表示Dockerfile位于当前目录。构建过程:
[+] Building 0.0s (9/9) FINISHED
:表示构建过程已经完成,总共有9个步骤,全部完成。构建步骤:
- 每个
=>
后面的步骤表示Docker构建过程中的一个阶段。这里显示了5个主要的构建阶段,每个阶段都以CACHED
标记,意味着这些步骤在之前的构建中已经完成,并且缓存了结果,因此在这次构建中没有重新执行。阶段详解:
构建层:
[1/5] FROM docker.io/library/ubuntu:22.04
:第一个构建层,基于Ubuntu 22.04镜像。[2/5] RUN ...
:第二个构建层,执行了多个RUN
命令,包括更新软件包列表、安装软件包、清理apt缓存等。[3/5] RUN mkdir /var/run/sshd
:第三个构建层,创建SSH服务运行目录。[4/5] RUN echo "root:openstack" | chpasswd
:第四个构建层,设置root用户的密码。[5/5] RUN echo 'PermitRootLogin yes' >> /etc/ssh/sshd_config
:第五个构建层,配置SSH服务允许root用户登录。导出镜像:
=> exporting to image
:开始导出构建的镜像。=> writing image sha256:627c68158e9d5cfb1a1ded9d214df939afc65158538683e027170cc5babaf634
:镜像写入完成,显示了镜像的唯一标识符(SHA256哈希)。命名镜像:
=> naming to docker.io/library/ub-test:22.04
:将构建好的镜像命名为ub-test:22.04
。构建完成:
- 最后,用户通过命令行提示符
root@host1:~#
回到命令行,表示构建过程已经结束。这个日志显示了Docker镜像构建的整个过程,包括加载Dockerfile、构建层、导出镜像以及命名镜像等步骤。由于使用了缓存,所以构建过程非常快。
4.3 使用自己的镜像创建Docker container
root@host1:~# docker run --name test --hostname test --ip 10.0.20.90 --network macvlan-net -d ub-test:22.04
4125f87a9e6a1242435b03b86329ec240126d6b003ba783d8b7be9d035cde0c5
root@host1:~#root@host1:~# docker exec -it test /bin/bash
root@test:/# ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00inet 127.0.0.1/8 scope host lovalid_lft forever preferred_lft foreverinet6 ::1/128 scope host valid_lft forever preferred_lft forever
12: eth0@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP group default link/ether 02:42:0a:00:14:5a brd ff:ff:ff:ff:ff:ff link-netnsid 0inet 10.0.20.90/24 brd 10.0.20.255 scope global eth0valid_lft forever preferred_lft forever
root@test:/# ping 10.0.20.2
PING 10.0.20.2 (10.0.20.2) 56(84) bytes of data.
64 bytes from 10.0.20.2: icmp_seq=1 ttl=128 time=0.391 ms
64 bytes from 10.0.20.2: icmp_seq=2 ttl=128 time=0.309 ms
^C
--- 10.0.20.2 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1020ms
rtt min/avg/max/mdev = 0.309/0.350/0.391/0.041 ms
root@test:/# root@test:/# service ssh status* sshd is running
root@test:/#