Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save Kambaa/55ecf491fa49fb6c84161065c3dfa9ef to your computer and use it in GitHub Desktop.
Save Kambaa/55ecf491fa49fb6c84161065c3dfa9ef to your computer and use it in GitHub Desktop.
My Ansible Learning Notes on an Ubuntu Server 22.04 VM

My Ansible Learning Notes on an Ubuntu Server 22.04 VM

What is Ansible:

A tool/way to automotize operations done in machine environments.

Installation:

First install ansible with sudo apt install ansible -y and then check it with ansible --version. This is my version info:

osboxes@jumphost:~/ansible-tutorial$ ansible --version
ansible 2.10.8
  config file = None
  configured module search path = ['/home/osboxes/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules']
  ansible python module location = /usr/lib/python3/dist-packages/ansible
  executable location = /usr/bin/ansible
  python version = 3.10.12 (main, Nov  6 2024, 20:22:13) [GCC 11.4.0]

Ansible Mentality:

From what i understand, basically, first you provide an inventory of machine connection infos to connect, and give some tasks to be completed in them.

Inventory

You provide machines to be connected in a file. This file can be a ini format or json/yaml format. Initially i wanted to learn ini format.

Ini file format:

You can give specific names and group/subgroup machine connection infos

[<GROUP NAME>]
<MACHINE CONNECTION(S) INFOS>

An example of an inventory ini file, a localhost connection grouped with the name local

[local]
localhost ansible_connection=local

Localhost connection inventory config:

  • localhost ansible_connection=local
    • connect to localhost
    • via ansible connection type of local
  • localhost ansible_connection=local ansible_become=true ansible_become_password=...
    • connect to localhost
    • using ansible connection type of local
    • but get the elevated privileges (sudo) by becomeing root user with ansible_become=true
    • and for the sudo password, it is provided in ansible_become_password

A basic connection checking/pinging with ansible example:

now write a inventory file like this:

Generate a workspace under $HOME (/home/<YOUR ACCOUNT NAME>/):

cd $HOME
mkdir ansible-learning
cd ansible-learning
nano inventory

Enter these to the inventory file and save it:

[local]
localhost ansible_connection=local

press CTRL and X keys, and then press Y key to save, and lastly press ENTER key to save it to the file /home/<YOUR ACCOUNT NAME>/ansible-learning/inventory (basic nano usage)

After saving the inventory file, then run this command to ping the connections to given hosts.

ansible -i inventory local -m ping
  • ansible : is the command for temporary or single use (ad-hoc) operations, like ping'ing host or other small and easy tasks.
  • -i: a flag for the ansible command that supplies the inventory file.
  • inventory : file name(that we've generated earlier) for the -i flag.
  • local: group name to be checked inside the inventory
  • -m: a flag for the ansible command that a module will be loaded and run.
  • ping: module name(shorthand for ansible.builtin.ping, already existing inside ansible) for the -m flag.

You should see a successfull operation result like this. To see the logs more detailed add -vvvv at the end of the command mentioned above.

osboxes@jumphost:~/ansible-tutorial$ ansible -i inventory local -m ping
localhost | SUCCESS => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "ping": "pong"
}

Another example that fails - Update the system using apt:

With localhost connection added to the inventory file and grouped with the name local, let's try to update the system packages (normally you do this with sudo apt update -y and sudo apt upgrade -y)

# This is equivalent of apt update, run this command
ansible -i inventory local -m apt -a "update-cache=yes"
  • ansible : is the command for temporary or single use (ad-hoc) operations, like ping'ing host or other small and easy tasks.
  • -i: a flag for the ansible command that supplies the inventory file.
  • inventory : file name(that we've generated earlier) for the -i flag.
  • local: group name to be checked inside the inventory
  • -m: a flag for the ansible command that a module will be loaded and run.
  • apt: module name(shorthand for ansible.builtin.apt, already existing inside ansible, docs)
  • -a: a flag that some arguments will be given to the ansible module
  • "update-cache=yes" arguments for the module that will run the apt update command(docs).

If you run this command you will see an error like this:

osboxes@jumphost:~/ansible-tutorial$ ansible -i inventory local -m apt -a "update-cache=yes"
localhost | FAILED! => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "changed": false,
    "msg": "Failed to lock apt for exclusive operation: Failed to lock directory /var/lib/apt/lists/: E:Could not open lock file /var/lib/apt/lists/lock - open (13: Permission denied)"
}

This error tells that ansible operation done on localhost has FAILED due to a (13. Permission denied) as you know apt commands require system administration elevation(sudo) to be run successfully, and we're trying to do it without it. We need to tell ansible that you need to run this via sudo, meaning you need to become an elevated user. There's so many ways to achieve this, but for the purpose of this topic (which is learning the inventory), i'll write the inventory host connection must become an elevated user way here:

modify the inventory file like this:

nano inventory

# modify the inventory to become like this:

[local]
localhost ansible_connection=local ansible_become=true ansible_become_password=<YOUR_ACCOUNT_PASSORD>

After that, the same command does the apt update with the given become flag and giving the user password(which is not secure, will cover this) and returns that update is successful. There's my result

osboxes@jumphost:~/ansible-tutorial$ ansible -i inventory local -m apt -a "update-cache=yes"
localhost | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "cache_update_time": 1736773491,
    "cache_updated": true,
    "changed": true
}

We're able to sudo apt update successfully, but writing your account password to a file openly and clearly is a very bad practice, we must not do it like this. For one of the more secure approaches, Ansible allows us to use the env variables, instead of entering in a file, which makes our sensitive data to be stored elsewhere and can be accessed according to our needs. The sensitive data we'we entered ansible_become_password=<YOUR_ACCOUNT_PASSORD> in inventory file can be given as an uppercase environment variable, like this: ANSIBLE_BECOME_PASS

In summary do it like this: First remove the sensitive info(ansible_become_password=<YOUR_ACCOUNT_PASSORD>) from the inventory file:

nano inventory

# modify the inventory to become like this:

[local]
localhost ansible_connection=local ansible_become=true 

After that run this command to enter the sensitive data to a env variable (for the current session).

export ANSIBLE_BECOME_PASS=<YOUR_ACCOUNT_PASSORD>

And then run the command again, value will be picked up by ansible and you'll see the success result:

ansible -i inventory local -m apt -a "update-cache=yes"

here is my output:

osboxes@jumphost:~/ansible-tutorial$ export ANSIBLE_BECOME_PASS=<YOUR_ACCOUNT_PASSORD>
osboxes@jumphost:~/ansible-tutorial$ ansible -i inventory local -m apt -a "update-cache=yes"
localhost | CHANGED => {
    "ansible_facts": {
        "discovered_interpreter_python": "/usr/bin/python3"
    },
    "cache_update_time": 1736775582,
    "cache_updated": true,
    "changed": true
}

Optional - More examples for the apt updates

# this is equivalent of apt upgrade
ansible -i inventory local -m apt -a "upgrade=yes"
# Optional, written here to show that can combine multiple arguments to a ansible module if you check the docs
# this is the combined version update and upgrade 
ansible -i inventory local -m apt -a "update-cache=yes upgrade=yes"

Writing tasks inside playbook files:

The examples above, make use of the ansible internal modules, and we're calling these operation/task one by one by calling the necessary ansible command one by one. To make this process more robust/automated/less error-prone, we can put these operations/tasks in a yaml file/files and group them. In Ansible terminology, these operations/tasks are defined as tasks, and these group of tasks is called playbook(s). Here's an example that does the same thing above (apt update):

Write a file named 1.local-apt-update.yaml.

nano 1.local-apt-update.yaml

# add these inside 
---
- name: Example Playbook for localhost to update the repos
  hosts: localhost
  become: true

  tasks:
    - name: Update the package cache
      ansible.builtin.apt:
        update_cache: yes

# Check become password exists
echo $ANSIBLE_BECOME_PASS
# if it doesn't, add like this: 
export ANSIBLE_BECOME_PASS=<YOUR ACCOUNT PASSWORD>

# run this command to use the inventory file and run the playbook file named `local-apt-update.yaml`
ansible-playbook -i inventory 1.local-apt-update.yaml

Here's my result of these commands:

osboxes@jumphost:~/ansible-tutorial$ ansible-playbook -i inventory 1.local-apt-update.yaml

PLAY [Example Playbook for localhost to update the repos] **********************************************
TASK [Gathering Facts] *********************************************************************************
ok: [localhost]

TASK [Update the package cache] ************************************************************************
changed: [localhost]

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

As you can see, by supplying the inventory and the playbook files (of course the sensitive ones too) to the ansible-playbook command, we can run the same operation, defined in a yaml file, more easily. This way is less error-prone.

Todos:

  • preparation for ansible connection type ssh
  • install necessary sshpass app to localhost, and add the ssh key to known hosts by writing playbooks and running them
  • connect to localhost via ansible connection type ssh
  • write the necessary steps to install docker (adding the gpg, trusting it, add apt repo of docker and installing docker, then verifying by running this command docker run hello-world)
  • advanced inventory ini, advanced playbooks topics
  • organizing playbooks
  • reading other's playbooks
  • ansible galaxy and its usage
@Kambaa
Copy link
Author

Kambaa commented Jan 13, 2025

add a passwordless sudo user

first add the user

sudo adduser yg

sudo visudo

yg ALL=(ALL) NOPASSWD: ALL

exit and connect via this user

sudo -l

test it

Clear the Cached Session: Run:
sudo -k

Then, try the command again:
sudo ls /root

remove user from sudo group:

sudo deluser osboxes sudo

check users groups:

groups osboxes

add passwordless sudo config:

sudo visudo

add this line under root...

osboxes ALL=(ALL:ALL) NOPASSWD:ALL

reboot and test it is a sudo command will NOT ask password

sudo ls /root

if ansible ssh connection will use a password, install sshpass to the machine that ansible commands will be run

master 10.0.2.16
node1 10.0.2.18
node2 10.0.2.19
node3 10.0.2.17
node4

jump-host: 10.0.2.20


docker run --rm -it
--group-add root
--user $(id -u):$(id -g)
--env-file $HOME/.bare_metal_creds.env
--volume $(pwd):/workspace
-v ~/.ssh:/root/.ssh:ro
wasd setup install

10.0.2.9 master

10.0.2.10 node1

10.0.2.11 node2

10.0.2.12 node3

10.0.2.13 jump-host

ssh [email protected].

10.0.2.6 node1

10.0.2.8 node2

10.0.2.7 node3

10.0.2.4 jump

sudo apt-mark unhold kubeadm kubelet kubectl
sudo apt remove --purge kubeadm kubelet kubectl

sudo rm -rf /var/lib/kubelet /etc/kubernetes/ /var/lib/etcd

sudo nano /etc/default/grub

swapon --show
lsblk
sudo swapoff /dev/sda4
swapon --show

Check systemd for Swap Configurations: It's possible that systemd has some swap configuration left, which might still bring the swap partition back on boot. To check for this, run:
sudo systemctl mask swap.target

Check for Swap Partition: Even though it is not in /etc/fstab, there might still be a swap-related entry in crypttab or other configuration files. You can search for any mention of /dev/sda4 using:
sudo grep -r "/dev/sda4" /etc/

  1. Reboot and Verify
    sudo reboot
    swapon --show

[k8s_control_plane]
10.0.2.9

[k8s_node]
10.0.2.10
10.0.2.11
10.0.2.12

[k8s:children]
k8s_control_plane
k8s_node

[jump_server]
10.0.2.13
git pull origin[jump:children]
jump_server

[nfs_server]
10.0.2.13

[nfs:children]
nfs_server

[all:children]
k8s
jump
nfs
cr

sudo rm /etc/machine-id
sudo systemd-machine-id-setup
sudo hostnamectl set-hostname

run these after virtualbox ova clone

ssh [email protected]
ssh [email protected]
ssh [email protected]
ssh [email protected]

export $(grep -v '^#' ~/.bare_metal_creds.env | xargs)

ansible-playbook -i $ANSIBLE_INVENTORY --extra-vars "deployment_type=$SYSTEM" --extra-vars $ANSIBLE_VARS $BASEDIR/playbooks/systems-install.yaml --flush-cache --tags install

sudo timedatectl set-timezone Europe/Istanbul
sudo timedatectl set-ntp true

@Kambaa
Copy link
Author

Kambaa commented Jan 18, 2025

For ansible to connect more easily, Set up SSH key-based authentication:

Here’s a step-by-step guide on how to set up SSH key-based authentication for your Ubuntu VMs.

  1. Generate an SSH Key Pair

On your local machine (the one from which you want to access the remote VMs), generate an SSH key pair (if you don’t have one already). Run:

ssh-keygen -t rsa -b 4096 -f ~/.ssh/id_rsa

Explanation:

-t rsa: Specifies the key type (RSA).
-b 4096: Specifies the key size (4096 bits, which is more secure).
-f ~/.ssh/id_rsa: Specifies the file path where the private key will be saved (default: ~/.ssh/id_rsa).

When prompted, you can leave the passphrase empty (just press Enter) if you don’t want to set a passphrase. You can also enter a passphrase for added security.
2. Copy the SSH Public Key to the Remote VM

Now you need to copy the public key (~/.ssh/id_rsa.pub) to the remote machine(s) in order to enable passwordless login. You can do this easily using the ssh-copy-id tool.

ssh-copy-id user@remote_host

For example, if you want to copy your key to 10.0.2.5 as the osboxes user:

ssh-copy-id [email protected]

You will be prompted for the password of the remote user (osboxes in this case). Once authenticated, the tool will copy your public key to the remote machine’s ~/.ssh/authorized_keys file.
3. Manually Copy the SSH Public Key (If ssh-copy-id is Unavailable)

If ssh-copy-id is unavailable or you prefer to do it manually, follow these steps:

On your local machine, display your public key:

cat ~/.ssh/id_rsa.pub

This will output your public key, which should look something like this:

ssh-rsa AAAAB3... rest of the key ... user@hostname

On the remote machine, log in via SSH:

ssh [email protected]

On the remote machine, create the ~/.ssh directory if it doesn’t exist:

mkdir -p ~/.ssh

Append the public key to the ~/.ssh/authorized_keys file:

echo "your_public_key_here" >> ~/.ssh/authorized_keys

Make sure to replace "your_public_key_here" with the public key you copied earlier (from ~/.ssh/id_rsa.pub).

Set the correct permissions for the ~/.ssh directory and authorized_keys file:

chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
  1. Test SSH Connection

Now that your public key is on the remote machine, test the connection:

ssh [email protected]

You should be able to log in without entering the password.
5. (Optional) Configure SSH to Disable Password Authentication

To further secure your server, you can disable password authentication, forcing users to log in only using SSH keys.

On the remote machine, open the SSH configuration file:

sudo nano /etc/ssh/sshd_config

Ensure the following lines are present and not commented out (remove # if they exist):

PasswordAuthentication no
ChallengeResponseAuthentication no

Restart the SSH service to apply the changes:

sudo systemctl restart ssh
  1. Add More Remote Hosts

To set up key-based authentication for multiple remote hosts, repeat the process for each VM or server:

Generate an SSH key pair (if you haven't already).
Use ssh-copy-id or manually copy the public key to each remote host.
Test SSH login for each machine.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment