diff --git a/Day91-100/91.团队项目开发的问题和解决方案.md b/Day91-100/91.团队项目开发的问题和解决方案.md index 4c1e45b..ea64fd8 100644 --- a/Day91-100/91.团队项目开发的问题和解决方案.md +++ b/Day91-100/91.团队项目开发的问题和解决方案.md @@ -497,7 +497,7 @@ Git不像SVN那样一定需要中央服务器才能工作,上面我们演示 git push --tags ``` -Git-flow流程比较容易控制各个分支的状况,但是在运用上github-flow要复杂得多,因此实际使用的时候通常会安装名为`gitflow`的命令行工具或者使用图形化的Git工具(如:SmartGit、SourceTree等)来简化操作,具体的可以参考[《git-flow 的工作流程》]()一文,因为这篇文章写得已经很好了,本文不再进行赘述。 +Git-flow流程比较容易控制各个分支的状况,但是在运用上github-flow要复杂得多,因此实际使用的时候通常会安装名为`gitflow`的命令行工具(Windows环境的Git自带了该工具)或者使用图形化的Git工具(如:SmartGit、SourceTree等)来简化操作,具体的可以参考[《git-flow 的工作流程》]()一文,因为这篇文章写得已经很好了,本文不再进行赘述。 ### 缺陷管理 diff --git a/Day91-100/92.使用Docker部署应用.md b/Day91-100/92.Docker容器详解.md similarity index 76% rename from Day91-100/92.使用Docker部署应用.md rename to Day91-100/92.Docker容器详解.md index 143b110..63c9b04 100644 --- a/Day91-100/92.使用Docker部署应用.md +++ b/Day91-100/92.Docker容器详解.md @@ -1,4 +1,6 @@ -## 使用Docker部署应用 +## Docker容器详解 + +Docker是基于Go语言开发的开源应用容器引擎,遵从Apache Licence 2.0协议,可以让开发者打包应用以及应用的依赖包到一个可移植的容器中,然后发布到各种发行版本的Linux系统上。 ### Docker简介 @@ -8,7 +10,7 @@ 虚拟机(virtual machine)就是带环境安装的一种解决方案,它可以在一种操作系统里面运行另一种操作系统,比如在Windows系统里面运行Linux系统,在macOS上运行Windows,而应用程序对此毫无感知。使用过虚拟机的人都知道,虚拟机用起来跟真实系统一模一样,而对于虚拟机的宿主系统来说,虚拟机就是一个普通文件,不需要了就删掉,对宿主系统或者其他的程序并没有影响。但是虚拟机通常会占用较多的系统资源,启动和关闭也非常的缓慢,总之用户体验并没有想象中的那么好。 -Docker属于对Linux容器技术的一种封装(利用了Linux的namespace和cgroup技术),它提供了简单易用的容器使用接口,是目前最流行的 Linux 容器解决方案。Docker将应用程序与该程序的依赖打包在一个文件里面,运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。下图是虚拟机和容器的对比,左边是传统的虚拟机,右边是Docker。 +Docker属于对Linux容器技术(LXC)的一种封装(利用了Linux的namespace和cgroup技术),它提供了简单易用的容器使用接口,是目前最流行的 Linux 容器解决方案。Docker将应用程序与该程序的依赖打包在一个文件里面,运行这个文件,就会生成一个虚拟容器。程序在这个虚拟容器里运行,就好像在真实的物理机上运行一样。下图是虚拟机和容器的对比,左边是传统的虚拟机,右边是Docker。 ![](./res/docker_vs_vm.png) @@ -299,7 +301,7 @@ select user, host, plugin, authentication_string from user where user='root'; 接下来我们试一试运行多个容器并让多个容器之间通过网络通信。我们创建4个Redis容器来实现一主三从的主从复制结构。 ```Shell -docker run -d -p 6379:6379 --name redis-master redis redis-server +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 @@ -336,9 +338,7 @@ repl_backlog_histlen:1988 #### 运行GitLab -GitLab是由GitLab Inc.开发的Git仓库管理工具,具有wiki、问题跟踪、持续集成等一系列的功能,分为社区版和企业版。通过Docker提供的虚拟化容器,我们可以安装社区版的Docker,命令如下所示。 - -因为GitLab需要使用SSH协议进行安全连接,我们要暴露容器的22端口,所以可以先将宿主机SSH连接的22端口修改为其他端口(如:12345),然后再进行后续的操作。 +GitLab是由GitLab Inc.开发的Git仓库管理工具,具有wiki、问题跟踪、持续集成等一系列的功能,分为社区版和企业版。通过Docker提供的虚拟化容器,我们可以安装社区版的Docker。因为GitLab需要使用SSH协议进行安全连接,我们要暴露容器的22端口,所以可以先将宿主机SSH连接的22端口修改为其他端口(如:12345),然后再进行后续的操作。 ```Shell vim /etc/ssh/sshd_config @@ -438,25 +438,78 @@ jackfrued/mywebserver latest 795b294d265a 14 seconds ago 189 MB Dockerfile使用DSL(Domain Specific Language)来构建一个Docker镜像,只要编辑好了Dockerfile文件,就可以使用`docker build`命令来构建一个新的镜像。 -我们先创建一个空文件夹并在文件夹下创建名为Dockerfile的文件。 +我们先创建一个名为myapp的文件夹来保存项目代码和Dockerfile的文件,如下所示: ```Shell -touch Dockerfile +[ECS-root temp]# tree myapp +myapp +├── api +│ ├── app.py +│ ├── requirements.txt +│ └── start.sh +└── Dockerfile ``` -编辑这个Dockerfile文件添加如下所示的内容。 +其中api是Flask项目的文件夹,其中包括了项目代码、依赖项以及启动脚本等文件,具体内容如下所示: + +app.py文件: + +```Python +from flask import Flask +from flask_restful import Resource, Api +from flask_cors import CORS + +app = 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文件: + +```INI +flask +flask-restful +flask-cors +redis +gunicorn +``` + +start.sh文件: ```Shell -vim Dockerfile +#!/bin/bash +exec gunicorn -w 4 -b 0.0.0.0:8000 app:app ``` +> **提示**:需要给start.sh文件以执行权限,可以使用`chmod 755 start.sh`命令来做到。 + +Dockerfile文件: + ```Dockerfile -# version: 0.0.1 -FROM ubuntu:14.04 +# 指定基础镜像 +FROM python:3.7 +# 指定镜像的维护者 MAINTAINER jackfrued "jackfrued@126.com" -RUN apt-get update && apt-get install -y nginx -RUN echo 'hello, world!' > /usr/share/nginx/html/index.html -EXPOSE 80 +# 将指定文件添加到容器中指定的位置 +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中的指令。 @@ -464,7 +517,7 @@ EXPOSE 80 接下来我们可以使用`docker build`命令来创建镜像,如下所示。 ```Shell -docker build -t="jackfrued/webserver" . +docker build -t "jackfrued/myapp" . ``` > 提示:上面的命令最后面的`.` 千万不要漏掉了哦,它表示从当前路径下寻找Dockerfile。 @@ -476,34 +529,31 @@ docker images ``` ``` -REPOSITORY TAG IMAGE ID CREATED SIZE -jackfrued/webserver latest 87d6cb096be2 23 minutes ago 222 MB +REPOSITORY TAG IMAGE ID CREATED SIZE +jackfrued/myapp latest 6d6f026a7896 5 seconds ago 930 MB ``` 如果想知道镜像文件是如何创建出来的,可以使用下面的命令。 ```Shell -docker history jackfrued/webserver +docker history jackfrued/myapp ``` ``` -IMAGE CREATED CREATED BY SIZE -87d6cb096be2 25 minutes ago /bin/sh -c #(nop) EXPOSE 80/tcp 0 B -53d3bc3a123e 25 minutes ago /bin/sh -c service nginx start 3 B -10646b63275e 25 minutes ago /bin/sh -c echo 'hello, world!' > /usr/sha... 14 B -f3e3bf3e998e 25 minutes ago /bin/sh -c apt-get update && apt-get insta... 34.3 MB -c98e22cf5a64 26 minutes ago /bin/sh -c #(nop) MAINTAINER jackfrued "j... 0 B -2c5e00d77a67 3 months ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B - 3 months ago /bin/sh -c mkdir -p /run/systemd && echo '... 7 B - 3 months ago /bin/sh -c rm -rf /var/lib/apt/lists/* 0 B - 3 months ago /bin/sh -c set -xe && echo '#!/bin/sh' >... 195 kB - 3 months ago /bin/sh -c #(nop) ADD file:1e01ab604c0cc30... 188 MB +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服务器。 ```Shell -docker run -d -p 80:80 --name mywebserver jackfrued/webserver nginx -g "daemon off;" +docker run -d -p 8000:8000 --name myapp jackfrued/myapp ``` 如果希望将上面创建的镜像文件放到dockerhub仓库中,可以按照如下所示的步骤进行操作。 @@ -611,7 +661,7 @@ docker push jackfrued/webserver USER nginx ``` -8. **VOLUME**:在创建容器时添加一个数据卷的挂载点。通过数据卷操作可以实现容器间数据的共享和重用,对卷所作的修改可以马上生效而不需要重新启动容器,我们之前创建容器时使用`—volume`参数就是为了实现数据卷的映射操作。 +8. **VOLUME**:在创建容器时添加一个数据卷的挂载点。通过数据卷操作可以实现容器间数据的共享和重用,对卷所作的修改可以马上生效而不需要重新启动容器,我们之前创建容器时使用`--volume`参数就是为了实现数据卷的映射操作。 ```Dockerfile VOLUME ["/路径1", "/路径2/子路径2.1/", ...] @@ -638,7 +688,7 @@ docker push jackfrued/webserver ONBUILD RUN cd /app/src && make ``` -### 容器编排 +### 多容器管理 我们的项目可能会使用了多个容器,容器多了之后管理容器的工作就会变得麻烦。如果要对多个容器进行自动配置使得容器可以相互协作甚至实现复杂的调度,这就需要进行容器编排。Docker原生对容器编排的支持非常弱,但是可以通过社区提供的工具来实现容器编排。 @@ -663,30 +713,31 @@ docker push jackfrued/webserver 2. 使用Docker Compose。 - 我们先创建一个名为`composeapp`的文件夹并在该文件夹下创建两个子文件夹`product-service`和`web-site`,如下所示。 + 我们在刚才的Flask项目中引入缓存,然后再利用Flask提供的数据接口为前端页面提供数据,使用Vue.js进行页面渲染并将静态页面部署在Nginx服务器上。项目文件夹结构如下所示: ```Shell - mkdir composeapp - cd composeapp - mkdir product-service - mkdir web-site + [ECS-root ~]# tree temp + temp + ├── docker-compose.yml + ├── html + │ └── index.html + └── myapp + ├── api + │ ├── app.py + │ ├── requirements.txt + │ └── start.sh + └── Dockerfile ``` - 我们先在`product-service`文件夹下编写提供数据的API接口程序。 - - ```Shell - vim product-service/api.py - ``` - - 我们用Flask来实现一个非常简单的数据接口服务程序。 + 修改后的app.py文件代码如下所示: ```Python from pickle import dumps, loads from flask import Flask from flask_restful import Resource, Api - from redis import Redis from flask_cors import CORS + from redis import Redis app = Flask(__name__) CORS(app, resources={r'/api/*': {'origins': '*'}}) @@ -698,52 +749,18 @@ docker push jackfrued/webserver def get(self): data = redis.get('products') - if not data: + if data: + products = loads(data) + else: products = ['Ice Cream', 'Chocolate', 'Coca Cola', 'Hamburger'] redis.set('products', dumps(products)) - else: - products = loads(data) return {'products': products} api.add_resource(Product, '/api/products') - - if __name__ == '__main__': - app.run(host='0.0.0.0', port=8000, debug=True) ``` - 由于上面的项目需要依赖`flask`、 `flask-restful`等三方库,所以我们再添加一个指明依赖库的文件并将其命名为`requirements.txt`,其内容如下所示。 - - ```Shell - vim product-service/requirements.txt - ``` - - ``` - flask - flask-restful - flask-cors - redis - ``` - - 稍后我们会将上面的接口服务放在一个容器中运行,为此我们先编写一个Dockerfile文件以便创建对应的镜像,其内容如下所示。 - - ```Shell - vim product-service/Dockerfile - ``` - - ```Dockerfile - FROM python:3 - ADD . /root/product-service - WORKDIR /root/product-service - RUN pip install -r requirements.txt - CMD ["python", "api.py"] - ``` - - 我们再去到`web-site`目录下创建一个页面,稍后我们会通一个容器来提供Nginx服务并运行该页面,而这个页面会访问我们刚才部署的数据接口服务获取数据并通过Vue.js将数据渲染到页面上。 - - ```Shell - vim web-site/index.html - ``` + html文件夹用来保存静态页面,稍后我们会通一个运行Nginx的容器来向浏览器提供静态页面。index.html文件的内容如下所示: ```HTML @@ -777,68 +794,63 @@ docker push jackfrued/webserver ``` - 接下来,我们要通过一个YAML文件来创建三个容器并指明容器之间的依赖关系。 - - ```Shell - vim docker-compose.yml - ``` + 接下来,我们要通过docker-compose.yml文件来创建三个容器并指明容器之间的依赖关系。 ```YAML version: '3' services: - - product-service: - build: ./product-service + api-server: + build: ./myapp ports: - '8000:8000' links: - redis-master - - web-site: + web-server: image: nginx ports: - '80:80' volumes: - - ./web-site:/usr/share/nginx/html - + - ./html:/usr/share/nginx/html redis-master: image: redis expose: - '6379' ``` - 有了这个YAML文件,我们就可以使用`docker-compose`命令来创建和管理这三个容器,其命令如下所示。 + 有了这个YAML文件,我们就可以使用`docker-compose`命令来创建容器运行项目,其命令如下所示: ```Shell - docker-compose up + [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 ``` - ``` - Creating network "composeapp_default" with the default driver - Building product-service - Step 1/5 : FROM python:3 - ---> e497dabd8450 - Step 2/5 : ADD . /root/product-service - ---> fbe62813d595 - Removing intermediate container 6579e845565a - Step 3/5 : WORKDIR /root/product-service - ---> 3a722675e3b1 - Removing intermediate container 57fc490436ce - Step 4/5 : RUN pip install -r requirements.txt - ---> Running in cadc2d0c1b9b - ... ... - ---> fc747fc11f4a - Removing intermediate container cadc2d0c1b9b - Step 5/5 : CMD python api.py - ---> Running in ecbbd2a69906 - ---> 637e760f2e5b - Removing intermediate container ecbbd2a69906 - Successfully built 637e760f2e5b - WARNING: Image for service product-service was built because it did not already exist. To rebuild this image you must use `docker-compose build` or `docker-compose up --build`. - Creating composeapp_redis-master_1 ... done - Creating composeapp_web-site_1 ... done - Creating composeapp_product-service_1 ... done - Attaching to composeapp_redis-master_1, composeapp_web-site_1, composeapp_product-service_1 - ... ... + 要停止容器的运行,可以使用下面的命令。 + + ```Shell + docker-compose down ``` +#### Kubernetes + +实际的生产环境中常常需要部署和管理多个协同工作的容器,docker compose解决了多容器创建和管理的问题,但是实际项目中,我们还需要Kubernetes(以下都简称为K8S)来提供一个跨主机集群的容器调度平台。K8S可以进行自动化容器的部署、扩展和操作,从而提供以容器为中心的基础架构。该项目是谷歌在2014年启动的项目,建立在谷歌公司十余年运维经验的基础之上,而且谷歌自己的应用也是运行在容器上的。 + diff --git a/Day91-100/94.网络API接口设计.md b/Day91-100/94.网络API接口设计.md index 159017e..574c0a7 100644 --- a/Day91-100/94.网络API接口设计.md +++ b/Day91-100/94.网络API接口设计.md @@ -140,5 +140,5 @@ API接口返回的数据通常都是**JSON**或**XML**格式,我们这里不 -> 提示:如果没有接口文档撰写经验,可以使用在线接口文档编辑平台RAP2或YAPI来进行接口文档撰写,也可以参考我的[《方天下(租房项目)接口文档》](../番外篇/方天下(租房项目)接口文档.md)来了解如何撰写接口文档。 +> 提示:如果没有接口文档撰写经验,可以使用在线接口文档编辑平台RAP2或YAPI来进行接口文档撰写,也可以参考我的[《FTX(租房项目)接口文档》](../番外篇/FTX(租房项目)接口文档.md)来了解如何撰写接口文档。 diff --git a/Day91-100/98.项目部署上线和性能调优.md b/Day91-100/98.项目部署上线和性能调优.md index 8b18962..bc00e5e 100644 --- a/Day91-100/98.项目部署上线和性能调优.md +++ b/Day91-100/98.项目部署上线和性能调优.md @@ -432,8 +432,9 @@ http { location / { proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-For $remote_addr; + # proxy_set_header X-Real-IP $remote_addr; + # proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_buffering off; proxy_pass http://fangtx; } @@ -798,7 +799,7 @@ class MasterSlaveRouter(object): [program:celery] ; Set full path to celery program if using virtualenv - command=/root/project/venv/bin/python manage.py celery -A fangtx worker + command=/root/project/venv/bin/celery -A fangtx worker user=root numprocs=1 stdout_logfile=/var/log/supervisor/celery.log diff --git a/番外篇/为什么我选择了Python.md b/番外篇/我为什么选择了Python.md similarity index 98% rename from 番外篇/为什么我选择了Python.md rename to 番外篇/我为什么选择了Python.md index d09ca7a..88973bd 100644 --- a/番外篇/为什么我选择了Python.md +++ b/番外篇/我为什么选择了Python.md @@ -1,4 +1,4 @@ -## 为什么我选择了Python +## 我为什么选择了Python 目前,Python语言的发展势头在国内国外都是不可阻挡的,Python凭借其简单优雅的语法,强大的生态圈从众多语言中脱颖而出,如今已经是稳坐编程语言排行榜前三的位置。国内很多Python开发者都是从Java开发者跨界过来的,我自己也不例外。我简单的跟大家交代一下,我为什么选择了Python。