Остановите и удалите контейнер докеров, если он запущен

81

Я хочу прагматично остановить и удалить контейнер докера, если он запущен. Это для сценария сборки.

Возьмем следующий пример. Как мне остановить и удалить докер-контейнер rabbitmq, как показано в столбце NAMES в сценарии bash?

docker ps

CONTAINER ID        IMAGE             COMMAND                  CREATED             STATUS              PORTS                   NAMES
9909a5e2856f        rabbitmq-image   "/docker-entrypoint.s"   11 minutes ago      Up 11 minutes       0.0.0.0:5672->5672/tcp, rabbitmq
8990dd1fe503        redis-image      "/entrypoint.sh redis"   6 weeks ago         Up 4 days           0.0.0.0:32770->6379/tcp redis
etc 

Следующая команда удалит контейнер и сделает то, что я хочу сделать.

docker stop rabbitmq && docker rm -f rabbitmq

Однако он объединяет это в сценарий, который я хотел бы знать? Думаю, это будет выглядеть примерно так.

#!/bin/bash

if [ /*docker ps check some value */ ]; then
   docker stop rabbitmq && docker rm -f rabbitmq
fi
Robbo_UK
источник

Ответы:

189

Как вы, наверное, заметили, docker stopа также docker rmвыход с кодом состояния, указывающим на сбой, если контейнер не существует или не запущен. Это приводит к сбою сборки.

Если вы можете справиться с сообщениями об ошибках в журнале сборки, вы можете сделать этот небольшой трюк, чтобы предотвратить сбой команды оболочки:

docker stop rabbitmq || true && docker rm rabbitmq || true

В случае сбоя одной из команд docker trueвызывается, который всегда завершается с кодом состояния, указывающим на успех.

Йоханнес Бароп
источник
Сообщение об ошибке, что контейнер не существует, по-прежнему возвращается, но код состояния указывает на успех. Мой бамбуковый конвейер работает без сбоев, поэтому он работает для меня.
shamanSK
3
Это будет сгенерировано, Error response from daemon: no such id: rabbitmqкогда контейнер уже удален.
anubhava 02
3
@anubhava: Верно. Как уже упоминалось в ответе: «Уловка» состоит в том, чтобы просто игнорировать возвращаемое значение команд докеров. Если вам нужно «чистое» решение без сообщений об ошибках, вам нужно проверить, присутствует ли контейнер.
Йоханнес Бароп 08
Для меня эта команда больше не работает, раньше работала, а теперь нет ... Такое бывает с кем-нибудь еще?
joudaon
37

У меня аналогичная проблема, но мне не понравился принятый ответ, поскольку он подавляет все ошибки команд, а не только ошибку «не найдено».

Однако docker ps -q --filter "name=rabbitmq"выводит результат только в том случае, если контейнер с таким именем действительно существует, поэтому, вдохновленный Test, если команда выводит пустую строку, которую я придумал:

docker ps -q --filter "name=rabbitmq" | grep -q . && docker stop rabbitmq && docker rm -fv rabbitmq

Следующая команда также полезна для тестирования определений фильтров:

docker ps -q --filter "name=rabbitmq" | grep -q . && echo Found || echo Not Found

Мой фактический вариант использования заключался в определении пары задач Ansible, которые удаляли все существующие в настоящее время контейнеры (независимо от того, запущены они или нет) из списка имен, сгенерированных в более ранней задаче:

- name: Check for containers that actually exist
  shell: 'docker ps -aq --filter "name={{ item }}"'
  with_items:
    - '{{ previous_command.stdout_lines }}'
  register: found_containers

- name: Remove the containers found by the above command
  shell: 'docker stop {{ item.item }} && docker rm -fv {{ item.item }}'
  with_items: '{{ found_containers.results }}'
  when: item.stdout
Ncoghlan
источник
5
Считаю полезным пользоваться docker ps -aq .... Эта -aопция означает, что он также найдет контейнеры, которые существуют, но в настоящее время не работают.
Питер Блумфилд
Отличный ответ, я просто использовал это в сценарии bash, позвольте ему перезапустить контейнер докера, если он уже запущен. Также используется ps -aq, как предлагает @PeterBloomfield.
Psiloc
если контейнер не найден, grep -q .также имеет ненулевой существующий код и, следовательно, всю вашу строку. Так что это все равно приводит к ошибке. Что заставило бы меня добавить || echo 'not found'или что-то в этом роде. И здесь нет большой разницы с принятым ответом
Felix B.
Чтобы фильтр не влиял на общий код возврата команды, вы можете реорганизовать его как оператор if:if docker ps -q --filter "name=rabbitmq" | grep -q .; then docker stop rabbitmq && docker rm -fv rabbitmq; fi
ncoghlan
23

Уже давно пользуюсь докером. Это мой предпочтительный способ остановки и удаления контейнера докеров. Принцип истины нужен для того, чтобы всегда обеспечивать успех. Без него все сценарии bash завершились бы и выдали ошибку, если бы имя контейнера не существовало.

docker rm -f container_name || true
Robbo_UK
источник
11
docker rm -f container_name &>/dev/null && echo 'Removed old container'
filimonov
док-контейнер rm | Документация Docker : принудительное удаление работающего контейнера (использует SIGKILL) .
Джейсон Лоу
13

Вы можете использовать:

app="rabbitmq"
if docker ps | awk -v app="$app" 'NR > 1 && $NF == app{ret=1; exit} END{exit !ret}'; then
  docker stop "$app" && docker rm -f "$app"
fi
  • awkкоманда получает appпеременную командной строки из переменной BASH$app
  • NR>1пропускает первую строку заголовка из docker psкоманды.
  • $(NF) == appСравнить последний столбец NAMESравен переменной приложения или нет
анубхава
источник
привет, спасибо за ответ, он работает. Не могли бы вы немного рассказать, чем он занимается. Если у меня есть app = '' some-rabbitmq ', ему не нравится шумиха?
Robbo_UK
Он должен работать с дефисами, в ответ я добавляю пояснения.
анубхава
1
слишком сложное решение. Второй ответ лучше
Scoota P
2
Это наиболее полный ответ. проблема с добавлением `|| true` заключается в том, что все еще существует сценарий bash с ошибкой докера. проверив список контейнеров докеров для контейнера, вы гарантированно вызовете только stop и rm, если они существуют, исключив ошибки и позволив скрипту выполнить все необходимые команды контейнера.
AsAP_Sherb 05
7
# Stop and remove containers with names like "rabbitmq" and "rabbitmq123" if they exist
CONTAINER_NAME="rabbitmq"
OLD="$(docker ps --all --quiet --filter=name="$CONTAINER_NAME")"
if [ -n "$OLD" ]; then
  docker stop $OLD && docker rm $OLD
fi
iver56
источник
4

Я предлагаю это заклинание в bash:

( docker stop $CONTAINER > /dev/null && echo Stopped container $CONTAINER && \
  docker rm $CONTAINER ) 2>/dev/null || true

Он всегда завершается с 0, не жалуется, если контейнер не запущен, и печатает, Stopped container $CONTAINERесли он действительно остановился.

DS.
источник
Чистый срыв, который "терпит неудачу" молча, если контейнер не найден, но не молча stop/ rmконтейнер, если он найден.
emmagras 03
2

Общая форма, основанная на некоторых ответах здесь:

docker rm -f container_name > /dev/null 2>&1 && echo 'removed container' || echo 'nothing to remove'

Кристоф Шранц
источник
0

Скопируйте этот код в свой script.sh, если хотите, stopи removeвсе

#!/bin/sh
ids=$(docker ps -a -q)
for id in $ids
do
  echo "$id"
  docker stop $id && docker rm $id
done
Алекс Монтойя
источник
0

Если вы не удалите остановленные контейнеры, другой простой способ решить эту проблему - положиться на него docker ps -a, который всегда будет возвращать этот идентификатор контейнера. Затем выполнение docker stopв этом остановленном контейнере идемпотентно просто ничего не сделает:

docker stop $(docker ps -a --filter name= rabbitmq -q )

Свенд
источник
-2

чтобы сначала остановить все контейнеры, вы должны остановить все контейнеры с помощью

docker kill $(docker ps -q)

и удалить все контейнеры

docker rm $(docker ps -a -q)

и если вы хотите удалить все изображения, это команда

docker rmi $(docker images -q)
Густаво Муньос Валенсуэла
источник