摘自《Docker 容器與容器云(第2版)》
點擊查看我的博客原文

1. 從容器到容器云
1.1 云計算平臺
經(jīng)典云計算架構(gòu)包括 IaaS(Infrastructure as a Service,基礎(chǔ)設(shè)施即服務(wù))、PaaS(Platform as a Service,平臺即服務(wù))、SaaS(Software as a Service,軟件即服務(wù))三層服務(wù),如下圖所示。

1.2 容器技術(shù)生態(tài)系統(tǒng)

可以看出,容器技術(shù)生態(tài)系統(tǒng)自上而下分別覆蓋了 IaaS 層和 PaaS 層所涉及的各類問題,包括資源調(diào)度、編排、部署、監(jiān)控、配置管理、存儲網(wǎng)絡(luò)管理、安全、容器化應(yīng)用支撐平臺等。容器技術(shù)主要帶來了以下幾點好處:
- 持續(xù)部署與測試:容器消除了線上線下的環(huán)境差異,保證了應(yīng)用生命周期的環(huán)境一致性和標(biāo)準(zhǔn)化。
- 跨云平臺支持:越來越多的云平臺都已支持容器,用戶無需擔(dān)心受到云平臺的捆綁。
- 環(huán)境標(biāo)準(zhǔn)化和版本控制:可使用 Git 等工具對容器鏡像進(jìn)行版本控制。相比基于代碼的版本控制來說,還能夠對整個應(yīng)用運行環(huán)境實現(xiàn)版本控制,一旦出現(xiàn)故障可以快速回滾。相比以前的虛擬機(jī)鏡像,容器壓縮和備份速度更快,鏡像啟動也像啟動一個普通進(jìn)程一樣快速。
- 高資源利用率與隔離:容器沒有管理程序的額外開銷,與底層共享操作系統(tǒng),性能更優(yōu),負(fù)載更低。同時,容器擁有不錯的資源隔離與限制能力,可以精確的對應(yīng)用分配 CPU、內(nèi)存等資源,保證應(yīng)用之間不會相互影響。
- 容器跨平臺性與鏡像:容器在原有 Linux 容器的基礎(chǔ)上進(jìn)行大膽革新,為容器設(shè)定了一整套標(biāo)準(zhǔn)化的配置方法,將應(yīng)用及其依賴的運行環(huán)境打包成鏡像,大大提高了容器的跨平臺性。
- 易于理解且易用:Docker 的英文原意是處理集裝箱的碼頭工人,標(biāo)志是鯨魚運送一大堆集裝箱,集裝箱就是容器。容器的易用性加速了容器標(biāo)準(zhǔn)化的步伐。
- 應(yīng)用鏡像倉庫:Docker 官方構(gòu)建了一個鏡像倉庫,已經(jīng)累積了成千上萬的鏡像,所有人都可以自由的下載微服務(wù)組件,為開發(fā)者提供了巨大便利。
1.3 從容器到容器云
容器云以容器為資源分割和調(diào)度的基本單位,封裝整個軟件運行時環(huán)境,為開發(fā)者和系統(tǒng)管理員提供用于構(gòu)建、發(fā)布和運行分布式應(yīng)用的平臺。
- 當(dāng)容器云專注于資源共享與隔離、容器編排與部署時,它更接近傳統(tǒng)的 IaaS
- 當(dāng)容器云滲透到應(yīng)用支撐與運行時環(huán)境時,它更接近傳統(tǒng)的 PaaS
容器云并不僅限于 Docker,基于 rkt 容器的 CoreOS 項目也是容器云。Docker 最初發(fā)布時只是一個單機(jī)下的容器管理工具,隨后 Docker 公司發(fā)布了 Compose、Machine、Swarm 等編排部署工具,并收購了 Socketplane 解決集群化后的網(wǎng)絡(luò)問題。
除了 Docker 公司之外,業(yè)界許多云計算廠商也對基于 Docker 的容器云做了巨大的投入。例如 Fleet、Flynn、Deis 以及目前成為事實主流標(biāo)準(zhǔn)的 Kubernetes,都是基于 Docker 技術(shù)構(gòu)建的廣為人知的容器云。
2. Docker 基礎(chǔ)
2.1 Docker 的安裝
安裝 Docker 的基本要求如下:
- 只支持 64 位 CPU 架構(gòu)的計算機(jī),目前不支持 32 位 CPU
- 建議系統(tǒng)的 Linux 內(nèi)核版本為
3.10及以上 - Linux 內(nèi)核需開啟 cgroups 和 namespace 功能
安裝過程可參考 CentOS 7 安裝 Docker CE。
2.2 Docker 操作參數(shù)解讀
docker命令的執(zhí)行一般都需要 root 權(quán)限,因為 Docker 的命令行工具docker與 Docker daemon 是同一個二進(jìn)制文件,而 Docker daemon 負(fù)責(zé)接收并執(zhí)行來自docker的命令,它的運行需要 root 權(quán)限。同時,從 Docker0.5.2版本開始,Docker daemon 默認(rèn)綁定一個 UNIX Socket 來代替原有的 TCP 端口,該 UNIX Socket 默認(rèn)是屬于 root 用戶的。
用戶在使用 Docker 時,需要使用 Docker 命令行工具docker與 Docker daemon 建立通信。Docker daemon 是 Docker 守護(hù)進(jìn)程,負(fù)責(zé)接收并分發(fā)執(zhí)行 Docker 命令??梢允褂?code>docker或docker help命令獲取docker的命令清單:
> docker
Usage: docker [OPTIONS] COMMAND
A self-sufficient runtime for containers
Options:
--config string Location of client config files (default "/root/.docker")
-D, --debug Enable debug mode
-H, --host list Daemon socket(s) to connect to
-l, --log-level string Set the logging level ("debug"|"info"|"warn"|"error"|"fatal") (default "info")
--tls Use TLS; implied by --tlsverify
--tlscacert string Trust certs signed only by this CA (default "/root/.docker/ca.pem")
--tlscert string Path to TLS certificate file (default "/root/.docker/cert.pem")
--tlskey string Path to TLS key file (default "/root/.docker/key.pem")
--tlsverify Use TLS and verify the remote
-v, --version Print version information and quit
Management Commands:
config Manage Docker configs
container Manage containers
image Manage images
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
例如可以使用docker start --help命令來獲取子命令start的詳細(xì)信息:
> docker start --help
Usage: docker start [OPTIONS] CONTAINER [CONTAINER...]
Start one or more stopped containers
Options:
-a, --attach Attach STDOUT/STDERR and forward signals
--detach-keys string Override the key sequence for detaching a container
-i, --interactive Attach container's STDIN
推薦閱讀:
根據(jù)命令的用途,可將 Docker 子命令進(jìn)行如下分類:

從docker命令的使用出發(fā),可以梳理出如下的命令結(jié)構(gòu)圖:

下面選擇每個功能分類中常用的子命令進(jìn)行用法和操作參數(shù)的解讀。
1. Docker 環(huán)境信息
docker info命令用于檢查 Docker 是否正確安裝。如果 Docker 正確安裝,該命令會輸出 Docker 的配置信息:
> docker info
Containers: 33
Running: 20
Paused: 0
Stopped: 13
Images: 23
Server Version: 18.06.1-ce
Storage Driver: overlay2
...
Kernel Version: 4.15.0-38-generic
Operating System: Ubuntu 18.04.1 LTS
...
docker info命令一般結(jié)合docker version命令使用,兩者結(jié)合能夠提取到足夠詳細(xì)的 Docker 環(huán)境信息:
> docker version
Client:
Version: 18.06.1-ce
API version: 1.38
Go version: go1.10.3
Git commit: e68fc7a
Built: Tue Aug 21 17:24:56 2018
OS/Arch: linux/amd64
Experimental: false
Server:
Engine:
Version: 18.06.1-ce
API version: 1.38 (minimum version 1.12)
Go version: go1.10.3
Git commit: e68fc7a
Built: Tue Aug 21 17:23:21 2018
OS/Arch: linux/amd64
Experimental: false
2. 容器生命周期管理
容器生命周期管理涉及容器啟動、停止等功能。
docker run 命令
docker run命令使用方法如下:
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
docker run命令用來基于特定的鏡像創(chuàng)建一個容器,并依據(jù)選項來控制該容器:
> docker run ubuntu echo "Hello Docker"
Hello Docker
該命令從 ubuntu 鏡像啟動一個容器,并執(zhí)行echo命令打印 Hello Docker。執(zhí)行完echo命令后,容器將停止運行。docker run命令啟動的容器會隨機(jī)分配一個容器 IDCONTAINER ID,用以標(biāo)識該容器。
root@ubuntu:~> docker run -i -t --name mytest ubuntu:latest /bin/bash
root@eb9dda25b0fe:/>
上例中,docker run命令啟動一個容器,并為它分配一個偽終端執(zhí)行/bin/bash命令,用戶可以在該偽終端與容器進(jìn)行交互。其中:
-
-i:表示使用交互模式,始終保持輸入流開放 -
-t:表示分配一個偽終端,一般兩個參數(shù)結(jié)合時使用-it -
--name:可以指定啟動的容器的名字。若無此選項,Docker 將為容器隨機(jī)分配一個名字 -
-c:用于給運行在容器中的所有進(jìn)程分配 CPU 的 shares 值,這是一個相對權(quán)重,實際處理速度還與宿主機(jī)的 CPU 有關(guān) -
-m:用于限制為容器中所有進(jìn)程分配的內(nèi)存總量,以 B、K、M、G 為單位 -
-v:用于掛載一個 volume,可以用多個-v參數(shù)同時掛載多個 volume。volume 的格式為[host-dir]:[container-dir]:[rw|ro] -
-p:用于將容器的端口暴露給宿主機(jī)的端口,其常用格式為hostPort:containerPort。這樣外部主機(jī)就可以通過宿主機(jī)暴露的端口來訪問容器內(nèi)的應(yīng)用
docker start/stop/restart 命令
對于已經(jīng)存在的容器,可以通過docker start/stop/restart命令來啟動、停止和重啟,一般利用CONTAINER ID標(biāo)識來確定具體容器,某些情況下也使用容器名來確定容器。
docker start命令使用-i選項來開啟交互模式,并始終保持輸入流開放。使用-a選項來附加標(biāo)準(zhǔn)輸入、輸出或錯誤輸出。此外,docker stop和docker restart命令使用-t選項來設(shè)定容器停止前的等待時間。
3. Docker registry
Docker registry 是存儲容器鏡像的倉庫,用戶可以通過 Docker client 與 Docker registry 進(jìn)行通信,以此來完成鏡像的搜索、下載和上傳等相關(guān)操作。
Docker Hub 是 Docker 公司官方提供的鏡像倉庫,提供鏡像的公有與私有存儲服務(wù),是目前最主要的鏡像來源。除此之外,用戶還可以自行搭建私有服務(wù)器來實現(xiàn)鏡像倉庫功能。
docker pull 命令
用于從 Docker registry 中拉取 image 或 repository:
docker pull [OPTIONS] NAME[:TAG @DIGEST]
使用示例如下:
# 從官方 Hub 拉取 ubuntu:latest 鏡像
> docker pull ubuntu
# 從官方 Hub 拉取指明 "ubuntu 16.04" tag 的鏡像
> docker pull ubuntu:16.04
# 從特定的倉庫拉取 ubuntu 鏡像
> docker pull SEL/ubuntu
# 從其他服務(wù)器拉取鏡像
> docker pull 10.10.103.215:5000/sshd
docker push 命令
用于將本地的 image 或 repository 推送到 Docker Hub 的公共或私有鏡像庫,以及私有服務(wù)器:
docker push [OPTIONS] NAME[:TAG]
使用示例如下:
> docker push SEL/ubuntu
4. 鏡像管理
用戶可以在本地保存鏡像資源,為此 Docker 提供了相應(yīng)的管理子命令。
docker images 命令
通過docker images命令可以列出主機(jī)上的鏡像,默認(rèn)只列出最頂層的鏡像。使用-a選項可以顯示所有鏡像:
docker images [OPTIONS] [REPOSITORY[:TAG]]
使用示例如下:
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 16.04 b0ef3016420a 11 days ago 117MB
influxdb latest 623f651910b3 7 weeks ago 238MB
memcached latest 8230c836a4b3 7 weeks ago 62.2MB
mongo 3.2 fb885d89ea5c 7 weeks ago 300MB
mist/mailmock latest 95c29bda552f 7 weeks ago 299MB
mist/docker-socat latest f00ed0eed13f 7 weeks ago 7.8MB
mistce/logstash v3-3-1 0f90a36d12c8 2 months ago 730MB
mistce/api v3-3-1 4a21b676352f 2 months ago 705MB
mistce/nginx v3-3-1 4f55dd9b39e0 2 months ago 109MB
mistce/gocky v3-3-1 ee93caf66f70 2 months ago 440MB
mistce/elasticsearch-manage v3-3-1 10a48b9ea0e1 2 months ago 65.8MB
mistce/ui v3-3-1 b8fdbe0ccb23 2 months ago 626MB
ubuntu-with-vi-dockerfile latest 74ba87f80b96 2 months ago 169MB
ubuntu-with-vi latest 9d2fac08719d 2 months ago 169MB
ubuntu latest ea4c82dcd15a 2 months ago 85.8MB
centos latest 75835a67d134 3 months ago 200MB
hello-world latest 4ab4c602aa5e 4 months ago 1.84kB
elasticsearch 5.6.10 73e6fdf8bd4f 4 months ago 486MB
mistce/landing v3-3-1 b0e433749aa9 5 months ago 532MB
kibana 5.6.10 bc661616b61c 5 months ago 389MB
hello-world <none> 2cb0d9787c4d 6 months ago 1.85kB
traefik v1.5 fde722950ccf 9 months ago 49.7MB
mist/swagger-ui latest 0b5230f1b6c4 10 months ago 24.8MB
rabbitmq 3.6.6-management c74093aa9895 22 months ago 179MB
上例中,從REPOSITORY屬性可以判斷出鏡像是來自于官方鏡像、私人倉庫還是私有服務(wù)器。
docker rmi/rm 命令
docker rmi命令用于刪除鏡像,docker rm命令用于刪除容器。它們可以同時刪除多個鏡像或容器,也可以按條件來刪除:
docker rm [OPTIONS] CONTAINER [CONTAINER...]
docker rmi [OPTIONS] IMAGE [IMAGE...]
使用
docker rmi命令刪除鏡像時,如果已有基于該鏡像啟動的容器存在,則無法直接刪除,需要首先刪除啟動的容器。當(dāng)然,這兩個子命令都提供了-f選項,可以強(qiáng)制刪除存在容器的鏡像或啟動中的容器。
5. 容器運維操作
作為 Docker 的核心,容器的操作是重中之重,Docker 也為用戶提供了豐富的容器運維操作命令。
docker attach 命令
docker attach命令可以連接到正在運行的容器,觀察該容器的運行情況,或與容器的主進(jìn)程進(jìn)行交互:
docker attach [OPTIONS] CONTAINER
docker inspect 命令
docker inspect命令可以查看鏡像和容器的詳細(xì)信息,默認(rèn)會列出全部信息,可以通過--format參數(shù)來指定輸出的模板格式,以便輸出特定信息:
docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]
具體示例如下:
> docker inspect --format='{{.NetworkSettings.IPAddress}}' ee36
172.17.0.8
docker ps 命令
docker ps命令可以查看容器的相關(guān)信息,默認(rèn)只顯示正在運行的容器的信息??梢圆榭吹降男畔?code>CONTAINER ID、NAMES、IMAGE、STATUS、容器啟動后執(zhí)行的COMMAND、創(chuàng)建時間CREATED和綁定開啟的端口PORTS:
docker ps [OPTIONS]
docker ps命令常用的選項有-a和-l。-a選項可以查看所有容器,包括停止的容器。-l選項則只查看最新創(chuàng)建的容器,包括不在運行中的容器。
> docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8befe85aa9b2 ubuntu "/bin/bash" 4 minutes ago Exited (0) 4 minutes ago elegant_hawking
eb9dda25b0fe ubuntu:latest "/bin/bash" About an hour ago Exited (0) About an hour ago mytest
33be0880de8a ubuntu "echo 'Hello Docker'" About an hour ago Exited (0) About an hour ago loving_neumann
9dbd65001cc2 ubuntu "echo hello" About an hour ago Exited (0) About an hour ago zealous_mendeleev
ee10555e84be hello-world "/hello" About an hour ago Exited (0) About an hour ago friendly_mestorf
4219345c98a0 ubuntu-with-vi-dockerfile "/bin/bash" 2 months ago Exited (0) 2 months ago ecstatic_wilson
7257b9828da4 centos "/bin/bash" 2 months ago Exited (0) 2 months ago hopeful_chaplygin
26119a6e11bd centos "/bin/bash" 2 months ago Exited (0) 2 months ago brave_khorana
f48bc1339340 ubuntu-with-vi "/bin/bash" 2 months ago Exited (127) 2 months ago agitated_hugle
1abe6e7341ca ubuntu "/bin/bash" 2 months ago Exited (0) 2 months ago laughing_leavitt
5c5eabb13be4 hello-world "/hello" 2 months ago Exited (0) 2 months ago eloquent_wiles
8f2f6854078c 2cb0d9787c4d "/hello" 4 months ago Exited (0) 4 months ago goofy_sinoussi
> docker ps -l
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
8befe85aa9b2 ubuntu "/bin/bash" 6 minutes ago Exited (0) 6 minutes ago elegant_hawking
6. 其他子命令
docker commit 命令
docker commit命令可以將一個容器固化為一個新的鏡像:
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
提交保存時,只能選用正在運行的容器來制作新的鏡像。在制作特定鏡像時,直接使用
docker commit命令只是一個臨時性的輔助命令,不推薦使用。官方建議通過docker build命令結(jié)合 Dockerfile 來創(chuàng)建和管理鏡像。
docker events/history/logs 命令
docker events/history/logs 這 3 個命令用于查看 Docker 的系統(tǒng)日志信息。docker events命令會打印出實時的系統(tǒng)事件。docker history命令會打印出指定鏡像的歷史版本信息,即構(gòu)建該鏡像的每一層鏡像的命令記錄。docker logs命令會打印出容器中進(jìn)程的運行日志:
docker events [OPTIONS]
docker history [OPTIONS] IMAGE
docker logs [OPTIONS] CONTAINER
2.3 搭建第一個 Docker 應(yīng)用棧
Docker 的設(shè)計理念是希望用戶能夠保證一個容器只運行一個進(jìn)程,即只提供一種服務(wù)。通常情況下,用戶需要利用多個容器,分別提供不同的服務(wù),并在不同容器間互連通信,最后形成一個 Docker 集群,以實現(xiàn)特定的功能。
基于 Docker 集群構(gòu)建的應(yīng)用稱為 Docker App Stack,即 Docker 應(yīng)用棧。
以下示例將在單臺機(jī)器上利用 Docker 自帶的命令行工具,搭建一個 Docker 應(yīng)用棧,利用多個容器來組成一個特定的應(yīng)用。
在開始搭建過程前,需要對所要搭建的應(yīng)用棧進(jìn)行簡單的設(shè)計和描述:我們將搭建一個包含 6 個節(jié)點的 Docker 應(yīng)用棧,其中包括 1 個代理節(jié)點、2 個 Web 應(yīng)用節(jié)點、1 個主數(shù)據(jù)庫節(jié)點及 2 個從數(shù)據(jù)庫節(jié)點。應(yīng)用棧具體結(jié)構(gòu)如下圖所示:

如圖所示,HAProxy 是負(fù)載均衡代理節(jié)點。Redis 是非關(guān)系型的數(shù)據(jù)庫,它由一個主數(shù)據(jù)庫節(jié)點和兩個從數(shù)據(jù)庫節(jié)點組成。App 是應(yīng)用,這里將使用 Python 語言、基于 Django 架構(gòu)設(shè)計一個訪問數(shù)據(jù)庫的基礎(chǔ) Web 應(yīng)用。
1. 獲取應(yīng)用棧各節(jié)點所需鏡像
在搭建過程中,可以從 Docker Hub 獲取現(xiàn)有可用的鏡像,在這些鏡像的基礎(chǔ)上啟動容器,按照需求進(jìn)行修改來實現(xiàn)既定的功能。
> docker pull ubuntu
> docker pull django
> docker pull haproxy
> docker pull redis
> docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
haproxy latest d23194a3929a 40 hours ago 72MB
redis latest 5d2989ac9711 12 days ago 95MB
ubuntu latest 1d9c17228a9e 12 days ago 86.7MB
django latest eb40dcf64078 2 years ago 436MB
2. 應(yīng)用棧容器節(jié)點互聯(lián)
鑒于在同一主機(jī)下搭建容器應(yīng)用棧的環(huán)境,只需要完成容器互聯(lián)來實現(xiàn)容器間的通信即可,可以采用docker run命令的--link選項建立容器間的互聯(lián)關(guān)系。使用示例如下:
> docker run --link redis:redis --name console ubuntu bash
上例將在 ubuntu 鏡像上啟動一個容器,并命名為console,同時將新啟動的console容器連接到名為redis的容器上。
通過
--link選項來建立容器間的連接,不但可以避免容器的 IP 和端口暴露到外網(wǎng)所導(dǎo)致的安全問題,還可以防止容器在重啟后 IP 地址變化導(dǎo)致的訪問失效,原理類似于 DNS 的域名和地址映射。
回到應(yīng)用棧的搭建,應(yīng)用棧各節(jié)點的連接信息如下:
- 啟動
redis-master容器節(jié)點 - 兩個
redis-slave容器節(jié)點啟動時要連接到redis-master上 - 兩個 App 容器節(jié)點啟動時要連接到
redis-master上 - HAProxy 容器節(jié)點啟動時要連接到兩個 App 節(jié)點上
綜上所述,容器的啟動順序為:
redis-master --> redis-slave --> APP --> HAProxy
此外,為了能夠從外網(wǎng)訪問應(yīng)用棧,并通過 HAProxy 節(jié)點來訪問應(yīng)用棧中的 App,在啟動 HAProxy 容器節(jié)點時,需要利用-p參數(shù)暴露端口給主機(jī),即可從外網(wǎng)訪問搭建的應(yīng)用棧。以下是整個應(yīng)用棧的搭建流程示例。
3. 應(yīng)用棧容器節(jié)點啟動
# 啟動 Redis 容器
> docker run -it --name redis-master redis /bin/bash
> docker run -it --name redis-slave1 --link redis-master:master redis /bin/bash
> docker run -it --name redis-slave2 --link redis-master:master redis /bin/bash
# 啟動 Django 容器,即應(yīng)用
> docker run -it --name APP1 --link redis-master:db -v ~/Projects/Django/App1:/usr/src/app django /bin/bash
> docker run -it --name APP2 --link redis-master:db -v ~/Projects/Django/App2:/usr/src/app django /bin/bash
# 啟動 HAProxy 容器
> docker run -it --name HAProxy --link APP1:APP1 --link APP2:APP2 -p 6301:6301 -v ~/Projects/HAProxy:/tmp haproxy /bin/bash
啟動的容器信息可以通過docker ps命令查看:
> docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
733e71e16ac5 haproxy "/docker-entrypoint.…" 30 seconds ago Up 29 seconds 0.0.0.0:6301->6301/tcp HAProxy
3f91ac2a23a6 django "/bin/bash" 47 seconds ago Up 46 seconds APP2
e94c7ff2c319 django "/bin/bash" 3 minutes ago Up 3 minutes APP1
5e7994e6ad59 redis "docker-entrypoint.s…" 5 minutes ago Up 4 minutes 6379/tcp redis-slave2
6fac6db730c3 redis "docker-entrypoint.s…" 8 minutes ago Up 8 minutes 6379/tcp redis-slave1
936c426faa29 redis "docker-entrypoint.s…" 8 minutes ago Up 8 minutes 6379/tcp redis-master
至此,所有搭建應(yīng)用棧所需容器的啟動工作已經(jīng)完成。
4. 應(yīng)用棧容器節(jié)點的配置
Redis Master 主數(shù)據(jù)庫容器節(jié)點的配置
Redis Master 主數(shù)據(jù)庫容器節(jié)點啟動后,我們需要在容器中添加 Redis 的啟動配置文件,以啟動 Redis 數(shù)據(jù)庫。
由于容器的輕量化設(shè)計,其中缺乏相應(yīng)的文本編輯命令工具,這時可以利用 volume 來實現(xiàn)文件的創(chuàng)建。在容器啟動時,利用
-v參數(shù)掛載 volume,在主機(jī)和容器之間共享數(shù)據(jù),就可以直接在主機(jī)上創(chuàng)建和編輯相關(guān)文件。
在利用 Redis 鏡像啟動容器時,鏡像中已經(jīng)集成了 volume 的掛載命令,通過docker inspect命令查看redis-master所掛載 volume 的情況:
> docker inspect --format "{{.Mounts}}" redis-master
[{volume a77509a99df7d7a9d78313c1a1bb19619bac98fedadd78dbab17f072a49a905c /var/lib/docker/volumes/a77509a99df7d7a9d78313c1a1bb19619bac98fedadd78dbab17f072a49a905c/_data /data local true }]
可以發(fā)現(xiàn),該 volume 在主機(jī)中的目錄為/var/lib/docker/volumes/a77509a99df7d7a9d78313c1a1bb19619bac98fedadd78dbab17f072a49a905c/_data,在容器中的目錄為/data。進(jìn)入主機(jī)目錄創(chuàng)建 Redis 的啟動配置文件:
> cd /var/lib/docker/volumes/a77509a99df7d7a9d78313c1a1bb19619bac98fedadd78dbab17f072a49a905c/_data
> cp <your-own-redis-dir>/redis.conf redis.conf
> vim redis.conf
對于 Redis 主數(shù)據(jù)庫,需要修改模板文件中的如下幾個參數(shù):
daemonize yes
pidfile /var/run/redis.pid
protected-mode no # 關(guān)閉保護(hù)模式
在主機(jī)創(chuàng)建好啟動配置文件后,切換到容器中的 volume 目錄,并復(fù)制redis.conf到 Redis 的執(zhí)行工作目錄,然后啟動 Redis 服務(wù)器:
> cd /data
> cp redis.conf /usr/local/bin/
> cd /usr/local/bin/
> redis-server redis.conf
Redis Slave 從數(shù)據(jù)庫容器節(jié)點的配置
與redis-master容器節(jié)點類似,在啟動redis-slave容器節(jié)點后,首先需要查看 volume 信息,然后將redis.conf復(fù)制到對應(yīng)的目錄中。不同的是,對于 Redis 從數(shù)據(jù)庫,需要修改如下幾個參數(shù):
daemonize yes
pidfile /var/run/redis.pid
protected-mode no # 關(guān)閉保護(hù)模式
replicaof master 6379 # 之前是 slaveof
replicaof參數(shù)的使用格式為replicaof <masterip> <masterport>
在主機(jī)修改好redis.conf配置文件后,切換到容器中的/data目錄,并復(fù)制配置文件到 Redis 的執(zhí)行工作目錄,然后啟動 Redis 服務(wù)器:
> cd /data
> cp redis.conf /usr/local/bin/
> cd /usr/local/bin/
> redis-server redis.conf
594:C 10 Jan 2019 23:10:43.936 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo
594:C 10 Jan 2019 23:10:43.936 # Redis version=5.0.3, bits=64, commit=00000000, modified=0, pid=594, just started
594:C 10 Jan 2019 23:10:43.936 # Configuration loaded
同理,可以完成對另一個 Redis Slave 容器節(jié)點的配置。至此,便完成了所有 Redis 數(shù)據(jù)庫容器節(jié)點的配置。
Redis 數(shù)據(jù)庫容器節(jié)點的測試
完成 Redis Master 和 Redis Slave 容器節(jié)點的配置以及服務(wù)器的啟動后,可以通過啟動redis-cli來測試數(shù)據(jù)庫。
首先,在redis-master容器內(nèi),啟動redis-cli,并存儲一個數(shù)據(jù):
> redis-cli
127.0.0.1:6379> info replication
# Replication
role:master
connected_slaves:2
slave0:ip=172.17.0.3,port=6379,state=online,offset=1260,lag=0
slave1:ip=172.17.0.4,port=6379,state=online,offset=1260,lag=0
master_replid:295c948cc1bbdf21eb49fdd8417ba5b4b76fc32b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1260
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:1
repl_backlog_histlen:1260
127.0.0.1:6379> set master 936c
OK
127.0.0.1:6379> get master
"936c"
隨后,在redis-slave1和redis-slave2兩個容器中,分別啟動redis-cli并查詢先前在redis-master數(shù)據(jù)庫中存儲的數(shù)據(jù):
> redis-cli
127.0.0.1:6379> info replication
# Replication
role:slave
master_host:master
master_port:6379
master_link_status:up
master_last_io_seconds_ago:3
master_sync_in_progress:0
slave_repl_offset:1330
slave_priority:100
slave_read_only:1
connected_slaves:0
master_replid:295c948cc1bbdf21eb49fdd8417ba5b4b76fc32b
master_replid2:0000000000000000000000000000000000000000
master_repl_offset:1330
second_repl_offset:-1
repl_backlog_active:1
repl_backlog_size:1048576
repl_backlog_first_byte_offset:127
repl_backlog_histlen:1204
127.0.0.1:6379> get master
"936c"
可以看到redis-master主數(shù)據(jù)庫中的數(shù)據(jù)已經(jīng)自動同步到了兩個從數(shù)據(jù)庫中。至此,應(yīng)用棧的數(shù)據(jù)庫部分已搭建完成,并通過測試。
APP 容器節(jié)點( Django)的配置
Django 容器啟動后,需要利用 Django 框架,開發(fā)一個簡單的 Web 程序。
為了訪問數(shù)據(jù)庫,需要在容器中安裝 Python 語言的 Redis 支持包:
> pip install redis
安裝完成后,驗證 Redis 支持包是否安裝成功:
> python
Python 3.4.5 (default, Dec 14 2016, 18:54:20)
[GCC 4.9.2] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import redis
>>> print(redis.__file__)
/usr/local/lib/python3.4/site-packages/redis/__init__.py
如果沒有報錯,就說明已經(jīng)可以使用 Python 語言來調(diào)用 Redis 數(shù)據(jù)庫。接下來開始創(chuàng)建 Web 程序。以APP1為例,首先在容器的 volume 目錄/usr/src/app/下創(chuàng)建 APP:
# 在容器內(nèi)
> cd /usr/src/app/
> mkdir dockerweb
> cd dockerweb/
> django-admin.py startproject redisweb
> ls
redisweb
> cd redisweb
> ls
manage.py redisweb
> python manage.py startapp helloworld
> ls
helloworld manage.py redisweb
在容器內(nèi)創(chuàng)建好 APP 后,切換到主機(jī)的 volume 目錄~/Projects/Django/App1,進(jìn)行相應(yīng)的編輯來配置 APP:
# 在主機(jī)內(nèi)
> cd ~/Projects/Django/App1
> ls
dockerweb
可以看到,在容器內(nèi)創(chuàng)建的 APP 文件在主機(jī)的 volume 目錄下同樣可見。之后修改helloworld應(yīng)用的視圖文件views.py:
> cd dockerweb/redisweb/helloworld
> ls
admin.py __init__.py models.py views.py apps.py migrations tests.py
> vim views.py
為了簡化設(shè)計,只要求完成 Redis 數(shù)據(jù)庫信息輸出,以及從 Redis 數(shù)據(jù)庫存儲和讀取數(shù)據(jù)的結(jié)果輸出。viwes.py文件如下:
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
import redis
def hello(request):
str = redis.__file__
str += "<br>"
r = redis.Redis(host="db", port=6379, db=0)
info = r.info()
str += ("Set Hi <br>")
r.set('Hi', 'HelloWorld-APP1')
str += ("Get Hi: %s <br>" % r.get('Hi'))
str += ("Redis Info: <br>")
str += ("Key: Info Value")
for key in info:
str += ("%s: %s <br>" % (key, info[key]))
return HttpResponse(str)
完成views.py文件修改后,接下來修改redisweb項目的配置文件setting.py,并添加新建的helloworld應(yīng)用:
> cd ../redisweb/
> ls
__init__.py __pycache__ settings.py urls.py wsgi.py
> vim settings.py
在settings.py文件中的INSTALLED_APPS選項下添加 helloworld,并修改ALLOWED_HOSTS:
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = ['*']
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'helloworld'
]
此處為了演示方便將
ALLOWED_HOSTS設(shè)置為['*']即允許所有連接,在實際開發(fā)環(huán)境中請勿按此設(shè)置。另外在生產(chǎn)環(huán)境中還需將DEBUG選項設(shè)置為False。
最后,修改redisweb項目的 URL 模式文件urls.py,它將設(shè)置訪問應(yīng)用的 URL 模式,并為 URL 模式調(diào)用視圖函數(shù)之間的映射表:
> vim urls.py
在urls.py文件中,引入 helloworld 應(yīng)用的hello視圖,并為hello視圖添加一個urlpatterns變量。urls.py文件內(nèi)容如下:
from django.conf.urls import url
from django.contrib import admin
from helloworld.views import hello
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^helloworld$', hello),
]
在主機(jī)下修改完成這幾個文件后,需要再次進(jìn)入APP1容器,在目錄/usr/src/app/dockerweb/redisweb下完成項目的生成:
> python manage.py makemigrations
No changes detected
> python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OK
> python manage.py createsuperuser
Username (leave blank to use 'root'): admin
Email address: admin@gmail.com
Password:
Password (again):
Superuser created successfully.
舊版本的 Django 使用
syncdb命令來同步數(shù)據(jù)庫并創(chuàng)建admin賬戶。在新版 Django 中syncdb命令已被移除,使用createsuperuser命令創(chuàng)建管理員賬戶。
至此,APP1容器的所有配置已經(jīng)完成,另一個APP2容器配置也是同樣的過程,這樣就完成了應(yīng)用棧 APP 部分的全部配置。
在啟動 APP 的 Web 服務(wù)器時,可以指定服務(wù)器的端口和 IP 地址。為了通過 HAProxy 容器節(jié)點接受外網(wǎng)所有的公共 IP 地址訪問,實現(xiàn)負(fù)載均衡,需要指定服務(wù)器的 IP 地址和端口。對于APP1使用 8001 端口,而APP2則使用 8002 端口。同時,都使用0.0.0.0地址。以APP1為例,啟動服務(wù)器的過程如下:
> python manage.py runserver 0.0.0.0:8001
Performing system checks...
System check identified no issues (0 silenced).
January 11, 2019 - 03:35:58
Django version 1.10.4, using settings 'redisweb.settings'
Starting development server at http://0.0.0.0:8001/
Quit the server with CONTROL-C.
[11/Jan/2019 03:37:01] "GET /helloworld HTTP/1.1" 200 3999
[11/Jan/2019 03:37:14] "GET /admin/ HTTP/1.1" 200 2779
...
HAProxy 容器節(jié)點的配置
在完成了數(shù)據(jù)庫和 APP 部分的應(yīng)用棧部署后,最后部署一個 HAProxy 負(fù)載均衡代理的容器節(jié)點,所有對應(yīng)用棧的訪問將通過它來實現(xiàn)負(fù)載均衡。
首先,將 HAProxy 的啟動配置文件復(fù)制進(jìn)容器中。在主機(jī)的 volume 目錄~/Projects/HAProxy下,執(zhí)行以下命令:
> cd ~/Projects/HAProxy
> vim haproxy.cfg
其中,haproxy.cfg配置文件的內(nèi)容如下:
global
log 127.0.0.1 local0 # 日志輸入配置,所有日志都記錄在本機(jī),通過 local0 輸出
maxconn 4096 # 最大連接數(shù)
chroot /usr/local/sbin # 改變當(dāng)前工作目錄
daemon # 以后臺形式運行 HAProxy 實例
nbproc 4 # 啟動 4 個 HAProxy 實例
pidfile /usr/local/sbin/haproxy.pid # pid 文件位置
defaults
log 127.0.0.1 local3 # 日志文件的輸出定向
mode http # { tcp|http|health } 設(shè)定啟動實例的協(xié)議類型
option dontlognull # 保證 HAProxy 不記錄上級負(fù)載均衡發(fā)送過來的用于檢測狀態(tài)沒有數(shù)據(jù)的心跳包
option redispatch # 當(dāng) serverId 對應(yīng)的服務(wù)器掛掉后,強(qiáng)制定向到其他健康>的服務(wù)器
retries 2 # 重試 2 次連接失敗就認(rèn)為服務(wù)器不可用,主要通過后面的 check 檢查
maxconn 2000 # 最大連接數(shù)
balance roundrobin # balance 有兩個可用選項:roundrobin 和 source,其中,roundrobin 表示
# 輪詢,而 source 表示 HAProxy 不采用輪詢的策略,而是把來自某個 IP 的請求轉(zhuǎn)發(fā)給一個固定 IP 的后端
timeout connect 5000ms # 連接超時時間
timeout client 50000ms # 客戶端連接超時時間
timeout server 50000ms # 服務(wù)器端連接超時時間
listen redis_proxy
bind 0.0.0.0:6301
stats enable
stats uri /haproxy-stats
server APP1 APP1:8001 check inter 2000 rise 2 fall 5 # 你的均衡節(jié)點
server APP2 aPP2:8002 check inter 2000 rise 2 fall 5
隨后,進(jìn)入到容器的 volume 目錄/tmp下,將 HAProxy 的啟動配置文件復(fù)制到 HAProxy 的工作目錄中:
# 在容器中
> cd /tmp
> cp haproxy.cfg /usr/local/sbin/
> cd /usr/local/sbin/
> ls
haproxy haproxy.cfg
接下來利用該配置文件來啟動 HAProxy 代理:
> haproxy -f haproxy.cfg
另外,如果修改了配置文件的內(nèi)容,需要先結(jié)束所有的 HAProxy 進(jìn)程,并重新啟動代理。Docker 鏡像為了精簡體積,本身并沒有安裝ps、killall等進(jìn)程管理命令,需要手動在容器中安裝:
> apt-get update
> apt-get install procps # ps、pkill
> apt-get install psmisc # killall
> killall haproxy
至此,完成了 HAProxy 容器節(jié)點的全部部署,同時也完成了整個 Docker 應(yīng)用棧的部署。
應(yīng)用棧訪問測試
參考結(jié)構(gòu)圖可知,整個應(yīng)用棧群的訪問是通過 HAProxy 代理節(jié)點來進(jìn)行的。HAProxy 在啟動時通過-p 6301:6301參數(shù),映射了容器訪問的端口到主機(jī)上,因此可在其他主機(jī)上通過本地主機(jī)的 IP 地址和端口來訪問搭建好的應(yīng)用棧。
首先在本地主機(jī)上進(jìn)行測試。在瀏覽器中訪問http://172.17.0.7:6301/helloworld,可以查看來自 APP1 或 APP2 的頁面內(nèi)容,具體訪問到的 APP 容器節(jié)點會由 HAProxy 代理進(jìn)行均衡分配。其中,172.17.0.7為 HAProxy 容器的 IP 地址。


本地測試通過后,嘗試在其他主機(jī)上通過應(yīng)用棧入口主機(jī)的 IP 地址和暴露的 6301 端口來訪問該應(yīng)用棧,即訪問http://116.56.129.153:6301/helloworld,可看到來自 APP1 或 APP2 容器節(jié)點的頁面,訪問http://116.56.129.153:6301/haproxy-stats
則可看到 HAProxy 的后臺管理頁面及統(tǒng)計數(shù)據(jù)。其中,116.56.129.153為 宿主機(jī)的 IP 地址。

