9102 年了,学点 Docker 知识

640?wx_fmt=png


最近工作需要,开发时需要用到 Docker。这篇文章从零开始演示几个 Demo,如果你之前没接触过 Docker,可以一步步跟着操作,加深对 Docker 的理解。
Docker 能解决什么问题

640?wx_fmt=png


无论你所处的公司大或小,多多少少都遇到开发环境和生产环境不一致的问题。有些开发者用 Windows,有些开发者用 Mac,而生产环境可能用的是 Linux,同时跑着多个应用,每个应用依赖的 Node 版本版本不一致,不同服务可能还占用相同的端口。于是我们常常听到这样的疑问:“本地明明是好的啊,为什么到线上就不行了?”
(我自己的亲身经历:很早以前开发前端项目,我需要在本地搭建 LEMP 环境,照着教程捣鼓好几天,一行前端代码都没有写。后来还因为一些“莫名其妙”的问题,反反复复重装了好几次。)
而使用了 Docker 之后呢,我们能通过配置文件一条命令快速构建环境,并且可以做到和其他服务隔离,互不影响,通过例子来讲解。
演示环境

640?wx_fmt=png


这篇文章执行的环境是 macOS 10.14.1,Docker version 18.09.0。
概念:镜像 vs 容器

640?wx_fmt=png


镜像是以一些列历史操作叠加而成的,对镜像的每一次操作都会产生新的只读层,比方说你往容器写入内容,提交,再把它移除,则会产生两个历史只读层,有点像 git commits,而容器可以理解问为镜像历史层 + 可写层的可运行系统,在可写层做任何操作都没问题,不提交的话,容器被删除后相应的改动也会丢失,有点像 Git 暂存区。(个人理解,不一定对) 
640?wx_fmt=jpeg
安装 Docker

640?wx_fmt=png


Mac 安装 Docker:
 
  
  1. brew cask install docker


其他环境安装 Docker 查看这里[1]。
安装完成之后,执行 hello-world 试一下。
 
  
  1. $ docker run hello-world

 
  
  1. Unable to find image 'hello-world:latest' locally

  2. latest: Pulling from library/hello-world

  3. 1b930d010525: Pull complete

  4. Digest: sha256:2557e3c07ed1e38f26e389462d03ed943586f744621577a99efb77324b0fe535

  5. Status: Downloaded newer image for hello-world:latest


  6. Hello from Docker!

  7. This message shows that your installation appears to be working correctly.


这条命令会连接本地 Docker 服务,Docker 服务检测到本地没有 hello-world 镜像,于是去 Docker 镜像市场下载这个镜像,然后创建新的容器,运行特定的命令,输出 Hello from Docker!
这篇文章不讲解 Docker 有哪些基础命令,直接从案例入手。
启动 Nginx 服务

640?wx_fmt=png


 
  
  1. docker run -d -p 80:80 --restart=always  nginx:latest


参数说明:
  • run 启动某个镜像

  • -d 让容器在后台运行

  • -p 指定端口映射,宿主机的80端口映射到容器的80端口

  • --restart 重启模式,设置 always,每次启动 Docker 都会启动 Nginx 容器。

640?wx_fmt=png
由于我本地没有 nginx:latest 的镜像,同样会先去镜像市场下载。启动完成打开 http://localhost:80 就能立马看到 Nginx 的欢迎页面。 
640?wx_fmt=png
如果想修改欢迎页面,可以进入到容器内修改页面。
 
  
  1. docker exec -it 4591552a4185 bash


参数说明:
  • exec 对容器执行某些操作

  • -it 让容器可以接受标准输入并分配一个伪tty

  • 4591552a4185 是刚刚启动的 Nginx 容器唯一标记

  • bash 指定交互的程序为 bash

640?wx_fmt=png
Nginx 默认文件路径是 /usr/share/nginx/html/index.html ,直接用 echo 写入内容即可。
 
  
  1. echo '<h1>Hello Docker<h1/>' > /usr/share/nginx/html/index.html


ctrl + D 退出容器,重新访问 localhost:80 即可看到 Hello Docker。 
640?wx_fmt=png
每次修改内容都需要手动进入容器,太过繁琐,并且上面提到了,对容器的直接修改不会持久保存,如果容器被删,数据也会跟着丢失。
(由于之前的 demo 已经占用了 80 端口,咱们先 kill 掉它。)
 
  
  1. docker kill 4591552a4185


Docker 提供数据挂载的功能,即可以指定容器内的某些路径映射到宿主机器上,修改命令,添加 -v 参数,启动新的容器。
 
  
  1. 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。 
640?wx_fmt=png
再次打开 http://localhost:80,同样能看到 Hello Docker。  640?wx_fmt=png
接着我们来用 Node + Redis + Docker 做一个 PV 展示的 demo。
运行命令:
 
  
  1. docker run -d -p 6379:6379 -v ~/docker-demo/redis:/data redis:latest


启动一个 Redis 容器,并将数据持久化到 ~/docker-demo/redis 目录。(考虑性能,Redis 并不会实时写入数据到磁盘)
用 koa 启动一个 node server,并连接 Redis , 每次访问 / 都给计数器加一。
 
  
  1. const Redis = require('ioredis');

  2. const Koa = require('koa');

  3. const Router = require('koa-router');


  4. const router = new Router();

  5. const app = new Koa();


  6. const redis = new Redis(`redis://127.0.0.1:6379/0`);


  7. router.get('/', async (ctx, next) => {

  8.  await next();

  9.  await redis.incr('pv');

  10.  const current = await redis.get('pv');

  11.  ctx.body = `current pv: ${current}`;

  12. });


  13. app

  14.  .use(router.routes())

  15.  .use(router.allowedMethods());


  16. app.listen(3000);


访问 http://localhost:3000,就能看到输出结果。 
640?wx_fmt=png
推荐使用 medis 可视化查看 Redis 数据。 
640?wx_fmt=png
OK,开发环境完成功能开发,交付运维上线。我们假设生成环境会启动四个 Node 服务,一个 Redis 服务,和一个 Nginx 做负载。
这时候需要把我们的 Node 服务也构建成镜像,新增 Dockerfile 文件。
 
  
  1. # 基于最新的 node 镜像

  2. FROM node:latest

  3. # 复制当前目录下所有文件到目标镜像 /app/ 目录下

  4. COPY . /app/

  5. # 修改工作目录

  6. WORKDIR /app/

  7. # yarn 一下,安装依赖

  8. RUN ["yarn"]

  9. # 启动 node server

  10. ENTRYPOINT ["node", "index.js"]


更多 Dockerfile 指令看这里[2]。
可以在本地构建一下,运行命令 docker build . --tag=pv,然后通过 docker images 就能看到刚刚构建的新镜像。
继续往下走,编排一组容器,docker 官方提供了 docker-compose 工具。在项目目录下新增 docker-compose.yml 文件。
 
  
  1. # 使用 docker-compose 2.2 版本

  2. version: "2.2"

  3. # 定义 services

  4. services:

  5.  redis:

  6.    image: redis:latest

  7.    volumes:

  8.      - "~/docker-demo/pv/data/:/data/"


  9.  web:

  10.    # 放大4倍,也就会有四个 node server

  11.    scale: 4

  12.    build: .

  13.    # 新增环境变量

  14.    environment:

  15.      - REDIS_HOST=redis://redis:6379/0

  16.    # 依赖关系

  17.    depends_on:

  18.      - redis


  19.  nginx:

  20.    image: nginx:latest

  21.    depends_on:

  22.      - web

  23.      - redis

  24.    ports:

  25.      - 80:80

  26.    volumes:

  27.      - "./default.conf:/etc/nginx/conf.d/default.conf"


更多 docker-compsoe 指令看这里[3]。
service web 新增环境变量 REDIS_HOST=redis://redis:6379/0 是给 ioredis 链接用的,对应的要修改 js 文件。
 
  
  1. const redis = new Redis(process.env.REDIS_HOST);


redis://redis:6379/0 第一个 Redis 是协议,第二个 Redis 是 service host。service 之间可以通过 host 互相通信。
复制 Nginx 容器下的 default.conf 文件出来修改:
 
  
  1. upstream web {

  2.  server pv_web_1:3000;

  3.  server pv_web_2:3000;

  4.  server pv_web_3:3000;

  5.  server pv_web_4:3000;

  6. }


  7. server {

  8.    #...


  9.    location / {

  10.      proxy_pass http://web;

  11.    }


  12.    #...

  13. }


新增上游服务 Web,这里的 PV 是我的项目文件名,Web 是 docker-compose 文件中定义的 service name,1 - 4 则是 scale 出来 Docker 自动给定的序号。启动起来之后,Nginx 访问 http://pvweb1:3000 的请求就会到达第一个 Web 容器。
万事具备,let's compose up!
640?wx_fmt=gif
好了,现在访问 http://localhost:80。 
640?wx_fmt=gif
到目前为止,我们已经把应用部署完成,每次访问 PV 数量自动加一,并且经过 Nginx 负载均衡,会随机打到不同的容器上面。
总结

640?wx_fmt=png


这篇文章演示了和前端相关的一些 Docker 操作,从中我们可以看到其对于软件的开发,测试,部署都带来了极大的便利。文中的内容仅仅是冰山一角,示例也只能作为学习使用,请不要直接用在生产环境。同时部分内容是个人理解,没有权威性,深入学习 Docker ,你应该去看详细的文档。
相关链接:

  1. https://yeasy.gitbooks.io/dockerpractice/install/

  2. https://yeasy.gitbooks.io/dockerpractice/image/dockerfile/

  3. https://yeasy.gitbooks.io/docker_practice/compose/


原文链接:https://juejin.im/post/5c2c69cee51d450d9707236e


Kubernetes实战培训

640?


Kubernetes实战培训将于2019年3月8日在深圳开课,3天时间带你系统掌握Kubernetes,学习效果不好可以继续学习 本次培训包括:云原生介绍、微服务;Docker基础、Docker工作原理、镜像、网络、存储、数据卷、安全;Kubernetes架构、核心组件、常用对象、网络、存储、认证、服务发现、调度和服务质量保证、日志、监控、告警、Helm、实践案例等。
640?wx_fmt=png

猜你喜欢

转载自blog.csdn.net/M2l0ZgSsVc7r69eFdTj/article/details/87658928