内容简介:最近工作需要,开发时需要用到 Docker。这篇文章从零开始演示几个 Demo,如果你之前没接触过 Docker,可以一步步跟着操作,加深对 docker 的理解。无论你所处的公司大或小,多多少少都遇到开发环境和生产环境不一致的问题。有些开发者用 Windows,有些开发者用 Mac,而生产环境可能用的是 Linux,同时跑着多个应用,每个应用依赖的 node 版本版本不一致,不同服务可能还占用相同的端口。于是我们常常听到这样的疑问:“本地明明是好的啊,为什么到线上就不行了?”(我自己的亲身经历:很早以前开
最近工作需要,开发时需要用到 Docker。这篇文章从零开始演示几个 Demo,如果你之前没接触过 Docker,可以一步步跟着操作,加深对 docker 的理解。
多图,请尽量在:computer:上观看
Docker 能解决什么问题
无论你所处的公司大或小,多多少少都遇到开发环境和生产环境不一致的问题。有些开发者用 Windows,有些开发者用 Mac,而生产环境可能用的是 Linux,同时跑着多个应用,每个应用依赖的 node 版本版本不一致,不同服务可能还占用相同的端口。于是我们常常听到这样的疑问:“本地明明是好的啊,为什么到线上就不行了?”
(我自己的亲身经历:很早以前开发前端项目,我需要在本地搭建 LEMP 环境,照着教程捣鼓好几天,一行前端代码都没有写。后来还因为一些“莫名其妙”的问题,反反复复重装了好几次。)
而使用了 Docker 之后呢,我们能通过配置文件一条命令快速构建环境,并且可以做到和其他服务隔离,互不影响,通过例子来讲解。
演示环境
这篇文章执行的环境是 macOS 10.14.1, Docker version 18.09.0
概念:镜像 vs 容器
镜像是以一些列历史操作叠加而成的,对镜像的每一次操作都会产生新的只读层,比方说你往容器写入内容,提交,再把它移除,则会产生两个历史只读层,有点像 git commits ,而容器可以理解问为镜像历史层 + 可写层的可运行系统,在可写层做任何操作都没问题,不提交的话,容器被删除后相应的改动也会丢失,有点像 git 暂存区 。
(个人理解,不一定对)
安装 Docker
Mac 安装 docker
brew cask install docker 复制代码
其他环境安装 Docker 查看这里。
安装完成之后,执行 hello-world 试一下。
$ docker run hello-world 复制代码
Unable to find image 'hello-world:latest' locally latest: Pulling from library/hello-world 1b930d010525: Pull complete Digest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535 Status: Downloaded newer image for hello-world:latest Hello from Docker! This message shows that your installation appears to be working correctly. 复制代码
这条命令会连接本地 Docker 服务,Docker 服务检测到本地没有 hello-world 镜像,于是去 Docker 镜像市场下载这个镜像,然后创建新的容器,运行特定的命令,输出 Hello from Docker!
这篇文章不讲解 docker 有哪些基础命令,直接从案例入手。
启动 nginx 服务
docker run -d -p 80:80 --restart=always nginx:latest 复制代码
参数说明: run
启动某个镜像 -d
让容器在后台运行 -p
指定端口映射,宿主机的80端口映射到容器的80端口 --restart
重启模式,设置 always,每次启动 docker 都会启动 nginx 容器。
由于我本地没有 nginx:latest 的镜像,同样会先去镜像市场下载。启动完成打开http://localhost:80 就能立马看到 nginx 的欢迎页面。(对比以前,先在网上找教程,处理)
如果想修改欢迎页面,可以进入到容器内修改页面。
docker exec -it 4591552a4185 bash 复制代码
参数说明:
exec
对容器执行某些操作
-it
让容器可以接受标准输入并分配一个伪tty
4591552a4185
是刚刚启动的 nginx 容器唯一标记
bash
指定交互的程序为 bash
nginx 默认文件路径是 /usr/share/nginx/html/index.html
,直接用 echo 写入内容即可。
echo '<h1>Hello Docker<h1/>' > /usr/share/nginx/html/index.html 复制代码
ctrl + D 退出容器,重新访问 localhost:80 即可看到 Hello Docker。
每次修改内容都需要手动进入容器,太过繁琐,并且:point_up_2:提到了,对容器的直接修改不会持久保存,如果容器被删,数据也会跟着丢失。
(由于之前的 demo 已经占用了 80 端口,咱们先 kill 掉它。)
docker kill 4591552a4185 复制代码
Docker 提供数据挂载的功能,即可以指定容器内的某些路径映射到宿主机器上,修改命令,添加 -v
参数,启动新的容器。
docker run -d -p 80:80 -v ~/docker-demo/nginx-htmls:/usr/share/nginx/html/ --restart=always nginx:latest 复制代码
启动成功之后,docker 会帮你生成目录 ~/docker-demo/nginx-htmls
,现在里面什么都没有,添加个 index.html。
再次打开 http://localhost:80
, 同样能看到 hello docker。
接着我们来用 Node + Redis + Docker 做一个 PV 展示的 DEMO。
运行命令:
docker run -d -p 6379:6379 -v ~/docker-demo/redis:/data redis:latest 复制代码
启动一个 redis 容器,并将数据持久化到 ~/docker-demo/redis
目录。(考虑性能,redis 并不会实时写入数据到磁盘)
用 koa 启动一个 node server,并连接 redis , 每次访问 / 都给计数器加一。
const Redis = require('ioredis'); const Koa = require('koa'); const Router = require('koa-router'); const router = new Router(); const app = new Koa(); const redis = new Redis(`redis://127.0.0.1:6379/0`); router.get('/', async (ctx, next) => { await next(); await redis.incr('pv'); const current = await redis.get('pv'); ctx.body = `current pv: ${current}`; }); app .use(router.routes()) .use(router.allowedMethods()); app.listen(3000); 复制代码
访问 http://localhost:3000 ,就能看到输出结果。
推荐使用 medis 可视化查看 redis 数据。
OK,开发环境完成功能开发,交付运维上线。我们假设生成环境会启动四个 node 服务,一个 redis 服务,和一个 nginx 做负载。
这时候需要把我们的 node 服务也构建成镜像,新增 dockerfile 文件。
# 基于最新的 node 镜像 FROM node:latest # 复制当前目录下所有文件到目标镜像 /app/ 目录下 COPY . /app/ # 修改工作目录 WORKDIR /app/ # yarn 一下,安装依赖 RUN ["yarn"] # 启动 node server ENTRYPOINT ["node", "index.js"] 复制代码
更多 dockerfile 指令看这里
可以在本地构建一下,运行命令 docker build . --tag=pv
,然后通过 docker images
就能看到刚刚构建的新镜像。
继续往下走,编排一组容器,docker 官方提供了 docker-compose 工具。在项目目录下新增 docker-compose.yml
文件。
# 使用 docker-compose 2.2 版本 version: "2.2" # 定义 services services: redis: image: redis:latest volumes: - "~/docker-demo/pv/data/:/data/" web: # 放大4倍,也就会有四个 node server scale: 4 build: . # 新增环境变量 environment: - REDIS_HOST=redis://redis:6379/0 # 依赖关系 depends_on: - redis nginx: image: nginx:latest depends_on: - web - redis ports: - 80:80 volumes: - "./default.conf:/etc/nginx/conf.d/default.conf" 复制代码
更多 docker-compsoe 指令看这里
service web 新增环境变量 REDIS_HOST=redis://redis:6379/0 是给 ioredis 链接用的,对应的要修改 js 文件。
const redis = new Redis(process.env.REDIS_HOST); 复制代码
redis://redis:6379/0
第一个 redis 是协议,第二个 redis 是 service host。service 之间可以通过 host 互相通信。
复制 nginx 容器下的 default.conf 文件出来修改
upstream web { server pv_web_1:3000; server pv_web_2:3000; server pv_web_3:3000; server pv_web_4:3000; } server { #... location / { proxy_pass http://web; } #... } 复制代码
新增上游服务 web,这里的 pv 是我的项目文件名,web 是 docker-compose 文件中定义的 service name, 1 - 4 则是 scale 出来 docker 自动给定的序号。启动起来之后,nginx 访问 http://pv_web_1:3000
的请求就会到达第一个 web 容器。
万事具备,let's compose up!
好了,现在访问 http://localhost:80
到目前为止,我们已经把应用部署完成,每次访问 pv 数量自动加一,并且经过 nginx 负载均衡,会随机打到不同的容器上面。:tada::tada::tada:
总结
这篇文章演示了和前端相关的一些 docker 操作,从中我们可以看到其对于软件的开发,测试,部署都带来了极大的便利。文中的内容仅仅是冰山一角,更全面的学习可以看 Docker — 从入门到实践 这本书。
示例也只能作为学习使用,请不要直接用在生产环境。同时部分内容是个人理解,没有权威性,深入学习 Docker ,你应该去看详细的文档。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 肖仰华谈知识图谱:知识将比数据更重要,得知识者得天下
- 基础知识:css3核心知识整理
- 从知识工程到知识图谱全面回顾 | AI&Society
- 知识图谱发展的难点&构建行业知识图谱的重要性
- 《面试知识,工作可待:集合篇》:Java 集合面试知识大全
- 第四期知识与认知图谱:神经机器翻译也应该嵌入「知识」
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JSP 2.0技术手册
杜远君、林康司、林上杰 / 湖北教育出版社,电子工业出版社 / 2004-5-1 / 59.0
本书图文并茂,以丰富的实例为引导,全面介绍了主流的Java Web开发技术——JSP 2.0,重点介绍Java在展示层的两项重要技术:Java Servlet与JavaServer Pages。它们是最重要的Java核心技术。对这两项技术的深入了解,将有助于您未来对于JavaServer Faces(JSF)技术以及Java Web Services技术的学习。 本书分为三大部分,前......一起来看看 《JSP 2.0技术手册》 这本书的介绍吧!