在Docker环境下的Web3层构造~nginx、django、PostgreSQL的协同配合~
首先
此次要创建的东西。

筹备工作
– 可以公开访问的服务器(在本篇文章中使用了EC2实例。选择的操作系统是Amazon Linux 2。)
– 安装Python 3(在本篇文章中使用了版本为3.8.9。后半部分有通过pyenv安装的步骤说明。)
验证 docker-compose 的运行(直到启动 nginx)。
安装 Docker
登录EC2实例并使用SSH执行以下命令安装Docker。
并且设置Docker可以在启动时自动启动,并且启用。
sudo yum install -y docker
sudo systemctl start docker
sudo systemctl enable docker
sudo docker info # 確認
将ec2-user添加到docker组中。
通过这个方法,您可以在不使用sudo的情况下使用docker命令。
此外,如同docker官方文档所述,可以完成以下操作。
如果在虚拟机上进行测试,为了使更改生效,可能需要重新启动虚拟机。
添加群组后重新启动实例。
sudo usermod -aG docker $USER
cat /etc/group | grep docker # 確認
sudo reboot # 再起動
安装Docker Compose
检查Docker官方文档,确定版本(本次使用1.29.2)。
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose
docker-compose version # 確認
通过docker-compose确认容器的启动
使用Docker Compose 获取nginx镜像并显示测试页面。
version: '3.7'
services:
nginx:
container_name: nginx
image: nginx:latest
ports:
- 80:80
执行以下指令来启动容器。
docker-compose up -d

docker-compose down
建立三层架构
准备nginx容器(Web服务器)
目录结构
容器/
├ Django/
│ └ …
├ Nginx/
└ 配置/
└ nginx.conf
└ uwsgi参数
├ docker-compose.yml
添加volume的分配和依赖关系。
version: '3.7'
services:
nginx:
container_name: nginx
image: nginx:latest
volumes: # add
- ./nginx/conf:/etc/nginx/conf.d # add
- ./nginx/uwsgi_params:/etc/nginx/uwsgi_params # add
ports:
- 80:80
depends_on: # add
- django # add
创建 nginx.conf 文件。
根据 upstream 指令中的描述,将通信流量设置到 Django 容器的8000端口上。
(Chinese translation)
upstream django {
server django:8000;
}
server {
listen 80;
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
}
创建用于WSGI通信所需的uwsgi_params(参考)。
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_ADDR $server_addr;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
准备django容器(应用服务器)。
目录结构
容器/
├ django/
│ └ 启动
│ └ setuser.sh
│ └ startup.sh
│ └ uwsgi
│ └ uwsgi.ini
│ └ Dockerfile
│ └ requirements.txt
├ nginx/
│ └ …
├ docker-compose.yml
为了能够接收nginx容器发送过来的通信请求,需要在Django容器中创建uwsgi.ini文件。
[uwsgi]
socket = :8000
module = djangoapp.wsgi
wsgi-file = /app/app/wsgi.py
logto = /wsgi/wsgi.log
py-autoreload = 1
创建一个用于构建Django容器镜像的Dockerfile。
FROM python:3
WORKDIR /app
COPY requirements.txt /app
RUN pip install -r requirements.txt
COPY . /app
创建一个在Django容器中所需的Python模块列表。
django
psycopg2
uwsgi
在Django容器中创建一个执行的shell脚本(startup.sh, setuser.sh)。
创建用户,并执行uwsgi.ini文件。
source /startup/setuser.sh # setuser.sh を実行する
uwsgi --ini /wsgi/uwsgi.ini # uwsgi.ini の設定をもとに uwsgi を実行する
#!/bin/bash -e
SHELL_NAME='setuser.sh'
echo "[$SHELL_NAME] START"
# setup group
if getent group "$GROUP_ID" > /dev/null 2>&1; then
echo "[$SHELL_NAME] GROUP_ID '$GROUP_ID' already exists."
else
echo "[$SHELL_NAME] GROUP_ID '$GROUP_ID' does NOT exist. So execute [groupadd -g \$GROUP_ID \$GROUP_NAME]."
groupadd -g $GROUP_ID $GROUP_NAME
fi
# setup user
if getent passwd "$USER_ID" > /dev/null 2>&1; then
echo "[$SHELL_NAME] USER_ID '$USER_ID' already exists."
else
echo "[$SHELL_NAME] USER_ID '$USER_ID' does NOT exist. So execute [useradd -m -s /bin/bash -u \$USER_ID -g \$GROUP_ID \$USER_NAME]."
useradd -m -s /bin/bash -u $USER_ID -g $GROUP_ID $USER_NAME
fi
echo "[$SHELL_NAME] FINISH"
给startup目录下的所有文件添加执行权限。
chmod +x -R ~/dev/3l_on_docker/django/startup/
首先,在主机端创建Django项目。为此,首先在主机端安装Django。
pip install django
在Django目录下创建Django项目。
本次项目名称将设为djangoapp。
django-admin startproject djangoapp
创建项目后,在Django目录中会自动生成一个名为djangoapp的文件夹。
默认情况下,拒绝所有来自外部的通信,需要对setting.py进行修改。
...
ALLOWED_HOSTS = ['*']
...
这次设置了允许外部所有通信的”*”选项。
使用docker-compose up -d命令启动容器,并访问EC2的公共IP地址。

只要能看到类似上面的 Django 测试页面就可以。
创建样品应用程序
既经看到了测试页面,从这里开始显示自己创建的页面。
首先,在主机端创建一个应用程序。
python manage.py startapp sampleapp
在sampleapp文件夹的根目录下创建urls.py。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
from django.http import HttpResponse
def index(request):
return HttpResponse("Hello, world. You're at the polls index.")
访问 http://[IP地址]/[应用名称],如果可以确认看到 “你好,世界。你在投票索引页面。”,那就可以了。
数据库布局(编辑 models.py 文件)
class User(models.Model):
user_name = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
class FigurePaths(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
figure_url = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
like_point = models.IntegerField(default=0)
INSTALLED_APPS = [
'instalikeapp.apps.InstalikeappConfig',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
python manage.py makemigrations sampleapp
在migration目录下会创建一个0001_initial.py文件。
使用docker exec -it bash命令进入django容器,并执行sqlmigrate和migrate命令,以完成向数据库添加数据的准备工作。
[ec2-user@ip-10-0-1-89 djangoapp]$ docker exec -it django bash
root@87e83722b47a:/app# python manage.py sqlmigrate sampleapp 0001
root@87e83722b47a:/app# python manage.py migrate
准备PostgreSQL容器(数据库服务器)。
在PostgreSQL容器中添加配置。
version: '3.7'
services:
nginx:
container_name: nginx
image: nginx:latest
volumes:
- ./nginx/conf:/etc/nginx/conf.d
- ./nginx/uwsgi_params:/etc/nginx/uwsgi_params
ports:
- 80:80
depends_on:
- django
django:
container_name: django
build: ./django
command: bash -c /startup/startup.sh
volumes:
- ./django/uwsgi:/wsgi
- ./django/djangoapp:/app
- ./django/startup:/startup
ports:
- 8000:8000
environment:
- USER_ID=1000
- GROUP_ID=1000
- USER_NAME=ec2-user
- GROUP_NAME=ec2-user
depends_on: # add
- postgresql # add
postgresql: # add
image: postgres:latest # add
container_name: postgresql # add
environment: # add
- POSTGRES_DB=instalikeapp_db # add
- POSTGRES_USER=user # add
- POSTGRES_PASSWORD=password # add
volumes: # add
- ./pgdata:/var/lib/postgresql/data # add
ports: # add
- 5432:5432 # add
重新启动容器。
docker-compose restart
尝试使用Django容器内的Python解释器将数据插入数据库。
可以参考Django教程以获取详细信息。
进入Django容器,并通过Python解释器向数据库输入数据。
docker exec -it django bash
root@87e83722b47a:/app# python manage.py shell
>>> from instalikeapp.models import User, FigurePaths
使用以下命令登录到 PostgreSQL 数据库。
psql -d myapp -U USERNAME -h localhost

额外奖励
通过pyenv安装Python 3系。
sudo yum install git
git clone https://github.com/yyuu/pyenv.git ~/.pyenv
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc
echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc
echo 'eval "$(pyenv init --path)"' >> ~/.bashrc
echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.bashrc
source ~/.bashrc
sudo yum install gcc zlib-devel bzip2 bzip2-devel readline readline-devel sqlite sqlite-devel openssl openssl-devel -y
pyenv install 3.8.9
pyenv global 3.8.9
将CSS应用到Django网站中
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static') # add
将静态文件复制到项目的静态目录下的static文件夹中。
python manage.py collectstatic
server {
listen 80;
location / {
uwsgi_pass django;
include /etc/nginx/uwsgi_params;
}
location /static/ {
root /app/;
}
}