docker快速部署node.js应用

最近研究了几天docker的快速部署,感觉很有新意,非常轻量级和方便,打算在公司推广一下,解放运维,省得每次部署一台新服务器都去跑安装脚本了,对于我们开发人员也是好事情,无需写太多重复的部署文档,直接将docker的images丢上服务器就可以运行了。

可能还有一些同学不了解docker这个项目,docker是由go语言编写的,一个快速部署的轻量级虚拟技术项目,他允许开发人员将自己的程序和运行环境一起打包,制作成一个docker的image(镜像),这样部署到服务器上,也只需要下载这个image就可以将程序跑起来,免去每次都安装各种依赖和环境的麻烦,还能够做到应用程序之间的隔离,因为我们公司部署的python程序,这样一来我也省去了每次都到新服务器配置 vitualenv了。

官网地址:http://www.docker.com/

如此看来docker非常适合做小巧的外包项目,免去每次都为客户配置一遍运行环境,费时费力。

一、安装:

安装很简单,直接进入下载页面,根据自己的操作系统下载相对应的安装包即可,下面说一下windows安装:

下载地址:https://docs.docker.com/installation/windows/

在安装docker时,会附带安装git和VirtualBox,所以可能安装时间稍微长一些,安装完毕重启系统,以管理员身份进入命令行,就可以使用进入linux虚拟机命令 “boot2docker” ,由于docker目前的镜像只针对linux,所以windows下面必须安装虚拟机才能使用。

第一次使用boot2docker start,会出现错误:

Failedtogetmachine"boot2docker-vm":machine doesnotexist

没关系,这是因为没有boot2docker iso的镜像所致,执行:

$ boot2docker init

就可以初始化镜像,耐心等待下载并安装完毕后,我们继续执行开启虚拟机。

$ boot2docker start

在windows下是无法直接使用cmd窗口来操作linux系统的,所以我们需要进入linux虚拟机来操作docker,执行:

$ boot2docker ssh

就可以进入linux虚拟机,如果要退出并关闭虚拟机,执行如下命令:

$exit$ boot2docker stop

调试时查看虚拟机ip地址,后面部署测试环境会用到:

$ boot2docker ip192.168.59.103

我们可以通过ssh的ip地址192.168.59.103,用户名 docker,密码 tcuser,登录到虚拟机中去。

如果不幸长时间无法init成功,说明镜像被GFW挡住了,手动去github上下载镜像,地址为:

https://github.com/boot2docker/boot2docker/releases

如果还是无法下载成功,我是好心人,把1.1.2版本的boot2docker.iso镜像丢到了百度云上:

http://pan.baidu.com/s/1c01qieG

下载完毕之后放到目录:

C:\Users\你的用户名\.boot2docker\boot2docker.iso

然后再执行

$ boot2docker init

二、下载镜像,安装环境

我们先执行如下命令,启动虚拟机:

$ boot2docker start2014/08/1821:22:41WaitingforVM to be started..............2014/08/1821:23:21Started.2014/08/1821:23:21Dockerclient doesnotrun onWindowsfornow.Pleaseuse2014/08/1821:23:21"boot2docker"ssh2014/08/1821:23:21to SSHintothe VM instead.

如果我们的物理机内存低于4G,那么跑这个boot2docker可能需要手动设置内存占用大小:

$ boot2docker start-m=512

我们利用命令就可以进入linux虚拟机了

$ boot2docker ssh##        .## ## ##      ==## ## ## ##      ===/""""""""""""""""\___/===~~~{~~~~~~~~~~~~~~~~/  ===- ~~~

\______ o          __/\    \        __/\____\______/_                _  ____    _            _||__  ___  ___||_|___ \ __||___  ___||_____ _ __|'_ \ / _ \ / _ \| __| __) / _` |/ _ \ / __| |/ / _ \ '__|||_)|(_)|(_)||_/__/(_||(_)|(__|<__/||_.__/\___/\___/\__|_____\__,_|\___/\___|_|\_\___|_|boot2docker:1.1.2master:740106c-ThuJul2403:24:10UTC2014

执行 docker 命令,会有一个命令列表,里面列出了所有 docker 支持的功能,列表如下:

Commands:attachAttachto a running container

buildBuildan imagefromaDockerfilecommitCreateanewimagefroma container's changes

... ...

下面会对一些常用的命令进行示例说明,我们可以通过如下命令,先查找centos的镜像,并把他下载下来

$ docker search centos#查找centos名字的镜像$ docker pull centos#下载官方纯净版本的centos镜像

在调用search命令时,你会看到有好多centos包,他们都是这样的/,这些不在根目录的镜像都是非官方的,是其他用户提交到docker hub上去的,耐心等待片刻我们就可以将centos的镜像拉下来了。

如果脸黑,镜像又不幸被墙,那么试试加上下面的hosts:

54.234.135.251get.docker.io54.234.135.251cdn-registry-1.docker.io

成功下载好centos镜像之后,我们可以利用如下命令来查看镜像列表:

$ docker images

REPOSITORY          TAG                IMAGE ID            CREATED

VIRTUAL SIZE

centos              centos6            b1bd49907d552weeks ago212.5MB

centos              centos7            b157b77b1a652weeks ago243.7MB

centos              latest              b157b77b1a652weeks ago243.7MB

接下来我们就利用centos7这个镜像输出一段 hello world

$ docker run b15/bin/echo'Hello world'Helloworld

注意这里的 b15,他表示centos7这个images的id,不用全部打全,只要保证输入的id前几位能找到唯一镜像即可,这点很赞。

稍微复杂一点的例子:

$ docker run-i-d-t b15/bin/sh-c"while true; do echo hello world; sleep 1; done"

-i表示同步container的stdin,-t表示同步container的输出,-d表示deamon,以后台启动这个container,执行这个container是永远不会停止的,每一秒钟都会输出hello world。

至于什么是container,container和image的关系我们下一段再说,列出镜像的历史:

$docker history image_name

三、安装环境

在开始第三段介绍之前,有必要说几个利用windows cmd窗口的小技巧。

1、如果想要使用标记选中功能,你会发现,当我们进入 boot2docker ssh 之后,鼠标对窗口的右键是无效的,所以想要利用标记选中窗口内的文字得这么弄:“点击左上角图标->编辑->标记”,这样就可以使用标记了

2、如果从其他地方复制了命令,但是窗口没有右键无法粘贴怎么办?用和1相同的办法:“点击左上角图标->编辑->粘贴”。

3、坑爹的windws如果命令太长,在boot2docker ssh里换行会错位的,在“点击左上角图标->属性->布局->屏幕缓冲区大小和窗口大小”的数值,保证长的命令也在一行内就没有问题了,注意要重启cmd窗口。

接下来我们简单说明一下image和container的关系,image顾名思义就是镜像的意思,我们把他理解为一个执行环境(env),当我们执行了docker run命令之后,dock就会根据当前的image创建一个新的container,container更像是一个操作或者程序运行的一个沙箱,他们互相独立,但是都运行在image创建的执行环境之上,根据上一段我们执行了2个run的任务,也就创建了2个独立的container,我们通过命令

$ docker ps#查看当前运行的container$ docker ps-a#查看所有的containerCONTAINER ID        IMAGE              COMMAND                CREATED            STATUS                      PORTS              NAMES

d1eca89869d0        centos:centos7/bin/sh-c'while tr  13 minutes ago      Exited (0) 7 minutes ago                                          distracted_mcclintock

0b71024c8a95        centos:centos7      /bin/sh -c 'whiletr15minutes agoExited(0)7minutes ago                        pensive_meitner79a488c9cfb6centos:centos7      console22minutes ago                                                      sick_babbage06f43c19d10acentos:centos7/bin/echo'Hello wor  25 minutes ago      Exited (0) 25 minutes ago                      berserk_einstein

我们可以分别利用命令对image和container进行删除

$ docker rm#删除container$ docker rmi#删除image$ docker rm`docker ps -a -q`#删除所有容器

这样执行一次run就创建一个container,势必会造成大量的无用的container,可能我不需要持久化保存container,如果在docker run命令加上 --rm=true 选项,那当这个container执行完毕,将自动自己删除,保证了container数量不会泛滥增长。

我们之前下载纯净版centos7是没有任何第三方软件的,包括wget,ping等命令都要通过yum工具来重新安装,我们当然不想每次都重新安装这些东西,我不仅希望要把一些常用的库安装到的image中去,同时还希望把程序运行的环境也安装进去,所以image更像是一个系统的模版。

你会发现,当你执行如下命令,wget命令时安装成功了,但是当你下次执行wget命令时,又会报错,说找不到这个命令,到底是怎么回事呢?

$ docker run-t b15 yum install-y wget#通过yum工具安装wget命令#安装完毕后,执行wget会报没有这个命令$ docker run-t b15 wget http://www.baidu.com2014/08/1815:42:19exec:"wget":executable filenotfoundin$PATH

为什么会出现这个问题呢?答案就是我们上面所说的那样,每次执行docker run都会去独立的创建一个新的container来执行程序,所以我们必须手动把这些对container的更改提交成一个新的image,才能够依据这个image执行wget操作。

我们先把当前所有的container都删除,然后直接登录到container的bash命令窗口中去,免得每次都去输入docker run了

$ docker run-t-i b15/bin/bash

bash-4.2#

这样我们进入了一个新的container,依据centos7作为模板,我们将要在其上面安装wget工具,直接执行

$ yum install-y wget

安装完毕之后,我们执行exit退出container

输入docker ps -a 我们找到刚才安装过wget工具的container ID,我们要把这个container重新做成一个新的image模版,这个模版将带wget命令。

$ docker ps-a

CONTAINER ID        IMAGE              COMMAND            CREATED            STATUS                    PORTS              NAMES26cc82ad29afcentos:centos7/bin/bash3minutes agoExited(0)5seconds ago                      desperate_mccarthy

我们执行如下命令,将一个安装过软件的container提交为一个image

$ docker commit26wget-centos760cd26c6ca1e753bf77aa913ed7b826767a678b75f6dd8421353f6c0899d3e5e

我们查看当前image镜像的列表:

wget-centos7        latest60cd26c6ca1e37seconds ago304.6MB

centos              centos6            b1bd49907d552weeks ago212.5MB

centos              centos7            b157b77b1a652weeks ago243.7MB

centos              latest              b157b77b1a652weeks ago243.7MB

你会发现多了一行我们刚才提交的 wget-centos7 的image镜像记录,现在我们执行这个镜像的wget命令,看看会不会报错

$ docker run-t-i--rm=true60wget http://www.baidu.com--2014-08-1815:54:55--http://www.baidu.com/Resolvingwww.baidu.com(www.baidu.com)...180.97.33.108,180.97.33.107Connectingto www.baidu.com(www.baidu.com)|180.97.33.108|:80...connected.HTTP request sent,awaiting response...200OK

执行完毕后,docker自动删除这个container,并且至今报错的wget命令无法找到也不会出错了。

第二种安装环境的办法,类似脚本安装,我们预先录入好一系列安装脚本,可以让docker帮我们依次执行这些安装脚本,然后生成image,例如有安装脚本Dockerfile:

# This is a commentFROM centos

MAINTAINER doublespoutRUN yum install-y wget

我们执行如下命令进行创建images

$ mkdir wget

$ cd wget

$ viDockerfile#将上面内容复制进去$ docker build-t="doublespout/wget"./#将看到安装脚本的执行输出,安装完成后,执行 docker images 就可以看到我们刚才创建的镜像了docker images

container 和 host 文件互相拷贝:

1、从container往host拷贝文件:

docker cp:/root/hello.txt.

2、从host往container里拷贝文件,比较麻烦一点,首先停止Contaitner(当然不停止也能拷贝)

docker stop

然后执行拷贝操作,执行完成之后就能看到Contaitner里有这个文件拉

#执行命令找到程序pidContainerID=$(docker inspect--format{{.Id}})cp/tmp/tmp.txt/var/lib/docker/aufs/mnt//tmp/

四、发布应用程序

我们根据上一段的步骤,手动将node.js环境装好container并且发布成image,并保存"app.js"文件到"/var/nodejs/app.js",文件内容为:

varhttp=require('http');http.createServer(function(req,res){res.writeHead(200,{'Content-Type':'text/plain'});res.end('Hello World\n');}).listen(1337);//注意这边不能和官网示例那样监听127.0.0.1console.log('Server running at http://0.0.0.0:1337/');

执行如下命令,运行container:

$ docker run-d-i-p--name=nodeapp1337:1337fa node/var/nodejs/app.js#32bac9ed8ba055c935bd641d23097a36a573a243ee942358fd74dc4140308bc6

其中fa是我创建的镜像id,这个值因人而异,name参数是给这个container取名字,必须是唯一的。

-p参数就是类似端口映射的功能,将主机的端口1337映射到contianer的1337端口,我们可以运行 docker ps,查看正在运行的container,打开浏览器,就能够看到 hello world了。

用同样的方法就可以将我们开发好的应用快速部署到生产服务器上去了。如果在docker run命令需要设置cpu的支持数和权重值可以这样:

-c,--cpu-shares=0CPU shares(relative weight)--cpuset=""CPUsinwhich to allow execution(0-3,0,1)

另外一个小敲门,如果我想要进入一个在运行中的docker container时,可以使用 docker attach,连上这个container的输入和输出。

五、将多个 container 连接起来

我现在先下载一个redis数据库image,这也是以后做项目的常规用法,数据库单独用一个image,程序一个image,利用docker的link属性将他们连接起来。

$ docker pull redis#下载官方的redis镜像,耐心等待一段时间

接着我们执行命令启动redis镜像到一个container,开启redis-server持久化服务

$ docker run--name redis-server-d redis redis-server--appendonly yes

然后我们再启动一个redis镜像的container作为客户端连接它

$ docker run-it--link redis-server:redis--rm redis/bin/bash

redis[@7441b8880e4e](/user/7441b8880e4e):/data$ env#想要知道当前我们在主机还是container,注意$前面的host和nameREDIS_PORT_6379_TCP_PROTO=tcp

HOSTNAME=7441b8880e4eTERM=xterm

REDIS_NAME=/boring_perlman/redis

REDIS_PORT_6379_TCP_ADDR=172.17.0.34#redis服务器ipREDIS_PORT_6379_TCP_PORT=6379#redis服务器端口PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin

PWD=/data

REDIS_PORT_6379_TCP=tcp://172.17.0.34:6379SHLVL=1REDIS_PORT=tcp://172.17.0.34:6379HOME=/

_=/usr/bin/env

$ redis-cli-h"$REDIS_PORT_6379_TCP_ADDR"-p"$REDIS_PORT_6379_TCP_PORT"172.17.0.34:6379>seta1#成功连入redis数据库服务器OK172.17.0.34:6379>geta"1"172.17.0.34:6379>

通过这样的方法,我们就可以将发布的应用程序和数据库分开,单独进行管理,以后对数据库进行升级或者对程序进行调整两者都没有冲突,系统环境变量我们可以通过程序的os模块来获得。

六、文件卷标加载

比如我们想要一个日志文件保存目录,如果直接写入container,那样image升级之后,日志文件处理就比较麻烦了,所以就需要将主机的文件卷标挂载到container中去,挂载方法如下:

$ docker run--rm=true-i-t--name=ls-volume-v/etc/:/opt/etc/centos ls/opt/etc

boot2docker  hostname    ld.so.conf    passwd-securetty  sysconfigdefaulthosts        mke2fs.conf    pcmcia      services  sysctl.conf

fstab        hosts.allow  modprobe.conf  profile      shadow    udevgrouphosts.deny  motd          profile.d    shadow-versiongroup-init.d      mtab          protocols    shells

gshadow      inittab      netconfig      rc.d        skel

gshadow-issue        nsswitch.conf  resolv.conf  ssl

host.conf    ld.so.cache  passwd        rpc          sudoers

如果想要挂载后的文件是只读,需要在这样挂载:

-v/etc/:/opt/etc/:ro#read only

我们也可以挂载其他container中的文件系统,需要用到 -volumes-from 参数,我们先创建一个container,他共享/var/目录给其他container。

$ docker run-d-i-t-p1337:1337--name nodedev-v/var/fa node/var/nodejs/app.js

然后我们启动一个ls-var的container来挂载nodedev共享的目录:

$ docker run--rm=true-i-t--volumes-fromnodedev--name=aaa1 centos ls/varadm    db    games  kerberoslocallog  nis    opt      run    tmp  yp

cache  empty  gopher  liblockmail  nodejs  preserve  spoolvar

我们打印var目录,会发现多了一个nodejs的目录,就是从nodedev中的container挂载过来的。其实我们挂载其他container的路径都是在根目录上的。

七、发布到docker hub上去

我们做完镜像,就需要将镜像发布到docker hub上,供服务器下载然后运行,这类似git仓库,将自己开发的东西丢到云服务器上,然后自己在其他机器或者其他开发者可以下载镜像,并且从这个镜像开始运行程序或者再进行2次制作镜像。

我们需要先登录docker帐号,执行:

$ docker login#输入你在docker官网注册的帐号和密码就可以登录了$ docker push<用户名>/<镜像名>#将你制作的镜像提交到docker hub上

非官方不允许直接提交根目录镜像,所以必须以<用户名>/<镜像名>这样的方式提交,比如 doublespout/dev 这样

八、总结

docker快速部署介绍完毕了,总结一下,要创建一个简单项目使用的步骤:

1、安装配置docker

2、pull镜像,安装程序执行环境

3、pull数据库镜像

4、开发程序

5、push 程序的镜像

6、服务器安装配置docker

7、运行数据库镜像

8、运行程序镜像,并且把数据库镜像link进来,并且挂载主机的日志目录或其他上传目录。

使用了docker以后,环境配置只需一次,免去了开发部署一套,测试部署一套,生产又部署一套的麻烦,以后程序搬家也是非常简单。

最后分享一个小敲门,如何像使用linux虚拟机那样运行一个container,比如我我们想要直接登录container执行多个任务,又不想直接借助 docker run 命令,以后我们还想登录到这个container来查看运行情况,比如执行top,ps -aux命令等等。

$ docker run-d-i-t-p1337:1337fa/bin/bash

$ docker attach58bash-4.2#

这样我们就可以通过进入container来调试程序了。但是一旦执行ctrl+d或者exit,container就将退出,这个方法也只适用于开发调试的时候。

最后请跳出 container 是一个小的vps的概念,详见另一篇文章.

不要用ssh连接直接到你的container常用的Dockerfile命令示例

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,311评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,339评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,671评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,252评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,253评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,031评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,340评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,973评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,466评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,937评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,039评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,701评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,254评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,259评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,485评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,497评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,786评论 2 345

推荐阅读更多精彩内容