使用 WORKDIR 指令可以来指定工作目录(或者称为当前目录),以后各层的当前目录就被改为指定的目录,如该目录不存在,WORKDIR 会帮你建立目录。
WORKDIR指令的格式为:
WORKDIR <工作目录路径>
一些初学者常犯的错误是把 Dockerfile 等同于 Shell 脚本来书写,这种错误的理解还可能会导致出现下面这样的错误:
# Dockerfile中的一层 RUN cd /app # Dockerfile中的另一层 RUN echo "hello" > world.txt
如果将这个 Dockerfile 进行构建镜像运行后,会发现找不到 /app/world.txt 文件,或者其内容不是 hello。原因其实很简单,在 Shell 中,连续两行是同一个进程执行环境,因此前一个命令修改的内存状态,会直接影响后一个命令;而在 Dockerfile 中,这两行 RUN 命令的执行环境根本不同,是两个完全不同的容器。这就是对 Dockerfile 构建分层存储的概念不了解所导致的错误。
每一个 RUN 都是启动一个容器、执行命令、然后提交存储层文件变更。第一层 RUN cd /app 的执行仅仅是当前进程的工作目录变更,一个内存上的变化而已,其结果不会造成任何文件变更。而到第二层的时候,启动的是一个全新的容器,跟第一层的容器更完全没关系,自然不可能继承前一层构建过程中的内存变化。
因此如果需要改变以后各层的工作目录的位置,那么应该使用 WORKDIR 指令。
实例:下面是 Docker hub 上面 redis 的 Dockerfile 文件的部分内容。
FROM debian:buster-slim # add our user and group first to make sure their IDs get assigned consistently, regardless of whatever dependencies get added # 创建 redis 组和 redis 用户 RUN groupadd -r redis && useradd -r -g redis redis # 省略了其他... # 创建 /data 目录 RUN mkdir /data && chown redis:redis /data # 设置 /data 目录为容器数据卷目录 VOLUME /data # 将当前工作目录切换到 /data 目录 # 如果你进入到 redis 容器,当前目录就是 /data 目录 WORKDIR /data COPY docker-entrypoint.sh /usr/local/bin/ ENTRYPOINT ["docker-entrypoint.sh"] # 暴露端口 EXPOSE 6379 # 启动 redis 服务 CMD ["redis-server"]