2019-05-20 | Docker | UNLOCK

Docker基础教程

Docker基础知识

Docker简介

1.什么是容器?

  • 一种虚拟化的方案
  • 操作系统级别的虚拟化
  • 只能运行相同或相似的操作系统
  • 依赖于Linux内核特性:Namespace 和Cgroup(Control Group)

2.Linux 容器技术 vs 虚拟机

容器技术复杂,不易实现自动化(Docker之前)

3.什么是Docker?

将应用程序自动部署到容器

Go语言开源引擎 Github地址:https://github.com/docker/docker

2013年初 dotCloud

基于Apache 2.0开源授权协议发行

官方示例教程:https://docs.docker.com/get-started/

4.docker 的目标

  • 提供简单轻量的建模模式——易上手,易运行,低开销
  • 职责的逻辑分离——开发人员只需要关心容器中运行的程序,运维人员只需关心容器
  • 快速高效的开发生命周期——缩短代码从开发,测试,部署上线,运行的周期
  • 鼓励使用面向服务的架构——推荐单个容器只运行一个应用程序和进程,形成分布式的应用程序模型

5.Docker的使用场景

  • 使用Docker容器开发、测试、部署服务
  • 创建隔离的运行环境
  • 搭建测试环境——集群环境
  • 构建多用户的平台即服务(PaaS)基础设施
  • 提供软件即服务(SaaS)应用程序
  • 高性能、超大规模的宿主主机部署

IaaS(基础设施即服务) 我理解为计算机和网络组成的硬件,PaaS(平台即服务) 我理解为装了操作系统的计算机和网络,操作系统即平台,SaaS(软件即服务)才是真正带有分布式的功能的集群,我觉等云计算就是SaaS(软件即服务)。

当前SaaS有多种典型应用。如:在线邮件服务、网络会议、[在线杀毒]等各种工具型服务。SaaS是应用软件的一个发展趋势。
当前PaaS的典型实例有微软的Windows Azure平台、GoogleApp engine 等。
在这种服务模式中,客户不需要购买底动的硬件和平台软件。只需要利用PaaS平台,就能够创建、测试和部署应用程序。
当前IaaS,在这个[服务类型]中,最著名的是[亚马逊]提供的AWS(Amazon Web Services)服务,它通过不同的服务方式把自己庞大数据中心的基础设施对外提供服务,比如通过弹性计算服务(EC2)提供虚拟机租用服务,通过简单储存服务(S3)提供存储租用服务等。
在这种服务型中,用户不用自己构建一个数据中心,而是通过租用的方式来使用基础设施服务,包括服务器、存储和网络等。

Docker的基本组成

  • Docker Client 客户端
  • Docker Daemon守护进程
  • Docker Image镜像
  • Docker Container容器
  • Docker Registry 仓库

Docker Client 客户端

C/S架构
本地/远程

由客户端来执行命令,然后提交给守护进程与容器进行交互,然后再把结果返回给用户。

Docker Image镜像

容器的基石
层叠的只读文件系统
联合加载(union mount)

docker的进行是一个层叠的只读文件系统,它的最低端是一个引导系统bootfs,这很像典型的linux引导系统,docker用户几乎永远不会和引导系统交互,实际上,当一个容器启动之后,它将会被移动到内存中,而引导文件系统将会被卸载。docker镜像的第二层是rootfs(Ubuntu/Centos…),它位于bootfs之上,永远只能是只读状态。并且docker利用联合加载技术,又会在root之上加载更多的只读文件系统。联合加载是指一次加载多个文件系统,但是在外界看来只能看到一个文件系统。联合加载会将各层文件系统叠加在一起,最终的系统会包含所有的底层文件和目录。docker将这样的文件系统称为镜像,一个镜像可以放到零一个镜像底部,对下面的镜像称为父镜像,可以依次类推,直到镜像栈的最底部。最底部的镜像称为基础镜像(rootfs)。

Docker Container容器

  • 通过镜像启动
  • 启动和执行阶段
  • 写时复制(copy on write)

容器通过镜像来启动,docker的容器时docker的执行单元,容器中可以运行客户的一个或多个进程,如果镜像是docker生命周期中的构建和打包阶段,那么容器则是启动和执行者。当一个容器启动时,docker会在该镜像的最顶层加一个读写文件系统,也就是一个可写文件层,我们在docker中运行的程序就是在这个层中进行执行的,当docker第一此启动一个容器时,初始的读写层是空的,当文件系统发生变化时,这些变化都将应用到这一层上,比如想修改一个文件,这个文件首先会从该读写层下面的只读层复制到该读写层,该文件的只读版本依然存在,但是已经被读写层中的该文件副本所隐藏,这就是docker容器中的一个重要技术——写时复制。每个只读镜像层都是只读的,并且以后永远不会变化,当创建一个新容器时,docker会构建出一个镜像栈,在栈的最顶层添加可写层,这个读写层加上下面的镜像层以及一些配置数据就构成了一个容器,容器的这种特点加上分层框架就可以快速构建镜像并允许包含自己的应用程序和服务的容器。

Docker Registry 仓库

docker用仓库来保存用户构建的镜像,分为共有和私有,Docker Hub。

Docker容器的相关技术简介

Docker依赖的Linux内核特性

  • NameSpaces 命名空间
  • Control groups(cgroups)控制组
NameSpaces 命名空间

编程语言:封装–>代码隔离

操作系统:系统资源的隔离–>进程、网络、文件系统….

  • PID 进程隔离
  • NET(netword) 管理网络接口
  • IPC 管理跨进程通信的访问
  • MNT(mount) 管理挂载点
  • UTS(Unix Timesharing System) 隔离内核和版本标识
Control groups(cgroups)控制组

用来分配资源、整合进Linux内核

作用:资源限制、进程优先级设定、进行使用资源计量、资源控制、

Docker容器的能力
  • 文件系统隔离:每个容器都有自己的root文件系统
  • 进程隔离:每个容器都运行在自己的进程环境中
  • 网络隔离:容器的虚拟网络接口和IP地址都是分开的
  • 资源隔离和分组:使用cgroups将CPU和内存之类的资源独立分配给每个Docker容器

Docker的安装

在Centos7中安装Docker

  • 检查系统是否满足要求

安装docker需要centos7以及内核版本高于3.10

1
uname -r   #查看内核版本

保持yum版本最新:

1
yum update
  • 卸载旧版本(如果安装过旧版本的话)
1
yum remove docker  docker-common docker-selinux docker-engine
  • 1.安装需要的软件包, yum-util 提供yum-config-manager功能,另外两个是devicemapper驱动依赖的
1
yum install -y yum-utils device-mapper-persistent-data lvm2
  • 2.设置yum源
1
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo #阿里镜像源
  • 可以查看所有仓库中所有docker版本,并选择特定版本安装
1
yum list docker-ce --showduplicates | sort -r
  • 3.安装docker
1
2
sudo yum install docker-ce  #由于repo中默认只开启stable仓库,故这里安装的是最新稳定版17.12.0
sudo yum install docker-ce-17.12.0.ce #指定版本
  • 启动
1
2
systemctl start docker #启动
systemctl enable docker #开机启动(可选)
  • 验证安装是否成功(有client和service两部分表示docker安装启动都成功了)
1
docker version

容器的基本操作

容器的启动和查看

  • 1.1启动新容器
1
2
docker run IMAGE [COMMAND][ARG...]
docker run ubuntu echo "Hello world"
  • 1.2启动交互式容器
1
docker run -i -t IMAGE /bin/bash

-i –interactive=true | false 默认是false

-t –tty=true | false 默认时false

  • 1.3启动时自定义容器名
1
docker run --name=自定义名字 -it IMAGE /bin/bash
  • 1.4重新启动停止的容器
1
2
docker start [-i] 容器名
docker start -i f0bee8b198f8/test999
  • 查看容器
1
docker ps [-a][-l] #-a 所有容器,-l最新容器
  • 查看某个容器配置信息
1
2
docker inspect CONTAINERID/CONTAINERNAME
docker inspect f0bee8b198f8/test999
  • 删除停止的容器
1
docker rm 容器名
  • 守护式容器(后台容器)

1)运行交互式容器后以Ctrl+E Ctrl+Q退出

2)以run -d命令进入

1
docker run -d 镜像名 [CONMAND][ARG...]

重新进入守护式容器:

1
docker attach 容器名/容器ID
  • 查看容器日志
1
2
docker logs [-f][-t][--tail] 容器名
docker logs -tf --tail 10

-t –fllows=true 一直跟踪容器的状态{使用Ctrl+C停止}

-t –timestamps 加上时间戳

–tail=“all” 返回结尾处多少条数据,默认所有

  • 查看运行中容器内进程
1
2
docker top 容器名
#docker top test999
  • 在运行中的容器内启动新进程
1
docker exec [-d][-i][-t] 容器名 [CONMAND][ARG...]
  • 停止守护式容器
1
2
docker stop
docker kill #立刻停止
  • 查看docker使用的存储驱动和存储的位置
1
docker info

一般是在 /var/lib/docker目录下。

容器的端口映射

设置容器的端口映射
1
2
3
4
5
6
run [-P][-p]
docker run -P -it ubuntu /bin/bash
docker run -p 80 -it ubuntu /bin/bash #指定容器端口号为80,宿主机端口随机映射
docker run -p 8080:80 -it ubuntu /bin/bash#指定端口映射,宿主机端口号8080,容器端口80
docker run -p 0.0.0.0:80 -it ubuntu /bin/bash#
docker run -p 0.0.0.0:8080:80 -it ubuntu /bin/bash#

[-P] –publish-all=true|false 默认是false

[-p] –publish=[]

  • 查看端口映射
1
2
docker port 容器名
docker port web

1
2
3
curl http://127.0.0.1:43167  #在宿主机中访问43167端口,相当于访问容器的9090端口,又因为容器的9090端口被nginx监听,所以能够被nginx代理(在宿主机中执行)
或者使用容器的ip地址访问
curl http://172.17.0.32:9090 #如果这里是80端口,就不需要指定端口了(还是在宿主机中执行)

如果在容器中安装了nginx,并且启动了nginx,如果把容器stop,重新启动容器后nginx没有启动。这是可以使用exec命令:

1
docker exec web nginx

查看和删除镜像

  • 列出镜像
1
docker images [OPTSIONS] [REPOSITORY]

-a, –all=false 显示所有镜像,默认是不显示中间层镜像

-f, –filter=[] 显示时的过滤条件

–no-trunc=false 指定不使用截断的形式显示数据,默认会阶段镜像的ID的(短ID)。

-q,–quiet=false 只显示镜像的唯一id

总共有5列:

REPOSITORY:所属仓库名,存储的是一个个镜像

TAG:镜像的标签名,默认是latest。REPOSITORY+TAG名构成一个完整的镜像名,ubuntu:latest.

IMAGE ID :镜像的唯一id(默认是短的)

  • 查看镜像
1
2
docker inspect 镜像名/镜像id #镜像名=REPOSITORY+TAG名
docker inspect ubuntu:latest
  • 删除镜像
1
2
3
4
5
docker rmi [OPTIONS] IMAGE [IMAGE...]
docker rmi ubuntu:latest #根据镜像名
docker rmi 7698f282e524 #根据镜像id,可能一次删除多个镜像标签
docker rmi ubuntu:latest ubuntu:14.02 #一次删除多个镜像标签
docker rmi $(docker images -q ubuntu) 删除ubuntu repository下的所有镜像

选项:

-f ,–force=false 强制删除

–no-prune=false 保留被删除镜像中被打标签的父镜像

Docker镜像与仓库

获取和推送镜像

  • 查找镜像

1)通过Docker Hub

https://registry.hub.docker.com

2)Docker命令行search

1
docker search [OPTIONS] TERM

–automated=false 只会显示自动化构建的

–no-trunc=false 不以截断的方式输出

-s,–stars=0 显示多少星以上的

一次search最多返回25个结果

  • 镜像拉取
1
2
docker pull [OPTITIONS] NAME [:TAG]
docker pull ubuntu:18.04

-a,–all-tags 把匹配到的对象都下载到本地

  • 使用–registry-mirror镜像

1.修改:/etc/default/docker

2.添加:DOCKER_OPTS=”–registry-mirror=http://MIRROR-ADDR"

https://www.daocloud.io注册账户,然后在dashboard中选择加速器,那么会自动生成一个链接,我们就可以使用这个链接来配置仓库的镜像地址

1
2
3
4
1.vim /etc/default/docker
2.然后在文件末尾添加:
DOCKER_OPTS="--registry-mirror=http://624869e7.m.daocloud.io"
3.重新启动docker的守护进程
  • 推送镜像
1
2
3
4
docker login #本地Linux登录docker
docker push NAME[:TAG]
docker push pengllrn/commit_web_nginx
docker push pengllrn/commit_web_nginx:tagname

注意,docker在push的时候并不会提交所有的镜像,而指挥提交修改的部分。

推送Docker Hub速度很慢,耐心等待,很有可能失败,失败会尝试多次重传,之后断开推送(但已推送上去的会保留,保留时间不知道是多久)。

在本地构建镜像

保存对容器的修改,并再次使用

自定义镜像的能力

以软件的形式打包并分发服务及运行环境

方式一:通过容器构建

1
2
3
docker commit #通过容器构建
docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]
docker commit -a 'pengllrn' -m 'nginx' web pengllrn/commit_web_nginx

选项:

-a,–author=”” Author 指定镜像的作者,通过会填写镜像的名字和联系方式

-m,–message=”” commit信息 ,镜像构建的信息

-p,–pause=true 由于commit命令会将正在执行的容器暂停,-p用来知识commit命令不暂停正在执行的容器。

执行完之后,再使用docker images即可查看到刚才上传的容器:

然后启动它:

1
docker run -d --name nginx_web pengllrn/commit_web_nginx nginx -g "daemon off;"

这里以守护进程的方式启动,并在启动后立刻执行启动nginx。然后检查这个容器的ip,然后通过curl命令(curl http://172.17.0.2:9090)去访问,结果和提交的web容器一样。

方式二:通过Dockerfile文件构建

1
docker build #通过Dockerfile文件构建

2.1)创建一个dockerfile

1
2
3
4
5
6
#First dockerfile for test
FROM ubuntu:14.04
MAINTAINER pengllrn "pengllrn.top"
RUN apt-get update
RUN apt-get install -y nginx
EXPOSE 80

2.2)执行docker build命令

1
2
docker build [OPTIONS] PATH | URL | - #PATH、URL都是指容器的路径
docker build -t='pengllrn/web_test' .#执行当前目录下的Dockerfile文件

选项:

–force-rm=false

–no-cache=false

–pull=false

-q,–quiet=false

–rm=true

-t,–tag=”” 用于指定构建出的容器的名字

最终将会出现一个新的镜像:pengllrn/web_test

Dockerfile指令

指令格式

注释:以”#“开头

指令:以大写的字母开头,后面是参数:INSTRUCTION argument

FROM指令

1
2
3
FROM <image> #必须是一个已经存在的镜像,如果不存在就会下载
FROM <image>:<tag>
例如FROM ubuntu:14.04

已经存在的镜像,后续指令都会基于这个镜像来执行——基础镜像,并且必须是在dockerfile中第一行非注释的指令。

MAINTAINER指令

1
2
MAINTAINER <name>
MAINTAINER pengllrn "pengllrn.top"

指定镜像的作者信息,包含镜像的所有者和联系信息,等价于commit中的 -a。

RUN指令

指定当前镜像中运行的命令

1
2
RUN <command> (Shell模式)# /bin/sh -c command
RUN ["executable","param1","param2"] (exec模式)# ["/bin/bash","-c","echo hello"]

EXPOSE指令

1
EXPOSE <port> [<port>...]

指定运行该镜像的容器使用的端口。

CMD/ENTERYPOINT指令

  • CMD命令
1
2
3
CMD ["executable","param1","param2"](exec模式)
CMD command param1 param2 (shell 模式)
CMD ["param1","paaram2"](作为ENTERYPOINT指令的默认参数)

CMD指令用来提供容器运行的默认命令。和RUN指令类似,都是执行一个/一系列命令,但是run指令的命令是在容器构建过程中运行的,而CMD指定的命令是在容器运行时运行的。并且RUN的命令优先级高于CMD。如果指定了RUN会覆盖CMD。

  • ENTERYPOINT命令
1
2
ENTERYPOINT ["executable","param1","param2"](exec模式)
ENTERYPOINT command param1 param2 (shell 模式)

和CMD的命令的区别:ENTERYPOINT命令不会被docker file中指定的启动命令所覆盖。

ADD/COPY/VOLUME指令

都是将文件和目录复制到使用docker file构建的镜像中,它们都支持两种参数,来源地址和目标地址。文件和目录的来源可以是本地地址(docker file的当前位置),也可以是远程的URL。curl,wget。目标路径需要指定镜像中的绝对路径。

  • ADD vs COPY

ADD包含了类似tar的解压缩功能

如果单纯复制文件,docker推荐使用COPY

  • VOLUM[“/data”]

用来向基于镜像创建的容器添加卷,一个卷是可以存在于一个或多个容器的特点目录,这个目录可以绕过联合文件系统,并提供如共享数据或者对数据持久化的功能。

WORKDIR/ENV/USER指令

  • WORKDIR /path/to/workdir

用来在从镜像创建一个新容器时,在容器内部设置工作目录,CMD或者ENTERYPOINT指定的命令都将在这个目录下执行。也可以使用这个指令在构建中为后续的指令指定工作目录,那么需要注意的是dir通常使用的是绝对路径,如果使用了相对路径,那么会一级一级的传递下去,比如:

1
2
3
4
WORKDIR /a
WORKDIR b
WORKDIR c #/a/b/c
RUN pwd
  • ENV 指令
1
2
ENV <key> <value>
ENV <key>=<value> ...

用来设置环境变量,与WORKDIR指令类似,环境变量的指令也可以作用于构建过程中,以及在运行过程中同样有效。

  • USER daemon

用来指定镜像以什么样的用户(角色)去运行,比如:

1
USER nginx  #启动的容器将以nginx的身份去运行,nginx为系统用户

也可以在USER指令中使用uid,用户组,jid,或者是这四种的组合。如果不使用USER指令,默认使用root用户。

ONBUILD指令

为容器添加触发器。当一个镜像被用作其他镜像的基础时,这个触发器会被执行。当子镜像在构建时会触发会插入触发器中的指令。

Dockerfile的构建过程

1.从基础容器运行一个容器

2.执行一条指令,对容器做出修改

3.对修改后的容器执行类似于docker commit的操作,提交一个新的镜像层(中间层镜像)

4.再基于刚提交的镜像运行一个新容器

5.执行Dockerfile的下一条指令,反复如此,直至所有指令执行完毕

在运行build结束之后,会返回最终的镜像id,并且会删除掉中间层容器,但不会删除中间层创建的镜像。因此可以通过docker run命令,运行中间层镜像的容器,从而查看每一步构建后镜像的状态。这实际上就可以进行调试。使用中间层镜像进行调试可以查找错误,排查错误出现的位置。

构建缓存

docker的每一步构建过程都会将结果提交为镜像,docker的镜像过程会将之前的镜像看作缓存。

可以看到第二次的构建过程都使用了缓存:Using cache,速度就会非常的快。

不使用缓存

如果不希望使用构建缓存,例如构建命令中使用的apt-get update,就希望不使用缓存,这样就能得到最新的版本。可以使用build命令中的–no-cache选项。

docker build -t="dockerfile/df_test1" --no-cache .

除了使用–no-cache选项,还可以使用环境变量E:

1
2
3
4
5
6
7
#First dockerfile ofr test
FROM ubuntu:latest
MAINTAINER pengllrn "www.pengllrn.top"
ENV REFRESH_DATE 2019-05-20
RUN apt-get update
RUN apt-get insatll -y nginx
EXPOSE 80

REFRESH_DATE表示缓存刷新的时间,只要修改这个时间就会带来缓存的刷新

查看镜像构建过程

对于一个给定的镜像,如何查看其构建过程?docker提供了如下命令来提对镜像构建过程的查看。

1
docker history [image]

还可以看到镜像构建过程所使用的存储空间和中间层镜像。

评论加载中