docker是一种虚拟化技术,可以在内核层隔离资源。因此对于上层应用而言,采用docker技术可以达到类似于虚拟机的沙盒环境。这大大简化了应用部署,让运维人员无需陷入无止境繁琐的依赖环境及系统配置中;另一方面,容器技术也可以充分利用硬件资源,做到资源共享。

本文将采用docker技术部署一个简单的nodejs应用,它包括一个简单的前置网关nginx、redis服务器以及业务服务器。同时使用dockerfile配置特定镜像,采用docker-compose进行容器编排,解决依赖、网络等问题。

docker基础

本文默认机器已安装docker环境,即可以使用docker和docker-compose服务,如果本地没有安装,则参考:

  1. 安装docker及docker-compose,可参考 Install Docker Compose
  2. docker compose 技术可以查看官方文档 Docker Compose

docker源

默认docker采用官方镜像,国内用户下载镜像速度较慢,为了更好的体验,建议切换源。
OSX系统通过添加 ~/.docker/daemon.json文件,

{"registry-mirrors": ["http://f1361db2.m.daocloud.io/"]
}

即可,镜像源地址可替换,随后重启docker服务即可。

linux系统通过修改 /etc/docker/daemon.josn文件,一样可以替换源。

docker简单操作

源切换完毕之后,就可以尝试简单的容器操作。
首先,运行一个简单的容器:

docker run -it node:8-slim node

run命令,根据某个版本的node镜像运行容器,同时执行 “node”命令,进入node命令行交互模式。

docker run -d node:8-slim node

执行 -d 选项,让容器以daemon进程运行,同时返回容器的hash值。根据该hash值,我们可以通过命令行进入运行的容器查看相关状态:

docker exec -it hashcode bash

hashcode可以通过

docker ps -l

找到对应容器的hashcode

关于镜像的选择以及版本的确定,可以通过访问官方 https://hub.docker.com/ 搜索,根据结果寻找 official image使用,当然也可根据下载量和star数量进行选择。

对于镜像的tag,则根据业务需求进行判断是否需要完整版的系统。如nodejs镜像,仅仅需要node基础环境而不需要其他的系统预装命令,因此选择了 node:<version>-slim 版本。

Dockerfile

从源下载的镜像大多数不满足实际的使用需求,因此需要定制镜像。镜像定制可以通过运行容器安装环境,最后提交为镜像:

docker run -it node:8-slim bash
root@ff05391b4cf8:/# echo helloworld > /home/text
root@ff05391b4cf8:/# exit
docker commit ff05391b4cf8 node-hello

然后运行该镜像即可。

另一种镜像定制可以通过Dockerfile的形式完成。Dockerfile是容器运行的配置文件,每次执行命令都会生成一个镜像,直到所有环境都已设置完毕。

Dockerfile文件中可以执行命令定制化镜像,如 “FROM、COPY、ADD、ENV、EXPOSE、RUN、CMD”等,具体dockerfile的配置可参考相关文档。

Dockerfile完成后,进行构建镜像:

docker build -t node:custom:v1 .

镜像构建成功后即可运行容器。

docker-compose

关于docker-compose,将在下文示例中进行说明。

示例:搭建nodejs应用

本文所有代码已开源至github

docker-compose.yml

在docker-compose.yml中配置相关服务节点,同时在每个服务节点中配置相关的镜像、网络、环境、磁盘映射等元信息,也可指定具体Dockerfile文件构建镜像使用。

version: '3'
services:nginx:image: nginx:latestports:- 80:80restart: always  volumes:- ./nginx/conf.d:/etc/nginx/conf.d- /tmp/logs:/var/log/nginxredis-server:image: redis:latestports:- 6479:6379restart: alwaysapp:build: ./volumes:- ./:/usr/local/apprestart: always  working_dir: /usr/local/appports:- 8090:8090command: node server/server.jsdepends_on:- redis-serverlinks:- redis-server:rd

redis服务器

首先搭建一个单节点缓存服务,采用官方提供的redis最新版镜像,无需构建。

version: '3'
services:redis-server:image: redis:latestports:- 6479:6379restart: always

关于version具体信息,可参考Compose and Docker compatibility matrix找到对应docker引擎匹配的版本格式。
在services下,创建了一个名为 redis-server 的服务,它采用最新的redis官方镜像,并通过宿主机的6479端口向外提供服务。并设置自动重启功能。

此时,在宿主机上可以通过6479端口使用该缓存服务。

web应用

使用node.js的koa、koa-router可快速搭建web服务器。在本节中,创建一个8090端口的服务器,同时提供两个功能:1. 简单查询单个key的缓存 2. 流水线查询多个key的缓存

docker-compose.yml

services:app:build: ./volumes:- ./:/usr/local/apprestart: always  working_dir: /usr/local/appports:- 8090:8090command: node server/server.jsdepends_on:- redis-serverlinks:- redis-server:rd

此处创建一个app服务,它使用当前目录下的Dockerfile构建后的镜像,同时通过 volumes 配置磁盘映射,将当前目录下所有文件映射至容器的/usr/local/app,并制定为运行时目录;同时映射宿主机的8090端口,最后执行node server/server.js命令运行服务器。

通过depends_on设置app服务的依赖,等待 redis-server 服务启动后再启动app服务;通过links设置容器间网络连接,在app服务中,可通过别名 rd 访问redis-server。

Dockerfile

FROM node:8-slim
COPY ./ /usr/local/app
WORKDIR /usr/local/app
RUN npm i --registry=https://registry.npm.taobao.org
ENV NODE_ENV dev
EXPOSE 8090  

指定的Dockerfile则做了初始化npm的操作。

web-server sourcecode

const Koa = require('koa');
const Router = require('koa-router');
const redis = require('redis');
const { promisify } = require('util');let app = new Koa();
let router = new Router();
let redisClient = createRedisClient({// ip为docker-compose.yml配置的redis-server别名 rd,可在应用所在容器查看dns配置ip: 'rd',port: 6379,prefix: '',db: 1,password: null
});function createRedisClient({port, ip, prefix, db}) {let client = redis.createClient(port, ip, {prefix,db,no_ready_check: true});client.on('reconnecting', (err)=>{console.warn(`redis client reconnecting, delay ${err.delay}ms and attempt ${err.attempt}`);});client.on('error', function (err) {console.error('Redis error!',err);});client.on('ready', function() {console.info(`redis初始化完成,就绪: ${ip}:${port}/${db}`);});return client;
}function execReturnPromise(cmd, args) {return new Promise((res,rej)=>{redisClient.send_command(cmd, args, (e,reply)=>{if(e){rej(e);}else{res(reply);}});});
}function batchReturnPromise() {return new Promise((res,rej)=>{let b = redisClient.batch();b.exec = promisify(b.exec);res(b);});
}router.get('/', async (ctx, next) => {await execReturnPromise('set',['testkey','helloworld']);let ret = await execReturnPromise('get',['testkey']);ctx.body = {status: 'ok',result: ret,};
});router.get('/batch', async (ctx, next) => {await execReturnPromise('set',['testkey','helloworld, batch!']);let batch = await batchReturnPromise();for(let i=0;i < 10;i++){batch.get('testkey');}let ret = await batch.exec();ctx.body = {status: 'ok',result: ret,};
});app.use(router.routes()).use(router.allowedMethods()).listen(8090);

需要注意的是,在web服务所在的容器中,通过别名 rd 访问缓存服务。

此时,运行命令 docker-compose up后,即可通过 http://127.0.0.1:8090/ http://127.0.0.1:8090/batch 访问这两个缓存服务。

转发

目前可以通过宿主机的8090端口访问服务,为了此后web服务的可扩展性,需要在前端加入转发层。实例中使用nginx进行转发:

services:nginx:image: nginx:latestports:- 80:80restart: always  volumes:- ./nginx/conf.d:/etc/nginx/conf.d- /tmp/logs:/var/log/nginx

采用最新版的nginx官方镜像,向宿主机暴露80端口,通过在本地配置nginx的抓发规则文件,映射至容器的nginx配置目录下实现快速高效的测试。

运行与扩展

默认单节点下,直接运行

docker-compose up -d

即可运行服务。

如果服务节点需要扩展,可通过

docker-compose up -d --scale app=3

扩展为3个web服务器,同时nginx转发规则需要修改:

upstream app_server { # 设置server集群,负载均衡关键指令server docker-web-examples_app_1:8090; # 设置具体server,server docker-web-examples_app_2:8090;server docker-web-examples_app_3:8090;
}server {listen 80;charset utf-8;location / {proxy_pass http://app_server;proxy_set_header Host $host:$server_port;proxy_set_header X-Forwarded-Host $server_name;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;}
}

app_server内部的各个服务器名称为docker-web-examples_app_1,format为“${path}_${service}_${number}”,

即第一部分为 docker-compose.yml所在目录名称,如果在根目录则为应用名称;
第二部分为扩展的服务名;
第三部分为扩展序号

通过设置nginx的配置的log_format中upstream_addr变量,可观察到负载均衡已生效。

http{log_format  main  '$remote_addr:$upstream_addr - $remote_user [$time_local] "$request" ''$status $body_bytes_sent "$http_referer" ''"$http_user_agent" "$http_x_forwarded_for"';
}

参考

docker官方文档

docker-compose.yml 配置文件编写详解

Dockerfile实践

一步步学会用docker部署应用(nodejs版)相关推荐

  1. docker部署项目 入门版

    1 通用 1.1 权限 开启挂载目录权限 #开启目录权限 chmod a+rwx /usr/local/test#开启docker挂载权限 chmod a+rw /var/run/docker.soc ...

  2. Docker部署免安装版tomcat+mysql+其它乱七八糟软件(包含解释为什么这样安装及同理安装的其他方式)

    前言:在学习使用Docker中,发现所有Docker案例都是直接就开始下载(pull)镜像然后命令叭叭叭的,对于刚接触linux系统和docker的完全看不懂,所以写了此份教程作为记录. 说明:因查找 ...

  3. docker部署redis集群_Docker部署Redis集群----第九节(docker-redis哨兵集群“轮询分流”篇实例一)...

    到此,我们的 Redis 哨兵集群就结束了,本篇章是第九篇章,也是使用集群方式的实例一来实现php的轮询分流. 1.准备工作: 在我们上一篇章实现redis 哨兵集群的基础上开始,在服务器上部署php ...

  4. 超实用,一口气学会 Centos/Docker/Nginx/Node/Jenkins 等基础操作

    作者:ask_the_sky https://juejin.cn/post/6951684431597797389 服务器作为开发的一环,并且现在非常多的商业公司部署在生产环境上的服务器都是CentO ...

  5. 从零实操基于WSL2 Docker部署Asp.Net Core项目

    前言 平日在公司里都是基于阿里Teambition中的飞流进行Docker部署Api项目或服务,已经习惯了那一套成熟的操作流程,开发和部署确实快捷方便,但是还没在自己的电脑上进行操作过,特别是Wind ...

  6. Docker+Jenkins持续集成环境(2)使用docker+jenkins构建nodejs前端项目

    Docker+Jenkins持续集成环境(2)使用docker+jenkins构建nodejs前端项目 前文使用Docker搭建Jenkins+Docker持续集成环境我们已经搭建了基于docker+ ...

  7. [转]Docker部署Django由浅入深系列(下): 八步部署Django+Uwsgi+Nginx+MySQL+Redis

    在上篇教程中,我们手动构建了两个容器,一个容器放Django + Uwsgi,另一个容器放Nginx,成功部署了一个简单的Django项目.然而在实际的生产环境中,我们往往需要定义数量庞大的 dock ...

  8. 如何将pytorch检测模型通过docker部署到服务器

    向AI转型的程序员都关注了这个号???????????? 人工智能大数据与深度学习  公众号:datayx 本文记录下如何使用docker部署pytorch文本检测模型到服务器, .镜像文件也上传到d ...

  9. Docker部署Django由浅入深系列(中): 双容器部署Django + Uwsgi + Nginx

    本文是使用Docker部署Django由浅入深系列的中篇,我们将构建两个容器,一个容器放Django + Uwsgi,另一个容器放Nginx.我们将了解不同容器间是如何通信的,并学会正确配置uwsgi ...

最新文章

  1. 9月——都已经9月了还不好好刷题?。。
  2. 【效率】神器工具:新一代多系统启动 U 盘装机解决方案
  3. 【AI超级美发师】深度学习算法打造染发特效(附代码)
  4. 一份详尽的IPC$入侵资料
  5. 从入门到放弃,C++ 真这么难?
  6. 几个SpringCloud常见面试题及答案
  7. http协议实现web服务器,http协议实现web服务器
  8. requestAnimationFrame 优化Web动画
  9. MyBatis 拦截器(4)
  10. Vue-Socket.io
  11. flash文件怎么打开 怎么将swf格式转换成avi视频
  12. paraview:python脚本
  13. 计算机电脑成像,计算机能重现你所看到的吗?
  14. PS 基础知识 .atn文件如何使用
  15. TBox、E-Call、B-Call、I-Call是什么?
  16. PCA9685--16路 PWM模块舵机驱动板--STM32 IIC接口模块
  17. 在U盘下安装ubuntu系统,从U盘启动Linux系统
  18. 三天写完毕业论文——经验分享
  19. 2D和3D版本的重力游戏
  20. HTTP请求错误码大全

热门文章

  1. ZigBee中的技术问题以及解决方案
  2. 又是一年1024(2019)
  3. 高斯衰减python实现
  4. DNSPod十问侯家文:如何为中小企业的网络安全保驾护航?
  5. T1114 白细胞计数(信息学一本通C++)
  6. SD卡变成RAW格式怎么办?SD卡RAW格式的解决办法
  7. 读书有益——》民间治咳偏方
  8. 关于Unity中天空盒的使用
  9. SCAU程序设计在线实训平台_实验_数据结构_实验4
  10. redis 数据删除策略和逐出算法