docker-学习第五天
- docker-学习第五天
- 1. 昨天的练习回顾
- 1.1. 练习1
- 1.2. 练习2
- 2. 命令
- 2.1. 看镜像的详细信息
- 3. Dockerfile指令
- 3.1. 常见的指令
- 3.2. ENTRYPOINT和CMD的区别
- 3.3. RUN中的set指令
- 4. 镜像的原理
- 4.1. 为什么 Docker 镜像要采用这种分层结构呢?
- 4.2. docker镜像分层的结构
- 4.3. base镜像
- 4.4. 容器的启动
- 4.5. 镜像分层的好处
- 5. 小知识点
- 5.1. 如何让你制作的镜像比较小
- 5.2. 镜像为什么要制作的小一点
docker-学习第五天
1. 昨天的练习回顾
1.1. 练习1
1、以nginx为基础镜像编写dockerfile。并构建为一个名为nginxtest的镜像,要求镜像内安装一个tcpdump工具,下载或从本地拉取一个tar包并解压。构建镜像后运行容器,并暴露端口访问。(要求熟悉dockerfile常用指令,容器运行常见参数,以及如何进入容器内部)
[root@docker-1 Dockerfile]# mkdir yuan
[root@docker-1 Dockerfile]# cd yuan/
[root@docker-1 yuan]# vim Dockerfile
[root@docker-1 yuan]# cat Dockerfile
FROM nginx:latest
WORKDIR /
RUN apt-get update && apt-get install tcpdump -y
RUN curl -O https://nginx.org/download/nginx-1.25.1.tar.gz && tar xf nginx-1.25.1.tar.gz
ENTRYPOINT ["/docker-entrypoint.sh"]
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["nginx", "-g", "daemon off;"]
[root@docker-1 yuan]# [root@docker-1 yuan]# docker build -t scnginx:1.0 .
[+] Building 187.4s (7/7) FINISHED => [internal] load build definition from Dockerfile => => transferring dockerfile: 308B => [internal] load metadata for docker.io/library/nginx:latest => [internal] load .dockerignore => => transferring context: 2B => CACHED [1/4] FROM docker.io/library/nginx:latest => [2/4] RUN apt-get update && apt-get install tcpdump -y => [3/4] RUN curl -O https://nginx.org/download/nginx-1.25.1.tar.gz && tar xf nginx-1.25.1.tar.gz => exporting to image => => exporting layers => => writing image sha256:c2f927b6b99793f3012e5065e75ddd83c663cf0923c33d6f0a4f713fb760da60 => => naming to docker.io/library/scnginx:1.0
[root@docker-1 yuan]# docker images|grep scnginx
scnginx 1.0 c2f927b6b997 5 minutes ago 172MB
[root@docker-1 yuan]# [root@docker-1 yuan]# docker run -d -p 4433:80 --name li-5 scnginx:1.0
9669cf417c8e3cf30bb641b3592924def5c64b15904324e203d467e53c6c29af
[root@docker-1 yuan]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
9669cf417c8e scnginx:1.0 "/docker-entrypoint.…" 2 seconds ago Up 2 seconds 0.0.0.0:4433->80/tcp, :::4433->80/tcp li-5
[root@docker-1 yuan]#
1.2. 练习2
1.以centos7作为基础镜像
2.在里面安装好ip,vim,ping命令
3.安装好nginx
4.启动容器的时候,就启动nginx
[root@docker-1 Dockerfile]# mkdir renlei/
cd /renlei
[root@docker-1 renlei]# vim onekey_install-nginx.sh
[root@docker-1 renlei]# cat onekey_install-nginx.sh
#!/bin/bash#解决软件的依赖关系,需要安装的软件包
yum install epel-release -y
yum -y install zlib zlib-devel pcre pcre-devel gcc gcc-c++ autoconf automake make psmisc lsof vim wget -y#新建luogan用户和组
id lilin || useradd lilin -s /sbin/nologin#下载nginx软件
mkdir /lilin99 -p
cd /lilin99
wget https://nginx.org/download/nginx-1.21.4.tar.gz#解压软件
tar xf nginx-1.21.4.tar.gz
#进入解压后的文件夹
cd nginx-1.21.4#编译前的配置
./configure --prefix=/usr/local/sclilin99 --user=lilin --group=lilin #如果上面的编译前的配置失败,直接退出脚本
if (( $? != 0));thenexit
fi
#编译,启动2个进程去编译,这样速度快
make -j 2
#编译安装
make install#修改PATH变量
echo "PATH=$PATH:/usr/local/sclilin99/sbin" >>/root/.bashrc[root@docker-1 renlei]# [root@docker-1 renlei]# vim Dockerfile
FROM centos:7
RUN yum install iproute vim net-tools -y
WORKDIR /sc
COPY onekey_install-nginx.sh /sc
RUN bash onekey_install-nginx.sh
EXPOSE 80
CMD ["/usr/local/sclilin99/sbin/nginx", "-g", "daemon off;"]
[root@docker-1 renlei]# [root@docker-1 renlei]# docker build -t renweb:1.0 .
[+] Building 342.1s (10/10) FINISHED docker:default=> [internal] load build definition from Dockerfile 0.0s=> => transferring dockerfile: 243B 0.0s=> [internal] load metadata for docker.io/library/centos:7 0.0s=> [internal] load .dockerignore 0.0s=> => transferring context: 2B 0.0s=> CACHED [1/5] FROM docker.io/library/centos:7 0.0s=> [internal] load build context 0.0s=> => transferring context: 881B 0.0s=> [2/5] RUN yum install iproute vim net-tools -y 127.7s=> [3/5] WORKDIR /sc 0.0s => [4/5] COPY onekey_install-nginx.sh /sc 0.0s => [5/5] RUN bash onekey_install-nginx.sh 213.7s => exporting to image 0.6s => => exporting layers 0.6s => => writing image sha256:77d49c8ba1e0f0514fac7833e031ef4fd3b85048e3c546d24659fb88a693937c 0.0s => => naming to docker.io/library/renweb:1.0 0.0s
[root@docker-1 renlei]# [root@docker-1 renlei]# docker run -d -p 4422:80 --name renweb-1 renweb:1.0
730b13da5358627bb843d82f73d840305ba274ca8efbccb44a54b740b1ecf073
[root@docker-1 renlei]# docker ps|grep renweb-1
730b13da5358 renweb:1.0 "/usr/local/sclilin9…" 14 seconds ago Up 13 seconds 0.0.0.0:4422->80/tcp, :::4422->80/tcp renweb-1
[root@docker-1 renlei]#
2. 命令
2.1. 看镜像的详细信息
[root@docker-1 ~] docker inspect hnweb:1.0
要加名字和版本号
3. Dockerfile指令
3.1. 常见的指令
Dockerfile 是一个用来构建 Docker 镜像的文本文件,它包含了一系列的指令,每一条指令都会创建一个新的镜像层。Dockerfile 的指令有以下几种:
-
FROM:这是 Dockerfile 的第一条指令,它指定了镜像的基础层,也就是从哪个镜像开始构建。例如,
FROM ubuntu:18.04
表示从 Ubuntu 18.04 的镜像开始构建。 -
RUN:这是 Dockerfile 的最常用的指令,它用来在镜像中执行一些命令,通常是安装一些软件包或库。例如,
RUN apt-get update && apt-get install -y python3
表示在镜像中更新软件源并安装 Python 3。 -
COPY:这是 Dockerfile 的另一个常用的指令,它用来将本地文件或目录复制到镜像中的指定位置。例如,
COPY app.py /app/
表示将本地的 app.py 文件复制到镜像中的 /app/ 目录下。 -
ADD:这是 Dockerfile 的一个类似于 COPY 的指令,它也用来将本地文件或目录复制到镜像中的指定位置,但是它还有一些额外的功能,如可以解压压缩文件,或者从 URL 下载文件。例如,
ADD https://example.com/file.tar.gz /app/
表示从网址下载 file.tar.gz 文件并解压到镜像中的 /app/ 目录下。 -
CMD:这是 Dockerfile 的一个用来指定容器启动时默认执行的命令的指令,它只能有一条,如果有多条,只有最后一条会生效。例如,
CMD ["python3", "app.py"]
表示容器启动时默认执行python3 app.py
命令。 -
ENTRYPOINT:这是 Dockerfile 的一个用来指定容器启动时的入口点的指令,它和 CMD 类似,但是它可以接受容器运行时传入的参数,而 CMD 不可以。例如,
ENTRYPOINT ["ping"]
表示容器启动时的入口点是ping
命令,如果运行时传入了google.com
参数,那么容器就会执行ping google.com
命令。 -
EXPOSE:这是 Dockerfile 的一个用来声明容器内部的端口号的指令,它不会真正地将端口暴露出来,而是只是一个文档化的作用,表示容器提供了哪些服务。例如,
EXPOSE 80
表示容器提供了 80 端口的服务,如 HTTP。 -
ENV:这是 Dockerfile 的一个用来设置环境变量的指令,它可以在镜像中或者容器运行时使用。例如,
ENV PORT 8080
表示设置环境变量 PORT 的值为 8080。 -
WORKDIR:这是 Dockerfile 的一个用来设置工作目录的指令,它会影响后续的 RUN,COPY,ADD,CMD,ENTRYPOINT 等指令的执行路径。例如,
WORKDIR /app
表示设置工作目录为 /app,那么后续的指令都会在 /app 下执行。 -
VOLUME:这是 Dockerfile 的一个用来创建数据卷的指令,它可以将容器中的某个目录挂载到宿主机上,从而实现数据的持久化和共享。例如,
VOLUME /data
表示创建一个数据卷,将容器中的 /data 目录挂载到宿主机上。 -
USER:这是 Dockerfile 的一个用来指定容器运行时的用户的指令,它可以提高容器的安全性,避免以 root 用户运行。例如,
USER www-data
表示指定容器运行时的用户为 www-data。 -
LABEL:这是 Dockerfile 的一个用来添加元数据的指令,它可以给镜像添加一些标签,如作者,版本,描述等。例如,
LABEL maintainer="John Doe" version="1.0" description="A simple web app"
表示给镜像添加了维护者,版本,描述等标签。 -
ARG:这是 Dockerfile 的一个用来定义构建参数的指令,它可以在构建镜像时传入一些变量,从而实现动态的配置。例如,
ARG PORT=80
表示定义了一个构建参数 PORT,它的默认值为 80,但是可以在构建时修改,如docker build --build-arg PORT=8080 .
。 -
ONBUILD:这是 Dockerfile 的一个用来定义触发器的指令,它可以在当前镜像被用作其他镜像的基础层时执行一些命令。例如,
ONBUILD RUN pip install -r requirements.txt
表示当当前镜像被用作其他镜像的基础层时,会自动执行pip install -r requirements.txt
命令。 -
STOPSIGNAL:这是 Dockerfile 的一个用来指定容器停止时的信号的指令,它可以覆盖默认的 SIGTERM 信号,使用其他的信号来优雅地停止容器。例如,
STOPSIGNAL SIGKILL
表示指定容器停止时的信号为 SIGKILL,强制杀死容器。 -
HEALTHCHECK:这是 Dockerfile 的一个用来检查容器健康状态的指令,它可以定期执行一些命令,来判断容器是否正常运行。例如,
HEALTHCHECK --interval=5m --timeout=3s CMD curl -f http://localhost/ || exit 1
表示每隔 5 分钟执行一次curl -f http://localhost/
命令,如果返回非 0 值,就表示容器不健康。
3.2. ENTRYPOINT和CMD的区别
ENTRYPOINT是比较新的用法,当ENTRYPOINT和CMD都存在的时候,以ENTRYPOINT为准,这个时候,cmd就成了ENTRYPOINT里的参数(参数位置):
ENTRYPOINT ["docker-entrypoint.sh"] EXPOSE 3306 33060 CMD ["mysqld"]
这是 Dockerfile 的三条指令,它们的含义如下:
- ENTRYPOINT [“docker-entrypoint.sh”]:这是用来指定容器启动时的入口点的指令,它表示容器启动时会执行 docker-entrypoint.sh 这个脚本,这个脚本通常是用来初始化一些配置或环境变量,或者执行一些前置操作,如创建数据库,设置密码等。
- EXPOSE 3306 33060:这是用来声明容器内部的端口号的指令,它表示容器提供了 3306 和 33060 这两个端口的服务,这两个端口分别是 MySQL 的默认端口和 X Protocol 的端口,用来提供数据库的连接和访问。
- CMD [“mysqld”]:这是用来指定容器启动时默认执行的命令的指令,它表示容器启动时默认执行 mysqld 这个命令,这个命令是用来启动 MySQL 服务器的,它会接受容器运行时传入的参数,如
--user=root
,--password=123456
等。相当于docker-entrypoint.sh mysqld —》
mysqld就相当于位置参数传递过去了
3.3. RUN中的set指令
set指令能设置所使用shell的执行方式,可依照不同的需求来做设置.
语法 set [±abCdefhHklmnpPtuvx]
参数说明:
-e 若指令传回值不等于0,则立即退出shell。
-u 当执行时使用到未定义过的变量,则显示错误信息。
-x 执行指令后,会先显示该指令的执行过程及所下的参数。
例子:
RUN set -eux; \savedAptMark="$(apt-mark showmanual)"; \apt-get update; \apt-get install -y --no-install-recommends ca-certificates wget; \rm -rf /var/lib/apt/lists/*; \
这是 Dockerfile 的一条 RUN 指令,它用来在镜像中执行一些命令,通常是安装一些软件包或库。这条指令的作用如下:
set -eux
:这是一个 shell 命令,它用来设置一些选项,如:
-e
表示如果命令执行失败,就退出脚本,避免继续执行错误的命令。-u
表示如果使用未定义的变量,就报错,避免使用错误的变量。-x
表示打印出执行的命令,方便调试和查看。
savedAptMark="$(apt-mark showmanual)"
:这是一个赋值语句,它用来将apt-mark showmanual
这个命令的输出赋值给savedAptMark
这个变量。apt-mark showmanual
这个命令是用来显示手动安装的软件包的,这样可以在后面恢复这些软件包的状态。
apt-get update
:这是一个更新软件源的命令,它用来获取最新的软件包信息,以便安装最新的软件包。apt-get install -y --no-install-recommends ca-certificates wget
:这是一个安装软件包的命令,它用来安装
ca-certificates
和
wget
这两个软件包,以及它们的依赖。其中:
-y
表示自动回答 yes,不需要用户交互。--no-install-recommends
表示不安装推荐的软件包,只安装必要的软件包,以减少镜像的大小。
rm -rf /var/lib/apt/lists/*
:这是一个删除文件的命令,它用来删除/var/lib/apt/lists/
这个目录下的所有文件,这个目录是用来存储软件源的信息的,删除它可以减少镜像的大小,因为这些信息在构建镜像后就不需要了。这就是这条 RUN 指令的解释,它的目的是在镜像中安装
ca-certificates
和wget
这两个软件包,并且尽量减少镜像的大小。
RUN set -x \
# create nginx user/group first, to be consistent throughout docker variants&& addgroup --system --gid 101 nginx \&& adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx \&& apt-get update \
这是 Dockerfile 的一条 RUN 指令,它用来在镜像中执行一些命令,通常是安装一些软件包或库。这条指令的作用如下:
set -x
:这是一个 shell 命令,它用来打印出执行的命令,方便调试和查看。addgroup --system --gid 101 nginx
:这是一个创建用户组的命令,它用来创建一个名为 nginx 的系统用户组,它的组 ID 为 101。adduser --system --disabled-login --ingroup nginx --no-create-home --home /nonexistent --gecos "nginx user" --shell /bin/false --uid 101 nginx
:这是一个创建用户的命令,它用来创建一个名为 nginx 的系统用户,它的用户 ID 为 101,它属于 nginx 用户组,它不能登录,它没有创建家目录,它的家目录为 /nonexistent,它的注释为 “nginx user”,它的 shell 为 /bin/false。apt-get update
:这是一个更新软件源的命令,它用来获取最新的软件包信息,以便安装最新的软件包。这就是这条 RUN 指令的解释,它的目的是在镜像中创建一个 nginx 用户和用户组,并且更新软件源。
4. 镜像的原理
4.1. 为什么 Docker 镜像要采用这种分层结构呢?
Docker 镜像是由多个只读层组成的,每一层都是基于上一层的修改。这种分层结构有以下几个优点:
- 节省空间:每一层都可以被多个镜像共享,只需要存储一份即可。例如,如果有多个镜像都基于同一个操作系统,那么操作系统层就只需要存储一次。
- 提高效率:每一层都可以被缓存,从而加快镜像的构建和拉取。例如,如果一个镜像只修改了最上层的文件,那么只需要拉取最上层即可,而不需要拉取整个镜像。
- 增加灵活性:每一层都可以被单独修改,从而实现不同的功能。例如,可以在同一个基础镜像上添加不同的应用程序,或者在同一个应用程序镜像上修改不同的配置文件。
4.2. docker镜像分层的结构
bootfs –》容器启动的时候需要的内容
rootfs –》容器内部的操作系统
这张图片是一个 Docker 容器的内部结构的示意图,它展示了一个容器是如何由不同的软件和配置层叠在一起的。下面我会逐层解释一下:
- 内核层:这是容器的最底层,它是操作系统的核心部分,负责和计算机硬件进行交互。容器共享宿主机的内核,因此不需要在容器中安装内核。
- bootfs 层:这是容器的第二层,它是启动文件系统,负责容器的启动过程。当容器启动完成后,这一层就会被卸载,因此不会占用空间。
- 基础层:这是容器的第三层,它是一个基础操作系统,提供了最基本的环境和工具。这个示例中使用的是 Debian,但也可以使用其他的操作系统,如 Ubuntu,Alpine 等。
- 依赖层:这是容器的第四层,它是在基础层之上添加的一些必要的软件包或库,为应用程序提供了依赖支持。这个示例中没有显示具体的依赖层,但可以想象,如果要运行一个 Python 应用程序,可能需要安装 Python,pip,numpy 等。
- 应用层:这是容器的第五层,它是在依赖层之上添加的一个或多个应用程序或服务,是容器的主要功能部分。这个示例中添加了 Apache 和 emacs 两个应用程序,分别是一个网页服务器和一个文本编辑器。
- 数据层:这是容器的最上层,它是可写的,负责存储容器运行时产生的数据或配置文件。这一层是容器的临时部分,当容器停止时,这一层的修改会被丢弃,除非使用
docker commit
命令将其保存为新的镜像。这就是 Docker 容器的分层结构,每一层都是只读的,除了最上层的数据层,它是可写的。每一层都可以被缓存,共享,复用,从而实现了容器的高效,轻量,灵活的特点。
镜像的内核使用的是宿主机的内核
所有的容器都是共享宿主机的内核kernel
上图 Debian 和 BusyBox上层提供各自的 rootfs,底层共用 Docker Host 的 kernel。
容器只能使用 Host 的 kernel,并且不能修改。所有容器都共用 host 的 kernel,在容器中没办法对 kernel 升级。
这张图片是一个 Linux 操作系统的组件和层次的示意图,它展示了一个操作系统是如何由不同的部分组合在一起的。下面我会逐个解释一下:
- 内核层:这是操作系统的最底层,它是一段负责管理硬件资源,提供系统服务,执行程序的代码。内核是操作系统的核心,决定了操作系统的性能和稳定性。这个示例中使用的是 Linux 内核,它是一个开源的,跨平台的,可定制的内核。
- BusyBox 层:这是操作系统的第二层,它是一个集成了许多常用命令和工具的软件包,如 ls,cp,cat,ping 等。BusyBox 是一个轻量级的,适合嵌入式系统的软件包,它可以提供基本的系统功能,如文件操作,网络通信,进程管理等。
- Debian 层:这是操作系统的第三层,它是一个基于 Linux 内核的发行版,提供了一个完整的,易用的,多样化的操作系统环境。Debian 是一个广泛使用的,有着严格的质量标准和社区支持的发行版,它包含了数以万计的软件包,可以满足各种用户的需求。
- rootfs 层:这是操作系统的最上层,它是一个可写的,负责存储用户数据和配置文件的文件系统。rootfs 是操作系统的根目录,它包含了所有的文件和目录,如 /bin,/etc,/home 等。rootfs 可以使用不同的文件系统类型,如 ext4,btrfs,zfs 等。
这就是 Linux 操作系统的组件和层次,每一层都是由不同的软件或配置构成的,从而实现了操作系统的功能和特性。我希望这个回答能够帮助你理解 Linux 操作系统的原理。
每装个软件,或者说用一次RUN 镜像就会增加一层,镜像就会越大
4.3. base镜像
base 镜像提供的是最小安装的 Linux 发行版
FROM scratch 官方说明:该镜像是一个空的镜像,可以用于构建busybox等超小镜像,可以说是真正的从零开始构建属于自己的镜像
4.4. 容器的启动
容器启动的时候是自下而上的
这个时候涉及到容器的写实拷贝
如果在第三层找到了文件1 那就不用继续往下找了
添加文件 在容器中创建文件时,新文件被添加到容器层中。
读取文件 在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,打开并读入内存。
修改文件 在容器中修改已存在的文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,立即将其复制到容器层,然后修改之。
删除文件 在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。
4.5. 镜像分层的好处
分层的好处在于共享资源,比如说有很多的镜像,可以从base镜像构建而来,那么docker host当中只需要在硬盘上保存一份base镜像即可,同时内存当中也只需要加载一份base镜像即可,也就是说我开多少的相同的镜像,内存在上涨值并不明显,就可以给所有的使用该base镜像的容器提供服务,而且镜像的每一层可以被单独的共享,也就是这一层如果跟其他的镜像重复的话,这一层就可以单独拿出来进行共享。
5. 小知识点
5.1. 如何让你制作的镜像比较小
- 使用基础镜像要小
- 少使用RUN和COPY,ADD、WORKDIR
- 使用镜像启动容器后,再在里面安装软件,使用卷挂载数据
5.2. 镜像为什么要制作的小一点
- 下载快
- 存贮,占用磁盘空间小
- 运行容器的时候占用的内容小
- 资源消耗小