{"id":15550068,"url":"https://github.com/hscspring/web-full-stack-practice","last_synced_at":"2025-09-08T03:31:22.358Z","repository":{"id":49687970,"uuid":"173717603","full_name":"hscspring/Web-Full-Stack-Practice","owner":"hscspring","description":"Web Full Stack Practice for Beginners：Docker + uWSGI + Celery + Django + Supervisor + React + Nginx + HTTPS + Postgres + Redis","archived":false,"fork":false,"pushed_at":"2022-09-23T22:25:38.000Z","size":47028,"stargazers_count":37,"open_issues_count":3,"forks_count":9,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-18T00:48:29.166Z","etag":null,"topics":["celery","django","django-rest-framework","docker","docker-compose","https","nginx","postgres","react","redis","supervisor","uwsgi"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hscspring.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-03-04T09:48:32.000Z","updated_at":"2024-02-12T19:53:11.000Z","dependencies_parsed_at":"2023-01-18T21:04:05.920Z","dependency_job_id":null,"html_url":"https://github.com/hscspring/Web-Full-Stack-Practice","commit_stats":null,"previous_names":[],"tags_count":0,"template":true,"template_full_name":null,"purl":"pkg:github/hscspring/Web-Full-Stack-Practice","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hscspring%2FWeb-Full-Stack-Practice","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hscspring%2FWeb-Full-Stack-Practice/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hscspring%2FWeb-Full-Stack-Practice/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hscspring%2FWeb-Full-Stack-Practice/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hscspring","download_url":"https://codeload.github.com/hscspring/Web-Full-Stack-Practice/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hscspring%2FWeb-Full-Stack-Practice/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274128188,"owners_count":25226962,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-08T02:00:09.813Z","response_time":121,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["celery","django","django-rest-framework","docker","docker-compose","https","nginx","postgres","react","redis","supervisor","uwsgi"],"created_at":"2024-10-02T13:49:35.269Z","updated_at":"2025-09-08T03:31:18.359Z","avatar_url":"https://github.com/hscspring.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Web Full Stack Practice：Docker + uWSGI + Celery + Django + Supervisor + React + Nginx + Https + Postgres + Redis\n\n本项目主要介绍基于 Docker 的 Web 开发和部署（开发要求在改动代码时服务或页面能够实时发生变化）全流程，来源于日常项目，后端以 Django 为例，前端以 React 为例，使用到的其他模块也可以换成同类产品，比如 uWSGI 可以换成 Gunicorn，数据库可以换成 Mysql 等。我们将通过一个案例前后端分离介绍，这样容易理解。\n\n## 目标\n\n- docker-compose 启动前后端同时开发\n- 本地开发 + 正式部署 Https\n- Supervisor + uWSGI + Nginx 部署\n\n\u003e 特别说明：在前后端联合调试时比较方便，如果单个开发后端或前端，直接本地很多时候会更方便。\n\u003e\n\u003e 关于本地 Https，很多框架本来就是支持的，就不要像本文这么麻烦了。\n\n## 环境\n\n- MacOS Mojave 10.14\n- Docker Desktop Community Version 2.0.0.2\n    - Engine：18.09.1\n    - Compose：1.23.2\n\n## 产品\n\n作为一名 NLP 算法工程师，我们决定做一个简单的 Language Model 的 Demo，前端用户输入一个词，返回一段自动生成的文本。\n\n模型参考：[递归神经网络  |  TensorFlow](https://www.tensorflow.org/tutorials/sequences/recurrent#language_modeling)，使用张爱玲作品集的句子作为训练集，800 个句子，52750 字，跑了 150 个 epoch。\n\n## 后端\n\n后端部分主要包括：uWSGI、Celery、Django、Supervisor、Postgres 和 Redis，首先分别简单介绍一下这些模块的功能：\n\n- uWSGI：[The uWSGI project — uWSGI 2.0 documentation](https://uwsgi-docs.readthedocs.io/en/latest/)，Web 服务器，针对后端服务。\n- Celery：[Celery - Distributed Task Queue — Celery 4.2.0 documentation](http://docs.celeryproject.org/en/latest/)，分布式任务队列服务，针对后端耗时较长的执行任务，也可以让 Django 异步。\n- Django：[The Web framework for perfectionists with deadlines | Django](https://www.djangoproject.com/)，Python 的 Web 开发框架。\n- Supervisor：[Supervisor: A Process Control System — Supervisor 3.3.5 documentation](http://supervisord.org/)，进程管理工具，主要用于在一个容器启动多个服务。\n- Postgres：数据库\n- Redis：缓存数据库（本例中用于 Celery 消息管理）\n\n### Step1: Postgres\n\n首先把 db 设置好，如果这里需要用到 redis 也需要一并设置。\n\n我们可以使用 `docker-compose up db`（在 `docker-compose.yml` 所在目录执行）只启动 db，然后在本地登陆 db 去创建用户，当然本地也可以直接使用 postgres 作为用户。**需要注意的是：host 地址是本机的 IP 地址**，Mac 可以使用 `ifconfig` 查看。\n\n`psql -h 192.168.0.103 -U postgres`，密码就是启动时创建的超级用户的密码，登陆后最好是更改一下 postgres 的密码，因为环境变量的那个只是启动时用一下。\n\n```sql\n# create user\ncreate user demo with password \"demopassword\";\n# create db\ncreate database demo;\n# grant privileges\ngrant all privileges ON database demo to demo;\n```\n\n当然，你也可以直接使用 `docker run -it --rm --name mypsql -e POSTGRES_PASSWORD=password4superuser -p 5432:5432  -v ~/docker_volume/pg9.5:/var/lib/postgresql/data postgres:9.5` 启动 db，启动后可以更改 postgres 用户的密码。\n\n这一步的主要目的就是创建用户和 db ，然后把 data 都映射出来，这样我们后端启动时，就可以通过配置文件连接到 db 了。\n\n### Step2: Django\n\n这块主要针对 Django 的流程和注意事项，熟悉或者不需要的可以跳过。\n\n```bash\n# 初始化项目\nmkdir demo \u0026\u0026 cd \"$_\"\ndjango-admin startproject demo_backend\ncd demo_backend\n# 创建一个 app\npython manage.py startapp text_generator\n```\n\n到这一步基本的框架就有了，之后就是项目配置和具体代码的编写。一些 Keypoints：\n\n- Python 环境及开发包管理：[pypa/pipenv: Python Development Workflow for Humans.](https://github.com/pypa/pipenv)\n    - 创建虚拟环境：`pipenv --python 3.6.5 # 我的本地环境是 python 3.6.5` \n    - `pipenv shell` 可以进入虚拟环境，或使用 `pipenv run python xxx.py` 等于直接在虚拟环境中运行 `python xxx.py`，`pipenv install xx` 可以安装需要的依赖，或安装指定版本，比如本例：`pipenv install tensorflow==1.12.0`\n    - **使用之前，将 Pipfile 中的 url 改成 https://pypi.douban.com/simple 或其他速度快的源**，Pipfile 能看到所有已安装的包\n\n- 关于 settings：\n    - 将 setting 分为 prod 和 dev 两个文件，分别设置开发和正式环境的参数，需要修改 `wsgi.py, manage.py` 以便 Django 能够找到配置文件\n    - 一般每个 APP 下面都可能有配置文件，编写代码时最好改成可以统一在项目的 settings 这里覆盖。比如训练好的模型文件（详见配置文件 `settings/base.py`）可以映射出来，这样不但可以方便我们随时更新模型，而且也能减小 image 的大小。\n- 关于 database：\n    - 使用配置文件，这样本地开发完部署时只要在服务器用正式的配置文件即可\n    - 如果使用 docker-compose，数据库的 host 是：192.168.65.2，而不是 127.0.0.1 或本机地址\n\n涉及到代码相关或相应文件的，项目中均有注释，可直接查看相应文件。\n\n### Step3: Celery+Redis\n\n首先代码需要做相应的修改，可以直接查看代码文件。\n\n本地开发环境配置方面需要注意的是：\n\n- 在 `wsgi.py` 同目录（settings文件夹同目录）新建 `celery.py` 并配置，然后修改 `__init__.py`，再修改 `settings/base.py` 中的配置\n- 启动 redis，运行：`celery -A demo_backend worker -l info` 即可\n\n正式环境需要将其作为 daemon 启动，需要配置 conf：\n\n- 在 `demo_backend/celery` 目录下，conf 最重要的两个配置是：`CELERY_APP=\"demo_backend\"` 和 `CELERYD_CHDIR=\"/demo-backend\"`，后者一般是 Dockerfile 后端 server 的根目录，也就是整个后端项目的根目录（settings 文件夹和 `celery.py` 的上级目录）；sh 主要就是把`DEFAULT_USER` 改成和 conf 一致。\n\n    需要说明的是：这两个文件可以放在任何地方，因为它们最终都是要放到 `/etc/` 下面的。\n\n- user 和 group 一般就选择 root，当然也可以自己创建 user 和 group，官方建议非 root，不过因为我们是在 docker 里面，所以 root 也没有太多问题。\n\n    这里的原因是，root 用户出错时可能会对系统造成一些意想不到的错误，如果系统有其他服务可能会出问题，但我们的 docker 服务是隔离和相对独立的，所以个人觉得使用 root 问题不大；同样的问题在 uWSGI 那里也有。\n\n\u003e 这里需要说明的是：本例直接等后端返回结果再传给前端，没有使用异步，因为速度不是特别慢。如果需要异步，可以在 `task.get()` 之前马上将 task id 返给前端，然后由前端根据 task id 获取最终的结果，这样就变成了异步操作。\n\n### Step4: uWSGI\n\n按照配置文件配置，需要注意的是，这里**不用配置 daemon**。可以设置 server 为：`socket=app.sock` 或直接使用 http（docker 中不能使用 `app.sock`，因为文件不在一个容器内）。\n\n需要注意的是，这个是在正式环境下使用的，如果在本地开发环境，直接用 `python manage.py runserver 0.0.0.0:8000` 启动服务即可。\n\n相关参数详细说明可以参考：\n\n- [How to use Django with uWSGI | Django documentation | Django](https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/uwsgi/) \n-  [Things to know (best practices and “issues”) READ IT !!! — uWSGI 2.0 documentation](https://uwsgi-docs.readthedocs.io/en/latest/ThingsToKnow.html)\n- [How to create a Django server running uWSGI, NGINX and PostgreSQL on AWS EC2 with Python 3.6](https://medium.freecodecamp.org/django-uwsgi-nginx-postgresql-setup-on-aws-ec2-ubuntu16-04-with-python-3-6-6c58698ae9d3)\n\n### Step5: Supervisor\n\n主要目的是把多个服务放在一个容器内启动，官方文档 [Run multiple services in a container | Docker Documentation](https://docs.docker.com/config/containers/multi-service_container/) 也有相应的介绍。关于 stopsignal 参数的说明，可以参考：[How to use supervisor fo start/stop uWSGI application? - Stack Overflow](https://stackoverflow.com/questions/19510195/how-to-use-supervisor-fo-start-stop-uwsgi-application)\n\n### Step6: Dockerfile\n\n接下来就是编写 Dockerfile 了，我们可以用一个 Dockerfile 同时满足本地开发和正式部署，主要通过 supervisor 不同的配置文件来实现，本地开发时还需要把整个目录映射出去，这样当文件内容发生变化时，服务会自动刷新。\n\n运行 `docker-compose build app` 单独 build app，build 完成后可以通过 `docker-compose up db redis app` 来启动 db、redis 和后端服务，`docker-compose stop` 停止服务。需要注意的是：\n\n- `backend.local.env` 中的 db host 和 redis host 都需要改为 192.168.65.2，这是 docker 服务的默认地址，否则无法连接到 db 和 redis。db host 也可以直接使用 `docker-compose.yml` 中 db 的 name（如本例中是：db）。\n- 本地开发时，需要把整个项目目录映射出去。但在测试正式环境时不需要（注释掉 `docker-compose.yml line 29`），因为映射后容器里面目录的内容会被清空，以映射出来的目录为准了，而我们本地并没有设置 Celery 的 deamon；而且 uWSGI 也可能会报错，因为我们没有设置虚拟环境的目录。\n- 本地开发时，command 要写成本地的 Supervisor 配置文件以替换 Docker 里面的正式配置文件。但在测试正式环境时要记得注释掉（`docker-compose.yml line 40`）。\n- 容器启动后，需要通过 `docker exec -it app bash`（app 可以替换为 container id）进入后端容器内部执行系列命令，包括：\n    - `python manage.py makemigrations` 生成 db 相关数据\n    - `python manage.py migrate` 将生成的数据 migrate 到 db\n    - `python manage.py createsuperuser` 可以创建管理后台的管理员\n    - `python manage.py collectstatic ` 自动输出静态文件到项目根目录\n- 本地开发时，log 会直接输出到屏幕。但在测试正式环境时，日志会映射出来到映射的目录（如本例的 `~/docker_volume/log/`），可以直接通过目录文件查看。\n\n到这一步，后端部分就已经完成了，我们可以通过 http://127.0.0.1:8000/admin/ 登陆管理员，也可以通过 http://127.0.0.1:8000/api/ 查看 Rest Framework，或者通过调用 http://127.0.0.1:8000/api/generate/ 生成。后端代码修改后，服务会自动刷新。\n\n## 前端\n\n刚刚后端的访问是直接通过 ip 地址 + 端口执行的，正式环境中需要用 Nginx 做转发；开发环境下，我们只需用 localhost 直接访问即可。这里稍微有点麻烦的是本地 Https 的配置。\n\n### React\n\n前端我们使用 Facebook 的 [Create React App · Set up a modern web app by running one command.](https://facebook.github.io/create-react-app/)\n\n```bash\nnpx create-react-app demo_frontend\ncd demo_frontend\nnpm start\n```\n\n上面的代码即可创建一个 App 并启动前端页面，安装依赖直接用 `npm i xxx ` 即可，如果只是在开发环境下使用，可以 `npm i xxx --save-dev`，然后完成代码编写。\n\n### Https\n\n要想在本地开发时使用 https 有两个重要的步骤：\n\n- 生成证书相关文件\n- 配置 React\n\n生成证书相关文件需要借助：[dakshshah96/local-cert-generator: 🚀 A set of scripts to quickly generate a HTTPS certificate for your local development environment.](https://github.com/dakshshah96/local-cert-generator)\n\n- 前三步是让你的本机成为一个 “证书颁发机构”，将第二步生成的 rootCA.pem 双击添加后，都改为 “信任” 即可。如图所示：\n\n    ![](http://qnimg.lovevivian.cn/blog-sslstep-1.jpeg)\n\n- 第二步有几个需要注意的地方：\n\n    - `Enter pass phrase for rootCA.key:` 输入一个自己定义的密码（要输三次），要记住，以后每次为域名生成证书时都需要输入\n    - 后面的除了 `Common Name` 都可以回车跳过，这个是证书颁发机构的名称，输入 `Local Cert` 或 `My PC Cert` 之类的都可以\n\n- 然后运行第四步生成 localhost 的证书，或者使用仓库中的 `g_ssl_for_domain.sh`：`./g_ssl_for_domain.sh localhost /path/to/store/ssl/file/`，两个参数分别是你网站的域名和生成 ssl 文件的存储目录。我们需要 `server.crt` 和 `server.key` 就可以了，为了方便之后的操作，我把这两个文件放到了 `demo_frontend/ssl.localhost` 目录下。\n\n然后要配置 React，这里我们需要安装 [timarney/react-app-rewired: Override create-react-app webpack configs without ejecting](https://github.com/timarney/react-app-rewired)：`npm i react-app-rewired --save-dev`。\n\n- 首先在项目根目录下创建一个 `config-overrides.js` 的文件，配置将在里面进行，详见配置文件。这里我们用了一下环境变量，指定 `server.crt, server.key` 的位置\n- 然后根据官方说明，修改 `package.json`，将相应的 `react-scripts` 改为 `react-app-rewired`\n\n最后就是 Dockerfile 的编写了，由于本地开发，所以这里会比较简单，只要有 node 环境即可。\n\n然后我们就可以使用 `docker-compose up redis db app web` 来启动所有服务了，然后通过 `https://localhost:3000` 访问前端，我们可以看下证书，没错，是我们的 `My PC Cert` 颁发的。\n\n![](http://qnimg.lovevivian.cn/blog-sslstep-3.jpeg)\n\n尝试生成一句：\n\n![](http://qnimg.lovevivian.cn/blog-sslstep-2.jpeg)\n\n我们可以修改前端代码，由于目录映射页面会重新编译、自动刷新。最后记得 `npm run build` 生成静态文件。\n\n### Nginx\n\n[NGINX | High Performance Load Balancer, Web Server, \u0026 Reverse Proxy](https://www.nginx.com/) 一般会用在产品部署上，作为代理和静态资源服务器，接下来我们主要介绍如何在本地调试 Nginx。主要有以下几步：\n\n- 生成域名的证书，我们随便用一个名字，比如 `naivegenerator.com`\n- 编写 Nginx 配置文件\n- 编写 Dockerfile 并 build image\n- 修改 Host\n\n生成域名证书时，需要将 `v3.ext` 中的 `DNS.1 = naivegenerator.com` 修改掉，然后执行 `sh createSelfSigned.sh` 即可生成新域名的证书，我们将其放在前端根目录的 ssl 目录下。\n\nNginx 配置文件我们主要编写 `nginx.proj.conf` 即可，各配置详细说明可以直接看文件，有个地方需要注意下，如果 location 块使用 `/xxx/` 时，`proxy_pass` 后面加斜杠和不加斜杠结果会不一样，举个例子：\n\n```nginx\nserver\n{\n    listen 80;\n    server_name: www.naivegenerator.com;\n    location /api/ {\n        proxy_pass http://127.0.0.1:8000; # 配置1\n        proxy_pass http://127.0.0.1:8000/; # 配置2\n    }\n}\n```\n\n使用配置 1 时，请求 `http://www.naivegenerator.com/api/generate/` 时会被成功转到：`http://127.0.0.1:8000/api/generate/`，而使用配置 2 时，则会被转到：`http://127.0.0.1:8000/generate/`。请求静态文件也是一样，所以这里需要稍微注意下。\n\n然后是编写 Dockerfile（最好创建并编写一下 `.dockerignore` 将 node modules 忽略掉），这里面有四个地方要强调一下：\n\n- 正式部署时，一般需要先在服务器上放一个验证文件，能点击下载后才能获得 `server.key` 和 `server.crt`，比如 [SSL For Free - Free SSL Certificates in Minutes](https://www.sslforfree.com/)，但我们本地因为是自己电脑给的证书，所以这一步不需要。当然如果你采用其他的证书授予商，也可能有不同的要求。除了上面那个免费的 SSl 外，还有很多，大家可以上网搜一搜，比如：[Getting Started - Let's Encrypt - Free SSL/TLS Certificates](https://letsencrypt.org/getting-started/)。\n- 关于 ARG 和 ENV 详细情况大家可以看一下官网，build image 时可以使用 arg。本例中，我们使用 `ENV BACKEND_HOST=${backend_host}` 设置了一个环境变量，变量值从 docker-compose 中 nginx build 的 arg 中取变量名为 `{backend_host}` 的变量，然后将该环境变量传入配置文件，让其生效。这么做的目的主要是因为本地 docker-compose up 后 Nginx 访问后端服务需要使用 `192.168.65.2`，而不是 `127.0.0.1`，正式部署时在 k8s 上可以使用后者。\n- 前端的静态文件在运行 `npm run build` 后会自动生成 build 文件夹，我们在 `nginx.proj.conf` 中将其设置为主页的根目录；后端（admin 和 rest）的静态文件则需要后端用 `python manage.py collectstatic` 收集后在 Dockerfile 中复制到前端某个地址。需要说明的是，因为我们将这个 static 目录共享了，所以虽然我们把本地目录整个映射出去了，但登陆 app 容器运行上面的命令后本地依然看不到 static 下的文件。因为 docker 会自动创建一个 volume（只要运行 `docker volume ls` 就看到了），文件就在这共享的 volume 里（本例中位 demo_app_static）。那怎么办呢？有三种办法：\n    - 注释掉共享的目录，重新启动运行；\n    \n    - 本地生成；\n    \n    - 找到文件的实际位置然后复制出来，关于 volume 的详细情况可以通过 `docker volume inspect demo_app_static` 查看，运行 `screen ~/Library/Containers/com.docker.docker/Data/vms/0/tty` 然后进入 inspect 的目录就可以看到了。注意需要运行一个 docker 才能复制，步骤如下:\n    \n        - `docker run -it --rm -v volume_name:/home_or_other_dir  container /bin/bash`\n        - `docker cp container_name:/home_or_other_dir /your_local_path`\n    \n        因为这里的 volume 并没有在你 `docker-compose up` 起来的容器里，所以我们需要先将它映射到一个容器里再复制。\n- 关于 Dockerfile 中 ENV 替换到 nginx 配置文件需要特别注意，可以参考这里：[configuration - nginx 'invalid number of arguments in \"map\" directive' - Stack Overflow](https://stackoverflow.com/questions/39968344/nginx-invalid-number-of-arguments-in-map-directive)\n\n最后别忘了修改 host：`sudo vim /etc/hosts`，添加一行：`127.0.0.1 naivegenerator.com`，这样当我们访问 `naivegenerator.com` 时等于是访问了 `127.0.0.1`。\n\n## 最终效果\n\n重新 `docker-compose up redis db app nginx` 后我们可以打开 `https://naivegenerator.com`，然后输入 “爱情” 让模型随机生成，如图所示：\n\n![](http://qnimg.lovevivian.cn/blog-sslstep-4.jpeg)\n\nadmin 网址：`https://naivegenerator.com/admin`，如图所示：\n\n![](http://qnimg.lovevivian.cn/blog-sslstep-5.jpeg)\n\nrest 网址：`https://naivegenerator.com/api`， 如图所示：\n\n![](http://qnimg.lovevivian.cn/blog-sslstep-6.jpeg)\n\n## 注意事项\n\n- 如果 `docker-compose stop` 后还想把生成的 container 也删掉（因为各种报错我们可能会不停地 build，有时候之前的需要彻底删除），可以使用这个命令：`docker stop $(docker ps -a -q); docker rm $(docker ps -a -q); docker volume rm $(docker volume ls -qf dangling=true)`\n\n- 由于本例把所有操作整合在一起了，有些童鞋如果想要了解每一步的细节，可以在本地把环境设置好，使用 docker-compose 单独 up 启动 db 和 redis，然后在本地操作后端交互。无论是 up 还是 stop，都须在 `docker-compose.yml` 所在目录执行。\n- docker-compose 中 build 之外的字段对 build 没有影响，build 主要受 Dockerfile 的影响；Dockerfile 如果名字不是 `Dockerfile` 需要指定文件名。\n- 本例把 `backend.local.env` 和 `frontend.local.env` 一起上传了，但在正式项目中，大家务必把 `*.env` 添加到 `.gitignore` 中，这样信息就不会泄露了。\n- 本例使用了 Celery 但后端代码并没有写成异步；同样使用了 Redis 但并未用在后端服务，而是用作了 Celery 的 Broker。\n- 本例中的各个模块（Docker）除外都可以替换为同类其他产品，前后端自不必说，uWSGI 有 Guniron，Postgres 有 Mysql、MongoDB 等等，但我觉得基本思路是类似的，我们的目的是 Docker 化。\n- 关于 Docker 操作的，台湾省一个同胞写的 [twtrubiks/docker-tutorial: Docker 基本教學 - 從無到有 Docker-Beginners-Guide 教你用 Docker 建立 Django + PostgreSQL 📝](https://github.com/twtrubiks/docker-tutorial) 还不错，另外官方文档也非常赞，所以我就没有废话了。我在他另一个项目里也学到了一些（第一个参考文献），推荐新手看看。\n- 本项目可优化之处还有很多，比如 Redis 用于后端服务、比如异步操作、比如 uWSGI socket 方式等等，不一而足，还请大家结合自己的实际情况灵活使用。另外，各个部分深入后都会有另外一些坑，比如 postgres create db 的编码问题，我会在其他文章中分享，欢迎大家关注。\n\n## 小结\n\n本项目主要介绍基于 Docker 的 Web 全栈开发系列，看似东西很多，但其实每个地方都没有过多深入，项目也非常简单，所以适合 beginners。如果大家觉得看起来好像非常繁琐那也是正常的，因为确实会有一点繁琐，但只要仔细点理清每个地方其实并不难。我们最后把整个项目的目录列出来并整体总结一下：\n\n```bash\ntree -L 2\n.\n├── Dockerfile_local\t\t\t\t# 前端本地的 Dockerfile\n├── Dockerfile_nginx\t\t\t\t# 前端 Nginx 的 Dockerfile\n├── Dockerfile_server\t\t\t\t# 后端 Server 的 Dockerfile\n├── backend.local.env\t\t\t\t# 后端 Server 在 docker-compose.yml 中的环境变量\n├── demo_backend\t\t\t\t# 后端项目目录\n│   ├── Pipfile\t\t\t\t\t# 项目依赖管理文件\n│   ├── Pipfile.lock\t\t\t\t# 同上，lock 文件\n│   ├── celery\t\t\t\t\t# Celery 的配置文件，可以放在任何地方\n│   ├── demo_backend\t\t\t\t# 项目配置文件和入口\n│   ├── manage.py\t\t\t\t# 本地开发入口文件\n│   ├── static\t\t\t\t\t# 后端 admin 和 rest 静态文件\n│   ├── supervisor-master.zip\t\t\t# supervisor repo，build 时下载太慢，采用本地安装\n│   ├── supervisord.conf\t\t\t# Supervisor 正式环境配置文件\n│   ├── supervisord.local.conf\t\t\t# Supervisor 开发环境配置文件\n│   ├── supervisord.log\t\t\t\t# Supervisor 运行时的 log 文件\n│   ├── text_generator\t\t\t\t# 后端的 App，本例只有一个\n│   └── uwsgi.ini\t\t\t\t# uWSGI 配置文件，用于正式环境\n├── demo_frontend\t\t\t\t# 前端项目目录\n│   ├── build\t\t\t\t\t# npm run build 后生成的静态文件\n│   ├── config-overrides.js\t\t\t# 覆盖配置 (本地 https) 使用的配置文件\n│   ├── node_modules\t\t\t\t# node modules\n│   ├── package-lock.json\t\t\t# package lock 文件\n│   ├── package.json\t\t\t\t# package 依赖\n│   ├── public\t\t\t\t\t# public 文件\n│   ├── src\t\t\t\t\t# 前端源代码\n│   ├── ssl\t\t\t\t\t# 域名 host ssl 证书相关文件\n│   └── ssl.localhost\t\t\t\t# localhost ssl 证书相关文件\n├── docker-compose.yml\t\t\t\t# docker-compose 配置文件\n├── frontend.local.env\t\t\t\t# 前端开发在 docker-compose.yml 中的环境变量\n├── nginx.conf\t\t\t\t\t# nginx 默认配置文件\n└── nginx.proj.conf\t\t\t\t# nginx 项目配置文件\n```\n\n\u003e 后端的 supervisor 和 celery 文件夹以及前端的 ssl 和 ssl.localhost 理论上是可以放在其他地方的，放在这些位置只是方便开发。\n\u003e\n\u003e 很多文件或文件夹是可以 ignore 的，比如前端的 build，后端的 supervisord.log 以及模型文件等，不过为了更加方便大家查看就都推上去了。\n\n这样我们就把前后端用 docker 完全地整合在一起，在全栈开发时可以前后端同时开发调试，正式上线时也可以快速完成部署。\n\n另外，把映射的日志文件目录也放在这里：\n\n```bash\ncd ~/docker_volume/log\ntree -L 2\n.\n├── celery\n│   ├── err.log\n│   ├── out.log\n│   ├── worker1-1.log\n│   ├── worker1-2.log\n│   ├── worker1-3.log\n│   ├── worker1-4.log\n│   ├── worker1-5.log\n│   ├── worker1-6.log\n│   ├── worker1-7.log\n│   ├── worker1-8.log\n│   └── worker1.log\n├── nginx\n│   ├── access.log\n│   ├── demo.access.log\n│   ├── demo.error.log\n│   └── error.log\n└── uwsgi\n    ├── demo.uwsgi.log\n    ├── err.log\n    └── out.log\n```\n\n分别是 celery、uwsgi 和 nginx 的日志文件，nginx 的 `demo.*` 就是我们针对项目做得配置，celery 的 `err.log` 和 `out.log` 是我们在 Supervisor 中做得配置，其余的则是 celery 的 conf 文件做得配置（`celeryd.conf` line 24：`CELERYD_OPTS=\"--time-limit=300 --concurrency=8\"`）。建议把一个项目的日志放（或映射）在一个地方，无论是本地开发还是正式部署。\n\n\u003e 有些 log 是没必要的，比如 uwsgi，如果配置本身设置了的话，supervisor 那里可以不用设置。\n\n## 参考文献和资源\n\n以下主要罗列使用过的参考文献和一些还不错的资源，简单的归了下类，大家按需取用。\n\n- [twtrubiks/docker-django-nginx-uwsgi-postgres-tutorial: Docker + Django + Nginx + uWSGI + Postgres 基本教學 - 從無到有 (Docker + Django + Nginx + uWSGI + Postgres Tutorial)](https://github.com/twtrubiks/docker-django-nginx-uwsgi-postgres-tutorial)\n\n- Https 配置\n    - [Django+uWsgi+nginx+https 完全配置 | Sunrise 博客](https://yjdwbj.github.io/2016/11/09/Django-uWsgi-nginx-https-%E5%AE%8C%E5%85%A8%E9%85%8D%E7%BD%AE/)\n    - [Django + uWSGI + Nginx + SSL - request for working configuration (emphasis on SSL) - Stack Overflow](https://stackoverflow.com/questions/29827299/django-uwsgi-nginx-ssl-request-for-working-configuration-emphasis-on-ss)\n    - [Nginx 配置 HTTPS 服务器 | Aotu.io「凹凸实验室」](https://aotu.io/notes/2016/08/16/nginx-https/index.html)\n\n- SSL 证书\n\n    - [JrCs/docker-letsencrypt-nginx-proxy-companion: LetsEncrypt companion container for nginx-proxy](https://github.com/JrCs/docker-letsencrypt-nginx-proxy-companion)\n    - [How to Set Up Free SSL Certificates from Let's Encrypt using Docker and Nginx](https://www.humankode.com/ssl/how-to-set-up-free-ssl-certificates-from-lets-encrypt-using-docker-and-nginx)\n    - [那些证书相关的玩意儿 (SSL,X.509,PEM,DER,CRT,CER,KEY,CSR,P12 等) - guogangj - 博客园](https://www.cnblogs.com/guogangj/p/4118605.html)\n    - [细说 CA 和证书 | 小胡子哥的个人网站](https://www.barretlee.com/blog/2016/04/24/detail-about-ca-and-certs/)\n\n- Celery\n    - [How to run celery as a daemon?](https://pythad.github.io/articles/2016-12/how-to-run-celery-as-a-daemon-in-production)\n    - [5. 任务状态追踪 on_message - 疯哥哥带你飞 - CSDN 博客](https://blog.csdn.net/oyw5201314ck/article/details/79669262)\n    - [Asynchronous Tasks With Django and Celery – Real Python](https://realpython.com/asynchronous-tasks-with-django-and-celery/#what-is-celery)\n    - [分布式队列神器 Celery - python web 学习轨迹 - SegmentFault 思否](https://segmentfault.com/a/1190000008022050)\n    - [Daemonization — Celery 4.2.0 documentation](http://docs.celeryproject.org/en/latest/userguide/daemonizing.html#available-options)\n\n- Supervisor + Nginx + uWSGI\n    - [Supervisor with Docker: Lessons learned - Advanced Web Machinery](https://advancedweb.hu/2018/07/03/supervisor_docker/)\n    - [Python, UWSGI, Supervisor \u0026 Nginx](https://gist.github.com/timmyomahony/1047615#file-supervisord-conf)\n    - [dorianpula/ansible-nginx-uwsgi-supervisor: An Ansible role to setup and manage a UWSGI app via supervisor, and served up on a NGINX web server.](https://github.com/dorianpula/ansible-nginx-uwsgi-supervisor)\n    - [Supervisor: A Process Control System — Supervisor 3.3.5 documentation](http://supervisord.org/)\n\n    - [Setting up Django and your web server with uWSGI and nginx — uWSGI 2.0 documentation](https://uwsgi-docs.readthedocs.io/en/latest/tutorials/Django_and_nginx.html)\n\n    - [How to use Django with uWSGI | Django documentation | Django](https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/uwsgi/)\n\n## 后记\n\n这篇文章包括这个项目花了一天时间才完成，这还是在已经对各模块都有一些经验的情况下。很多地方都踩过坑，说这篇文章的内容 “坑坑洼洼” 也不夸张，尤其是我提到要注意或者特别强调的点。在解决这些坑的过程中得到不少同事的帮助，尤其是 [scottming (Scott Ming)](https://github.com/scottming) 在本地 Https 和 Dockerfile 相关的配置中给予了很多启发和提示。最后，希望这个 demo 项目和文章能对大家有所帮助，如果有 Google、Stackoverflow 没有找到答案且与此项目相关的问题或本项目疏漏的地方，欢迎大家 Issue。\n\n## CHANGELOG\n\n- 20190303 创建","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhscspring%2Fweb-full-stack-practice","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhscspring%2Fweb-full-stack-practice","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhscspring%2Fweb-full-stack-practice/lists"}