Use the Ansible ali_ros_stack module to deploy a LNMP (Linux, NGINX, MySQL, PHP) environment through Resource Orchestration Service (ROS) API operations. The Ansible playbook references a ROS template that provisions an Elastic Compute Service (ECS) instance with NGINX, MariaDB, and PHP installed and configured.
Prerequisites
Before you begin, make sure you have:
Ansible installed and configured on a Linux system by using pip3
A valid Alibaba Cloud AccessKey pair configured for Ansible
Basic familiarity with Ansible playbooks and ROS templates
How it works
The deployment uses two files:
| File | Type | Purpose |
|---|---|---|
create_lnmp.yml | Ansible playbook | Defines the automation task and passes parameters to the ROS template |
create_lnmp_instance.json | ROS template | Defines the cloud resources (VPC, vSwitch, security group, and ECS instance) and software installation steps |
When you run the playbook, Ansible calls the ROS API to create a stack based on the template. The stack provisions all required infrastructure and installs the LNMP software on the ECS instance.
Step 1: Create the Ansible playbook
Create a file named
create_lnmp.yml.vi create_lnmp.ymlAdd the following content to
create_lnmp.yml. The following table describes the template parameters.Note For more information about the ali_ros_stack module parameters, see Parameters.Parameter Description Constraints ZoneIdThe zone where the ECS instance is created. Must be a valid ECS availability zone ID. ImageIdThe image used to create the ECS instance. Only CentOS 7 is supported. Must be a valid CentOS 7 image ID. InstancePasswordThe login password for the ECS instance. 8 to 30 characters. Must contain at least three of the following: uppercase letters, lowercase letters, digits, and special characters. SystemDiskCategoryThe system disk type. Valid values: cloud_ssd(standard SSD),cloud_efficiency(ultra disk). Default:cloud_ssd.InstanceTypeThe ECS instance type. Default: ecs.c5.large. Check the Instance families page to verify availability in your zone.DBNameThe name of the MySQL database. 1 to 64 characters. Must start with a letter. Can contain letters, digits, periods (.), underscores (\_), and hyphens (-). Default: MyDatabase.DBUserThe username for MySQL database access. Up to 16 characters. Must start with a letter. Can contain lowercase letters, digits, and underscores (\_). Default: DefaultUser.DBRootPasswordThe root password for MySQL. 6 to 32 characters. Can contain letters, digits, and underscores (\_). DBPasswordThe password for the MySQL database user. 6 to 32 characters. Can contain letters, digits, and underscores (\_). NginxDownloadUrlThe download URL for the NGINX RPM package. Default: http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm.- hosts: localhost remote_user: root tasks: - name: Create LNMP Instance ali_ros_stack: state: present stack_name: create_lnmp_instance template: create_lnmp_instance.json # Path to the ROS template file timeout_in_minutes: 60 # Maximum time to wait for stack creation template_parameters: ZoneId: <zone-id> # Example: cn-beijing-g ImageId: <image-id> # Example: centos_7_03_64_20G_alibase_2017****.vhd InstancePassword: <instance-password> # ECS instance login password SystemDiskCategory: cloud_ssd # Standard SSD InstanceType: ecs.c5.large # ECS instance type DBName: MyDatabase # MySQL database name DBUser: DefaultUser # MySQL database username DBRootPassword: <db-root-password> # MySQL root password DBPassword: <db-password> # MySQL user password NginxDownloadUrl: http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpmSave the file and exit the text editor.
Step 2: Create the ROS template
Create a file named
create_lnmp_instance.json.vi create_lnmp_instance.jsonAdd the following ROS template content to
create_lnmp_instance.json. The template creates the following resources: The ECS instanceUserDatascript installs and configures the following software:NGINX (downloaded and installed via yum)
PHP and PHP-FPM with extensions (php-mysql, php-gd, php-ldap, php-odbc, php-pear, php-xml, php-xmlrpc, php-mbstring, php-bcmath, php-mhash, php-mcrypt, and libjpeg)
MariaDB (a MySQL-compatible database server)
A
test.phppage that verifies the MySQL connection and displays PHP configuration
WarningThe security group in this template opens all ports to all IP addresses (
0.0.0.0/0). This configuration is intended for testing purposes only. For production environments, restrict security group rules to allow only the ports and source IP addresses that your application requires.Resource Type Details VPC ALIYUN::ECS::VPCCIDR block: 192.168.0.0/16vSwitch ALIYUN::ECS::VSwitchCIDR block: 192.168.1.0/24Security group ALIYUN::ECS::SecurityGroupAllows all inbound and outbound traffic ECS instance ALIYUN::ECS::InstanceI/O optimized, maximum outbound Internet bandwidth: 80 Mbps Wait condition ALIYUN::ROS::WaitConditionTimeout: 1,800 seconds. Confirms that software installation is complete. { "Description": "Deploy LNMP(Linux+Nginx+MySQL+PHP) stack on 1 ECS instance. *** WARNING *** Only support CentOS-7.", "Parameters": { "NginxDownloadUrl": { "Type": "String", "Description": { "en": "The download path of nginx-*.rpm", "en-us": "The download path of nginx-*.rpm." }, "Label": "Nginx Download Url", "Default": "http://nginx.org/packages/centos/7/noarch/RPMS/nginx-release-centos-7-0.el7.ngx.noarch.rpm" }, "DBPassword": { "NoEcho": true, "Type": "String", "Description": { "en": "The MySQL password, consisting of letters, numbers, and underline(_), 6 to 32 characters in length", "en-us": "The MySQL password, consisting of letters, numbers, and underline(_), 6 to 32 characters in length" }, "Label": "DB Password", "ConstraintDescription": "Consisting of letters, numbers, and underline(_), 6 to 32 characters in length", "MinLength": 6, "MaxLength": 32 }, "ZoneId": { "Type": "String", "AssociationProperty": "ALIYUN::ECS::Instance:ZoneId", "Description": { "en": "ECS Available Zone ID,</font><a href='https://www.alibabacloud.com/help/doc-detail/123712.html' target='_blank'><b> View region and zone info</b><font color='blue'></a>", "en-us": "ECS Available Zone ID,</font><a href='https://www.alibabacloud.com/help/doc-detail/123712.htm?spm=a2c63.l28256.b99.10.19347453Kki9VF' target='_blank'><b> Regions and zones</b><font color='blue'></a>" }, "Label": "Available Zone ID" }, "ImageId": { "Type": "String", "Description": { "en": "Image ID, represents the image resource to startup one ECS instance, <font><a href='https://www.alibabacloud.com/help/doc-detail/112977.html' target='_blank'><b>View image resources</b></font color='blue'></a>", "en-us": "Image ID, represents the image resource to startup one ECS instance, <font><a href='https://www.alibabacloud.com/help/doc-detail/112977.html' target='_blank'><b>Find an image</b></font color='blue'></a>" }, "Label": "Image ID", "Default": "cent****" }, "DBName": { "Type": "String", "Description": { "en": "MySQL database name, [1, 64] English or Chinese characters, must start with a letter or Chinese in size, can contain numbers, '_' or '.', '-'.", "en-us": "The name of the MySQL database. It must be 1 to 64 characters in length and can contain letters, digits, periods (.), underscores (_), and hyphens (-). It must start with a letter." }, "Label": "DB Name", "ConstraintDescription": "Must begin with a letter and contain only alphanumeric characters.", "MinLength": 1, "MaxLength": 64, "Default": "MyDatabase" }, "DBUser": { "Type": "String", "Description": { "en": "Username for MySQL database access. It consists of lowercase letters, numbers and underscores (_), and begins with a letter. Not longer than 16 characters.", "en-us": "The username used to access the MySQL database. The name can be up to 16 characters in length and can contain letters, digits and underscores (_). It must start with a letter." }, "Label": "DB Username", "ConstraintDescription": "Must begin with a letter and contain only alphanumeric characters.", "MinLength": 1, "MaxLength": 16, "Default": "DefaultUser" }, "DBRootPassword": { "NoEcho": true, "Type": "String", "Description": { "en": "Root password for MySQL, consisting of letters, numbers, and underline(_), 6 to 32 characters in length", "en-us": "The root password used to access the MySQL database. The password can be 6-32 characters in length and can contain letters, digits and underscores (_)." }, "Label": "DB Root Password", "ConstraintDescription": "Consisting of letters, numbers, and underline(_), 6 to 32 characters in length", "MinLength": 6, "MaxLength": 32 }, "InstanceType": { "Type": "String", "Description": { "en": "The ECS instance type, go to the product console to ensure the current instance is available, <font><a href='https://www.alibabacloud.com/help/doc-detail/25378.html' target='_blank'><b>View instance types</b></font color='blue'></a>", "en-us": "The ECS instance type, go to the product console to ensure the current instance is available, <font><a href='https://www.alibabacloud.com/help/doc-detail/25378.html' target='_blank'><b>Instance families</b></font color='blue'></a>" }, "Label": "Instance Type", "Default": "ecs.c5.large" }, "SystemDiskCategory": { "Type": "String", "Description": { "en": "System disk category: standard SSD (cloud_ssd) or ultra disk (cloud_efficiency)", "en-us": "The type of the system disk, which can be ultra disk (cloud_efficiency) or standard SSD (cloud_ssd)." }, "AllowedValues": [ "cloud_efficiency", "cloud_ssd" ], "Label": "System Disk Category", "Default": "cloud_ssd" }, "InstancePassword": { "NoEcho": true, "Type": "String", "Description": { "en": "The 8-30 long login password of instance, consists of the uppercase, lowercase letter and number. <br> special characters include ( ) ` ~ ! @ # $ % ^ & * _ - + = | { } [ ] : ; ' < > , . ? / ", "en-us": "It must be 8 to 30 characters in length and contain at least three of the following character types: uppercase letters, lowercase letters, digits, and special characters. Special characters include ( ) ` ~ ! @ # $ % ^ & * _ - + = | { } [ ] : ; ' < > , . ? /" }, "AllowedPattern": "[0-9A-Za-z\\_\\-&:;'<>,=%`~! @#\\(\\)\\$\\^\\*\\+\\|\\{\\}\\[\\]\\. \\? \\/]+$", "Label": "Instance Password", "ConstraintDescription": "Length 8-30, must contain upper case letters, lower case letters, Numbers, special symbols three; special characters include: ( ) ` ~ ! @ # $ % ^ & * _ - + = | { } [ ] : ; ' < > , . ? /", "MinLength": "8", "MaxLength": "30" } }, "ROSTemplateFormatVersion": "2015-09-01", "Metadata": { "ALIYUN::ROS::Interface": { "ParameterGroups": [ { "Parameters": [ "ZoneId", "ImageId", "InstanceType", "SystemDiskCategory", "InstancePassword" ], "Label": { "default": "ECS" } }, { "Parameters": [ "DBName", "DBUser", "DBPassword", "DBRootPassword" ], "Label": { "default": "DATABASE" } }, { "Parameters": [ "NginxDownloadUrl" ], "Label": { "default": "Nginx" } } ], "TemplateTags": [ "Deploy LNMP(Linux+Nginx+MySQL+PHP) stack on 1 ECS instance." ] } }, "Outputs": { "NginxWebsiteURL": { "Description": "URL for newly created Nginx home page.", "Value": { "Fn::Join": [ "", [ "http://", { "Fn::GetAtt": [ "WebServer", "PublicIp" ] }, ":80/test.php" ] ] } } }, "Resources": { "VSwitch": { "Type": "ALIYUN::ECS::VSwitch", "Properties": { "VpcId": { "Fn::GetAtt": [ "Vpc", "VpcId" ] }, "ZoneId": { "Ref": "ZoneId" }, "CidrBlock": "192.168.1.0/24" } }, "WebServerConditionHandle": { "Type": "ALIYUN::ROS::WaitConditionHandle" }, "WebServer": { "Type": "ALIYUN::ECS::Instance", "Properties": { "InternetMaxBandwidthOut": 80, "IoOptimized": "optimized", "VpcId": { "Fn::GetAtt": [ "Vpc", "VpcId" ] }, "UserData": { "Fn::Replace": [ { "ros-notify": { "Fn::GetAtt": [ "WebServerConditionHandle", "CurlCli" ] } }, { "Fn::Join": [ "", [ "#! /bin/bash \n", "NginxUrl=", { "Ref": "NginxDownloadUrl" }, "\n", "dbname=", { "Ref": "DBName" }, "\n", "dbuser=", { "Ref": "DBUser" }, "\n", "dbpassword=", { "Ref": "DBPassword" }, "\n", "dbrootpassword=", { "Ref": "DBRootPassword" }, "\n", "export HOME=/root \n", "export HOSTNAME=`hostname` \n", "systemctl stop firewalld.service \n", "systemctl disable firewalld.service \n", "sed -i 's/^SELINUX=/# SELINUX=/' /etc/selinux/config \n", "sed -i '/# SELINUX=/a SELINUX=disabled' /etc/selinux/config \n", "setenforce 0 \n", "yum install yum-priorities -y \n", "yum -y install aria2 \n", "aria2c $NginxUrl \n", "rpm -ivh nginx-*.rpm \n", "yum -y install nginx \n", "systemctl start nginx.service \n", "systemctl enable nginx.service \n", "yum -y install php-fpm \n", "systemctl start php-fpm.service \n", "systemctl enable php-fpm.service \n", "sed -i '/FastCGI/,/htaccess/s/ #/ /' /etc/nginx/conf.d/default.conf \n", "sed -i '/FastCGI/s/^ / #/' /etc/nginx/conf.d/default.conf \n", "sed -i '/htaccess/s/^ / #/' /etc/nginx/conf.d/default.conf \n", "sed -i '/SCRIPT_FILENAME/s/\\/scripts/\\/usr\\/share\\/nginx\\/html\\//' /etc/nginx/conf.d/default.conf \n", "yum -y install mariadb mariadb-server \n", "systemctl start mariadb.service \n", "systemctl enable mariadb.service \n", "yum -y install php php-mysql php-gd libjpeg* php-ldap php-odbc php-pear php-xml php-xmlrpc php-mbstring php-bcmath php-mhash php-mcrypt \n", "MDSRING=`find / -name mbstring.so` \n", "echo extension=$MDSRING >> /etc/php.ini \n", "systemctl restart mariadb.service \n", "mysqladmin -u root password \"$dbrootpassword\" \n", "$(mysql $dbname -u root --password=\"$dbrootpassword\" >/dev/null 2>&1 </dev/null); (( $?!=0 )) \n", "echo CREATE DATABASE $dbname \\; > /tmp/setup.mysql \n", "echo GRANT ALL ON $dbname. * TO \"$dbuser\"@\"localhost\" IDENTIFIED BY \"'$dbpassword'\" \\; >> /tmp/setup.mysql \n", "mysql -u root --password=\"$dbrootpassword\" < /tmp/setup.mysql \n", "$(mysql $dbname -u root --password=\"$dbrootpassword\" >/dev/null 2>&1 </dev/null); (( $?!=0 )) \n", "cd /root \n", "systemctl restart php-fpm.service \n", "systemctl restart nginx.service \n", "echo \\<? php > /usr/share/nginx/html/test.php \n", "echo \\$conn=mysql_connect\\(\"'127.0.0.1'\", \"'$dbuser'\", \"'$dbpassword'\"\\)\\; >> /usr/share/nginx/html/test.php \n", "echo if \\(\\$conn\\){ >> /usr/share/nginx/html/test.php \n", "echo echo \\\"LNMP platform connect to mysql is successful\\! \\\"\\; >> /usr/share/nginx/html/test.php \n", "echo }else{ >> /usr/share/nginx/html/test.php \n", "echo echo \\\"LNMP platform connect to mysql is failed\\! \\\"\\; >> /usr/share/nginx/html/test.php \n", "echo } >> /usr/share/nginx/html/test.php \n", "echo phpinfo\\(\\)\\; >> /usr/share/nginx/html/test.php \n", "echo \\? \\> >> /usr/share/nginx/html/test.php \n", "ros-notify -d '{\"data\" : \"Install LNMP stack.\"}'\n" ] ] } ] }, "SecurityGroupId": { "Ref": "SecurityGroup" }, "VSwitchId": { "Ref": "VSwitch" }, "ImageId": { "Ref": "ImageId" }, "InstanceType": { "Ref": "InstanceType" }, "SystemDiskCategory": { "Ref": "SystemDiskCategory" }, "Password": { "Ref": "InstancePassword" } } }, "WebServerWaitCondition": { "Type": "ALIYUN::ROS::WaitCondition", "DependsOn": "WebServer", "Properties": { "Timeout": 1800, "Count": 1, "Handle": { "Ref": "WebServerConditionHandle" } } }, "Vpc": { "Type": "ALIYUN::ECS::VPC", "Properties": { "CidrBlock": "192.168.0.0/16" } }, "SecurityGroup": { "Type": "ALIYUN::ECS::SecurityGroup", "Properties": { "VpcId": { "Ref": "Vpc" }, "SecurityGroupIngress": [ { "PortRange": "-1/-1", "Priority": 1, "SourceCidrIp": "0.0.0.0/0", "IpProtocol": "all", "NicType": "intranet" } ], "SecurityGroupEgress": [ { "PortRange": "-1/-1", "Priority": 1, "IpProtocol": "all", "DestCidrIp": "0.0.0.0/0", "NicType": "intranet" } ] } } } }Save the file and exit the text editor.
Step 3: Run the playbook
Run the Ansible playbook to deploy the LNMP environment.
ansible-playbook create_lnmp.ymlThe deployment may take several minutes. Ansible calls the ROS API to create a stack, which provisions the VPC, vSwitch, security group, and ECS instance. After the ECS instance starts, the UserData script installs and configures NGINX, MariaDB, and PHP.
Verify the deployment
After the stack is created, verify that the LNMP environment is running:
Log in to the ROS console and find the stack named
create_lnmp_instance.On the Outputs tab of the stack details page, find the
NginxWebsiteURLvalue. The URL format ishttp://<ECS-public-IP>:80/test.php.Open the URL in a web browser. If the deployment is successful, the page displays a message confirming the MySQL connection and the PHP configuration information.
Clean up resources
To avoid ongoing charges, delete the ROS stack when you no longer need the LNMP environment. Delete the stack from the ROS console or modify the playbook to set state: absent:
- hosts: localhost
remote_user: root
tasks:
- name: Delete LNMP Instance
ali_ros_stack:
state: absent
stack_name: create_lnmp_instanceRun the updated playbook:
ansible-playbook create_lnmp.ymlDeleting the stack removes all resources that the template created, including the VPC, vSwitch, security group, and ECS instance.