Case Study: Minio S3 - Part 1

Case Study: Minio S3 - Part 1

Introduction

Amazon Simple Storage Service (Amazon S3) is an object storage service offering industry-leading scalability, data availability, security, and performance. Millions of customers of all sizes and industries store, manage, analyze, and protect any amount of data for virtually any use case, such as data lakes, cloud-native applications, and mobile apps. With cost-effective storage classes and easy-to-use management features, you can optimize costs, organize and analyze data, and configure fine-tuned access controls to meet specific business and compliance requirements.

Amazon’s S3 became the de facto standard object storage system, and there exist several fully open source implementations of the protocol. One of them is MinIO: designed to allow enterprises to consolidate all of their data on a single, private cloud namespace. Architected using the same principles as the hyperscalers, AIStor delivers performance at scale at a fraction of the cost compared to the public cloud.

IPng Networks is an Internet Service Provider, but I also dabble in self-hosting things, for example [PeerTube], [Mastodon], [Immich], [Pixelfed] and of course [Hugo]. These services all have one thing in common: they tend to use lots of storage when they grow. At IPng Networks, all hypervisors ship with enterprise SAS flash drives, mostly 1.92TB and 3.84TB. Scaling up each of these services, and backing them up safely, can be quite the headache.

This article is for the storage-buffs. I’ll set up a set of distributed MinIO nodes from scatch.

Physical

I’ll start with the basics. I still have a few Dell R720 servers laying around, they are getting a bit older but still have 24 cores and 64GB of memory. First I need to get me some disks. I order 36pcs of 16TB SATA enterprise disk, a mixture of Seagate EXOS and Toshiba MG series disks. I’ve once learned (the hard way), that buying a big stack of disks from one production run is a risk - so I’ll mix and match the drives.

Three trays of caddies and a melted credit card later, I have 576TB of SATA disks safely in hand. Each machine will carry 192TB of raw storage. The nice thing about this chassis is that Dell can ship them with 12x 3.5" SAS slots in the front, and 2x 2.5" SAS slots in the rear of the chassis.

So I’ll install Debian Bookworm on one small 480G SSD in software RAID1.

Cloning an install

I have three identical machines so in total I’ll want six of these SSDs. I temporarily screw the other five in 3.5" drive caddies and plug them into the first installed Dell, which I’ve called :

The first command takes my installed disk, , and copies the first sector over to the other five. This will give them the same partition table. Next, I’ll add the first partition of each disk to the raidset. Then, I’ll expand the raidset to have six members, after which the kernel starts a recovery process that syncs the newly added paritions to (by copying from to all other disks at once). Finally, I’ll watch this exciting movie and grab a cup of tea.

Once the disks are fully copied, I’ll shut down the machine and distribute the disks to their respective Dell R720, two each. Once they boot they will all be identical. I’ll need to make sure their hostnames, and machine/host-id are unique, otherwise things like bridges will have overlapping MAC addresses - ask me how I know:

After which I have three beautiful and unique machines:

  • : which will go into my server rack at the IPng office.

  • : which will go to [Daedalean], doing AI since before it was all about vibe coding.

  • : which will go to [IP-Max], one of the best ISPs on the planet. 🥰

Deploying Minio

The user guide that MinIO provides [ref] is super good, arguably one of the best documented open source projects I’ve ever seen. it shows me that I can do three types of install. A ‘Standalone’ with one disk, a ‘Standalone Multi-Drive’, and a ‘Distributed’ deployment. I decide to make three independent standalone multi-drive installs. This way, I have less shared fate, and will be immune to network partitions (as these are going to be in three different physical locations). I’ve also read about per-bucket replication, which will be an excellent way to get geographical distribution and active/active instances to work together.

I feel good about the single-machine multi-drive decision. I follow the install guide [ref] for this deployment type.

IPng Frontends

At IPng I use a private IPv4/IPv6/MPLS network that is not connected to the internet. I call this network [IPng Site Local]. But how will users reach my Minio install? I have four redundantly and geographically deployed frontends, two in the Netherlands and two in Switzerland. I’ve described the frontend setup in a [previous article] and the certificate management in [this article].

I’ve decided to run the service on these three regionalized endpoints:

  1. which will back into

  2. which will back into

  3. which will back into

The first thing I take note of is that S3 buckets can be either addressed by path, in other words something like , but they can also be addressed by virtual host, like so: . A subtle difference, but from the docs I understand that Minio needs to have control of the whole space under its main domain.

There’s a small implication to this requirement – the Web Console that ships with MinIO (eh, well, maybe that’s going to change, more on that later), will want to have its own domain-name, so I choose something simple: and so on. This way, somebody might still be able to have a bucket name called :)

Let’s Encrypt Certificates

Alright, so I will be neading nine domains into this new certificate which I’ll simply call . I configure it in Ansible:

I run the playbook and it does two things:

  1. On the machines from group and , it will ensure there exists a user with an SSH key and write permissions to ; this is where the automation will write (and update) the certificate keys.

  2. On the machine, it’ll create two files. One is the certificate requestor, and the other is a certificate distribution script that will copy the cert to the right machine(s) when it renews.

On the machine, I’ll run the cert request for the first time:

The first script asks me to add the _acme-challenge DNS entries, which I’ll do, for example on the instance (and similar for the and ones:

I push and reload the zonefile with these changes after which the certificate gets requested and a cronjob added to check for renewals. The second script will copy the newly created cert to all three machines, and all four machines. From now on, every 90 days, a new cert will be automatically generated and distributed. Slick!

NGINX Configs

With the LE wildcard certs in hand, I can create an NGINX frontend for these minio deployments.

First, a simple redirector service that punts people on port 80 to port 443:

Next, the Minio API service itself which runs on port 9000, with a configuration snippet inspired by the MinIO [docs]:

Finally, the Minio Console service which runs on port 9090:

This last one has an NGINX trick. It will only allow users in if they are in the map called , which contains a set of IPv4 and IPv6 prefixes. Visitors who are not in this map will receive an HTTP redirect back to the [IPng.ch] homepage instead.

I run the Ansible Playbook which contains the NGINX changes to all frontends, but of course nothing runs yet, because I haven’t yet started MinIO backends.

MinIO Backends

The first thing I need to do is get those disks mounted. MinIO likes using XFS, so I’ll install that and prepare the disks as follows:

From the top: I’ll install which contains the things I need to manipulate XFS filesystems in Debian. Then I’ll install the kernel module, and make sure it gets inserted upon subsequent startup by adding it to and regenerating the initrd for the installed kernels.

Next, I’ll format all twelve 16TB disks (which are - on these machines), and add their resulting blockdevice id’s to so they get persistently mounted on reboot.

Finally, I’ll create their mountpoints, mount all XFS filesystems, and chown them to the user that MinIO is running as. End result:

MinIO likes to be configured using environment variables - and this is likely because it’s a popupar thing to run in a containerized environment like Kubernetes. The maintainers ship it also as a Debian package, which will read its environment from , and I’ll prepare that file as follows:

Incidentally - I am pretty pleased with this 192TB disk tank, sporting 24 cores, 64GB memory and 2x10G network, casually hanging out at 154 Watts of power all up. Slick!

MinIO implements erasure coding as a core component in providing availability and resiliency during drive or node-level failure events. MinIO partitions each object into data and parity shards and distributes those shards across a single so-called erasure set. Under the hood, it uses [Reed-Solomon] erasure coding implementation and partitions the object for distribution. From the MinIO website, I’ll borrow a diagram to show how it looks like on a single node like mine above this paragraph.

Anyway, MinIO detects 12 disks and installs an erasure set with 8 data disks and 4 parity disks, which it calls encoding, also known in the industry as . Just like that, the thing shoots to life. Awesome!

MinIO Client

On Summer, I’ll install the MinIO Client called . This is easy because the maintainers ship a Linux binary which I can just download. On OpenBSD, they don’t do that. Not a problem though, on Squanchy, Pencilvester and Glootie, I will just the client. Using the commandline, I can all any of the S3 APIs on my new MinIO instance:

Cool beans. I think I should get rid of this root account though, I’ve installed those credentials into the environment file, but I don’t want to keep them out in the open. So I’ll make an account for myself and assign me reasonable privileges, called in the default install:

OK, I feel less gross now that I’m not operating as root on the MinIO deployment. Using my new user-powers, let me set some metadata on my new minio server:

By the way, what’s really cool about these open standards is that both the Amazon client works with MinIO, but also works with AWS!

MinIO Console

Although I’m pretty good with APIs and command line tools, there’s some benefit also in using a Graphical User Interface. MinIO ships with one, but there as a bit of a kerfuffle in the MinIO community. Unfortunately, these are pretty common – Redis (an open source key/value storage system) changed their offering abruptly. Terraform (an open source infrastructure-as-code tool) changed their licensing at some point. Ansible (an open source machine management tool) changed their offering also. MinIO developers decided to strip their console of ~all features recently. The gnarly bits are discussed on [reddit]. but suffice to say: the same thing that happened in literally 100% of the other cases, also happened here. Somebody decided to simply fork the code from before it was changed.

Enter OpenMaxIO. A cringe worthy name, but it gets the job done. Reading up on the [GitHub], reviving the fully working console is pretty straight forward – that is, once somebody spent a few days figuring it out. Thank you for this excellent pointer. With this, I can create a systemd service for the console and start it:

The first snippet is an update to the MinIO configuration that instructs it to redirect users who are not trying to use the API to the console endpoint on , and then the console-server needs to know where to find the API, which from its vantage point is running on . Hello, beautiful fully featured console:

MinIO Prometheus

MinIO ships with a prometheus metrics endpoint, and I notice on its console that it has a nice metrics tab, which is fully greyed out. This is most likely because, well, I don’t have a Prometheus install here yet. I decide to keep the storage nodes self-contained and start a Prometheus server on the local machine. I can always plumb that to IPng’s Grafana instance later.

For now, I’ll install Prometheus as follows:

In the first snippet, I’ll tell MinIO where it should find its Prometheus instance. Since the MinIO console service is running on port 9090, and this is also the default port for Prometheus, I will run Promtheus on port 19090 instead. From reading the MinIO docs, I can see that normally MinIO will want prometheus to authenticate to it before it’ll allow the endpoints to be scraped. I’ll turn that off by making these public. On the IPng Frontends, I can always remove access to /minio/v2 and simply use the IPng Site Local access for local Prometheus scrapers instead.

After telling Prometheus its runtime arguments (in ) and its scraping endpoints (in ), I can restart minio and prometheus. A few minutes later, I can see the Metrics tab in the console come to life.

But now that I have this prometheus running on the MinIO node, I can also add it to IPng’s Grafana configuration, by adding a new data source on and pointing the default Grafana [Dashboard] at it:

A two-for-one: I will both be able to see metrics directly in the console, but also I will be able to hook up these per-node prometheus instances into IPng’s alertmanager also, and I’ve read some [docs] on the concepts. I’m really liking the experience so far!

MinIO Nagios

Prometheus is fancy and all, but at IPng Networks, I’ve been doing monitoring for a while now. As a dinosaur, I still have an active [Nagios] install, which autogenerates all of its configuration using the Ansible repository I have. So for the new Ansible group called , I will autogenerate the following snippet:

I’ve shown the snippet for IPv6 but I also have three services defined for legacy IP in the hostgroup . The check command here uses which has the IPv4 or IPv6 address to talk to, for the port to consule, for the URI to hit and an option for a regular expression to expect in the output. For the Nagios afficianados out there: my Ansible correspond one to one with autogenerated Nagios . This allows me to add arbitrary checks by group-type, like above in the group for IPv4 and IPv6.

In the MinIO [docs] I read up on the Healthcheck API. I choose to monitor the Cluster Write Quorum on my minio deployments. For Prometheus, I decide to hit the endpoint and expect the to be among them. Finally, for the MinIO Console, I expect to see a login screen with the words in the returned page. I guessed right, because Nagios is all green:

My First Bucket

The IPng website is a statically generated Hugo site, and when-ever I submit a change to my Git repo, a CI/CD runner (called [Drone]), picks up the change. It re-builds the static website, and copies it to four redundant NGINX servers.

But IPng’s website has amassed quite a bit of extra files (like VM images and VPP packages that I publish), which are copied separately using a simple push script I have in my home directory. This avoids all those big media files from cluttering the Git repository. I decide to move this stuff into S3:

OK, two things that immediately jump out at me. This stuff is fast: Summer is connected with a 2.5GbE network card, and she’s running hard, copying the 6.5GB of data that are in these web assets essentially at line rate. It doesn’t really surprise me because Summer is running off of Gen4 NVME, while MinIO has 12 spinning disks which each can write about 160MB/s or so sustained [ref], with 24 CPUs to tend to the NIC (2x10G) and disks (2x SSD, 12x LFF). Should be plenty!

The second is that MinIO allows for buckets to be publicly shared in three ways: 1) read-only by setting ; 2) write-only by setting , and 3) read-write by setting . I set here, which means I should be able to fetch an asset now publicly:

The first here shows the path-based access, while the second one shows an equivalent virtual-host based access. Both retrieve the file I just pushed via the public Internet. Whoot!

What’s Next

I’m going to be moving [Restic] backups from IPng’s ZFS storage pool to this S3 service over the next few days. I’ll also migrate PeerTube and possibly Mastodon from NVME based storage to replicated S3 buckets as well. Finally, the IPng website media that I mentinoed above, should make for a nice followup article. Stay tuned!

Stefan Lamble

Staff Silicon Design Engineer at Intel Corporation

1mo

Jellyfin is great

Niek Van Der Ven

Technical Sales Engineer Webscale at Nokia

1mo

Great project that is also on my short term summer list.. one question if the servers are more local eg same rack or same Dc campes would you still take standalone setup ?

Craig Spiers

Independent Consultant - Telecommunications & Carrier

1mo

I actually love this, how cool!!

Ingmar Stein

Staff Software Engineer, Tech Lead / Manager at Google

1mo

Always interesting to see what services others are self-hosting. Here's what's currently running on my homelab: paperless-ngx, miniflux, Home Assistant, tronbyt-server, InfluxDB2, Grafana, Loki, Alloy, cloudflared, EMQX, TeslaMate, Prometheus, technitium-dns, Caddy, Postgres, Redis, Jellyfin.

wait, you host Pixelfed? nice! 😛

To view or add a comment, sign in

Others also viewed

Explore topics