Meteor Docker部署详解

作者: 时见疏星 | 来源:发表于2016-03-16 21:02 被阅读4023次

    很早以前,就有读者要求我详细讲讲Meteor的部署,虽然我在极客学院的视频中讲解过个大概,但是实际操作中大家还是遇到了不少问题。如果大家曾经试图在国内部署Meteor的程序或者应用的话,会感到非常沮丧,因为GFW的原因,很多东西都不明不白地「挂了」。尤其是国外已经有一系列自动化部署工具(如Docker image)的时候,却怎么用都用不了,或者没办法改(或懒得改),只得手动一步步部署,这对于我们使用Meteor的热情还是打击很大的。

    所以,这里我也花了几天,研究了一下部署Meteor应用的简便方式,以供大家参考。

    0. 遇到问题

    之前在视频中有讲到过,部署meteor应用最方便的方法就是使用meteor-up这个项目的mupx分支。这是一种基于Docker的自动化部署方式,只需要在本地写一些配置文件,我们就可以自动把本地的代码库部署到远程服务器上了。

    但在国内,我们却经常失败,主要原因有三:

    1. Docker Hub被墙,我们无法直接使用Docker官方镜像源
    2. NPM被墙,我们无法下载安装npm相应扩展包
    3. meteor本身不稳定,有时候国内packages下载较慢

    不瞒大家说,当初选择meteor的一大主要原因就是meteor的官方扩展包源没有被墙,meteor add添加扩展包的时候速度还行,做prototype开发比较方便。但是,随着meteor越来越受到关注,又开始拥抱NPM,如何科学获得官方包就提上了议事日程。

    这里插一句,人大咋从来没有程序员代表,问问这么多编程必备的网站被墙的问题。天朝封锁最厉害的互联网居然是天朝唯一能拿的出手和美帝比肩的领域,不得不说是一种讽刺。

    1. Docker和NPM

    1.1 Docker 和 DaoCloud

    DaoCloud

    Docker翻墙一般使用DaoCloud提供的加速器服务,我们来看一下它的描述:

    DaoCloud 加速器是广受欢迎的 Docker 工具,解决了国内用户访问 Docker Hub 缓慢的问题。DaoCloud 加速器经历了两个版本,1.0 版本主要采用了 Docker Registry Mirror 的功能,结合国内的 CDN 服务,为用户提升镜像下载的速度;2.0 版本加入了 DaoCloud 大量自主研发的协议层优化,并提供了可以替代 Docker Pull 的客户端,完美解决国内获取 Docker 镜像 metadata 的问题,并再次成倍提升下载速度。

    至于如何安装并使用dao,可以看一下我之前写的文章使用DaoCloud安装Docker和镜像

    首先,肯定是注册DaoCloud账户,然后进入Dashboard,点击「加速器」,然后点击「立即开始」,这里进入选择界面,选择你的主机,这里以Ubuntu为例。

    Ubuntu

    然后我们可以看到,使用这个命令进行Docker的安装:

    curl -sSL https://get.daocloud.io/docker | sh
    

    安装过程结束后,可执行下面命令验证安装结果。如果看到输出 docker start/running 就表示安装成功。

    sudo service docker status
    

    点击「安装好了」,接着安装我们的主机监控程序:

    curl -sSL https://get.daocloud.io/daomonit/install.sh | sh -s 72346df3cbe80你自己的token8bcc77d434518
    

    这样,我们的Docker和dao就全部安装完成。

    2. NPM

    由于众所周知的网络原因,npm install命令行从npm官方源拖代码时会遇上麻烦。一般的方法是将npm仓库源替换为国内镜像:

    npm config set registry https://registry.npm.taobao.org
    npm config set disturl https://npm.taobao.org/dist
    

    但是由于我们是在Docker中进行部署,所以我们需要在相应的Docker image中加入这两条修改镜像源的命令。至于在哪里修改,我们则需要看看mupx的内容了。

    3. meteor-up with docker

    首先,需要说明的是,这里的mupx指的是一种基于Docker部署meteor应用的工具,采用node.js和shell script编写,等mupx稳定之后,将合并到mup分支中。

    可以看到,实际上有两个采用Docker的meteor-up项目:

    arunoda mupx kadirahq mup

    其中,第二个是mupx的新项目地址,从更新上也可以看出,第一个repo已经停止更新,迁移到第二个kadirahq下面了。但是由于mupmupx重复的问题,第二个repo的命令需要自己编译一下,我们clone这个项目,然后来自己编译它:

    git clone https://github.com/kadirahq/meteor-up
    cd meteor-up
    npm install
    npm link
    

    这里,如果你之前安装过(npm install -g mup)mup的话,需要删除mup和它的link,不然这里npm link会出错。

    在新的meteor-up项目中,这里我们使用mup.js代替之前的mup.json。原因是我们可以在这里面写JavaScript代码,来处理例如读写ssh key文件等事情。一个示例的mup.js文件如下:

    module.exports = {
      servers: {
        one: {
          host: '1.2.3.4',
          username: 'root'
          // pem:
          // password:
          // or leave blank for authenticate from ssh-agent
        }
      },
    
      meteor: {
        name: 'app',
        path: '../app',
        servers: {
          one: {}, two: {}, three: {} //list of servers to deploy, from the 'servers' list
        },
        env: {
          ROOT_URL: 'app.com',
          MONGO_URL: 'mongodb://localhost/meteor'
        },
        logs: { //optional
          driver: 'syslog',
          opts: {
            url:'udp://syslogserverurl.com:1234'
          }
        }
        dockerImage: 'madushan1000/meteord-test', //optional
        deployCheckWaitTime: 60 //default 10
      },
    
      mongo: { //optional
        oplog: true,
        port: 27017,
        servers: {
          one: {},
        },
      },
    };
    

    4. mup详解

    下面,我们就来看一下这个repo的结构和内容,它到底干了些什么。

    ├── index.js
    ├── lib
    │   ├── execute.js
    │   ├── modules
    │   │   ├── default
    │   │   │   ├── __tests__
    │   │   │   │   └── index.js
    │   │   │   ├── index.js
    │   │   │   └── template
    │   │   │       ├── mup.js
    │   │   │       └── settings.json
    │   │   ├── docker
    │   │   │   ├── __tests__
    │   │   │   │   └── index.js
    │   │   │   ├── assets
    │   │   │   │   └── docker-setup.sh
    │   │   │   └── index.js
    │   │   ├── index.js
    │   │   ├── meteor
    │   │   │   ├── __tests__
    │   │   │   │   └── index.js
    │   │   │   ├── assets
    │   │   │   │   ├── meteor-deploy-check.sh
    │   │   │   │   ├── meteor-setup.sh
    │   │   │   │   ├── meteor-start.sh
    │   │   │   │   ├── meteor-stop.sh
    │   │   │   │   ├── templates
    │   │   │   │   │   ├── env.list
    │   │   │   │   │   └── start.sh
    │   │   │   │   └── verify-ssl-config.sh
    │   │   │   ├── build.js
    │   │   │   └── index.js
    │   │   ├── mongo
    │   │   │   ├── __tests__
    │   │   │   │   └── index.js
    │   │   │   ├── assets
    │   │   │   │   ├── mongo-setup.sh
    │   │   │   │   ├── mongo-start.sh
    │   │   │   │   ├── mongo-stop.sh
    │   │   │   │   └── mongodb.conf
    │   │   │   └── index.js
    │   │   ├── proxy
    │   │   │   ├── __tests__
    │   │   │   │   └── index.js
    │   │   │   └── index.js
    │   │   └── utils.js
    │   ├── mup-api.js
    │   └── updates.js
    ├── package.json
    ├── scripts
    │   └── mocha-bootload.js
    └── tests
    

    可以看到,我们的主要模块有这五个default, docker, meteor, mongo, proxy。我们来分别讲解一下:

    4.1 default

    default里面定义了一些mup的基本命令,包括deploy,help,init,logs,reconfig,restart,setup,start,stop等。

    4.2 docker

    使用mup setup命令时,执行的是assets/docker-setup.sh这个脚本,这个脚本里的内容就是安装Docker。当然,由于墙的原因,在国内是安装不了Docker的,所以我们可以通过1.1讲述的那样,采用DaoCloud手动安装Docker。注意,这里setup命令会首先检测是否安装过Docker,如果安装过,那么不会重复安装了。

    4.3 mongo

    可以看到,在mongo/assets/mongo-start.sh中,我们看到这样一行,获取官方的mongodb最新镜像,然后运行:

    sudo docker pull mongo:latest
    
    sudo docker run \
      -d \
      --restart=always \
      --publish=127.0.0.1:27017:27017 \
      --volume=/var/lib/mongodb:/data/db \
      --volume=/opt/mongodb/mongodb.conf:/mongodb.conf \
      --name=mongodb \
      mongo mongod -f /mongodb.conf
    

    使用docker pull肯定是不行的,会被墙,所以这里我们把docker pull改为dao pull

    4.4 meteor

    index.js中,我们定义了start.sh中image的名字,也就是我们使用的基础Docker镜像。

    list.copy('Pushing the Startup Script', {
      src: path.resolve(__dirname, 'assets/templates/start.sh'),
      dest: '/opt/' + config.name + '/config/start.sh',
      vars: {
        appName: config.name,
        useLocalMongo: api.getConfig().mongo ? 1 : 0,
        port: config.env.PORT || 80,
        sslConfig: config.ssl,
        logConfig: config.log,
        image: config.dockerImage || 'meteorhacks/meteord:base'
      }
    });
    

    可以看到,假如我们没有在mup.js中另外指定image的话,默认image则是meteorhacks/meteord:base。这里我们改成loongmxbt/meteord:base,你也可以修改成自己的meteord base image。

    然后我们看一下meteor/assets/templates/start.sh这个文件,其中有这几行:

    set +e
    docker pull <%= image %>
    set -e
    

    这里和上面一样,将docker pull改为dao pull。这里的<%= image %>就是我们之前指定的image,默认为meteorhacks/meteord:base。在下一个部分里,我们详细看看这个image做了些啥。

    接着,我们会运行这个meteorhacks/meteord:base

    docker run \
      -d \
      --restart=always \
      --publish=$PORT:80 \
      --volume=$BUNDLE_PATH:/bundle \
      --hostname="$HOSTNAME-$APPNAME" \
      --env-file=$ENV_FILE \
      <% if(useLocalMongo)  { %>--link=mongodb:mongodb --env=MONGO_URL=mongodb://mongodb:27017/$APPNAME <% } %>\
      <% if(logConfig && logConfig.driver)  { %>--log-driver=<%= logConfig.driver %> <% } %>\
      <% for(var option in logConfig.opts) { %>--log-opt <%= option %>=<%= logConfig.opts[option] %> <% } %>\
      --name=$APPNAME \
      <%= image %>
    

    如果还设置了SSL的话,我们会多运行一个meteorhacks/mup-frontend-server作为前端反代,之后,我们也会看看这个前端反代服务器是什么:

    <% if(typeof sslConfig === "object")  { %>
      # We don't need to fail the deployment because of a docker hub downtime
      set +e
      dao pull meteorhacks/mup-frontend-server:latest
      set -e
      docker run \
        -d \
        --restart=always \
        --volume=/opt/$APPNAME/config/bundle.crt:/bundle.crt \
        --volume=/opt/$APPNAME/config/private.key:/private.key \
        --link=$APPNAME:backend \
        --publish=<%= sslConfig.port %>:443 \
        --name=$APPNAME-frontend \
        meteorhacks/mup-frontend-server /start.sh
    <% } %>
    

    小贴士:这里你可能会看到诸如set +eset -e的情况。set -e告诉bash一但有任何一个语句返回非真的值,则退出bash。使用-e的好处是避免错误滚雪球般的变成严重错误,能尽早的捕获错误。如果你必须使用返回非0值的命令,或者你对返回值并不感兴趣,或者你需要暂时关闭错误检查功能,就可以用set +eset -e包裹命令:
    set +e
    command1
    command2
    set -e
    这样,如果docker hub挂了,我们的deploy也不会因此中断。

    4.5 lib/updates.js

    lib/updates.js中,我们看到有一行定义const uri的,我们依旧将它改成淘宝的uri:

    const uri = 'https://registry.npm.taobao.org/npm';
    

    可以看到,这里用到了silent-npm-registry-client这个包,具体的API可以查看npm-registry-client的文档

    5. meteorhacks/meteord:base

    5.1 Dockerfile

    首先看这个Dockerfile,它主要运行了两个shell script:

    RUN bash $METEORD_DIR/init.sh
    
    EXPOSE 80
    ENTRYPOINT bash $METEORD_DIR/run_app.sh
    

    接着看看这个init.sh

    5.2 init.sh

    可以看到,主要有以下这四个脚本构成,它们都在scripts/lib目录下:

    bash $METEORD_DIR/lib/install_base.sh
    bash $METEORD_DIR/lib/install_node.sh
    bash $METEORD_DIR/lib/install_phantomjs.sh
    bash $METEORD_DIR/lib/cleanup.sh
    

    其中,install_base安装了curl命令,install_node安装node和npm,install_phantomjs安装了phantomjs,cleanup删除了一些临时文件。

    5.3 install_node

    可以看到这里的scripts/lib/install_node.sh中,下载了现在meteor所使用的node版本,也就是0.10.41,当然,这是个比较古老的版本,据说1.3要换一个比较新的版本。

    cd /tmp
    curl -O -L http://nodejs.org/dist/v${NODE_VERSION}/${NODE_DIST}.tar.gz
    tar xvzf ${NODE_DIST}.tar.gz
    rm -rf /opt/nodejs
    mv ${NODE_DIST} /opt/nodejs
    
    ln -sf /opt/nodejs/bin/node /usr/bin/node
    ln -sf /opt/nodejs/bin/npm /usr/bin/npm
    

    这里我测试了一下,curl命令下载node在国内是可以成功的,只不过速度有点慢,为方便起见,这里还是更换下载地址为淘宝node源

    curl -O -L http://npm.taobao.org/mirrors/node/v${NODE_VERSION}/${NODE_DIST}.tar.gz
    

    当然,npm也是用不了的,所以这里还要修改npm的registry到淘宝镜像:

    npm config set registry https://registry.npm.taobao.org
    npm config set disturl https://npm.taobao.org/dist
    

    这样,我们就大功告成了吗?显然还没有,我们继续看看install_phantomjs这个文件。

    5.4 install_phantomjs

    我们可以看到这一行

    curl -L -O https://bitbucket.org/ariya/phantomjs/downloads/phantomjs-${PHANTOMJS_VERSION}-linux-${ARCH}.tar.bz2
    

    显然,既有https,又是bitbucket,撞墙概率大大增加。幸运的是,淘宝也给我们提供了phantomjs镜像源。我们修改这个命令:

    curl -L -O http://npm.taobao.org/mirrors/phantomjs/phantomjs-${PHANTOMJS_VERSION}-linux-${ARCH}.tar.bz2
    

    安装phantomjs能够帮助你的站点被爬虫爬到。

    小贴士:phantomjs是什么?PhantomJS 是一个基于 WebKit 的服务器端 JavaScript API。它全面支持web而不需浏览器支持,其快速,原生支持各种Web标准: DOM 处理, CSS 选择器, JSON, Canvas, 和 SVG。 PhantomJS 可以用于页面自动化 ,网络监测,网页截屏,以及无界面测试等。它和meteor的spiderable这个扩展包有关,如果你使用meteor bundle部署应用,你必须安装phantomjs。如果你是部署在galaxy上,那么MDG已经帮你搞定了一切。

    5.5 Docker Hub Automated Build

    当然,如果不是在本地(国内)服务器上自行构建image的话,我们只需要添加这两行,发布到Docker Hub上构建即可。

    npm config set registry https://registry.npm.taobao.org
    npm config set disturl https://npm.taobao.org/dist
    

    这里插入一点Docker Hub的自动构建使用方法,以便你今后自己的image发布。

    我们fork了meteorhacks/meteord这个仓库之后,git clone到本地,然后创建一个新的分支,名为npm-taobao

    git checkout -b npm-taobao
    
    创建自动构建镜像 连接Github帐号 选择meteord项目 Build Settings中设置Docker file路径和tag 成功构建镜像

    6. meteorhacks/mup-frontend-server

    之前我们也提到过,如果你需要使用SSL证书,则需要添加一个Nginx前端的代理,也就是mup-frontend-server

    lib/install_nginx.sh中,我们有这么一行下载nginx安装包。

    wget http://nginx.org/download/nginx-$NGINX_VERSION.tar.gz
    

    当然,这个目前没有被墙,所以我们暂时不用担心哈。

    7. 综合

    综上所述,我们主要修改了两个repo,分别是mup命令的repo和meteord这个镜像的repo。

    修改完成后的两个repo分别在这里:

    所以我们的部署步骤变成了:

    1. 在服务器上,使用DaoCloud安装Docker和DaoCloud Toolbox加速器
    2. 使用mup init初始化配置
    3. 使用mup setup部署服务器环境
    4. 使用mup deploy部署本地应用至服务器

    mup setup命令结果:

    Started TaskList: Setup Docker
    [www.meteorain.com] - setup docker
    [www.meteorain.com] - setup docker: SUCCESS
    
    Started TaskList: Setup Meteor
    [www.meteorain.com] - Setup Environment
    
    Started TaskList: Setup Mongo
    [www.meteorain.com] - setup environment
    [www.meteorain.com] - setup environment: SUCCESS
    [www.meteorain.com] - copying mongodb.conf
    [www.meteorain.com] - Setup Environment: SUCCESS
    [www.meteorain.com] - copying mongodb.conf: SUCCESS
    
    Started TaskList: Start Mongo
    [www.meteorain.com] - start mongo
    [www.meteorain.com] - start mongo: SUCCESS
    

    服务器端docker ps结果:

    CONTAINER ID        IMAGE                    COMMAND                  CREATED             STATUS              PORTS                        NAMES
    9b87110206f8        loongmxbt/meteord:base   "/bin/sh -c 'bash $ME"   21 minutes ago      Up 21 minutes       0.0.0.0:80->80/tcp           meteorain
    832f2cd61298        mongo                    "/entrypoint.sh mongo"   24 hours ago        Up 24 hours         127.0.0.1:27017->27017/tcp   mongodb
    

    8. TODO List

    1. 你可能会发现,我们并没有改变安装Docker的步骤,那是因为daocloud加速器需要通过一个key来安装,这个key是网站产生的,后期考虑如果能轻松获取key的话做一下自动化的部署。

    2. 每次mup setup时,mongo都会重新安装,还是挺费时费流量的,考虑后期修改成Docker那样,检测是否已经有image。

    3. 使用meteord:base镜像重新deploy时,会重新pull镜像并构建,对于原型项目来说比较浪费时间。可以测试一下devbuild。

    4. SSL测试,使用Let's Encrypt.

    9. MeteoRain

    部署成功后的Meteor1.3项目: http://www.meteorain.com/

    Meteor全栈开发

    参考资料

    相关文章

      网友评论

      • 平凡之路呀呀:请问如果是使用mup内部mongo,需要设置mongo登陆密码认证吗
      • sosocom:东西都写完了,最后一步部署卡死了。 不知道现在有没有更好的方法,部署到阿里云的。
      • d57341f790d0:好吧,大概是这样的,不是太懂mup deploy的机制还掺着docker一起用。我感觉每次我用mup deploy的时候是在docker里面npm进行安装所以老超时,是这样的么
        时见疏星:@九九丸 DaoCloud的加速器改版了,公众号里发文章提到过,不再有dao命令了,应该是和docker公司达成了某种共识,其他部分的话主要就是npm的源设定了。
        https://www.daocloud.io/mirror.html#accelerator-doc
        https://github.com/kadirahq/meteor-up
        https://github.com/chriswessels/meteor-tupperware
        天梦飘香:@九九丸 你这个问题已经解决了,看楼主说的,用dao pull替换docker pull
      • d57341f790d0:所以我每次mup deploy失败的原因就是各种墙。。。。。
        虽然不太懂,但是我按照这个步骤先走下来看看是什么吧
      • 21062e2af0e7:和我之前的部署方式一模一样。。。不过上周升级到1.4以后,meteorhacks base包已经没法使用了,所以现在我全在daocloud上面实行全docker化管理了,把开发跟部署的docker分开,部署的效率也明显提升,已经弃用mupx了~~~
        天梦飘香:@21062e2af0e7 你现在不用mup来部署了吗?
        天梦飘香:@21062e2af0e7 具体是怎么搞呢?刚刚搞了一半,mup配置好了,可是meteorD不知道要怎么被驱动
      • wtf1943:部署的最后一步 Verifying Deployment 是做什么的 每次到这里 就会报错
      • 90edb4b73d9d:meteor setup 之后
        有了这样一个错误:
        bash: line 4: docker: command not found
        sudo:抱歉,您必须拥有一个终端来执行 sudo
        是要在服务器上面自己docker吗?
      • TL教官:还有更简单模式,,,使用阿里云服务器,三步完成,,1 安装npm ,安装mupx2,配置mupx.json 3启动
        TL教官:@疯子纳尼 最新版本已考虑独立部署,详看我最近写的一篇文章
        疯子纳尼:这样还是会遇到网络的问题,还请详解
        jwdzzhz777:@TL教官123 具体说说呗
      • 42cd9b7e2ca3:写的好细致啊 谢谢您的指点。
        4.5 和 5.5是不是写的有点问题?
        时见疏星:@温柔的狮子 已修正,谢谢哈!
        42cd9b7e2ca3:@时见疏星 谢谢您的回复。4.5中,update.js应该是在lib目录下吧
        时见疏星:@温柔的狮子 可能没写清楚哈,之后再改改。大体意思就是:如果image是在国外构建(Docker Hub)的话,只需要加npm taobao registry就行了,如果是国内构建的话,需要将一系列的源都替换掉,构建之后可以发布在DaoCloud的源上。国外的image可以通过dao加速器来pull到本地,但image本身是Docker Auto Build构建的。

      本文标题:Meteor Docker部署详解

      本文链接:https://www.haomeiwen.com/subject/kcqflttx.html