How to Install Docker Engine with Ansible on Oracle Linux 7

I am wanting to make sure Docker engine is present on my Jenkins host. To prove that Docker is not installed yet, here is the result of sudo systemctl status docker on the Jenkins host.

Unit docker.service could not be found.

Here is the Ansible playbook I came up with. The target host is an Oracle Linux 7 on ARM64 processor.

---
- name: Install docker
  gather_facts: No
  hosts: jenkins

  tasks:
    - name: Install Docker
      yum:
        name: docker-engine
        state: installed
      become: yes
    
    - name: Enable/Start Docker
      systemd:
        name: docker
        enabled: yes
        state: started
      become: yes

    - name: Add user vagrant to docker group
      user:
        name: vagrant
        groups: docker
        append: yes
      become: yes

Here is the result when I execute it.

ansible-playbook playbooks/docker-ce.yaml
PLAY [Install docker] ****************************************************************************************************************************

TASK [Install Docker] ****************************************************************************************************************************
changed: [jenkins.hayato-iriumi.net]

TASK [Enable/Start Docker] ***********************************************************************************************************************
changed: [jenkins.hayato-iriumi.net]

TASK [Add user vagrant to docker group] **********************************************************************************************************
ok: [jenkins.hayato-iriumi.net]

PLAY RECAP ***************************************************************************************************************************************
jenkins.hayato-iriumi.net  : ok=3    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

When I ssh into the Jenkins host and check if Docker daemon is running here is the output.

[opc@jenkins ~]$ sudo systemctl status docker
● docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since Mon 2022-01-03 04:55:11 GMT; 1min 37s ago
     Docs: https://docs.docker.com
 Main PID: 18476 (dockerd)
    Tasks: 8
   Memory: 37.5M
   CGroup: /system.slice/docker.service
           └─18476 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock

Jan 03 04:55:10 jenkins dockerd[18476]: time="2022-01-03T04:55:10.694835968Z" level=warning msg="Your kernel does not support cgroup blkio weight"
Jan 03 04:55:10 jenkins dockerd[18476]: time="2022-01-03T04:55:10.695237969Z" level=warning msg="Your kernel does not support cgroup bl..._device"
Jan 03 04:55:10 jenkins dockerd[18476]: time="2022-01-03T04:55:10.695923611Z" level=info msg="Loading containers: start."
Jan 03 04:55:11 jenkins dockerd[18476]: time="2022-01-03T04:55:11.014165992Z" level=info msg="Default bridge (docker0) is assigned with...address"
Jan 03 04:55:11 jenkins dockerd[18476]: time="2022-01-03T04:55:11.130298822Z" level=info msg="Loading containers: done."
Jan 03 04:55:11 jenkins dockerd[18476]: time="2022-01-03T04:55:11.147750103Z" level=warning msg="Not using native diff for overlay2, th...overlay2
Jan 03 04:55:11 jenkins dockerd[18476]: time="2022-01-03T04:55:11.148281344Z" level=info msg="Docker daemon" commit=9bb540d graphdriver...03.11-ol
Jan 03 04:55:11 jenkins dockerd[18476]: time="2022-01-03T04:55:11.148597025Z" level=info msg="Daemon has completed initialization"
Jan 03 04:55:11 jenkins systemd[1]: Started Docker Application Container Engine.
Jan 03 04:55:11 jenkins dockerd[18476]: time="2022-01-03T04:55:11.208274084Z" level=info msg="API listen on /var/run/docker.sock"
Hint: Some lines were ellipsized, use -l to show in full.

How to do sudo with Ansible

Now I have the following content in my test.yaml playbook. I want Ansible to write a file under /etc directory.

- name: My playbook
  hosts: test
  tasks:
     - name: Leaving a mark
       command: "touch /etc/foo"

When I execute it like the following, I get an error.

ansible-playbook-3 test.yaml
fatal: [ansibletest.westcentralus.cloudapp.azure.com]: FAILED! => {"changed": true, "cmd": ["touch", "/etc/foo"], "delta": "0:00:00.003802", "end": "2022-01-01 02:53:01.519156", "msg": "non-zero return code", "rc": 1, "start": "2022-01-01 02:53:01.515354", "stderr": "touch: cannot touch '/etc/foo': Permission denied", "stderr_lines": ["touch: cannot touch '/etc/foo': Permission denied"], "stdout": "", "stdout_lines": []}

Obviously, azureuser does not have permission to write out the file, so how do we do sudo in Ansible?

You just have to pass --become parameter to do sudo in Ansible.

ansible-playbook-3 test.yaml --become
PLAY [My playbook] *******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
ok: [ansibletest.westcentralus.cloudapp.azure.com]

TASK [Leaving a mark] ****************************************************************************************************************************
[WARNING]: Consider using the file module with state=touch rather than running 'touch'.  If you need to use command because file is insufficient
you can add 'warn: false' to this command task or set 'command_warnings=False' in ansible.cfg to get rid of this message.
changed: [ansibletest.westcentralus.cloudapp.azure.com]

PLAY RECAP ***************************************************************************************************************************************
ansibletest.westcentralus.cloudapp.azure.com : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

After executing it, I checked the file and it was created.

How to Dry Run Ansible Playbook

I have test.yaml playbook with the following content.

- name: My playbook
  hosts: all
  tasks:
     - name: Leaving a mark
       command: "touch /tmp/ansible_was_here"

I would like to do dry run using this playbook. Here is what I can do.

ansible-playbook-3 test.yaml --check

Here is the output.

[opc@ansible-primary ansible]$ ansible-playbook-3 test.yaml --check

PLAY [My playbook] *******************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
[WARNING]: Platform linux on host jenkins.pub.ashburn.oraclevcn.com is using the discovered Python interpreter at /usr/bin/python, but future
installation of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [jenkins.pub.ashburn.oraclevcn.com]
ok: [ansibletest.westcentralus.cloudapp.azure.com]

TASK [Leaving a mark] ****************************************************************************************************************************
skipping: [jenkins.pub.ashburn.oraclevcn.com]
skipping: [ansibletest.westcentralus.cloudapp.azure.com]

PLAY RECAP ***************************************************************************************************************************************
ansibletest.westcentralus.cloudapp.azure.com : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0
jenkins.pub.ashburn.oraclevcn.com : ok=1    changed=0    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0

It skipped the actual execution on the hosts specified in /etc/ansbile/hosts and returned ok for each host.

Managed Node in Azure

I provisioned a Red Hat Linux in Azure. When you provision a Linux host in Azure, the default user is azureuser. I would like to manage the host from the Ansible node on OCI. Here is the /etc/ansible/hosts file that I modified.

[jenkins]
jenkins.pub.ashburn.oraclevcn.com

[test]
ansibletest.westcentralus.cloudapp.azure.com

When I test the managed nodes, I get the following error.

ansibletest.westcentralus.cloudapp.azure.com | UNREACHABLE! => {
    "changed": false,
    "msg": "Failed to connect to the host via ssh: Permission denied (publickey,gssapi-keyex,gssapi-with-mic).",
    "unreachable": true
}

This is because Ansible tries to access the host with the default user instead of azureuser. To specify a certain user to be used on the managed node, here is what you can do.

[jenkins]
jenkins.pub.ashburn.oraclevcn.com

[test]
ansibletest.westcentralus.cloudapp.azure.com ansible_user=azureuser

If you run the following commnad…

ansible-3 all -m ping

Output:

jenkins.pub.ashburn.oraclevcn.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}
ansibletest.westcentralus.cloudapp.azure.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/libexec/platform-python"
    },
    "changed": false,
    "ping": "pong"
}

This way, it uses azureuser to access the host in Azure.

Getting Started with Ansible

Why Ansible?

If you have tens, hundreds or thousands of servers, you will need a solution like Ansible or Puppet. These products allow you to define how each server is configured in declarative languages and they can control thousands of servers.

Starting with Ansible

I provisioned an ARM64 host for Ansible yesterday. I wrote an article that explains how to install Ansible on it here. Now I want to test it.

So the Ansible host has to know about Jenkins host. I have added the following lines in /etc/ansible/hosts file.

[jenkins]
jenkins.pub.ashburn.oraclevcn.com

The hosts file can have IP address or FQDN, so I added the FQDN on OCI.

Now I should be able to ssh into the Jenkins host from Ansible host, so I added the public key of Ansible to Jenkins’ authorized_keys. Now I can ssh into the Jenkins host from the Ansible host.

On the Ansible host, test the configuration.

ansible-3 all -m ping

Output:

jenkins.pub.ashburn.oraclevcn.com | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python"
    },
    "changed": false,
    "ping": "pong"
}

Test running a command on the client host.

ansible all -a "/bin/echo hello"

Output:

jenkins.pub.ashburn.oraclevcn.com | CHANGED | rc=0 >>
hello

Creating a Playbook

I am creating the following file jenkins_playbook.yaml with the content below.

- name: Jenkins Playbook
  hosts: all
  tasks:
    - name: Create a file
      shell: |
       echo 'hoge hoge hoge' >> ~/test.txt

Execute the playbook.

ansible-playbook-3 jenkins_playbook.yaml

Output:

PLAY [Jenkins Playbook] **************************************************************************************************************************

TASK [Gathering Facts] ***************************************************************************************************************************
[WARNING]: Platform linux on host jenkins.pub.ashburn.oraclevcn.com is using the discovered Python interpreter at /usr/bin/python, but future
installation of another Python interpreter could change this. See
https://docs.ansible.com/ansible/2.9/reference_appendices/interpreter_discovery.html for more information.
ok: [jenkins.pub.ashburn.oraclevcn.com]

TASK [Create a file] *****************************************************************************************************************************
changed: [jenkins.pub.ashburn.oraclevcn.com]

PLAY RECAP ***************************************************************************************************************************************
jenkins.pub.ashburn.oraclevcn.com : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0

We’ll ignore the warning for now. Now I am going to ssh into the Jenkins host and check the ~/test.txt file.

[opc@jenkins ~]$ cat test.txt
hoge hoge hoge

This concludes the very basics of how Ansible works. I am planning to dig into it more as I have time during the end of year holidays.

How to List Listening Ports on Linux

When you start daemons or Docker containers that listen to certain ports, you want to make sure they are actually listening. You can use netstat to list listening ports.

netstat -tulpn

As you can see, we can’t tell the process or process ID for each port. If you just add sudo, you can see the process information.

This is an essential tool when you diagnose what’s going on on the host where you want to host daemons/services.

How to Install Ansible on Oracle Linux 7 on ARM64

I’m wanting to learn Ansible and I would like to get my hands on it, so I am installing Ansible on an ARM64 host on Oracle Cloud Infrastructure.

  • Install Epel Release.
    sudo yum-config-manager --enable ol7_developer_EPEL
  • Install Ansible.
    sudo yum install ansible-python3
  • Check the installation.
    ansible-playbook-3 --version

It worked on Oracle Linux 7 on ARM64. I will blog more about how Ansible works in the coming days.

Migrated to ARM64 Oracle Linux 7

I have migrated my site to Oracle Linux 7 on ARM64 just because I was curious how it would work on ARM64 or if it would work on ARM64 at all.

As of this writing, I’ve got this WordPress site on Docker to work on Oracle Linux 7 on ARM64 processor. It was some struggle but I feel it’s snappier.

Here is the Docker Compose file that works for this site. As you can see, I commented out the mysql image and using Maria DB for ARM64 instead. Maria DB is a fork for MySQL, so it’s sufficient enough for my personal blog site.

version: "3.9"
services:
  db:
    image: arm64v8/mariadb # mysql/mysql-server:8.0.20-aarch64
    volumes:
      - ./mysql:/var/lib/mysql
    restart: always
    command: mysqld --default-authentication-plugin=mysql_native_password
    environment:
      MYSQL_ROOT_PASSWORD: YourPassword
      MYSQL_DATABASE: hayato_iriumi_db
      MYSQL_USER: blog_admin
      MYSQL_PASSWORD: AnotherPassword
    networks:
      proxynet:
  phpmyadmin:
    depends_on:
      - db
    image: arm64v8/phpmyadmin
    restart: always
    ports:
      - '8080:80'
    environment:
      PMA_HOST: db
      MYSQL_ROOT_PASSWORD: YourPassword
    networks:
      proxynet:
  wordpress:
    image: arm64v8/wordpress
    container_name: wordpress
    depends_on:
      - db
        # ports:
        # - "8000:80"
    restart: always
    environment:
      WORDPRESS_DB_HOST: db:3306
      WORDPRESS_DB_USER: blog_admin
      WORDPRESS_DB_PASSWORD: AnotherPassword
      WORDPRESS_DB_NAME: hayato_iriumi_db
      WORDPRESS_DEBUG: 'true'
    volumes:
      - ./html:/var/www/html
      - ./wp-content:/var/www/html/wp-content
    networks:
      proxynet:
  reverse:
    image: arm64v8/nginx
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d:/etc/nginx/conf.d
    ports:
      - "80:80"
      - "443:443"
    restart: always
    networks:
      proxynet:
volumes:
  db_data: {}
  wordpress: {}
networks:
  proxynet:

Docker Compose 2 works well on ARM64 as well. It is definitely faster than Docker Compose 1. By just changing the platform, I feel I upgraded my blog site for a better performance.

How to Install Docker Compose 2 on ARM Processor Linux Host (OL7)

I have provisioned Oracle Linux 7 on ARM processor on OCI. In an attempt to get my blog site to work on the host, I have installed Docker engine and now I want to install Docker Compose 2. Here are the steps.

  1. Create a directory.
    mkdir -p ~/.docker/cli-plugins/
  2. Download the Docker Compose 2 binary for ARM64.
    curl -SL https://github.com/docker/compose/releases/download/v2.2.2/docker-compose-linux-armv7 -o ~/.docker/cli-plugins/docker-compose
  3. Give the binary file execute permission.
    chmod +x ~/.docker/cli-plugins/docker-compose
  4. Check if the binary has been installed successfully.
    docker compose version