将你的Django/Flask应用部署到云服务器(Docker实战)

目录

  • 将你的Django/Flask应用部署到云服务器(Docker实战)
    • [1 引言](#1 引言)
      • [1.1 为什么需要容器化部署?](#1.1 为什么需要容器化部署?)
      • [1.2 Django与Flask简介](#1.2 Django与Flask简介)
    • [2 Docker基础](#2 Docker基础)
      • [2.1 Docker核心概念](#2.1 Docker核心概念)
      • [2.2 Docker的优势](#2.2 Docker的优势)
    • [3 准备工作](#3 准备工作)
      • [3.1 项目结构准备](#3.1 项目结构准备)
      • [3.2 环境配置](#3.2 环境配置)
    • [4 编写Dockerfile](#4 编写Dockerfile)
      • [4.1 Flask应用Dockerfile](#4.1 Flask应用Dockerfile)
      • [4.2 Django应用Dockerfile](#4.2 Django应用Dockerfile)
      • [4.3 多阶段构建优化](#4.3 多阶段构建优化)
    • [5 使用Docker Compose编排多容器应用](#5 使用Docker Compose编排多容器应用)
      • [5.1 Flask多服务编排](#5.1 Flask多服务编排)
      • [5.2 Django多服务编排](#5.2 Django多服务编排)
      • [5.3 生产环境优化](#5.3 生产环境优化)
    • [6 生产环境配置](#6 生产环境配置)
      • [6.1 使用Gunicorn作为WSGI服务器](#6.1 使用Gunicorn作为WSGI服务器)
      • [6.2 Nginx配置](#6.2 Nginx配置)
      • [6.3 环境变量管理](#6.3 环境变量管理)
    • [7 完整示例代码](#7 完整示例代码)
      • [7.1 Flask应用完整示例](#7.1 Flask应用完整示例)
      • [7.2 Django应用完整示例](#7.2 Django应用完整示例)
    • [8 部署到云服务器](#8 部署到云服务器)
      • [8.1 服务器准备](#8.1 服务器准备)
      • [8.2 部署流程](#8.2 部署流程)
      • [8.3 使用CI/CD自动化部署](#8.3 使用CI/CD自动化部署)
    • [9 常见问题与解决方案](#9 常见问题与解决方案)
      • [9.1 部署常见错误](#9.1 部署常见错误)
      • [9.2 性能优化建议](#9.2 性能优化建议)
    • [10 总结](#10 总结)
      • [10.1 关键要点回顾](#10.1 关键要点回顾)
      • [10.2 后续学习方向](#10.2 后续学习方向)

『宝藏代码胶囊开张啦!』------ 我的 CodeCapsule 来咯!✨

写代码不再头疼!我的新站点 CodeCapsule 主打一个 "白菜价"+"量身定制 "!无论是卡脖子的毕设/课设/文献复现 ,需要灵光一现的算法改进 ,还是想给项目加个"外挂",这里都有便宜又好用的代码方案等你发现!低成本,高适配,助你轻松通关!速来围观 👉 CodeCapsule官网

将你的Django/Flask应用部署到云服务器(Docker实战)

1 引言

1.1 为什么需要容器化部署?

在现代软件开发中,将应用程序快速、可靠地部署到不同环境是一项关键挑战。传统部署方式常常面临"在我机器上能运行"的问题,因为开发、测试和生产环境之间的差异导致应用程序行为不一致。容器化技术通过将应用程序及其所有依赖项打包在一起,彻底解决了环境一致性问题。

Docker作为目前最流行的容器化平台,提供了一种轻量级的虚拟化解决方案。与传统虚拟机相比,Docker容器共享主机操作系统内核,启动更快、资源开销更小,同时仍能保持应用程序的隔离性。对于Python Web应用而言,无论是使用轻量级的Flask还是全功能的Django,Docker都能确保它们在各种环境下具有完全相同的行为。

1.2 Django与Flask简介

Django是一个高级Python Web框架,遵循"包含电池"(batteries-included)理念,提供了ORM、认证、管理后台等丰富功能,适合快速构建复杂的企业级应用。Flask则是一个轻量级微框架,核心简单但可通过扩展增强功能,给予开发者更多灵活性,适合小型项目、API服务和微服务架构。

尽管这两个框架在设计哲学和功能集上有所不同,但它们都可以通过Docker实现标准化部署,这也是本文将要重点介绍的内容。

2 Docker基础

2.1 Docker核心概念

在开始容器化部署之前,需要了解以下几个核心概念:

  • 镜像(Image):只读模板,包含运行应用程序所需的一切------代码、运行时、库、环境变量和配置文件。
  • 容器(Container):镜像的运行实例,是一个隔离的进程,拥有自己的文件系统、网络和进程空间。
  • Dockerfile:文本文件,包含一系列构建镜像的指令。
  • Docker Compose:工具用于定义和运行多容器Docker应用程序。

2.2 Docker的优势

使用Docker部署Python Web应用带来了多重优势:

  • 环境一致性:确保应用从开发到生产在不同环境中具有完全相同的行为。
  • 快速部署:容器启动秒级完成,极大缩短部署周期。
  • 资源高效:容器共享主机OS内核,无需为每个应用分配完整操作系统资源。
  • 易于扩展:结合Kubernetes等编排工具,可以轻松实现应用的水平扩展。

3 准备工作

3.1 项目结构准备

在开始容器化之前,需要合理组织项目结构。一个典型的Docker化Python Web项目结构如下:

复制代码
mywebapp/
├── app/                  # 应用代码目录
│   ├── __init__.py
│   ├── models.py        # 数据模型
│   ├── views.py         # 视图函数
│   └── static/          # 静态文件
├── requirements.txt      # Python依赖
├── Dockerfile           # Docker构建文件
├── docker-compose.yml   # 多服务编排
├── entrypoint.sh        # 启动脚本
└── .dockerignore        # Docker忽略文件

3.2 环境配置

为适应容器环境,需要对应用配置进行一些调整:

Flask应用配置调整(app.py):

python 复制代码
from flask import Flask
import os

app = Flask(__name__)

# 从环境变量读取配置,增强灵活性
app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev-secret-key')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get('DATABASE_URL', 'sqlite:///app.db')

@app.route('/')
def hello():
    return 'Hello, Dockerized Flask!'

if __name__ == '__main__':
    # 关键:监听所有网络接口
    app.run(host='0.0.0.0', port=5000)

Django设置调整(settings.py):

python 复制代码
import os

# 从环境变量读取密钥
SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-dev-key')

# 从环境变量读取数据库配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ.get('DB_NAME', 'mydatabase'),
        'USER': os.environ.get('DB_USER', 'myuser'),
        'PASSWORD': os.environ.get('DB_PASSWORD', 'mypassword'),
        'HOST': os.environ.get('DB_HOST', 'localhost'),
        'PORT': os.environ.get('DB_PORT', '5432'),
    }
}

# 静态文件配置
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')

4 编写Dockerfile

4.1 Flask应用Dockerfile

创建Flask应用的Dockerfile:

dockerfile 复制代码
# 使用官方Python精简版作为基础镜像
FROM python:3.10-slim

# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    FLASK_APP=app.py \
    FLASK_ENV=production

# 设置工作目录
WORKDIR /app

# 安装系统依赖
RUN apt-get update \
    && apt-get install -y --no-install-recommends gcc python3-dev \
    && rm -rf /var/lib/apt/lists/*

# 复制依赖文件
COPY requirements.txt .

# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 创建非root用户(安全最佳实践)
RUN useradd -m -u 1000 webuser && chown -R webuser:webuser /app
USER webuser

# 暴露端口
EXPOSE 5000

# 定义启动命令
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:app"]

4.2 Django应用Dockerfile

Django应用的Dockerfile与Flask类似,但有一些特定配置:

dockerfile 复制代码
# 使用官方Python精简版作为基础镜像
FROM python:3.10-slim

# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

# 设置工作目录
WORKDIR /app

# 安装系统依赖
RUN apt-get update \
    && apt-get install -y --no-install-recommends \
        gcc \
        python3-dev \
        libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# 复制依赖文件
COPY requirements.txt .

# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 创建非root用户
RUN useradd -m -u 1000 webuser && chown -R webuser:webuser /app
USER webuser

# 收集静态文件(在启动脚本中处理更合适)
# RUN python manage.py collectstatic --noinput

# 暴露端口
EXPOSE 8000

# 启动命令
CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "myproject.wsgi:application"]

4.3 多阶段构建优化

对于生产环境,可以使用多阶段构建来减小镜像大小:

dockerfile 复制代码
# 第一阶段:构建阶段
FROM python:3.10-slim as builder

WORKDIR /app

COPY requirements.txt .

# 安装构建依赖
RUN apt-get update && apt-get install -y --no-install-recommends gcc python3-dev

# 安装依赖到用户目录
RUN pip install --user -r requirements.txt

# 第二阶段:最终镜像
FROM python:3.10-slim

WORKDIR /app

# 从构建阶段复制已安装的Python包
COPY --from=builder /root/.local /root/.local
COPY . .

# 确保使用的Python可以找到用户安装的包
ENV PATH=/root/.local/bin:$PATH

# 创建非root用户
RUN useradd -m -u 1000 webuser && chown -R webuser:webuser /app
USER webuser

EXPOSE 8000

CMD ["gunicorn", "--bind", "0.0.0.0:8000", "myproject.wsgi:application"]

5 使用Docker Compose编排多容器应用

5.1 Flask多服务编排

大多数Web应用不仅包含应用本身,还需要数据库、缓存等支撑服务。以下是一个典型的Flask多服务编排示例:

yaml 复制代码
version: '3.8'

services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - DATABASE_URL=postgresql://flaskuser:flaskpass@db:5432/flaskdb
      - REDIS_URL=redis://redis:6379/0
      - SECRET_KEY=your-secret-key-here
    depends_on:
      - db
      - redis
    volumes:
      - .:/app  # 开发时使用,生产环境应移除
    restart: unless-stopped

  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=flaskdb
      - POSTGRES_USER=flaskuser
      - POSTGRES_PASSWORD=flaskpass
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:6-alpine
    volumes:
      - redis_data:/data
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:

5.2 Django多服务编排

Django项目的Docker Compose文件与Flask类似,但可能包含更多服务:

yaml 复制代码
version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DJANGO_SETTINGS_MODULE=myproject.settings.production
      - DATABASE_URL=postgresql://djangouser:djangopass@db:5432/djangodb
      - REDIS_URL=redis://redis:6379/0
      - SECRET_KEY=your-django-secret-key
    depends_on:
      - db
      - redis
    volumes:
      - static_volume:/app/staticfiles
      - media_volume:/app/mediafiles
    command: >
      sh -c "python manage.py migrate &&
             python manage.py collectstatic --noinput &&
             gunicorn --bind 0.0.0.0:8000 --workers 3 myproject.wsgi:application"
    restart: unless-stopped

  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=djangodb
      - POSTGRES_USER=djangouser
      - POSTGRES_PASSWORD=djangopass
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

  redis:
    image: redis:6-alpine
    volumes:
      - redis_data:/data
    restart: unless-stopped

  nginx:
    image: nginx:1.21-alpine
    ports:
      - "80:80"
    volumes:
      - static_volume:/app/staticfiles
      - media_volume:/app/mediafiles
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
    depends_on:
      - web
    restart: unless-stopped

volumes:
  postgres_data:
  redis_data:
  static_volume:
  media_volume:

5.3 生产环境优化

生产环境的Docker Compose需要更多优化配置:

yaml 复制代码
version: '3.8'

services:
  web:
    build: 
      context: .
      target: builder  # 多阶段构建
    ports:
      - "8000:8000"
    environment:
      - DJANGO_SETTINGS_MODULE=myproject.settings.production
      - DATABASE_URL=postgresql://${DB_USER}:${DB_PASSWORD}@db:5432/${DB_NAME}
      - SECRET_KEY=${SECRET_KEY}
    env_file:
      - .env.production
    depends_on:
      - db
    volumes:
      - static_volume:/app/staticfiles
      - media_volume:/app/mediafiles
    command: >
      sh -c "python manage.py migrate &&
             python manage.py collectstatic --noinput &&
             gunicorn --bind 0.0.0.0:8000 --workers 4 --threads 2 myproject.wsgi:application"
    restart: unless-stopped
    logging:
      driver: "json-file"
      options:
        max-size: "10m"
        max-file: "3"

  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=${DB_NAME}
      - POSTGRES_USER=${DB_USER}
      - POSTGRES_PASSWORD=${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped
    command: >
      postgres -c shared_preload_libraries=pg_stat_statements
               -c pg_stat_statements.track=all
               -c max_connections=100

  nginx:
    image: nginx:1.21-alpine
    ports:
      - "80:80"
      - "443:443"  # HTTPS端口
    volumes:
      - static_volume:/app/staticfiles
      - media_volume:/app/mediafiles
      - ./nginx.conf:/etc/nginx/conf.d/default.conf
      - ./ssl:/etc/nginx/ssl  # SSL证书
    depends_on:
      - web
    restart: unless-stopped

volumes:
  postgres_data:
  static_volume:
  media_volume:

6 生产环境配置

6.1 使用Gunicorn作为WSGI服务器

Python内置的开发服务器不适合生产环境,应当使用专业的WSGI服务器如Gunicorn或uWSGI。

Gunicorn基础配置:

python 复制代码
# gunicorn.conf.py
# 并行工作进程数,通常建议 (2 * CPU核心数) + 1
workers = 4

# 指定每个工作者的线程数
threads = 2

# 监听内网端口
bind = '0.0.0.0:8000'

# 工作模式协程
worker_class = 'sync'

# 最大请求数,防止内存泄漏
max_requests = 1000
max_requests_jitter = 100

# 超时时间
timeout = 30

# 访问日志文件
accesslog = '-'

# 错误日志文件
errorlog = '-'

# 日志级别
loglevel = 'info'

对于I/O密集型应用,可以使用异步worker:

python 复制代码
# 安装异步worker支持
# pip install gevent

worker_class = 'gevent'
workers = 2
worker_connections = 1000

6.2 Nginx配置

Nginx作为反向代理,可以提供静态文件服务、负载均衡和SSL终端等功能。

基础Nginx配置(nginx.conf):

nginx 复制代码
# 定义上游应用服务器
upstream webapp {
    server web:8000;
}

server {
    listen 80;
    server_name yourdomain.com;
    
    # 静态文件配置
    location /static/ {
        alias /app/staticfiles/;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
    
    # 媒体文件配置
    location /media/ {
        alias /app/mediafiles/;
        expires 30d;
        add_header Cache-Control "public";
    }
    
    # 动态请求代理
    location / {
        proxy_pass http://webapp;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header Host $host;
        proxy_redirect off;
        
        # 超时设置
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
    
    # 安全头
    add_header X-Frame-Options DENY;
    add_header X-Content-Type-Options nosniff;
    add_header X-XSS-Protection "1; mode=block";
}

6.3 环境变量管理

敏感信息如密钥、数据库密码等应通过环境变量管理:

bash 复制代码
# .env.production
# Django设置
SECRET_KEY=your-very-secure-secret-key-here
DEBUG=False
ALLOWED_HOSTS=yourdomain.com,localhost,127.0.0.1

# 数据库配置
DB_NAME=mydatabase
DB_USER=mydbuser
DB_PASSWORD=mysecurepassword
DB_HOST=db
DB_PORT=5432

# Redis配置
REDIS_URL=redis://redis:6379/0

在Django设置中使用这些环境变量:

python 复制代码
# settings/production.py
import os

SECRET_KEY = os.environ['SECRET_KEY']
DEBUG = os.environ.get('DEBUG', 'False').lower() == 'true'
ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ['DB_NAME'],
        'USER': os.environ['DB_USER'],
        'PASSWORD': os.environ['DB_PASSWORD'],
        'HOST': os.environ['DB_HOST'],
        'PORT': os.environ.get('DB_PORT', '5432'),
    }
}

CACHES = {
    'default': {
        'BACKEND': 'django_redis.cache.RedisCache',
        'LOCATION': os.environ['REDIS_URL'],
        'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
        }
    }
}

7 完整示例代码

7.1 Flask应用完整示例

项目结构:

复制代码
flask-docker-app/
├── app/
│   ├── __init__.py
│   ├── models.py
│   ├── routes.py
│   └── static/
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
├── entrypoint.sh
└── .env

app/init.py:

python 复制代码
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import os

db = SQLAlchemy()

def create_app():
    app = Flask(__name__)
    
    # 配置
    app.config['SECRET_KEY'] = os.environ.get('SECRET_KEY', 'dev')
    app.config['SQLALCHEMY_DATABASE_URI'] = os.environ.get(
        'DATABASE_URL', 'sqlite:///app.db'
    )
    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    
    # 初始化扩展
    db.init_app(app)
    
    # 注册蓝图
    from .routes import main
    app.register_blueprint(main)
    
    return app

app/routes.py:

python 复制代码
from flask import Blueprint, jsonify
from .models import db, Visit

main = Blueprint('main', __name__)

@main.route('/')
def index():
    # 记录访问
    visit = Visit()
    db.session.add(visit)
    db.session.commit()
    
    # 获取总访问量
    total_visits = Visit.query.count()
    
    return jsonify({
        'message': 'Hello from Dockerized Flask!',
        'total_visits': total_visits
    })

@main.route('/health')
def health():
    return jsonify({'status': 'healthy'})

app/models.py:

python 复制代码
from . import db
from datetime import datetime

class Visit(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    timestamp = db.Column(db.DateTime, default=datetime.utcnow)

requirements.txt:

复制代码
Flask==2.3.3
Flask-SQLAlchemy==3.0.5
Flask-Migrate==4.0.4
gunicorn==21.2.0
psycopg2-binary==2.9.7
python-dotenv==1.0.0

Dockerfile:

dockerfile 复制代码
FROM python:3.10-slim

# 设置环境变量
ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1 \
    FLASK_APP=app

WORKDIR /app

# 安装系统依赖
RUN apt-get update \
    && apt-get install -y --no-install-recommends gcc python3-dev libpq-dev \
    && rm -rf /var/lib/apt/lists/*

# 复制依赖文件
COPY requirements.txt .

# 安装Python依赖
RUN pip install --no-cache-dir -r requirements.txt

# 复制应用代码
COPY . .

# 创建非root用户
RUN useradd -m -u 1000 webuser && chown -R webuser:webuser /app
USER webuser

# 暴露端口
EXPOSE 5000

# 启动命令
CMD ["gunicorn", "--bind", "0.0.0.0:5000", "--workers", "4", "app:create_app()"]

docker-compose.yml:

yaml 复制代码
version: '3.8'

services:
  web:
    build: .
    ports:
      - "5000:5000"
    environment:
      - DATABASE_URL=postgresql://flaskuser:flaskpass@db:5432/flaskdb
      - SECRET_KEY=your-production-secret-key
    depends_on:
      - db
    volumes:
      - ./app:/app/app  # 开发时使用,便于代码热重载
    restart: unless-stopped

  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=flaskdb
      - POSTGRES_USER=flaskuser
      - POSTGRES_PASSWORD=flaskpass
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres_data:

entrypoint.sh

bash 复制代码
#!/bin/bash
set -e

# 等待数据库就绪
echo "Waiting for database..."
while ! nc -z db 5432; do
  sleep 0.1
done
echo "Database started"

# 运行数据库迁移
flask db upgrade

# 启动应用
exec "$@"

7.2 Django应用完整示例

项目结构:

复制代码
django-docker-app/
├── myproject/
│   ├── __init__.py
│   ├── settings/
│   │   ├── __init__.py
│   │   ├── base.py
│   │   └── production.py
│   ├── urls.py
│   └── wsgi.py
├── manage.py
├── requirements.txt
├── Dockerfile
├── docker-compose.yml
├── entrypoint.sh
└── .env

myproject/settings/base.py:

python 复制代码
import os
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent.parent

SECRET_KEY = os.environ.get('SECRET_KEY', 'django-insecure-dev-key')

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

ROOT_URLCONF = 'myproject.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

WSGI_APPLICATION = 'myproject.wsgi.application'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

LANGUAGE_CODE = 'en-us'
TIME_ZONE = 'UTC'
USE_I18N = True
USE_TZ = True

STATIC_URL = '/static/'
STATIC_ROOT = BASE_DIR / 'staticfiles'

DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'

myproject/settings/production.py:

python 复制代码
from .base import *
import os

DEBUG = False

ALLOWED_HOSTS = os.environ.get('ALLOWED_HOSTS', '').split(',')

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': os.environ['DB_NAME'],
        'USER': os.environ['DB_USER'],
        'PASSWORD': os.environ['DB_PASSWORD'],
        'HOST': os.environ['DB_HOST'],
        'PORT': os.environ.get('DB_PORT', '5432'),
    }
}

# 安全配置
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
CSRF_COOKIE_SECURE = True
SESSION_COOKIE_SECURE = True

# 静态文件
STATIC_ROOT = '/app/staticfiles'
MEDIA_ROOT = '/app/mediafiles'

Dockerfile:

dockerfile 复制代码
FROM python:3.10-slim

ENV PYTHONUNBUFFERED=1 \
    PYTHONDONTWRITEBYTECODE=1

WORKDIR /app

RUN apt-get update \
    && apt-get install -y --no-install-recommends gcc python3-dev libpq-dev \
    && rm -rf /var/lib/apt/lists/*

COPY requirements.txt .

RUN pip install --no-cache-dir -r requirements.txt

COPY . .

RUN useradd -m -u 1000 webuser && chown -R webuser:webuser /app
USER webuser

EXPOSE 8000

CMD ["gunicorn", "--bind", "0.0.0.0:8000", "--workers", "3", "myproject.wsgi:application"]

docker-compose.yml:

yaml 复制代码
version: '3.8'

services:
  web:
    build: .
    ports:
      - "8000:8000"
    environment:
      - DJANGO_SETTINGS_MODULE=myproject.settings.production
      - DB_NAME=djangodb
      - DB_USER=djangouser
      - DB_PASSWORD=djangopass
      - DB_HOST=db
      - SECRET_KEY=your-django-production-secret
      - ALLOWED_HOSTS=localhost,127.0.0.1,0.0.0.0
    depends_on:
      - db
    volumes:
      - static_volume:/app/staticfiles
      - media_volume:/app/mediafiles
    command: >
      sh -c "python manage.py migrate &&
             python manage.py collectstatic --noinput &&
             gunicorn --bind 0.0.0.0:8000 --workers 3 myproject.wsgi:application"
    restart: unless-stopped

  db:
    image: postgres:13
    environment:
      - POSTGRES_DB=djangodb
      - POSTGRES_USER=djangouser
      - POSTGRES_PASSWORD=djangopass
    volumes:
      - postgres_data:/var/lib/postgresql/data
    restart: unless-stopped

volumes:
  postgres_data:
  static_volume:
  media_volume:

entrypoint.sh

bash 复制代码
#!/bin/bash
set -e

# 等待数据库就绪
echo "Waiting for PostgreSQL..."
while ! nc -z $DB_HOST $DB_PORT; do
  sleep 0.1
done
echo "PostgreSQL started"

# 应用数据库迁移
echo "Applying database migrations"
python manage.py migrate

# 收集静态文件
echo "Collecting static files"
python manage.py collectstatic --noinput

exec "$@"

8 部署到云服务器

8.1 服务器准备

在部署到云服务器前,需要完成以下准备工作:

  1. 选择云服务商:AWS、Google Cloud、Azure、阿里云等
  2. 配置服务器:建议至少1GB内存,2核CPU
  3. 安装Docker:在服务器上安装Docker和Docker Compose

服务器初始化脚本:

bash 复制代码
#!/bin/bash
# install-docker.sh

# 更新系统
apt-get update
apt-get upgrade -y

# 安装Docker
curl -fsSL https://get.docker.com -o get-docker.sh
sh get-docker.sh

# 添加用户到docker组
usermod -aG docker $USER

# 安装Docker Compose
curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
chmod +x /usr/local/bin/docker-compose

# 启用防火墙
ufw allow OpenSSH
ufw allow 80
ufw allow 443
ufw --force enable

8.2 部署流程

手动部署步骤:

bash 复制代码
# 1. 登录服务器
ssh user@server-ip

# 2. 创建项目目录
mkdir -p /opt/myapp
cd /opt/myapp

# 3. 上传代码(可以使用git、rsync等)
scp -r . user@server-ip:/opt/myapp

# 4. 设置环境变量
cp .env.production .env
# 编辑.env文件,填入实际值

# 5. 构建和启动服务
docker-compose -f docker-compose.prod.yml build
docker-compose -f docker-compose.prod.yml up -d

# 6. 检查服务状态
docker-compose ps
docker-compose logs web

8.3 使用CI/CD自动化部署

GitLab CI示例(.gitlab-ci.yml):

yaml 复制代码
stages:
  - test
  - deploy

variables:
  DOCKER_HOST: tcp://docker:2375
  DOCKER_DRIVER: overlay2

test:
  stage: test
  image: python:3.10
  before_script:
    - pip install flake8 pytest
  script:
    - flake8 .
    - python -m pytest

deploy:
  stage: deploy
  image: docker:latest
  services:
    - docker:dind
  before_script:
    - apk add --no-cache docker-compose
  script:
    - docker-compose -f docker-compose.prod.yml build
    - docker-compose -f docker-compose.prod.yml down
    - docker-compose -f docker-compose.prod.yml up -d
  only:
    - main

9 常见问题与解决方案

9.1 部署常见错误

  1. 数据库连接错误

    • 问题:应用无法连接数据库
    • 解决:检查数据库服务是否运行,环境变量配置是否正确,网络连接是否通畅
  2. 静态文件404错误

    • 问题:CSS、JS等静态文件无法加载
    • 解决:确保执行了collectstatic,Nginx配置正确指向静态文件目录
  3. 权限错误

    • 问题:容器内应用没有文件写入权限
    • 解决:确保使用非root用户运行应用,挂载卷权限正确

9.2 性能优化建议

  1. 数据库优化

    yaml 复制代码
    # docker-compose.yml中对PostgreSQL的优化配置
    db:
      image: postgres:13
      environment:
        - POSTGRES_DB=mydb
        - shared_buffers=256MB
        - effective_cache_size=1GB
        - work_mem=16MB
      command: >
        postgres -c shared_buffers=256MB
                 -c effective_cache_size=1GB
                 -c work_mem=16MB
  2. Gunicorn优化

    python 复制代码
    # gunicorn.conf.py
    workers = (2 * cpu_count()) + 1
    worker_class = 'gevent'
    worker_connections = 1000
    max_requests = 1000
    max_requests_jitter = 100
    timeout = 30
    keepalive = 2
  3. Nginx优化

    nginx 复制代码
    # nginx.conf中的优化配置
    worker_processes auto;
    worker_connections 1024;
    
    gzip on;
    gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
    
    # 静态文件缓存
    location ~* \.(jpg|jpeg|png|gif|ico|css|js)$ {
        expires 365d;
        add_header Cache-Control "public, immutable";
    }

10 总结

通过本文的详细介绍,我们学习了如何将Django和Flask应用使用Docker容器化并部署到云服务器。容器化部署不仅解决了环境一致性问题,还大大简化了部署流程。

10.1 关键要点回顾

  1. Dockerfile是基础:正确编写Dockerfile是成功容器化的第一步
  2. 多服务编排:使用Docker Compose管理应用依赖的多个服务
  3. 生产就绪配置:使用Gunicorn作为WSGI服务器,Nginx作为反向代理
  4. 环境变量管理:敏感信息通过环境变量传递,提高安全性
  5. 持续部署:结合CI/CD工具实现自动化部署

10.2 后续学习方向

要进一步深入Docker和部署领域,可以学习:

  • Kubernetes容器编排
  • 微服务架构设计
  • 监控和日志管理(Prometheus、ELK Stack)
  • 服务网格(Istio、Linkerd)
  • 云原生技术

容器化技术正在成为现代应用部署的标准,掌握这些技能将大大提升你的开发和运维能力。

相关推荐
没有口袋啦3 小时前
K8s集群多节点部署(Ubuntu22.04)
docker·云原生·容器·kubernetes
荣光波比3 小时前
K8S(三)—— 基于kubeadm 1.20版本部署Kubernetes集群与Harbor私有仓库实战
云原生·容器·kubernetes
IvanCodes3 小时前
七、Docker核心技术:深入理解网络模式 (Bridge, Host, None, Container)
网络·docker·容器
荣光波比4 小时前
K8S(二)—— K8S 1.28 集群部署指南(kubeadm 方式)
云原生·容器·kubernetes
峰顶听歌的鲸鱼4 小时前
38.Shell脚本编程2
linux·运维·服务器·笔记·学习方法
问道飞鱼5 小时前
【Kubernets进阶】Kubernetes VPA (Vertical Pod Autoscaler) 详解与配置指南
云原生·容器·kubernetes·vpa
Cx330❀5 小时前
《Linux基础入门指令》:从零开始理解Linux系统
linux·运维·服务器·经验分享
HaSaKing_7215 小时前
二三级等保检测对比项
linux·服务器·网络
error:(5 小时前
【Linux命令从入门到精通系列指南】export 命令详解:环境变量管理的核心利器
linux·运维·服务器