A tool/way to automotize operations done in machine environments.
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]
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.
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.
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 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
- connect to
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 theinventory
-m
: a flag for the ansible command that a module will be loaded and run.ping
: module name(shorthand foransible.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"
}
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 theinventory
-m
: a flag for the ansible command that a module will be loaded and run.apt
: module name(shorthand foransible.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 theapt 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
}
# 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"
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
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:
check users groups:
add passwordless sudo config:
add this line under root...
reboot and test it is a sudo command will NOT ask password
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$(id -u):$ (id -g)
--group-add root
--user
--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/
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)
sudo timedatectl set-timezone Europe/Istanbul
sudo timedatectl set-ntp true