路飞学城八:项目部署
八:项目部署
项目部署的相关概念
关于企业项目上线的部署流程,主要包含以下3个方面:
- 落实并调整部署方案[1.0/2.0/3.0/]
- 确认与完善部署环境
- 确定部署架构与服务器节点
部署方案
1. 分析项目的产品需求文档,定好部署方案的方向
2. 分析项目开发文档,按照功能边界,设计部署的结点
3. 分析项目功能软件,合理的取舍,选符合当前业务场景的
4. 梳理项目部署涉及到的部署软件实现方案,根据上面第2点确定的结点,确定初版部署方案
5. 根据项目实际情况,调整优化并确定项目部署方案【短期内的持续方案】
部署环境
个人开发环境
工作人员:自己
工作平台:个人笔记本、公司配的电脑
平台特点:根据公司的项目要求,环境是自己配的,团队中不同的个人开发环境可以不一样
工作内容:项目的子模块,子功能的开发和维护
完成标准:完成领导/leeder安排的内容[项目的功能子模块开发]
公司内部服务器环境
工作人员:开发团队
工作平台:公司内部服务器
平台特点:服务器环境和线上的服务器环境完全一致
工作内容:项目子模块间的功能联调
完成标准:项目阶段开发、调试完成
项目测试环境
工作人员:测试团队
工作平台:公司内部服务器
平台特点:服务器环境和线上的服务器环境完全一致
工作内容:项目功能/非功能/探索等测试
完成标准:项目阶段功能正常运行
项目预发布环境
工作人员:运维团队
工作平台:公司线上服务器组中的一台
平台特点:服务器环境和线上的服务器环境完全一致
工作内容:特殊功能测试(比如支付)、数据压力测试、其他安全测试等
完成标准:项目阶段功能正常运行,最后一道防线
项目生产环境
工作人员:运维团队
工作平台:公司线上服务器组
平台特点:标准线上的服务器环境
工作内容:代码部署和维护、记录内部架构文档
完成标准:项目正常运行
线上项目的基本架构
单点部署:容灾性低,不够健壮。
分布式集群架构:可用性高,容灾性强,费用不低,有较高的维护成本。
服务器相关
服务器: 根据节点来衡量,计算需要多少台服务器才能承受指定的QPS【每秒请求数】访问压力。
服务器类型:物理服务器,云服务器
并发瓶颈:
短板原理[水桶原理]
1. 带宽:20M/40kb
2. 处理并发的能力[web服务器], nginx 客户端静态资源代理-> 4C4M上3-4W,Uwsgi/Gunicorn/Uvincorn -> 3000-5000
3. 数据库查询能力:
mysql 默认最大连接数151个,最高可以达到16384个,处理QPS的能力,默认大概1000次/s 。
show variables like '%max_connections%';
redis 默认是1W的连接数,最高可以配置到10W,并发处理能力与连接数有关,默认1W连接的话,5W/s。
CONFIG GET maxclients
4. 物理服务器本身的承载能力[8核16G起步...] 【腾讯云、华为云、阿里云、网易云、百度云等等,找新手礼包】
当前项目要完整的跑起来,务必购买的服务器配置:2C4G2M
可以使用apache ab工具进行压测,根据短板原理得到系统的并发处理数的乐观数据。
# 安装命令:
# sudo apt install apache2-utils
# 基本使用命令
ab -n 1000 -c 100 -w https://www.luffycity.com/ >> ~/ab.html # SSL not compiled in; no https support
ab -n 1000 -c 100 -w http://www.luffycity.com/ >> ./ab.html
# -n 表示本次压测的总请求执行数
# -c 表示本次压测的并发数
# -w 表示以HTML格式输出压测结果
# ab命令会创建多个并发访问线程,模拟多个访问者同时对某一URL地址进行访问。它的测试目标是基于URL的,因此,它既可以用来测试apache的负载压力,也可以测试nginx、lighthttp、tomcat、IIS等其它Web服务器的压力。
域名相关
域名要正常使用必须要绑定服务器以后,进行ICP(工信部)备案和公安部备案,否则会出现无法通过域名访问站点的情况。
服务器与域名尽量在同一家公司购买,另外服务器要备份务必要三个月以上。
热门的域名商:.com
,.cn
,.net
,.org
、com.cn
。
域名在工信部备案的周期:首次备案15个工作日,再次备案10个工作日。
网站在公安部备案的周期:2个工作日。
域名等级 | 例子 |
---|---|
顶级域名 | luffycity.com ,sina.com.cn ,baidu.com |
二级域名 | www.luffycity.com ,study.luffycity.com ,www.sina.com.cn ,www.baidu.com |
三级域名 | abc.www.luffycity.com , |
四级域名 | pic.zhuxian.tiebai.baidu.com |
域名影射
114.115.200.1 api.luffycity.dabanyu.com api服务端
114.115.200.1 www.luffycity.dabanyu.com web客户端
SSH
ssh( Secure Shell,安全外壳协议),是一种可靠的,专为远程登录会话和其他网络服务提供安全性的协议。最初是UNIX系统上的一个程序,后来又迅速扩展到其他操作平台。简而言之,就是说白了,就是一种网络协议,用于计算机之间的加密登录。当然基于ssh协议实现的程序有商业收费的,也有社区免费的。我们可以在windows下安装putty、xshell来帮助我们远程登录,也可以ssh命令来操作。
SSH的默认端口是22,在实际工作中,有些运维人员在安装配置ssh服务端时为了增强隐蔽性会更改端口。
# 语法:
ssh 用户名@主机地址 # 有时为安全考虑,运维人员会修改ssh端口,则客户端需要指定端口:ssh -p 端口 用户名@主机地址
# 退出ssh远程登录
exit
注意,首次使用ssh登录到远程主机,需要手动输入yes记录主机的指纹信息,如下图所示(windows系统终端效果):
基于ssh实现免密登录
所谓的免密登录就是基于rsa加密算法生成一对成对的秘钥对。然后当前主机保管私钥,把公钥上传到远程主机,这样以后登陆时仅需要验证公私钥即可,不再需要密码登陆了。
# 第一步:生成秘钥对。私钥和公钥成对才能匹配成功,秘钥文件默认保存在~/.ssh/目录下。
# 私钥文件名:id_rsa
# 公钥文件名:id_rsa.pub
ssh-keygen # 连续三下回车
# 第二步: 将公钥文件信息上传到需要被管理的主机上,远程服务器中的公钥保存目录就在该服务器的家目录下~/.ssh/authorized_keys
ssh-copy-id root@114.115.200.1
基于ssh实现文件上传下载
scp(secure copy)是一个基于 SSH 协议在网络之间文件进行安全传输的命令。
# 语法:
# scp 【可选参数】 源目录/目标文件 新目录/目标文件
# 可选参数:
# -r:递归复制整个目录【强烈建议尽量是采用压缩包的上传】
# -v:详细方式输出
# -q:不显示传输进度条
# -C:允许压缩
# -6:使用 IPv6 协议
# 上传本地文件到远程地址
# scp 本地目录/本地文件 远程用户名@远程ip:远程目录/ # 不修改文件名,直接上传
# scp 本地目录/本地文件 远程用户名@远程ip:远程目录/远程文件名 # 修改文件名,再上传
# scp -r 本地目录 远程用户名@远程ip:远程目录
scp /home/moluo/Desoktop/luffycity.zip root@114.115.200.1:/home/
# 下载远程文件到本地
# scp 远程用户名@远程ip:远程目录/远程文件名 本地目录/ # 不修改文件名,直接下载
# scp 远程用户名@远程ip:远程目录/远程文件名 本地目录/本地文件 # 修改文件名,再下载
# scp -r 远程用户名@远程ip:远程目录 本地目录
scp -r root@114.115.200.1:/home/abc.html /home/moluo/Desoktop/
Dockerfile镜像制作
docker/podman中, 镜像是容器的基础,每次执行docker run的时候都会指定哪个基本镜像作为容器运行的基础。我们之前的docker的操作都是使用来自dockerhub提供的官方镜像,直接使用这些镜像只能满足一定的基本需求,当基础镜像无法满足我们的业务需求时,就得使用Dockerfile自己定制这些镜像了。
Dockerfile是提供开发者用于定制自定义镜像的配置文件。所以我们需要掌握Dockerfile文件的基本语法。
镜像的定制就类似小时候学画画的水彩画一样,水彩画是一层一层的涂抹上去的,而镜像的定制则是编写定制每一层所添加的配置、文件等命令信息。如果可以把每一层修改、安装、构建、操作的命令都写入到一个脚本,用脚本来构建、定制镜像,这个脚本就是Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条用于自定义镜像的指令(Instruction),这些指令每一条就构建一层,因此每一条指令的内容,就是告诉docker该如何构建每一层的镜像内容。
注意:每一层指令执行结束以后就会隔离打开一层。所以Dockerfile中的命令层级如果越多,则构建生成的镜像就越大,也就越臃肿,所以我们应该在学习完Dockerfile的语法以后,尽量采用最少的指令来定制镜像。
基本指令
FROM参数
一般写在Dockerfile的首行,指定基础镜像,一个Dockerfile中只有一个FROM
FROM elasticsearch # 制作base image(基础镜像),尽量使用官方的image作为base image
FROM centos:8 # 以centos为基础镜像,进行二次构建镜像
FROM ubuntu:20.04 # 带有tag的基础镜像
LABEL参数
等价于MAINTAINER,用于设置当前dockerfile镜像的作者相关信息。
FROM elasticsearch
LABEL version="1.0.0" # 容器元信息,帮助信息,Metadata,类似于代码注释
LABEL maintainer="649641514@qq.com"
RUN参数
RUN参数是一个万能指令,用于指定镜像内部执行系统命令, 对于复杂的RUN命令,避免不必要的分层,多条命令用反斜线换行,或者使用&&
组合成一条命令!
RUN apt update && apt install -y vim \
Python-dev && # 反斜线表示命令没有结束,仅仅换行
/bin/bash -c "source $HOME/.bashrc;echo $HOME"
WORKDIR参数
设置镜像启动以后的容器默认工作目录,相当于linux的cd命令
WORKDIR /ect # 相当于linux的cd命令,改变目录,尽量使用绝对路径!!!不要用RUN cd
WORKDIR /home/demo # 如果没有就自动创建
RUN pwd # 打印结果应该是/home/demo
ADD参数
复制解压,把宿主机的一个文件,添加到容器空间内,相当于 docker的cp
ADD /opt/django/manage.py /opt/ # 把宿主机的/opt/django/manage.py放到容器空间内的/opt/目录下
ADD /opt/python3.8.12.tar.gz /opt/ # ADD的解压文件作用,将宿主机的/opt/下的python3.6.tar.gz解压到容器内的/opt/目录下
COPY参数
拷贝指令,功能与ADD类似,但是没有解压过程,只有单纯复制,也相当于 docker的cp。
将宿主机的文件, 拷贝到容器内,但是没有解压缩的命令,尽量使用COPY,不要使用ADD
COPY /opt/django/manage.py /opt/ # 把宿主机的/opt/django/manage.py放到容器空间内的/opt/目录下
注意:ADD与COPY的区别:
- 如果不是复制压缩包到容器内部,优先使用COPY命令
- ADD除了COPY功能还有解压功能
- 添加远程网络文件/目录使用RUN curl或RUN wget
ENV参数
设置docker容器内部的系统环境变量,相当于 docker -e
ENV MYSQL_VERSION 5.6 # 设置一个mysql常量,这个${MYSQL_VERSION}类似于全局常量
RUN yum install -y mysql-server="${MYSQL_VERSION}" # 如果版本号有变更,则只需要改这个常量就可以了
CMD参数
Dockerfile的结尾运行命令,类似RUN ,但是参数值是一个数组/列表,使用中括号括起来。
CMD ["sh", "run.sh"]
使用 Dockerfile之前,先切换成国内docker镜像源
cd /etc/docker
sudo vim /etc/docker/daemon.json
{
"registry-mirrors" : [
"https://registry.docker-cn.com",
"https://docker.mirrors.ustc.edu.cn",
"http://hub-mirror.c.163.com",
"https://cr.console.aliyun.com/"
]
}
使用Dockerfile封装Django镜像
在/home下建立了docker目录,在这个目录下准备好要构建镜像的文件和源码包。
mkdir /home/docker && cd /home/docker
# 创建镜像制作配置文件,
touch Dockerfile
# 创建一个shell脚本,用于后面启动django项目的。
touch run.sh
# docker官方提供的ubuntu镜像,里面源地址过时了,所以我们提供一个新的到容器内部
touch sources.list
sudo cp /etc/apt/sources.list ./
vim sources.list
sources.list,代码:
deb http://repo.huaweicloud.com/ubuntu/ focal main restricted
deb http://repo.huaweicloud.com/ubuntu/ focal-updates main restricted
deb http://repo.huaweicloud.com/ubuntu/ focal universe
deb http://repo.huaweicloud.com/ubuntu/ focal-updates universe
deb http://repo.huaweicloud.com/ubuntu/ focal multiverse
deb http://repo.huaweicloud.com/ubuntu/ focal-updates multiverse
deb http://repo.huaweicloud.com/ubuntu/ focal-backports main restricted universe multiverse
deb http://repo.huaweicloud.com/ubuntu focal-security main restricted
deb http://repo.huaweicloud.com/ubuntu focal-security universe
deb http://repo.huaweicloud.com/ubuntu focal-security multiverse
下载一个django-3.2.5的源码包
wget -O Django-3.2.5.tar.gz https://gitee.com/mirrors/django/repository/archive/3.2.5?format=tar.gz
编写Dockerfile构建镜像的配置文件
vim Dockerfile
FROM ubuntu:20.04
LABEL version="3.2.5"
LABEL maintainer="649641514@qq.com"
ADD Django-3.2.5.tar.gz /opt/
COPY sources.list /etc/apt/sources.list
RUN apt update && apt install -y python3 python3-pip
WORKDIR /opt/
RUN mv django-3.2.5 django
WORKDIR /opt/django
RUN python3 setup.py install
WORKDIR /opt
RUN django-admin.py startproject djdemo
COPY run.sh /opt/djdemo/run.sh
WORKDIR /opt/djdemo
RUN sed -i "s/ALLOWED_HOSTS = \[\]/ALLOWED_HOSTS = \['\*'\]/g" /opt/djdemo/djdemo/settings.py && chmod 755 run.sh
EXPOSE 8000
CMD ["/bin/sh","run.sh"]
注意:Dockerfile中不能出现命令以外的任何注释。以下是注释版本:
# 指定当前定制镜像的基础镜像以及版本号
FROM ubuntu:20.04
# 指定镜像的描述信息[版本号、作者]
LABEL version="1.0.0"
LABEL maintainer="649641514@qq.com"
# 从镜像外复制并解压到镜像内部
ADD Django-3.2.5.tar.gz /opt/
# 设置当前定制镜像的镜像源
COPY /etc/apt/sources.list /etc/apt/sources.list
# 运行终端命令,安装python3以及相关工具包
RUN apt update && apt install -y python3 python3-pip
# 切换工作目录
WORKDIR /opt/
# 改目录名
RUN mv django-3.2.5 django
# 切换工作目录
WORKDIR /opt/django
# 安装django框架
RUN python3 setup.py install
# 切换工作目录
WORKDIR /opt
# 创建一个django项目
RUN django-admin.py startproject djdemo
# 从镜像外界复制一个run.sh启动脚本到django项目根目录下
COPY run.sh /opt/djdemo/run.sh
# 切换工作目录
WORKDIR /opt/djdemo
# 替换django配置文件settings.py的ALLOWED_HOSTS配置项,允许客户端通过任何地址访问django项目并设置django的启动脚本的权限为755
RUN sed -i "s/ALLOWED_HOSTS = \[\]/ALLOWED_HOSTS = \['\*'\]/g" /opt/djdemo/djdemo/settings.py && chmod 755 run.sh
# 开放镜像的端口8000
EXPOSE 8000
# CMD就是RUN,用于在结尾执行终端命令
CMD ["/bin/sh","run.sh"]
编写run.sh
vim run.sh
python3 manage.py runserver 0.0.0.0:8000
Docker构建镜像
# docker build -t 镜像名:镜像版本 Dockerfile所在路径
sudo docker build -t djdemo:3.2.5 .
docker images
构建完成后,可以看到生成一个新镜像。
此时后台启动镜像,并把容器8000端口映射到物理机的8008端口。
docker run -d -p 8008:8000 --name=django1 djdemo:3.2.5
注意:在云服务器上找到安全组,并在入方向,开放上述的端口,例如:8008。
Docker-Compose容器编排
基本介绍
使用一个Dockerfile模板文件,可以很方便的定义一个适合自己使用的自定义镜像。但在工作中经常会碰到需要多个容器相互配合来完成某项任务或运行某个项目的情况。例如要运行一个django项目,除了django容器本身,往往还需要再加上后端的数据库服务容器,甚至还包括负载均衡容器等,此时我们就需要使用到Docker-Compose了。
注意:
Docker-Compose仅仅用于批量操作docker容器不能用于podman的,对于podman的容器批量操作则需要安装podman-compose来批量操作。
当然,podman-compose和docker-compose的使用和语法是一样的。
Docker-Compose项目是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。Docker-Compose项目由Python编写,调用Docker服务提供的API来对容器进行管理和编排。因此只要所操作的平台支持Docker API,就可以在其上利用Docker-Compose来进行Docker容器的编排和管理。
Docker-Compose将所管理的Docker容器分为三层,分别是工程(project),服务(service)以及容器(container)。
Docker-Compose允许我们开发者通过一个单独的docker-compose.yml配置文件(YAML 格式)来定义一组相关联的docker容器为一个工程(project)。一个工程至少有一个服务,一个服务下至少有一个容器。
Docker-Compose运行指定目录下的所有关联文件组成一个工程(工程名默认为当前目录名)。一个工程当中可包含多个服务,每个服务中可以定义Docker容器运行的镜像,参数,环境依赖等信息。
Docker-Compose的工程配置文件默认为docker-compose.yml,也可以通过-f 参数来指定成其他的配置文件名。
安装与卸载
安装环境查看
uname -a
lsb_release -a # CentOS: yum install redhat-lsb
安装地址:https://docs.docker.com/compose/install/
发布地址:https://github.com/docker/compose/releases
# 下载docker-compose的python二进制执行脚本程序
sudo curl -L "https://github.com/docker/compose/releases/download/v2.10.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 给当前docker-compose脚本程序增加执行权限
sudo chmod +x /usr/local/bin/docker-compose
# 生成一个硬连接,作为全局命令
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
查看安装的版本
docker-compose version
卸载
sudo rm /usr/local/bin/docker-compose
sudo rm /usr/bin/docker-compose
常用命令
基本命令格式
docker-compose [-f <arg>...] [options] [COMMAND] [ARGS...]
命令选项如下
-f --file FILE指定Compose模板文件,默认为当前目录下docker-compose.yml
# -p --project-name NAME 指定项目名称,默认使用当前所在目录为项目名
# --verbose 输出更多调试信息
# -v,-version 打印版本并退出
# --log-level LEVEL 定义日志等级(DEBUG, INFO, WARNING, ERROR, CRITICAL)
docker-compose up
根据容器编排配置文件docker-compose.yml,进行编排和启动容器。相当于docker run
的增强版。
docker-compose up [options] [--scale SERVICE=NUM...] [SERVICE...]
选项包括:
-f 指定compose模板文件名
-d 在系统守护进程的方式批量运行服务容器
# 常用写法:
# docker-compose up # 以占据终端的方式批量启动docker-compose.yml中配置的所有容器
# docker-compose up -d # 以系统守护进程的方式批量启动docker-compose.yml中配置的所有容器,不会占据终端
# docker-compose -f docker-compose.yaml up -d
docker-compose down
停止运行并删除docker-compose.yml配置的容器、网络、卷。相当于 docker stop
与 docker rm
的组合
docker-compose down [options]
选项包括:
-f 指定compose模板文件名
# 常用写法:
# docker-compose down
# docker-compose -f docker-compose.yml down
docker-compose logs
列出当前工程项目中运行容器过程中的运行日志。相当于docker logs
docker-compose logs [options] [SERVICE...]
选项包括:
-f 跟踪日志输出
# 常用写法:
docker-compose logs # 查看整个docker-compose.yml配置中所有的容器的运行日志,不占据终端
docker-compose logs -f # 监控整个docker-compose.yml配置中所有的容器的运行日志,占据终端
docker-compose stop
停止运行docker-compose.yml配置的容器,可以通过docker-compose start 再次启动
docker-compose stop [options] [SERVICE...]
选项包括:
-f 指定compose模板文件名
# 常用写法:
# docker-compose stop
# docker-compose -f docker-compose.yml stop
docker-compose start
启动运行docker-compose.yml配置的容器,可以通过docker-compose stop 关闭运行。
docker-compose start [SERVICE...]
选项包括:
-f 指定compose模板文件名
# 常用写法:
# docker-compose stop
# docker-compose -f docker-compose.yml stop
docker-compose ps
列出当前工程项目中的所有服务容器
docker-compose ps [options] [SERVICE...]
docker-compose bulid
docker-compose build [options] [--build-arg key=val...] [SERVICE...]
构建(重新构建)项目中的服务容器。
选项包括:
–compress 通过gzip压缩构建上下环境
–force-rm 删除构建过程中的临时容器
–no-cache 构建镜像过程中不使用缓存
–pull 始终尝试通过拉取操作来获取更新版本的镜像
-m, –memory MEM为构建的容器设置内存大小
–build-arg key=val为服务设置build-time变量
服务容器一旦构建后,将会带上一个标记名。可以随时在项目目录下运行docker-compose build来重新构建服务
docker-compose pull
docker-compose pull [options] [SERVICE...]
拉取服务依赖的镜像。
选项包括:
–ignore-pull-failures,忽略拉取镜像过程中的错误
–parallel,多个镜像同时拉取
–quiet,拉取镜像过程中不打印进度信息
docker-compose pull
拉取服务依赖的镜像
docker-compose restart
docker-compose restart [options] [SERVICE...]
重启项目中的服务。
选项包括:
-t, –timeout TIMEOUT,指定重启前停止容器的超时(默认为10秒)
docker-compose restart
重启项目中的服务
docker-compose rm
docker-compose rm [options] [SERVICE...]
删除所有(停止状态的)服务容器。
选项包括:
–f, –force,强制直接删除,包括非停止状态的容器
-v,删除容器所挂载的数据卷
docker-compose rm
删除所有(停止状态的)服务容器。推荐先执行docker-compose stop命令来停止容器。
docker-compose run
docker-compose run [options] [-v VOLUME...] [-p PORT...] [-e KEY=VAL...] SERVICE [COMMAND] [ARGS...]
在指定服务上执行一个命令。
docker-compose run ubuntu ping www.baidu.com
在指定容器上执行一个ping命令。
docker-compose scale
docker-compose scale web=3 db=2
设置指定服务运行的容器个数。通过service=num的参数来设置数量
docker-compose pause
docker-compose pause [SERVICE...]
暂停一个服务容器
docker-compose uppause
docker-compose unpause [SERVICE...]
恢复处于暂停状态中的服务。
docker-compose kill
docker-compose kill [options] [SERVICE...]
通过发送SIGKILL信号来强制停止服务容器。
支持通过-s参数来指定发送的信号,例如通过如下指令发送SIGINT信号:
docker-compose kill -s SIGINT
docker-compose config
docker-compose config [options]
验证并查看compose文件配置。
选项包括:
–resolve-image-digests 将镜像标签标记为摘要
-q, –quiet 只验证配置,不输出。 当配置正确时,不输出任何内容,当文件配置错误,输出错误信息
–services 打印服务名,一行一个
–volumes 打印数据卷名,一行一个
docker-compose create
docker-compose create [options] [SERVICE...]
为服务创建容器。
选项包括:
–force-recreate:重新创建容器,即使配置和镜像没有改变,不兼容–no-recreate参数
–no-recreate:如果容器已经存在,不需要重新创建,不兼容–force-recreate参数
–no-build:不创建镜像,即使缺失
–build:创建容器前 ,生成镜像
docker-compose exec
docker-compose exec [options] SERVICE COMMAND [ARGS...]
选项包括:
-d 分离模式,后台运行命令。
–privileged 获取特权。
–user USER 指定运行的用户。
-T 禁用分配TTY,默认docker-compose exec分配TTY。
–index=index,当一个服务拥有多个容器时,可通过该参数登陆到该服务下的任何服务,例如:docker-compose exec –index=1 web /bin/bash ,web服务中包含多个容器
docker-compose port
docker-compose port [options] SERVICE PRIVATE_PORT
显示某个容器端口所映射的公共端口。
选项包括:
–protocol=proto,指定端口协议,TCP(默认值)或者UDP
–index=index,如果同意服务存在多个容器,指定命令对象容器的序号(默认为1)
docker-compose push
docker-compose push [options] [SERVICE...]
推送服务端的镜像。
选项包括:
–ignore-push-failures 忽略推送镜像过程中的错误
模板配置入门
Compose模板文件是一个定义服务、网络和逻辑卷的YAML文件。Compose模板文件默认路径是当前目录下的docker-compose.yml,可以使用.yml或.yaml作为文件扩展名。
Docker-Compose标准模板文件应该包含version、services、networks 三大部分,最关键的是services和networks两个部分。networks是可选参数。
例如,我们要一次性启动3个ubuntu容器运行各自不同的配置下。
mkdir -p /home/compose && cd /home/compose
vim docker-compose.yml
docker-compose.yml,代码:
version: "3.8"
services:
ubuntu1:
image: ubuntu:20.04
container_name: "ubuntu1"
networks:
- dev
ubuntu2:
image: ubuntu:20.04
container_name: "ubuntu2"
networks:
- dev
- pro
ubuntu3:
image: ubuntu:20.04
container_name: "ubuntu3"
networks:
- pro
networks:
dev:
driver: bridge
pro:
driver: bridge
docker-compose.yml,注释版本,代码:
# 目前我们使用的基本都是Version3版本,最新版本是3.9。
version: "3.8"
# 声明接下来开始配置服务容器
services:
# 服务名,开发者自定义的,
ubuntu1:
# image 当前服务容器的基本依赖镜像,如果本地没有该镜像,则会自动从官网pull拉取
# image 也可以是自己本地基于Dockerfile编译后产生的定制镜像,但是必须是已经build编译好的
# 如希望在docker-compose up启动容器服务时自动编译Dockerfile,则必须增加配置项build指定Dockerfile
# 文件的所在路径,如果不指定,则可能出现从官网拉取镜像失败的情况,build配置项写法如下:
# build: .
# 如使用了build配置项时还声明了image配置项,则基于build所在的Dockerfile编译的镜像名为image指定名字。
# build: .
# image: djdemo:1.0.0
image: ubuntu:20.04
# container_name 指定当前服务容器启动以后的容器名
container_name: "ubuntu1"
# networks 指定网络,可以分配容器在一个或多个网络,如果不指定,则默认分配在docker的default网络中
networks:
- dev
ubuntu2:
image: ubuntu:20.04
container_name: "ubuntu2"
networks:
- dev
- pro
ubuntu3:
image: ubuntu:20.04
container_name: "ubuntu3"
networks:
- pro
# 网络配置
networks:
# 指定网络名称,相当于网卡名
dev:
# driver 网卡驱动:bridge 桥接模式,网卡驱动有三种模式:bridge、host、none
# 查看网络:docker network ls
driver: bridge
pro:
driver: bridge
完成上面的配置以后,可以通过以下命令,批量创建ubuntu容器。
# 切换目录
cd /home/compose
# 批量启动容器
docker-compose up
# 查看当前目录下的docker-compose.yml配置的容器
docker-compose ps
# 删除docker-compose.yml配置的容器
docker-compose down
docker-compose up -d
docker-compose ps
docker-compose down
配置项说明
image
image是指定服务的镜像名称或镜像ID。如果镜像在本地不存在,Compose将会尝试拉取镜像。
注意:如果镜像有版本号,则镜像名和版本号之间不能出现空格!否则报错!!!
services:
ubuntu1:
image: ubuntu:20.04
build
服务除了可以基于指定的基础镜像,还可以基于一份Dockerfile的自定义镜像,在使用docker-compose up启动时执行docker容器的构建任务,构建标签是build,可以指定Dockerfile所在文件夹的路径。Compose将会利用Docker自动构建镜像,然后使用镜像启动服务容器。
services:
web:
build: /home/docker # 指定Dockerfile文件的路径,如果与docker-compose.yml在同一个目录,则可以使用 . 表示当前目录
也可以是相对路径,只要上下文确定就可以读取到Dockerfile。
services:
web:
build: ../docker
设定上下文根目录,然后以该目录为准指定Dockerfile。
services:
web:
build:
context: ../docker
dockerfile: Dockerfile
build都是一个目录,如果要指定Dockerfile文件需要在build标签的子级标签中使用dockerfile标签指定。
如果同时指定image和build两个标签,那么Compose会构建镜像并且把镜像命名为image值指定的名字。
services:
web:
image: djdemo:1.0.0
build:
context: /home/docker
dockerfile: Dockerfile
context选项可以是Dockerfile的文件路径,也可以是到链接到git仓库的url,当提供的值是相对路径时,被解析为相对于撰写文件的路径,此目录也是发送到Docker守护进程的context
dockerfile选项可以指定context对应目录下的Dockerfile文件来构建,必须指定构建路径
command
使用command可以覆盖容器启动后默认执行的命令。相当于 docker
命令的末尾参数。
compose的command会覆盖Dockerfile里面的CMD的值。
command: shell命令
container_name
Compose的容器名称格式是:<项目名称><服务名称><序号>,相当于 docker [选项] --name
可以自定义项目名称、服务名称,但如果想完全控制容器的命名,可以使用标签指定:
container_name: app
restart
指定容器是否在操作系统重启以后,docker启动以后,是否容器也自动重启。相当于 docker --restart=always
restart: always
environment
指定服务容器中的环境变量,可以多个环境变量,每个环境变量就一个成员,相当于 docker -e
version: '3.7'
services:
mysql:
image: mysql:8.0.26
restart: always
container_name: mysql
networks:
- mysql
environment:
- "MYSQL_ROOT_PASSWORD=root"
- "MYSQL_USER=luffycity"
- "MYSQL_PASSWORD=luffycity"
- "MYSQL_DATABASE=luffycity"
- "TZ=Asia/Shanghai"
depends_on
在使用Compose时,最大的好处就是少打启动命令,但一般项目中多个容器的启动,顺序是有要求的,如果直接从上到下启动容器,必然会因为容器依赖问题而启动失败。例如在没启动数据库容器的时候启动应用容器,应用容器会因为找不到数据库而退出。depends_on标签用于解决容器的依赖、启动先后顺序的问题
version: '3.7'
services:
django:
build: .
depends_on:
- mysql
- redis
redis:
image: redis:6.0
mysql:
image: mysql:8.0.26
上述YAML文件定义的容器会先启动redis和db两个服务,最后才启动django服务。
ports
ports用于映射端口的标签。
使用HOST:CONTAINER格式或者只是指定容器的端口,宿主机会随机映射端口。相当于docker -p
ports:
- "3000" # 等价于 "3000:3000"
- "8000:8000"
- "49100:22"
当使用HOST:CONTAINER格式来映射端口时,如果使用的容器端口小于60可能会得到错误得结果,因为YAML将会解析xx:yy这种数字格式为60进制。所以建议采用字符串格式。
volumes
挂载一个目录或者一个已存在的数据卷容器,可以直接使用 [HOST:CONTAINER]格式,或者使用[HOST:CONTAINER:ro]格式,后者对于容器来说,数据卷是只读的,可以有效保护宿主机的文件系统。
Compose的数据卷指定路径可以是相对路径,使用 . 或者 .. 来指定相对目录。
相当于 docker run 终端命令选项-v参数
数据卷的格式可以是下面多种形式
volumes:
# 只是指定一个路径,Docker 会自动在创建一个数据卷(这个路径是容器内部的)。
# 相当于 /var/lib/mysql:/var/lib/mysql
- /var/lib/mysql
# 使用绝对路径挂载数据卷
- /opt/data:/var/lib/mysql
# 以 Compose 配置文件为中心的相对路径作为数据卷挂载到容器。
- ./cache:/tmp/cache
# 使用用户的相对路径(~/ 表示的目录是 /home/<用户目录>/ 或者 /root/)。
- ~/configs:/etc/configs/:ro
# 已经存在的命名的数据卷。
- datavolume:/var/lib/mysql
如果不使用宿主机的路径,可以指定一个volume_driver。
volume_driver: mydriver
volumes_from
从另一个服务或容器挂载其数据卷:
volumes_from:
- service_name
- container_name
dns
自定义DNS服务器。可以是一个值,也可以是一个列表。
dns:8.8.8.8
dns:
- 8.8.8.8
- 9.9.9.9
expose
暴露端口,但不映射到宿主机,只允许能被连接的服务访问。仅可以指定内部端口为参数,如下所示:
expose:
- "3000"
- "8000"
links
链接到其它服务中的容器。使用服务名称(同时作为别名),或者“服务名称:服务别名”(如 SERVICE:ALIAS),例如:
links:
- db
- db:database
- redis
net
设置网络模式。
net: "bridge"
net: "none"
net: "host"
模板配置编写案例
在开发中,我们的前端项目经常需要提供给外界浏览服务,所以往往我们会使用nginx这样的web服务器来提供前端文件给外界访问。所以我们创建并切换到/home/website目录,创建docker-compose.yml,并编写配置多个nginx容器批量启动。
mkdir /home/website && cd /home/website
vim docker-compose.yml
配置代码:
version: "3.8"
services:
web1:
image: nginx:1.21.4
container_name: "web1"
ports:
- "8081:80"
networks:
- dev
web2:
image: nginx:1.21.4
container_name: "web2"
ports:
- "8082:80"
networks:
- dev
- pro
web3:
image: nginx:1.21.4
container_name: "web3"
ports:
- "8083:80"
networks:
- pro
networks:
dev:
driver: bridge
pro:
driver: bridge
启动服务容器
使用docker-compose批量启动容器
# docker-compose up # 阻塞运行
docker-compose up -d # 后台运行
服务访问
开放安全组端口以后,可以通过浏览器访问web1,web2,web3。
http://114.115.200.1:8081/
http://114.115.200.1:8082/
http://114.115.200.1:8083/
注意IP要换成自己的,同时,注意添加安全组的入方向规则的端口。
Nginx
基本介绍
nginx是一个开源免费的,高性能,高并发的web服务和代理服务软件。它是俄罗斯人lgor sysoev(伊戈尔·塞索耶夫)在2000开发的,在2004年将源代码开源出来供全球使用。
nginx比传统的web服务器apache性能改进了许多,nginx占用的系统资源更少,支持更高的并发连接,有更高的访问效率。
nginx不但是一个优秀的web服务软件,还可以作为反向代理,负载均衡,以及缓存服务使用。
安装更为简单,方便,灵活,具有强大的生态,因为使用的开发者非常多,很多人都给nginx开发了各种应用场景的插件。
优势
1. 支持高并发,能支持几万并发连接
2. 资源消耗少,在3万并发连接下开启10个nginx线程消耗的内存不到200M
3. 可以做http反向代理和负载均衡
4. 支持异步网络io,epoll事件模型
5. 是目前最优秀的静态网页web服务器,最优秀静态资源web服务器
nginx配置文件解析
nginx.conf,代码:
nginx.conf,核心配置文件
# nginx运行的用户身份,一般要么注释掉,要么就把nobody改成www或者nginx
# user nobody;
# 工作进程数 (如果是双核4线程,可以设置为4,一般建议跟CPU的逻辑核数量一致)
worker_processes auto;
# 单个进程最大可打开文件数,因为nginx经常用于提供静态文件访问支持,所以往往运营时间长了就会出现单个进程打开文件超标,所以要设置。
worker_rlimit_nofile 65535;
# 错误日志配置,如果不配置,则错误日志保存在安装路径下
# error_log logs/error.log;
# error_log logs/error.log notice;
# error_log logs/error.log info;
# 进程标识符
# pid logs/nginx.pid;
events {
worker_connections 102400;
# 最大可以调整到100000以上,nginx的最大并发连接数 = worker_processes * worker_connections
# use epoll;
# 设置nginx使用的IO多路复用的事件模型,默认不设置即可,nginx会采用当前系统最优的事件模型
# linux建议epoll,FreeBSD建议采用kqueue,window下不指定,采用select。
}
# nginx作为httpweb服务器的相关配置,站点配置server必须填写在http选项中。
http {
# 导入mime.types模块,让nginx能够识别各种各样的文件资源
include mime.types;
# nginx默认识别的mime.types,默认采用二进制数据格式。
default_type application/octet-stream;
# 设置客户端访问nginx的访问日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# 开启访问日志
access_log logs/access.log main;
# Nginx在进行数据传输,会调用sendfile()函数, Linux 2.0+ 以后的推出的一个系统调用。
# 对比一般的数据的网络传输sendfile会有更少的切换和更少的数据拷贝。
sendfile on;
tcp_nopush on;
# 客户端保持连接时间
#keepalive_timeout 0;
keepalive_timeout 65;
# 开启网络传输的数据gzip压缩[会额外消耗一定的cpu资源,但是会节约大量的出口带宽来提高访问速度,gzip压缩算法的使用会带来一定的安全隐患,不建议压缩图片和大文件]
gzip on;
#低于1kb的资源不压缩
gzip_min_length 1k;
# 设置压缩级别,级别范围:1~9,数字越大压缩率越高,同时消耗cpu资源也越多,建议设置在5左右。
gzip_comp_level 5;
# 指定压缩哪些MIME类型的静态资源,多个空格隔开。不建议压缩图片,视频等二进制文件
gzip_types text/plain application/javascript application/x-javascript text/javascript text/xml text/css;
# web站点的虚拟主机,类似python的虚拟环境,这里的配置,会让nginx自动提供一个站点给外界访问
# nginx可以通过设置多个server配置项,提供多个站点的访问支持
# 一个server就代表一个站点,也可以理解为一个服务端的源,源是通过端口、地址、协议进行区分。
server {
# 站点的访问端口,要允许外界访问,还需要设置防火墙
listen 80;
# 站点的访问域名地址
server_name www.luffycity.dabanyu.com;
#charset koi8-r;
#access_log logs/host.access.log main;
# 地址模式匹配,location后面的是访问url路径,相当于django的正则路由一样,
# 地址匹配成功以后,则会调用当前花括号的配置信息
location / {
# root 表示当前站点所在目录
root html;
# index 指定默认首页
index main.html main.htm;
}
location /admin {
# http代理
proxy_pass http://localhost:81/main.html;
}
error_page 404 /404.html;
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;
# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;
# location / {
# root html;
# index index.html index.htm;
# }
#}
}
使用docker进行容器化集成部署
远程服务器更新源
更新ubuntu的apt源
sudo apt-get update
安装包允许apt通过HTTPS使用仓库
sudo dpkg --configure -a
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common
添加Docker官方GPG key【这个是国外服务器地址,所以网路不好的时候,会失败!在网路好的情况下,多执行几次就没问题了,如果下一行结果不是OK,则执行多几遍】
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
设置Docker稳定版仓库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
添加仓库后,更新apt源索引
sudo apt-get update
前面的准备工作完成以后,接下来安装最新版Docker CE(社区版)
sudo apt-get install -y docker-ce
检查Docker CE是否安装正确
sudo docker pull ubuntu:20.04
要获取基础镜像可以直接去官方网站上获取: https://hub.docker.com/
启动与停止
安装完成Docker后,默认已经启动了docker服务,如需手动控制docker服务的启停,可执行如下命令
# 启动docker
sudo service docker start
# 停止docker
sudo service docker stop
# 重启docker
sudo service docker restart
安装docker-compose,这种安装方式是基于Linux操作系统的源来决定安装版本的,所以往往版本会比较低。
apt install -y docker-compose
客户端项目部署
前端自动化项目构建工具vite提供了一个build命令给我们可以将前端项目中src目录下的所有前端资源进行打包成普通的HTML/CSS/JS,生成到项目的dist文件夹中。这个dist目录下的内容会剔除掉vite中所有的测试服务器运行相关依赖包,只保留项目本身的代码和相关文件,这样将来我们在线上服务器运行时就可以直接通过nginx来运行,而不需要依赖于vite工具。
执行build命令进行项目编译之前,先修改配置文件vite.config.js的信息,例如当前客户端的域名和api服务端的域名。src/settings.js
,代码:
export default {
// api服务端所在地址
host: "http://api.luffycity.dabanyu.com:8000", // ajax服务度地址
// 防水墙验证码的应用ID
captcha_app_id: "199911961", // IP应用ID
// 视频更新进度的时间阈值
seed_time: 5
}
src/http/index.js,代码:
import axios from "axios"
import settings from "../settings";
const http = axios.create({
// timeout: 2500, // 请求超时,有大文件上传需要关闭这个配置
baseURL: settings.host, // 设置api服务端的默认地址[如果基于服务端实现的跨域,这里可以填写api服务端的地址,如果基于nodejs客户端测试服务器实现的跨域,则这里不能填写api服务端地址]
withCredentials: false, // 是否允许客户端ajax请求时携带cookie
})
终端下,在前端项目的根目录下,执行以下命令:
/home/moluo/Desktop/luffycity/luffycityweb
yarn upgrade
rm -rf /home/moluo/Desktop/luffycity/luffycityweb/dist
vite build
效果:
编译完成的内容会被保存在dist目录下,并且编译出来的项目不能直接通过file://协议来访问,只能通过http服务器的运行这个项目。同时,我们之前如果通过node进行跨域代理的话,则编译后会丢失跨域代理,此时我们就需要依靠后台实现cors代理了。
通过scp上传代码到线上服务器
# scp 本地路径 账号@远程地址:线上路径
# 本地Ubuntu终端
cd ~/Desktop/luffycity/luffycityweb
tar -zcf luffycityweb.tar.gz dist
scp ~/Desktop/luffycity/luffycityweb/luffycityweb.tar.gz root@114.115.200.1:/home/
# 线上服务器操作[远程部署终端]
cd /home
tar -zxf luffycityweb.tar.gz
mkdir /home/luffycity
mv /home/dist /home/luffycity/luffycityweb # 把dist目录下的移动到luffycityweb目录下
因为根据我们上面的部署方案,我们需要安装nginx来运行这个前端项目。
目录结构
服务器,目录结构:
/home/luffycity/
├── conf/
│ └── nginx/
│ └── web.conf
├── docker-compose.yml
└── luffycityweb/
远程部署服务器终端操作:
mkdir -p /home/luffycity/conf/nginx/
touch /home/luffycity/conf/nginx/web.conf
vim /home/luffycity/docker-compose.yml
编排 nginx服务容器
/home/luffycity/docker-compose.yml,代码:
version: '3.8'
services:
nginx:
image: nginx:1.21.4
restart: always
ports:
- "80:80"
container_name: "nginx"
volumes:
- ./luffycityweb:/home/luffycityweb
- ./conf/nginx/web.conf:/etc/nginx/conf.d/nginx.conf
networks:
- default
networks:
default:
/home/luffycity/conf/nginx/web.conf,这个文件是准备给nginx容器内部的虚拟主机配置文件
vim /home/luffycity/conf/nginx/web.conf
web.conf,代码:
server {
listen 80;
server_name www.luffycity.dabanyu.com;
# client_max_body_size 1000M;
# root /opt/dist;
# client_max_body_size 20M;
# Load configuration files for the default server block.
location / {
root /home/luffycityweb;
index index.html;
# 单入口的url路径转发
try_files $uri $uri/ /index.html;
}
}
在远程部署终端启动容器服务
docker-compose -f docker-compose.yml up -d
服务端项目部署
根据我们之前分析的项目部署架构,后端需要调用mysql、redis和elasticsearch。因此我们需要先把这些外部软件先预装。
目录结构
/home/luffycity/
├── conf/
│ ├── redis/
│ │ └── master.conf
│ ├── mysql/
│ └── nginx/
│ └── web.conf
├── data/
│ ├── redis/
│ ├── elastic/
│ └── mysql/
├── docker-compose.yml
├── luffycityweb/
├── initdb/
│ └── luffycity.sql
├── logs/
│ └── mysql
└── plugins/
编排mysql服务容器
在远程部署服务器上进行终端操作,创建目录结构
cd /home/luffycity
mkdir -p data/mysql
mkdir -p logs/mysql
mkdir -p initdb
vim /home/luffycity/docker-compose.yml
/home/luffycity/docker-compose.yml,代码:
version: '3.8'
services:
nginx:
image: nginx:1.21.4
restart: always
ports:
- "80:80"
container_name: "nginx"
volumes:
- ./luffycityweb:/home/luffycityweb
- ./conf/nginx/web.conf:/etc/nginx/conf.d/nginx.conf
networks:
- default
mysql_master:
image: mysql:8.0.30
restart: always
container_name: "mysql_master"
networks:
- default
environment:
- "MYSQL_ROOT_PASSWORD=root2022"
- "MYSQL_USER=luffycity"
- "MYSQL_PASSWORD=luffycity"
- "MYSQL_DATABASE=luffycity"
- "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:
- 3306:3306
volumes:
- ./data/mysql:/var/lib/mysql
- ./logs/mysql:/logs
- ./initdb:/docker-entrypoint-initdb.d/
networks:
default:
把本地的数据导入到SQL文件并上传到服务器
# 把本地的数据库导出到桌面
mysqldump -uroot -p123 luffycity > ~/Desktop/luffycity/luffycityapi/luffycity.sql
# 把luffycityapi目录下导出的数据库文件luffycity.sql导入到远程阿里元服务器下的docker容器中的mysql数据库
scp ~/Desktop/luffycity/luffycityapi/luffycity.sql root@114.115.200.1:/home/luffycity/initdb/
重新启动docker-compose的容器服务
cd /home/luffycity
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml up -d
# 注意:如果mysql容器要初始化数据库,则必须要保证在运行mysql容器之前,data目录是没有任何内容的!!!
# rm -rf /home/luffycity/data/
# 可以通过以下命令进入mysql容器
docker exec -it mysql_master mysql -uroot -proot2022
use luffycity
show tables;
编排redis服务容器
创建目录和必须的文件
cd /home/luffycity
mkdir conf/redis
touch conf/redis/master.conf
mkdir -p data/redis/master
vim conf/redis/master.conf
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
保存redis配置文件,并编辑/home/luffycity/docker-compose.yml。
cd /home/luffycity
vim /home/luffycity/docker-compose.yml
代码:
version: '3.8'
services:
nginx:
image: nginx:1.21.4
restart: always
ports:
- "80:80"
container_name: "nginx"
volumes:
- ./luffycityweb:/home/luffycityweb
- ./conf/nginx/web.conf:/etc/nginx/conf.d/nginx.conf
networks:
- default
mysql_master:
image: mysql:8.0.30
restart: always
container_name: "mysql_master"
networks:
- default
environment:
- "MYSQL_ROOT_PASSWORD=root2022"
- "MYSQL_USER=luffycity"
- "MYSQL_PASSWORD=luffycity"
- "MYSQL_DATABASE=luffycity"
- "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:
- 3306:3306
volumes:
- ./data/mysql:/var/lib/mysql
- ./logs/mysql:/logs
- ./initdb:/docker-entrypoint-initdb.d/
redis_master:
image: redis:6.2.6-alpine3.15
restart: always
networks:
- default
ports:
- "6379:6379"
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
container_name: "redis_master"
volumes:
- ./conf/redis/master.conf:/usr/local/etc/redis/redis.conf
- ./data/redis/master:/data
networks:
default:
启动服务
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml up -d
# 测试
docker exec -it redis_master redis-cli -a 123456
编排elasticsearch搜索引擎服务容器
在网络安全组中,开放9200,9300这2个端口
编辑/home/luffycity/docker-compose.yml,
vim /home/luffycity/docker-compose.yml
代码:
version: '3.8'
services:
nginx:
image: nginx:1.21.4
restart: always
ports:
- "80:80"
container_name: "nginx"
volumes:
- ./luffycityweb:/home/luffycityweb
- ./conf/nginx/web.conf:/etc/nginx/conf.d/nginx.conf
networks:
- default
mysql_master:
image: mysql:8.0.30
restart: always
container_name: "mysql_master"
networks:
- default
environment:
- "MYSQL_ROOT_PASSWORD=root2022"
- "MYSQL_USER=luffycity"
- "MYSQL_PASSWORD=luffycity"
- "MYSQL_DATABASE=luffycity"
- "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:
- 3306:3306
volumes:
- ./data/mysql:/var/lib/mysql
- ./logs/mysql:/logs
- ./initdb:/docker-entrypoint-initdb.d/
redis_master:
image: redis:6.2.6-alpine3.15
restart: always
networks:
- default
ports:
- "6379:6379"
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
container_name: "redis_master"
volumes:
- ./conf/redis/master.conf:/usr/local/etc/redis/redis.conf
- ./data/redis/master:/data
elasticsearch_1:
image: elasticsearch:7.16.1
restart: always
container_name: "elasticsearch_1"
environment:
- "discovery.type=single-node"
- "bootstrap.memory_lock=true"
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- "xpack.security.enabled=false"
- "http.cors.enabled=true"
- "http.cors.allow-origin=*"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./data/elastic:/usr/share/elasticsearch/data
- ./logs/elastic:/var/log/elasticsearch
- ./plugins:/usr/share/elasticsearch/plugins
ports:
- 9200:9200
networks:
- default
networks:
default:
把ik分词器下载解压到/home/luffycity/plugins/目录下。
mkdir -p /home/luffycity/data/elastic
mkdir -p /home/luffycity/plugins
chmod 777 /home/luffycity/data/elastic
# 下载ik中文分词插件
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.16.1/elasticsearch-analysis-ik-7.16.1.zip
# 解压保存到es的插件目录
unzip elasticsearch-analysis-ik-7.16.1.zip -d /home/luffycity/plugins/ik-7.16.1
docker-compose -f docker-compose.yml up
# postman中测试访问
post http://114.115.200.1:9200/_analyze?pretty
{
"analyzer": "ik_max_word",
"text": "我是中国人,我来自北京。"
}
启动服务
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml up -d
# 监控docker-compose的所有容器运行的日志信息
docker-compose logs -f
es索引的生成是要结合django项目的ORM来生成,所以暂时先不用执行其他操作,先完成django项目的部署先。
编排uwsgi运行服务端项目容器服务
基于Dockerfile定制服务端项目镜像
把luffycityapi服务端项目进行部署前的修改和调整
1. 本地的项目配置,保存在dev.py中,不管mysql数据库密码或者redis的地址或者配置信息都是属于开发阶段,
项目上线以后肯定换成公司的。所以mysql数据库等信息一定会修改,因为将来还需要本地继续开发新的功能业务,
所以不能直接修改dev.py,因此我们需要把dev.py的配置内容复制粘贴一份到pro.py中,并修改对应的配置信息,
改成公司的。
2. 在本地开发时,我们使用的django框架运行在debug模式下的,但是项目上线采用uwsgi运行时肯定会关闭debug模式的,
而如果关闭了debug模式,则django框架就不再提供静态文件的浏览服务,也就是之前simpleUI,drf的接
口调试页面的css/js/图片都不能访问了。我们需要使用django提供的命令提前收集这些静态文件到项目的
指定目录下,后面通过nginx来对外提供http浏览服务
3. 原来的drf项目是运行在python内置的提供的web服务器中,wsgiref
在项目上线的时候,我们不能使用这个模块来提供对外服务!
wsgiref不支持多线程多进程,本身性能不好,本身在安全性上并不完善,
python提供wsgiref这个模块仅供学习和开发测试使用。
runserver 不能用于生产, 改成uwsgi, gunicorn, uvicorn
4. 原来drf项目在本地开发时,其实要运行这个项目,我们是安装了很多的第三方依赖模块。
现在项目上线了,我们也肯定需要把本地的模块同步到线上服务器!否则项目跑不起来。
因此需要使用pip freeze > requirements.txt 把项目运行的模块列表以及它们的版本
记录到requirements.txt文件中,然后在线上服务器对requirements.txt执行重新安装模块
修改现有的django配置文件
在项目中复制开发配置文件dev.py 到生产配置pro.py
修改配置中的IP地址为公网地址,所有的127.0.0.1全部换成公网地址,所有域名替换成真实域名。
所有的第三方接口账号全部换成公司的账号信息。
settings/pro.py,代码:
"""
Django settings for luffycityapi project.
Generated by 'django-admin startproject' using Django 3.2.9.
For more information on this file, see
https://docs.djangoproject.com/en/3.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.2/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
# 主应用目录
BASE_DIR = Path(__file__).resolve().parent.parent
import sys
sys.path.insert(0, str(BASE_DIR / "apps"))
sys.path.insert(0, str( BASE_DIR / "utils") )
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-n)zr+vbuap+s%zd*d6b2@nhpaox_3ii^zv3e9bsgp+2-oicwry'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = False
# 允许客户端使用任意域名访问django站点
ALLOWED_HOSTS = ["*"]
# Application definition
INSTALLED_APPS = [
'simpleui', # admin界面美化,必须写在admin上面
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
"rest_framework", # 注意:记得加入 rest_framework
'corsheaders', # cors跨域子应用
'ckeditor', # 富文本编辑器
'ckeditor_uploader', # 富文本编辑器上传文件子用用
'stdimage', # 生成缩略图
'haystack', # 搜索引擎框架
"home",
'users',
'courses',
'cart',
'orders',
'coupon',
'payments',
]
MIDDLEWARE = [
'corsheaders.middleware.CorsMiddleware', # cors跨域的中间件
'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',
]
# CORS_ORIGIN_WHITELIST = (
# 'http://www.luffycity.cn:3000',
# )
# CORS_ALLOW_CREDENTIALS = False # 不允许ajax跨域请求时携带cookie
CORS_ALLOW_ALL_ORIGINS = True
ROOT_URLCONF = 'luffycityapi.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [
BASE_DIR / "templates", # BASE_DIR 是apps的父级目录,是主应用目录,templates需要手动创建
],
'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 = 'luffycityapi.wsgi.application'
# Database
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases
DATABASES = {
# 'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': BASE_DIR / 'db.sqlite3',
# }
'default': {
# 'ENGINE': 'django.db.backends.mysql',
'ENGINE': 'dj_db_conn_pool.backends.mysql',
'NAME': 'luffycity',
'PORT': 3306,
'HOST': '114.115.200.1',
'USER': 'luffycity',
'PASSWORD': 'luffycity',
'OPTIONS': {
'charset': 'utf8mb4', # 连接选项配置,mysql8.0以上无需配置
},
'POOL_OPTIONS': { # 连接池的配置信息
'POOL_SIZE': 10, # 连接池默认创建的链接对象的数量
'MAX_OVERFLOW': 10 # 连接池默认创建的链接对象的最大数量
}
},
'read1': {
# 'ENGINE': 'django.db.backends.mysql',
'ENGINE': 'dj_db_conn_pool.backends.mysql',
'NAME': 'luffycity',
'PORT': 3306,
'HOST': '114.115.200.1',
'USER': 'luffycity',
'PASSWORD': 'luffycity',
'OPTIONS': {
'charset': 'utf8mb4', # 连接选项配置,mysql8.0以上无需配置
},
'POOL_OPTIONS': { # 连接池的配置信息
'POOL_SIZE': 10, # 连接池默认创建的链接对象的数量
'MAX_OVERFLOW': 10 # 连接池默认创建的链接对象的最大数量
}
}
}
# 自动数据库路由
DATABASE_ROUTERS = ["luffycityapi.db_router.DBRouter"]
# redis configration
# 设置redis缓存
CACHES = {
# 默认缓存
"default": {
"BACKEND": "django_redis.cache.RedisCache",
# 项目上线时,需要调整这里的路径
# "LOCATION": "redis://:密码@IP地址:端口/库编号",
"LOCATION": "redis://:123456@114.115.200.1:6379/0",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 10}, # 连接池
}
},
# 提供给admin运营站点的session存储
"session": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:123456@114.115.200.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 10},
}
},
# 提供存储短信验证码
"sms_code": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:123456@114.115.200.1:6379/2",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"CONNECTION_POOL_KWARGS": {"max_connections": 10},
}
},
# 提供存储搜索热门关键字
"hot_word": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:123456@114.115.200.1:6379/3",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
# 提供存储购物车课程商品
"cart": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:123456@114.115.200.1:6379/4",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
# 提供存储优惠券
"coupon": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://:123456@114.115.200.1:6379/5",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
}
},
}
# 设置用户登录admin站点时,记录登录状态的session保存到redis缓存中
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
# 设置session保存的位置对应的缓存配置项
SESSION_CACHE_ALIAS = "session"
# haystack连接elasticsearch的配置信息
HAYSTACK_CONNECTIONS = {
'default': {
# haystack操作es的核心模块
'ENGINE': 'haystack.backends.elasticsearch7_backend.Elasticsearch7SearchEngine',
# es服务端地址
'URL': 'http://114.115.200.1:9200/',
# es索引仓库
'INDEX_NAME': 'haystack',
},
}
# 当mysqlORM操作数据库改变(添加、删除、修改)时,自动更新es的索引,否则es的索引会找不到新增的数据
HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor'
# Password validation
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators
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',
},
]
# Internationalization
# https://docs.djangoproject.com/en/3.2/topics/i18n/
LANGUAGE_CODE = 'zh-hans'
TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True
USE_L10N = True
USE_TZ = False # 关闭时区转换以后,django会默认使用TIME_ZONE作为时区。
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.2/howto/static-files/
STATIC_URL = '/static/'
# 设置django的静态文件目录[手动创建]
STATICFILES_DIRS = [
BASE_DIR / "static",
]
# 项目中存储上传文件的根目录[手动创建],注意,uploads目录需要手动创建否则上传文件时报错
MEDIA_ROOT = BASE_DIR / "uploads"
# 访问上传文件的url地址前缀
MEDIA_URL = "/uploads/"
# Default primary key field type
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
# 日志配置
LOGGING = {
'version': 1, # 使用的日志模块的版本,目前官方提供的只有版本1,但是官方有可能会升级,为了避免升级出现的版本问题,所以这里固定为1
'disable_existing_loggers': False, # 是否禁用其他的已经存在的日志功能?肯定不能,有可能有些第三方模块在调用,所以禁用了以后,第三方模块无法捕获自身出现的异常了。
'formatters': { # 日志格式设置,verbose或者simple都是自定义的
'verbose': { # 详细格式,适合用于开发人员不在场的情况下的日志记录。
# 格式定义:https://docs.python.org/3/library/logging.html#logrecord-attributes
# levelname 日志等级
# asctime 发生时间
# module 文件名
# process 进程ID
# thread 线程ID
# message 异常信息
'format': '{levelname} {asctime} {module} {process:d} {thread:d} {message}',
'style': '{', # 变量格式分隔符
},
'simple': { # 简单格式,适合用于开发人员在场的情况下的终端输出
'format': '{levelname} {message}',
'style': '{',
},
},
'filters': { # 过滤器
'require_debug_true': {
'()': 'django.utils.log.RequireDebugTrue',
},
},
'handlers': { # 日志处理流程,console或者mail_admins都是自定义的。
'console': {
'level': 'DEBUG', # 设置当前日志处理流程中的日志最低等级
'filters': ['require_debug_true'], # 当前日志处理流程的日志过滤
'class': 'logging.StreamHandler', # 当前日志处理流程的核心类,StreamHandler可以帮我们把日志信息输出到终端下
'formatter': 'simple' # 当前日志处理流程的日志格式
},
# 'mail_admins': {
# 'level': 'ERROR', # 设置当前日志处理流程中的日志最低等级
# 'class': 'django.utils.log.AdminEmailHandler', # AdminEmailHandler可以帮我们把日志信息输出到管理员邮箱中。
# 'filters': ['special'] # 当前日志处理流程的日志过滤
# }
'file': {
'level': 'INFO',
'class': 'logging.handlers.RotatingFileHandler',
# 日志位置,日志文件名,日志保存目录logs必须手动创建
'filename': BASE_DIR.parent / "logs/luffycity.log",
# 单个日志文件的最大值,这里我们设置300M ~ 500M
'maxBytes': 300 * 1024 * 1024,
# 备份日志文件的数量,设置最大日志数量为10
'backupCount': 10,
# 日志格式:详细格式
'formatter': 'verbose'
},
},
'loggers': { # 日志处理的命名空间
'django': {
'handlers': ['console','file'], # 当基于django命名空间写入日志时,调用那几个日志处理流程
'propagate': True, # 是否在django命名空间对应的日志处理流程结束以后,冒泡通知其他的日志功能。True表示允许
},
}
}
# drf配置
REST_FRAMEWORK = {
# 自定义异常处理
'EXCEPTION_HANDLER': 'luffycityapi.utils.exceptions.custom_exception_handler',
# 自定义认证
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication', # jwt认证
'rest_framework.authentication.SessionAuthentication', # session认证
'rest_framework.authentication.BasicAuthentication',
),
}
import datetime
# jwt认证相关配置项
JWT_AUTH = {
# 设置jwt的有效期
# 如果内部站点,例如:运维开发系统,OA,往往配置的access_token有效期基本就是15分钟,30分钟,1~2个小时
'JWT_EXPIRATION_DELTA': datetime.timedelta(weeks=1), # 一周有效
# 自定义载荷
'JWT_PAYLOAD_HANDLER': 'luffycityapi.utils.authenticate.jwt_payload_handler',
# 自定义响应数据
'JWT_RESPONSE_PAYLOAD_HANDLER': 'luffycityapi.utils.authenticate.jwt_response_payload_handler'
}
# 告诉django,系统认证相关的功能,采用我们自定义的用户模型类,格式:
# AUTH_USER_MODEL = "子应用目录名.模型类"
AUTH_USER_MODEL = 'users.User'
# django自定义认证
# AUTHENTICATION_BACKENDS django.conf.global_settings中
AUTHENTICATION_BACKENDS = ['luffycityapi.utils.authenticate.CustomAuthBackend', ]
# 腾讯云API接口配置[改成公司的]
TENCENTCLOUD = {
# 腾讯云访问秘钥ID
"SecretId": "AKID0sjfDbliV4OUXPYUBD3f2Qn10kGYcTAd",
# 腾讯云访问秘钥key
"SecretKey": "HKgplTwEI7i7MPMAl8G8mETUGnnbBk7Q",
# 验证码API配置
"Captcha": {
"endpoint": "captcha.tencentcloudapi.com", # 验证码校验服务端域名
"CaptchaType": 9, # 验证码类型,固定为9
"CaptchaAppId": 199911961, # 验证码应用ID,务必保证和客户端一致!!!
"AppSecretKey": "iOvn8DnejVq0iAgVB4dsl0iib", # 验证码应用key
},
}
# 容联云短信[改成公司的]
RONGLIANYUN = {
"accId": '8a216da863f8e6c20164139687e80c1b', # 主账户ID
"accToken": '6dd01b2b60104b3dbc88b2b74158bac6', # 账户授权令牌
"appId": '8a216da863f8e6c20164139688400c21', # APP应用ID
"reg_tid": 1, # 注册短信验证码的模板ID
"sms_expire": 300, # 短信有效期,单位:秒(s)
"sms_interval": 60,# 短信发送的冷却时间,单位:秒(s)
}
"""Celery异步任务队列框架的配置项[注意:django的配置项必须大写,所以这里的所有配置项必须全部大写]"""
# 任务队列
CELERY_BROKER_URL = 'redis://:123456@114.115.200.1:6379/14'
# 结果队列
CELERY_RESULT_BACKEND = 'redis://:123456@114.115.200.1:6379/15'
# 时区,与django的时区同步
CELERY_TIMEZONE = TIME_ZONE
# 防止死锁
CELERY_FORCE_EXECV = True
# 设置并发的worker数量
CELERYD_CONCURRENCY = 200
# 设置失败允许重试[这个慎用,如果失败任务无法再次执行成功,会产生指数级别的失败记录]
# 如果设置了True,必须在异步任务中指定失败重试的次数
CELERY_ACKS_LATE = True
# 每个worker工作进程最多执行500个任务被销毁,可以防止内存泄漏,500是举例,根据自己的服务器的性能可以调整数值
CELERYD_MAX_TASKS_PER_CHILD = 500
# 单个任务的最大运行时间,超时会被杀死[慎用,有大文件操作、长时间上传、下载任务时,需要关闭这个选项,或者设置更长时间]
CELERYD_TIME_LIMIT = 10 * 60
# 任务发出后,经过一段时间还未收到acknowledge, 就将任务重新交给其他worker执行
CELERY_DISABLE_RATE_LIMITS = True
# celery的任务结果内容格式
CELERY_ACCEPT_CONTENT = ['json', 'pickle']
# 之前定时任务(定时一次调用),使用了apply_async({}, countdown=30);
# 设置定时任务(定时多次调用)的调用列表,需要单独运行SCHEDULE命令才能让celery执行定时任务:celery -A mycelery.main beat,当然worker还是要启动的
# https://docs.celeryproject.org/en/stable/userguide/periodic-tasks.html
from celery.schedules import crontab
CELERY_BEAT_SCHEDULE = {
# "user-send_sms1": { # 定时任务的注册标记符[必须唯一的]
# "task": "send_sms1", # 定时任务的任务名称
# "schedule": 10, # 定时任务的调用时间,10表示每隔10秒调用一次add任务
# # "schedule": crontab(hour=7, minute=30, day_of_week=1),, # 定时任务的调用时间,每周一早上7点30分调用一次add任务
# },
}
# ckeditor富文本编辑器配置
# 上传文件的存储路径
CKEDITOR_UPLOAD_PATH = "ckeditor/"
# 工具条配置
CKEDITOR_CONFIGS = {
'default': {
# 'toolbar': 'full', # full 显示全部工具
# 'toolbar': 'Basic', # Basic 显示基本工具
'toolbar': 'Custom', # 自定义工具条的显示数量
'toolbar_Custom': [
['Bold', 'Italic', 'Underline', 'Image', 'Styles', 'Format', 'Font', 'Fontsize'],
['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'JustifyLeft', 'JustifyCenter',
'JustifyRight', 'JustifyBlock'],
['Link', 'Unlink', 'Table'],
['RemoveFormat', 'Source', 'Smiley']
],
# 设置编辑器的高度
'height': 120,
},
}
# admin站点公共配置
from django.contrib import admin
admin.AdminSite.site_header = '路飞2.0在线教育'
admin.AdminSite.site_title = '路飞2.0在线教育站点管理'
# 登录界面logo
SIMPLEUI_LOGO = '/uploads/logo.svg'
# 快速操作
SIMPLEUI_HOME_QUICK = True
# 服务器信息
SIMPLEUI_HOME_INFO = True
# 关闭simpleui内置的使用分析
SIMPLEUI_ANALYSIS = False
# 离线模式
SIMPLEUI_STATIC_OFFLINE = True
# 首页图标地址
SIMPLEUI_INDEX = 'http://www.luffycity.dabanyu.com/'
# 阿里云OSS云存储[改成公司的]
OSS_ACCESS_KEY_ID = "LTAI5t991uBJjk8TunKooM7M"
OSS_ACCESS_KEY_SECRET = "oEDvV9RaoCf6rHIZXlJCJAmk0phub2"
OSS_ENDPOINT = "oss-cn-beijing.aliyuncs.com" # 访问域名, 根据服务器上的实际配置修改
OSS_BUCKET_NAME = "luffycityoline" # oss 创建的 BUCKET 名称
# 添加下面配置后 Django admin 后台上传的 ImageField, FileField 类型的字段都会被自动上传到 oss 的服务器中, 访问路径也会自动替换
# 如果注释掉的话 oss 的配置会失效, 上传文件会存储到本地, 且访问路径也会变成本地
DEFAULT_FILE_STORAGE = 'django_oss_storage.backends.OssMediaStorage'
# 支付宝相关配置[改成公司的]
ALIPAY = {
# 'gateway': 'https://openapi.alipay.com/gateway.do', # 真实网关地址
'gateway': 'https://openapi.alipaydev.com/gateway.do', # 沙箱网关地址
'appid': '2016091600523592', # 支付应用ID,要修改成自己的
'sign_type': 'RSA2', # 签证的加密算法
'debug': True, # 沙箱模式下必须设置为True
'verbose': True, # 是否在调试模式下输出调试数据
'timeout': 15, # 请求超时时间,单位:秒
"app_private_key_path": BASE_DIR / "apps/payments/keys/app_private_key.pem", # 应用私钥路径
"alipay_public_key_path": BASE_DIR / "apps/payments/keys/alipay_public_key.pem", # 支付宝公钥路径
"return_url": "http://www.luffycity.dabanyu.com/alipay", # 同步回调结果通知地址
"notify_url": "hhttp://api.luffycity.dabanyu.com/payments/alipay/notify", # 异步回调结果通知地址
}
# 保利威视频加密服务[改成公司的]
POLYV = {
"userId": "fce2712a86",
"writeToken": "2f802363-1d15-4278-848d-29a9eda0d320",
"readToken": "5dc202ca-0838-411e-b3ab-1659ae007dad",
"secretkey": "uqlJtarKuJ",
"tokenUrl": "https://hls.videocc.net/service/v1/token",
}
luffycityapi/db_router.py,代码:
import random
class DBRouter(object):
def db_for_read(self, model, **hints):
"""
Reads go to a randomly-chosen replica.
"""
return random.choice(['read1',])
def db_for_write(self, model, **hints):
"""
Writes always go to primary.
"""
return 'default'
从服务端项目中收集静态文件
当Django运行在生产环境中,我们会关闭debug调试,那么项目将不再提供静态文件的支持,需要将静态文件交给静态文件的nginx服务器来提供访问支持。
我们先收集所有静态文件。项目中的静态文件除了我们使用的上传文件之外,django本身还有自己的静态文件,如rest_framework、simpleUI、admin、ckeditor等。我们需要收集这些静态文件,集中一起放到静态文件服务器中。
我们要将收集的静态文件放到项目的static目录中,所以先创建目录static。
Django提供了收集静态文件的方法。先在配置文件中配置收集之后存放的目录
settings/dev.py 和settings/pro.py,追加以下代码:
# 收集到的静态文件存储目录
STATIC_ROOT = BASE_DIR.parent / 'static'
因为目前在本地我们依然使用了dev.py配置文件通过manage.py运行项目,所以我们在本地项目收集静态文件就需要把配置信息同步到dev.py
在本地终端下luffycityapi根目录下执行收集命令
/home/moluo/Desktop/luffycity/luffycityapi
python manage.py collectstatic
执行收集命令以后,效果如下:
本地uwsgi测试
django项目通常使用uwsgi服务器或者gunicorn来运行
uwsgi/gunicorn服务器 同步模式运行项目
uvicorn服务器 异步模式运行项目
先修改luffycityapi/wsgi.py,加载线上的pro配置文件:
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'luffycityapi.settings.pro')
我们目前针对uwsgi服务器的了解还少,所以我们可以先在本地环境中先安装uwsgi模块进行使用,然后成功使用uwsgi启动项目,后面再一并上传到服务器上面重新安装并运行。
conda install -c conda-forge uwsgi
uwsgi --version # 查看 uwsgi 版本
在项目根目录下创建uwsgi配置文件uwsgi.ini
cd /home/moluo/Desktop/luffycity/luffycityapi
touch uwsgi.ini
注意:在manage.py的同级目录下创建uwsgi.ini配置文件。
[uwsgi]
# 使用nginx连接时使用,Django程序所在服务器地址[反向代理】
# socket=0.0.0.0:8000
# 直接做web服务器使用,Django程序所在服务器地址
http=0.0.0.0:8000
# 项目根目录[一定要使用绝对路径,否则无法访问]
chdir=/home/moluo/Desktop/luffycity/luffycityapi
# 项目中主应用下的wsgi.py文件的路径[相对于项目根目录]
wsgi-file=luffycityapi/wsgi.py
# 进程数[CPU逻辑核数]
processes=4
# 线程数[CPU线程数]
threads=4
# uwsgi服务器的角色[是否开启主进程管理模式]
master=True
# 存放进程编号的文件[相对于项目根目录]
pidfile=uwsgi.pid
# 日志文件,因为uwsgi可以脱离终端在后台运行,日志看不见。我们以前的runserver是依赖终端的[相对于项目根目录]
daemonize=logs/uwsgi.log
# 指定依赖的虚拟环境[聚堆路径,写到虚拟环境目录名]
virtualenv=/home/moluo/anaconda3/envs/luffycity
启动uwsgi服务器,必须在uwsgi.ini配置文件所在的目录中运行下面的命令
注意,在本地环境中必须先把runserver关闭
uwsgi --ini uwsgi.ini
停止uwsgi服务器,还可以通过使用kill命令停止
uwsgi --stop uwsgi.pid
访问项目 http://127.0.0.1:8000/nav/header/
把django项目代码上传部署到服务器并使用uwsgi服务器运行
在本地导出当前虚拟环境中的模块包列表到requirements.txt文件中
cd ~/Desktop/luffycity/luffycityapi
pip freeze > requirements.txt
# 注意:在requirements.txt中有conda安装的模块以 file://开头的,务必删除,否则将来报错。
# PyMySQL @ file:///home/conda/feedstock_root/build_artifacts/pymysql_1610208984992/work
# uWSGI @ file:///home/conda/feedstock_root/build_artifacts/uwsgi_1640027751896/work
# 另外,把django-haystack==3.2.1 改为 django-haystack==3.1.1
# django-oss-storage==1.1.1这个包也必须有,不然后面项目上线会无法加载
本地把服务端项目打包成luffycityapi.tar.gz,并在远程部署服务器中解压。
# 本地终端,执行打包命令并上传压缩包
cd ~/Desktop/luffycity/
tar -zcf luffycityapi.tar.gz luffycityapi
scp ~/Desktop/luffycity/luffycityapi.tar.gz root@114.115.200.1:/home/luffycity/
# 远程部署服务器终端,删除原来就api服务端代码
cd /home/luffycity
rm -rf luffycityapi
# 把上传到服务端的压缩包进行解压
tar -zxf luffycityapi.tar.gz
编写Dockerfile定制服务端项目的镜像
把素材目录下的Miniconda3-py38_4.10.3-Linux-x86_64.sh上传到远程部署服务器,代码操作:
scp /home/moluo/Desktop/luffycity/Miniconda3-py38_4.10.3-Linux-x86_64.sh root@114.115.200.1:/home/luffycity/
创建Dockerfile文件:
vim /home/luffycity/Dockerfile
/home/luffycity/Dockerfile,代码(本地ubuntu20.04版本gcc需选择4.8才能pip install uwsgi):
FROM ubuntu:20.04
LABEL maintainer="luffycity.Edu"
ENV PYTHONUNBUFFERED 1
COPY ./Miniconda3-py38_4.10.3-Linux-x86_64.sh /opt/
COPY ./sources.list /etc/apt/sources.list
COPY ./luffycityapi /luffycityapi
COPY ./luffycityapi/requirements.txt /requirements.txt
RUN apt-get update \
&& apt-get -y install wget \
&& mkdir /root/.conda \
&& bash /opt/Miniconda3-py38_4.10.3-Linux-x86_64.sh -b \
&& rm -f /opt/Miniconda3-py38_4.10.3-Linux-x86_64.sh
ENV PATH /root/miniconda3/bin:$PATH
RUN echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial main" >> /etc/apt/sources.list && echo "deb http://dk.archive.ubuntu.com/ubuntu/ xenial universe" >> /etc/apt/sources.list \
&& apt-get update && apt -y install gcc-4.8 && apt -y install g++-4.8 && cd /usr/bin && ln -s gcc-4.8 gcc && ln -s g++-4.8 g++
RUN apt-get -y install python3.8-dev\
&& pip install uwsgi --default-timeout=1000 \
&& pip install -r /requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple --default-timeout=1000 \
&& chmod -R 755 /luffycityapi
WORKDIR /luffycityapi
EXPOSE 8000
VOLUME /luffycityapi
注释教学版:
# 设置当前镜像的基础镜像,FROM指令只能使用一次,而且在Dockerfile顶部第一个使用
FROM ubuntu:20.04
# 设置描述信息
LABEL maintainer="luffycity.Edu"
# 设置环境变量,PYTHONUNBUFFERED设置python解析器在终端下stdout错误信息,一次性输出,要不在暂存区积攒
ENV PYTHONUNBUFFERED 1
# 复制文件 容器外部:容器内部
COPY ./Miniconda3-py38_4.10.3-Linux-x86_64.sh /opt/
COPY ./sources.list /etc/apt/sources.list
COPY ./luffycityapi /luffycityapi
COPY ./luffycityapi/requirements.txt /requirements.txt
# 运行系统命令
RUN apt-get update \ # 更新源
&& apt-get -y install wget \ # 安装wget下载工具
&& mkdir /root/.conda \
&& bash /opt/Miniconda3-py38_4.10.3-Linux-x86_64.sh -b \
&& rm -f /opt/Miniconda3-py38_4.10.3-Linux-x86_64.sh
ENV PATH /root/miniconda3/bin:$PATH # 设置环境变量
# 给conda全局base虚拟环境安装pymysql和uwsgi
RUN conda install pymysql -c conda-forge \
&& conda install uwsgi -c conda-forge \
# 递归安装requirements.txt中的所有依赖模块
&& pip install -r /requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple \
# 因为drf-haystack安装以后,会自动帮django-haystack降版本到3.1.0,但是django-haystack没有支持es7版本的类,所以在requirements.txt文件执行完成以后,重新把 django-haystack更新到3.1.1版本,实现兼容
&& pip install django-haystack==3.1.1 -i https://pypi.tuna.tsinghua.edu.cn/simple \
# 设置目录权限
&& chmod -R 755 /luffycityapi
# 设置进入容器的默认工作目录
WORKDIR /luffycityapi
# 暴露容器的端口
EXPOSE 8000
# 设置/luffycityapi目录为逻辑卷,方便创建容器时,可以把当前目录的内容映射到容器外面进行持久化存储
VOLUME /luffycityapi
vim /home/luffycity/sources.list
/home/luffycity/sources.list,代码:
deb http://repo.huaweicloud.com/ubuntu/ focal main restricted
deb http://repo.huaweicloud.com/ubuntu/ focal-updates main restricted
deb http://repo.huaweicloud.com/ubuntu/ focal universe
deb http://repo.huaweicloud.com/ubuntu/ focal-updates universe
deb http://repo.huaweicloud.com/ubuntu/ focal multiverse
deb http://repo.huaweicloud.com/ubuntu/ focal-updates multiverse
deb http://repo.huaweicloud.com/ubuntu/ focal-backports main restricted universe multiverse
deb http://repo.huaweicloud.com/ubuntu focal-security main restricted
deb http://repo.huaweicloud.com/ubuntu focal-security universe
deb http://repo.huaweicloud.com/ubuntu focal-security multiverse
通过docker-compose启动luffycityapi镜像,docker-compose.yml,代码:
version: '3.8'
services:
web1:
image: nginx:1.21.4
restart: always
ports:
- "80:80"
container_name: "web1"
volumes:
- ./luffycityweb:/home/luffycityweb
- ./conf/nginx/web.conf:/etc/nginx/conf.d/nginx.conf
networks:
- default
mysql_master:
image: mysql:8.0.26
restart: always
container_name: mysql_master
networks:
- default
environment:
- "MYSQL_ROOT_PASSWORD=root2022"
- "MYSQL_USER=luffycity"
- "MYSQL_PASSWORD=luffycity"
- "MYSQL_DATABASE=luffycity"
- "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:
- 3306:3306
volumes:
- ./data/mysql:/var/lib/mysql
- ./logs/mysql:/logs
- ./initdb:/docker-entrypoint-initdb.d/
redis_master:
image: redis:6.2.6-alpine3.15
restart: always
networks:
- default
ports:
- "6379:6379"
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
container_name: redis_master
volumes:
- ./conf/redis/master.conf:/usr/local/etc/redis/redis.conf
- ./data/redis/master:/data
elasticsearch:
image: elasticsearch:7.16.1
restart: always
environment:
- "discovery.type=single-node"
- "bootstrap.memory_lock=true"
- "ES_JAVA_OPTS=-Xms256m -Xmx256m"
- "xpack.security.enabled=false"
- "http.cors.enabled=true"
- "http.cors.allow-origin=*"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./data/elastic:/usr/share/elasticsearch/data
- ./logs/elastic:/var/log/elasticsearch
- ./plugins:/usr/share/elasticsearch/plugins
ports:
- 9200:9200
networks:
- default
api1:
build: .
image: luffycityapi:1.0.0
container_name: api1
restart: always
ports:
- 8000:8000
networks:
- default
volumes:
- ./luffycityapi:/luffycityapi
command: >
sh -c "uwsgi --http :8000 --workers 4 --master --enable-threads --module luffycityapi.wsgi"
environment:
- "DJANGO_SETTINGS_MODULE=luffycityapi.settings.pro"
depends_on:
- mysql_master
- redis_master
- elasticsearch
networks:
default:
启动服务
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml up -d
# 等待1分钟后给elasticsearch构建索引
docker exec -it api1 python manage.py rebuild_index
可以看到api已经启动了,但是我们通过浏览器http://api.luffycity.dabanyu.com:8000
访问发现,访问不了,我们尝试访问到后台运营站点admin,http://api.luffycity.dabanyu.com:8000/admin/
。
使用nginx对uwsgi进行反向代理
接下来,我们就需要把api1服务端容器里面的uwsgi的运行模式从http改成socket模式。
docker-compose.yml,修改api1里的http换成socket如下。
version: '3.8'
services:
nginx:
image: nginx:1.21.4
restart: always
ports:
- "80:80"
container_name: "nginx"
volumes:
- ./luffycityweb:/home/luffycityweb
- ./conf/nginx/web.conf:/etc/nginx/conf.d/nginx.conf
networks:
- default
mysql_master:
image: mysql:8.0.30
restart: always
container_name: "mysql_master"
networks:
- default
environment:
- "MYSQL_ROOT_PASSWORD=root2022"
- "MYSQL_USER=luffycity"
- "MYSQL_PASSWORD=luffycity"
- "MYSQL_DATABASE=luffycity"
- "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:
- 3306:3306
volumes:
- ./data/mysql:/var/lib/mysql
- ./logs/mysql:/logs
- ./initdb:/docker-entrypoint-initdb.d/
redis_master:
image: redis:6.2.6-alpine3.15
restart: always
networks:
- default
ports:
- "6379:6379"
command:
["redis-server", "/usr/local/etc/redis/redis.conf"]
container_name: "redis_master"
volumes:
- ./conf/redis/master.conf:/usr/local/etc/redis/redis.conf
- ./data/redis/master:/data
elasticsearch_1:
image: elasticsearch:7.16.1
restart: always
container_name: "elasticsearch_1"
environment:
- "discovery.type=single-node"
- "bootstrap.memory_lock=true"
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- "xpack.security.enabled=false"
- "http.cors.enabled=true"
- "http.cors.allow-origin=*"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- ./data/elastic:/usr/share/elasticsearch/data
- ./logs/elastic:/var/log/elasticsearch
- ./plugins:/usr/share/elasticsearch/plugins
ports:
- 9200:9200
networks:
- default
api1:
build: .
image: luffycityapi:1.0.0
container_name: api1
restart: always
ports:
- 8000:8000
networks:
- default
volumes:
- ./luffycityapi:/luffycityapi
command: >
sh -c "uwsgi --socket :8000 --workers 4 --master --enable-threads --module luffycityapi.wsgi"
environment:
- "DJANGO_SETTINGS_MODULE=luffycityapi.settings.pro"
depends_on:
- mysql_master
- redis_master
- elasticsearch_1
networks:
default:
修改nginx配置文件
vim conf/nginx/web.conf
conf/nginx/web.conf,如下:
upstream luffycity {
server 114.115.200.1:8000;
# server 114.115.200.1:8001;
}
server {
listen 80;
server_name api.luffycity.dabanyu.com;
location / {
include uwsgi_params;
uwsgi_pass luffycity;
}
}
server {
listen 80;
server_name www.luffycity.dabanyu.com;
# client_max_body_size 1000M;
# root /opt/dist;
# client_max_body_size 20M;
# Load configuration files for the default server block.
location / {
root /home/luffycityweb;
index index.html;
try_files $uri $uri/ /index.html;
}
}
重启docker-compose
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml up -d
# 接下来,我们直接在浏览器中,通过如下地址访问:
http://api.luffycity.dabanyu.com/home/nav/header/
http://www.luffycity.dabanyu.com/
针对后端服务器的运营站点提供静态文件的访问支持
直接让前端的nginx容器同时提供服务端静态文件的访问支持。
修改docker-compose.yml,把服务端的静态文件目录映射到nginx容器中。代码:
version: '3.8'
services:
web1:
image: nginx:1.21.4
restart: always
ports:
- "80:80"
container_name: "web1"
volumes:
- ./luffycityapi/static:/home/luffycityapi/static
- ./luffycityweb:/home/luffycityweb
- ./conf/nginx/web.conf:/etc/nginx/conf.d/nginx.conf
。。。。。
修改nginx配置文件web.conf,服务端对应的server配置项中增加一个location配置信息。conf/nginx/web.conf,如下:
upstream luffycity {
server 114.115.200.1:8000;
# server 114.115.200.1:8001;
}
server {
listen 80;
server_name api.luffycity.dabanyu.com;
location / {
include uwsgi_params;
uwsgi_pass luffycity;
}
location /static {
root /home/luffycityapi;
}
}
。。。。
完成上面操作以后,我们可以看到当前api服务端的静态文件就提供了http正常浏览了。
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml up -d
因为服务端的访问端口从8000变成了80,所以此处我们需要重新调整下客户端的settings.js中的url路径的值。
终端下,在前端项目的根目录下,执行以下命令:
cd /home/moluo/Desktop/luffycity/luffycityweb
rm -rf /home/moluo/Desktop/luffycity/luffycityweb/dist
vite build
# 线上服务器操作[远程部署终端]
rm -rf /home/luffycityweb.tar.gz
# 本地ubuntu终端,通过scp上传客户端代码到线上服务器
tar -zcf luffycityweb.tar.gz dist
scp ~/Desktop/luffycity/luffycityweb/luffycityweb.tar.gz root@114.115.200.1:/home/
# 线上服务器操作[远程部署终端]
cd /home
tar -zxf luffycityweb.tar.gz
rm -rf /home/luffycity/luffycityweb
mv /home/dist /home/luffycity/luffycityweb
再次重启docker-compose
cd /home/luffycity
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml up -d
启动celery
关于celery的启动,因为需要依赖于django项目的。所以celery的启动自然也就和当前api容器一并运行。
修改celery的启动配置文件
vim /home/luffycity/luffycityapi/scripts/luffycity_celery_worker.ini
代码:
[program:luffycity_celery_worker]
# 启动命令 conda env list
command=/root/miniconda3/bin/celery -A luffycityapi worker -l info -n worker1
# 以管理员身份执行运行celery
user=root
numprocs=1
# 项目根目录的绝对路径,通过pwd查看
directory=/luffycityapi
# 项目虚拟环境
enviroment=PATH="/root/miniconda3/bin"
# 输出日志绝对路径
stdout_logfile=/luffycityapi/logs/celery.worker.info.log
# 错误日志绝对路径
stderr_logfile=/luffycityapi/logs/celery.worker.error.log
# 自动启动,开机自启
autostart=true
# 重启
autorestart=true
# 进程启动后跑了几秒钟,才被认定为成功启动,默认1
startsecs=10
# 进程结束后60秒才被认定结束
stopwatisecs=60
# 优先级
priority=999
vim /home/luffycity/luffycityapi/scripts/luffycity_celery_beat.ini
代码:
[program:luffycity_celery_beat]
# 启动命令 conda env list
command=/root/miniconda3/bin/celery -A luffycityapi beat -l info
# 项目根目录的绝对路径,通过pwd查看
directory=/luffycityapi
# 项目虚拟环境
enviroment=PATH="/root/miniconda3/bin"
# 输出日志绝对路径
stdout_logfile=/luffycityapi/logs/celery.beat.info.log
# 错误日志绝对路径
stderr_logfile=/luffycityapi/logs/celery.beat.error.log
# 自动启动,开机自启
autostart=true
# 重启
autorestart=true
# 进程启动后跑了几秒钟,才被认定为成功启动,默认1
startsecs=10
# 进程结束后60秒才被认定结束
stopwatisecs=60
# 优先级
priority=998
vim /home/luffycity/luffycityapi/scripts/luffycity_celery_flower.ini
代码:
[program:luffycity_celery_flower]
# 启动命令 conda env list
command=/root/miniconda3/bin/celery -A luffycityapi flower --port=5555
# 项目根目录的绝对路径,通过pwd查看
directory=/luffycityapi
# 项目虚拟环境
enviroment=PATH="/root/miniconda3/bin"
# 输出日志绝对路径
stdout_logfile=/luffycityapi/logs/celery.flower.info.log
# 错误日志绝对路径
stderr_logfile=/luffycityapi/logs/celery.flower.error.log
# 自动启动,开机自启
autostart=true
# 重启
autorestart=true
# 进程启动后跑了几秒钟,才被认定为成功启动,默认1
startsecs=10
# 进程结束后60秒才被认定结束
stopwatisecs=60
# 优先级
priority=990
vim /home/luffycity/luffycityapi/scripts/supervisor.service
代码:
[Unit]
Description=supervisor
After=network.target
[Service]
Type=forking
ExecStart=/root/miniconda3/bin/supervisord -n -c /luffycityapi/scripts/supervisord.conf
ExecStop=/root/miniconda3/bin/supervisorctl $OPTIONS shutdown
ExecReload=/root/miniconda3/bin/supervisorctl $OPTIONS reload
KillMode=process
Restart=on-failure
RestartSec=42s
[Install]
WantedBy=multi-user.target
记得开启服务端的安全组,开发5555端口。
docker-compose,代码:
version: '3.8'
services:
....
api1:
build: .
image: luffycityapi:1.0.0
container_name: api1
restart: always
ports:
- 8000:8000
networks:
- default
volumes:
- ./luffycityapi:/luffycityapi
command: >
sh -c "supervisord -c scripts/supervisord.conf && uwsgi --socket :8000 --workers 4 --master --enable-threads --module luffycityapi.wsgi"
environment:
- "DJANGO_SETTINGS_MODULE=luffycityapi.settings.pro"
- "C_FORCE_ROOT=1"
depends_on:
- mysql
- redis
- elasticsearch
networks:
default:
启动服务
docker-compose -f docker-compose.yml down
docker-compose -f docker-compose.yml up -d