PHP & Web Development Blogs

Search Results For: monitoring
Showing 1 to 2 of 2 blog articles.
3909 views · 1 years ago

![Create Alarm and Monitoring on Custom Memory and Disk Metrics for Amazon EC2](https://images.ctfassets.net/vzl5fkwyme3u/2HgdCq2lZucMyuiYyYiQie/b7376c29a2f94799613e8c1cb8ff4d3b/AdobeStock_91111530.jpeg?w=1000)

Today I am going write a blog on how to Monitor Memory and Disk custom metrics and creating alarm in Ubuntu.

To do this, we can use Amazon CloudWatch, which provides a flexible, scalable and reliable solution for monitoring our server.

Amazon Cloud Watch will allow us to collect the custom metrics from our applications that we will monitor to troubleshoot any issues, spot trends, and configure operational performance. CloudWatch functions display alarms, graphs, custom metrics data and including statistics.

## Installing the Scripts

Before we start installing the scripts for monitoring, we should install all the dependent packages need to perform on Ubuntu.

First login to your AWS server, and from our terminal, install below packages

```ssh

sudo apt-get update

sudo apt-get install unzip

sudo apt-get install libwww-perl libdatetime-perl

```

### Now Install the Monitoring Scripts

Following are the steps to download and then unzip we need to configure the Cloud Watch Monitoring scripts on our server:

**1. In the terminal, we need to change our directory and where we want to add our monitoring scripts.**

**2. Now run the below command and download the source:**

```ssh

curl https://aws-cloudwatch.s3.amazonaws.com/downloads/CloudWatchMonitoringScripts-1.2.2.zip -O

```

**3. Now uncompress the currently downloaded sources using the following commands**

```ssh

unzip CloudWatchMonitoringScripts-1.2.2.zip && \

rm CloudWatchMonitoringScripts-1.2.2.zip && \

cd aws-scripts-mon

```

The directory will contain Perl scripts, because of the execution of these scripts only report memory run and disk space utilization metrics will run in our Ubuntu server.

Currently, our folder will contain the following files:

**mon-get-instance-stats.pl** - This Perl file is used to displaying the current utilization statistics reports for our AWS instance on which these file scripts will be executed.

**mon-put-instance-data.pl** - This Perl script file will be used for collecting the system metrics on our ubuntu server and which will send them to the Amazon Cloud Watch.

**awscreds.template** - This Perl script file will contain an example for AWS credentials keys and secret access key named with access key ID.

**CloudWatchClient.pm** - This Perl script file module will be used to simplify by calling Amazon Cloud Watch from using other scripts.

**LICENSE.txt** – This file contains the license details for Apache 2.0.

**NOTICE.txt** – This file contains will gives us information about Copyright notice.

**4. For performing the Cloud Watch operations, we need to confirm that whether our scripts have corresponding permissions for the actions:**

If we are associated with an IAM role with our EC2 Ubuntu instance, we need to verify that which will grant the permissions to perform the below-listed operations:

```ssh

cloudwatch:GetMetricStatistics

cloudwatch:PutMetricData

ec2:DescribeTags

cloudwatch:ListMetrics

```

Now we need to copy the ‘awscreds.template’ file into ‘awscreds.conf’ by using the command below and which will update the file with details of the AWS credentials.

```ssh

cp awscreds.template awscreds.conf

AWSAccessKeyId = my_access_key_id

AWSSecretKey = my_secret_access_key

```

Now we completed the configuration.

## mon-put-instance-data.pl

This Perl script file will collect memory, disk space utilization data and swap the current system details and then it makes handling a remote call to Amazon Cloud Watch to reports details to the collected cloud watch data as a custom metrics.

We can perform a simple test run, by running the below without sending data to Amazon CloudWatch

```ssh

./mon-put-instance-data.pl --mem-util --verify --verbose

```

Now we are going to set a cron for scheduling our metrics and we will send them to Amazon CloudWatch

**1. Now we need to edit the crontab by using below command:**

```ssh

crontab -e

```

**2. Now we will update the file using the following query which will disk space utilization and report memory for particular paths to Amazon CloudWatch in every five minutes:**

```ssh

*/5 * * * * ~/STORAGE/cloudwatch/aws-scripts-mon/mon-put-instance-data.pl --mem-util --mem-avail --mem-used --disk-space-util --disk-space-avail --disk-space-used --disk-path=/ --disk-path=/STORAGE --from-cron

```

If there is an error, the scripts will write an error message in our system log.

### Use of Options

**--mem-used**

The above command will collect the information about used memory and which will send the details of the reports in MBs into the MemoryUsed metrics. This will give us information about the metric counts memory allocated by applications and the OS as used.

**--mem-util**

The above command will collect the information about memory utilization in percentages and which will send the details of the Memory Utilization metrics and it will count the usage of the memory applications and the OS.

**--disk-space-util**

The above command will collect the information to collect the current utilized disk space and which will send the reports in percentages to the DiskSpaceUtilization for the metric and for the selected disks.

**--mem-avail**

The above command will collect the information about the available memory and which will send the reports in MBs to the MemoryAvailable metrics details. This is the metric counts memory allocated by the applications and the OS as used.

**--disk-path=PATH**

The above command will collect the information and will point out the which disk path to report disk space.

**--disk-space-avail**

The above command will collect the information about the available disk space and which will send the reports in GBs to the DiskSpaceAvailable metric for the selected disks.

**--disk-space-used**

The above command will collect the information about the disk space used and which will send the reports in GBs to the DiskSpaceUsed metric for the selected disks.

The PATH can specify to point or any of the files can be located on which are mounted point for the filesystem which needs to be reported.

If we want to points to the multiple disks, then specify both of the disks like below:

```ssh

--disk-path=/ --disk-path=/home

```

## Setting an Alarm for Custom Metrics

Before we are going to running our Perl Scripts, then we need to create an alarm that will be listed in our default metrics except for the custom metrics. You can see some default metrics are listed in below image:

![](https://4.bp.blogspot.com/-dYiNgtv5t4Q/XB-Kw76S49I/AAAAAAAAExM/DKKgzPUmVPUNpDqb0c57sVd3DjhY_3O6wCLcBGAs/s1600/aws.jpg)

Once we completed setting the cron, then the custom metrics will be located in Linux System Metrics.

Now we are going to creating the alarm for our custom metrics

**1. We need to open the cloudwatch console panel at https://console.aws.amazon.com/cloudwatch/home**

**2. Now navigate to the navigation panel, we need to click on Alarm and we can Create Alarm.**

**3. This will open a popup which with the list of the CloudWatch metrics by category.**

**4. Now click on the Linux System Metrics . This will be listed out with custom metrics you can see in the below pictures**

![](https://3.bp.blogspot.com/-3OxSPmHahYc/XB-MhlaGSAI/AAAAAAAAExY/d9UIOFakcGAOADlbNOyd75r0Vkl5GtOfwCLcBGAs/s1600/aws2.jpg)

![](https://2.bp.blogspot.com/-Urfud3sv5LA/XB-My0O-qXI/AAAAAAAAExg/1isOeglXYyoS3U0u2uhwJN8ddhtnoHUwACLcBGAs/s1600/aws3.jpg)

![](https://2.bp.blogspot.com/-tXU4ieWKKdo/XB-NDrArsII/AAAAAAAAExo/7oz3qQZ7GrMETiJwWJdK75_hJ0qfADLvgCLcBGAs/s1600/aws4.jpg)

**5. Now we need to select metric details and we need to click on the NEXT button. Now we need to navigate to Define Alarm step.**

![](https://3.bp.blogspot.com/-Vu0WoCCP_5o/XB-NiKGzmSI/AAAAAAAAExw/IPBX8ir-97Q_Q6L6Ajq2vNpukYGN8BB_QCLcBGAs/s1600/aws5.jpg)

**6. Now we need to define an Alarm with required fields**

Now we need to enter the Alarm name for identifying them. Then we need to give a description of our alarm.

Next, we need to give the condition with the maximum limit of bytes count or percentage when it notifies the alarm. If the condition satisfies, then the alarm will start trigger.

We need to provide a piece of additional information about for our alarm.

We need to define what are the actions to be taken when our alarm changes it state.

We need to select or create a new topic with emails needed for sending notification about alarm state.

**7. Finally, we need to choose the Create Alarm.**

So its completed. Now the alarm is created for our selected custom metrics.

### Finished!

Now the alarm will be listed out under the selected state in our AWS panel. Now we need to select an alarm from the list seen and we can see the details and history of our alarm.

2763 views · 1 years ago

![Creating a PHP Daemon Service](https://images.ctfassets.net/vzl5fkwyme3u/18L41PfcrcYYkM0qAsCous/7caca26b8cfb5a643d8cb16b14ae5eae/AdobeStock_147870533.jpeg?w=1000)

# What is a Daemon?

The term daemon was coined by the programmers of Project MAC at MIT. It is inspired on Maxwell's demon in charge of sorting molecules in the background. The UNIX systems adopted this terminology for daemon programs.

It also refers to a character from Greek mythology that performs the tasks for which the gods do not want to take. As stated in the "Reference System Administrator UNIX", in ancient Greece, the concept of "personal daemon" was, in part, comparable to the modern concept of "guardian angel." BSD family of operating systems use the image as a demon's logo.

Daemons are usually started at machine boot time. In the technical sense, a demon is considered a process that does not have a controlling terminal, and accordingly there is no user interface. Most often, the ancestor process of the deamon is init - process root on UNIX, although many daemons run from special rcd scripts started from a terminal console.

Richard Stevenson describes the following steps for writing daemons:

1. Resetting the file mode creation mask to 0 function umask(), to mask some bits of access rights from the starting process.

2. Cause fork() and finish the parent process. This is done so that if the process was launched as a group, the shell believes that the group finished at the same time, the child inherits the process group ID of the parent and gets its own process ID. This ensures that it will not become process group leader.

3. Create a new session by calling setsid(). The process becomes a leader of the new session, the leader of a new group of processes and loses the control of the terminal.

4. Make the root directory of the current working directory as the current directory will be mounted.

5. Close all file descriptors.

6. Make redirect descriptors 0,1 and 2 (STDIN, STDOUT and STDERR) to /dev/null or files /var/log/project_name.out because some standard library functions use these descriptors.

7. Record the pid (process ID number) in the pid-file: /var/run/projectname.pid.

8. Correctly process the signals and SigTerm SigHup: end with the destruction of all child processes and pid - files and / or re-configuration.

# How to Create Daemons in PHP

To create demons in PHP you need to use the extensions pcntl and posix. To implement the fast communication withing daemon scripts it is recommended to use the extension libevent for asynchronous I/O.

Lets take a closer look at the code to start a daemon:

```php

umask(0); / / § 1

$pid = pcntl_fork(); / / § 2

if ($pid < 0) {

print('fork failed');

exit 1;

}

```

After a fork, the execution of the program works as if there are two branches of the code, one for the parent process and the second for the child process. What distinguishes these two processes is the result value returned the fork() function call. The parent process ID receives the newly created process number and the child process receives a 0.

```php

if ($pid > 0) {/ / the parent process

echo "daemon process started

";

exit; / / Exit

}

/ / (pid = 0) child process

$sid = posix_setsid();/ / § 3

if ($sid < 0) {

exit 2;

}

chdir('/'); / / § 4

file_put_contents($pidFilename, getmypid() ); / / § 6

run_process(); / / cycle start data

```

The implementation of step 5 "to close all file descriptors" can be done in two ways. Well, closing all file descriptors is difficult to implement in PHP. You just need to open any file descriptors before fork(). Second, you can override the standard output to an error log file using init_set() or use buffering using ob_start() to a variable and store it in log file:

```php

ob_start(); / / slightly modified, § 5.

var_dump($some_object); / /some conclusions

$content = ob_get_clean(); / / takes part of the output buffer and clears it

fwrite($fd_log, $content); / / retains some of the data output to the log.

```

Typically, ob_start() is the start of the daemon life cycle and ob_get_clean() and fwrite() calls are the end. However, you can directly override STDIN, STDOUT and STDERR:

```php

ini_set('error_log', $logDir.'/error.log'); / / set log file

/ / $logDir - /var/log/mydaemon

/ / Closes an open file descriptors system STDIN, STDOUT, STDERR

fclose(STDIN);

fclose(STDOUT);

fclose(STDERR);

/ / redirect stdin to /dev/null

$STDIN = fopen('/dev/null', 'r');

/ / redirect stdout to a log file

$STDOUT = fopen($logDir.'/application.log', 'ab');

/ / redirect stderr to a log file

$STDERR = fopen($logDir.'/application.error.log', 'ab');

```

Now, our process is disconnected from the terminal and the standard output is redirected to a log file.

# Handling Signals

Signal processing is carried out with the handlers that you can use either via the library pcntl (pcntl_signal_dispatch()), or by using libevent. In the first case, you must define a signal handler:

```php

/ / signal handler

function sig_handler($signo)

{

global $fd_log;

switch ($signo) {

case SIGTERM:

/ / actions SIGTERM signal processing

fclose($fd_log); / / close the log-file

unlink($pidfile); / / destroy pid-file

exit;

break;

case SIGHUP:

/ / actions SIGHUP handling

init_data();/ / reread the configuration file and initialize the data again

break;

default:

/ / Other signals, information about errors

}

}

/ / setting a signal handler

pcntl_signal(SIGTERM, "sig_handler");

pcntl_signal(SIGHUP, "sig_handler");

```

Note that signals are only processed when the process is in an active mode. Signals received when the process is waiting for input or in sleep mode will not be processed. Use the wait function pcntl_signal_dispatch(). We can ignore the signal using flag SIG_IGN: pcntl_signal(SIGHUP, SIG_IGN); Or, if necessary, restore the signal handler using the flag SIG_DFL, which was previously installed by default: pcntl_signal(SIGHUP, SIG_DFL);

# Asynchronous I/O with Libevent

In the case you use blocking input / output signal processing is not applied. It is recommended to use the library libevent which provides non-blocking as input / output, processing signals, and timers. Libevent library provides a simple mechanism to start the callback functions for events on file descriptor: Write, Read, Timeout, Signal.

Initially, you have to declare one or more events with an handler (callback function) and attach them to the basic context of the events:

```php

$base = event_base_new(); / / create a context for monitoring basic events

/ / create a context of current events, one context for each type of events

$event = event_new();

$errno = 0;

$errstr = '';

/ / the observed object (handle)

$socket = stream_socket_server("tcp:/ /$IP:$port", $errno, $errstr);

stream_set_blocking($socket, 0); / / set to non-blocking mode

/ / set handler to handle

event_set($event, $socket, EV_READ | EV_PERSIST, 'onAccept', $base);

```

Function handlers 'onRead', 'onWrite', 'onError' must implement the processing logic. Data is written into the buffer, which is obtained in the non-blocking mode:

```php

function onRead($buffer, $id)

{

/ / reading from the buffer to 256 characters or EOF

while($read = event_buffer_read($buffer, 256)) {

var_dump($read);

}

}

```

The main event loop runs with the function event_base_loop($base);. With a few lines of code, you can exit the handler only by calling: event_base_loobreak(); or after the specified time (timeout) event_loop_exit();.

Error handling deals with failure Events:

```php

function onError($buffer, $error, $id)

{

/ / declare global variables as an option - class variables

global $id, $buffers, $ctx_connections;

/ / deactivate buffer

event_buffer_disable($buffers[$id], EV_READ | EV_WRITE);

/ / free, context buffer

event_buffer_free($buffers[$id]);

/ / close the necessary file / socketed destkriptory

fclose($ctx_connections[$id]);

/ / frees the memory occupied by the buffer

unset($buffers[$id], $ctx_connections[$id]);

}

```

It should be noted the following subtlety: Working with timers is only possible through the file descriptor. The example of official the documentation does not work. Here is an example of processing that runs at regular intervals.

```php

$event2 = event_new();

/ / use as an event arbitrary file descriptor of the temporary file

$tmpfile = tmpfile();

event_set($event2, $tmpfile, 0, 'onTimer', $interval);

$res = event_base_set($event2, $base);

event_add($event2, 1000000 * $interval);

```

With this code we can have a working timer finishes only once. If we need a "permanent" Timer, using the function onTimer we need create a new event each time, and reassign it to process through a "period of time":

```php

function onTimer($tmpfile, $flag, $interval)

{

$global $base, $event2;

if ($event2) {

event_delete($event2);

event_free($event2);

}

call_user_function(‘process_data’,$args);

$event2 = event_new();

event_set($event2, $tmpfile, 0, 'onTimer', $interval);

$res = event_base_set($event2, $base);

event_add($event2, 1000000 * $interval);

}

```

At the end of the daemon we must release all previously allocated resources:

```php

/ / delete the context of specific events from the database monitoring is performed for each event

event_delete($event);

/ / free the context of a particular event is executed for each event

event_free($event);

/ / free the context of basic events monitoring

event_base_free($base);

/ / bind event to the base context

event_base_set($event, $base);

/ / add/activate event monitoring

event_add($event);

```

Also it should be noted that for the signal processing handler is set the flag EV_SIGNAL: event_set($event, SIGHUP, EV_SIGNAL, 'onSignal', $base);

If needed constant signal processing, it is necessary to set a flag EV_PERSIST. Here follows a handler for the event onAccept, which occurs when a new connection is a accepted on a file descriptor:

```php

/ / function handler to the emergence of a new connection

function onAccept($socket, $flag, $base) {

global $id, $buffers, $ctx_connections;

$id++;

$connection = stream_socket_accept($socket);

stream_set_blocking($connection, 0);

/ / create a new buffer and tying handlers read / write access to the buffer or illustrations of error.

$buffer = event_buffer_new($connection, 'onRead', NULL, 'onError', $id);

/ / attach a buffer to the base context

event_buffer_base_set($buffer, $base);

/ / exhibiting a timeout if there is no signal from the source

event_buffer_timeout_set($buffer, 30, 30);

event_buffer_watermark_set($buffer, EV_READ, 0, 0xffffff); / / flag is set

event_buffer_priority_set($buffer, 10); / / set priority

event_buffer_enable($buffer, EV_READ | EV_PERSIST); / / flag is set

$ctx_connections[$id] = $connection;

$buffers[$id] = $buffer;

}

```

# Monitoring a Daemon

It is good practice to develop the application so that it was possible to monitor the daemon process. Key indicators for monitoring are the number of items processed / requests in the time interval, the speed of processing with queries, the average time to process a single request or downtime.

With the help of these metrics can be understood workload of our demon, and if it does not cope with the load it gets, you can run another process in parallel, or for running multiple child processes.

To determine these variables need to check these features at regular intervals, such as once per second. For example downtime is calculated as the difference between the measurement interval and total time daemon.

Typically downtime is determined as a percentage of a measurement interval. For example, if in one second were executed 10 cycles with a total processing time of 50ms, the time will be 950ms or 95%.

Query performance wile be 10rps (request per second). Average processing time of one request: the ratio of the total time spent on processing requests to the number of requests processed, will be 5ms.

These characteristics, as well as additional features such as memory stack size queue, number of transactions, the average time to access the database, and so on.

An external monitor can be obtain data through a TCP connection or unix socket, usually in the format of Nagios or zabbix, depending on the monitoring system. To do this, the demon should use an additional system port.

As mentioned above, if one worker process can not handle the load, usually we run in parallel multiple processes. Starting a parallel process should be done by the parent master process that uses fork() to launch a series of child processes.

Why not run processes using exec() or system()? Because, as a rule, you must have direct control over the master and child processes. In this case, we can handle it via interaction signals. If you use the exec command or system, then launch the initial interpreter, and it has already started processes that are not direct descendants of the parent process.

Also, there is a misconception that you can make a demon process through command nohup. Yes, it is possible to issue a command: nohup php mydaemon.php -master >> /var/log/daemon.log 2 >> /var/log/daemon.error.log &

But, in this case, would be difficult to perform log rotation, as nohup "captures" file descriptors for STDOUT / STDERR and release them only at the end of the command, which may overload of the process or the entire server. Overload demon process may affect the integrity of data processing and possibly cause partial loss of some data.

# Starting a Daemon

Starting the daemon must happen either automatically at boot time, or with the help of a "boot script."

All startup scripts are usually in the directory /etc/rc.d. The startup script in the directory service is made /etc/init.d/ . Run command start service myapp or start group /etc/init.d/myapp depending on the type of OS.

Here is a sample script text:

```sh

#! /bin/sh

#

$appdir = /usr/share/myapp/app.php

$parms = --master –proc=8 --daemon

export $appdir

export $parms

if [ ! -x appdir ]; then

exit 1

fi

if [ -x /etc/rc.d/init.d/functions ]; then

. /etc/rc.d/init.d/functions

fi

RETVAL=0

start () {

echo "Starting app"

daemon /usr/bin/php $appdir $parms

RETVAL=$?

[ $RETVAL -eq 0 ] && touch /var/lock/subsys/mydaemon

echo

return $RETVAL

}

stop () {

echo -n "Stopping $prog: "

killproc /usr/bin/fetchmail

RETVAL=$?

[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/mydaemon

echo

return $RETVAL

}

case $1 in

start)

start

;;

stop)

stop

;;

restart)

stop

start

;;

status)

status /usr/bin/mydaemon

;;

*)

echo "Usage: $0 {start|stop|restart|status}"

;;

RETVAL=$?

exit $RETVAL

```

# Distributing Your PHP Daemon

To distribute a daemon it is better to pack it in a single phar archive module. The assembled module should include all the necessary PHP and .ini files.

Below is a sample build script:

```php

if (is_file('app.phar')) {

unlink('app.phar');

}

$phar = new Phar('app.phar', 0, 'app.phar');

$phar->compressFiles(Phar::GZ);

$phar->setSignatureAlgorithm (Phar::SHA1);

$files = array();

$files['bootstrap.php'] = './bootstrap.php';

$rd = new RecursiveIteratorIterator(new RecursiveDirectoryIterator('.'));

foreach($rd as $file){

if ($file->getFilename() != '..' && $file->getFilename() != '.' && $file->getFilename() != __FILE__) {

if ( $file->getPath() != './log'&& $file->getPath() != './script'&& $file->getPath() != '.')

$files[substr($file->getPath().DIRECTORY_SEPARATOR.$file->getFilename(),2)] =

$file->getPath().DIRECTORY_SEPARATOR.$file->getFilename();

}

}

if (isset($opt['version'])) {

$version = $opt['version'];

$file = "buildFromIterator(new ArrayIterator($files));

$phar->setStub($phar->createDefaultStub('bootstrap.php'));

$phar = null;

}

```

Additionally, it may be advisable to make a PEAR package as a standard unix-console utility that when run with no arguments prints its own usage instruction:

```sh

#php app.phar

myDaemon version 0.1 Debug

usage:

--daemon – run as daemon

--debug – run in debug mode

--settings – print settings

--nofork – not run child processes

--check – check dependency modules

--master – run as master

--proc=[8] – run child processes

```

# Conclusion

Creating daemons in PHP it is not hard but to make them run correctly it is important to follow the steps described in this article.

Post a comment here if you have questions or comments on how to create daemon services in PHP.

    SPONSORS

    Faster PHP Cloud Hosting