Docker镜像分层技术
Docker镜像分层技术
在之前的两篇文章《使用Dockerfile制作镜像》和《Docker制作镜像的两种方式》中,我们对如何构建和存储镜像有了一定的认识,并且了解到镜像是docker的核心,镜像的概念主要就是把运行环境和业务代码进行镜像打包。
这一篇文章我们再来初步认识下镜像是如何分层的。
每个镜像都可以分成很多个layer,每个layer都有它对应的ID和大小,比如一个php镜像有10多层组成,如下图:
可以看到,拉取了11个目录包含的11个镜像层下来,每一层在docker目录下都有其对应自己的目录。
/var/lib/docker/image/overlay2:存储镜像管理数据的目录,以使用的存储驱动命名。比如这里的驱动就是overlay2。
一、关于base镜像
1、base 镜像有两层含义:
1)不依赖其他镜像,从 scratch 构建。
2)其他镜像可以之为基础进行扩展。
所以,能称作 base 镜像的通常都是各种 Linux 发行版的 Docker 镜像,比如 Ubuntu, Debian, CentOS 等。
base 镜像提供的是最小安装的 Linux 发行版。
我们大部分镜像都将是基于base镜像构建的。所以,通常使用的是官方发布的base镜像。可以在docker hub里找到。比如centos: https://hub.docker.com/_/centos
点击版本可以看到github里的Dockerfile。
1 FROM scratch 2 ADD centos-7-docker.tar.xz / 3 4 LABEL org.label-schema.schema-version="1.0" \ 5 org.label-schema.name="CentOS Base Image" \ 6 org.label-schema.vendor="CentOS" \ 7 org.label-schema.license="GPLv2" \ 8 org.label-schema.build-date="20181205" 9 10 CMD ["/bin/bash"]
ADD 命令将本地的centos7的tar包添加到镜像,并解压到根目录/下。生成/dev、/proc/、/bin等。我们可以自己构建docker base镜像,也可以直接使用已有的base镜像。比如centos。我们可以直接从docker hub上拉取。
2、拉取镜像
docker pull centos
3、查看镜像
1 # docker images centos 2 REPOSITORY TAG IMAGE ID CREATED SIZE 3 centos latest 1e1148e4cc2c 2 months ago 202MB
可以看到最新的centos镜像只有200mb,是不是觉得太小了?这是因为docker镜像在运行的时候直接使用docker宿主机器的kernel。
Linux操作系统由内核空间和用户空间组成。
内核空间是kernel,用户空间是rootfs, 不同Linux发行版的区别主要是rootfs.比如 Ubuntu 14.04 使用 upstart 管理服务,apt 管理软件包;而 CentOS 7 使用 systemd 和 yum。这些都是用户空间上的区别,Linux kernel 差别不大。
所以 Docker 可以同时支持多种 Linux 镜像,模拟出多种操作系统环境。
需要注意的是:
base镜像只是用户空间和发行版一致。kernel使用的是docker宿主机器的kernel。例如 CentOS 7 使用 3.x.x 的 kernel,如果 Docker Host 是 Ubuntu 16.04(比如我们的实验环境),那么在 CentOS 容器中使用的实际是是 Host 4.x.x 的 kernel。
二、容器、镜像和层
对于容器和镜像(container和image)的主要区别就是顶部的可写层(the top writable layer)。
1、容器启动的原理是什么?
镜像层都是只读的,不能往里面写数据。想写数据就需要在其上启动一层container layer,就是相当于把镜像启动成一个容器。
2、容器层是可写的
在容器层,我们是可写的。在容器中添加数据或者修改现有数据的所有读写操作都会存储在此可写层中。删除容器后,可写层也会被删除,而基础镜像则保持不变。
每个容器都会有自己的可写层,所有的改变都存储在该容器层中。多个容器可以共享对同一基础镜像的访问,但可以拥有自己的数据状态。
3、子镜像与父镜像
上层的image依赖于下层的image,因此想要从一个image启动container,docker会先加载这个image和依赖的父image以及base image。
三、实际开发中,由自己制作镜像还是使用别人已经制作好的?
在实际开发中,一个image文件通过继承另一个image文件,docker把应用程序及其依赖,打包在image文件(二进制文件)里。加上一些个性化设置而成。一般来说,为了节省时间,我们应该尽量使用别人制作好的image文件,而不是自己重新制作。
但是这还是需要根据情况而定,比如在做集群部署的时候,将项目打包镜像中,更方便我们项目后面进行更新迭代之后的部署。
下面贴一下hyperf官方拉下来的dockerfile,使用它可以构建hyperf项目的镜像:
# Default Dockerfile # # @link https://www.hyperf.io # @document https://hyperf.wiki # @contact group@hyperf.io # @license https://github.com/hyperf/hyperf/blob/master/LICENSE FROM hyperf/hyperf:7.4-alpine-v3.11-swoole ## # ---------- env settings ---------- ## # --build-arg timezone=Asia/Shanghai ARG timezone ENV TIMEZONE=${timezone:-"Asia/Shanghai"} \ APP_ENV=prod \ SCAN_CACHEABLE=(true) ENV PHPIZE_DEPS \ freetype-dev \ libjpeg-turbo-dev \ libpng-dev \ libzip-dev \ php-mongodb # update RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tencent.com/g' /etc/apk/repositories; \ set -ex; \ apk add --no-cache --virtual .build-deps \ $PHPIZE_DEPS \ curl-dev \ ; \ # show php version and extensions php -v \ && php -m \ && php --ri swoole \ # ---------- some config ---------- && cd /etc/php7 \ # - config PHP && { \ echo "upload_max_filesize=128M"; \ echo "post_max_size=128M"; \ echo "memory_limit=1G"; \ echo "date.timezone=${TIMEZONE}"; \ } | tee conf.d/99_overrides.ini \ # - config timezone && ln -sf /usr/share/zoneinfo/${TIMEZONE} /etc/localtime \ && echo "${TIMEZONE}" > /etc/timezone \ # ---------- clear works ---------- # && apk del --no-network .build-deps \ && rm -rf /var/cache/apk/* /tmp/* /usr/share/man \ && echo -e "\033[42;37m Build Completed :).\033[0m\n" \ && composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ WORKDIR /opt/www # Composer Cache # COPY ./composer.* /opt/www/ # RUN composer install --no-dev --no-scripts # COPY . /opt/www # RUN composer install --no-dev -o # RUN composer install --no-dev -o && php bin/hyperf.php RUN php /opt/www/bin/hyperf.php EXPOSE 9501 ENTRYPOINT ["php", "/opt/www/bin/hyperf.php", "start"]
参考链接:
https://www.cnblogs.com/woshimrf/p/docker-container-lawyer.html
https://zhuanlan.zhihu.com/p/139653646