Build Your Own SaaS using Docker


Published on

Build Your Own SaaS using Docker. A proof of concept with a simple Memcached SaaS.
See the Memcached as a service application in action at
Find the source code on GitHub:

Published in: Technology
No Downloads
Total views
On SlideShare
From Embeds
Number of Embeds
Embeds 0
No embeds

No notes for slide

Build Your Own SaaS using Docker

  1. 1. Build Your Own SaaSBuild Your Own SaaSwithDockerA proof of concept with a simple Memcached SaaS04/14/2013 – by JulienBarbier
  3. 3. Build Your Own SaaSIntroductionThis is cs50This document and source code are part of my final project for cs50x that I startedon few months ago. For this final project I had several goals: - learn a new language: I chose to learn Ruby and Rails - use a new piece of technology. I chose to useDocker - build a cool product. Building a SaaS a new way and with a new piece of technology sounds fun! - make it open source, and learn how to use Git and GitHubAlong the way I wrote several documents that are available on SlideShare. Some ofthem have been used by Docker in their documentation.SourcesYou can find, clone, fork, or download the source code of the project on GitHub: of conceptBy downloading the source code and reading this document you will be able to run aminimalist SaaS. Your users will be able to get their own Memcached server. Ofcourse this is only a proof of concept, but it runs quite well.MemcachedMemcached is afree & open source, high-performance, distributed memory objectcaching system, generic in nature, but intended for use in speeding up dynamic webapplications by alleviating database load.I chose Memcached because it is a widely used service. It is also easy to install anduse, so that the tests are not too complicated to perform.
  4. 4. Build Your Own SaaSThank you!I had only a few weeks to learn Ruby, Rails, Git, Github, iptables, sudoers, … andbuild this proof of concept. I would like to thank all the people who gave their timeto help me and answer all my questions: - Guillaume Charmes, alias Cortex, alias MPM, my Docker teacher - Guillaume Luccizano, Steeve Morin and Sylvain Kalache, my Rails and Ruby teachers (sorry I was not able to use TDD until the end, I didn’t have enough time!) - Daniel Mizyrycki, my Git and GitHub teacher - Jerome Petazzoni, my iptables teacher - andThomas Meson for giving me an Ubuntu server to play with
  5. 5. Build Your Own SaaSRequirementsUbuntu machineIn order to follow this tutorial you will need a server with the last version of Ubuntu(or any other OS, but using Vagrant and VirtualBox to run an Ubuntu image). Weneed Ubuntu because our minimalist SaaS will use Docker, which runs on Ubuntuservers only.The community behind Dockeris growing fast and is very active. And at the time Iwrite this document, it is now possible to use Docker on different operating systems.For instance, FlavioCastelli has written a blog-post on how to use Docker onopenSUSE. And I’ve seen people using it on CentOS during a Docker demo days.DockerDocker is a Linux container runtime. It has been released few weeks ago as an open-source project by dotCloud. Docker complements LXC with a high-level API whichoperates at the process level. It runs unix processes with strong guarantees ofisolation and repeatability across servers.Please visit Docker’s website for a tutorial on how to get Dockerrunning on yourUbuntu machine or using Vagrant + VirtualBox on any other Operating system.We will use only few Dockercommands through this tutorial. To learn more aboutthe Docker command line interface, you can take a look at theirCLI documentationpage.MemcachedYou don’t need to have Memcached installed on your server. Memcached will runinside our Docker containers.I will explain in this document how you can build your Memcached container. If youare not interested in learning how to build your own image, you can skip the firstchapter, jump directly to the next chapter “Creating a Memcached SaaS” and use theimage called jbarbier/memcached. To get this image, use the docker pullcommand: docker pull jbarbier/memcached
  6. 6. Build Your Own SaaSCreating a Docker image with MemcachedThe first step to build the minimalist Memcached SaaS is to have an image of acontainer with Memcached installed.Start DockerLet’s check if Docker is already running. ps aux | grepdocker sudodocker –d &If you do not see a line “docker –d”, then start Docker as a daemon:Installing Memcached on DockerWe will install Memcached on a Docker container with the docker run command. docker run -d base apt-get -y install memcachedThis command will return you the id of the new created container running yourcommand will need to keep this id in order to use it later. In our example, the idisf1ab59fbc9d5.We can check that the installation is complete by using the command docker logswith the container id given by the previous command. docker logs f1ab59fbc9d5 | less
  7. 7. Build Your Own SaaSRemember to replace f1ab59fbc9d5by your container id.
  8. 8. Build Your Own SaaSCommitting our Memcached containerWe can commit our new container with the docker commit command, using ourcontainer id. With the following command line we will name it jbarbier/memcached,but you should use your own name. docker commit f1ab59fbc9d5 jbarbier/memcachedRemember to replace f1ab59fbc9d5by your container id and jbarbier/memcachedby your own name.This command gives you back a new id, which is the image id of your committedcontainer. In our example it isc3b6fcb48266.Checking our Memcached container imageLet’s check that Memcached is installed on this image. To do so we can spawn a new docker run -i -t jbarbier/memcached /bin/bashcontainer from this image and run bash inside.Remember to replacejbarbier/memcached with the name of your image.We are now inside a new container spawned from our image. Let’s see ifMemcached is installed. Run memcachedOK!
  9. 9. Build Your Own SaaSNote that you could have used the id of your image instead of the name of yourrepository. docker run -i -t c3b6fcb48266 /bin/bashRemember to replacec3b6fcb48266by your image id.Playing with our Memcached imageNow that we have an image with Memcached installed, let’s use it :)Spawning a new container based on our Memcached image docker run -d -p 11211 jbarbier/memcachedmemcached -u daemonRemember to replacejbarbier/memcached with the name of your image.We need to launch Memcached with the –u option because you can not run it asroot. With –u daemon, our Memcached will run as a daemon.In the next chapter we will build a SaaS with this image. So we will need any user tobe able to access their Memcached. In order to be able to use the Memcached serverrunning in the container from outside our server, we canuse the–p option. Thisoption tells Docker to map the internal port of the container used byMemcached(11211), with a public port of the host.As usual, Docker gives you back the id of the container you launched. In our case it isc360f228e22f.
  10. 10. Build Your Own SaaSRetrieving the public port of our Memcached containerIn order to use Memcached from outside the localhost we need to know the hostpublic port mapped by Docker. In order to know that we can use the dockerinspectcommand. docker inspect c360f228e22fRemember to replacec360f228e22fby your container id before running thiscommand.This will give you a JSON output with plenty of configuration details (see next page).In theNetworkSettings/PortMapping you will find the public port you can useMemcached with from outside the server. In our case the public port is 49153.
  11. 11. Build Your Own SaaS
  12. 12. Build Your Own SaaSTesting ourMemcachedLet’s test and use our Memcached service, from an outside machine. In the followingexamples I will use as the IP of the server where the container isrunning, and 49153 as the public port.Before running any of these examples be sure to replace the IP with your server IP,and the port number with the one docker inspect gave you.Ruby Guillotine:test_memcachedjbarbier$ cat test.rb # gem install dalli require dalli ip= port = 49153 dc ="#{ip}:#{port}") dc.set(abc, "Always Be Closing") value = dc.get(abc) puts valuePython Guillotine:test_memcachedjbarbier$ cat # pip install python-memcached importmemcache ip = port = 49153 mc = memcache.Client(["{0}:{1}".format(ip, port)], debug=0) mc.set("best_dev", "Guillaume C.") value = mc.get("best_dev") print value
  13. 13. Build Your Own SaaS Guillotine:test_memcachedjbarbier$ cat test.php <?php $ip =; $port = 49153; $memcache_obj = new Memcache; $memcache_obj->connect($ip, $port); $memcache_obj->set(rule_1, You DO NOT talk about FIGHT CLUB); $v = $memcache_obj->get(rule_1); echo "$vn"; ?>PHP
  14. 14. Build Your Own SaaSGo Guillotine:test_memcachedjbarbier$ cat test.go package main import ( "fmt" "" ) func main() { ip := "" port := 49153 memc, err := gomemcache.Connect(ip, port) if err != nil { panic(err)
 } err = memc.Set("foo", []byte("bar"), 0, 0) if err != nil { panic(err) } val, _, _ := memc.Get("foo") fmt.Printf("%sn", val) }
  15. 15. Build Your Own SaaSCreating a Memcached SaaSNow that we have an image with Memcached installed, and that we know almost allthe required commands, the plan is to use that to create our SaaS. Each user willhave its own Memcached running inside his own container. 1. A user registers through our website 2. We spawn a Memcached container using our image 3. We give the user an IP and a port of his Memcached server 4. We add a layer of securityThis last step is required because otherwise everybody could use the user’sMemcached since there is no built-in security for Memcached servers.Building the websiteAs I previously mentioned I chose to learn Ruby and Rails, so the website is usingthese technologies, but you could use any language.Since this article is not about building websites we won’t go into details on how tobuild a website. Feel free to use my code or to build your own website with yourfavorite language and database.The example website does only handle sign-up, sign-in and a profile page whichdisplays information about the user’s Memcached server. These are the onlyrequired pages in order to build this proof of concept. You can find the source codeof the website on GitHub. Sign-up page Sign-in page Profile page
  16. 16. Build Your Own SaaSSpawning a Memcached container on registrationWhen the user has signed-up, we need to run the commands to launch a newcontainer with our Memcached image. The function create_memcached_instance isdoing all the job. You can find this function in the file users_controller.rb defcreate_memcached_instance docker_path=/home/julien/docker-master/ container_id=`#{docker_path}docker run -d -p 11211 jbarbier/memcachedmemcached -udaemon` cmd="#{docker_path}docker inspect #{container_id}" json_infos=`#{cmd}` i=JSON.parse(json_infos) @user.memcached=i["NetworkSettings"]["PortMapping"]["11211"] @user.container_id=container_id @user.docker_ip=i["NetworkSettings"]["IpAddress"] endIf you havecloned the repository and are running the website from that, rememberto edit users_controller.rb and set the docker_path variable to your Docker’s path.Alternatively you can add Docker to your PATH.Let’s go through all the lines: 1. docker_path=/home/julien/docker-master/docker_path should contain the path of your Docker’s executable 2. container_id=`#{docker_path}docker run -d -p 11211 jbarbier/memcachedmemcached - u daemon`As discussed in the previous chapter, docker run -druns a command in a newcontainer. We pass the option -d so that it leaves the container run in thebackground.The option -p 11211 maps the internal port of the container used by Memcached witha public port of our server.jbarbier/memcached is the name of our image with Memcached installed (see previouschapter to see how we built this image). If you have created your own image, youshould replace jbarbier/memcached by the name of your image.memcached -u daemon is the command we run inside the new container. We use theoption -u daemon to run Memcached with user daemon. The command dockerrunreturns the id of the new container. We will need it so we save it to the variablecontainer_id.
  17. 17. Build Your Own SaaS 3. cmd="#{docker_path}docker inspect #{container_id}"As discussed earlier, we need to get the public port to give it to the user. So weinspect our newly created container with the docker inspect command. We pass it thecontainer_id variable to tell Dockerwhich container to inspect. This commandreturns us lots of information about the container formatted in JSON. We save it and 4. i=JSON.parse(json_infos)we parse it to access the information. We then store all the required informationinto the user variable. 5. @user.memcached=i["NetworkSettings"]["PortMapping"]["11211"]i["NetworkSettings"]["PortMapping"]["11211"] contains the public port mapped with theport 11211, used by Memcached. 6. @user.container_id=container_idsaves the container id and 7. @user.docker_ip=i["NetworkSettings"]["IpAddress"]saves the internal IP address of our container. We will need this IP when we will beadding a basic layer of security on top of the user’s Memcached service.
  18. 18. Build Your Own SaaSDisplaying the public Memcached IP and port to the userWe now have to give the user the IP and port with which he can access hisMemcached.The code to show these information is in the file show.html.erb.<%provide(:title,><h1> <%=gravatar_for@user%> <></h1><divclass="alert alert-info"> Congratulations <>. Your Memcached server is ready to use.</div><h1>Your Memcached Server is ready!</h1><divclass="block-info"> <h3>IP: <%=my_public_ip%></h3> <h3>PORT: <%=@user.memcached%></h3></div><divclass="alert alert-info"> Use it with your favorite language.</div><%=rendercode%><%=renderip%>PortWe already have the port saved. We just need to display it PORT: <%=@user.memcached%>
  19. 19. Build Your Own SaaSIP addressWe just need to know our public IP address (the IP of our server). There are plentyof ways to know it. One of which is to ask an online service. I chose to but you could use any service of this type. The code to discover its ownpublic IP address is in the file users_helper.rb. defmy_public_ip @@ip||=Net::HTTP.get_response(URI.parse("")).body.chomp end simply returns the IP. So we just have to store it and then showit to the user. IP: <%=my_public_ip%>Alternatively, if you just want to test, you can hard code your IP address inmy_public_ipor even in show.html.erb.
  20. 20. Build Your Own SaaSAt this point you should have all you need to build your own Memcached SaaS.If you cloned my GitHub repository, after the registration you should see theProfile’s page showing the IP and port whith which you can access your Memcached.You can also check that a new container is running every time a new user registers. julien@cs50:~$ ps aux | grepdocker root 23863 0.0 0.0 27540 1220 ? S Apr11 0:00 lxc-start -n 48610f83f354bd5a7675bf41daedbb87958e6acf618f8c24487526373ddde8b8 -f /var/lib/docker/containers/48610f83f354bd5a7675bf41daedbb87958e6acf618f8c244 87526373ddde8b8/config.lxc -- /sbin/init -g -- memcached -u daemon […]
  21. 21. Build Your Own SaaSUsing the Memcached serverOnce you register you can test yourMemcached with the IP and port provided by theprofile’s page, with your favorite language.Ruby# gem install dallirequire dalliip = = 49159dc ="#{ip}:#{port}")dc.set(1762c2acf87, = dc.get(1762c2acf87)puts "Welcome #{value}! Your Memcached server is ready to use :)"PHP$ip =$port = 49159;$memcache_obj = new Memcache;$memcache_obj->connect($ip, $port);$memcache_obj->set(1762c2acf87,;$v = $memcache_obj->get("1762c2acf87");echo "Welcome $v! Your Memcached server is ready to use :)n";
  22. 22. Build Your Own SaaSPython# pip install python-memcachedimportmemcacheip = = 49159mc = memcache.Client(["{0}:{1}".format(ip, port)], debug=0)mc.set("1762c2acf87", "")value = mc.get("1762c2acf87")print "Welcome {0}! Your Memcached server is ready to use :)".format(value)Gopackage mainimport ( "fmt" "")func main() {ip := "" port := 49159memc, err := gomemcache.Connect(ip, port) if err != nil { panic(err) } err = memc.Set("1762c2acf87", []byte(""), 0, 0) if err != nil { panic(err) }val, _, _ := memc.Get("1762c2acf87")fmt.Printf("Welcome %s! Your Memcached server is ready to use :)n", val)}
  23. 23. Build Your Own SaaSAdding securityWe have now a minimalist Memcached SaaS. But our users are not happy becauseanybody can access their Memcached server. So we need to give the option to ourusers to restrict somehow the access to their Memcached. There are plenty of waysto do so. In this tutorial we will give the user the option to restrict the access to theMemcached to one IP. And to do so we will use iptables.Using iptables to filter by IPIptables is used to set up, maintain, and inspect the tables of IPv4 packet filter rulesin the Linux kernel. The command lines to restrict the access to the service to one IPaddress is in the file add_ip#!/bin/sh/sbin/iptables -I FORWARD -d $1 -s $2 -j ACCEPT/sbin/iptables -A FORWARD -d $1 -j DROPadd_ip is an executable shell script. That is why we have 1. #!/bin/shat the beginning of the file. The script will take two arguments in parameters.$1 is the internal IP of the container that we previously stored in@user.docker_ipupon user account creation.$2 is the IP provided by the user which will become the only authorized IP to accessthe user’s container, and the user’s Memcached server. 2. /sbin/iptables -I FORWARD -d $1 -s $2 -j ACCEPTTells iptables to add a rule to accept IP $2 to access internal IP $1. 3. /sbin/iptables -A FORWARD -d $1 -j DROPTells iptables to add a rule to deny all access to IP $1 from any IP.Since the first rule is “checked” first, only IP $2 will be able to access IP $1.The file remove_ip does exactly the opposite using the –D option to delete theprevious rules.#!/bin/sh/sbin/iptables -D FORWARD -d $1 -s $2 -j ACCEPT/sbin/iptables -D FORWARD -d $1 -j DROP
  24. 24. Build Your Own SaaSCalling iptables from a web serverWe call the previous scripts from the two following functions in users_controller.rbdefiptables_add_ip(i) cwd=Dir.pwd `sudo#{cwd}/iptables/add_ip#{@user.docker_ip}#{i}` end defiptables_remove_ip(i) cwd=Dir.pwd `sudo#{cwd}/iptables/remove_ip#{@user.docker_ip}#{i}` endBut in order to use iptables we need to have root privileges. And our web server isprobably not running as root (and it should not). So we will need to use the sudocommand and allow our webserver to run the two scripts.To do so we will use /etc/sudoers.The /etc/sudoers file controls who can run whatcommands as what users on what machines and can also control special things suchas whether you need a password for particular commands.A simple way to tackle our problem is to create a new file in /etc/sudoers.d. We cancall it saas_memcached for instance. julien@cs50:~$ cat /etc/sudoers.d/saas_memcached Cmnd_Alias ADD_REM_IPS_CMDS = /home/julien/final_proj/SaaS_Memcached/iptables/add_ip, /home/julien/final_proj/SaaS_Memcached/iptables/remove_ip www-data ALL=(ALL) NOPASSWD: ADD_REM_IPS_CMDSThere are two lines in the file. The first line creates an alias of all the executable filesand the second allow the user www-data to run these executable files with rootprivilege without requiring typing any password.You should replace /home/julien/final_proj/SaaS_Memcached by the root of yourwebsite. If your server does not run as www-data, simply replace www-data by theright user in the file.Now the user should be able to specify the allowed IP from which he can accessMemcached. Every other IP address will be blocked.
  25. 25. Build Your Own SaaSTesting the security filterIn order to test our security filter, let’s create an account to launch a newMemcached server.In the following examples we will run the ruby script from IP and thePHP script from IP we run we run these scripts from our two IPs it will work. Guillotine:test_memcachedjbarbier$ ruby ip_ok.rb Welcome! Your Memcached server is ready to use :)Let’s scroll to the bottom of the profile page and add the IP so ourMemcached access can be restricted to this IP. julien@revolution:/tmp$ phpip_nok.php Welcome! Your Memcached server is ready to use :)
  26. 26. Build Your Own SaaSAfter saving the IP, we should check that the access is really restricted to the IP69.42.42.42.Running the Ruby script from Guillotine:test_memcachedjbarbier$ ruby ip_ok.rb Welcome! Your Memcached server is ready to use :)Running the PHP script from julien@revolution:/tmp$ phpip_nok.php PHP Notice: Memcache::connect(): Server (tcp 49164, udp 0) failed with: Connection timed out (110) in /tmp/ip_nok.php on line 6 PHP Warning: Memcache::connect(): Cant connect to, Connection timed out (110) in /tmp/ip_nok.php on line 6 […]As we can see only the script ran from IP is able to connect toMemcached.
  27. 27. Build Your Own SaaSLet’s check the iptables by running iptables –L julien@cs50:~$ sudoiptables -L [sudo] password for julien: Chain INPUT (policy ACCEPT) targetprot opt source destination Chain FORWARD (policy ACCEPT) targetprot opt source destination ACCEPT all -- DROP all -- anywhere Chain OUTPUT (policy ACCEPT) targetprot opt source destinationThe two linesACCEPT all -- all -- anywhere been added by our add_ip script to restrict the access of’scontainer running Memcached.We can verify that the the IP is the right internal IP of the container byusing the docker inspect command on the container id. We saved this id into@user.container_id during registration (see users_controller.rb). Let’s retrieve this idfrom the database. julien@cs50:~/final_proj/SaaS_Memcached$ sqlite3 db/www.sqlite3 SQLite version 3.7.13 2012-06-11 02:05:22 Enter ".help" for instructions Enter SQL statements terminated with a ";" sqlite> select container_id from users where email =; 190c7d70fbc1Be sure to replace www.sqlite3 by your database.
  28. 28. Build Your Own SaaSOn our server, the id of’s container is 190c7d70fbc1. To check the IPof the container we can use thedocker inspectcommand. julien@cs50:~/docker-master$ ./docker inspect 190c7d70fbc1 | grepIpAddress "IpAddress": "",Be sure to replace 190c7d70fbc1by the right container id that the SQL request gaveyou during the last step.As expected the IP address is If you do the same on your server besure that this IP matches the one shown in the iptables listing.Congratulations! You are now running a Memcached SaaS, with a simple securitylayer. Congratulations!
  29. 29. Build Your Own SaaSWhere to go from hereWe’ve seen how to run Memcahed as a service with Docker. But you could createyour own container image, running another service. John Costa did write an articleon how to install Redis on Docker for instance. But you could create an imagerunning any service (MySQL, MongoDB, PHP, …), and then build a SaaS using thiscontainer image.Why should we offer only one type of service on ourSaaS? We could offer multipleservices. We could simply add a new table “services” to the database so thatourusers could be able to have multiple services. And we could add an admin page inorder to list, activate and deactivate the available services.The website shown in this example is very basic. We could easily improve it. Wecould for instance: - add an admin page to list users and delete/suspend them - let users change recover and change their password - let users delete their account - let user restart their Memcached server - let the user specify the memory limit he needs (using run docker –m) - let the user know how much memory he uses - add a payment gateway to make our customer pay for the service - …You can also add more security, scalability, etc… but this will be another story :)I hope you had fun playing with this article. Feel free to contact me if you have anyquestion.Happy SaaSing! To be continued…
  30. 30. Build Your Own SaaS