一、Docker镜像的创建
创建镜像有三种方法,分别为【基于已有镜像创建】、【基于本地模板创建】以及【基于Dockerfile创建】。
1.基于现有镜像创建
(1)首先启动一个镜像,在容器里做修改
docker run -it --name web centos:7 /bin/bash #启动容器
yum install -y epel-release 安装epel源
yum install -y nginx 安装nginx
yum install net-tools 安装tools工具
nginx 启动服务
netstat -natp |grep 80 查看端口是否开启
docker ps -a #查看容器ID
(2)然后将修改后的容器提交为新的镜像,需要使用该容器的ID号创建新镜像
docker commit -m "new nginx" -a "cx" 容器id nginx:centos7
#常用选项:
-m 指定说明信息;
-a 指定作者信息;
-p 生成过程中停止容器的运行。
fe21b13926d1 原容器ID。
nginx:centos 生成新的镜像名称。
docker images #查看生成的新镜像
docker run -itd nginx:centos7 bash 使用新的镜像创建容器
docker ps -a 查看容器状态
docker exec -it 容器id bash 进入容器
nginx 启动nginx服务
netstat -natp |grep 80 查看80端口是否开启
2.基于模板创建
通过导入操作系统模板文件可以生成镜像,模板可以从OPENVZ 开源项目下载,下载地址为:
openvz.org/ Download/template/precreated
模板里面就是使用docker export 命令导出的容器文件
#下载模板
wget http://download.openvz.org/template/precreated/debian-7.0-x86-minimal.tar.gz
#导入为镜像,两种方法
cat debian-7.0-x86-minimal.tar.gz | docker import - debian:test #方法一
docker import debian-7.0-x86-minimal.tar.gz -- debian:test #方法二
#查看镜像
docker images
#使用导入的镜像创建容器
docker run -itd debian:test bash
docker ps -a
3.基于Dockerfile 创建
联合文件系统(UnionFS )
Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性: 一次同时加载多个文件系统,但从外面看起来,只能看到一一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录。
我们下载的时候看到的一层层的就是联合文件系统。
镜像加载原理
Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统就是UnionFS。
bootfs主要包含bootloader和kernel,bootloader主 要是引导加载kernel,Linux刚启 动时会加载bootfs文件系统。
在Docker镜像的最底层是bootfs,这一层 与我们典型的Linux/Unix系统是一样的, 包含boot加载器和内核。当boot加载完成之 后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
rootfs,在bootfs之 上。包含的就是典型Linux系统中的/dev、/proc、/bin、/etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu, Centos等。
bootfs就是内核引导器(引导加载内核)和内核。
rootfs是n多个基础镜像(提供基础操作环境)和应用镜像叠加在一起的只读层。
运行的容器实例会在rootfs之上添加一个可读可写层。
Docker镜像结构的分层
镜像不是一个单一的文件,而是有多层构成。容器其实是在镜像的最上面加了一层读写层,在运行容器里做的任何文件改动,都会写到这个读写层。如果删除了容器,也就删除了其最上面的读写层,文件改动也就丢失了。Docker使用存储驱动管理镜像每层内容及可读写层的容器层。
(1)Dockerfile中的每个指令都会创建一个新的镜像层;
(2)镜像层将被缓存和复用;
(3)当Dockerfile的指令修改了,复制的文件变化了,或者构建镜像时指定的变量不同了,对应的镜像层缓存就会失效;
(4)某一层的镜像缓存失效,它之后的镜像层缓存都会失效;
(5)镜像层是不可变的,如果在某一层中添加一个文件,然后在下一层中删除它,则镜像中依然会包含该文件,只是这个文件在Docker 容器中不可见了。
二、 Dockerfile操作命令的指令
FROM | 指定基础镜像,dockerfile构建镜像的第一个指令 |
MAINTAINER | 指定镜像维护人信息(可选) |
RUN | 指定Linux命令,建议多条命令可用 && 或 ; 串起来使用 |
ENV | 设置镜像的环境变量 |
EXPOSE | 暴露容器端口 |
VOLUME | 指定容器的匿名数据卷 |
ADD/COPY | 复制本地文件/目录到镜像中 |
USER | 指定容器的运行用户 |
WORKDIR | 指定容器的工作目录 |
CMD/ENTRYPOINT | 指定容器启动时执行的命令 |
ARG | 指定构建镜像时传入的参数变量 docker build --build-arg 变量=值 |
ADD 和 COPY 的区别
COPY 只能复制本地文件/目录到镜像中
ADD 不光可以复制本地文件/目录到镜像中,还可以通过URL下载文件复制到镜像中,还能将本地的tar压缩包解压后复制到镜像中(URL下载和tar包解压不能一起使用)
CMD 和 ENTRYPOINT 的区别
ENTRYPOINT指定的容器启动命令优先级更高,如果CMD和ENTRYPOINT同时存在,那么CMD指定的内容将作为ENTRYPOINT指定的命令的选项或参数使用
容器启动时运行的命令优先级
docker run --entrypoint=命令 > 镜像里的 ENTRYPOINT ["命令"] > docker run ... 镜像 命令 > 镜像里的 CMD ["命令"]
在编写Dockerfile 时,有严格的格式需要遵循:
第一行必须使用FROM指令指明所基于的镜像名称;
之后使用MAINTAINER 指令说明维护该镜像的用户信息;
然后是镜像操作相关指令,如RUN指令/EXPOSE/ADD/ENV/ARG等等。每运行一条指令,都会给基础镜像添加新的一层。(多条命令可以使用 ; 或 && 合并成一条命令,减少镜像的层数)
最后使用CMD或者ENTRYPOINT指令指定启动容器时要运行的命令操作
如何缩小镜像体积大小?
1)尽可能的使用小体积的基础镜像
2)尽可能检查Dockerfile文件中指令的数量
3)可以构建镜像步骤最后添加清空系统和应用程序的缓存命令
4)使用多阶段(多级)构建 FROM 第一阶段的基础镜像 [AS 别名]
.....
FROM 第二阶段的基础镜像
COPY --from=别名/0 第一阶段构建的文件/目录 当前阶段的文件/目录
三、Dockefile的实际运用
1.Dockfile源码编译nginx
通过Dockerfile创建源码编译的nginx(基于centos7基础镜像),并且通过后台运行
vim Dockerfile
#基于镜像的指定
FROM centos:7
#作者信息
MAINTAINER this is nginx image <cx 20230724>
ADD nginx-1.22.0.tar.gz /usr/local/
RUN yum install -y pcre-devel zlib-devel openssh-devel gcc gcc-c++ make ncurses ncurses-devel bison cmake gd \
libjpeg libjpeg-devel \
libpng libpng-devel \
freetype freetype-devel \
libxml2 libxml2-devel \
zlib zlib-devel \
curl curl-devel \
openssl openssl-devel
RUN useradd -M -s /sbin/nologin nginx && \
cd /usr/local/nginx-1.22.0 && \
./configure \
--prefix=/usr/local/nginx \
--user=nginx \
--group=nginx \
--with-http_stub_status_module && make && make install
EXPOSE 80
#EXPOSE 443
CMD ["/usr/local/nginx/sbin/nginx", "-g","daemon off;"]
注意:容器里的服务必须为前台启动程序,否则会容器启动又关闭,无法正常提供服务。
所以在进行容器提供服务时,需要了解各种服务的前台运行,若该服务没有前台运行选项,则需要在容器中挂一个占用前台的运行指令,避免容器开启后有关闭。
2.镜像容量过大的解决方案
1)尽可能的使用小体积的基础镜像
2)尽可能检查Dockerfile文件中指令的数量
3)可以构建镜像步骤最后添加清空系统和应用程序的缓存命令
4)使用多阶段(多级)构建 FROM 第一阶段的基础镜像 [AS 别名]
.....
FROM 第二阶段的基础镜像
COPY --from=别名/0 第一阶段构建的文件/目录 当前阶段的文件/目录
针对上面的镜像的创建进行基础镜像的进行多阶级构建 :
第一阶段构建
FROM centos:7 AS first
MAINTAINER this is nginx image <cx 20230724>
ADD nginx-1.22.0.tar.gz /usr/local/
RUN yum install -y pcre-devel zlib-devel openssh-devel gcc gcc-c++ make ncurses ncurses-devel bison cmake gd \
libjpeg libjpeg-devel \
libpng libpng-devel \
freetype freetype-devel \
libxml2 libxml2-devel \
zlib zlib-devel \
curl curl-devel \
openssl openssl-devel
RUN useradd -M -s /sbin/nologin nginx && \
cd /usr/local/nginx-1.22.0 && \
./configure \
--prefix=/usr/local/nginx \
--user=nginx \
--group=nginx \
--with-http_stub_status_module && make && make install && \
yum clean all
#第二阶段构建
FROM centos:7
#复制第一阶段的目录到当前阶段
COPY --from=first /usr/local/nginx/ /usr/local/nginx/
RUN useradd -M -s /sbin/nologin nginx
EXPOSE 80
#EXPOSE 443
CMD ["/usr/local/nginx/sbin/nginx", "-g","daemon off;"
四、总结
Dockerfile结构大致分为四个部分:基础镜像信息(用from指定)、维护者信息(maintainer、镜像操作指令和容器启动时执行指令。
- 第一行必须使用FROM指令指明所基于的镜像名称;
- 之后使用MAINTAINER 指令说明维护该镜像的用户信息;
- 然后是镜像操作相关指令,如RUN指令/EXPOSE/ADD/ENV/ARG等等。每运行一条指令,都会给基础镜像添加新的一层。(多条命令可以使用 ; 或 && 合并成一条命令,减少镜像的层数)
- 最后使用CMD或者ENTRYPOINT指令指定启动容器时要运行的命令操作。