Introduction
In today’s IT landscape, the automation of cloud infrastructure has become a pivotal aspect of managing scalable, reliable, and efficient systems. Oracle Cloud Infrastructure (OCI) is one such platform that benefits greatly from automation. This article will guide you through creating a compute instance pool and launching instances in OCI using Ansible, providing a detailed example and best practices.
Understanding Ansible
Ansible is an open-source automation tool that simplifies the management of complex IT environments. It uses a human-readable language, YAML, to describe automation jobs, known as playbooks. Ansible is agentless, using SSH or WinRM for communication, making it a secure and efficient choice for IT automation.
Key Features of Ansible
- Human-Readable Automation: Uses YAML for playbooks, making it easy to read and write.
- Agentless Architecture: Requires no agents on the managed nodes, reducing overhead and security risks.
- Cross-Platform Support: Works with Linux, Windows, UNIX, and network devices.
- Extensible: Can be extended with modules and plugins written in any language.
Setting Up the Environment
Before diving into the playbook, ensure you have the following prerequisites:
- Ansible Installed: Ensure Ansible is installed on your control node. You can install it using pip:
pip install ansible
- OCI CLI Configured: Configure the OCI CLI on your control node. Follow the OCI CLI installation guide.
Sample Playbook: Creating a Compute Instance Pool
Below is a sample Ansible playbook to create a compute instance pool in OCI and launch instances.
---
# Copyright (c) 2020, 2024 Oracle and/or its affiliates.
# This software is made available to you under the terms of the GPL 3.0 license or the Apache 2.0 license.
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)
# Apache License v2.0
# See LICENSE.TXT for details.
- name: Create a compute instance pool and launch instances
hosts: localhost
collections:
- oracle.oci
vars:
# Common networking definitions
quad_zero_route: "0.0.0.0/0"
TCP_protocol: "6"
SSH_port: "22"
vcn_name: "ansible-sample-create-instance-pool-vcn"
vcn_cidr_block: "10.0.0.0/16"
vcn_dns_label: "samplevcn"
ig_name: "ansible-sample-create-instance-pool-ig"
route_table_name: "ansible-sample-create-instance-pool-rt"
# Route all internet access to our Internet Gateway
route_table_rules:
- cidr_block: "{{ quad_zero_route }}"
network_entity_id: "{{ ig_id }}"
subnet_cidr: "10.0.0.48/28"
subnet_name: "ansible-sample-create-instance-pool-subnet"
subnet_dns_label: "samplesubnet"
securitylist_name: "ansible-sample-create-instance-pool-sec-list"
instance_shape: "{{ lookup('env', 'SAMPLE_INSTANCE_SHAPE') | default('VM.Standard2.1', true) }}"
instance_display_name: "ansible-sample-create-instance-pool-instance"
instance_pool_configuration_name: "ansible-sample-create-instance-pool-configuration"
instance_pool_name: "ansible-sample-create-instance-pool"
#########################################
# Tenancy specific configuration
# *Note* - Override the following variables based on your tenancy
# or set a valid value for the corresponding environment variable
#########################################
instance_ad: "{{ lookup('env', 'SAMPLE_AD_NAME') }}"
instance_compartment: "{{ lookup('env', 'SAMPLE_COMPARTMENT_OCID') }}"
# Provide an "OL" image
instance_image: "{{ lookup('env', 'SAMPLE_IMAGE_OCID') }}"
tasks:
- block:
- import_tasks: setup.yaml
#==========================================================================================
- name: Create a new basic compute instance configuration that has launch_details and VNIC configuration
oci_compute_management_instance_configuration:
compartment_id: "{{instance_compartment}}"
name: "{{ instance_pool_configuration_name }}"
instance_details:
instance_type: "compute"
launch_details:
compartment_id: "{{instance_compartment}}"
create_vnic_details:
assign_public_ip: True
display_name: "{{instance_display_name}}"
display_name: "{{instance_display_name}}"
shape: "{{instance_shape}}"
source_details:
source_type: "image"
image_id: "{{instance_image}}"
metadata:
ssh_authorized_keys: "{{ lookup('file', my_test_public_key ) }}"
register: result
- name: Print instance configuration details
debug:
msg: "Created a new instance configuration {{ result.instance_configuration }}"
- set_fact:
instance_configuration_id: "{{result.instance_configuration.id }}"
#==========================================================================================
- name: Create a new instance pool from this instance configuration
oci_compute_management_instance_pool:
name: "{{ instance_pool_name }}"
compartment_id: "{{instance_compartment}}"
instance_configuration_id: "{{instance_configuration_id}}"
# Launch 2 instances when pool is created
size: 2
placement_configurations:
- availability_domain: "{{instance_ad}}"
primary_subnet_id: "{{instance_subnet_id}}"
register: result
- set_fact:
instance_pool_id: "{{result.instance_pool.id}}"
- name: Print instance pool details
debug:
msg: "Created a new instance pool {{ result.instance_pool }}"
#==========================================================================================
- name: Get instance details of instances in the instance pool
oci_compute_management_instance_pool_instance_facts:
compartment_id: "{{instance_compartment}}"
id: "{{instance_pool_id}}"
register: result
- name: Print details of instances in the new instance pool
debug:
msg: "Instances in instance pool {{instance_pool_id}} are {{ result.instance_pool_instances }}"
- name: Get the first instance from the pool
set_fact:
instance_id: "{{result.instance_pool_instances[0].id}}"
#==========================================================================================
- name: Get the VNIC attachment details of that instance
oci_compute_vnic_attachment_facts:
compartment_id: "{{ instance_compartment }}"
instance_id: "{{ instance_id }}"
register: result
- name: Get details of the VNIC of that VNIC attachment
oci_network_vnic_facts:
id: "{{ result.vnic_attachments[0].vnic_id }}"
register: result
- set_fact:
instance_public_ip: "{{result.vnic.public_ip}}"
- name: Print the public ip of the newly launched instance
debug:
msg: "Public IP of launched instance {{ instance_public_ip }}"
#==========================================================================================
- name: Wait (upto 10 minutes) for port 22 to become open
wait_for:
port: 22
host: '{{ instance_public_ip }}'
state: started
delay: 10
timeout: 600
vars:
ansible_connection: local
#==========================================================================================
- name: Attempt a ssh connection to the newly launched instance
# Use "opc" user as this is an OL image
# Disable SSH's strict host key checking just for this one command invocation
command: ssh -o "UserKnownHostsFile=/dev/null" -o "StrictHostKeyChecking=no" -i {{ temp_certificates_path }}/private_key.pem opc@{{ instance_public_ip }} uname -a
retries: 3
delay: 5
register: result
until: result.rc == 0
- name: Print SSH response from launched instance
debug:
msg: "SSH response from instance -> {{ result.stdout_lines }}"
#==========================================================================================
- import_tasks: teardown.yaml
rescue:
- import_tasks: teardown.yaml
ignore_errors: yes
- fail:
msg: "{{ ansible_failed_result }}"
Code from https://github.com/oracle/oci-ansible-collection/tree/master/samples/compute/create_instance_pool
Explanation of the Playbook
- Setup: This section imports the
setup.yaml
task file to initialize the environment. - Instance Configuration: It creates a new instance configuration with details about the instance type, shape, and VNIC settings.
- Instance Pool Creation: It creates an instance pool using the previously defined configuration and launches two instances.
- VNIC Details: Retrieves and prints the VNIC attachment details of the instances.
- SSH and Validation: Waits for SSH port 22 to become available and attempts an SSH connection to validate the instance is running correctly.
- Teardown: This section imports the
teardown.yaml
task file to clean up resources if needed.
Best Practices for Ansible with OCI
- Modular Playbooks: Break down your playbooks into smaller, reusable tasks using
import_tasks
. - Environment Variables: Use environment variables for sensitive information to avoid hardcoding them in playbooks.
- Error Handling: Use Ansible’s error handling mechanisms (
block
,rescue
,ignore_errors
) to manage failures gracefully. - Testing: Regularly test your playbooks in a staging environment before applying them in production.
Conclusion
Automating OCI with Ansible can significantly streamline your cloud operations, reduce human error, and enhance overall efficiency. By following the example playbook and best practices outlined in this article, you can create and manage compute instance pools effectively. Embrace automation to stay ahead in the dynamic world of cloud computing.
For further reading and advanced topics, consider exploring additional resources and official documentation provided by Oracle and the Ansible community.
Subscribe to the YouTube channel, Medium, and Website, X (formerly Twitter) to not miss the next episode of the Ansible Pilot.Academy
Learn the Ansible automation technology with some real-life examples in my Udemy 300+ Lessons Video Course.
My book Ansible By Examples: 200+ Automation Examples For Linux and Windows System Administrator and DevOps
Donate
Want to keep this project going? Please donate