Wednesday, August 21, 2013

Caching Apt packages with Nginx - and without Squid

For a few years I have been using Squid-Deb-Proxy to cache downloaded Ubuntu deb packages for multiple machines on my home network. Early on in the project I contributed some code and fixed some bugs in Squid-Deb-Proxy, so I am fairly comfortable with the code and how it works.

Recently I wondered about changing the backend from a Squid Caching server to a Nginx Cache. Why? Well, because it seemed interesting, and Nginx is a fair bit less resource hungry than Squid.

The less expected advantages have been better logging by Nginx helping me find a few issues and better control over the caching.

I installed Nginx on a Debian machine to cache packages for a handful of (K)ubuntu machines, but it could be just as easily installed on a Ubuntu machine.

I only needed the light version, show it's the usual 'sudo apt-get install nginx-light'. The configuration files were already set up in a useful fashion so I didn't touch them. I did need to add a site configuration to /etc/nginx/sites-available though:

proxy_cache_path /var/cache/nginx levels=1 keys_zone=STATIC:10m inactive=30d max_size=12g;

server { 
  listen 8080; server_name mirror.optus.net ; 

  location ~ \.(deb|udeb)$ {
    proxy_pass http://mirror.optus.net ; 
    include /etc/nginx/proxy.conf ;
  } 

  location / { 
    proxy_pass http://mirror.optus.net ;
  } 


server {
  #security.ubuntu.com, extras.ubuntu.com, changelogs.ubuntu.com and others...
  listen 8080; 
  server_name ~(.*).ubuntu.com;
  set $prefix $1 ;
  resolver 127.0.0.1;
  location ~ \.(deb|udeb)$ { 
    proxy_pass     http://$prefix.ubuntu.com ;
    include /etc/nginx/proxy.conf ;

  } location / {
    proxy_pass http://$prefix.ubuntu.com ;
  } 


server { 
  listen 8080;
  server_name ppa.launchpad.net ;

  location ~ \.(deb|udeb)$ {
    proxy_pass http://ppa.launchpad.net ;
    include /etc/nginx/proxy.conf ;
  }
  
  location / { 
    proxy_pass http://ppa.launchpad.net ;
  } 


This is a bit of a mouthful, so lets step through it:

proxy_cache_path /var/cache/nginx levels=1 keys_zone=STATIC:10m inactive=30d max_size=12g;


The cache will be stored in /var/cache/nginx (So make sure this directory exists!) with one level of subdirectories. The downloaded packages will be stored for up to 30 days and the maximum total cache size will be 12 Gb.

If necessary Nginx cache manage will remove packages to keep within these limits.

server {
    listen 8080;
    server_name mirror.optus.net ;

  location ~ \.(deb|udeb)$ {
    proxy_pass http://mirror.optus.net ;
    include /etc/nginx/proxy.conf ;
  }

  location / {
    proxy_pass http://mirror.optus.net ;
  }
}

 This is my local mirror. Nginx will listen on port 8080 for requests to mirror.optus.net. For requests for '.deb' and '.udeb' files Nginx will pass them on, but cache the packages as defined in /etc/nginx/proxy.conf – more on that later.

Other requests to mirror.optus.net are just passed on to the remote server. No caching for these. 

server {
  #security.ubuntu.com, extras.ubuntu.com, changelogs.ubuntu.com and others...
  listen 8080;
  server_name ~(.*).ubuntu.com;
  set $prefix $1 ;
  resolver 127.0.0.1;


This is a bit more complicated and uses regex in the site names. This regex will match anything with '.ubuntu.com' in the hostname, such as security.ubuntu.com, or nz-archive.ubuntu.com and provide appropriate proxy actions.

 The resolver line is required to prevent an error message.

Having put this file into /etc/nginx/sites-available, you need to make a symbolic link to it in etc/nginx/sites-enabled. You could also put a copy of the file directly in etc/nginx/sites-enabled, but symbolic links seem to be the Debian way.

The final piece of work is to add the previously mentioned cache file /etc/nginx/proxy.conf:

proxy_redirect off;
proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
client_max_body_size 100m;
client_body_buffer_size 1m;
proxy_cache STATIC;
proxy_temp_path /var/cache/nginx/partial;
proxy_store_access user:rw group:rw all:r;
proxy_cache_valid 30d;
proxy_ignore_headers X-Accel-Expires Expires Cache-Control;


Of note here is that /var/cache/nginx/partial needs to exist to store partially downloaded files, and the cache validity is 30 days.

Reload Nginx with sudo service nginx reload and check the logs for error messages.

Note that you do need to set apt on each machine to connect to the proxy, and I'll cover this in later post.