Docker 筆記 1:Docker 基礎與搭建第一個 Docker 應用棧

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

《Docker 容器與容器云(第2版)》

1. 從容器到容器云

1.1 云計算平臺

經典云計算架構包括 IaaS(Infrastructure as a Service,基礎設施即服務)、PaaS(Platform as a Service,平臺即服務)、SaaS(Software as a Service,軟件即服務)三層服務,如下圖所示。

云平臺經典架構

1.2 容器技術生態系統

容器技術生態系統

可以看出,容器技術生態系統自上而下分別覆蓋了 IaaS 層PaaS 層所涉及的各類問題,包括資源調度、編排、部署、監控、配置管理、存儲網絡管理、安全、容器化應用支撐平臺等。容器技術主要帶來了以下幾點好處:

  • 持續部署與測試:容器消除了線上線下的環境差異,保證了應用生命周期的環境一致性標準化
  • 跨云平臺支持:越來越多的云平臺都已支持容器,用戶無需擔心受到云平臺的捆綁。
  • 環境標準化和版本控制:可使用 Git 等工具對容器鏡像進行版本控制。相比基于代碼的版本控制來說,還能夠對整個應用運行環境實現版本控制,一旦出現故障可以快速回滾。相比以前的虛擬機鏡像,容器壓縮和備份速度更快,鏡像啟動也像啟動一個普通進程一樣快速。
  • 高資源利用率與隔離:容器沒有管理程序的額外開銷,與底層共享操作系統,性能更優,負載更低。同時,容器擁有不錯的資源隔離與限制能力,可以精確的對應用分配 CPU、內存等資源,保證應用之間不會相互影響。
  • 容器跨平臺性與鏡像:容器在原有 Linux 容器的基礎上進行大膽革新,為容器設定了一整套標準化的配置方法,將應用及其依賴的運行環境打包成鏡像,大大提高了容器的跨平臺性
  • 易于理解且易用Docker 的英文原意是處理集裝箱的碼頭工人,標志是鯨魚運送一大堆集裝箱,集裝箱就是容器。容器的易用性加速了容器標準化的步伐。
  • 應用鏡像倉庫:Docker 官方構建了一個鏡像倉庫,已經累積了成千上萬的鏡像,所有人都可以自由的下載微服務組件,為開發者提供了巨大便利。

1.3 從容器到容器云

容器云以容器為資源分割和調度的基本單位,封裝整個軟件運行時環境,為開發者和系統管理員提供用于構建、發布和運行分布式應用的平臺。

  • 當容器云專注于資源共享與隔離、容器編排與部署時,它更接近傳統的 IaaS
  • 當容器云滲透到應用支撐與運行時環境時,它更接近傳統的 PaaS

容器云并不僅限于 Docker,基于 rkt 容器的 CoreOS 項目也是容器云。Docker 最初發布時只是一個單機下的容器管理工具,隨后 Docker 公司發布了 Compose、Machine、Swarm編排部署工具,并收購了 Socketplane 解決集群化后的網絡問題

除了 Docker 公司之外,業界許多云計算廠商也對基于 Docker 的容器云做了巨大的投入。例如 Fleet、Flynn、Deis 以及目前成為事實主流標準的 Kubernetes,都是基于 Docker 技術構建的廣為人知的容器云。

2. Docker 基礎

2.1 Docker 的安裝

安裝 Docker基本要求如下:

  • 只支持 64 位 CPU 架構的計算機,目前不支持 32 位 CPU
  • 建議系統的 Linux 內核版本3.10及以上
  • Linux 內核需開啟 cgroupsnamespace 功能

安裝過程可參考 CentOS 7 安裝 Docker CE

2.2 Docker 操作參數解讀

docker命令的執行一般都需要 root 權限,因為 Docker 的命令行工具dockerDocker daemon 是同一個二進制文件,而 Docker daemon 負責接收并執行來自docker的命令,它的運行需要 root 權限。同時,從 Docker 0.5.2 版本開始,Docker daemon 默認綁定一個 UNIX Socket 來代替原有的 TCP 端口,該 UNIX Socket 默認是屬于 root 用戶的。

用戶在使用 Docker 時,需要使用 Docker 命令行工具dockerDocker daemon 建立通信。Docker daemon 是 Docker 守護進程,負責接收并分發執行 Docker 命令。可以使用dockerdocker 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的詳細信息:

> 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

推薦閱讀:

  1. docker專題(2):docker常用管理命令(上)| Sean's Notes
  2. docker專題(2):docker常用管理命令(下)| Sean's Notes

根據命令的用途,可將 Docker 子命令進行如下分類

Docker 子命令分類

docker命令的使用出發,可以梳理出如下的命令結構圖

Docker 命令結構圖

下面選擇每個功能分類中常用的子命令進行用法和操作參數的解讀。

1. Docker 環境信息

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命令一般結合docker version命令使用,兩者結合能夠提取到足夠詳細的 Docker 環境信息

> 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命令用來基于特定的鏡像創建一個容器,并依據選項來控制該容器:

> docker run ubuntu echo "Hello Docker"
Hello Docker

該命令從 ubuntu 鏡像啟動一個容器,并執行echo命令打印 Hello Docker。執行完echo命令后,容器將停止運行docker run命令啟動的容器會隨機分配一個容器 IDCONTAINER ID,用以標識該容器。

root@ubuntu:~> docker run -i -t --name mytest ubuntu:latest /bin/bash
root@eb9dda25b0fe:/>

上例中,docker run命令啟動一個容器,并為它分配一個偽終端執行/bin/bash命令,用戶可以在該偽終端與容器進行交互。其中:

  • -i:表示使用交互模式,始終保持輸入流開放
  • -t:表示分配一個偽終端,一般兩個參數結合時使用-it
  • --name:可以指定啟動的容器的名字。若無此選項,Docker 將為容器隨機分配一個名字
  • -c:用于給運行在容器中的所有進程分配 CPUshares 值,這是一個相對權重,實際處理速度還與宿主機的 CPU 有關
  • -m:用于限制為容器中所有進程分配的內存總量,以 B、K、M、G 為單位
  • -v:用于掛載一個 volume,可以用多個-v參數同時掛載多個 volume。volume 的格式為[host-dir]:[container-dir]:[rw|ro]
  • -p:用于將容器的端口暴露給宿主機的端口,其常用格式為hostPort:containerPort。這樣外部主機就可以通過宿主機暴露的端口來訪問容器內的應用
docker start/stop/restart 命令

對于已經存在的容器,可以通過docker start/stop/restart命令來啟動、停止和重啟,一般利用CONTAINER ID標識來確定具體容器,某些情況下也使用容器名來確定容器。

docker start命令使用-i選項來開啟交互模式,并始終保持輸入流開放。使用-a選項來附加標準輸入、輸出或錯誤輸出。此外,docker stopdocker restart命令使用-t選項來設定容器停止前的等待時間

3. Docker registry

Docker registry存儲容器鏡像的倉庫,用戶可以通過 Docker clientDocker registry 進行通信,以此來完成鏡像的搜索、下載和上傳等相關操作。

Docker Hub 是 Docker 公司官方提供的鏡像倉庫,提供鏡像的公有與私有存儲服務,是目前最主要的鏡像來源。除此之外,用戶還可以自行搭建私有服務器來實現鏡像倉庫功能。

docker pull 命令

用于從 Docker registry 中拉取 imagerepository

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
# 從其他服務器拉取鏡像
> docker pull 10.10.103.215:5000/sshd
docker push 命令

用于將本地的 imagerepository 推送到 Docker Hub 的公共或私有鏡像庫,以及私有服務器:

docker push [OPTIONS] NAME[:TAG]

使用示例如下:

> docker push SEL/ubuntu

4. 鏡像管理

用戶可以在本地保存鏡像資源,為此 Docker 提供了相應的管理子命令。

docker images 命令

通過docker images命令可以列出主機上的鏡像,默認只列出最頂層的鏡像。使用-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屬性可以判斷出鏡像是來自于官方鏡像、私人倉庫還是私有服務器

docker rmi/rm 命令

docker rmi命令用于刪除鏡像docker rm命令用于刪除容器。它們可以同時刪除多個鏡像或容器,也可以按條件來刪除:

docker rm [OPTIONS] CONTAINER [CONTAINER...]
docker rmi [OPTIONS] IMAGE [IMAGE...]

使用docker rmi命令刪除鏡像時,如果已有基于該鏡像啟動的容器存在,則無法直接刪除,需要首先刪除啟動的容器。當然,這兩個子命令都提供了-f選項,可以強制刪除存在容器的鏡像或啟動中的容器。

5. 容器運維操作

作為 Docker 的核心,容器的操作是重中之重,Docker 也為用戶提供了豐富的容器運維操作命令。

docker attach 命令

docker attach命令可以連接到正在運行的容器,觀察該容器的運行情況,或與容器的主進程進行交互

docker attach [OPTIONS] CONTAINER
docker inspect 命令

docker inspect命令可以查看鏡像和容器的詳細信息,默認會列出全部信息,可以通過--format參數來指定輸出的模板格式,以便輸出特定信息:

docker inspect [OPTIONS] CONTAINER|IMAGE [CONTAINER|IMAGE...]

具體示例如下:

> docker inspect --format='{{.NetworkSettings.IPAddress}}' ee36
172.17.0.8
docker ps 命令

docker ps命令可以查看容器的相關信息,默認只顯示正在運行的容器的信息。可以查看到的信息包括CONTAINER IDNAMESIMAGESTATUS、容器啟動后執行的COMMAND、創建時間CREATED和綁定開啟的端口PORTS

docker ps [OPTIONS]

docker ps命令常用的選項有-a-l-a選項可以查看所有容器,包括停止的容器。-l選項則只查看最新創建的容器,包括不在運行中的容器。

> 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命令結合 Dockerfile 來創建和管理鏡像。

docker events/history/logs 命令

docker events/history/logs 這 3 個命令用于查看 Docker 的系統日志信息。docker events命令會打印出實時的系統事件docker history命令會打印出指定鏡像的歷史版本信息,即構建該鏡像的每一層鏡像的命令記錄docker logs命令會打印出容器中進程的運行日志

docker events [OPTIONS]
docker history [OPTIONS] IMAGE
docker logs [OPTIONS] CONTAINER

2.3 搭建第一個 Docker 應用棧

Docker設計理念是希望用戶能夠保證一個容器只運行一個進程,即只提供一種服務。通常情況下,用戶需要利用多個容器,分別提供不同的服務,并在不同容器間互連通信,最后形成一個 Docker 集群,以實現特定的功能。

基于 Docker 集群構建的應用稱為 Docker App Stack,即 Docker 應用棧

以下示例將在單臺機器上利用 Docker 自帶的命令行工具,搭建一個 Docker 應用棧,利用多個容器來組成一個特定的應用。

在開始搭建過程前,需要對所要搭建的應用棧進行簡單的設計和描述:我們將搭建一個包含 6 個節點的 Docker 應用棧,其中包括 1 個代理節點、2 個 Web 應用節點、1 個主數據庫節點及 2 個從數據庫節點。應用棧具體結構如下圖所示:

Docker 應用棧結構圖

如圖所示,HAProxy負載均衡代理節點。Redis 是非關系型的數據庫,它由一個主數據庫節點和兩個從數據庫節點組成。App 是應用,這里將使用 Python 語言、基于 Django 架構設計一個訪問數據庫的基礎 Web 應用。

1. 獲取應用棧各節點所需鏡像

在搭建過程中,可以從 Docker Hub 獲取現有可用的鏡像,在這些鏡像的基礎上啟動容器,按照需求進行修改來實現既定的功能。

> 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. 應用棧容器節點互聯

鑒于在同一主機下搭建容器應用棧的環境,只需要完成容器互聯來實現容器間的通信即可,可以采用docker run命令的--link選項建立容器間的互聯關系。使用示例如下:

> docker run --link redis:redis --name console ubuntu bash

上例將在 ubuntu 鏡像上啟動一個容器,并命名為console,同時將新啟動的console容器連接到名為redis的容器上

通過--link選項來建立容器間的連接,不但可以避免容器的 IP 和端口暴露到外網所導致的安全問題,還可以防止容器在重啟后 IP 地址變化導致的訪問失效,原理類似于 DNS 的域名和地址映射。

回到應用棧的搭建,應用棧各節點的連接信息如下:

  • 啟動redis-master容器節點
  • 兩個redis-slave容器節點啟動時要連接到redis-master
  • 兩個 App 容器節點啟動時要連接到redis-master
  • HAProxy 容器節點啟動時要連接到兩個 App 節點上

綜上所述,容器的啟動順序為:

redis-master --> redis-slave --> APP --> HAProxy

此外,為了能夠從外網訪問應用棧,并通過 HAProxy 節點來訪問應用棧中的 App,在啟動 HAProxy 容器節點時,需要利用-p參數暴露端口給主機,即可從外網訪問搭建的應用棧。以下是整個應用棧的搭建流程示例。

3. 應用棧容器節點啟動

# 啟動 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 容器,即應用
> 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

至此,所有搭建應用棧所需容器的啟動工作已經完成。

4. 應用棧容器節點的配置

Redis Master 主數據庫容器節點的配置

Redis Master 主數據庫容器節點啟動后,我們需要在容器中添加 Redis 的啟動配置文件,以啟動 Redis 數據庫

由于容器的輕量化設計,其中缺乏相應的文本編輯命令工具,這時可以利用 volume 來實現文件的創建。在容器啟動時,利用-v參數掛載 volume,在主機和容器之間共享數據,就可以直接在主機上創建和編輯相關文件

在利用 Redis 鏡像啟動容器時,鏡像中已經集成了 volume 的掛載命令,通過docker inspect命令查看redis-master所掛載 volume 的情況

> docker inspect --format "{{.Mounts}}" redis-master
[{volume a77509a99df7d7a9d78313c1a1bb19619bac98fedadd78dbab17f072a49a905c /var/lib/docker/volumes/a77509a99df7d7a9d78313c1a1bb19619bac98fedadd78dbab17f072a49a905c/_data /data local  true }]

可以發現,該 volume 在主機中的目錄/var/lib/docker/volumes/a77509a99df7d7a9d78313c1a1bb19619bac98fedadd78dbab17f072a49a905c/_data在容器中的目錄/data。進入主機目錄創建 Redis 的啟動配置文件

> cd /var/lib/docker/volumes/a77509a99df7d7a9d78313c1a1bb19619bac98fedadd78dbab17f072a49a905c/_data
> cp <your-own-redis-dir>/redis.conf redis.conf
> vim redis.conf

對于 Redis 主數據庫,需要修改模板文件中的如下幾個參數:

daemonize yes
pidfile /var/run/redis.pid
protected-mode no # 關閉保護模式

在主機創建好啟動配置文件后,切換到容器中的 volume 目錄,并復制redis.conf到 Redis 的執行工作目錄,然后啟動 Redis 服務器

> cd /data
> cp redis.conf /usr/local/bin/
> cd /usr/local/bin/
> redis-server redis.conf
Redis Slave 從數據庫容器節點的配置

redis-master容器節點類似,在啟動redis-slave容器節點后,首先需要查看 volume 信息,然后redis.conf復制到對應的目錄中。不同的是,對于 Redis 從數據庫,需要修改如下幾個參數:

daemonize yes
pidfile /var/run/redis.pid
protected-mode no # 關閉保護模式
replicaof master 6379 # 之前是 slaveof

replicaof參數的使用格式replicaof <masterip> <masterport>

在主機修改好redis.conf配置文件后,切換到容器中的/data目錄,并復制配置文件到 Redis 的執行工作目錄,然后啟動 Redis 服務器

> 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 容器節點的配置。至此,便完成了所有 Redis 數據庫容器節點的配置

Redis 數據庫容器節點的測試

完成 Redis MasterRedis Slave 容器節點的配置以及服務器的啟動后,可以通過啟動redis-cli來測試數據庫。

首先,在redis-master容器內,啟動redis-cli,并存儲一個數據:

> 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-slave1redis-slave2兩個容器中,分別啟動redis-cli查詢先前在redis-master數據庫中存儲的數據

> 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主數據庫中的數據已經自動同步到了兩個從數據庫中。至此,應用棧的數據庫部分已搭建完成,并通過測試。

APP 容器節點( Django)的配置

Django 容器啟動后,需要利用 Django 框架,開發一個簡單的 Web 程序。

為了訪問數據庫,需要在容器中安裝 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

如果沒有報錯,就說明已經可以使用 Python 語言來調用 Redis 數據庫。接下來開始創建 Web 程序。以APP1為例,首先在容器的 volume 目錄/usr/src/app/下創建 APP

# 在容器內
> 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

在容器內創建好 APP 后,切換到主機的 volume 目錄~/Projects/Django/App1進行相應的編輯來配置 APP

# 在主機內
> cd ~/Projects/Django/App1
> ls
dockerweb

可以看到,在容器內創建的 APP 文件在主機的 volume 目錄下同樣可見。之后修改helloworld應用的視圖文件views.py

> cd dockerweb/redisweb/helloworld
> ls
admin.py  __init__.py  models.py  views.py  apps.py   migrations  tests.py
> vim views.py

為了簡化設計,只要求完成 Redis 數據庫信息輸出,以及從 Redis 數據庫存儲和讀取數據的結果輸出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應用

> 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設置為['*']允許所有連接,在實際開發環境中請勿按此設置。另外在生產環境中還需DEBUG選項設置為False

最后,修改redisweb項目的 URL 模式文件urls.py,它將設置訪問應用的 URL 模式,并為 URL 模式調用視圖函數之間的映射表

> vim urls.py

urls.py文件中,引入 helloworld 應用的hello視圖,并hello視圖添加一個urlpatterns變量urls.py文件內容如下:

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),
]

在主機下修改完成這幾個文件后,需要再次進入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命令來同步數據庫并創建admin賬戶。在新版 Django 中syncdb命令已被移除,使用createsuperuser命令創建管理員賬戶

至此,APP1容器的所有配置已經完成,另一個APP2容器配置也是同樣的過程,這樣就完成了應用棧 APP 部分的全部配置

在啟動 APP 的 Web 服務器時,可以指定服務器的端口和 IP 地址。為了通過 HAProxy 容器節點接受外網所有的公共 IP 地址訪問,實現負載均衡,需要指定服務器的 IP 地址和端口。對于APP1使用 8001 端口,而APP2則使用 8002 端口。同時,都使用0.0.0.0地址。以APP1為例,啟動服務器的過程如下:

> 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 容器節點的配置

在完成了數據庫和 APP 部分的應用棧部署后,最后部署一個 HAProxy 負載均衡代理的容器節點,所有對應用棧的訪問將通過它來實現負載均衡

首先,將 HAProxy 的啟動配置文件復制進容器中。在主機的 volume 目錄~/Projects/HAProxy下,執行以下命令:

> cd ~/Projects/HAProxy
> vim haproxy.cfg

其中,haproxy.cfg配置文件的內容如下:

global
    log 127.0.0.1  local0  # 日志輸入配置,所有日志都記錄在本機,通過 local0 輸出
    maxconn 4096   # 最大連接數
    chroot /usr/local/sbin # 改變當前工作目錄
    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 } 設定啟動實例的協議類型
    option   dontlognull    # 保證 HAProxy 不記錄上級負載均衡發送過來的用于檢測狀態沒有數據的心跳包
    option   redispatch     # 當 serverId 對應的服務器掛掉后,強制定向到其他健康>的服務器
    retries  2              # 重試 2 次連接失敗就認為服務器不可用,主要通過后面的 check 檢查
    maxconn  2000           # 最大連接數
    balance roundrobin      # balance 有兩個可用選項:roundrobin 和 source,其中,roundrobin 表示
                            # 輪詢,而 source 表示 HAProxy 不采用輪詢的策略,而是把來自某個 IP 的請求轉發給一個固定 IP 的后端
    timeout connect 5000ms  # 連接超時時間
    timeout client 50000ms  # 客戶端連接超時時間
    timeout server 50000ms  # 服務器端連接超時時間

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  # 你的均衡節點
        server APP2 aPP2:8002 check inter 2000 rise 2 fall 5

隨后,進入到容器的 volume 目錄/tmp下,將 HAProxy啟動配置文件復制到 HAProxy工作目錄中:

# 在容器中
> cd /tmp
> cp haproxy.cfg /usr/local/sbin/
> cd /usr/local/sbin/
> ls
haproxy  haproxy.cfg

接下來利用該配置文件來啟動 HAProxy 代理

> haproxy -f haproxy.cfg

另外,如果修改了配置文件的內容,需要先結束所有的 HAProxy 進程,并重新啟動代理。Docker 鏡像為了精簡體積,本身并沒有安裝pskillall進程管理命令,需要手動在容器中安裝

> apt-get update
> apt-get install procps # ps、pkill
> apt-get install psmisc # killall
> killall haproxy

至此,完成了 HAProxy 容器節點的全部部署,同時也完成了整個 Docker 應用棧的部署

應用棧訪問測試

參考結構圖可知,整個應用棧群的訪問是通過 HAProxy 代理節點來進行的。HAProxy 在啟動時通過-p 6301:6301參數,映射了容器訪問的端口到主機上,因此可在其他主機上通過本地主機的 IP 地址和端口來訪問搭建好的應用棧。

首先在本地主機上進行測試。在瀏覽器中訪問http://172.17.0.7:6301/helloworld,可以查看來自 APP1 或 APP2 的頁面內容,具體訪問到的 APP 容器節點會由 HAProxy 代理進行均衡分配。其中,172.17.0.7HAProxy 容器的 IP 地址

訪問 APP1 容器節點
訪問 APP2 容器節點

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

其他主機訪問本地主機
HAProxy 后臺管理頁面
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,646評論 6 533
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,595評論 3 418
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,560評論 0 376
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,035評論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,814評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,224評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,301評論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,444評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,988評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,804評論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,998評論 1 370
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,544評論 5 360
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,237評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,665評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,927評論 1 287
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,706評論 3 393
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,993評論 2 374

推薦閱讀更多精彩內容