В чем смысл WORKDIR в Dockerfile?

114

Я изучаю Докер. Я много раз видел, что Dockerfileесть WORKDIRкоманда:

FROM node:latest
RUN mkdir -p /usr/src/app
WORKDIR /usr/src/app
COPY package.json /usr/src/app/
RUN npm install
COPY . /usr/src/app
EXPOSE 3000
CMD [ “npm”, “start” ] 

Разве я не могу просто опустить WORKDIRи оставить Copyсвой Dockerfileв корне моего проекта? Каковы недостатки использования этого подхода?

Le Garcon
источник
Во время сборки вы меняете каталог наWORKDIR
Ultraviolet
1
@Ultraviolet, пожалуйста, объясните это. Я не совсем понял суть
Le garcon

Ответы:

124

По документации :

Инструкция WORKDIR устанавливает рабочий каталог для любых инструкций RUN, CMD, ENTRYPOINT, COPY и ADD, которые следуют за ней в Dockerfile. Если WORKDIR не существует, он будет создан, даже если он не будет использоваться в последующих инструкциях Dockerfile.

Кроме того, в лучших практиках Docker рекомендуется использовать его:

... вам следует использовать WORKDIR вместо множества инструкций, таких как RUN cd… && do-something, которые трудно читать, устранять неполадки и поддерживать.

Я бы посоветовал оставить его себе.

Я думаю, вы можете реорганизовать свой Dockerfile примерно так:

FROM node:latest
WORKDIR /usr/src/app
COPY package.json .
RUN npm install
COPY . ./
EXPOSE 3000
CMD [ “npm”, “start” ] 
juanlumn
источник
2
@MarioGil Пожалуйста, ознакомьтесь с документацией COPY.
juanlumn
1
Когда я использую, FROM ubuntu as builderа затем последовательно использую изображение COPY, «знает» ли он, что я использовал WORKDIR в образе «построителя», или я должен предполагать, что нет (и использовать абсолютный путь)?
Alex 75,
Согласно документации Docker я бы сказал , что он сохраняет WORKDIRзначение , потому что это RAN инструкции в Dockerfile , прежде чем запустить COPYодин
juanlumn
Ваша RUN mkdirкоманда не нужна; т.е. эту строку можно удалить. Согласно документации, «Если WORKDIR не существует, он будет создан, даже если он не будет использоваться в последующих инструкциях Dockerfile». - docs.docker.com/engine/reference/builder/#workdir
Purplejacket
@Purplejacket, это правильно, я
обновлю
61

Вам не нужно

RUN mkdir -p /usr/src/app

Он будет создан автоматически, когда вы укажете свой WORKDIR

FROM node:latest
WORKDIR /usr/src/app
COPY package.json .
RUN npm install
COPY . ./
EXPOSE 3000
CMD [ “npm”, “start” ] 
Сэмюэл Дэйр
источник
4
Однако иногда RUN mkdir необходим, потому что WORKDIR не уважает USER при создании каталогов - github.com/moby/moby/issues/20295
Joe Bowbeer
24
Мне нравится, что вы указали, что WORKDIR создаст папку автоматически.
GingerBeer
33

Вы можете думать об этом WORKDIRкак о cdвнутри контейнера (он влияет на команды, которые появляются позже в Dockerfile, например, на RUNкоманду). Если вы удалили WORKDIRв своем примере выше, RUN npm installэто не сработает, потому что вы не будете в /usr/src/appкаталоге внутри вашего контейнера.

Я не понимаю, как это связано с тем, куда вы помещаете свой Dockerfile (поскольку местоположение вашего Dockerfile на хост-машине не имеет ничего общего с pwd внутри контейнера). Вы можете разместить Dockerfile в любом месте вашего проекта. Однако первый аргумент для COPY- это относительный путь, поэтому, если вы переместите свой Dockerfile, вам может потребоваться обновить эти COPYкоманды.

Мкасберг
источник
3
Если WORKDIRдобавить подобное cd, не будут ли у двух COPYв исходном примере один и тот же источник и место назначения?
Йонас Розенквист,
5
Нет. WORKDIRВлияет на рабочий каталог внутри контейнера . В исходном примере первые COPYкопии с package.json хоста (относительный путь к Dockerfile) /usr/src/app/package.json в контейнер . Фактически, это WORKDIRне влияет на эту конкретную команду, потому что место назначения (внутри контейнера) не использует относительный путь (путь начинается с /).
mkasberg
@mkasberg Если WORKDIRдействует как cd. Так эквивалентны ли 2 фрагмента ниже? WORKDIR /usr/src/app COPY package.json /usr/src/app/и WORKDIR /usr/src/app COPY package.json . спасибо
kcatstack
1
Да, они эквивалентны.
mkasberg
1

Перед применением WORKDIR. Здесь WORKDIR находится не в том месте и используется неразумно.

FROM microsoft/aspnetcore:2
COPY --from=build-env /publish /publish
WORKDIR /publish
ENTRYPOINT ["dotnet", "/publish/api.dll"]

Мы исправили приведенный выше код, чтобы поместить WORKDIR в нужное место, и оптимизировали следующие операторы, удалив /Publish

FROM microsoft/aspnetcore:2
WORKDIR /publish
COPY --from=build-env /publish .
ENTRYPOINT ["dotnet", "/api.dll"]
Синие облака
источник
1
У вас не должно быть косой черты перед api.dll, так как это приведет его к корню контейнера
Тимоти c
1

Остерегайтесь использования vars в качестве имени целевого каталога для WORKDIR- это может привести к фатальной ошибке «ничего не может нормализовать». IMO, также стоит отметить, что он WORKDIRведет себя так же, как и mkdir -p <path>все элементы пути, если они еще не существуют.

ОБНОВЛЕНИЕ: я столкнулся с проблемой, связанной с переменной (упомянутой выше), при запуске многоэтапной сборки - теперь кажется, что использование переменной нормально - если она (переменная) находится «в области действия», например, в следующем примере, вторая WORKDIRссылка не работает ...

FROM <some image>
ENV varname varval
WORKDIR $varname

FROM <some other image>
WORKDIR $varname

тогда как он преуспевает в этом ...

FROM <some image>
ENV varname varval
WORKDIR $varname

FROM <some other image>
ENV varname varval
WORKDIR $varname

.oO ( Возможно, это в документации, и я это пропустил )

Дэвид Пойнтон
источник
0

Будьте осторожны при установке, WORKDIRпоскольку это может повлиять на поток непрерывной интеграции. Например, установка его на /home/circleci/projectвызовет ошибку, что-то вроде .sshили того, что удаленный circleci делает во время установки.

собственное поле
источник