<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://dmarby.se/</id>
    <title>David Marby</title>
    <updated>2024-05-08T19:27:46.062Z</updated>
    <generator>Saber</generator>
    <author>
        <name>David Marby</name>
        <email>david@dmarby.se</email>
        <uri>https://dmarby.se/</uri>
    </author>
    <link rel="alternate" href="https://dmarby.se/"/>
    <link rel="self" href="https://dmarby.se/blog/atom.xml"/>
    <subtitle>David Marby - Full-Stack Engineer</subtitle>
    <rights>All rights reserved</rights>
    <entry>
        <title type="html"><![CDATA[Lorem Picsum, death by a million pixel-gigabits]]></title>
        <id>https://dmarby.se/blog/lorem-picsum/</id>
        <link href="https://dmarby.se/blog/lorem-picsum/"/>
        <updated>2024-05-08T19:27:20.203Z</updated>
        <summary type="html"><![CDATA[Updated 2023-04-16
Or how to serve half a billion placeholder images a month on a budget.
Lorem Picsum is one of the most popular placeholder sites on the internet. It started as a small side project written in NodeJS five years ago to cover my own needs and has since gone through several iterations as it has grown. In this post, I'll be going through the architecture and inner-workings of it.
]]></summary>
        <content type="html"><![CDATA[<p class="text-bold text-center">Updated 2023-04-16</p> <p>Or how to serve half a billion placeholder images a month on a budget.</p> <p><a rel="noopener noreferrer" target="_blank" href="https://picsum.photos">Lorem Picsum</a> is one of the most popular placeholder sites on the internet. It started as a small side project written in NodeJS <a rel="noopener noreferrer" target="_blank" href="https://github.com/DMarby/picsum-photos/commit/a444d8d3f235c877ac3e3302d04de42388d6d619">five years ago</a> to cover my own needs and has since gone through several iterations as it has grown. In this post, I'll be going through the architecture and inner-workings of it.</p> <h2 id="challenges">Challenges</h2> <p>Processing images is very CPU intensive. As Lorem Picsum runs on a very small budget, to cope with all the requests as the service became more popular, we wanted to avoid doing so as much as possible. This meant adding caching to multiple layers of the architecture. We use <a rel="noopener noreferrer" target="_blank" href="https://fastly.com">Fastly</a> as a CDN in front of our image processing service.
To make the image processing as efficient as possible, we decided to use <a rel="noopener noreferrer" target="_blank" href="https://libvips.github.io/libvips/">libvips</a>, as it's very fast and resource-efficient.</p> <p>As part of making Lorem Picsum as easy to use as possible, we've never required any registration, API keys, or enforced any usage limits.
This has generally worked out well, but once in a while a high traffic site deploys production code calling our API, usually accidentally, which leads to two main problems:</p> <ul><li>Extreme load on the service: One site used Lorem Picsum for fallback images when they failed to serve an image. At some point, their image service broke, leading to several hundred millions of requests per month hitting Lorem Picsum, without them noticing. They were however very quick in rectifying this error once we contacted them.</li> <li>Privacy concerns. In the past, some sites have included an image from Lorem Picsum on every page in production, leaking user information and activity through the URL in the Referral header.</li></ul> <p>While we allow the use of Lorem Picsum for pretty much anything, when someone has used the service to the point of instability/very high load, we've tried to contact them and ask them to stop. This has generally worked out well, and in most cases the usage was accidental. However, in a few cases, we've gotten no responses to such attempts, which lead to us needing to block the sites in our cache layer.</p> <h2 id="architecture">Architecture</h2> <br> <p><img src="/_saber/2d8708917dfe88beb3d329de0dcf849a-1000.png" alt="img"></p> <p>Lorem Picsum is designed to be stateless, and runs on top of <a rel="noopener noreferrer" target="_blank" href="https://kubernetes.io/">Kubernetes</a>. The overall system consists of a few different components, as can be seen in the diagram above.</p> <p>When a user requests an image, a user request hits the Picsum API service, which lives on <a rel="noopener noreferrer" target="_blank" href="https://picsum.photos">picsum.photos</a>. It validates the incoming image request, and responds with a redirect to our image service, at <a rel="noopener noreferrer" target="_blank" href="https://fastly.picsum.photos">fastly.picsum.photos</a>.</p> <p>The URL for every image being returned by the image service is normalised based on the image id/width/height/etc, and hmac signed, to allow for better caching while still being able return random images in the Picsum API itself (eg, picsum.photos/200/200), and removing the need for the image service to do additional validation.</p> <p>Due to the redirect response, a second request is made to the image service by the user. The image service checks if the image is cached in the <a href="/blog/lorem-picsum/#image-storage-cache">image storage cache</a>. If not, it fetches it from the <a href="/blog/lorem-picsum/#image-storage">image storage</a>, processes the image and returns it to the user. This gets cached by our <a rel="noopener noreferrer" target="_blank" href="https://fastly.com">CDN</a>, to speed up future requests and avoid re-processing an image every time it is requested.</p> <h4 id="load-balancing">Load balancing</h4> <p>We use <a rel="noopener noreferrer" target="_blank" href="https://www.digitalocean.com/products/load-balancer/">DigitalOcean's load balancer</a> to handle incoming requests and pass them on to our cache layer.</p> <h4 id="caching">Caching</h4> <p>To reduce the load on the image processor, we aggressively cache images using our CDN <a rel="noopener noreferrer" target="_blank" href="https://fastly.com">Fastly</a>.</p> <h4 id="application">Application</h4> <p>The Lorem Picsum application, written in <a rel="noopener noreferrer" target="_blank" href="https://golang.org/">Go</a>, is responsible for processing images and serving up files for the website.
The website is statically generated during build-time using <a rel="noopener noreferrer" target="_blank" href="https://gulpjs.com/">Gulp</a>.</p> <h4 id="image-storage">Image storage</h4> <p>We use an object storage to store the source images, namely <a rel="noopener noreferrer" target="_blank" href="https://www.digitalocean.com/products/spaces/">DigitalOcean Spaces</a>.</p> <h4 id="image-storage-cache">Image storage cache</h4> <p>We store all source images in <a rel="noopener noreferrer" target="_blank" href="https://redis.io/">Redis</a> to avoid fetching them from the image storage on every request.</p> <h2 id="orchestration">Orchestration</h2> <p>We use <a rel="noopener noreferrer" target="_blank" href="https://www.terraform.io/">Terraform</a> for orchestration; to set up and configure all the services on DigitalOcean.
This allows any changes to be versioned in the git repo and makes it easy to re-create the environment if needed.</p> <h2 id="open-source">Open source</h2> <p>Lorem Picsum is fully open source and licensed under MIT.
Source code and documentation can be found in the <a rel="noopener noreferrer" target="_blank" href="https://github.com/DMarby/picsum-photos">GitHub repo</a>.</p> <h2 id="thanks">Thanks</h2> <p>Lorem Picsum wouldn't exist without the support of <a rel="noopener noreferrer" target="_blank" href="https://www.digitalocean.com">DigitalOcean</a> and <a rel="noopener noreferrer" target="_blank" href="https://fastly.com">Fastly</a>.
Big thanks to them for providing the infrastructure for Picsum.</p>]]></content>
        <published>2019-08-11T00:00:00.000Z</published>
    </entry>
</feed>