bash: <EDITOR_NAME>: command not found
- если вы когда-либо сталкивались с этим сообщением оболочки после входа в контейнер докеров с намерением отредактировать текстовый файл, вам следует прочитать это сообщение.
В чем проблема?
Docker должен быть легковесным (выполнять одну работу и делать это хорошо), поэтому контейнеры докеров сокращены до минимума - в них установлены только необходимые пакеты, чтобы играть требуемую роль в данной экосистеме проекта. С этой точки зрения установка любого редактора бессмысленна и вносит ненужные сложности. Итак, если вы подготовили Dockerfile
, построили образ и после запуска контейнера вам нужно отредактировать файл, вы можете быть удивлены:
~/ docker run -it openjdk:11 bash root@d0fb3a0b527c:/# vi Lol.java bash: vi: command not found root@d0fb3a0b527c:/#
Какие есть возможные решения?
# 1 Используйте объем
Давайте воспользуемся следующим Dockerfile
:
FROM openjdk:11 WORKDIR "/app"
Теперь создайте образ с помощью:
docker build -t lol .
И, наконец, запустите контейнер с прикрепленным volume
(volume
также можно создать с помощью команды docker volume create
):
docker run --rm -it --name=lol -v $PWD/app-vol:/app lol bash
$PWD/app-vol
папка будет создана автоматически, если она не существует. Теперь, если вы попытаетесь перечислить все файлы в каталоге /app
, вы получите пустой результат:
~/ docker run --rm -it --name=lol -v $PWD/app-vol:/app lol bash root@4b72fbabb0af:/app# ls
Перейдите в каталог $PWD/app-vol
из другого терминала и создайте файл Lol.java
. Если вы попытаетесь еще раз перечислить файлы в запущенном контейнере, вы увидите, что там находится только что созданный Lol.java
файл:
root@4b72fbabb0af:/app# ls Lol.java root@4b72fbabb0af:/app# cat Lol.java public class Lol { } root@4b72fbabb0af:/app#
Как видите, команда cat
работает, поэтому вы можете хотя бы просмотреть содержимое файла.
# 2 Установить редактор
Если использование volume
не является вариантом, вы можете установить редактор, который вам нужен, в работающем контейнере. Сначала запустите контейнер (на этот раз монтировать volume
не нужно):
docker run --rm -it --name=lol lol bash
А затем установите редактор:
root@4b72fbabb0af:/app# apt-get update root@4b72fbabb0af:/app# apt-get -y install vim
Установка пакета в работающий контейнер - это то, что нужно делать случайно. Если вы будете делать это неоднократно, лучше добавить требуемый пакет в Dockerfile
:
FROM openjdk:11 RUN ["apt-get", "update"] RUN ["apt-get", "-y", "install", "vim"] WORKDIR "/app"
Кажется, что vim-tiny - легкая альтернатива, следовательно, лучший выбор для редактора в контейнере докера.
# 3 Скопируйте файл в работающий док-контейнер
Давайте запустим контейнер без установленного редактора (Dockerfile
из №1):
docker run --rm -it --name=lol lol bash
(опять же, volume
не требуется). Если вы попытаетесь скопировать ls
файлов в /app
папку, вы получите пустой результат. На этот раз мы будем использовать инструменты докера, чтобы скопировать файл в работающий контейнер. Итак, на хост-машине создайте файл Lol.java
и используйте следующую команду для копирования файла:
docker cp Lol.java lol:/app
где lol
представляет имя контейнера. Вместо имени контейнера при копировании файла также может использоваться его ID
. Файлы нельзя копировать напрямую между контейнерами. Итак, если есть необходимость скопировать файл из одного контейнера в другой, необходимо задействовать хост-машину.
Другой, очень похожий вариант - использовать командуdocker exec
в сочетании с cat
. Следующая команда также скопирует файлLol.java
в работающий контейнер:
docker exec -i lol sh -c 'cat > /app/Lol.java' < Lol.java
Где /app/Lol.java
представляет файл в контейнере докера, а Lol.java
- это существующий файл на хосте.
# 4 Используйте инструменты Linux
В докер-контейнере нет любимого (или даже какого-либо) редактора? Без проблем! Другие инструменты Linux, такие как sed
, awk
, echo
, cat
, cut
, уже есть и придут на помощь. С помощью некоторых из них, например sed
или awk
, вы можете редактировать файл на месте. Другие, такие как echo
, cat
, cut
в сочетании с мощным перенаправлением потока, могут использоваться для создания и последующего редактирования файлов. Как вы уже видели в предыдущих примерах, эти инструменты можно комбинировать с командой thedocker exec
, что делает их еще более надежными.
# 5 Используйте пульт vim (или другого редактора)
ВАЖНО: эта идея плохая по многим причинам (например, запуск нескольких процессов в контейнере докеров или разрешение другим пользователям использовать ssh в работающем контейнере через открытый порт номер 22). Я показываю это скорее как любопытство, чем как то, что вы должны использовать в повседневной работе. Давайте посмотрим на Dockerfile
, он немного изменился:
FROM openjdk:11 RUN ["apt-get", "update"] RUN ["apt-get", "install", "-y", "openssh-server"] RUN mkdir /var/run/sshd RUN echo 'root:lollol0' | chpasswd RUN sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin yes/' /etc/ssh/sshd_config RUN ["/etc/init.d/ssh", "start"] EXPOSE 22 WORKDIR "/app" CMD ["/usr/sbin/sshd", "-D"]
На этот раз, поскольку scp
будет использоваться для удаленного редактирования, нам нужно установить openssh-server
, открыть порт и, наконец, запустить его. После сборки контейнера с помощью следующей команды:
docker build -t lol .
запустите его с помощью следующей команды:
docker run --rm -p 2222:22 -d --name=lol lol
Теперь, когда контейнер запущен, вы можете редактировать Lol.java
файл с помощью следующей команды:
vim scp://root@localhost:2222//app/Lol.java
После подтверждения подключения и ввода пароля открывается vi
, и файл можно редактировать. Из-за этой проблемы запустите :set bt=acwrite
на экране vi
и продолжайте редактирование файла. После того, как вы закончите, сохраните и выйдите vi
, подтвердите паролем root, и все готово. Теперь запустите:
docker exec -it lol cat /app/Lol.java
чтобы подтвердить, что файл действительно был создан и сохранен.
Зачем мне это нужно?
На самом деле, вы этого не делаете;) Контейнеры Docker - это неизменяемые единицы работы, предназначенные для запуска одного конкретного процесса. Образы следует создавать и запускать без какого-либо дополнительного вмешательства. Более того, когда вы редактируете файл в работающем контейнере докера, вам необходимо убедиться, что все процессы, зависящие от редактируемого файла, были уведомлены об изменении. Если они не настроены для повторного развертывания при изменении конфигурации, их необходимо перезапустить вручную.
Редактирование файлов в контейнере докеров может быть полезно только во время разработки. Если вы не хотите или даже не хотите создавать образ, запустите его и убедитесь, что внесенное изменение привело к желаемому результату каждый раз, когда вы добавляете или удаляете что-то в Dockerfile
. Таким образом можно сэкономить время, но после этого избыточные пакеты, добавленные в образ, должны быть удалены.
Like this post and interested in learning more?
Follow us on Medium!
Need help with your Cassandra, Kafka or Scala/Java projects?
Just contact us here.