本章將在本地搭建一個(gè)容器化的 Django 項(xiàng)目,感受 Docker 的運(yùn)作方式。
雖然有基于 Windows 的 Docker 版本,但各方面兼容做得都不太好(安裝也麻煩些),因此建議讀者在學(xué)習(xí)前,自行安裝好 Linux 或 Mac 系統(tǒng)。當(dāng)然你愿意折騰的話,在 Windows 上搞也行。
別擔(dān)心,以后開發(fā) Django 項(xiàng)目仍然可以在 Windows 下進(jìn)行,僅僅是開發(fā)時(shí)不使用 Docker 而已。
Docker:學(xué)習(xí) Docker 當(dāng)然要安裝 Docker 軟件了(免費(fèi)的社區(qū)版),安裝方法見(jiàn)官方文檔。
Docker-compose:這是 Docker 官方推出的用于編排、運(yùn)行多個(gè)容器的工具,安裝方法見(jiàn)官方文檔。本教程大部分內(nèi)容都與它有關(guān)。
Python3:教程部署的是 Django 項(xiàng)目,那 Python3 是當(dāng)然要有的了(包括 python 的包管理工具 pip)。
準(zhǔn)備就緒后就繼續(xù)下一步吧。
打開 Linux/Mac 的終端,安裝 Django 庫(kù):
$ pip install django==2.2
在一個(gè)你喜歡得位置(比如/home/)創(chuàng)建新的 Django 項(xiàng)目:
$ django-admin startproject django_app
進(jìn)入項(xiàng)目根目錄:
$ cd django_app
教程后面的內(nèi)容全部都在此目錄中操作了。為方便閱讀,命令提示符 $
代表目前在項(xiàng)目根目錄 django_app/
,mysql $
則代表目前在目錄 django_app/mysql/
中,請(qǐng)讀者操作時(shí)稍加留意當(dāng)前的工作目錄。
然后遷移數(shù)據(jù):
$ python manage.py migrateOperations to perform: Apply all migrations: admin, auth, contenttypes, sessionsRunning migrations: Applying contenttypes.0001_initial... OK ... Applying sessions.0001_initial... OK
準(zhǔn)備工作就搞定了。
Docker 的整個(gè)生命周期由三部分組成:鏡像(image)+ 容器(container)+ 倉(cāng)庫(kù)(repository)。
容器是由鏡像實(shí)例化而來(lái),這有點(diǎn)像面向?qū)ο蟮母拍睿虹R像就是類,容器是類實(shí)例化之后的對(duì)象。
鏡像是一個(gè)只讀的模板,它包括了運(yùn)行容器所需的數(shù)據(jù)。鏡像可以包含一個(gè)完整的 Linux 操作環(huán)境,里面僅安裝了 Python 或者其他用戶需要的程序。
容器是由鏡像創(chuàng)建出來(lái)的實(shí)例,類似虛擬機(jī),里面可以運(yùn)行特定的應(yīng)用,并且容器與容器是相互隔離的。
倉(cāng)庫(kù)概念與 Git 和 Github 類似,如果你用過(guò)它們就非常容易理解。Docker 使用的默認(rèn)倉(cāng)庫(kù)是由官方維護(hù)的 Docker hub 公共倉(cāng)庫(kù),從中上傳、拉取的操作類似 Git。
目前需要了解的就這么多,下面通過(guò)實(shí)踐來(lái)理解。
為了確認(rèn) Docker 已經(jīng)正確安裝了,運(yùn)行以下指令:
$ docker run hello-worldUnable to find image 'hello-world:latest' locally...latest: Pulling from library/hello-world1b930d010525: Pull complete ...Hello from Docker!This message shows that your installation appears to be working correctly....
一切正常的話,終端將打印出如上圖所示的歡迎語(yǔ)句。docker run hello-world
指令的含義是:用名稱為 hello-world
的鏡像構(gòu)建一個(gè)容器并運(yùn)行。如果本地上沒(méi)有這個(gè) hello-world
的鏡像, Docker 會(huì)自動(dòng)從倉(cāng)庫(kù)搜索并下載同名的鏡像。
我們可以用 docker images
查看本地已有的鏡像:
$ docker imagesREPOSITORY TAG IMAGE ID CREATED SIZEhello-world latest fce289e99eb9 9 months ago 1.84kB
表列分別為鏡像名、版本、ID 號(hào)、創(chuàng)建時(shí)間、大小。
還可以查看本地已有的容器:
$ docker ps -aCONTAINER ID IMAGE .. CREATED ..38cb03a96dca hello-world .. 2 minutes ago ..
除此之外還有一些非常有用的基礎(chǔ)指令:
docker rmi [images ID] # 刪除此 ID 的鏡像docker container stop [container ID] # 停止此 ID 的容器docker container start [container ID] # 啟動(dòng)此 ID 的容器docker container rm [container ID] # 刪除此 ID 的容器
由于測(cè)試時(shí)會(huì)頻繁生成鏡像,因此你肯定會(huì)用上面的指令查看、刪除無(wú)用的鏡像和容器。
牛刀小試完畢,接下來(lái)正式構(gòu)建 Django 容器。
Docker 允許通過(guò)文本格式的配置文件來(lái)構(gòu)建鏡像,默認(rèn)名稱為 Dockerfile。因此在項(xiàng)目根目錄新建文件 Dockerfile,寫入:
# 從倉(cāng)庫(kù)拉取 帶有 python 3.7 的 Linux 環(huán)境FROM python:3.7# 設(shè)置 python 環(huán)境變量ENV PYTHONUNBUFFERED 1# 創(chuàng)建 code 文件夾并將其設(shè)置為工作目錄RUN mkdir /codeWORKDIR /code# 更新 pipRUN pip install pip -U# 將 requirements.txt 復(fù)制到容器的 code 目錄ADD requirements.txt /code/# 安裝庫(kù)RUN pip install -r requirements.txt# 將當(dāng)前目錄復(fù)制到容器的 code 目錄ADD . /code/
理解這些 Docker 指令的關(guān)鍵在于,一定要牢記容器里的環(huán)境和外界**(宿主機(jī))**是隔離的,它兩是完全不一樣的。換句話說(shuō),要搞清楚哪些操作是針對(duì)宿主機(jī)、哪些操作是針對(duì)容器。
FROM python:3.7
指令從倉(cāng)庫(kù)拉取一個(gè)包含 python 3.7 的 Linux 操作系統(tǒng)環(huán)境(Linux 版本為 Debian)。
RUN
和 WORKDIR
指令都是針對(duì)容器的,功能是在容器里創(chuàng)建目錄、并將其設(shè)置為工作目錄。注意宿主機(jī)是沒(méi)有這個(gè)目錄的。
ADD
指令出現(xiàn)了兩次。ADD requirements.txt /code/
意思是將宿主機(jī)當(dāng)前目錄(即 Dockerfile 所在目錄)的 requirements.txt
文件復(fù)制到容器的 /code
目錄中。ADD . /code/
意思是把當(dāng)前目錄所有內(nèi)容復(fù)制到容器 /code/
目錄,注意中間那個(gè)點(diǎn)。
目前項(xiàng)目依賴的唯一庫(kù)就是 Django,所以在項(xiàng)目根目錄創(chuàng)建 requirements.txt
并寫入:
django==2.2
前面不是已經(jīng)安裝過(guò) Django 了嗎,為什么這里還要安裝?原因是前面是在宿主機(jī)安裝的,容器里是沒(méi)有 Django 的!
所以目前的文件結(jié)構(gòu)如下:
django_app - Dockerfile - requirements.txt - manage.py - django_app - db.sqlite3
配置文件就寫好了,接下來(lái)看看 Docker-compose
怎么用。
在線上環(huán)境中,通常不會(huì)將項(xiàng)目的所有組件放到同一個(gè)容器中;更好的做法是把每個(gè)獨(dú)立的功能裝進(jìn)單獨(dú)的容器,這樣方便復(fù)用。比如將 Django 代碼放到容器A,將 Mysql 數(shù)據(jù)庫(kù)放到容器B,以此類推。
因此同一個(gè)服務(wù)器上有可能會(huì)運(yùn)行著多個(gè)容器,如果每次都靠一條條指令去啟動(dòng),未免也太繁瑣了。 Docker-compose
就是解決這個(gè)問(wèn)題的,它用來(lái)編排多個(gè)容器,將啟動(dòng)容器的命令統(tǒng)一寫到 docker-compose.yml 文件中,以后每次啟動(dòng)這一組容器時(shí),只需要 docker-compose up
就可以了。因此教程也會(huì)用 docker-compose 來(lái)管理容器。
首先確認(rèn) docker-compose 是否安裝成功:
$ docker-compose -vdocker-compose version 1.24.1, build 4667896b
確認(rèn)無(wú)誤后,在項(xiàng)目根目錄創(chuàng)建 docker-compose.yml 并寫入:
version: "3"services: app: restart: always build: . # '點(diǎn)'代表當(dāng)前目錄 command: "python3 manage.py runserver 0.0.0.0:8000" volumes: - .:/code ports: - "8000:8000"
讓我們來(lái)分解一下其中的各項(xiàng)含義。
version
代表 docker-compose.yml 的版本,目前最新版為 3,不需要改動(dòng)它。
接著定義了一個(gè)名叫 app
的容器。后面的內(nèi)容都是 app
容器的相關(guān)配置:
restart
:除正常工作外,容器會(huì)在任何時(shí)候重啟,比如遭遇 bug、進(jìn)程崩潰、docker 重啟等情況。
build
:指定一個(gè)包含 Dockerfile 的路徑,并通過(guò)此 Dockerfile 來(lái)構(gòu)建容器鏡像。注意那個(gè) “.” ,代表當(dāng)前目錄。
command
:容器運(yùn)行時(shí)需要執(zhí)行的命令。這里就是我們很熟悉的運(yùn)行開發(fā)服務(wù)器了。
volumes
:卷,這是個(gè)很重要的概念。前面說(shuō)過(guò)容器是和宿主機(jī)完全隔離的,但是有些時(shí)候又需要將其連通;比如我們開發(fā)的 Django 項(xiàng)目代碼常常會(huì)更新,并且更新時(shí)還依賴如 Git 之類的程序,在容器里操作就顯得不太方便。所以就有卷,它定義了宿主機(jī)和容器之間的映射:"." 表示宿主機(jī)的當(dāng)前目錄,":" 為分隔符,"/code" 表示容器中的目錄。即宿主機(jī)當(dāng)前目錄和容器的 /code 目錄是連通的,宿主機(jī)當(dāng)前目錄的 Django 代碼更新時(shí),容器中的 /code 目錄中的代碼也相應(yīng)的更新了。這有點(diǎn)兒像是在容器上打了一個(gè)洞,某種程度上也是實(shí)用性和隔離性的一種妥協(xié)。
嚴(yán)格意義上講,這里用到的
.:/code
并不是卷,而是叫掛載,它兩是有區(qū)別的,只不過(guò) docker-compose 允許將掛載寫到卷的配置中。后面章節(jié)會(huì)講到。
ports
:定義了宿主機(jī)和容器的端口映射。容器的隔離不止環(huán)境,甚至連端口都隔離起來(lái)了。但 web 應(yīng)用不通過(guò)端口跟外界通信當(dāng)然不行,因此這里定義將宿主機(jī)的 8000 端口映射到容器的 8000 端口,即訪問(wèn)宿主機(jī)的 8000 端口就是訪問(wèn)到了容器的 8000 端口,但要確保端口沒(méi)有被其他程序占用。
配置就寫好了?,F(xiàn)在項(xiàng)目的目錄結(jié)構(gòu)如下:
django_app - docker-compose.yml - Dockerfile - requirements.txt - manage.py - django_app - db.sqlite3
輸入指令 docker-compose up
啟動(dòng)容器服務(wù):
$ docker-compose upCreating network "django_app_default" with the default driverBuilding appStep 1/8 : FROM python:3.73.7: Pulling from library/python4a56a430b2ba: Pull complete...6933d3d46042: Pull completeDigest: sha256:0f0e991a97426db345ca7ec59fa911c8ed27ced27c88ae9966b452bcc6438c2fStatus: Downloaded newer image for python:3.7 ---> 02d2bb146b3bStep 1/8 : FROM python:3.7 ---> 02d2bb146b3b...Step 7/8 : RUN pip install -r requirements.txt ---> Running in 62a60a3003feLooking in indexes: https://pypi.tuna.tsinghua.edu.cn/simpleCollecting django==2.2 (from -r requirements.txt (line 1)) Downloading https://files.pythonhosted.org/packages/54/85/0bef63668fb170888c1a2970ec897d4528d6072f32dee27653381a332642/Django-2.2-py3-none-any.whl (7.4MB)...Installing collected packages: sqlparse, pytz, djangoSuccessfully installed django-2.2 pytz-2019.2 sqlparse-0.3.0...Step 8/8 : ADD . /code/ ---> cb23f483ffb6Successfully built cb23f483ffb6Successfully tagged django_app_app:latestWARNING: Image for service app was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`.Creating django_app_app_1 ... doneAttaching to django_app_app_1app_1 | Watching for file changes with StatReloaderapp_1 | Performing system checks...app_1 | app_1 | System check identified no issues (0 silenced).app_1 | October 05, 2019 - 15:03:15app_1 | Django version 2.2, using settings 'django_app.settings'app_1 | Starting development server at http://0.0.0.0:8000/app_1 | Quit the server with CONTROL-C.
可以看到 Docker 按照配置文件的要求,成功構(gòu)建了鏡像及容器,并啟動(dòng)了容器。
打開瀏覽器,輸入本地 IP 端口 127.0.0.1:8000
:
看到 Django 的小火箭,項(xiàng)目成功運(yùn)行起來(lái)啦。按 Ctrl + C
即可停止開發(fā)服務(wù)器運(yùn)行。
停止服務(wù)器后實(shí)際上容器還存在,只是停止運(yùn)行了而已。輸入:
$ docker-compose down
就可以刪除容器。
如果想在后臺(tái)運(yùn)行容器,則輸入:
$ docker-compose up -d
另外,如果你需要重新構(gòu)建鏡像,則輸入命令:
$ docker-compose build
啟動(dòng)和停止已有的容器:
$ docker-compose start$ docker-compose stop
很簡(jiǎn)單吧。
由于眾所周知的原因,國(guó)內(nèi)的網(wǎng)絡(luò)環(huán)境非常復(fù)雜。在構(gòu)建鏡像時(shí)經(jīng)常需要從國(guó)外的遠(yuǎn)程倉(cāng)庫(kù)拉取資源,巋然不動(dòng)的下載速度真的頭疼。
解決方法就是將資源拉取鏈接修改為國(guó)內(nèi)的鏡像源,比如清華的鏡像源。
修改 Dockerfile 如下:
FROM python:3.7ENV PYTHONUNBUFFERED 1# 添加 Debian 清華鏡像源RUN echo deb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster main contrib non-freedeb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-updates main contrib non-freedeb https://mirrors.tuna.tsinghua.edu.cn/debian/ buster-backports main contrib non-freedeb https://mirrors.tuna.tsinghua.edu.cn/debian-security buster/updates main contrib non-free > /etc/apt/sources.listRUN mkdir /codeWORKDIR /code# 添加 pip 清華鏡像源RUN pip install pip -U -i https://pypi.tuna.tsinghua.edu.cn/simpleADD requirements.txt /code/# 添加 pip 清華鏡像源RUN pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simpleADD . /code/
重新構(gòu)建鏡像,下載速度就飛一樣快了。
教程后面的內(nèi)容都使用了清華源,但為了方便閱讀,把這一部分更換源的代碼省去了,讀者心里明白就好。
本章初步感受了 Docker 的工作流程,并且很輕松的構(gòu)建了一個(gè)容器化的 Django 項(xiàng)目。
下一章將 MySQL 數(shù)據(jù)庫(kù)也加入到容器編排中。
聯(lián)系客服