Skip to content

相关命令

bash
# 登录各个容器内部
docker exec -it order_mysql mysql -uzhangkai -p123
docker exec -it orderapi /bin/bash
docker exec -it order_redis redis-cli
docker exec -it order_nginx /bin/bash

# 编译
docker-compose build


# 当你重新编译MySQL容器的时候,需要清空宿主机的数据目录
rm -rf /data/myorder/data/mysql/*

# 启动/关闭
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml up -d
docker-compose -f docker-compose.yml up -d  && docker-compose logs -f --tail 20
docker-compose -f docker-compose.yml down
# 删除当前目录下的所有的容器并重新创建,--remove-orphans强制删除之前遗留(rm删不掉的)的容器
docker-compose -f docker-compose.yml up -d  --force-recreate --remove-orphans

# up之后,监控所有容器的日志
docker-compose logs -f --tail 20
# 查看指定容器的日志
docker-compose logs --tail 20 [容器名称]

整体目录结构

服务器上的整体项目部署目录架构

bash
/data/myorder:  		# django项目和配套文件等都在这个目录下面

MySQL容器

初始化数据准备

首先在本地导出MySQL数据:

bash
mysqldump -uroot -p123 --default-character-set=utf8 -B day06 >day06.sql

然后你自行把它上传到initdb目录内:

bash
[root@cs myorder]# pwd
/data/myorder
[root@cs myorder]# ls initdb/
day06.sql

编写mysql的docker-compose

bash
[root@cs myorder]# pwd
/data/myorder
[root@cs myorder]# vim docker-compose.yml

version: '3.8'
services:
  order_mysql:
    image: mysql:8.0.30
    restart: always
    container_name: "order_mysql"
    networks:
      - default
    environment:
      - "MYSQL_ROOT_PASSWORD=123"
      - "MYSQL_USER=zhangkai"
      - "MYSQL_PASSWORD=123"
      - TZ=Asia/Shanghai
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
    ports:
      - 3307:3306
    volumes:
      - ./data/mysql:/var/lib/mysql
      - ./logs/mysql:/logs
      - ./initdb:/docker-entrypoint-initdb.d/

注释版:

bash
version: '3.8'					# 目前我们使用的基本都是Version3版本,最新版本是3.9
services:						# 声明接下来开始配置服务容器
  order_mysql:					# 服务名,开发者自定义的
    # image 当前服务容器的基本依赖镜像,如果本地没有该镜像,则会自动从官网pull拉取
    # image 也可以是自己本地基于Dockerfile编译后产生的定制镜像,但是必须是已经build编译好的
    # 如希望在docker-compose up启动容器服务时自动编译Dockerfile,则必须增加配置项build指定Dockerfile
    # 文件的所在路径,如果不指定,则可能出现从官网拉取镜像失败的情况,build配置项写法如下:
    # build: .
    # 如使用了build配置项时还声明了image配置项,则基于build所在的Dockerfile编译的镜像名为image指定名字。
    # build: .
    # image: mysql:8.0.30
    image: mysql:8.0.30		
    restart: always				   # 重启
    container_name: "order_mysql"  # 指定当前服务容器启动以后的容器名
    networks:
      - default						# 使用默认网卡
    environment:
      - "MYSQL_ROOT_PASSWORD=123"   # 指定root账号密码,请不要外泄
      - "MYSQL_USER=zhangkai"       # 普通账号
      - "MYSQL_PASSWORD=123"		# 普通账号的密码
      - "TZ=Asia/Shanghai"          # 更改时区
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
    ports:
      - 3307:3306      # 映射外部端口3307(你也可以指定3306)到容器内部的端口为3306
    volumes:
      - ./data/mysql:/var/lib/mysql # 将容器内部的mysql数据目录映射到宿主机的./data/mysql目录下
      - ./logs/mysql:/logs
      # 将需要初始化的数据,放到initdb目录内,MySQL容器在第一次编译时,它会自动将/docker-entrypoint-initdb.d/目录内
      # 的SQL文件进行初始化,注意, 只有在容器第一次编译时才读一次,后续每次up不会再做这个操作,
      # 除非你删除容器并清空./data/mysql之后,再尝试编译一个新容器,这样就再次初始化数据了
      - ./initdb:/docker-entrypoint-initdb.d/

进行up吧,你也可以进入容器内部,观察数据是否初始化成功。

bash
[root@cs myorder]# pwd
/data/myorder
[root@cs myorder]# docker-compose -f docker-compose.yml up -d
[root@cs myorder]# docker exec -it order_mysql mysql -uzhangkai -p123
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 8.0.30 MySQL Community Server - GPL

Copyright (c) 2000, 2022, Oracle and/or its affiliates.

Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

mysql> use day06;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-----------------------+
| Tables_in_day06       |
+-----------------------+
| django_migrations     |
| web_administrator     |
| web_customer          |
| web_level             |
| web_order             |
| web_pricepolicy       |
| web_transactionrecord |
+-----------------------+
7 rows in set (0.01 sec)

mysql> select * from web_level;
+----+--------+-------+---------+
| id | active | title | percent |
+----+--------+-------+---------+
|  1 |      1 | vip1  |       8 |
|  2 |      1 | vip2  |       9 |
|  3 |      1 | vip3  |       7 |
|  4 |      1 | vip5  |       3 |
+----+--------+-------+---------+
4 rows in set (0.00 sec)

mysql> exit;
Bye

我想要的数据已经初始化好了。

OK,MySQL没问题了。

redis容器

在宿主机上搞好redis的配置文件:

bash
[root@cs myorder]# pwd
/data/myorder
[root@cs myorder]# cat conf/redis/master.conf 
requirepass 123456
masterauth 123456
bind 0.0.0.0

# RDB快照
save 900 1
save 300 10
save 60 10000
dir "/data"
rdbcompression yes
dbfilename dump.rdb

# AOF日志
appendonly yes
appendfilename "appendonly.aof"
appendfsync everysec

编写docker-compose.yml文件关于redis容器的配置:

yaml
[root@cs myorder]# cat docker-compose.yml 
version: '3.8'
services:
  order_mysql:
    image: mysql:8.0.30
    restart: always
    container_name: "order_mysql"
    networks:
      - default
    environment:
      - "MYSQL_ROOT_PASSWORD=123"
      - "MYSQL_USER=zhangkai"
      - "MYSQL_PASSWORD=123"
      - TZ=Asia/Shanghai
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
    ports:
      - 3307:3306
    volumes:
      - ./data/mysql:/var/lib/mysql
      - ./logs/mysql:/logs
      - ./initdb:/docker-entrypoint-initdb.d/
  order_redis:
    image: redis:6.2.6-alpine3.15
    restart: always
    networks:
      - default
    ports:
      - 6400:6379   # 我的宿主机端口,也改成了6400,你们可以用6379的
    command:
      ["redis-server", "/usr/local/etc/redis/redis.conf"]
    container_name: "order_redis"
    volumes:
      # 用宿主机的配置替换到容器内部的redis配置
      - ./conf/redis/master.conf:/usr/local/etc/redis/redis.conf
      # redis的持久化文件将会映射到宿主机的./data/redis目录内
      - ./data/redis:/data

重新up:

bash

[root@cs myorder]# docker-compose -f docker-compose.yml down
[root@cs myorder]# docker-compose -f docker-compose.yml up -d
[root@cs myorder]# docker exec -it order_redis redis-cli 
127.0.0.1:6379> ping
(error) NOAUTH Authentication required.
127.0.0.1:6379> auth 123456
OK
127.0.0.1:6379> ping
PONG
127.0.0.1:6379> exit
[root@cs myorder]#

没得问题!

Python容器

这里我采用Ubuntu20.04+miniconda的形式构建Dockerfile

miniconda下载

首先Python解释器的我采用Python3.10版本。

所以,你可以去清华源先下载Linux用的miniconda。

下载地址:

1832669575641038848.png

你可以手动下载到本地,然后拷贝到服务器的myorder目录的script目录内。

bash
# 你也可以wget下载,没有wget的,就yum install -y wget
[root@cs myorder]# cd script/ && wget https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py310_23.5.2-0-Linux-x86_64.sh && cd ..
[root@cs myorder]# ls script/
Miniconda3-py311_23.5.2-0-Linux-x86_64.sh

Dockerfile文件编写

dockerfile
[root@cs myorder]# cat Dockerfile

FROM python:3.10

ENV PYTHONUNBUFFERED=1

ADD . /myorder
WORKDIR /myorder/day06

RUN apt-get update 
    
RUN pip config set global.index-url "https://pypi.tuna.tsinghua.edu.cn/simple" \
    && pip install --upgrade pip \    
    && pip install uwsgi==2.0.22 \ 
    && pip install -r requirements.txt"

#EXPOSE 8700

稍微解释下:

dockerfile
# 镜像是基于debinn系统构建的
FROM python:3.10

# 设置环境变量,PYTHONUNBUFFERED设置python解析器在终端下stdout错误信息,一次性输出,要不在暂存区积攒
ENV PYTHONUNBUFFERED=1

# 将myorder目录内的所有东西都映射到容器的/myorder目录中
ADD . /myorder

# 设置进入容器的默认工作目录
WORKDIR /myorder/day06

# 更新下apt-get,由于是云服务器,这里没有更换国内源
RUN apt-get update 

# 配置国内源,升级pip,下载uwsgi和其他项目依赖模块
RUN pip config set global.index-url "https://pypi.tuna.tsinghua.edu.cn/simple" \
    && pip install --upgrade pip \    
    && pip install uwsgi==2.0.22 \ 
    && pip install -r requirements.txt"

#EXPOSE 8700

docker-compose

yaml
version: '3.8'
services:
  order_mysql:
    image: mysql:8.0.30
    restart: always
    container_name: "order_mysql"
    networks:
      - default
    environment:
      - "MYSQL_ROOT_PASSWORD=123"
      - "MYSQL_USER=zhangkai"
      - "MYSQL_PASSWORD=123"
      - TZ=Asia/Shanghai
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
    ports:
      - 3307:3306
    volumes:
      - ./data/mysql:/var/lib/mysql
      - ./logs/mysql:/logs
      - ./initdb:/docker-entrypoint-initdb.d/
  order_redis:
    image: redis:6.2.6-alpine3.15
    restart: always
    networks:
      - default
    ports:
      - 6400:6379
    command:
      ["redis-server", "/usr/local/etc/redis/redis.conf"]
    container_name: "order_redis"
    volumes:
      - ./conf/redis/master.conf:/usr/local/etc/redis/redis.conf
      - ./data/redis/master:/data

  orderapi:
    build: .
    image: orderapi:1.0.0
    container_name: orderapi
    restart: on-failure
    ports:
      - 8700:8000
      - 8800:8800
    networks:
      - default
    volumes:
      - ./script:/myorder/script
      - ./day06:/myorder/day06
      - ./logs/uwsgi:/myorder/logs/
    command:
      ["sh", "/myorder/script/run.sh"]
    environment:
      - "DJANGO_SETTINGS_MODULE=day06.settings"        
    depends_on:
      - order_mysql
      - order_redis

解释:

bash
  orderapi:
    build: .    # 根据当前目录下的Dockerfile文件构建容器
    image: orderapi:1.0.0      # 构建出来的镜像版本
    container_name: orderapi   # 容器命
    restart: always
    ports:
      - 8700:8000
    networks:
      - default
    volumes:
      - ./script:/myorder/script
      - ./day06:/myorder/day06
      - ./logs/uwsgi:/myorder/logs/
    command:
      ["sh", "/myorder/script/run.sh"]
    environment:
      - "DJANGO_SETTINGS_MODULE=day06.settings"        
    depends_on:
      - order_mysql
      - order_redis

启动脚本编写

为了后续的方便,我们选择将一些命令都写在一个sh文件中,我们调整也方便。

sh
[root@cs myorder]# pwd
/data/myorder
[root@cs myorder]# cat script/run.sh 

# 需要执行数据库迁移,就把下面两行取消注释
#python3 manage.py makemigrations
#python3 manage.py migrate

# 收集静态文件
python3 manage.py collectstatic
#python3 manage.py runserver 0.0.0.0:8800

# 将来,在容器中,选择以命令行的形式运行uwsgi
sh -c "uwsgi  --http :8000 --workers 4 --master --enable-threads --wsgi-file day06/wsgi.py --home /root/miniconda3 --chdir /myorder/day06 --buffer-size 32768 --pidfile /myorder/day06/uwsgi.pid  --vacuum"

# --http :8000					# 外部通过8700端口访问uwsgi的8000端口
# --wsgi-file day06/wsgi.py     # wsgi文件
# --home /root/miniconda3       # 解释器家目录
# --chdir /myorder/day06		# 容器内的项目根目录
# --pidfile /myorder/day06/uwsgi.pid   # pid文件

Django项目上传

将本地的项目上传到云服务器中,并调整配置文件。

python
import os
import sys
from pathlib import Path

BASE_DIR = Path(__file__).resolve().parent.parent
# 手动再次添加项目根目录到sys.path中
sys.path.insert(0, BASE_DIR)   

# debug改为False
DEBUG = False
ALLOWED_HOSTS = ["127.0.0.1"]


# MySQL数据库配置
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'day06',  # 数据库名字
        'USER': 'zhangkai',
        'PASSWORD': '123',
        'HOST': '你的服务器IP地址',  # ip
        'PORT': 3307,
    }
}

# redis缓存配置
CACHES = {
    "default": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://:@云服务器的ip:6400/2",  # 注意端口是6400
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD": "123456",
            "CONNECTION_POOL_KWARGS": {
                "max_connections": 100,
            },
        }
    },
    # 提供存储短信验证码
    "sms_code": {
        "BACKEND": "django_redis.cache.RedisCache",
        "LOCATION": "redis://:@云服务器的ip:6400/2",  # 注意端口是6400
        "OPTIONS": {
            "CLIENT_CLASS": "django_redis.client.DefaultClient",
            "PASSWORD": "123456",
        }
    }
}

# 静态文件配置
STATIC_URL = '/static/'
# 必须指定这个配置,因为后续要进行NGINX的静态文件代理
STATIC_ROOT = os.path.join(BASE_DIR, 'allstatic')

别忘了在项目根目录下生成requirements.txt文件:

bash
pip freeze > requirements.txt

完事把项目上传到服务器的myorder目录内。

测试uwsgi是否工作正常

直接up:

bash

NGINX容器

NGINX和uwsgi通信是基于socket的,所以,我们需要先修改run.sh这个启动脚本:

bash
sh -c "uwsgi  --socket :8000 --workers 4 --master --enable-threads --wsgi-file day06/wsgi.py --home /root/miniconda3 --chdir /myorder/day06 --buffer-size 32768 --pidfile /myorder/day06/uwsgi.pid  --vacuum"

# 将原来的--http 调整为 --socket

然后,配置nginx的配置文件:

bash
[root@cs myorder]# cat conf/nginx/web.conf 

upstream orderapi {
    server 你的服务器IP:8700;
}
server {
	# 浏览器访问你的云服务器的ip和8100端口,将被容器内的80端口接收,转发给uwsgi的8700端口
	# 最终被uwsgi那个容器内的8000端口uwsgi处理请求并返回动态网页
    listen  80;
    server_name api.neeo.cc;

    location / {
        include uwsgi_params;
        uwsgi_pass orderapi;
    }
	# nginx代理静态文件
    location /static {
        alias /allstatic/;
    }
}

配置nginx的docker-compose配置:

bash
version: '3.8'
services:
  order_nginx:
    image: nginx:1.21.4
    restart: always
    ports:
      # 我的外部80端口被占用,所以更改为8100
      - 8100:80
    container_name: order_nginx
    volumes:
      - ./day06/allstatic:/allstatic
      - ./conf/nginx/web.conf:/etc/nginx/conf.d/default.conf
    networks:
      - default
  order_mysql:
    image: mysql:8.0.30
    restart: always
    container_name: "order_mysql"
    networks:
      - default
    environment:
      - "MYSQL_ROOT_PASSWORD=123"
      - "MYSQL_USER=zhangkai"
      - "MYSQL_PASSWORD=123"
      - TZ=Asia/Shanghai
    command:
      --default-authentication-plugin=mysql_native_password
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
    ports:
      - 3307:3306
    volumes:
      - ./data/mysql:/var/lib/mysql
      - ./logs/mysql:/logs
      - ./initdb:/docker-entrypoint-initdb.d/
  order_redis:
    image: redis:6.2.6-alpine3.15
    restart: always
    networks:
      - default
    ports:
      - 6400:6379
    command:
      ["redis-server", "/usr/local/etc/redis/redis.conf"]
    container_name: "order_redis"
    volumes:
      - ./conf/redis/master.conf:/usr/local/etc/redis/redis.conf
      - ./data/redis/master:/data

  orderapi:
    build: .
    image: orderapi:1.0.0
    container_name: orderapi
    restart: always
    ports:
      - 8700:8000
      - 8800:8800
    networks:
      - default
    volumes:
      - ./script:/myorder/script
      - ./day06:/myorder/day06
      - ./logs/uwsgi:/myorder/logs/
    command:
      ["sh", "/myorder/script/run.sh"]
    environment:
      - "DJANGO_SETTINGS_MODULE=day06.settings"        
    depends_on:
      - order_mysql
      - order_redis
networks:
  default:

你可以最终在up一下,然后浏览器访问云服务的IP地址:8100会自动的跳转到登录页面,然后你选择管理员,输入用户root,密码是123就能登录到网站内部。

常见问题

Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding

当用uwsgi启动Django项目的时候,报错如错误:

bash
(envs) root@b6730c182101:/myorder# uwsgi --ini /myorder/script/uwsgi.ini 
[uWSGI] getting INI configuration from /myorder/script/uwsgi.ini
*** Starting uWSGI 2.0.22 (64bit) on [Tue Aug 15 11:56:30 2023] ***
compiled with version: 12.2.0 on 15 August 2023 11:43:12
os: Linux-3.10.0-1160.88.1.el7.x86_64 #1 SMP Tue Mar 7 15:41:52 UTC 2023
nodename: b6730c182101
machine: x86_64
clock source: unix
detected number of CPU cores: 2
current working directory: /myorder
detected binary path: /myorder/envs/bin/uwsgi
!!! no internal routing support, rebuild with pcre support !!!
uWSGI running as root, you can use --uid/--gid/--chroot options
*** WARNING: you are running uWSGI as root !!! (use the --uid flag) *** 
chdir() to /myorder/day06
your memory page size is 4096 bytes
detected max file descriptor number: 1048576
lock engine: pthread robust mutexes
thunder lock: disabled (you can enable it with --thunder-lock)
uWSGI http bound on 0.0.0.0:8700 fd 4
uwsgi socket 0 bound to TCP address 127.0.0.1:33422 (port auto-assigned) fd 3
uWSGI running as root, you can use --uid/--gid/--chroot options
*** WARNING: you are running uWSGI as root !!! (use the --uid flag) *** 
Python version: 3.10.12 (main, Jul 28 2023, 05:35:08) [GCC 12.2.0]
!!! Python Home is not a directory: /myorder/envs/bin/uwsgi !!!
Set PythonHome to /myorder/envs/bin/uwsgi
Python path configuration:
  PYTHONHOME = '/myorder/envs/bin/uwsgi'
  PYTHONPATH = (not set)
  program name = '/myorder/envs/bin/uwsgi/bin/python'
  isolated = 0
  environment = 1
  user site = 1
  import site = 1
  sys._base_executable = '/myorder/envs/bin/uwsgi/bin/python'
  sys.base_prefix = '/myorder/envs/bin/uwsgi'
  sys.base_exec_prefix = '/myorder/envs/bin/uwsgi'
  sys.platlibdir = 'lib'
  sys.executable = '/myorder/envs/bin/uwsgi/bin/python'
  sys.prefix = '/myorder/envs/bin/uwsgi'
  sys.exec_prefix = '/myorder/envs/bin/uwsgi'
  sys.path = [
    '/myorder/envs/bin/uwsgi/lib/python310.zip',
    '/myorder/envs/bin/uwsgi/lib/python3.10',
    '/myorder/envs/bin/uwsgi/lib/python3.10/lib-dynload',
  ]
Fatal Python error: init_fs_encoding: failed to get the Python codec of the filesystem encoding
Python runtime state: core initialized
ModuleNotFoundError: No module named 'encodings'

Current thread 0x00007f3702f997c0 (most recent call first):
  <no Python frame>

解决方案,就是在你的Django的settings.py文件中,将项目根目录添加到sys.path中。

python
# 别忘了导包
import os
import sys
from pathlib import Path

# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# 就是下面这一行
sys.path.insert(0, BASE_DIR)

invalid request block size: 21573 (max 4096)...skip

ubuntu20.04云服务器

如果你是以uwsgi.ini文件启动的话报这个错误,那么你可以在uwsgi.ini文件中添加:

ini
[uwsgi]

buffer-size=65535

如果是命令行启动的:

# 在下面的命令中添加 -b 参数
# before
uwsgi --socket :8000 --workers 4 --master --enable-threads --module luffycityapi.wsgi

# after
uwsgi --socket :8000 --workers 4 --master --enable-threads -b 65535 --module luffycityapi.wsgi

nginx配置文件挂载失败

docker-compose中,指定volumes挂载,将本地的nginx的web.conf配置文件,挂载到容器中的nginx配置文件中,但是访问仍然是nginx的欢迎页面,说明我们挂载的配置文件没有生效。

bash
version: '3.8'
services:
  nginx:
    image: nginx:1.21.4
    restart: always
    ports:
      - "80:80"
    container_name: "nginx"
    volumes:
        - ./luffycityweb:/home/luffycityweb
        - /home/luffycity/conf/nginx/web.conf:/etc/nginx/conf.d/nginx.conf
    networks:
        - default
networks:
  default:

上面这么配置不生效,容器中不生效,进入容器发现,有两个配置文件,而default.conf配置文件也监听了80端口,导致我们外部访问80端口,没有按照预期走我们期望的nginx.conf,而是走了默认的default.conf,它也监听了80端口,返回了nginx欢迎页,这就是我们网站访问80端口为啥出现nginx欢迎页的原因。

bash
root@c7406994da7b:/home# ls /etc/nginx/conf.d/
default.conf  nginx.conf

解决办法也很简单,容器中的default.conf配置文件不是优先级高么,我干脆挂载时,让宿主机的web.conf配置文件覆盖掉容器中的default.conf配置文件,就ok了。

bash
version: '3.8'
services:
  nginx:
    image: nginx:1.21.4
    restart: always
    ports:
      - "80:80"
    container_name: "nginx"
    volumes:
        - ./luffycityweb:/home/luffycityweb
        - /home/luffycity/conf/nginx/web.conf:/etc/nginx/conf.d/default.conf
    networks:
        - default
networks:
  default:

然后重新启动容器,就ok了。 ��