【1】通过文件构建镜像
以下是我们比较推荐的保存镜像的方式,也是我们工作中比较常用的方式,以Dockerfile文件的方式
1.创建一个Dockerfile
注意:一定要创建一个新的目录 因为在构建镜像的时候,会默认把dockerfile所在的目录中的所有目录发送给docker引擎,举个例子 如果你把dockerfile放在/目录下 那么这个过程会变得非常的缓慢
1):mkdir /mnt/docker
vim /mnt/docker/dockerfileFROM busybox #以busybox 基础镜像为模板 RUN echo testfile > file1 #在新镜像中要执行的哪些动作 RUN echo testfile > file2
2):用dockerfile文件来构建镜像docker build -t busyboxtest:v1 .
##busyboxtest为镜像名称,"."代表当前目录
docker images ##查看到创建的镜像
docker history busyboxtest:v1
docker run -it busyboxtest:v1 ##以busyboxtest创建容器查看 / # cat file1 / # cat file2
docker ps -a ##创建容器不给容器起名字的时候,会自动给起名字
再次更改dockerfile缓存特性
再次更改dockerfile 注意:不要随便加空格(要使用缓存之前内容不能随便更改)
vim /mnt/docker/dockerfileFROM busybox RUN echo testfile > file1 RUN echo testfile > file2 RUN echo testfile > file3
docker build -t busyboxtest:v2 .
注意此时的版本不能和之前镜像存在的版本重和docker run -it busyboxtest:v2 / # cat file1 / # cat file3
docker history busyboxtest:v1
docker history busyboxtest:v2
对比来看:v2和v3 有几层是一样的 每一个RUN就会构建一层镜像
而且我们可以看到 每一层的操作
再次说明同一个的镜像层之间是共享的
本质:dockerfile中的每一层其实就是执行了一个docker commit如果我们希望在构建镜像时不使用缓存,可以在docker build命令中加上 --no-cache参数 #dockerfile中每一个指令都会创建一个镜像层,上层是依赖于下层的,无论什么时候,只要某一层发生变化,其上面所有层的缓存都会失败
删除之前做的容器
docker rm -f
【2】dockerfile的详解
便于我们书写更成熟的dockerfile文件
dockerfile常用指令FROM:指定base镜像,如果本地不存在会从远程仓库下载(虚拟机要配置上网) MAINTAINER:设置镜像的作者,比如用户邮箱等 (不是必须的) COPY:把文件从buile context复制到镜像 支持两种形式:COPY src dest 和 COPY ["src","dest"] src必须指定build context中的文件或目录
1:vim /mnt/docker/dockerfile
FROM busybox COPY testfile /tmp ##将testfile文件复制到/tmp下 COPY ["hello","/tmp"] ##将hello文件复制到/tmp下
vim testfile
testvim hello
hello
docker build -t test:v4 .
docker images
docker history test:v4 docker
docker run -it --name vm1 test:v4 / # ls / # cd /tmp/ /tmp # cat hello /tmp # cat testfile # 建议:不用的镜像和容器 要及时删除
2: ADD:用法与COPY类似,不同的是src可以是归档压缩文件,文件会被自动解压到dest,也可以下载URL并拷贝到镜像eg:ADD html.tar /var/www
ADD http://ip/html.tar /var/www
##下载一个nginx包
scp root@172.25.78.250:/mnt/nginx/nginx-1.17.4.tar.gz /mnt/docker
vim /mnt/docker/dockerfileFROM busybox COPY testfile /tmp ADD nginx-1.17.4.tar.gz /tmp
docker build -t test:v5 .docker run -it --name vm2 test:v5 ##进去查看有nginx / # cd /tmp/ /tmp # ls
环境变量 ENV:设置环境变量,变量可以被后续的指令使用(不是必须的) ENV HOSTNAME server1.example.com EXPOSE:如果容器运行应用服务,可以把服务的端口暴露出去
docker history game2048
/bin/sh -c #(nop) EXPOSE 80/ tcp
服务的端口暴露出去 方便我们去做端口映射和用户在容器启动后去访问的 VOLUME:申明数据卷,通常指定的是应用的数据挂载点
#后面会有专门去讲解的
#目的:容器数据持久化的 VOLUME ["/var/www/html"]3:【创建目录】
vim /mnt/docker/dockerfile
FROM busybox COPY testfile /tmp ADD nginx-1.17.4.tar.gz /tmp VOLUME ["/data"] #在启动容器的时候会帮我们自动的新建
docker build -t test:v6 .
docker run -it --name vm1 test:v6
/ # ls ##有data
/ # cd data/
/ # touch file ##创建一个文件
此时再打开一个shell连接server1查看
docker ps -a
docker inspect 20d880b418b3
##看到容器中的/data目录与宿主机上的一个新建的本地目录发生了联系在宿主机中 进入到这个目录中去:(注意:此目录是docker引擎自动帮我们创建出来的,目录名是随机生成的) cd /var/lib/docker/volumes/7b5b369b9e45e050d0d36d2c01812c8977a7fee7a1275dff26f22b36f0e3cc08/_data ls ##有创建的file文件 touch redhat ##在宿主机建立文件,容器中/data目录也会有此文件 在容器中: /data # ls ##有宿主机创建的文件redhat
>
4:如需自己创建,手工指定挂载点如不存在 会自动创建 /opt/data(宿主机上的路径 如果没有会自动帮我们创建) :/data(容器内的路径)
docker run -it --name vm2 -v /opt/data:/data
test:v6
/ # cd /data/
/data # ls
/data # touch westos
/data # ls
/data # exit
退出后在宿主机查看:
cd /opt/ --> ls
cd data/ --> ls##有容器建立的westos文件 在宿主机写westos文件,然后在容器中查看
echo ‘hello’ > westos
cat westos
容器中查看(因为以exit退出,所以要重新启动进入容器) docker start vm2
docker attach vm2
/ # cd data/
/data # cat westos ##有在宿主机写的内容
5:
WOEKDIR:为RUN CMD ENTRYPOINT ADD COPY 指令设置镜像中的当前工作目录(类似于cd
)如果目录不存在会自动创建 RUN:在容器中运行命令并创建新的镜像层,常用于安装包 容器瘦身:每一个RUN指令都会构建一层镜像层 尽可能将 多个命令放在一个RUN指令下 eg:RUN yum clean all && yum repolist CMD与ENTRYPOIN1T:这两个指令都是用于设置容器启动后执行的命令, 但CMD会被docker run后面的命令覆盖,而ENTRYPOINT不会被忽略,一定会被执行 docker run后面的参数可以传递给ENTRYPOINT指令当作参数 dockerfile中只能指定一个ENTRYPOINT,如果指定了很多,只有最后一个有效 CMD与ENTRYPOINT(容器启动后要运行的!!) RUN :在创建容器的时候!!!(在容器中)
两种书写格式的区别 shell exec 先举一个简单的例子:
cd /mnt/docker (shell的方式)
vim dockerfileFROM busybox ENV name world #ENV:定义变量 ENTRYPOINT echo "hello $name"
docker build -t test:v7 .
docker run --rm test:v7 ##(一次性的 运行停止后就删除)
docker ps -a ##并没有容器,因为已经删除
区别:
1):exec的方式vim dockerfile
FROM busybox ENV name world ENTRYPOINT ["/bin/echo","hello,$name"]
docker build -t test:v9.
docker run --rm test:v9 #发现问题 变量无法被解析
2)shell的方式shell格式底层会调用/bin/sh -c 来执行命令,可以解析变量, 而exec格式不会,所以我们需要修改写法
vim dockerfileFROM busybox ENV name world ENTRYPOINT ["/bin/sh","-c","echo hello,$name"] docker build -t test:v10 . docker run --rm test:v10 ##变量被解析
增加参数:exec格式时,ENTRYPOINT可以通过CMD提供的额外参数,CMD的额外参数可以在容器启动时动态替换, 在shell格式时ENTRYPOINT会忽略任何CMD或docker run提供的参数
vim dockerfile
FROM busybox ENTRYPOINT ["/bin/echo","hello"] CMD ["world"]
docker build -t test:v11 .
docker run --rm test:v10 westos
westos 覆盖了dockerfile CMD后面的world值
镜像的优化
选择最经简的基础镜像 减少镜像的层数 清理镜像构建的中间产物 注意优化网络请求 尽量去构建缓存 使用多阶段构建镜像 前提:保证镜像存在于本地 ls ##有rhel7.tar
docker load -i rhel7.tar ##导入此镜像
docker images
cd /mnt/docker
cp /etc/yum.repos.d/westos.repo .
##复制yum文件到当前目录
cat westos.repo
实例:部署nginx1:vim /mnt/docker/dockerfile
FROM rhel7 EXPOSE 80 MAINTAINER dd@westos.org COPY westos.repo /etc/yum.repos.d/ RUN rpmdb --rebuilddb #重新构建rpm数据库 如不执行这一条命令会报错 RUN yum install -y gcc make pcre-devel zlib-devel ADD nginx-1.17.4.tar.gz /mnt WORKDIR /mnt/nginx-1.17.4 ##WORKDIR 相当于命令 cd RUN ./configure --prefix=/usr/local/nginx RUN make RUN make install CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"] #通过exec的方式,nginx的启动方式
docker build -t nginx:v1 .
docker images
2:给镜像"瘦身":减少中间产物
vim dockerfile
FROM rhel7 EXPOSE 80 MAINTAINER dd@westos.org COPY westos.repo /etc/yum.repos.d/ RUN rpmdb --rebuilddb RUN yum install -y gcc make pcre-devel zlib-devel && yum clean all #清理yum缓存 ADD nginx-1.17.4.tar.gz /mnt WORKDIR /mnt/nginx-1.17.4 RUN ./configure --prefix=/usr/local/nginx RUN make RUN make install RUN rm -rf /mnt/nginx-1.17.4 #删除原始编译路径 CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"] # 注意:一但改动 缓存就不能用了 docker build -t nginx:v2 . docker images
3:给镜像"瘦身":减少镜像层数 ,合并多个RUN注意:修改了指令 哪怕是一个空格 缓存就不能用了 使用缓存可以加快镜像构建速度
vim dockerfile
FROM rhel7 EXPOSE 80 MAINTAINER dd@westos.org COPY westos.repo /etc/yum.repos.d/ ADD nginx-1.17.4.tar.gz /mnt WORKDIR /mnt/nginx-1.17.4 RUN rpmdb --rebuilddb && yum install -y gcc make pcre-devel zlib-devel && yum clean all && ./configure --prefix=/usr/local/nginx && make && make install && rm -rf /mnt/nginx-1.17.4 CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"] docker build -t nginx:v3 . docker images
层数对比:
docker history nginx:v1
docker history nginx:v2
docker history nginx:v3 ##层数最少
镜像的优化(续) 使用多阶段构建镜像 #比较优秀的方式(杀手锏)细看dockerfile 我们其实只需要编译好的nginx的二进制文件 , 也就是只取做好的nginx环境,而不要部署nginx的过程
vim dockerfile
FROM rhel7:latest as build EXPOSE 80 MAINTAINER dd@westos.org COPY westos.repo /etc/yum.repos.d/ ADD nginx-1.17.4.tar.gz /mnt WORKDIR /mnt/nginx-1.17.4 RUN rpmdb --rebuilddb && yum install -y gcc make pcre-devel zlib-devel && yum clean all && ./configure --prefix=/usr/local/nginx && make && make install && rm -rf /mnt/nginx-1.17.4 #以上只是一个桥梁 FROM rhel7:latest #基于rhel7的基础镜像 EXPOSE 80 MAINTAINER dd@westos.org VOLUME ["/usr/local/nginx/html"] COPY --from=build /usr/local/nginx /usr/local/nginx #从上一层的构建中拷贝 CMD ["/usr/local/nginx/sbin/nginx","-g","daemon off;"] docker build -t nginx:v4 . #可以发现会很快,安装包完全使用缓存 docker images
那我们有没有办法去减小基础镜像
https://github.com/search?utf8=%E2%9C%93&q=distroless&type=
谷歌为我们提供了非常精简的镜像
docker容器
1.运行容器
docker run是启动容器的方法
docker run ubuntu pwd
docker run ubuntu ls
docker ps -a
# -a会显示所有状态的容器,可以看到,之前的容器已经退出了,状态为exited
# 但是“一闪而过”的容器通常不是我们想要的结果,我们希望容器能够保持running状态,这样才能被我们使用,让容器长期运行
如何让容器保存运行呢?
因为容器的生命周期依赖于启动时执行的命令,只要该命令不结束,容器也就不会退出
docker run ubuntu /bin/bash -c ‘while true;do sleep 1;done’
##会发现退不出去,只有另外打开一个shell进行stop来停止
#while语句让bash不会退出,可以打开另一个终端查看容器的状态
再打开一个shell连接server1查看
docker ps -a
docker stop 0ddba8b13ae4
##通过while启动的容器虽然能够保持运行,但实际上没有干什么有意义的事情,容器常见的用途是运行后台服务##–name指定容器的名字
docker run --name “my_http_server” -d httpd
docker ps
docker inspect 3e938fd312f7 ##查看信息
curl 172.17.0.2
ifconfig
##安装brctl命令
yum whatprovides */brctl
yum install bridge-utils-1.5-9.el7.x86_64 -y
brctl show
【attach 和 exec】
两种进入容器的方法
我们经常需要进入到容器里去做一些工作,比如查看日志,调式,启动其他进程等,
有两种方法进入容器:attach 和 exec
再此处,因为进入容器需要用到yum,所以镜像也得包含yum
所以用文件创建镜像,文件内容写好yum配置
vim /mnt/docker/dockerfile
FROM rhel7:latest COPY westos.repo /etc/yum.repos.d/ RUN rpmdb --rebuilddb && yum clean all docker build -t test:v1 . ##用文件做镜像 所以以下的比较就用test:v1镜像
1:attach-d 以后台的方式启动
docker run -d test:v1 /bin/bash -c "while true; do sleep 1;echo I_am_in_container;done"
docker ps
docker attach 20a96a916dc4
##会一直执行下去
##通过docker attach可以attach到容器启动命令的终端 再打开一个终端server1,停止运行的容器docker stop 20a96a916dc4
2:exec
docker run -d test:v1 /bin/bash -c "while true; do sleep1;echo I_am_in_container;done"
-it:以交互模式打开pseudo-TTY,执行bash,其结果就是打开了一个bash终端
docker exec -it 19e1e1338cdc85405deecdb9190802fbaf09a50f1552e7368c067af08083b2d6 bash bash-4.2# yum whatprovides */ps bash-4.2# yum install procps-ng-3.3.10-17.el7.x86_64 -y bash-4.2# ps -elf ##查看所有的线程
attach与exec主要的区别1.attach直接进入容器启动命令的终端,不会启动新的进程
2.exec则是在容器中打开新的终端,并且可以启动新的进程
3.如果想直接在终端中查看启动命令的输入,用attach,其他情况使用exec#当然,如果只是为了查看启动命令的输出,可以使用docker logs命令 docker logs -f c72e8ee26615
-f的作用与tail -f 类似,能够持续打印输出