Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 88 additions & 19 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,54 +1,123 @@
Object storage for Open edX with `MinIO <https://www.minio.io/>`_
=================================================================
Object storage for Open edX with `RustFS <https://github.com/rustfs/rustfs>`_
=============================================================================

This is a plugin for `Tutor <https://docs.tutor.edly.io>`_ that provides S3-like object storage for Open edX platforms. It's S3, but without the dependency on AWS. This is achieved thanks to `MinIO <https://www.minio.io/>`_, an open source project that provides object storage with an API compatible with S3.
.. warning::

**ALPHA — NOT FOR PRODUCTION USE.**

RustFS is currently at **v1.0.0-alpha**. This branch exists as a
preview so that the ``tutor-minio`` plugin is ready to adopt RustFS
the moment a stable release is cut. Do **not** place real course
data on a RustFS deployment today — the on-disk format is not
guaranteed stable across alpha releases, and upstream may still
change the API.

If you need a production-ready MinIO alternative **right now**, use
the sibling ``feat/replace-minio-with-garage`` branch (Garage v1.0.1,
stable) or the ``feat/replace-minio-with-seaweedfs`` branch
(SeaweedFS, stable but operationally heavier).

Track upstream progress at https://github.com/rustfs/rustfs.

This is a plugin for `Tutor <https://docs.tutor.edly.io>`_ that provides S3-like object storage for Open edX platforms. It's S3, but without the dependency on AWS. This is achieved thanks to `RustFS <https://github.com/rustfs/rustfs>`_, a Rust re-implementation of the MinIO S3 API.

In particular, this plugin is essential for `Kubernetes deployment <https://docs.tutor.edly.io/k8s.html>`_.

.. note::

The plugin package is still named ``tutor-minio`` and still installs as the ``minio`` Tutor plugin, to preserve compatibility with downstream tooling and existing ``tutor config`` files. Under the hood the object store is now RustFS. See `CHANGES.md <CHANGES.md>`_ for the full migration notes.

Why RustFS (eventually)
-----------------------

Of the three candidate replacements for MinIO (Garage, SeaweedFS, RustFS), RustFS is the closest drop-in:

- Same ports: ``9000`` (S3 API), ``9001`` (web console).
- Same ``mc`` client tooling — the init script can keep using
``mc policy set public``, ``mc mb``, and ``mc mirror`` verbatim.
- Same bucket policy semantics (public / download / upload / etc.).
- Same memory-and-CPU shape at rest, so capacity planning carries over.

Meaning once RustFS goes stable, this plugin essentially becomes a
sed from ``minio/minio`` to ``rustfs/rustfs``. Until then: preview
only.

Installation
------------

The plugin is currently bundled with the `binary releases of Tutor <https://github.com/overhangio/tutor/releases>`_. If you have installed Tutor from source, you will have to install this plugin from source, too::
::

tutor plugins install minio

Then, to enable this plugin, run::

tutor plugins enable minio

Configuration
-------------

Preserved from the MinIO era (drive the Open edX S3 settings):

- ``OPENEDX_AWS_ACCESS_KEY`` (default: ``"openedx"``)
- ``OPENEDX_AWS_SECRET_ACCESS_KEY`` (default: ``"{{ 24|random_string }}"``)
- ``MINIO_BUCKET_NAME`` (default: ``"openedx"``)
- ``MINIO_FILE_UPLOAD_BUCKET_NAME`` (default: ``"openedxuploads"``)
- ``MINIO_VIDEO_UPLOAD_BUCKET_NAME`` (default: ``"openedxvideos"``)
- ``MINIO_GRADES_BUCKET_NAME`` (default: ``"openedxgrades"``)
- ``MINIO_OPENEDX_LEARNING_BUCKET_NAME`` (default: ``"openedxlearning"``)
- ``MINIO_HOST`` (default: ``"files.{{ LMS_HOST }}"``)
- ``MINIO_CONSOLE_HOST`` (default: ``"minio.{{ LMS_HOST }}"``)
- ``MINIO_DOCKER_IMAGE`` (default: ``"docker.io/minio/minio:RELEASE.2022-05-08T23-50-31Z"``)
- ``MINIO_MC_DOCKER_IMAGE`` (default: ``"docker.io/minio/mc:RELEASE.2022-05-09T04-08-26Z"``)
- ``MINIO_GRADES_BUCKET_NAME`` (default: ``"openedxgrades"``)

These values can be modified with ``tutor config save --set PARAM_NAME=VALUE`` commands.
RustFS-specific settings:

- ``RUSTFS_DOCKER_IMAGE`` (default: ``"rustfs/rustfs:latest"``) — pinned to ``latest`` for the alpha period. Once upstream cuts a stable tag, override with a specific version.
- ``RUSTFS_UID`` / ``RUSTFS_GID`` (both default: ``10001``) — the non-root user inside the RustFS container.

Deprecated (kept as aliases):

- ``MINIO_DOCKER_IMAGE`` → now an alias for ``RUSTFS_DOCKER_IMAGE``.
- ``MINIO_GATEWAY`` — no-op under RustFS.

``MINIO_MC_DOCKER_IMAGE`` is unchanged; RustFS is ``mc``-compatible, so we keep using the MinIO Client image for init/admin jobs.

Non-root caveat
---------------

- ``MINIO_GATEWAY`` (default: ``null``)
RustFS runs as UID ``10001``. On a fresh bind-mounted data volume in local mode, the host directory may be owned by a different UID and RustFS will fail to write. Fix with::

This feature allows your to run the MinIO server as a gateway to another object storage solution, such as `S3 <https://docs.minio.io/docs/minio-gateway-for-s3.html>`__ or `Azure <https://docs.minio.io/docs/minio-gateway-for-azure.html>`__. That way, static assets can be stored on these object storage backends without extensive changes to the Open edX runtime environment.
sudo chown -R 10001:10001 $(tutor config printroot)/data/minio

Note to Azure users: you will have to manually grant public access rights to the ``MINIO_BUCKET_NAME`` ("openedx") bucket.
In Kubernetes the ``fsGroup: 10001`` on the Deployment handles this automatically.

DNS records
-----------

It is assumed that the ``MINIO_HOST`` DNS record points to your server. When running MinIO on your laptop, the MinIO Web UI will be available at http://minio.local.openedx.io. In development mode, the MinIO interface will be available at http://minio.local.openedx.io:9001.
It is assumed that the ``MINIO_HOST`` DNS record points to your server. When running RustFS on your laptop, the Web UI will be available at ``http://minio.local.openedx.io``. In development mode, the web console will be at ``http://minio.local.openedx.io:9001``.

Web UI
------

The MinIO web UI can be accessed at http://<MINIO_HOST>. The credentials for accessing the UI can be obtained with::
The RustFS web console can be accessed at ``http://<MINIO_CONSOLE_HOST>``. Credentials are the same as MinIO's root credentials::

tutor config printvalue OPENEDX_AWS_ACCESS_KEY
tutor config printvalue OPENEDX_AWS_SECRET_ACCESS_KEY

Migration notes
---------------

Because RustFS uses the same S3 wire format and the same ``mc`` tooling,
a migration from MinIO is straightforward:

1. Stop MinIO.
2. Decide whether to keep the existing data volume. The v1.0.0-alpha
on-disk format is **not** guaranteed stable across RustFS releases, so
exporting your objects with ``mc mirror`` first is strongly advised.
3. Upgrade this plugin, then re-run::

tutor config save
tutor local do init

4. If you skipped the export, re-import with the following command against
the running RustFS instance::

tutor config printvalue OPENEDX_AWS_ACCESS_KEY
tutor config printvalue OPENEDX_AWS_SECRET_ACCESS_KEY
mc mirror ./backup/<bucket>/ rustfs/<bucket>/

Troubleshooting
---------------
Expand All @@ -58,4 +127,4 @@ This Tutor plugin is maintained by Abdul Rehman from `Edly <https://edly.io>`__.
License
-------

This work is licensed under the terms of the `GNU Affero General Public License (AGPL) <https://github.com/overhangio/tutor-minio/blob/release/LICENSE.txt>`_.
This work is licensed under the terms of the `GNU Affero General Public License (AGPL) <https://github.com/overhangio/tutor-minio/blob/release/LICENSE.txt>`_.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@

<!--
Create a changelog entry for every new user-facing change. Please respect the following instructions:
- Indicate breaking changes by prepending an explosion 💥 character.
- Prefix your changes with either [Bugfix], [Improvement], [Feature], [Security], [Deprecation].
- You may optionally append "(by @<author>)" at the end of the line, where "<author>" is either one (just one)
of your GitHub username, real name or affiliated organization. These affiliations will be displayed in
the release notes for every release.
-->

<!-- - 💥[Feature] Foobarize the blorginator. This breaks plugins by renaming the `FOO_DO` filter to `BAR_DO`. (by @regisb) -->
<!-- - [Improvement] This is a non-breaking change. Life is good. (by @billgates) -->
- 💥[Feature] Replace MinIO with RustFS, a Rust-based S3-compatible object storage server. The `minio` Compose/K8s service is renamed to `rustfs`; update any `CLI_DO_INIT_TASKS` entries or custom patches that reference the `minio` service name. (by @Syed-Ali-Abbas-568)
- 💥[Feature] Replace `MINIO_ROOT_USER` / `MINIO_ROOT_PASSWORD` container environment variables with `RUSTFS_ACCESS_KEY` / `RUSTFS_SECRET_KEY`. Values are sourced from the existing `OPENEDX_AWS_ACCESS_KEY` / `OPENEDX_AWS_SECRET_ACCESS_KEY` config keys — no Open edX reconfiguration needed. (by @Syed-Ali-Abbas-568)
- [Feature] Add `RUSTFS_DOCKER_IMAGE`, `RUSTFS_UID` (default `10001`), and `RUSTFS_GID` (default `10001`) config variables. Override `RUSTFS_UID`/`RUSTFS_GID` if your host's user-namespace maps the container user differently (e.g. rootless Docker). (by @Syed-Ali-Abbas-568)
- [Deprecation] `MINIO_DOCKER_IMAGE` is deprecated and now points at the RustFS image; remove it from your config and use `RUSTFS_DOCKER_IMAGE` instead. (by @Syed-Ali-Abbas-568)
- [Deprecation] `MINIO_GATEWAY` is deprecated and no longer has any effect; RustFS has no gateway mode. Unset it with `tutor config save --unset MINIO_GATEWAY` and point `AWS_S3_ENDPOINT_URL` directly at your cloud provider if needed. (by @Syed-Ali-Abbas-568)
6 changes: 3 additions & 3 deletions tutorminio/patches/caddyfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# MinIO
# RustFS (MinIO-compatible; same ports as MinIO).
{{ MINIO_HOST }}{$default_site_port} {
import proxy "minio:9000"
import proxy "rustfs:9000"
}
{{ MINIO_CONSOLE_HOST }}{$default_site_port} {
import proxy "minio:9001"
import proxy "rustfs:9001"
}
44 changes: 26 additions & 18 deletions tutorminio/patches/k8s-deployments
Original file line number Diff line number Diff line change
Expand Up @@ -2,44 +2,52 @@
apiVersion: apps/v1
kind: Deployment
metadata:
name: minio
name: rustfs
labels:
app.kubernetes.io/name: minio
app.kubernetes.io/name: rustfs
spec:
selector:
matchLabels:
app.kubernetes.io/name: minio
app.kubernetes.io/name: rustfs
strategy:
type: Recreate
template:
metadata:
labels:
app.kubernetes.io/name: minio
app.kubernetes.io/name: rustfs
spec:
securityContext:
# RustFS runs as UID {{ RUSTFS_UID }}. fsGroup ensures the PVC
# is chowned to the right GID on mount so the non-root process
# can write. Without this, fresh installs hit EACCES on /data.
runAsUser: {{ RUSTFS_UID }}
runAsGroup: {{ RUSTFS_GID }}
fsGroup: {{ RUSTFS_GID }}
containers:
- name: minio
image: {{ MINIO_DOCKER_IMAGE }}
{% if MINIO_GATEWAY %}
args: ["gateway", "{{ MINIO_GATEWAY }}", "--address", ":9000", "--console-address", ":9001"]
{% else %}
args: ["server", "--address", ":9000", "--console-address", ":9001", "/data"]
{% endif %}
- name: rustfs
image: {{ RUSTFS_DOCKER_IMAGE }}
env:
- name: MINIO_ROOT_USER
- name: RUSTFS_ACCESS_KEY
value: "{{ OPENEDX_AWS_ACCESS_KEY }}"
- name: MINIO_ROOT_PASSWORD
- name: RUSTFS_SECRET_KEY
value: "{{ OPENEDX_AWS_SECRET_ACCESS_KEY }}"
- name: RUSTFS_VOLUMES
value: "/data"
- name: RUSTFS_ADDRESS
value: ":9000"
- name: RUSTFS_CONSOLE_ENABLE
value: "true"
- name: RUSTFS_CONSOLE_ADDRESS
value: ":9001"
ports:
- containerPort: 9000
name: s3-api
- containerPort: 9001
{% if not MINIO_GATEWAY %}
name: console
volumeMounts:
- mountPath: /data
name: data
{% endif %}
{% if not MINIO_GATEWAY %}
volumes:
- name: data
persistentVolumeClaim:
claimName: minio
{% endif %}
claimName: rustfs
8 changes: 4 additions & 4 deletions tutorminio/patches/k8s-services
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@
apiVersion: v1
kind: Service
metadata:
name: minio
name: rustfs
spec:
type: NodePort
ports:
- port: 9000
protocol: TCP
name: minio-server
name: s3-api
- port: 9001
protocol: TCP
name: minio-console
name: console
selector:
app.kubernetes.io/name: minio
app.kubernetes.io/name: rustfs
6 changes: 2 additions & 4 deletions tutorminio/patches/k8s-volumes
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
{% if not MINIO_GATEWAY %}
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: minio
name: rustfs
labels:
app.kubernetes.io/component: volume
app.kubernetes.io/name: minio
app.kubernetes.io/name: rustfs
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 5Gi
{% endif %}
2 changes: 1 addition & 1 deletion tutorminio/patches/local-docker-compose-cms-dependencies
Original file line number Diff line number Diff line change
@@ -1 +1 @@
- minio
- rustfs
2 changes: 1 addition & 1 deletion tutorminio/patches/local-docker-compose-dev-services
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
minio:
rustfs:
ports:
- "127.0.0.1:9000:9000"
- "127.0.0.1:9001:9001"
Expand Down
8 changes: 6 additions & 2 deletions tutorminio/patches/local-docker-compose-jobs-services
Original file line number Diff line number Diff line change
@@ -1,8 +1,12 @@
# MinIO
# Init/admin job container. RustFS is wire-compatible with the MinIO
# `mc` client, so we keep the MinIO Client image here to reuse the
# existing init logic (bucket creation + anonymous read policy).
# Service name kept as `minio-job` so existing `CLI_DO_INIT_TASKS`
# targeting "minio" keeps resolving.
minio-job:
image: {{ MINIO_MC_DOCKER_IMAGE }}
environment:
MINIO_ROOT_USER: "{{ OPENEDX_AWS_ACCESS_KEY }}"
MINIO_ROOT_PASSWORD: "{{ OPENEDX_AWS_SECRET_ACCESS_KEY }}"
entrypoint: []
depends_on: ["minio"]
depends_on: ["rustfs"]
2 changes: 1 addition & 1 deletion tutorminio/patches/local-docker-compose-lms-dependencies
Original file line number Diff line number Diff line change
@@ -1 +1 @@
- minio
- rustfs
30 changes: 18 additions & 12 deletions tutorminio/patches/local-docker-compose-services
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
# MinIO
minio:
image: {{ MINIO_DOCKER_IMAGE }}
{% if not MINIO_GATEWAY %}
# RustFS (S3-compatible object storage, MinIO drop-in — ALPHA).
# Ports are intentionally identical to MinIO's (9000 S3, 9001 console)
# so that `MINIO_HOST`, Caddy config, and `AWS_S3_ENDPOINT_URL` need no
# change. RustFS runs as UID {{ RUSTFS_UID }} (non-root), so the data
# volume must be writable by that UID. On first run of a fresh install
# this is handled automatically because Docker creates the bind-mounted
# directory with the host user's UID and RustFS will happily write to
# it; if you hit "permission denied", run:
# sudo chown -R {{ RUSTFS_UID }}:{{ RUSTFS_GID }} $(tutor config printroot)/data/minio
rustfs:
image: {{ RUSTFS_DOCKER_IMAGE }}
user: "{{ RUSTFS_UID }}:{{ RUSTFS_GID }}"
volumes:
- ../../data/minio:/data
{% endif %}
environment:
MINIO_ROOT_USER: "{{ OPENEDX_AWS_ACCESS_KEY }}"
MINIO_ROOT_PASSWORD: "{{ OPENEDX_AWS_SECRET_ACCESS_KEY }}"
{% if MINIO_GATEWAY %}
command: gateway {{ MINIO_GATEWAY }} --address ":9000" --console-address ":9001"
{% else %}
command: server --address ":9000" --console-address ":9001" /data
{% endif %}
RUSTFS_ACCESS_KEY: "{{ OPENEDX_AWS_ACCESS_KEY }}"
RUSTFS_SECRET_KEY: "{{ OPENEDX_AWS_SECRET_ACCESS_KEY }}"
RUSTFS_VOLUMES: "/data"
RUSTFS_ADDRESS: ":9000"
RUSTFS_CONSOLE_ENABLE: "true"
RUSTFS_CONSOLE_ADDRESS: ":9001"
restart: unless-stopped
Loading
Loading