Build and customize your CDN

Content Delivery Networks (CDNs) are used on sites and in applications primarily to speed up the loading of static elements. This happens due to caching files on CDN servers located in different geographical regions. Having requested data via CDN, the user receives it from the nearest server.

The principle of operation and functionality of all content delivery networks is approximately the same. Having received a request to download a file, the CDN server once takes it from the original server and gives it to the user, at the same time caching for a given period of time. All subsequent requests are answered from the cache. All CDNs have options for preloading files, clearing the cache, setting its retention period, and much more.

It happens that for one reason or another it is required to organize our own content delivery network, and then - let us help you build another bike.


Source: Infographic vector created by pikisuperstar - www.freepik.com


When you need your own CDN


Consider the cases when starting your own CDN makes sense:

  • when you want to save money, and running costs even when using inexpensive CDNs like BunnyCDN are several hundred dollars a month
  • if we want to get a permanent cache or a cache without neighbors on the server and channel
  • in the region you need, CDN services have no points of presence
  • any special content delivery settings required
  • we want to accelerate the delivery of dynamic content by placing closer to the users of the production server
  • there is a concern that a third-party CDN service may unlawfully collect or use information about user behavior (hi to services without GDPR-compliant) or engage in other illegal actions

In most other cases, it is more advisable to use existing ready-made solutions.


What you need to run


It's wonderful if you have your own autonomous system (AS). With it, you can assign the same IP to several servers and follow this instruction at the network level to direct users to the nearest one. It is worth saying that even with the block of addresses / 24, it is possible to build a content delivery network. Some server providers allow you to make an announcement for use in all regions available to them.

If you are not a happy owner of a block of IP addresses, then to run a simple CDN you will need:

  • . ,
  • geoDNS . , ,



With domain registration, everything is simple - register in any zone with any registrar. You can also use a subdomain for a CDN, for example something like cdn.namedomain.com . Actually in our example, we will do so.

As for ordering servers, they should be rented in the regions and countries where your user audience is located. If the project is intercontinental, it is convenient to choose hosting providers that offer servers all over the world right away. Examples: OVH , Leaseweb, and 100Tb for dedicated servers, Vultr and DigitalOcean for virtual cloud *.

For our private CDN we order 3 virtual servers on different continents. At vultron the server for $ 5 / month we get 25GB SSD space and 1TB of traffic . When installing, select the latest Debian. Our servers:

Frankfurt , ip: 199.247.18.199

Chicago , ip: 149.28.121.123

Singapore , ip: 157.230.240.216

* Vultr and DigitalOcean promise a $ 100 loan to users registered using the links in the article immediately after adding a payment method. The author also receives a small compliment from this, which is very significant for him now. Please be understanding.


Configure geoDNS


So that when a user accesses a CDN domain or subdomain, he is directed to the desired (closest to him) server, we need a DNS server with the geoDNS function.

The principle and procedure of geoDNS is as follows:

  1. Defines the IP of the client that sent the DNS request, or the IP of the recursive DNS server that is used to process the client request. These recursive servers are usually DNS providers.
  2. By IP client knows his country or region. For this, GeoIP bases are used, of which there are a great many today. There are some good free options .
  3. Depending on the location of the client, he gives him the IP address of the nearest CDN server.

You can build a DNS server with the geoDNS function yourself , but it is better to use ready-made solutions with a network of DNS servers around the world and Anycast out of the box:

  • louDNS from $ 9.95 / month , GeoDNS tariff, by default there is one DNS Failover
  • Zilore from $ 25 / month , DNS Failover enabled
  • Amazon Route 53 from $ 35 / mo for 50M net geo-requests. DNS failover is charged separately
  • DNS Made Easy from $ 125 / mo , there are 10 DNS Failover
  • Cloudflare , Geo Steering Feature Available in Enterprise Rates

When ordering geoDNS, you should pay attention to the number of requests included in the tariff and take into account that the actual number of calls to the domain can exceed expectations by several times. Millions of spiders, scanners, spammers and other evil spirits work tirelessly.

Almost all DNS services include an indispensable for building a CDN service - DNS Failover. Using it, you can configure monitoring of your servers and, in the absence of signs of life, automatically replace the non-working server address with a backup server in DNS answers.

To build our CDN, we will use ClouDNS , the GeoDNS tariff.

Add a new DNS zone in your account, indicating your domain. If we will build the CDN on a subdomain, and the main domain is already in use, then immediately after adding the zone, do not forget to add the existing working DNS records. The next step is to create for the CDN domain / subdomain several A-records, each of which will be applied to the region that we set. You can specify continents or countries as regions; subregions are available for the USA and Canada.

In our case, the CDN will be raised on the cdn.sayt.in subdomain . Adding the sayt.in zone , create the first A record for the subdomain and direct all of North America to the server in Chicago:


Repeat for other regions, remembering to create one entry for the default regions. Here is the result:



The last default record in the screenshot means that all unspoken regions (and this is Europe, Africa, satellite Internet users, etc.) will be sent to a server in Frankfurt.

This completes the basic DNS configuration. It remains to go to the domain registrar's website and replace the current domain NSs with those issued by ClouDNS. And while NSs will be updated we will prepare the server.


Install SSL Certificates


Our CDN will work on HTTPS, so if you already have SSL certificates for a domain or subdomain, upload them to all servers, for example, to the directory / etc / ssl / yourdomain /

If there are no certificates, you can get a free one from Let's Encrypt. The ACME Shell script is perfect for this . The client is convenient and easy to configure, and most importantly - allows you to validate the domain / subdomain for DNS through the API from ClouDNS.

We will install acme.sh only on one of the servers - European 199.247.18.199, from which certificates will be copied to all the others. To install, do:

root@cdn:~# wget -O - https://get.acme.sh | bash; source ~/.bashrc

During the installation of the script, a CRON task will be created for further renewal of certificates without our participation.

Domain verification when issuing a certificate will be performed using DNS using the API, therefore, in the ClouDNS personal account in the Reseller API menu, you need to create a new user API and set a password for it. The resulting auth-id with the password is written in the file ~ / .acme.sh / dnsapi / dns_cloudns.sh (not to be confused with the dns_clou d dns.sh file ). Here are the lines to uncomment and edit:

CLOUDNS_AUTH_ID=<auth-id>
CLOUDNS_AUTH_PASSWORD="<>"

Now we ask for an SSL certificate for cdn.sayt.in

root@cdn:~# acme.sh --issue --dns dns_cloudns -d cdn.sayt.in --reloadcmd "service nginx reload"

In the parameters for the future, we specified a command to automatically reload the web server configuration after each renewal of the certificate validity period in the future.

The whole process of obtaining a certificate can take up to 2 minutes, do not interrupt it. If a domain validation error occurs, try running the command again. In the end, we will see where the certificates were uploaded:



Remember these paths, they will need to be specified when copying the certificate to other servers, as well as in the web server settings. We do not pay attention to the error of reloading Nginx config files - on a fully configured server it will not be present when renewing certificates.

All that remains for us over SSL is to copy the received certificate to two other servers with saving the path to the files. Create the same directories on each of them and make a copy:

root@cdn:~# mkdir -p /root/.acme.sh/cdn.sayt.in/
root@cdn:~# scp -r root@199.247.18.199:/root/.acme.sh/cdn.sayt.in/* /root/.acme.sh/cdn.sayt.in/

So that certificate renewal is regular, we will create a daily CRON task on both servers with the command:
scp -r root@199.247.18.199:/root/.acme.sh/cdn.sayt.in/* /root/.acme.sh/cdn.sayt.in/ && service nginx reload

In this case, access to the remote source server must be configured by key , i.e. without entering a password. Do not forget to do it.


Install and configure Nginx


To return static content, we will use Nginx, configured as a caching proxy server. Update the list of packages and install it on all three servers:

root@cdn:~# apt update
root@cdn:~# apt install nginx

Instead of the default, use the config from the spoiler below:
nginx.conf
user www-data;
worker_processes auto;
pid /run/nginx.pid;

events {
    worker_connections 4096;
    multi_accept on;
}

http {
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    types_hash_max_size 2048;

    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    access_log off;
    error_log /var/log/nginx/error.log;

    gzip on;
    gzip_disable "msie6";
    gzip_comp_level 6;
    gzip_proxied any;
    gzip_vary on;
    gzip_types text/plain application/javascript text/javascript text/css application/json application/xml text/xml application/rss+xml;
    gunzip on;            

    proxy_temp_path    /var/cache/tmp;
    proxy_cache_path   /var/cache/cdn levels=1:2 keys_zone=cdn:64m max_size=20g inactive=7d;
    proxy_cache_bypass $http_x_update;

server {
  listen 443 ssl;
  server_name cdn.sayt.in;

  ssl_certificate /root/.acme.sh/cdn.sayt.in/cdn.sayt.in.cer;
  ssl_certificate_key /root/.acme.sh/cdn.sayt.in/cdn.sayt.in.key;

  location / {
    proxy_cache cdn;
    proxy_cache_key $uri$is_args$args;
    proxy_cache_valid 90d;
    proxy_pass https://sayt.in;
    }
  }
}


In the config, edit:

  • max_size - cache size not exceeding available disk space
  • inactive - storage time for cached data that no one accessed
  • ssl_certificate and ssl_certificate_key - paths to SSL certificate and key files
  • proxy_cache_valid - storage time for cached data
  • proxy_pass - the address of the original server from which the CDN will request files for caching. In our example, this is sayt.in

As you can see, everything is simple. Complexity can only arise in setting the cache time due to the similarity of the inactive and proxy_cache_valid directives . We will analyze them in our example. Here is what happens with inactive = 7d and proxy_cache_valid 90d :

  • if the request is not repeated within 7 days, then the data will be deleted from the cache after this period
  • if the request is repeated at least once every 7 days, then the data in the cache will be considered outdated after 90 days and at the next request Nginx will update it by taking it from the original server

Having finished editing nginx.conf , reload the configuration:

root@cdn:~# service nginx reload

Our CDN is completely ready. For $ 15 / month we got points of presence on three continents and 3 TB of traffic: 1 TB in each location.


Checking the operation of CDN


Let's look at the pings to our CDN from different geographical locations. For this, any ping services are suitable.
Launch pointHostIPAvg time, ms
Germany Berlincdn.sayt.in199.247.18.1999.6
Netherlands, Amsterdamcdn.sayt.in199.247.18.19910.1
France Pariscdn.sayt.in199.247.18.19916.3
Great Britain, Londoncdn.sayt.in199.247.18.19914.9
Canada Torontocdn.sayt.in149.28.121.12316.2
USA, San Franciscocdn.sayt.in149.28.121.12352.7
USA, Dallascdn.sayt.in149.28.121.12323.1
USA, Chicagocdn.sayt.in149.28.121.1232.6
USA, New Yorkcdn.sayt.in149.28.121.12319.8
Singaporecdn.sayt.in157.230.240.2161.7
Japan Tokyocdn.sayt.in157.230.240.21674.8
Australia, Sydneycdn.sayt.in157.230.240.21695.9
The results are good. Now we will place the test image test.jpg in the root of the main site and check the speed of its download via CDN. No sooner said than done . Content is delivered quickly.

We will write a small script in case we want to clear the cache on the CDN point.
purge.sh
#!/bin/bash
if [ -z "$1" ]
then
    echo "Purging all cache"
    rm -rf /var/cache/cdn/*
else
    echo "Purging $1"
    FILE=`echo -n "$1" | md5sum | awk '{print $1}'`
    FULLPATH=/var/cache/cdn/${FILE:31:1}/${FILE:29:2}/${FILE}
    rm -f "${FULLPATH}"
fi


To delete the entire cache, just run it, a separate file can be cleaned like this:

root@cdn:~# ./purge.sh /test.jpg


Instead of conclusions


Finally, I want to give some useful tips in order to immediately step over the rake, which at one time made my head sore to me:

  • To increase CDN fault tolerance, it is recommended to configure DNS Failover, which helps to quickly change A record in case of server failure. This is done in the control panel of the DNS records of the domain
  • CDN, . CDN, 6-7 : , (), (), , ,
  • CDN. , , -
  • , ,
  • Try to check pings from different places to your servers. So you can see the regions closest to the CDN points and configure GeoDNS correctly
  • Depending on the tasks, it is not out of place to configure Nginx for specific caching requirements and taking into account the load on the server. The articles about the Nginx cache - here and the acceleration of work under heavy loads: here and here helped me a lot.

All Articles