如何通过Django + Nginx + Kubernetes来从Nginx展示静态文件的方法

2021年8月14日,补充如下。

因为我注意到了一些更简单的方法,所以请参考以下文章。
https://qiita.com/legacyworld/items/cee23b2ee1530bcb7f55

想要做什么

image.png

使用HostPath可能解决问题,但最终还是需要将文件复制到主机上,因此不太想这样做。
或者可以手动创建静态文件夹,或者在Kubernetes部署后进行复制,但尽可能希望自动化完成。

我想要使用 git clone 进行源码修改,然后构建容器,并通过 kubectl apply -f manifest.yml 完成。

这里是源代码链接:https://github.com/legacyworld/django_k8s

环境

CentOS7
minikube 版本 1.21.0

我借用了以下作为Django源代码:
https://github.com/kakky/mybook30
https://qiita.com/kaki_k/items/511611cadac1d0c69c54
我对其进行了一些小的修改。

整体的趋势

我不想留下垃圾给主机,所以全部塞进容器里。
换句话说,就是如何将在指定了STATIC_ROOT文件夹内的内容展示给nginx。

    1. 在Django的Dockerfile中使用pip安装Django,并进行migrate和collectstatic操作,然后推送到Docker Hub。

 

    1. 在nginx.conf中编写配置以使得当请求/static路径时返回静态文件,否则使用uwsgi转发。然后构建镜像。

 

    1. 在部署到Kubernetes时,创建一个Persistent Volume,用于在nginx和Django容器之间共享。

 

    1. 将collectstatic生成的静态文件复制到该持久卷中,可以使用postStart命令进行。

 

    这样做之后,nginx将能够看到静态文件。

我觉得还有更好的方法,但暂时就这样吧。
没有考虑到数据库备份和loaddata之类的情况。

整体结构

.
|-- docker-compose.yml
|-- manifest.yml
|-- mybook30
|   |-- api
|   |   |-- admin.py
|   |   |-- apps.py
|   |   |-- __init__.py
|   |   |-- migrations
|   |   |   `-- __init__.py
|   |   |-- models.py
|   |   |-- tests.py
|   |   |-- urls.py
|   |   `-- views.py
|   |-- cms
|   |   |-- admin.py
|   |   |-- apps.py
|   |   |-- forms.py
|   |   |-- __init__.py
|   |   |-- migrations
|   |   |   |-- 0001_initial.py
|   |   |   `-- __init__.py
|   |   |-- models.py
|   |   |-- static
|   |   |   `-- cms
|   |   |       |-- css
|   |   |       |   |-- bootstrap.min.css
|   |   |       |   `-- bootstrap.min.css.map
|   |   |       `-- js
|   |   |           |-- bootstrap.bundle.min.js
|   |   |           |-- bootstrap.bundle.min.js.map
|   |   |           |-- jquery-3.4.1.min.js
|   |   |           `-- jquery-3.4.1.min.map
|   |   |-- templates
|   |   |   `-- cms
|   |   |       |-- base.html
|   |   |       |-- book_edit.html
|   |   |       |-- book_list.html
|   |   |       |-- impression_edit.html
|   |   |       `-- impression_list.html
|   |   |-- tests.py
|   |   |-- urls.py
|   |   `-- views.py
|   |-- Dockerfile
|   |-- manage.py
|   |-- mybook
|   |   |-- asgi.py
|   |   |-- __init__.py
|   |   |-- settings.py
|   |   |-- urls.py
|   |   `-- wsgi.py
|   |-- requirements.txt
|   `-- uwsgi.ini
`-- nginx
    |-- Dockerfile
    `-- nginx_conf
        `-- nginx.tpl

docker-compose (中文别名:容器编排)

只需要为Nginx和Django分别准备两个。

version: '3'
services:

  django:
    image: legacyworld/django_k8s_django
    build:
      context: ./mybook30
      dockerfile: Dockerfile
    container_name: django
    ports:
      - 8000:8000
    stdin_open: true
    tty: true

  nginx:
    image: legacyworld/django_k8s_nginx
    build:
      context: ./nginx
      dockerfile: Dockerfile
    container_name: nginx
    ports:
      - 80:80

Django的配置

我已从原始版本做出以下更改。

静态文件夹设置

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
STATICFILES_DIRS = [
    '/src/cms/static/cms',
    '/usr/local/lib/python3.8/site-packages/django/contrib/admin/static',
]

启动容器后,需要CMS/static/CMS和管理员用的/static文件。可以使用以下命令找到它们的位置。

root@ec7157d343ce:/src# python manage.py findstatic .
Found '.' here:
  /src/cms/static/cms
  /usr/local/lib/python3.8/site-packages/django/contrib/admin/static
  /usr/local/lib/python3.8/site-packages/django/contrib/admin/static
  /src/cms/static

将此内容添加到STATICFILES_DIR中,collectstatic会将其收集起来。
STATIC_ROOT是collectstatic收集文件的位置。在此情况下,它会被放置在mybook30的下面。
需要将此内容展示给nginx。

数据库的设置。

必须使得dq.sqlite3可以由非root用户进行写入,但是如果目录没有被设置为这样,就会出现错误,因此我创建了一个名为db的目录。
稍后会在Dockerfile中进行chmod操作。
详细情况请参考以下链接:
https://qiita.com/juniskw/items/7f948843d571ca2dd0f4

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db/db.sqlite3'),
    }
}

nginx的配置

将”forwarding”这部分提取出来。

将“forwarding”的内容提取出来。

  server{
    listen 80;
    server_name nginx;
    location / {
      proxy_request_buffering off;
      include uwsgi_params;
      uwsgi_pass django:8000;
    }
    location /static {
      proxy_request_buffering off;
      root /usr/share/nginx/html;
    }
  }

由于在STATIC_URL中指定了’/static/’,所以只需要在location /static中指定对静态文件的访问。
因此,只需配置将STATIC_ROOT内容显示在usr/share/nginx/html中。
这将在Kubernetes的Deploy时执行。

Django的Dockerfile

使用Python 3.8-buster,一次性创建静态文件夹并进行数据库迁移。

FROM python:3.8-buster

WORKDIR /src

ADD ./ /src/
RUN pip3 install -r requirements.txt

RUN mkdir db
RUN chmod 777 db
RUN python manage.py collectstatic
RUN python manage.py migrate
RUN chmod 777 ./db/db.sqlite3

RUN useradd -r -s /bin/false uwsgiusr
USER uwsgiusr
ENTRYPOINT ["uwsgi","--ini","/src/uwsgi.ini"]

使用ADD命令将所有源代码复制到requirements.txt中列出的所有内容进行安装。
创建db目录和进行chmod操作,如之前在settings.py中所述。

Kubernetes 资源清单

由于这部分内容太长,只简略介绍一部分。

持续卷索赔

首先创建PersistentVolumeClaim。

kind: PersistentVolumeClaim
apiVersion: v1
metadata:
    name: static-pvc
spec:
    accessModes:
      - ReadWriteOnce
    resources:
        requests:
            storage: 1Gi

Django的部署

将上述的volume挂载到Django容器中,并复制文件。
将此volume挂载到/mnt目录中。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: django-deploy
spec:
  selector:
    matchLabels:
      run: django
  replicas: 1
  template:
    metadata:
      labels:
        run: django
    spec:
      containers:
      - name: django
        image: legacyworld/django_k8s_django:latest
        volumeMounts:
        - name: static
          mountPath: /mnt
        lifecycle:
          postStart:
            exec:
              command: ["cp", "-r", "/src/static", "/mnt"]
      securityContext:
        fsGroup: 999
      volumes:
      - name: static
        persistentVolumeClaim:
          claimName: static-pvc

在生命周期的postStart中指定的命令可以在Dockerfile中指定的ENTRYPOINT之后执行,并附加执行额外的命令。
通过在securityContext中指定fsGroup,可以避免默认情况下以root的644权限挂载无法复制的问题。
通过在容器中使用id uwsgiusr命令,确认999这个用户在容器中启动后是可用的。
一旦确认过,无论创建多少个镜像都不会改变。

nginx的部署

apiVersion: apps/v1
kind: Deployment
metadata:
  name: web-deploy
spec:
  selector:
    matchLabels:
      run: web
  replicas: 1
  template:
    metadata:
      labels:
        run: web
    spec:
      containers:
      - name: nginx
        image: legacyworld/django_k8s_nginx:latest
        env:
        - name: PORT
          value: "80"
        ports:
        - containerPort: 80
        volumeMounts:
        - name: static
          mountPath: /usr/share/nginx/html
      volumes:
      - name: static
        persistentVolumeClaim:
          claimName: static-pvc

在这里只需要进行volumeMounts。一旦复制完成,它将自动显示出来。

确认

有必要使用NodePort来访问,因此要使用kubectl确认端口号。

[nutanix@localhost django_k8s]$ kubectl get services
NAME            TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)          AGE
django          NodePort    10.98.62.97      <none>        8000:32746/TCP   86m
kubernetes      ClusterIP   10.96.0.1        <none>        443/TCP          10d
nginx-service   NodePort    10.111.177.213   <none>        80:30199/TCP     86m

只需要访问 nginx 的 30199。

一般来说的页面

image.png

管理页面

image.png

辛苦了。

广告
将在 10 秒后关闭
bannerAds