Docker的镜像和容器

什么是Image?

废话不多说,先上图


image.png

最底层的Linux Kernel就是宿主机的操作系统内核,这一部分是共享的,称之为boot filesystem。在这之上,是我们的Base Image,是文件和meta data的集合,也称为root filesystem,这一层就是我们的系统镜像,比如Ubuntu,CentOS等,这个镜像只包含操作系统,不包含其它软件。我们在base image之上可以安装一些软件,比如Nginx,PHP等,这会产生新的一层image,如图中image123,都是在系统之上装了一些软件,而image4是在image2的基础上,又装了一些软件,则会产生新一层image。每一层image都是read only的,生成以后就不能改变。

Image获取

那么Image是怎么获取的?在Docker中有个Dockerfile,这个Dockerfile就定义了一个Docker的Image。Dockerfile有自己的语法,后面我们会说到。先来看看一个基本的Dockerfile

FROM ubuntu:16.04 #选择BaseImage
LABEL maintainer="Heheda <heheda@gamil.com>" #作者标识
RUN apt-get update && apt-get install -y redis-server #在BaseImage上运行某个命令
EXPOSE 6379 #暴露端口
ENTRYPOINT [ "/usr/bin/redis-server" ] #程序入口,此处是redis启动入口

通过命令docker build -t your-directory/xxx:latest .build当前目录下的Dockerfile,你会看到每执行一步,都会生成一个id,一共五个id,对应Dockerfile里的五条语句,每一步都是image的一层(layer),层是可以共享的。以上是手动创建Image的方式。

另一种获取Image的方式是从Docker的Registry中获取。Registry类似docker的github,里面有许许多多的Image供选择。
docker pull targetImageName来获取

小提示,如果需要在虚拟机中执行docker命令是不输入sudo,那么需要把当前用户加到docker用户组里。具体命令如下

sudo groupadd docker
sudo gpasswd -a vagrant docker
sudo service docker restart

最后退出账户重新登录,再执行docker命令就不需要sudo了

创建一个简单的BaseImage

  • 创建一个目录,里面可以编写一段简单的程序,俗套来了,这里用C语言写个HelloWorld
mkdir hello-world
vim hello.c
image.png
  • 用gcc将c文件编译好,得到hello这个二进制可执行文件
    gcc -static hello.c -o hello

  • 编写Dockerfile

FROM scratch #因为我们是创建BaseImage,不是安装软件,所以用scratch
ADD hello / #将hello这个可执行文件放在根目录
CMD ["/hello"] #执行根目录下的hello程序
  • build image
docker build -t guojh/hello-world .

这条命令 -t是指定image的tag,后面跟的参数就是tag的名字,最后的点表示Dockerfile在当前目录。不加-t也是可以的
可以看到创建过程一共执行了3步,也就是3层,对应Dockerfile里的三条语句。


image.png

docker image ls 查看创建的guojh/hello-world镜像

image.png

使用docker history imageID可以查看image的创建记录。可以看到,我们FROM指定的是scratch,在执行过程中这并不算一层,因为我们创建的是BaseImage。

image.png

最后使用docker run guojh/hello-world执行我们的image

image.png

Container

Container是通过Image创建的,会拷贝一份Image。之后会在Image上创建一个Container层,负责读写操作。可以将Image和Container类比成类和实例,Image描述了一个镜像该有的样子,Container根据Image的描述实例化一个镜像。Image在职责上负责存储和分发,Container负责运行。


image.png

通过docker run xxx就是最快的根据Image创建Container的方式。
docker container ls或者docker ps可以查看当前Container,执行命令后我们发现没有任何Container,这是因为我们的hello-world镜像打印出hello, my docker后就退出了,我们可以通过-a参数查看正在运行及已经退出的Container

image.png

如何不让Container退出呢?我们需要运行一个有不会退出的进程的image,比如操作系统Image,centos
如果我们直接docker run centos:7,那么Container依旧会退出。如果不希望推出,可以加-t参数,采用交互式运行
docker run -it centos:7

image.png

查询现有Container,可以看到up的Container是centos


image.png

如果要删除容器,使用docker container rm ContainerID或者docker rm ContainerID
如果删除多个镜像,使用docker container rm $(docker container ls -aq),-ap是列出所有镜像ID。
如果想删除退出状态的Container,使用docker rm $(docker container ls -f "status=exited" -q)

构建自己的Docker镜像

第一个需要了解的命令docker container commit,将当前运行的Container变成一个镜像。

首先我们运行前面的centos这个baseImage,运行docker run -it centos:7
这个centos7是不包含vim软件的,我们就在baseImage的基础上,安装一个,运行命令sudo yum install vim进行安装。

安装完成后,退出centos,运行docker container ls -a查看当前container。

image.png

我们可以看到,红线标出的就是刚才我们退出的container,这个container包含了vim。我们使用docker container commit命令来创建Image。这个命令需要两个参数,一个是container的名字,一个是Image的tag,tag默认是latest的,所以我们不用加latest了。最终命令是这样docker container commit epic_stonebraker guojh/centos7-vim

创建完成后,查看image,你会发现多了个我们刚才创建的带VIM的镜像,比baseImage大了100MB+。


image.png

这种创建镜像的方式不是特别推荐,因为当发布镜像的时候,只提供了一个image,但具体有没有多加什么其它软件,你并不知道,所以接下来我们说第二种创建Image的方式,用Dockerfile。

我们新建一个目录,docker-centos-vim。进入这个目录后,创建一个Dockerfile,内容如下:

FROM centos
RUN yum install -y vim

然后使用docker build -t your-tag完成创建。这里有个问题,image既然是只读权限,那么如何在image上安装vim呢?docker不会让自己陷入这个困境的,docker build命令会运行一个临时的container,将baseImage运行起来,然后安装vim,最后生成image。以后我们就可以直接分享Dockerfile给其他人,保证我们的Image不会安装其他恶意软件了。另外提一点,如果希望container在后台运行,加上-t即可。

Dockerfile语法

  • FROM
    FROM后面跟scratch是创建baseImage,FROM centos是使用已有的baseImage,尽量使用官方的baseImage。

  • LABEL
    LABEL标签就是增加对镜像的描述,比如LABLE maintainer=xxx就标识了镜像的持有者。另外还有version,description这些字段。对于LABEL来说,

  • RUN
    RUN是执行一些命令,比如安装软件等。这里有一点要注意,每执行一次RUN,便会产生一层Image,为了避免无用分层,尽量用&&连接命令,如果命令太长,用反斜线来换行。比如

RUN yum install vim && yum install PHP \
yum install Nginx
  • WORKDIR
    这条命令是设定当前工作目录。类似cd命令,如果WORKDIR一个不存在的目录,会自动创建这个目录。这里需要注意,不要使用RUN cd,这会产生无用层,推荐使用WORKDIR,并且尽量使用绝对路径。

  • ADD and COPY
    ADD和COPY都能将本地的文件添加到docker的image中,比如我们之前创建hello镜像的时候。ADD和COPY第一个区别是如果拷贝的是压缩文件,ADD会将压缩文件解压缩后再拷贝,而COPY只是单纯拷贝压缩文件而已。如果只是拷贝,不需要解压缩,还是用COPY。如果是添加远程文件,比如下载一个文件,那么还是RUN来执行wget或者crul吧。

  • ENV
    这个关键字是声明常量。比如

ENV MYSQL_VERSION 5.6
RUN apt-get install -y mysql-server="${MYSQL_VERSION}" \
&& rm -rf /var/lib/apt/lists/*

尽量使用ENV增加代码可读性,减少魔数的使用。

  • CMD
    CMD是容器启动时默认执行的命令,如果定义了多个CMD,只会执行最后一个CMD,如果docker run指定了其它命令,则会忽略CMD。
    对于最后一种情况解释一下,比如我们有这样一个Dockerfile:
FROM centos
ENV name Docker
CMD echo "hello, $name"

假设这个Dockerfile生成的image名字是testImage,那么执行docker run tetsImage后,默认会执行CMD命令,打印hello, Docker。如果执行docker run testImage /bin/bash,那么就不会打印hello, Docker,因为我们指定了运行/bin/bash。

  • ENTRYPOINT
    ENTRYPOINT让容易以应用程序或者服务的形式启动,例如启动Nginx这种守护进程。ENTRYPOINT永远不会被忽略,一定会被执行。最好是将要执行的命令写成shell,用ENTRYPOINT来执行这个shell。比如:
COPY docker-entrypoint.sh /usr/local/bin
ENTRYPOINT ["docker-entrypoint.sh"]
  • EXPOSE
    暴露docker内的端口给外部。
EXPOSE 5000

更多命令可以参考Dockerfile官方文档

Dockerfile

Dockerfile中的格式有两种,一种是Shell格式,一种是Exec格式
Shell:

RUN apt=get install -y vim
CMD echo "finished..."
ENTRYPOINT echo "Hello..."

Exec:

RUN [ "apt-get", "install", "-y", "vim"]
CMD[ "/bin/echo", "finished..."]
ENTRYPOINT ['/bin/echo', "Hello..."]

如果命令中定义了常量,使用常量的时候,Exec格式不会识别常量,CMD可以识别常量。如果必须让Exec识别常量,那么第一个命令应该是/bin/bash,后面跟上shell命令即可。如

ENV name Heheda
ENTRYPOINT ["/bin/bash", "-c", "echo $name"]
  • Dockerfile中执行命令时的动态参数
    如果Dockerfile中需要执行一段shell命令,但参数又不确定怎么办?可以使用ENTRYPOINT搭配CMD,如:
ENTRYPOINT ["/usr/bin/bash/ps"]
CMD[]

在运行container的时候,直接跟上需要的参数即可。如:
docker run -it containerID aux | grep parity
这样aux | grep parity就会被当成ps的参数

更多Dockerfile例子可以在GitHub上搜索docker-library仓库

搭建自己的Docker镜像仓库

搭建很简单,官方提供了一个搭建仓库的仓库镜像,按照教程来就能搭建好。

关键是创建镜像的时候,tag需要指定为私有仓库的ip+端口。比如我们的仓库地址是12.12.12.12:5000,那么build镜像的时候,tag为12.12.12.12:5000/imagename

在push之前,需要在/etc/docker目录下创建damon.json文件,输入下面的代码:

{
    "insecure-registries" : ["12.12.12.12:5000"]
}

把我们的仓库地址被docker信任。

再编辑/lib/systemd/system/docker.service文件,在文件中增加EnviromentFile=-/etc/docker/daemin.json
最后重启docker

现在我们可以push到私有仓库了,docker push 12.12.12.12:5000/imagename

私有仓库没有提供web界面,可以通过API来检查是否上传成功。
12.12.12.12:5000/v2/_catalog来查看

容器操作

  • 进入container执行命令或查看container运行的程序
    如果希望进入container,可以使用命令docker exec -it containerID shell命令来实现。比如我想进入container的shell里,那就运行docker exec -it containerID /bin/bash,这样就能进入container的bash里了。

  • 给要运行的容器起名
    运行docker run --name=demo imageID,指定一个名字即可,这个名字是唯一的,不能重复。

  • 显示container的详细信息
    运行docker inspect containerID

  • 查看container运行log
    运行docker logs containerID

容器资源限制

  • 限制container的内存使用量
    使用--memory=200M来限制内存使用。这里指定了内存是200MB,但linux还有交换区内存,默认是和memory指定的大小一致,所以实际的可用内存有400MB。可以使用--memory-swap来指定交换区内存大小。查看等多命令使用docker run --help

  • 限制CPU使用
    这里介绍一个参数--cpu-shares,这是限制CPU使用权重,以整数表示。比如我container A限制--cpu-share=10,另一个container B限制--cpu-share=5,这意味着A的CPU使用率是B的两倍。如果这两个container占用了宿主机全部的CPU资源,那么A对CPU的使用率是B的两倍。

stress压力测试工具

在运行的container里安装好stress后,第一个介绍的命令是--vm,意思是启动进程及给进程分配内存,默认内存是256MB。如果需要多个进程,在命令后面空一格,跟上进程数量,如stress --vm 2,这里就会创建两个进程。如果要指定进程的内存大小,用--vm-bytes,比如stress --vm 2 --vm-bytes 128M。如果要查看log,加上--verbose参数。

参考资料
慕课网Docker教程

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

推荐阅读更多精彩内容