DockerFile 是面相开发的
通过 DockerFile 构建生成镜像
比如基于jekins与dockerfile部署java项目:
指令:
补充说明:
- CMD:要运行的命令,只有最后一个会生效(不能追加,比如安装命令后下一个条 CMD 直接使用),可被替代
- ENTRYPOINT:指定这个容器启动的时候要运行的命令,可以直接追加命令
- ONBUILD:当构建一个被继承的 DockerFile 的时候,就会运行 ONBUILD 指令
- COPY:类似 ADD,将文件拷贝到镜像中,不会自动解压
- ENV:设置环境变量
示例:
FROM centos8.4MAINTAINER yangluqing<ylq.win@aliyun.com>ENV MYPATH /user/localWORKDIR $MYPATHRUN yum -y install vimRUN yum -y install net-toolsEXPOSE 80CMD echo $MYPATHCMD echo "------------end------------"CMD /bin/bash
执行构建命令
# -f 指定DockerFile 文件位置,如果当前目录下有名为 DockerFile 的文件,就不用-f指定# -t 目标镜像# 有一个 .docker build -f DockerFile -t mycentos0.1 .
可以通过 docker history 看构建步骤
比如 nginx:
docker history nginxIMAGE CREATED CREATED BY SIZE COMMENT87a94228f133 5 days ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon… 0B<missing> 5 days ago /bin/sh -c #(nop) STOPSIGNAL SIGQUIT 0B<missing> 5 days ago /bin/sh -c #(nop) EXPOSE 80 0B<missing> 5 days ago /bin/sh -c #(nop) ENTRYPOINT ["/docker-entr… 0B<missing> 5 days ago /bin/sh -c #(nop) COPY file:09a214a3e07c919a… 4.61kB<missing> 5 days ago /bin/sh -c #(nop) COPY file:0fd5fca330dcd6a7… 1.04kB<missing> 5 days ago /bin/sh -c #(nop) COPY file:0b866ff3fc1ef5b0… 1.96kB<missing> 5 days ago /bin/sh -c #(nop) COPY file:65504f71f5855ca0… 1.2kB<missing> 5 days ago /bin/sh -c set -x && addgroup --system -… 64MB<missing> 5 days ago /bin/sh -c #(nop) ENV PKG_RELEASE=1~buster 0B<missing> 5 days ago /bin/sh -c #(nop) ENV NJS_VERSION=0.6.2 0B<missing> 5 days ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.21.3 0B<missing> 5 days ago /bin/sh -c #(nop) LABEL maintainer=NGINX Do… 0B<missing> 5 days ago /bin/sh -c #(nop) CMD ["bash"] 0B<missing> 5 days ago /bin/sh -c #(nop) ADD file:910392427fdf089bc… 69.3MB
FROM:
- FROM scratch: 最基础镜像
- FROM centos:使用 centos 做基础镜像
- FROM ubuntu:18.04
LABEL:
- 类似注释
- LABEL maintainer=”ylq.win@aliyun.com”
- LABEL version=”1.0”
- LABEL description=”This is proj description”
RUN:
- 每运行一次 RUN,image 都会生成新的一层
```
RUN:yum update && yum install -y vim \
python-dev # 反斜线换行
RUN:apt-get update && apt-get install -y perl \ pwgen —no-install-recommends && rm -rf /var/lib/apt/lists/* # 注意清理 cache
RUN /bin/bash -c ‘source $HOME/.bashrc;echo$HOME’ # bash 执行
<a name="EIxhg"></a>### CMD 与 ENTRYPOINT:- CMD:设置容器启动后-默认-执行的命令和参数- ENTRYPOINT:设置容器启动时运行的命令- 区别:- CMD:- 如果 docker run 指定了其他命令,CMD命令会被忽略- 如果定义了多个 CMD,只有最后一个会被执行- ENTRYPOINT:- 让容器以应用程序或者服务的形式运行- 不会被忽略,一定会执行- 最佳实践:写一个 shell 脚本作为 entrypoint- `ENTRYPOINT ["docker-entrypoint.sh"]`Shell 格式:
RUN apt-get install -y vim CMD echo “hello docker” ENTRYPOINT echo “hello docker”
Exec 格式:
RUN [“apt-get”, “install”, “-y”, “vim”] CMD [“/bin/echo”, “hello docker”] ENTRYPOINT [“/bin/echo”, “hello docker”]
- Exec 格式执行带常量命令:
FROM centos ENV name Docker ENTRYPOINT [“/bin/bash”, “-c”, “echo hello $name”] # -c 后面命令是一个整体
一些初学者将 `CMD` 写为:
CMD service nginx start
然后发现容器执行后就立即退出了。甚至在容器内去使用 `systemctl` 命令结果却发现根本执行不了。这就是因为没有搞明白前台、后台的概念,没有区分容器和虚拟机的差异,依旧在以传统虚拟机的角度去理解容器。<br />对于容器而言,其启动程序就是容器应用进程,容器就是为了主进程而存在的,主进程退出,容器就失去了存在的意义,从而退出,其它辅助进程不是它需要关心的东西。<br />而使用 `service nginx start` 命令,则是希望 upstart 来以后台守护进程形式启动 `nginx` 服务。而刚才说了 `CMD service nginx start` 会被理解为 `CMD [ "sh", "-c", "service nginx start"]`,因此主进程实际上是 `sh`。那么当 `service nginx start` 命令结束后,`sh` 也就结束了,`sh` 作为主进程退出了,自然就会令容器退出。<br />正确的做法是直接执行 `nginx` 可执行文件,并且要求以前台形式运行。比如:
CMD [“nginx”, “-g”, “daemon off;”]
<a name="Kly8E"></a>###<a name="EiczE"></a>### WORKDIR:- 改变目录,类似 cd , 不要用 RUN cd,尽量使用绝对目录
WORKDIR /test # 如果没有会自动创建 test 目录 WORKDIR demo RUN pwd # 输出结果应该是 /test/demo
<a name="Ujwvq"></a>### ADD 与 COPY:- 都是把本地文件添加到 image 里面- ADD 不止能添加文件到指定目录,还能添加到根目录并解压- 大部分情况,COPY 优先于 ADD- 添加远程文件/目录 使用 RUN curl 或者 RUN wget- 注意:这里的路径是执行 build 命令时的上下文路径的相对路径- 如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 `.gitignore` 一样的语法写一个 `.dockerignore`,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
ADD test.tar.gz /home/ # 添加到根目录并解压
WORKDIR /root ADD hello test/ # /root/test/hello
等同
WORKDIR /root COPY hello test/
- `<源路径>` 可以是多个,甚至可以是通配符,其通配符规则要满足 Go 的 `[filepath.Match](https://golang.org/pkg/path/filepath/#Match)` 规则,如:
COPY hom* /mydir/ COPY hom?.txt /mydir/
- 在 Docker 官方的 [Dockerfile 最佳实践文档](https://yeasy.gitbooks.io/docker_practice/appendix/best_practices.html) 中要求,尽可能的使用 `COPY`,因为 `COPY` 的语义很明确,就是复制文件而已,而 `ADD` 则包含了更复杂的功能,其行为也不一定很清晰。最适合使用 `ADD` 的场合,就是所提及的需要自动解压缩的场合。- 另外需要注意的是,`ADD` 指令会令镜像构建缓存失效,从而可能会令镜像构建变得比较缓慢。因此在 `COPY` 和 `ADD` 指令中选择的时候,可以遵循这样的原则,所有的文件复制均使用 `COPY` 指令,仅在需要自动解压缩的场合使用 `ADD`。<a name="sVrc5"></a>### ENV:- 设定环境变量,生成一个常量- 尽量使用 ENV 增加可维护性
ENV MYSQL_VERSION 5.6 # 设置常量 RUN apt-get install -y mysql-server=”${MYSQL_VERSION}” \ && rm -rf /var/lib/apt/lists/* # 引用常量
<a name="h61ip"></a>###<a name="FFu5K"></a>### ARG 构建参数格式:`ARG <参数名>[=<默认值>]`<br />`Dockerfile` 中的 `ARG` 指令是定义参数名称,以及定义其默认值。该默认值可以在构建命令 `docker build` 中用 `--build-arg <参数名>=<值>` 来覆盖。<br />这对于使用 CI 系统,用同样的构建流程构建不同的 `Dockerfile` 的时候比较有帮助,避免构建命令必须根据每个 Dockerfile 的内容修改。<a name="Ll2NK"></a>### VOLUME 定义匿名卷格式为:- `VOLUME ["<路径1>", "<路径2>"...]`- `VOLUME <路径>`容器运行时应该尽量保持容器存储层不发生写操作,对于数据库类需要保存动态数据的应用,其数据库文件应该保存于卷(volume)中。
VOLUME /data
这里的 `/data` 目录就会在运行时自动挂载为匿名卷,任何向 `/data` 中写入的信息都不会记录进容器存储层,从而保证了容器存储层的无状态化。当然,运行时可以覆盖这个挂载设置。比如:
docker run -d -v mydata:/data xxxx
在这行命令中,就使用了 `mydata` 这个命名卷挂载到了 `/data` 这个位置,替代了 `Dockerfile` 中定义的匿名卷的挂载配置。<br />把mysql数据持久化到本地:<br />使用相同 volum 名即可访问这里的数据```powershelldocker run --name qj-mysql -v mysql:/var/lib/mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=true mysql
另一种数据持久化的方式:
Bind Mouting:文件或目录映射
docker run -v /home/xxx:/root/xxx ....
EXPOSE 声明端口
格式为 EXPOSE <端口1> [<端口2>...]
EXPOSE指令是声明运行时容器提供服务端口,这只是一个声明,在运行时并不会因为这个声明应用就会开启这个端口的服务。在 Dockerfile 中写入这样的声明有两个好处,一个是帮助镜像使用者理解这个镜像服务的守护端口,以方便配置映射;另一个用处则是在运行时使用随机端口映射时,也就是docker run -P时,会自动随机映射EXPOSE的端口。- 要将
EXPOSE和在运行时使用-p <宿主端口>:<容器端口>区分开来。-p,是映射宿主端口和容器端口,换句话说,就是将容器的对应端口服务公开给外界访问,而EXPOSE仅仅是声明容器打算使用什么端口而已,并不会自动在宿主进行端口映射。
USER 指定当前用户
格式:USER <用户名>[:<用户组>]
USER指令和WORKDIR相似,都是改变环境状态并影响以后的层。WORKDIR是改变工作目录,USER则是改变之后层的执行RUN,CMD以及ENTRYPOINT这类命令的身份。当然,和
WORKDIR一样,USER只是帮助你切换到指定用户而已,这个用户必须是事先建立好的,否则无法切换。RUN groupadd -r redis && useradd -r -g redis redisUSER redisRUN [ "redis-server" ]
如果以
root执行的脚本,在执行期间希望改变身份,比如希望以某个已经建立好的用户来运行某个服务进程,不要使用su或者sudo,这些都需要比较麻烦的配置,而且在 TTY 缺失的环境下经常出错。建议使用[gosu](https://github.com/tianon/gosu)。# 建立 redis 用户,并使用 gosu 换另一个用户执行命令RUN groupadd -r redis && useradd -r -g redis redis# 下载 gosuRUN wget -O /usr/local/bin/gosu "https://github.com/tianon/gosu/releases/download/1.7/gosu-amd64" \&& chmod +x /usr/local/bin/gosu \&& gosu nobody true# 设置 CMD,并以另外的用户执行CMD [ "exec", "gosu", "redis", "redis-server" ]
