# 多容器管ç†å®žè·µ(å¯é€‰) 在上一节ä¸ï¼Œæˆ‘们从一个简å•çš„é™æ€ç½‘站开始,然åŽå°è¯•äº†Flask应用。åªéœ€å°‘é‡å‘½ä»¤ï¼Œæˆ‘们都å¯ä»¥åœ¨æœ¬åœ°å’Œäº‘ä¸è¿è¡Œè¿™ä¸¤ç§æ–¹æ³•ã€‚这两个应用程åºçš„å…±åŒç‚¹æ˜¯å®ƒä»¬åœ¨å•ä¸ªå®¹å™¨ä¸è¿è¡Œã€‚ 那些具有在生产环境ä¸è¿è¡ŒæœåŠ¡çš„ç»éªŒçš„人知é“,如今通常应用程åºå¹¶ä¸é‚£ä¹ˆç®€å•ã€‚å‡ ä¹Žæ€»æ˜¯æ¶‰åŠä¸€ä¸ªæ•°æ®åº“(或任何其他类型的æŒä¹…性å˜å‚¨ï¼‰ã€‚ Rediså’ŒMemcached之类的系统已æˆä¸ºå¤§å¤šæ•°Web应用程åºä½“ç³»ç»“æž„çš„åŸºç¡€ã€‚å› æ¤ï¼Œåœ¨æœ¬èŠ‚ä¸ï¼Œæˆ‘们将花费一些时间æ¥å¦ä¹ 如何对ä¾èµ–于ä¸åŒæœåŠ¡çš„应用程åºè¿›è¡ŒDockerize。 我们将è¦ç”¨äºŽDockerize的应用程åºç§°ä¸ºFood Trucks。我构建这个应用程åºçš„ç›®æ ‡æ˜¯è¦æœ‰ä¸€ä¸ªæœ‰ç”¨çš„ä¸œè¥¿ï¼ˆå› ä¸ºå®ƒç±»ä¼¼äºŽçœŸå®žä¸–ç•Œçš„åº”ç”¨ç¨‹åºï¼‰ï¼Œè‡³å°‘ä¾èµ–一项æœåŠ¡ï¼Œä½†å¯¹äºŽæœ¬æ•™ç¨‹è€Œè¨€å¹¶ä¸å¤ªå¤æ‚。 首先,让我们进入我们的程åºæ–‡ä»¶ç›®å½• ``` cd docker-workshop/2-advanced/ ``` å…¶ä¸ï¼Œflask-app文件夹包å«Python应用程åºï¼Œè€Œutils文件夹具有一些实用程åºï¼Œå¯å°†æ•°æ®åŠ 载到Elasticsearchä¸ã€‚该目录还包å«ä¸€äº›YAML文件和一个Dockerfile,我们将在本教程ä¸é€æ¥è¯¦ç»†ä»‹ç»æ‰€æœ‰è¿™äº›æ–‡ä»¶ã€‚ 让我们考虑如何对应用程åºè¿›è¡ŒDocker化。我们å¯ä»¥çœ‹åˆ°è¯¥åº”用程åºç”±FlaskåŽç«¯æœåŠ¡å™¨å’ŒElasticsearchæœåŠ¡ç»„æˆã€‚拆分æ¤åº”用的自然方法是拥有两个容器-一个è¿è¡ŒFlask进程,å¦ä¸€ä¸ªè¿è¡ŒElasticsearch(ESï¼‰è¿›ç¨‹ã€‚è¿™æ ·ï¼Œå¦‚æžœæˆ‘ä»¬çš„åº”ç”¨ç¨‹åºå˜å¾—越æ¥è¶Šå¤šäººç”¨ï¼Œæˆ‘们å¯ä»¥æ ¹æ®ç“¶é¢ˆæ‰€åœ¨çš„ä½ç½®æ·»åŠ 更多容器æ¥æ‰©å±•å®ƒã€‚å› æ¤æˆ‘们需è¦ä¸¤ä¸ªå®¹å™¨ã€‚在上一节ä¸ï¼Œæˆ‘们已ç»æž„建了自己的Flask容器。对于Elasticsearch,让我们看看是å¦å¯ä»¥åœ¨Docker Hub上找到åˆé€‚çš„é•œåƒã€‚ ``` docker search elasticsearch ``` Elasticsearchå˜åœ¨å®˜æ–¹æ”¯æŒçš„é•œåƒã€‚为了使ESè¿è¡Œï¼Œæˆ‘们å¯ä»¥ç®€å•åœ°ä½¿ç”¨docker run并让一个å•èŠ‚点ES容器立å³åœ¨æœ¬åœ°è¿è¡Œã€‚ ``` docker pull docker.elastic.co/elasticsearch/elasticsearch:6.3.2 ``` 然åŽé€šè¿‡æŒ‡å®šç«¯å£å¹¶è®¾ç½®ä¸€ä¸ªçŽ¯å¢ƒå˜é‡å°†å…¶è¿è¡Œåœ¨å¼€å‘模å¼ä¸‹ï¼Œè¯¥çŽ¯å¢ƒå˜é‡å°†Elasticsearch集群é…置为作为å•èŠ‚点è¿è¡Œã€‚ ``` docker run -d --rm --name es -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2 ``` 如上所示,我们使用--name es为我们的容器命å,这使得在åŽç»å‘½ä»¤ä¸æ˜“于使用。å¯åŠ¨å®¹å™¨åŽï¼Œæˆ‘们å¯ä»¥é€šè¿‡è¿è¡Œå¸¦æœ‰å®¹å™¨å称(或ID)的`docker logs -f <é•œåƒid>`æ¥æŸ¥çœ‹æ—¥å¿—,以查看日志。如果ElasticsearchæˆåŠŸå¯åŠ¨ï¼Œæ‚¨åº”该看到类似于以下的日志。 ``` docker logs -f <é•œåƒid> ``` 现在,让我们å°è¯•çœ‹çœ‹æ˜¯å¦å¯ä»¥å‘Elasticsearch容器å‘é€è¯·æ±‚。我们使用9200端å£å°†`cURL`请求å‘é€åˆ°å®¹å™¨ã€‚ ``` $ curl 0.0.0.0:9200 { "name" : "kg_udER", "cluster_name" : "docker-cluster", "cluster_uuid" : "QTuIzSsKTM2dzdf1zojwKQ", "version" : { "number" : "6.3.2", "build_flavor" : "default", "build_type" : "tar", "build_hash" : "053779d", "build_date" : "2018-07-20T05:20:23.451332Z", "build_snapshot" : false, "lucene_version" : "7.3.1", "minimum_wire_compatibility_version" : "5.6.0", "minimum_index_compatibility_version" : "5.0.0" }, "tagline" : "You Know, for Search" } ``` 看起æ¥ä¸é”™ï¼åœ¨æ¤è¿‡ç¨‹ä¸ï¼Œè®©æˆ‘们也è¿è¡ŒFlask容器。但在æ¤ä¹‹å‰ï¼Œæˆ‘们需è¦ä¸€ä¸ªDockerfile。在上一节ä¸ï¼Œæˆ‘们使用python:3图åƒä½œä¸ºåŸºæœ¬é•œåƒã€‚但是,这次,除了通过pip安装Pythonä¾èµ–项之外,我们还希望我们的应用程åºè¿˜ç”Ÿæˆç”¨äºŽç”Ÿäº§çš„å°åž‹Javascript文件。为æ¤ï¼Œæˆ‘们将需è¦Nodejs。由于我们需è¦è‡ªå®šä¹‰æž„建æ¥éª¤ï¼Œå› æ¤æˆ‘们将从ubuntu基本镜åƒå¼€å§‹ï¼Œä»Žå¤´å¼€å§‹æž„建Dockerfile。 å¯ä»¥ç›´æŽ¥é€šè¿‡Cloud9编辑器打开`2-advanced/Dockerfile` ``` # start from base FROM ubuntu:18.04 LABEL maintainer="Prakhar Srivastav <prakhar@prakhar.me>" # install system-wide deps for python and node RUN apt-get -yqq update RUN apt-get -yqq install python3-pip python3-dev curl gnupg RUN curl -sL https://deb.nodesource.com/setup_10.x | bash RUN apt-get install -yq nodejs # copy our application code ADD flask-app /opt/flask-app WORKDIR /opt/flask-app # fetch app specific deps RUN npm install RUN npm run build RUN pip3 install -r requirements.txt # expose port EXPOSE 5000 # start app CMD [ "python3", "./app.py" ] ``` 让我们快速æµè§ˆä¸€ä¸‹æ¤æ–‡ä»¶ã€‚我们从Ubuntu LTS基本镜åƒå¼€å§‹ï¼Œç„¶åŽä½¿ç”¨è½¯ä»¶åŒ…管ç†å™¨apt-get安装ä¾èµ–项-Pythonå’ŒNode。 然åŽï¼Œæˆ‘们使用ADD命令将应用程åºå¤åˆ¶åˆ°å®¹å™¨ä¸çš„æ–°å·`/opt/flask-app`。这是我们的代ç 所在的ä½ç½®ã€‚我们还将其设置为工作目录,以便在该ä½ç½®çš„上下文ä¸è¿è¡Œä»¥ä¸‹å‘½ä»¤ã€‚现在,我们已安装了系统范围的ä¾èµ–项,接下æ¥æˆ‘们将é€æ¥å®‰è£…特定于应用程åºçš„ä¾èµ–项。首先,我们通过从npm安装软件包并è¿è¡Œ`package.json`文件ä¸å®šä¹‰çš„build命令æ¥å®‰è£…Node。我们通过安装Python软件包,暴露端å£å¹¶å®šä¹‰CMDæ¥è¿è¡Œæ–‡ä»¶ã€‚ 最åŽï¼Œæˆ‘们å¯ä»¥ç»§ç»æž„建镜åƒå¹¶è¿è¡Œå®¹å™¨ ``` docker build -t nikosheng/foodtrucks-web . ``` 构建完æˆåŽï¼Œè®©æˆ‘们å°è¯•è¿è¡Œæˆ‘们的应用程åºã€‚ ``` $ docker run -P --rm nikosheng/foodtrucks-web Unable to connect to ES. Retrying in 5 secs... Unable to connect to ES. Retrying in 5 secs... Unable to connect to ES. Retrying in 5 secs... Out of retries. Bailing out... ``` 糟糕ï¼ç”±äºŽæ— 法连接到Elasticsearch,我们的flask应用程åºæ— 法è¿è¡Œã€‚我们如何将一个容器è”通å¦ä¸€ä¸ªå®¹å™¨ï¼Œå¹¶è®©ä»–们彼æ¤äº’通? ### 如果对于Docker比较熟悉的å°ä¼™ä¼´å¯ä»¥æŒ‘战一下,å¯ä»¥æ ¹æ®é¡¹ç›®é‡Œé¢çš„代ç 去自己实现解决方法。如果第一次接触Dockerçš„å°ä¼™ä¼´ï¼Œå¯ä»¥å±•å¼€ä¸‹é¢çš„折å å—æ¥è·Ÿéšæ•™ç¨‹ä¸€æ¥ä¸€æ¥å®Œæˆå®žéªŒã€‚ <details> <summary>查看解决方案</summary> <pre> ## Docker Network 在讨论Docker用于处ç†æ¤ç±»æƒ…况的功能之å‰ï¼Œè®©æˆ‘们先看看是å¦å¯ä»¥æ‰¾åˆ°è§£å†³é—®é¢˜çš„方法。希望这会使您对我们将è¦ç ”究的能有所了解。 好的,让我们è¿è¡Œ`docker container ls`(与`docker ps`相åŒï¼‰ï¼Œçœ‹çœ‹æœ‰ä»€ä¹ˆã€‚ ``` admin:~/environment $ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b17bd4d6a319 docker.elastic.co/elasticsearch/elasticsearch:6.3.2 "/usr/local/bin/dock…" 20 minutes ago Up 20 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp es admin:~/environment $ ``` å› æ¤ï¼Œæˆ‘们有一个ES容器è¿è¡Œåœ¨0.0.0.0:9200端å£ä¸Šï¼Œæˆ‘们å¯ä»¥ç›´æŽ¥è®¿é—®è¯¥å®¹å™¨ã€‚如果我们å¯ä»¥å‘Šè¯‰Flask应用连接到该URL,则它应该å¯ä»¥è¿žæŽ¥å¹¶ä¸ŽESé€šä¿¡ã€‚è®©æˆ‘ä»¬æ·±å…¥ç ”ç©¶Python代ç ,看看如何定义连接的详细信æ¯ã€‚ 让我们打开`2-advanced/flask-app/app.py` ``` es = Elasticsearch(host='es') ``` 在代ç ä¸ï¼Œæˆ‘们需è¦å‘Šè¯‰Flask容器,ES容器æ£åœ¨0.0.0.0主机上è¿è¡Œï¼ˆé»˜è®¤ç«¯å£ä¸º9200),这应该使它工作,对å—?ä¸å¹¸çš„是,这是ä¸æ£ç¡®çš„ï¼Œå› ä¸ºIP 0.0.0.0是从主机(localhost)访问ES容器的IP。å¦ä¸€ä¸ªå®¹å™¨å°†æ— 法在åŒä¸€IP地å€ä¸Šè®¿é—®å®ƒã€‚如果ä¸æ˜¯è¯¥IP,那么ES容器应å¯è®¿é—®å“ªä¸ªIP地å€ï¼Ÿ 现在是开始探索Docker网络的好时机。安装dockeråŽï¼Œå®ƒå°†è‡ªåŠ¨åˆ›å»ºä¸‰ä¸ªç½‘络。 ``` admin:~/environment $ docker network ls NETWORK ID NAME DRIVER SCOPE d037c38ecd83 bridge bridge local 6a3b3ab8c510 host host local de2a13d13a7a none null local ``` 桥接网络(bridge)是默认情况下è¿è¡Œå®¹å™¨çš„ç½‘ç»œã€‚å› æ¤ï¼Œè¿™æ„味ç€å½“我è¿è¡ŒES容器时,它æ£åœ¨æ¤æ¡¥æŽ¥ç½‘络ä¸è¿è¡Œã€‚为了验è¯è¿™ä¸€ç‚¹ï¼Œè®©æˆ‘们检查网络。 ``` admin:~/environment $ docker network inspect bridge [ { "Name": "bridge", "Id": "d037c38ecd83d19ab65cd70020b996e9a53dd9c31a457658b47c14208f483410", "Created": "2020-07-29T01:04:11.675105689Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": null, "Config": [ { "Subnet": "172.17.0.0/16", "Gateway": "172.17.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "b17bd4d6a319e6206d47d9a5496d107a025c5cdfd7a37c64d7aa8e3570232b79": { "Name": "es", "EndpointID": "9944409131e57a61f18d86fab2e776126d42a2d20884b5c01ecfa7faecc385ce", "MacAddress": "02:42:ac:11:00:02", "IPv4Address": "172.17.0.2/16", "IPv6Address": "" } }, "Options": { "com.docker.network.bridge.default_bridge": "true", "com.docker.network.bridge.enable_icc": "true", "com.docker.network.bridge.enable_ip_masquerade": "true", "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0", "com.docker.network.bridge.name": "docker0", "com.docker.network.driver.mtu": "1500" }, "Labels": {} } ] ``` 您å¯ä»¥çœ‹åˆ°æˆ‘们的容器`b17bd4d6a319`在输出的“容器â€éƒ¨åˆ†ä¸‹åˆ—出。我们还看到的是æ¤å®¹å™¨å·²åˆ†é…çš„IP地å€-172.17.0.2。这是我们è¦æŸ¥æ‰¾çš„IP地å€å—?让我们通过è¿è¡ŒFlask容器并å°è¯•è®¿é—®æ¤IPæ¥æ‰¾å‡ºç”案。 ``` $ docker run -it --rm nikosheng/foodtrucks-web curl 172.17.0.2:9200 { "name" : "-ml6cJ3", "cluster_name" : "docker-cluster", "cluster_uuid" : "LoB0rDHaTqKMqoKMnnD2dA", "version" : { "number" : "6.3.2", "build_flavor" : "default", "build_type" : "tar", "build_hash" : "053779d", "build_date" : "2018-07-20T05:20:23.451332Z", "build_snapshot" : false, "lucene_version" : "7.3.1", "minimum_wire_compatibility_version" : "5.6.0", "minimum_index_compatibility_version" : "5.0.0" }, "tagline" : "You Know, for Search" } ``` 我们å¯ä»¥ç›´æŽ¥åœ¨è¿è¡Œdockerçš„æ—¶å€™åŠ ä¸Šæˆ‘ä»¬å¸Œæœ›å®¹å™¨æ‰§è¡Œçš„å‘½ä»¤ï¼Œæˆ‘ä»¬çœ‹åˆ°æˆ‘ä»¬ç¡®å®žå¯ä»¥åœ¨172.17.0.2:9200上与ES对è¯ã€‚Awesomeï¼ å°½ç®¡æˆ‘ä»¬å·²ç»æ‰¾åˆ°äº†ä¸€ç§ä½¿å®¹å™¨ç›¸äº’通信的方法,但是这ç§æ–¹æ³•ä»ç„¶å˜åœ¨ä¸¤ä¸ªé—®é¢˜ - 我们如何告诉Flask容器es主机å代表172.17.0.2或其他IPï¼Œå› ä¸ºIPå¯èƒ½ä¼šéšæ—¶å˜åŒ– - 由于默认情况下æ¯ä¸ªå®¹å™¨éƒ½å…±äº«æ¡¥æŽ¥ç½‘ç»œï¼Œå› æ¤æ¤æ–¹æ³•å¹¶ä¸å®‰å…¨ã€‚我们如何隔离我们的网络? 好消æ¯æ˜¯Docker对我们的问题有很好的ç”案。它å…许我们定义自己的网络,åŒæ—¶ä½¿ç”¨docker network命令将它们隔离。 首先,让我们创建自己的网络。 ``` $ docker network create aws-net $ docker network ls NETWORK ID NAME DRIVER SCOPE a5e79e75b97b aws-net bridge local ``` `docker network create`命令创建一个新的网桥网络,这是我们目å‰éœ€è¦çš„。就Docker而言,网桥网络使用软件网桥,该软件网桥å…许连接到åŒä¸€ç½‘桥网络的容器进行通信,åŒæ—¶å°†æœªè¿žæŽ¥åˆ°è¯¥ç½‘桥网络的容器隔离。 Docker网桥驱动程åºä¼šè‡ªåŠ¨åœ¨ä¸»æœºä¸éƒ¨ç½²è§„则,以使ä¸åŒç½‘æ¡¥ç½‘ç»œä¸Šçš„å®¹å™¨æ— æ³•ç›´æŽ¥ç›¸äº’é€šä¿¡ã€‚ 现在我们有了一个自定义网络,我们å¯ä»¥ä½¿ç”¨--netæ ‡å¿—åœ¨è¯¥ç½‘ç»œå†…å¯åŠ¨å®¹å™¨ã€‚但首先,为了å¯åŠ¨å…·æœ‰ç›¸åŒå称的新容器,我们将åœæ¢å¹¶åˆ 除在网桥(默认)网络ä¸è¿è¡Œçš„ES容器。 ``` $ docker container stop es es $ docker container rm es es $ docker run -d --name es --net aws-net -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.3.2 $ docker network inspect aws-net [ { "Name": "aws-net", "Id": "a5e79e75b97bddc921f59cc233e3d4fd47cf05753fe9cba833dcf6d0e39e9fb2", "Created": "2020-07-29T01:52:29.251280599Z", "Scope": "local", "Driver": "bridge", "EnableIPv6": false, "IPAM": { "Driver": "default", "Options": {}, "Config": [ { "Subnet": "172.18.0.0/16", "Gateway": "172.18.0.1" } ] }, "Internal": false, "Attachable": false, "Ingress": false, "ConfigFrom": { "Network": "" }, "ConfigOnly": false, "Containers": { "aa04d18a7d073df6159717029ae5b42aa10a476d93a637fab24f122b94eee651": { "Name": "es", "EndpointID": "1ab25adb60508edc9d422095c89a8a6f28da0eb3576882be16ca0f234a8b8f46", "MacAddress": "02:42:ac:12:00:02", "IPv4Address": "172.18.0.2/16", "IPv6Address": "" } }, "Options": {}, "Labels": {} } ] ``` 如您所è§ï¼Œæˆ‘们的es容器现在在aws-net网桥网络ä¸è¿è¡Œã€‚现在,让我们检查一下在aws-net网络ä¸å¯åŠ¨æ—¶å‘生的情况。 ``` $ docker run -it --rm --net aws-net nikosheng/foodtrucks-web curl es:9200 { "name" : "8HgCcmE", "cluster_name" : "docker-cluster", "cluster_uuid" : "-C8xgQccSVmqx3KQFteyZA", "version" : { "number" : "6.3.2", "build_flavor" : "default", "build_type" : "tar", "build_hash" : "053779d", "build_date" : "2018-07-20T05:20:23.451332Z", "build_snapshot" : false, "lucene_version" : "7.3.1", "minimum_wire_compatibility_version" : "5.6.0", "minimum_index_compatibility_version" : "5.0.0" }, "tagline" : "You Know, for Search" } ``` å¯è¡Œï¼åœ¨ç”¨æˆ·å®šä¹‰çš„网络(如aws-net)上,容器ä¸ä»…å¯ä»¥é€šè¿‡IP地å€è¿›è¡Œé€šä¿¡ï¼Œè€Œä¸”还å¯ä»¥å°†å®¹å™¨å称解æžä¸ºIP地å€ã€‚æ¤åŠŸèƒ½ç§°ä¸ºè‡ªåŠ¨æœåŠ¡å‘现。让我们立å³å¯åŠ¨Flask容器 请注æ„,如果出现`The container name "foodtrucks-web" is already in use by container` 的错误,å¯ä»¥å…ˆé€šè¿‡ docker container ls -a 获å–åœæ¢çš„容器id,然åŽé€šè¿‡docker container stop <容器id> ä»¥åŠ docker container rm <容器id> æ¥å½»åº•æ³¨é”€å®¹å™¨ ``` $ docker run -d --net aws-net -p 5000:5000 --name foodtrucks-web nikosheng/foodtrucks-web $ docker container ls CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 308e2ed9d218 nikosheng/foodtrucks-web "python3 ./app.py" 18 seconds ago Up 17 seconds 0.0.0.0:5000->5000/tcp foodtrucks-web aa04d18a7d07 docker.elastic.co/elasticsearch/elasticsearch:6.3.2 "/usr/local/bin/dock…" 42 minutes ago Up 42 minutes 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp es $ curl -I 0.0.0.0:5000 HTTP/1.0 200 OK Content-Type: text/html; charset=utf-8 Content-Length: 3685 Server: Werkzeug/1.0.1 Python/3.6.9 Date: Wed, 29 Jul 2020 02:40:47 GMT ``` 我们å¯ä»¥çœ‹åˆ°webæœåŠ¡å·²ç»å¯ä»¥æˆåŠŸè°ƒç”¨esæœåŠ¡å•¦ã€‚ </pre> </details>