Goal
Access minio server via subdomain from Django server in docker environment (both minio server and Django server are behind nginx proxy)
Expected result: Django can access minio server using the subdomain specified in nginx.
I can successfully open the minio console via s3-console.localhost
Problem
Django service can't connect properly. Docker logs:
api | Traceback (most recent call last):
api | File "manage.py", line 22, in <module>
api | main()
api | File "manage.py", line 18, in main
api | execute_from_command_line(sys.argv)
api | File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 446, in execute_from_command_line
api | utility.execute()
api | File "/usr/local/lib/python3.8/site-packages/django/core/management/__init__.py", line 386, in execute
api | settings.INSTALLED_APPS
api | File "/usr/local/lib/python3.8/site-packages/django/conf/__init__.py", line 87, in __getattr__
api | self._setup(name)
api | File "/usr/local/lib/python3.8/site-packages/django/conf/__init__.py", line 74, in _setup
api | self._wrapped = Settings(settings_module)
api | File "/usr/local/lib/python3.8/site-packages/django/conf/__init__.py", line 183, in __init__
api | mod = importlib.import_module(self.SETTINGS_MODULE)
api | File "/usr/local/lib/python3.8/importlib/__init__.py", line 127, in import_module
api | return _bootstrap._gcd_import(name[level:], package, level)
api | File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
api | File "<frozen importlib._bootstrap>", line 991, in _find_and_load
api | File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
api | File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
api | File "<frozen importlib._bootstrap_external>", line 843, in exec_module
api | File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
api | File "/code/ezevent_api/settings.py", line 235, in <module>
api | found = client.bucket_exists(AWS_STORAGE_BUCKET_NAME)
api | File "/usr/local/lib/python3.8/site-packages/minio/api.py", line 658, in bucket_exists
api | self._execute("HEAD", bucket_name)
api | File "/usr/local/lib/python3.8/site-packages/minio/api.py", line 400, in _execute
api | region = self._get_region(bucket_name, None)
api | File "/usr/local/lib/python3.8/site-packages/minio/api.py", line 467, in _get_region
api | response = self._url_open(
api | File "/usr/local/lib/python3.8/site-packages/minio/api.py", line 311, in _url_open
api | raise InvalidResponseError(
api | minio.error.InvalidResponseError: non-XML response from server; Response code: 500, Content-Type: text/html; charset=UTF-8, Body: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
api | <html><head>
api | <title>Revive Adserver</title>
api | <meta name="generator" content="Revive Adserver v5.2.1 - http://www.revive-adserver.com">
api | <meta name="robots" content="noindex, nofollow">
api | <meta http-equiv="X-UA-Compatible" content="IE=7">
api | <link rel="shortcut icon" type="image/vnd.microsoft.icon" href="https://a03.uadexchange.com/admin/assets/images/favicon.ico" />
api |
api | <link rel="stylesheet" type="text/css" href="https://a03.uadexchange.com/admin/assets/min.php?g=oxp-css-ltr&v=5.2.1">
api |
api |
api | <script type="text/javascript">
api | <!--
api | var validatorPreferences = {
api | 'strFieldContainsErrors': "The following fields contain errors:",
api | 'strFieldFixBeforeContinue1': "Before you can continue you need",
api | 'strFieldFixBeforeContinue2': "to correct these errors.",
api | 'strWarningMissing': "Warning, possibly missing ",
api | 'strWarningMissingOpening': " opening tag \'<\'",
api | 'strWarningMissingClosing': " closing tag \'>\'",
api | 'strSubmitAnyway': "Submit Anyway",
api | 'thousandsSeperator': ","
api | };
api |
api | var tablePreferences = {
api | 'warningBeforeDelete': false
api | };
api | //-->
api | </script>
api | <script type="text/javascript" src="https://a03.uadexchange.com/admin/assets/min.php?g=oxp-js&v=5.2.1"></script>
api |
api |
api |
api |
api |
api |
api |
api |
api | </head>
api | <body class="hasInterface hasGradient hasSidebar " onload="initPage();">
api |
api |
api |
api |
api | <div id="oaHeader">
api |
api | <div id="oaHeaderBranding" class="brandingAdServer">Revive Adserver</div>
api | <div id="oaNavigationExtraTop">
api | <ul>
api |
api |
api |
api |
api |
api | </ul>
api | </div>
api |
api |
api | </div>
api |
api | <div id="oaNavigation">
api | <ul id="oaNavigationTabs">
api | <li class="active first last">
api | <div class="left"><div class="right">
api | <a href="https://a03.uadexchange.com/admin/index.php" accesskey="Home">Authentication</a>
api | </div></div>
api | </li>
api | </ul>
api |
api | </div>
api |
api | <div id="firstLevelContent">
api |
api |
api | <div id="secondLevelContent">
Context
nginx.config
user nginx;
events {
worker_connections 1000;
}
http {
server {
server_name api.lamanin.com api.localhost;
location / {
proxy_pass http://api:8000;
}
}
server {
server_name s3.lamanin.com s3.localhost;
listen 80;
location / {
proxy_pass http://s3:9000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
server {
server_name s3-console.lamanin.com s3-console.localhost;
listen 80;
location / {
proxy_pass http://s3:9090;
}
}
server {
server_name lamanin.com *.lamanin.com localhost *.localhost;
listen 80;
location / {
proxy_pass http://web:3000;
}
location /ws {
proxy_pass http://web:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "Upgrade";
}
}
}
docker-compose.yml
version: "3.8"
services:
s3:
image: minio/minio
container_name: s3
restart: on-failure
ports:
- "9000:9000"
- "9090:9090"
volumes:
- s3:/data
environment:
- MINIO_ROOT_USER=${MINIO_ROOT_USER}
- MINIO_ROOT_PASSWORD=${MINIO_ROOT_PASSWORD}
command: server --console-address :9090 --address :9000 /data
web:
image: lamanin/web
container_name: web
environment:
- WDS_SOCKET_PORT=${WDS_SOCKET_PORT}
- ESLINT_NO_DEV_ERRORS=${ESLINT_NO_DEV_ERRORS}
- REACT_APP_API_URL=${REACT_APP_API_URL}
- REACT_APP_SHOW_COMING_SOON=${REACT_APP_SHOW_COMING_SOON}
build:
context: ./web
dockerfile: Dockerfile.${ENVIRONMENT}
restart: on-failure
volumes:
- web:/app/src
ports:
- "${WEB_PORT}"
depends_on:
- api
db:
image: postgres
container_name: "db"
hostname: "db"
volumes:
- db:/var/lib/postgresql/data
environment:
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
api:
image: lamanin/api
environment:
- SECRET_KEY=${SECRET_KEY}
- POSTGRES_DB=${POSTGRES_DB}
- POSTGRES_USER=${POSTGRES_USER}
- POSTGRES_PASSWORD=${POSTGRES_PASSWORD}
- DJANGO_SUPERUSER_USERNAME=${DJANGO_SUPERUSER_USERNAME}
- DJANGO_SUPERUSER_PASSWORD=${DJANGO_SUPERUSER_PASSWORD}
- MINIO_URL=${MINIO_URL}
- MINIO_DOMAIN=${MINIO_DOMAIN}
- MINIO_SA_ACCESS_KEY=${MINIO_SA_ACCESS_KEY}
- MINIO_SA_SECRET_KEY=${MINIO_SA_SECRET_KEY}
- API_URL=${API_URL}
container_name: api
build:
context: ./api
restart: on-failure
command: >
sh -c "sleep 10 && python3 manage.py collectstatic --no-input &&
python3 manage.py migrate &&
python3 manage.py ensure_admin &&
python3 manage.py runserver 0.0.0.0:8000"
volumes:
- api:/code
ports:
- "${API_PORT}"
depends_on:
- db
- s3
nginx:
build:
context: .
dockerfile: Dockerfile.nginx
container_name: "nginx"
restart: on-failure
depends_on:
- api
- web
- s3
ports:
- "80:80"
volumes:
api:
web:
db:
s3:
Relevant Django settings
AWS_ACCESS_KEY_ID = os.getenv('MINIO_SA_ACCESS_KEY')
AWS_SECRET_ACCESS_KEY = os.getenv('MINIO_SA_SECRET_KEY')
AWS_STORAGE_BUCKET_NAME = 'main'
AWS_S3_ENDPOINT_URL = os.getenv('MINIO_URL')
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
AWS_STATIC_LOCATION = 'static'
STATICFILES_STORAGE = 'ezevent_api.storage_backends.StaticStorage'
STATIC_URL = f'{AWS_S3_ENDPOINT_URL}/{AWS_STORAGE_BUCKET_NAME}/'
AWS_PUBLIC_MEDIA_LOCATION = 'media/public'
DEFAULT_FILE_STORAGE = 'ezevent_api.storage_backends.PublicMediaStorage'
AWS_PRIVATE_MEDIA_LOCATION = 'media/private'
PRIVATE_FILE_STORAGE = 'ezevent_api.storage_backends.PrivateMediaStorage'
client = Minio(
os.getenv('MINIO_DOMAIN'),
access_key=AWS_ACCESS_KEY_ID,
secret_key=AWS_SECRET_ACCESS_KEY,
secure=os.getenv('MINIO_SECURE') == 'True'
)
found = client.bucket_exists(AWS_STORAGE_BUCKET_NAME)
if not found: client.make_bucket(AWS_STORAGE_BUCKET_NAME)
Relevant Environment Variables
MINIO_URL=http://s3.localhost
MINIO_DOMAIN=s3.localhost
MINIO_ROOT_USER=admin
MINIO_ROOT_PASSWORD=[REDACTED]
MINIO_SA_ACCESS_KEY=service-account-1
MINIO_SA_SECRET_KEY=[REDACTED]
MINIO_SECURE=False
Relevant Packages
https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html
Things I Have Tried
Initially I didn't use any headers in the proxy settings, so I added several headers that I found from this github thread: https://github.com/minio/minio/issues/7661.
I suspected that the
http://s3:9000
address isn't recognized by nginx so I usedhttp://localhost:9000
but the localhost of nginx is probably different from thes3
service because they are in different containers.When running the
s3
& nginx service using docker, and running the Django server outside of docker (running it manually using thepython manage.py runserver
command) it successfully connect's to the minio server iflocalhost:9000
is used. But failes if the subdomainhttp://s3.localhost
is used.