基于docker的django + mysql + nginx应用部署实践

文章目录

  • 引言
  • 一台服务器
  • 前置知识
  • 开始部署流程
    • 下载运行docker
    • 新建用户组
    • 编写Dockerfile
      • django Dockerfile
      • Nginx Dockerfile
    • 容器快速编排
      • linux上安装docker-compose
      • 编写 docker-compose.yml
    • 项目启动

引言

最近这段时间空闲比较多,稍微研究了一下docker。并尝试使用它部署了一个基于Django的后端应用。前前后后遇到过不少问题,在这里做一个流程的总结。

下面就来开始愉快的踩坑之旅吧~

一台服务器

首先,得有台Linux服务器对吧(我的阿里云快到期了,然而续费好贵…),系统对版本没有特别的要求,这里不得不佩服Docker镜像的强大(你可以把他当成一种文件系统,通过它,我们可以构建一个轻量级的“虚拟机”,项目都在这个“虚拟机”里跑)

我的服务器是系统是Centos

前置知识

使用Docker之前,我们先来做一个简单的了解的吧:

Docker从入门到实践

无耻的放上链接:P,链接里的解释我觉得已经十分的详细,就不重复赘述了。
直接开始操作吧~

开始部署流程

下载运行docker

我这里在CentOS下进行安装,为了能够快速安装(自带的源太慢鸟),首先添加国内的yum软件源

sudo yum-config-manager --add-repo https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo

接下来我们安装一些依赖包

sudo yum install -y yum-utils device-mapper-persistent-data lvm2

安装本体(Docker CE

sudo yum makecache fast
sudo yum install docker-ce

启动

sudo systemctl enable docker
sudo systemctl start docker

果然还是linux方便,一行行命令直接敲下来,安装启动就完成了

我们来测试一下docker是否正确的安装:

docker run hello-world

如果出现以下字样,则说明安装已经成功完成了安装

Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/

如果在 CentOS 使用 Docker CE 看到下面的这些警告信息:

WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled

请添加内核配置参数以启用这些功能。

sudo tee -a /etc/sysctl.conf <<-EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF

然后重新加载 sysctl.conf 即可

sudo sysctl -p

新建用户组

默认情况下,docker 命令会使用 Unix socket 与 Docker 引擎通讯。而只有 root 用户和 docker 组的用户才可以访问 Docker 引擎的 Unix socket。出于安全考虑,一般 Linux 系统上不会直接使用 root 用户。因此,更好地做法是将需要使用 docker 的用户加入 docker 用户组。

既然这么说的话,我们就来新建一个docker用户组

sudo groupadd docker

将我们的要操作docker的用户加入(这里我的用户名是develop):

sudo usermod -aG docker develop

好了,现在我们用这个账户来进行操作

编写Dockerfile

django Dockerfile

为了构建适合项目部署的镜像,我们需要编写Dockerfile配置镜像构建时的行为。
Django项目为例,我们在项目根目录下新建一个文件,名称就叫Dockerfile

由于linux下载软件默认是从国外下载,所以如果网速不行的话,我们可以考虑将软件源换成国内的,下面放上一个参考链接:

Ubuntu 16.04配置国内高速apt-get更新源

下面我们来看看Dockerfile的内容:

# 选择基础镜像,如果没有事先下载的话会先执行下载操作,也可以直接下载centos镜像,但是在下面就需要额外配置python环境
FROM python:3.6

# 创建目录
RUN mkdir /easywork_workspace

# 将上面的目录设置为工作目录(这个工作目录是镜像内的)
WORKDIR /easywork_workspace

# 将当前目录加入到工作目录中(注意这个".",表示当前我们的项目目录,别漏了)
ADD . /easywork_workspace

RUN mv /etc/apt/sources.list /etc/apt/sources.list.bak && \
    mv sources.list /etc/apt/

# 这里使用豆瓣源安装python第三方库
RUN pip install -i https://pypi.douban.com/simple --upgrade pip && \
    pip3 install -i https://pypi.douban.com/simple -r requirements.txt

# 对外暴露端口,这里的端口也是指的镜像的端口,用来和主机对接
EXPOSE 80 8080 8000
# 设置环境变量
ENV SPIDER=/easywork_workspace

脚本注释解释的比较清楚了,这里稍微解释下RUNADDEXPOSEENV等命令,这些是Dockerfile自带的指令,用于指定构建行为。

由于dockerfile的构建过程是分层执行的,文件中每一条指令就代表一层以及这一层是如何构建的。通常在构建的过程中,不推荐添加太多层级,资源的浪费不说,还容易出错。

关于docker镜像分层的概念,依旧请参考:)

Docker从入门到实践:镜像

Nginx Dockerfile

没错,nginx也放到了docker中运行,我们除了要编写Nginx配置文件之外,也要为其编写一个Dockerfile,先来看看Dockerfile的内容:

# nginx镜像
FROM nginx    

# 对外暴露端口
EXPOSE 80 8000 8080
# 删除镜像自带的配置文件
RUN rm -rf /etc/nginx/conf.d/*.conf
# 添加我们自己的配置文件道镜像
ADD nginx.conf /etc/nginx/
# 复制vue静态资源文件(如果项目有前端也需要部署的话~~)
COPY dist/ /usr/share/nginx/html/
# 创建静态资源文件夹
RUN mkdir -p /usr/share/nginx/html/static

Dockerfile行为注释写的也比较清楚了,就不多做解释。

接下来我们来编写一下nginx配置文件(顺便简单的了解一些nginx配置项,偏题了…)

# 配置权限用户
user nginx;
# 配置Nginx要开启的进程数
worker_processes  1;
# 事件模块
events {
	# use epoll # 不写的话,nginx也会自动选择
	# 定义每条woker的进程连接数量
    worker_connections  1024;
}
# http 模块,外到内有http块、server块、location块,同时各个模块有各自的属性元素
# 一个http处理模块内部可进行http的相关参数配置,内可以包含多个server块 
http {
	# 负载均衡相关,暂不考虑
	# upstream backend{
	#    ip_hash;
	#    server xxx.xxx.x.x;
	#    server xxx.xxx.x.x:8080;
	#    server xxx.xxx.x.x max_fails=5 fail_timeout=30s;
	#    server xxx.xxx.x.x down;
	# }
	
	# 指定服务端向客户端发送的请求头对应的文件格式
    include       mime.types;
    default_type  application/octet-stream;
    # 启动以提高 Nginx 静态资源托管效率
    sendfile        on;
    # 设置长连接超时时间
    keepalive_timeout  65;
    # 配置访问错误时返回的内容
    error_page   500 502 503 504  /50x.html;
	# 配置虚拟机
    server {
    	# 配置监听的端口
        listen       80;
        # 配置虚拟主机机名称,用于与http请求header头部的Host匹配
        server_name  localhost;
		# 配置定位
        location / {
        	# 配置重定向
            try_files $uri $uri/ /index.html;
            # 配置文件路径
	    	root   /usr/share/nginx/html;
	    	# 设置缓存过期时间
            expires 1h;
        }
		location = /50x.html {
	        root   /usr/share/nginx/html;
	    }
    }

    server {
    	# 限制请求body的大小,超过返回 413 request entity too large
        client_max_body_size 20M;
        listen       8000;
        server_name  localhost:8000;

        location / {
        	# 配置限速
            limit_rate 256k;
            # 配置代理转发
            proxy_pass http://web:8000;
            # 配置http版本
            proxy_http_version 1.1;
            # 配置请求头
            # 配置协议升级,主要用于反向代理websocket,这里好像没啥作用
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection keep-alive;
            proxy_set_header Host $host;
            # 配置缓存抓取策略
            proxy_cache_bypass $http_upgrade;
        }

        location /static/ {
            root /data1;
        }
    }
}

唯一一点让人产生疑惑的地方

proxy_pass http://web:8000;

这个web既不是ip,也不是域名,这啥?
带着这个问题继续往下走~

参考:
nginx的worker_processes优化
nginx三大模块——事件模块
nginx中的http模块配置
nginx配置详解之http模块
MIME与mime.types
nginx send file
nginx中keepalive_timeout参数的说明
关于Nginx的server_name
try_files指令说明
nginx的location、root、alias指令用法和区别
Nginx expires缓存
nginx error_page详解
nginx 限速指令limit_rate
nginx 之 proxy_pass详解
nginx配置http为1.0到1.1
Nginx 代理 WebSocket
Nginx proxy_set_header 理解
Nginx缓存

容器快速编排

Dockerfile写好了之后,镜像的构建算是完成了,我们可以从构建的镜像中实例化若干容器出来进行运行。

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的 类 和 实例 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
一个容器实质上是一个完全独立的进程

实际情况中,我们往往需要多个容器互相配合协作来完成任务,那么如何来编排管理这么多容器呢?

我们使用docker官方的compose来完成这个任务

linux上安装docker-compose

sudo curl -L https://github.com/docker/compose/releases/download/1.17.1/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

很简单完成了安装:)

编写 docker-compose.yml

我们通过编写docker-compose.yml来定义一组相关联的容器为一个项目,那么直接来看看文件的内容吧

# 打印版本的信息,没啥大用
version: "3"

services:
  db:
    image: mysql:5.7  # mysql镜像
    # 预设置数据库
    environment:
      - MYSQL_DATABASE=database_name
      - MYSQL_PASSWORD=xxxxxxx
      - MYSQL_ROOT_PASSWORD=xxxxxxx
    volumes:
    	# 宿主机路径:容器内数据库数据文件路径
      - /home/root/docker_db:/var/lib/mysql  # 将宿主机与容器中的文件映射
    restart: always  # 若容器运行出现问题,会自动重启容器
    
  # 这个地方的web就对应nginx配置文件中的proxy_pass http://web:8000
  web:
  	# 构建项目容器的路径
    build: ./easywork
    volumes:
    - ./easywork:/easywork
    - /tmp/logs:/tmp
    command: bash start.sh  # 执行命令,有多种格式,可以写在脚本中执行,也可以向下面这样直接使用命令执行
    # command: python3 manage.py runserver 0.0.0.0:8000
    
    # 在we和db间创建一个网络,使得容器能够通过名字反问另外一个容器
    links:
    - db
    # 定义容器间的依赖关系,比较直观一点就是启动顺序会严格按照depend_on顺序启动
    depends_on:
    - db
    restart: always

  nginx:
    build: ./nginxd
    # 绑定容器端口到主机端口
    ports:
    - "80:80"
    - "8000:8000"
    volumes:
    - ./easywork/static:/usr/share/nginx/html/static:ro
    links:
    - web
    depends_on:
    - web
    restart: always

稍微需要注意的一点是,web也就是我们的django项目连接db(mysql)的时候,在项目的settings.py文件中,需要把相应的数据库连接的HOST改为容器名称(这里为db)

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'database_name',
        'USER': 'root',
        'PASSWORD': 'xxxxxxxx',
        'HOST': 'db',  # 这里改成容器的名字
        'PORT': 3306,
        'CHARSET': 'utf8',
        'TEST': {
            'CHARSET': 'utf8',
            'COLLATION': 'utf8_general_ci',
        },
    },
}

nginx中同理。

其他解释都放到注释中去了,这里就不再赘述。

项目启动

基本的配置已经编写完成了,我们进入到docker-compose.yml所在的目录下
首先,构建镜像:

docker-compose build

如果构建失败的话,请仔细阅读失败信息,并修改对应的Dockerfile
构建成功后,使用docker images命令查看,会发现除了官方镜像之外,多了两个新的镜像

REPOSITORY              TAG                 IMAGE ID            CREATED             SIZE
# 前面这俩是你构建的
xxxx_xxxxx              latest              f14889f32614        3 days ago          126MB
xxxxx_xxx2              latest              10409b376f3f        3 days ago          973MB
##
nginx                   latest              5a3221f0137b        8 days ago          126MB
python                  3.6                 cfcdf565ff94        9 days ago          913MB
mysql                   5.7                 e1e1680ac726        10 days ago         373MB
hello-world             latest              fce289e99eb9        7 months ago        1.84kB

接下来执行

docker-compose up

生成容器并启动,所有的信息都会打印在前台,如果没有问题的话,此时项目就已经成功运行了。

如果没有,同样请查看容器运行的打印信息,一般报错信息都很明显

docker的容器编排十分灵活,我们可以再次基础上添加其他容器实现项目需求等。
另外docker的其他功能也十分强大,推荐有空瞅瞅~:)

你可能感兴趣的:(nginx,django)