之前部署Web應(yīng)用,沒(méi)用docker,直接在服務(wù)器上部署,使用了fabric+nginx+supervisor+gunicorn部署,可查看我寫(xiě)過(guò)的一片博客:部署Web應(yīng)用。但后來(lái)了解了docker,為其“Build,Ship and Run Any App,Anywhere”的思想所折服,覺(jué)得這個(gè)太牛逼了,所以我也嘗試一下自己用docker部署Web應(yīng)用。本篇文章為了記錄我用docker部署web應(yīng)用的過(guò)程和心得。
一、網(wǎng)絡(luò)架構(gòu)
我用Visio大概畫(huà)了一下我的網(wǎng)絡(luò)架構(gòu)圖:

我構(gòu)建的容器:
- Nginx容器;
- Web server容器
- Redis容器
- memcached容器
- MySQL容器
如果把所有應(yīng)用都部署到一個(gè)應(yīng)用中,可能會(huì)更簡(jiǎn)單,但不同容器之間就稍微復(fù)雜點(diǎn)。首先你要考慮好容器之間的依賴(lài)關(guān)系,比如nginx要依賴(lài)web服務(wù)器,如果web服務(wù)器不正常工作,那nginx就不能正常工作;web服務(wù)器要依賴(lài)于數(shù)據(jù)庫(kù)等等;其次,要設(shè)置好容器間的數(shù)據(jù)共享問(wèn)題。比如對(duì)于web應(yīng)用的靜態(tài)資源,怎么讓nginx實(shí)現(xiàn)反向代理。
帶著這些疑問(wèn),開(kāi)始部署。
二、環(huán)境:
準(zhǔn)備docker的環(huán)境。
- Ubuntu 16.04 (主機(jī)環(huán)境)
- docker 17.06.0
- docker-compose 1.14.0
- compose file 版本: version 3
對(duì)于docker的作用和介紹可參考官網(wǎng):Docker service。
要注意你的docker版本以及composefile版本,因?yàn)椴煌陌姹?,語(yǔ)法可能略有不同。我之前在配置共享數(shù)據(jù)卷的時(shí)候就遇到過(guò)坑,比如在version 3中刪掉了volumes_from,我還不知道有版本差異,所以怎么配置都不對(duì)。更詳細(xì)的請(qǐng)看官網(wǎng):dockerfile 版本。
1、工程結(jié)構(gòu)
├── blog
│ ├── account
│ ├── blog
│ ├── dailyblog
│ ├── Dockerfile
│ ├── gunicorn.conf
│ ├── manage.py
│ ├── media
│ ├── requirements.txt
│ ├── start.sh
│ └── static
├── docker-compose.yml
└── nginx
├── Dockerfile
└── nginx.conf
blog是我的django應(yīng)用,內(nèi)有Dockfile文件;nginx文件里也有一個(gè)Dockfile。blog和nginx分別是一個(gè)service,我們通過(guò)docker-compose.yml文件的配置來(lái)創(chuàng)建鏡像和容器。也就是說(shuō)你必須要做幾件事:
- 在每個(gè)服務(wù)(應(yīng)用)下編寫(xiě)Dockerfile;
- 在docker-compose.yml文件中配置相關(guān)的服務(wù);
- 執(zhí)行docker-compose命令 build和up
2、Django應(yīng)用的配置(blog包):
1)Dockfile
FROM ubuntu:16.04
#更新軟件源,必須要執(zhí)行,否則可能會(huì)出錯(cuò)。-y就是要跳過(guò)提示直接安裝。
RUN apt-get -y update
RUN apt-get install -y python-dev python-pip
RUN apt-get install -y python-setuptools
#MySQL-Python必須得先安裝這個(gè)庫(kù)
RUN apt-get install -y libmysqlclient-dev
RUN mkdir /blog
#設(shè)置工作目錄
WORKDIR /blog
#將當(dāng)前目錄加入到工作目錄中
ADD . /blog
#install any needed pacakges in requirements.txt,你要把所有需要安裝的Python模塊加到這文件中。
RUN pip install -r requirements.txt
#對(duì)外暴露端口
EXPOSE 80 8080 8000 5000
#設(shè)置環(huán)境變量
ENV SPIDER=/blog
我的基礎(chǔ)鏡像選擇了Ubuntu,是因?yàn)槲矣X(jué)得我可能更習(xí)慣一些。
2)啟動(dòng)腳本 start.sh
#!/bin/bash
#命令只執(zhí)行最后一個(gè),所以用 &&
python manage.py collectstatic --noinput &&
python manage.py migrate &&
gunicorn blog.wsgi:application -c gunicorn.conf
在你初次部署時(shí),你要收集各個(gè)app的static目錄到工程static目錄中,同時(shí)要?jiǎng)?chuàng)建數(shù)據(jù)庫(kù)。上面的3個(gè)命令通過(guò) && 拼接,相當(dāng)于一個(gè)命令。
此外,django應(yīng)用選擇gunicorn做web服務(wù)器,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
#對(duì)外暴露端口
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的基礎(chǔ)鏡像選擇docker倉(cāng)庫(kù)中的基礎(chǔ)鏡像nginx即可,同時(shí)要把自己的配置文件添加到相關(guān)目錄中。這里有一點(diǎn)要注意,就是我自己曾經(jīng)在主機(jī)配置nginx的時(shí)候,一般/etc/nginx/nginx.conf會(huì)從 /etc/nginx/conf.d,和/etc/nginx/site-enabled/兩個(gè)文件目錄尋找conf文件,我之前都是添加到/etc/nginx/site-enabled/,這次也是這么做的,但是我配置運(yùn)行之后,nginx沒(méi)有正常工作,我進(jìn)入nginx容器看了一下,想看看為啥我的配置沒(méi)有加載,打開(kāi)/etc/nginx/nginx.conf一看,果然,它只include了/etc/nginx/conf.d中的conf文件。Bingo!改了我的配置文件,OK。
后面創(chuàng)建的static和media是為了web應(yīng)用的靜態(tài)文件存儲(chǔ)。
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;
}
}
關(guān)于nginx配置,要注意以下兩點(diǎn),是非常重要的:
location
靜態(tài)文件配置,nginx指定的靜態(tài)文件原目錄是在/usr/share/nginx/html/,而該目錄下的靜態(tài)文件是從web容器中通過(guò)volumes同步的。所以,等下docker-compose是非常非常重要的。
proxy_pass
這和你直接在主機(jī)上配置是不一樣的,host不能寫(xiě)成具體的IP,要寫(xiě)服務(wù)名,這里要寫(xiě)web service的name,web是在docker-compose中定義的web應(yīng)用的service名稱(chēng)。后面要寫(xiě)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
這個(gè)文件是非常重要的!?。?/p>
定義了5個(gè)服務(wù):
- db。 MySQL數(shù)據(jù)庫(kù);
- Redis。 緩存,NoSQL數(shù)據(jù)庫(kù);
- memcached。 緩存;
- web。 web應(yīng)用;
- nginx。 反向代理。
服務(wù)名稱(chēng)對(duì)于容器間的溝通是非常重要的。我們這里一個(gè)一個(gè)說(shuō)。
1)db
配置的幾個(gè)方面:
- 基礎(chǔ)鏡像從docker倉(cāng)庫(kù)中獲得(配置image);
- 配置了環(huán)境變量,創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)(該數(shù)據(jù)庫(kù)名為app_blog,django在執(zhí)行migrate操作時(shí)會(huì)用);
- volumes。數(shù)據(jù)卷,為了實(shí)現(xiàn)備份用的,/srv/db,是主機(jī)目錄,/var/lib/mysql是MySQL容器內(nèi)目錄;
- restart 默認(rèn)是no,意思是在任何情況都不會(huì)重啟;如果設(shè)成always,就是如果stop了,就會(huì)重啟;
- root用戶(hù)的密碼;你在django應(yīng)用的settings.py里也要寫(xiě)成響應(yīng)的配置,具體如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'app_blog',
'USER': 'root',
'PASSWORD':'admin',
'PORT':3306,
'HOST':'db',
}
}
2)redis,memcached
這兩個(gè)就一起說(shuō)了,因?yàn)椴恍枰匦屡渲?,直接用倉(cāng)庫(kù)中的鏡像即可。
3)web應(yīng)用
配置的幾個(gè)方面:
- build。 根據(jù)Dockerfile重新build一個(gè)鏡像;
- ports。 格式為HOST:CONTAINER。相當(dāng)于一個(gè)nat轉(zhuǎn)換,設(shè)置內(nèi)部的端口向外轉(zhuǎn)發(fā)的端口;
- volumes。同樣是設(shè)置數(shù)據(jù)文件備份,也可以說(shuō)成是同步,web容器的工作目錄/blog備份到主機(jī)上的目錄;
- links. 創(chuàng)建和其他容器中的service的鏈接,指定服務(wù)名字即可。有了這個(gè)連接,服務(wù)之間就可以通過(guò)service名字通信了,在前面nginx配置中的proxy_pass就用了web服務(wù);
- depends_on. 它有兩層含義,一是在啟動(dòng)服務(wù)的時(shí)候,會(huì)先啟動(dòng)db,然后再啟動(dòng)web;二是如果執(zhí)行ocker-compose up web也會(huì)創(chuàng)建和啟動(dòng)db.
4)nginx
- build。 根據(jù)Dockerfile重新build一個(gè)鏡像;
- ports。 格式為HOST:CONTAINER。相當(dāng)于一個(gè)nat轉(zhuǎn)換,設(shè)置內(nèi)部的端口向外轉(zhuǎn)發(fā)的端口; http默認(rèn)端口
- links. 上面已經(jīng)介紹了;
- depends_on. 上面也介紹了;
- volumes。 這個(gè)我認(rèn)為是最重要的,重點(diǎn)說(shuō)一下。
關(guān)于如何實(shí)現(xiàn)nginx容器和web容器間的數(shù)據(jù)共享,即靜態(tài)文件共享的問(wèn)題,真是把我困擾住了。我先是按照官網(wǎng)配置,在頂級(jí)配置了volumes,在服務(wù)下配置type,source,之類(lèi)的,但一直沒(méi)成功(如果有配成功的,拿出來(lái)分享下哈);后來(lái)網(wǎng)上搜了一堆資料,容器間共享他們都用了volumes_from,這在version3中已經(jīng)取消了,退回舊版本還不行。上周五真是煩得我頭疼啊。后來(lái)看了一篇文章,才突然開(kāi)竅,我本應(yīng)該早點(diǎn)想到啊。邏輯是這樣的:
首先,我在web應(yīng)用中就已經(jīng)設(shè)置volumes數(shù)據(jù)的備份,即將容器中的文件同步到主機(jī)上,然后主機(jī)就可以充當(dāng)這個(gè)中間者,nginx容器再?gòu)闹鳈C(jī)上同步靜態(tài)文件。這就相當(dāng)于celery中,生產(chǎn)者將任務(wù)消息寫(xiě)到消息中間件中,然后消費(fèi)者從中間件中取消息來(lái)消息,而這里面web應(yīng)用就類(lèi)似生產(chǎn)者,nginx是消費(fèi)者。
這樣問(wèn)題就迎刃而解了?。。?/p>
到目前為止,所有部署相關(guān)的配置都已經(jīng)寫(xiě)完了。
首先執(zhí)行:
然后執(zhí)行:
題外話(huà):我是在周六晚上11點(diǎn)跑的第一個(gè)build命令,執(zhí)行各種鏡像下載,軟件源更新,Get資源比較慢,我太困了,就睡覺(jué)了。晚上睡覺(jué)做夢(mèng)感覺(jué)都是docker,然后不到6點(diǎn)我就起了,到了客廳看電腦build已經(jīng)成功。我就開(kāi)始執(zhí)行up命令,當(dāng)我打開(kāi)瀏覽器,輸入localhost,然后成功返回結(jié)果的那一刻,甭提多有成就感啦!??!
隨意幾個(gè)知識(shí)點(diǎn):
Docker刪除所有容器:
docker rm docker ps -a -q
最重要的是后面的 -q選項(xiàng),表示只顯示ID。
刪除none鏡像:
docker rmi docker images -f "dangling=true" -q
更新。dockerfile要加上apt-get update,否則后面的命令不能正常執(zhí)行;
command命令只執(zhí)行最后一個(gè),在腳本中寫(xiě)了三個(gè)命令,但最后只執(zhí)行最后一個(gè)。后來(lái)把三個(gè)命令用 && 拼接起來(lái)。
docker鏡像內(nèi)的文件互相拷貝
1、將本地文件拷貝到docker 鏡像內(nèi)
docker cp /Users/howey/Documents/apache-maven-3.5.2/ 749056ea1637:/opt
docker cp 本地路徑 容器Id或name:容器目錄
2、將docker內(nèi)文件拷貝到本地文件夾內(nèi)
docker cp 749056ea1637:/Users/howey/Documents/apache-maven-3.5.2 /opt/
docker cp 容器Id:本地路徑 鏡像路徑
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。