Memcached is a distributed object caching system which stores information in memory, rather than on disk, for faster access. PHP’s Memcache module can be used to handle sessions which would otherwise be stored on the file system. Storing PHP sessions in Memcached has the advantage of being able to distribute them to multiple cloud servers running Memcached, so as to maintain session redundancy.
Without this Memcached setup, if your application is being load balanced on multiple servers, it would be necessary to configure session stickiness on the load balancer. This maintains user experience and prevents them from being logged off suddenly. Configuring Memcached to handle sessions will ensure all cloud servers in the Memcached pool have the same set of session data, which eliminates the need to be sticky with one server to preserve the session.
This tutorial assumes you are familiar with setting up LAMP servers in Ubuntu. This setup will make use of 3 Droplets with the Ubuntu 14.04 image.
Droplet 1
Droplet 2
Droplet 3
Ensure that the Private Networking checkbox is ticked when creating the Droplets. Also, make note of the private IP addresses as we will need them later.
Install LAMP on all three servers.
First, update the repository and install Apache.
apt-get update
apt-get install apache2
Install PHP and Apache’s mod_php extension.
apt-get install php5 libapache2-mod-php5 php5-mcrypt
For more information, see this article.
On lamp01, install the Memcached daemon and PHP’s Memcache module.
apt-get install php5-memcache memcached
PHP has two packages: php5-memcache and php5-memcached (notice the “d” at the end). We will be using the first package (memcache) as it is lighter without any dependencies. Read the comparison between memcache and memcached.
The Memcached service listens only on localhost (127.0.0.1). This has to be changed to accept connections from the private network.
nano /etc/memcached.conf
Find the following line:
-l 127.0.0.1
Change it to listen on this server’s private IP address.
-l 10.1.1.1
Restart the memcached
service.
service memcached restart
Repeat these steps on the other two servers, replacing 127.0.0.1
with the appropriate private IP address.
lamp02
-l 10.2.2.2
lamp03
-l 10.3.3.3
Restart the memcached
service on the second two servers.
On lamp01, open the php.ini
file for editing.
nano /etc/php5/apache2/php.ini
This file is located at /etc/php5/fpm/php.ini
on PHP-FPM installations.
Find the following configuration directives:
session.save_handler =
session.save_path =
Modify them to use Memcache as follows. Use all three private IP addresses in the session.save_path
.
session.save_handler = memcache
session.save_path = 'tcp://10.1.1.1:11211,tcp://10.2.2.2:11211,tcp://10.3.3.3:11211'
You may need to uncomment session.save_path
by removing the semicolon at the beginning. Remember to enter the port number 11211 after each IP address, as Memcached listens on this port.
Add exactly the same settings on the other two servers.
On lamp02:
session.save_handler = memcache
session.save_path = 'tcp://10.1.1.1:11211,tcp://10.2.2.2:11211,tcp://10.3.3.3:11211'
On lamp03:
session.save_handler = memcache
session.save_path = 'tcp://10.1.1.1:11211,tcp://10.2.2.2:11211,tcp://10.3.3.3:11211'
This configuration has to be exactly same on all the Droplets for session sharing to work properly.
On lamp01, edit the memcache.ini
file.
nano /etc/php5/mods-available/memcache.ini
Add the following configuration directives to the end of this file.
memcache.allow_failover=1
memcache.session_redundancy=4
The memcache.session_redundancy
directive must be equal to the number of memcached servers + 1 for the session information to be replicated to all the servers. This is due to a bug in PHP.
These directives enable session failover and redundancy, so PHP writes the session information to all servers specified in session.save_path
; similar to a RAID-1 setup.
Restart the web server or the PHP FPM daemon depending on what is being used.
service apache2 reload
Repeat these steps exactly on lamp02 and lamp03.
To test this setup create the following PHP script on all the Droplets.
/var/www/html/session.php
<?php
header('Content-Type: text/plain');
session_start();
if(!isset($_SESSION['visit']))
{
echo "This is the first time you're visiting this server\n";
$_SESSION['visit'] = 0;
}
else
echo "Your number of visits: ".$_SESSION['visit'] . "\n";
$_SESSION['visit']++;
echo "Server IP: ".$_SERVER['SERVER_ADDR'] . "\n";
echo "Client IP: ".$_SERVER['REMOTE_ADDR'] . "\n";
print_r($_COOKIE);
?>
This script is for testing only and can be removed once the Droplets are set up.
Access this file on the first Droplet using curl and extract the cookie information.
curl -v -s http://1.1.1.1/session.php 2>&1 | grep 'Set-Cookie:'
This will return output similar to the following.
< Set-Cookie: PHPSESSID=8lebte2dnqegtp1q3v9pau08k4; path=/
Copy the PHPSESSID
cookie and send the request to the other Droplets using this cookie. This session will be removed by PHP if no requests are made for 1440 seconds, so make sure you complete the test within this timeframe. Read about PHP’s session.gc-maxlifetime to learn more about this.
curl --cookie "PHPSESSID=8lebte2dnqegtp1q3v9pau08k4" http://1.1.1.1/session.php http://2.2.2.2/session.php http://3.3.3.3/session.php
You will find that the session is being carried over across all Droplets.
Your number of visits: 1
Server IP: 1.1.1.1
Client IP: 117.193.121.130
Array
(
[PHPSESSID] => 8lebte2dnqegtp1q3v9pau08k4
)
Your number of visits: 2
Server IP: 2.2.2.2
Client IP: 117.193.121.130
Array
(
[PHPSESSID] => 8lebte2dnqegtp1q3v9pau08k4
)
Your number of visits: 3
Server IP: 3.3.3.3
Client IP: 117.193.121.130
Array
(
[PHPSESSID] => 8lebte2dnqegtp1q3v9pau08k4
)
To test failover, stop the memcached
service and access this file on it.
service memcached stop
The Droplet transparently uses the session information stored on the other two servers.
curl --cookie "PHPSESSID=8lebte2dnqegtp1q3v9pau08k4" http://1.1.1.1/session.php
Output:
Your number of visits: 4
Server IP: 1.1.1.1
Client IP: 117.193.121.130
Array
(
[PHPSESSID] => 8lebte2dnqegtp1q3v9pau08k4
)
Now a load balancer can be configured to distribute requests evenly without the hassle of configuring session stickyness.
Start memcached
again once you are done testing:
service memcached start
Even if Memcached is using the private network, other DigitalOcean users in the same data center can connect to your Droplet if they know your private IP. So, we will set up IPTables rules to allow only the cloud servers in our Memcached pool to communicate with each other.
We are doing this step after testing for session redundancy so that it is easier to troubleshoot problems which may arise if incorrect rules are applied.
Create firewall rules on lamp01 with the private IP addresses of lamp02 and lamp03.
iptables -A INPUT -s 10.2.2.2 -i eth1 -p tcp -m state --state NEW -m tcp --dport 11211 -j ACCEPT
iptables -A INPUT -s 10.3.3.3 -i eth1 -p tcp -m state --state NEW -m tcp --dport 11211 -j ACCEPT
On a typical LAMP server the following would be the complete set of rules:
iptables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 443 -j ACCEPT
iptables -A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT
iptables -A INPUT -s 10.2.2.2 -i eth1 -p tcp -m state --state NEW -m tcp --dport 11211 -j ACCEPT
iptables -A INPUT -s 10.3.3.3 -i eth1 -p tcp -m state --state NEW -m tcp --dport 11211 -j ACCEPT
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p icmp -m icmp --icmp-type 8 -j ACCEPT
iptables -P INPUT DROP
Enter the firewall rules on lamp02 with the private IP addresses of lamp01 and lamp03.
iptables -A INPUT -s 10.1.1.1 -i eth1 -p tcp -m state --state NEW -m tcp --dport 11211 -j ACCEPT
iptables -A INPUT -s 10.3.3.3 -i eth1 -p tcp -m state --state NEW -m tcp --dport 11211 -j ACCEPT
Do the same on lamp03 with the private IP addresses of lamp01 and lamp02.
iptables -A INPUT -s 10.1.1.1 -i eth1 -p tcp -m state --state NEW -m tcp --dport 11211 -j ACCEPT
iptables -A INPUT -s 10.2.2.2 -i eth1 -p tcp -m state --state NEW -m tcp --dport 11211 -j ACCEPT
Repeat the tests in Step 4 to confirm that the firewall is not blocking our traffic.
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.
Best tutorial
This has worked beautifully for me. In the past I was load balancing using an IP Hash so I could ensure my users were always getting sent to the same box. This is a much better solution - my load is now evenly distributed, and if one of my web servers goes down none of my users will be logged off. Thanks for another fantastic tutorial Digital Ocean!
thanks you for your tutorial. I’m using apache2, laravel and php5-memcached. Can I use same way with php5-memcach ? Could you give me quick guide for my case ?
Can something similar be done for the all other key/values stored in Memcache? I am aware of the existence of Repcache but I want to replicate the cache of one server over to the cache of several others. Similar to a Redis cluster.
I have question: run curly http://1.1.1.1/testing/tes.php -s -v 2> & 1 | grep ‘Set-Cookie:’ but no output coming out . how to fix?
I would like to implement this setup on a Redhat Drupal server. But I do not have a memcached.conf or memcache.ini. What are the steps for getting this to work on my Redhat installation?
Great tutorial!
I have a question: if you stop memcached on server 1, The Droplet transparently uses the session information stored on the other two servers. Then if I start memcached again, and run curl --cookie “PHPSESSID=8lebte2dnqegtp1q3v9pau08k4” http://1.1.1.1/session.php
Output will be Your number of visits: 4 or Your number of visits: 5?
Thanks for this great tutorial. I bit unclear, what size of server do you recommend for the one running memcached?
Also, cant I just install Syncthing and only use the 2x app servers?
Are you asking about clearing all the stored session information?
You can do that by restarting Memcached:
Do we need to keep memcache session in our server?if i dont want to keep session in our server then how we can do that?