背景
有需求需要對django系統進行docker化,以達到靈活部署和容災。該系統基於django 2.2版本開發,數據庫採用mongodb,服務器使用nginx,因系統有部分異步任務,異步任務則採用clelery+redis實現。
基於該需求,所採用的思路是:“基於ubuntu16.04”源鏡像,根據dockerfile製作各個運行環境的鏡像。因docker提倡單應用單鏡像,故這裏將django源代碼程序作爲一個鏡像、mongodb作爲一個鏡像、nginx作爲一個鏡像、redis作爲一個鏡像。並最終使用docker-compose對這些鏡像做編排。(假設當前已瞭解docker與docker-compose知識)
實現
下面就是一步步製作docker鏡像了。關於各個鏡像的Dockerfile模板,這裏有一個非常好用的網站,可在網站中搜索自己感興趣的項目,得到其Dockerfile。假設ubuntu16.04的源鏡像及版本名爲:ubuntu:16.04。
首先我們在宿主機(宿主機爲ubuntu16.04系統,用戶爲user)中建立一個父文件夾例如名爲vs,其中的目錄如下:
mongodb_vs: 存放mongod的數據、配置文件與dockerfile文件;
vsapp: 存放django系統的源代碼、相關配置文件與dockerfile文件;
redis_vs: 存放redis的配置文件與dockerfile文件;
nginx_vs: 存放nginx的配置文件與dockerfile文件。
1. mongodb鏡像
首先是mongodb鏡像的Dockerfile,內容如下:
FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install gcc -y
RUN apt-get install g++ -y
RUN apt-get install make -y
RUN apt-get install wget -y
# Install MongoDB.
RUN \
apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv 7F0CEB10 && \
echo 'deb http://downloads-distro.mongodb.org/repo/ubuntu-upstart dist 10gen' > /etc/apt/sources.list.d/mongodb.list && \
apt-get update && \
apt-get install -y mongodb-org && \
rm -rf /var/lib/apt/lists/*
# Define mountable directories.
VOLUME ["/data/db"]
# Define working directory.
WORKDIR /data
RUN mkdir bin && mkdir log
COPY mongodb.conf ./bin/
# Expose ports.
# - 27017: process
# - 28017: http
EXPOSE 27017
EXPOSE 28017
# Define default command.
# CMD ["mongod", "-f", "./bin/mongodb.conf"]
Dockerfile解釋:首先更新ubuntu的源,然後下載mongodb的安裝包並安裝;在鏡像中定義了“/data/db”的數據卷路徑,並在/data/路徑下創建了bin文件夾與log文件夾(db文件夾與log文件夾分別用來存儲mongodb的數據庫和mongodb運行的log。bin文件夾用來存放mongodb的配置文件);將宿主機下的mongodb配置文件“mongodb.conf”文件拷貝到鏡像中的bin文件夾下;暴露mongodb運行的相應端口。
Dockerfile運行條件:要想運行該Dockerfile,需要有配置文件mongodb.conf的支持。在mongodb_vs文件夾下放入Dockerfile並創建一個文件mongodb.conf和一個data文件夾,data文件夾中有db和log兩個空文件夾(db和log用來存儲mongodb運行中的數據庫和log)。整體目錄如下:
--mongodb_vs
--Dockerfile
--mongodb.conf
--data
--db
--log
mongodb.conf內容如下:
# db path
dbpath = /data/db/
# log path
logpath = /data/log/logs.log
port = 27017
# fork = true
# auth = true
以上內容中,fork表示是否在後臺運行mongodb,auth表示mongodb是否要進行認證,目前我們先將這兩個禁用掉。
編譯鏡像:在mongodb_vs文件夾下運行如下命令:
docker build -t mongodb:v1.0 .
命令運行後若是沒有報錯,則會編譯出一個名爲:mongodb:v1.0的鏡像。
運行容器:在mongodb文件夾下運行如下命令:
docker run -it --name mongodb_test -p 27017:27017 -p 27018:27018 -v /home/user/vs/mongodb_vs/data/db:/data/db -v /home/user/vs/mongodb_vs/data/log:/data/log mongodb:v1.0
我們基於mongodb_vs鏡像在前臺運行了一個名爲mongodb_test的容器,並將其端口與宿主機端口綁定,將mongodb產生的數據、log與宿主機進行映射。
若無報錯,則會進入到該容器中,在容器中運行命令:mongod -f ./bin/mongodb.conf --fork則會在後臺啓動開mongodb服務。我們可以在該容器中再輸入mongo命令,若能正常進入到mongodb的shell中,則說明mongodb啓動成功(也可用工具檢驗是否能連接到該mongodb實例)。
2. 源程序鏡像
因該系統採用python的django框架完成。故需要系統有python環境。我們可以先基於ubuntu源鏡像製作一個python3的鏡像,再基於python3的鏡像製作源程序鏡像。
製作python3的鏡像方法有很多,這裏列出一種供參考:
python3.6.3的Dockerfile
FROM ubuntu:16.04
RUN apt-get update
RUN apt-get -y install gcc make zlib1g zlib1g-dev openssl libpcre3 libpcre3-dev
RUN apt-get -y install libbz2-dev libsqlite3-dev libxml2-dev libffi-dev libssl-dev libxslt1-dev
RUN apt-get -y install wget
RUN apt-get -y upgrade
RUN apt-get -y dist-upgrade
RUN apt-get -y install bzip2
RUN apt-get -y install python3-pip python3-dev
RUN wget https://www.python.org/ftp/python/3.6.3/Python-3.6.3.tar.xz
RUN tar -xvf Python-3.6.3.tar.xz
RUN cd Python-3.6.3 && ./configure && make -j 8 && make install
運行構建命令:**docker build -t python:v3.6.3 .**即可製作出python3.6.3的鏡像。
下面就是製作源程序的鏡像,製作之前還需要一些準備工作:
a.
將源程序代碼放入到vsapp文件夾下,例如源代碼項目名爲:vscode(vscode子一級目錄下有manage.py文件);爲了使項目支持nginx,在vscode子一級目錄下放入名爲uwsgi_params文件,文件內容爲:
uwsgi_param QUERY_STRING $query_string;
uwsgi_param REQUEST_METHOD $request_method;
uwsgi_param CONTENT_TYPE $content_type;
uwsgi_param CONTENT_LENGTH $content_length;
uwsgi_param REQUEST_URI $request_uri;
uwsgi_param PATH_INFO $document_uri;
uwsgi_param DOCUMENT_ROOT $document_root;
uwsgi_param SERVER_PROTOCOL $server_protocol;
uwsgi_param REQUEST_SCHEME $scheme;
uwsgi_param HTTPS $https if_not_empty;
uwsgi_param REMOTE_ADDR $remote_addr;
uwsgi_param REMOTE_PORT $remote_port;
uwsgi_param SERVER_PORT $server_port;
uwsgi_param SERVER_NAME $server_name;
b.
利用命令“pip freeze > requirements.txt”將源程序所需要的python依賴包都導出到requirements.txt文件中。並將該文件放入到vsapp文件夾下。我這裏的requirements.txt內容如下,供參考:
beautifulsoup4==4.6.0
redis==2.10.6
celery==3.1.26.post2
celery-with-redis==3.0
Django==2.2.2
django-rest-framework-mongoengine==3.3.1
jira==2.0.0
ldap3==2.6
mongoengine==0.17.0
multi-key-dict==2.0.3
mysqlclient==1.3.13
pymongo==3.8.0
python-jenkins==1.4.0
requests==2.22.0
requests-oauthlib==1.2.0
requests-toolbelt==0.9.1
sqlparse==0.3.0
urllib3==1.25.3
xlrd==1.2.0
xlwt==1.3.0
generic==0.3.1
uWSGI==2.0.17.1
djangorestframework==3.9.4
c.
因項目中使用了celery,故還要將celery的配置文件放入到vsapp中。在vsapp中建立名爲celeryconf的文件夾,下載官方的兩個腳本並放入到celeryconf中;在vsapp中建立名爲celeryd的文件,並將以下內容存儲到該文件中:
# Names of nodes to start
# most will only start one node:
CELERYD_NODES="worker1"
# but you can also start multiple and configure settings
# for each in CELERYD_OPTS (see `celery multi --help` for examples).
#CELERYD_NODES="worker1 worker2 worker3"
# Absolute or relative path to the 'celery' command:
CELERY_BIN="/usr/local/bin/celery"
#CELERY_BIN="/virtualenvs/def/bin/celery"
# App instance to use
# comment out this line if you don't use an app
# 這裏填寫實際的項目中項目名
CELERY_APP="vscode"
# or fully qualified:
#CELERY_APP="proj.tasks:app"
# Where to chdir at start.
# 這裏填寫項目中應用名在容器中對應的絕對路徑,因編譯鏡像時我這裏命名爲/web/vscode/
CELERYD_CHDIR="/web/vscode/"
# Extra command-line arguments to the worker
CELERYD_OPTS="--time-limit=300 --concurrency=8"
# %N will be replaced with the first part of the nodename.
CELERYD_LOG_FILE="/var/log/celery/worker1.log"
CELERYD_PID_FILE="/var/run/celery/%N.pid"
# Workers should run as an unprivileged user.
# You need to create this user manually (or you can choose
# a user/group combination that already exists, e.g. nobody).
CELERYD_USER="root"
CELERYD_GROUP="root"
# If enabled pid and log directories will be created if missing,
# and owned by the userid/group configured.
CELERY_CREATE_DIRS=1
若是想查看celery運行的log,可以在vsapp下建立一個名爲celerywork.log的文件。
關於celery的配置詳情,可參考這篇博客
d.
因源程序中採用的是“nginx + uwsgi”搭建服務器端,故這裏需要對uwsgi進行配置。在vsapp下建立名爲vsapp_uwsgi.ini的文件。並在其中保存以下內容:
# mysite_uwsgi.ini file
[uwsgi]
# Django-related settings
# the base directory (full path)
chdir = /web/vscode
# Django's wsgi file
module = vscode.wsgi
# the virtualenv (full path)
# home = /var/local/bin/python3
# process-related settings
# master
master = true
# maximum number of worker processes
processes = 4
chmod-socket = 666
# 這裏指定了與nginx對接的端口爲8000
socket = :8000
# clear environment on exit
vacuum = true
uid = www-data
gid = www-data
pidfile = /web/webapp_uwsgi.pid
# daemonize = /web/uwsgi_log.log
e.
下面就是配置源程序運行的啓動命令。因源程序運行既需要啓動uwsgi還需要啓動celery的異步任務與定時任務。故我們在vsapp下建立名爲run_web.sh的文件,並放入如下內容:
#!/bin/bash
# 啓動celery的異步任務與定時任務
/etc/init.d/celeryd start
/etc/init.d/celerybeat start
# 啓動uwsgi
uwsgi --ini vsapp_uwsgi.ini
f.
準備工作做完後,就是編寫源程序的Dockerfile了。在vsapp下建立名爲Dockerfile的文件,其中內容如下:
FROM python:v3.6.3
RUN apt-get update \
&& apt-get install -y libmysqlclient-dev \
&& apt-get install -y --no-install-recommends \
postgresql-client \
&& rm -rf /var/lib/apt/lists/*
VOLUME ["/web/vscode"]
WORKDIR /web
COPY requirements.txt ./
RUN pip3 install -r requirements.txt
COPY vsapp_uwsgi.ini ./
COPY celeryconf/celeryd /etc/init.d/
COPY celeryconf/celerybeat /etc/init.d/
COPY celeryd /etc/default/
COPY run_web.sh ./
RUN chmod 777 run_web.sh \
&& chmod 777 /etc/init.d/celeryd \
&& chmod 777 /etc/init.d/celerybeat \
&& chmod 640 /etc/default/celeryd
EXPOSE 8000
# CMD ["./run_web.sh"]
最後,整個vsapp下的目錄如下:
**編譯鏡像:**在vsapp文件夾下運行如下命令:
docker build -t vsapp:v1.0 .
運行容器:鏡像編譯成功後,運行如下命令:
docker run -it --name vsapp_test -p 8000:8000 -v /home/user/vs/vsapp/vscode/:/web/vscode/ -p /home/user/vs/vsapp/celerywork.log:/var/log/celery/worker1.log
若容器運行成功,在該容器中運行shell腳本命令“./run_web.sh”,即可啓動源程序的服務,若期間沒有報錯,則源程序鏡像製作成功。
3. nginx鏡像
nginx鏡像的製作只需要重新配置一下nginx的配置文件nginx.conf,以使其能夠與源程序對接並加載源程序中的靜態資源文件。
在nginx_vs文件夾下創建名爲nginx.conf的文件,該文件內容如下:
#user www-data;
worker_processes 1;
#pid /run/nginx.pid;
events {
worker_connections 1024; ## Default: 1024
}
http {
# Definethe MIME types for files.
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;
upstream django {
# server unix:///usr/share/nginx/html/webapp.sock;
# 這裏的端口要寫成8000,因爲在源程序鏡像中的vsapp_uwsgi.ini中配置的是8000
server 127.0.0.1:8000; # for a web port socket (we'll use this first)
}
fastcgi_connect_timeout 300;
fastcgi_send_timeout 300;
fastcgi_read_timeout 300;
fastcgi_buffer_size 256k;
fastcgi_buffers 16 256k;
fastcgi_busy_buffers_size 512k;
fastcgi_temp_file_write_size 512k;
# configuration of the server
server {
# the port your site will be served on
listen 8090;
# the domain name it will serve for
server_name 127.0.0.1; # substitute your machine's IP address or FQDN
charset UTF-8;
access_log logs/myweb_access.log;
error_log logs/myweb_error.log;
# max upload size
client_max_body_size 75M; # adjust to taste
# Django media
location /static/ {
expires 30d;
autoindex on;
add_header Cache-Control private;
alias /usr/share/nginx/html/webapp/static/; # your Django project's static files - amend as required
}
# Finally, send all non-media requests to the Django server.
location / {
uwsgi_read_timeout 500;
uwsgi_pass django;
include /usr/share/nginx/html/webapp/uwsgi_params; # the uwsgi_params file you installed
}
}
}
daemon off;
該配置文件其實是對原nginx中的配置文件進行了更改。主要是聲明瞭內部nginx與uwsgi通訊的端口,以及外部正常訪問服務器的端口;源程序的靜態資源文件夾路徑;源程序的的uwsgi_params文件路徑。
在nginx_vs文件夾下建立Dockerfile文件,內容如下:
FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install gcc -y
RUN apt-get install g++ -y
RUN apt-get install make -y
RUN apt-get install wget -y
RUN wget http://nginx.org/download/nginx-1.14.0.tar.gz && wget https://ftp.pcre.org/pub/pcre/pcre-8.37.tar.gz
#RUN cd /usr/local && mkdir src
RUN tar -xvf nginx-1.14.0.tar.gz -C /usr/local/src && tar -xvf pcre-8.37.tar.gz -C /usr/local/src
WORKDIR /usr/local/src/nginx-1.14.0/
RUN ./configure --prefix=/usr/local/nginx --without-http_gzip_module --with-pcre=/usr/local/src/pcre-8.37 && make && make install
ADD nginx.conf /usr/local/nginx/conf/
VOLUME ["/usr/share/nginx/html/webapp/"]
ENV PATH /usr/local/nginx/sbin:$PATH
EXPOSE 80 8090 8000
ENTRYPOINT ["nginx"]
編譯鏡像:在nginx_vs文件夾下運行命令
docker build -t nginx:v1.0 .
運行容器: 運行命令:
docker run -it --name nginx_test -p 8090:8090 -v /home/user/vs/vsapp/vscode/:/usr/share/nginx/html/webapp nginx:v1.0
4. redis鏡像
redis在該vscode系統中充當的角色是任務隊列,故直接製作原始的redis鏡像即可。不過爲了使我們能在宿主機上查看redis鏡像運行容器時產生的數據庫數據與log,我們有必要重新配置redis配置文件redis.conf。
拿到原始的redis.conf修改以下幾處:
# 指定log存放路徑
logfile /data/logs/redis.log
# 指定數據庫存放路徑
dir /data/redisData/
# 若連接redis需要密碼,則配置以下項
requirepass 1234*
在redis_vs下建立文件夾data,並將修改後的redis.conf放到data中,再在data目錄中建立logs、redisData文件夾,logs文件夾中建立名爲redis.log的文件。整個redis_vs的目錄結構如下:
-- redis_vs
-- data
-- redisData
-- logs
-- redis.log
-- redis.conf
-- Dockerfile
redis的Dockerfile文件內容如下(我這裏安裝的redis版本爲3.0.6):
FROM ubuntu:16.04
RUN apt-get update
RUN apt-get install gcc -y
RUN apt-get install g++ -y
RUN apt-get install make -y
RUN apt-get install wget -y
RUN \
cd /tmp && \
wget http://download.redis.io/releases/redis-3.0.6.tar.gz && \
tar xvzf redis-3.0.6.tar.gz && \
cd redis-3.0.6 && \
make && \
make install && \
cp -f src/redis-sentinel /usr/local/bin && \
mkdir -p /etc/redis && \
cp -f *.conf /etc/redis && \
rm -rf /tmp/redis-3.0.6* && \
sed -i 's/^\(bind .*\)$/# \1/' /etc/redis/redis.conf && \
sed -i 's/^\(daemonize .*\)$/# \1/' /etc/redis/redis.conf && \
sed -i 's/^\(dir .*\)$/# \1\ndir \/data/' /etc/redis/redis.conf && \
sed -i 's/^\(logfile .*\)$/# \1/' /etc/redis/redis.conf
# Define mountable directories.
VOLUME ["/data"]
COPY data/redis.conf .
# Define working directory.
WORKDIR /data
# Define default command.
# CMD ["redis-server", "redis.conf"]
# Expose ports.
EXPOSE 6379
編譯鏡像: 在redis_vs目錄下運行命令:
docker build -t redis:v1.0 .
運行容器: 運行如下命令:
docker run -it --name redis_test -p 6379:6379 -v /home/user/vs/redis_vs/data/:/data/ redis:v1.0
當進入到容器中,在容器中運行命令“redis-server redis.conf”即可啓動redis服務。
5. 編寫docker-compose.yml
現在所有的鏡像都已編譯成功,下面就是將這些鏡像所運行的容器聯合起來以搭建成系統。這裏我們採用docker-compose對這些鏡像進行編排。
在vsapp下建立名爲docker-compose.yml的文件,其內容如下:
version: '3'
services:
mongodb:
image: mongodb:v1.0
ports:
- "27017:27017"
- "27018:27018"
volumes:
- "$PWD/mongodb_vs/data/db/:/data/db"
- "$PWD/mongodb_vs/data/log/:/data/log/"
command: ["./start_mongo.sh"]
webapp:
image: vsapp:v1.0
network_mode: "host"
ports:
- "8000:8000"
volumes:
- "$PWD/vsapp/vscode/:/web/vscode/"
- "$PWD/vsapp/celerywork.log:/var/log/celery/worker1.log"
depends_on:
- mongodb
- nginx
- redis
command: ["./run_web.sh"]
nginx:
image: nginx:v1.0
network_mode: "host"
ports:
- "8090:8090"
volumes:
- "$PWD/vsapp/vscode/:/usr/share/nginx/html/webapp"
redis:
image: redis:v1.0
network_mode: "host"
ports:
- "6379:6379"
volumes:
- "$PWD/redis_vs/data/:/data/"
command: ["redis-server","redis.conf"]
此時,docker-compose已經編寫完成,下面就是利用docker-compose運行這些容器了
運行命令:
docker-compose up
若運行後沒有報錯,說明整個系統已經搭建並啓動成功。可以對該系統進行訪問嘗試。
總結
將系統進行docker化其實就是重新對原來的系統進行了一個在新機器上的配置,只不過配置的可以分應用一個個配置了。