Docker 入门(二)

引言

Docker 是一个非常有趣的项目。它自己宣称可以减轻部署服务器的难度,当然我相信里面有炒作的成分。但是实际使用后,我觉得 Docker 的表现还是可圈可点的。
Docker最大的作用就是隔绝了操作系统环境,类似于虚拟机,但是相对于虚拟机,他又拥有绝对的高效率、和通用性

  1. docker有着比虚拟机更少的抽象层
  2. docker利用的是宿主机的内核

所以本节为了能使大家对Docker有一个直观的认识,这里引用一个网友的实际程序来给大家讲解

开始之前,我们先要引入一个叫做 Image 的东西,不过这里不是图片的意思,他代表的是镜像
  Docker最核心的一个概念就是镜像,他类似于虚拟机当中的虚拟机文件,但是又完全不同,最直观的感觉就是运行一个Docker Image是非常迅速的,通常只需要几秒钟!你可以从Docker的镜像库中下载到很多很多的镜像,上一章的“Hello-Docker”大家还记得么,那就是其中的一个,还有很多,比如说Ubuntu,RabbitMQ等等。

这里有一个镜像库,感兴趣的同学可以去看看,阿里镜像库

阿里镜像库

</br>
</br>
</br>


开始

好了,这里先不说那么多复杂的概念,咱们上手试试吧!
今天,我们要在使用Jetty + Docker快速实现和部署一个能显示随机正态分布的页面,非常简单,最终效果如下:
我们要在使用Jetty + Docker快速实现和部署一个能显示随机正态分布的页面,非常简单,最终效果如下:

运行结果(图中的钟型曲线是使用Java的Random.nextGaussian产生的。用户可以在页面上输入限制最大值、最小值、数学期望值、标准差和样本总量并由这些参数生成正态分布图。)

核心目标:

  1. 理解Docker两条基本命令(打包运行)。
  2. 了解Docker基本使用方式。

开发环境

先看看开发环境是否准备好了,嗯~想一下我们需要什么,我们需要一个房子和一些零部件,房子就是我们Docker环境,零部件就是JDK环境,然后为了使应用看起来更简单,我们引入Groovy语言来代替java,接着就是一个好的编辑器,vim、emacs,当然哪个顺手就用哪个。

Jetty服务器

我们需要写一个Groovy脚本来跑Jetty,首先新建一个文件夹,把这个文件夹作为这个原型的根目录。

然后在原型根目录中新建app.groovy作为程序入口。这个脚本主要的任务就是为我们启动Jetty服务器,内容如下:

@Grab('org.eclipse.jetty.aggregate:jetty-server')
@Grab('org.eclipse.jetty.aggregate:jetty-servlet')
@Grab('javax.servlet:javax.servlet-api')
import groovy.servlet.GroovyServlet
import org.eclipse.jetty.server.Server
import org.eclipse.jetty.servlet.DefaultServlet
import org.eclipse.jetty.servlet.ServletContextHandler

def server = new Server(8080)
def context = new ServletContextHandler(server, '/', ServletContextHandler.SESSIONS)

context.with { 
    resourceBase = 'webroot' // 使用webroot文件夹作为根目录 
    addServlet(DefaultServlet, '/') // 挂入DefaultServlet为*.groovy脚本以外的文件提供访问 
    addServlet(GroovyServlet, '*.groovy') // Groovy脚本用的Servlet     
    welcomeFiles = ['index.groovy'] // welcome文件设置为index.groovy,此处Optional
}
server.start()

简单说明一下这个脚本的作用。

首先我们用@Grab抓取需要的jar包,完了之后导入一些Servlet和Server,然后把这些Servlet挂到端口为8080的Jetty Server上,最后启动Jetty。

服务器有了,接下来我们需要写正态分布的页面了。

正态分布

在原型根目录下新建webroot,在其中新建一个脚本,名字随意取,在这里我们取名叫test1.groovy。我不准备在这里贴这个脚本的全部代码了,你可以在这个项目里面看到源代码。
  更详细的程序说明也可以查看这个博客,注意!这里的代码不是重点,我们只是引用了这位博主的代码,大家不愿意看代码的同学可以直接下载源码。
  这里用到了几个名词需要简单说一下

  1. java.util.Random

Java的java.util.Random中自带一个能生成数学期望(u)是0、标准差(a)是1的近似随机标准正态分布的随机方法,叫做nextGaussian,我们用它来生成随机数。
  由标准正态分布的特性可知,99%以上的数值落在(u-2.58a, u+2.58a)区间中,由于标准正态分布u = 0, a = 1所以我们可以假定nextGaussian方法生成的双精度值范围在-2.58 ~ +2.58之间,我们要做的只是对这个结果值做一下线性平移,然后收集结果即可。

  1. GroovyServlet

如果你查看groovy.servlet.ServletBinding
,可以看到在GroovyServlet中,已经默认绑定了以下几个变量供我们调遣
Eager variables

  • “request” : the HttpServletRequest object
  • “response” : the HttpServletRequest object
  • “context” : the ServletContext object
  • “application” : same as context
  • “session” : shorthand for request.getSession(false) - can be null!
  • “params” : map of all form parameters - can be empty
  • “headers” : map of all request header fields

Lazy variables

  • “out” : response.getWriter()
  • “sout” : response.getOutputStream()
  • “html” : new MarkupBuilder(response.getWriter())
  • “json” : new JsonBuilder()

Methods

  • “forward(String path)” : request.getRequestDispatcher(path).forward(request, response)
  • “include(String path)” : request.getRequestDispatcher(path).include(request, response)
  • “redirect(String location)” : response.sendRedirect(location)
  1. chart.js

一个开源图表控件,参考这里的中文文档

详细代码如下:

def random = new Random();
def map = [:]

int MIN = params.distMin? params.distMin.toInteger():0 // 最大值
int MAX = params.distMax? params.distMax.toInteger():100 // 最小值
int mean = params.distMean? params.distMean.toInteger():50 // 数学期望值
int sd = params.distSD? params.distSD.toInteger():2 // 标注差
int nums = params.distTimes? params.distTimes.toInteger():100
MAX = Math.max(MAX, MIN)
MIN = Math.min(MAX, MIN)

if(MAX == MIN) {
    MAX = MIN + 100
}

if(nums <=0) {
    nums = 100
}

// System.out.println "MIN:$MIN"
// System.out.println "MAX:$MAX"
// System.out.println "mean:$mean"
// System.out.println "sd:$sd"
// System.out.println "nums:$nums"

nums.times {
    def gaussian = MIN - 1
    while(gaussian < MIN || gaussian > MAX) {
        gaussian = random.nextGaussian(); // mean 0.0, standard deviation 1.0// transform
        gaussian = (int)(sd * gaussian) + mean
    }
    // System.out.println "runs[$it]=$gaussian"
    if(map[gaussian]) {
        map[gaussian] += 1
    } else {
        map[gaussian] = 1
    }
}

// System.out.println map
def keys = map.keySet().sort()
def dataLabels = (keys[0]..keys[-1]).collect { it.toString() }
def dataList = (keys[0]..keys[-1]).collect { map[it]?:0 }

//这里动态生成html页面
html.html {
    head {
        title 'Gaussian Distribution Test'
        script src:'//cdn.bootcss.com/Chart.js/1.0.2/Chart.min.js'
    }
    body {
        h1 '(伪)正态分布研究'
        canvas id:'myChart', width: 800, height: 500
        form (method:'POST') {
            label ('最小值MIN') { input(type:'number', name:'distMin', placeholder:'最小值', value:"$MIN", required:'required') }
            label ('最大值MAX') { input(type:'number', name:'distMax', placeholder:'最大值', value:"$MAX", required:'required') }
            label ('数学期望值') { input(type:'number', name:'distMean', placeholder:'数学期望', value:"$mean", required:'required') }
            label ('标准差') { input(type:'number', name:'distSD', placeholder:'标准差', value:"$sd", required:'required') }
            label ('样本总量') { input(type:'number', name:'distTimes', placeholder:'样本总量', value:"$nums", required:'required') }
            button(type:'submit', '提交')
        }
        script {
            mkp.yield 'var data ='
        json {
            labels dataLabels
            datasets ([[
                fillColor : "rgba(151,187,205,0.5)",
                strokeColor : "rgba(151,187,205,1)",
                pointColor : "rgba(151,187,205,1)",
                pointStrokeColor : "#fff",
                data : dataList
            ]])
        }
    }
    script {
        mkp.yieldUnescaped '''
        var options = {
            //String - Colour of the scale line
            scaleLineColor : "rgba(0,0,0,.5)",
            //String - Colour of the grid lines
            scaleGridLineColor : "rgba(0,0,0,.1)",
            //Boolean - Whether to show a dot for each point
            pointDot : false,
         };
         var ctx = document.getElementById("myChart").getContext("2d");
         new Chart(ctx).Line(data, options);
         '''
        }
    }
}

运行

重头戏现在才开始,首先让我们运行一下上面的代码,先检查一下工程文件是否像下图这样:

目录1

webroot中包含test1.groovy文件

目录2

好啦,让我们在主目录尝试运行如下命令:

$ groovy app.groovy

完成!浏览器访问http://localhost:8080/test1.groovy

是不是看到运行的效果了?如果不对的话再检查检查,相信不会有什么问题!

部署

这里我们先回忆一下我们需要部署什么东西,首先就是java环境,很多人可能要问,为什么要部署java环境,本机上的java不是已经安装了么?其实,用上面的房子理论来解释就是,docker他默认是一个空房子,并不包含任何运行环境,为了让我们的工具可以运行起来,我们首先是要配置好房子里的工具,也就是运行环境,其次才是打包我们的代码、数据库文件等。
  接下来就是要部署Groovy的运行环境,配置完成以后还需要将相应的工具配置到环境变量,就和本机配置一样。
  Jetty呢?这个不用在容器中配置,因为它是通过Groovy动态引入的,这也是Groovy的一个魅力所在!我们可以用过它简化很多操作,并实现一些Java不容易实现的功能。这个我在后面会单独开一个Groovy的文章详细说明。
总结一下:

都用到了如下这些工具:

  1. JAVA
    openjdk-8FROM java:openjdk-8-jdk
  2. Groovy
    Install groovyADD http://dl.bintray.com/groovy/maven/apache-groovy-binary-${GROOVY_VERSION}.zip /tmp/ RUN unzip -d /opt/ /tmp/apache-groovy-binary-${GROOVY_VERSION}.zip \ && rm /tmp/apache-groovy-binary-${GROOVY_VERSION}.zip

Dockerfile代码如下:

# 使用openjdk-8
FROM java:openjdk-8-jdk

# 安装wget和unzip
RUN apt-get update && \
    apt-get -y install wget unzip && \
    apt-get clean

# 设定环境变量
ENV GROOVY_VERSION=2.4.5
ENV JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 \
    GROOVY_HOME=/opt/groovy-${GROOVY_VERSION}

ENV PATH=$GROOVY_HOME/bin/:$JAVA_HOME/bin:$PATH

# Install groovy
ADD http://dl.bintray.com/groovy/maven/apache-groovy-binary-${GROOVY_VERSION}.zip /tmp/

RUN unzip -d /opt/ /tmp/apache-groovy-binary-${GROOVY_VERSION}.zip \
  && rm /tmp/apache-groovy-binary-${GROOVY_VERSION}.zip

# 复制代码
ADD ./src/ /groovyApp

EXPOSE 8080

WORKDIR /groovyApp

# 运行groovy
ENTRYPOINT ["groovy", "app.groovy"]

OK,然后在docker环境中运行:(如果你是docker-machine的话,就是mac或者windows的docker,就是在Docker Quickstart Terminal
中,对就是头上有个金鱼
鲸鱼的那个终端)

这里需要注意下目录:

目录3

src中就是刚才的项目文件夹,在这个目录中我们打开Terminal,运行

docker build -t gaussianranddemo .

需要注意新版本的Docker规定命名必须是全部 小写
首次运行Groovy需要下载依赖包, 可能需要点时间, 请耐心等候....
等待Docker下载并构建成功后运行

docker run -d -p 8080:8080 GaussianRandDemo

-d为是否后台运行,如果想查错误,可以把-d去掉,有异常可以调试

这个时候我们去查看http://localhost:8080/test1.groovy
如果页面正常显示,证明部署,运行都已经成功了!
最后再看一眼实际运行的效果

最终效果

排查问题

  • 如果没有成功的话,可以去查看下端口占用情况

$ netstat -ap|grep 8080

  • 容器运行情况

$ sudo docker ps -a


容器实例运行状态
  • 镜像状态

$ sudo docker images


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

推荐阅读更多精彩内容