Docker容器詳解
Docker是基于Go語言開發的開源應用容器引擎,遵從Apache Licence 2.0協議,可以讓開發者打包應用以及應用的依賴包到一個可移植的容器中,然后發布到各種發行版本的Linux系統上。
Docker簡介
軟件開發中最為麻煩的事情可能就是配置環境了。由于用戶使用的操作系統具有多樣性,即便使用跨平臺的開發語言(如Java和Python)都不能保證代碼能夠在各種平臺下都可以正常的運轉,而且在不同的環境下我們安裝的軟件需要依賴的軟件包也是不一樣的。
那么問題來了,我們安裝軟件的時候可不可以把軟件運行的環境一并安裝?我們是不是可以把原始環境一模一樣地復制過來呢?
虛擬機(virtual machine)就是帶環境安裝的一種解決方案,它可以在一種操作系統里面運行另一種操作系統,比如在Windows系統里面運行Linux系統,在macOS上運行Windows,而應用程序對此毫無感知。使用過虛擬機的人都知道,虛擬機用起來跟真實系統一模一樣,而對于虛擬機的宿主系統來說,虛擬機就是一個普通文件,不需要了就刪掉,對宿主系統或者其他的程序并沒有影響。但是虛擬機通常會占用較多的系統資源,啟動和關閉也非常的緩慢,總之用戶體驗并沒有想象中的那么好。
Docker屬于對Linux容器技術(LXC)的一種封裝(利用了Linux的namespace和cgroup技術),它提供了簡單易用的容器使用接口,是目前最流行的 Linux 容器解決方案。Docker將應用程序與該程序的依賴打包在一個文件里面,運行這個文件,就會生成一個虛擬容器。程序在這個虛擬容器里運行,就好像在真實的物理機上運行一樣。下圖是虛擬機和容器的對比,左邊是傳統的虛擬機,右邊是Docker。
?
目前,Docker主要用于幾下幾個方面:
- 提供一次性的環境。
- 提供彈性的云服務(利用Docker很容易實現擴容和收縮)。
- 實踐微服務架構(隔離真實環境在容器中運行多個服務)。
安裝Docker
下面以CentOS為例講解如何安裝Docker,使用Ubuntu、macOS或Windows的用戶可以通過點擊對應的鏈接了解這些平臺下如何進行安裝。
- 確定操作系統內核版本(CentOS 7要求64位,內核版本3.10+;CentOS 6要求64位,內核版本2.6+),可以通過下面的命令確定Linux系統內核版本并更新底層庫文件。
uname -r yum update
- 在CentOS下使用yum安裝Docker并啟動。
yum -y install docker systemctl start docker
- 查看Docker的信息和版本。
docker version docker info
接下來可以通過下載鏡像和創建容器來看看Docker是否可以運轉起來。可以使用下面的命令從Docker的鏡像倉庫下載名為hello-world的鏡像文件。
docker pull hello-world
查看所有鏡像文件。
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/hello-world latest fce289e99eb9 7 months ago 1.84 kB
通過鏡像文件創建并運行容器。
docker container run --name mycontainer hello-world
說明:其中
mycontainer
是我們給容器起的名字,跟在--name
參數之后;hello-world
就是我們剛才下載的鏡像文件。
Hello from Docker!
This message shows that your installation appears to be working correctly.To generate this message, Docker took the following steps:1. The Docker client contacted the Docker daemon.2. The Docker daemon pulled the "hello-world" image from the Docker Hub.(amd64)3. The Docker daemon created a new container from that image which runs theexecutable that produces the output you are currently reading.4. The Docker daemon streamed that output to the Docker client, which sent itto your terminal.To try something more ambitious, you can run an Ubuntu container with:$ docker run -it ubuntu bashShare images, automate workflows, and more with a free Docker ID:https://hub.docker.com/For more examples and ideas, visit:https://docs.docker.com/get-started/
如果要刪除這個容器,可以使用下面的命令。
docker container rm mycontainer
在刪除容器之后,我們還可以刪除剛才下載的鏡像文件。
docker rmi hello-world
說明:如果要在Ubuntu(內核版本3.10+)下面安裝和啟動Docker,可以按照如下的步驟進行。
apt update apt install docker-ce service docker start國內用戶可以通過更換Ubuntu軟件下載源來提升下載速度,具體請參照清華大學開源軟件鏡像站上的《Ubuntu鏡像使用幫助》。
安裝Docker后,由于直接訪問dockerhub下載鏡像會非常緩慢,建議將服務器更換為國內鏡像,可以通過修改?/etc/docker/daemon.json
?文件來做到。一般的云服務器會有自己專屬的鏡像,就不需要手動修改了。
{"registry-mirrors": ["http://hub-mirror.c.163.com","https://registry.docker-cn.com"] }
使用Docker
想要玩轉Docker,最簡單的辦法就是馬上用Docker創建一些自己學習和工作中需要用到的容器,下面我們帶著大家一起來創建這些容器。
運行Nginx
Nginx是高性能的Web服務器,同時也是做反向代理服務器的上佳選擇。使用Docker可以非常簡單的創建一個運行Nginx的容器,命令如下所示。
docker container run -d -p 80:80 --rm --name mynginx nginx
說明:上面的參數
-d
表示容器在后臺運行(不產生輸出到Shell)并顯示容器的ID;-p
是用來映射容器的端口到宿主機的端口,冒號前面是宿主機的端口,冒號后面是容器內部使用的端口;--rm
表示容器停止后自動刪除容器,例如執行命令docker container stop mynginx
后,容器就不復存在了;--name
后面的mynginx是自定義的容器名字;在創建容器的過程中,需要用到nginx的鏡像文件,鏡像文件的下載是自動完成的,如果沒有指定版本號,默認是最新版本(latest)。
如果需要將自己的Web項目(頁面)部署到Nginx上,可以使用容器拷貝命令將指定路徑下所有的文件和文件夾拷貝到容器的指定目錄中。
docker container cp /root/web/index.html mynginx:/usr/share/nginx/html
如果不愿意拷貝文件也可以在創建容器時通過數據卷操作--volume
將指定的文件夾映射到容器的某個目錄中,例如將Web項目的文件夾直接映射到/usr/share/nginx/html
目錄。我們先通過下面的命令讓剛才創建的容器停止運行。
docker container stop mynginx
然后用下面的命令重新創建容器。
docker container run -d -p 80:80 --rm --name mynginx --volume /root/docker/nginx/html:/usr/share/nginx/html nginx
說明:上面創建容器和拷貝文件的命令中,
container
是可以省略的,也就是說docker container run
和docker run
是一樣的,而docker container cp
和docker cp
是一樣的。此外,命令中的--volume
也可以縮寫為-v
,就如同-d
是--detach
的縮寫,-p
是--publish
的縮寫。$PWD
代表宿主系統當前文件夾,這些對于使用過Unix或者Linux系統的人來說,應該是很容易理解的。
要查看運行中的容器,可以使用下面的命令。
docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
3c38d2476384 nginx "nginx -g 'daemon ..." 4 seconds ago Up 4 seconds 0.0.0.0:80->80/tcp mynginx
要啟動和停止容器,可以使用下面的命令。
docker start mynginx docker stop mynginx
由于在創建容器時使用了--rm
選項,容器在停止時會被移除,當我們使用下面的命令查看所有容器時,應該已經看不到剛才的mynginx
容器了。
docker container ls -a
如果在創建容器時沒有指定--rm
選項,那么也可以使用下面的命令來刪除容器。
docker rm mynginx
要刪除正在運行中的容器,需要使用-f
選項。
docker rm -f mynginx
運行MySQL
我們再來嘗試用Docker安裝一臺MySQL服務器,首先可以先檢查一下有沒有MySQL的鏡像文件。
docker search mysql
INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
docker.io docker.io/mysql MySQL is a ... 8486 [OK]
...
說明:上面查詢結果的列依次代表索引、鏡像名、鏡像描述、用戶評價、是否官方鏡像、自動構建。
下載MySQL鏡像并指定鏡像的版本號。
docker pull mysql:5.7
如果需要查看已經下載的鏡像文件,可以使用下面的命令。
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/nginx latest e445ab08b2be 2 weeks ago 126 MB
docker.io/mysql 5.7 f6509bac4980 3 weeks ago 373 MB
創建并運行MySQL容器。
docker run -d -p 3306:3306 --name mysql57 -v /root/docker/mysql/conf:/etc/mysql/mysql.conf.d -v /root/docker/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
注意:上面創建容器時我們又一次使用了數據卷操作,那是因為通常容器是隨時創建隨時刪除的,而數據庫中的數據卻是需要保留下來的。
上面的兩個數據卷操作一個是映射了MySQL配置文件所在的文件夾,一個是映射了MySQL數據所在的文件夾,這兩個數據卷操作非常重要。我們可以將MySQL的配置文件放在$PWD/mysql/conf
目錄下,配置文件的具體內容如下所示:
[mysqld] pid-file=/var/run/mysqld/mysqld.pid socket=/var/run/mysqld/mysqld.sock datadir=/var/lib/mysql log-error=/var/log/mysql/error.log server-id=1 log-bin=/var/log/mysql/mysql-bin.log expire_logs_days=30 max_binlog_size=256M symbolic-links=0
如果安裝了MySQL 8.x版本(目前的最新版本),在使用客戶端工具連接服務器時可能會遇到error 2059: Authentication plugin 'caching_sha2_password' cannot be loaded
的問題,這是因為MySQL 8.x默認使用了名為“caching_sha2_password”的機制對用戶口令進行了更好的保護,但是如果客戶端工具不支持新的認證方式,連接就會失敗。解決這個問題有兩種方式:一是升級客戶端工具來支持MySQL 8.x的認證方式;二是進入容器,修改MySQL的用戶口令認證方式。下面是具體的步驟,我們先用docker exec
命令進入容器的交互式環境,假設運行MySQL 8.x的容器名字叫mysql8x
。
docker exec -it mysql8x /bin/bash
進入容器的交互式Shell之后,可以首先利用MySQL的客戶端工具連接MySQL服務器。
mysql -u root -p Enter password: Your MySQL connection id is 16 Server version: 8.0.12 MySQL Community Server - GPL Copyright (c) 2000, 2018, Oracle and/or its affiliates. All rights reserved. Oracle is a registered trademark of Oracle Corporation and/or its affiliates. Other names may be trademarks of their respective owners. Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. mysql>
接下來通過SQL來修改用戶口令就可以了。
alter user 'root'@'%' identified with mysql_native_password by '123456' password expire never;
當然,如果愿意你也可以查看一下用戶表檢查是否修改成功。
use mysql; select user, host, plugin, authentication_string from user where user='root'; +------+-----------+-----------------------+-------------------------------------------+ | user | host | plugin | authentication_string | +------+-----------+-----------------------+-------------------------------------------+ | root | % | mysql_native_password | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 | | root | localhost | mysql_native_password | *6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9 | +------+-----------+-----------------------+-------------------------------------------+ 2 rows in set (0.00 sec)
在完成上面的步驟后,現在即便不更新客戶端工具也可以連接MySQL 8.x了。
運行Redis
接下來我們試一試運行多個容器并讓多個容器之間通過網絡通信。我們創建4個Redis容器來實現一主三從的主從復制結構。
docker run -d -p 6379:6379 --name redis-master redis docker run -d -p 6380:6379 --name redis-slave-1 --link redis-master:redis-master redis redis-server --replicaof redis-master 6379 docker run -d -p 6381:6379 --name redis-slave-2 --link redis-master:redis-master redis redis-server --replicaof redis-master 6379 docker run -d -p 6382:6379 --name redis-slave-3 --link redis-master:redis-master redis redis-server --replicaof redis-master 6379
上面的命令中,--link
參數用于給容器創建網絡別名,因為三臺從機(slave)需要通過網絡連接自己的主機(master)。雖然,我們可以通過docker inspect --format '{{ .NetworkSettings.IPAddress }}' <container-ID>
命令來查看到容器的IP地址,但是由于容器的即裝即用性,容器的IP地址有可能會發生變化,如果直接使用IP地址,在容器重啟后就可能會因為IP地址的變化導致從機無法連接到主機。使用--link
參數創建網絡別名就是為了在啟動Redis服務器時在redis-server
后面的--replicaof
參數后使用這個別名而不是IP地址。
接下來我們進入名為redis-master
的容器,看看主從復制的配置是否成功。
docker exec -it redis-master /bin/bash
通過redis-cli
啟動命令行工具。
redis-cli 127.0.0.1:6379> info replication # Replication role:master connected_slaves:3 slave0:ip=172.17.0.4,port=6379,state=online,offset=1988,lag=0 slave1:ip=172.17.0.5,port=6379,state=online,offset=1988,lag=1 slave2:ip=172.17.0.6,port=6379,state=online,offset=1988,lag=1 master_replid:94703cfa03c3ddc7decc74ca5b8dd13cb8b113ea master_replid2:0000000000000000000000000000000000000000 master_repl_offset:1988 second_repl_offset:-1 repl_backlog_active:1 repl_backlog_size:1048576 repl_backlog_first_byte_offset:1 repl_backlog_histlen:1988
運行GitLab
GitLab是由GitLab Inc.開發的Git倉庫管理工具,具有wiki、問題跟蹤、持續集成等一系列的功能,分為社區版和企業版。通過Docker提供的虛擬化容器,我們可以安裝社區版的Docker。因為GitLab需要使用SSH協議進行安全連接,我們要暴露容器的22端口,所以可以先將宿主機SSH連接的22端口修改為其他端口(如:12345),然后再進行后續的操作。
vim /etc/ssh/sshd_config
將其中定義端口的那行代碼去掉注釋并將端口修改為12345。
Port 12345
重新啟動sshd
服務。
systemctl restart sshd
提示:修改端口后應該確保防火墻上也開啟對應的端口,否則無法使用SSH連接到Linux服務器。
創建需要用于數據卷映射操作的文件夾。
mkdir -p /root/gitlab/config mkdir -p /root/gitlab/logs mkdir -p /root/gitlab/data
基于gitlab/gitlab-ce
鏡像創建容器,并暴露80端口(HTTP連接)和22端口(SSH連接)。
docker run -d -p 80:80 -p 22:22 --name gitlab -v /root/gitlab/config:/etc/gitlab -v /root/gitlab/logs:/var/log/gitlab -v /root/gitlab/data:/var/opt/gitlab gitlab/gitlab-ce
說明:GitLab的啟動比較緩慢,創建好容器后可能需要等待一段時間才能通過瀏覽器來進行訪問。
首次進入GitLab訪問界面會提示我們修改管理員密碼,設置好管理員密碼后就可以在登錄界面輸入用戶名root
和剛才設置的密碼登錄到管理員控制臺,在使用上還是非常簡單和人性化的。
構建鏡像
通過上面的講解,我們已經掌握了如何通過官方提供的鏡像來創建容器。當然如果愿意,我們也可以用配置好的容器來生成鏡像。簡而言之,Docker鏡像是由文件系統疊加而成的,系統的最底層是bootfs,相當于就是Linux內核的引導文件系統;接下來第二層是rootfs,這一層可以是一種或多種操作系統(如Debian或Ubuntu文件系統),Docker中的rootfs是只讀狀態的;Docker利用聯合掛載技術將各層文件系統疊加到一起,最終的文件系統會包含有底層的文件和目錄,這樣的文件系統就是一個鏡像。
之前我們講過了如何查找、列出鏡像和拉取(下載)鏡像,接下來看看構建鏡像的兩種方式:
- 使用
docker commit
命令。(不推薦) - 使用
docker build
命令和Dockerfile文件。
使用commit命令構建鏡像
為了演示如何構建鏡像,我們先使用Ubuntu鏡像來定制一個容器,命令如下所示。
docker run --name myubuntu -it ubuntu /bin/bash
在容器中執行下面的命令來安裝Apache服務器并退出容器。
apt -y upgrade apt -y install apache2 exit
我們將這個容器作為一個定制的Web服務器保存起來,當需要這樣一臺Web服務器的時候,就沒有必要重新創建容器并安裝Apache了。
首先我們通過下面的命令查看容器的ID。
docker container ls -a
docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
014bdb321612 ubuntu "/bin/bash" 5 minutes ago Exited (0) myubuntu
提交定制的容器。
docker commit 014bdb321612 jackfrued/mywebserver
查看鏡像文件。
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
jackfrued/mywebserver latest 795b294d265a 14 seconds ago 189 MB
生成鏡像文件以后,后面就可以利用剛才創建的鏡像文件來創建新的容器。
使用Dockerfile構建鏡像
Dockerfile使用DSL(Domain Specific Language)來構建一個Docker鏡像,只要編輯好了Dockerfile文件,就可以使用docker build
命令來構建一個新的鏡像。
我們先創建一個名為myapp的文件夾來保存項目代碼和Dockerfile的文件,如下所示:
[ECS-root temp]# tree myapp myapp ├── api │ ├── app.py │ ├── requirements.txt │ └── start.sh └── Dockerfile
其中api是Flask項目的文件夾,其中包括了項目代碼、依賴項以及啟動腳本等文件,具體內容如下所示:
app.py文件:
from flask import Flask from flask_restful import Resource, Api from flask_cors import CORSapp = Flask(__name__) CORS(app, resources={r'/api/*': {'origins': '*'}}) api = Api(app)class Product(Resource):def get(self):products = ['Ice Cream', 'Chocolate', 'Coca Cola', 'Hamburger']return {'products': products}api.add_resource(Product, '/api/products')
requirements.txt文件:
flask flask-restful flask-cors redis gunicorn
start.sh文件:
#!/bin/bash exec gunicorn -w 4 -b 0.0.0.0:8000 app:app
提示:需要給start.sh文件以執行權限,可以使用
chmod 755 start.sh
命令來做到。
Dockerfile文件:
# 指定基礎鏡像 FROM python:3.7 # 指定鏡像的維護者 MAINTAINER jackfrued "jackfrued@126.com" # 將指定文件添加到容器中指定的位置 ADD api/* /root/api/ # 設置工作目錄 WORKDIR /root/api # 執行命令(安裝Flask項目的依賴項) RUN pip install -r requirements.txt -i https://pypi.doubanio.com/simple/ # 容器啟動時要執行的命令 ENTRYPOINT ["./start.sh"] # 暴露端口 EXPOSE 8000
我們來解釋一下上面的Dockerfile文件。Dockerfile文件通過特殊的指令來指定基礎鏡像(FROM指令)、創建容器后需要指定的命令(RUN指令)以及需要暴露的端口(EXPOSE)等信息。我們稍后會專門為大家介紹這些Dockfile中的指令。
接下來我們可以使用docker build
命令來創建鏡像,如下所示。
docker build -t "jackfrued/myapp" .
提示:上面的命令最后面的
.
?千萬不要漏掉了哦,它表示從當前路徑下尋找Dockerfile。
通過下面的命令可以查看創建好的鏡像。
docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
jackfrued/myapp latest 6d6f026a7896 5 seconds ago 930 MB
如果想知道鏡像文件是如何創建出來的,可以使用下面的命令。
docker history jackfrued/myapp
IMAGE CREATED CREATED BY SIZE COMMENT
6d6f026a7896 31 seconds ago /bin/sh -c #(nop) EXPOSE 8000/tcp 0 B
3f7739173a79 31 seconds ago /bin/sh -c #(nop) ENTRYPOINT ["./start.sh"] 0 B
321e6bf09bf1 32 seconds ago /bin/sh -c pip install -r requirements.txt... 13 MB
2f9bf2c89ac7 37 seconds ago /bin/sh -c #(nop) WORKDIR /root/api 0 B
86119afbe1f8 37 seconds ago /bin/sh -c #(nop) ADD multi:4b76f9c9dfaee8... 870 B
08d465e90d4d 3 hours ago /bin/sh -c #(nop) MAINTAINER jackfrued "j... 0 B
fbf9f709ca9f 12 days ago /bin/sh -c #(nop) CMD ["python3"] 0 B
使用該鏡像來創建容器運行Web服務器。
docker run -d -p 8000:8000 --name myapp jackfrued/myapp
如果希望將上面創建的鏡像文件放到dockerhub倉庫中,可以按照如下所示的步驟進行操作。
通過下面的命令登錄到dockerhub。
docker login
輸入用戶名和口令進行登錄。
Login with your Docker ID to push and pull images from Docker Hub. If you don't have a Docker ID, head over to https://hub.docker.com to create one.
Username: jackfrued
Password:
Login Succeeded
通過下面的命令將鏡像推到倉庫中。
docker push jackfrued/webserver
轉存失敗重新上傳取消
Dockerfile指令
想了解Dockerfile的指令可以查看官方提供的參考手冊,下面我們為大家介紹一些常用的指令。
-
FROM:設置基礎鏡像,必須是Dockerfile中的第一條指令。
FROM <鏡像名> [AS <別名>]
或
FROM <鏡像名>[:<標簽>] [AS <別名>]
-
RUN:指定構建鏡像時要執行的命令。
RUN <命令> [參數1], [參數2], ...
或
RUN ["可執行文件", "參數1", "參數2", ...]
-
CMD:指定構建鏡像后要執行的命令。
CMD <命令> [參數1], [參數2], ...
或
CMD ["可執行文件", "參數1", "參數2", ...]
說明:Docker不同于虛擬機,容器本身就是一個進程,容器中的應用應該位于前臺運行。CMD命令相當于就是用來指定容器主進程(創建容器后要在前臺執行的程序)的,如果主進程結束了,容器也就停止運行了。所以在容器中啟動Nginx不能使用
service nginx start
或是systemctl start nginx
而是要通過CMD ["nginx", "-g", "daemon off;"]
讓它在前臺運行。 -
ENTRYPOINT:和CMD類似,也可以執行命令,但
docker run
命令行中指定的任何參數都會被當做參數再次傳給ENTRYPOINT指令中的命令,這就使得我們可以構建一個鏡像,它既可以運行一個默認的命令,也支持通過docker run
命令行為該命令指定可覆蓋的參數選項。ENTRYPOINT <命令> [參數1], [參數2], ...
或
ENTRYPOINT ["可執行文件", "參數1", "參數2", ...]
-
WORKDIR:在通過鏡像創建新容器時,在容器內部創建一個工作目錄,ENTRYPOINT和CMD指定的程序會在這個目錄下執行。在使用
docker run
命令時可以通過-w
參數來覆蓋由WORKDIR指定的工作目錄。例如:WORKDIR /opt/webapp
docker run -w /usr/share/webapp ...
-
ENV:在創建鏡像時設置環境變量。在使用
docker run
命令時,可以通過-e
參數來修改環境變量的設置。例如:ENV DEFAULT_PORT=8080
docker run -e "DEFAULT_PORT=8000" ...
-
USER:指定鏡像會以什么用戶身份去運行。例如:
USER nginx
-
VOLUME:在創建容器時添加一個數據卷的掛載點。通過數據卷操作可以實現容器間數據的共享和重用,對卷所作的修改可以馬上生效而不需要重新啟動容器,我們之前創建容器時使用
--volume
參數就是為了實現數據卷的映射操作。VOLUME ["/路徑1", "/路徑2/子路徑2.1/", ...]
-
ADD:將構建目錄下的文件和文件夾復制到鏡像中,如果是壓縮文件和歸檔文件,ADD命令會對這些文件進行解壓縮解歸檔的操作。
ADD [--chown=<用戶>:<用戶組>] <源文件> <目標文件>
-
COPY:非常類似于ADD,但不會主動對文件進行提取操作。
-
LABEL:為Docker鏡像添加一些元數據,在使用
docker inspect
命令時會看到這些元數據。LABEL version="1.0.0" location="Chengdu"
-
ONBUILD:為鏡像添加觸發器,當一個鏡像被用作其他鏡像的基礎鏡像,觸發器將會被執行。例如:
ONBUILD ADD . /app/src ONBUILD RUN cd /app/src && make
多容器管理
我們的項目可能會使用了多個容器,容器多了之后管理容器的工作就會變得麻煩。如果要對多個容器進行自動配置使得容器可以相互協作甚至實現復雜的調度,這就需要進行容器編排。Docker原生對容器編排的支持非常弱,但是可以通過社區提供的工具來實現容器編排。
Docker Compose
可以通過安裝Docker Compose工具來實現基于YAML文件的容器編排,YAML文件會定義一系列的容器以及容器運行時的屬性,Docker Compose會根據這些配置來管理容器。
-
安裝Docker Compose。
curl -L https://github.com/docker/compose/releases/download/1.25.0-rc2/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose chmod +x /usr/local/bin/docker-compose
說明:如果沒有curl工具,在CentOS下可以先通過包管理工具yum安裝curl再執行上面的命令。
當然我們也可以使用Python的包管理工具pip來安裝Docker Compose,命令如下所示。
pip3 install -U docker-compose
-
使用Docker Compose。
我們在剛才的Flask項目中引入緩存,然后再利用Flask提供的數據接口為前端頁面提供數據,使用Vue.js進行頁面渲染并將靜態頁面部署在Nginx服務器上。項目文件夾結構如下所示:
[ECS-root ~]# tree temp temp ├── docker-compose.yml ├── html │ └── index.html └── myapp├── api│ ├── app.py│ ├── requirements.txt│ └── start.sh└── Dockerfile
修改后的app.py文件代碼如下所示:
from pickle import dumps, loadsfrom flask import Flask from flask_restful import Resource, Api from flask_cors import CORS from redis import Redisapp = Flask(__name__) CORS(app, resources={r'/api/*': {'origins': '*'}}) api = Api(app) redis = Redis(host='redis-master', port=6379)class Product(Resource):def get(self):data = redis.get('products')if data:products = loads(data)else:products = ['Ice Cream', 'Chocolate', 'Coca Cola', 'Hamburger']redis.set('products', dumps(products))return {'products': products}api.add_resource(Product, '/api/products')
html文件夾用來保存靜態頁面,稍后我們會通一個運行Nginx的容器來向瀏覽器提供靜態頁面。index.html文件的內容如下所示:
<!DOCTYPE html> <html lang="en"> <head><meta charset="utf-8"><title>首頁</title> </head> <body><div id="app"><h2>產品列表</h2><ul><li v-for="product in products">{{ product }}</li></ul></div><script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script><script>new Vue({el: '#app', data: {products: []},created() {fetch('http://1.2.3.4:8000/api/products').then(resp => resp.json()).then(json => {this.products = json.products})}})</script> </body> </html>
接下來,我們要通過docker-compose.yml文件來創建三個容器并指明容器之間的依賴關系。
version: '3' services:api-server:build: ./myappports:- '8000:8000'links:- redis-masterweb-server:image: nginxports:- '80:80'volumes:- ./html:/usr/share/nginx/htmlredis-master:image: redisexpose:- '6379'
有了這個YAML文件,我們就可以使用
docker-compose
命令來創建容器運行項目,其命令如下所示:[ECS-root temp]# docker-compose up Creating network "temp_default" with the default driver Creating temp_web-server_1 ... done Creating temp_redis-master_1 ... done Creating temp_api-server_1 ... done Attaching to temp_redis-master_1, temp_web-server_1, temp_api-server_1 redis-master_1 | 1:C 05 Dec 2019 11:57:26.828 # oO0OoO0OoO0Oo Redis is starting oO0OoO0OoO0Oo redis-master_1 | 1:C 05 Dec 2019 11:57:26.828 # Redis version=5.0.6, bits=64, commit=00000000, modified=0, pid=1, just started redis-master_1 | 1:C 05 Dec 2019 11:57:26.828 # Warning: no config file specified, using the default config. In order to specify a config file use redis-server /path/to/redis.conf redis-master_1 | 1:M 05 Dec 2019 11:57:26.830 * Running mode=standalone, port=6379. redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 # WARNING: The TCP backlog setting of 511 cannot be enforced because /proc/sys/net/core/somaxconn is set to the lower value of 128. redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 # Server initialized redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 # WARNING overcommit_memory is set to 0! Background save may fail under low memory condition. To fix this issue add 'vm.overcommit_memory = 1' to /etc/sysctl.conf and then reboot or run the command 'sysctl vm.overcommit_memory=1' for this to take effect. redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 # WARNING you have Transparent Huge Pages (THP) support enabled in your kernel. This will create latency and memory usage issues with Redis. To fix this issue run the command 'echo never > /sys/kernel/mm/transparent_hugepage/enabled' as root, and add it to your /etc/rc.local in order to retain the setting after a reboot. Redis must be restarted after THP is disabled. redis-master_1 | 1:M 05 Dec 2019 11:57:26.831 * Ready to accept connections api-server_1 | [2019-12-05 11:57:27 +0000] [1] [INFO] Starting gunicorn 20.0.4 api-server_1 | [2019-12-05 11:57:27 +0000] [1] [INFO] Listening at: http://0.0.0.0:8000 (1) api-server_1 | [2019-12-05 11:57:27 +0000] [1] [INFO] Using worker: sync api-server_1 | [2019-12-05 11:57:27 +0000] [8] [INFO] Booting worker with pid: 8 api-server_1 | [2019-12-05 11:57:27 +0000] [9] [INFO] Booting worker with pid: 9 api-server_1 | [2019-12-05 11:57:27 +0000] [10] [INFO] Booting worker with pid: 10 api-server_1 | [2019-12-05 11:57:27 +0000] [11] [INFO] Booting worker with pid: 11
要停止容器的運行,可以使用下面的命令。
docker-compose down
Kubernetes
實際的生產環境中常常需要部署和管理多個協同工作的容器,docker compose解決了多容器創建和管理的問題,但是實際項目中,我們還需要Kubernetes(以下都簡稱為K8S)來提供一個跨主機集群的容器調度平臺。K8S可以進行自動化容器的部署、擴展和操作,從而提供以容器為中心的基礎架構。該項目是谷歌在2014年啟動的項目,建立在谷歌公司十余年運維經驗的基礎之上,而且谷歌自己的應用也是運行在容器上的。