Institute of Computer Science
  1. Courses
  2. 2021/22 fall
  3. DevOps: Automating Software Delivery and Operations (LTAT.06.015)
ET
Log in

DevOps: Automating Software Delivery and Operations 2021/22 fall

Please contact chinmaya.dehury@ut.ee for more information.

  • Homepage
  • Lectures
  • Practicals
  • Exam & Grading
    • Final Exam Sample Questions
    • Final Exam Guidelines
  • Submit Homework
  • Grades
  • Plagiarism
  • Communication

Practice Session 4: Ansible automation tool

Make sure that you have already gone through Lab-03

Ansible is a simple automation engine that automates cloud provisioning, configuration management, application deployment, intra-service orchestration, and many other needs. It uses the YAML language that you have used in Lab-02, in the form of Ansible Playbooks, and that allows you to describe your automation jobs in a way that approaches plain English. The aim of this lab is to get acquainted with the Ansible automation tool and get to know its most commonly used commands.

Introduction to Ansible

Ansible works by connecting to your nodes and pushing out small programs, called "Ansible modules" to them. These programs are written to be resource models of the desired state of the system. Ansible then executes these modules (over SSH by default) and removes them when finished.

References

Referred documents and websites contain supportive information for the practice.

Manuals

  1. How Ansible works
  2. Demo of this lab. This may not have very exact steps.

Prerequisite: Preparing the VMs

Lets manually create 3 VMs:

  • VM1: (Centos7, m3.tiny, allow-all security group, Assign floating IP)
  • VM2: (Centos7, m3.xsmall, allow-all security group, Assign floating IP)
  • VM3: (Centos7, m3.xsmall, allow-all security group, Assign floating IP)
  • imp: Configure the VMs so that you can login (using ssh) to VM2 and VM3 from VM1
    • If you have private key with extension .ppk, than convert from .ppk to .pem(openssh base64 format). Manual here
    • Windows users (using gitbashm) and Linux users (bash), can use scp to copy the key file scp -i <SSH_KEY_FILE> <SSH_KEY_FILE_TO_COPY> centos@<VM1_IP>:/home/centos/.ssh/
    • Make sure that the key file is in .pem format.
  • Install git sudo yum install git
  • Install python3.6 on VM1 you may refer this manual
    • python --version
  • Install pip v21.2.4 on VM1
    • pip --version

Usage

VM1: Used for installing Ansible and managing other VMs.
VM2 & VM3: Used for Kubernetes cluster deployed using Ansible.

Exercise 1. Installation and basic configuration of Ansible v2.9.

The goal of this task is to get aquinted with installation of Ansible using pip and configure the ansibel to manage other virtual machines remotely.

Note: you can use your local machine, but we recommend creating the virtual machine instead.

Installing Ansible on VM1
Here we will install Ansible on Centos using pip

  • Make sure that you have pip v21.2.4 and python3.6
  • Install Ansible
    • pip install ansible
  • Check ansible verison
    • ansible --version

Exercise 2. Basic Ansible commands

The aim of this task is to make you to learn basic ansible commands.
Make sure that your in VM1

2.1. Basic ansible commands to check hosts and version

  • Check for ansible version using ansible --version
    Sample Output:
  • List all the ansible hosts ansible --list-hosts all
    Sample Output:
  • List localhost hostsansible --list-hosts localhost
    Sample Output:

2.2. Ansible Hosts/inventories file

Inventories: Ansible works against multiple managed nodes or “hosts” in your infrastructure at the same time, using a list or group of lists know as inventory. The default location for inventory is a file called /etc/ansible/hosts. You can specify a different inventory file at the command line using the -i <path> option. For more information on inventory follow here....

From ansible --list-hosts all and ansible --list-hosts localhost commands, all and localhost are some group of machines with related information, that are expected to be present in default /etc/ansible/hosts file. Note!!! Since you the ansible using pip, you cannot see the default hosts file at /etc/ansible/hosts

  • So, Now lets create hosts.yaml file vi hosts.yaml and copy the following content.
 
[iotusecase]
VM2 ansible_host=172.17.90.248 ansible_port=22 ansible_user=centos ansible_ssh_private_key_file=/home/centos/.ssh/chinmayadehury
VM3 ansible_host=172.17.91.168 ansible_port=22 ansible_user=centos ansible_ssh_private_key_file=/home/centos/.ssh/chinmayadehury
  • Modify with your key name instead of chinmayadehury
  • Change the IPs of iot-cluster-master and iot-cluster-worker with VM2 and VM3
  • [iotusecase] is the custom tag used to group your VMs and it can be changed.
  • Here, hosts.yaml file contains with the required information to connect to the other VMs (i.e. VM1 and VM2 and VM3) such as username or private_key or port are used for ssh connection etc,.

note: Writing $HOME instead of /home/centos may not work.

  • The hosts.yaml can also have hostname or the IP address of other VMs. Use can use either of one. Hence the minimal version would be:
 
[iotusecase]
172.17.90.248
172.17.91.168
172.17.89.193

2.3. Basic tasks using Ansible

The ansible command defines and run a single task playbook against a set of hosts. The ansible-doc <module-name> is a handy command to know more about a specific module.

  • Now lets ping all servers in iotusecase group
    ansible -m ping iotusecase -i hosts.yaml
    Sample Output:

    NOTE: ping module is useful from /usr/bin/ansible to verify the ability to login and that a usable Python is configured. This is NOT ICMP ping, this is just a trivial test module that requires Python on the remote-node.
  • Execute pwd command in all servers in iotusecase group
    ansible -m command -a "pwd" iotusecase -i hosts.yaml
    Sample Output:

    NOTE: The given command with -a "command name" option will be executed on all selected nodes.
  • Execute ls command in all servers in iotusecase group
    ansible -m command -a "ls" iotusecase -i hosts.yaml
    Sample Output:
  • Execute ls command in all servers in iotusecase group with a private key. Change the private key name according to your ssh_key_name with chinmayadehury

ansible -m command --private-key .ssh/chinmayadehury -a "ls" iotusecase -i hosts.yaml
Sample Output:

  • Execute ls command in all servers in iotusecase group inside myinven_list inventory list with a private key. myinven_list is present in my present directory (this is similar to /etc/ansible/hosts inventory file.)
    ansible -m command --private-key .ssh/chinmayadehury -a "ls" -i myinven_list iotusecase
    Sample Output
  • Create a directiry in the remote VM using command module:
    ansible remote -m command -a "mkdir /home/centos/mydir1" -i hosts.yaml
    NOTE: here remote is a host group (similar to iotusecase).
    Sample Output:
  • Create a directiry in the remote VM using file module:
    ansible remote -m file -a "path=/home/centos/mydir2 state=directory" -i hosts.yaml
    NOTE: Here again I am using remote host group (similar to iotusecase).
    Sample Output:
  • Transfer a file from local VM to the remote VM using copy module:
    ansible remote -m copy -a "src=/home/centos/my_ansible_dir/playbooks/mydummyfile dest=/home/centos/" -i hosts.yaml

Exercise 3. Working with Ansible Playbook

Ansible command line is great for executing a single task. But playbooks are more useful for multiple tasks. Playbooks are text files written in the YAML format. You can check http://www.yamllint.com/ or other alternatives to validate your YAML file.

The basic command to invoke any playbook is:
ansible-playbook <path to your playbook>

Make sure that your in VM1

3.1. Playbook for file/directory operation

Following playbook will let you execute above Ansible commands, e.g create new directory, new files, copy file from local machine to remote machine, print the message, etc.
Create your first playbok with following content.
vi play-1.yaml

---
- hosts: remote

  tasks:
   - name: what is the present working directory
     command: pwd
     register: out
   - debug:
      var: out.stdout_lines
   - debug:
      msg: "present working directory is: {{out.stdout_lines}}"

   - name: See what is in the current directory
     command: ls
     register: lsout
   - debug:
      msg: "output of ls command: {{lsout.stdout_lines}}"

   - name: create a new directory
     file: 
       path: /home/centos/mydir3
       state: directory

   - name: Create an empty file inside new directory
     file:
       path: /home/centos/mydir3/emptyfile.txt
       state: touch

   - name: copy the file from local vm to the remote vm
     copy: 
      src: /home/centos/my_ansible_dir/playbooks/lists
      dest: "/home/centos/mydir3/"

   - name: "list of files inside that new directory"
     shell: "ls /home/centos/mydir3/"
     register: lsout
   - debug:
      msg: "list of files inside that new directory: {{lsout.stdout_lines}}"

NOTE: Update the remote group name in the second line of above content ( hosts: remote ) based on your inventory file and host group name.

Ansible playbook command: ansible-playbook play-1.yaml -i hosts.yaml
Sample Output:

You may visit below source for more information

  • https://docs.ansible.com/ansible/2.8/modules/list_of_files_modules.html
  • https://docs.ansible.com/ansible/2.8/modules/file_module.html#file-module

3.2. Package install/remove/reinstall/download operation

Below is a playbook example for working with package management (using yum module).
Note: Follwing things will work only on the centos Operating sytem.
vi install-packages.yaml

---
- hosts: remote
  gather_facts: true
  become: true
  become_method: sudo

  tasks:
    - name: Install the latest version of vim editor
      yum:
        name: vim
        state: latest

    - name: Download the nano editor package but do not install it
      yum:
        name:
          - nano
        state: latest
        download_only: true
        download_dir: "/home/centos/"
    - name: Install the latest version of Apache
      yum:
        name: httpd
        state: latest
    - name: Start apache service
      service:
        name: httpd
        state: started

NOTE: Update the remote group name in the second line of above content ( hosts: remote ) based on your inventory file and host group name.

Command: ansible-playbook install-packages.yaml -i hosts.yaml
Sample Output:

Now go to the browser and test if apache web server is accessible, like below:

Figure out if you are unable to access the apache test page.
Hint:

  • login to the remote VM and check if the httpd server is running
  • Check the security group of the remote VM.

3.3. Playbook to stop and remove a package

Below playbook will stop the previously started apache server and remove/uninstall from the system Note: Following playbook will work only on the centos Operating sytem.
vi remove_package.yaml
================================

---
- hosts: remote
  gather_facts: true
  become: true
  become_method: sudo

  tasks:
	- name: Stop service httpd, if started
	  service:
		name: httpd
		state: stopped

	- name: Remove the Apache package
	  yum:
		name: httpd
		state: absent

NOTE: Update the remote group name in the second line of above content ( hosts: remote ) based on your inventory file and host group name.

Command: ansible-playbook remove_package.yaml -i hosts.yaml

Sample Output:

Exercise 4. Install Docker

Now you will install Docker using Ansible playbook.
vi install-docker.yaml
================================

---
- hosts: iotusecase
  gather_facts: true
  become: true
  become_method: sudo

  tasks:
    - name: Create target directory 
      file: 
           path=/etc/docker/ 
           state=directory 
           mode=0755

    - name: configure docker network address, ahead of time, to solve issues with docker default network collision with UT VPN
      lineinfile:
          path: /etc/docker/daemon.json
          line: |
            {
              "default-address-pools": [{"base":"172.80.0.0/16","size":24}]
            }
          owner: root
          group: root
          mode: '0644' 
          create: yes
    - name: add the official Docker repository, download the latest version of Docker, and install it
      shell: "curl -fsSL https://get.docker.com/ | sh"

    - name: Start and enable docker service
      service:
        name: docker
        enabled: true
        state: started

NOTE: Update the remote group name in the second line of above content ( hosts: remote ) based on your inventory file and host group name.

Command: ansible-playbook install-docker.yaml -i hosts.yaml
Sample Output:

Exercise 5. Creating a K8 cluster using Ansible

You will use Kubespray, that uses ansible script to install and configure kubernetes cluster. Now make sure that you have following:

  • One VM for Master (i.e. VM2)
    • Centos7, m3.xsmall, allow-all security group, Assign floating IP
  • One VM for Worker (i.e. VM3)
    • Centos7, m3.xsmall, allow-all security group, Assign floating IP

Kubespray src:

  • Official resources for Kubespray GitHub Repo
  • Production environment using kubespray : https://kubernetes.io/docs/setup/production-environment/tools/kubespray/

The following commands need to be run on VM1

  • Download the kubespray package by cloning the github repo and install all the kubespray requirements using python pip3.
    • git clone https://github.com/kubernetes-sigs/kubespray.git
    • cd kubespray
  • Just have a quick look at the files present in kubespray directory.
  • Now either you can install all the packages present in requirements.txt file using pip install -r requirements.txt OR selectively install following packages:
    • pip install ansible-base
    • pip install ruamel.yaml
    • pip install ruamel.yaml.clib
    • Please use pip show <package name> command to see if the package is already installed.
  • Copy the kubespray files to your own folder.
    • cp -rfp inventory/sample inventory/mycluster
  • Folloing commands will prepare hosts.yml file (It's not same as earlier host.yaml file) specifying the hosts and their roles in the cluster.Declare a variable IPs specifying internal IPs of both the servers.
    • The command to declare is declare -a IPS=(<internal_ip_of_node1> <internal_ip_of_node2>)
    • Use the variable IPS to build up a config file hosts.yml using the below command
      • Configure hosts.yaml using this command CONFIG_FILE=inventory/mycluster/hosts.yaml python3 contrib/inventory_builder/inventory.py ${IPS[@]}
    • Now have a look at the following file, cat inventory/mycluster/hosts.yaml
  • Deploy with ansible-playbook command
    • Deploy Kubespray with Ansible Playbook - run the playbook as root. The option --become is required, as for example writing SSL keys in /etc/, installing packages and interacting with various systemd daemons. Without --become the playbook will fail to run!
    • Run the command by changing the <SSH_KEY> ansible-playbook -i inventory/mycluster/hosts.yaml --become --become-user=root cluster.yml --private-key ~/.ssh/<SSH_KEY>

The final Sample output should look like below:

  • Login to the master node (should be either VM2 or VM3) and enter following command:
    • kubectl get nodes

If you see below error run the following commands: Possible Error:

  • mkdir -p $HOME/.kube
  • sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
  • sudo chown $(id -u):$(id -g) $HOME/.kube/config
  • Now you should see following sample output of the kubectl get nodes command:

[ src: https://github.com/kubernetes-sigs/kubespray ]

5.1. Deploy your service on k8 cluster

Create a playbook with following content.

vi install-app.yaml
================================

---
- name: create
  hosts: master
  gather_facts: true
  become: true
  become_user: centos

  tasks:

	- name: Create target directory 
	  file: 
		 path=$HOME/deploy_nginx/ 
		 state=directory

	- name: create the deployment file
	  lineinfile:
		path: $HOME/deploy_nginx/deploy1.yaml
		create: yes
		line: |
		  apiVersion: apps/v1
		  kind: Deployment
		  metadata:
			name: nginx-deployment
			labels:
			  app: nginx
		  spec:
			replicas: 3
			selector:
			  matchLabels:
				app: nginx
			template:
			  metadata:
				labels:
				  app: nginx
			  spec:
				containers:
				- name: nginx
				  image: nginx:1.14.0
				  ports:
				  - containerPort: 80

	- name: create the deployment by running the kubectl command
	  command: "kubectl create -f $HOME/deploy_nginx/deploy1.yaml"

	- name: create the service file
	  lineinfile:
		path: $HOME/deploy_nginx/service1.yaml
		create: yes
		line: |    
		  apiVersion: v1
		  kind: Service
		  metadata:
			name: nginx-service
			labels:
			  run: nginx-service
		  spec:
			type: NodePort
			ports:
			- port: 80
			  protocol: TCP
			selector:
			  app: nginx
	- name: create the service
	  command: "kubectl create -f $HOME/deploy_nginx/service1.yaml"

Note: Make sure that the alignment is correct.
NOTE: Update the master group in the third line of above content ( hosts: master ) based on your inventory file and host group name. This should refer to the master node (either VM2 or VM3)

  • Now execute the playbook:

ansible-playbook install-app.yaml

After Nginx app deployment, go to the master node and execute kubectl get services command to know the port number.
Get the port number (as shown in below figure)
Now in the browser hit master node's external_ip with the obtained port number(as shown in above fig). Output looks like below.

Take a screenshot of a webpage where your IP address are clearly seen.

5.2. Homework: Deploy your flask application

In this task, your are going deploy the flask application from Practice Session 3 and Exercise 5. This task need to be completed by yourself with minimum instructions noted below. The following are the changes need to make to install-app.yaml from above exercise.

  • Create directory deploy_flask, and update first task, i.e. Create target directory task.
  • Update second task, i.e. create the deployment file task, with following hints.
    • Update the path argument in lineinfile module (from $HOME/deploy_nginx/deploy1.yaml to $HOME/deploy_flask/deploy1.yaml)
    • Update the deployment name from nginx-deployment to flask-deployment
    • Update the labels: with app: flask
    • Update the container information; name, image, ports. Also add hostPort: 5000 to the ports list.
  • Update third task, i.e. create the deployment by running the kubectl command task.
    • Update the path to the yaml. The path should be same to the path of previous (or second) task.
  • Update forth task, i.e. create the service file task.
    • Update path vlaue,
    • Update service name and labels, as per your choice.
    • Update port number from 80 to 5000
    • Update selector as well
  • Update the kubectl command accordingly, in the fifth task, i.e. create the service task.

Finally, when the playbook is executed (on VM1) you should see the sensor data on the web page, similar to above Exercise 5.1
Take a screenshot of a webpage where your name and IP address are clearly seen.

Deliverables

  • Upload the screenshot taken wherever mentioned
  • Pack the screenshots into a single zip file and upload them through the following submission form.
  • Attach the playbooks from 5.1 and 5.2 in text file
  • Your instance must be terminated!

Deadline: 15th Oct 2021

4. Lab 4
Solutions for this task can no longer be submitted.

Bonus Task:

What to do

  • Create first VM manually
  • Prepare the Ansible playbook to:
    • Create VM2 and VM3
    • Install K8 and clreat the cluster using kubespray
    • Deploy your application atop cluster

Hint:

  • To create VMs using Ansible, you need to install Waldur
	sudo pip install six, setuptools_rusts, 
	pip install cryptography
	pip install dataclasses
	pip install waldur_core 
	pip install  waldur_ansible
	pip install ansible-waldur-module
  • Playbook to create VM. (Update access_token and mykey)
---
- hosts: local
  vars:
	access_token: "0f4afa15bdc1923015"
	api_url: "https://api.etais.ee/api/"
	instance_name: "ansible-vm2"
	mykey: "chinmayadehury"
  tasks:
	- name: Create an instance
	  waldur_marketplace_os_instance:
		access_token: " {{ access_token }} "
		api_url: "{{ api_url }}"
		flavor: m3.tiny
		floating_ip: auto
		image: "centos7"
		name: "{{ instance_name }}"
		project: "DevOpscourse-internal"
		offering: "Virtual machine in devops-cloud"
		ssh_key: "{{ mykey }}"
		subnet: devops-pi-sub-net
		system_volume_size: 10
		state: "present"

Deliverable (possibly in zip format):

  • Don't terminate the VM
  • give the Ansible playbooks
  • Give output of ansible --version command (the screenshot)
  • Give ansible configuration file
  • Institute of Computer Science
  • Faculty of Science and Technology
  • University of Tartu
In case of technical problems or questions write to:

Contact the course organizers with the organizational and course content questions.
The proprietary copyrights of educational materials belong to the University of Tartu. The use of educational materials is permitted for the purposes and under the conditions provided for in the copyright law for the free use of a work. When using educational materials, the user is obligated to give credit to the author of the educational materials.
The use of educational materials for other purposes is allowed only with the prior written consent of the University of Tartu.
Terms of use for the Courses environment