上一章我們實(shí)現(xiàn)了在 Docker 中添加了 MySQL 數(shù)據(jù)庫,但采用的開發(fā)服務(wù)器雖然使用便捷,但性能差、可靠性低,無法應(yīng)用在生產(chǎn)環(huán)境中。 因此本章將實(shí)現(xiàn) Docker + Django + MySQL + Nginx + Gunicorn 容器項(xiàng)目,完成最終的服務(wù)器部署。 直接進(jìn)入本章的 Docker 入門讀者,建議回到教程第一章開始閱讀,否則某些內(nèi)容不好理解。對(duì) Django 項(xiàng)目部署都沒有概念的讀者,還可以先閱讀我的博文:將 Django 項(xiàng)目部署到服務(wù)器。
Docker-compose在部署到服務(wù)器之前,先來嘗試本地部署。 在上一章的基礎(chǔ)上,繼續(xù)修改 docker-compose.yml 配置: version: "3"
services:
app:
restart: always
build: .
command: bash -c "python3 manage.py collectstatic --no-input && python3 manage.py migrate && gunicorn --timeout=30 --workers=4 --bind :8000 django_app.wsgi:application"
volumes:
- .:/code
- static-volume:/code/collected_static
expose:
- "8000"
depends_on:
- db
networks:
- web_network
- db_network
db:
image: mysql:5.7
volumes:
- "./mysql:/var/lib/mysql"
ports:
- "3306:3306"
restart: always
environment:
- MYSQL_ROOT_PASSWORD=mypassword
- MYSQL_DATABASE=django_app
networks:
- db_network
nginx:
restart: always
image: nginx:latest
ports:
- "8000:8000"
volumes:
- static-volume:/code/collected_static
- ./config/nginx:/etc/nginx/conf.d
depends_on:
- app
networks:
- web_network
networks:
web_network:
driver: bridge
db_network:
driver: bridge
volumes:
static-volume:
有點(diǎn)復(fù)雜。來看看大體思路: 定義了 3 個(gè)容器,分別是 app 、 db 和 nginx 。容器之間通過定義的端口進(jìn)行通訊。 定義了 2 個(gè)網(wǎng)絡(luò),分別是 web_network 和 db_network 。只有處在相同網(wǎng)絡(luò)的容器才能互相通訊。不同網(wǎng)絡(luò)之間是隔離的,即便采用同樣的端口,也無法通訊。 定義了 1 個(gè)數(shù)據(jù)卷, static-volume 。數(shù)據(jù)卷非常適合多個(gè)容器共享使用同一數(shù)據(jù),你可以看到 app 和 nginx 都用到了它。 expose 和 ports 都可以暴露容器的端口,區(qū)別是 expose 僅暴露給其他容器,而 ports 會(huì)暴露給其他容器和宿主機(jī)。
這么講可能還是很難理解,讓我們繼續(xù)分解。 網(wǎng)絡(luò) networkDocker 允許用戶給每個(gè)容器定義其工作的網(wǎng)絡(luò),只有在相同的網(wǎng)絡(luò)之中才能進(jìn)行通訊。你可以看到 nginx 容器處于 web_network 網(wǎng)絡(luò),而 db 容器處于 db_network 網(wǎng)絡(luò),因此它兩是無法通訊的,實(shí)際上確實(shí)也不需要通訊。而 app 容器同時(shí)處于 web_network 和 db_network 網(wǎng)絡(luò),相當(dāng)于是橋梁,連通了3個(gè)容器。 定義網(wǎng)絡(luò)可以隔離容器的網(wǎng)絡(luò)環(huán)境,也方便運(yùn)維人員一眼看出網(wǎng)絡(luò)的邏輯關(guān)系。 數(shù)據(jù)卷之前我們見識(shí)過的用于映射宿主機(jī)和容器目錄的卷了,實(shí)際上稱為掛載;現(xiàn)在新出現(xiàn)的 static-volume 才叫卷。它的使用方式像這樣:static-volume:/code/collected_static ,冒號(hào)后面還是容器內(nèi)的目錄,但冒號(hào)前的卻不是宿主機(jī)目錄、僅僅是卷的名稱而已。從本質(zhì)上講,數(shù)據(jù)卷也是實(shí)現(xiàn)了宿主機(jī)和容器的目錄映射,但是數(shù)據(jù)卷是由 Docker 進(jìn)行管理的,你甚至都不需要知道數(shù)據(jù)卷保存在宿主機(jī)的具體位置。 相比掛載,數(shù)據(jù)卷的優(yōu)點(diǎn)是由于是 Docker 統(tǒng)一管理的,不存在由于權(quán)限不夠引發(fā)的掛載問題,也不需要在不同服務(wù)器指定不同的路徑;缺點(diǎn)是它不太適合單配置文件的映射。 和掛載一樣,數(shù)據(jù)卷的生命周期脫離了容器,刪除容器之后卷還是存在的。下次構(gòu)建鏡像時(shí),指定卷的名稱就可以繼續(xù)使用了。 既然 Docker 能夠管理卷,所以要想刪除卷也是非常容易的。指令嘛,我不告訴你,生產(chǎn)環(huán)境千萬不要手賤。定期備份數(shù)據(jù)是個(gè)好習(xí)慣。
數(shù)據(jù)卷有個(gè)很重要的特性:啟動(dòng)時(shí)如果卷是空的,則會(huì)將容器映射目錄的所有內(nèi)容復(fù)制到卷里去。換句話說就是,只要卷初始化完成后,容器原始的 collected_static 目錄就不會(huì)再使用了,新增的文件也只存在于卷中,容器中是沒有的。 實(shí)際上 static 靜態(tài)文件(以及 media 媒體文件)的持久存儲(chǔ),通過掛載或者數(shù)據(jù)卷都可以實(shí)現(xiàn);具體用哪種,這個(gè)就見仁見智了,你自己選擇。 篇幅有限,教程沒有講到 media 媒體文件,但它的設(shè)置和 static 是完全相同的。
其他配置首先修改 Nginx 的配置文件,即映射到 nginx 容器的 config/nginx/django_app.conf : upstream app {
ip_hash;
server app:8000;
}
server {
listen 8000;
server_name localhost;
location /static/ {
autoindex on;
alias /code/collected_static/;
}
location / {
proxy_pass http://app/;
}
}
此配置下 Nginx 會(huì)監(jiān)聽容器的 8000 端口,并將受到的請(qǐng)求發(fā)送到 app 容器(靜態(tài)文件請(qǐng)求除外)。 在 requirements.txt 文件中增加 gunicorn 庫: django==2.2
mysqlclient==1.3.14
gunicorn==19.9.0
最后修改 django_app/settings.py 的域和靜態(tài)文件存放目錄的配置: ...
ALLOWED_HOSTS = ['*']
...
STATIC_ROOT = os.path.join(BASE_DIR, 'collected_static')
STATIC_URL = '/static/'
所有配置就完成了。 教程使用空的 Django 項(xiàng)目,為演示效果,就沒有修改 DEBUG=False 了。若你用的自己的項(xiàng)目測試,記得把它為 False。
測試測試指令就一條: $ docker-compose up
瀏覽器訪問 127.0.0.1:8000 又看到熟悉的 Django 小火箭了。 和上一章類似,第一次啟動(dòng)容器時(shí)可能會(huì)出現(xiàn)無法連接 MySQL 的錯(cuò)誤,這是由于雖然 db 容器已經(jīng)啟動(dòng),但初始化并未完成;重新啟動(dòng)容器之后就可以正常工作了。若多次啟動(dòng)都無法正常工作,那就是別的原因了,好好檢查吧。
本地部署成功,下一步服務(wù)器部署。 服務(wù)器部署有了本地部署的經(jīng)驗(yàn),服務(wù)器部署就非常非常簡單了。 還是類似的,部署前將 Docker 、 Docker-compose 、 Python3 等工具在服務(wù)器上安裝好;將項(xiàng)目用 Git 克隆到服務(wù)器本地。 接下來把 settings.py 、config/nginx/django_app.conf 、requirements.txt 相關(guān)位置都按教程流程改好;將 docker-compose.yml 和 Dockerfile 復(fù)制到服務(wù)器。 由于 http 請(qǐng)求默認(rèn)為 80 端口,所以為了接收公網(wǎng)請(qǐng)求,還需要做一點(diǎn)點(diǎn)修改 docker-compose.yml 的工作: version: "3"
services:
app:
...
command: bash -c "... your_project_name.wsgi:application" # 改為你的項(xiàng)目名稱
...
db:
...
nginx:
...
ports:
- "80:8000" # 監(jiān)聽 80 端口
...
networks:
...
volumes:
...
修改 Gunicorn 綁定的項(xiàng)目名稱,以及讓宿主機(jī)監(jiān)聽公網(wǎng) http 默認(rèn)的 80 端口。 此外還要修改 config/nginx/django_app.conf : upstream your_domain_name {
ip_hash;
server app:8000;
}
server {
...
location / {
proxy_pass http://your_domain_name/;
}
}
這個(gè)改動(dòng)主要是為了照顧各種第三方登錄的回調(diào)地址(不改這里, GitHub、Weibo 三方登錄都會(huì)失?。H绻銢]有類似的需求,不改也是可以的。比如博主的個(gè)人網(wǎng)站是 www. ,所以這里的 your_domain_name 就修改為 www. 。 最后,記得將 settings.py 中的 DEBUG 配置修改好: # DEBUG=True 注釋掉
DEBUG=False
這樣就可以了!構(gòu)建鏡像并啟動(dòng)容器: docker-compose up
在瀏覽器中就可以正常訪問你的網(wǎng)站了。 總結(jié)現(xiàn)在你已經(jīng)可以部署一個(gè)線上的容器化 Django 項(xiàng)目了,恭喜!
|