Go CD 学习笔记(一)

Go CD 是属于在持续集成,持续交付过程中使用的自动化构建工具,可以执行编译,自动化测试,自动部署等等。
在此之前,已经使用过Jenkins工具了,Go CD 的功能和Jenkins差不多,但又有其特点。

Go CD中的某些概念

Task

task是要执行的一个build任务,一般是一个命令行指令

Job

  • 一个Job由多个task组成,每个task按排列顺序执行。
  • 当一个task执行失败,那么这个job就会被认为失败,并且没有其他特殊的设置的话,这个失败的task之后的其他task都不会被执行。
  • 在一个job中的task作为独立的程序运行的,某一个task改变的环境变量不会影响后续的tasks,但是这个task对文件系统的更改在后续的tasks中可见。

    Stage

  • 一个Stage由多个job组成,job之间可以相互独立运行,也就是说在Stage之中的job可以并行。
  • 一个job失败了,那么该stage也被认为是失败的,由于stage的job相互独立,所以其他的job还是会继续执行,不受影响。

    Pipeline

    一个Pipeline由多个stage组成,stage按顺序执行,若是一个stage执行失败了,那么该pipeline也被认为是失败的,并且之后的stage不会继续执行。

    Materials

  • Materials是pipeline跑的起因,一般是源代码仓库,例如git、SVN等。
  • 当代码更新的时候,Go Server会持续的获取代码仓库的代码。
  • 在某一个pipeline1中的stage也可以作为另一个pipeline2的Materials,如此在stage执行完毕后会触发pipeline2运行。

    Artifacts

    Artifacts一般是文件或者文件夹,当job执行完毕后会发布Artifacts,供给用户、后续执行的stage或pipeline使用。

    Agent

    Go Agents是tasks, jobs, stages 和 pipeline 运行的地方。

    Resources

    Resources指的是Agent中有的资源,它帮助jobs寻找适合的Agent,当job需要的资源在某agent中都存在的时候,才会使用该agent。

    Environments

  • Environments是用来分组的,把有联系agent 和 pipelines分成一组。
  • 一个pipeline最多只能在一个environment中。
  • 一个agent可以在多个environment中,或不属于任何一个environment。
  • 一个agent只能获取到属于和该agent有联系的environment中的pipeline中的jobs;无法获取其他jobs,哪怕这些jobs不属于任何environment。

    Environment Variables

  • Environment Variables和Environments没有关系,它是用户在配置中自己定义的变量。这些环境变量与Task在运行时可用的其他环境变量一样。
  • Environment Variables可以在 Environment ,pipelines,stages 和 jobs 中定义。并且下一层的Environment Variables可以重写上一层的,例如jobs重写Environment的变量。

java 生成 pdf

在日常开发过程中,若是想要把java中的某个对象中的内容,按某种排列方式输出到PDF中,有几种方式,此处只使用了itextpdf的方式。
itext pdf 文档

一、加入依赖

在gradle中只需要加入

1
compile group: 'com.itextpdf', name: 'itextpdf', version: '5.5.6'

二、创建一个Document,并加入内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public void createPdf() {
Document document = new Document();
try {
PdfWriter writer = PdfWriter.getInstance(document, new FileOutputStream("HelloWorld.pdf"));
document.open();
document.add(new Paragraph("Some content here"));
settings(document);
style(document);
document.close();
writer.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (DocumentException e) {
e.printStackTrace();
}
}

新建一个Document对象,再定义PdfWriter,把document中的内容写入HelloWorld.pdf文件。
在添加内容到document之前需要执行document.open()才可以。
document可以添加Paragraph、PdfPTable等元素。

三、踩过的坑

  • 问题:使用PdfPTable来构造整体结构时,如何设置行间距?
    解决方案:在给cell中添加内容的时候有两种方式,一种是直接初始化的时候添加,另一种是调用addElement方法添加内容。
    1
    PdfPCell cell = new PdfPCell(new Paragraph(100,"Table 1"));
1
2
PdfPCell cell= new PdfPCell();
cell.addElement(new Paragraph(100,"Table 1"));

这两种方法中第一种不支持设置Leading为100,第二种方法支持设置Leading为100。除了在初始化Paragraph时可以设置Leading,还可以调用setLeading()来设置,第一个参数是固定的行间距,第二个参数是行间距为行高的倍数。

1
2
Paragraph element = new Paragraph("Some content here");
element.setLeading(0,2);

  • 问题:如何给PdfPTable设置每列的宽度?
    解决方案:设置table的列宽时,需要把所有列的宽度放进数组列表中,缺一不可,如果数组的大小和table的列数不匹配则整体都不会显示出来。

    1
    2
    PdfPTable table = new PdfPTable(3);
    table.setWidths(new int[]{2, 1, 1});
  • 问题:如何设置边框?
    解决方案:边框的值不一样,当setBorder()函数的参数为0时,没有边框,1为上边框TOP,2为下边框BOTTOM,4为左边框LEFT,8为右边框RIGHT。具体的可以调用Rectangle中的固定值。

    1
    2
    PdfPCell cell = new PdfPCell(new Phrase("StackOverflow"));
    cell.setBorder(Rectangle.NO_BORDER);

Java 下载 pdf

在实际应用中有这么一个场景,用户希望在页面上有一个下载按钮,点击按钮时把当前页的内容生产一个PDF文件下载下来。

这个需求有两种表现形式,一种是当用户点击按钮时直接在浏览器中弹出保存框下载PDF文件,另一种是返回PDF的视图,在浏览器中预览PDF文件的内容,然后再下载。

这里分别介绍两种实现方式:

一、直接下载PDF文件

本质上来说,把PDF文件读取到inputStream中,继而放入返回的的实体中。
返回的ResponseEntity中,注意指定Content-Type的内容为application/pdf,
且Content-Disposition的内容为attachment; filename=”helloWorld.pdf”

1
2
3
4
5
6
7
8
9
@RequestMapping(value = "/pdfDownload", method = GET)
public ResponseEntity download() throws IOException {
File file = new File("HelloWorld.pdf");
InputStream in = new FileInputStream(file);
final HttpHeaders headers = new HttpHeaders();
headers.add("Content-Type", "application/pdf");
headers.add("Content-Disposition", "attachment; filename=" + file.getName() );
return new ResponseEntity<>(IOUtils.toByteArray(in), headers, HttpStatus.OK);
}

该方法在浏览器中直接访问localhost:8080/pdfDownload就回弹出一个文件保存框,选择保存的本地路径即可下载,或者有的效果是直接在浏览器中下载了PDF文件,这是因为所使用的浏览器本身的下载设置,可以根据个人喜好来设置究竟弹出还是不弹出保存框。

二、浏览器预览PDF文件,再下载

如果使用的不是RestController,就可以直接返回一个String字符串,内容为PDF文件的视图名称,这里定义为helloWorldPDF,该视图名在views.properties中配置

1
2
3
4
@RequestMapping(value = "/pdfDownload2", method = GET)
public String download2(HttpServletRequest request) throws IOException {
return "helloWorldPDF";
}

resource资源文件夹中的views.properties文件的内容为:

1
helloWorldPDF.(class)=com.test.report.PDFView

这里的com.test.report.PDFView指的是定义的PDF视图的内容。
我们使用了itextpdf的jar包来生成PDF文件的内容。

1
compile group: 'com.itextpdf', name: 'itextpdf', version: '5.5.6'

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class PDFView extends AbstractView {
public PDFView() {
setContentType("application/pdf");
}
@Override
protected boolean generatesDownloadContent() {
return true;
}
@Override
protected final void renderMergedOutputModel(Map<String, Object> model,
HttpServletRequest request, HttpServletResponse response)
throws Exception {
ByteArrayOutputStream baos = createTemporaryOutputStream();
Document document = new Document(PageSize.A4);
PdfWriter writer = PdfWriter.getInstance(document, baos);
writer.setViewerPreferences(PdfWriter.ALLOW_PRINTING | PdfWriter.PageLayoutSinglePage);
document.open();
Paragraph header = new Paragraph(new Chunk("hello world"));
document.add(header);
document.close();
writeToResponse(response, baos);
}
}

以上就生成了我们需要的PDF文件的内容,并放进ByteArrayOutputStream中,调用writeToResponse方法把字节流传进response中。

因为使用的是SpringBootApplication,所以需要加的一些配置是在 Application 的包含main方法的class中进行的

1
2
3
4
5
6
7
@Bean
public ResourceBundleViewResolver viewResolver() {
ResourceBundleViewResolver resolver = new ResourceBundleViewResolver();
resolver.setOrder(1);
resolver.setBasename("views");
return resolver;
}

如此一来就可以直接在浏览器中访问localhost:8080/pdfDownload2看到PDF文件的预览页面,在页面中可以进行下载,打印等操作。

Angular2 Learn note(1)

推荐官方学习文档

安装开发环境

新建一个文件夹simpleExample,创建一个文件名为tsconfig.json的文件,内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"compilerOptions": {
"target": "es5",
"module": "system",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": false,
"noImplicitAny": false
},
"exclude": [
"node_modules",
"typings/main",
"typings/main.d.ts"
]
}

创建一个文件名为typings.json的文件,内容为:

1
2
3
4
5
6
7
{
"globalDependencies": {
"core-js": "registry:dt/core-js#0.0.0+20160602141332",
"jasmine": "registry:dt/jasmine#2.2.0+20160621224255",
"node": "registry:dt/node#6.0.0+20160621231320"
}
}

创建一个文件名为package.json的文件,该文件中包含了我们应用所需要用到的packages,文件内容为:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
{
"name": "angular2-demo",
"version": "1.0.0",
"scripts": {
"start": "concurrent \"npm run tsc:w\" \"npm run lite\" ",
"tsc": "tsc",
"tsc:w": "tsc -w",
"lite": "lite-server",
"typings": "typings",
"postinstall": "typings install"
},
"license": "ISC",
"dependencies": {
"angular2": "2.0.0-beta.7",
"systemjs": "0.19.22",
"es6-promise": "^3.0.2",
"es6-shim": "^0.33.3",
"reflect-metadata": "0.1.2",
"rxjs": "5.0.0-beta.2",
"zone.js": "0.5.15"
},
"devDependencies": {
"concurrently": "^2.0.0",
"lite-server": "^2.1.0",
"typescript": "^1.7.5",
"typings":"^0.6.8"
}
}

执行npm install 可以下载以上的依赖包

开始第一个angular 2 程序

新建文件夹名为app,在该文件目录下新建hello_world_main.ts

1
2
3
4
import {bootstrap} from "angular2/platform/browser"
import {MyHelloWorldClass} from "./hello_world_app.component"
bootstrap(MyHelloWorldClass);

同样目录下新建hello_world_app.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
import {Component, View} from "angular2/core";
@Component({
selector: 'my-app'
})
@View({
template: '<h2>Hello World !!</h2>'
})
export class MyHelloWorldClass {
}

回到上一层目录新建index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html>
<head>
<title>Hello World</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/es6-shim/0.33.3/es6-shim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/0.19.20/system-polyfills.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.6/angular2-polyfills.js"></script>
<script src="https://code.angularjs.org/tools/system.js"></script>
<script src="https://code.angularjs.org/tools/typescript.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.6/Rx.js"></script>
<script src="https://code.angularjs.org/2.0.0-beta.6/angular2.dev.js"></script>
<script>
System.config({
transpiler: 'typescript',
typescriptOptions: { emitDecoratorMetadata: true },
packages: {'app': {defaultExtension: 'ts'}},
map: { 'app': 'app' }
});
System.import('app/hello_world_main')
.then(null, console.error.bind(console));
</script>
</head>
<body>
<my-app>Loading...</my-app>
</body>
</html>

index.html中使用SystemJS加载所需的模块,通过System.config函数配置。
配置详情为:
packages 告知System loader当没有文件名或者没有后缀的时候如何加载文件
map 告知System loader在何处加载所需的文件,此处app(左边)指向的是名为app(右边)的目录,所以在import的时候可以找到在app目录下的hello_world_main.ts 文件。

参考文章
Angular2 – SystemJS解析
tutorialspoint 网站

text editor(tinymce)

最近需要在 react 代码中插入一个文本编辑器,然后看了一下tinymce这个库。

导入库

npm中搜索相关的库,然后发现有直接包装好的react-tinymce库,但是在引用过程中会出现 “Unknown prop config on tag” 的错误提示。所以就放弃了直接引用这个库转而使用tinymce

导入tinymce,可以参考官方网站,有多种导入的方式。
这里选用了在js中直接引入库的方式。
把下面的代码插入到HTML中:

1
<script src="//tinymce.cachefly.net/4.3/tinymce.min.js"></script>

使用

在react js 代码中加入以下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
componentDidMount(){
tinymce.init({
selector: "textarea.tinymce-editor",
theme: "modern",
skin_url: '//cdnjs.cloudflare.com/ajax/libs/tinymce/4.3.5/skins/lightgray/',
height: 150,
resize: "both",
content_css: ["//nuance.jiveon.com/resources/statics/30387/nuance_simplified.css", "//nuance.jiveon.com/resources/statics/30387/nuance_bootstrap_combined.css"],
plugins: [
'advlist autolink lists link image charmap print preview hr anchor pagebreak',
'searchreplace wordcount visualblocks visualchars code fullscreen',
'insertdatetime media nonbreaking save table contextmenu directionality',
'emoticons template paste textcolor colorpicker textpattern imagetools'
],
toolbar1: 'undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image',
toolbar2: 'forecolor backcolor emoticons',
image_advtab: true
});
}

在对应的HTML中加入以下代码,然后就生效了。

1
<textarea className="tinymce-editor"></textarea>

Docker 学习笔记(三)--创建image

build 构建一个镜像

读取配置文件构建image(文件名默认Dockerfile,也可以自定义):
1)”-“表示将从STDIN读入数据,即将Dockerfile中的数据通过STDIN传入build中。

1
Docker build - < Dockerfile

2)PATH参数表示Dockerfile全路径,即将Dockerfile放置到$(HOME)/Users目录下。

1
2
Docker build $(HOME)/Users/Dockerfile
Docker build $(HOME)/Users/otherfile 名字可变

3)使用URL参数,URL必须指向一个git地址。Docker会把指定的git仓库克隆到本地,然后把仓库中的Dockerfile内容传给Docker Daemon。

1
Docker build https://github.com/wszhi/Docker-image.git#container:Docker

以上命令通知Docker Daemon加载https://github.com/wszhi/Docker-image.git 仓库,切换到container分支,在Docker目录下的Dockerfile文件来构建image。
注意: “:”后面的是Dockerfile文件的路径,此方式不支持自定义命名。若为master分支#后为空
若当前目录有Dockerfile文件可以省略路径使用

1
Docker build --rm=false .

–rm=false表示不删除临时镜像,当经常构建且变化不大的时候建议使用。

build 构建镜像三要素

build命令构建镜像是用Dockerfile文件中的指令来构建的,三个要素为:Dockerfile文件、所涉及文件和镜像参数。

1
Docker build -t image_name .

默认执行Dockerfile文件。

build命令执行的主要逻辑

1)获取Dockerfile文件——客户端
2)加载Dockerfile文件所在目录下的所有文件——-客户端
3)解析Dockerfile文件指令
4)执行文件内指令,判断是否存在有效缓存镜像
5)执行指令

其中获取Dockerfile文件的方式有三种:
1.通过标准输入传入,参数”-“;
2.通过远程方式获取,git;
3.通过指定路径方式获取。

build命令构建镜像原理

客户端将Dockerfile所需要的文件和镜像参数传递给Daemon。
然后Daemon处理客户端传来的各项参数(为后续的解压操作和文件I/O操作做准备)。
当参数处理完成之后,Daemon开始解压文件,抽取文件。
最后Daemon解析Dockerfile,生成指令语法树。
之后Daemon使用解析后的指令从已经初始化的指令——函数对应表中找到对应的处理函数,最后执行函数。

流程:
(客户端)
加载Dockerfile—>压缩构建目录—>添加镜像参数——发送构建请求—->
(服务端)
—->生成构建参数—>获取压缩包—>解压文件—>解析Dockerfile生成语法树—>初始化指令处理对照表—>循环执行指令—>删除解压文件—>构建完成

Dockerfile

Dockerfile 文件记录着用户”创建”镜像过程中需要执行的所有命令,Docker可以读懂的脚本文件。
镜像指的是一组特定的文件层,镜像的构建过程就是Docker执行Dockerfile所定义命令而形成这组文件层的过程。

Dockerfile 内置命令

FROM 指明基础镜像名称,必填

FROM <image>
FROM <image>:<tag>
FROM <iamge>@<digest>

当同时构建多个image时,可以出现多次FROM,但是只会返回最后一个镜像的ID,前几个会被标记为:

MAINTAINER 备注信息,包括作者、版本等,可选填

MAINTAINER 作者

RUN 用于执行后面的命令,当RUN执行完毕后,将产生一个新的文件层,可选填

# 当镜像中有/bin/sh
RUN <command>
# 当镜像中没有/bin/sh,使用以下方案,可以执行基础镜像中任意一个二进制程序,注意只能用双引号
RUN ["/bin/bash","-c","echo hello"]

镜像最多只能保存126个文件层,包括基础镜像的文件层;执行一次RUN就会产生一个文件层。

CMD 指定此镜像启动时默认执行命令,可选填

#推荐用法,其设定的命令将作为容器启动时的默认执行命令
CMD ["executable","param1","param2"]
#param 将作为ENTERPOINT的默认参数使用
CMD ["param1","param2"]
#将后面的命令作为shell命令,依靠/bin/sh -C 来执行
CMD command param1 param2
example: CMD echo "This is a test" | wc -

一个Dockerfile可以有多个CMD命令,可是只有最后一个CMD命令生效,CMD中也只能出现双引号,如果使用环境变量使用sh -C。

LABEL 在镜像中添加元数据。例如版本号、构建日期等,可选填

LABEL <key>=<value> <key>=<value> <key>=<value> ...

使用键值对的形式来向镜像文件中添加元数据的命令
如果键值对中存在空格,则需要使用双引号来回避错误
一个LABEL命令也会产生新的文件层

LABEL "description"="just for test" "version"="1.0"

EXPOSE 指定需要暴露的网络端口号,可选填

EXPOSE 8080
CMD ["catalina.sh","run"]

当容器运行时,来通知Docker这个容器中哪些端口是应用程序用来监听的,这些端口不会被外部网络访问到,只能被主机的其他容器访问。

ENV 在镜像中添加环境变量,可选填

# 第一个字符为key,后面所有字符为value
ENV <key> <value>
左边为key,右边为value,value有空格时,需要"\"转义或者使用双引号
ENV <key>=<value> ....

ADD 向镜像添加新文件或者新目录,可选填
将src标记的文件,添加到容器中的dest所标记的路径中去。src标记的文件可以是本地文件,可以是本地目录,甚至可以是URL链接。
src标记的是本地文件或者目录时,其相对路径应该是相对于Dockerfile所在目录的路径,而dest则应该指向容器中的目录。如果这个目录不存在,当ADD命令执行时,将会在容器中自动创建此目录。
在src标记的路径中,允许使用通配符。而dest路径不能使用通配符,必须是绝对路径,或者相对于WORKDIR的相对路径。

#添加所有以hom开头的文件
ADD hom* /mydir/
# ? 号可以被任意单个字符所代替
ADD hom?.txt /mydir/

规则:
1.src指定路径必须存在于Dockerfile所在目录。因为在Dockerfile执行时,Docker daemon会读取Dockerfile所在目录的所有数据。如果ADD命令使用的文件在此目录中不存在,那么daemon将找不到指定文件。
2.如果src指定的是URL,并且dest所指定的路径没有以”/“结尾,那么URL下载的数据将直接覆盖dest所给定的文件。
3.如果src指定的是URL,并且dest所指定的路径以”/“结尾,那么URL下载后的数据将直接写入dest所指定的目录中。
4.如果src指向一个目录,那么ADD指令将包括元数据在内的所有数据复制到容器中dest指定的文件中,但src所指定的目录本身不会被复制进去,只会复制此目录下的文件。
5.如果src指向的是一个已知格式的压缩文件。当添加到容器之后会自动执行解压缩动作。从而从URL中下载的压缩文件则不会执行解压缩。
6.如果src使用通配符指定了多个文件,那么此时dest必须是一个以”/“结尾的目录。
7.如果dest指向的路径没有以”/“结尾,那么这个路径指向的文件将会被src指定的文件覆盖。
8.如果dest指向的路径不存在,那么此路径中所涉及的父级目录都将会被创建。
9.当src指向的URL没有下载权限时,首先需要使用RUN wget或者RUN curl获取文件。
10.当ADD命令所标记的文件发生变化时,从变化的那个ADD命令开始,保存在缓存中的镜像将会实效,同时RUN命令产生的镜像也会失效。
COPY 从主句镜像复制文件,可选填
向容器中指定的路径下添加文件。

#添加所有以hom开头的文件
COPY hom* /mydir/
# ? 号可以被任意单个字符所代替
COPY hom?.txt /mydir/    

1-1
2-4
3-6
4-7
5-8
6如果使用STDIN输入Dockerfile内容,那么Docker命令将会失效。

ENTRYPOINT 在镜像中设定默认执行的二进制程序,可选填
ENTRYPOINT用来设定容器运行时默认执行程序的命令。

#第一种,推荐使用,可以自行设定需要执行的二进制程序和参数。
ENTRYPOINT ["executable","param1","param2"] 
#第二种,将所设定的二进制程序限制在/bin/sh -C 下执行。
ENTRYPOINT command param1 param2

ENTRYPOINT可以出现多次,只有最后一次起作用。
CMD指定的值将作为参数附加到ENTRYPOINT里的命令之后。如果run命令后面添加了其他参数,此时的CMD指定的参数将会失效。
第二种用法,ENTRYPOINT 命令设定的二进制程序将会忽略所有来自于CMD和RUN命令后面所添加的所有参数,只会运行ENTRYPOINT命令所设定的二进制程序。
为了确保容器可以正确处理stop命令发来的SIG信号,Docker建议使用exec来启动二进制程序。

VOLUME 向镜像挂载一个卷组,可选填

VOLUME ["/data"]

VOLUME可以在容器内部创建一个指定名称的挂载点。
如果以及在Dockerfile里声明了某个挂载点,那么以后对此挂载点种文件的操作将不会生效。一般在Dockerfile结尾处声明挂载点。

FROM nginx
COPY content /usr/share/nginx/html
COPY conf /etc/nginx
VOLUME /usr/share/nginx/html
VOLUME /etc/nginx

构建镜像

Docker build -t mynginximage .

创建容器

Docker run --name mynginx -P -d mynginximage
Docker run -it --volumes-from mynginx --name mynginxfiles debian /bin/bash

–volumes-from,从mynginx继承了配置文件信息,此时在mynginxfiles当中只能看到继承文件的信息而无法看到其他文件。在这个容器中可以任意编写配置文件而不会影响其他文件和容器。
USER 在镜像构建过程中,生成或者切换到另外一个用户,可选填

USER daemon

切换用户身份,当执行完USER命令后,其后面所有的命令都将以新用户的身份来执行。

WORKDITR 设定此镜像后续操作的默认工作目录,可选填

WORKDIR /path/to/workdir

WORKDIR是用来切换当前工作目录的指令。切换后的路径影响后续的指令中的路径。
WORKDIR可以在Dockerfile出现多次,但最终生效的路径是所有WORKDIR指定的路径的叠加。
如果需要切换到其他的工作目录,那么应该使用全路径进行切换。如果使用相对路径,默认在当前目录中切换。
在WORKDIR中只可以使用ENV设定的环境变量值

ENV DIRPATH /path
WORKDIR $DIRPATH/$DIRNAME

结果为/path/$DIRNAME

ONBUILD 配置构建触发指令集,可选填
由ONBUILD创建的触发命令集在当前Dockerfile执行过程中不会执行,而当此镜像被其他镜像当作其他镜像当作基础镜像使用时,将会被执行。

Dockerfile1

FROM tomcat:7.0.72-jre7
MAINTAINER WSZ
ADD build_image_share /build_image_share
RUN ["chmod","+x", "/build_image_share"]

LABEL "description"="just for test" "version"="1.0"
ENV myName="Shengzhi Wang" mySoft="Docker image build"


RUN mkdir /myvolume
RUN echo "hello world" > /myvolume/greeting
VOLUME /myvolume

CMD echo "This is a test" | wc -
EXPOSE 8080
CMD["catalina.sh","run"]

Dockerfile2

FROM tomcat:7.0.72-jre7
MAINTAINER WSZ

ADD build_image_share /build_image_share
RUN ["chmod","+x", "/build_image_share"]

WORKDIR /usr/local/tomcat/webapps
RUN rm -rf ROOT
WORKDIR /usr/local/tomcat/conf/Catalina/localhost
RUN echo "<?xml version='1.0' encoding='utf-8'?>" > ROOT.xml 
RUN echo "<Context path='/show_books' docBase='/build_image_share/show_books_base_on_angularJS_Underscore_master' debug='0' privileged='true' reloadable='true'/> " >> ROOT.xml
# RUN echo "<Context path='/shoppingweb' docBase='/build_image_share/shoppingweb' debug='0' privileged='true' reloadable='true'/> " >> ROOT.xml

LABEL "description"="just for test" "version"="1.0"
ENV myName="Shengzhi Wang" mySoft="Docker image build"

CMD /usr/local/tomcat/bin/catalina.sh run

build_image_share里面有一个命名为show_books_base_on_angularJS_Underscore_master的war包,只有网页没有集成数据库。
然后在同目录下运行docker build -t showbooks .
生成镜像后运行docker run -d -p 8888:8080 showbooks
在本机访问localhost:8080查看结果

Dockerfile3

FROM tomcat:7.0.72-jre7
MAINTAINER WSZ

ADD build_image_share /build_image_share
RUN ["chmod","+x", "/build_image_share"]

WORKDIR /usr/local/tomcat/webapps
RUN cp /build_image_share/shoppingweb.war shoppingweb.war

WORKDIR /usr/local/tomcat/conf
RUN sed -i~ '/<\/Host/i <Context path="/" docBase="shoppingweb.war" debug="0" privileged="true" reloadable="true"/>' server.xml

CMD /usr/local/tomcat/bin/catalina.sh run

Dockerfile 优化方案

1.容器要尽可能短小精悍,在写Dockerfile之前确定容器要提供的服务,要可以快速的启动和停止,同时用最少的步骤来配置容器服务。
2.多使用.Dockerignore文件,当加载文件时过滤某些文件。
当Docker加载文件时,如果发现目录存在.Dockerignore文件,那么此目录中符合.Dockerignore文件中定义规则的文件将会被过滤掉,不会被加载到缓存中。
./Dockerfile文件必须放到Dockerfile所在目录下面。./Dockerfile文件中的路径是以./Dockerfile所在目录为起点的。
3.不要安装非必需的软件包
4.一个容器尽量只运行一种服务
为了达到容器可以快速部署的目的,应该尽量每个容器只对外提供一种服务。当容器需要其他服务时,通过link的方式从其他容器获取服务,而不是在当前容器再启动另外一种服务。
5.控制文件层数量
文件层过多时,创建容器将会耗费更多的时间。
可以将相似的命令放到同一个RUN指令中运行,以减少文件层数量。但是命令合并的太厉害,会影响到Dockerfile中RUN指令的可读性,导致Dockerfile的维护成本很高。
要在文件层数量和可维护性之间找到平衡。
6.对命令中的参数进行排序

RUN apt-get update && apt-get install -y \
    bzr \
    cvs \
    git \
    subversion

对参数排序以后,可以避免下载重复的软件包,同时增添软件包时更为容易和控制,提高Dockerfile的可读性和可维护性。
默认规则,每行最后加空格和\
7.尽可能多使用缓存中的数据
在Dockerfile执行过程中,Docker会讲某些命令的执行结果保存成临时镜像,保存在缓存中。在下次执行Dockerfile时,Docker会在执行每一条指令之前检测此条指令缓存值是否有效。只有当缓存没有或者缓存失效时,才会再次执行此条指令。
若是不希望使用缓存中的结果,通过–no-cache=true来关闭此项功能。
Docker加载完基础镜像之后,会对比由此基础镜像所派生出来的所有子镜像。对比规则就是看这些子镜像是否由Dockerfile后面的命令所创建的,如果后面的命令发生了变化,那么缓存自动失效。
单纯的比较指令是否发生变化来判定缓存是否有效。ADD和COPY命令会检查其所涉及的文件是否发生了变更,标准是计算文件的校验和,而且只是校验镜像中的数据,不校验容器中的数据。
一旦缓存的镜像失效,那么此缓存镜像以后的所有镜像都自动失效。

某些命令

Delete all stopped containers
docker rm $( docker ps -q -f status=exited)
Delete all dangling (unused) images
docker rmi $( docker images -q -f dangling=true)
安装mysql server
RUN apt-get update \
&& apt-get install -y mysql-server
mysql server启动
docker run –name my-container-name -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql/mysql-server:tag
重启mysql service
service mysql restart
mysql -u root -p

在容器外进入mysql
docker exec -it mysql mysql -uroot -p
加一个目录存数据
docker run –name mysql2 -v /Users/szwang/DockerForMe/build_image/init_data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql/mysql-server

例子

基于原先做的Shoppingweb项目,数据库是Mysql
修改root-context.xml相关的数据库的内容

<bean id="dataSource"
            class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="com.mysql.jdbc.Driver" />
            <property name="url" value="jdbc:mysql://mysql:3306/shoppingdb?useUnicode=true&amp;characterEncoding=UTF-8" />
            <property name="username" value="shop" />
            <property name="password" value="123456" />
</bean>

Dockerfile文件的内容如下:

FROM tomcat:7.0.72-jre7
MAINTAINER WSZ

ADD build_image_share /build_image_share
RUN ["chmod","+x", "/build_image_share"]

WORKDIR /usr/local/tomcat/webapps
RUN cp /build_image_share/shoppingweb.war shoppingweb.war

WORKDIR /usr/local/tomcat/conf
RUN sed -i~ '/<\/Host/i <Context path="/" docBase="shoppingweb.war" debug="0" privileged="true" reloadable="true"/>' server.xml

CMD /usr/local/tomcat/bin/catalina.sh run

template_envfile的内容如下:

MYSQL_ROOT_PASSWORD=123456
MYSQL_ROOT_HOST=mysql
MYSQL_DATABASE=shoppingdb
MYSQL_USER=shop
MYSQL_PASSWORD=123456

在命令行里跑

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#下载mysql server
docker pull mysql/mysql-server
#跑起一个数据库容器,root密码设置为123456,数据库的host设置为mysql,数据库名为shoppingdb,数据库用户名和密码为shop和123456
docker run --name mysql2 --env-file ./template_envfile -d mysql/mysql-server
#一样的效果
#docker run --name mysql -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_ROOT_HOST=mysql -e MYSQL_DATABASE=shoppingdb -e MYSQL_USER=shop -e MYSQL_PASSWORD=123456 -d mysql/mysql-server
#进入mysql容器,初始化数据库
docker exec -it mysql mysql -uroot -p
#如果Access denied失败,则运行docker exec -it mysql2 bash,在容器内运行mysql -u root -p
#运行sql文件
#启动运行应用的容器
docker run --name shoppingwithlinkmysqlserver -d -p 8080:8080 --env-file ./template_envfile --link mysql:mysql shoppingweb:2.0
#一样的效果
#docker run --name shoppingwithlinkmysqlserver -p 8080:8080 -d -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_ROOT_HOST=mysql -e MYSQL_DATABASE=shoppingdb -e MYSQL_USER=shop -e MYSQL_PASSWORD=123456 --link mysql:mysql shoppingweb:2.0

参考书籍
《Docker 全攻略》张涛著 该本书有很长的篇幅介绍docker的命令
《Docker 官方文档》
mysql/mysql-server in Docker
Using Docker to spin up databases for development
Creating a MySQL Docker Container

Docker 学习笔记

Docker是什么

Docker是一个开放源代码软件专案,让应用程序布署在软件容器下的工作可以自动化进行,借此在Linux操作系统上,提供一个额外的软件抽象层,以及操作系统层虚拟化的自动管理机制。
Docker目标是实现轻量级的操作系统虚拟化解决方案。减少软件开发周期中最繁琐、最耗时的环境准备环节。
Docker满足了免费的、轻量级、可移植、虚拟化、资源隔离、数据统一、语言无关、封装后的镜像可以随处部署和迁移这些要求。

Docker优点

0)启动可以在秒级实现,比传统的虚拟机快很多
1)Docker的资源利用率比传统虚拟机要高,一台主机上可以同时运行数千个Docker容器
2)除了运行其中的应用,基本不消耗额外的系统资源,使得应用性能很高,同时系统开销小。
(传统的虚拟机方式运行10个不同的应用要10个虚拟机,Docker只要启动10个隔离的应用即可)
3)更快速的交付和部署,一次创建或配置可以在任意地方正常运行
4)更高效的虚拟化,Docker容器是内核级的虚拟化,可以实现更高的性能和效率
5)Docker支持跨节点部署,使得更轻松的迁移和扩展,Docker容器可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。兼容性高,用户可以把一个应用程序从一个平台直接迁移到另一个平台。“一次构建,自由分发。”
6)更简单的管理,小的修改代替大量的更新工作,所有的修改都以增量的方式被分发和更新,实现自动化并且高效的管理。
7)版本可控,组件可复用。镜像之间不是相互隔离的,镜像之间可以产生松耦合的关系。镜像是多层文件的联合体,这些文件层通过不同的组合可以产生不同的镜像,每一个镜像都有标签,标签代表了唯一的镜像,通过标签可以回溯加载特定的镜像。
8)共享镜像,Docker是开源的,构建的镜像可以上传到Docker Hub上
9)轻量,Docker是基于Linux内核进行的虚拟化操作,所有的容器共享内核资源的。

Docker缺点

1)宿主资源没有完成做到隔离
2)Golang语言尚未成熟
3)Docker虽已经开源,但在在未来可能收费

mxgraph

mxgraph

mxGraph是一套用于图表图形的显示交互的开发工具库。支持的技术有:Java JavaScript PHP .NET。

Java版本用于创建高性能的桌面应用程序。
JavaScript版本的mxGraph是一个JS绘图组件,适用于需要在网页中设计/编辑Workflow/BPM流程图、图表、网络图和普通图形的Web应用程序,即主要用在所有的工作于网页浏览器本身的应用。

JavaScript版本主要技术优势:
不需要第三方插件。
涉及的技术是开放的。
标准化的技术,应用程序可以轻松部署到最大数量的浏览器用户,而不需要在用户端的电脑进行额外的配置或安装。
JavaScript版本主要技术劣势:
渲染速度。当用户屏幕上可见的单元数量攀升到数百时,在大多数浏览器上重绘减缓到超出可接受的水平。 在信息管理理论中,同时显示数百个单元通常是不正确的,因为用户无法一下子理解这么多的数据。有几种方法, 我们统称为复杂性管理机制,可以用来限制信息的数量,达到合理水平,从而保持可接受的重绘率。 复杂性管理在本用户手册后面的章节中有详细描述。

下载地址: http://www.jgraph.com/mxdownload.html
中文手册: http://www.mxgraph.cn/doc/mxgraph

如何使用

在HTML页面加载mxGraph核心js文件即可使用mxGraph所有功能,几乎所有的js工具库都是这样使用的(JQuery ExtJs…)。

下载mxGraph并解压
https://github.com/jgraph/mxgraph/releases/tag/v3.6.0.0

mxGraph/javascript/src 目录下包含了开发mxGraph项目所需要的所有东西,复制一份到项目中并指定正确的路径即可使用

PS : 当显示的元素数量增多到上百个时, mxGraph的渲染速度会相当的缓慢。这将使用复杂性管理机制来解决。

mxgraph的包

JavaScript的版本分成8个package。顶层是 mxClient 类。
editor package 提供实现图表编辑器所需的类在这个包中的主类是mxEditor。

view and model packages 实现图形组件, mxGraph. 它指的是一个 mxGraphModel包含 mxCells和 缓存 mxGraphView的cells状态.
handler, layout and shape packages 包含事件监听器、布局算法和形状, 事件监听器包括 mxRubberband for rubberband selection, mxTooltipHandler for tooltips and mxGraphHandler for basic cell modifications. mxCompactTreeLayout implements a tree layout algorithm, and the shape package provides various shapes, which are subclasses of mxShape.
util package 提供了实用工具类包括mxclipboard复制粘贴, 拖放, mxConstants 键和值的样式, mxEvent and mxUtils 跨浏览器的事件处理和通用函数,mxResources 国际化 and mxLog 控制台输出。
io package 实现将JavaScript对象到XML的通用 mxObjectCodec 。 主类是 mxCodec. mxCodecRegistry 是全球注册自定义的编解码器。

hello,world简单例子

显示hello和world两个格子相连。

1)新建一个helloWorld.html文件
2)放入一个container用于装载mxGraph
3)检测浏览器支持与否
4)画图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script type="text/javascript">
mxBasePath = 'src/';
</script>
<script type="text/javascript" src="src/js/mxClient.js"></script>
</head>
<script type="text/javascript">
function main(container) {
// 检查浏览器支持
if (!mxClient.isBrowserSupported()) {
mxUtils.error('Browser is not supported!', 200, false);
}
else {
// 在指定容器中创建图形
var model = new mxGraphModel();
var graph = new mxGraph(container, model);
// 激活橡皮圈选择
new mxRubberband(graph);
// 拿到插入单元的默认父节点。
// 这通常是根节点的第一子节点(如0层)。
var parent = graph.getDefaultParent();
// 在一个步骤中,加入所有的单元到模型中
model.beginUpdate();
try {
var v1 = graph.insertVertex(parent, null,
'Hello', 20, 20, 80, 30);
var v2 = graph.insertVertex(parent, null,
'World', 200, 150, 80, 30);
var e1 = graph.insertEdge(parent, null, '', v1, v2);
}
finally {
// 更新显示
model.endUpdate();
}
}
}
;
</script>
<!-- 页面传递图形的容器给程序 -->
<body onload="main(document.getElementById('container'))">
<div id="container"></div>
</body>
</html>

在script中的mxBasePath:
一个JavaScript变量,用来定义CSS,图片,资源和js的使用的目录。是一段JavaScript代码,并需要被放置在脚本标记内。 它必须在加载mxClient.js之前,而且不应该斜线。

mxClient.js
这是mxGraph库的路径。

onload=”main(document.getElementById(‘container’))”
创建容器,onload加载网页时会被调用。它通过在传递即下定义的一个div 容器作为参数。
mxGraph组件将被放置在这个div容器中。

mxClient.isBrowserSupported()
任何mxGraph应用程序的第一行应该检查浏览器的支持,如果不支持应该适当退出。如果浏览器支持, mxGraph将在div容器内被创建,在开始/结束更新调用之间,三个单元被添加到图形中。

部分方法

// 无效
graph.setEnabled(false);

// 连接
graph.setConnectable(true);

// 提示信息
graph.setTooltips(true);

// 右键移动容器坐标轴
graph.setPanning(true);

// 容器大小自适应
graph.setResizeContainer(true);

// 使用浏览器默认的按键监听
new mxRubberband(graph);

// 动态改变样式
graph.getView().updateStyle = true;

// 重复连接
graph.setMultigraph(false);

// Label 将显示 Html 格式的 Value
graph.setHtmlLabels(true);

// 禁用浏览器默认的右键菜单栏
mxEvent.disableContextMenu(container);

// 允许移动 Vertex 的 Label
graph.setVertexLabelsMovable(true);

// 禁止改变元素大小
graph.setCellsResizable(false);

// 允许连线的目标和源是同一元素
graph.setAllowLoops(true);

//选中所有的线
raph.selectEdges();

//使线在所有元素的底下
graph.orderCells(true);

//取消选中的元素
graph.clearSelection();

——节点样式设置
1、如何设节点颜色?
通过属性fillColor(填充色)、gradientColor(渐变色),设为一样的颜色就是节点颜色了。
2、如何设置透明颜色?
在配置文件中none可设为默认颜色,transparent可设透明颜色
3、节点不可改变大小
0否,1是
4、节点与连线紧密连接,无空隙

5、节点颜色渐变方向?

6、如果不用配置文件?
你可以通过cell.setStyle(“XXXX”) 来实现样式的动态设置。
XXX形如:”样式属性=属性值;样式属性2=属性值2;”,可通过mxGraph官方doc搜索mxConstants来查找你需要的属性。
若设定选中节点的样式,还可通过graph.setCellStyles(mxConstants.属性,mxConstants.属性值);来设定该节点的样式。如:graph.setCellStyles(mxConstants.STYLE_GRADIENT_DIRECTION,mxConstants.DIRECTION_NORTH);
参考资料
mxGraph 教程1 - 开发入门指南
mxGraph 用户手册 – JavaScript客户端

Docker 学习笔记(一)

安装Docker

Mac 安装地址
关于在Ubuntu、CentOS和Debian系统安装Docker,Docker —— 从入门到实践书里有安装教程
其他系统安装Docker,参考官网

Boot2Docker

Boot2Docker 是一个包含了 VirtualBox 虚拟软件、Docker 程序和Boot2Docker Management Tool的一个集合软件。Boot2Docker Management Tool是一个运行在Mac系统上的轻量级Linux虚拟机。
这个轻量级虚拟机完全运行在内存中,启动很快,体积很小。
通过Boot2Docker安装的Docker相当于运行在一个 VirtualBox 虚拟机中,而客户端运行在Mac OS 上。
(Mac直接安装Docker,不通过Boot2Docker安装感觉起来没什么区别啊?)

Docker 三大组件

仓库
镜像
容器

仓库

Docker 官方维护了一个公共仓库Docker Hub

登录

本地命令:docker login
会有提示输入用户名和密码,一次登录后,会记住登录信息。本地用户目录的 .dockercfg 中将保存用户的认证信息。

搜索

命令为docker search 命令来查找官方仓库中的镜像,并利用docker pull 命令来将它下载到本地。

1
$ docker search centos -s 3

-s N(仅显示评价为 N 星以上的镜像)
下载命令

1
$ sudo docker pull centos

私有仓库配置

参考书籍Docker —— 从入门到实践

Docker 镜像

Docker 镜像分层

有3层,分别为:
基础镜像
中间件镜像
应用镜像

获取镜像

1
docker pull 镜像版本

将从 Docker Hub 仓库下载一个符合要求的镜像
完整的命令为:

1
$ sudo docker pull registry.hub.docker.com/ubuntu:12.04

即从注册服务器registry.hub.docker.com 中的 ubuntu 仓库来下载标记为 12.04 的镜像。

镜像操作

下载镜像完成后,可以使用该镜像了,摘自书中的例子:创建一个容器,让其中运行 bash 应用。

1
$ sudo docker run -t -i ubuntu:12.04 /bin/bash

显示本地已有的镜像

命令:docker images

修改镜像:

1.docker commit 来扩展一个镜像
镜像启动容器:

1
$ sudo docker run -t -i ubuntu:12.04 /bin/bash

-t 选项让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上, -i 则让容器的标准输入保持打开

在容器中添加json package:

1
root@0b2616b0e5a8:/# gem install json

退出: exit
提交更新后的副本:

1
sudo docker commit -m "Added json gem" -a "Docker Newbee" 0b2616b0e5a8 ouruser/sinatra:v2

ouruser/sinatra:v2为目标镜像的仓库名和 tag 信息,如v2

2.docker build 来创建一个新的镜像
新建一个文件Dockerfile,里面的每一条指令都创建镜像的一层。

Dockerfile 基本的语法是:
使用 # 来注释
FROM 指令告诉 Docker 使用哪个镜像作为基础
接着是维护者的信息
RUN 开头的指令会在创建中运行,比如安装一个软件包,在这里使用 apt-get来安装了一些软件

编写完成 Dockerfile 后可以使用 docker build 来生成镜像。例如:

1
$ sudo docker build -t ouruser/sinatra:v2 .

其中 -t 标记来添加 tag,指定新的镜像的用户信息。 “.” 是 Dockerfile 所在的路径(当前目录),也可以替换为一个具体的 Dockerfile 的路径。

改变镜像标签内容

1
$ sudo docker tag 5db5f8471261 ouruser/sinatra:devel #5db...是镜像ID 后面的是修改后的标签

本地导入镜像:

1
sudo cat ubuntu-14.04-x86_64-minimal.tar.gz |docker import - ubuntu:14.04

镜像上传到远程仓库:

Docker Hub 上完成注册后,可以推送自己的镜像到仓库中

1
$ sudo docker push ouruser/sinatra

保存镜像到本地文件:

1
docker save -o ubuntu_14.04.tar ubuntu:14.04

从导出的本地文件中再加载到本地镜像库:

1
2
3
$ sudo docker load --input ubuntu_14.04.tar
$ sudo docker load < ubuntu_14.04.tar

移除镜像

1
docker rmi training/sinatra

在删除镜像之前要先用 docker rm 删掉依赖于这个镜像的所有容器。

清理所有未打过标签的本地镜像

1
2
$ sudo docker rmi $(docker images -q -f "dangling=true")
sudo docker rmi $(docker images --quiet --filter "dangling=true")

Docker 容器

容器是独立运行的一个或一组应用,以及它们的运行态环境。

启动容器

有两种方式:
1.基于镜像新建一个容器并启动

1
$ sudo docker run -t -i ubuntu:12.04 /bin/bash

使用docker run 来创建容器时,Docker 在后台运行的标准操作包括:

  • 检查本地是否存在指定的镜像,不存在就从公有仓库下载
  • 利用镜像创建并启动一个容器
  • 分配一个文件系统,并在只读的镜像层外面挂载一层可读写层
  • 从宿主主机配置的网桥接口中桥接一个虚拟接口到容器中去
  • 从地址池配置一个 ip 地址给容器
  • 执行用户指定的应用程序
  • 执行完毕后容器被终止

2.将在终止状态(stopped)的容器重新启动
docker start命令,直接将一个已经终止的容器启动运行。可以利用 ps 或 top 来查看当前的进程信息。

查看容器信息

命令:docker ps
可以查看所有的容器及进程

容器在后台运行

普通的docker run命令会在当前宿主机输出结果。
加上-d,此时容器会在后台运行并不会把输出的结果打印到宿主机上面,而是返回一个ID。

1
sudo docker run -d ubuntu:14.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"

查看输出结果可以用docker logs

1
$ sudo docker logs [container ID or NAMES]

里面可以是刚刚返回的ID也可以是NAME

终止容器

使用命令docker stop ID来终止

当Docker容器中指定的应用终结时,容器也自动终止。所以之前通过 exit 命令或 Ctrl+d 来退出终端时,所创建的容器立刻终止。
查看终止状态的容器可以用 docker ps -a命令。

重启已经终止的容器

处于终止状态的容器,可以通过 docker start 命令来重新启动。
docker restart 命令会将一个运行态的容器终止,然后再重新启动它。

进入容器

当容器进入后台时,需要进入容器的操作
使用Attach命令:

1
2
3
docker run -idt ubuntu #后台运行
docker ps #查看进程
docker attach 容器名或者ID

使用nsenter 命令
nsenter工具在 util-linux 包2.23版本后包含,设备上没有的,需要手动安装。
详细步骤参考书籍Docker —— 从入门到实践

导出容器

导出本地某个容器,使用 docker export 命令:

1
$ sudo docker export 容器ID > ubuntu.tar(文件名)

导入容器

使用docker import从容器快照文件中再导入为镜像:

1
2
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0(镜像名和标签)
$ docker images #查看结果

或者通过指定 URL 或者某个目录来导入,例如:

1
$sudo docker import http://example.com/exampleimage.tgz example/imagerepo

用户既可以使用 docker load 来导入镜像存储文件到本地镜像库,
也可以使用 docker import 来导入一个容器快照到本地镜像库。
这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。
此外,从容器快照文件导入时可以重新指定标签等元数据信息。

删除容器

使用 docker rm 来删除一个处于终止状态的容器

1
$sudo docker rm trusting_newton

如果要删除一个运行中的容器,可以添加 -f 参数。
清理所有处于终止状态的容器,使用以下命令:

1
docker rm $(docker ps -a -q)

书籍:
Docker —— 从入门到实践此处有电子书的下载途径

Docker 学习笔记(二)

Docker中的某些命令详解

logs 获取容器内部应用输出的日志,只能获取到重定向到STDOUT和STDERR的日志

attach 挂载到正在运行的容器中,并且可以在容器中执行任何命令

可以通过ID和name挂载

1
2
3
4
5
➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6caf2b2b8d00 b1873e247b48 "/bin/bash" 32 minutes ago Up 32 minutes reverent_thompson
➜ ~ docker attach 6caf2b2b8d00
root@6caf2b2b8d00:/#

或者

1
2
➜ ~ docker attach reverent_thompson
root@6caf2b2b8d00:/#

通过Ctrl+P+Q来退出容器,容器还在运行中。
使用exit退出容器,容器同步被关闭。

1
2
3
root@6caf2b2b8d00:/# exit 10
➜ ~ docker ps -a |grep reverent_thompson
6caf2b2b8d00 b1873e247b48 "/bin/bash" 52 minutes ago Exited (10) 45 seconds ago reverent_thompson

Exited (10) 容器的退出响应码变成了10,即可以通过exit命令来指定容器退出码。

commit 以某个容器为基础保存成一个新的image

-a, –author=””这个参数的目标是保存新的作者。
-c, –change=[]这个参数保存的是修改image里面的配置参数。
目前支持的可以修改的配置参数有CMD|ENTRYPOINT|ENV|EXPOSE|LABEL|ONBUILD|USER|VOLUME|WORKDIR。其余的参数继承于上一个image

1
2
➜ ~ docker commit --change "ENV DEBUG true" 3af7938d2d31 svendowideit/testimage:version1
sha256:c24a8c4cf76d9485e69fa4d975a6b8ccc3070dbf368a118672ab6a76a4249605

3af7938d2d31是一个正在运行的容器,svendowideit/testimage是repository的名字,version1是tag的内容
查看结果:

1
2
➜ ~ Docker inspect -f "{{ .Config.Env}}" c24a8c4cf76d
[DEBUG=true no_proxy=local PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin]

-m, –message=””这个参数是提交一个comment备注信息,说明image的用途,修改的内容等信息。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
➜ ~ docker commit -m "test message" 3af7938d2d31 svendowideit/testimage:version2
sha256:0d4f24b6a8d7d7239b9cfa163845c09866f30de9cc7ac23d48510e3ac095f6a4
➜ ~ Docker inspect 0d4f24b6a8d7
[
{
"Id": "sha256:0d4f24b6a8d7d7239b9cfa163845c09866f30de9cc7ac23d48510e3ac095f6a4",
"RepoTags": [
"svendowideit/testimage:version2"
],
"RepoDigests": [],
"Parent": "sha256:78a0e8d9b39cdcbb93d433ec5de61ba6c6d10369eccdf45b7e60318b47356d9a",
"Comment": "test message",
.........
}
]

-p, –pause=true 默认情况下执行commit时,容器中所有进程处于暂停状态,如果此时容器对外提供服务,那其他容器会受到影响。
-pause=false使用这个参数使得在执行commit时保持容器内进程继续工作。但是commit效率会下降。

create 创建容器

create命令是以image为模板创建的一个容器,并且在容器文件的最上面一层添加一个读写层,容器里面的数据变化会被保存在读写层。
create成功后需要通过start来启动容器。
参数:
-i 将Host的Stdin和容器的Stdin相连接
-t 创建一个pty终端
(-it 前两者结合使用相当于在host环境中启动一个链接容器的pty伪终端,在host的pty中输入的数据就会传到容器之中。)

1
2
3
4
5
➜ ~ docker create -it addwszfile bash
7019c6b575fe673954f2019a99fb6679b7a2f4a604d324290ff5111c42feae2b
➜ ~ docker create -it -v /dockerforme/sinatra/share_folder/:/tmp --name data1 addwszfile
7cfc3205a8df843d1b7219a31f51976b5df638cb20a03c57134ec690da990875
➜ ~ docker create -P --name data2 addwszfile

addwszfile是image名称。

-p 映射网络端口,当应用需要访问其他应用或被访问,需要开放一个端口
-P 容器中所有需要暴露的端口都会映射到主机随机的一个端口(端口范围49153~65535)
-v 挂载参数,把主机的一个文件或者一个目录挂载到容器中

diff 比较容器文件文件层数据差异的命令

首先启动一个容器

1
$ sudo docker run -t -i ubuntu:12.04 /bin/bash

在容器中做某些操作

1
2
3
4
5
6
7
root@543f21cb4a4e:/# touch /tmp/wsz.txt
root@543f21cb4a4e:/# echo "This is wsz add test" > /tmp/wsz.txt
root@543f21cb4a4e:/# touch /tmp/wsz2.txt
root@543f21cb4a4e:/# rm /tmp/wsz2.txt
root@543f21cb4a4e:/# touch /tmp/wsz3.txt
root@543f21cb4a4e:/# echo "This is a change test" > /tmp/wsz3.txt
root@543f21cb4a4e:/# echo "I add a new line" >> /tmp/wsz3.txt

新开一个命令行窗口,查看diff

1
2
3
4
5
6
7
➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
543f21cb4a4e ubuntu:12.04 "/bin/bash" 4 minutes ago Up 4 minutes trusting_bell
➜ ~ docker diff 543f21cb4a4e
C /tmp
A /tmp/wsz.txt
A /tmp/wsz3.txt

结果中的A表示add;C表示Change;D表示Delete
可以看见之中我们是创建一个wsz2.txt的文件然后又删除了,但是在diff中并没有反应出来,这是因为我们操作产生的数据保存在最上面的读写层里,diff比较的是最上层和其他层的差异,其中wsz2的文件不存在于其他层所以不显示。
若想时时刻刻保存状态,可以save读写层,再进行删除。

1
2
3
4
➜ ~ docker commit 543f21cb4a4e addwszfile
sha256:b1873e247b48d2ffb12725552691db3c8a2d1f097efdfbb04aeddb9cf13d8c9c
➜ ~ docker run -it b1873e247b48d2ffb12725552691db3c8a2d1f097efdfbb04aeddb9cf13d8c9c
root@6caf2b2b8d00:/# rm /tmp/wsz.txt

新开一个命令行窗口可以看到删除的内容

1
2
3
4
5
6
➜ ~ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
6caf2b2b8d00 b1873e247b48 "/bin/bash" 40 seconds ago Up 39 seconds reverent_thompson
➜ ~ docker diff 6caf2b2b8d00
C /tmp
D /tmp/wsz.txt

events 获取容器所报告的事件

Docker容器再运行过程中会向Docker Daemon报告如下事件:
create、destroy、die、export、kill、oom、pause、start、stop、unpause
Image镜像会向Docker Daemon报告untag、delete事件
筛选的几种方式:
–since 指从某一个时间点开始Docker Daemon接收的所有事件。

1
➜ ~ docker events --since '2016-09-01'

–until 需要和 –since结合使用,指的是从某一个时间点开始到某一个时间点结束。

1
➜ ~ docker events --since '2016-09-01' --until '2016-11-08T15:00:00'

–filter 接收的是key=value的键值对。目前有效的key是container、event、image。

1
2
3
➜ ~ docker events --filter 'event=stop'
➜ ~ docker events --filter 'image=ubuntu:14.04'
➜ ~ docker events --filter 'container=543f21cb4a4e'

exec 在容器中运行其他命令

执行ps删选进程

1
➜ ~ docker exec 6caf2b2b8d00 ps -s|grep bash

创建一个新文件

1
➜ ~ docker exec 6caf2b2b8d00 touch /tmp/1.txt

修改文件,前提需要申请一个pty和绑定一个stdin用来保存编辑的文件,因为没有pty容器无法收到控制字符

1
➜ ~ docker exec -it 6caf2b2b8d00 vi /tmp/1.txt

history 追踪image的历史记录

diff 查看的是容器中文件系统的变更历史。History查看的是image的变更历史。

1
➜ ~ docker history ouruser/sinatra:v3

-H,–human 表示是否输出方便用户识别的格式。
–no-trunc=false 默认false,表示将输出按照一定的格式进行截断。
–quiet 若是设置为true,只返回image构建过程中所有的临时文件层的image ID,为false时返回所有信息。

info 显示Docker摘要信息

info 指令显示Docker摘要信息,用来确认Docker信息。包含Docker内核版本号、Docker所设定的CPU、内存参数等环境底层信息。

1
➜ ~ docker info

inspect 返回指定目标的底层信息

inspect可以返回image和container的底层信息。包括某个容器的IP地址,MAC地址,端口信息等等。
返回的几类信息:
1.基本信息类:名称,创建时间,当前状态(运行、停止、暂停)等
2.运行参数类:网络信息、环境变量、主机名称等
3.底层数据类:CPU设置、内存设置、虚拟化设置等

1
2
3
docker inspect -f {{要查看的参数名}} ID号
docker inspect -f '{{json .Config}}' ID号
docker inspect -f '{{(index (index .Config "Env") 0)}}' ID号 取出Config中Env第一个元素命令

json是指把数据格式化成json字符串
index取第几个

kill 强制停止容器

一般使用stop关掉容器。出现异常使用kill

1
docker kill 容器名

ps 查看当前容器的运行状态

rename 对容器重命名

docker rename 旧名字 新名字

tag 对镜像重命名

……还有很多命令看文档

参考书籍
《Docker 全攻略》张涛著
《Docker 官方文档》

本站总访问量 次, 访客数 人次, 本文总阅读量
靡不有初,<br><br>鲜克有终。<br><br>不断努力,一步一步往前走,做最好的自己