I have a Django project running on Kubernetes with Redis, Postgres, Celery, Flower, and Nginx. I deploy it using Minikube and Kubectl on my localhost. Everything looks fine; the pod logs appear good, but when I’m trying to tunnel the Nginx service, it returns a Bad Request (400). The logs, the project files, the dockerfile, and the different YAML files are attached.
Django pod logs:
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, website
Running migrations:
No migrations to apply.
138 static files copied to '/app/staticfiles'.
[2024-05-11 15:21:33 +0000] [9] [INFO] Starting gunicorn 22.0.0
[2024-05-11 15:21:33 +0000] [9] [INFO] Listening at: http://0.0.0.0:8000 (9)
[2024-05-11 15:21:33 +0000] [9] [INFO] Using worker: sync
[2024-05-11 15:21:33 +0000] [10] [INFO] Booting worker with pid: 10
Nginx pod logs:
Nginx pod logs:
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
/docker-entrypoint.sh: Looking for shell scripts in /docker-entrypoint.d/
/docker-entrypoint.sh: Launching /docker-entrypoint.d/10-listen-on-ipv6-by-default.sh
10-listen-on-ipv6-by-default.sh: info: can not modify /etc/nginx/conf.d/default.conf (read-only file system?)
/docker-entrypoint.sh: Sourcing /docker-entrypoint.d/15-local-resolvers.envsh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/20-envsubst-on-templates.sh
/docker-entrypoint.sh: Launching /docker-entrypoint.d/30-tune-worker-processes.sh
/docker-entrypoint.sh: Configuration complete; ready for start up
2024/05/14 19:15:32 [notice] 1#1: using the "epoll" event method
2024/05/14 19:15:32 [notice] 1#1: nginx/1.25.5
2024/05/14 19:15:32 [notice] 1#1: built by gcc 12.2.0 (Debian 12.2.0-14)
2024/05/14 19:15:32 [notice] 1#1: OS: Linux 6.6.16-linuxkit
2024/05/14 19:15:32 [notice] 1#1: getrlimit(RLIMIT_NOFILE): 1048576:1048576
2024/05/14 19:15:32 [notice] 1#1: start worker processes
2024/05/14 19:15:32 [notice] 1#1: start worker process 20
2024/05/14 19:15:32 [notice] 1#1: start worker process 21
2024/05/14 19:15:32 [notice] 1#1: start worker process 22
2024/05/14 19:15:32 [notice] 1#1: start worker process 23
2024/05/14 19:15:32 [notice] 1#1: start worker process 24
2024/05/14 19:15:32 [notice] 1#1: start worker process 25
2024/05/14 19:15:32 [notice] 1#1: start worker process 26
2024/05/14 19:15:32 [notice] 1#1: start worker process 27
10.244.0.1 - - [14/May/2024:19:15:43 +0000] "GET / HTTP/1.1" 400 154 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" "-"
10.244.0.1 - - [14/May/2024:19:15:43 +0000] "GET /favicon.ico HTTP/1.1" 400 154 "http://127.0.0.1:58187/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36" "-"
Project setting.py:
import os
import environ
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Create environment object
env = environ.Env(
# set casting, default value
DEBUG=(bool, False)
)
# Take environment variables from .env file
environ.Env.read_env(os.path.join(BASE_DIR, ".env"))
# False if not in os.environ because of casting above
DEBUG = env("DEBUG")
# Raises Django's ImproperlyConfigured exception if SECRET_KEY not in os.environ
SECRET_KEY = env("SECRET_KEY")
ALLOWED_HOSTS = ["localhost", "127.0.0.1"]
# Application definition
INSTALLED_APPS = [
"website",
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
ROOT_URLCONF = "premier_league_predictions.urls"
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
WSGI_APPLICATION = "premier_league_predictions.wsgi.application"
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.postgresql_psycopg2",
"NAME": env("DB_NAME"),
"USER": env("DB_USER"),
"PASSWORD": env("DB_PASSWORD"),
"HOST": env("DB_HOST"),
"PORT": env("DB_PORT"),
}
}
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_TZ = True
STATIC_ROOT = BASE_DIR / "staticfiles"
STATIC_URL = "/static/"
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
STATICFILES_DIRS = [
BASE_DIR / "static",
]
MEDIA_ROOT = BASE_DIR / "media"
MEDIA_URL = "/media/"
# Retrieve Redis connection details from environment variables
REDIS_URL = f"redis://{env('REDIS_HOST')}:{env('REDIS_PORT')}/"
# Django cache settings
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": REDIS_URL,
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
},
}
}
# Configure Django session engine to use Redis for session storage
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = "default"
# Cache time to live is 15 minutes.
CACHE_TTL = 60 * 15
# Celery Configuration
CELERY_BROKER_URL = REDIS_URL
CELERY_RESULT_BACKEND = REDIS_URL
Project urls.py:
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("website.urls")),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
Project wsgi.py:
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "premier_league_predictions.settings")
application = get_wsgi_application()
Dockerfile:
FROM python:3.11.8-slim
ENV PYTHONUNBUFFERED 1
ENV PYTHONDONTWRITEBYTECODE 1
WORKDIR /app
COPY . /app
RUN pip install -r requirements.txt
EXPOSE 8000
Django deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: django
spec:
replicas: 1
selector:
matchLabels:
app: django
template:
metadata:
labels:
app: django
spec:
containers:
- name: django
image: ***/***:latest
command: ["/bin/sh", "-c"]
args:
- >
python manage.py migrate &&
python manage.py collectstatic --no-input &&
gunicorn premier_league_predictions.wsgi:application --bind 0.0.0.0:8000
ports:
- containerPort: 8000
env:
- name: DEBUG
valueFrom:
configMapKeyRef:
name: django-cm
key: debug
- name: SECRET_KEY
valueFrom:
secretKeyRef:
name: django-credentials
key: key
- name: DB_NAME
valueFrom:
configMapKeyRef:
name: postgres-cm
key: name
- name: DB_USER
valueFrom:
secretKeyRef:
name: postgres-credentials
key: user
- name: DB_PASSWORD
valueFrom:
secretKeyRef:
name: postgres-credentials
key: password
- name: DB_HOST
valueFrom:
configMapKeyRef:
name: django-cm
key: db_host
- name: DB_PORT
valueFrom:
configMapKeyRef:
name: django-cm
key: db_port
- name: REDIS_HOST
valueFrom:
configMapKeyRef:
name: django-cm
key: redis_host
- name: REDIS_PORT
valueFrom:
configMapKeyRef:
name: django-cm
key: redis_port
volumeMounts:
- name: django-staticfiles
mountPath: /data/staticfiles
- name: django-media
mountPath: /data/media
imagePullSecrets:
- name: dockerhub-credentials
volumes:
- name: django-staticfiles
persistentVolumeClaim:
claimName: django-staticfiles-pvc
- name: django-media
persistentVolumeClaim:
claimName: django-media-pvc
Django pv.yaml:
apiVersion: v1
kind: PersistentVolume
metadata:
name: django-staticfiles-pv
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
hostPath:
path: /data/django-staticfiles-pv
---
apiVersion: v1
kind: PersistentVolume
metadata:
name: django-media-pv
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
capacity:
storage: 1Gi
hostPath:
path: /data/django-media-pv
Django pvc.yaml:
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: django-staticfiles-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
volumeName: django-staticfiles-pv
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: django-media-pvc
spec:
storageClassName: manual
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 1Gi
volumeName: django-media-pv
Django service.yaml:
kind: Service
apiVersion: v1
metadata:
name: django-service
spec:
selector:
app: django
ports:
- port: 8000
targetPort: 8000
Nginx deployment.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nginx
spec:
replicas: 1
selector:
matchLabels:
app: nginx
template:
metadata:
labels:
app: nginx
spec:
containers:
- name: nginx
image: nginx:latest
ports:
- containerPort: 80
volumeMounts:
- name: nginx-config
mountPath: /etc/nginx/conf.d
- name: django-staticfiles
mountPath: "/data/staticfiles"
- name: django-media
mountPath: "/data/media"
volumes:
- name: nginx-config
configMap:
name: nginx-cm
- name: django-staticfiles
persistentVolumeClaim:
claimName: django-staticfiles-pvc
- name: django-media
persistentVolumeClaim:
claimName: django-media-pvc
Nginx config-map.yaml:
apiVersion: v1
kind: ConfigMap
metadata:
name: nginx-cm
data:
default.conf: |
server {
listen 80;
server_name _;
location / {
proxy_pass http://django-service:8000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Host $server_name;
proxy_set_header X-Real-IP $remote_addr;
}
}
Nginx service.yaml:
apiVersion: v1
kind: Service
metadata:
name: nginx-service
spec:
type: NodePort
ports:
- port: 80
targetPort: 80
selector:
app: nginx
II would be very grateful for help in accessing the application using the URL. I’ve tried a lot, but I feel that I lack knowledge
Thanks!