Dockerfile基本结构与指令

Dockerfile是一个文本格式的配置文件,用户可以使用Dockerfile快速创建自定义的镜像。

基本结构

Dockerfirl由一行行命令语句组成,并且支持以#开头的注释行。 一般而言,Dockerfile分为四部分:基础镜像信息、维护者信息、镜像操作指令和容器启动时执行指令。例如:

# This dockerfile uses the ubuntu image
# VERSION 2 - EDITION 1
# Author: docker_user
# Command format: Instruction [arguments / command] ..

# Base image to use, this must be set as the first line
FROM ubuntu

# Maintainer: docker_user  (@docker_user)
MAINTAINER docker_user docker_user@email.com

# Commands to update the image
RUN echo "deb http://archive.ubuntu.com/ubuntu/ raring main universe" >> /etc/apt/sources.list
RUN apt-get update && apt-get install -y nginx
RUN echo "\ndaemon off;" >> /etc/nginx/nginx.conf

# Commands when creating a new container
CMD /usr/sbin/nginx

其中,一开始必须指明所基于的镜像名称,接下来推荐说明维护者信息。 后面则是镜像操作指令,例如RUN指令,RUN指令将对镜像执行跟随的命令。每运行一条 RUN 指令,镜像添加新的一层,并提交。最后是 CMD指令,来指定运行容器时的操作命令。

指令

指令的一般格式为:INSTRUCTION arguments,指令包括 FROM、MAINTAINER、RUN 等。

FROM

格式为 FROM 或FROM :。 第一条指令必须为 FROM 指令。并且,如果在同一个Dockerfile中创建多个镜像时,可以使用多个 FROM 指令(每个镜像一次)。

MAINTAINER

格式为 MAINTAINER ,指定维护者信息。

RUN

格式为 RUN 或RUN ["executable", "param1", "param2"]。 前者将在 shell 终端中运行命令,即 /bin/sh -c;后者则使用 exec 执行。指定使用其它终端可以通过第二种方式实现,例如RUN ["/bin/bash", "-c", "echo hello"]。 每条 RUN指令将在当前镜像基础上执行指定命令,并提交为新的镜像。当命令较长时可以使用 \ 来换行。

CMD

支持三种格式

  • CMD ["executable","param1","param2"] 使用 exec 执行,推荐方式;
  • CMD command param1 param2 在 /bin/sh 中执行,提供给需要交互的应用;
  • CMD ["param1","param2"] 提供给 ENTRYPOINT 的默认参数;

指定启动容器时执行的命令,每个 Dockerfile 只能有一条 CMD命令。如果指定了多条命令,只有最后一条会被执行。 如果用户启动容器时候指定了运行的命令,则会覆盖掉 CMD指定的命令。

EXPOSE

格式为 EXPOSE [...]。
告诉 Docker 服务端容器暴露的端口号,供互联系统使用。在启动容器时需要通过 -P,Docker 主机会自动分配一个端口转发到指定的端口。

ENV

格式为 ENV
指定一个环境变量,会被后续 RUN 指令使用,并在容器运行时保持。 例如

ENV PG_MAJOR 9.3
ENV PG_VERSION 9.3.4
RUN curl -SL http://example.com/postgres-$PG_VERSION.tar.xz | tar -xJC /usr/src/postgress && …
ENV PATH /usr/local/postgres-$PG_MAJOR/bin:$PATH

ADD

格式为 ADD
该命令将复制指定的 到容器中的 。 其中 可以是Dockerfile所在目录的一个相对路径;也可以是一个 URL;还可以是一个 tar 文件(自动解压为目录)。

COPY

格式为 COPY
复制本地主机的 (为 Dockerfile 所在目录的相对路径)到容器中的
当使用本地目录为源目录时,推荐使用 COPY。

ENTRYPOINT

两种格式:

  • ENTRYPOINT ["executable", "param1", "param2"]
  • ENTRYPOINT command param1 param2(shell中执行)。

配置容器启动后执行的命令,并且不可被 docker run 提供的参数覆盖。 每个 Dockerfile 中只能有一个 ENTRYPOINT,当指定多个时,只有最后一个起效。

VOLUME

格式为 VOLUME ["/data"]。 创建一个可以从本地主机或其他容器挂载的挂载点,一般用来存放数据库和需要保持的数据等。

USER

格式为 USER daemon。 指定运行容器时的用户名或 UID,后续的 RUN 也会使用指定用户。 当服务不需要管理员权限时,可以通过该命令指定运行用户。并且可以在之前创建所需要的用户,例如:RUN groupadd -r postgres && useradd -r -g postgres postgres。要临时获取管理员权限可以使用 gosu,而不推荐 sudo。

WORKDIR

格式为 WORKDIR /path/to/workdir。 为后续的 RUN、CMD、ENTRYPOINT 指令配置工作目录。 可以使用多个 WORKDIR 指令,后续命令如果参数是相对路径,则会基于之前命令指定的路径。例如

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

则最终路径为 /a/b/c。

ONBUILD

格式为 ONBUILD [INSTRUCTION]。 配置当所创建的镜像作为其它新创建镜像的基础镜像时,所执行的操作指令。 例如,Dockerfile 使用如下的内容创建了镜像 image-A。

[...]
ONBUILD ADD . /app/src
ONBUILD RUN /usr/local/bin/python-build --dir /app/src
[...]

如果基于 image-A 创建新的镜像时,新的Dockerfile中使用 FROM image-A指定基础镜像时,会自动执行 ONBUILD指令内容,等价于在后面添加了两条指令。

FROM image-A

#Automatically run the following
ADD . /app/src
RUN /usr/local/bin/python-build --dir /app/src

使用 ONBUILD指令的镜像,推荐在标签中注明,例如 ruby:1.9-onbuild。

2015/6/7 posted in  Docker

Docker网络基础

大量的互联网应用服务包括多个服务组件,这往往需要多个容器之间通过网络通信进行相互配合。 Docker目前提供了映射容器端口到宿主主机和容器互联机制来为容器提供网络服务。

端口映射实现访问容器

在启动容器的时候,如果不指定对应参数,在容器外部是无法通过网络来访问容器内的网络应用和服务的。

从外部访问容器应用

当容器中运行一些网络应用,要让外部访问这些应用时,可以通过-P或-p参数来指定端口映射。当使用-P标记时,Docker会随即映射49000~49900中的端口至容器内部开放的网络端口。 小写-p则可以指定要映射的端口,并且,在一个指定端口上只可以帮顶一个容器。支持的格式有:

  • ip:hostPort:containerPort
  • ip::containerPort
  • hostPort:containerPort

映射所有接口地址

使用hostPort:containerPort格式将本地5000端口映射到容器5000端口(之前创建私有仓库的例子):

docker run -d -p 5000:5000 registry

这时默认会帮顶本地所有接口上的所有地址。可以多次使用-p参数从而映射多个端口。

映射到指定地址的指定端口

可以使用ip:hostPort:containerPort格式指定映射使用一个特定地址,比如locakhost地址127.0.0.1。

docker run -d -p 127.0.0.1:5000:5000 registry

映射到指定地址的任意端口

可以使用ip::containerPort格式帮顶localhost的任意端口到容器的5000端口,本地主机会自动分配一个端口:

docker run -d -p 127.0.0.1::5000 registry

可以使用udp标记来指定udp端口:

docker run -d -p 127.0.0.1:5000:5000/udp registry

查看端口映射配置

可以使用docker port命令来查看当前映射的端口配置,也可以查看绑定的地址:

docker port registry 
5000/tcp -> 0.0.0.0:5000
docker port registry 5000
0.0.0.0:5000

容器有自己的内部网络和IP地址(使用docker inspect + ID可以获取所有变量值)。

容器互联实现容器间通信

容器的连接(linking)系统是除了端口映射外另一种可以与容器中应用进行交互的方式。他会在源和接收容器之间创建一个隧道,接受容器可以看到源容器指定的信息。

容器互联

使用--link参数可以让容器之间安全的进行交互。 比如创建一个数据库容器:

docker run --name dbserver mysql

让后创建一个容器,并将这个容器连接到dbserver容器:

docker run -d --name likeweb --link dbserver:dbserver ubuntu

因为要连接的容器并没有启动,所以建立完容器之后会报错,这里只是演示--link,请忽略。 --link参数的格式为--link name:alias,其中name是要链接的容器的名称,alias是这个连接的别名。 可以用docker ps 命令查看连接情况。 这样Docker两个容器之间创建了一个安全隧道而不需要开放外部端口,避免了数据库端口到外部网络上。 Docker通过两种方式为容器公开连接信息:

  • 环境变量
  • 更新/etc/hosts文件

可以使用env命令来查看容器的环境变量。 Docker目前采用了Linux系统自带的网络系统来实现对网络服务的支持,这既可以利用现有成熟的技术提供稳定的支持,又可以实现快速的高性能转发。

2015/6/6 posted in  Docker

Docker数据管理

用户在使用Docker的过程中,往往需要能查看容器内应用产生的数据,或者需要把容器内的数据进行备份,甚至多个容器之间进行数据共享,这必然涉及到Docker的数据管理。 容器中管理数据主要有两种方式:

  • 数据卷(Data Volumes)
  • 数据卷容器(Data Volumes Dontainers)

数据卷

数据卷是一个可供容器使用的特殊目录,他绕过文件系统,可以提供很多有用的特性:

  • 数据卷可以在容器之间共享和重用
  • 对数据卷的修改会立马生效
  • 对数据卷的更新,不会影响到镜像
  • 卷会一直存在,直到没有容器使用

数据卷的使用,类似于Linux下对目录或文件进行mount操作。

创建一个数据卷

在使用docker run命令的时候,使用-v参数可以在容器内创造一个数据卷,如果多次使用-v参数可以创造多个数据卷。 比如创建一个私有仓库的时候:

docker run -d -p 5000:5000 --name registry -v /tmp/registry registry

创建一个/tmp/registry数据卷挂在到这个容器里。

挂在一个主机目录作为数据卷

使用-v参数也可以制定挂在一个本地已有的目录到容器中作为数据卷,还是举创建私有仓库的例子:

docker run -d -p 5000:5000 --name registry -v /data/registry:/tmp/registry registry

这样便是把本地的/data/registry挂在到容器中作为/tmp/registry。不过本地目录一定要是绝对路径,如果,目录不存在Docker会自动创建。 另外,Docker挂在数据卷的默认权限是读写(rw),也可以使用(ro)指定为只读权限:

docker run -d -p 5000:5000 --name registry -v /data/registry:/tmp/registry:ro registry

这样容器就没有写入的权限了。

挂在一个主机文件作为数据卷

也是可以把主机的一个文件挂在到容器中作为数据卷,使用命令和挂在目录一样。 不过直接挂在一个文件到容器中,又在外部编辑文件有可能导致文件inode信息的改变,从Docker1.1.0起,这回导致错误信息。 所以建议挂在目录以达到访问文件的目的。

数据卷容器

如果需要在容器之间共享一些持续的更新的数据,最简单的方式是使用数据卷容器。数据卷容器其实就是一个普通的容器,专门用它提供数据卷提供其他容器挂载。使用方法如下: 首先,创建一个数据卷容器比如dbdata,并在其中创建一个数据卷并挂在到/dbdata。

docker run -it -v /dbdata --name dbdata ubuntu

然后可以在其他容器使用--volumes-from来挂载dbdata容器中的数据卷。例如创建db1和db2两个容器,并从dadata容器挂在数据卷。

docker run -it --volumes-from dbdata --name db1 ubuntu
docker run -it --volumes-from dbdata --name db2 ubuntu

完成后,db1和db2都挂载同一个数据卷到相同的/dbdata目录。三个容器任何一方在该目录下的写入,其他容器都可以看得到。 可以多次使用--volumes-from从而从多个容器中获得多个数据卷,也可以从其他已经挂在容器卷的容器挂在数据卷。 使用--volumes-from参数所挂载的容器自身不需要保存在运行状态。

删除数据卷容器

如果删除了挂载的容器比如删除了dbdata,db1,数据卷并不会被自动删除,因为此时还有一个db2正在使用这个数据卷容器。 如果想删除一个数据卷必须在删除最后一个还挂载着它的容器时显式使用docker rm-v命令来制定同时删除关联的容器。

利用数据卷容器迁移数据

可以利用数据卷容器对其中的数据卷进行备份、回复,以实现数据的迁移。

备份

使用下面的命令来备份dbdata数据卷容器内的数据卷:

docker run --volumes-from dbdata -v $(pwd):/backup --name worker ubuntu tar cvf /backup/backup.tar /dbdata

很长,慢慢分析,首先是要建立一个利用ubuntu镜像容器,把本地当前路径挂在到容器的/backup下,容器执行的命令是tar cvf /backup/backup.tar /dbdata,命令达到的效果就是吧/dbdata压缩并保存到/backup下。 因为/backup是由本地当前路径挂载的,因此就是保存到当前路径下,从而实现备份的效果。

恢复

如果要恢复数据到容器中,可以按照下面的操作。 首先创建一个带有数据卷的容器dbdata2:

docker run -v /dbdata --name dbdata2 ubuntu /bin/bash

然后创建另一个新容器,挂在dbdata2容器,使用tar解压备份文件到所挂载的容器卷中即可:

docker run --volumes-from dbdata2 -v $(pwd):/backup ubuntu tar xvf /backup/backup.tar

容器是廉价的,数据是宝贵的,根据数据卷和本地的挂载以及容器之间容器卷的共享实现数据备份和还原很方便(说的好违心),总之,数据备份和恢复是没有问题了。

2015/6/5 posted in  Docker

搭建和使用Docker私有仓库

有没有感觉下载DockerHub的镜像很慢?至少我感觉慢的不行了,恰好学到了怎么搭建私有仓库,可以把常用的镜像在本地管理。 安装Docker之后,可以是使用官方提供的registry镜像来搭建一套本地私有仓库环境:

docker run -d -p 5000:5000 registry

输入之后就可以等待了,其实本地仓库本身就是容器,这句命令会下载并创建一个registry容器,创建本地的私有仓库。 默认情况下,会将仓库创建在容器的tmp/registry目录下,当然,可以通过-v参数将镜像文件存放到本地的指定路径上。 因为我的腾讯云加了一个10G的数据卷mount在了/data,所以直接:

docker run -d -p 5000:5000 --name registry -v /data/registry:/tmp/registry registry

然后就建好了本地的仓库。比如先上传一个ubuntu的镜像。 然后就可以管理这个私有仓库,上传镜像前需要使用docker tag命令将这个镜像标记为127.0.0.1:500/ubuntu。 标签的格式为:[:TAG][REGISTRYHOST/][USERNAME/]NAME[:TAG]

docker tag ubuntu:latest 127.0.0.1:5000/ubuntu
docker images 
REPOSITORY                    TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
dbserver                      lastest             b81857c55926        19 hours ago        281.8 MB
ubuntu                        14.04               ab80404d13d5        25 hours ago        215.4 MB
mysql                         latest              e0db8fe06e30        36 hours ago        283.5 MB
ubuntu                        latest              fa81ed084842        4 days ago          188.3 MB
127.0.0.1:5000/ubuntu         latest              fa81ed084842        4 days ago          188.3 MB
registry                      latest              d849e35be7b0        9 days ago          413.9 MB

可以使用docker pull上传标记的镜像:

docker push 127.0.0.1:5000/ubuntu

在获取镜像的时候,只需要在前面加127.0.0.1:5000便可以了。 不过这只是在本地提供服务,因为Docker传输要求https,因此还没有尝试证书问题,所以对外提供服务等有机会再尝试。

2015/6/5 posted in  Docker

Docker容器管理

容器是Docker第二个核心概念,简单的的说容器是镜像的一个运行实例,所不同的是,它带有额外的可写文件层。 如果说虚拟机是模拟运行了一整套操作系统(提供运行态环境和其他系统环境)和跑在上面的应用。那么Docker容器就是独立运行的一个或一组应用,以及他们的必须运行环境。

新建容器

Docker的容器十分轻量,用户可以随意的创建或删除容器。 可以使用docler create创建一个容器,比如创建一个Mysql的容器:

docker create -it mysql:latest

然后使用ps命令就可以查看所有的容器,不过要加上-a参数,因为create后的容器默认是不开启的。

docker ps -a
CONTAINER ID        IMAGE               COMMAND                CREATED              STATUS                   PORTS               NAMES
5f660cfdf9b1        mysql:latest        "/entrypoint.sh --na   About a minute ago                                                goofy_newton        
d8990fec2141        ubuntu:latest       "/bin/bash"            3 hours ago          Exited (0) 3 hours ago                       serene_payne

如果要开启这个容器,就需要docker start

docker start 5f660cfdf9b1

在start可以加ID,也可以加容器的名字,可以用--name在创建的时候为容器起名字,如果不起名的话,docker会自动为它分配一个。

新建并启动容器

启动容器的方式有两种,一是创建一个容器并启动这个容器,另一种就是一个将一个处于中止状态的容器重新启动。 如果是创建一个容器并立即启动,就是使用的docker run命令,这个命令等价与docker create 加 docker start。 当用docker run来创建一个容器时,操作包括:

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

比如使用ubuntu镜像执行bash终端:

docker run -t -i ubuntu:14.04 /bin/bash
root@ce5e7fe50200:/# 

其中-t参数是让docker分配一个伪终端并绑定到容器的标准输入上,-i则让容器的标准输入保持打开。 exit或ctrl+D退出之后,容器也就处于了终止状态,因为bash程序结束了。 更多时候,需要容器在后台以守护态运行,可以通过添加-d参数来实现。 比如:

docker run -d ubuntu:14.04

终止容器

可以使用docker stop终止一个正在运行的容器,命令格式为:

docker stop [-t|--time[=10]]

它会首先想容器发送SIGTERM信号,然后等待一段时间(默认10s),再发送SIGKILL信号终止容器。 当然,可以使用docker kill命令直接发送SIGKILL信号强行终止容器。 如果想重新启动一个容器只需要docker restart

进入容器

因为在使用-d参数之后,容器启动之后便会进入后台,用户无法看到容器中的信息,某些时候需要近土容器进行操作,有多种方法进入容器,包括docker attach命令、docker exec命令,以及nsenter工具。

attach命令

docker attach是docker自带的命令,使用简单docker attach 后面跟ID或容器名就可以,不过attach命令有时候并不方便,当多个窗口同时attach到同一个容器的时候,所有的窗口都会同步显示,当某个窗口因命令阻塞时,其他窗口也就没法进行别的操作了。

exec命令

这也是昨天看直播的时候讲到的命令,目测很好用,exec是docker 1.3版本提供的工具,可以直接在容器内运行命令,例如进入一个容器并启动一个bash:

docker exec -ti [ID|NAME] /bin/bash

nsenter工具

nsenter是util-linux包2.23版本后包含的工具,并不是docker的自带工具,需要安装util-linux包,使用起来稍麻烦,不打算尝试。

删除容器

使用docker rm指令便可以删除指定容器,语法格式:

docker rm [OPTIONS] CONTAINER[CONTAINER..]

支持的选项包括:

  • -f,--force=false 强行终止并删除一个运行中的容器
  • -l,--link=false 删除容器链接,但保留容器
  • -v,--columes=false删除容器挂载的数据卷

导出容器

导出容器是指导出一个已经创建好的容器到一个文件,不管这个容器是否处于运行状态都可以导出。 使用导出命令docker export命令,该命令使用格式为:

docker export CONTAINER

比如:

docker export boring_galileo > dbserver.tar

可以将这些文件传输到其他机器上,在其他机器通过导入命令实现容器的迁移。

导入容器

导出的文件又可以使用docker import命令导入,成为镜像,例如: 把刚才导出的文件再导入:

cat dbserver.tar | docker import - dbserver:lastest

这个效果和Docker镜像的创建、存出、载入中的基于本地模板导入非常一致。 实际上,既可以通过docker load导出镜像存储文件到本地镜像库,又可以使用docker import命令导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢失所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。

2015/6/4 posted in  Docker