Docker超详解,包括Docker基本命令和Dockerfile

1 Docker简介

1.1 Docker是什么

有助于持续集成和部署的 容器虚拟化技术,运行环境和配置的标准化解决方案。

1.2 为什么需要Docker

解决环境配置的难题(软件带环境安装)。
常用的两种用于解决带环境安装技术,虚拟机技术容器虚拟化技术

  1. 虚拟机(virtual machine)就是带环境安装的一种解决方案。它可以在一种操作系统里面运行另一种操作系统,比如在 Windows 系统里面运行 Linux 系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响。

    • 资源占用多
      虚拟机会独占一部分内存和硬盘空间。它运行的时候,其他程序就不能使用这些资源了。哪怕虚拟机里面的应用程序,真正使用的内存只有 1MB,虚拟机依然需要几百 MB 的内存才能运行。

    • 冗余步骤多
      虚拟机是完整的操作系统,一些系统级别的操作步骤,往往无法跳过,比如用户登录。

    • 启动慢
      启动操作系统需要多久,启动虚拟机就需要多久。可能要等几分钟,应用程序才能真正运行。

  2. 容器虚拟化不是模拟一个完整的操作系统,而是对进程进行隔离。或者说,在正常进程的外面套了一个保护层。对于容器里面的进程来说,它接触到的各种资源都是虚拟的,从而实现与底层系统的隔离。由于容器是进程级别的,相比虚拟机有很多优势。

    • 启动快
      容器里面的应用,直接就是底层系统的一个进程,而不是虚拟机内部的进程。所以,启动容器相当于启动本机的一个进程,而不是启动一个操作系统,速度就快很多。

    • 资源占用少
      容器只占用需要的资源,不占用那些没有用到的资源;虚拟机由于是完整的操作系统,不可避免要占用所
      有资源。另外,多个容器可以共享资源,虚拟机都是独享资源。

      扫描二维码关注公众号,回复: 17066635 查看本文章
    • 体积小
      容器只要包含用到的组件即可,而虚拟机是整个操作系统的打包,所以容器文件比虚拟机文件要小很多。

  3. 虚拟机和容器虚拟化比较

image.png
虚拟化的封装是系统级的封装,docker 或者其他容器是进程级的封装。

Docker容器技术与传统虚拟机技术的比较
特性 容器 虚拟机
启动速度 秒级 分钟级
性能 接近原生 较弱
内存代价 很小 较多
硬盘使用 一般为MB 一般为GB
运行密度 单机支持上千个容器 一般几十个
隔离性 安全隔离 安全隔离
迁移性 优秀 一般

1.3 Docker组成

1.3.1 docker的基本组成

  • 镜像(image):**Docker 把应用程序及其依赖,打包在 image 文件里面。**只有通过这个文件,才能生成 Docker 容器。image 文件可以看作是容器的模板。Docker 根据 image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。image 是二进制文件。实际开发中,一个 image 文件往往通过继承另一个 image 文件,加上一些个性化设置而生成。举例来说,你可以在 Ubuntu 的 image 基础上,往里面加入 Apache 服务器,形成你的 image。
  • 容器(container):**image 文件生成的容器实例,本身也是一个文件,称为容器文件。**也就是说,一旦容器生成,就会同时存在两个文件: image 文件和容器文件。而且关闭容器并不会删除容器文件,只是容器停止运行而已。
  • 仓库(Repository):仓库是集中存放镜像文件的场所。仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是 Docker Hub(https://hub.docker.com/),国内的公开仓库包括阿里云 、网易云 等。

1.3.2 docker架构图

在这里插入图片描述
docker run 具有自动抓取 Image 文件的功能。如果发现本地没有指定的 Image 文件,就会从仓库自动抓取。

1.4 docker基础命令

1.4.1 docker基础命令

# 1.启动docker
systemctl start docker 或 service docker start
# 2.设置开机启动docker服务:
systemctl enable docker
# 3.关闭docker
systemctl stop docker 或 service docker stop
# 4.重启docker
systemctl restart docker 或 service docker restart
# 5.查看docker 版本号和详细信息
docker version
docker info
# 6.docker 帮助命令
docker --help # 忘记了docker有哪些命令,便可使用此进行查看与回顾
docker pull --help # 拉取命令 不知道可以带哪些参数,可以这样使用

1.4.2 docker镜像命令

# 1.查看服务器中docker镜像列表
docker images
# 2.搜索镜像
docker search 镜像名
docker search --filter=STARS=9000 mysql # 搜索 STARS >9000的 mysql 镜像
# 3.拉取镜像
docker pull 镜像名 # 不加tag(版本号) 即拉取docker仓库中该镜像的最新版本latest
docker pull 镜像名:tag # 加:tag 则是拉取指定版本
# 4.删除镜像 ------当前镜像没有被任何容器使用才可以删除
docker rmi -f 镜像名/镜像ID # 删除一个
docker rmi -f 镜像名/镜像ID 镜像名/镜像ID 镜像名/镜像ID # 删除多个,镜像ID或镜像名用空格隔开即可 
docker rmi -f $(docker images -aq) # 删除全部镜像  -a 意思为显示全部, -q 意思为只显示ID
# 5.强制删除镜像
docker image rm -f 镜像名称/镜像ID
# 6.保存镜像
docker save 镜像名/镜像ID -o 镜像保存的位置与名字 # docker save tomcat -o haha/myTomcat.tar
# 7.加载镜像
docker load -i 镜像保存文件位置
# 8.给镜像打标签
docker tag SOURCE_IMAGE[:TAG] TARGET_IMAGE[:TAG]
docker tag 源镜像名:TAG 想要生成新的镜像名:新的TAG
# 8.1 如果省略TAG 则会为镜像默认打上latest TAG
docker tag aaa bbb # 等于 docker tag aaa:latest bbb:test
# 9.上传镜像到docker hub仓库
docker push<hub-user>/<repo-name>:<tag> # docker push pikaiqu/myTomcat:0001
# 10.查看指定镜像的创建历史。
docker history pikaqiu/ubuntu:v3		
# 11.从容器创建一个新的镜像
docker commit -a "pikaqiu.com" -m "my apache" a404c6c174a2  mymysql:v1 # 将容器a404c6c174a2 保存为新的镜像,并添加提交人信息和说明信息。

1.4.3 docker容器命令

# 1.查看正在运行容器列表
docker ps
# 2.查看所有容器 -----包含正在运行 和已停止的
docker ps -a
# 3. 创建容器但不运行
docker create -p 3000:80 --name exampleApp pikaqiu/exampleapp
# 4.运行一个容器
docker run -it -d --name 要取的别名 镜像名:Tag /bin/bash # -it 表示 与容器进行交互式启动 -d 表示可后台运行容器 (守护式运行)  --name 给要运行的容器 起的名字  /bin/bash  交互路径
# 5.删除容器 
docker rm -f 容器名/容器ID # 删除一个容器
docker rm -f 容器名/容器ID 容器名/容器ID # 删除多个容器,空格隔开要删除的容器名或容器ID
docker rm -f $(docker ps -aq) # 删除全部容器
# 6.容器端口与服务器端口映射
docker run -itd --name redis001 -p 8888:6379 redis:5.0.5 /bin/bash
# 7.进入容器
# 7.1 方式一
docker exec -it 容器名/容器ID /bin/bash # docker exec -it redis001 /bin/bash
# 7.2 方式二
docker attach 容器名/容器ID
# 8.从容器内 退出到自己服务器中 
exit # 直接退出  未添加 -d(持久化运行容器) 时 执行此参数 容器会被关闭  
Ctrl + p + q # 优雅退出 --- 无论是否添加-d 参数 执行此命令容器都不会被关闭
# 9.停止容器
docker stop 容器ID/容器名
# 10.重启容器
docker restart 容器ID/容器名
# 11.启动容器
docker start 容器ID/容器名
# 12.强制停止容器
docker kill 容器ID/容器名
# 13.容器文件拷贝 --- 无论容器是否开启 都可以进行拷贝,docker cp 容器ID/名称:文件路径  要拷贝到外部的路径  或  要拷贝到外部的路径  容器ID/名称:文件路径
# 13.1 从容器内拷出
docker cp 容器ID/名称: 容器内路径  容器外路径
# 13.2 从外部 拷贝文件到容器内
docker  cp 容器外路径 容器ID/名称: 容器内路径
# 14.查看容器日志
docker logs -f --tail=要查看末尾多少行 默认all 容器ID
# 15.目录挂载
# -v 宿主机文件存储位置:容器内文件位置
# 运行一个docker redis 容器 进行 端口映射 两个数据卷挂载 设置开机自启动
docker run -d -p 6379:6379 --name redis001 --restart=always  -v /var/lib/redis/data/:/data -v /var/lib/redis/conf/:/usr/local/etc/redis/redis.conf  redis:5.0.5 --requirepass "password"
# 16.更换容器名
docker rename 容器ID/容器名 新容器名

1.4.4 docker运维命令

# 1.查看镜像、容器、数据卷所占的空间大小
docker system df 
# 2.删除异常停止的容器
docker rm `docker ps -a | grep Exited | awk '{print $1}'` 
# 3.删除名称或标签为none的镜像
docker rmi -f  `docker images | grep '<none>' | awk '{print $3}'`
# 4.查找大文件
find / -type f -size +100M -print0 | xargs -0 du -h | sort -nr
# 5.查找指定docker使用目录下大于指定大小文件
find / -type f -size +100M -print0 | xargs -0 du -h | sort -nr |grep '/var/lib/docker/overlay2/*'

1.4.5 容器互联

# 1.创建一个新的 Docker 网络
# -d 参数指定 Docker 网络类型,有 bridge overlay。其中 overlay 网络类型用于 Swarm mode
docker network create -d bridge my-net
# 2.连接容器
# 2.1 运行一个容器并连接到新建的 my-net 网络
docker run -it --rm --name busybox1 --network my-net busybox sh
# 2.2 打开新的终端,再运行一个容器并加入到 my-net 网络
docker run -it --rm --name busybox2 --network my-net busybox sh
# 3.通过 ping 来证明 busybox1 容器和 busybox2 容器建立了互联关系
# 3.1 在busybox1 中ping busybox2
ping busybox2
# 3.2 在busybox2中 ping busybox1
ping busybox1

2 Dockerfile详解

2.1 什么是Dockerfile

Dockerfile仅仅是用来制作镜像的源码文件,是构建容器过程中的指令,docker能够读取dockerfile的指定进行自动构建容器,基于dockerfile制作镜像,每一个指令都会创建一个镜像层,即镜像都是多层叠加而成(如图所示),因此,层越多,效率越低,创建镜像,层越少越好。因此能在一个指令完成的动作尽量通过一个指令定义。

Docker镜像都是只读的,当容器启动时,一个新的可写层镜像加载到镜像顶部,这一层就是我们通常说的容器层,容器之下的都叫镜像层。 分层 :现在构建一个镜像,第一层需要一个Linux内核 比如是centos 6.8、第二层需要安装nginx、第三层安装php、第四层安装mysql5.0版本、如果有一天需要一个mysql5.5版本,可以直接把第四层替换掉,给它换成mysql5.5版本。这样的话一个镜像看起来是一个文件,其实它并不是一个文件,而是由很多很多个子文件结合起来,但也是有数目限制的,限制池为128,也就是不能够超过128层。如果新建一个有很多层级的新镜像,其中某一层和之前的镜像某一层是一样的话,则会跳过这一层,直接下载别的,这样就大大的减少了存储量(共享资源,有多个镜像都从相同的 base 镜像构建而来,那么宿主机只需在磁盘上保存一份base镜像,同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了,而且镜像的每一层都可以被共享。)

2.2 使用Dockerfile的目的

实现制作镜像脚本化,同时用户可以根据自己的需要对官方镜像做扩展,或者将自己的应用打包成镜像,实现容器化部署。

2.3 基于Dockerfile实现image生成、保存,容器运行的完整过程

1、编写一个Dockerfile 文件
2、docker build构建成为一个镜像
3、docker run 运行镜像
4、docker push 发布镜像(Docker Hub 、阿里云镜像仓库)
image.png

2.4 Dockerfile语法格式

FROM #基础镜像,一切从这里开始构建
MAINTAINER #镜像是谁写的,名字+邮箱(新版即将废弃,被LABEL替代)
LABEL #附加到Ilmage之上的元数据,键值格式
RUN #镜像构建的时候需要运行的命令
ADD #将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget
WORKDIR #镜像的工作目录(为RUN、CMD、ENTRYPOINT、COPY和ADD等指令设定工作目录)
VOLUME #指定基于新生成的Image运行Container时期望作为Volume使用的目录
EXPOSE #指定基于新生成的Image运行Container时期望暴露的端口,但实际暴露与否取决于docker run命令的选项,支持TCP和UDP协议
CMD #指定这个容器启动的时候要运行的命令(只有最后一个会生效,可被替代)
ENTRYPOINT #类似于CMD指令的功能,指定这个容器启动的时候要运行的命令,可以追加命令,于CMD共存时,CMD的内容将作为指令中定义的程序的参数
COPY #功能类似ADD,但是不会自动解压文件,也不能访问网络资源,可复制主机上或者前一阶段构建结果中(需要使用--from选项)文件或目录生成新的镜像层
ARG #定义专用于build过程中的变量,但仅对该指标之后的调用生效,其值可由命令行选项"--build-arg"进行传递
ENV #构建的时候以键值格式设置环境变量,可被其后的指令所调用,且基于新生成的lmage运行的Container中也会存在这些变量

Dockerfile 分为四部分基础镜像信息(FROM)、维护者信息(MAINTAINER、LABEL)、镜像操作指令(RUN、ADD、WORKDIR、VOLUME、EXPOSE、COPY、ARG、ENV)和 容器启动时执行指令CMD、ENTRYPOINT)。

2.4.1 FROM

指定基础镜像,必须为第一个命令

格式:
  FROM <image>
  FROM <image>:<tag> #不填tag默认为latest
  FROM <image>@<digest> #digest可用于验证下载的镜像是否保证完整性,FROM node:12.18.4-alpine@sha256:757574c5a2102627de54971a0083d4ecd24eb48fdf06b234d063f19f7bbc22fb
示例:  
	FROM mysql:5.6
注:
   tag或digest是可选的,如果不使用这两个值时,会使用latest版本的基础镜像

2.4.2 MAINTAINER(新版即将废弃)

维护者信息

格式:
    MAINTAINER <name>
示例:
    MAINTAINER pikaqiu
    MAINTAINER [email protected]
    MAINTAINER pikaqiu <[email protected]>

2.4.3 LABEL

用于为镜像添加元数据

格式:
    LABEL <key>=<value> <key>=<value> <key>=<value> ...
示例:
  LABEL version="1.0" description="这是一个Web服务器" by="pikaqiu"
注:
  使用LABEL指定元数据时,一条LABEL指定可以指定一或多条元数据,指定多条元数据时不同元数据
  之间通过空格分隔。推荐将所有的元数据通过一条LABEL指令指定,以免生成过多的中间镜像(因为每一个指令都会创建一个镜像层)。

2.4.4 EXPOSE

指定于外界交互的端口

格式:
    EXPOSE <port> [<port>...]
示例:
    EXPOSE 80 443
    EXPOSE 8080    
    EXPOSE 11211/tcp 11211/udp
注:EXPOSE并不会让容器的端口访问到主机。要使其可访问,需要在docker run运行容器时通过-p来发布这些端口,或通过-P参数来发布EXPOSE导出的所有端口
如果没有暴露端口,后期也可以通过-p 8080:80方式映射端口,但是不能通过-P形式映射

2.4.5 VOLUME

容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。
为了防止运行时用户忘记将动态文件所保存目录挂载为卷,在 Dockerfile 中,可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据。
docker容器数据卷更详细的可参考 https://www.cnblogs.com/sucretan2010/p/11014957.html

  1. 匿名卷
VOLUME /data

这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无a状态化。
在容器中会自动创建/data目录
image.png
使用docker inspect 容器名可以看到/data目录挂载到了本地机器上的 /var/lib/docker/volumes/***中
image.png

  1. 命名卷

运行时可以覆盖这个挂载设置,比如:

docker run -d -v mydata:/data 镜像名
# 使用了 mydata 这个命名卷挂载到了 /data 这个位置,替代了 Dockerfile 中定义的匿名卷的挂载配置
VOLUME /var/data /var/log
# 指定容器中的/var/log挂载到宿主机的/var/data目录,等同于-v /var/data:/var/log

2.4.6 RUN

每条 RUN 指令将在当前镜像的基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行,多条命令用&&来连接,避免执行多个RUN,使得镜像层数变多,最好只保留一个RUN。

RUN tar xf nginx-${nginx_ver}.tar.gz && \
        yum -y install gcc pcre-devel openssl-devel make &&  \
        cd nginx-${nginx_ver} && \
        ./configure && make && make install
RUN用于在构建镜像时执行命令,其有以下两种命令执行方式:
shell执行
格式:
    RUN <command>
exec执行
格式:
    RUN ["executable", "param1", "param2"]
示例:
    RUN apk update
    RUN ["/etc/execfile", "arg1", "arg1"]
注:RUN指令创建的中间镜像会被缓存,并会在下次构建中使用。如果不想使用这些缓存镜像,
可以在构建时指定--no-cache参数,如:docker build --no-cache

2.4.7 WORKDIR

镜像的工作目录(为RUN、CMD、ENTRYPOINT、COPY和ADD等指令设定工作目录),类似于cd命令。

格式:
    WORKDIR /path/to/workdir
示例:
    WORKDIR /a  (这时工作目录为/a)
    WORKDIR b  (这时工作目录为/a/b)
    WORKDIR c  (这时工作目录为/a/b/c)
注: 
  通过WORKDIR设置工作目录后,Dockerfile中其后的命令RUN、CMD、ENTRYPOINT、ADD、COPY
  等命令都会在该目录下执行。在使用docker run运行容器时,可以通过-w参数覆盖构建时所设置的工作目录。

2.4.8 ADD

将本地文件添加到容器中,tar类型文件会自动解压(网络压缩资源不会被解压),可以访问网络资源,类似wget

格式:
    ADD <src>... <dest>
    ADD ["<src>",... "<dest>"] 用于支持包含空格的路径
示例:
    ADD hom* /mydir/          # 添加所有以"hom"开头的文件
    ADD hom?.txt /mydir/      # ? 替代一个单字符,例如:"home.txt"
    ADD test relativeDir/     # 添加 "test" 到 `WORKDIR`/relativeDir/
    ADD test /absoluteDir/    # 添加 "test" 到 /absoluteDir/
注意:尽量别用,会增加镜像的大小,ADD 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。

2.4.9 COPY

与ADD类似,对于压缩文件,不会自动解压
注意:不能直接拷贝目录,但可以拷贝目录下的内容,使用 COPY 指令,源文件的各种元数据都会保留。比如读、写、执行权限、文件变更时间等。

COPY config/ /opt/config/	把当前config目录下所有文件拷贝到/opt/config/下,如果/opt/config不存在,会创建

2.4.10 ARG(给dockerfile传参)

用于指定传递给构建运行时的变量(给dockerfile传参),相当于构建镜像时可以在外部为里面传参

格式:
    ARG <name>[=<default value>]
示例:
    ARG site
    ARG build_user=www

From centos:7
ARG parameter
VOLUME /usr/share/nginx
RUN yum -y install $parameter
EXPOSE 80 443
CMD nginx -g "daemon off;"

# 可以如下这样灵活传参
docker build --build-arg=parameter=net-tools -t nginx:01 . 

2.4.11 ENV

设置环境变量

格式:
#一次设置一个变量,#<key>之后的所有内容均会被视为其<value>的组成部分
ENV <key> <value>    
#设置多个环境变量,每个变量为一个"<key>=<value>"的键值对,如果<key>中包含空格,可以使用\来进行转义,也可以通过""来进行标示;另外,反斜线也可以用于续行
ENV <key1>=<value1> 	<key2>=<value2> \
 <key3>=<value3>   

示例:
    ENV myName John Doe
    ENV myDog Rex The Dog	
    ENV myCat=fluffy	myName=zhangsan
ENV  DOC_ROOt=/data/web/html/    单个文件 
ENV  DOC_ROOt=/data/web/html/ \     多个文件
     WEB_SERVER_PACKAGE="nginx-1.15.5"

COPY index.html ${DOC_ROOT:-/data/web/html/} # ${variable:-defaultValue} 表示如果变量值存在,就是用原来的值,否则使用默认值
COPY yum.repos.d /etc/yum.repos.d/
#ADD http://nginx.org/download/nginx-1.15.5.tar.gz /usr/local/src/
WORKDIR /usr/locl/
ADD ${WEB_SERVER_PACKAGE}.tar.gz ./src/

2.4.12 CMD

指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD 命令。如果指定了多条 CMD 命令,只有
最后一条会被执行。如果用户在启动容器时指定了要运行的命令,则会覆盖掉 CMD 指定的命令。
覆盖的原因:CMD给出的是一个容器的默认的可执行体。也就是容器启动以后,默认的执行命令。重点就是这个“默认”。意味着,如果docker run没有指定任何的执行命令或者dockerfile里面也没有ENTRYPOINT,那么,就会使用CMD指定的默认的执行命令执行。同时也从侧面说明了ENTRYPOINT的含义,它才是真正的容器启动以后要执行命令。

格式:
    CMD ["executable","param1","param2"] (执行可执行文件,优先)
    CMD ["param1","param2"] (设置了ENTRYPOINT,则直接调用ENTRYPOINT添加参数)
    CMD command param1 param2 (执行shell内部命令)
示例:
    CMD echo "This is a test." # 这种格式,在创建容器后会首先调用Shell,即自动在命令前面追加/bin/sh -c,即上述设置将执行如下命令:/bin/sh -c echo "This is a test."
    CMD ["/usr/bin/wc","--help"] # 如果你想在没有 shell 的情况下运行 <command> ,那么必须将命令表示为 JSON 数组并提供可执行文件的完整路径。

注:CMD不同于RUN,CMD用于指定在容器启动时所要执行的命令,而RUN用于指定镜像构建时所要执行的命令。

2.4.13 ENTRYPOINT

配置容器启动后执行的命令,并且不会被 docker run 提供的参数覆盖(但是,docker run的**–entrypoint**可以覆盖Dockerfile中ENTRYPOINT设置的命令)。
每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个 ENTRYPOINT 时,只有最后一个生效。
当指定了 ENTRYPOINT 后,CMD 的含义就发生了改变,不再是直接的运行其命令,而是将 CMD 的内容作为参数传给 ENTRYPOINT 指令,换句话说实际执行时,将变为:<ENTRYPOINT> "<CMD>"

格式:
    ENTRYPOINT ["executable", "param1", "param2"] (可执行文件, 优先,这种格式可追加CMD参数)
    ENTRYPOINT command param1 param2 (shell内部命令,这种格式屏蔽追加任何参数)
示例:
    FROM ubuntu
    ENTRYPOINT ["ls", "/usr/local"]
    CMD ["/usr/local/tomcat"]
  之后,docker run 传递的参数,都会先覆盖cmd,然后由cmd 传递给entrypoint ,做到灵活应用

注:ENTRYPOINT与CMD非常类似,不同的是通过docker run执行的命令不会覆盖ENTRYPOINT,
 而docker run命令中指定的任何参数,都会被当做参数再次传递给CMD。
 Dockerfile中只允许有一个ENTRYPOINT命令,多指定时会覆盖前面的设置,
 而只执行最后的ENTRYPOINT指令。
 通常情况下,ENTRYPOINT 与CMD一起使用,ENTRYPOINT 写默认命令,当需要参数时候 使用CMD传参。
CMD和ENTRYPOINT的区别可参考:https://docs.docker.com/engine/reference/builder/

2.4.14 CMD和ENTRYPOINT的比较

  1. Dockerfile 应至少指定 CMD 或 ENTRYPOINT 命令之一。
  2. ENTRYPOINT指令,往往用于设置容器启动后的第一个命令,这对一个容器来说往往是固定的。
  3. CMD指令,往往用于设置容器启动的第一个命令的默认参数,这对一个容器来说可以是变化的。
  4. docker run 往往用于给出替换CMD的临时参数

ENTRYPOINT&CMD

No ENTRYPOINT ENTRYPOINT exec_entry p1_entry ENTRYPOINT [“exec_entry”, “p1_entry”]
No CMD error, not allowed /bin/sh -c exec_entry p1_entry exec_entry p1_entry
CMD [“exec_cmd”, “p1_cmd”] exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry exec_cmd p1_cmd
CMD exec_cmd p1_cmd /bin/sh -c exec_cmd p1_cmd /bin/sh -c exec_entry p1_entry exec_entry p1_entry /bin/sh -c exec_cmd p1_cmd

3 Docker镜像制作

3.1 注意事项

  1. 减少镜像层
    • 一次RUN指令形成新的一层,尽量Shell命令都写在一行,减少镜像层。
    • 文件比较多时,放入到一个目录中,使用COPY拷贝整个目录
    • 不要多个 Label、ENV 等标签
  2. 避免使用ADD
    • 会增加镜像的大小
  3. 为了减少镜像的大小,应减少依赖,仅安装需要的软件包
  4. 如果有多个CMD,只有最后一个运行
  5. 如果有多个Entrypoint,只有最后一个运行
  6. 如果CMD和entrypoint共存,只有entrypoint运行,且最后的CMD会当做entrypoint的参数
  7. 多阶段构建镜像
# git clone https://github.com/lizhenliang/tomcat-java-demo
# cd tomcat-java-demo
# vi Dockerfile
FROM maven AS build
ADD ./pom.xml pom.xml
ADD ./src src/
RUN mvn clean package

FROM lizhenliang/tomcat
RUN rm -rf /usr/local/tomcat/webapps/ROOT
COPY --from=build target/*.war /usr/local/tomcat/webapps/ROOT.war
# docker build -t demo:v1 .
# docker container run -d -v demo:v1
# 首先,第一个FROM 后边多了个 AS 关键字,可以给这个阶段起个名字。
# 然后,第二部分FROM用的我们上面构建的Tomcat镜像,COPY关键字增加了—from参数,用于拷贝某个阶段的文件到当前阶段。这样一个Dockerfile就都搞定了。

3.2 镜像制作

镜像制作分为两个阶段:
docker build阶段 基于dockerfile制作镜像 (RUN,用于此阶段的运行命令)
docker run阶段 基于镜像运行容器 (CMD,基于image run容器时候,需要运行的命令)

3.3 镜像制作案例

3.3.1 Dockerfile实例

# 指定基础镜像
FROM pikaqiu/ubuntu:14.04.20161014
 
# 维护者信息
MAINTAINER [email protected]
 
# 设置环境
ENV RTMP_VERSION=1.1.10 \
    NPS_VERSION=1.11.33.4 \
    LIBAV_VERSION=11.8 \
    NGINX_VERSION=1.10.1 \
    NGINX_USER=www-data \
    NGINX_SITECONF_DIR=/etc/nginx/sites-enabled \
    NGINX_LOG_DIR=/var/log/nginx \
    NGINX_TEMP_DIR=/var/lib/nginx \
    NGINX_SETUP_DIR=/var/cache/nginx
 
# 设置构建时变量,镜像建立完成后就失效
ARG BUILD_LIBAV=false
ARG WITH_DEBUG=false
ARG WITH_PAGESPEED=true
ARG WITH_RTMP=true
 
# 复制本地文件到容器目录中
COPY setup/ ${NGINX_SETUP_DIR}/
RUN bash ${NGINX_SETUP_DIR}/install.sh
 
# 复制本地配置文件到容器目录中
COPY nginx.conf /etc/nginx/nginx.conf
COPY entrypoint.sh /sbin/entrypoint.sh
 
# 运行指令
RUN chmod 755 /sbin/entrypoint.sh
 
# 允许指定的端口
EXPOSE 80/tcp 443/tcp 1935/tcp
 
# 指定网站目录挂载点
VOLUME ["${NGINX_SITECONF_DIR}"]
 
ENTRYPOINT ["/sbin/entrypoint.sh"]
CMD ["/usr/sbin/nginx"]

3.3.2 构建镜像实战

  1. 环境准备

tomcat和jdk压缩包,创建一个txt,创建一个Dockerfile目录
image.png

  1. Dockerfile文件
FROM centos:7
MAINTAINER pikaqiu<[email protected]>
 
COPY readme.txt /usr/local/readme.txt
 
ADD jdk-8u333-linux-x64.tar.gz /usr/local/  #add命令会自动解压,解压到/usr/local/
ADD apache-tomcat-9.0.64.tar.gz /usr/local/
 
RUN yum -y install vim
 
ENV MYPATH /usr/local
WORKDIR $MYPATH
# 多个ENV最好合并为一个
ENV JAVA_HOME /usr/local/jdk1.8.0_333
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.64
ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.64
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
 
EXPOSE 8080
 
CMD /usr/local/apache-tomcat-9.0.64/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.64/logs/catalina.out
  1. docker build构建成为一个镜像
docker build -t diytomcat:1.0 .

启动生成的镜像,构建Tomcat容器
这里设置了数据卷,宿主机的/home/wjc/build/tomcat/test对应该容器的/usr/local/apache-tomcat-9.0.64/webapps/test。这样关于test项目的修复只需要在宿主机上修改就可以了,不需要进入到容器中修改。

docker run -d -p 9090:8080 --name diytomcat -v /home/wjc/build/tomcat/test:/usr/local/apache-tomcat-9.0.64/webapps/test -v /home/wjc/build/tomcat/tomcatlogs:/usr/local/apache-tomcat-9.0.64/logs diytomcat:1.0

image.png
image.png
挂载成功!
进入到容器中
image.png

3.4 Dokcer容器文件系统

2843746737-ec875617987376ae.png

4 参考文档

  1. https://blog.csdn.net/m0_46090675/article/details/121846718
  2. https://anqixiang.blog.csdn.net/article/details/113958341
  3. https://blog.csdn.net/m0_46845579/article/details/125533650
  4. https://huaweicloud.csdn.net/63311bd6d3efff3090b525b7.html

猜你喜欢

转载自blog.csdn.net/hansome_hong/article/details/128512531