feat: add configurable S3 object storage
This commit is contained in:
56
docker/storage/docker-compose.storage.yml.example
Normal file
56
docker/storage/docker-compose.storage.yml.example
Normal file
@@ -0,0 +1,56 @@
|
||||
name: imageforge-storage
|
||||
|
||||
services:
|
||||
garage:
|
||||
image: dxflrs/garage:v2.3.0
|
||||
command: ["/garage", "server", "--single-node"]
|
||||
restart: unless-stopped
|
||||
read_only: true
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
cap_drop:
|
||||
- ALL
|
||||
pids_limit: 256
|
||||
mem_limit: 1200m
|
||||
cpus: 1.75
|
||||
ports:
|
||||
- "127.0.0.1:3900:3900"
|
||||
- "10.70.0.2:3900:3900"
|
||||
- "10.70.0.2:3901:3901"
|
||||
- "127.0.0.1:3903:3903"
|
||||
volumes:
|
||||
- ./garage.toml:/etc/garage.toml:ro
|
||||
- /srv/garage/meta:/var/lib/garage/meta
|
||||
- /srv/garage/data:/var/lib/garage/data
|
||||
- /srv/garage/snapshots:/var/lib/garage/snapshots
|
||||
secrets:
|
||||
- source: garage_rpc_secret
|
||||
target: garage_rpc_secret
|
||||
mode: 0400
|
||||
- source: garage_admin_token
|
||||
target: garage_admin_token
|
||||
mode: 0400
|
||||
- source: garage_metrics_token
|
||||
target: garage_metrics_token
|
||||
mode: 0400
|
||||
tmpfs:
|
||||
- /tmp:size=64m,mode=1777
|
||||
healthcheck:
|
||||
test: ["CMD", "/garage", "status"]
|
||||
interval: 15s
|
||||
timeout: 5s
|
||||
retries: 10
|
||||
start_period: 20s
|
||||
logging:
|
||||
driver: json-file
|
||||
options:
|
||||
max-size: "20m"
|
||||
max-file: "3"
|
||||
|
||||
secrets:
|
||||
garage_rpc_secret:
|
||||
file: ./secrets/garage_rpc_secret
|
||||
garage_admin_token:
|
||||
file: ./secrets/garage_admin_token
|
||||
garage_metrics_token:
|
||||
file: ./secrets/garage_metrics_token
|
||||
37
docker/storage/garage.toml.example
Normal file
37
docker/storage/garage.toml.example
Normal file
@@ -0,0 +1,37 @@
|
||||
replication_factor = 1
|
||||
consistency_mode = "consistent"
|
||||
|
||||
metadata_dir = "/var/lib/garage/meta"
|
||||
data_dir = "/var/lib/garage/data"
|
||||
metadata_snapshots_dir = "/var/lib/garage/snapshots"
|
||||
metadata_fsync = true
|
||||
data_fsync = false
|
||||
metadata_auto_snapshot_interval = "12h"
|
||||
use_local_tz = false
|
||||
|
||||
# The first node is small and traffic is currently low. SQLite avoids a large
|
||||
# memory map; a future multi-node rollout can use LMDB on the new nodes.
|
||||
db_engine = "sqlite"
|
||||
block_size = "1M"
|
||||
block_ram_buffer_max = "64MiB"
|
||||
block_max_concurrent_reads = 8
|
||||
block_max_concurrent_writes_per_request = 3
|
||||
|
||||
# JPEG, PNG, WebP, AVIF and ZIP outputs are already compressed.
|
||||
compression_level = 'none'
|
||||
|
||||
rpc_secret_file = "/run/secrets/garage_rpc_secret"
|
||||
rpc_bind_addr = "[::]:3901"
|
||||
rpc_public_addr = "10.70.0.2:3901"
|
||||
bootstrap_peers = []
|
||||
|
||||
[s3_api]
|
||||
api_bind_addr = "[::]:3900"
|
||||
s3_region = "garage"
|
||||
root_domain = ".s3.garage.internal"
|
||||
|
||||
[admin]
|
||||
api_bind_addr = "[::]:3903"
|
||||
admin_token_file = "/run/secrets/garage_admin_token"
|
||||
metrics_token_file = "/run/secrets/garage_metrics_token"
|
||||
metrics_require_token = true
|
||||
46
docker/storage/lifecycle.json
Normal file
46
docker/storage/lifecycle.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"Rules": [
|
||||
{
|
||||
"ID": "results-1d-safety",
|
||||
"Status": "Enabled",
|
||||
"Filter": { "Prefix": "results/1d/" },
|
||||
"Expiration": { "Days": 3 }
|
||||
},
|
||||
{
|
||||
"ID": "archives-1d-safety",
|
||||
"Status": "Enabled",
|
||||
"Filter": { "Prefix": "archives/1d/" },
|
||||
"Expiration": { "Days": 3 }
|
||||
},
|
||||
{
|
||||
"ID": "results-7d-safety",
|
||||
"Status": "Enabled",
|
||||
"Filter": { "Prefix": "results/7d/" },
|
||||
"Expiration": { "Days": 9 }
|
||||
},
|
||||
{
|
||||
"ID": "archives-7d-safety",
|
||||
"Status": "Enabled",
|
||||
"Filter": { "Prefix": "archives/7d/" },
|
||||
"Expiration": { "Days": 9 }
|
||||
},
|
||||
{
|
||||
"ID": "results-15d-safety",
|
||||
"Status": "Enabled",
|
||||
"Filter": { "Prefix": "results/15d/" },
|
||||
"Expiration": { "Days": 17 }
|
||||
},
|
||||
{
|
||||
"ID": "archives-15d-safety",
|
||||
"Status": "Enabled",
|
||||
"Filter": { "Prefix": "archives/15d/" },
|
||||
"Expiration": { "Days": 17 }
|
||||
},
|
||||
{
|
||||
"ID": "abort-incomplete-multipart",
|
||||
"Status": "Enabled",
|
||||
"Filter": { "Prefix": "" },
|
||||
"AbortIncompleteMultipartUpload": { "DaysAfterInitiation": 1 }
|
||||
}
|
||||
]
|
||||
}
|
||||
45
docker/storage/nginx-files.conf.example
Normal file
45
docker/storage/nginx-files.conf.example
Normal file
@@ -0,0 +1,45 @@
|
||||
server {
|
||||
listen 80;
|
||||
listen [::]:80;
|
||||
server_name files.example.com;
|
||||
|
||||
location /.well-known/acme-challenge/ {
|
||||
root /var/www/certbot;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
listen [::]:443 ssl http2;
|
||||
server_name files.example.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/files.example.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/files.example.com/privkey.pem;
|
||||
|
||||
# Signed URLs contain credentials and signatures. Do not write the query
|
||||
# string to the standard access log.
|
||||
access_log off;
|
||||
|
||||
location / {
|
||||
limit_except GET HEAD {
|
||||
deny all;
|
||||
}
|
||||
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_set_header Connection "";
|
||||
proxy_request_buffering off;
|
||||
proxy_buffering off;
|
||||
proxy_max_temp_file_size 0;
|
||||
proxy_read_timeout 300s;
|
||||
proxy_send_timeout 300s;
|
||||
proxy_pass http://127.0.0.1:3900;
|
||||
}
|
||||
|
||||
add_header X-Content-Type-Options nosniff always;
|
||||
}
|
||||
Reference in New Issue
Block a user