Dockerfile 理论基础

一、必知理论基础 

1、资源隔离与限制(都是内核实现)

Namespace :资源隔离

  • pid:用于进程隔离(PID:进程ID)
  • net:管理网络接口(NET:网络)
  • ipc:管理对 IPC 资源的访问(IPC:进程间通信(信号量、消息队列和共享内存))
  • mnt:管理文件系统挂载点(MNT:挂载)
  • uts:隔离主机名和域名
  • user:隔离用户和用户组

CGroup(Control Groups) :资源限制

通过namespace可以保证容器之间的隔离,但是无法控制每个容器可以占用多少资源, 如果其中的某一个容器正在执行 CPU 密集型的任务,那么就会影响其他容器中任务的性能与执行效率,导致多个容器相互影响并且抢占资源。如何对多个容器的资源使用进行限制就成了解决进程虚拟资源隔离之后的主要问题。

Control Groups(简称 CGroups)

cgroups是Linux内核提供的一种机制,这种机制可以根据需求把一系列系统任务及其子任务整合(或分隔)到按资源划分等级的不同组中,从而为系统资源管理提供一个统一的框架。

CGroups能够隔离宿主机器上的物理资源,例如 CPU、内存、磁盘 I/O 。每一个 CGroup 都是一组被相同的标准和参数限制的进程。而我们需要做的,其实就是把容器这个进程加入到指定的Cgroup中。

2、存储驱动、(UnionFS(联合文件系统)

Use the OverlayFS storage driver | Docker Documentation

docker的 Storage Driver用的是UnionFS(联合文件系统)。

UnionFS(联合文件系统组成很多,如 AUFSOverlayFS 和 Btrfs 等)

UnionFS是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。

作用:

  每个容器都有一个本地存储空间,用于保存层叠的镜像层以及挂载的容器文件系统。默认情况下,容器的所有读写操作都发生在其镜像层上或挂载的文件系统中,所以存储是每个容器的性能和稳定性不可或缺的一个环节。本地存储是通过存储驱动(Storage Driver)进行管理的,有时候也被称为 Graph Driver。docker 存储驱动的职责就是将镜像层和可写容器层管理起来。不同的驱动实现管理的方式也不一致,实现容器与镜像管理的两个关键技术就是分层镜像机制和copy-on-write (写时复制)。

2、docker支持的几种存储驱动

Docker支持以下存储驱动程序:

(1)overlay2 :当前所有受支持的Linux发行版的首选存储驱动程序,不需要任何额外的配置,已经并入内核主线。

  overlayFS 是Linux内核3.18后支持的,也是一种Union FS,和AUFS的多层不同的是Overlay只有两层:一个upper文件系统和一个lower文件系统,分别代表

Docker的镜像层和容器层。当需要修改一个文件时,使用CoW将文件从只读的lower复制到可写的upper进行修改,结果也保存在upper层。

OverlayFS将单个Linux主机上的两个目录合并成一个目录(一个是image,一个是container,联合挂载成一个目录)。这些目录被称为层,统一过程被称为联合挂载

OverlayFS底层目录称为lowerdir(image), 高层目录称为upperdir(container)。合并统一视图称为merged

当需要修改一个文件时,将文件从只读的Lower复制到可写的Upper进行修改,结果也保存在Upper层。在Docker中,底下的只读层就是image,可写层就是Container。

下图是一个docker镜像和docke容器的分层图,docker镜像是lowdir,docker容器是upperdir。而统一的视图层是merged层

overlay具有:上下合并,同名覆盖 ,写时拷贝 ,等特点,具体如下:

Lowerdir与Upperdir同名文件在 Merged中显示的只有Upperdir中的同名文件,不同名文件会合并在Merged中显示修改文件或目录策略:
        (1)若文件存在于Upperdir,则直接修改Upperdir中的文件

        (2)若文件只存在于Lowerdir,则先将文件从Lowerdir中拷贝至Upperdir再进行修改

在Merged层创建文件或目录,会直接在Upperdir层创建,在Merged层删除文件策略:
        (1)若文件只存在Upperdir层,则直接在Upprdir层删除文件

        (2)若文件存在Lowerdir层,则在Upperdir层创建一个同名的任何用户都没有任何权限,大小为0的字符设备

基于以上策略,Lowerdir层内容都不会被修改,使得在OverlayFS中,Lower可以是只读的,而Upper则需要是可读写的文件系统。

使用overlay和overlay2驱动性能好于aufs和devicemapper,在实际生产环境overlay2的性能高于btrfs

  • 页缓存:overlayfs支持页缓存共享,也就是说如果多个容器访问同一个文件,可以共享同一个页缓存。这使得overlay/overlay2驱动高效地利用了内存
  • copy_up:aufs和overlayfs,由于第一次写入都会导致copy_up,尤其是大文件,会导致写延迟,以后的写入不会有问题。由于overlayfs层级 比aufs的多,所以ovelayfs的拷贝高于aufs
  • inode限制:使用overlay存储驱动可能导致inode过度消耗,特别是当容器和镜像很多的情况下,所以建议使用overlay2.

overlay2 容器结构

docker overlay2存储驱动简介_51CTO博客_docker overlay2

启动一个容器,也是载/var/lib/docker/overlay2目录下生成一层容器层,目录包括diff,link,lower,merged,work,diff记录每一层自己内容的数据,link记录该层链接目录(实际是目录下到层的链接),比如在容器中创建目录或在diff新增该目录 

(2)aufs更早版本的首选存储驱动程序。

  AUFS(AnotherUnionFS)是一种Union FS,是文件级的存储驱动。AUFS是一个能透明覆盖一个或多个现有文件系统的层状文件系统,把多层合并成文件系统的单层表示。简单来说就是支持将不同目录挂载到同一个虚拟文件系统下的文件系统。这种文件系统可以一层一层地叠加修改文件。无论底下有多少层都是只读的,只有最上层的文件系统是可写的。当需要修改一个文件时,AUFS创建该文件的一个副本,使用CoW将文件从只读层复制到可写层进行修改,结果也保存在可写层。在Docker中,底下的只读层就是image,可写层就是Container。

(3)常用存储驱动对比

存储驱动特点优点缺点适用场景
AUFS 联合文件系统、未并入内核主线、文件级存储 作为docker的第一个存储驱动,已经有很长的历史,比较稳定,且在大量的生产中实践过,有较强的社区支持 有多层,在做写时复制操作时,如果文件比较大且存在比较低的层,可能会慢一些 大并发但少IO的场景
overlayFS 联合文件系统、并入内核主线、文件级存储 只有两层 不管修改的内容大小都会复制整个文件,对大文件进行修改显示要比小文件消耗更多的时间 大并发但少IO的场景
Devicemapper 并入内核主线、块级存储 块级无论是大文件还是小文件都只复制需要修改的块,并不是整个文件 不支持共享存储,当有多个容器读同一个文件时,需要生成多个复本,在很多容器启停的情况下可能会导致磁盘溢出 适合io密集的场景
Btrfs 并入linux内核、文件级存储 可以像devicemapper一样直接操作底层设备,支持动态添加设备 不支持共享存储,当有多个容器读同一个文件时,需要生成多个复本 不适合在高密度容器的paas平台上使用
ZFS 把所有设备集中到一个存储池中来进行管理 支持多个容器共享一个缓存块,适合内存大的环境 COW使用碎片化问题更加严重,文件在硬盘上的物理地址会变的不再连续,顺序读会变的性能比较差 适合paas和高密度的场景

AUFS VS OverlayFS

AUFS和Overlay都是联合文件系统,但AUFS有多层,而Overlay只有两层,所以在做写时复制操作时,如果文件比较大且存在比较低的层,则AUSF可能会慢一些。而且Overlay并入了linux kernel mainline,AUFS没有。目前AUFS已基本被淘汰

OverlayFS VS Device mapper

OverlayFS是文件级存储,Device mapper是块级存储,当文件特别大而修改的内容很小,Overlay不管修改的内容大小都会复制整个文件,对大文件进行修改显示要比小文件要消耗更多的时间,而块级无论是大文件还是小文件都只复制需要修改的块,并不是整个文件,在这种场景下,显然device mapper要快一些。因为块级的是直接访问逻辑盘,适合IO密集的场景。而对于程序内部复杂,大并发但少IO的场景,Overlay的性能相对要强一些。

Supported backing filesystems 存储驱动支持的文件系统

3、镜像层和容器层

1、理解的前提:

(1)镜像是分层的文件系统(分层镜像机制),不可写,容器才是可写入数据的。

(2)容器 = 镜像 + 读写层

(3)多个容器可以共用一个images,利用写时复制(CoW)机制。

Images and layers

  Docker uses storage drivers to store image layers, and to store data in the writable layer of a container. The container’s writable layer does not persist after the container is deleted, but is suitable for storing ephemeral data that is generated at runtime. Storage drivers are optimized for space efficiency, but (depending on the storage driver) write speeds are lower than native file system performance, especially for storage drivers that use a copy-on-write filesystem. Write-intensive applications, such as database storage, are impacted by a performance overhead, particularly if pre-existing data exists in the read-only layer

Docker使用存储驱动程序存储图像层并将数据存储在容器的可写层中。删除容器后,容器的可写层不会持续存在,但适用于存储在运行时生成的短暂数据。

Container and layers

The major difference between a container and an image is the top writable layer. All writes to the container that add new or modify existing data are stored in this writable layer. When the container is deleted, the writable layer is also deleted. The underlying image remains unchanged.

Because each container has its own writable container layer, and all changes are stored in this container layer, multiple containers can share access to the same underlying image and yet have their own data state

容器和图像之间的主要区别是顶部的可写层。所有写入添加新的或修改现有数据的容器中都存储在此写入层中。删除容器时,也会删除可写的层。基础图像保持不变。

因为每个容器都有自己的可写容器层,并且所有更改都存储在此容器层中,因此多个容器可以共享对同一基础图像的访问权限,但具有自己的数据状态

3、写时复制(copy-on-write)

  Copy-on-write is a strategy of sharing and copying files for maximum efficiency. If a file or directory exists in a lower layer within the image, and another layer (including the writable layer) needs read access to it, it just uses the existing file. The first time another layer needs to modify the file (when building the image or running the container), the file is copied into that layer and modified. This minimizes I/O and the size of each of the subsequent layers. These advantages are explained in more depth below

Sharing promotes smaller images

a)当修改容器内文件时,只会在容器层保存变化,并不会修改镜像
b)  当容器查找文件的时候,首先会在容器层查找,如果容器层没有的话,那么就会从镜像层的最上层,依次向下层镜像中查找。

文件会从镜像层复制到容器层,修改后会被保存在容器层,这就叫写时复制。

Copying makes containers efficient

When you start a container, a thin writable container layer is added on top of the other layers. Any changes the container makes to the filesystem are stored here. Any files the container does not change do not get copied to this writable layer. This means that the writable layer is as small as possible.

When an existing file in a container is modified, the storage driver performs a copy-on-write operation. The specifics steps involved depend on the specific storage driver. For the overlay2overlay, and aufs drivers, the copy-on-write operation follows this rough sequence:

About storage drivers | Docker Documentation

二、dockerfile部分指令区别

Dockerfile 是一个文本文件,其内包含了一条条的指令(Instruction),每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。

1、指定维护者信息

MAINTAINER
格式: 
MAINTAINER <name>

2、执行命令行命令

RUN  
定义每一层该如何构建(不是在写 Shell 脚本)
每一个 RUN = 启动一个容器、执行命令、然后提交存储层文件变更
两行 RUN 命令的执行环境不同

格式:
1) shell 格式: RUN <命令>                                   #类似命令行输入
2) exec  格式: RUN ["可执行文件", "参数1", "参数2"]           #类似函数调用

行尾 \ 换行
行首 # 注释
&& 命令串联

3、复制文件:COPY和ADD

COPY 
格式: 
1) COPY <源路径>... <目标路径> 
2) COPY ["<源路径1>",... "<目标路径>"]

<源路径> 可以是多个,甚至可以是通配符   #上下文路径的相对路径,且不能是url
<目标路径> 可以是容器内的绝对路径,也可以是相对于工作目录的相对路径(工作目录可以用 WORKDIR 指令来指定)

#更高级的复制文件
ADD
1)ADD <src>... <dest>
2)ADD ["<src>",... "<dest>"]

<源路径> 可以是一个 URL, 如果是gzip , bzip2 以及 xz 的情况下,ADD 指令将会自动解压缩这个压缩文件到 <目标路径> 去 所有的文件复制均使用COPY 指令,仅在需要自动解压缩的场合使用 ADD
尽量不要把<scr>写成一个文件夹,如果<src>是一个文件夹了,复制整个目录的内容,包括文件系统元数据

4、容器启动命令:ENTRYPOINT和CMD

CMD

容器就是进程,既然是进程,在启动的时候,需要指定所运行的程序及参数。CMD 指令就是用于指定默认的容器主进程的启动命令。

对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。

Dockerfile reference | Docker Documentation

三种格式: 
CMD ["executable","param1","param2"] (exec 格式, 首先格式)
CMD ["param1","param2"] (as default parameters to ENTRYPOINT)
CMD command param1 param2 (shell 格式)

1、一般推荐使用 exec 格式,这类格式在解析时会被解析为 JSON 数组,因此一定要使用双引号 " ,而不要使用单引号
2、If CMD is used to provide default arguments for the ENTRYPOINT instruction, both the CMD and ENTRYPOINT instructions should be specified with the JSON array format.
3、CMD echo $HOME 在实际执行中,会将其变更为: CMD [ "sh", "-c", "echo $HOME" ]

ENTRYPOINT

Dockerfile reference | Docker Documentation

#入口点,功能是启动时的默认命令
ENTRYPOINT 两种格式shell和exec
1)ENTRYPOINT ["executable", "param1", "param2"]
2)ENTRYPOINT command param1 param2

和 CMD 一样,都是在指定容器启动程序及参数。实际执行时,将变为: <ENTRYPOINT> "<CMD>" 

#启动时,可再对可执行文件进行传参 ENTRYPOINT ["docker-entrypoint.sh"]

#应用运行前的准备工作,指定了 ENTRYPOINT 为 docker-entrypoint.sh 脚本,并且可在镜像启动时候传入参数来服务脚本

ENTRYPOINT和CMD 异同点

相同点:

  • 只能写一条,如果写了多条,那么只有最后一条生效
  • 容器启动时才运行,运行时机相同

不同点

  • ENTRYPOINT不会被运行的 docker run 后面的执行参数覆盖,一定会运行,而CMD则会被覆盖
  • 如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD指令不是一个完整的可执行命令,那么CMD指定的内容将会作为ENTRYPOINT的参数
  • FROM ubuntu
    ENTRYPOINT ["top", "-b"]
    CMD ["-c"]
  • 如果我们在Dockerfile种同时写了ENTRYPOINT和CMD,并且CMD是一个完整的指令,那么它们两个会互相覆盖,谁在最后谁生效,
FROM ubuntu

ENTRYPOINT ["top", "-b"]

CMD ls -al

那么将执行ls -al ,top -b不会执行

5、构建参数ARG和环境变量ENV

1) ENV <key> <value> 
2) ENV <key1>=<value1> <key2>=<value2>...
两者的区别就是第一种是一次设置一个,第二种是一次设置多个

#构建参数
ARG 
和 ENV 所不同的是, ARG 所设置的构建环境的环境变量,在将来容器运行时是不会存在这些环境变量的。
格式: 
ARG <参数名>[=<默认值>]

可以在构建命令docker build 中用 --build-arg <参数名>=<值> 来覆盖

如果我们在后续的时候,想对这个进行升级,需要对涉及版本的内容都进行修改,非常麻烦,这个时候我们就可以使用ARG或者ENV将版本号提取出来,然后使用${}对内容进行替换

ENV不仅可以在build阶段使用,而且会 作为环境变量永久的保存在系统中,当我们创建容器的时候,同样可以使用该变量

ARG更多的是关注构建,ENV更多关注的是镜像和后续创建容器的时候,是否可以使用这个变量。

ENV

FROM ubuntu:20.04
ENV VERSION=2.10.0
RUN apt-get update && \
apt-get install -y wget && \
wget https://github.com/ipinfo/cli/releases/download/ipinfo-${VERSION}/ipinfo_${VERSION}_linux_amd64.tar.gz && \
tar -zxvf ipinfo_${VERSION}_linux_amd64.tar.gz && \
mv ipinfo_${VERSION}_linux_amd64 /usr/bin/ipinfo && \
rm -rf ipinfo_${VERSION}_linux_amd64.tar.gz

ARG(构建过程中设置变量,不存在于后续的容器中)

FROM ubuntu:20.04
ARG VERSION=2.10.0
RUN apt-get update && \
apt-get install -y wget && \
wget https://github.com/ipinfo/cli/releases/download/ipinfo-${VERSION}/ipinfo_${VERSION}_linux_amd64.tar.gz && \
tar -zxvf ipinfo_${VERSION}_linux_amd64.tar.gz && \
mv ipinfo_${VERSION}_linux_amd64 /usr/bin/ipinfo && \
rm -rf ipinfo_${VERSION}_linux_amd64.tar.gz

6、定义匿名卷

VOLUME 

为了防止运行时用户忘记将动态文件所保存目录挂载为卷(volume),指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也可以正常运行,不会向容器存储层写入大量数据

格式: 
1) VOLUME <路径>
2) VOLUME ["<路径1>", "<路径2>"...] 

VOLUME /data 
这里的 /data 目录就会在运行时自动挂载为匿名卷,任何向 /data 中写入的信息都不会记录进容器存储层

-v mydata:/data
mydata 这个命名卷挂载到了 /data 这个位置,替代了Dockerfile 中定义的匿名卷的挂载配置

7、声明端口

EXPOSE 
声明运行时容器提供服务端口

8、指定工作目录,相当于 cd

WORKDIR 
改变以后各层的工作目录
格式:
WORKDIR <工作目录路径> 
设置工作目录,对RUN,CMD,ENTRYPOINT,COPY,ADD生效。如果不存在则会创建,也可以设置多次。 如:
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd
则最终路径为 /a/b/c

WORKDIR也可以解析环境变量, 如:
ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

pwd的执行结果是/path/$DIRNAME

9、指定当前用户

USER USER 指令和 WORKDIR 相似,都是改变环境状态并影响以后的层 
USER 只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。 

10、健康检查 HEALTHCHECK

格式: 
1) HEALTHCHECK [选项] CMD <命令> #设置检查容器健康状况的命令 

2) HEALTHCHECK NONE #如果基础镜像有健康检查指令,使用这行可以屏蔽掉其健康检查指令 

HEALTHCHECK 支持下列选项: 

--interval=<间隔> :两次健康检查的间隔,默认为 30 秒; 

--timeout=<时长> :健康检查命令运行超时时间,如果超过这个时间,本次健康检查就被视为失败,默认 30 秒; 

--retries=<次数> :当连续失败指定次数后,则将容器状态视为 unhealthy ,默认 3次。

和 CMD , ENTRYPOINT 一样, HEALTHCHECK 只可以出现一次,如果写了多个,只有最后一个生效。 

HEALTHCHECK --interval=5s --timeout=3s \ CMD curl -fs http://localhost/ || exit 1 #为他人做嫁衣裳 

ONBUILD 当以当前镜像为基础镜像,去构建下一级镜像的时候才会被执行 
格式: ONBUILD <其它指令> 做一个基础镜像,基础镜像更新,各个项目不用同步 
Dockerfile 的变化,重新构建后就继承了基础镜像的更新

11、构建镜像

#构建镜像 
docker build [选项] <指定上下文路径/URL/-> 
镜像并非在本地构建,而是在服务端,也就是镜像是在 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢? 
当构建的时候,用户会指定构建镜像上下文的路径, docker build 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。
这样Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。 
为什么COPY ../package.json /app 或者 COPY /opt/xxxx /app 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。 
例如:COPY ./package.json /app/  是复制 上下文(context) 目录下的package.json   #COPY 这类指令中的源文件的路径都是上下文路径的相对路径 
-f ../Dockerfile.php 参数指定某个文件作为Dockerfile 
其它 docker build 的用法 
直接用 Git repo 进行构建:Docker 就会自己去 git clone 这个项目、切换到指定分支、并进入到指定目录后开始构建 
用给定的 tar 压缩包构建:Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建

三、Dockerfile的编写案例

# 版本信息
FROM centos:7
MAINTAINER wuweixiang <wuweixiang.alex@gmail.com>
# 设置工作目录
WORKDIR /var/
# 添加jdk、tomcat
ADD jdk-8u191-linux-x64.tar.gz .
#ADD http://mirrors.hust.edu.cn/apache/tomcat/tomcat-8/v8.5.35/bin/apache-tomcat-8.5.35.tar.gz .
ADD apache-tomcat-8.5.35.tar.gz .

# 设置环境变量
ENV JAVA_HOME /var/jdk1.8.0_191
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin
ENV TIME_ZONE Asia/Shanghai

# 更改时区
RUN set -x \
&& echo "${TIME_ZONE}" > /etc/timezone \
&& ln -sf /usr/share/zoneinfo/${TIME_ZONE} /etc/localtime

# 开启内部服务端口
EXPOSE 8080

# 启动tomcat服务器
CMD ["/var/apache-tomcat-8.5.35/bin/catalina.sh","run"] && tail -f /var/apache-tomcat-8.5.35/logs/catalina.out

build  nginx

FROM ubuntu:focal

ENV LANG en_US.UTF-8
ENV LANGUAGE en_US:en
ENV LC_ALL en_US.UTF-8
ENV TZ Asia/Shanghai

ENV DEBIAN_FRONTEND=noninteractive

RUN apt update \
    && apt install bash ca-certificates lsb-release wget gnupg apt-transport-https software-properties-common apt-utils --yes \
    && apt install vim git curl expect perl pkg-config tzdata dirmngr locales jq tree openssh-client --yes \
    && apt install tmux net-tools tcpdump telnet iftop iotop nload dnsutils sysstat psmisc htop rsync --yes \
    && apt install python3 python3-pip python3-lxml python3-requests python3-termcolor unzip --yes \
    && ln -fs /usr/share/zoneinfo/${TZ} /etc/localtime \
    && echo ${TZ} > /etc/timezone \
    && dpkg-reconfigure --frontend noninteractive tzdata \
    && apt clean \
    && rm -rf /var/lib/apt/lists/*

RUN locale-gen en_US.UTF-8

RUN pip3 install --upgrade pip \
    && pip3 install ansible paramiko

RUN apt update \
    && apt install nginx --yes \
    && rm -f /etc/nginx/nginx.conf \
    && rm -f /etc/nginx/sites-available/* \
    && rm -f /etc/nginx/sites-enabled/* \
    && rm -f /etc/nginx/conf.d/* \
    && apt clean \
    && rm -rf /var/lib/apt/lists/* \
    && rm -fr /etc/apt/sources.list.d/* \
    && echo > /etc/apt/sources.list

ADD docker/nginx.conf /etc/nginx/nginx.conf
ADD docker/pkg.conf /etc/nginx/conf.d/pkg.conf

USER root
VOLUME ["/ai","/root"]
WORKDIR  /ai/deploy

CMD ["nginx", "-g", "daemon off; master_process on;"]

备注:

"nginx", "-g", "daemon off;master_process on;

用于启动Nginx容器,

-g 参数用于设置Nginx配置文件外的全局配置

daemon off:表示不以守护进程方式运行Nginx,而是在前台运行

master_process on:表示开启Nginx的master进程。

构建

# docker build -t nginx:v1  -f  Dockerfile-nginx  . (-f 指定dockerfile文件)
# docker build -t nginx:v1 .  (此文件为标准的Dockerfile名不用-f)

#docker  run  --name docker_nginx_v1  -itd  -p 80:80 nginx:v1  

build一个centos

[root@localhost Dockerfile]# pwd
/root/Dockerfile
[root@localhost Dockerfile]# cat Dockerfile 
FROM daocloud.io/centos:7
MAINTAINER "you" <your@email.here>
ENV container docker
RUN yum -y swap -- remove fakesystemd -- install systemd systemd-libs
RUN yum -y update; yum clean all; \
(cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i ==systemd-tmpfiles-setup.service ] || rm -f $i; done); \
rm -f /lib/systemd/system/multi-user.target.wants/*;\
rm -f /etc/systemd/system/*.wants/*;\
rm -f /lib/systemd/system/local-fs.target.wants/*; \
rm -f /lib/systemd/system/sockets.target.wants/*udev*; \
rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \
rm -f /lib/systemd/system/basic.target.wants/*;\
rm -f /lib/systemd/system/anaconda.target.wants/*;
VOLUME [ "/sys/fs/cgroup" ]
CMD ["/usr/sbin/init"]

执行构建

#docker build --rm -t centos:v1 .
Successfully built b9fa69e719a8
Successfully tagged centos:v1

构建php

FROM centos:7
MAINTAINER www.ctnrs.com
RUN yum install epel-release -y && \
    yum install -y gcc gcc-c++ make gd-devel libxml2-devel \
    libcurl-devel libjpeg-devel libpng-devel openssl-devel \
    libmcrypt-devel libxslt-devel libtidy-devel autoconf \
    iproute net-tools telnet wget curl && \
    yum clean all && \
    rm -rf /var/cache/yum/*

RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
    tar zxf php-5.6.36.tar.gz && \
    cd php-5.6.36 && \
    ./configure --prefix=/usr/local/php \
    --with-config-file-path=/usr/local/php/etc \
    --enable-fpm --enable-opcache \
    --with-mysql --with-mysqli --with-pdo-mysql \
    --with-openssl --with-zlib --with-curl --with-gd \
    --with-jpeg-dir --with-png-dir --with-freetype-dir \
    --enable-mbstring --with-mcrypt --enable-hash && \
    make -j 4 && make install && \
    cp php.ini-production /usr/local/php/etc/php.ini && \
    cp sapi/fpm/php-fpm.conf /usr/local/php/etc/php-fpm.conf && \
    sed -i "90a \daemonize = no" /usr/local/php/etc/php-fpm.conf && \
    mkdir /usr/local/php/log && \
    cd / && rm -rf php* && \
    ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

ENV PATH $PATH:/usr/local/php/sbin:/usr/local/php/bin
COPY php.ini /usr/local/php/etc/
COPY php-fpm.conf /usr/local/php/etc/
WORKDIR /usr/local/php
EXPOSE 9000
CMD ["php-fpm"]

验证
docker exec -it  4bb bash
[root@4bb85a16488a php]# ls
bin  etc  include  lib  log  php  sbin  var
[root@4bb85a16488a php]# php -v
PHP 5.6.36 (cli) (built: Aug 23 2020 12:01:07) 
Copyright (c) 1997-2016 The PHP Group
Zend Engine v2.6.0, Copyright (c) 1998-2016 Zend Technologies

构建debian(修改国内源)

FROM debian:latest
RUN sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list \
          && apt-get update -y \
          && apt-get upgrade -y \
          && apt-get install ansible -y \
          && apt-get install sshpass -y \
          && apt-get install pip -y \
          && apt-get autoclean
RUN pip3 install pymongo
RUN mkdir -p /etc/ansible/
CMD ["/bin/bash"]

构建python、go环境

FROM  huawei/ascend-infer:22.0.0-ubuntu18.04

USER root
WORKDIR /tmp
RUN apt-get update && \
    apt-get install -y apt-transport-https ca-certificates curl wget  python3.8 python3.8-distutils  python3-pip  && \
    apt-get clean

# 切换成国内镜像源
COPY sources.list /etc/apt/sources.list

RUN pwd && mkdir /tmp/moqi/
COPY requirements.txt /tmp/moqi/
RUN pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple/ --upgrade pip && \
    pip3 install -i https://pypi.tuna.tsinghua.edu.cn/simple/ -r /tmp/moqi/requirements.txt && \
    rm -rf ~/.cache/pip

ADD go1.16.8.linux-arm64.tar.gz  /usr/local/bin/
RUN rm -rf /usr/local/bin/go1.16.8.linux-arm64.tar.gz

ENV GOROOT=/usr/local/bin/go
ENV GOPATH=$HOME
ENV PATH=$GOPATH/bin:$GOROOT/bin:$PATH

CMD ["--", "bash"]

build脚本

#!/bin/bash

if [ "$#" -ne 1 ]; then
    echo "usage: bash build_docker.sh <1.0|2.0|test>"
    exit 1
fi

wget -O  go1.16.8.linux-arm64.tar.gz  https://seafile.moqi.com.cn/f/dfc4080b48a/?dl=1

docker build --no-cache -t huawei/moqi-ascend-tools:$1 --network=host .
#docker push harbor.internal.moqi.ai/huawei/moqi-ascend-tools:$1

--no-cache 不使用缓存,多次构建会很慢

FROM centos:7
MAINTAINER www.ctnrs.com
RUN yum install epel-release -y && \
    yum install -y gcc gcc-c++ make gd-devel libxml2-devel \
    libcurl-devel libjpeg-devel libpng-devel openssl-devel \
    libmcrypt-devel libxslt-devel libtidy-devel autoconf \
    iproute net-tools telnet wget curl && \
    yum clean all && \
    rm -rf /var/cache/yum/*

RUN wget http://docs.php.net/distributions/php-5.6.36.tar.gz && \
    tar zxf php-5.6.36.tar.gz && \
    cd php-5.6.36 && \
    ./configure --prefix=/usr/local/php \
    make -j 4 && make install && \
    cd / && rm -rf php*

注意:

1、 docker  build   -t  后面镜像的名字中不能包含大写字母!
2、 docker run –p 8080:80 –d nginx 符号错误 “-” 其实为"-"
       docker run -p 8080:80 -d nginx

3、配置文件里面可能会发生转行,注意检查

4、当前路径下不要放无用的文件,因为会一起打包发送给 docker 引擎,如果文件过多会造成过程缓慢。

 

 

参考

https://docs.docker.com/engine/reference/builder/   dockerfile官网介绍

写 Dockerfile 的一些技巧 (qq.com)

Best practices for writing Dockerfiles | Docker Documentation

Docker学习:理论基础之Docker镜像分层 | 容器和镜像的关系 | 写时复制与用时分配_血煞长虹的博客-CSDN博客_docer镜像和复制有什么区别

https://github.com/zhangguanzhang/docker-need-to-know   不错的学习地址

http://blog.ctnrs.com/post/dockerfile-skills/

https://www.runoob.com/docker/docker-dockerfile.html  菜鸟教程,讲解详细

https://docs.docker.com/engine/reference/builder/#add  docker官方文档

posted @ 2020-04-02 11:44  凡人半睁眼  阅读(245)  评论(0编辑  收藏  举报