通常说来 SPA 的项目咱们只要启一个静态文件 Server 就能够了,可是针对传统项目就不同了,一个项目会依赖不少服务端程序。以前咱们的开发模式是在一台开发机上部署开发环境,全部人都在这台开发机上使用 Samba 链接开发。老式开发是没什么问题的,可是前端由于引入了编译流程,增长了 Webpack 打包构建的行为,当多人共同开发的时候常常会由于内存爆满进程被杀致使打包失败。痛定思痛后为了解决这个问题,我决定将 Docker 引入咱们的开发环境,经过将开发环境本地化来解决这个问题,因此有了本文。php
本文内容主要是归纳性,详情参照文尾连接1html
也可参考视频连接2前端
普通的 Web 服务通常都会依赖不少程序,例如 PHP, MySQL, Redis, Node 等等。正常状况下咱们会去手动安装这些程序来配置服务须要的环境,这样会带来几个问题:node
同一环境不一样的服务依赖同一个软件的不一样版本,经典的例如 python2 和 python3, 本地 Mac 上是 PHP7,可是服务只能支持 PHP5.6。python
同一环境不一样的服务可能会修改同一份文件,例如系统的配置,Nginx 的配置等,都会形成影响。nginx
同一服务在多台机器上部署须要手工操做,致使大量的人力成本浪费。git
这样逐个的安装软件实在是太麻烦了,因此你们就想干脆就直接把整个系统打包好放到机器上得了,因而就出现了虚拟机技术。这样作能保证系统环境的稳定以及重复的手工操做能够避免,可是也一样会带来一些问题:docker
打包后的虚拟机文件包含系统镜像因此特别大。npm
打包后的虚拟机文件包含系统镜像因此服务须要等待系统启动成功以后才能启动。json
打包过程没法实现自动化。
针对第三点,后来出现了 Vagrant 使用 vagrantfile 的形式将镜像构建脚本化从而实现自动化的功能,不过其它两点没有解决。因此后来就出来了系统之上的进程级别虚拟化技术 —— Docker。它为咱们带来了如下几个优势:
不须要打包系统进镜像因此体积很是小
不须要等待虚拟系统启动因此启动快速资源占用低
沙箱机制保证不一样服务之间环境隔离
Dockerfile 镜像构建机制让镜像打包部署自动化
Docker hub 提供镜像平台方便共享镜像
如下是 VM 和 Docker 技术的具体区别,能够看到 VM 是打包了 Guest OS 进入镜像中的,而 Docker 是直接基于宿主系统虚拟化的实例。
Docker 支持 Windows/Linux/Mac/AWS/Azure 多种平台的安装,其中 Windows 须要 Win10+,Mac 须要 EI Captain+。Docker 是一个 C/S 架构的服务,安装好 docker 以后须要启动 docker 软件后才能使用 docker 命令。
Docker 主要有 Dockerfile, Image, Container, Repository 四个基本概念。经过 Dockerfile 咱们能够生成 Docker Image(镜像)。本身制做的镜像能够上传到 Docker hub 平台,也能够从平台上拉去咱们须要的镜像。当镜像拉到本地以后,咱们就能够实例化这个镜像造成一个 Container(实例) 了。一个简单的镜像启动的命令是:
$ docker run [组织名称]/<镜像名称>:[镜像标签]`
其中除了镜像名称,其它的都是可选参数。组织名称不填默认为library,镜像标签不填则默认为latest。例如经典的启动一个 Hello World 镜像的过程以下:
能够看到当我实例化hello-world这个镜像的时候,docker 发现本地没有这个镜像会先去 Docker hub 远端拉取镜像,如刚才说的,默认是latest标签。拉取后就会实例化执行入口命令了。咱们除了可使用 Docker hub 查找咱们须要的镜像以外,也可使用docker search命令来查找。16年的一篇文章③显示,Docker hub 上的镜像包总量已经超过40万了,而且以每周4-5k的速度增加着。
下面咱们就来看看如何运行一个 Nginx 容器实例:
$ docker run -d --rm -p 8080:80 -v "$PWD/workspace":/var/www/hello.world -v "$PWD/hello.world.conf":/etc/nginx/conf.d/hello.world.conf nginx
使用docker run命令就能启动一个实例了,其中-p表示将本机的 8080 端口映射到镜像实例内的 80 端口,而-v表示将本地的$PWD/workspace文件夹映射到镜像实例里的/var/www/hello.world文件夹,后面的同理。最后再指定一下镜像名称,就能完成一次 Nginx 实例的启动了。此时访问http://hello.world:8080便可看到效果。
注:千万不要在容器实例中存储内容,实例销毁时实例内的全部内容都会被销毁,下次启动的时候又是全新的实例,内容不会保存下来。若是须要存储服务须要使用挂载卷或者外部存储服务。
Dockerfile 是 Docker 比较重要的概念。它是 Docker 建立镜像的核心,它的出现给 Docker 提供了两大好处:
文本化的镜像生成操做让其方便版本管理和自动化部署
每条命令对应镜像的一层,细化操做后保证其可增量更新,复用镜像块,减少镜像体积
Dockerfile 的一些编写规则主要以下:
使用#来注释
FROM 指令告诉 Docker 使用哪一个镜像做为基础
RUN 开头的指令会在建立中运行,好比安装一个软件包
COPY 指令将文件复制进镜像中
WORKDIR 指定工做目录
CMD/ENTRYPOINT 容器启动执行命令
RUN 和 CMD/ENTRYPOINT 都是执行命令,区别在于 RUN 是在镜像构建过程当中执行的,而 CMD/ENTRYPOINT 是在镜像生成实例的时候执行的,相似于 C/C++ 语言的头文件的正常代码的区别。并且后者在一个 Dockerfile 文件中只能有一个存在。CMD/ENTRYPOINT 的区别除了在写法上有区别以外,还有在docker run命令后增长 CMD 参数的状况下有区别(CMD会被复写)。通常建议使用 ENTRYPOINT 会更方便点。一个简单的 Node 命令行脚本的 Dockerfile 文件以下:
FROM mhart/alpine-node:8.9.3 LABEL maintainer="lizheming <i@imnerd.org>" org.label-schema.name="Drone Wechat Notification" org.label-schema.vendor="lizheming" org.label-schema.schema-version="1.1.0" WORKDIR /wechat COPY package.json /wechat/package.json RUN npm install --production --registry=https://registry.npm.taobao.org COPY index.js /wechat/index.js ENTRYPOINT [ "node", "/wechat/index.js" ]
这里我认为依赖是比较固定的,没有代码修改那么频繁,因此将其提早了。最终保证了因此越稳定的变化的命令至于上层,保证了每层打包出来的 Layer 可以尽量的复用,而不会徒增镜像的大小。最后咱们使用以下命令就能够完成一个 Docker 镜像的构建了:
$ docker build lizheming/drone-wechat:latest
参数和docker run是同样的。构建完成以后就能够开心的 push 到 Docker hub 上啦~
以上咱们说了下如何启动一个服务,可是咱们都明白一个完整的项目确定是不止依赖一个服务的,而 Docker 镜像的 ENTRYPOINT 只能设置一个,因此难道咱们要使用docker run命令手动建立 N 个容器实例吗?为了解决这个问题,Docker Compose 就瞬时出现了。Docker Compose 是一款容器编排程序,使用 YAML 配置的形式将你须要启动的容器管理起来,免去咱们须要屡次执行docker run命令的烦恼。
Docker Compose 是使用 Python 开发的,它的安装很是的简单,直接pip install docker-compose就行了。安装完成以后分别使用up和stop命令能够启动和中止服务。一个简单的 docker-compose.yaml 配置文件大概以下:
version: "2" services: nginx: depends_on: - "php" image: "nginx:latest" volumes: - "$PWD/src/docker/conf:/etc/nginx/conf.d" - "$PWD:/home/q/system/m_look_360_cn" ports: - "8082:80" container_name: "m.look.360.cn-nginx" php: image: "lizheming/php-fpm-yaf" volumes: - "$PWD:/home/q/system/m_look_360_cn" container_name: "m.look.360.cn-php"
Docker Compose 的另一个好处就是可以帮咱们处理容器的依赖关系,在每一个容器中会将容器的 IP 和服务的名称使用 hosts 的方式绑定,这样咱们就能在容器中直接使用服务名称来接入对应的容器了。例以下面这个 Nginx 配置中的php:9000就是利用了这个原理。
server { listen 80; server_name dev.m.look.360.cn; charset utf-8; root /home/q/system/m_look_360_cn/public; index index.html index.htm index.php; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # location ~ .php$ { fastcgi_pass php:9000; #fastcgi_pass unix:/tmp/fcgi.sock; fastcgi_index index.php; } }
基于 Docker 容器虚拟化技术除了以上说的解决部署环境以外,还有一些其它的优势,例如:
基于 Docker 的 CI 持续集成和 CD 持续支付
基于 Kubernetes, Docker Swarm 的集群弹性扩容和缩容
CI/CD 对于如今的敏捷开发是很是重要的,自动化任务帮助咱们节省不少没必要要的开发时间浪费,具体可查看连接④。而 k8s 和 Docker Swarm 带来的弹性扩容和缩容让业务不在为流量问题而头疼。经过监控报警设置当出现峰值的时候自动扩容抗压,当出现低谷的时候自动去除多余的容器来节省成本,同时也将多余的资源给其它服务使用。
什么是 Docker ?https://cloud.tencent.com/developer/article/1005172
Docker 从入门到实践 https://yeasy.gitbooks.io/docker_practice/
Docker compose 详解 https://www.jianshu.com/p/2217cfed29d7
深刻浅出Docker https://www.kancloud.cn/infoq/docker/79768
①https://ppt.baomitu.com/d/025d62c6
②http://cloud.live.360vcloud.net/theater/play?roomid=2321
③https://medium.com/microscaling-systems/how-many-public-images-are-there-on-docker-hub-bcdd2f7d6100
④https://imnerd.org/drone.html