相关命令
# 登录各个容器内部
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 [容器名称]
整体目录结构
服务器上的整体项目部署目录架构
/data/myorder: # django项目和配套文件等都在这个目录下面
MySQL容器
初始化数据准备
首先在本地导出MySQL数据:
mysqldump -uroot -p123 --default-character-set=utf8 -B day06 >day06.sql
然后你自行把它上传到initdb
目录内:
[root@cs myorder]# pwd
/data/myorder
[root@cs myorder]# ls initdb/
day06.sql
编写mysql的docker-compose
[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/
注释版:
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吧,你也可以进入容器内部,观察数据是否初始化成功。
[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的配置文件:
[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容器的配置:
[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:
[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。
下载地址:
- 总地址:https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/
- 我选择的:https://mirrors.tuna.tsinghua.edu.cn/anaconda/miniconda/Miniconda3-py310_23.5.2-0-Linux-x86_64.sh
你可以手动下载到本地,然后拷贝到服务器的myorder目录的script目录内。
# 你也可以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文件编写
[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
稍微解释下:
# 镜像是基于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
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
解释:
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文件中,我们调整也方便。
[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项目上传
将本地的项目上传到云服务器中,并调整配置文件。
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
文件:
pip freeze > requirements.txt
完事把项目上传到服务器的myorder目录内。
测试uwsgi是否工作正常
直接up:
NGINX容器
NGINX和uwsgi通信是基于socket的,所以,我们需要先修改run.sh这个启动脚本:
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的配置文件:
[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配置:
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项目的时候,报错如错误:
(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中。
# 别忘了导包
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
文件中添加:
[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的欢迎页面,说明我们挂载的配置文件没有生效。
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欢迎页的原因。
root@c7406994da7b:/home# ls /etc/nginx/conf.d/
default.conf nginx.conf
解决办法也很简单,容器中的default.conf
配置文件不是优先级高么,我干脆挂载时,让宿主机的web.conf
配置文件覆盖掉容器中的default.conf
配置文件,就ok了。
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了。 ��