docker-操作实战
前言
C++镜像制作
因为我平常不用,所以不书写了
SpringBoot 微服务镜像制作
mkdir java
ca java
cp /data/maxhou/myapp/xxx.jar .
vi Dockerfile
FROM openjdk:8
COPY ./xxx.jar /app.jar
CMD ["java","-jar","/app.jar"]
COPY ./xxx.jar /app.jar就是把宿主机当前目录下的jar包拷贝到镜像目录里面,并改名
CMD就是在创建镜像的时候启动容器的默认命令
docker build -t springboot:v0.1 .
dokcer run -d --name myspringboot1 -p 8888:8080 springboot:v0.1
docker ps
docker logs -f myspringboot1
看的出来这个springboot程序占用的是8080端口,为什么会打印出这个呢
因为CMD命令启动了jar包,CMD就是在创建容器的时候发动的
CMD与EntryPoint实战
基础知识
ENTRYPOINT和CMD都是在docker image里执行一条命令, 但是他们有一些微妙的
区别.一般来说两个大部分功能是相似的都能满足。
比如执行运行一个没有调用ENTRYPOINT或者CMD的docker镜像, 返回错误,一般
的镜像最后都提供了CMD或者EntryPoint作为入口。
覆盖
在写Dockerfile 时, ENTRYPOINT或者CMD命令会自动覆盖之前的ENTRYPOINT
或者CMD命令.
在docker镜像运行时, 用户也可以在命令指定具体命令, 覆盖在Dockerfile里的命令.
如果你希望你的docker镜像只执行一个具体程序, 不希望用户在执行docker run的时
候随意覆盖默认程序. 建议用ENTRYPOINT.
就是写了很多个CMD和EntryPoint,只会执行最后一个,后面的会把前面的覆盖掉
docker run的时候指定命令的话,会覆盖CMD,而EntryPoint不会被覆盖,docker run的时候指定–entrypoint,就可以覆盖EntryPoint了
Shell 和 Exec模式
ENTRYPOINT和CMD指令支持2种不同的写法: shell表示法和exec表示法.
CMD命令语法
shell模式就是直接把命令放在后面,exec模式就是把命令用双引号用json数组的形式放在里面
Shell
#EXEC 语法
CMD ["executable","param1","param2"] (exec form, this is the
preferred form)
#用于给ENTRYPOINT 传入参数,推荐使用
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
#shell 语法
CMD command param1 param2 (shell form)
ENTRYPOINT语法
Shell
EXEC 语法
ENTRYPOINT ["executable", "param1", "param2"] (exec form,
preferred)
Shell 语法
ENTRYPOINT command param1 param2 (shell form)
当使用shell表示法时, 命令行程序作为sh程序的子程序运行, docker用/bin/sh -c的语
法调用. 如果我们用docker ps命令查看运行的docker, 就可以看出实际运行的是
/bin/sh -c 命令。这样运行的结果就是我们启动的程序的PID不是1,如果从外部发送
任何POSIX信号到docker容器, 由于/bin/sh命令不会转发消息给实际运行的命令, 则
比特就业课
不能安全得关闭docker容器。
EXEC语法没有启动/bin/sh命令, 而是直接运行提供的命令, 命令的PID是1. 无论你用
的是ENTRYPOINT还是CMD命令, 都强烈建议采用exec表示法。
CMD nginx -g “daemon off;”shell模式
执行的是 /bin/sh -c “nginx -g “daemon off;””-------》nginx的pid不是1,sh的pid才是1
如果docker stop的时候,并不会把指令转发给nginx,nginx最后也是强制结束,不是正常结束
CMD [“nginx”,“-g”, “daemon off;”]exec模式
nginx -g "daemon off;"这个就是这样运行的----》nginx的pid是1,docker stop会把停止信号发给nginx 进程,nginx进程就会优雅的结束
所以我们推荐使用exec模式
组合模式
组合使用ENTRYPOINT和CMD, ENTRYPOINT指定默认的运行命令, CMD指定默认
的运行参数.ENTRYPOINT和CMD同时存在时, docker把CMD的命令拼接到
ENTRYPOINT命令之后, 拼接后的命令才是最终执行的命令.
组合使用ENTRYPOINT和CMD,CMD会作为参数传递给ENTRYPOINT,所有ENTRYPOINT单独负责可执行程序,CMD单独负责参数,推荐这个
实战-多次覆盖
mkdir cmd
cd cmd
vi Dockerfile
FROM busybox
CMD echo "hello world!"
CMD echo "hello bit!"
docker build -t cmd:v0.1 .
docker run -it --rm cmd:v0.1
说明后面的覆盖前面的了
cp Dockerfile Dockerfile2
vi Dockerfile2
FROM busybox
ENTRYPOINT echo "hello world!"
ENTRYPOINT echo "hello bit!"
ENTRYPOINT 也是这样的
docker build -t cmd:v0.2 -f Dockerfile2 .
docker run -it --rm cmd:v0.2
参数覆盖
docker run -it --rm cmd:v0.1 echo hello bit2!
说明docker run的指令会把CMD覆盖
docker run -it --rm cmd:v0.2 echo hello bit2!
我们发现ENTRYPOINT是不会被docker run的指令覆盖的,还是会执行ENTRYPOINT的
我们要覆盖entrypoint的话,就要指定–entrypoint
docker run -it --entrypoint “/bin/sh” --rm cmd:v0.2 -c “echo hello bit2!”
这个就是把"/bin/sh" 覆盖为 “echo hello bit2!”,-c是指定覆盖的内容
因为是shell模式,所以只能去覆盖/bin/sh,ENTRYPOINT的内容都是/bin/sh的参数
这样我们就覆盖ENTRYPOINT了
如果是exec格式的话,
cp Dockerfile2 Dockerfile3
vi Dockerfile3
FROM busybox
ENTRYPOINT echo "hello world!"
ENTRYPOINT ["echo","hello bit!"]
docker build -t cmd:v0.3 -f Dockerfile3 .
docker run -it --rm cmd:v0.3 echo hello bit2!
我们可以发现变成了这个样子
因为这个相当于把echo hello bit2!这个命令全部作为参数全部喂给了ENTRYPOINT 中的echo命令
因为在shell格式中,echo hello bit2!无法作为参数赋给/bin/sh,所以没有什么用,
所以cmd,ENTRYPOINT 可以正常运行
但是exec格式,会把docker run里面的内容作为参数喂给ENTRYPOINT 中的命令
Shell VS Exec
vi Dockerfile4
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
CMD ping localhost
docker build -t cmd:v0.4 -f Dockerfile4 .
docker run -it --rm cmd:v0.4
我们发现一直在ping本机
其实命令不是ping xxxx
而是 /bin/sh -c "ping xxx "
另一个shell
docker exec -it 容器id bash
ps -ef
这里可以看到pid为1的 不是ping
这个就是shell格式
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
CMD ["ping", "localhost"]
docker build -t cmd:v0.5 -f Dockerfile4 .
docker run -it --rm cmd:v0.5
另一个shell
docker ps
docker exec -it 容器id bash
ps -ef
强烈推荐使用exec格式
组合
就是ENTYRPOINT存命令
CMD存参数
vi Dockerfile6
FROM ubuntu:22.04
RUN apt-get update -y && apt install -y iputils-ping
ENTRYPOINT ["ping","-c","3"]
CMD ["localhost"]
参数就是[“localhost”]
docker build -t cmd:v0.6 -f Dockerfile6 .
docker run -it --rm cmd:v0.6
ping了三次就自动停止了,如果没有指定,就会一直ping
docker run -it --rm cmd:v0.6 www.baidu.com
这样也可以
因为www.baidu.com会替换CMD,作为ENTEYPOINT的参数
合理使用dockerignore
基础知识
Docker 是 C-S 架构,理论上Client和Server可以不在一台机器上。在构建docker镜
像的时候,需要把所需要的文件由Client发送给Server,这些要发送的文件叫做
build context。
回忆一下我们之前讲到的构建镜像的命令, 在命令的最后我们加了一个.表示当前目
录, 这个目录其实就代表了build context所指向的目录。
Shell
docker build -f -t <dockerfile_name> .
如果想忽略掉一些传送给Sever端的文件, 这就会用到.dockerignore文件。它会将记
录的所有文件都忽略掉, 不会传送给Server端, 有效的避免一些和容器内应用运行
无关的文件不会被复制到Server端, 即不会将无关的文件打入生成的镜像中
实战
mkdir ignore
cd ignore
vi Dockerfile
FROM centos:7
COPY ./* /
vi .dockerignore
*.txt
这个就是会忽略txt文件
echo "hello bit data " > ./test.data
echo “hello bit txt” > ./test.txt
docker build -t ignore:v0.1 .
docker run -it --rm ignore:v0.1 bash
ll
我们看到并没有test.txt
多阶段构建
基础知识
构建docker镜像可以有下面两种方式
• 将全部组件及其依赖库的编译、测试、打包等流程封装进一个docker镜像中。但
是这种方式存在一些问题, 比如 Dockefile 特别长,可维护性降低;镜像的层次多,
体积大,部署时间长等问题
• 将每个阶段分散到多个 Dockerfile。一个 Dockerfile 负责将项目及其依赖库
编译测试打包好后,然后将运行文件拷贝到运行环境中,这种方式需要我们编写多个
Dockerfile 以及一些自动化脚本才能将其两个阶段自动整合起来
• 为了解决以上的两个问题,Docker 17.05版本开始支持多镜像阶段构建。只需
要编写一个 Dockerfile即可解决上述问题。
实战
我们先拷贝之前c++写的Dockerfile
cp -r …/cpp/* .
docker build -t multi:v1.0 .
docker images multi
docker run --rm -it multi:v1.0
但是镜像很大
我们来修改Dockerfile
FROM centos:7
# 设置版本
ENV VERSION 1.0
#设置国内源
RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e
's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirro
rs.ustc.edu.cn/centos|g' \
-i.bak \
/etc/yum.repos.d/CentOS-Base.repo
RUN yum makecache
# 设置工作目录
WORKDIR /src
# 拷贝源文件
COPY demo.c .
# 安装gcc
RUN yum install -y gcc && \
gcc -v
# 编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum erase -y gcc
# 运行可执行文件
CMD ["/src/demo"]
修改后
FROM centos:7 as buildstage
# 设置版本
ENV VERSION 1.0
#设置国内源
RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e
's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirro
rs.ustc.edu.cn/centos|g' \
-i.bak \
/etc/yum.repos.d/CentOS-Base.repo
RUN yum makecache
# 设置工作目录
WORKDIR /src
# 拷贝源文件
COPY demo.c .
# 安装gcc
RUN yum install -y gcc && \
gcc -v
# 编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum erase -y gcc
# 运行可执行文件
CMD ["/src/demo"]
FROM centos:7
COPY --from=buildstage /src/demo /src/
CMD ["/src/demo"]
docker build -t multi:v2.0 .
docker images multi
docker run --rm -it multi:v2.0
多级构建之后大小大大的减小了
FROM centos:7 as buildstage
# 设置版本
ENV VERSION 1.0
#设置国内源
RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e
's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirro
rs.ustc.edu.cn/centos|g' \
-i.bak \
/etc/yum.repos.d/CentOS-Base.repo
RUN yum makecache
# 设置工作目录
WORKDIR /src
# 拷贝源文件
COPY demo.c .
# 安装gcc
RUN yum install -y gcc && \
gcc -v
# 编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum erase -y gcc
# 运行可执行文件
CMD ["/src/demo"]
FROM busybox
COPY --from=buildstage /src/demo /src/
CMD ["/src/demo"]
docker build -t multi:v3.0 .
docker images multi
docker run --rm -it multi:v2.0
构建docker镜像可以有下面两种方式
• 将全部组件及其依赖库的编译、测试、打包等流程封装进一个docker镜像中。但
是这种方式存在一些问题, 比如 Dockefile 特别长,可维护性降低;镜像的层次多,
体积大,部署时间长等问题
• 将每个阶段分散到多个 Dockerfile。一个 Dockerfile 负责将项目及其依赖库
编译测试打包好后,然后将运行文件拷贝到运行环境中,这种方式需要我们编写多个
Dockerfile 以及一些自动化脚本才能将其两个阶段自动整合起来
• 为了解决以上的两个问题,Docker 17.05版本开始支持多镜像阶段构建。只需
要编写一个 Dockerfile即可解决上述问题。
合理使用缓存
基础知识
• 在镜像的构建过程中,Docker 会根据 Dockerfile 指定的顺序执行每个指令。在执
行每条指令之前,Docker 都会在缓存中查找是否已经存在可重用的镜像,如果有就使
用现存的镜像,不会再重复创建。
• 在上边提到 Dockerfile中的每一条指令都会产生一层image layer。当某一个
layer 修改后,后面的所有layer我们都不能使用缓存, 这一点一定要注意。
• 如果不想在构建过程中使用缓存,你可以在 docker build 命令中使用 --no
cache=true 选项。但是我们建议最好合理使用缓存, 这样可以加快构建镜像的效率。
编译软件放在上面,那么每次修改代码都能使用安装编译软件的缓存了,不然使用不到它的缓存
实战
FROM centos:7
# 设置版本
ENV VERSION 1.0
#设置国内源
RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e
's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirro
rs.ustc.edu.cn/centos|g' \
-i.bak \
/etc/yum.repos.d/CentOS-Base.repo
RUN yum makecache
# 设置工作目录
WORKDIR /src
# 拷贝源文件
COPY demo.c .
# 安装gcc
RUN yum install -y gcc && \
gcc -v
# 编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum erase -y gcc
# 运行可执行文件
CMD ["/src/demo"]
docker build -t mucache:v1.0 .
因为有缓存,所以构建很快
vi demo.c
docker build -t mucache:v2.0 .
这次就很慢了
看的出来第一次构建用了0.1s
第二次用了20多s
FROM centos:7
# 设置版本
ENV VERSION 1.0
#设置国内源
RUN sed -e 's|^mirrorlist=|#mirrorlist=|g' \
-e
's|^#baseurl=http://mirror.centos.org/centos|baseurl=https://mirro
rs.ustc.edu.cn/centos|g' \
-i.bak \
/etc/yum.repos.d/CentOS-Base.repo
RUN yum makecache
# 设置工作目录
WORKDIR /src
# 安装gcc
RUN yum install -y gcc && \
gcc -v
# 拷贝源文件
COPY demo.c .
# 编译源文件
RUN gcc demo.c -o demo && \
rm -f demo.c && \
yum erase -y gcc
# 运行可执行文件
CMD ["/src/demo"]
现在优化一下顺序
docker build -t mucache:v3.0 .
vi demo.c
docker build -t mucache:v4.0 .
这样就变快了