跟随狂神学Java-44,Docker

第四十四:Docker

“弱小无知不是生存的障碍,傲慢才是”

【狂神说Java】Docker最新超详细版教程通俗易懂_哔哩哔哩_bilibili

蚂蚁课堂

Docker Docs

初始Docker


什么是Docker

Docker是一种开源平台和工具,用于容器化应用程序。容器是一种轻量级、可移植的封装,其中包含应用程序和其依赖的所有组件,包括代码、运行时、库、环境变量和配置文件。Docker的主要目标是使应用程序在不同的环境中具有一致的运行方式,并提供更高效的资源利用和部署过程。

Docker的优势包括简化应用程序的开发、测试和部署过程,提高了资源利用率,使开发人员能够更容易地构建和交付应用程序。它在容器化技术方面的普及使得容器化应用程序在云计算和DevOps领域得到广泛应用。


Docker为什么会出现

Docker之所以出现,是为了解决软件开发和部署过程中的环境一致性问题。在传统的软件开发过程中,开发环境和生产环境通常是不同的,这可能导致以下问题:

  1. 环境差异导致问题:由于开发环境和生产环境的差异,开发人员在开发阶段无法完全模拟生产环境,导致在上线后可能出现意外的问题。这些问题可能包括依赖项不匹配、配置不一致以及操作系统或库的差异。

  2. 依赖管理困难:在传统的环境中,管理应用程序所需的依赖项(例如库、框架、运行时环境等)通常是复杂和耗时的(集群中环境的配置)。不同的应用程序可能需要不同版本的依赖项,这可能导致冲突和管理困难。

  3. 开发与运维隔离:传统的开发和运维过程通常是分离的,开发人员编写应用程序后,需要将其移交给运维团队进行部署。这可能导致部署过程不流畅,而且难以协作和快速交付。

Docker的出现解决了这些问题。Docker允许开发人员将应用程序和其所有依赖项封装到一个称为容器的单独单元中。容器包含了应用程序的代码、依赖项、运行时环境和配置,以及操作系统的一部分。这意味着开发人员可以在其开发环境中创建一个与生产环境几乎相同的容器,确保了环境的一致性。

image-20230910155931853

Docker的思想来自于集装箱,将每套环境打包装箱,相互隔离。


Docker的历史

在2010年,一群年轻的IT专业人士在美国创立了名为dotcloud的公司,专注于提供Platform as a Service(PaaS)云计算服务。他们开始使用与LXC(Linux容器)相关的容器技术来解决他们自己的开发和部署问题。

这个公司决定将他们的容器技术命名为Docker,并试图将其推向市场。然而,在刚刚诞生的时候,Docker并没有引起行业的广泛关注,而dotcloud公司也陷入困境。

然后,在2013年,Docker决定开源他们的技术,这一举措改变了一切。人们开始注意到Docker的优点,特别是其轻巧性。在容器技术兴起之前,虚拟机技术是主流,但虚拟机相对笨重,需要在每个虚拟机中运行完整的操作系统。而Docker容器技术允许多个容器在同一个操作系统上共享内核,因此更加轻巧和高效。

比较虚拟机和Docker容器,虚拟机需要运行完整的操作系统,而Docker容器只包含应用程序和其依赖项,因此更小巧。这使得Docker在资源利用和部署速度方面具有明显的优势。

随着Docker的持续发展和改进,它于2014年发布了1.0版本,正式迈入了成熟阶段。Docker的火爆程度得益于其轻便和高效的特性,以及对开发和部署流程的极大改进,这使得它成为了现代软件开发和部署的主要工具之一。


关于Docker

Docker能做什么
  • 传统的虚拟机机制

    image-20230910161637034
    • 缺点:

      资源占用大

      冗余步骤多

      启动速度慢

  • Docker的容器化技术

    image-20230910161855971
    • 容器化技术不是模拟一个完整的操作系统,它们之间相互隔离,互不影响

  • 比较

    特点 Docker 虚拟机技术
    架构层次 容器化技术,共享操作系统内核 完整虚拟化,每个虚拟机有独立操作系统
    资源利用 高效,轻量级,共享资源 相对较高,独立虚拟机需额外资源
    启动速度 快速,通常在秒级内启动 相对较慢,通常需要数分钟
    隔离级别 适度的隔离,共享内核可能存在风险 更高级别的隔离,适用于敏感工作负载
    移植性 可移植,无需修改即可在不同环境中运行 需要适应不同虚拟化平台的配置和修改
    生态系统 庞大的生态系统,包括Docker Hub等 相对较小的生态系统

DevOps

DevOps(Development和Operations的组合词)是一种软件开发和IT运维的文化、实践和方法论,旨在通过加强开发团队和运维团队之间的协作和自动化来加速软件开发、测试和部署过程。DevOps的目标是缩短从应用程序开发到交付的周期,提高交付质量,并确保持续交付的可靠性和可维护性。

可以实现:

  • 应用更快速的交付和部署

    传统:一堆帮助文档,安装程序。Docker:打包镜像发布测试,一键运行

  • 更便捷的升级和扩缩容

    使用了Docker之后,我们部署应用就像搭积木一样,更简单的运维系统!

  • 更高效的计算资源利用

    Docker是内核级别的虚拟化,可以在一个物理机上运行很多个实例。服务器的性能可以压榨到极致


Docker中的名词概念
一、Docker基础入门及架构介绍

以下是Docker的关键概念和组件:

  1. Docker镜像(Image):Docker镜像是容器的构建模块,它包含了运行容器所需的一切,包括文件系统、库、依赖和配置。镜像是不可更改的,用户可以基于镜像创建容器实例。

  2. Docker容器(Container):容器是从Docker镜像创建的实例,它可以运行、停止、删除和管理。每个容器都是独立的,具有自己的文件系统和运行时环境。

  3. Docker仓库(Repository):Docker Hub是一个在线的镜像仓库,允许用户分享和下载Docker镜像。它包含了大量的官方和社区维护的镜像,可以用于快速构建和部署容器化应用程序。

  4. Docker Compose:Docker Compose是一个工具,用于定义和运行多个容器的应用程序,以便它们可以协同工作。它使用YAML文件定义多容器应用的配置。

  5. Docker Swarm:Docker Swarm是Docker的官方编排和集群管理工具,允许用户在多个主机上运行和管理容器集群。

  6. Kubernetes:虽然不是Docker的一部分,但Kubernetes是一种流行的容器编排平台,用于自动化容器部署、扩展和管理。它可以与Docker一起使用,以管理大规模的容器化应用程序。

安装Docker


环境准备
  1. 需要会一点Linux基础

  2. CentOS 7

  3. 使用XShell连接远程服务器进行操作


环境查看
1
2
3
# 查看系统版本
[root@Joker-CentOS7 ~]# uname -r
3.10.0-1160.76.1.el7.x86_64
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 查看系统配置
[root@Joker-CentOS7 ~]# cat /etc/os-release
NAME="CentOS Linux"
VERSION="7 (Core)"
ID="centos"
ID_LIKE="rhel fedora"
VERSION_ID="7"
PRETTY_NAME="CentOS Linux 7 (Core)"
ANSI_COLOR="0;31"
CPE_NAME="cpe:/o:centos:centos:7"
HOME_URL="https://www.centos.org/"
BUG_REPORT_URL="https://bugs.centos.org/"

CENTOS_MANTISBT_PROJECT="CentOS-7"
CENTOS_MANTISBT_PROJECT_VERSION="7"
REDHAT_SUPPORT_PRODUCT="centos"
REDHAT_SUPPORT_PRODUCT_VERSION="7"

安装
  1. 卸载

    1
    2
    3
    4
    5
    6
    7
    8
    yum remove docker \
    docker-client \
    docker-client-latest \
    docker-common \
    docker-latest \
    docker-latest-logrotate \
    docker-logrotate \
    docker-engine
  2. 安装需要的安装包

    1
    yum install -y yum-utils
  3. 设置yum安装源

    1
    2
    3
    4
    # (中央仓库)
    yum-config-manager --add-repo http://download.docker.com/linux/centos/docker-ce.repo
    # (国内建议安装阿里仓库)
    yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
  4. 安装Docker相关的源,docker-ce社区,ee企业

    1
    2
    3
    4
    # 更新索引
    yum makecache fast
    # 更新
    yum install docker-ce docker-ce-cli containerd.io
  5. 启动Docker

    1
    systemctl start docker
  6. 使用docker version查看版本

    image-20230910165905003

  7. 运行hello-world

    1
    docker run hello-world

    image-20230910170023546

  8. 查看下载的hello-world镜像

    1
    docker images

    image-20230910170204767

  9. 【了解】卸载Docker

    1
    2
    3
    4
    # 依赖
    yum remove docker-ce docker-ce-cli containerd.io
    # 资源
    rm -rf /var/lib/docker

阿里云镜像加速


  1. 登录阿里云控制台首页 (aliyun.com)

  2. 找到容器镜像服务

    image-20230910171319942

  3. 点击镜像加速器

    image-20230910171723353

  4. 配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    sudo mkdir -p /etc/docker

    sudo tee /etc/docker/daemon.json <<-'EOF'
    {
    "registry-mirrors": ["https://l0s2tlrb.mirror.aliyuncs.com"]
    }
    EOF

    sudo systemctl daemon-reload

    sudo systemctl restart docker

回顾hello-world执行流程


image-20230910172334422

底层原理


Docker是如何工作的

Docker是一个Client-Server结构的系统,Docker的守护进程运行在主机上,通过Socket从客户端访问。

DockerServer接收到Docker-Client的指令,就会执行这条命令!

image-20230911090613973
为什么Docker比虚拟机快?
  1. Docker有着比虚拟机更少的抽象层

  2. Docker用的是宿主机的内核,VM用的是GuestOS

    image-20230911180727397

    所以,新建一个容器的时候,Docker不需要像虚拟机 一样重新加载一个操作系统内核,避免了引导行为

    image-20230911183416953

Docker的常用命令


帮助命令
1
2
3
4
5
6
# Docker版本
docker verion
# Docker信息
docker info
# Docker帮助
docker [命令] --help

镜像命令
  • 查看镜像

    1
    2
    # 查看镜像
    docker images
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    [root@Joker-CentOS7 ~]# docker images
    REPOSITORY TAG IMAGE ID CREATED SIZE
    hello-world latest 9c7a54a9a43c 4 months ago 13.3kB


    # 解释
    REPOSITORY 镜像的仓库源
    TAG 镜像的标签
    IMAGE ID 镜像的ID
    CREATED 创建时间
    SIZE 镜像的大小


    # 可选项
    -a, --all # 列出所有的镜像
    -q, --quiet # 只显示镜像的ID
  • 搜索镜像

    1
    2
    # 搜索镜像
    docker search
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    [root@Joker-CentOS7 ~]# docker search mysql
    NAME DESCRIPTION STARS OFFICIAL AUTOMATED
    mysql MySQL is a widely used, open-source relation… 14440 [OK]
    mariadb MariaDB Server is a high performing open sou… 5510 [OK]
    percona Percona Server is a fork of the MySQL relati… 619 [OK]
    phpmyadmin phpMyAdmin - A web interface for MySQL and M… 860 [OK]


    # 可选项,通过收藏过滤
    --filter=STARS=3000 # 选择3000以上STAR
  • 下载镜像

    1
    2
    # 下载镜像 docker pull 镜像名[:tag] 
    docker pull mysql
    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    [root@Joker-CentOS7 ~]# docker pull mysql
    Using default tag: latest # 如果不写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 mysql
    docker pull docker.io/library/mysql:latest

    # 指定版本下载
    [root@Joker-CentOS7 ~]# 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

  • 删除镜像

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # 删除指定的容器:docker rmi -f [容器ID]
    [root@Joker-CentOS7 ~]# docker rmi -f c20987f18b13
    Untagged: mysql:5.7
    Untagged: mysql@sha256:f2ad209efe9c67104167fc609cca6973c8422939491c9345270175a300419f94
    Deleted: sha256:c20987f18b130f9d144c9828df630417e2a9523148930dc3963e9d0dab302a76
    Deleted: sha256:6567396b065ee734fb2dbb80c8923324a778426dfd01969f091f1ab2d52c7989
    Deleted: sha256:0910f12649d514b471f1583a16f672ab67e3d29d9833a15dc2df50dd5536e40f
    Deleted: sha256:6682af2fb40555c448b84711c7302d0f86fc716bbe9c7dc7dbd739ef9d757150
    Deleted: sha256:5c062c3ac20f576d24454e74781511a5f96739f289edaadf2de934d06e910b92


    # 删除多个指定的容器:docker rmi -f [容器ID] [容器ID] [容器ID]
    [root@Joker-CentOS7 ~]# docker rmi -f c20987f18b13 9c7a54a9a43c 3218b38490ce

    # 删除全部的容器:docker rmi -f $(docker images -aq)
    # 其中 $(docker images -aq),是查询所有容器id
    [root@Joker-CentOS7 ~]# docker rmi -f $(docker images -aq)

容器命令
  • 说明:我们有了镜像才可以创建容器。我们在这里下载一个CentOS镜像来进行测试

    1
    [root@Joker-CentOS7 ~]# docker rmi -f $(docker images -aq)
  • 运行镜像

    1
    docker run [参数] 镜像

    参数说明

    1
    2
    3
    4
    5
    6
    7
    8
    9
    --name="Name" 	#容器名字 tomcat01 tomcat02,用来区分容器
    -d #后台方式运行
    -t #使用交互方式运行,进入容器查看内容
    -p #指定容器的端口,小写p
    举例:
    -p ip:主机端口:容器端口
    -p 主机端口:容器端口(常用)
    -p 容器端口
    -P #随机指定端口,大写P
  • 进入容器

    1
    2
    3
    4
    # 启动并进入容器
    [root@Joker-CentOS7 ~]# docker run -it centos bin/bash
    [root@4e8db04c9471 /]# ls
    bin dev etc home lib lib64 lost+found media mnt opt proc root run sbin srv sys tmp usr var
  • 进入当前正在运行的容器

    1
    2
    # 我们通常容器都是以后台方式运行的,需要进入容器,修改一些配置
    docker exec -it [容器ID] [bashShell]
    1
    2
    3
    4
    # 除了使用exec命令,我们还可以使用attach命令进入容器。
    # 其区别是:exec会进入容器并开启一个新的bash终端,exit时不会导致容器stop。而attach进入会到当前正在执行的终端,不会启动新的进程,退出时会导致容器stop

    docker attach [容器ID]
  • 退出容器

    1
    2
    # 停止并退出容器
    [root@4e8db04c9471 /]# exit
    1
    2
    # 容器不停止,退出。快捷键
    Ctrl+P+Q
  • 查看容器

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    # 查看曾经运行过的容器 docker ps 命令
    # 列出当前正在运行的容器
    -a # 列出当前正在运行的容器+带出历史运行过的容器
    -n=? # 显示最近创建的容器
    -q # 只显示容器的编号


    [root@Joker-CentOS7 ~]# docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    4e8db04c9471 centos "bin/bash" 4 minutes ago Exited (0) 2 minutes ago naughty_elion
    39aa5f2ef46a 5d0da3dc9764 "bin/bash" 5 minutes ago Exited (0) 4 minutes ago gracious_borg
    28f0834cf88a 5d0da3dc9764 "/bin/bash" 6 minutes ago Exited (130) 5 minutes ago brave_darwin
    dc5da60f8116 9c7a54a9a43c "/hello" 46 hours ago Exited (0) 46 hours ago sleepy_hawking
  • 删除容器

    1
    2
    3
    4
    5
    6
    7
    # 删除指定的容器,不能删除正在运行的服务器。强制删除 rm -f
    docker rm [容器id]
    # 删除所有的容器
    docker rm -f $(docker ps -aq)

    # 一个一个删除
    docker ps -a -q|xargs docker rm
  • 启动或停止容器

    1
    2
    3
    4
    docker start [容器ID]		# 启动容器
    docker restart [容器ID] # 重启容器
    docker stop [容器ID] # 停止当前运行的容器
    docker kill [容器id] # 强制停止容器
  • 从容器内拷贝文件到主机上

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

其他常用命令
  • 后台启动被杀死

    以后台形式启动CentOS:

    1
    2
    [root@Joker-CentOS7 ~]# docker run -d centos
    d386bd6eca2f6371722797d1ab24d64e0eb517360c40b8a8fc24353c3b6f8f06

    会发现被强制停止了,在docker ps命令中找不到其id

    1
    2
    [root@Joker-CentOS7 ~]# docker ps
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES

    通过docker ps -a命令发现被停止了

    1
    2
    3
    [root@Joker-CentOS7 ~]# docker ps -a
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    d386bd6eca2f centos "/bin/bash" 40 seconds ago Exited (0) 40 seconds ago blissful_gates

    解释:

    • docker容器使用后台运行,就必须要有一个前台进程,docker发现没有应用,就会自动停止

    • 比如Nginx,容器启动后,发现自己没有提供服务,就会立刻停止,就是没有程序了

  • 查看日志

    1
    2
    3
    docker logs -tf --tail [日志行数] [容器id]
    -tf # 显示日志,格式化
    -- tail number # 要显示的日志条数

    如果容器没有运行,那么就会不显示日志

  • 查看容器中的进程信息

    1
    2
    # top命令
    [root@Joker-CentOS7 ~]# docker top [容器ID]
  • 查看镜像的元数据

    1
    2
    # inspect命令
    [root@Joker-CentOS7 ~]# docker inspect [容器ID]

总结
image-20230912154810469

部署容器


部署Nginx
1
2
3
4
5
6
# 搜索镜像
docker images
# 下载镜像
docker pull
# 运行镜像
docker run -d --name nginx01 -p 3344:80 nginx # 从外网访问服务器的3344端口,将会连接到Docker容器中的80端口

部署Tomcat
1
2
3
4
5
6
7
8
# 官方的使用
docker run -it --rm tomcat:9.0

# 我们之前的启动都是后台,停止了容器之后,容器还是可以查到。 docker run -it --rm 一般用来测试,用完就删

# 下载再启动
docker pull tomcat:9.0
docker run -d -p 3355:8080 --name tomcat01 tomcat
1
2
3
4
5
6
7
8
# 进入容器
[root@Joker-CentOS7 ~]# docker run -d -p 3355:8080 --name tomcat01 tomcat:9.0
b1fdff03c3d08453e5e6da3bc23b50b1ac9fea0bb131d0694047af76e7d0586c
[root@Joker-CentOS7 ~]# docker exec -it tomcat01 /bin/bash


# 发现问题:1.Linux命令少了。2.没有webapps。
# 这是由于镜像源的原因,默认是最小的镜像,把不必要的全都剔除,只保证最小可运行的环境

部署es+kibana
  • ElasticSearch

    1
    2
    3
    4
    5
    6
    7
    8
    # ES暴露的端口非常多
    # ES非常耗费内存
    # ES的数据一般需要放置在安全目录中!挂载
    # --net somenetwork ? 网络配置


    # 启动ES
    $ docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" elasticsearch:7.17.13
    1
    2
    # 查看docker状态
    docker status
    1
    2
    # 修改环境 -e
    $ docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms64m -Xmx512m" elasticsearch:7.17.13
Kibana连接ES

可视化


  • portainer(先用这个)

    1
    2
    # 启动镜像
    docker run -d -p 9000:9000 -v /var/run/docker.sock:/var/run/docker.sock -v /dockerData/portainer:/data --restart=always --name portainer portainer/portainer-ce:latest
  • Rancher(CI/CD再用)


什么是portainer

这是一个Docker的图形化管理工具,提供一个后台面板供我们操作。可视化面板平时几乎不会使用

Docker镜像


镜像是什么

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

所有的应用,直接打包docker镜像,就可以直接跑起来

如何得到一个镜像:

  1. 从远程仓库下载

  2. 从别人那里拷贝

  3. 自己制作一个 DockerFile


Dockers镜像的加载原理
  • UnionFS 联合文件系统

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

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

    我们下载的时候看到的一层层的就是这个!

  • Docker镜像加载原理

    Docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统是使用UnionFS实现的。

    • bootfs(boot filesystem):bootfs是Docker镜像中的最底层,它主要包含bootloader和kernel。bootloader的主要作用是引导加载kernel。当Linux系统启动时,它会加载bootfs文件系统。在这个阶段,bootloader负责引导加载kernel。一旦kernel加载完成,整个内核就会存在于内存中。此时,内存的控制权已由bootfs转交给内核,并且系统会卸载bootfs,不再需要它的内容。

    • rootfs(root filesystem):rootfs位于bootfs之上,它包含了典型Linux系统中的标准目录和文件,如/dev、/proc、/bin、/etc等。rootfs是各种不同操作系统发行版的基础,例如Ubuntu、Centos等。当Docker容器启动时,它将加载rootfs作为容器的文件系统,从而构建容器的运行环境。

    image-20230912194430878

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

    image-20230912194537399

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


分层理解

我们之前下载一个镜像,可以观察到,是一层一层的下载!

image-20230912195119857
1
2
3
4
5
6
"RootFS": {
"Type": "layers",
"Layers": [
"sha256:74ddd0ec08fa43d09f32636ba91a0a3053b02cb4627c35051aff89f853606b59"
]
},

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

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

image-20230912200020482

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

image-20230912195949263

上图中的镜像层跟之前图中的略有不同,主要目的是便于展示文件。

下图中展示了一个稍微复杂的三层镜像,在外部看起来只有6个文件,这是因为最上层中的文件7是文件5的一个更新版本

image-20230912200240253
特点

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

这一层就是我们通常说的容器层,容器之下的都叫镜像层。

image-20230912200626816
commit镜像
1
2
3
docker commit 提交容器成为一个新的副本
# 命令和git原理类似
docker commit -m="提交的信息" -a="作者" [容器ID] [目标镜像名]:[tag]

Docker容器数据卷


什么是容器数据卷

Docker的核心思想是将应用和其运行环境打包成一个镜像。然而,存在一个重要问题:当容器被删除时,其中的数据也会丢失。这引出了一个关键需求,即数据的持久性。特别是对于像MySQL这样的数据库容器,容器的删除会导致数据库丢失,这是不可接受的。因此,我们需要一种技术来实现数据的持久性,即将数据存储在本地环境中。

为满足这一需求,Docker引入了卷技术。卷技术允许容器内部产生的数据与本地环境进行同步。它通过目录的挂载实现,将容器内部的目录挂载到宿主机的Linux文件系统上。这意味着容器内部的数据可以在容器删除后保留在本地环境中,从而实现了数据的持久性和共享。这个技术在处理持久化数据和数据共享方面发挥了关键作用,使得Docker在应用开发和部署中更加灵活和可靠。

总结一句话,容器数据卷是为了方便容器的持久化和同步操作而出现的!


使用容器卷
  • 方式一:使用命令来挂载

    1
    2
    3
    4
    docker run -it -v [主机目录]:[容器内目录]

    # 将本机中/home/test 挂载到 Docker中centos的/home/test
    docker run -it -v /home/test:/home centos /bin/bash

    与硬链接很相似,但是也有区别:

    1. 数据存储方式

      • Docker容器卷技术:Docker容器卷是一种机制,允许容器内部的数据与宿主机或其他容器之间共享或持久化。这是通过将宿主机文件系统上的目录或文件挂载到容器内部来实现的。这意味着容器内的数据可以与宿主机的文件系统交互,可以在容器之间共享数据,也可以在容器删除后保留数据。
      • 硬链接:硬链接是文件系统级别的链接,允许在文件系统中创建多个指向相同数据块的引用。这些引用之间的关系是平等的,如果删除一个硬链接,不会影响其他硬链接。硬链接通常用于在文件系统中创建多个文件名引用相同的数据块。
    2. 用途

      • Docker容器卷技术:主要用于Docker容器内的数据管理,如持久化存储、数据共享以及容器之间的数据交换。
      • 硬链接:主要用于文件系统中的文件管理,以便在不同位置使用相同的文件数据,节省存储空间。
    3. 适用范围

      • Docker容器卷技术:适用于容器化应用的数据管理,特别是在容器的生命周期内管理数据。
      • 硬链接:适用于本地文件系统上的文件管理,通常用于减少存储占用或创建多个文件名引用相同数据的情况。

    虽然Docker容器卷技术和硬链接都涉及到文件系统中的数据引用,但它们是两个不同的概念,用于不同的目的和场景。 Docker容器卷技术更加高级和复杂,涉及容器生命周期和跨容器通信,而硬链接更侧重于文件系统级别的数据共享和节省存储空间。

  • 具名挂载和匿名挂载

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 匿名挂载
    -v 容器内路径!
    docker run -d -P --name nginx01 -v /ect/nginx nginx

    # 查看所有的 volume 的情况
    [root@kuangshen home]# docker volume ls
    DRIVER VOLUME NAME
    local 9f38292179faa178afcce54d80be99d4ddd68c91d2a68870bccee72d2b7ed061
    # 这里发现,这种就是匿名挂载,我们在 -v 只写了容器内的路径,没有写容器外的路径!

    # 具名挂载
    [root@kuangshen home]# docker run -d -P --name nginx02 -v juming-nginx:/etc/nginx nginx
    [root@kuangshen home]# docker volume ls
    DRIVER VOLUME NAME
    local 95b809564484c8ac87d65c69643e7e67447f1c7ff9a91b93edec7003692e3a9

    # 通过 -v 卷名:容器内路径
    # 查看一下这个卷
    [root@kuangshen home]# docker volume ls
    DRIVER VOLUME NAME
    local juming-nginx

    如何区别是具名挂载还是匿名挂载,还是指定路径挂载?

    • -v 容器内路径是匿名挂载

    • -v 卷名:容器内路径具名挂载

    • -v /宿主机路径::容器内路径指定路径挂载

  • 方式二:DockerFile

    Dockerfile是用来构建docker镜像的构建文件,比如一个Jar包的dockerfile如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # 拉取Zulu JDK 8作为基础镜像
    FROM azul/zulu-openjdk:8

    # 作者
    MAINTAINER sams-jar

    # 设置Java启动参数
    ENV JAVA_OPTS="-Xms256m -Xmx1024m -XX:MetaspaceSize=128m -XX:MaxMetaspaceSize=512m"

    # 修复时差
    RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime

    # 添加jar到镜像并命名为sams.jar
    ADD sams-admin.jar sams.jar

    # 镜像启动后暴露的端口
    EXPOSE 8080

    # jar运行命令,参数使用逗号隔开
    ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -jar sams.jar"]
    1
    2
    docker build -t sams-jar .	# 打包成一个docker镜像
    docker run -d -p 8080:8080 --add-host host.docker.internal:host-gateway sams-jar # 运行

    一个vite项目的dockerfile如下,同时需要配合nginx:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    # 使用 nginx 作为基础镜像
    FROM nginx

    # 作者信息
    MAINTAINER sams-ui

    # 移除默认的 Nginx 配置文件
    RUN rm /etc/nginx/conf.d/default.conf

    # 添加自定义的 Nginx 配置文件
    ADD default.conf /etc/nginx/conf.d/

    # 复制前端构建文件到 Nginx 的 html 目录
    COPY dist/ /usr/share/nginx/html/
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    server {
    listen 80;
    server_name host.docker.internal;

    location / {
    root /usr/share/nginx/html;
    index index.html index.htm;
    try_files $uri $uri/ /index.html =404;
    }

    location /prod-api/ {
    proxy_pass http://host.docker.internal:8080/;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    }

    error_page 500 502 503 504 /50x.html;
    location = /50x.html {
    root html;
    }
    }
    1
    2
    docker build -t sams-ui .	# 打包成docker镜像
    docker run -d -p 8080:80 --add-host host.docker.internal:host-gateway sams-ui # 运行

Docker网络


深入理解Docker0

宿主机使用ip addr可以查看到如下的地址:

image-20240715161521790

上述地址中docker0是docker生成的一个虚拟网卡,用来处理与容器的交互。

我们创建一个容器并尝试在容器内部运行ip addr,可以发现容器内部除了一个默认网卡,还生成了一个eth网卡,这是docker为其生成的。同时,宿主机此时查看会多一个对应的veth网卡。

我们可以在宿主机中直接ping此网卡的地址,是可以联通的。这意味着宿主机可以直接访问到容器内部。

这是桥接模式,使用的技术是evth-pair。每增加一个容器,宿主机增加有一个evth,容器内部增加一个eth,都是成对出现的。

容器和容器之间是可以相互连通的。

image-20240715163046301
自定义网络

你可以使用docker network ls查看所有的docker网络

image-20240715163404206

说明如下:

网络模式 描述
bridge 默认模式,容器通过Docker桥接网络与宿主机和其他容器通信。
none 不连接任何网络,完全隔离,无法与其他容器或外部网络通信。
host 容器共享宿主机的网络栈,直接使用宿主机的IP和端口。
container 允许容器共享另一个容器的网络栈,使用相同的IP地址。

我们启动容器的时候会自动拼接--net bridge

1
docker run -d -p --name mytomcat --net bridge tomcat

我们可以自定义一个网络

1
2
3
4
5
6
docker network create --driver bridge --subnet 192.168.0.0/16 --gateway 192.168.0.1 mynet
# docker network create:这是创建新网络的命令。
# --driver bridge:指定使用bridge驱动程序,这意味着创建一个桥接网络,允许容器之间以及与宿主机的通信。
# --subnet 192.168.0.0/16:定义网络的子网范围,这里是192.168.0.0,并且允许最多65536个IP地址。
# --gateway 192.168.0.1:设置网络的网关地址,容器将通过此地址与外部网络通信。
# mynet:这是所创建网络的名称。

于是你会看到:

image-20240715163929290 image-20240715164110365

然后我们尝试在自定义的网络上创建两个容器,现在你应该可以在docker network inspect上看到。

image-20240715164215321 image-20240715164321906
网络联通

我们刚才创建的网络如下,它们现在是两个独立的个体,如何让Docker0中的容器访问到mynet中的?

image-20240715164512192

我们可以使用connect命令:

image-20240715164716992 image-20240715164804909

使用如下:

1
docker network connect mynet tomcat01 

结果就是将tomcat01放到了mynet网络下(一个容器两个IP)

image-20240715165000251