Example: Volume Automation

Explanation

This example shows off how cloud-init can be configured to spin up a custom LAMP server. Additionally, this example shows how python can be incorporated to use cloud services API to create and attach a volume. Each section broken down follows.

timezone:

timezone: quickly sets the locale of this instance

bootcmd:

                Looking at https://stackoverflow.com/questions/34095839/cloud-init-what-is-the-execution-order-of-cloud-config-directives , bootcmd is the third module ran and commands listed here are ran every boot, instead of running during the instance initialization. The command in the example is disabling SELinux for the current session. Disabling SELinux via its configuration file requires a reboot

package_upgrade:

                As discussed in Automate System Patches, this variable, when true, updates the OS via yum update or apt-get update && apt-get upgrade

packages:

                As discussed in Automate System Patches, this list of packages is installed via yum install or apt-get install

users:

                As discussed in Automate System Patches, the users section of the configuration file creates the user and sets user permissions

runcmd:

                Looking at https://stackoverflow.com/questions/34095839/cloud-init-what-is-the-execution-order-of-cloud-config-directives runcmd is ran much later in the initialization process. So any files written via cloud-init can be ran here.

                This tutorial adds a php repo, and yum installs a newer php and python release. The commands go on to start and enable services, unzip the application that will be running on this machine, change ownership of directories, add httpd rules to the firewall, run sql scripts that are written in the latter part of this example, and change python environments to run the volume create and attach script.

write_files:

                This section is where files can be written by providing first the desired path of the file, and then providing the files’ contents.

‘sqlinit.sh’ is writing commands to mariadb, mimicking the mysql_secure_installation script. ‘Sqlnextcloud.sh’ writes commands to create the database for the main application ran on this instance.

‘attachcreate.py’ first connects to the api using the ec2 access and secret keys generated via the webUI. Then the client object calls the create_volume method with a few parameters, then ID of the current instance is requested, and then the volume is attached via the attach_volume method.

The volume is ready to be formatted, mounted, and fstab will need to edited. As a challenge, include those commands to the example. 

Configuration Example

#cloud-config

timezone: US/Central

bootcmd:

 - setenforce 0

package_upgrade: True

packages:

 - wget

 - epel-release

 - httpd

 - git

 - vim

 - yum-utils

 - bzip2

 - mariadb-server

 - mariadb

 - samba-client

users:

 - name: demo

   ssh-authorized-keys:

     - ssh-rsa sshkey1line

   sudo: ['ALL=(ALL) NOPASSWD:ALL']

   groups: wheel

   shell: /bin/bash

runcmd:

 - rpm -Uvh http://rpms.remirepo.net/enterprise/remi-release-7.rpm

 - yum-config-manager --enable remi-php70

 - yum -y install php php-mysql php-pecl-zip php-xml php-mbstring php-gd php-smbclient https://centos7.iuscommunity.org/ius-release.rpm

 - yum -y install python36u

 - yum install python36u-pip

 - yum install python36u-devel

 - systemctl start httpd; systemctl enable httpd

 - systemctl start mariadb; systemctl enable mariadb

 - wget https://download.nextcloud.com/server/releases/nextcloud-12.0.3.tar.bz2

 - tar -jxpvf nextcloud-12.0.3.tar.bz2  -C /var/www/html/

 - chown -R apache:apache /var/www/html/nextcloud

 - firewall-cmd --permanent --zone=public --add-service=http

 - firewall-cmd --permanent --zone=public --add-service=https

 - firewall-cmd --reload

 - chmod u+x /sqlinit.sh /sqlnextcloud.sh

 - /./sqlinit.sh

 - /./sqlnextcloud.sh

 - mkdir /awsapi

 - cd /awsapi

 - python3.6 -m venv awsapi

 - source awsapi/bin/activate

 - pip install awscli boto3 requests

 - python /attachcreate.py

write_files:

 - path: /sqlinit.sh

   content: |

     #!/bin/bash

     mysqladmin -u root password "root"

     mysql -u root -p"root" -e "UPDATE mysql.user SET Password=PASSWORD('root') WHERE User='root'"

     mysql -u root -p"root" -e "DELETE FROM mysql.user WHERE User='root' AND Host NOT IN ('localhost', '127.0.0.1', '::1')"

     mysql -u root -p"root" -e "DELETE FROM mysql.user WHERE User=''"

     mysql -u root -p"root" -e "DELETE FROM mysql.db WHERE Db='test' OR Db='test\_%'"

     mysql -u root -p"root" -e "FLUSH PRIVILEGES"

 - path: /sqlnextcloud.sh

   content: |

     #!/bin/bash

     mysql -u root -p"root" -e "create database nextcloud"

     mysql -u root -p"root" -e "grant all on nextcloud.* to 'nextcloud'@'localhost' identified by 'nextcloud'"

     mysql -u root -p"root" -e "FLUSH PRIVILEGES"

 - path: /attachcreate.py

   content: |

     import boto3

     import sys

     import requests

 

     class Connection:

         def __init__(self):

             self.client = boto3.Session.client(boto3.session.Session(), service_name="ec2", region_name="symphony",

             endpoint_url="https://198.199.56.210/api/v2/ec2/",

             verify=False,

             aws_access_key_id="123456789abc",

             aws_secret_access_key="zxyabc987654321")

    

         def createattachVol(self):

             vol = self.client.create_volume(Size=100,AvailabilityZone='symphony')

             response = requests.get('http://169.254.169.254/latest/meta-data/instance-id')

             instance_id = response.text

             attach_resp = self.client.attach_volume(

                 VolumeId=vol['VolumeId'],

                 InstanceId=instance_id,

                 Device='/dev/sdm'

             )

    

         def testID(self):

             instances = self.client.describe_instances()

             expectedID = requests.get('http://169.254.169.254/latest/meta-data/instance-id').text

             for row in instances['Reservations']:

                 if row['Instances'][0]['InstanceId'] == expectedID:

                     return True

     

     if __name__ == "__main__":

         client = Connection()

         if client.testID():

             client.createattachVol()

Last modified: Thursday, 16 November 2017, 6:13 PM