Docker

1. docker概述

docker三件套:镜像,容器,仓库

容器与镜像的关系类似于面向对象编程中的对象与类

Docker 面向对象
容器 对象
镜像
  1. 从面向对象角度

    Docker利用容器独立运行的一个或一组应用,应用程序或服务运行在容器里面,容器就类似于一个虚拟化的运行环境,容器是用镜像创建的运行实例。就像是Java中的类和实例对象一样,镜像是静态的定义,容器是镜像运行时的实体。容器为镜像提供了一个标准的和隔离的运行环境,它可以被启动、开始、停止、删除。每个容器都是相互隔离的、保证安全的平台。

  2. 从镜像容器的角度

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

关于docker安装可自行上CSDN搜索,比较简单。注意配置国内镜像仓库

2. 为什么docker会比虚拟机快

  1. docker有着比虚拟机更少的抽象层

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

  2. 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镜像演示

  1. 启动ubuntu

    docker run -it ubuntu bash

    参数说明:

    • -i :交互式操作
    • -t: 终端
    • Ubuntu:Ubuntu镜像
    • bash:放在镜像名后的是命令,这里我们希望有个交互式Shell,因此用bash(/bin/bash)
    • exit退出终端(ctrl+p+q退出不会停止容器)
  2. 列出正在运行的容器

    docker ps

    image-20230103190907366

  3. 重启容器

    docker restart容器ID或容器名
  4. 停止容器

    docker stop 容器ID或容器名
  5. 强制停止容器

    docker kill 容器ID或容器名
  6. 启动已停止的容器

    docker start 容器ID或容器名
  7. 启动守护式容器(后台服务器)

    说明:Docker容器后台运行,就必须有一个前台进程。容器运行的命令如果不是那些一直挂起的命令(比如top,tail),就会自动退出(交互式容器),如

    docker run -d ubuntu

    image-20230105103203716

​ 我们可以看到虽然启动了一个容器,但是没有正在运行的容器,代表在启动一瞬间就自己退出了。

​ 下面用redis前后台启动演示case

  • ​ 前台交互式启动

    docker run -it redis:latest
  • 后台守护式启动

    docker run -d redis

    image-20230105103832097

​ 此时并没有自动退出,因为redis是守护式容器

​ 可以通过docker logs+容器ID查看前台输出

image-20230105104209092

  1. 查看容器内运行的进程

    docker top 容器ID

    image-20230105104721030

  2. 查看容器内部细节

    docker inspect 容器ID
  3. 进入后台容器并以命令行交互

     docker exec -it 容器ID bash
     docker attach 容器ID

    推荐使用docker exec命令,因为推出容器终端,不会导致容器停止

    • attach直接进入容器启动命令的终端,不会启动新的进程,用exit推出,会导致容器的停止
    • exec是在容器中打开新的终端,并且可以启动新的进程,用exit推出,不会导致容器的停止
  4. 拷贝容器文件到主机上

     docker cp 容器ID:容器内路径 目的主机路径
  5. 导入导出容器

    docker export 容器ID > 文件名.tar

    image-20230105111801366

    cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号

image-20230105112321255

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 要创建的目标镜像名:[标签名]

image-20230108132254285

如图,原始默认的ubuntu镜像是不带 vim 命令的

首先升级apt

apt update

安装vim

apt install -y vim

安装完成后就可以使用vim命令

打包成镜像,注意等号‘=’前后不要加空格

docker commit -m="add vim cmd" -a="yellowswimming" 6aad128010e3 hy/myubuntu:1.0

image-20230108135117537

打包后可以查看到我们发布的镜像以及大小

现在重新启动打包好的镜像

image-20230108135655380

现在就有vim命令了,不用再去额外安装,且刚才写入的内容依旧存在

6. 远程仓库和本地仓库

6.1 远程仓库

这里我使用的是阿里云容器镜像服务

  1. 创建命名空间

image-20230110142452295

  1. 创建仓库(以添加了vim的ubuntu镜像为例)

    image-20230110142520979

  2. 根据操作显示指令

    如果镜像仓库建立完毕,则会在网页上显示如何推送和拉取本地镜像(如果在创建仓库时选择其他代码源可忽略)

    image-20230110142609860

本地推送成功后,则会在阿里云镜像版本中看到对应的镜像

image-20230110142741309

6.2 拉取远程镜像

现在我先删除已有的ubuntu镜像image-20230110143014874

从远程仓库拉取镜像

image-20230110143133115

可以看到这里已经被拉取下来了

测试ubuntu之前的1.txt文件是否仍然存在

image-20230110143239489

7. docker私有仓库搭建

官方Docker Hub地址:https://hub.docker.com/,中国大陆访问过慢并且有被阿里云替代的趋势

但是阿里云这样的镜像仓库也可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要搭建一个本地私人仓库给团队使用

Docker Registry是官方提供的工具,可以用于构建私有镜像仓库

  1. 下载registry镜像

    image-20230110145458184

  2. 运行容器

    docker run -d -p 5000:5000 -v /hyuse/myregistry/:/tmp/registry --privileged=true registry
  3. 在官方ubuntu上添加ifconfig命令

    apt update
    apt install net-tools

    image-20230110150458689

  4. commit镜像

    docker commit -m="ifconfig cmd add" -a="hy" 8e87c4a29c2f  myubuntu:1.1

    image-20230110150813214

  5. curl验证私服上存在哪些镜像

    curl -XGET http://10.0.0.16:5000/v2/_catalog

    image-20230110151309673

  6. 将新镜像myubuntu:1.1 修改为符合私服规范的tag

    docker tag myubuntu:1.1 10.0.0.16:5000/myubuntu:1.1

    image-20230110151705755

  7. 修改配置文件使之支持http(修改配置文件后需要重启docker)

image-20230110151932334

  1. push推送到私服库(提前再次启动registry容器)

    docker push 10.0.0.16:5000/myubuntu:1.1

image-20230110152607542

  1. 再次查看私服镜像

    image-20230110152709947

  2. 拉取私服镜像

     docker pull 10.0.0.16:5000/myubuntu:1.1

    image-20230110153251345

8. 数据卷

卷就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供的一些用于持续存储或共享数据的特性:

卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。

命令:

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

容器卷特点:

  1. 数据卷可在容器之间共享或重用数据
  2. 卷中的更改可以直接实时生效
  3. 数据卷中的更改不会包含在镜像的更新中
  4. 数据卷的生命周期一直持续到没有容器使用它为之

下面以ubuntu演示

docker run -it --privileged=true -v /tmp/host_data:/tmp/docker_data --name=u1 ubuntu

image-20230111210852728

如图,可以实现两端数据的同步

可通过docker inspcet 容器ID 查看容器详细信息

image-20230111211600263

若限制容器内只读,则

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

image-20230112162725639

如图,u1的容器卷已经被继承过来了,并且u1和u2的修改都会被同步到对方以及主机目录(已经不是简单的继承了)

9.DockerFile

DockerFile是用来构建Docker镜像的文本文件,是由一条条构建镜像所需的指令和参数构成的脚本文件

9.1 DockerFile内容的基础知识

  1. 每条保留字指令都必须为大写字母且后面要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. 表示注释

  4. 每条指令都会创建一个新的镜像层并对镜像进行提交

9.2 docker执行DockerFile的大致流程

  1. docker从基础镜像运行一个容器
  2. 执行一条指令并对容器进行修改
  3. 执行类似docker commit的操作提交一个新的镜像层
  4. docker再基于刚提交的镜像运行一个新容器
  5. 执行dockerfile的下一条指令直到所有指令都执行完成

9.3 DockerFile常见保留字

  1. FROM 基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板,第一条必须是from

  2. MAINTAINER 镜像维护者的姓名和邮箱地址

  3. RUN 容器构建时需要运行的命令。有shell和exec格式,RUN是在docker build 时运行

  4. EXPOSE 当前容器对外暴露出的端口

  5. WORKDIR 指定在创建容器后,终端默认登陆进来的工作目录,一个落脚点

  6. USER 指定该镜像以什么样的用户去执行,如果都不指定,默认是root

  7. ENV 用来在构建镜像过程中设置环境变量

  8. VOLUME 用来做数据保存和持久化工作

  9. ADD 将宿主机目录下的文件拷贝进镜像且会自动处理URL和解压tar压缩包

  10. CMD 指定容器启动后要干的事情。DockerFile可以有多个CMD指令,但只有最后一个生效,CMD会被docker run 之后的参数替换。它和前面RUN命令区别:1.CMD是在docker run 时运行 2.RUN是在docker build时运行

  11. ENTRYPOINT

    image-20230125110630791

10. centos之dockerFile案例

要求:

  1. vim
  2. jdk8
  3. ifconfig

jdk下载地址:mirrors.yangxingzhen.com/jdk/

首先编写Dockerfile文件

新建文件夹 myfile

image-20230129102406313

上传刚下载的jdk8并且编辑Dockerfile

image-20230129103746373

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文件

开始运行新镜像

image-20230129105132843

现在已经实现了所有功能

11. 虚悬镜像

查看本地所有虚悬镜像

docker images ls -f dangling=true

一般来说,用了FROM 基本镜像后会生成虚悬镜像,此时我们需要把生成的虚悬镜像删除掉

移除所有虚悬镜像

docker image prune

12. Docker之network

image-20230131155251086

列出docker网络

docker network ls

image-20230131112815779

通过 docker network --help 可以查看所有命令

作用:

  1. 容器间的互联和通信以及端口映射
  2. 容器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和容器映射变化

image-20230131161301482

这里IP是docker内部自动分配的

12.3 docker网络之bridge

Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信

  1. 整个宿主机的网桥模式都是docker0,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让它们彼此连通,这样一对接口叫veth pair
  2. 每个容器实例内部也有一块网卡,每个接口叫eth0
  3. docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配

通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。

image-20230202104207112

12.4 docker网络之host

直接使用宿主机的IP地址与外界进行通信,不需要额外的NAT转换

容器将不会获得一个独立的Network Namespace,而是和宿主机共用一个Network Namespace。容器将不会虚拟出自己的网卡而是使用宿主机的IP和端口。

image-20230202105751070

12.5 docker网络之none

在none模式下,并不为Docker容器进行任何网络配置。也就是说,这个Docker容器没有网卡、ip、路由等信息,只有一个lo。需要我们自己为Docker容器添加网卡、配置IP等。

12.6 docker网络之container

新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。

image-20230202111438532

启动命令例如

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

image-20230202115505567

接着启动两台tomcat容器,分别为tomcat81和tomcat82,注意启动时加入自定义网络

进入tomcat81容器,然后直接ping服务名,如果没有安装ping命令,需要手动安装

image-20230202120715421

image-20230202120845117

13. Docker-compose容器编排

Compose是Docker公司推出的一个工具软件,可以管理多个Docker容器组成一个应用。你需要定义一个yaml格式的配置文件docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器

13.1 首先按照官方文档下载docker-compose

image-20230203155200902

13.2 docker-compose核心概念

  1. 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
  2. 使用docker-compose.yml定义一个完整的业务单元,安排好整体应用中的各个容器服务。
  3. 随后,执行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后即可见到如下界面

image-20230203195343440

最后修改:2023 年 02 月 04 日
如果觉得我的文章对你有用,请随意赞赏