Docker
1. docker概述
docker三件套:镜像,容器,仓库
容器与镜像的关系类似于面向对象编程中的对象与类
Docker | 面向对象 |
---|---|
容器 | 对象 |
镜像 | 类 |
-
从面向对象角度
Docker利用容器独立运行的一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。就像是Java中的类和实例对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。
-
从镜像容器的角度
可以把容器看做是一个简易版的Linux环境(包括root用户权限、进程空间、用户空间和网络空间等)和运行在其中的应用程序
关于docker安装可自行上CSDN搜索,比较简单。注意配置国内镜像仓库
2. 为什么docker会比虚拟机快
-
docker有着比虚拟机更少的抽象层
由于docker不需要Hypervisor实现硬件资源虚拟化,运行在docker容器上的程序直接使用的都是实际物理机的硬件资源。因此在CPU、内存利用率上docker将会在效率上有明显的优势
-
docker利用的是宿主机内核,而不需要加载操作系统OS的内核
当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。进而避免引寻、加载操作系统内核返回等比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载OS,返回新建过程是分钟级。而docker由于直接利用宿主机操作系统,则省略了返回过程,因此新建一个docker容器只需要几秒钟
Docker容器 | 虚拟机(VM) | |
---|---|---|
操作系统 | 与宿主机共享OS | 宿主机OS上运行虚拟机OS |
存储大小 | 镜像小,便于存储与传输 | 镜像庞大(vmdk、vdi等) |
运行性能 | 几乎无额外性能损失 | 操作系统额外的CPU、内存消耗 |
移植性 | 轻便、灵活、适应于Linux | 笨重,与虚拟化技术耦合度高 |
硬件亲和性 | 面向软件开发者 | 面向硬件运维者 |
部署速度 | 快速,秒级 | 较慢,10s以上 |
3. Docker常用命令
3.1 帮助启动类命令
- 启动docker:systemctl start docker
- 停止docker: systemctl stop docker
- 重启docker:systemctl restart docker
- 查看docker状态 systemctl status docker
- 开机启动:systemctl enable docker
- 查看docker概要信息:docker info
- 查看docker总体帮助文档:docker --help
- 查看docker命令帮助文档: docker具体命令 --help
3.2 镜像命令
- docker images-》 列出本地主机上的镜像
- docker search 镜像名
- docker pull 镜像名
- docker system df-》 查看镜像/容器/数据卷所占的空间
- docker rmi 镜像ID
ps:仓库名和标签名都是None的镜像称为虚悬镜像
3.3 容器命令
只要记住--help现查就可以
本次以Ubuntu镜像演示
-
启动ubuntu
docker run -it ubuntu bash
参数说明:
- -i :交互式操作
- -t: 终端
- Ubuntu:Ubuntu镜像
- bash:放在镜像名后的是命令,这里我们希望有个交互式Shell,因此用bash(/bin/bash)
- exit退出终端(ctrl+p+q退出不会停止容器)
-
列出正在运行的容器
docker ps
-
重启容器
docker restart容器ID或容器名
-
停止容器
docker stop 容器ID或容器名
-
强制停止容器
docker kill 容器ID或容器名
-
启动已停止的容器
docker start 容器ID或容器名
-
启动守护式容器(后台服务器)
说明:Docker容器后台运行,就必须有一个前台进程。容器运行的命令如果不是那些一直挂起的命令(比如top,tail),就会自动退出(交互式容器),如
docker run -d ubuntu
我们可以看到虽然启动了一个容器,但是没有正在运行的容器,代表在启动一瞬间就自己退出了。
下面用redis前后台启动演示case
-
前台交互式启动
docker run -it redis:latest
-
后台守护式启动
docker run -d redis
此时并没有自动退出,因为redis是守护式容器
可以通过docker logs+容器ID查看前台输出
-
查看容器内运行的进程
docker top 容器ID
-
查看容器内部细节
docker inspect 容器ID
-
进入后台容器并以命令行交互
docker exec -it 容器ID bash
docker attach 容器ID
推荐使用docker exec命令,因为推出容器终端,不会导致容器停止
- attach直接进入容器启动命令的终端,不会启动新的进程,用exit推出,会导致容器的停止
- exec是在容器中打开新的终端,并且可以启动新的进程,用exit推出,不会导致容器的停止
-
拷贝容器文件到主机上
docker cp 容器ID:容器内路径 目的主机路径
-
导入导出容器
docker export 容器ID > 文件名.tar
cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
4. docker镜像底层原理
UnionFS(联合文件·系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(类比Java中的Object类),可以制作各种具体的镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有的底层和目录。
4.1 docker镜像加载原理
docker镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootfs(boot file system)主要包含bootloader和kernel,bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是引导文件系统bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就在内存中了,此时内存的使用权已由botofs转交给内核,此时系统也会卸载bootfs。
rootfs(root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc,/bin,/etc等标准目录和文件。rootfs就是各种不同操作系统发行版,比如Ubuntu,Centos等等。对于一个精简的OS,rootfs可以很小,只需要包括最基本的命令、工具和程序库就可以了,因为底层直接用Host的kernel,自己只需要提供rootfs就行了。由此可见对于不同的linux发行版,bootfs基本是一致的,rootfs会有差别,因此不同的发行版可以公用bootfs。
4.2 镜像分层好处及注意点
镜像分层的最大一个好处就是共享资源,方便复制迁移,就是为了复用。比如说有多个镜像都从相同的base镜像构建而来,那么Docker Host只需要在磁盘上保存一份base镜像;同时内存中也只需要加载一份base镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。
ps:Docker镜像层都是只读的,容器层是可写的。当容器层启动时,一个新的可写层被加载到镜像的顶部。这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层。只有容器层是可写的,容器层下面的所有镜像层都是只读的。
5.docker commit操作案例
本案例以ubuntu镜像添加 vim 命令为例
docker commit //提交容器副本使之成为一个新的镜像
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名]
如图,原始默认的ubuntu镜像是不带 vim 命令的
首先升级apt
apt update
安装vim
apt install -y vim
安装完成后就可以使用vim命令
打包成镜像,注意等号‘=’前后不要加空格
docker commit -m="add vim cmd" -a="yellowswimming" 6aad128010e3 hy/myubuntu:1.0
打包后可以查看到我们发布的镜像以及大小
现在重新启动打包好的镜像
现在就有vim命令了,不用再去额外安装,且刚才写入的内容依旧存在
6. 远程仓库和本地仓库
6.1 远程仓库
这里我使用的是阿里云容器镜像服务
- 创建命名空间
-
创建仓库(以添加了vim的ubuntu镜像为例)
-
根据操作显示指令
如果镜像仓库建立完毕,则会在网页上显示如何推送和拉取本地镜像(如果在创建仓库时选择其他代码源可忽略)
本地推送成功后,则会在阿里云镜像版本中看到对应的镜像
6.2 拉取远程镜像
现在我先删除已有的ubuntu镜像
从远程仓库拉取镜像
可以看到这里已经被拉取下来了
测试ubuntu之前的1.txt文件是否仍然存在
7. docker私有仓库搭建
官方Docker Hub地址:https://hub.docker.com/,中国大陆访问过慢并且有被阿里云替代的趋势
但是阿里云这样的镜像仓库也可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要搭建一个本地私人仓库给团队使用
Docker Registry是官方提供的工具,可以用于构建私有镜像仓库
-
下载registry镜像
-
运行容器
docker run -d -p 5000:5000 -v /hyuse/myregistry/:/tmp/registry --privileged=true registry
-
在官方ubuntu上添加ifconfig命令
apt update
apt install net-tools
-
commit镜像
docker commit -m="ifconfig cmd add" -a="hy" 8e87c4a29c2f myubuntu:1.1
-
curl验证私服上存在哪些镜像
curl -XGET http://10.0.0.16:5000/v2/_catalog
-
将新镜像myubuntu:1.1 修改为符合私服规范的tag
docker tag myubuntu:1.1 10.0.0.16:5000/myubuntu:1.1
-
修改配置文件使之支持http(修改配置文件后需要重启docker)
-
push推送到私服库(提前再次启动registry容器)
docker push 10.0.0.16:5000/myubuntu:1.1
-
再次查看私服镜像
-
拉取私服镜像
docker pull 10.0.0.16:5000/myubuntu:1.1
8. 数据卷
卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供的一些用于持续存储或共享数据的特性:
卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。
命令:
docker run -it/-d --privileged=true -v /宿主机绝对路径目录:/容器内目录 镜像名
容器卷特点:
- 数据卷可在容器之间共享或重用数据
- 卷中的更改可以直接实时生效
- 数据卷中的更改不会包含在镜像的更新中
- 数据卷的生命周期一直持续到没有容器使用它为之
下面以ubuntu演示
docker run -it --privileged=true -v /tmp/host_data:/tmp/docker_data --name=u1 ubuntu
如图,可以实现两端数据的同步
可通过docker inspcet 容器ID 查看容器详细信息
若限制容器内只读,则
docker run -it --privileged=true -v /tmp/host_data:/tmp/docker_data:ro --name=u1 ubuntu
这样,容器内修改文件就禁止了
数据卷的继承:
命令:docker run -it --privileged=true --volumes-from 父类容器名 --name u2 ubuntu
docker run -it --privileged=true --volumes-from u1 --name u2 ubuntu
如图,u1的容器卷已经被继承过来了,并且u1和u2的修改都会被同步到对方以及主机目录(已经不是简单的继承了)
9.DockerFile
DockerFile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本文件
9.1 DockerFile内容的基础知识
- 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
- 指令按照从上到下,顺序执行
-
表示注释
- 每条指令都会创建一个新的镜像层并对镜像进行提交
9.2 docker执行DockerFile的大致流程
- docker从基础镜像运行一个容器
- 执行一条指令并对容器进行修改
- 执行类似docker commit的操作提交一个新的镜像层
- docker再基于刚提交的镜像运行一个新容器
- 执行dockerfile的下一条指令直到所有指令都执行完成
9.3 DockerFile常见保留字
-
FROM 基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是from
-
MAINTAINER 镜像维护者的姓名和邮箱地址
-
RUN 容器构建时需要运行的命令。有shell和exec格式,RUN是在docker build 时运行
-
EXPOSE 当前容器对外暴露出的端口
-
WORKDIR 指定在创建容器后,终端默认登陆进来的工作目录,一个落脚点
-
USER 指定该镜像以什么样的用户去执行,如果都不指定,默认是root
-
ENV 用来在构建镜像过程中设置环境变量
-
VOLUME 用来做数据保存和持久化工作
-
ADD 将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包
-
CMD 指定容器启动后要干的事情。DockerFile可以有多个CMD指令,但只有最后一个生效,CMD会被docker run 之后的参数替换。它和前面RUN命令区别:1.CMD是在docker run 时运行 2.RUN是在docker build时运行
-
ENTRYPOINT
10. centos之dockerFile案例
要求:
- vim
- jdk8
- ifconfig
jdk下载地址:mirrors.yangxingzhen.com/jdk/
首先编写Dockerfile文件
新建文件夹 myfile
上传刚下载的jdk8并且编辑Dockerfile
FROM centos:7
MAINTAINER hy<yellowswimming@126.com>
ENV MYPATH /usr/local
WORKDIR $MYPATH
#安装vim编辑器
RUN yum -y install vim
#安装ifconfig命令查看网络IP
RUN yum -y install net-tools
#安装java8及lib库
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
ADD jdk-8u171-linux-x64.tar.gz /usr/local/java/
#配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_171
ENV JRE_HOME $JAVA_HOME/jre
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar:$JRE_HOME/lib:$CLASSPATH
ENV PATH $JAVA_HOME/bin:$PATH
EXPOSE 80
CMD echo $MYPATH
CMD echo "success run centos by Dockerfile"
CMD /bin/bash
下面开始构建
docker build -t centosjava8:1.0 .
最后那个. 代表当前目录下寻找Dockerfile文件
开始运行新镜像
现在已经实现了所有功能
11. 虚悬镜像
查看本地所有虚悬镜像
docker images ls -f dangling=true
一般来说,用了FROM 基本镜像后会生成虚悬镜像,此时我们需要把生成的虚悬镜像删除掉
移除所有虚悬镜像
docker image prune
12. Docker之network
列出docker网络
docker network ls
通过 docker network --help 可以查看所有命令
作用:
- 容器间的互联和通信以及端口映射
- 容器IP变动时候可以通过服务名直接网络通信而不受到影响
12.1. network常见网络模式
网络模式 | 简介 |
---|---|
bridge | 为每一个容器分配、设置IP等,并将容器连接到一个docker0。虚拟网桥,默认为该模式 |
host | 容器将不会虚拟出自己的网卡,配置自己的IP等,而是使用宿主机的IP和端口 |
none | 容器有独立的Network namespace,但并没有对其进行任何网络设置,如分配veth pair 和网桥连接,IP等 |
container | 新创建的容器不会创建自己的网卡和配置自己的IP,而是和一个指定对的容器共享IP、端口范围等 |
- bridge模式:使用 --network bridge 指定, 默认使用docker0
- host模式:使用 --network host 指定
- none模式:使用 --network none 指定
- container模式:使用 --network container:NAME或者容器ID指定
12.2. 底层ip和容器映射变化
这里IP是docker内部自动分配的
12.3 docker网络之bridge
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信
- 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通,这样一对接口叫veth pair
- 每个容器实例内部也有一块网卡,每个接口叫eth0
- docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配
通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
12.4 docker网络之host
直接使用宿主机的IP地址与外界进行通信,不需要额外的NAT转换
容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。
12.5 docker网络之none
在none模式下,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、ip、路由等信息,只有一个lo。需要我们自己为Docker容器添加网卡、配置IP等。
12.6 docker网络之container
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
启动命令例如
docker run -d -p 8081:8080 --network container:tomcat82 --name tomcat81 billygoo/tomcat8-jdk8
ps:如果被依赖的docker容器关闭了,那么自己也会只有一个lo了
12.7 docker网络之自定义网络
如果想让容器之间通过服务名相互访问,而不是简单通过ip地址访问(ip地址可能会发生变化),此时就需要加入自定义网络
docker network create customnetwork
接着启动两台tomcat容器,分别为tomcat81和tomcat82,注意启动时加入自定义网络
进入tomcat81容器,然后直接ping服务名,如果没有安装ping命令,需要手动安装
13. Docker-compose容器编排
Compose是Docker公司推出的一个工具软件,可以管理多个Docker容器组成一个应用。你需要定义一个yaml格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器
13.1 首先按照官方文档下载docker-compose
13.2 docker-compose核心概念
- 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
- 使用docker-compose.yml定义一个完整的业务单元,安排好整体应用中的各个容器服务。
- 随后,执行docker-compose up 命令来启动并运行整个应用程序,完成一键部署上线
14. 容器可视化工具
Portainer是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境
使用如下命令下载Portainer
docker run -d -p 8000:8000 -p 9000:9000 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v portainer_data:/data portainer/portainer
然后访问linux服务器9000端口,注册登录好admin后即可见到如下界面