如何通过Django + Nginx + Kubernetes来从Nginx展示静态文件的方法
2021年8月14日,补充如下。
因为我注意到了一些更简单的方法,所以请参考以下文章。
https://qiita.com/legacyworld/items/cee23b2ee1530bcb7f55
想要做什么
使用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。
-
- 在Django的Dockerfile中使用pip安装Django,并进行migrate和collectstatic操作,然后推送到Docker Hub。
-
- 在nginx.conf中编写配置以使得当请求/static路径时返回静态文件,否则使用uwsgi转发。然后构建镜像。
-
- 在部署到Kubernetes时,创建一个Persistent Volume,用于在nginx和Django容器之间共享。
-
- 将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。
一般来说的页面
管理页面
辛苦了。