1. 程式人生 > >Docker部署Web應用(Django)

Docker部署Web應用(Django)

之前部署Web應用,沒用docker,直接在伺服器上部署,使用了fabric+nginx+supervisor+gunicorn部署,可檢視我寫過的一片部落格:部署Web應用。但後來瞭解了docker,為其“Build,Ship and Run Any App,Anywhere”的思想所折服,覺得這個太牛逼了,所以我也嘗試一下自己用docker部署Web應用。本篇文章為了記錄我用docker部署web應用的過程和心得。

一、網路架構

我用Visio大概畫了一下我的網路架構圖:

這裡寫圖片描述

我構建的容器:

  1. Nginx容器;
  2. Web server容器
  3. Redis容器
  4. memcached容器
  5. MySQL容器

如果把所有應用都部署到一個應用中,可能會更簡單,但不同容器之間就稍微複雜點。首先你要考慮好容器之間的依賴關係,比如nginx要依賴web伺服器,如果web伺服器不正常工作,那nginx就不能正常工作;web伺服器要依賴於資料庫等等;其次,要設定好容器間的資料共享問題。比如對於web應用的靜態資源,怎麼讓nginx實現反向代理。

帶著這些疑問,開始部署。

二、環境:

準備docker的環境。

  1. Ubuntu 16.04 (主機環境)
  2. docker 17.06.0
  3. docker-compose 1.14.0
  4. compose file 版本: version 3

對於docker的作用和介紹可參考官網,也可參考我自己寫的簡易介紹:

Docker service

要注意你的docker版本以及composefile版本,因為不同的版本,語法可能略有不同。我之前在配置共享資料卷的時候就遇到過坑,比如在version 3中刪掉了volumes_from,我還不知道有版本差異,所以怎麼配置都不對。更詳細的請看官網:dockerfile 版本

1、工程結構

├── blog    
│   ├── account
│   ├── blog
│   ├── dailyblog
│   ├── Dockerfile
│   ├── gunicorn.conf
│   ├── manage.py
│   ├── media
│   ├── requirements.txt
│   ├── start.sh
│   └── static
├── docker-compose.yml
└── nginx
    ├── Dockerfile
    └── nginx.conf

blog是我的django應用,內有Dockfile檔案;nginx檔案裡也有一個Dockfile。blog和nginx分別是一個service,我們通過docker-compose.yml檔案的配置來建立映象和容器。也就是說你必須要做幾件事:

  1. 在每個服務(應用)下編寫Dockerfile;
  2. 在docker-compose.yml檔案中配置相關的服務;
  3. 執行docker-compose命令 build和up

2、Django應用的配置(blog包):

1)Dockfile

FROM ubuntu:16.04

#更新軟體源,必須要執行,否則可能會出錯。-y就是要跳過提示直接安裝。
RUN apt-get -y update

RUN apt-get install -y python-dev python-pip
RUN apt-get install -y python-setuptools
#MySQL-Python必須得先安裝這個庫
RUN apt-get install -y libmysqlclient-dev   
RUN mkdir /blog
#設定工作目錄
WORKDIR /blog
#將當前目錄加入到工作目錄中
ADD . /blog
#install any needed pacakges in requirements.txt,你要把所有需要安裝的Python模組加到這檔案中。
RUN pip install -r requirements.txt
#對外暴露埠
EXPOSE 80 8080 8000 5000
#設定環境變數
ENV SPIDER=/blog

我的基礎映象選擇了Ubuntu,是因為我覺得我可能更習慣一些。

2)啟動指令碼 start.sh

#!/bin/bash

#命令只執行最後一個,所以用 &&

python manage.py collectstatic --noinput &&
python manage.py migrate &&
gunicorn blog.wsgi:application -c gunicorn.conf

在你初次部署時,你要收集各個app的static目錄到工程static目錄中,同時要建立資料庫。上面的3個命令通過 && 拼接,相當於一個命令。
此外,django應用選擇gunicorn做web伺服器,gunicorn的配置檔案如下:

workers=4
bind=['0.0.0.0:8000']
proc_name='blog'
pidfile='/tmp/blog.pid'
worker_class='gevent'
max_requests=6000

gunicorn中host選擇 0.0.0.0:8000。

3、Nginx配置(nginx目錄)

1)Dockfile

FROM nginx


#對外暴露埠
EXPOSE 80 8000

RUN rm /etc/nginx/conf.d/default.conf

ADD nginx.conf  /etc/nginx/conf.d/

RUN mkdir -p /usr/share/nginx/html/static
RUN mkdir -p /usr/share/nginx/html/media

nginx的基礎映象選擇docker倉庫中的基礎映象nginx即可,同時要把自己的配置檔案新增到相關目錄中。這裡有一點要注意,就是我自己曾經在主機配置nginx的時候,一般/etc/nginx/nginx.conf會從 /etc/nginx/conf.d,和/etc/nginx/site-enabled/兩個檔案目錄尋找conf檔案,我之前都是新增到/etc/nginx/site-enabled/,這次也是這麼做的,但是我配置執行之後,nginx沒有正常工作,我進入nginx容器看了一下,想看看為啥我的配置沒有載入,開啟/etc/nginx/nginx.conf一看,果然,它只include了/etc/nginx/conf.d中的conf檔案。Bingo!改了我的配置檔案,OK。

後面建立的static和media是為了web應用的靜態檔案儲存。

2)nginx.conf

server {
    listen      80;
    server_name localhost;
    charset     utf-8;

    error_log /tmp/nginx_error.log;
    access_log /tmp/nginx_access.log;


    location /media {
        alias /usr/share/nginx/html/media;
    }

    location /static {
        alias /usr/share/nginx/html/static;
        }

    location / {
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header Host $http_host;
        proxy_redirect off;
        proxy_pass http://web:8000;
    }

}

關於nginx配置,要注意以下兩點,是非常重要的:

  1. location
    靜態檔案配置,nginx指定的靜態檔案原目錄是在/usr/share/nginx/html/,而該目錄下的靜態檔案是從web容器中通過volumes同步的。所以,等下docker-compose是非常非常重要的。

  2. proxy_pass
    這和你直接在主機上配置是不一樣的,host不能寫成具體的IP,要寫服務名,這裡要寫web service的name,web是在docker-compose中定義的web應用的service名稱。後面要寫docker-compose的配置。

4、docker-compose.yml配置

version: "3"

services:


  db:
    image: mysql
    environment:
       MYSQL_DATABASE: app_blog
       MYSQL_ROOT_PASSWORD: admin
    volumes:
      - /srv/db:/var/lib/mysql
    restart: always

  redis:
    image: redis
    restart: always

  memcached:
    image: memcached
    restart: always

  web:
    build: ./blog
    ports:
    - "8000:8000"
    volumes:
    - ./blog:/blog
    - /tmp/logs:/tmp
    command: bash start.sh
    links:
    - redis
    - memcached
    - db
    depends_on:
      - db
    restart: always


  nginx:
    build: ./nginx
    ports:
    - "80:80"
    volumes:
    - ./blog/static:/usr/share/nginx/html/static:ro
    - ./blog/media:/usr/share/nginx/html/media:ro
    links:
    - web
    depends_on:
    - web
    restart: always

這個檔案是非常重要的!!!

定義了5個服務:

  1. db。 MySQL資料庫;
  2. Redis。 快取,NoSQL資料庫;
  3. memcached。 快取;
  4. web。 web應用;
  5. nginx。 反向代理。

服務名稱對於容器間的溝通是非常重要的。我們這裡一個一個說。

1)db

配置的幾個方面:

  • 基礎映象從docker倉庫中獲得(配置image);
  • 配置了環境變數,建立一個數據庫(該資料庫名為app_blog,django在執行migrate操作時會用);
  • volumes。資料卷,為了實現備份用的,/srv/db,是主機目錄,/var/lib/mysql是MySQL容器內目錄;
  • restart 預設是no,意思是在任何情況都不會重啟;如果設成always,就是如果stop了,就會重啟;
  • root使用者的密碼;你在django應用的settings.py裡也要寫成響應的配置,具體如下:

    DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'app_blog',
        'USER': 'root',
        'PASSWORD':'admin',
        'PORT':3306,
        'HOST':'db',
    }
    }
    

2)redis,memcached

這兩個就一起說了,因為不需要重新配置,直接用倉庫中的映象即可。

3)web應用

配置的幾個方面:

  • build。 根據Dockerfile重新build一個映象;
  • ports。 格式為HOST:CONTAINER。相當於一個nat轉換,設定內部的埠向外轉發的埠;
  • volumes。同樣是設定資料檔案備份,也可以說成是同步,web容器的工作目錄/blog備份到主機上的目錄;
  • links. 建立和其他容器中的service的連結,指定服務名字即可。有了這個連線,服務之間就可以通過service名字通訊了,在前面nginx配置中的proxy_pass就用了web服務;
  • depends_on. 它有兩層含義,一是在啟動服務的時候,會先啟動db,然後再啟動web;二是如果執行ocker-compose up web也會建立和啟動db.

4)nginx

  • build。 根據Dockerfile重新build一個映象;
  • ports。 格式為HOST:CONTAINER。相當於一個nat轉換,設定內部的埠向外轉發的埠; http預設埠
  • links. 上面已經介紹了;
  • depends_on. 上面也介紹了;
  • volumes。 這個我認為是最重要的,重點說一下。

關於如何實現nginx容器和web容器間的資料共享,即靜態檔案共享的問題,真是把我困擾住了。我先是按照官網配置,在頂級配置了volumes,在服務下配置type,source,之類的,但一直沒成功(如果有配成功的,拿出來分享下哈);後來網上搜了一堆資料,容器間共享他們都用了volumes_from,這在version3中已經取消了,退回舊版本還不行。上週五真是煩得我頭疼啊。後來看了一篇文章,才突然開竅,我本應該早點想到啊。邏輯是這樣的:

首先,我在web應用中就已經設定volumes資料的備份,即將容器中的檔案同步到主機上,然後主機就可以充當這個中間者,nginx容器再從主機上同步靜態檔案。這就相當於celery中,生產者將任務訊息寫到訊息中介軟體中,然後消費者從中介軟體中取訊息來訊息,而這裡面web應用就類似生產者,nginx是消費者。

這樣問題就迎刃而解了!!!

到目前為止,所有部署相關的配置都已經寫完了。

首先執行:

docker-compose build

然後執行:

docker-compose up -d 

題外話:我是在週六晚上11點跑的第一個build命令,執行各種映象下載,軟體源更新,Get資源比較慢,我太困了,就睡覺了。晚上睡覺做夢感覺都是docker,然後不到6點我就起了,到了客廳看電腦build已經成功。我就開始執行up命令,當我開啟瀏覽器,輸入localhost,然後成功返回結果的那一刻,甭提多有成就感啦!!!

隨意幾個知識點:

  1. Docker刪除所有容器:

    docker rm docker ps -a -q

    最重要的是後面的 -q選項,表示只顯示ID。

  2. 刪除none映象:

    docker rmi docker images -f "dangling=true" -q

  3. 更新。dockerfile要加上apt-get update,否則後面的命令不能正常執行;

  4. command命令只執行最後一個,在指令碼中寫了三個命令,但最後只執行最後一個。後來把三個命令用 && 拼接起來。