diff --git a/.travis/book.json b/.travis/book.json index 07a348b..c9f0ea4 100644 --- a/.travis/book.json +++ b/.travis/book.json @@ -10,8 +10,9 @@ "plugins": [ "-livereload", "image-captions", - "github-buttons", - "page-treeview" + "github", + "page-treeview", + "editlink" ], "pluginsConfig": { "image-captions": { @@ -20,13 +21,12 @@ }, "caption": "图 _PAGE_LEVEL_._PAGE_IMAGE_NUMBER_ - _CAPTION_" }, - "github-buttons": { - "buttons": [{ - "user": "yeasy", - "repo": "docker_practice", - "type": "star", - "size": "small" - }] + "github": { + "url": "https://github.com/yeasy/docker_practice" + }, + "editlink": { + "base": "https://github.com/yeasy/docker_practice/blob/master/", + "label": "编辑本页" }, "page-treeview": { "copyright": "Copyright © yeasy", diff --git a/CHANGELOG.md b/CHANGELOG.md index 5c33154..1326ad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,10 @@ * 0.9-rc3: 2017-12-20 + * 精简示例代码 + + * 调整目录结构 + * 0.9-rc2: 2017-12-10 * 增加 Docker 中文资源链接 diff --git a/README.md b/README.md index 8d17eac..8828590 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,9 @@ [![](https://img.shields.io/github/stars/yeasy/docker_practice.svg?style=social&label=Stars)](https://github.com/yeasy/docker_practice) [![](https://img.shields.io/docker/pulls/yeasy/docker_practice.svg)](https://store.docker.com/community/images/yeasy/docker_practice) [![](https://travis-ci.org/yeasy/docker_practice.svg?branch=master)](https://travis-ci.org/yeasy/docker_practice) [![](https://img.shields.io/github/release/yeasy/docker_practice/all.svg)](https://github.com/yeasy/docker_practice/releases) [![](https://badges.gitter.im/docker_practice/Lobby.svg)](https://gitter.im/docker_practice/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) -0.9-rc1(2017-11-29) +0.9-rc2(2017-12-09) -*修订说明:本书内容将基于 Docker CE v17.x 进行重新修订,计划 2017 年底发布 0.9.0 版本。旧版本(Docker 1.13-)内容,请阅读 [docker-legacy](https://github.com/yeasy/docker_practice/tree/docker-legacy) 分支的内容。* +*修订说明:本书内容将基于 Docker CE v17.MM 进行重新修订,计划 2017 年底发布 0.9.0 版本。旧版本(Docker 1.13-)内容,请阅读 [docker-legacy](https://github.com/yeasy/docker_practice/tree/docker-legacy) 分支的内容。* [Docker](http://www.docker.com) 是个划时代的开源项目,它彻底释放了计算虚拟化的威力,极大提高了应用的运行效率,降低了云计算资源供应的成本!使用 Docker,可以让应用的部署、测试和分发都变得前所未有的高效和轻松! @@ -18,7 +18,7 @@ * pdf 版本 [下载](https://www.gitbook.com/download/pdf/book/yeasy/docker_practice) * epub 版本 [下载](https://www.gitbook.com/download/epub/book/yeasy/docker_practice) -Docker 自身仍在快速发展中,生态环境也在蓬勃成长。建议初学者使用最新版的 Docker (v17.x) 进行学习实践。欢迎 [参与维护项目](CONTRIBUTING.md)。 +Docker 自身仍在快速发展中,生态环境也在蓬勃成长。建议初学者使用最新版的 Docker (v17.MM) 进行学习实践。欢迎 [参与维护项目](CONTRIBUTING.md)。 * [修订记录](CHANGELOG.md) * [贡献者名单](https://github.com/yeasy/docker_practice/graphs/contributors) diff --git a/SUMMARY.md b/SUMMARY.md index da8be8a..18a4764 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -57,9 +57,9 @@ * [使用网络](network/README.md) * [外部访问容器](network/port_mapping.md) * [容器互联](network/linking.md) + * [配置 DNS](network/dns.md) * [高级网络配置](advanced_network/README.md) * [快速配置指南](advanced_network/quick_guide.md) - * [配置 DNS](advanced_network/dns.md) * [容器访问控制](advanced_network/access_control.md) * [端口映射实现](advanced_network/port_mapping.md) * [配置 docker0 网桥](advanced_network/docker0.md) diff --git a/book.json b/book.json index 07a348b..c9f0ea4 100644 --- a/book.json +++ b/book.json @@ -10,8 +10,9 @@ "plugins": [ "-livereload", "image-captions", - "github-buttons", - "page-treeview" + "github", + "page-treeview", + "editlink" ], "pluginsConfig": { "image-captions": { @@ -20,13 +21,12 @@ }, "caption": "图 _PAGE_LEVEL_._PAGE_IMAGE_NUMBER_ - _CAPTION_" }, - "github-buttons": { - "buttons": [{ - "user": "yeasy", - "repo": "docker_practice", - "type": "star", - "size": "small" - }] + "github": { + "url": "https://github.com/yeasy/docker_practice" + }, + "editlink": { + "base": "https://github.com/yeasy/docker_practice/blob/master/", + "label": "编辑本页" }, "page-treeview": { "copyright": "Copyright © yeasy", diff --git a/cloud/alicloud.md b/cloud/alicloud.md index 53f2920..b024b24 100644 --- a/cloud/alicloud.md +++ b/cloud/alicloud.md @@ -8,3 +8,4 @@ 阿里云容器服务提供了高性能、可伸缩的容器应用管理服务,支持在一组云服务器上通过 Docker 容器来进行应用生命周期管理。容器服务极大简化了用户对容器管理集群的搭建工作,无缝整合了阿里云虚拟化、存储、网络和安全能力。容器服务提供了多种应用发布方式和流水线般的持续交付能力,原生支持微服务架构,助力用户无缝上云和跨云管理。 +![](https://img.alicdn.com/tps/TB10yjtPpXXXXacXXXXXXXXXXXX-1531-1140.png) diff --git a/cloud/qcloud.md b/cloud/qcloud.md index 9cd9bf6..9221470 100644 --- a/cloud/qcloud.md +++ b/cloud/qcloud.md @@ -7,3 +7,5 @@ 具体包括云服务器、云存储、云数据库和弹性 web 引擎等基础云服务;腾讯云分析(MTA)、腾讯云推送(信鸽)等腾讯整体大数据能力;以及 QQ互联、QQ 空间、微云、微社区等云端链接社交体系。这些正是腾讯云可以提供给这个行业的差异化优势,造就了可支持各种互联网使用场景的高品质的腾讯云技术平台。 腾讯云容器服务是高度可扩展的高性能容器管理服务,用户可以在托管的云服务器实例集群上轻松运行应用程序。使用该服务,将无需安装、运维、扩展用户的集群管理基础设施,只需进行简单的API调用,便可启动和停止Docker应用程序,查询集群的完整状态,以及使用各种云服务。用户可以根据用户的资源需求和可用性要求在用户的集群中安排容器的置放,满足业务或应用程序的特定要求。 + +![](https://mc.qcloudimg.com/static/img/0581dbeb97c869bbe6e62025dbc592d7/image.png) diff --git a/compose/_images/docker_compose.jpg b/compose/_images/docker_compose.jpg deleted file mode 100644 index d5fa64a..0000000 Binary files a/compose/_images/docker_compose.jpg and /dev/null differ diff --git a/compose/demo/app/Dockerfile b/compose/demo/app/Dockerfile new file mode 100644 index 0000000..7c4b8c7 --- /dev/null +++ b/compose/demo/app/Dockerfile @@ -0,0 +1,5 @@ +FROM python:3.6-alpine +ADD . /code +WORKDIR /code +RUN pip install redis flask +CMD ["python", "app.py"] diff --git a/compose/demo/app/app.py b/compose/demo/app/app.py new file mode 100644 index 0000000..f0dfc20 --- /dev/null +++ b/compose/demo/app/app.py @@ -0,0 +1,13 @@ +from flask import Flask +from redis import Redis + +app = Flask(__name__) +redis = Redis(host='redis', port=6379) + +@app.route('/') +def hello(): + count = redis.incr('hits') + return 'Hello World! 该页面已被访问 {} 次。\n'.format(count) + +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) diff --git a/compose/demo/app/docker-compose.yml b/compose/demo/app/docker-compose.yml new file mode 100644 index 0000000..f2a202f --- /dev/null +++ b/compose/demo/app/docker-compose.yml @@ -0,0 +1,10 @@ +version: '3' +services: + + web: + build: . + ports: + - "5000:5000" + + redis: + image: "redis:alpine" diff --git a/compose/demo/compose-haproxy-web/docker-compose.yml b/compose/demo/compose-haproxy-web/docker-compose.yml deleted file mode 100644 index 46c0841..0000000 --- a/compose/demo/compose-haproxy-web/docker-compose.yml +++ /dev/null @@ -1,29 +0,0 @@ -version: "3" -services: - - weba: - build: ./web - expose: - - 80 - - webb: - build: ./web - expose: - - 80 - - webc: - build: ./web - expose: - - 80 - - haproxy: - image: haproxy:latest - volumes: - - ./haproxy:/haproxy-override - - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro - ports: - - "80:80" - - "70:70" - expose: - - "80" - - "70" diff --git a/compose/demo/compose-haproxy-web/haproxy/haproxy.cfg b/compose/demo/compose-haproxy-web/haproxy/haproxy.cfg deleted file mode 100644 index 99251ed..0000000 --- a/compose/demo/compose-haproxy-web/haproxy/haproxy.cfg +++ /dev/null @@ -1,32 +0,0 @@ -global - log 127.0.0.1 local0 - log 127.0.0.1 local1 notice - -defaults - log global - mode http - option httplog - option dontlognull - timeout connect 5000ms - timeout client 50000ms - timeout server 50000ms - -listen stats - bind 0.0.0.0:70 - stats enable - stats uri / - -frontend balancer - bind 0.0.0.0:80 - mode http - default_backend web_backends - -backend web_backends - mode http - option forwardfor - balance roundrobin - server weba weba:80 check - server webb webb:80 check - server webc webc:80 check - option httpchk GET / - http-check expect status 200 diff --git a/compose/demo/compose-haproxy-web/web/Dockerfile b/compose/demo/compose-haproxy-web/web/Dockerfile deleted file mode 100644 index d0e74cd..0000000 --- a/compose/demo/compose-haproxy-web/web/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -FROM python:2.7 -WORKDIR /code -ADD . /code -EXPOSE 80 -CMD python index.py diff --git a/compose/demo/compose-haproxy-web/web/index.py b/compose/demo/compose-haproxy-web/web/index.py deleted file mode 100644 index d274ec9..0000000 --- a/compose/demo/compose-haproxy-web/web/index.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/python -#authors: yeasy.github.com -#date: 2013-07-05 - -import sys -import BaseHTTPServer -from SimpleHTTPServer import SimpleHTTPRequestHandler -import socket -import fcntl -import struct -import pickle -from datetime import datetime -from collections import OrderedDict - -class HandlerClass(SimpleHTTPRequestHandler): - def get_ip_address(self,ifname): - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return socket.inet_ntoa(fcntl.ioctl( - s.fileno(), - 0x8915, # SIOCGIFADDR - struct.pack('256s', ifname[:15]) - )[20:24]) - def log_message(self, format, *args): - if len(args) < 3 or "200" not in args[1]: - return - try: - request = pickle.load(open("pickle_data.txt","r")) - except: - request=OrderedDict() - time_now = datetime.now() - ts = time_now.strftime('%Y-%m-%d %H:%M:%S') - server = self.get_ip_address('eth0') - host=self.address_string() - addr_pair = (host,server) - if addr_pair not in request: - request[addr_pair]=[1,ts] - else: - num = request[addr_pair][0]+1 - del request[addr_pair] - request[addr_pair]=[num,ts] - file=open("index.html", "w") - file.write("

HA Webpage Visit Results

") - for pair in request: - if pair[0] == host: - guest = "LOCAL: "+pair[0] - else: - guest = pair[0] - if (time_now-datetime.strptime(request[pair][1],'%Y-%m-%d %H:%M:%S')).seconds < 3: - file.write("

#"+ str(request[pair][1]) +": "+str(request[pair][0])+ " requests " + "from <"+guest+"> to WebServer <"+pair[1]+">

") - else: - file.write("

#"+ str(request[pair][1]) +": "+str(request[pair][0])+ " requests " + "from <"+guest+"> to WebServer <"+pair[1]+">

") - file.write(" ") - file.close() - pickle.dump(request,open("pickle_data.txt","w")) - -if __name__ == '__main__': - try: - ServerClass = BaseHTTPServer.HTTPServer - Protocol = "HTTP/1.0" - addr = len(sys.argv) < 2 and "0.0.0.0" or sys.argv[1] - port = len(sys.argv) < 3 and 80 or int(sys.argv[2]) - HandlerClass.protocol_version = Protocol - httpd = ServerClass((addr, port), HandlerClass) - sa = httpd.socket.getsockname() - print "Serving HTTP on", sa[0], "port", sa[1], "..." - httpd.serve_forever() - except: - exit() diff --git a/compose/introduction.md b/compose/introduction.md index 9a41593..cde4aff 100644 --- a/compose/introduction.md +++ b/compose/introduction.md @@ -1,7 +1,5 @@ ## Compose 简介 -![Docker Compose 项目](_images/docker_compose.jpg) - `Compose` 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。从功能上看,跟 `OpenStack` 中的 `Heat` 十分类似。 其代码目前在 [https://github.com/docker/compose](https://github.com/docker/compose) 上开源。 diff --git a/compose/usage.md b/compose/usage.md index 6001b2f..593b754 100644 --- a/compose/usage.md +++ b/compose/usage.md @@ -1,6 +1,7 @@ ## 使用 ### 术语 + 首先介绍几个术语。 * 服务 (`service`):一个应用容器,实际上可以运行多个相同镜像的实例。 @@ -10,197 +11,64 @@ 可见,一个项目可以由多个服务(容器)关联而成,`Compose` 面向项目进行管理。 ### 场景 -下面,我们创建一个经典的 Web 项目:一个 [Haproxy](http://www.haproxy.org/),挂载三个 Web 容器。 -创建一个 `compose-haproxy-web` 目录,作为项目工作目录,并在其中分别创建两个子目录:`haproxy` 和 `web`。 +最常见的项目是 web 网站,该项目应该包含 web 应用和缓存。 -### web 子目录 +下面我们用 `Python` 来建立一个能够记录页面访问次数的 web 网站。 -这里用 Python 程序来提供一个简单的 HTTP 服务,打印出访问者的 IP 和 实际的本地 IP。 +#### web 应用 -#### index.py +新建文件夹,在该目录中编写 `app.py` 文件 -编写一个 `index.py` 作为服务器文件,代码为 ```python -#!/usr/bin/python -#authors: yeasy.github.com -#date: 2013-07-05 +from flask import Flask +from redis import Redis -import sys -import BaseHTTPServer -from SimpleHTTPServer import SimpleHTTPRequestHandler -import socket -import fcntl -import struct -import pickle -from datetime import datetime -from collections import OrderedDict +app = Flask(__name__) +redis = Redis(host='redis', port=6379) -class HandlerClass(SimpleHTTPRequestHandler): - def get_ip_address(self,ifname): - s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - return socket.inet_ntoa(fcntl.ioctl( - s.fileno(), - 0x8915, # SIOCGIFADDR - struct.pack('256s', ifname[:15]) - )[20:24]) - def log_message(self, format, *args): - if len(args) < 3 or "200" not in args[1]: - return - try: - request = pickle.load(open("pickle_data.txt","r")) - except: - request=OrderedDict() - time_now = datetime.now() - ts = time_now.strftime('%Y-%m-%d %H:%M:%S') - server = self.get_ip_address('eth0') - host=self.address_string() - addr_pair = (host,server) - if addr_pair not in request: - request[addr_pair]=[1,ts] - else: - num = request[addr_pair][0]+1 - del request[addr_pair] - request[addr_pair]=[num,ts] - file=open("index.html", "w") - file.write("

HA Webpage Visit Results

") - for pair in request: - if pair[0] == host: - guest = "LOCAL: "+pair[0] - else: - guest = pair[0] - if (time_now-datetime.strptime(request[pair][1],'%Y-%m-%d %H:%M:%S')).seconds < 3: - file.write("

#"+ str(request[pair][1]) +": "+str(request[pair][0])+ " requests " + "from <"+guest+"> to WebServer <"+pair[1]+">

") - else: - file.write("

#"+ str(request[pair][1]) +": "+str(request[pair][0])+ " requests " + "from <"+guest+"> to WebServer <"+pair[1]+">

") - file.write(" ") - file.close() - pickle.dump(request,open("pickle_data.txt","w")) +@app.route('/') +def hello(): + count = redis.incr('hits') + return 'Hello World! 该页面已被访问 {} 次。\n'.format(count) -if __name__ == '__main__': - try: - ServerClass = BaseHTTPServer.HTTPServer - Protocol = "HTTP/1.0" - addr = len(sys.argv) < 2 and "0.0.0.0" or sys.argv[1] - port = len(sys.argv) < 3 and 80 or int(sys.argv[2]) - HandlerClass.protocol_version = Protocol - httpd = ServerClass((addr, port), HandlerClass) - sa = httpd.socket.getsockname() - print "Serving HTTP on", sa[0], "port", sa[1], "..." - httpd.serve_forever() - except: - exit() -``` - -#### index.html -生成一个临时的 `index.html` 文件,其内容会被 `index.py` 更新。 -```bash -$ touch index.html +if __name__ == "__main__": + app.run(host="0.0.0.0", debug=True) ``` #### Dockerfile + 编写 `Dockerfile` 文件,内容为 + ```docker -FROM python:2.7 -WORKDIR /code +FROM python:3.6-alpine ADD . /code -EXPOSE 80 -CMD python index.py +WORKDIR /code +RUN pip install redis flask +CMD ["python", "app.py"] ``` -### haproxy 目录 -编写 `haproxy.cfg` 文件,内容为 -```bash -global - log 127.0.0.1 local0 - log 127.0.0.1 local1 notice +#### docker-compose.yml -defaults - log global - mode http - option httplog - option dontlognull - timeout connect 5000ms - timeout client 50000ms - timeout server 50000ms - -listen stats - bind 0.0.0.0:70 - stats enable - stats uri / - -frontend balancer - bind 0.0.0.0:80 - mode http - default_backend web_backends - -backend web_backends - mode http - option forwardfor - balance roundrobin - server weba weba:80 check - server webb webb:80 check - server webc webc:80 check - option httpchk GET / - http-check expect status 200 -``` -### docker-compose.yml -编写 `docker-compose.yml` 文件,这个是 `Compose` 使用的主模板文件。内容十分简单,指定 3 个 `web` 容器,以及 1 个 `haproxy` 容器。 +编写 `docker-compose.yml` 文件,这个是 Compose 使用的主模板文件。 ```yaml -version: "3" +version: '3' services: - weba: - build: ./web - expose: - - 80 - - webb: - build: ./web - expose: - - 80 - - webc: - build: ./web - expose: - - 80 - - haproxy: - image: haproxy:latest - volumes: - - ./haproxy:/haproxy-override - - ./haproxy/haproxy.cfg:/usr/local/etc/haproxy/haproxy.cfg:ro + web: + build: . ports: - - "80:80" - - "70:70" - expose: - - "80" - - "70" + - "5000:5000" + + redis: + image: "redis:alpine" ``` -### 运行 compose 项目 -现在 `compose-haproxy-web` 目录结构如下: -```bash -compose-haproxy-web -├── docker-compose.yml -├── haproxy -│ └── haproxy.cfg -└── web - ├── Dockerfile - ├── index.html - └── index.py -``` -在该目录下执行 `docker-compose up` 命令,会整合输出所有容器的输出。 +#### 运行 compose 项目 + ```bash $ docker-compose up -Recreating composehaproxyweb_webb_1... -Recreating composehaproxyweb_webc_1... -Recreating composehaproxyweb_weba_1... -Recreating composehaproxyweb_haproxy_1... -Attaching to composehaproxyweb_webb_1, composehaproxyweb_webc_1, composehaproxyweb_weba_1, composehaproxyweb_haproxy_1 ``` -此时访问本地的 `80` 端口,会经过 `haproxy` 自动转发到后端的某个 web 容器上,刷新页面,可以观察到访问的容器地址的变化。 - -访问本地 `70` 端口,可以查看到 `haproxy` 的统计信息。 +此时访问本地 `5000` 端口,每次刷新页面,计数就会加 1。 diff --git a/docker-compose.yml b/docker-compose.yml index 88bf1ae..583d0e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,7 +2,6 @@ version: "3" services: server: - # build: ./.travis image: yeasy/docker_practice:latest ports: - 4000:4000 @@ -27,7 +26,7 @@ services: development: build: ./.travis - image: yeasy/docker_practice:development + image: yeasy/docker_practice:latest ports: - 4000:4000 volumes: diff --git a/install/centos.md b/install/centos.md index fb0907d..56324fe 100644 --- a/install/centos.md +++ b/install/centos.md @@ -98,6 +98,8 @@ $ sudo groupadd docker $ sudo usermod -aG docker $USER ``` +退出当前终端并重新登录,进行如下测试。 + ### 测试 Docker 是否安装正确 ```bash diff --git a/install/debian.md b/install/debian.md index b6a9ce5..b757a84 100644 --- a/install/debian.md +++ b/install/debian.md @@ -144,6 +144,8 @@ $ sudo groupadd docker $ sudo usermod -aG docker $USER ``` +退出当前终端并重新登录,进行如下测试。 + ### 测试 Docker 是否安装正确 ```bash diff --git a/install/raspberry-pi.md b/install/raspberry-pi.md index bd2d388..93c27b7 100644 --- a/install/raspberry-pi.md +++ b/install/raspberry-pi.md @@ -102,6 +102,8 @@ $ sudo groupadd docker $ sudo usermod -aG docker $USER ``` +退出当前终端并重新登录,进行如下测试。 + ### 测试 Docker 是否安装正确 ```bash diff --git a/install/ubuntu.md b/install/ubuntu.md index cb60aae..e6c78a8 100644 --- a/install/ubuntu.md +++ b/install/ubuntu.md @@ -132,6 +132,8 @@ $ sudo groupadd docker $ sudo usermod -aG docker $USER ``` +退出当前终端并重新登录,进行如下测试。 + ### 测试 Docker 是否安装正确 ```bash diff --git a/machine/_images/docker_machine.png b/machine/_images/docker_machine.png deleted file mode 100644 index e8c5632..0000000 Binary files a/machine/_images/docker_machine.png and /dev/null differ diff --git a/advanced_network/dns.md b/network/dns.md similarity index 86% rename from advanced_network/dns.md rename to network/dns.md index 973ea31..00b7161 100644 --- a/advanced_network/dns.md +++ b/network/dns.md @@ -33,7 +33,7 @@ nameserver 114.114.114.114 nameserver 8.8.8.8 ``` -如果用户想要手动指定容器的配置,可以利用下面的选项。 +如果用户想要手动指定容器的配置,可以在使用 `docker run` 命令启动容器时加入如下参数: `-h HOSTNAME` 或者 `--hostname=HOSTNAME` 设定容器的主机名,它会被写到容器内的 `/etc/hostname` 和 `/etc/hosts`。但它在容器外部看不到,既不会在 `docker ps` 中显示,也不会在其他的容器的 `/etc/hosts` 看到。 @@ -41,4 +41,4 @@ nameserver 8.8.8.8 `--dns-search=DOMAIN` 设定容器的搜索域,当设定搜索域为 `.example.com` 时,在搜索一个名为 host 的主机时,DNS 不仅搜索 host,还会搜索 `host.example.com`。 -*注意:* 如果没有上述最后 2 个选项,Docker 会默认用主机上的 `/etc/resolv.conf` 来配置容器。 +>注意:如果在容器启动时没有指定最后两个参数,Docker 会默认用主机上的 `/etc/resolv.conf` 来配置容器。