最近工作需要,开发时需要用到 Docker。这篇文章从零开始演示几个 Demo,如果你之前没接触过 Docker,可以一步步跟着操作,加深对 Docker 的理解。
Docker 能解决什么问题
(我自己的亲身经历:很早以前开发前端项目,我需要在本地搭建 LEMP 环境,照着教程捣鼓好几天,一行前端代码都没有写。后来还因为一些“莫名其妙”的问题,反反复复重装了好几次。)
而使用了 Docker 之后呢,我们能通过配置文件一条命令快速构建环境,并且可以做到和其他服务隔离,互不影响,通过例子来讲解。
演示环境
概念:镜像 vs 容器
安装 Docker
brew cask install docker
其他环境安装 Docker 查看这里[1]。
安装完成之后,执行 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。
每次修改内容都需要手动进入容器,太过繁琐,并且上面提到了,对容器的直接修改不会持久保存,如果容器被删,数据也会跟着丢失。
(由于之前的 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 指令看这里[2]。
可以在本地构建一下,运行命令 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 指令看这里[3]。
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://pvweb1:3000 的请求就会到达第一个 Web 容器。
万事具备,let's compose up!
好了,现在访问 http://localhost:80。
到目前为止,我们已经把应用部署完成,每次访问 PV 数量自动加一,并且经过 Nginx 负载均衡,会随机打到不同的容器上面。
总结
相关链接:
https://yeasy.gitbooks.io/dockerpractice/install/
https://yeasy.gitbooks.io/dockerpractice/image/dockerfile/
https://yeasy.gitbooks.io/docker_practice/compose/
原文链接:https://juejin.im/post/5c2c69cee51d450d9707236e