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.