Search code examples
djangodockerdocker-composeazure-akskompose

Docker-Kubernets can't find manage.py


I built an API in Django and I used docker-compose to orchestrate redis and celery services. Now, I would like to move the API to a Kubernetes cluster (AKS). However, I get the error python can't find manage.py when I run it into the cluster. I used Kompose tool to write the kubernets manifest.yaml. This is my Dockerfile, docker-compose.yml and kubernets.yaml files

# docker-compose.yml
version: '3'

services:
  app:
    build:
      context: .
    ports:
      - "8000:8000"        
    volumes:
      - ./app:/app
    command: >
      sh -c "python3 ./manage.py makemigrations &&
             python3 ./manage.py migrate &&
             python3 ./manage.py runserver 0.0.0.0:8000"

The Dockerfile

# Dockerfile
FROM python:3.8 

ENV PYTHONUNBUFFERED 1
COPY ./requirements.txt /requirements.txt
RUN pip install -r /requirements.txt

RUN mkdir /app
COPY ./app /app
WORKDIR /app

And the kubernets manifest

apiVersion: v1
items:
  - apiVersion: v1
    kind: Service
    metadata:
      annotations:
        kompose.cmd: kompose convert -f docker-compose.yml -o kb_manifests.yaml
        kompose.version: 1.22.0 (955b78124)
      creationTimestamp: null
      labels:
        io.kompose.service: app
      name: app
    spec:
      ports:
        - name: "8000"
          port: 8000
          targetPort: 8000
      selector:
        io.kompose.service: app
    status:
      loadBalancer: {}


  - apiVersion: apps/v1
    kind: Deployment
    metadata:
      annotations:
        kompose.cmd: kompose convert -f docker-compose.yml -o kb_manifests.yaml
        kompose.version: 1.22.0 (955b78124)
      creationTimestamp: null
      labels:
        io.kompose.service: app
      name: app
    spec:
      replicas: 1
      selector:
        matchLabels:
          io.kompose.service: app
      strategy:
        type: Recreate
      template:
        metadata:
          annotations:
            kompose.cmd: kompose convert -f docker-compose.yml -o kb_manifests.yaml
            kompose.version: 1.22.0 (955b78124)
          creationTimestamp: null
          labels:
            io.kompose.service: app
        spec:
          containers:
            - args:
                - sh
                - -c
                - |-
                  python manage.py makemigrations &&
                  python manage.py migrate &&
                  python manage.py runserver 0.0.0.0:8000
              image: <image pushed in a Azure Container Registry (ACR)>
              name: app
              ports:
                - containerPort: 8000
              resources: {}
              volumeMounts:
                - mountPath: /app
                  name: app-claim0
          restartPolicy: Always
          volumes:
            - name: app-claim0
              persistentVolumeClaim:
                claimName: app-claim0
    status: {}
  - apiVersion: v1
    kind: PersistentVolumeClaim
    metadata:
      creationTimestamp: null
      labels:
        io.kompose.service: app-claim0
      name: app-claim0
    spec:
      accessModes:
        - ReadWriteOnce
      resources:
        requests:
          storage: 100Mi
    status: {}

Log error

$ kubectl logs app-6fc488bf56-hb8g9 --previous
# python: can't open file 'manage.py': [Errno 2] No such file or director

Solution

  • I think your problem is with the volumes segment of your kubernetes config.

    In your docker-compose config, you have a volume mounting

    volumes:
     - ./app:/app
    

    This is great for local development because it'll take the copy of the code on your machine and overlay it on the docker image so the changes you make on your machine will be reflected in your running docker container, allowing runserver to see file changes and reload the django server as needed.

    This is less great in kubernetes - when you're running in prod, you want to be using the code that's been baked in to the image. As this is currently configured, I think you're creating an empty PersistentVolumeClaim and then mounting it on top of your /app directory in the running container. Since it's empty, there is no manage.py file to be found.

    Try making your kubernetes configuration look like this:

    apiVersion: v1
    items:
      - apiVersion: v1
        kind: Service
        metadata:
          annotations:
            kompose.cmd: kompose convert -f docker-compose.yml -o kb_manifests.yaml
            kompose.version: 1.22.0 (955b78124)
          creationTimestamp: null
          labels:
            io.kompose.service: app
          name: app
        spec:
          ports:
            - name: "8000"
              port: 8000
              targetPort: 8000
          selector:
            io.kompose.service: app
        status:
          loadBalancer: {}
    
    
      - apiVersion: apps/v1
        kind: Deployment
        metadata:
          annotations:
            kompose.cmd: kompose convert -f docker-compose.yml -o kb_manifests.yaml
            kompose.version: 1.22.0 (955b78124)
          creationTimestamp: null
          labels:
            io.kompose.service: app
          name: app
        spec:
          replicas: 1
          selector:
            matchLabels:
              io.kompose.service: app
          strategy:
            type: Recreate
          template:
            metadata:
              annotations:
                kompose.cmd: kompose convert -f docker-compose.yml -o kb_manifests.yaml
                kompose.version: 1.22.0 (955b78124)
              creationTimestamp: null
              labels:
                io.kompose.service: app
            spec:
              containers:
                - args:
                    - sh
                    - -c
                    - |-
                      python manage.py makemigrations &&
                      python manage.py migrate &&
                      python manage.py runserver 0.0.0.0:8000
                  image: <image pushed in a Azure Container Registry (ACR)>
                  name: app
                  ports:
                    - containerPort: 8000
                  resources: {}
              restartPolicy: Always
        status: {}
    

    That should be the same as what you have minus any references to volumes.

    If that works then you're off to the races. Once it's up and running, take a look at the Django deployment guide about some other settings you should configure - namely, take a look at gunicorn; runserver isn't your best path forward for production deploys.