Issues deploying Swarm Service with a command

Hello,

I am currently in the process of moving from local docker containers to Docker Swarm. I use Ansible docker modules to deploy my containers and this has worked great. I am now using the Docker Swarm Ansible modules, namely - community.docker.docker_swarm_service module. This is working fine until I need to include a command for the service. For example, with Portainer - this will work:

- name: Create portainer service
  community.docker.docker_swarm_service:
    name: '{{ portainer_defaults_name }}'
    image: '{{ portainer_defaults_image_repo }}:{{ portainer_defaults_image_tag }}'
    networks:
      - name: '{{ network_overlay }}'
    env:
      PUID: '{{ puid }}'
      PGID: '{{ pgid }}'
      TZ: '{{ timezone }}'
    labels: '{{ portainer_defaults_labels }}'
    publish:
      - target_port: '{{ portainer_defaults_ports_http_cont }}'
        published_port: '{{ portainer_defaults_ports_http_host }}'
        protocol: tcp
        mode: ingress
      - target_port: '{{ portainer_defaults_ports_tcp_tunnel_cont }}'
        published_port: '{{ portainer_defaults_ports_tcp_tunnel_host }}'
        protocol: tcp
        mode: ingress
      - target_port: '{{ portainer_defaults_ports_webui_cont }}'
        published_port: '{{ portainer_defaults_ports_webui_host }}'
        protocol: tcp
        mode: ingress
    mounts:
      - source: '{{ portainer_defaults_location }}'
        target: /data
        type: bind
    restart_config:
      condition: '{{ portainer_defaults_restart_policy }}'
      delay: 5s
      max_attempts: 3
      window: 120s
    mode: replicated
    replicas: 1
    placement:
      constraints: [node.role == manager]

However, if I include the command to connect to the Portainer agent:

- name: Create portainer service
  community.docker.docker_swarm_service:
    name: '{{ portainer_defaults_name }}'
    image: '{{ portainer_defaults_image_repo }}:{{ portainer_defaults_image_tag }}'
    networks:
      - name: '{{ network_overlay }}'
    command: '-H tcp://tasks.{{ portainer_agent_defaults_name }}:9001 --tlsskipverify'
    env:
      PUID: '{{ puid }}'
      PGID: '{{ pgid }}'
      TZ: '{{ timezone }}'
    labels: '{{ portainer_defaults_labels }}'
    publish:
      - target_port: '{{ portainer_defaults_ports_http_cont }}'
        published_port: '{{ portainer_defaults_ports_http_host }}'
        protocol: tcp
        mode: ingress
      - target_port: '{{ portainer_defaults_ports_tcp_tunnel_cont }}'
        published_port: '{{ portainer_defaults_ports_tcp_tunnel_host }}'
        protocol: tcp
        mode: ingress
      - target_port: '{{ portainer_defaults_ports_webui_cont }}'
        published_port: '{{ portainer_defaults_ports_webui_host }}'
        protocol: tcp
        mode: ingress
    mounts:
      - source: '{{ portainer_defaults_location }}'
        target: /data
        type: bind
    restart_config:
      condition: '{{ portainer_defaults_restart_policy }}'
      delay: 5s
      max_attempts: 3
      window: 120s
    mode: replicated
    replicas: 1
    placement:
      constraints: [node.role == manager]

Portainer will not successfully deploy, and upon checking with ‘docker ps -a’, it will show portainer listed 4 times with the status of ‘created’. At first I thought this was a Portainer issue - but it is occurring with every service where I try to include a command. I had none of these issues with the container modules, previously.

Is there a difference in how the ‘command:’ component works for services, as opposed to containers? Is this not a Docker issue at all, and just a quirk with the Ansible docker service module? Any ideas here?

OS Version/build

Client: Docker Engine - Community
Version: 27.5.1
Context: default
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.20.0
Path: /usr/libexec/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.32.4
Path: /usr/libexec/docker/cli-plugins/docker-compose
Swarm: active
Is Manager: true
Kernel Version: 6.1.0-28-amd64
Operating System: Debian GNU/Linux 12 (bookworm)
OSType: linux
Architecture: x86_64

ansible [core 2.17.8]
config file = /etc/ansible/ansible.cfg
configured module search path = [‘/root/.ansible/plugins/modules’, ‘/usr/share/ansible/plugins/modules’]
ansible python module location = /usr/lib/python3/dist-packages/ansible
ansible collection location = /root/.ansible/collections:/usr/share/ansible/collections
executable location = /usr/bin/ansible
python version = 3.11.2 (main, Nov 30 2024, 21:22:50) [GCC 12.2.0] (/usr/bin/python3)
jinja version = 3.1.2
libyaml = True

Steps to reproduce
  1. Deploy service without a command using the Ansible docker swarm service module
  2. Try deploying the same service with a command using the Ansible docker swarm service module

To determine whether it’s a problem with the Ansible module or docker swarm itself, you should render the compose file and deploy it manually with docker stack deploy

  • If the problem exists on the manually deployed compose file as well, I would suggest raising an issue in the Moby GitHub project: https://github.com/moby/moby/issues.
  • If the problem doesn’t exist on the manual deployment, you need to find the primary support channel for the Ansible module and raise your issue there.

Upate: my bad, I missed that you are not deploying a stack. You directly schedule swarm services. You would create the command mnaully run the command in the terminal to check whether it works, instead of rendering a compose file and deploying it with stack deploy.

Hi meyay, thanks for the prompt reply. I’ve formatted it for a service create terminal command:

docker service create \
  --name portainer \
  --replicas 1 \
  --constraint node.role==manager \
  --network overlay \
  --env PUID=1000 \
  --env PGID=1000 \
  --env TZ=Australia/Melbourne \
  --publish published=9000,target=9000,protocol=tcp,mode=ingress \
  --publish published=8000,target=8000,protocol=tcp,mode=ingress \
  --publish published=9443,target=9443,protocol=tcp,mode=ingress \
  --mount type=bind,source=/opt/portainer,destination=/data \
  --restart-condition on-failure \
  portainer/portainer-ce

It loads fine and the webui is accessible. But I’m not even sure how to add the command via this method? I didn’t see a command section in the docker service create | Docker Docs page. I’m just trying to add the ‘-H tcp://tasks.agent:9001 --tlsskipverify’ command.

I’ve used a similar command with the Portainer (non-swarm) container in the past before without issue:

- name: Create portainer container
  community.docker.docker_container:
    name: '{{ portainer_defaults_name }}'
    image: '{{ portainer_defaults_image_repo }}:{{ portainer_defaults_image_tag }}'
    networks:
      - name: '{{ network_backend }}'
      - name: '{{ traefik_network }}'
    command: '-H {{ socket_proxy_endpoint }}'
    env:
      PUID: '{{ puid }}'
      PGID: '{{ pgid }}'
      TZ: '{{ timezone }}'
    labels: '{{ portainer_defaults_labels }}'
    ports:
      - '{{ portainer_defaults_ports_http_host }}:{{ portainer_defaults_ports_http_cont }}'
      - '{{ portainer_defaults_ports_tcp_tunnel_host }}:{{ portainer_defaults_ports_tcp_tunnel_cont }}'
      - '{{ portainer_defaults_ports_webui_host }}:{{ portainer_defaults_ports_webui_cont }}'
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - '{{ portainer_defaults_location }}:/data'
    restart_policy: '{{ portainer_defaults_restart_policy }}'

Thanks

It should be docker service create [OPTIONS] IMAGE [COMMAND] [ARG...].

docker service create \
  --name portainer \
  --replicas 1 \
  --constraint node.role==manager \
  --network overlay \
  --env PUID=1000 \
  --env PGID=1000 \
  --env TZ=Australia/Melbourne \
  --publish published=9000,target=9000,protocol=tcp,mode=ingress \
  --publish published=8000,target=8000,protocol=tcp,mode=ingress \
  --publish published=9443,target=9443,protocol=tcp,mode=ingress \
  --mount type=bind,source=/opt/portainer,destination=/data \
  --restart-condition on-failure \
  portainer/portainer-ce \
  -H tcp://tasks.agent:9001 --tlsskipverify

This should work as long as the agent service uses --name agent --network overlay.

1 Like

Worked fine, thank you. I’ll go and suss out a support channel for this Ansible module. Otherwise, I’ll just put the services in compose files and use the community.docker.docker_compose_v2 module.

This one should be a better fit, as it deploys swarm services, instead of plain containers on a single node:
https://docs.ansible.com/ansible/latest/collections/community/docker/docker_stack_module.html

Thanks. But yeah, I want to avoid Compose/Swarm stacks altogether. I have 1 Ansible role per app with everything required (apart from group_vars/host_vars/pre_tasks) inside the role. Hence why I deploy as individual apps (services). I simply uncomment a line in the Ansible playbook to deploy app/apps.Pre_tasks exist to set up all the required shared volumes, overlay network, themes, and others during the playbook run.


pre_task for Swarm (on manager), for example:

- name: Check if Swarm is already initialised
  ansible.builtin.command:
    cmd: docker info
  register: swarm_check
  changed_when: false

- name: Initialise Docker Swarm
  ansible.builtin.command:
    cmd: docker swarm init --advertise-addr {{ pvr_machine_address }}
  when: '"Swarm: inactive" in swarm_check.stdout'
  register: swarm_init
  changed_when: swarm_init.rc == 0

- name: Join manager to Swarm
  ansible.builtin.command:
    cmd: docker swarm join --token {{ swarm_manager_token }} {{ pvr_machine_address }}:2377
  when: '"Is Manager: false" in swarm_check.stdout'
  register: swarm_join
  changed_when: '"This node joined a swarm as a manager" in swarm_join.stdout'

I’ve reached out for support on the Ansible community github, so hopefully I can get it sorted.

1 Like