Docker是什么

将程序、开发环境、配置打包放到一个容器中,不需要在部署时进行环境的配置。解决了运行环境和配置问题软件容器,方便做持续集成并有助于整体发布的容器虚拟化技术。

Docker是基于Go语言实现的云开源项目。

Docker的主要目标是“Build,Ship and Run Any App,Anywhere”,也就是对应用组件进行封装、分发、部署、运行等生命周期的管理,使用户的APP(可以是一个WEB应用或数据库应用等等)及其运行环境能够做到“一次封装,到处运行”。

Docker包含两方面技术:

镜像技术:“镜像即应用”,(运行文档、配置环境、运行环境、运行依赖包、操作系统发行版、内核)

Docker能做什么

之前的虚拟机技术:

虚拟机就是带环境安装的一种解决方案。

它可以在一种操作系统里面运行另一种操作系统,比如在Windows系统里面运行Linux系统。应用程序对此毫无感知,因为虚拟机看上去跟真实系统一模一样,而对于底层系统来说,虚拟机就是一个普通文件,不需要了就删掉,对其他部分毫无影响,这类虚拟机完美地运行了另一套系统,能够使应用程序,操作系统和硬件三者之间逻辑不变。

容器虚拟化技术

Linux发展出了另一种虚拟化技术:Linux容器(Linux Containers 简写为LXC)。

Linux容器不是模拟一个完整的操作系统,而是对进程进行隔离。有了容器,就可以将软件运行时所需的所有资源打包到一个隔离的容器中。容器与虚拟机不同,不需要捆绑一整套操作系统,只需要将软件工作所需的库资源和设置。系统因此而变得高效轻量并保证部署在任何环境下的软件都能始终如一地运行。

开发/运维(DevOps)

开发者自己运维

一次构建、随处运行

Docker的安装

CentOS Docker安装

Docker支持以下的CentOS版本:

CentOS 7(64bit)

CentOS 6.5 (64bit)或更高版本

前提条件

目前,CentOS仅发行版中的内核支持Docker。

Docker运行在CentOS 7上,要求系统为64位,系统内核版本为3.10以上。

Docker运行在CentOS 6.5或更高版本的CentOS上,要求系统为64位,系统内核版本为2.6.32-431或者更高版本。

使用uname -r查看CentOS版本。

Docker的基本组成

镜像(image)就是一个只读的模板,镜像可以用来创建Docker容器,一个镜像可以创建很多容器。

**容器(container)**Docker利用容器独立运行的一个或一组应用。容器是镜像创建的运行实例。它可以被启动、开始、停止、删除。每个容器都是互相隔离的、保证安全的平台。

可以把容器看成是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序。

容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。

**仓库(repository)**是集中存放镜像文件的地方。仓库(repository)和仓库注册服务器(registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库又包含了多个镜像,每个镜像有不同的标签(tag)。

CentOS 7安装Docker

使用命令查看CentOS的版本:

1
cat /etc/redhat-release
1
CentOS Linux release 7.3.1611 (Core) 

安装gcc:

1
2
yum -y install gcc
yum -y install gcc-c++

如果安装过Docker则需要删除软件包:

1
2
3
4
5
6
7
8
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

如果未安装过Docker,则会有以下提示:

1
2
3
4
5
6
7
8
9
10
Loaded plugins: fastestmirror
No Match for argument: docker
No Match for argument: docker-client
No Match for argument: docker-client-latest
No Match for argument: docker-common
No Match for argument: docker-latest
No Match for argument: docker-latest-logrotate
No Match for argument: docker-logrotate
No Match for argument: docker-engine
No Packages marked for removal

安装Docker CE:

通过添加仓库安装:

1
2
3
4
sudo yum install -y yum-utils
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo

国外服务器太慢可以切换到阿里云的源:

1
2
3
sudo yum-config-manager \ 
--add-repo \
http://mirrors.aliyun.com/dockerce/linux/centos/docker-ce.repo

查看源文件:

1
cat /etc/yum.repos.d/docker-ce.repo

更新yum软件包索引:

1
sudo yum makecache fast

安装Docker CE:

1
yum -y install docker-ce

查看Docker版本:

1
docker version

启动Docker:

1
systemctl start docker

首次运行hello world:

1
docker run hello-world
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
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
0e03bdcc26d7: Pull complete
Digest: sha256:d58e752213a51785838f9eed2b7a498ffa1cb3aa7f946dda11af39286c3db9a9
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

查看镜像:

1
docker images
1
2
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
hello-world latest bf756fb1ae65 5 months ago 13.3kB

配置镜像加速:

1
2
3
4
5
6
7
8
9
10
11
12
13
mkdir -p /etc/docker
vim /etc/docker/daemon.json
(配置网易云或者阿里云镜像加速)
#网易
{
"registry-mirrors":["http://hub-mirror.c.163.com"]
}
#阿里云
{
"registry-mirrors":["https://{自己的编码}.mirror.aliyuncs.com"]
}
systemctl daemon-reload
systemctl restart docker

Docker运行底层原理

(1)docker有着比虚拟机更少的抽象层,由于docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显优势。

(2)docker利用的是宿主机的内核,而不需要Guest OS。因此,当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。仍可避免引寻、加载操作系统内核返个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载Guest OS,返个新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了返个过程,因此新建一个docker容器只需要几秒钟。

Docker常用命令

Docker帮助命令

docker version

docker info

docker --help

Docker镜像命令

docker images

列出本地主机上的镜像。

选项说明:

-a:列出本地所有的镜像(含中间映像层)

-q:只显示镜像ID。

–digests:显示镜像的摘要信息。

–no-trunc:显示完整的镜像信息。

docker search [OPTIONS] 镜像名字

选项说明:

–no-trunc:显示完整的镜像描述

-s:列出收藏数不小于指定值的镜像。

–automated:只列出automated build类型的镜像

docker pull [OPTIONS] 镜像名字 [:TAG]

docker rmi 删除某个镜像

Docker容器命令

**新建并启动一个容器:**docker run [OPTIONS] IMAGE [COMMAND] [ARG…]

1
docker run -it centos

列出当前所有正在运行的容器

docker ps [OPTIONS]

OPTIONS说明(常用)

-a:列出当前所有正在运行的容器+历史运行过的

-l:显示最近创建的容器

-n:显示最近n个创建的容器

-q:静默模式,只显示容器编号

–not-trunc:不截断输出

退出容器

exit:容器停止退出

ctrl+P+Q:容器不停止退出

启动容器

docker start 容器ID或者容器名

重启容器

docker restart 容器ID或者容器名

停止容器

docker stop 容器ID或者容器名

强制停止容器

docker kill 容器ID或者容器名

删除容器

docker rm 容器ID或者容器名

一次性删除多个容器

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

docker ps -a -1 | xargs docker rm

重要命令

启动守护式容器:

docker run -d

查看容器日志:

docker logs -f -t --tail 容器ID

显示docker内部进程

docker top 容器ID

查看容器内部细节

docker inspect 容器ID

进入正在运行的容器并以命令行交互

docker exex -it 容器ID bashShell

重新进入docker attach 容器ID

上述两者区别:

attach直接进入容器启动命令的终端,不会启动新的进程

exec是在容器中打开新的终端,并且可以启动新的进程

从容器内拷贝文件到主机上

docker cp 容器ID:容器内路径 目的主机路径

1
docker cp id: /tmp/log /root

Docker镜像

镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。

UnionFS(联合文件系统)

联合文件系统(Union File System):2004年由纽约州立大学石溪分校开发,它可以把多个目录(也叫分支)内容联合挂载到同一个目录下,而目录的物理位置是分开的。UnionFS允许只读和可读写目录并存,就是说可同时删除和增加内容。UnionFS应用的地方很多,比如在多个磁盘分区上合并不同文件系统的主目录,或把几张CD光盘合并成一个统一的光盘目录(归档)。另外,具有写时复制(copy-on-write)功能UnionFS可以把只读和可读写文件系统合并在一起,虚拟上允许只读文件系统的修改可以保存到可写文件系统当中。

特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件叠加起来,这样最终的文件系统会包含所有底层的文件和目录。

镜像加载原理

rootfs和bootfs

任何程序运行时都会有依赖,无论是开发语言层的依赖库,还是各种系统lib、操作系统等,不同的系统上这些库可能是不一样的,或者有缺失的。为了让容器运行时一致,docker将依赖的操作系统、各种lib依赖整合打包在一起(即镜像),然后容器启动时,作为它的根目录(根文件系统rootfs),使得容器进程的各种依赖调用都在这个根目录里,这样就做到了环境的一致性。

不过,这时你可能已经发现了另一个问题:难道每开发一个应用,都要重复制作一次rootfs吗(那每次pull/push一个系统岂不疯掉)?

比如,我现在用Debian操作系统的ISO做了一个rootfs,然后又在里面安装了Golang环境,用来部署我的应用A。那么,我的另一个同事在发布他的Golang应用B时,希望能够直接使用我安装过Golang环境的rootfs,而不是重复这个流程,那么本文的主角UnionFS就派上用场了。

Docker镜像的设计中,引入了层(layer)的概念,也就是说,用户制作镜像的每一步操作,都会生成一个层,也就是一个增量rootfs(一个目录),这样应用A和应用B所在的容器共同引用相同的Debian操作系统层、Golang环境层(作为只读层),而各自有各自应用程序层,和可写层。启动容器的时候通过UnionFS把相关的层挂载到一个目录,作为容器的根文件系统。

需要注意的是,rootfs只是一个操作系统所包含的文件、配置和目录,并不包括操作系统内核。这就意味着,如果你的应用程序需要配置内核参数、加载额外的内核模块,以及跟内核进行直接的交互,你就需要注意了:这些操作和依赖的对象,都是宿主机操作系统的内核,它对于该机器上的所有容器来说是一个“全局变量”,牵一发而动全身。

Docker镜像commit操作

docker commit 提交容器副本使之成为新的镜像

docker commit -m=“提交的描述信息” -a=“作者” 容器ID 要创建的目标镜像名:[标签名]

docker run -it p 8080:8080 tomcat

容器数据卷

容器的持久化、容器间继承和共享数据。

作用

1.数据卷可在容器之间共享或重用数据

2.卷中的更改可以直接生效

3.数据卷中的更改不会包含在镜像的更新中

4.数据卷的生命周期一直持续到没有容器使用它为止

容器内添加数据卷

直接命令添加

docker run -it -v /宿主机绝对路径目录:/容器内目录 镜像名

命令(带权限):docker run -it -v /宿主机绝对路径目录:/容器内目录:ro 镜像名 (read only)

容器是否挂载成功

docker inspect 容器ID 获取JSON格式的配置

Volumes字段

Dockerfile添加

**Dockerfile:**描述镜像的文件

VOLUME:["/dataVoumeContainer","/dataVolumeContainer2","/dataVolumeContainer3"]

说明:

出于可移植和分享的考虑,用-v 主机目录:容器目录这种方法不能够直接在Dockerfile里实现。

由于宿主机目录是依赖于特定宿主机的,并不能够保证在所有的宿主机上都存在这样的特定目录。

步骤:

根目录下新建mydocker文件夹并进入

可在Dockerfile中使用VOLUME指令来给镜像添加一个或多个数据卷

1
2
3
4
5
6
7
8
9
10
# volume test
FROM centos
VOLUME ["dataVolumeContainer1","/dataVolumeContainer2"]
CMD echo "fininsher,-------success1"
CMD /bin/bash

==>
docker run -it -v /host1:/dataVolumeContainer1 -v /host2:/dataVolumeContainer2 centos /bin/bash


File构建

build后生成镜像 —获得一个新镜像

1
docker build -f /mydocker/dockerfile2 -t zzyy/centos.

run容器

通过上述步骤,容器内的卷目录地址已经知道对应的主机目录地址

Docker images ====> DockerFile

数据卷容器

命名的容器挂载数据卷,其它容器通过挂载这个(父容器)实现数据共享,挂载数据卷的容器,称之为数据卷容器。

以上一步新建的镜像zzyy/centos为模板并运行容器dc01/dc02/dc03

它们已经有容器卷/dataVolumeContainer1 /dataVolumeContainer2

容器间传递共享(–volumes-from)

先启动一个父容器dc01 在dataVolumeContainer2新增内容