提示:以下内容均为学习过程中的个人笔记,有用自取,无用勿喷,如有错误欢迎指正
一、什么是镜像
容器创建时需要指定镜像,每个镜像都由唯一的标示 Image ID ,和容器的 Container ID 一样,默认 128 位,可以使用前 16 为缩略形式,也可以使用镜像名与版本号两部分组合唯一标示,如果省略版本号,默认使用最新版本标签 ( latesr )
二、镜像的特性是什么
- 镜像的分层:Docker 的镜像通过联合文件系统 ( union filesystem ) 将各层文件系统叠加在一起
- bootfs:用于系统引导的文件系统,包括 bootloader 和 kernel,容器启动完成后会被卸载以节省内存资源; rootfs:位于 bootfs 之上,表现为 Docker 容器的跟文件系统; 传统模式中,系统启动时,内核挂载 rootfs 时会首先将其挂载为“只读”模式,完整性自检完成后将其挂载为读写模式;Docker 中,rootfs 由内核挂载为“只读”模式,而后通过 UFS 技术挂载一个“可写” 层
- 已有的分层只能读不能修改
- 上层镜像优先级大于底层镜像
注:UFS最大只能叠加128层,当镜像层级超过128层时,镜像就不能使用了
解决办法:同类聚合,例如,多个软件同时编译RUN 编译apache && 编译MYSQL,此办法缺点:聚合太多重用性变差,build时报错,不方便错误查找
三、镜像制作
方案一:通过docker commit命令将可运行容器转换为镜像(此外该命令还可以备份容器)
原理:将docker run添加的可写层(nginx编译、安装过程)变为镜像层并和基础镜像lhhlinux/centos:7.9run一起封装为镜像,但是commit只能保存可写层文件系统级别的修改,无法保存元数据级别(包括启动命令以及对应层级的环境变量)的修改,因为无法判断元数据
#首先需要一个能运行的起来的容器,接下来以制作nginx镜像为例,官方下载系统基础镜像
docker pull centos:centos7.9.2009
#以此镜像运行容器
docker run --name centos -d centos:centos7.9.2009
#发现无法运行,需要配合Dockerfile文件,封装一个启动命令,使用dw可以删除多余的空格
vim Dockerfile
FROM centos:centos7.9.2009
RUN touch /root/startup.sh
CMD tail -f /root/startup.sh
#在Dockerfile文件同目录下执行封装命令
docker build -t lhhlinux/centos:7.9run .
#此时根据刚刚生成的镜像再次创建运行容器,发现已经可以运行
docker run --name nginx -p 80:80 -d lhhlinux/centos:7.9run
#进入容器,安装nginx依赖包,上传nginx源码包,进行编译安装
docker exec -it nginx /bin/bash
cd
yum -y install gcc gcc-c++ make pcre pcre-devel zlib zlib-devel lrzsz
tar -xvf nginx-1.13.8.tar.gz
cd nginx-1.13.8
./configure --prefix=/usr/local/nginx #因为容器中只有一个进程因此不用指定用户
make && make install
/usr/local/nginx/sbin/nginx
#简化系统
cd
yum clean all
rm -rf /var/cache/yum/*
rm -rf nginx-1.13.8*
#封装nginx镜像,以此镜像创建启动的容器可以运行但是nginx无法访问,因为当前镜像的启动命令为基础镜像的启动命令,实际上nginx并未启动
docker commit nginx lhhlinux/nginx:v0.1
#解决需进入容器再次执行启动命令问题
vim Dockerfile
FROM lhhlinux/nginx:v0.1
LABEL email=lu17531110201@163.com
RUN rm -rf /root/startup.sh
EXPOSE 80
EXPOSE 443
WORKDIR /usr/local/nginx
CMD /usr/local/nginx/sbin/nginx && tail -f /usr/local/nginx/logs/access.log
#再次封装,生成新版镜像
docker build -t lhhlinux/nginx:v2.0 .
docker image inspect lhhlinux/nginx:v2.0
docker run --name nginx -p 80:80 -d lhhlinux/nginx:v2.0
客户端打开浏览器访问宿主机IP,进行验证
方案二:使用docker build 命令将dockerfile文件制作成镜像
原理:docker build 根据Dockerfile文件内容逐行进行,首先将基础镜像拿出来作为基础镜像层,基础镜像层上面加一层可写层,若第二行是RUN命令,则在基础镜像中进行相应操作并将这些文件系同级别的变化记录到可写层中,第三行若是标签则是元数据,在可写层上添加json文件将其变为镜像层,再将第四行的默认启动名称变为第四层镜像层
FROM(指定基础镜像)注:基础镜像使用官方镜像仓库中的centos就行
Dockerfile必要字段,每个文件中只能有一个,应放在文件首行。后续的指令都依赖于该指令指定的 image。FROM 指令指定的基础 image 可以是官方远程仓库中的,也可以位于本地仓库
FROM centos:7.2
FROM centos
MAINTAINER(用来指定镜像创建者信息)
用于将 image 的制作者相关的信息写入到 image 中。当我们对该 image 执行 docker inspect 命令时,输出中有相应的字段记录该信息。,新版本使用LABEL标签定义作者信息
MAINTAINER wangyang "wangyang@itxdl.cn"
LABEL(将元数据添加到镜像)
标签的定义
# 指令将元数据添加到镜像。`LABEL` 是键值对。要在 `LABEL` 值中包含空格,请像在命令行中一样使用引号和反斜杠
LABEL "com.example.vendor"="ACME Incorporated"
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
LABEL description="This text illustrates \
that label-values can span multiple lines."
# 多行标签定义方式
LABEL multi.label1="value1" multi.label2="value2" other="value3"
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"
标签的继承
基础或父镜像(FROM 行中的镜像)中包含的标签由您的镜像继承。如果标签已经存在但具有不同的值,则最近应用的值将覆盖任何先前设置的值
查看镜像标签
$ docker image inspect --format=''myimage
案例
#编写Dockerfile文件
vim Dockerfile
FROM busybox #指定基础镜像
LABEL author=wangyanglinux #打标签,声明作者
CMD echo wangyanglinux #容器启动时默认的启动命令
# 构建镜像时新建一个空目录,将Dockerfile文件放到空目录中,以免docker daemon将文件同目录中的其他文件全部放进镜像中;点表示在当前目录查找Dockerfile文件,--no-cache不重用缓存,-t表示生成的镜像名
[root@k8s-master01 testd]# docker build -t wangyanglinux:0.0.1 . --no-cache
# 安装 jq
[root@k8s-master01 testd]# yum install epel-release
[root@k8s-master01 testd]# yum install jq
# 查看标签,json后面的点表示根顶级对象,ContainerConfig表示次级对象
[root@k8s-master01 testd]# docker image inspect wangyanglinux:0.0.1 --format "{{json .ContainerConfig.Labels}}" | jq
{
"author": "wangyanglinux"
}
RUN(在基础镜像中安装软件用)
RUN 可以运行任何被基础 image 支持的命令。如基础 image 选择了 Centos,那么软件管理部分只能使用 Centos 的包管理命令
RUN cd /tmp && curl -L 'http://archive.apache.org/dist/tomcat/tomcat-7/v7.0.8/bin/apache-tomcat-7.0.8.tar.gz' | tar -xz
USER(设置container容器的用户,一般不用创建用户)
设置启动容器的用户,默认是 root 用户
USER daemon
EXPOSE(指定容器需要映射到宿主机器的端口)
# 映射多个容器重要端口
EXPOSE port1 port2 port3
# 将容器暴漏的端口映射为宿主机的随机端口
docker run -P image
# 相应的运行容器使用的命令
docker run-p port1 -p port2 -p port3 image
# 还可以指定需要映射到宿主机器上的某个端口号
docker run-p host_port1:port1 -p host_port2:port2 -p host_port3:port3 image
ENV(用于设置环境变量,此环境变量只能在容器启动时将json文件中的元数据注入可写层,然后再启动,无法在Dockerfile文件中被调用)
假如你安装了 JAVA 程序,需要设置 JAVA_HOME,那么可以在 Dockerfile 中这样写:
ENV JAVA_HOME /path/to/java/dirent
ARG(设置变量)
起作用的时机
- arg 是在 build 的时候存在的, 可以在 Dockerfile 中当做变量来使用
- env 是容器构建好之后的环境变量, 不能在 Dockerfile 中当参数使用
案例
# Dockerfile
FROM redis:3.2-alpine
LABEL maintainer="wangyanglinux@163.com"
ARG REDIS_SET_PASSWORD=developer
ENV REDIS_PASSWORD ${REDIS_SET_PASSWORD}
VOLUME /data
EXPOSE 6379
CMD ["sh", "-c", "exec redis-server --requirepass \"$REDIS_PASSWORD\""]
ADD(从 src 复制文件到 container 的 dest 路径,如果是压缩包放到容器中自动解压)
ADD <src> <dest>
<src> 是相对被构建的源目录的相对路径,可以是文件或目录的路径,也可以是一个远程的文件 url;
<dest> 是 container 中的绝对路径
COPY (从 src 复制文件到 container 的 dest 路径)
COPY <src> <dest>
VOLUME(指定挂载点)
设置指令,使容器中的一个目录具有持久化存储数据的功能,将数据存储在宿主机的/var/lib/docker/volumes目录下随机创建一个子目录中,该目录可以被容器本身使用,也可以共享给其他容器使用。在 Dockerfile中 使用该指令
FROM base
VOLUME 需持久化的目录
WORKDIR(声明工作目录,指定进入容器后所在目录)
设置指令,可以多次切换(相当于cd命令),对RUN,CMD,ENTRYPOINT生效
WORKDIR /p1 WORKDIR p2 RUN vim a.txt
CMD(设置 container 启动时执行的操作)
设置指令,用于 container 启动时指定的操作。该操作可以是执行自定义脚本,也可以是执行系统命令。该指令只能在文件中存在一次,如果有多个,则只执行最后一条
CMD echo “Hello, World!”
CMD /usr/local/nginx/sbin/nginx 此命令执行完就结束了,不能作为容器的启动命令,解决办法如下
CMD /usr/local/nginx/sbin/nginx && tail -f /usr/local/nginx/logs/access.log
ENTRYPOINT(设置container启动时执行的操作)
设置指令,指定容器启动时执行的命令,可以多次设置,但是只有最后一个有效。
ENTRYPOINT ls -l
#该指令的使用分为两种情况,一种是独自使用,另一种和 CMD 指令配合使用。当独自使用时,如果你还使用了 CMD 命令且 CMD 是一个完整的可执行的命令,那么 CMD 指令和 ENTRYPOINT 会互相覆盖只有最后一个 CMD 或者 ENTRYPOINT 有效
CMD echo “Hello, World!”
ENTRYPOINT ls -l
# CMD 指令将不会被执行,只有 ENTRYPOINT 指令被执行
#另一种用法和 CMD 指令配合使用来指定 ENTRYPOINT 的默认参数,这时 CMD 指令不是一个完整的可执行命令,仅仅是参数部分;ENTRYPOINT 指令只能使用 JSON 方式指定执行命令,而不能指定参数
FROM ubuntu
CMD ["-l"]
ENTRYPOINT ["/usr/bin/ls"]
# 如果我们在 run 时指定了命令或者有entrypoint,那么 cmd 就会被覆盖。仍然是上面的 image。run 命令变了:
docker run xx echo glgl
==> glgl
#cmd 是默认体系,entrypoint 是正统地用于定义容器启动以后的执行体
FROM centos
CMD ["p in cmd"]
ENTRYPOINT ["echo"]
[root@k8s-master01 testd]# docker run --name test1 ent:v1
p in cmd
[root@k8s-master01 testd]# docker run --name test2 ent:v1 p in run
p in run
#例:
镜像:webserver:v1
apache
Nginx
tomcat
#Dockerfile文件
CMD ["nginx"]
ENTRYPOINT ["/bin/bash /root/startup.sh"]
docker run xx -d xxx apache | nginx | tomcat
#脚本
vim startup.sh
#!/bin/bash
func apche (){
启动 apache
}
func nginx (){
启动 Nginx
}
func tomcat (){
启动 tomcat
}
$1
ONBUILD(在基础镜像中执行)
ONBUILD 指定的命令在构建镜像时并不执行,而是在它的子镜像中执行
ONBUILD rm -rf /* #防盗窃
STOPSIGNAL signal
STOPSIGNAL 指令设置将发送到容器以退出的系统调用信号。这个信号可以是一个有效的无符号数字,与内核的syscall表中的位置相匹配,例如9,或者是SIGNAME格式的信号名,例如:SIGKILL
SIGHUP 1 A 终端挂起或者控制进程终止
SIGINT 2 A 键盘中断(如break键被按下)
SIGQUIT 3 C 键盘的退出键被按下
SIGILL 4 C 非法指令
SIGABRT 6 C 由abort(3)发出的退出指令
SIGFPE 8 C 浮点异常
SIGKILL 9 AEF Kill信号
SIGSEGV 11 C 无效的内存引用
SIGPIPE 13 A 管道破裂: 写一个没有读端口的管道
SIGALRM 14 A 由alarm(2)发出的信号
SIGTERM 15 A 终止信号
SIGUSR1 30,10,16 A 用户自定义信号1
SIGUSR2 31,12,17 A 用户自定义信号2
SIGCHLD 20,17,18 B 子进程结束信号
SIGCONT 19,18,25 进程继续(曾被停止的进程)
SIGSTOP 17,19,23 DEF 终止进程
SIGTSTP 18,20,24 D 控制终端(tty)上按下停止键
SIGTTIN 21,21,26 D 后台进程企图从控制终端读
SIGTTOU 22,22,27 D 后台进程企图从控制终端写
SHELL (覆盖命令的shell模式所使用的默认 shell)
Linux 的默认shell是 [“/bin/sh”, “-c”],Windows 的是 [“cmd”, “/S”, “/C”]。SHELL 指令必须以 JSON 格式编写。SHELL 指令在有两个常用的且不太相同的本地 shell:cmd 和 powershell,以及可选的 sh 的 windows 上特别有用