2-3-1-镜像制作
1 . Docker镜像有没有内核?
从镜像大小上面来说 , 一个比较小的镜像只有十几MB , 而内核文件需要一百多兆 , 因此镜像里面是没有内核的 , 镜像在被启动为容器后将直接使用宿主机的内核 , 而镜像本身则只提供相应的rootfs , 即系统正常运行所必须的用户空间的文件系统 , 比如 /dev/ , /proc , /bin , /etc等目录,所以容器当中基本是没有/boot目录的 , 而/boot当中保存的就是与内核相关的文件和目录 。
2. 为什么没有内核?
由于容器启动和运行过程中是直接使用宿主机的内核 , 所以没有直接调用过物理硬件 , 也不会涉及到硬件驱动 , 因此也用不上内核和驱动 , 另外有内核的那是虚拟机。
1.手动制作镜像
1.1 命令使用说明
一般情况使用docker commit 、docker container commit命令实现手动制作镜像的功能
语法 :
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]OPTIONS-a,--author string: 提交镜像的作者。("John Hannibal Smith <hannibal@a-team.com>)-c,--change list: 使用Dockerfile指令来创建镜像-m,--message string: 提交时的说明文字-p,--pause:commit时 , 将容器暂停
#如果没有指定REPOSITORY:TAG , 则 创建新的镜像中REPOSITORY的值为none,tag的值也为none
[root@centos7 ~]# docker commit -a "xxxxx@qq.com" -m "nginx image" --change="EXPOSE 80 443 " d979e52a92ca
sha256:6db9a3a42c719808ebb7ab77e2c0b9a6434ee2c2a1f82b126cbcb6dd8d25e3d6
[root@centos7 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
<none> <none> 6db9a3a42c71 3 seconds ago 388MB
#如果指定了REPOSITORY ,没有指定TAG , 则TAG会默认设置为latest
[root@centos7 ~]# docker commit -a "xxxxx@qq.com" -m "nginx image" --change="EXPOSE 80 443 " d979e52a92ca nginx-stop
sha256:9f3edc7b87f9edbb470b493a128f0e4212a9e7429716a89baec8adf9992ed866
[root@centos7 ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx-stop latest 9f3edc7b87f9 2 seconds ago 388MB1.2 手动制作yum版nginx步骤
Docker 制作类似于虚拟机的模板制作 , 即按照公司的实际业务务求将需要安装的软件、相关配置等基础环境配置完成 , 然后将虚拟机再提交为模板 , 最后再批量从模板批量创建新的虚拟机 , 这样可以极大的简化业务中相同环境的虚拟机 运行环境的部署工作 , Docker的镜像制作分为手动制作和自动制作(基于 DockerFile) , 企业通常都是基于Dockerfile制作镜像 , 其中手动制作镜像步骤具体
下载镜像和初始化系统
[root@centos7 ~]# docker pull centos [root@centos7 ~]# docker run -it -d centos:latest d979e52a92ca7f453634452e9013c8ef137473bdfa1e5969dfa0dbd13d960ca7 [root@centos7 ~]# docker exec -it d9 bash [root@d979e52a92ca /]# sed -e "s|^mirrorlist=|#mirrorlist=|g" \ > -e "s|^#baseurl=http://mirror.centos.org/centos/\$releasever|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.4.2105|g" \ > -e "s|^#baseurl=http://mirror.centos.org/\$contentdir/\$releasever|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.4.2105|g" \ > -i.bak \ > /etc/yum.repos.d/CentOS-*.repo [root@d979e52a92ca /]#yum安装并配置nginx[root@d979e52a92ca /]# yum install nginx -y #yum安装nginx [root@d979e52a92ca /]# yum install -y vim wget pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop #安装常用命令关闭
nginx后台运行[root@0263c0d0873d /]# cat /etc/nginx/nginx.conf user nginx; worker_processes auto; error_log /var/log/nginx/error.log; pid /run/nginx.pid; daemon off;自定义网页
[root@0263c0d0873d /]# echo "Docker Yum Nginx" > /usr/share/nginx/html/index.html提交为镜像
提交的时候标记
tag号 : 标记tag号 , 生产当中比较常用 , 后期可以根据tag标记创建不同版本的镜像以及创建不同版本的容器。[root@centos7 ~]# docker commit -a "xxxxx@qq.com" -m "nginx image" --change="EXPOSE 80 443 " d979e52a92ca nginx:v1 sha256:b179311545c318d473d2b698b1347e5220aee2577dd5bce1b78e8ae25fd9a000从自制镜像启动容器
[root@centos7 ~]# docker run -it -d -p 80:80 --name nginx-test nginx:v1 /usr/sbin/nginx 0263c0d0873d7c18c650bb4078dfb9a735cb28119d5ecf00b11f3e15c6fb8de3
2. DockerFile说明
2.1 DockerFile介绍
Docker中有个非常重要的概念叫做——镜像(Image ) 。Docker镜像是一个特殊的文件系统 , 除了提供容器运行时所需的程序、库、资源、配置等文件外 , 还包含了一些为运行时准备的一些配置参数 ( 如匿名卷、环境变量、用户等 ) 。镜像不包含任何动态数据 , 其内容在构建之后也不会被改变。
镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本 , 用这个脚本来构建、定制镜像 , 那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile是一个文本文件 , 其内包含了一条条的指令(Instruction) , 每一条指令构建一层 , 因此每一条指令的内容 , 就是描述该层应当如何构建。
DockerFile可以说是 一种可以被Docker程序解释的脚本 , DockerFile是由一条条的命令组成的 , 每条命令对应linux下面的一条命令 , Docker程序将这些DockerFile指令再翻译成真正的指令再翻译成真正的linux命令 , Docker程序读取DockerFile并根据指令生成 Docker镜像 , 相比手动制作镜像的方式 , DockerFile更能直观的展示镜像是怎么产生的 , 有了DockerFile, 当后期有额外的需求时 , 只要在之前的DockerFile添加或者修改响应的命令即可。
Dockerfile是Docker中用于定义镜像自动化构建流程的配置文件在
Dockerfile中 , 包含了构建镜像过程中需要执行的命令和其他操作它可以明确设定
Docker镜像的制作过程 , 帮助我们在容器体系下能够完成自动构建
2.2 dockerfile基础
使用Dockerfile构建镜像的步骤 :
编写一个
dockerfile文件docker build构建成为一个镜像docker run运行镜像docker push发布镜像 (DockerHub、阿里云仓库)
Dockerfile文件的结构
Dockerfile主要包含四部分内容 : 1. 基础镜像信息、2. 维护者信息 、3. 镜像操作指令、4. 容器启动时指令
可以将Dockerfile理解为一个由上往下执行指令的脚本文件
当调用构建命令 , 通过Dockerfile构建镜像时 , Docker会按照指令顺序进行对应的操作
Dockerfile文件构建过程 :
每个保留关键字(指令) 都是必须是大写字母
执行从上到下顺序
#表示注释每一个指令都会创建提交一个新的镜像层 , 并提交
为什么要用Dockerfile :
Dockerfile是面向开发的 , 以后要发布项目 , 做镜像 , 就需要编写dockerfile文件Docker镜像逐渐成企业交付的标准 , 必须要掌握!DockerFile构建镜像文件 , 定义了一切的步骤 , 源代码Dockerfile的体积小 , 容易进行快速迁移部署环境构建流程记录在
Dockerfile中 , 能够直观的看到镜像构建的顺序和逻辑Docker Images通过DockerFile构建生成的镜像 , 最终发布和运行产品Docker容器 , 镜像运行起来提供服务
示例1
编写
dockerfile文件在任意目录创建一个
dockerfile文件 , 输入以下命令# 构建一个基于ubuntu的docker定制镜像 # 基础镜像 FROM ubuntu # 镜像作者 MAINTAINER my_name polotest@domain.com # 执行命令 ## 换成国内的软件源 RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list RUN sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list ## 安装nginx RUN apt update >/dev/null 2>&1 RUN apt install nginx -y >/dev/null 2>&1 # 暴露对外端口 EXPOSE 80执行
docker builddocker build --network=host -t ubuntu-nginx:v1 .镜像的构建过程
[user@centos8 nginx]$ docker build --network=host -t ubuntu-nginx:v1 . # 将上下文求发送给Docker引擎 Sending build context to Docker daemon 2.56kB # 下载依赖的镜像 Step 1/7 : FROM ubuntu latest: Pulling from library/ubuntu d51af753c3d3: Pull complete fc878cd0a91c: Pull complete 6154df8ff988: Pull complete fee5db0ff82f: Pull complete Digest: sha256:747d2dbbaaee995098c9792d99bd333c6783ce56150d1b11e333bbceed5c54d7 Status: Downloaded newer image for ubuntu:latest # 生成镜像 1d622ef86b13 ---> 1d622ef86b13 Step 2/7 : MAINTAINER my_name myemail@domain.com # 运行容器 4eec6e3094f0 , 在容器内运行上面的这个命令 , 标记维护者信息 ---> Running in 4eec6e3094f0 # 移除临时容器 4eec6e3094f0 Removing intermediate container 4eec6e3094f0 # 生成镜像 6679d1c204e3 ---> 6679d1c204e3 Step 3/7 : RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list # 运行容器84d38c20d8c4 , 在容器内运行上面的这个命令 , 更换软件源记录 ---> Running in 84d38c20d8c4 # 移除临时容器 84d38c20d8c4 Removing intermediate container 84d38c20d8c4 # 生成镜像 83f29f7b055a ---> 83f29f7b055a Step 4/7 : RUN sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list # 运行容器 763e4493d93f , 在容器内运行上面的这个命令 , 更换软件源记录 ---> Running in 763e4493d93f # 移除临时容器 763e4493d93f Removing intermediate container 763e4493d93f # 生成镜像 6297f20605d9 ---> 6297f20605d9 Step 5/7 : RUN apt update >/dev/null 2>&1 # 运行容器 2665a7e5a2e9 , 在容器内运行上面的这个命令 , 更新软件源缓存 ---> Running in 2665a7e5a2e9 # 移除临时容器 2665a7e5a2e9 Removing intermediate container 2665a7e5a2e9 # 生成镜像 fdfed940ca4d ---> fdfed940ca4d Step 6/7 : RUN apt install nginx -y >/dev/null 2>&1 # 运行 容器 722a9a544643 , 在容器内运行上面的这个命令 , 安装nginx ---> Running in 722a9a544643 # 移除临时容器 722a9a544643 Removing intermediate container 722a9a544643 # 生成镜像 6ee76f7df9e5 ---> 6ee76f7df9e5 Step 7/7 : EXPOSE 80 # 运行容器 a12ed3216ee0 , 在容器内运行上面的这个命令 , 暴露80端口 ---> Running in a12ed3216ee0 # 移除临时容器 a12ed3216ee0 Removing intermediate container a12ed3216ee0 # 生成最终的镜像 7cf64279ba98 ---> 7cf64279ba98 Successfully built 7cf64279ba98 # 将这个镜像标记命名 ubuntu-nginx 版本号v1 Successfully tagged ubuntu-nginx:v1Dockerfile就是将在文件中书写的构建指令 , 一层一层从FROM指定的基础镜像使用临时容器过渡 , 逐层叠加起来最终生成目标镜像使用
docker history查看镜像历史[user@centos8 nginx]$ docker history ubuntu-nginx:v1 IMAGE CREATED CREATED BY SIZE COMMENT 7cf64279ba98 21 minutes ago /bin/sh -c #(nop) EXPOSE 80 0B 6ee76f7df9e5 21 minutes ago /bin/sh -c apt install nginx -y >/dev/null 2… 59.2MB fdfed940ca4d 21 minutes ago /bin/sh -c apt update >/dev/null 2>&1 21.4MB 6297f20605d9 21 minutes ago /bin/sh -c sed -i 's/security.ubuntu.com/mir… 2.76kB 83f29f7b055a 21 minutes ago /bin/sh -c sed -i 's/archive.ubuntu.com/mirr… 2.76kB 6679d1c204e3 21 minutes ago /bin/sh -c #(nop) MAINTAINER my_name myemai… 0B 1d622ef86b13 2 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0B <missing> 2 weeks ago /bin/sh -c mkdir -p /run/systemd && echo 'do… 7B <missing> 2 weeks ago /bin/sh -c set -xe && echo '#!/bin/sh' > /… 811B <missing> 2 weeks ago /bin/sh -c [ -z "$(apt-get indextargets)" ] 1.01MB <missing> 2 weeks ago /bin/sh -c #(nop) ADD file:a58c8b447951f9e30… 72.8MB
示例2
dockerfile文件内容FROM centos:latest MAINTAINER polo ENV MYPATH /usr/local WORKDIR $MYPATH RUN yum -y install vim RUN yum -y install net-tools EXPOSE 80 CMD echo $MYPATH CMD echo "-----end----" CMD /bin/bash执行
docker builddocker build -f test.dockerfile -t test .查看镜像和容器

2.3 docker build命令详解
语法 :
docker buildx build [OPTIONS] PATH | URL | -OPTIONS-f,--file: 指定dockerfile路径docker build -f /path/to/a/Dockerfile .不指定的话 , 默认会读取上下文路径(
.)下的dockerfile-t,--tag: 指定构建的镜像名和tagdocker build -t ubuntu-nginx:v1 .构建的镜像指定多个
tagdocker build -t shykes/myapp:1.0.2 -t shykes/myapp:latest .--add-host:可以使用一个或多个
--add-host标志将其他主机添加到容器的/etc/hosts文件中docker build --add-host=docker:10.180.0.1 .--no-cache: 构建镜像时不使用缓存--network: 在构建过程中为RUN指令设置网络模式--target: 设置要构建的目标构建阶段
docker build命令原理
docker build命令从Dockerfile和上下文构建镜像构建的上下文 : 位于指定
PATH或URL中的一组文件构建过程可以引用上下文中的任何文件。例如 , 构建可以使用
COPY指令来引用上下文中的文件PATH: 就是本地文件系统上的一个目录路径。表示要构建镜像的上下文路径 , 通常是包含Dockerfile的目录。URL:Git地址。 是一个远程Git仓库的URL, 可以直接从远程仓库构建镜像。-: 表示从标准输入流中读取Dockerfile的内容进行构建。
个人理解 : 以设置的上下文为根目录 , 在dockerfile中写的文件路径都会以这个上下文开始找
注 : 构建上下文是递归处理的。
PATH包含任何子目录 ,URL包含repository及其子模块
示例
上下文为当前目录 , 也是最简单的docker build使用方式
docker build .指定 PATH 为 . , 因此本地目录中的所有文件都会被延迟并发送到Docker守护程序。PATH指定在哪里可以找到Docker守护程序上构建的“上下文”的文件
请记住 , 守护进程可以在远程机器上运行 , 并且不会在客户端 ( 运行docker build的地方 ) 解析Dockerfile。这意味着PATH中的所有文件都会被发送 , 而不仅仅是Dockerfile中列出的ADD文件
当看到Sending build context消息时 , docker客户端的意思是将上下文从本地机器传输到Docker守护进程。
构建由Docker守护程序(Daemon)运行 , 而不是CLI (命令行) 运行。构建过程做的第一件事是将整个上下文 ( 递归 ) 发送到守护进程
官方建议: 将一个空目录作为上下文起点 , 并将Dockerfile保存在该目录中 , 仅添加构建Dockerfile所需的文件
特别注意 : 不要使用根目录
/作为构建上下文的PATH, 因为会导致构建时 , 将硬盘驱动器的全部内容发送到Docker守护程序
Docker守护进程将逐一运行Dockerfile中的指令 , 如有必要 , 会将每条指令的结果提交到新镜像 , 最后会输出一个最新镜像的 ID。Docker守护进程将自动清理发送的上下文
重点 : 每条指令都是独立运行的 , 并会创建一个新镜像 , 因此像RUN cd /tmp不会对下一条自定产生任何影响 。只要有可能 , Docker就会使用构建缓存来加速Docker构建过程 , 这由控制台输出中的CACHED消息指示。
> docker build -t svendowideit/ambassador .
[internal] load build definition from Dockerfile 0.1s
=> transferring dockerfile: 286B 0.0s
[internal] load .dockerignore 0.1s
=> transferring context: 2B 0.0s
[internal] load metadata for docker.io/library/alpine:3.2 0.4s
CACHED [1/2] FROM docker.io/library/alpine:3.2@sha256:e9a2035f9d0d7ce 0.0s
CACHED [2/2] RUN apk add --no-cache socat 0.0s
exporting to image 0.0s
=> exporting layers 0.0s
=> writing image sha256:1affb80ca37018ac12067fa2af38cc5bcc2a8f09963de 0.0s
=> naming to docker.io/svendowideit/ambassador 0.0s2.4 Dockerfile详细指令

MAINTAINER: 官方写着已弃用 , 不学了STOPSIGNAL: 暂不展开讲解 , 发送到容器退出的系统调用信号 , 还没搞清楚有什么作用ONBUILD:ONBUILD是一个特殊的指令 , 它后面跟的是其它指令 , 比如RUN,COPY等 , 而这些指令 , 在当前镜像构建时并不会被执行只有当以当前镜像为基础镜像 , 去构建下一级镜像的时候才会被执行
Dockerfile中的其它指令都是为了定制当前镜像而准备的 , 唯有ONBUILD是为了帮助别人定制自己而准备的感觉实际应用场景不会很多 , 也不展开讲解
2.4.1 FROM
FROM指令必须是Dockerfile中非注释行的第一个指令 , 即一个Dockerfile从FROM语句开始 ; 除了ARG命令可以在FROM前面
FROM指令用于为镜像文件构建过程指定基础镜像 , 后续的指令运行于此基础镜像所提供的运行环境 ;
实践中 , 基准镜像可以是任何可用镜像文件 , 默认情况下 , docker build会在docker主机上查找指定的镜像文件 , 在其不存在时 , 则会自动从Docker的公共库pull镜像。如果找不到指定的镜像文件 , docker build会返回一个错误信息 ;
FROM可以在一个Dockerfile中出现多次 , 如果有需求在一个Dockerfile中创建多个镜像。
如果FROM语句没有指定镜像标签 , 则默认使用latest标签。
FROM [--platform=<platform>] <image> [AS <name>]
FROM [--platform=<platform>] <image>[:<tag>] [AS <name>]
FROM [--platform=<platform>] <image>[@<digest>] [AS <name>]2.4.1.1 一个dockerfile可以有多个FROM
可以有多个
FROM来创建多个镜像 , 或区分构建阶段 , 将一个构建阶段作为另一个构建阶段的依赖项AS <name>就是命名当前构建阶段在后续构建阶段 , 可以给
FROM、COPY指令用上 , 通过--from=<name>引用前面构建的镜像
# 第一构建阶段:将仅用于生成 requirements.txt 文件
FROM tiangolo/uvicorn-gunicorn:python3.9 as requirements-stage
# 将当前工作目录设置为 /tmp
WORKDIR /tmp
# 生成 requirements.txt
RUN touch requirements.txt
# 第二构建阶段 , 在这往后的任何内容都将保留在最终容器映像中
FROM python:3.9
# 将当前工作目录设置为 /code
WORKDIR /code
# 复制 requirements.txt;这个文件只存在于前一个 Docker 阶段 , 这就是使用 --from=requirements-stage 复制它的原因
COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt
# 运行命令
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
# 复制
COPY ./app /code/app2.4.1.2 了解 ARG 和 FROM 如何交互
FROM指令支持由出现在第一个FROM之前的任何ARG指令声明的变量
ARG CODE_VERSION=latest
FROM base:${CODE_VERSION}
CMD /code/run-app
FROM extras:${CODE_VERSION}
CMD /code/run-extras注意
在
FROM之前声明的ARG在构建阶段之外 , 因此不能在FROM之后的任何指令中使用要使用在第一个
FROM之前声明的ARG的默认值 , 要在构建阶段内声明一次没有值的ARG指令
ARG VERSION=latest
FROM busybox:$VERSION
ARG VERSION
RUN echo $VERSION > image_version2.4.2 LABEL
可以为生成的镜像添加元数据标签信息 , 这些信息可以用来辅助过滤出特定镜像
LABEL <key>=<value> <key>=<value> <key>=<value> ...示例一
# key 加了 "
LABEL "com.example.vendor"="ACME Incorporated"
# key 没有 "
LABEL com.example.label-with-value="foo"
LABEL version="1.0"
# 换行
LABEL description="This text illustrates \
that label-values can span multiple lines."示例二
一行添加多个key=value
LABEL multi.label1="value1" multi.label2="value2" other="value3"等价写法
LABEL multi.label1="value1" \
multi.label2="value2" \
other="value3"通过 docker inspect 查看添加的元数据
> docker image inspect --format='' myimage
{
"com.example.vendor": "ACME Incorporated",
"com.example.label-with-value": "foo",
"version": "1.0",
"description": "This text illustrates that label-values can span multiple lines.",
"multi.label1": "value1",
"multi.label2": "value2",
"other": "value3"
}2.4.3 WORKDIR
切换到镜像中的指定路径 , 设置工作目录。 在WORKDIR中需要使用绝对路径 , 如果镜像中对应的路径不存在 , 会自动创建此目录。
一般用WORKDIR来替代RUN cd <path> && <do something>切换目录进行操作的指令。
WORKDIR指令为Dockerfile中跟随它的任何RUN、CMD、ENTRYPOINT、COPY、ADD指令设置工作目录。如果WORKDIR不存在 , 即使它没有在任何后续 , Dockerfile指令中使用 , 它也会被创建。
在Dockerfile文件中 , WORKDIR指令可出现多次 , 其路径也可以为相对路径 , 不过 , 其是相对此前一个WORKDIR指令指定的路径 ;
另外 , WORKDIR也可调用由ENV指定定义的变量 ;
使用相对路径的场景
WORKDIR指令可以在Dockerfile中多次使用
如果提供了相对路径 , 它将相对于前一个WORKDIR指令的路径
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwdpwd的输出将会是/a/b/c
推荐 : 为了避免出错 , 推荐
WORKDIR指令中只使用绝对路径
使用环境变量
WORKDIR指令可以使用前面ENV设置的环境变量
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd # /path/$DIRNAME示例
会将宿主机的test.txt文件复制到 镜像的/tmp/test.txt
WORKDIR /tmp
COPY test.txt .2.4.4 RUN
用于指定docker build过程中运行的程序 , 其可以是任何命令
run指令的原理
RUN指令将在当前镜像上加新的一层 , 并执行任何命令和提交结果 , 生成的提交镜像将用于Dockfile中的后续步骤分层
RUN指令和生成提交符合Docker核心概念 , 提交成本低 , 并且可以通过docker history中的任意步骤创建容器 , 像git代码控制一样
shell形式命令在
shell中运行Linux上默认为/bin/sh -cWindows上cmd/S /C
RUN <command>第一种格式中 ,
<command>通常是一个shell命令 , 且以“/bin/sh -c”来运行它 , 这意味着此进程在容器中的PID不为1,不能接收Unix信号 , 因此 , 当使用docker stop <container>命令停止容器时 , 此进程接收不到SIGTERM信号 ;exec形式RUN ["executable", "param1", "param2"]必须双引号 , 不能是单引号
第二种语法格式中的参数是一个
JSON格式的数组 , 其中<executable>为要运行的命令 , 后面的<paramN>为传递给命令的选项或参数 ; 然而 , 此种格式指定的命令不会以"/bin/sh -c"来发起 , 因此常见的shell操作如变量替换以及通配符(?,*等)替换将不会进行 ; 不过 , 如果要运行的命令依赖于此shell特性的话 , 可以将其替换为类似下面的格式。RUN ["/bin/bash", "-c", "<executable>", "<param1>"]
两种写法的实际例子
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
RUN ["/bin/bash", "-c", "echo hello"]示例
shell模式下使用\ , 可以将单个RUN指令延续到下一行 , 等于执行了两个命令
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME' exec模式下使用环境变量
# 错误写法 , 不会使用 HOME 环境变量
RUN [ "echo", "$HOME" ]
# 正确写法 , 需要将 echo 和使用环境变量放一起
RUN [ "sh", "-c", "echo $HOME" ]
2.4.5 CMD
类似于
RUN指令 ,CMD指令也可用于运行任何命令或应用程序 , 不过 , 二者的运行时间点不同RUN指令运行于映像文件构建过程中 , 而
CMD指令运行于基于Dockerfile构建出的新映像文件启动一个容器时CMD指令的首要目的在于为启动的容器指定默认要运行的程序 , 且其运行结束后 , 容器也将终止 ; 不过 ,CMD指定的命令其可以被docker run的命令行选项所覆盖 , 而ENTRYPOINT不会被后面的docker run命令覆盖。在
Dockerfile中可以存在多个CMD指令 , 但仅最后一个会生效
RUN和CMD区别 :RUN可以在构建阶段运行很多个命令 , 而且每运行一个命令都会单独提交结果 ; CMD在构建阶段不执行任何操作 , 而是指定镜像默认执行的命令
# exec 形式 , 推荐
CMD ["executable","param1","param2"]
CMD ["可执行命令", "参数1", "参数2"...]
# 作为ENTRYPOINT的默认参数
CMD ["param1","param2"]
# shell ,与run命令运行方式一样
CMD 命令 param1 param2 exec模式下使用环境变量
# 错误写法 , 不会使用 HOME 环境变量
CMD [ "echo", "$HOME" ]
# 正确写法 , 需要将 echo 和使用环境变量放一起
CMD [ "sh", "-c", "echo $HOME" ]2.4.6 EXPOSE
通知Docker容器在运行时监听指定的网络端口
语法 :
EXPOSE <port>[/<protocol>] [<port>[/<protocol>] ....<protocol>用于指定传输层协议 , 可为tcp或udp二者之一 , 默认为TCP协议EXPOSE指令可一次指定多个端口 , 例如 :EXPOSE 11211/udp 11211/tcp
EXPOSE 原理
个人理解 :
EXPOSE暴露的端口更像是指明了该容器提供的服务需要用到的端口EXPOSE并不会直接将端口自动和宿主机某个端口建立映射关系如果
docker run, 指定了自动映射-P, 那么会将所有暴露的端口随机映射到宿主机的高阶端口如果
docker run, 指定了--net=host宿主机网络模式 , 容器中EXPOSE指令暴露的端口会直接使用宿主机对应的端口 , 不存在映射关系如果
EXPOSE暴露的端口确定要和某个宿主机端口建立映射关系 , 还是要用到docker run -p参数EXPOSE显式地标明镜像开放端口 , 一定程度上提供了操作的便利 , 也提高了Dockerfile的可读性和可维护性
示例
下面四个例子子都以这个dockerfile来启动容器
FROM nginx
EXPOSE 80EXPOST端口和docker run的例子启动容器命令
docker run -it test通过
docker inspect查看容器元数据docker inspect mynginx1容器
80端口并没有映射到宿主机上
EXPOSE端口和docker run -P的栗子启动容器命令
docker run -it -d -P test通过
docker inspect查看容器元数据docker inspect mynginx1
容器
80端口随机映射到宿主机的高阶端口 , 所谓高阶端口就是比较大的端口号啦EXPOSE端口和docker run --network=host的栗子启动容器命令
docker run -it -d -network=host test查看容器列表
docker ps
PORTS为空 , 代表没有映射关系通过
docker inspect查看元数据docker inspect nd_cray
宿主机后台可以查看到宿主机中的
80端口直接映射到容器中的80端口EXPOSE端口和docker run -p的栗子启动容器命令
docker run -it -d -p 80:800 test通过
docker inspect查看容器元数据docker inspect mynginx1
容器
80端口成功映射到宿主机800端口验证
nginx是否部署成功
2.4.7 ENV
用于为镜像定义所需的环境变量 , 并可被Dockerfile文件中位于其后的其它指令(如ENV、ADD、COPY等)所调用 , 调用格式为$variable_ name 或${variable_name}
格式
ENV <key> <value> 或 ENV <key>=<value> . .第一种格式中 ,
<key>之后的所有内容均会被视作其<value>的组成部分 , 因此 , 一次只能设置一个变量;第二种格式可用一次设置多个变量 , 每个变量为一个”
<key>=<value>"的键值对 , 如果<value>中包含空格 , 可以以反斜线(\) 进行转义 , 也可通过对<value>加引号进行标识 ; 另外 , 反斜线也可用于续行;定义多个变量时 , 建议使用第二种方式 , 以便在同一层中完成所有功能
${variable_name}语法还支持以下指定的一些标准bash修饰符
${variable:-word}: 表示如果设置了variable, 那么结果就是那个值 ; 如果未设置变量 , 则结果将是word${variable:+word}: 表示如果设置了variable, 则结果为word, 否则为空字符串在所有情况下 ,
word可以是任何字符串 , 包括额外的环境变量
示例一
FROM busybox
ENV FOO=/bar
WORKDIR ${FOO} # WORKDIR /bar
ADD . $FOO # ADD . /bar
COPY \$FOO /quux # COPY $FOO /quux示例二
覆盖环境变量的栗子
ENV abc=hello
ENV abc=bye def=$abc
ENV ghi=$abcdef的值是hello , ghi的值是bye
注意
当容器从生成的镜像运行时 , 使用
ENV设置的环境变量将持续存在可以使用
docker inspect查看值 , 并使用docker run --env <key>=<value>更改它们
示例三
dockerfile
FROM nginx
EXPOSE 80
ENV test=test1234构建镜像 , 启动容器 , inspect查看
docker build -f test.dockerfile -t test2 .
docker run -it -d -P test2
docker inspect 120b
通过ENV设置的环境变量是会保留到容器中的
2.4.8 USER
用于指定运行image时的用户名或UID , 后续的RUN等指令也会使用指定的用户身份, 运行Dockerfile中任何RUN、CMD或EntRyPoInT指令指定的程序时的用户名或UID
默认情况下 , container的运行身份为root用户
USER <user>[:<group>]
或
USER <UID>[:<GID>]当容器中运行的服务不需要管理员权限时 , 可以先建立一个特定的用户和用户组 , 为它分配必要的权限 , 使用USER切换到这个用户
需要注意的是 , <UID>可以为任意数字 , 但实践中其必须为/etc/passwd中某用户的有效UID,否则 , docker run命令将运行失败
重点
使用
USER指定用户时 , 可以使用用户名、UID或GID, 或是两者的组合使用
USER指定用户后 ,Dockerfile中后续的命令RUN、CMD、ENTRYPOINT都将使用该用户
注意事项
在
Windows上 , 如果用户不是内置帐户 , 则必须先创建该用户可以通过
Dockerfile的RUN net user命令来完成
FROM microsoft/windowsservercore
# 在容器创建新用户
RUN net user /add patrick
# 设置用户
USER patrick 也可以使用docker run -u指定用户
替代默认设置的用户
可以使用
uid来指定用户
docker run -i -t -u 1001 busybox sh2.4.9 ENTRYPOINT
2.4.9.1 ENTRYPOINT基础
类似CMD指令的功能 , 用于为容器指定默认运行程序 , 从而使得容器像是一个单独的可执行程序。 与CMD不同的是 , 由ENTRYPOINT启动的程序不会被docker run命令行指定的参数所覆盖 , 而且 , 这些命令行参数会被当作参数传递给ENTRYPOINT指定指定的程序
ENTRYPOINT指定镜像的默认入口命令 , 该入口命令会在启动容器时作为根命令执行 , 所有其他传入值作为该命令的参数。 不过 , docker run命令的--entrypoint选项的参数可覆盖ENTRYPOINT指令指定的程序
ENTRYPOINT 和 CMD 联合使用
当指定了
ENTRYPOINT后 ,CMD的含义就发生了改变 , 不再是直接的运行其命令 , 而是将CMD的内容作为参数传给ENTRYPOINT指令换句话说实际执行时 , 会变成
<ENTRYPOINT> "<CMD>"
那么有了CMD后 , 为什么还要有ENTRYPOINT呢 ? 这种<ENTRYPOINT> "<CMD>" 有什么好处么?
CMD # 指定这个容器启动的时候要运行的命令 , 不可以追加命令
ENTRYPOINT # 指定这个容器启动的时候要运行的命令 , 可以追加命令2.4.9.2 第一个场景测试
测试 CMD
编写
dockerfile文件FROM centos CMD ["ls","-a"]构建镜像
docker build -f CMD.dockerfile -t test .运行容器
> docker run test . .. .dockerenv bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var运行容器并追加命令
> docker run test -l docker: Error response from daemon: OCI runtime create failed: container_linux.go:380: starting container process caused: exec: "-l": executable file not found in $PATH: unknown.看到可执行文件找不到的报错 ,
executable file not found跟在镜像名后面的是
command, 运行时会替换CMD的默认值 , 因此这里的-l替换了原来的CMD, 而不是追加在原来的ls -a后面而
-l根本不是命令 , 所以自然找不到如果想加入
-i参数 , 必须重写ls命令> docker run test ls -a -l total 56 drwxr-xr-x 1 root root 4096 Oct 28 09:36 . drwxr-xr-x 1 root root 4096 Oct 28 09:36 .. -rwxr-xr-x 1 root root 0 Oct 28 09:36 .dockerenv lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin drwxr-xr-x 5 root root 340 Oct 28 09:36 dev drwxr-xr-x 1 root root 4096 Oct 28 09:36 etc drwxr-xr-x 2 root root 4096 Nov 3 2020 home lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64 drwx------ 2 root root 4096 Sep 15 14:17 lost+found drwxr-xr-x 2 root root 4096 Nov 3 2020 media drwxr-xr-x 2 root root 4096 Nov 3 2020 mnt drwxr-xr-x 2 root root 4096 Nov 3 2020 opt dr-xr-xr-x 221 root root 0 Oct 28 09:36 proc dr-xr-x--- 2 root root 4096 Sep 15 14:17 root drwxr-xr-x 11 root root 4096 Sep 15 14:17 run lrwxrwxrwx 1 root root 8 Nov 3 2020 sbin -> usr/sbin drwxr-xr-x 2 root root 4096 Nov 3 2020 srv dr-xr-xr-x 13 root root 0 Oct 28 09:36 sys drwxrwxrwt 7 root root 4096 Sep 15 14:17 tmp drwxr-xr-x 12 root root 4096 Sep 15 14:17 usr drwxr-xr-x 20 root root 4096 Sep 15 14:17 var可以了 , 但这明显不是最优选择 ,
ENTRYPOINT就可以解决这个问题
测试 ENTRYPOINT
编写
dockerfile文件FROM centos ENTRYPOINT ["ls","-a"]构建镜像
docker build -f ENTRYPOINT.dockerfile -t test .运行容器并追加命令
> docker run test -l total 56 drwxr-xr-x 1 root root 4096 Oct 28 09:38 . drwxr-xr-x 1 root root 4096 Oct 28 09:38 .. -rwxr-xr-x 1 root root 0 Oct 28 09:38 .dockerenv lrwxrwxrwx 1 root root 7 Nov 3 2020 bin -> usr/bin drwxr-xr-x 5 root root 340 Oct 28 09:38 dev drwxr-xr-x 1 root root 4096 Oct 28 09:38 etc drwxr-xr-x 2 root root 4096 Nov 3 2020 home lrwxrwxrwx 1 root root 7 Nov 3 2020 lib -> usr/lib lrwxrwxrwx 1 root root 9 Nov 3 2020 lib64 -> usr/lib64 drwx------ 2 root root 4096 Sep 15 14:17 lost+found drwxr-xr-x 2 root root 4096 Nov 3 2020 media drwxr-xr-x 2 root root 4096 Nov 3 2020 mnt drwxr-xr-x 2 root root 4096 Nov 3 2020 opt dr-xr-xr-x 207 root root 0 Oct 28 09:38 proc dr-xr-x--- 2 root root 4096 Sep 15 14:17 root drwxr-xr-x 11 root root 4096 Sep 15 14:17 run lrwxrwxrwx 1 root root 8 Nov 3 2020 sbin -> usr/sbin drwxr-xr-x 2 root root 4096 Nov 3 2020 srv dr-xr-xr-x 13 root root 0 Oct 28 09:38 sys drwxrwxrwt 7 root root 4096 Sep 15 14:17 tmp drwxr-xr-x 12 root root 4096 Sep 15 14:17 usr drwxr-xr-x 20 root root 4096 Sep 15 14:17 var
2.4.9.3 第二个应用场景
启动容器就是启动主进程 , 但启动主进程前 , 可能需要一些准备工作 , 比如
mysql可能需要一些数据库配置、初始化的工作 , 这些工作要在最终的mysql服务器运行之前解决还可能希望避免使用
root用户去启动服务 , 从而提高安全性 , 而在启动服务前还需要以root身份执行一些必要的准备工作 , 最后切换到服务用户身份启动服务这些准备工作是和容器
CMD无关的 , 无论CMD为什么 , 都需要事先进行一个预处理的工作 , 这种情况下 , 可以写一个脚本 , 然后放入ENTRYPOINT中去执行 , 而这个脚本会将接到的参数 ( 也就是<CMD>) 作为命令 , 在脚本最后执行
官方镜像redis
FROM alpine:3.4
...
RUN addgroup -S redis && adduser -S -G redis redis
...
ENTRYPOINT ["docker-entrypoint.sh"]
EXPOSE 6379
CMD [ "redis-server" ]
docker-entrypoint.sh
#!/bin/sh
...
# allow the container to be started with `--user`
if [ "$1" = 'redis-server' -a "$(id -u)" = '0' ]; then
find . \! -user redis -exec chown redis '{}' +
exec gosu redis "$0" "$@"
fi
exec "$@"该脚本的内容就是根据CMD的内容来判断 , 如果是 redis-server 的话 , 则切换到 redis 用户身份启动服务器 , 否则依旧使用root身份执行
[root@poloyy ~]# docker run -it redis id
uid=0(root) gid=0(root) groups=0(root)
# 直接进入容器内部
[root@poloyy ~]# docker run -it redis
root@565f89976d63:/#
2.4.10 ADD
ADD指令类似于COPY指令 , ADD支持使用TAR文件和URL路径
作用 : 添加内容到镜像
详解 : 该命令将复制指定的
<src>路径下内容到镜像中的<dest>路径下语法 :
ADD [--chown=<user>:<group>] <src>... <dest> ADD [--chown=<user>:<group>] ["<src>",... "<dest>"]--chown: 仅适用于linux上的dockerfile, 在window上没有用户、组的概念
2.4.10.1 ADD作用
ADD指令从<src>复制新文件、目录或远程文件URL, 并将它们添加到路径<dest>可以指定多个
<src>资源 , 但如果它们是文件或目录 , 则它们的路径被解析为相对于构建上下文的源每个
<src>可能包含通配符 , 匹配将使用Go的filepath.Match规则完成
示例
*通配符把所有
hom开头的文件添加到镜像文件系统的/mydir/目录下ADD hom* /mydir/?通配符?匹配0或1个字符 , 比如会把home.txt文件添加到/mydir/目录下ADD hom?.txt /mydir/
重点 : <dest>是绝对路径 , 或相对于WORKDIR的路径 , 源将在目标容器内复制到该路径中
使用相对路径的栗子
ADD test.txt relativeDir/等价于
ADD test.txt <WORKDIR>/relativeDir/使用绝对路径的栗子
将
test.txt添加到/absoluteDir/目录下ADD test.txt /absoluteDir/包含特殊字符的文件
添加名为
arr[0].txt的文件ADD arr[[]0].txt /mydir/
2.4.10.2 ADD遵循的规则
如果<src>为URL且<dest>不以/ 结尾 , 则<src>指定的文件将被下载并直接被创建为<dest>;如果<dest>以/结尾 , 则文件名URL指定的文件将被直接下载并保存为<dest>/<filename>
如果<src>是一个本地系统上的压缩格式的tar文件 , 它将被展开为一个目录 , 其行为类似于"tar-x"命令 ; 然而 , 通过URL获取到的tar文件将不会自动展开;
如果<src>有多个 , 或其间接或直接使用了通配符 , 则<dest>必须是一个以/结尾的目录路径 ;如果<dest>不以/结尾 , 则其被视作一个普通文件 , <src>的内容将被直接写入到<dest>;
<src>路径必须在构建的上下文中不能添加
../something、/something, 因为docker构建的第一步是将上下文目录(和子目录)发送到docker守护进程# test.txt 是相对路径 , 相对于构建上下文 ADD test.txt /mkdir/ # 错误写法 , 文件均不在上下文目录中 , 并不会被找到 # 这个找的就是构建上下文的上级目录的 test.txt ADD ../test.txt /mkdir/ # 这个找的是本机根目录下的 test.txt ADD /test.txt /mkdir/<src>是一个URL<dest>不以斜杠结尾 , 那么文件将从 URL 下载并复制到<dest><dest>以斜杠结尾 , 则从 URL 推断文件名并将文件下载到<dest>/<filename>ADD http://example.com/foobar/这将创建文件
/foobarURL必须有一个重要的路径 , 以便在这种情况下可以 找到文件名 (http://example.com将不起作用 )
<src>是目录则复制目录的全部内容 , 包括文件系统元数据
不会复制目录本身 , 只会复制其内容
ADD dir /mydir/<src>是压缩格式 (gzip、bzip2、identity、xz) 的本地tar文件会将它自动解压为目录
但来自远程
URL资源不会被解压缩当一个目录被复制或解压时 , 它的行为与
tar -x相同
注意 : 文件是否被识别为可识别的压缩格式完全取决于文件的内容 , 而不是文件的名称 ; 例如 , 如果一个空文件恰好以
.tar.gz结尾 , 黄不会被识别为压缩文件 , 也不会生成任何类型的解压缩错误消息 , 而只会将该文件复制到目标位置<src>是任何其他类型的文件则将其与其元数据一起单独复制
<dest>以斜杠 / 结尾 , 它将被视为一个目录 , 并且<src>的内容将写入<dest>/base(<src>)
指定了多个
<src>资源 , 或者由于使用了通配符 , 则<dest>必须是一个目录 , 并且必须以斜杠/结尾ADD test1.txt test2.txt /mydir/<dest>不以斜杠结尾它将被视为常规文件 , 并且
<src>的内容将写入<dest>ADD test.txt /mytext<dest>不存在路径中所有缺失的目录都会自动创建
ADD test.txt /dir/test/my/
注意事项 : <src>的内容发生变化 , 第一个遇到的 ADD 指令将使来自 Dockerfile 的所有后续指令的缓存无效 , 这包括使RUN指令的缓存无效
完整练习的 dockerfile
FROM centos
# 添加文件到目录下
ADD test.txt /mydir/
# 将文件内容写入 mytest
ADD test.txt /mytest
# 压缩文件,自动解压
ADD jmeter.log.zip /myzipdir/
# 添加目录
ADD TeamFile /
# 其他文件
ADD jmeter.log /mydir/
# 多个文件
ADD test1.txt test2.txt /mydir/
# 通配符,dest 不存在自动创建
ADD test*.txt /mydir/test/
# 特殊字符串
ADD add[[]0].txt /mydir/
WORKDIR /data
# 相对路径
ADD test.txt test/2.4.10.3 ADD 和 COPY 的区别和使用场景
ADD支持添加远程url和自动提取压缩格式的文件 ,COPY只允许从本机中复制文件COPY支持从其他构建阶段中复制源文件(--from)根据官方
Dockerfile最佳实践 , 除非真的需要从远程url添加文件或自动提取压缩文件才用ADD, 其他情况一律使用COPY
注意
ADD从远程url获取文件和复制的效果并不理想 , 因为该文件会增加Docker Image最终的大小相反 , 应该使用
curl或wget来获取远程文件 , 然后在不需要它时进行删除
示例 : COPY网上路径(URL)的tar包
编写
dockerfile文件# Description: test image FROM busybox:latest MAINTAINER "Along <along@along.com>" COPY index.html /data/web/html/ COPY yum.repos.d /etc/yum.repos.d/ ADD http://nginx.org/download/nginx-1.15.8.tar.gz /usr/local/src/注 :
ADD的<src>是网上的nginx下载路径
使用
build制作镜像[root@along img1]# docker build -t busyboxhttpd:v0.3 ./ Sending build context to Docker daemon 27.65 kB Step 1/5 : FROM busybox:latest ---> 758ec7f3a1ee Step 2/5 : MAINTAINER "Along <along@along.com>" ---> Using cache ---> 5d2cda1631d7 Step 3/5 : COPY index.html /data/web/html/ ---> Using cache ---> 2e0fe0eb0cb7 Step 4/5 : COPY yum.repos.d /etc/yum.repos.d/ ---> Using cache ---> 68586925bdf9 Step 5/5 : ADD http://nginx.org/download/nginx-1.15.8.tar.gz /usr/local/src/ Downloading 1.028 MB/1.028 MB ---> 91c71b469a9e Removing intermediate container abbad8dcdefb Successfully built 91c71b469a9e基于此新建镜像运行容器 , 进行验证
[root@along ~]# docker run --name web1 --rm busyboxhttpd:v0.3 ls /usr/local/src nginx-1.15.8.tar.gz
示例 : COPY本地的路径的tar包
编写
dockerfile文件# Description: test image FROM busybox:latest MAINTAINER "Along <along@along.com>" COPY index.html /data/web/html/ COPY yum.repos.d /etc/yum.repos.d/ #ADD http://nginx.org/download/nginx-1.15.8.tar.gz /usr/local/src/ ADD nginx-1.15.8.tar.gz /usr/local/src/在
dockerfile同级目录下准备好yum.repos.d目录[root@along img1]# wget http://nginx.org/download/nginx-1.15.8.tar.gz使用
build制作镜像[root@along img1]# docker build -t busyboxhttpd:v0.4 ./ Sending build context to Docker daemon 1.056 MB Step 1/5 : FROM busybox:latest ---> 758ec7f3a1ee Step 2/5 : MAINTAINER "Along <along@along.com>" ---> Using cache ---> 5d2cda1631d7 Step 3/5 : COPY index.html /data/web/html/ ---> Using cache ---> 2e0fe0eb0cb7 Step 4/5 : COPY yum.repos.d /etc/yum.repos.d/ ---> Using cache ---> 68586925bdf9 Step 5/5 : ADD nginx-1.15.8.tar.gz /usr/local/src/ ---> f99baec4b992 Removing intermediate container 7f02f3fe649基于此新建镜像运行容器 , 进行验证
[root@along ~]# docker run --name web1 --rm busyboxhttpd:v0.4 ls /usr/local/src /usr/local/src/nginx-1.15.8 /usr/local/src: nginx-1.15.8 /usr/local/src/nginx-1.15.8: CHANGES CHANGES.ru LICENSE README auto conf configure contrib html man src
2.4.11 COPY
作用 : 复制内容到镜像
详解 : 复制本地主机的
<src>下内容到镜像中的<dest>, 目标路径不存在时 , 会自动创建。路径 : 支持正则表达式 ,
COPY test* /tmp语法 :
COPY <src>... <dest> 或 COPY ["<src>",... "<dest>"]--chown: 仅适用于linux上的dockerfile, 在window上没有用户、组的概念
2.4.11.1 COPY的作用
COPY指令从<src>复制新文件、目录或远程文件URL, 并将它们添加到路径<dest>可以指定多个
<src>资源 , 但如果它们是文件或目录 , 则它们的路径被解析为相对于构建上下文的源每个
<src>可能包含通配符 , 匹配将使用Go的filepath.Match规则完成
示例
*通配符把所有
hom开头的文件添加到镜像文件系统的/mydir/目录下COPY hom* /mydir/?通配符?匹配0或1个字符 , 比如会把home.txt文件添加到/mydir/目录下COPY hom?.txt /mydir/
重点 : <dest>是绝对路径 , 或相对于WORKDIR的路径 , 源将在目标容器内复制到该路径中
使用相对路径的栗子
COPY test.txt relativeDir/等价于
COPY test.txt <WORKDIR>/relativeDir/使用绝对路径的栗子
将
test.txt添加到/absoluteDir/目录下COPY test.txt /absoluteDir/包含特殊字符的文件
添加名为
arr[0].txt的文件COPY arr[[]0].txt /mydir/
2.4.11.2 标志--from=<name>
FROM node:12.13.1
LABEL maintainer="a@abc.com"
COPY . /app/
WORKDIR /app
RUN npm install --registry=https://registry.npm.taobao.org --cache=$HOME/.npm/.cache/cnpm --disturl=https://npm.taobao.org/dist --userconfig=$HOME/.cnpmrc
RUN npm run build
FROM nginx
LABEL maintainer="a@abc.com"
COPY --from=0 /app/dist /usr/share/nginx/html将从from指定的构建阶段中寻找源文件<src>。最后面的--from=0 参数, 从前边的阶段中拷贝文件到当前阶段中, Dockerfile中包含多个FROM语句时, 0代表第一个阶段。除了使用数字, 我们还可以给阶段命名
# 第一构建阶段:将仅用于生成 requirements.txt 文件
FROM tiangolo/uvicorn-gunicorn:python3.9 as requirements-stage
# 将当前工作目录设置为 /tmp
WORKDIR /tmp
# 生成 requirements.txt
RUN touch requirements.txt
# 第二构建阶段 , 在这往后的任何内容都将保留在最终容器映像中
FROM python:3.9
# 将当前工作目录设置为 /code
WORKDIR /code
# 从第一个阶段复制 requirements.txt;这个文件只存在于前一个 Docker 阶段 , 这就是使用 --from-requirements-stage 复制它的原因
COPY --from=requirements-stage /tmp/requirements.txt /code/requirements.txt
# 运行命令
RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt2.4.11.3 文件复制准则
<src>必须是build上下文中的路径 , 不能是其父目录中的文件 ;
如果<src>是目录 , 则其内部文件或子目录会被递归复制 , 但<src>目录自身不会被复制 ;
如果指定了多个<src> , 或在<src>中使用了通配符 , 则<dest>必须是一个目录 , 且必须以 / 结尾 ;
如果<dest>事先不存在 , 他将会被自动创建 , 这包括父目录路径。
<src>路径必须在构建的上下文中不能添加
../something、/something, 因为docker构建的第一步是将上下文目录 ( 和子目录 ) 发送到docker守护进程# test.txt 是相对路径 , 相对于构建上下文 COPY test.txt /mkdir/ # 错误写法 , 文件均不在上下文目录中 , 并不会被找到 # 这个找的就是构建上下文的上级目录的 test.txt COPY ../test.txt /mkdir/ # 这个找的是本机根目录下的 test.txt COPY /test.txt /mkdir/<src>是目录则复制目录的全部内容 , 包括文件系统元数据
不会复制目录本身 , 只会复制其内容
COPY dir /mydir/<src>是任何其他类型的文件则将其与其元数据一起单独复制
<dest>以斜杠/结尾 , 它将被视为一个目录 , 并且<src>的内容将写入<dest>/base(<src>)
指定了多个
<src>资源 , 或者由于使用了通配符则
<dest>必须是一个目录 , 并且必须以斜杠/结尾COPY test1.txt test2.txt /mydir/<dest>不以斜杠结尾它将被视为常规文件 , 并且
<src>的内容将写入<dest>COPY test.txt /mytext<dest>不存在路径中所有缺失的目录都会自动创建
COPY test.txt /dir/test/my/
注意事项 : <src>的内容发生变化 , 第一个遇到的COPY指令将使来自Dockerfile的所有后续指令的缓存无效 , 这包括使RUN指令的缓存无效
完整练习的 dockerfile
FROM centos
# 添加文件到目录下
COPY test.txt /mydir/
# 将文件内容写入 mytest
COPY test.txt /mytest
# 压缩文件,自动解压
COPY jmeter.log.zip /myzipdir/
# 添加目录
COPY TeamFile /
# 其他文件
COPY jmeter.log /mydir/
# 多个文件
COPY test1.txt test2.txt /mydir/
# 通配符,dest 不存在自动创建
COPY test*.txt /mydir/test/
# 特殊字符串
COPY COPY[[]0].txt /mydir/
WORKDIR /data
# 相对路径
COPY test.txt test/2.4.12 HEALTHCHECK
HEALTHCHECK指令告诉Docker如何测试容器以检查它是否仍在工作。 即使服务器进程仍在运行 , 这也可以检测出陷入无限循环且无法处理新连接的Web服务器等情况。
当在一个镜像指定了HEALTHCHECK指令后 , 用其启动容器 , 初始状态会为starting , 在HEALTHCHECK指令检查成功后变为healthy , 如果连续一定次数失败 , 则会变为unhealthy
格式
# 设置检查容器健康状况的命令 HEALTHCHECK [选项] CMD <命令> # 如果基础镜像有健康检查指令 , 使用这行可以屏蔽掉其健康检查指令 HEALTHCHECK NONEOPTIONS--interval=DURATION(default: 30s) : 两次健康检查的间隔 , 默认为30秒 ;-- timeout= DURATION(default: 30s) : 健康检查命令运行超时时间 , 如果超过这个时间 , 本次健康检查就被视为失败 , 默认30秒;--start-period= DURATION(default: 0s) : 服务启动多久后开始探测 , 默认0秒--retries=N(default: 3) : 当连续失败指定次数后 , 则将容器状态视为unhealthy, 默认 3 次
返回值 :
0:成功 , 容器成功是健康的 , 随时可以使用1: 失败 , 不健康的容器无法正常工作2: 保留 , 保留不使用此退出代码
注意 : 和
CMD,ENTRYPOINT一样 ,HEALTHCHECK只可以出现一次 , 如果写了多个 , 只有最后一个生效
为啥要用 HEALTHCHECK
在没有
HEALTHCHECK指令前 ,Docker引擎只可以通过容器内主进程是否退出来判断容器是否状态异常很多情况下这没问题 , 但是如果程序进入死锁状态 , 或者死循环状态 , 应用进程并不退出 , 但是该容器已经无法提供服务了
在
1.12以前 ,Docker不会检测到容器的这种状态 , 从而不会重新调度 , 导致可能会有部分容器已经无法提供服务了却还在接受用户请求自
1.12之后 ,Docker提供了HEALTHCHECK指令 , 通过该指令指定一行命令 , 用这行命令来判断容器主进程的服务状态是否还正常 , 从而比较真实的反应容器实际状态
示例一
假设有个镜像是个最简单的Web服务 , 希望增加健康检查来判断其Web服务是否在正常工作 , 可以用curl来帮助其判断
dockerfile
FROM nginx RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/* HEALTHCHECK --interval=5s --timeout=3s \ CMD curl -fs http://localhost/ || exit 1如果返回
1则退出
执行
docker build$ docker build -f HEALTHCHEK.dockerifle -t myweb .执行
docker rundocker run -d --name web -p 80:80 myweb查看容器状态
docker ps
刚启动时的状态是
health : starting, 过多几秒后就变成healthy了如果健康检查连续失败超过了重试次数 , 状态就会变为
unhealthy
查看容器健康检查信息
为了帮助排障 , 健康检查命令的输出 ( 包括
stdout以及stderr) 都会被存储于健康状态里 , 可以用docker inspect来查看> docker inspect --format '{{json .State.Health}}' web | python -m json.tool { "FailingStreak": 0, "Log": [ { "End": "2021-10-31T04:43:58.8446416Z", "ExitCode": 0, "Output": "<!DOCTYPE html>\n<html>\n<head>\n<title>Welcome to nginx!</title>\n<style>\nhtml { color-scheme: light dark; }\nbody { width: 35em; margin: 0 auto;\nfont-family: Tahoma, Verdana, Arial, sans-serif; }\n</style>\n</head>\n<body>\n<h1>Welcome to nginx!</h1>\n<p>If you see this page, the nginx web server is successfully installed and\nworking. Further configuration is required.</p>\n\n<p>For online documentation and support please refer to\n<a href=\"http://nginx.org/\">nginx.org</a>.<br/>\nCommercial support is available at\n<a href=\"http://nginx.com/\">nginx.com</a>.</p>\n\n<p><em>Thank you for using nginx.</em></p>\n</body>\n</html>\n", "Start": "2021-10-31T04:43:58.7591924Z" } ], "Status": "healthy" }
示例二
编写
dockerfile文件检测
web2容器的10080端口( 其实打开的是80端口 ,10080并没有 , 所有会检测失败)FROM busybox LABEL maintainer="Along <along@along.com>" app="httpd" ENV WEB_DOC_ROOT="/data/web/html" RUN mkdir -p ${WEB_DOC_ROOT} && \ echo "<h1>Busybox httpd server</h1>" > ${WEB_DOC_ROOT}/index.html CMD /bin/httpd -f -h ${WEB_DOC_ROOT} HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:10080/wget命令在上面过程中主要时输出页面内容 :-O -, 直接将页面内容输入到屏幕中 ;-q, 不输出其他命令提示内容使用
build制作镜像[root@docker2 image2]# docker build -t busyboxhttpd:v1.3 ./ Sending build context to Docker daemon 2.048kB Step 1/6 : FROM busybox ---> 3a093384ac30 Step 2/6 : LABEL maintainer="Along <along@along.com>" app="httpd" ---> Using cache ---> 164a0af1b5dc Step 3/6 : ENV WEB_DOC_ROOT="/data/web/html" ---> Using cache ---> 930023fca80b Step 4/6 : RUN mkdir -p ${WEB_DOC_ROOT} && echo "<h1>Busybox httpd server</h1>" > ${WEB_DOC_ROOT}/index.html ---> Using cache ---> 1df364fca443 Step 5/6 : CMD /bin/httpd -f -h ${WEB_DOC_ROOT} ---> Running in 671f9491d0c3 Removing intermediate container 671f9491d0c3 ---> 71451fa629e9 Step 6/6 : HEALTHCHECK --start-period=3s CMD wget -O - -q http://${IP:-0.0.0.0}:${PORT:-80}/ ---> Running in 474395f265da Removing intermediate container 474395f265da ---> 757ab550ea25 Successfully built 757ab550ea25 Successfully tagged busyboxhttpd:v1.3基于此新建镜像运行容器 , 进行验证
[root@docker2 ~]# docker run --name web2 --rm -d busyboxhttpd:v1.3 7060a6717e77acecd683a6b05332ab7cec128b836e6aa2d7d5ce8fb1b38b7fd6 --- 容器刚创建 , 还没检测完时 , 容器还是health 健康状态 [root@docker2 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7060a6717e77 busyboxhttpd:v1.3 "/bin/sh -c '/bin/ht…" 3 seconds ago Up 3 seconds (health: starting) web2 --- 30s 检测完时 , 没有检测到10080端口 , 容器变为unhealthy不健康状态 [root@docker2 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 7060a6717e77 busyboxhttpd:v1.3 "/bin/sh -c '/bin/ht…" 30 seconds ago Up 30 seconds (unhealthy) web2
2.4.13 SHELL
SHELL指令允许覆盖用于shell命令形式的默认shell。
Linux上的默认shell是[“/bin /sh” , “-c”] , 在Windows上是[“cmd” , “/ S” , “/ C”]
SHELL指令必须以JSON格式写入Dockerfile。
语法 :
SHELL ["executable", "parameters"]SHELL指令可以多次出现。每个
SHELL指令都会覆盖所有先前的SHELL指令 , 并影响所有后续指令。
示例
SHELL ["/bin/sh", "-c"]
RUN lll ; ls
SHELL ["/bin/sh", "-cex"]
RUN lll ; ls两个RUN运行同一命令
第二个RUN运行的命令会打印出每条命令并当遇到错误时退出
2.4.14 VOLUME
用于在image中创建一个挂载点目录 , 以挂载Docker host上的卷或其它容器上的卷
语法
VOLUME <mountpoint> 或 VOLUME ["<mountpoint>"]
mountpoint : 代表容器中的目录 , 与docker run不同 , Dockerfile中不能指定宿主机目录 , 默认使用docker管理的挂载点
如果挂载点目录路径下此前在文件中存在 , docker run命令会在卷挂载完成后将此前的所有文件复制到新挂载的卷中
VOLUME ["/data"] 这里的/data目录就会在运行时自动挂载为匿名卷 , 任何向/data中写入的信息都不会记录进容器存储层 , 从而保证了容器存储层的无状态化
注意事项
使用基于
Windows的容器时挂载的目录必须是以下之一 :
一个不存在或空的目录
C盘以外的盘
无法通过
VOLUME挂载主机目录为了镜像的可移植性 ,
VOLUME指令不支持指定主机目录参数(像docker run -v <主机目录>:<容器目录>是可以指定主机目录的)如果要指定 , 还是要通过
docker run -v来指定主机目录哦
示例一 : 使用docker run -v
# 下面的命令是将宿主机上面的/home/data 目录挂载到了容器中/data 目录 , 无论在容器里面还是物理机中修改对应目录下面的内容 , 都会同步
[root@docker01 ~]# docker run --name test -it -v /home/data:/data centos /bin/sh
# 容器中加入一个文件
sh-4.4# cd /data/
sh-4.4# ls
sh-4.4# pwd
/data
sh-4.4# echo "hello" > 1.txt
sh-4.4# cat 1.txt
hello
# 物理机中查看一下
[root@docker01 dockerfiledaemon]# cat /home/data/1.txt
hello
# 查看一下容器的详情
[root@docker01 dockerfiledaemon]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8c11c574a6e7 centos "/bin/sh" 3 minutes ago Up 3 minutes test
[root@docker01 dockerfiledaemon]# docker inspect 8c11c574a6e7
"Mounts": [
{
"Type": "bind",
"Source": "/home/data",
"Destination": "/data",
"Mode": "",
"RW": true,
"Propagation": "rprivate"
}
],示例二 : 使用docker run -v参数 , 但不指定物理机的目录
# 启动一个docker 实例
[root@docker01 ~]# docker run --name test -it --rm -v /data centos /bin/sh
# 查看容器运行情况
[root@docker01 dockerfiledaemon]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
d42f7d885c87 centos "/bin/sh" 6 seconds ago Up 4 seconds test
# 从下面可以看到 , source 的路径是自动分配的一个目录
[root@docker01 dockerfiledaemon]# docker inspect d42f7d885c87
"Mounts": [
{
"Type": "volume",
"Name": "fa853d8d48b38edf36af1064854f5d85102c577dc2581184b979479be21495ed",
"Source": "/var/lib/docker/volumes/fa853d8d48b38edf36af1064854f5d85102c577dc2581184b979479be21495ed/_data",
"Destination": "/data",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],示例三 : 使用dockerfile中挂载点进行
通过docker run命令的-v标识创建的挂载点只能对创建的容器有效。通过dockerfile的VOLUME指令可以在镜像中创建挂载点 , 这样只要通过该镜像创建的容器都有了挂载点。但在dockerfile中无法指定主机上对应的目录 , 是自动生成的。
# 新建一个dockerfile
FROM centos
VOLUME ["/data1","/data2"]
# 构建镜像
[root@docker01 dockerfiledaemon]# docker build -t volume:01 .
Sending build context to Docker daemon 2.048kB
Step 1/2 : FROM centos
---> 300e315adb2f
Step 2/2 : VOLUME ["/data1","/data2"]
---> Running in 2ac39c2c1918
Removing intermediate container 2ac39c2c1918
---> 65ce3fb0ff1a
Successfully built 65ce3fb0ff1a
Successfully tagged volume:01
[root@docker01 dockerfiledaemon]# docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
volume 01 65ce3fb0ff1a 14 seconds ago 209MB
# 创建一个容器
[root@docker01 dockerfiledaemon]# docker run -it --name volumename volume:01 /bin/sh
sh-4.4#
# 查看容器id
[root@docker01 ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
071fdcf0a2eb volume:01 "/bin/sh" 26 seconds ago Up 24 seconds volumename
# 查看挂载点 , 会发现一共有两个自动挂载的目录
[root@docker01 ~]# docker inspect 071fdcf0a2eb
"Mounts": [
{
"Type": "volume",
"Name": "479d38b89f7eb8cbf52fa4b50f1e7c0caf358707bca418a46dcc330b43983817",
"Source": "/var/lib/docker/volumes/479d38b89f7eb8cbf52fa4b50f1e7c0caf358707bca418a46dcc330b43983817/_data",
"Destination": "/data2",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
},
{
"Type": "volume",
"Name": "926f1a3c58d1574433b0488b1f8b0a88011e1143925621729c6ba5b8fd2b5150",
"Source": "/var/lib/docker/volumes/926f1a3c58d1574433b0488b1f8b0a88011e1143925621729c6ba5b8fd2b5150/_data",
"Destination": "/data1",
"Driver": "local",
"Mode": "",
"RW": true,
"Propagation": ""
}
],2.4.15 ARG
ARG指令类似ENV , 定义了一个变量 ; 区别于ENV : 用户可以在构建时docker build --build-arg <varname> = <value>进行对变量的修改 , ENV不可以 ;
如果用户指定了未在Dockerfile中定义的构建参数 , 那么构建输出警告
语法 :
ARG <name>[= <default value>]
当镜像编译成功后 , ARG 指定的变量将不再存在(ENV指定的变量将在镜像中保留)
Docker内置了一些镜像创建变量 , 用户可以直接使用而无须声明 , 包括(不区分大小写)HTTP_PROXY、HTTPS_PROXY、FTP_PROXY、NO_PROXY
ARG和ENV的区别
ARG定义的变量只会存在于镜像构建过程 , 启动容器后并不保留这些变量ENV定义的变量在启动容器后仍然保留
ARG 指令有生效范围
如果在FROM指令之前指定 , 那么只能用于FROM指令中
ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine
RUN set -x ; echo ${DOCKER_USERNAME}使用上述
Dockerfile会发现无法输出${DOCKER_USERNAME}变量的值要想正常输出 , 必须在
FROM之后再次指定ARG
# 只在 FROM 中生效
ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine
# 要想在 FROM 之后使用 , 必须再次指定
ARG DOCKER_USERNAME=library
RUN set -x ; echo ${DOCKER_USERNAME}多阶段构建的时候 , ARG定义的变量 , 每个FROM都能用
# 这个变量在每个 FROM 中都生效
ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine
RUN set -x ; echo 1
FROM ${DOCKER_USERNAME}/alpine
RUN set -x ; echo 2对于上述 Dockerfile 两个 FROM 指令都可以使用 ${DOCKER_USERNAME} , 对于在各个阶段中使用的变量都必须在每个阶段分别指定 :
ARG DOCKER_USERNAME=library
FROM ${DOCKER_USERNAME}/alpine
# 在FROM 之后使用变量 , 必须在每个阶段分别指定
ARG DOCKER_USERNAME=library
RUN set -x ; echo ${DOCKER_USERNAME}
FROM ${DOCKER_USERNAME}/alpine
# 在FROM 之后使用变量 , 必须在每个阶段分别指定
ARG DOCKER_USERNAME=library
RUN set -x ; echo ${DOCKER_USERNAME}2.5 DockerFile 多阶段构建
在Docker 17.05版本之前 , 我们构建Docker镜像时 , 通常会采用两种方式 :
2.5.1 全部放入一个 Dockerfile
一种方式是将所有的构建过程编包含在一个 Dockerfile 中 , 包括项目及其依赖库的编译、测试、打包等流程 , 这里可能会带来的一些问题 :
镜像层次多 , 镜像体积较大 , 部署时间变长
源代码存在泄露的风险
例如 , 编写 app.go 文件 , 该程序输出 Hello World!
package main
import "fmt"
func main(){
fmt.Printf("Hello World!");
}编写 Dockerfile.one 文件
FROM golang:alpine
RUN apk --no-cache add git ca-certificates
WORKDIR /go/src/github.com/go/helloworld/
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
&& cp /go/src/github.com/go/helloworld/app /root
WORKDIR /root/
CMD ["./app"]构建镜像
$ docker build -t go/helloworld:1 -f Dockerfile.one .2.5.2 分散到多个 Dockerfile
另一种方式 , 就是我们事先在一个 Dockerfile 将项目及其依赖库编译测试打包好后 , 再将其拷贝到运行环境中 , 这种方式需要我们编写两个 Dockerfile 和一些编译脚本才能将其两个阶段自动整合起来 , 这种方式虽然可以很好地规避第一种方式存在的风险 , 但明显部署过程较复杂。
例如 , 编写 Dockerfile.build 文件
FROM golang:alpine
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .编写 Dockerfile.copy 文件
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]新建 build.sh
#!/bin/sh
echo Building go/helloworld:build
docker build -t go/helloworld:build . -f Dockerfile.build
docker create --name extract go/helloworld:build
docker cp extract:/go/src/github.com/go/helloworld/app ./app
docker rm -f extract
echo Building go/helloworld:2
docker build --no-cache -t go/helloworld:2 . -f Dockerfile.copy
rm ./app现在运行脚本即可构建镜像
$ chmod +x build.sh
$ ./build.sh对比两种方式生成的镜像大小
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB2.5.3 使用多阶段构建
为解决以上问题 , Docker v17.05开始支持多阶段构建 (multistage builds)。使用多阶段构建我们就可以很容易解决前面提到的问题 , 并且只需要编写一个 Dockerfile :
例如 , 编写 Dockerfile 文件
FROM golang:alpine as builder
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest as prod
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]构建镜像
$ docker build -t go/helloworld:3 .对比三个镜像大小
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
go/helloworld 3 d6911ed9c846 7 seconds ago 6.47MB
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB很明显使用多阶段构建的镜像体积小 , 同时也完美解决了上边提到的问题。
2.5.4 只构建某一阶段的镜像
我们可以使用 as 来为某一阶段命名 , 例如
FROM golang:alpine as builder例如当我们只想构建 builder 阶段的镜像时 , 增加 --target=builder 参数即可
$ docker build --target builder -t username/imagename:tag .2.5.5 构建时从其他镜像复制文件
上面例子中我们使用 COPY --from=0 /go/src/github.com/go/helloworld/app . 从上一阶段的镜像中复制文件 , 我们也可以复制任意镜像中的文件。
$ COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf3. DockerFile制作镜像
3.1 nginx镜像制作
3.1.1 制作yum版nginx镜像
CentOS安装nginx
先经过
docker下载centos镜像[root@centos-7 ~]# yum install docker-ce -y #安装官网docker-ce , 默认安装最新版 [root@centos-7 ~]# systemctl start docker #启动docker [root@centos-7 ~]# docker pull centos #下载centos镜像切换到一个新建的
nginx目录下 , 进行实验测试[root@centos-7 ~]# mkdir /data/nginx [root@centos-7 ~]# cd /data/nginx创建一个
nginx测试页 , 然后访问网页测试[root@centos-7 nginx]# cd /data/nginx [root@centos-7 nginx]# cat index.html #修改一个测试页面 nginx web test [root@centos-7 nginx]# cat index1.html #修改一个测试页面 nginx web index1 test [root@centos-7 nginx]# tar zcvf code.tar.gz index1.html index.html开始在
nginx目录下创建一个Dockerfile文件 , 创建的nginx镜像 , 不能在镜像内改配置文件 , 只能将配置文件传出来 , 在宿主机内修改 , 然后再传到配置文件中 , 测试的代码也是如此。FROM centos # docker image inspect 查看镜像元数据时 ,写入"Author": "liu 958421213@@qq.com" MAINTAINER liu 958421213@@qq.com # 写入镜像元数据的LABEL标签中 LABEL auth="liu 9584@qq.com" #修改镜像中的yum源 RUN sed -e "s|^mirrorlist=|#mirrorlist=|g" \ -e "s|^#baseurl=http://mirror.centos.org/centos/\$releasever|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.4.2105|g" \ -e "s|^#baseurl=http://mirror.centos.org/\$contentdir/\$releasever|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.4.2105|g" \ -i.bak \ /etc/yum.repos.d/CentOS-*.repo #通过yum源安装nginx RUN yum install epel-release -y && yum install nginx -y && rm -rf /usr/share/nginx/html/index.html ENV MY_HOST 192.168.7.101 ADD code.tar.gz /usr/share/nginx/html/ EXPOSE 80 443 CMD ["nginx","-g" ,"daemon off;"]使用
docker build命令生成新的镜像docker build -t nginx:make .运行容器
[root@centos7 ~]# docker run -it -d -p 8080:80 nginx:make
Ubuntu安装nginx
编写
dockerfile[root@centos7 ubuntu]# cat dockerfile # 构建一个基于ubuntu的docker定制镜像 # 基础镜像 FROM ubuntu # 镜像作者 MAINTAINER my_name polotest@domain.com # 执行命令 ## 换成国内的软件源 RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list RUN sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list ## 安装nginx RUN apt update >/dev/null 2>&1 RUN apt install nginx -y >/dev/null 2>&1 # 暴露对外端口 EXPOSE 80使用
docker build命令生成新的镜像docker build --network=host -t ubuntu-nginx:v2 .运行容器
[root@centos7 ~]# docker run -it -d -p 80:80 ubuntu-nginx:v2 nginx -g 'daemon off;'需要注意的是,容器中因为没有像
CentOS系统中systemctl接管服务, 所以容器中的命令不能直接设置成daemon,在后台运行。而是通过脚本或者命令, 在容器中的前台运行。
3.1.2 制作编译nginx镜像
nginx源码包下载官网 : https://nginx.org/en/download.html
创建一个
nginx测试页 , 然后访问网页测试[root@centos7 ~]# cd /opt/dockerfile/system/centos/ [root@centos7 centos]# cat index.html #修改一个测试页面 nginx web test [root@centos7 centos]# cat index1.html #修改一个测试页面 nginx web index1 test [root@centos7 centos]# tar zcvf code.tar.gz index1.html index.html [root@centos7 centos]# ls code.tar.gz dockerfile index1.html index.html nginx-1.24.0.tar.gz nginx.conf [root@centos7 centos]#在上面的基础修改DockerFile配置文件
[root@centos7 centos]# cat dockerfile FROM centos MAINTAINER liu xxxxx@@qq.com RUN sed -e "s|^mirrorlist=|#mirrorlist=|g" \ -e "s|^#baseurl=http://mirror.centos.org/centos/\$releasever|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.4.2105|g" \ -e "s|^#baseurl=http://mirror.centos.org/\$contentdir/\$releasever|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.4.2105|g" \ -i.bak \ /etc/yum.repos.d/CentOS-*.repo RUN yum install epel-release -y && yum -y install gcc gcc-c++ make automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop ADD nginx-1.24.0.tar.gz /usr/local/src/ WORKDIR /usr/local/src/nginx-1.24.0/ #RUN cd /usr/local/src/nginx-1.24.0/ RUN ./configure --prefix=/apps/nginx && make && make install RUN ln -sv /apps/nginx/sbin/nginx /usr/sbin/nginx ADD code.tar.gz /apps/nginx/html/ EXPOSE 80 443 CMD ["nginx","-g","daemon off;"]制作镜像
docker build -t nginx:v1 .生成对应容器, 并启动
docker run -it -d -p 80:80 nginx:v1
3.2 jdk业务镜像制作
基于官方提供的centos基础镜像构建JDK和tomcat镜像 , 先构建JDK镜像 , 然后再基于JDK镜像构建tomcat镜像。
3.2.1 制作JDK镜像
JDK下载路径 : Java Downloads | Oracle 中国
先下载基础
centos镜像[root@centos7 centos]# docker pull centos Using default tag: latest latest: Pulling from library/centos a1d0c7532777: Already exists Digest: sha256:a27fd8080b517143cbbbab9dfb7c8571c40d67d534bbdee55bd6c473f432b177 Status: Downloaded newer image for centos:latest docker.io/library/centos:latest [root@centos7 centos]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE centos latest 5d0da3dc9764 2 years ago 231MB切换到指定的
centos系统目录下 , 安装一些常用命令[root@centos7 ~]# mkdir /opt/dockerfile/{web/{nginx,tomcat,jdk,apache},system/{centos,ubuntu,redhat}} -p # 先创建存放镜像目录 [root@centos7 ~]# cd /opt/dockerfile/system/centos创建一个
Dockerfile文件[root@centos7 centos]# cat dockerfile FROM centos RUN sed -e "s|^mirrorlist=|#mirrorlist=|g" \ -e "s|^#baseurl=http://mirror.centos.org/centos/\$releasever|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.4.2105|g" \ -e "s|^#baseurl=http://mirror.centos.org/\$contentdir/\$releasever|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.4.2105|g" \ -i.bak \ /etc/yum.repos.d/CentOS-*.repo RUN yum install epel-release -y && yum -y install gcc gcc-c++ make automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop RUN useradd nginx -u 2019 && useradd tomcat -u 2020 RUN rm -rf /etc/localtime && ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime生成
CentOS系统基础命令镜像[root@centos7 centos]# docker build -t centos-base:8.4.2105 . [+] Building 1.8s (9/9) FINISHED => [internal] load build definition from dockerfile 0.1s => => transferring dockerfile: 727B 0.1s => [internal] load .dockerignore 0.1s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/centos:latest 0.0s => [1/5] FROM docker.io/library/centos 0.0s => CACHED [2/5] RUN sed -e "s|^mirrorlist=|#mirrorlist=|g" -e "s|^#baseurl=http://mirror.centos.org/centos/$releasever|baseurl=https://mirrors.tuna.tsinghua.edu.cn/centos-vault/8.4.2105|g" -e "s 0.0s => CACHED [3/5] RUN yum install epel-release -y && yum -y install gcc gcc-c++ make automake pcre pcre-devel zlib zlib-devel openssl openssl-devel iproute net-tools iotop 0.0s => [4/5] RUN useradd nginx -u 2019 && useradd tomcat -u 2020 0.9s => [5/5] RUN rm -rf /etc/localtime && ln -sv /usr/share/zoneinfo/Asia/Shanghai /etc/localtime 0.4s => exporting to image 0.1s => => exporting layers 0.1s => => writing image sha256:1fed1882e596c4191b1cdecb2263eccad0e55e4ad9bd827906d723861661683b 0.0s => => naming to docker.io/library/centos-base:8.4.2105查看创建好的
CentOS镜像
切换到
jdk目录下 , 创建指定的jdk版本目录 , 制作profile环境变量文件 , 并制作JDK镜像切换到指定的
jdk目录下 , 并将下载下来的jdk文件传到/opt/dockerfile/web/jdk/[root@centos7 jdk]# ls jdk-8u401-linux-x64.tar.gz profile生成
profile[root@centos-7 8u92]# cat profile #在当前目录下创建profile环境变量文件 # /etc/profile # System wide environment and startup programs, for login setup # Functions and aliases go in /etc/bashrc # It's NOT a good idea to change this file unless you know what you # are doing. It's much better to create a custom.sh shell script in # /etc/profile.d/ to make custom changes to your environment, as this # will prevent the need for merging in future updates. pathmunge () { case ":${PATH}:" in *:"$1":*) ;; *) if [ "$2" = "after" ] ; then PATH=$PATH:$1 else PATH=$1:$PATH fi esac } if [ -x /usr/bin/id ]; then if [ -z "$EUID" ]; then # ksh workaround EUID=`/usr/bin/id -u` UID=`/usr/bin/id -ru` fi USER="`/usr/bin/id -un`" LOGNAME=$USER MAIL="/var/spool/mail/$USER" fi # Path manipulation if [ "$EUID" = "0" ]; then pathmunge /usr/sbin pathmunge /usr/local/sbin else pathmunge /usr/local/sbin after pathmunge /usr/sbin after fi HOSTNAME=`/usr/bin/hostname 2>/dev/null` HISTSIZE=1000 if [ "$HISTCONTROL" = "ignorespace" ] ; then export HISTCONTROL=ignoreboth else export HISTCONTROL=ignoredups fi export PATH USER LOGNAME MAIL HOSTNAME HISTSIZE HISTCONTROL # By default, we want umask to get set. This sets it for login shell # Current threshold for system reserved uid/gids is 200 # You could check uidgid reservation validity in # /usr/share/doc/setup-*/uidgid file if [ $UID -gt 199 ] && [ "`/usr/bin/id -gn`" = "`/usr/bin/id -un`" ]; then umask 002 else umask 022 fi for i in /etc/profile.d/*.sh /etc/profile.d/sh.local ; do if [ -r "$i" ]; then if [ "${-#*i}" != "$-" ]; then . "$i" else . "$i" >/dev/null fi fi done unset i unset -f pathmunge export JAVA_HOME=/usr/local/jdk export TOMCAT_HOME=/apps/tomcat export PATH=$JAVA_HOME/bin:$JAVA_HOME/jre/bin:$TOMCAT_HOME/bin:$PATH export CLASSPATH=.$CLASSPATH:$JAVA_HOME/lib:$JAVA_HOME/jre/lib:$JAVA_HOME/lib/tools.jar创建
Dockerfile文件[root@centos7 jdk]# cat dockerfile FROM centos-base:8.4.2105 ADD jdk-8u401-linux-x64.tar.gz /usr/local/src/ RUN ln -sv /usr/local/src/jdk1.8.0_401 /usr/local/jdk ADD profile /etc/profile ENV JAVA_HOME /usr/local/jdk ENV JRE_HOME $JAVA_HOME/jre ENV CLASSPATH $JAVA_HOME/lib/:$JRE_HOME/lib/ ENV PATH $PATH:$JAVA_HOME/bin生成
jdk镜像[root@centos7 jdk]# docker build -t jdk-base:1.8.0.401 . [+] Building 32.4s (9/9) FINISHED => [internal] load build definition from dockerfile 0.0s => => transferring dockerfile: 327B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/centos-base:8.4.2105 0.0s => [1/4] FROM docker.io/library/centos-base:8.4.2105 0.5s => [internal] load build context 6.7s => => transferring context: 141.63MB 6.6s => [2/4] ADD jdk-8u401-linux-x64.tar.gz /usr/local/src/ 19.9s => [3/4] RUN ln -sv /usr/local/src/jdk1.8.0_401 /usr/local/jdk 4.0s => [4/4] ADD profile /etc/profile 0.0s => exporting to image 1.5s => => exporting layers 1.4s => => writing image sha256:1c21819c43fc7511f28b22833517f7f1ed8be05db1a00baeaa707df5b308ba49 0.0s => => naming to docker.io/library/jdk-base:1.8.0.401测试生成的镜像
[root@centos7 jdk]# docker images REPOSITORY TAG IMAGE ID CREATED SIZE jdk-base 1.8.0.401 1c21819c43fc 8 seconds ago 883MB centos-base 8.4.2105 1fed1882e596 About an hour ago 533MB centos latest 5d0da3dc9764 2 years ago 231MB [root@centos7 jdk]# docker run -d -it --rm jdk-base:1.8.0.401 bash 714b1e6e0f1590fc47840294206875d5551d477840d926d83e14c257abac5852 [root@centos7 jdk]# docker exec -it 714 bash [root@714b1e6e0f15 /]# ls bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var [root@714b1e6e0f15 /]# java -version java version "1.8.0_401" Java(TM) SE Runtime Environment (build 1.8.0_401-b10) Java HotSpot(TM) 64-Bit Server VM (build 25.401-b10, mixed mode)
3.2.2 制作tomcat镜像
tomcat 镜像下载: https://archive.apache.org/dist/tomcat/tomcat-8/
切换到tomcat 目录, 准备dockerfile文件
[root@centos7 tomcat-base]# cd /opt/dockerfile/web/tomcat/tomcat-base/ [root@centos7 tomcat-base]# ls apache-tomcat-8.5.98.tar.gz [root@centos7 tomcat-base]# cat dockerfile FROM jdk-base:1.8.0.401 ADD apache-tomcat-8.5.98.tar.gz /apps RUN ln -sv /apps/apache-tomcat-8.5.98 /apps/tomcat生成
tomcat-base镜像[root@centos7 tomcat-base]# docker build -t tomcat-base:8.5.98 . [+] Building 1.4s (8/8) FINISHED => [internal] load build definition from dockerfile 0.0s => => transferring dockerfile: 153B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/jdk-base:1.8.0.401 0.0s => [internal] load build context 0.3s => => transferring context: 10.86MB 0.2s => [1/3] FROM docker.io/library/jdk-base:1.8.0.401 0.1s => [2/3] ADD apache-tomcat-8.5.98.tar.gz /apps 0.4s => [3/3] RUN ln -sv /apps/apache-tomcat-8.5.98 /apps/tomcat 0.5s => exporting to image 0.2s => => exporting layers 0.2s => => writing image sha256:02e0b9f22b6ded7e30b9b08d63cae8b49134256a519324343ccf1b8647dd9c0f 0.0s => => naming to docker.io/library/tomcat-base:8.5.98将
Dockerfile、build脚本与要执行的tomcat脚本进行分离, 因此在/opt/dockerfile/web/tomcat/目录下创建一个目录 :tomcat-app1[root@centos7 tomcat]# mkdir tomcat-app1 [root@centos7 tomcat]# cd tomcat-app1如果宿主机安装好了
tomcat文件, 然后修改里边的sever.xml文件, 指定index.html文件路径。vim /etc/tomcat/conf/server.xml #修改本地的server.xml文件并传到此目录下 <Host name="localhost" appBase="/data/tomcat/webapps" unpackWARs="true" autoDeploy="true"> # 指定存放代码路径到 /data/tomcat/webapps目录下创建一个
index.html测试页面[root@centos7 tomcat-app1]# cat index.html tomcat web app1 page创建
Dockerfile文件和run_tomcat.sh脚本[root@centos7 tomcat-app1]# cat Dockerfile FROM tomcat-base:8.5.98 COPY index.html /data/tomcat/webapps/app1/ COPY run_tomcat.sh /apps/tomcat/bin/ COPY server.xml /apps/tomcat/conf/ RUN chown -R tomcat.tomcat /apps/apache-tomcat-8.5.98 /apps/tomcat /data/tomcat EXPOSE 8080 443 CMD ["/apps/tomcat/bin/run_tomcat.sh"] [root@centos7 tomcat-app1]# cat run_tomcat.sh #!/bin/bash source /etc/profile echo "1.2.1.3 ww.google.net" >> /etc/hosts su - tomcat -c "/apps/tomcat/bin/catalina.sh start" #su - tomcat -c "/apps/tomcat/bin/catalina.sh run" #为了测试拉起容器, 执行脚本时, 可以运行起tomcat, 没有此命令, 就会运行完容器又会退出tomcat服务 tail -f /etc/hosts [root@centos7 tomcat-app1]# chmod +x run_tomcat.sh [root@centos7 tomcat-app1]# ls Dockerfile index.html run_tomcat.sh server.xml [root@centos7 tomcat-app1]#生成镜像
[root@centos7 tomcat-app1]# docker build -t tomcat-app1:v1 . [+] Building 1.6s (10/10) FINISHED => [internal] load build definition from Dockerfile 0.0s => => transferring dockerfile: 319B 0.0s => [internal] load .dockerignore 0.0s => => transferring context: 2B 0.0s => [internal] load metadata for docker.io/library/tomcat-base:8.5.98 0.0s => [1/5] FROM docker.io/library/tomcat-base:8.5.98 0.0s => [internal] load build context 0.0s => => transferring context: 7.97kB 0.0s => CACHED [2/5] COPY index.html /data/tomcat/webapps/app1/ 0.0s => CACHED [3/5] COPY run_tomcat.sh /apps/tomcat/bin/ 0.0s => [4/5] COPY server.xml /apps/tomcat/conf/ 0.0s => [5/5] RUN chown -R tomcat.tomcat /apps/apache-tomcat-8.5.98 /apps/tomcat /data/tomcat 1.4s => exporting to image 0.1s => => exporting layers 0.1s => => writing image sha256:9e7334922dd921cfd23d86f2c3e420905917e7f0c25bbc0b8b387baaba456ea1 0.0s => => naming to docker.io/library/tomcat-app1:v1生成对应容器,并启动
docker run -it -d --rm -p 8080:8080 tomcat-app1:v1测试
tomcat页面
3.2.3 创建第二个tomcat服务
方法很简单, 只需要将tomcat-app1目录复制一份, 修改里边的index.html文件和docker build命令
准备目录
[root@centos7 tomcat-app1]# pwd /opt/dockerfile/web/tomcat/tomcat-app1 [root@centos7 tomcat-app1]# cd .. [root@centos7 tomcat]# cp -ra tomcat-app1 tomcat-app2 [root@centos7 tomcat]# cd tomcat-app2 [root@centos7 tomcat-app2]#修改
tomcat访问页面# vim index.html tomcat web app2 page生成新的
tomcat镜像docker build -t tomcat-app2:v1 .启动容器
docker run -it -d --rm -p 8081:8080 tomcat-app2:v1
3.3 haproxy镜像制作
官网下载haproxy源码包:https://www.haproxy.org/
lua 下载地址: https://www.lua.org/ftp/lua-5.4.6.tar.gz
haproxy 下载地址 : https://www.haproxy.org/download/2.9/src/haproxy-2.9.4.tar.gz
准备
haproxy的dockerfile[root@centos7 haproxy]# cat Dockerfile FROM centos-base:8.4.2105 ADD haproxy-2.9.4.tar.gz /usr/local/src/ ADD lua-5.4.6.tar.gz /usr/local/src/ WORKDIR /usr/local/src/lua-5.4.6/ RUN make linux test WORKDIR /usr/local/src/haproxy-2.9.4/ RUN make ARCH=x86_64 TARGET=linux-glibc USE_PCRE=1 USE_OPENSSL=1 USE_ZLIB=1 USE_CPU_AFFINITY=1 USE_LUA=1 LUA_INC=/usr/local/src/lua-5.4.6/src/ LUA_LIB=/usr/local/src/lua-5.4.6/src/ PREFIX=/usr/local/haproxy RUN make install PREFIX=/usr/local/haproxy && mkdir /usr/local/haproxy/run/ COPY haproxy.cfg /etc/haproxy/ COPY run_haproxy.sh /usr/bin/ EXPOSE 80 9999 CMD ["/usr/bin/run_haproxy.sh"] [root@centos7 haproxy]# pwd /opt/dockerfile/web/haproxy准备运行的
cmd脚本[root@centos7 haproxy]# cat run_haproxy.sh #!/bin/bash /usr/local/haproxy/sbin/haproxy -f /etc/haproxy/haproxy.cfg # 执行此脚本, 运行haproxy tail -f /etc/hosts [root@centos7 haproxy]# ls Dockerfile haproxy-2.9.4.tar.gz haproxy.cfg lua-5.4.6.tar.gz run_haproxy.sh准备
haproxy的配置文件[root@centos7 haproxy]# cat haproxy.cfg global maxconn 100000 chroot /usr/local/haproxy #stats socket /var/lib/haproxy/haproxy.sock mode 600 level admin uid 99 gid 99 daemon #nbproc 2 #cpu-map 1 0 #cpu-map 2 1 pidfile /usr/local/haproxy/run/haproxy.pid log 127.0.0.1 local3 info defaults option http-keep-alive option forwardfor maxconn 100000 mode http timeout connect 300000ms timeout client 300000ms timeout server 300000ms listen stats mode http bind 0.0.0.0:9999 stats enable log global stats uri /haproxy-status stats auth haadmin:123456 listen web_port bind 0.0.0.0:80 mode http log global server web1 192.168.194.130:8080 check inter 3000 fall 2 rise 5 server web2 192.168.194.130:8081 check inter 3000 fall 2 rise 5构建镜像
[root@centos7 haproxy]# docker build -t haproxy:2.9.4 .运行容器
[root@centos7 haproxy]# docker run -d -it --rm -p 80:80 -p 9999:9999 haproxy:2.9.4 8ac76d58b6de5ffc4ef8951a9207ebc072a551501d09cd2bb1f324c5c0aefc69查看运行的容器
[root@centos7 haproxy]# docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 8ac76d58b6de haproxy:2.9.4 "/usr/bin/run_haprox…" 8 seconds ago Up 7 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:9999->9999/tcp, :::9999->9999/tcp musing_merkle 532fbe362f96 tomcat-app2:v1 "/apps/tomcat/bin/ru…" 7 hours ago Up 7 hours 443/tcp, 0.0.0.0:8081->8080/tcp, :::8081->8080/tcp boring_merkle fd0fca27554f tomcat-app1:v1 "/apps/tomcat/bin/ru…" 7 hours ago Up 7 hours 443/tcp, 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp cranky_darwin测试网页

直接网页访问

3.4 alpine
3.4.1 anpine镜像介绍

Alpine 操作系统是一个面向安全的轻型 Linux 发行版。它不同于通常 Linux 发行版 , Alpine 采用了 musl libc 和 busybox 以减小系统的体积和运行时资源消耗 , 但功能上比busybox 又完善的多 , 因此得到开源社区越来越多的青睐。在保持瘦身的同时 , Alpine 还提供了自己的包管理工具 apk , 可以通过 https://pkgs.alpinelinux.org/packages 网站上查询包信息 , 也可以直接通过 apk 命令直接查询和安装各种软件。
Alpine 由非商业组织维护的 , 支持广泛场景的 Linux发行版 , 它特别为资深/重度Linux用户而优化 , 关注安全 , 性能和资源效能。Alpine 镜像可以适用于更多常用场景 , 并且是一个优秀的可以适用于生产的基础系统/环境。
Alpine Docker 镜像也继承了 Alpine Linux 发行版的这些优势。相比于其他 Docker 镜像 , 它的容量非常小 , 仅仅只有 5 MB 左右(对比Ubuntu 系列镜像接近 200 MB) , 且拥有非常友好的包管理机制。官方镜像来自 docker-alpine 项目。
目前 Docker 官方已开始推荐使用 Alpine 替代之前的 Ubuntu 做为基础镜像环境。这样会带来多个好处。包括镜像下载速度加快、镜像安全性提高 、主机之间的切换更方便 、占用更少磁盘空间等。
下表是官方镜像的大小比较 :
REPOSITORY TAG IMAGE ID VIRTUAL SIZE
alpine latest 4e38e38c8ce0 4.799 MB
debian latest 4d6ce913b130 84.98 MB
ubuntu latest b39b81afc8ca 188.3 MB
centos latest 8efe422e6104 210 MB地址链接 :
阿里云的镜像仓库 : https://mirrors.aliyun.com/apline
范例 : alpine安装软件
#修改源替换成阿里源, 将里面 dl-cdn.alpinelinux.org 的 改成 mirrors.aliyun.com
vi etc/apk/repositories
http://mirrors.aliyun.com/alpine/v3.8/main/
http://mirrors.aliyun.com/alpine/v3.8/community/
#更新源
apk update
#安装软件
apk add vim
#删除软件
apk del openssh openntp vim范例 :
/ # apk --help
apk-tools 2.10.4, compiled for x86_64.
Installing and removing packages:
add Add PACKAGEs to 'world' and install (or upgrade) them, while ensuring that all dependencies are met
del Remove PACKAGEs from 'world' and uninstall them
System maintenance:
fix Repair package or upgrade it without modifying main dependencies
update Update repository indexes from all remote repositories
upgrade Upgrade currently installed packages to match repositories
cache Download missing PACKAGEs to cache and/or delete unneeded files from cache
Querying information about packages:
info Give detailed information about PACKAGEs or repositories
list List packages by PATTERN and other criteria
dot Generate graphviz graphs
policy Show repository policy for packages
Repository maintenance:
index Create repository index file from FILEs
fetch Download PACKAGEs from global repositories to a local directory
verify Verify package integrity and signature
manifest Show checksums of package contents
Use apk <command> --help for command-specific help.
Use apk --help --verbose for a full command listing.
This apk has coffee making abilities.
/ # apk add nginx
/ # apk info nginx
nginx-1.16.1-r6 description:
HTTP and reverse proxy server (stable version)
nginx-1.16.1-r6 webpage:
https://www.nginx.org/
nginx-1.16.1-r6 installed size:
1126400
~ # apk manifest nginx
sha1:d21a96358a10b731f8847e6d32799efdc2a7f421 etc/logrotate.d/nginx
sha1:50bd6d3b4f3e6b577d852f12cd6939719d2c2db5 etc/init.d/nginx
sha1:379c1e2a2a5ffb8c91a07328d4c9be2bc58799fd etc/nginx/scgi_params
sha1:da38e2a0dded838afbe0eade6cb837ac30fd8046 etc/nginx/fastcgi_params
sha1:cc2fcdb4605dcac23d59f667889ccbdfdc6e3668 etc/nginx/uwsgi_params
sha1:cbf596ddb3433a8e0d325f3c188bec9c1bb746b3 etc/nginx/fastcgi.conf
sha1:e39dbc36680b717ec902fadc805a302f1cf62245 etc/nginx/mime.types
sha1:e9dddf20f1196bb67eef28107438b60c4060f4d3 etc/nginx/nginx.conf
sha1:7b2a4da1a14166442c10cbf9e357fa9fb53542ca etc/nginx/conf.d/default.conf
sha1:cd7f5dc8ccdc838a2d0107511c90adfe318a81e7 etc/conf.d/nginx
sha1:05f050f6ed86c5e6b48c2d2328e81583315431be usr/sbin/nginx
sha1:c3f02ca81f7f2c6bde3f878b3176f225c7781c7d var/lib/nginx/modules
sha1:0510312d465b86769136983657df98c1854f0b60 var/lib/nginx/run
sha1:35db17c18ce0b9f84a3cc113c8a9e94e19f632b1 var/lib/nginx/logs
sha1:7dd71afcfb14e105e80b0c0d7fce370a28a41f0a var/lib/nginx/html/index.html
sha1:95de71d58b37f9f74bede0e91bc381d6059fc2d7 var/lib/nginx/html/50x.html
~ # ls -l /bin
total 824
lrwxrwxrwx 1 root root 12 Jan 16 21:52 arch -> /bin/busybox
lrwxrwxrwx 1 root root 12 Jan 16 21:52 ash -> /bin/busybox
lrwxrwxrwx 1 root root 12 Jan 16 21:52 base64 -> /bin/busybox
lrwxrwxrwx 1 root root 12 Jan 16 21:52 bbconfig -> /bin/busybox
-rwxr-xr-x 1 root root 841288 Jan 15 10:36 busybox
lrwxrwxrwx 1 root root 12 Jan 16 21:52 cat -> /bin/busybox
lrwxrwxrwx 1 root root 12 Jan 16 21:52 chgrp -> /bin/busybox
lrwxrwxrwx 1 root root 12 Jan 16 21:52 chmod -> /bin/busybox
lrwxrwxrwx 1 root root 12 Jan 16 21:52 chown -> /bin/busybox3.4.2 DockerFile配置
FROM alpine
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories
RUN apk update
RUN apk add iotop gcc libgcc libc-dev libcurl libc-utils pcre-dev zlib-dev libnfs make pcre pcre2 zip unzip net-tools pstree wget libevent libevent-dev iproute2
ADD nginx-1.24.0.tar.gz /opt/
WORKDIR /opt/nginx-1.24.0/
RUN ./configure --prefix=/apps/nginx && make && make install
RUN addgroup -g 2019 -S nginx && adduser -s /sbin/nologin -S -D -u 2019 -G nginx nginx
RUN chown -R nginx.nginx /apps/nginx/
EXPOSE 80 443
CMD ["/apps/nginx/sbin/nginx","-g","daemon off;"]3.5 ubuntu dockerfile配置
按照编译nginx的方式显示dockerfile
[root@centos7 ubuntu]# cat dockerfile
FROM ubuntu
RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
RUN sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
RUN apt update
RUN apt install -y libpcre3 libpcre3-dev zlib1g-dev gcc openssh-server openssl libssl-dev libpcre3 libpcre3-dev zlib1g-dev unzip zip make
ADD nginx-1.24.0.tar.gz /usr/local/src/
WORKDIR /usr/local/src/nginx-1.24.0/
RUN ./configure --prefix=/apps/nginx && make && make install
RUN groupadd -g 2019 nginx && useradd -g nginx -s /usr/sbin/nologin -u 2019 nginx
RUN chown -R nginx.nginx /apps/nginx
EXPOSE 80 443
CMD ["/apps/nginx/sbin/nginx","-g","daemon off;"]参考链接
Docker ( 44) - 初识 Dockerfile - 小菠萝测试笔记 - 博客园 (cnblogs.com)
Docker ( 45 ) - docker build 命令详解 - 小菠萝测试笔记 - 博客园 (cnblogs.com)
Docker ( 46 ) - 详解 Dockerfile 指令 - 小菠萝测试笔记 - 博客园 (cnblogs.com)
Docker之六----Dockerfile详解 - 一叶知秋~~ - 博客园 (cnblogs.com)
Dockerfile( 6) - EXPOSE 指令详解 - 小菠萝测试笔记 - 博客园 (cnblogs.com)
Dockerfile ( 9 ) - ENTRYPOINT 指令详解 - 小菠萝测试笔记 - 博客园 (cnblogs.com)
Dockerfile 多阶段构建 · Docker -- 从入门到实践 (docker-practice.github.io)
Docker之三----java业务镜像制作 - 一叶知秋~~ - 博客园 (cnblogs.com)