1

I'm trying to setup a mini ansible lab with docker containers. I have 1 Rockylinux based container as the ansible controller, and then (for right now) 1 Rockylinux based container as the host.

For ansible to work, it needs to be able to ssh into the host. I've installed opensh and configured for root login and I'm still getting connection refused whenever I try.

Any config that I'm missing?

my Dockerfiles are pretty basic. Here is the file for the ansible server:

FROM rockylinux:8.8
RUN dnf update -y && \
dnf install python38 -y && \ 
dnf install ansible-core -y &&\
dnf install openssh -y && \
dnf install openssh-server -y && \
dnf install vim -y && \
dnf install net-tools -y 
EXPOSE 22

the "host" server is the same except there isn't ansible-core installed

any reason why I can't ssh from one container to the other?

1
  • I don't see any entrypoint nor command launching the ssh daemon in your example hence how do you start it and make sure it is running prior to attempting to connect? Commented Oct 22, 2023 at 14:59

1 Answer 1

1

I'm not going to answer why you can't ssh to your container. There might be a dozen reasons which are impossible to guess with as little information provided in your question. Moreover, installing ssh inside a container is usually not a good idea anyway.

In fact, your assumption is wrong:

For Ansible to work, it needs to be able to ssh into the host

Actually, for Ansible "to work" i.e. to be able to control targets from a controller, you need a way to connect to the target. And even if ssh is the most common, it's not the only one. There's actually a specific connection plugin to connect to docker containers. The only real prerequisite is to have python inside the target.

There's also a dynamic inventory plugin that will collect all hosts running on a specific docker daemon.

If you really want to create a lab running entirely inside docker and with Ansible running in one of the containers, this is what I suggest as a basic scaffold. You can adapt to your exact needs

Project structure:

$ tree lab
lab
├── ansible-lab
│   ├── dummy_playbook.yml
│   └── inventories
│       └── default
│           └── hosts_docker.yml
├── compose.yml
└── images
    ├── controller
    │   └── Dockerfile
    └── target
        └── Dockerfile

6 directories, 5 files

I used the same base image as in your example. Here is images/controller/Dockerfile for the Ansible controller (side note: please refrain from installing Ansible through your package manager and use pip which is now the only officially supported method)

FROM rockylinux:8.8

WORKDIR /ansible-lab

RUN \
  dnf update -y && \
  dnf install -y dnf-plugins-core && \
  dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo && \
  dnf install -y python3.8 docker-ce-cli bash-completion && \
  python3 -m pip install --upgrade pip && \
  # Note: the docker python lib is required for docker connection and inventory plugins
  pip install ansible docker

This is the images/target/Dockerfile for the target host(s) we will manage

FROM rockylinux:8.8
RUN \
  dnf update -y && \
  dnf install -y python3.8

The compose.yml file puts everything together:

---
services:
  controller:
    image: rocky_ansible_controller:local
    build: images/controller
    container_name: lab-controller
    command: tail -f /dev/null
    volumes:
      # Access our ansible project which can be edited from the host if needed
      - ./ansible-lab:/ansible-lab
      # We mount the host docker socket so we can access docker containers
      # running there from the container with the docker cli
      - /var/run/docker.sock:/var/run/docker.sock

  host:
    image: rocky_ansible_target:local
    build: images/target
    command: tail -f /dev/null

Let's build and start our project with a single target host for now:

$ cd lab
$ docker compose build
[... lots of build output cut out ...]

$ docker compose up -d
[+] Running 3/3
 ✔ Network lab_default       Created    0.0s 
 ✔ Container lab-host-1      Started    1.0s 
 ✔ Container lab-controller  Started    1.1s 

Now here is our basic inventory ansible-lab/inventories/default/hosts_docker.yml using the docker-container inventory plugin (see link above- important: it must end with '_docker.yml' as explained in the documentation)

Note that the lab- prefix for containers is taken from the directory name holding the compose project. If your directory has an other name than lab, you'll have to adapt below or to set the project name yourself.

---
plugin: community.docker.docker_containers
docker_host: unix://var/run/docker.sock
connection_type: docker-cli
compose:
  # All host will be accessed through docker
  ansible_connection: docker
  # Don't use platform python but the one specifically installed
  ansible_python_interpreter: "'/usr/bin/python3'"
groups:
  # This creates a lab_hosts group for all hostnames starting with 'lab-host-'
  lab_hosts: inventory_hostname is match('^lab-host-.*$')

And here is the basic ansible-lab/dummy-playbook.yml which will simply install a new package on the hosts:

---
- name: Dummy playbook for lab
  hosts: lab_hosts

  tasks:
    - name: Install a package on host
      ansible.builtin.dnf:
        name: bind-utils
        state: present

We can run this playbook using the inventory from the controller (note that the ansible files are mounted directly inside the container working directory, see the compose file)

$ docker compose exec controller ansible-playbook -i inventories/default dummy_playbook.yml

PLAY [Dummy playbook for lab] ********************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************************************************************************************
ok: [lab-host-1]

TASK [Install a package on host] *****************************************************************************************************************************************************************************************************
changed: [lab-host-1]

PLAY RECAP ***************************************************************************************************************************************************************************************************************************
lab-host-1                 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Let's start some more target just to show it's working as intended and scales

docker compose up -d --scale host=3
[+] Running 4/4
 ✔ Container lab-host-1      Running    0.0s 
 ✔ Container lab-host-3      Started    0.5s 
 ✔ Container lab-host-2      Started    0.6s 
 ✔ Container lab-controller  Running    0.0s 

$ docker compose exec controller ansible-playbook -i inventories/default dummy_playbook.yml

PLAY [Dummy playbook for lab] ********************************************************************************************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************************************************************************************************
ok: [lab-host-3]
ok: [lab-host-2]
ok: [lab-host-1]

TASK [Install a package on host] *****************************************************************************************************************************************************************************************************
ok: [lab-host-1]
changed: [lab-host-2]
changed: [lab-host-3]

PLAY RECAP ***************************************************************************************************************************************************************************************************************************
lab-host-1                 : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
lab-host-2                 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
lab-host-3                 : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

Of course you'll want to do many more things with those containers. But at any time you can simply restart from scratch. Just shut down everything and start over:

$ docker compose down 
[+] Running 5/5
 ✔ Container lab-host-1      Removed    10.6s 
 ✔ Container lab-host-2      Removed    10.5s 
 ✔ Container lab-host-3      Removed    10.4s 
 ✔ Container lab-controller  Removed    10.5s 
 ✔ Network lab_default       Removed    0.5s 

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.