最近做了一个小的Web项目,是基于Django2.x开发的,想要部署到云服务器上。因为在之前经常使用Ubuntu作为服务器系统进行项目的部署,但据了解国内好像更愿意选择更新频率更低,更为稳定的CentOS作为服务器系统,所以这次特意将我的服务器更换为CentOS8.0来试试手。而选择Docker则是为了能让我在一个沙箱环境中随意折腾,不用担心会破坏宿主机。

此贴主要是为了记录一下项目部署的过程,方便以后自己回顾,也可以为大家提供一点参考。

闲话少说,开干!

1. 项目文件上传

好多小伙伴在本地开发完项目,不知道怎么上传到服务器上,我在这里交给小伙伴们一种方式,大神们请忽略。我使用的开发工具是PyCharm,这个开发工具可以直接配置远程服务器,然后将项目文件上传到服务器上。

1.1 配置服务器地址

找到菜单栏中Tools -> Deployment -> Configuration

centos 部署 sendemail centos部署django项目_django

打开以后,点击左上角加号,选择SFTP,在弹窗中输入配置名称,新建服务器配置,创建好的配置如下图所示。

centos 部署 sendemail centos部署django项目_docker_02

Host中输入你的远程服务器的IP地址,User name中输入系统用户,Password中输入系统用户的登录密码,其他的选项默认即可。

centos 部署 sendemail centos部署django项目_centos 部署 sendemail_03

配置完成后,可点击Test Connection按钮测试是否能够连接远程服务器。

centos 部署 sendemail centos部署django项目_centos_04

仅仅连接到远程服务器还不够,还需要对本地项目文件路径,以及要上传到远程服务器上的目标路径进行设置。还在Deployment窗口中,切换到Mappings选项卡,Local path选择项目文件根目录,Deployment path选择远程服务器上的目标路径(若之前的路径配置正确,并测试连接成功,在此处,点击输入框后的文件夹图标,可以直接显示远程服务器的磁盘目录)。

centos 部署 sendemail centos部署django项目_django_05

1.2 上传项目文件

路径映射配置完毕以后,就可以上传项目文件了。在PyCharm左侧的项目目录中,选择项目根目录,单击右键,找到Deployment,选择Upload to '服务器名称'这里的服务器名称指的是刚才创建服务器配置时的名称。

centos 部署 sendemail centos部署django项目_docker_06

等待一段时间以后,当看到Upload to '服务器名称' completed: xxx file transferred的提示时,说明项目文件已经上传完毕(因为我之前已经上传过了,所以我就只上传了一个文件,给大家演示一下)。

centos 部署 sendemail centos部署django项目_centos_07

2. Docker安装

Docker的优点我就不再赘述了,不了解的小伙伴们自己百度一下吧,安装方法的话,英语好的同学也可以参考官网文档:https://docs.docker.com/engine/install/centos/

2.1 安装yum-utils

这是一个管理repository及扩展包的工具(主要是针对repository),这里我们安装yum-utils也是为了一会添加Docker的repo源。

yum install -y yum-utils

2.2 添加docker.repo源

这里再和大家解释一下为什么要添加源,熟悉Linux系统的同学请自动忽略。手动添加repo是为了让系统从该repo以及CentOS官方源中查找更新,这样通常是为了允许开发人员比官方存储库中更快的提供更新。

yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

看到这里有人可能会有疑问了:我们不是安装Docker吗,那这个docker-ce又是个嘛玩意儿?这里先卖个关子,后面再向大家解释docker-ce的含义。

2.3 安装containerd.io

在CentOS8.0中安装Docker时,还需要依赖一个容器服务也就是containerd.io,那么containerd是什么呢,有兴趣的同学可以参考:https://github.com/containerd/containerd/blob/master/docs/getting-started.md,我就不多做解释了。

dnf install https://download.docker.com/linux/centos/8/x86_64/stable/Packages/containerd.io-1.3.7-3.1.el8.x86_64.rpm

CentOS8启用了DNF,作为系统软件包管理工具,DNF包管理器克服了YUM包管理器的一些瓶颈,提升了包括用户体验,内存占用,依赖分析,运行速度等多方面的内容。

2.4 安装Docker

yum install -y docker-ce docker-ce-cli

这里又出现了docker-ce,docker-ce指的是什么呢?现在为大家来解释一下,其实在2017年3月,Docker在原来的基础上分为两个分支版本:Docker CE和Docker EE。Docker CE即社区版(免费哦),Docker EE即企业版(得花钱)。那由于这只是一个个人的小项目(主要是因为穷),我们只需要安装免费的Docker CE即可。

2.5 启动Docker

Docker安装完成后不会自动启动服务,需要我们手动开启。

systemctl enable docker
systemctl start docker

可以通过docker -v或docker version命令查看版本信息,来判断docker服务是否成功启动。

2.6 Docker加速

我们在下载需要的镜像时,默认是直接从国外的官方镜像仓库中下载的,往往需要花费很长的时间。我们可以通过为docker配置加速器,以提高下载速度。

这里我们选择的是阿里云的镜像加速器:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors,访问前面的网址,注册/登录阿里云账号后,可以免费获取镜像加速器的地址。

centos 部署 sendemail centos部署django项目_centos 部署 sendemail_08

打开/etc/docker/daemon.json,将前面获取到的阿里云镜像加速器地址配置到该文件中,内容如下:

{
  "registry-mirrors": ["这里是你自己的镜像加速器地址"]
}

编辑完成后,需要重启服务让配置生效:

systemctl daemon-reload
systemctl restart docker

3. 部署数据库

这里我使用的数据库是MySQL数据库,既然前面都已经安装了Docker,那么数据库也一并放到容器里吧。

3.1 下载MySQL镜像

docker pull mysql

这里默认拉取的是最新版的MySQL,版本号可以通过docker images来查看。

3.2 创建桥接网络

因为MySQL是一个单独的容器,而容器与容器之间又是相互隔离的,所以,为了让运行后端服务的容器与MySQL容器之间可以相互通信,需要在创建并启动容器时,指定桥接网络network,同在一个桥接网络中的容器之间可以相互通信。所以我们需要先创建一个桥接网络:

docker network create mynet  // mynet是创建的桥接网络的名称,一会创建容器时,需要用到

关于桥接网络的其他操作,可以通过docker network命令来查看。

3.3 启动MySQL

现在就可以启动一个运行MySQL镜像的容器了,运行容器时,还需要指定端口、root用户口令、桥接网络及其他相关资源目录,格式为docker run -参数名 参数值 镜像名:版本号,若不指定版本号,版本默认为latest。

docker run -itd --network mynet --name mydb -p 3307:3306 -v /opt/mysql/sql:/opt/sql -e MYSQL_ROOT_PASSWORD=123456 mysql

参数解释:

-itd这里其实是-i -t -d的一种简写,这三个参数组合起来的意思就是以交互模式运行容器,并为容器分配一个伪输入终端,容器运行在后台。容器运行后,可以通过docker exec -it mydb bash命令进入容器。

--network表示该容器在哪一个桥接网络中,后面的参数值就是我们之前创建的mynet这个桥接网络。

--name这个最容易理解,表示该容器的名称。

-p表示指定端口映射,前面的3307表示宿主机的端口,后面的3306表示容器的端口,意思就是将宿主机的3307端口映射到容器的3306端口,当需要访问容器的3306端口时,只需要访问宿主机的3307端口即可。

-v挂载本地目录,格式为-v 容器目录-v 本地目录:容器目录,这样一来,容器中的sql资源就会与本地目录同步,我们只需要修改本地的sql资源,并重新启动容器,即可实现资源更新。

-e指定MySQL的root用户的口令(密码),我设置的是123456,如果不指定口令,容器中MySQL的root用户默认无口令。

3.4 数据库初始化

在项目启动前,我们需要提前搭建项目数据库,需要执行初始化的sql脚本,该脚本包含在项目文件目录中。执行初始化脚本有两种方式,一种是在项目的Dockerfile中执行,另一种是手动在服务器端执行(我们选择手动执行)。

因为启动容器时,我们挂在了本地目录,所以只需要将init.sql脚本复制到/opt/mysql/sql目录中,该脚本文件将会被自动同步到容器的/opt/sql目录中。

cp /项目文件目录/init.sql /opt/mysql/sql/init.sql

然后需要进入到容器中,执行该脚本。

//先从宿主机切换到容器的终端环境
docker exec -it mydb bash

//此时已经进入到容器的伪输入终端,还需要进入到MySQL中
mysql -uroot -p123456

//进入MySQL后,执行初始化脚本
source /opt/sql/init.sql

4. 构建项目镜像

我们想要将项目运行到容器中,就需要这个容器中有一个可以支持项目运行的系统。这里我们下载了一个Ubuntu的镜像,然后将我们的项目文件与Ubuntu镜像,重新构建一个项目镜像,这样,就可以将程序运行到容器中了。

4.1 下载Ubuntu镜像

这里大家要注意一下,Docker的镜像仓库中,除了有官方的镜像,也会有其他人上传的自定义的镜像,可以使用docker search ubuntu命令来搜索Docker镜像仓库中的ubuntu镜像

centos 部署 sendemail centos部署django项目_docker_09

倒数第二列OFFICIAL如果是OK的话,说明就是官方的镜像,并且也可以通过STARS的数量判断镜像的质量,这里我们拉取的就是第一个官方的Ubuntu镜像。

docker pull ubuntu

4.2 编写Dockerfile

我们想要自己定制一个镜像,那Dockerfile是必不可少的,它是一个用来构建镜像的文本文件,文本内容包含了一条条构建镜像所需的指令和说明。

FROM ubuntu
MAINTAINER HOU houhaixu_email@163.com
ADD . /usr/src/
WORKDIR /usr/src
RUN ./init.sh
CMD /usr/src/run.sh

在这里也跟小伙伴们解释一下每一行的意思:

FROM ubuntu这句话比较好理解,我们定制的镜像是基于FROM的镜像,我们定制的镜像就是基于前面拉取的Ubuntu的镜像。

MAINTAINER HOU houhaixu_email@163.com指定该镜像的作者以及联系方式。

ADD . /usr/src/这是一个复制命令,是将当前目录下的项目文件复制到Ubuntu镜像启动的临时容器中的/usr/src/目录中。

WORKDIR /usr/src设置工作目录,将临时容器的工作目录切换到我们刚才拷贝项目文件的目录下,后一般跟RUNCMDCOPY等。

RUN ./init.sh执行项目文件中的初始化脚本,这个脚本一般用于在镜像系统中配置安装项目运行的必要组件。

CMD /usr/src/run.sh设置容器启动时,要运行的命令或脚本,这样启动容器后,容器中的后端服务自动运行。

4.3 编写init.sh文件

前面已经提到了init.sh文件的主要作用是在镜像系统中配置安装项目运行的必要组件。本文所编写的文件主要针对于Ubuntu 18.04版本的镜像,用于安装Python3环境,以及其他项目运行所需的依赖包。

#!/bin/bash

cat sources.list > /etc/apt/sources.list
apt update
apt install python3 python3-pip -y
apt install -y build-essential zlib1g-dev libncurses5-dev libgdbm-dev libnss3-dev libssl-dev libreadline-dev libffi-dev
apt install -y unixodbc unixodbc-dev libmysqlclient-dev
pip3 install -r requirements.txt  -i https://mirrors.aliyun.com/pypi/simple
pip3 install gunicorn -i https://mirrors.aliyun.com/pypi/simple

熟悉Linux系统的小伙伴应该知道,咱们在下载一些项目资源的时候,默认使用的是Ubuntu官方的源,下载速度非常慢,网络不好的时候,还会经常超时报错。所以我们通常会手动的修改为国内源,init.sh文件中的第一句命令,就是将系统的源设置为阿里云的源,sources.list文件位于系统根目录下,内容如下:

deb http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-security main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-updates main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-backports main restricted universe multiverse

deb http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ bionic-proposed main restricted universe multiverse

4.4 编写run.sh文件

当项目环境及项目依赖配置好后,我们还需要一个启动项目的脚本,在前面4.2编写Dockerfile中,Dockerfile的最后一行CMD /usr/src/run.sh,实际上就是在执行我们的启动脚本,这里名字起什么都无所谓,只要和Dockerfile中对应起来即可。这个脚本非常简单,只需要一行命令即可:

#!/bin/bash

gunicorn EQMS.wsgi:application -w 2 -b 0.0.0.0:8000

有同学要问了,不是一行命令吗,你这里怎么写了两行?其实大家会发现,我们在前面编写init.sh的时候,文件的第一行也出现了#!/bin/bash,这个是什么意思呢?这个啊其实只是为了告诉系统,这个shell脚本是哪一种shell脚本,例如我们这里写的两个脚本都是bash,Linux中还有很多其他的shell脚本,例如:sh、csh、ksh等等。这里就不多说了,感兴趣的同学自己去百度哈。

再来跟大家解释一下脚本中的这一行命令gunicorn EQMS.wsgi:application -w 2 -b 0.0.0.0:8000,Gunicorn绿色独角兽是一个unix上被广泛使用的高性能的Python WSGI UNIX HTTP Server,这个服务器与大部分web框架兼容,并且实现简单,高性能,消耗系统资源较少。这里的-w指的是工作进程的数量,因为Gunicorn是依靠操作系统来实现的负载均衡,所以我们这里设置工作进程数量为2。-b指的是与指定的socket进行绑定,通俗的讲就是访问地址,这里设置的是任意ip地址通过8000端口进行访问。

4.5 构建项目镜像

准备工作完成后,我们将新添加的几个文件,通过1中所讲的方法,上传至服务器后,我们通过ssh或者其他连接工具,连接至服务器。通过cd命令切换到Dockerfile所在的项目目录下,然后执行构建项目镜像的命令:

docker build -t myproj:latest .

-t也可以写作--tag,用来设置镜像名字以及标签,通常的格式是name:tag,前面是名字,后面是标签(可以视为镜像的版本)。

【注意】大家在执行命令的时候,不要漏掉最后的.,这个代表的是使用哪个目录下的Dockerfile构建项目镜像,因为我们当前已经切换到了Dockerfile所在的目录,所以可以使用.来表示当前目录。

若构建过程中,出现一些警告,不用担心,不会影响整体的进度,若镜像构建成功,则可以通过docker images命令,查看镜像列表中自己构建的镜像。

4.6 运行容器

现在到了最激动人心的时刻了,镜像创建成功,现在可以运行容器了。

docker run -itd --name myserver -p 8000:8000 --network mynet -v /projects/myproj:/usr/src/ myproj

/projects/myproj是我的项目在服务器中存放的路径,这里大家根据自己的实际情况填写。

总结

好了到这里整个项目就算是部署成功了,小伙伴们快点在浏览器中访问一下试试吧。如果想玩的更高级一点,还可以下载一个Nginx,做一个反向代理和负载均衡,有兴趣的小伙伴们自行研究一下吧。

ps:这里再跟大家分享一个查看错误的方法,如果大家在运行容器时失败,或者容器启动以后,项目无法访问,可以输入docker logs 容器名来查看gunicorn启动项目时的日志信息,这样就可以检查错误信息了。