使用Ansible停止所有现有的Docker容器

6

我正在创建一个Ansible角色,用于对托管不同Docker容器的虚拟机进行操作系统和配置更新。

在角色开始时,如果有Docker容器存在,我想停止所有Docker容器。我发现了这个线程,但它有点老旧,所以我正在尝试提出一个新问题,请问可以吗?

最简单的方法是这样的:

- name: Stop docker containers
  shell: |
    docker stop $(docker ps -aq)

很遗憾,当主机没有Docker容器时我会收到一个错误。使用 ignore_errors: yes 的方式可能不是一个好方法,所以我尝试了另外一种方式。

- name: Get info on docker host and list images
  docker_host_info:
    containers: yes
  register: containers_to_stop

- name: Stop docker containers
  shell: |
    docker stop $(docker ps -aq)
  when: containers_to_stop.containers != 0

但是仍然与第一部分相同。当主机没有Docker容器时,我会遇到错误。

因此,如链接的线程所述,我正在尝试像这样使用docker_container模块:

- name: Get info on docker host and list images
  docker_host_info:
    containers: yes
  register: containers_to_stop

- name: Stop running docker containers
  docker_container:
    name: '{{ item.Names }}'
    image: '{{ item.Image }}'
    state: stopped
  loop: '{{ containers_to_stop.containers }}'

很遗憾,`docker_host_info`模块不能正常工作,因为我的所有Docker容器名称都以 `/` 开头。我已经为大家调试好了:
``` failed: [app01] (item={u'Status': u'Up 12 minutes', u'Command': u'./replace_props_and_start.sh', u'Names': [u'/image-name'], u'Created': 1588071879, u'Image': u'image-name', u'Ports': [{u'IP': u'0.0.0.0', u'Type': u'tcp', u'PublicPort': 8091, u'PrivatePort': 80}], u'Id': u'ad5b0b3d6d623e2ac1d0a2ead9fbbf8a5ce5bca58492410a31035fd160de149a'}) => {"ansible_loop_var": "item", "changed": false, "item": {"Command": "./replace_props_and_start.sh", "Created": 1588071879, "Id": "ad5b0b3d6d623e2ac1d0a2ead9fbbf8a5ce5bca58492410a31035fd160de149a", "Image": "image-name", "Names": ["/image-name"], "Ports": [{"IP": "0.0.0.0", "PrivatePort": 80, "PublicPort": 8091, "Type": "tcp"}], "Status": "Up 12 minutes"}, "msg": "Error creating container: 400 Client Error: Bad Request ("Invalid container name (['/image-name']), only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed")"} ```
所以我的容器名为 `/image-name` 而不是 Ansible 为我创建的目录中的 `image-name`。因此错误是明显的,但如何解决呢?
也许这是一个模块问题,我需要联系 Ansible 的开发人员吗?
感谢和问候,
2个回答

14
以下代码在我的家用电脑上完美地运行。根据docker_container模块文档中的说明,您可以使用容器的短或长ID字符串来标识正在运行的容器作为name。长的Id大写的I)在docker_host_infocontainers列表中的输出中可用。
---
- hosts: localhost
  gather_facts: false

  tasks:
    - name: Get running containers
      docker_host_info:
        containers: yes
      register: docker_info

    - name: Stop running containers
      docker_container:
        name: "{{ item }}"
        state: stopped
      loop: "{{ docker_info.containers | map(attribute='Id') | list }}"

演示运行:

# Show we have no running containers
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

# Spawn some test containers for demo
$ for i in $(seq 1 5); do docker run -d --rm centos:8 bash -c "while true; do sleep 1; done"; done
a492efab9ec7dace786b610f3b93c335fbb84f041f7954557e971a5cbb0905a0
8cd55145c7cb267b37d2af346571797e283cac75777c531caeb88df7ec2e57d6
f009140260f5daee6efc6fba8dd8f73f9c83e31e7f1e09d48681b0738bc86f50
e7af30b1ade41fbc65b3db8e4146497ee736065103af769331d9df4e8e39b131
643e6831b958e0410bb148aeaec29dfeec6fa2773af5fb286ad74ab0368f2e50

# Make sure containers are running
$ docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS               NAMES
643e6831b958        centos:8            "bash -c 'while true…"   9 seconds ago       Up 7 seconds                            quizzical_allen
e7af30b1ade4        centos:8            "bash -c 'while true…"   10 seconds ago      Up 9 seconds                            frosty_khayyam
f009140260f5        centos:8            "bash -c 'while true…"   12 seconds ago      Up 10 seconds                           ecstatic_ramanujan
8cd55145c7cb        centos:8            "bash -c 'while true…"   14 seconds ago      Up 12 seconds                           focused_sammet
a492efab9ec7        centos:8            "bash -c 'while true…"   15 seconds ago      Up 13 seconds                           agitated_jones

# Stop containers with playbook
$ ansible-playbook test.yml 

PLAY [localhost] **************************************************************************************************************************************************************************************************

TASK [Get running containers] *************************************************************************************************************************************************************************************
ok: [localhost]

TASK [Stop running containers] ************************************************************************************************************************************************************************************
changed: [localhost] => (item=643e6831b958e0410bb148aeaec29dfeec6fa2773af5fb286ad74ab0368f2e50)
changed: [localhost] => (item=e7af30b1ade41fbc65b3db8e4146497ee736065103af769331d9df4e8e39b131)
changed: [localhost] => (item=f009140260f5daee6efc6fba8dd8f73f9c83e31e7f1e09d48681b0738bc86f50)
changed: [localhost] => (item=8cd55145c7cb267b37d2af346571797e283cac75777c531caeb88df7ec2e57d6)
changed: [localhost] => (item=a492efab9ec7dace786b610f3b93c335fbb84f041f7954557e971a5cbb0905a0)

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost                  : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

# Verify containers are stopped
$ docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES

# Check that playbook succeeds without containers running
$ ansible-playbook test.yml 

PLAY [localhost] **************************************************************************************************************************************************************************************************

TASK [Get running containers] *************************************************************************************************************************************************************************************
ok: [localhost]

TASK [Stop running containers] ************************************************************************************************************************************************************************************

PLAY RECAP ********************************************************************************************************************************************************************************************************
localhost                  : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0   

好的,非常感谢您展示了解决问题的方法。我没有想到使用id,使用id会更明显地表示容器的名称。非常感谢,我明天也会尝试这个方法。 - gurbelunder
docker_host_info模块仅列出正在运行的容器,而不包含已退出的容器。 - wow qing
@wowqing 一个已经退出的容器不在运行,所以不需要停止。 - Zeitounator
@wowqing 除了你的评论在这个上下文中与主题无关之外,请注意最近模块版本已经不再如此。请参阅https://docs.ansible.com/ansible/latest/collections/community/docker/docker_host_info_module.html#parameter-containers_all - Zeitounator

4
很遗憾,“docker_host_info”模块的工作效果不佳,因为我的所有Docker容器名称都将以“/”开头。 问题不在这里,问题在于由“docker_host_info”返回的值中,“names”是一个列表。仔细看一下错误信息:
400 Client Error: Bad Request (\"Invalid container name (['/image-name']), only [a-zA-Z0-9][a-zA-Z0-9_.-] are allowed\")"}

你看如何将['/image-name']作为容器名称?你需要这样做:

- name: Get info on docker host and list images
  docker_host_info:
    containers: yes
  register: containers_to_stop

- name: Stop running docker containers
  docker_container:
    name: '{{ item.Names.0[1:] }}'
    image: '{{ item.Image }}'
    state: stopped
  loop: '{{ containers_to_stop.containers }}'

在上述代码中,我们正在请求Names列表中的第一个元素(item.Names.0),然后选择除第一个字符外的所有内容(因此/something变成了something)。

使用/image-name只是我复制粘贴时的错误。很抱歉,但这种情况经常发生在一个辛苦工作周的星期五下午。你解决问题的方式非常好,明天我会尝试这样做。非常感谢你的解释。 - gurbelunder

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接