clubs:cloud_club:cloud_club_iac
Home | clubs :: cloud club :: python_club :: 3D-Printing | projects :: Proxmox | Kubernetes | scripting | utilities | games
Table of Contents
Cloud Club Topics - Infrastructure as Code ( IaC )
Questions
- What is Infrastructure as Code ( IaC )?
- Why do we need it? We can do it manually.
Forms of IaC
- Configuration files designed for specific virtual platform
- Examples
- AWS CloudFormation
- Custom scripts
- Code tools designed for multiple platforms
- Examples
- Pulumi
- Chef
- Puppet
- Ansible
- Terraform
Plan your installation
- See “Plan your Installation” checklist from server installationa
- Select your IaC tool
- Use Git for virsion control!!!
Build the VM
- Create the base code needed to build the network, security, and VM
Install software and configure
- If capable, add code incrementally to configure the VM as required
- If capable, add code incremetnally to install and configure software as required
Hands-on
- Build Ansible Server
- Create Linux VM ( Manually )
- Install Ansible
sudo dnf install ansible-core -y
- Verify ansible command works to display the version
ansible --version ansible [core 2.14.17] config file = /etc/ansible/ansible.cfg configured module search path = ['/home/garfield/.ansible/plugins/modules', '/usr/share/ansible/plugins/modules'] ansible python module location = /usr/lib/python3.9/site-packages/ansible ansible collection location = /home/garfield/.ansible/collections:/usr/share/ansible/collections executable location = /usr/bin/ansible python version = 3.9.19 (main, Sep 11 2024, 00:00:00) [GCC 11.5.0 20240719 (Red Hat 11.5.0-2)] (/usr/bin/python3) jinja version = 3.1.2
Configure Ansible
- ansible.cfg file(s)
- Generate your own ansible.cfg in your home directory
ansible-config init --disabled -t all > ~/ansible.cfg
- Review the ansible.cfg file and make changes as desired
- setup Ansible static inventory
- Append the following to the bottom of the inventory file ( /etc/ansible/hosts )
[repo-servers] repo.cloudclub.edu [web-servers] repo.cloudclub.edu [dns-servers] dns.cloudclub.edu
- Test inventory with command
ansible-inventory --graph @all: |--@ungrouped: |--@repo_servers: | |--repo.cloudclub.edu |--@web_servers: | |--repo.cloudclub.edu |--@dns_servers: | |--dns.cloudclub.edu
- Setup Ansible dynamic inventorya
- Setup ssh keys
- Generate ssh key pair - Note: Keep pressing enter ( 3 times ) for the default values until you get your command prompt back
ssh-keygen
- Copy the public key to the target devices - Note: type “yes” if prompted and enter the password when prompted
ssh-copy-id 192.168.1.11 /usr/bin/ssh-copy-id: INFO: Source of key(s) to be installed: "/home/garfield/.ssh/id_rsa.pub" The authenticity of host '192.168.1.11 (192.168.1.11)' can't be established. ED25519 key fingerprint is SHA256:CFrma1AskxuKYOHZm1mB0B4y1v1WPPhD2zD+TKcZHSY. This host key is known by the following other names/addresses: ~/.ssh/known_hosts:1: repo.cloudclub.edu Are you sure you want to continue connecting (yes/no/[fingerprint])? yes /usr/bin/ssh-copy-id: INFO: attempting to log in with the new key(s), to filter out any that are already installed /usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys garfield@192.168.1.11's password: Number of key(s) added: 1 Now try logging into the machine, with: "ssh '192.168.1.11'" and check to make sure that only the key(s) you wanted were added. - Login to the target device and configure ssh with the following content to use the key by default
command ============= sudo nano ~/.ssh/config content ============= Host *.cloudclub.edu IdentityFile ~/.ssh/id_rsa - Test the key by using ssh with the key
ssh 192.168.1.11
- Test inventory with command
ansible -m ping dns_servers dns.cloudclub.edu | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" } ----------------------------------------------------------------- ansible -m ping all repo.cloudclub.edu | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" } dns.cloudclub.edu | SUCCESS => { "ansible_facts": { "discovered_interpreter_python": "/usr/bin/python3" }, "changed": false, "ping": "pong" }
Ansbile Components
- Accessing and readding the Ansible documentation
- There are 2 ways to access the documentation
- Online
- You can use your browser to navigate through https://docs.ansible.com/
- Easiest: Google search for what you're tying to do
- Example search: “ansible add user” and select the top result ( reference: https://www.google.com/search?q=ansible+add+user )
- ansible-doc command
- To see a list of builtin modules
ansible-doc -l ansible.builtin
- To see the documentation on a particular module ( same one as above )
ansible-doc ansible.builtin.user
- Understand the components
- Modules
- Playbooks
- Tasks
- Roles
- collections
- List module collections with command
ansible-galaxy collection list
- Collection installation
ansible-galaxy collection install <namespace.collection>
- Offline collection installation
- Use a computer connected to the internet to find the collection on https://galaxy.ansible.com/ui/collections/ and search for the collection you need. For example, you will find https://galaxy.ansible.com/ui/repo/published/ansible/windows/ for the ansible.windows collection.
- Compare the installed ansible version from the command listed above with the version listed on the website. Lower the version number on the website until you are ⇐ the installed version.
- Click the “Download tarball” link and save the tar.gz file(s) some place where you can transfer the file(s) to the ansible server.
- When you're in the directory containing the files, run the following installation command - NOTE: This command assumes you want to install the collection for the entire server instead of individual users.
sudo ansible-galaxy collection install * -p /usr/share/ansible/collections/
Writing Ansible code
- Prepare a location for your ansible codea
- Create a “code” directory
mkdir ~/code cd ~/code
- As a best practice, create a local git repo
git init -b main ansible-basic cd ansible-basic
- Create a playbook with tasks
- Edit a new file fist-playbook.yml and add the following content
- name: My first playbook hosts: localhost tasks: - name: My first task ansible.builtin.debug: msg: Hello, user - Run your shiny new playbook
ansible-playbook first-playbook.yml PLAY [My first playbook] ************************************************************************************************* TASK [Gathering Facts] *************************************************************************************************** ok: [localhost] TASK [My first task] ***************************************************************************************************** ok: [localhost] => { "msg": "Hello, user" } PLAY RECAP *************************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0 - Use git command to add your file
git add first-playbook.yml
- Use git to create a “commit” object
git commit -m 'Initial version' 1 file changed, 9 insertions(+) create mode 100644 first-playbook.yml
- Change the string “user” to a veriable
- name: My first playbook hosts: localhost vars: user: Garfield tasks: - name: My first task ansible.builtin.debug: msg: Hello, {{ user }} - Use the above git commands to add the file, create a new commit object with a different message
git add first-playbook.yml git commit -m 'Add user variable' 1 file changed, 4 insertions(+), 1 deletion(-)
- ( Optional ) If you want to see the changes made to your files, use the git log command
git log
- Run your updated playbook
ansible-playbook first-playbook.yml PLAY [My first playbook] **************************************************************************************************************************************************************************************** TASK [Gathering Facts] ****************************************************************************************************************************************************************************************** ok: [localhost] TASK [My first task] ******************************************************************************************************************************************************************************************** ok: [localhost] => { "msg": "Hello, Garfield" } PLAY RECAP ****************************************************************************************************************************************************************************************************** localhost : ok=2 changed=0 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
- Createing roles
- Create ( initialize ) a new git repository
git init -b main ansible-roles cd ansible-roles mkdir roles cd roles
- use the ansible-galaxy command to create the directory structure of a role
ansible-galaxy init docs - Role docs was created successfully
- Install the tree command to help visualize the directory structure
sudo dnf install -y tree
- View the directory strcuture
tree . └── docs ├── defaults │ └── main.yml ├── files ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── README.md ├── tasks │ └── main.yml ├── templates ├── tests │ ├── inventory │ └── test.yml └── vars └── main.yml 9 directories, 8 files - Add “hidden” files to instruct git to keep these empty directories
touch docs/files/.gitkeep touch docs/templates/.gitkeep
- Run the tree command to show the differece
tree -a . └── docs ├── defaults │ └── main.yml ├── files │ └── .gitkeep ├── handlers │ └── main.yml ├── meta │ └── main.yml ├── README.md ├── tasks │ └── main.yml ├── templates │ └── .gitkeep ├── tests │ ├── inventory │ └── test.yml └── vars └── main.yml 9 directories, 10 files - Change to the tasks directory
cd docs/tasks
- Edit the main.yml file with the following content
--- # tasks file for docs - name: Create a directory ansible.builtin.file: path: /opt/docs state: directory user: nobody group: nobody mode: '0400' - name: Create an empty file in the new directory ansible.builtin.file: path: /opt/docs/git-notes.txt state: touch - Add the new files and create a commit
git add -A git commit -m 'Initial version - Add role' 10 files changed, 120 insertions(+) create mode 100644 roles/docs/README.md create mode 100644 roles/docs/defaults/main.yml create mode 100644 roles/docs/files/.gitkeep create mode 100644 roles/docs/handlers/main.yml create mode 100644 roles/docs/meta/main.yml create mode 100644 roles/docs/tasks/main.yml create mode 100644 roles/docs/templates/.gitkeep create mode 100644 roles/docs/tests/inventory create mode 100644 roles/docs/tests/test.yml create mode 100644 roles/docs/vars/main.yml
- Change to the “root” of the repo
cd ../..
- Create a playbook with the new role
nano doc-files.yml - name: My first playbook hosts: repo_servers roles: - name: docs - Add and commit the new playbook
- Run the new playbook in “check” mode. This mode will run the code, but won't actually do the actions.
ansible-playbook doc-files.yml --check PLAY [My first playbook] **************************************************************************************************************************************************************************************** TASK [Gathering Facts] ****************************************************************************************************************************************************************************************** ok: [repo.cloudclub.edu] TASK [docs : Create a directory] ******************************************************************************************************************************************************************************** fatal: [repo.cloudclub.edu]: FAILED! => {"changed": false, "msg": "Unsupported parameters for (ansible.builtin.file) module: user. Supported parameters include: _diff_peek, _original_basename, access_time, access_time_format, attributes, follow, force, group, mode, modification_time, modification_time_format, owner, path, recurse, selevel, serole, setype, seuser, src, state, unsafe_writes (attr, dest, name)."} PLAY RECAP ****************************************************************************************************************************************************************************************************** repo.cloudclub.edu : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0- Oops! It looks like we found an error. Let's fix it.
- “msg”: “Unsupported parameters for (ansible.builtin.file) module: user.
- Modify the docs/tasks/main.yml file to change 'user:' to 'owner:' and run the playbook again.
ansible-playbook doc-files.yml --check PLAY [My first playbook] **************************************************************************************************************************************************************************************** TASK [Gathering Facts] ****************************************************************************************************************************************************************************************** ok: [repo.cloudclub.edu] TASK [docs : Create a directory] ******************************************************************************************************************************************************************************** changed: [repo.cloudclub.edu] TASK [docs : Create an empty file in the new directory] ********************************************************************************************************************************************************* changed: [repo.cloudclub.edu] PLAY RECAP ****************************************************************************************************************************************************************************************************** repo.cloudclub.edu : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
- It looks like we fixed it.
- Add and commit the changes
- Run the playbook again without check mode
ansible-playbook doc-files.yml PLAY [My first playbook] **************************************************************************************************************************************************************************************** TASK [Gathering Facts] ****************************************************************************************************************************************************************************************** ok: [repo.cloudclub.edu] TASK [docs : Create a directory] ******************************************************************************************************************************************************************************** fatal: [repo.cloudclub.edu]: FAILED! => {"changed": false, "msg": "There was an issue creating /opt/docs as requested: [Errno 13] Permission denied: b'/opt/docs'", "path": "/opt/docs"} PLAY RECAP ****************************************************************************************************************************************************************************************************** repo.cloudclub.edu : ok=1 changed=0 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 - Oops! Another error - See the task it failed on “TASK [docs | Create a directory ]” and the error itself -There was an issue creating /opt/docs as requested: [Errno 13] Permission denied: b'/opt/docs
- Update the main.yml file - add “become: true” to the bottom of the task like this. Be sure to “outdent” the line.
group: nobody mode: '0400' become: true - Run the playbook again
- Yet another error. This time it's asking about the sudo password. Why? Because we added the “become: true” line. We told it to use sudo, but didn't give it a password to use.
- Run the playbook again, but add these 2 options
Options: ======== -k = ask ssh password -K = ask sudo/become password New command: ============= ansible-playbook doc-files.yml -kK ansible-playbook doc-files.yml -kK SSH password: BECOME password[defaults to SSH password]: PLAY [My first playbook] **************************************************************************************************************************************************************************************** TASK [Gathering Facts] ****************************************************************************************************************************************************************************************** ok: [repo.cloudclub.edu] TASK [docs : Create a directory] ******************************************************************************************************************************************************************************** changed: [repo.cloudclub.edu] TASK [docs : Create an empty file in the new directory] ********************************************************************************************************************************************************* fatal: [repo.cloudclub.edu]: FAILED! => {"changed": false, "msg": "Error, could not touch target: [Errno 13] Permission denied: b'/opt/docs/git-notes.txt'", "path": "/opt/docs/git-notes.txt"} PLAY RECAP ****************************************************************************************************************************************************************************************************** repo.cloudclub.edu : ok=2 changed=1 unreachable=0 failed=1 skipped=0 rescued=0 ignored=0 - We fixed the error for creating the directory, but we're failing to create the empty file. Why?
- Whom are we running the playbook as? YOU! – More specifically, your user account that you loged into the ansible server with.
- What is the ownership and permissions of the directory? – Owner: nobody – Group: nobody – Permissions: read-only for the owner and nothing for everyone elsae.
- Update the main.yml file to fix these things
- Run the playbook again - Success!!
ansible-playbook doc-files.yml -kK SSH password: BECOME password[defaults to SSH password]: PLAY [My first playbook] **************************************************************************************************************************************************************************************** TASK [Gathering Facts] ****************************************************************************************************************************************************************************************** ok: [repo.cloudclub.edu] TASK [docs : Create a directory] ******************************************************************************************************************************************************************************** changed: [repo.cloudclub.edu] TASK [docs : Create an empty file in the new directory] ********************************************************************************************************************************************************* changed: [repo.cloudclub.edu] PLAY RECAP ****************************************************************************************************************************************************************************************************** repo.cloudclub.edu : ok=3 changed=2 unreachable=0 failed=0 skipped=0 rescued=0 ignored=0
- What does the file ownership and permission look like now? – System default, since we didn't specify them in our task to create the file.
- Create a collection with roles ??
Build a web server
- Create a role ( prefix with your initials ) to install and start nginx web server
- Update the role to configure and restart the webserver
- Create web content ( About me content ) as index.html
- Example playbook
- name: Setup web server hosts: web_servers roles: - name: miked_web - Example main.yml from the role
--- # tasks file for miked_web - name: Install xnginx web server ansible.builtin.dnf: name: nginx become: true - name: Configure nginx web server to start ansible.builtin.systemd_service: name: nginx enabled: true state: started become: true - name: Add new file for web content ansible.builtin.template: src: templates/pg2.html.j2 dest: /usr/share/nginx/html/pg2.html owner: garfield group: garfield mode: 774 become: true - Example template:
<html> <body bgcolor="lightblue"> <h2>Welcome, {{ ansible_user_gecos }}, <font color="red"><b>from {{ ansible_host }}</b></font>!!</h2> <p>Let's have some more fun. ;)</p> </body> </html>
Backup your web content and rebuild
- Backup your web content with the tar command
- Create a new role to backup your site content by creating a .tar.gz or .tgz file.
- Destroy your VM
- Build a new server
- Restore your content from backup with the tar command ( or untar if you prefer )
clubs/cloud_club/cloud_club_iac.txt · Last modified: by 127.0.0.1
