Docker的镜像制作方法详解
作者:Ac.oviseng
Docker的镜像制作
1.1 镜像的基本原理
Docker 的镜像是创建容器的基础,就是一个普通文件,是一个面向 Docker 容器的只读模板。其实镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容(包括代码、运行时、库、环境变量和配置文件),这个打包好的运行环境就是image镜像文件。
例如:一个镜像可以是一个完整的 CentOS 操作系统环境,称为一个 CentOS 镜像;也可以是一个安装了 MySQL 的应用程序,称之为一个 MySQL 镜像等等。镜像是一个静态的概念,不包含任何动态数据,其内容在构建之后也不会被改变。
1、镜像的底层加载原理
Docker 的镜像实际上由一层一层的文件系统组成,这种层级的文件系统 UnionFS(联合文件系统)。
UnionFS:是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。Union 文件系统是 Docker 镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
2、镜像的结构
镜像不是一个单一的文件,而是有多层构成。可以通过 docker history 镜像
命令查看镜像中各层内容及大小,每层对应着 Dockerfile 中的一条指令。
Docker 镜像默认存储在 /var/lib/docker
目录中 。容器其实是在镜像的最上面加了一层读写层, 在运行容器里做的任何文件改动,都会写到这个读写层。如果删除了容器,也就删除了其最上面的读写层,文件改动也就丢失了。Docker 使用存储驱动管理镜像每层内容及可读写层的容器层。
1、分层结构的特点其实我们也会考虑 Docker 为什么会才用这种分层的结果,它有什么好处呢?最大的一个好处就是共享资源。
比如:有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份 base 镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
2、分层结构的特点Docker 镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部,这一层通常被称为容器层,容器层之下的都叫镜像层。
3、镜像的制作方式
- 基于容器创建镜像
基于现有容器创建主要使用 docker commit
命令,就是把一个容器里面运行的程序以及该程序的运行环境打包起来生成新的镜像。
它的缺点是:在基础镜像之上做的操作不会记录,别人不知道做了哪些操作,因此被称为黑盒镜像。
docker commit [选项] 容器ID/名称 生成的镜像:[标签] -m:说明信息; -a:作者信息; -p:生成过程中停止容器的运行;
# 示例 [root@localhost ~]# docker run -d --name web1 nginx:latest //创建一个容器 [root@localhost ~]# docker ps -a //查看运行状态 [root@localhost ~]# docker commit web1 nginx:v1 //生成镜像,生成之前先在容器里写点东西 [root@localhost ~]# docker images //查看生成的镜像 [root@localhost ~]# docker run -itd -p 80:80 --name web2 nginx:v1 //用生成的镜像创建容器
- 基于Dockerfile创建常用基础服务
除了手动生成 Docker 镜像之外,可以使用 Dockerfile 自动生成镜像。Dockerfile 是由一组指令组成的文件,其中每条指令对应 Linux 中的一条命令,Docker 程序将读取Dockerfile 中 的指令生成指定镜像。
1.2 Dockerfile制作镜像
镜像的制作实际上就是定制每一层所添加的配置、文件等信息。但是命令毕竟只是命令,每次定制都得去重复执行这个命令,而且还不够直观,如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像。这个脚本就是我们说的 Dockerfile
Dockerfile 是一个文本文件,其内包含了一条条的指令 (Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
1、Dockerfile常用指令
Dockerfile 结构大致分为四个部分:基础镜像信息
、维护者信息
、镜像操作指令
和 容器启动时执行指令
。Dockerfile 文件每行支持一条指令,每条指令可携带多个参数,每运行一条指令,都会给基础镜像添加新的一层。
指令 | 解释 |
---|---|
FROM 镜像 | 指定基础镜像(新镜像所基于的镜像),第一条指令必须为 FROM 指令,每创建一个镜像就需要一条 FROM 指令。 |
MAINTAINER 名字 | 说明新镜像的维护人信息(可以不写) |
RUN 命令 | 在所基于的镜像上执行命令,并提交到新的镜像中 |
CMD [“命令”,“参数”] | 指令启动容器时默认要运行的命令或者脚本,如果指定多条则只能最后一条被执行(如果在启动容器时指定命令,则不起作用) |
EXPOSE 端口号 | 指定新镜像加载到 Docker 时要开启的端口(只是定义,不更改) |
ENV 环境变量 变量值 | 设置一个环境变量的值,会被后面的 RUN 使用 |
ADD 源 目标 | 将宿主机文件拷贝到容器里面去,源文件要与 Dockerfile 文件在相同目录中(源文件是压缩包会自动解压) |
COPY 源 目标 | 将本地主机上的文件或目录复制到容器里,源文件要与 Dockerfile 文件在相同的目录中 |
VOLUME [“目录”] | 在容器中创建一个挂载点(自动创建匿名卷) |
USER 用户名/UID | 指定运行容器时的用户 |
WORKDIR 路径 | 为后续的 RUN、CMD、ENTRYPOINT 指定工作目录 |
ONBUILD 命令 | 指定所生成的镜像作为一个基础镜像时所要运行的命令 |
HEALTHCHECK | 健康检查,定义检查指令 |
2、Dockerfile制作镜像:案例
- 构建Nginx镜像
[root@localhost ~]# docker tag hub.atomgit.com/amd64/centos:centos7 centos:7 //下载centos镜像,并修改标签 [root@localhost ~]# mkdir nginx;cd nginx [root@localhost nginx]# vim Dockerfile FROM centos:7 RUN sed -i 's/mirrorlist/#mirrorlist/g' /etc/yum.repos.d/* && \ sed -i 's/mirror.centos.org/vault.centos.org/g' /etc/yum.repos.d/* && \ sed -i 's/#baseurl=/baseurl=/g' /etc/yum.repos.d/* && \ yum -y install gcc make pcre-devel zlib-devel tar zlib ADD nginx-1.12.0.tar.gz /usr/src/ RUN cd /usr/src/nginx-1.12.0 && \ mkdir -p /usr/local/nginx && \ ./configure --prefix=/usr/local/nginx && \ make && make install && \ ln -s /usr/local/nginx/sbin/* /usr/local/sbin/ RUN rm -rf /usr/src/nginx-1.12.0 EXPOSE 80 EXPOSE 443 CMD ["nginx","-g","daemon off;"] [root@localhost nginx]# ls //将nginx源码包拉上来 Dockerfile nginx-1.12.0.tar.gz [root@localhost nginx]# docker build -t 镜像:tag . //构建镜像,要注意要在有Dockerfile文件的目录执行
- 构建LNMP镜像
[root@localhost ~]# mkdir lnmp;cd lnmp [root@localhost lnmp]# vim Dockerfile FROM centos:7 # 安装ngix RUN rm -f /etc/yum.repos.d/* && \ curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo && \ yum -y install epel-release && \ yum clean all && \ yum makecache && \ yum -y install nginx RUN sed -i '/^user/s/nginx/nginx\ nginx/g' /etc/nginx/nginx.conf COPY default.conf /etc/nginx/conf.d/default.conf # 安装mariadb和php RUN yum -y install mariadb-server mariadb-devel mariadb php-mysql php php-fpm # 修改php-fpm配置文件 RUN sed -i '/^user/s/apache/nginx/g' /etc/php-fpm.d/www.conf && \ sed -i '/^group/s/apache/nginx/g' /etc/php-fpm.d/www.conf # 声明mariadb的用户名和密码 ENV MARIADB_USER=root ENV MARIADB_PASS=123456 # 支持中文 ENV LC_ALL=en_US.UTF-8 # 添加并运行脚本 ADD db_init.sh /root/db_init.sh RUN chmod 775 /root/db_init.sh RUN /root/db_init.sh ADD run.sh /root/run.sh RUN chmod 775 /root/run.sh ADD index.php /usr/share/nginx/html/index.php # 开放的端口 EXPOSE 80 EXPOSE 3306 EXPOSE 443 EXPOSE 9000 # 每次创建容器运行此脚本 CMD ["/root/run.sh"] [root@localhost lnmp]# vim default.conf server { listen 80; server_name localhost; location / { root /usr/share/nginx/html; index index.php index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } location ~ \.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /usr/share/nginx/html$fastcgi_script_name; include fastcgi_params; } } [root@localhost lnmp]# vim db_init.sh //数据库初始化脚本 #!/bin/bash #初始化数据库命令 mysql_install_db --user=mysql sleep 3 #启动数据库 mysqld_safe & sleep 3 # 涉及到的变量在 Dockerfile 中都已经声明 mysqladmin -u "$MARIADB_USER" password "$MARIADB_PASS" # 授权命令 mysql -u"$MARIADB_USER" -p"$MARIADB_PASS" -e "use mysql; grant all privileges on *.* to '$MARIADB_USER'@'%' identified by '$MARIADB_PASS' with grant option;" mysql -u"$MARIADB_USER" -p"$MARIADB_PASS" -e "grant all privileges on *.* to '$MARIADB_USER'@'localhost' identified by '$MARIADB_PASS';" h=$(hostname) mysql -u"$MARIADB_USER" -p"$MARIADB_PASS" -e "use mysql; update user set password=password('$MARIADB_PASS') where user='$MARIADB_USER' and host='$h';" [root@localhost lnmp]# vim run.sh //服务启动脚本 #!/bin/bash mysqld_safe & /usr/sbin/nginx & /usr/sbin/php-fpm [root@localhost lnmp]# vim index.php <?php echo date("Y-m-d H:i:s")."<br />\n"; $link=mysql_connect("localhost","root","123456"); if(!$link) echo "FAILD!"; else echo "MySQL is OK!"; phpinfo(); ?> [root@localhost lnmp]# docker build -t centos:lnmp . [root@localhost lnmp]# docker run -d --name lnmp -P centos:lnmp
1.3 多阶段构建镜像
在 Docker 17 版本后提供的,使用多阶段构建可以在一个 Dockerfile 中使用多个 FROM 语句。每个FROM指令都可以使用不同的基础镜像,并表示开始一个新的构建阶段。你可以很方便的将一个阶段的文件复制到另一个阶段,在最终的镜像中留下你需要的内容。
使用多阶段构建的好处:
1.减小镜像大小:每个构建阶段只包含必要的依赖项和文件,从而减小了生成的镜像大小。这可以减少镜像的存储空间和传输时间。
2.提高构建速度:每个构建阶段可以一起执行。而且,每个构建阶段只构建所需的内容,从而减少了构建时间。
3.简化 Dockerfile:使用多个构建阶段可以将 Dockerfile 分解为更小的部分,从而使Dockerfile 更加易于管理和维护。
4.提高安全性:使用多个构建阶段可以限制敏感信息的泄露。例如,在第一个构建阶段中,可以包含敏感信息,例如私有密钥或密码。而在第二个构建阶段中,可以只包含必要的文件和依赖项
➤ 多阶段构建案例(未使用多阶段构建)
[root@localhost ~]# mkdir demo;cd demo [root@localhost demo]# vim demo.c # include<stdio.h> int main() { printf("%s\n","This is a demo!"); return 0; } [root@localhost demo]# vim Dockerfile FROM centos:7 ENV version=1.0 WORKDIR /demo COPY demo.c . RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo && \ yum -y install gcc && \ gcc -v RUN gcc demo.c -o demo && \ rm -rf demo.c && \ yum -y remove gcc && \ cp demo /usr/local/bin/ CMD ["demo"] [root@localhost demo]# docker build -t demo:v1 . [root@localhost demo]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE demo v1 d05f8303b0bc 22 seconds ago 557MB
➤ 多阶段构建案例(使用多阶段构建)
[root@localhost demo]# vim Dockerfile //基于上面的未使用构建,添加这两行 FROM centos:7 ENV version=1.0 WORKDIR /demo COPY demo.c . RUN curl -o /etc/yum.repos.d/CentOS-Base.repo https://mirrors.aliyun.com/repo/Centos-7.repo && \ yum -y install gcc && \ gcc -v RUN gcc demo.c -o demo && \ rm -rf demo.c && \ yum -y remove gcc && \ cp demo /usr/local/bin/ FROM centos:7 # --from表示从其他阶段拷贝内容到本阶段,0表示从第一个阶段拷贝到本阶段 COPY --from=0 /usr/local/bin/demo /usr/local/bin/demo CMD ["demo"] [root@localhost demo]# docker build -t demo:v1 . [root@localhost demo]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE demo v2 68bc7352dcfa 13 seconds ago 204MB //使用多阶段构建 demo v1 d05f8303b0bc 9 minutes ago 557MB //未使用多阶段构建
总结
到此这篇关于Docker的镜像制作方法的文章就介绍到这了,更多相关Docker镜像制作内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!