Docker 容器的本质是一个特殊的进程,而 Docker 镜像则是容器运行所需的文件系统。可以说Docker容器是Docker镜像的实例,镜像是容器的模板。容器是在镜像的基础上运行的,当我们修改原镜像时,并不会对正在运行的容器产生影响。
那么Docker镜像里面到底包含哪些东西那?要想知道Docker镜像里面含有什么东西,我们需要先看看Docker镜像是怎么来的,最常见的构建Docker镜像的方式是通过编写Dockerfile。
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
如上是一个简单的Dockerfile文件,其中FROM指定基础镜像为 nginx
RUN命令执行shell命令向index.html 文件中写入Hello, Docker!
编写好Dockerfile后,我们可以执行命令:docker build -t nginx:v1 . 来构建镜像:
C02D9251MD6R :: ~/Downloads » docker build -t nginx:v1 . 1 ↵
[+] Building 9.3s (6/6) FINISHED
...
=> [internal] load metadata for docker.io/library/nginx:latest 3.8s
=> [1/2] FROM docker.io/library/nginx@sha256:b8f2383a95879e1ae064940d9a2 4.9s
...
=> [2/2] RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/inde 0.4s
=> exporting to image 0.0s
=> => exporting layers 0.0s
=> => writing image sha256:e8b2a9dd1015eb395f7f18d3b4502050d9c4ab919d253 0.0s
=> => naming to docker.io/library/nginx:v1 0.0s
如上可知,第一步是下载基础镜像nginx,第二步是执行echo命令修改ngnix镜像里面的index.html文件的内容
执行完毕build命令后,我们的docker镜像nginx:v1就在本地构建好嘞,我们可使用docker images查看:
C02D9251MD6R :: ~/Downloads » docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v1 e8b2a9dd1015 9 minutes ago 142MB
httpd latest 6e794a483258 5 days ago 145MB
nginx latest a99a39d070bf 12 days ago 142MB
如上可知 nginx v1的大小只有142MB,因为我们镜像里面只是修改了nginx镜像里面的内容,本身并没有新增任何大的东西,所以我们需要看看nginx镜像里面有什么,那我们可看看构建ngnix的Dockerfile(https://github.com/nginxinc/docker-nginx/blob/5ce65c3efd395ee2d82d32670f233140e92dba99/mainline/debian/Dockerfile)里面有什么:
#
# NOTE: THIS DOCKERFILE IS GENERATED VIA "update.sh"
#
# PLEASE DO NOT EDIT IT DIRECTLY.
#
FROM debian:bullseye-slim
LABEL maintainer="NGINX Docker Maintainers <docker-maint@nginx.com>"
ENV NGINX_VERSION 1.23.3
ENV NJS_VERSION 0.7.9
ENV PKG_RELEASE 1~bullseye
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 \
&& apt-get install --no-install-recommends --no-install-suggests -y gnupg1 ca-certificates \
&& \
NGINX_GPGKEY=573BFD6B3D8FBC641079A6ABABF5BD827BD9BF62; \
found=''; \
for server in \
hkp://keyserver.ubuntu.com:80 \
pgp.mit.edu \
; do \
echo "Fetching GPG key $NGINX_GPGKEY from $server"; \
apt-key adv --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$NGINX_GPGKEY" && found=yes && break; \
done; \
test -z "$found" && echo >&2 "error: failed to fetch GPG key $NGINX_GPGKEY" && exit 1; \
apt-get remove --purge --auto-remove -y gnupg1 && rm -rf /var/lib/apt/lists/* \
&& dpkgArch="$(dpkg --print-architecture)" \
&& nginxPackages=" \
nginx=${NGINX_VERSION}-${PKG_RELEASE} \
nginx-module-xslt=${NGINX_VERSION}-${PKG_RELEASE} \
nginx-module-geoip=${NGINX_VERSION}-${PKG_RELEASE} \
nginx-module-image-filter=${NGINX_VERSION}-${PKG_RELEASE} \
nginx-module-njs=${NGINX_VERSION}+${NJS_VERSION}-${PKG_RELEASE} \
" \
&& case "$dpkgArch" in \
amd64|arm64) \
# arches officialy built by upstream
echo "deb https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \
&& apt-get update \
;; \
*) \
# we're on an architecture upstream doesn't officially build for
# let's build binaries from the published source packages
echo "deb-src https://nginx.org/packages/mainline/debian/ bullseye nginx" >> /etc/apt/sources.list.d/nginx.list \
\
# new directory for storing sources and .deb files
&& tempDir="$(mktemp -d)" \
&& chmod 777 "$tempDir" \
# (777 to ensure APT's "_apt" user can access it too)
\
# save list of currently-installed packages so build dependencies can be cleanly removed later
&& savedAptMark="$(apt-mark showmanual)" \
\
# build .deb files from upstream's source packages (which are verified by apt-get)
&& apt-get update \
&& apt-get build-dep -y $nginxPackages \
&& ( \
cd "$tempDir" \
&& DEB_BUILD_OPTIONS="nocheck parallel=$(nproc)" \
apt-get source --compile $nginxPackages \
) \
# we don't remove APT lists here because they get re-downloaded and removed later
\
# reset apt-mark's "manual" list so that "purge --auto-remove" will remove all build dependencies
# (which is done after we install the built packages so we don't have to redownload any overlapping dependencies)
&& apt-mark showmanual | xargs apt-mark auto > /dev/null \
&& { [ -z "$savedAptMark" ] || apt-mark manual $savedAptMark; } \
\
# create a temporary local APT repo to install from (so that dependency resolution can be handled by APT, as it should be)
&& ls -lAFh "$tempDir" \
&& ( cd "$tempDir" && dpkg-scanpackages . > Packages ) \
&& grep '^Package: ' "$tempDir/Packages" \
&& echo "deb [ trusted=yes ] file://$tempDir ./" > /etc/apt/sources.list.d/temp.list \
# work around the following APT issue by using "Acquire::GzipIndexes=false" (overriding "/etc/apt/apt.conf.d/docker-gzip-indexes")
# Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
# ...
# E: Failed to fetch store:/var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages Could not open file /var/lib/apt/lists/partial/_tmp_tmp.ODWljpQfkE_._Packages - open (13: Permission denied)
&& apt-get -o Acquire::GzipIndexes=false update \
;; \
esac \
\
&& apt-get install --no-install-recommends --no-install-suggests -y \
$nginxPackages \
gettext-base \
curl \
&& apt-get remove --purge --auto-remove -y && rm -rf /var/lib/apt/lists/* /etc/apt/sources.list.d/nginx.list \
\
# if we have leftovers from building, let's purge them (including extra, unnecessary build deps)
&& if [ -n "$tempDir" ]; then \
apt-get purge -y --auto-remove \
&& rm -rf "$tempDir" /etc/apt/sources.list.d/temp.list; \
fi \
# forward request and error logs to docker log collector
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log \
# create a docker-entrypoint.d directory
&& mkdir /docker-entrypoint.d
COPY docker-entrypoint.sh /
COPY 10-listen-on-ipv6-by-default.sh /docker-entrypoint.d
COPY 20-envsubst-on-templates.sh /docker-entrypoint.d
COPY 30-tune-worker-processes.sh /docker-entrypoint.d
ENTRYPOINT ["/docker-entrypoint.sh"]
EXPOSE 80
STOPSIGNAL SIGQUIT
CMD ["nginx", "-g", "daemon off;"]
如上可知nginx本身的构建基于基础镜像debian:bullseye-slim,nginx在基础镜像基础上大概新增了60M的内容,主要是拷贝的文件、创建临时目录、已经安装一些软件。
下面我么看看基础镜像debian:bullseye-slim(https://github.com/debuerreotype/docker-debian-artifacts/blob/64b13cf5860ac15c1d909abd7239516db9748fea/bullseye/slim/Dockerfile),其是比较常用的基础镜像,其Dockerfile如下:
FROM scratch
ADD rootfs.tar.xz /
CMD ["bash"]
scratch是官方提供一个空镜像,其用来构建基础镜像比如(debian,busybox)或者体积超级小的镜像时候特别有用。
rootfs.tar.xz是用户空间的文件系统。
这里我们又必须要说下,Linux操作系统由内核空间和用户空间组成。Linux刚启动时会加载bootfs文件系统,之后bootfs会被卸载掉。用户空间的文件系统是rootfs,包含我们熟悉的 /dev、/proc、/bin等目录。对于基础镜像来说,底层复用用Host的kernel,镜像本身只需要包含rootfs就行了。而对于一个精简的OS, rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了。
所以Docker镜像里面其实并不包含linux内核文件,而仅仅是包含用户空间的文件系统rootfs,另外Docker镜像里面还包含开发者自己的应用文件,仅此而已。最后所有的Docker容器运行时是复用主机操作系统的内核的,但是每个Docker容器有自己独立的用户空间的文件系统。
戳下面阅读
👇
点亮再看哦👇