docker简介
Docker 和其他容器(container)技术都属于系统虚拟化范畴
容器以进程的形式存在于操作系统之中
容器为应用程序提供了隔离的运行空间:每个容器内都包含一个独享的完整用户环境空间,并且一个容器内的变动不会影响其他容器的运行环境。为了能达到这种效果,容器技术使用了一系列的系统级别的机制诸如利用Linux namespaces 来进行空间隔离,通过文件系统的挂载点来决定容器可以访问哪些文件,通过 cgroups 来确定每个容器可以利用多少资源。此外容器之间共享同一个系统内核,这样当同一个库被多个容器使用时,内存的使用效率会得到提升。
容器( container-based )虚拟化方案,充分利用了操作系统本身已有的机制和特性,以实现轻量级的虚拟化(每个虚拟机安装的不是完整的虚拟机),甚至有人把他称为新一代的虚拟化技术,Docker 无疑就是其中的佼佼者
Docker 的目标
Build,Ship and Run Any App,Anywhere— 即通过对应用组件的封装 (Packaging), 发布(Distribution), 运行(runing) 等生命周期的管理 , 达到应用组件级别的”一次封装 , 到处运行”
docker 的特点
优点:
-启动快,资源占用小 , 资源利用高,快速构建标准化运行环境
-创建分布式应用程序时快速交付和部署,更轻松的迁移和扩展,更简单的更新管理
局限:
-Docker 是基于 Linux 64bit 的,无法在 windows/unix 或 32bit 的 linux环境下使用
-LXC 是基于 cgroup 等 linux kernel 功能的,因此 container 的 guest 系统只能是linux
-隔离性相比 KVM 之类的虚拟化方案还是有些欠缺,所有 container 公用一部分的运行库
-管理相对简单,主要是基于 namespace 隔离
-cgroup 的 cpu 和 cpuset 提供的 cpu 功能相比 KVM 的等虚拟化方案相比难以度量( 所以 dotcloud 主要是按内存收费 )
-docker 对 disk 的管理比较有限
-container 随着用户进程的停止而销毁, container 中的 log 等用户数据不便收集
docker与虚拟机比较
-启动快比虚拟机 , 可以秒级启动
-对资源占用小 , 宿主机上可运行千台容器
-方便用户获取 , 分布 , 和更新应用镜像 , 指令简单 , 学习费用低
-通过 Dockerfile 配置文件来灵活的自动创建和部署镜像 & 容器 , 提高工作效率
-Docker 除了运行其中应用外 , 基本不消耗其他系统资源 , 保证应用性能同时 ,尽量减小系统开销
检测docker是否运行
#systemctl status docker
#docker ps
docker镜像
镜像(docker image) --- Docker 运行容器之前需要本地存在镜像,若本能地不存在,那么 Docker 会找默认镜像仓库( Docker Hub 公共注册服务器中的仓科)下载,用户也可以通过配置使用自己的镜像库
查看所有镜像列表
#docker images
下载镜像
#docker pull mariadb
或
#docker pull mariadb:latest
镜像在下载过程中是分层的,并且在每层前会有各层的 ID号,层(Layer )是 AUFS (联合文件系统)的重要概念,是实现增量保存与更新的基础
修改镜像TAG
#docker tag mariadb:latest mariadb:latestv1
搜索库中满足需求的镜像,以此下载符合需求的镜像文件
#docker search maraidb
删除镜像
#docker rmi mariadb:latest
镜像分层
docker镜像的分层结构:支持通过扩展现有镜像,创建新的镜像
可以看到,新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。
分层结构的优势:
共享资源:
有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享
如果多个容器共享一份基础镜像,当某个容器修改了基础镜像的内容,比如 /etc 下的文件,这时其他容器的 /etc 是否也会被修改?
答案是不会!
修改会被限制在单个容器内。这就是容器 Copy-on-Write 特性。
可写的容器层:
当容器启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。
所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。
只有容器层是可写的,容器层下面的所有镜像层都是只读的。
镜像层数量可能会很多,所有镜像层会联合在一起组成一个统一的文件系统。如果不同层中有一个相同路径的文件,比如 /a,上层的 /a 会覆盖下层的 /a,也就是说用户只能访问到上层中的文件 /a。在容器层中,用户看到的是一个叠加之后的文件系统。
添加文件
在容器中创建文件时,新文件被添加到容器层中。
读取文件
在容器中读取某个文件时,Docker 会从上往下依次在各镜像层中查找此文件。一旦找到,打开并读入内存。
删除文件
在容器中删除文件时,Docker 也是从上往下依次在镜像层中查找此文件。找到后,会在容器层中记录下此删除操作。
只有当需要修改时才复制一份数据,这种特性被称作 Copy-on-Write。可见,容器层保存的是镜像变化的部分,不会对镜像本身进行任何修改。
这样就解释了我们前面提出的问题:容器层记录对镜像的修改,所有镜像层都是只读的,不会被容器修改,所以镜像可以被多个容器共享。
镜像创建
三种方法
1-基于修改后的容器创建:docker commit 命令
#docker run -it mariadb:latest /bin/bash镜像启动后的环境变量
#mkdir /pikachu
#exit 退出
此时容器较镜像已经发生改变,我们以此生成新镜像
#docker commit 0c0d3fb78bb6 pikachu
2-基于本地模板导入
导出(导出的名字可以自定义)
#docker save -o httpd.tar.gz httpd:latest
当镜像被容器占用是要先把容器删掉
#docker rm CONTAINER ID
导入
#docker load -i httpd.tar.gz
3-基于 Dockerfile 文件创建
Dockerfile 是一个文本文件,用来配置 image,记录了镜像构建的所有步骤。Docker 根据 该文件生成二进制的 image 文件。
用 Dockerfile 创建centos-with-vi
下载镜像
#docker pull centos
#vim /root/test/dockerfile
FROM centos:7
RUN yum install -y vim
#docker run -it centos:latest /bin/bash
基于dockerfile文件创建新镜像
#docker build -t centos:vim /root/test/
查看执行命令
#docker history IMAGE ID
Docker 会缓存已有镜像的镜像层,构建新镜像时,如果某镜像层已经存在,就直接使用,无需重新创建。在前面的 Dockerfile 中添加一点新内容,往镜像中复制一个文件
#docker build -t centos:hello /root/test
下面列出了 Dockerfile 中最常用的指令,完整列表和说明可参看官方文档。
FROM:指定 base 镜像。
MAINTAINER:设置镜像的作者,可以是任意字符串。
COPY:将文件从 build context 复制到镜像。
COPY 支持两种形式:
COPY src dest
COPY ["src", "dest"]
注意:src 只能指定 build context 中的文件或目录。
ADD:与 COPY 类似,从 build context 复制文件到镜像。不同的是,如果 src 是归档文件(tar, zip, tgz, xz 等),文件会被自动解压到 dest。
ENV:设置环境变量,环境变量可被后面的指令使用。例如:
...
ENV MY_VERSION 1.3
RUN apt-get install -y mypackage=$MY_VERSION
...
EXPOSE:指定容器中的进程会监听某个端口,Docker 可以将该端口暴露出来。我们会在容器网络部分详细讨论。
VOLUME:将文件或目录声明为 volume。
WORKDIR:为后面的 RUN, CMD, ENTRYPOINT, ADD 或 COPY 指令设置镜像中的当前工作目录。
RUN:在容器中运行指定的命令,RUN 指令通常用于安装应用和软件包。
CMD:容器启动时运行指定的命令。
Dockerfile 中可以有多个 CMD 指令,但只有最后一个生效。CMD 可以被 docker run 之后的参数替换。
ENTRYPOINT:设置容器启动时运行的命令,可让容器以应用程序或者服务的形式运行。
Dockerfile 中可以有多个 ENTRYPOINT 指令,但只有最后一个生效。CMD 或 docker run 之后的参数会被当做参数传递给ENTRYPOINT。
docker的使用
(1)创建容器,create命令是创建并不启动容器
#docker creat -it centos:7
(2)查看所有状态的容器
#docker ps -a
#docker ps
(3)启动停止的容器
#docker start ID
(4)运行一个容器
#docker run -ti docker.io/centos:7 /bin/bash
创建并运行一个容器 ,此时你会直接被切换到容器中
#ctrl+d//exit退出,并且容器也退出
#ctrl+p+q退出容器,并且保持容器up的状态
-t 分配一个伪终端
-i 让容器的标准输入持续打开
-d 不进入容器
用/bin/bash环境显示
(5)以后台守护进程(Daemonized)形态运行 用-d参数实现
#docker run -dti docker.io/centos:7
(6)终止容器
#docker stop NAME/ID #docker kill NAME/ID
(7)重启容器
#docker restart NAME/ID
需要注意,非持久后台守护状态的虚拟机,在重启后当你查看时还是关闭状态
(8)删除容器
语法: docker rm 参数 NAME NAME …
#docker -rm -f NAME/ID
-f 强制删除处于运行中的容器
-l 删除链接,保留容器
-v 删除挂载的数据卷
删除之前要停止容器
(9)进入容器
docker attach 和 docker exec 两种方式:
docker attach直接进入容器 启动命令的终端,不会启动新的进程
#docker run -d centos /bin/bash -c “while true; do sleep 1;echo I_am_docker;done”
#docker attach NAME/ID
docker exec则是在容器中打开新的终端,并且可以启动新的进程
#docker exec -it NAME/ID /bin/bash
(10)导入和导出容器
a. 导出容器:导出一个已经创建的容器到文件,不管容器是否运行
#docker ps -a
假设当前系统中有两个处于运行和停止状态的容器
#docker export ID > *.tar
#ls
b.将容器导入系统成为镜像
#cat *.tar | docker import - test
(11)查看容器日志
显示容器启动进程的控制台输出
#docker logs -f NAME/ID
(12)暂停容器
#docker pause NAME/ID
(13)取消暂停继续运行容器
#docker unpause NAME/ID
docker镜像的仓库
仓库分为公共仓库和私有仓库
DockerHub的官方仓库 https://hub.docker.com
DockerPool社区仓库 https://dl.dockerpool.com
Dockern Pool (http://dockerpool.com)是国内专业的Docker社区,目前也提供了官方镜像的下载管理服务
创建自己的私有仓库
# docker run -d -p 5000:5000 -v /var/lib/registry:/var/lib/registry --restart=always --name registry registry:2
registry:2-镜像
docker客户端
# vim /etc/docker/daemon.json
"insecure-registries": ["192.168.146.201:5000"],
#systemctl restart docker
#docker tag my-imge:latest 192.168.146.201:5000/centos:latest
更换 my-imge:latest 一个新的名称标签
上传到私有仓库
#docker push 192.168.146.201:5000/my-imge1:latest
下载
#docker pull 192.168.146.201:5000/my-imge1:latest
docker依赖的底层核心技术
命名空间 (Namespaces)
控制组 (Control Groups)
联合文件系统 (Union File System)
Linux 虚拟网络支持:本地和容器内创建虚拟接口
(1)命名空间 (Namespaces):
实现了容器间资源的隔离每个容器拥有自己独立的命名空间 , 运行其中的应用就像是运行在独立的操作系统中一样 , 我们都可以看到文件系统,网卡等资源保证了容器之间互不影响,namesaces管理进程号 , 每个进程命名空间有一套自己的进程号管理方法 , 进程命名空间是一个父子关系的结构 , 子空间中的进程对于父空间是可见的
网络命名空间—有了 PID 命名空间 , 那么每个名字中的进程就可以相互隔离 ,但是 , 网络端口还是共享本地的端口 . 网络命名空间就是为进程提供一个完全独立的网络协议栈的视图包括 : 网络设备接口 ,IPv4 和 IPv6 协议栈 ,IP 路由表 , 防火墙规则 ,sockets等等 ...
(2)控制组功能 cgroups:
控制组 (Control groups)--CGroups 是 Linux 内核的一个特性 ,主要用来对共享资源进行隔离、限制、审计等 。cgroups 允许对于进程或进程组公平( 不公平 ) 的分配 CPU 时间、内存分配和 I/O 带宽。
容器通过 cgroups 来得到所能够管理资源的分配和使用。因此容器所获得资源仅为所有系统资源的一个部分
资源限制 : 内存子系统为进程组设置内存使用上限,内存达到上限后再申请内存,就会发出 Out of Memory
优先级 : 通过优先级让一些组得到更多 CPU 等资源
资源审计 : 用来统计系统上实际把多少资源用到适合的目的上 , 可以使用 cpuacct 子系统记录某个进程组使用的 CPU 时间
隔离 : 为组隔离名字空间 , 这样一个组不会看到其他组的进程 .网络连接和文件系统
控制 : 挂起 . 恢复和启动等操作
(3)联合文件系统 (Union FS)
docker 中使用AUFS(another Union File System 或 v2 版本以后的Advanced multi-layered Unification File System) 控制为每一个成员目录设定只读 / 读写 / 写出权限 , 同时 AUFS 有一个类似分层的概念 , 对只读权限的分支可以逻辑上进行增量的修改
轻量级的高性能分层文件系统 , 它支持将文件系统中的修改信息作为一次提交 ,并层层叠加 , 并且可以将不同目录挂载到同一个虚拟文件系统下docker 目前支持的联合文件系统包括AUFS/btrfs/vfs/DeviceMappe
docker的存储
用户在使用 Docker 的过程中,势必需要查看容器内应用产生的数据,或者需要将容器内数据进行备份,甚至多个容器之间进行数据共享,这必然会涉及到容器的数据管理
docker 两种存储资源类型
(1)Data Volume (数据卷)
(2)Data Volume Dontainers --- 数据卷容器
Data Volume (数据卷)
Data Volume 本质上是 Docker Host 文件系统中的目录或文件,使用类似与 Linux 下对目录或者文件进行 mount 操作。数据卷可以在容器之间共享和重用,对数据卷的更改会立马生效,对数据卷的更新不会影响镜像,卷会一直存在,直到没有容器使用
Data Volume 有以下特点:
a)Data Volume 是目录或文件,而非没有格式化的磁盘(块设备)。
b)容器可以读写 volume 中的数据。
c)volume 数据可以被永久的保存,即使使用它的容器已经销毁。
Data Volume的使用:
通过-v 参数格式为 <host path>:<container path>
a)运行一个容器,并创建一个数据卷挂载到容器的目录上
#docker run -dti -v /web centos:7.0 /bin/bash
利用 centos:7.0 的镜像运行一个容器,并在容器内创建一个数据卷挂载到容器的 /web 目录上
b)运行一个容器,本地创建/date目录挂载到容器的/var/log/目录上
#docker run -dti -v /data:/var/log centos:7.0 /bin/bash
Data Volume Dontainers --- 数据卷容器
如果用户需要在容器之间共享一些持续更新的数据,最简单的方法就是使用数据卷容器,其实数据卷容器就是一个普通的容器,只不过是专门用它提供数据卷供其他容器挂载使用
Data Volume Dontainers使用:
a)创建一个名为 dbdata 的数据卷,并在其中创建一个数据卷挂载到 /dbdata
#docker run -dti -v /dbdata --name dbser centos:7.0 /bin/bash
--name 参数为给容器指定名字为dbser方便记忆
b)其他容器使用--volume-from 去挂载dbdata容器中的/dbdata数据卷
eg :创建 db1&db2 两个容器, 并挂载 /dbdata 数据卷到本地
#docker run -dti --volumes-from dbser --name db1 centos:7.0
#docker run -dti --volumes-from dbser --name db2 centos:7.0
此时,容器 db1 和 db2 同时挂载了同一个数据卷到本地相同 /dbdata目录。三个容器任何一个目录下的写入,都可以时时同步到另外两个
docker网络
docker 网络介绍
大量的互联网应用服务需要多个服务组件,这往往需要多个容器之间通过网络通信进行相互配合
docker 网络从覆盖范围可分为单个 host 上的容器网络和跨多个 host 的网络docker 目前提供了映射容器端口到宿主主机和容器互联机制来为容器提供网络服务,在启动容器的时候,如果不指定参数,在容器外部是没有办法通过网络来访问容器内部的网络应用和服务的
docker 安装时会自动在host上创建三个网络
#docker network ls
docker--none 网络
none 网络就是什么都没有的网络。挂在这个网络下的容器除了 lo,没有其他任何网卡。容器创建时,可以通过 --network=none 指定使用 none 网络。
none 网络的应用:
封闭的网络意味着隔离,一些对安全性要求高并且不需要联网的应用可以使用none 网络。
比如某个容器的唯一用途是生成随机密码,就可以放到 none 网络中避免密码被窃取。
docker--host 网络
连接到 host 网络的容器,共享 docker host 的网络栈,容器的网络配置与host 完全一样。可以通过 --network=host 指定使用 host 网络
在容器中可以看到 host 的所有网卡,并且连 hostname 也是 host 的。host网络的使用场景又是什么呢?
直接使用 Docker host 的网络最大的好处就是性能,如果容器对网络传输效率有较高要求,就可以选择 host 网络。
当然不便之处就是牺牲一些灵活性,比如要考虑端口冲突问题,Docker host上已经使用的端口就不能再用了。
Docker host 的另一个用途是让容器可以直接配置 host 网路。比如某些跨host 的网络解决方案,其本身也是以容器方式运行的,这些方案需要对网络进行配置,比如管理 iptables
docker--bridge 网络
docker 安装时会创建一个 命名为 docker0 的 linux bridge。如果不指定--network,创建的容器默认都会挂到 docker0 上
当前 docker0 上没有任何其他网络设备,我们创建一个容器看看有什么变化
一个新的网络接口 veth28c57df 被挂到了 docker0 上,veth28c57df就是新创建容器的虚拟网卡。
进入刚才运行的容器查看网络,容器有一个网卡 eth0@if34
实际上 eth0@if34 和 veth28c57df 是一对 veth pair
eth0@if34 已经配置了 IP 172.17.0.2,为什么是这个网段呢?
看一下 bridge 网络的配置信息:
#docker network inspect bridge
bridge 网络配置的 subnet 就是 172.17.0.0/16,并且网关是 172.17.0.1,在docker0上
容器创建时,docker 会自动从 172.17.0.0/16 中分配一个 IP,这里 16 位的掩码保证有足够多的 IP 可以供容器使用
创建 user-defined网络
我们可通过 bridge 驱动创建类似前面默认的 bridge 网络
(1)利用bridge驱动创建名为my-net2网桥(docker会自动分配网段):
#docker network create --driver brigde my-net2
(2)查看一下当前 host 的网络结构变化:
#docker network ls
(3)查看容器bridge网桥配置(bridge就是容器和网桥形成一对veth pair)
#docker network inspect bridge
(4)利用bridge驱动创建名为my-net3网桥(user-defined网段及网关)
#docker network create --driver brigde --subnet 172.22.1.0/24 --gateway 172.22.1.1 my-net3
(5)启动容器使用新建的my-net3网络
#docker run -it --network=my-net3 httpd
(6)启动容器使用my-net3网络并指定ip(只有使用 --subnet 创建的网络才能指定静态 IP,如果是docker自动分配的网段不可以指定ip)
#docker run -it --network=my-net3 --ip 172.22.1.10 httpd
(7)让已启动不同vlan的ningx容器,可以连接到my-net2(其实在nigx中新建了my-net2的网卡)
#docker run -it --network=my-net3 ningx
#docker network connect my-net2 ningx
(8)使用--name指定启动容器名字,可以使用docker自带DNS通信,但只能工作在user-defined 网络,默认的 bridge 网络是无法使用 DNS 的
#docker run -it --network=my_net2 --name=bbox1 busybox
#docker run -it --network=my_net2 --name=bbox2 busybox
(9)容器之间的网络互联
a). 首先创建一个 db 容器
# docker run -dti --name db centos:7.0
b). 创建一个 web 容器,并使其连接到 db
# docker run -dti --name web --link db:dblink centos:7.0/bin/bash
--link db:dblink 实际是连接对端的名字和这个链接的名字,也就是和 db 容器建立一个叫做 dblink 的链接
c). 查看链接的情况
#docker ps -a
d). 使用 ping 命令来测试网络链接的情况
(10)容器端口映
在启动容器的时候,如果不指定参数,在容器外部是没有办法通过网络来访问容
器内部的网络应用和服务的
当容器需要通信时,我们可以使用 -P (大) &&-p (小)来指定端口映射
-P : Docker 会随机映射一个 49000 ~ 49900 的端口到容器内部开放的网络端口
-p :则可以指定要映射的端口,并且在一个指定的端口上只可以绑定一个容器。
支持的格式有
IP : HostPort : ContainerPort
IP : : ContainerPort
IP : HostPort :
查看映射
#docker port
a)映射所有接口地址,此时绑定本地所有接口上的 5000 到容器的 5000 接口,访问任何一个本地接口的 5000 ,都会直接访问到容器内部
#docker run -dti -p 5000:5000 centos:7.0 /bin/bash
b)多次使用可以实现多个接口的映射
#docker run -dti -p 5000:5000 -p 5022:22 centos:7.0 /bin/bash
c)映射到指定地址的指定接口
此时会绑定本地 192.168.4.169 接口上的 5000 到容器的 5000 接口
#docker run -dti -p 192.168.4.169:5000:5000 centos:7.0 /bin/bash
d) 映射到指定地址的任意接口
此时会绑定本地 192.168.4.169 接口上的任意一个接口到容器的 5000 接口
#docker run -dti -p 192.168.4.169::5000 centos:7.0 /bin/bash
e) 使用接口标记来指定接口的类型
#docker run -dti -p 192.168.4.169::5000/UDP centos:7.0 /bin/bash
(11)实验:通过端口映射实现访问本地的 IP:PORT 可以访问到容器内的 web
a)将容器80端口映射到主机8080端口
#docker run -itd -p 8080:80 --name http-test httpd
b) 查看刚运行docker
#docker ps
c) 进入容器
#docker exec -itd httpd-test
d) 容器内部编辑网页文件 index.html
********#echo “hhhhhhhhhhhh” > /var/www/html/index.html
e)到宿主机上打开浏览器输入 IP:PORT 访问验证
网络排查命令:
iptables -t nat -L
ip r
tcpdump -i docker0 -n icmp
tcpdump -i eth0 -n icmp