# Docker学习与使用

By [samehada](https://paragraph.com/@samehada) · 2023-03-20

---

Docker—学习与应用
============

1.Docker概述
----------

Docker是一种轻量级的容器化技术，可以将应用和其依赖项封装成一个可移植的容器，这使得在不同的环境中部署和运行应用程序变得更加容易。Docker容器技术的核心思想是隔离和打包装箱，每个容器是互相隔离的。

Docker诞生于2010年，最初是由一家名为dotCloud的公司开发的，该公司提供了一些pass的云计算服务。Docker最初并没有受到行业的关注，直到2013年它成为了开源项目，越来越多的人开始认识到它的优点，Docker也因此变得越来越流行。

![docker-logo](https://storage.googleapis.com/papyrus_images/28c55a135ab51f67a05f972c415a48b8569a7de5eead886fe676a72bcc8ceec8.webp)

docker-logo

与传统的虚拟机技术相比，Docker的优点在于轻量级、资源占用少、启动快速等方面。Docker容器化技术也是一种虚拟化技术，但与虚拟机不同的是，容器内的应用直接运行在宿主机的内核上，没有自己的内核和虚拟硬件，每个容器是互相隔离的，因此可以更快速地部署和运行应用程序。

![docker-structure](https://storage.googleapis.com/papyrus_images/d893976101365049640742776bdac5452b53ce8946e9453dc85882b77a6018ed.png)

docker-structure

Docker的作用在于实现DevOps，即开发和运维的快速协作，将应用程序的构建、测试、部署等过程自动化，提高效率和质量，降低成本。Docker提供了一整套的工具和服务，包括Docker引擎、Docker Hub、Docker Compose等，可以方便地管理和维护容器化应用程序。

> Docker是基于Golang开发的开源项目
> 
> 官网地址：[https://www.docker.com/](https://www.docker.com/)
> 
> 文档地址：[https://docs.docker.com/](https://docs.docker.com/)
> 
> 仓库地址：[https://hub.docker.com](https://hub.docker.com)

* * *

2.Docker安装
----------

### 2.1 Docker的基本组成

![docker](https://storage.googleapis.com/papyrus_images/9f14d2e252b48614f16737f3452c00b9f505b7097ffb7ec5c194c5003104d6c3.png)

docker

Docker是一种轻量级容器技术，它的核心组成部分有镜像、容器和仓库。本文将在下面介绍这三个组成部分的概念和作用。

*   **镜像**：镜像就像是一个模板，可以通过它来创建容器服务。例如，使用tomcat镜像可以创建一个tomcat01容器，容器中可以运行最终服务或项目。
    
*   **容器**：容器则是通过镜像来创建的，利用容器技术独立运行一个或多个应用程序。启动、停止、删除等基本命令可以操作容器。可以把容器理解为一个简易的Linux系统。
    
*   **仓库**：仓库是存放镜像的地方，分为公有仓库和私有仓库。DockerHub和阿里云都有容器服务器，可以配置镜像加速。
    

通过使用Docker，可以方便地创建、部署和管理应用程序，加速开发和部署流程，提高开发效率和应用程序的可移植性。

### 2.2 搭建Linux环境

本文，笔者使用的是Unbuntu18.04版本的虚拟机，使用XShell连接工具对虚拟机进行操作，配置完成Linux环境后确保Xshell可以正常访问Ubuntu文件。

![ubuntu](https://storage.googleapis.com/papyrus_images/f1a5035c7d7ce62c25b687766b284bd2b4ff44c25db53ee3b4790f63b138adfe.png)

ubuntu

### 2.3 安装Docker

1.  首先需要先卸载掉Docker的旧版本文件数据。
    
        sudo apt-get remove docker docker-engine docker.io containerd runc
        
    
2.  通过仓库的方式安装。
    
         sudo apt-get update
        
         sudo apt-get install \
            ca-certificates \
            curl \
            gnupg \
            lsb-release
        
    
3.  下面添加Docker的官方GPG key。
    
         curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
        
         # 国内阿里云版
         curl -fsSL https://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | apt-key add -
        
    
4.  接下来验证key的指纹
    
        sudo apt-key fingerprint 0EBFCD88
        
    
    正常的输出为：
    
        root@camile:/# sudo apt-key fingerprint 0EBFCD88
        pub   rsa4096 2017-02-22 [SCEA]
              9DC8 5822 9FC7 DD38 854A  E2D8 8D81 803C 0EBF CD88
        uid           [ 未知 ] Docker Release (CE deb) <docker@docker.com>
        sub   rsa4096 2017-02-22 [S]
        
    
5.  下面我们添加稳定版的docker仓库或者国内阿里云版本。
    
        # 官方版本
        sudo add-apt-repository \
           "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
           $(lsb_release -cs) \
           stable"
        # 国内阿里云版
        sudo add-apt-repository \
           "deb [arch=amd64] https://mirrors.aliyun.com/docker-ce/linux/ubuntu \
           $(lsb_release -cs) \
           stable"
        
    
6.  安装我们最新版本的docker ce和container。
    
         sudo apt-get update
         sudo apt-get install docker-ce docker-ce-cli containerd.io
        
    
7.  查看我们安装的docker版本信息。
    
        docker --version
        
    
8.  将非root用户加入docker组，以允许免`sudo`执行`docker`.
    
        sudo gpasswd -a 用户名 docker
        
    
    重启服务并刷新docker组成员：
    
        sudo service docker restart
        newgrp - docker
        
    
    设置开机自启动并启动 Docker-ce（安装成功后默认已设置并启动，可忽略）
    
        sudo systemctl enable docker
        sudo systemctl start docker
        
    
9.  接下来我们安装docker-compose [https://www.runoob.com/docker/docker-compose.html](https://www.runoob.com/docker/docker-compose.html)
    
        sudo curl -L https://github.com/docker/compose/releases/download/1.25.4/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
        
        sudo chmod +x /usr/local/bin/docker-compose
        
        sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose
        
        docker-compose --version
        
    
10.  测试我们的第一个镜像hello-world。
    
        docker run hello-world
        
    

![hello-world](https://storage.googleapis.com/papyrus_images/76f4ed993e4c00f62eab19c71290f1084bdbd3a9a1ecc9ab128706012370869a.png)

hello-world

1.  卸载docker命令如下所示：
    
         sudo apt-get purge docker-ce docker-ce-cli containerd.io
         sudo rm -rf /var/lib/docker
         sudo rm -rf /var/lib/containerd
        
    

### 2.4 配置阿里云镜像加速器

在阿里云上，提供了一些免费的容器镜像服务，我们找到控制台->左侧菜单->容器镜像服务->镜像加速器，其中Ubuntu配置镜像加速器的命令已给出:

    sudo mkdir -p /etc/docker
    sudo tee /etc/docker/daemon.json <<-'EOF'
    {
      "registry-mirrors": ["https://alvnh3pv.mirror.aliyuncs.com"]
    }
    EOF
    sudo systemctl daemon-reload
    sudo systemctl restart docker
    

### 2.5 回顾

上面我们运行了一个docker run的命令，笔者绘制了一个流程图供大家参考

![process](https://storage.googleapis.com/papyrus_images/4d307bf1fb96a415a8356a96c2db4e87549006de709be85eb4dda7c5ad86559d.png)

process

那么Docker究竟是怎么工作的呢？这里笔者简单的介绍一下，Dcoker是一个 Client-Server结构的系统，Docker的守护进程运行在主机上，通过Socker从客户端访问。而DockerServer 接收到 Docker-Client 的指令，就会执行这个命令。如下所示

![工作方式](https://storage.googleapis.com/papyrus_images/38ae305828730a86e78bb78afd761e4286941a57b54b7afde71857b16658cb01.png)

工作方式

* * *

3.Docker常用命令
------------

这一节，笔者总结了一些Docker的常用命令，速览以下有助于后续的使用。对于一些命令，笔者会在虚拟机上试验并给出响应结果。

### 3.1 帮助命令

    docker version        # 版本信息
    docker info           # 显示docker的系统信息，包括容器和镜像的信息
    docker [命令] --help   # 帮助文档，官网在 doc下的reference 下的 command-line reference
    

### 3.2 镜像命令

**docker images**：查看本机所有的镜像

    root@camile:~# docker images
    REPOSITORY    TAG       IMAGE ID       CREATED        SIZE
    hello-world   latest    feb5d9fea6a5   5 months ago   13.3kB
    
    # 解释
    REPOSITORY：镜像的仓库源
    TAG：镜像的标签
    IMAGE ID：镜像的ID
    CREATED：镜像的创建时间
    SIZE：镜像的大小
    
    # 可选项
    Options:
      -a, --all             # 列出所有镜像 
      -f, --filter          # 过滤镜像
      -q, --quiet           # 只显示镜像的id
      -aq                   # 列出所有镜像id
    

\*\*docker search：\*\*搜索镜像

    root@camile:~# docker search mysql
    NAME                             DESCRIPTION                                     STARS     OFFICIAL   AUTOMATED
    mysql/mysql-server               Optimized MySQL Server Docker images. Create…   907                  [OK]
    
    # 可选项
    Options:
      -f=STARS=3000 --filter=STARS=3000
    

**docker pull**：下载镜像

    # 下载镜像 docker pull 镜像名[:tag]
    root@camile:~# docker pull mysql
    Using default tag: latest # 默认是最新版本
    latest: Pulling from library/mysql
    72a69066d2fe: Pull complete  # 分层下载，docker image 的核心 联合文件系统
    93619dbc5b36: Pull complete 
    99da31dd6142: Pull complete 
    626033c43d70: Pull complete 
    37d5d7efb64e: Pull complete 
    ac563158d721: Pull complete 
    d2ba16033dad: Pull complete 
    688ba7d5c01a: Pull complete 
    00e060b6d11d: Pull complete 
    1c04857f594f: Pull complete 
    4d7cfa90e6ea: Pull complete 
    e0431212d27d: Pull complete 
    Digest: sha256:e9027fe4d91c0153429607251656806cc784e914937271037f7738bd5b8e7709  #签名
    Status: Downloaded newer image for mysql:latest
    docker.io/library/mysql:latest # 真实地址
    
    # 等价于以下命令
    docker pull docker.io/library/mysql:latest
    
    # 指定版本下载
    root@camile:~# docker pull mysql:5.7
    5.7: Pulling from library/mysql
    72a69066d2fe: Already exists 
    93619dbc5b36: Already exists 
    99da31dd6142: Already exists 
    626033c43d70: Already exists 
    37d5d7efb64e: Already exists 
    ac563158d721: Already exists 
    d2ba16033dad: Already exists 
    0ceb82207cd7: Pull complete 
    37f2405cae96: Pull complete 
    e2482e017e53: Pull complete 
    70deed891d42: Pull complete 
    Digest: sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
    Status: Downloaded newer image for mysql:5.7
    docker.io/library/mysql:5.7
    

![images](https://storage.googleapis.com/papyrus_images/6e228f72656679ba7b9f641bdc4d99e47b3f3ac7205b037e73e47c0cf91dc20f.png)

images

**docker rmi**：删除镜像

    # 按照id删除镜像
    docker rmi -f 容器id # 删除指定的镜像
    docker rmi -f 容器id 容器id 容器id # 删除多个镜像
    docker rmi -f $(docker images -aq) # 删除全部镜像
    

### 3.3 容器命令

有了镜像，我们才可以创建容器，我们下载一个centos镜像来测试学习。

**新建容器并启动**

    docker run [可选参数] image
    
    # 参数说明
    --name=“Name”     容器名字 tomcat01 tomcat02 用来区分容器
    -d                后台方式运行
    -i -t -it         使用交互方式运行，进入容器查看内容
    -p                指定容器的端口 -p 8080：8080
    -p ip:主机端口：容器端口
    -p 主机端口：容器端口（常用）
    -p 容器端口
    容器端口
    -p    随机指定端口
    
    # 测试
    root@camile:~# docker run -it centos /bin/bash # 启动容器并进入容器内部
    [root@f2e7722031c0 /] # 进入容器内部 root@后面就是容器的id
    [root@f2e7722031c0 /]# ls # 查看容器内的centos目录
    bin  etc   lib      lost+found  mnt  proc  run   srv  tmp  var
    dev  home  lib64  media       opt  root  sbin  sys  usr
    [root@f2e7722031c0 /]# exit  # 从容器中退回主机
    exit
    root@camile:~# 
    

**列出所有运行中的容器**

    # docker ps 命令
        # 列出当前正在运行的容器
    -a    # 列出当前正在进行的容器+带出历史运行的容器
    -n=？    # 显示最近几个创建的容器
    -q  # 只显示容器的ID
    root@camile:~# docker ps -a
    CONTAINER ID   IMAGE     COMMAND       CREATED       STATUS                   PORTS     NAMES
    f2e7722031c0   centos    "/bin/bash"   4 hours ago   Exited (0) 4 hours ago             amazing_rosalind
    

**退出容器**

     exit # 直接容器停止并退出
     CTRL + P + Q # 容器不停止并tui'chu
    

**删除容器**

    docker rm 容器id                 # 删除指定的容器，不能删除正在运行的容器
    docker rm -f $(docker ps -aq)   # 删除所有的容器
    docker ps -aq | xargs docker rm # 删除所有的容器
    

**启动和停止容器的操作**

    docker start 容器 id
    docker restart 容器id
    docker stop 容器id
    docker kill 容器id
    

### 3.4 其他命令

**后台启动容器**

    # 命令 docker run -d 镜像名;
    root@camile:~# docker run -d centos
    # 问题 docker ps 发现，centos 停止了
    
    # 常见的坑， docker 容器使用后台运行，就必须有一个前台进程，docker容器发现没有应用，就会自动停止
    # 容器启动后，发现自己没有提供服务，就会立刻停止，就是没有程序了
    

**查看日志**

    docker logs -f -t --tail 容器，没有日志
    
    # 自己编写一段shell脚本
    docker run -d centos /bin/bash -c "while true;do echo qiaowei;sleep 1;done"
    
    # 显示日志
    root@camile:~# docker logs -tf --tail 10 397eabe2c023
    2022-02-26T09:21:25.266776907Z qiaowei
    2022-02-26T09:21:26.268727710Z qiaowei
    2022-02-26T09:21:27.270738576Z qiaowei
    
    -tf # 显示日志
    --tail  # 显示日志条数
    

**查看容器中的进程信息**

    # 命令docker top 容器id
    root@camile:~# docker top 19334600a733
    UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
    root                14960               14931               0                   17:27               pts/0               00:00:00            /bin/bash
    

**查看镜像的元数据**

    # 命令
    docker inspect 容器id
    

**进入当前正在运行的容器**

    # 我们通常容器都是使用后台方式运行的，需要进入容器，修改一些配置
    
    # 命令
    docker exec -it 容器id 路径
    
    # 测试
    root@camile:~# docker exec -it 19334600a733 /bin/bash
    [root@19334600a733 /]# ls
    bin  etc   lib      lost+found  mnt  proc  run   srv  tmp  var
    dev  home  lib64  media       opt  root  sbin  sys  usr
    [root@19334600a733 /]# ps -ef
    UID         PID   PPID  C STIME TTY          TIME CMD
    root          1      0  0 09:27 pts/0    00:00:00 /bin/bash
    root         15      0  0 09:46 pts/1    00:00:00 /bin/bash
    root         31     15  0 09:46 pts/1    00:00:00 ps -ef
    
    # 方式二
    docker attach 容器id
    root@camile:~# docker attach 19334600a733          
    [root@19334600a733 /]# ls
    bin  etc   lib      lost+found  mnt  proc  run   srv  tmp  var
    dev  home  lib64  media       opt  root  sbin  sys  usr
    
    # docker exec     # 进入容器后开启一个新的终端，可以在里面操作（常用）
    # docker attach   # 进入容器正在执行的终端，不会启动新的进程
    

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

    docker cp 容器id：容器内路径 目前的主机路径
    
    # 测试
    [root@19334600a733 /]# cd /home
    [root@19334600a733 home]# ls
    [root@19334600a733 home]# touch test.java
    [root@19334600a733 home]# exit
    exit
    root@camile:~# docker ps 
    CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
    root@camile:~# docker ps -a
    CONTAINER ID   IMAGE     COMMAND                  CREATED          STATUS                        PORTS     NAMES
    19334600a733   centos    "/bin/bash"              32 minutes ago   Exited (0) 12 seconds ago               admiring_bohr
    397eabe2c023   centos    "/bin/bash -c 'while…"   39 minutes ago   Exited (137) 37 minutes ago             focused_ellis
    cd940d7abbb2   centos    "/bin/bash -C 'while…"   40 minutes ago   Exited (127) 40 minutes ago             ecstatic_galileo
    23db6f7950f8   centos    "/bin/bash -C 'while…"   40 minutes ago   Exited (127) 40 minutes ago             keen_sanderson
    8f9ce6c66f30   centos    "/bin/bash"              43 minutes ago   Exited (0) 40 minutes ago               flamboyant_moser
    root@camile:~# docker cp 19334600a733:/home/test.java /home
    root@camile:~# cd home
    -bash: cd: home: No such file or directory
    root@camile:~# cd /home
    root@camile:/home# ls
    camile  test.java
    
    # 拷贝是一个手动过程，未来我们使用 -v 卷的技术，可以实现
    

* * *

4.Docker部署简单环境
--------------

接下来，笔者将带领大家安装Nginx和Tomcat这两个常见的镜像。

### 4.1 安装Nginx

    # 1.搜索镜像 search 建议大家去docker搜索，可以看到帮助文档
    
    # 2.下载nginx镜像
    docker pull nginx
    
    # 3.运行测试
    root@camile:/# docker images
    REPOSITORY   TAG       IMAGE ID       CREATED        SIZE
    nginx        latest    605c77e624dd   8 weeks ago    141MB
    centos       latest    5d0da3dc9764   5 months ago   231MB
    
    # -d 后台运行
    # --name 给容器命名
    # -p 宿主机端口：容器内端口
    root@camile:/# docker run -d --name nginx01 -p 3344:80 nginx
    586cda0f86a49f103522efcf0300a8592639ae2da23c8317745a487d8373a555
    root@camile:/# docker ps
    CONTAINER ID   IMAGE     COMMAND                  CREATED         STATUS         PORTS                                   NAMES
    586cda0f86a4   nginx     "/docker-entrypoint.…"   4 seconds ago   Up 4 seconds   0.0.0.0:3344->80/tcp, :::3344->80/tcp   nginx01
    root@camile:/# curl localhost:3344
    <!DOCTYPE html>
    <html>
    <head>
    <title>Welcome to nginx!</title>
    <style>
    html { color-scheme: light dark; }
    body { width: 35em; margin: 0 auto;
    font-family: Tahoma, Verdana, Arial, sans-serif; }
    </style>
    </head>
    <body>
    <h1>Welcome to nginx!</h1>
    <p>If you see this page, the nginx web server is successfully installed and
    working. Further configuration is required.</p>
    
    <p>For online documentation and support please refer to
    <a href="http://nginx.org/">nginx.org</a>.<br/>
    Commercial support is available at
    <a href="http://nginx.com/">nginx.com</a>.</p>
    
    <p><em>Thank you for using nginx.</em></p>
    </body>
    </html>
    

### 4.2 部署Tomcat

    # 官方的使用
    docker run -it --rm tomcat:9.0
    
    # 我们之前的启动都是后台，停止了容器之后，容器还是可以查到，官方的方法一般是用完及删
    
    # 我们的逻辑是先下载再启动
    docker pull tomcat:9.0
    
    # 启动运行
    root@camile:/# docker run -d -p 3355:8080 --name tomcat01 tomcat
    
    # 发现问腿：1.Linux命令少了 2.没有webapps，阿里云镜像的原因，默认是最小的镜像，所有不必要的都删除吊
    # 保证最小的运行环境
    

### 4.3 部署ES+kibana

    # es 暴露的端口很多！
    # es 十分的耗内存
    # es 的数据一般需要放置在安全目录！挂载
    # --net somenetwork ? 网络配置
    
    # 启动 elasticsearch
    docker run -d --name elasticsearch --net somenetwork -p 9200:9200  -e "discovery.type=single-node" elasticsearch:7.6.2
    
    # 启动了 linux就卡住了 docker stats 查看 cpu状态
    
    # es 是十分耗内存的  1核2G
    
    # 查看 docker stats
    
    # 测试以下 es 是否成功了
    
    # 赶紧关闭，增加内存的限制 -e环境配置修改
    docker run -d --name elasticsearch --net somenetwork -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:tag
    docker run -d --name elasticsearch --net somenetwork -p 9200:9200  -e ES_JAVA_OPTS="-Xms64m -Xmx12m" elasticsearch:7.6.2
    

### 4.4 Docker可视化工具

这里我们可以使用一个portainer，它是一个Docker的图形化界面管理工具，提供一个后台面板供我们操作，登陆进入可以管理我们的容器。

    docker run -d -p 8088:9000 --restart=always -v /var/run/docker.sock:/var/run/docker.sock --privileged=true   portainer/portaioner
    

在虚拟机中可以使用：localhost:8080来访问界面

![portainer](https://storage.googleapis.com/papyrus_images/6ba3aff68875990170b5a585c60399b0d6f2db9e2815a2f2ea16d1848401e45d.png)

portainer

* * *

5.Docker镜像理解
------------

### 5.1 镜像是什么

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

如何获得镜像：

*   从远程仓库下载
    
*   朋友拷贝
    
*   自己制作一个镜像DockerFile
    

### 5.2 镜像加载原理

**UnionFS(联合文件系统)**

我们下载的时候看到的一层一层的数据就是这个。

UnionFS（联合文件系统）：Union文件系统是一种分层、轻量级并且高性能的文件系统，它支持对文件系统的修改作为一次提交来一层层地叠加，同时可以将不同目录挂载到同一个虚拟文件系统下。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承，基于基础镜像（没有父镜像），可以制作各种具体的应用镜像。

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

> Docker镜像加载原理

docker的镜像实际上由一层一层的文件系统组成，这种层级的文件系统UnionFS。

bootfs(boot file system)主要包含bootloader和kernel, bootloader主要是引导加载kernel,Linux刚启动时会加载bootfs文件系统，在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的，包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了，此时内存的使用权已由bootfs转交给内核，此时系统也会卸载bootfs。

rootfs (root file system)，在bootfs之上。包含的就是典型Linux系统中的/dev,/proc, /bin, /etc等标准目录和文件。各种不同的操作系统发行版，比如Ubuntu , Centos等等

![镜像分层](https://storage.googleapis.com/papyrus_images/dd4ad3d75ae672c6b1d6cb64b6fe57a89cc281068c7ba6bb461b3003d2621182.png)

镜像分层

> 平时我们安装虚拟机的CentOS都是好几个G，为什么Docker这里才200M？

对于一个精简的OS，rootfs可以很小，只需要包含最基本的命令、工具和程序库就可以了，因为底层直接用Host的kernel，自己只需要提供rootfs就可以了。因此可见对于不同的linux发行版，bootfs基本是一致的，rootfs会有差别，因此不同的发行版可以公用bootfs。

### 5.3 分层理解

> 分层的镜像

我们可以去下载一个镜像，注意观察下载的日志输出，可以看到是一层一层地在下载。

![下载时分层](https://storage.googleapis.com/papyrus_images/90f2433ac890df94945ecd2aaaf2dd40d0299b302fb7e7c3ec67b4bcf5c24716.png)

下载时分层

思考：为什么Docker镜像要采用这种分层结构？

最大的好处，我觉得莫过于是资源共享了!比如有多个镜像都从相同的Base镜像构建而来，那么宿主机只需在磁盘上保留一份base镜像，同时内存中也只需要加载一份base镜像，这样就可以为所有的容器服务了，而且镜像的每一层都可以被共享。

查看镜像分层的方式可以通过docker image inspect命令!

    [root@iZwz99sm8v95sckz8bd2c4Z ~]# docker image inspect nginx:latest
    [
        {
            "Id": "sha256:ae2feff98a0cc5095d97c6c283dcd33090770c76d63877caa99aefbbe4343bdd",
            "RepoTags": [
                "nginx:latest"
            ],
            "RepoDigests": [
                "nginx@sha256:4cf620a5c81390ee209398ecc18e5fb9dd0f5155cd82adcbae532fec94006fb9"
            ],
            "Parent": "",
            "Comment": "",
            "Created": "2020-12-15T20:21:00.007674532Z",
            "Container": "4cc5da85f27ca0d200407f0593422676a3bab482227daee044d797d1798c96c9",
            "ContainerConfig": {
                "Hostname": "4cc5da85f27c",
                "Domainname": "",
                "User": "",
                "AttachStdin": false,
                "AttachStdout": false,
                "AttachStderr": false,
                "ExposedPorts": {
                    "80/tcp": {}
                },
                "Tty": false,
                "OpenStdin": false,
                "StdinOnce": false,
                "Env": [
                    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                    "NGINX_VERSION=1.19.6",
                    "NJS_VERSION=0.5.0",
                    "PKG_RELEASE=1~buster"
                ],
                "Cmd": [
                    "/bin/sh",
                    "-c",
                    "#(nop) ",
                    "CMD [\"nginx\" \"-g\" \"daemon off;\"]"
                ],
                "Image": "sha256:13bffe371b56f4aeed88218ec17d0c6f653a83b49bd3e211fc8cfa2ca5d7a3d3",
                "Volumes": null,
                "WorkingDir": "",
                "Entrypoint": [
                    "/docker-entrypoint.sh"
                ],
                "OnBuild": null,
                "Labels": {
                    "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
                },
                "StopSignal": "SIGQUIT"
            },
            "DockerVersion": "19.03.12",
            "Author": "",
            "Config": {
                "Hostname": "",
                "Domainname": "",
                "User": "",
                "AttachStdin": false,
                "AttachStdout": false,
                "AttachStderr": false,
                "ExposedPorts": {
                    "80/tcp": {}
                },
                "Tty": false,
                "OpenStdin": false,
                "StdinOnce": false,
                "Env": [
                    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
                    "NGINX_VERSION=1.19.6",
                    "NJS_VERSION=0.5.0",
                    "PKG_RELEASE=1~buster"
                ],
                "Cmd": [
                    "nginx",
                    "-g",
                    "daemon off;"
                ],
                "Image": "sha256:13bffe371b56f4aeed88218ec17d0c6f653a83b49bd3e211fc8cfa2ca5d7a3d3",
                "Volumes": null,
                "WorkingDir": "",
                "Entrypoint": [
                    "/docker-entrypoint.sh"
                ],
                "OnBuild": null,
                "Labels": {
                    "maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
                },
                "StopSignal": "SIGQUIT"
            },
            "Architecture": "amd64",
            "Os": "linux",
            "Size": 132935043,
            "VirtualSize": 132935043,
            "GraphDriver": {
                "Data": {
                    "LowerDir": "/var/lib/docker/overlay2/cb791e78a08db7091bf2ce1d78603f1758f52199e57f1805156fe30e39067aae/diff:/var/lib/docker/overlay2/1e73a72b25af68ee9abf4eb443f778d31226e12e9af428fcc14c7b044c83b258/diff:/var/lib/docker/overlay2/88c9c01762f2af8327db65d0b0d4a64785e87c9c2ab76c62e7d03619db03a985/diff:/var/lib/docker/overlay2/7304ab112ac4a9cb91fc6f74730be28fecbe19f042e92d321aa9181424cc4b2e/diff",
                    "MergedDir": "/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/merged",
                    "UpperDir": "/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/diff",
                    "WorkDir": "/var/lib/docker/overlay2/48b288740bbb2b07b41ed43a4d17a005c46b08d3357d2960b5ef7db4b2de6618/work"
                },
                "Name": "overlay2"
            },
            "RootFS": {
                "Type": "layers",
                "Layers": [
                    "sha256:87c8a1d8f54f3aa4e05569e8919397b65056aa71cdf48b7f061432c98475eee9",
                    "sha256:5c4e5adc71a82a96f02632433de31c998c5a9e2fccdcbaee780ae83158fac4fa",
                    "sha256:7d2b207c26790f693ab1942bbe26af8e2b6a14248969e542416155a912fec30d",
                    "sha256:2c7498eef94aef8c40d106f3e42f7da62b3eee8fd36012bf7379becc4cd639a2",
                    "sha256:4eaf0ea085df254fd5d2beba4e2c11db70a620dfa411a8ad44149e26428caee4"
                ]
            },
            "Metadata": {
                "LastTagTime": "0001-01-01T00:00:00Z"
            }
        }
    ]
    

这里指示了分层信息:

    "RootFS": {
        "Type": "layers",
        "Layers": [
            "sha256:87c8a1d8f54f3aa4e05569e8919397b65056aa71cdf48b7f061432c98475eee9",
            "sha256:5c4e5adc71a82a96f02632433de31c998c5a9e2fccdcbaee780ae83158fac4fa",
            "sha256:7d2b207c26790f693ab1942bbe26af8e2b6a14248969e542416155a912fec30d",
            "sha256:2c7498eef94aef8c40d106f3e42f7da62b3eee8fd36012bf7379becc4cd639a2",
            "sha256:4eaf0ea085df254fd5d2beba4e2c11db70a620dfa411a8ad44149e26428caee4"
        ]
    },
    

所有的Docker镜像都起始于一个基础镜像层，当进行修改或增加新的内容时，就会在当前镜像层之上，创建新的镜像层。

举一个简单的例子，假如基于Ubuntu Linux 16.04创建一个新的镜像，这就是新镜像的第一层;如果在该镜像中添加Python包，就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁，就会创建第三个镜像层。

该镜像当前已经包含3个镜像层，如下图所示(这只是一个用于演示的很简单的例子)。

![3个镜像层](https://storage.googleapis.com/papyrus_images/c50d0eebcf7aeb20a03e9b2ab9f2cedcf289359c93f613704d2ebfac7822a8cd.png)

3个镜像层

在添加额外的镜像层的同时，镜像始终保持是当前所有镜像的组合，理解这一点非常重要。下图中举了一个简单的例子，每个层包含3个文件，而镜像包含了来自两个镜像层的6个文件。

![6个文件](https://storage.googleapis.com/papyrus_images/0731c89b86e32d117479b3bd360518e9e0aac22f88a468c454c88d28c9da8292.png)

6个文件

这种情况下，上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。Docker通过存储引擎（新版本采用快照机制)的方式来实现镜像层堆栈，并保证多镜像层对外展示为统一的文件系统。

Linux上可用的存储引擎有AUFS、Overlay2、Device Mapper、Btrfs以及ZFS。顾名思义，每种存储引擎都基于Linux中对应的文件系统或者块设备技术，并且每种存储引擎都有其独有的性能特点。

Docker在Windows 上仅支持windowsfilter一种存储引擎，该引擎基于NTFS文件系统之上实现了分层和CoW\[1\].下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并，对外提供统一的视图。

![](https://storage.googleapis.com/papyrus_images/b09d9535d9e8ca81bc449376faf2f1942335ad63c37662130f98c5530b24a765.png)

特点

Docker镜像都是只读地，当容器启动时，一个新的可写层被加载到镜像的顶部！

这一层就是我们通常说的容器层，容器之下的都是镜像层！

### 5.4 commit镜像

当我们想要保存当前容器的状态，就可以通过commit来提交，获得一个镜像：

    docker commit 提交容器成为一个新的副本
    
    docker commit -m="提交的描述信息" -a="作者" 容器id 目标镜像名：[tag]
    

实战测试

    # 启动一个默认的tomcat
    
    # 发现这个默认的 tomcat 没有webapps应用，镜像的原因，官方的镜像默认 webapps 下是没有文件的
    
    # 自己拷贝进去基本的文件
    
    # 将我们操作过的容器通过commit提交为一个镜像！我们以后就可以使用我们修改过的镜像即可，这也就是我们自己的一个修改镜像。
    

* * *

6.Docker容器数据卷
-------------

### 6.1 什么是容器数据卷

容器数据卷是Docker中一种用于持久化数据的技术，它可以让容器中的数据在容器删除后不会丢失。Docker的理念是将应用和环境打包成一个镜像，但如果数据都在容器中，那么容器删除后数据也会消失。因此，需要使用数据卷技术实现数据持久化和同步操作。

![挂载](https://storage.googleapis.com/papyrus_images/2125282a414ab5a2e72a9fbe5891fa75ba840de8a43f4b17df0265ecaf60dd76.png)

挂载

例如，在使用MySQL时，如果容器删除，则数据库中的数据也会丢失，因此需要将MySQL数据存储在本地并使用卷技术实现容器和本地数据的同步。卷技术通过目录挂载实现，将容器内的目录挂载到Linux系统上。因此，容器间也可以共享数据，实现数据共享。容器数据卷是Docker中非常重要的一部分，可以实现容器的持久化和同步操作，为容器应用提供了强大的数据管理功能。

### 6.2 使用数据卷

> 方式一：直接使用命令来挂载 -v

     docker run -it -v 主机目录:容器目录
    
     # 测试
     docker run -it -v /home/ceshi:/home centos /bin/bash
    
     # 容器起来时候我们可以通过 docker inspect 容器id 来查看挂载状态
    

![挂载状态](https://storage.googleapis.com/papyrus_images/3efcc95eef99b55b2836346578fa70673d048545cd3f97aa4bbd90d140d10142.png)

挂载状态

在容器内简历一个test.java测试文件。测试文件的同步，是一个双向绑定的过程。

![双向绑定](https://storage.googleapis.com/papyrus_images/39027bcb1890e8de57012137bb40dbba9a8da1c299f222321f165983ec5bee7d.png)

双向绑定

### 6.3 实战：安装MySQL

    # 获取镜像
    camile@camile:/$ docker pull mysql:5.7
    
    # 运行容器，需要做数据卷挂载 # 安装启动mysql -e表示配置环境
    # 官方测试：docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
    
    # 启动我们的mysql
    -d 后台运行
    -p 端口映射
    -v 卷挂载
    -n 容器名字
    camile@camile:~$ docker run -d -p 3310:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=zuiweizhiming666 --name mysql01 mysql:5.7
    
    # 启动成功之后，我们在本地使用 navicat 来测试以下
    # navicat 连接到服务器的3310 -3310和容器内的3306映射，这个时候我们就可以连接上了
    
    # 在本地测试创建一个数据库，查看一下我们创建的路径是否ok
    

![test](https://storage.googleapis.com/papyrus_images/6eae297368b70d41701d579b50cd044432dc8daaa7098e9f6440cd2c4b636d30.png)

test

![test](https://storage.googleapis.com/papyrus_images/63809284db2379c9bcd176cc5775445cb49fc7e2a81808819d005aff7b8146dd.png)

test

### 6.4 具名和匿名挂载

Docker中的匿名挂载和具名挂载是两种不同的数据卷挂载方式。

匿名挂载是指在启动容器时，只指定容器内的路径，而不指定宿主机的目录。这样会生成一个随机的宿主机挂载目录，可以通过 `docker volume ls` 命令查看。

具名挂载是指在启动容器时，既指定容器内的路径，又指定一个卷名作为宿主机的目录。 这样可以方便地找到和管理数据卷，也可以通过 `docker volume inspect 卷名` 命令查看详细信息。

匿名挂载和具名挂载的语法如下：

*   `-v 容器内路径` # 匿名挂载
    
*   `-v 卷名:容器内路径` # 具名挂载
    

下面是具名挂载和匿名挂载的相关命令代码块：

    # 启动一个nginx容器，使用匿名挂载，将容器内的/etc/nginx目录挂载到宿主机上
    docker run -d -P --name nginx01 -v /etc/nginx nginx
    
    # 查看所有的数据卷
    docker volume ls
    
    # 启动一个nginx容器，使用具名挂载，将容器内的/etc/nginx目录挂载到宿主机上，并命名为juming-nginx
    docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
    
    # 查看具体的数据卷信息
    docker volume inspect juming-nginx
    

### 6.5 DockerFile

DockerFile 就是用来构建Docker镜像的构建文件、命令脚本，下面先体验一下！

通过这个脚本可以生成镜像，镜像是一层一层的，脚本是一个个的命令。

    # 创建一个DockerFile文件，名字可以随机 ，建议 Dockerfile
    
    # 文件中的内容
    FROM centos
    
    VOLUME ["volume01","volume02"]
    
    CMD echo "---end---"
    CMD /bin/bash
    
    # 这里的每个命令，就是镜像的一层
    

    # 启动自己写的容器
    

![](https://storage.googleapis.com/papyrus_images/e87d1eade445895b9db3856c4754318263f2f3577d07caf3bb4dc0da191fb7d8.png)

这个目录就是我们生成镜像的时候自动挂载的数据卷目录。这个卷和外部一定有一个同步的目录。

![](https://storage.googleapis.com/papyrus_images/4b900d5f73fd5401a19c3103e614f5e37f929a36d6e8ea18cc6316acf060bbad.png)

查看一下卷挂载的路径。

测试一下刚才的文件是否同步了。

![](https://storage.googleapis.com/papyrus_images/6f3629e426e0d954ec278692359fb0e20c296fe29cf2fee4dc56f86dd350fb9a.png)

这种方式我们未来使用的十分多，因为我们通常会构建自己的镜像！假设构建镜像的时候没有挂载卷，要手动镜像挂载 -v 卷名:容器内路径。

* * *

7.DockerFile
------------

### 7.1 DockerFile

dockerFile 是用来docker镜像的文件，是命令参数脚本。

构建步骤：编写一个dockerfile文件

1.  docker build 构建成一个镜像
    
2.  docker run 运行镜像
    
3.  docker push 发布镜像
    

### 7.2 dockerFile构建过程

**基础知识**：

1.  每个保留关键字都是大写字母
    
2.  执行从上到下执行
    
3.  `#`表示注释
    
4.  每一个指令都会创建提交一个新的镜像层，并提交！
    

![dockerfile](https://storage.googleapis.com/papyrus_images/4b2a047a7246b78dc60b70f2339a0609aeaf01622ee38c09076fe135bc0ef764.png)

dockerfile

dockerfile是面向开发的，我们以后要发布项目，做镜像，就需要编写dockerfile文件，这个文件十分简单。

Docker镜像逐渐成为企业交付的标准，必须要掌握。

步骤：

DockerFile：构建文件，定义了一切的步骤，源代码

DockerImages：DockerFile构建生成的镜像，最终发布和运行的产品，原来是 jar war。

Docker容器：容器就是镜像运行起来提供服务的。

### 7.3 DockerFile指令

知晓了DockerFile命令，我们就可以自己制作镜像：

    FROM          # 基础镜像，一切从这里开始构建
    MANITAINER    # 镜像是谁写的 姓名+邮箱
    RUN           # 镜像构建的时候需要运行的命令
    ADD           # 步骤，tomcat镜像，这个tomcat压缩包！添加内容
    WORKDIR       # 镜像的工作目录
    VOLUME        # 挂载的目录
    EXPOSE        # 指定暴露端口
    CMD           # 指定这个容器启动的时候要运行的命令，只有最后一个会生效，可被替代
    ENTRYPOINT    # 指定这个容器启动的时候要运行的命令，可以追加命令
    ONBUILD       # 当个构建一个被继承dockerfile 的时候，会运行这个指令，触发指令
    COPY          # 类似ADD，将我们文件拷贝到镜像中
    ENV           # 构建的时候设置环境变量
    

### 7.4 实战测试

Docker Hub 中绝大多数镜像都是从这个基础镜像过来的 `FROM scratch`，然后配置需要的软件和配置来进行的构建。

> 创建一个自己的Centos

    # 1.编写DockerFile的文件
    FROM centos:7
    
    MAINTAINER qiaowei<454921269@qq.com>
    
    ENV MYPATH /usr/local
    
    WORKDIR $MYPATH
    
    RUN yum -y install vim
    RUN yum -y install net-tools
    
    EXPOSE 80
    
    CMD echo $MYPATH
    CMD echo "---end---"
    CMD /bin/bash
    
    # 2.通过这个文件构建镜像
    # 命令 docker build -f dockerfile文件路径 -t 镜像名:[tag]
    Successfully built 11a9535c2896
    Successfully tagged mycentos:0.1
    
    # 3.测试运行
    root@camile:/home/dockerfile# docker run -it 11a9535c2896
    [root@f9c5dbcee829 local]# pwd
    /usr/local
    [root@f9c5dbcee829 local]# ifconfig
    eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
            inet 172.17.0.6  netmask 255.255.0.0  broadcast 172.17.255.255
            ether 02:42:ac:11:00:06  txqueuelen 0  (Ethernet)
            RX packets 23  bytes 2804 (2.7 KiB)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 0  bytes 0 (0.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    lo: flags=73<UP,LOOPBACK,RUNNING>  mtu 65536
            inet 127.0.0.1  netmask 255.0.0.0
            loop  txqueuelen 1000  (Local Loopback)
            RX packets 0  bytes 0 (0.0 B)
            RX errors 0  dropped 0  overruns 0  frame 0
            TX packets 0  bytes 0 (0.0 B)
            TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
    
    [root@f9c5dbcee829 local]# vim test
    

自己写的centos 添加了vim 和 net-tools ，测试成功！

我们可以列出本地镜像的变更历史

    root@camile:/home/dockerfile# docker history 11a9535c2896
    IMAGE          CREATED         CREATED BY                                      SIZE      COMMENT
    11a9535c2896   4 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "/bin…   0B        
    5005cf751c81   4 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B        
    21407997234d   4 minutes ago   /bin/sh -c #(nop)  CMD ["/bin/sh" "-c" "echo…   0B        
    a567cd94200e   4 minutes ago   /bin/sh -c #(nop)  EXPOSE 80                    0B        
    48ad5b128e9a   4 minutes ago   /bin/sh -c yum -y install net-tools             161MB     
    da7d8a18d88e   4 minutes ago   /bin/sh -c yum -y install vim                   216MB     
    b1524097a173   4 minutes ago   /bin/sh -c #(nop) WORKDIR /usr/local            0B        
    01282534605d   4 minutes ago   /bin/sh -c #(nop)  ENV MYPATH=/usr/local        0B        
    9bb0de5c2898   4 minutes ago   /bin/sh -c #(nop)  MAINTAINER qiaowei<454921…   0B        
    eeb6ee3f44bd   5 months ago    /bin/sh -c #(nop)  CMD ["/bin/bash"]            0B        
    <missing>      5 months ago    /bin/sh -c #(nop)  LABEL org.label-schema.sc…   0B
    

### 7.5 tomcat镜像

1.  准备镜像文件 tomcat 压缩包，jdk的压缩包。
    

![jdk](https://storage.googleapis.com/papyrus_images/584570e9d5bb90b06b12bf49e7dd71866adef04a4dded7f19eb3138b9f91522f.png)

jdk

1.  编写dockerfile文件，官方命名“Dockerfile”，build会自动寻找这个文件，就不需要 -f 指定了。
    
        root@camile:/home/qiaowei/build/tomcat# touch readme.txt
        root@camile:/home/qiaowei/build/tomcat# vim Dockerfile
        
    
        # 自己制作Dockerfile
        FROM centos:7
        MAINTAINER qiaowei<454921269@qq.com>
        
        COPY readme.txt /usr/local/readme.txt
        
        ADD jdk-17_linux-x64_bin.tar.gz /usr/local/
        ADD apache-tomcat-9.0.59.tar.gz /usr/local/
        
        RUN yum -y install vim
        
        ENV MYPATH /usr/local
        WORKDIR $MYPATH
        
        ENV JAVA_HOME /usr/local/jdk-17.0.2
        ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
        ENV CATALINA_HOME /usr/local/apache-tomcat-9.0.59
        ENV CATALINA_BASH /usr/local/apache-tomcat-9.0.59
        ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/lib:$CATALINA_HOME/bin
        
        EXPOSE 8080
        
        CMD /usr/local/apache-tomcat-9.0.59/bin/startup.sh && tail -F /usr/local/apache-tomcat-9.0.59/bin.logs/catalina.out
        
    
2.  构建镜像
    
        # docker build -t diytomcat .
        
    

![构建镜像](https://storage.googleapis.com/papyrus_images/56ed7d104bb597fba3ae51a2145143692c849e85c8624829b41d65540a12f4bb.png)

构建镜像

1.

2.  运行容器：
    
        docker run -d -p 9090:8080 --name qiaoweitomcat -v /home/qiaowei/build/tomcat/test:/usr/local/apache-tomcat-9.0.59/webapps/test -v /home/qiaowei/build/tomcat/tomcatlogs/:/usr/local/apache-tomcat-9.0.59/logs diytomcat
        
    
3.  访问测试
    

![tomcat](https://storage.googleapis.com/papyrus_images/17f974d07deedc1d0953370ad579fc17650788b2b840e9fd2aa43b096e52fd8c.png)

tomcat

1.  发布项目（由于做了卷挂载，我们直接在本地编写项目就可以）
    
        <?xml version="1.0" encoding="UTF-8"?>
        <web-app xmlns="http://java.sun.com/xml/ns/javaee"
                   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                   xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
                  http://java.sun.com/xml/ns/javaee/web-app_4_0.xsd"
                   version="4.0">
        </web-app>
        
    
2.  随便写一个html文件，测试访问
    
        <!DOCTYPE html>
        <html>
            <head>
                 <meta charset="UTF-8"/>
                <title>这是个标题</title>
            </head>
            <body>
                <h1>这是一个一个简单的HTML</h1>
                <p>Hello World！</p>
            </body>
        </html>
        
    

![html](https://storage.googleapis.com/papyrus_images/1aedf5e691a39e6f69295cca6cd0b112d50b314bbda3164825b5f57fddf675db.png)

html

### 7.6 发布自己的镜像

> Dockerhub

1.  地址：[https://hub.docker.com/，注册自己的账号](https://hub.docker.com/%EF%BC%8C%E6%B3%A8%E5%86%8C%E8%87%AA%E5%B7%B1%E7%9A%84%E8%B4%A6%E5%8F%B7)
    
2.  确定这个账号可以登录
    
3.  在我们服务器上提交自己的镜像
    
        Usage:  docker login [OPTIONS] [SERVER]
        
        Log in to a Docker registry.
        If no server is specified, the default is defined by the daemon.
        
        Options:
          -p, --password string   Password
              --password-stdin    Take the password from stdin
          -u, --username string   Username
        
    
4.  登录完毕后可以提交镜像了
    
        # 先给镜像打一个标签
        docker tag 1ba62e6aa598 wulegekong/tomcat:1.0
        
        # 提交镜像
        docker push wulegekong/tomcat:1.0
        
        # 注意：镜像名前一定是自己dockerhub的用户名，否则会拒绝push请求
        
    

### 7.7 发布镜像到阿里云容器服务

*   登录阿里云
    
*   找到容器镜像服务
    
*   创建命名空间
    
*   根据官方提供的操作指南操作即可。
    

* * *

8.Docker网络
----------

我们使用 ip addr 命令查看本机的所有网络信息：

![ip addr](https://storage.googleapis.com/papyrus_images/1eb21eb5f47160787f9512996d88c7b3ab281d1d4fb1a2a8c64a633891d18bab.png)

ip addr

这里出现了docker0地址，下面简单理解一下docker网络的原理：

1.  我们每安装一个docker容器，docker就会给docker容器分配一个ip，只要电脑安装了docker，就会有一个网卡docker0桥接模式，使用的是evth-pair技术。
    
2.  再次测试ip addr
    
3.  再启动一个容器测试，发现又多了一对网卡
    

    # 我们发现这个容器带来网卡，都是一对一对的 
    # evth-pair 就是一对的虚拟设备接口，它们是成对出现的，一端连着协议，一段彼此相连。 
    # 正因为有这个特性，evth-pair充当一个桥梁，连接各种虚拟网络设备 
    # openstac,Docker容器之间的连接 ovs的连接都是使用evth-pair技术
    

1.  我们来测试tomcat01和tomcat02这两个之前创建的容器是否可以相互ping通，能够ping通！
    

![网络](https://storage.googleapis.com/papyrus_images/d2eb12e021de9113b6df13ede82eb37865fc13a0961a716e5327ff16ae8d25cc.png)

网络

结论：tomcat01和tomcat02是公用的一个路由器，docker0.

所有的容器不指定网络的情况下，都是docker0路由的，docker会给我们的容器分配一个默认的可用IP。

> 小结

Docker使用的是Linux的桥接，宿主机中是一个Docker容器的网桥 docker0。

![网络](https://storage.googleapis.com/papyrus_images/2aa2a7753319e27030c6d1cffb527f88764c8e2c039052ccd524ba0354ffbd5b.png)

网络

Docker中的所有的网络接口都是虚拟的，虚拟的转发效率高，只要容器删除，对应网桥一对就没了。

* * *

9.总结
----

笔者仅仅简单地学习和应用了Docker的一些基本内容，勉强算是入门，后续会学习Docker-Compose等进阶内容，敬请期待。

---

*Originally published on [samehada](https://paragraph.com/@samehada/docker)*
