1 Star 1 Fork 0

huai / Docker

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
该仓库未声明开源许可证文件(LICENSE),使用请关注具体项目描述及其代码上游依赖。
克隆/下载
贡献代码
同步代码
取消
提示: 由于 Git 不支持空文件夾,创建文件夹后会生成空的 .keep 文件
Loading...
README

Docker快速入门

一、基础篇

1. Docker简介

1.1 为啥要用Docker?

为啥要用Docker?这要从目前软件行业的痛点来讲起

1、软件更新发布及部署低效,过程繁琐且需要人工介入 2、环境一致性难以保证 3、不同环境之间迁移成本太高

有了Docker可以很大程度解决上面的问题。

  • 首先,Docker的使用简单至极,从开发的角度来看就是三步走:构建,运输,运行。其中关键步骤就是构建环节,即打包镜像文件。但是从测试和运维的角度来看,那就只有两步:复制,运行。有了这个镜像,那么想复制到哪运行都可以,完全和平台无关了。同时Docker这种容器技术隔离出了独立的运行空间,不会和其他应用争用系统资源了以及还不需要考虑应用之间相互影响,想想就开心。
  • 其次,因为在构建镜像的时候就处理完了服务程序对于系统的所有依赖,所以在你使用的时候,你可以忽略掉原本程序的依赖以及开发语言。对测试和运维而言,更多专注于自己的业务内容上。
  • 最后,Docker于开发者而言提供了一种开发环境的管理办法,与测试人员而言保证了环境的同步,于运维人员提供了可移植的标准化部署流程。

1.2 Docker 能干啥?

  • 构建容易分发简单
  • 隔离应用解除依赖
  • 快速部署测完就销

1.3 Docker的应用场景在哪?

1. 本地依赖(Local Dependency)

你需要在本地系统快速尝试 Magento,或者为一个项目使用 MySQL?还是希望尝试大部分开源项目?那就使用 Docker 吧,它将帮你节省大量时间。Docker 能提升开发者的开发效率,让我们快速搭建开发环境。 开发环境的机器通常内存比较小,此前使用虚拟的时候,经常需要为开发环境的机器加内存,而通过 Docker 可以轻易的让几十个服务在 Docker 中跑起来。

2. 搭建环境(Build Environment)

如果你希望构建源码,但发现没有准备好合适的环境。 那么使用 Docker是一个值得考虑的方案。毕竟如果使用传统的方法一个一个地安装软件,一大堆软件安装下来确实十分费时间,使用容器技术省时省力,何乐而不为?它能让你将运行环境和配置放在代码中然后部署,同一个 Docker 的配置可以在不同的环境中使用,这样就降低了硬件要求和应用环境之间耦合度。这里有一个值得一看的例子: docker golang builder。

3. 微服务(Microservices)

你在使用微服务吗?微服务架构将一个整体式的应用拆分成松耦合的单个服务。 那不妨考虑一下 Docker,你可以将每个服务打包为一个docker镜像并使用docker-compose 来模拟生产环境(checkout docker networks)。最开始实践的时候可能会比较费时费力,但长远地来看,最终将产生巨大的生产力。

4. 自动测试(Automated testing)

试想这样一个问题,如何编写自动化的集成测试用例,这些测试用例无需花很长时间来开始运行,使用者也可轻松管理。这里不是指在 Docker 中运行测试用例,而是将测试用例与镜像紧密运行在一起。当你针对一个 docker 镜像编写测试用例时会有一个很大的优势。下面简单介绍一下我的测试流程:运行两个 docker 镜像(app + db),在 MySQL 启动时加载数据,并在 app docker 上使用 API。可查看此脚本以获取快速的示例。

5. 部署过程(Deployment process)

你可以使用 docker 镜像进行自我部署。许多主流的主机提供商都支持托管 docker,如果你拥有一个具有 shell 访问权限的专用节点/vm,那么事情将变得更容易。只需要设置好docker,并在你想要的端口上运行你的镜像即可。

6. 持续部署(Continuous Deployment)

都说 Docker 天生适合持续集成/持续部署,在部署中使用Docker,持续部署将变得非常简单,并会在进入新的镜像后重新开始。关于这个部分的自动化工作,现在已经有许多方案以供选择,Kubernetes就是一个耳熟能详的名字。Kubernetes是容器集群管理系统,是一个开源的平台,可以实现容器集群的自动化部署、自动扩缩容、维护等功能。

7. 多租户环境(Multi-tenancy)

Docker 有意思的一个使用场景是在多租户的应用中,它可以避免关键应用的重写。如果你将应用程序服务公开给多个租户(租户指一组用户,例如组织),使用单租户方案设计的应用程序如果用上了 sub-domain + docker 可以快速获得提供多租户的服务。 关于这个场景的一个例子是为物联网的应用开发一个快速、易用的多租户环境。这种多租户的基本代码非常复杂,很难处理,重新规划这样一个应用不但消耗时间,也浪费金钱。使用Docker,可以为每一个租户的应用层的多个实例创建隔离的环境,这不仅简单而且成本低廉,当然这一切得益于 Docker 环境的启动速度和其高效的 diff 命令。

8. 来自一台机器的多个 APP(Multiple apps from one machine)

这与上面提到的微服务有些联系,但即使你没有使用微服务,只是提供服务,Docker仍可以很好地管理单个机器上的所有服务。你应该使用文件夹挂载来为每个基于数据的 docker 镜像保留数据。

9. 扩容 QPS(Scaling QPS)

Docker 通过创建另一个容器来帮助你轻松地进行水平扩展。如果遇到巨大的高峰流量,Docker可以帮助你解决问题 —— 只需添加更多的机器并增加负载均衡器背后运行的容器数量。

1.3 跟普通虚拟机的对比

特性 普通虚拟机 Docker
跨平台 通常只能在桌面级系统运行,例如 Windows/Mac,无法在不带图形界面的服务器上运行 支持的系统非常多,各类 windows 和 Linux 都支持
性能 性能损耗大,内存占用高,因为是把整个完整系统都虚拟出来了 性能好,只虚拟软件所需运行环境,最大化减少没用的配置
自动化 需要手动安装所有东西 一个命令就可以自动部署好所需环境
稳定性 稳定性不高,不同系统差异大 稳定性好,不同系统都一样部署方式

1.4 打包、分发、部署

打包:就是把你软件运行所需的依赖、第三方库、软件打包到一起,变成一个安装包 分发:你可以把你打包好的“安装包”上传到一个镜像仓库,其他人可以非常方便的获取和安装 部署:拿着“安装包”就可以一个命令运行起来你的应用,自动模拟出一摸一样的运行环境,不管是在 Windows/Mac/Linux。

1.5 Docker 部署的优势

常规应用开发部署方式:自己在 Windows 上开发、测试 --> 到 Linux 服务器配置运行环境部署。

问题:我机器上跑都没问题,怎么到服务器就各种问题了

用 Docker 开发部署流程:自己在 Windows 上开发、测试 --> 打包为 Docker 镜像(可以理解为软件安装包) --> 各种服务器上只需要一个命令部署好

优点:确保了不同机器上跑都是一致的运行环境,不会出现我机器上跑正常,你机器跑就有问题的情况。

1.6 Docker 通常用来做什么

  • 应用分发、部署,方便传播给他人安装。特别是开源软件和提供私有部署的应用
  • 快速安装测试/学习软件,用完就丢(类似小程序),不把时间浪费在安装软件上。例如 Redis / MongoDB / ElasticSearch / ELK
  • 多个版本软件共存,不污染系统,例如 Python2、Python3,Redis4.0,Redis5.0
  • Windows 上体验/学习各种 Linux 系统

1.7 重要概念:镜像、容器、仓库

  • 镜像一个特殊的文件系统

操作系统分为内核和用户空间。对于 Linux 而言,内核启动后,会挂载 root 文件系统为其提供用户空间支持。而 Docker 镜像(Image),就相当于是一个 root 文件系统。

Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数(如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。

Docker 设计时,就充分利用 Union FS 的技术,将其设计为分层存储的架构。 镜像实际是由多层文件系统联合组成。镜像构建时,会一层层构建,前一层是后一层的基础。每一层构建完就不会再发生改变,后一层上的任何改变只发生在自己这一层。

比如,删除前一层文件的操作,实际不是真的删除前一层的文件,而是仅在当前层标记为该文件已删除。在最终容器运行的时候,虽然不会看到这个文件,但是实际上该文件会一直跟随镜像。因此,在构建镜像的时候,需要额外小心,每一层尽量只包含该层需要添加的东西,任何额外的东西应该在该层构建结束前清理掉。分层存储的特征还使得镜像的复用、定制变的更为容易。甚至可以用之前构建好的镜像作为基础层,然后进一步添加新的层,以定制自己所需的内容,构建新的镜像。

  • 容器:镜像运行时的实体

镜像(Image)和容器(Container)的关系,就像是面向对象程序设计中的类和实例一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等 。

容器的实质是进程,但与直接在宿主执行的进程不同,容器进程运行于属于自己的独立的命名空间。前面讲过镜像使用的是分层存储,容器也是如此。容器存储层的生存周期和容器一样,容器消亡时,容器存储层也随之消亡。因此,任何保存于容器存储层的信息都会随容器删除而丢失。按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据 ,容器存储层要保持无状态化。所有的文件写入操作,都应该使用数据卷(Volume)、或者绑定宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主(或网络存储)发生读写,其性能和稳定性更高。数据卷的生存周期独立于容器,容器消亡,数据卷不会消亡。因此, 使用数据卷后,容器可以随意删除、重新run,数据却不会丢失。

注意:

容器在整个应用程序生命周期工作流中提供以下优点:隔离性、可移植性、灵活性、可伸缩性和可控性。 最重要的优点是可在开发和运营之间提供隔离。

  • 仓库:集中存放镜像文件的地方

镜像构建完成后,可以很容易的在当前宿主上运行,但是, 如果需要在其他服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。

一个 Docker Registry 中可以包含多个仓库(Repository);每个仓库可以包含多个标签(Tag);每个标签对应一个镜像。所以说,镜像仓库是 Docker 用来集中存放镜像文件的地方,类似于我们之前常用的代码仓库。通常,一个仓库会包含同一个软件不同版本的镜像,而标签就常用于对应该软件的各个版本 。

我们可以通过<仓库名>:<标签>的格式来指定具体是这个软件哪个版本的镜像。如果不给出标签,将以 latest 作为默认标签。

这里补充一下 Docker Registry 公开服务和私有 Docker Registry 的概念:Docker Registry 公开服务是开放给用户使用、允许用户管理镜像的 Registry 服务。一般这类公开服务允许用户免费上传、下载公开的镜像,并可能提供收费服务供用户管理私有镜像。最常使用的 Registry 公开服务是官方的 Docker Hub ,这也是默认的 Registry,并拥有大量的高质量的官方镜像,网址为:hub.docker.com 。

在国内访问 Docker Hub 可能会比较慢,国内也有一些云服务商提供类似于 Docker Hub 的公开服务。

除了使用公开服务外,用户还可以在本地搭建私有 Docker Registry 。Docker 官方提供了 Docker Registry 镜像,可以直接使用做为私有 Registry 服务。开源的 Docker Registry 镜像只提供了 Docker Registry API 的服务端实现,足以支持 Docker 命令,不影响使用。但不包含图形界面,以及镜像维护、用户管理、访问控制等高级功能。

  • 总结

需要正确的理解仓库/镜像/容器这几个概念:

Docker 本身是一个容器运行载体或称之为管理引擎。我们把应用程序和配置依赖打包好形成一个可交付的运行环境,这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。

image文件可以看作是容器的模板。Docker 根据image 文件生成容器的实例。同一个 image 文件,可以生成多个同时运行的容器实例。

镜像文件

image 文件生成的容器实例,本身也是一个文件,称为镜像文件。

容器实例

一个容器运行一种服务,当我们需要的时候,就可以通过docker客户端创建一个对应的运行实例,也就是我们的容器

仓库

就是放一堆镜像的地方,我们可以把镜像发布到仓库中,需要的时候再从仓库中拉下来就可以了。

2. Docker安装

2.1 下载地址

桌面版:https://www.docker.com/products/docker-desktop 服务器版:https://docs.docker.com/engine/install/#server

2.2 前提条件

目前,CentOS仅发行版本中的内核支持Docker。Docker运行在CentOS 7 (64-bit)上,要求系统为64位、Linux系统内核版本为3.8以上,这里选用Centos7.x。在Linux上运行uname命令查看系统内核版本。

[root@yaochuang ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
[root@yaochuang ~]# uname -r
3.10.0-1160.el7.x86_64

2.3 Docker版本

Docker如今划分成了2个版本:

  • Docker CE(社区版)

社区版并非阉割版,而是改了个名称;非常适合开发人员和运维团队构建容器APP。

  • Docker EE(企业版)

企业版则提供了一些收费的高级特性。可在经过认证的操作系统和云提供商中使用。

一般都会选择Docker CE(社区版),因为CE版本是开源免费的。对于大多数企业公司都比较节约成本。

2.4 Docker CE 安装方式

  1. 设置Docker的存储库并从中进行安装,以便安装和升级任务。这是推荐的方法。
  2. 有些用户下载RPM软件包并手动安装,并完全手动管理升级。这对于在无法访问互联网的系统上安装Docker等情况很有用。
  3. 在测试和开发环境中,一些用户选择使用自动便利脚本来安装Docker。

这里我们就选择第一种方式进行安装,这也是推荐的做法。

至于其他的安装方式,可自行网上学习,这里不做过多讲解。

2.5 安装

A. CentOS7安装Docker官方文档

https://docs.docker.com/engine/install/centos/

B. 确定Linux版本必须是CentOS7及以上

[root@localhost ~]# cat /etc/redhat-release
CentOS Linux release 7.9.2009 (Core)
[root@localhost ~]# uname -r
3.10.0-1160.el7.x86_64

C. 卸载旧版本

旧版本的 Docker 被称为dockerdocker-engine. 如果安装了这些,请卸载它们以及相关的依赖项。

# 第一次安装不需要执行此命令
·

D. 设置存储库

安装所需的包。 yum-utils提供yum-config-manager实用程序,devicemapper存储驱动程序需要device-mapper-persistent-data和lvm2。

yum install -y yum-utils device-mapper-persistent-data lvm2

E. 指定Docker镜像源

默认下载Docker回去国外服务器下载,速度较慢,我们可以设置为阿里云镜像源,速度更快

yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo

F. 安装最新版的Docker CE

yum install docker-ce

稍作等待,当命令行最终输出“Complete!”或者“完毕!”即表示安装完成。

G. 启动Docker

systemctl start docker

H. 验证Docker是否安装成功 最简单的验证方式,就是使用 docker version 命令,类似于JDK的 java -version 一样的道理。 Docker安装成功的话,就可以直接在命令行运行Docker的命令的。

docker version

如果出现如下信息,说明Docker以及安装成功了!

[root@yaochuang ~]# docker version
Client: Docker Engine - Community
 Version:           20.10.18
 API version:       1.41
 Go version:        go1.18.6
 Git commit:        b40c2f6
 Built:             Thu Sep  8 23:14:08 2022
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server: Docker Engine - Community
 Engine:
  Version:          20.10.18
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.18.6
  Git commit:       e42327a
  Built:            Thu Sep  8 23:12:21 2022
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          1.6.8
  GitCommit:        9cd3357b7fd7218e4aec3eae239db1f68a5a6ec6
 runc:
  Version:          1.1.4
  GitCommit:        v1.1.4-0-g5fd4c4d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0

【可选】:当然,你也可以通过运行 hello-world 映像来检验Docker是否安装成功:

docker run hello-world

上面的命令含义:下载官方提供的用于测试使用的hello-world镜像并将其运行到容器中,来检验Docker服务是否正常安装并运行。

执行上面的命令之后,Docker会自动下载hello-world镜像并自动运行到容器中,当命令行中出现“Hello from Docker!”的字样,说明已经成功运行了hello-world镜像,一切就OK了!

2.6 Docker默认挂载目录

Docker CE安装成功之后,你可以发现 /var/lib 目录下有一个 docker 目录,你可以进入Docker目录查看Docker的一些结构,如下图所示:

其中有一个containers 目录,这个目录就是存放Docker容器的。上面我们有提及到 hello-world 镜像,如果你运行了 hello-world 镜像,那么这个镜像所生成的容器信息,就会存储在 containers 目录中。

2.7 启动Docker

1、通过在命令行中执行以下命令,即可实现Docker服务的启动。当服务器重启的时候,Docker服务需要手工去启动。

systemctl start docker

2、自动启动

systemctl enable docker

2.8 卸载Docker

systemctl stop docker
yum remove docker-ce
rm -rf /var/lib/docker
rm -rf /var/lib/containerd

2.9 阿里云镜像加速器

登录阿里云:https://promotion.aliyun.com/ntms/act/kubernetes.html

针对Docker客户端版本大于 1.10.0 的用户

您可以通过修改daemon配置文件/etc/docker/daemon.json来使用加速器。

注意将https://rxxxxxx0.mirror.aliyuncs.com修改为自己的地址。

# 分步骤执行
sudo mkdir -p /etc/docker
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": ["https://rxxxxxx0.mirror.aliyuncs.com"]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

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 镜像命令

  1. 列出本地主机上的镜像:docker [OPTIONS] images

OPTIONS说明:

  • -a :列出本地所有的镜像(含历史映像层)
  • -q :只显示镜像ID。
[root@yaochuang ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
hello-world   latest    feb5d9fea6a5   12 months ago   13.3kB

选项说明:

  • REPOSITORY:表示镜像的仓库源

  • TAG:镜像的标签版本号

  • IMAGE ID:镜像ID

  • CREATED:镜像创建时间

  • SIZE:镜像大小

  1. docker search [OPTIONS] 镜像名字
docker search redis

OPTIONS说明:

  • --limit : 只列出N个镜像,默认25个
  • docker search --limit 5 redis
  • NAME:镜像名称
  • DESCRIPTION:镜像说明
  • STARS:点赞数量
  • OFFICIAL:是否是官方的
  • AUTOMATED:是否是自动构建的
  1. docker pull 下载镜像
[root@yaochuang ~]# docker pull ubuntu
Using default tag: latest
latest: Pulling from library/ubuntu
2b55860d4c66: Pull complete
Digest: sha256:20fa2d7bb4de7723f542be5923b06c4d704370f0390e4ae9e1c833544c1
Status: Downloaded newer image for ubuntu:latest
docker.io/library/ubuntu:latest
[root@yaochuang ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
ubuntu        latest    2dc39ba059dc   3 weeks ago     77.8MB
hello-world   latest    feb5d9fea6a5   12 months ago   13.3kB
  1. docker system df 查看镜像/容器/数据卷所占的空间
[root@yaochuang ~]# docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          2         1         77.85MB   77.83MB (99%)
Containers      2         0         0B        0B
Local Volumes   0         0         0B        0B
Build Cache     0         0         0B        0B
  1. docker rmi 镜像ID
# 删除镜像
docker rmi -f 镜像ID
# 删除多个
docker rmi -f 镜像名1:TAG 镜像名2:TAG
# 删除全部
docker rmi -f $(docker images -qa)
  1. 面试题:谈谈docker虚悬镜像是什么?

构建和删除镜像时出现错误,导致仓库名(REPOSITORY)、标签(TAG)都是的镜像,俗称虚悬镜像dangling image。

# 删除虚悬镜像
docker image prune

3.3 容器命令

本次演示用ubuntu演示

有镜像才能创建容器, 这是根本前提(下载一个CentOS或者ubuntu镜像演示)

docker pull centos
docker pull ubuntu
3.3.1 新建 + 启动容器

运行容器需要指定具体镜像,如果镜像不存在,会直接下载

# 简单操作
docker run 镜像的标识|镜像名称[:tag]
	
# 常用的参数
docker run -d -p 宿主机端口:容器端口 --name 容器名称 镜像的标识|镜像名称[:tag]

docker run -d -p 8081:8080 --name tomcat b8

OPTIONS说明:

  • --name="容器新名字"为容器指定一个名称;

  • -d: 后台运行容器并返回容器ID,也即启动守护式容器(后台运行);

  • -i:以交互模式运行容器,通常与 -t 同时使用;

  • -t:为容器重新分配一个伪输入终端,通常与 -i 同时使用;

    也即启动交互式容器(前台有伪终端,等待交互);

  • -P: 随机端口映射,大写P

  • -p: 指定端口映射,小写p

3.3.2 启动交互式容器
# 使用镜像centos:latest以交互模式启动一个容器,在容器内执行/bin/bash命令。
docker run -it centos /bin/bash

参数说明:

  • -i: 交互式操作。
  • -t: 终端。

centos : centos 镜像。

/bin/bash:放在镜像名后的是命令,这里我们希望有个交互式 Shell,因此用的是 /bin/bash。

要退出终端,直接输入exit

注意:有镜像才能创建容器,这是根本前提(下载一个Redis6.0.8镜像演示)

在大部分的场景下,我们希望 docker 的服务是在后台运行的,我们可以过 -d 指定容器的后台运行模式。

docker run -d 容器名

#使用镜像centos:latest以后台模式启动一个容器
docker run -d centos

问题:然后docker ps -a 进行查看, 会发现容器已经退出。 注意:Docker容器后台运行,就必须有一个前台进程。 容器运行的命令如果不是那些一直挂起的命令(比如运行top,tail),就是会自动退出的。

这个是docker的机制问题,比如你的web容器,我们以nginx为例,正常情况下,我们配置启动服务只需要启动响应的service即可。例如service nginx start。但是,这样做,nginx为后台进程模式运行,就导致docker前台没有运行的应用,这样的容器后台启动后,会立即自杀因为他觉得他没事可做了,所以,最佳的解决方案是,将你要运行的程序以前台进程的形式运行,常见就是命令行模式,表示我还有交互操作,别中断。

# 前台交互式启动
[root@dk ~]# docker run -it redis:6.0.8
# 后台守护式启动
[root@dk ~]# docker run -d redis:6.0.8
# 查看容器日志
[root@dk ~]# docker logs 容器ID
# 查看容器内运行的进程
[root@dk ~]# docker top 容器ID
# 查看容器内部细节
[root@dk ~]# docker inspect 容器ID
3.3.3 列出正在运行的容器
docker ps [OPTIONS]

OPTIONS说明:

  • -a :列出当前所有正在运行的容器+历史上运行过的
  • -l :显示最近创建的容器。
  • -n:显示最近n个创建的容器。
  • -q :静默模式,只显示容器编号。
3.3.4 退出容器
方式 结果 再次启动
exit(命令) 退出后,这个容器也就消失了,容器销毁ps查不到 docker start 容器名/容器ID
Ctrl + D(快捷方式) 退出后,这个容器也就消失了,容器销毁ps查不到 docker start 容器名/容器ID
先按Ctrl + P,再按Ctrl + Q(快捷方式) 退出容器,ps能查到,还在后台运行 docker attach 容器名/容器ID
3.3.5 重启&停止&删除容器
# 重新启动容器
docker restart 容器ID或者容器名 # 启动停止运行的容器
docker stop 容器ID或者容器名 # 停止指定容器
docker rm 容器ID或者容器名 # 删除全部容器docker rm $(docker ps -qa)
docker kill 容器ID或容器名 # 强制停止容器
docker rm -f $(docker ps -qa) # 一次性删除多个容器实例
docker ps -a -q | xargs docker rm
3.3.6 重新进入容器

首先启动容器:

# 以交互式模式在后台运行ubuntu
[root@dk ~]# docker run -itd ubuntu /bin/bash 
659360a3583c2aa96dc420307af704c8efdf59723f4014704ed9f4c1431fc954

在使用 -d 参数时,容器启动后会进入后台。此时想要进入容器,可以通过以下指令进入:

  • **docker attach:**直接进入容器启动命令的终端,不会启动新的进程。用exit退出,会导致容器的停止。
[root@dk ~]# docker attach 659360a3583c
  • docker exec:推荐大家使用 docker exec 命令,因为此命令会退出容器终端,但不会导致容器的停止。
# 进入正在运行的容器并以命令行交互,只有容器正在运行才能执行成功!
[root@dk ~]# docker exec -it 659360a3583c /bin/bash

总结:一般用-d后台启动的程序,再用exec进入对应容器实例

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

语法格式如下:

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

# 1. 运行ubuntu
[root@dk ~]# docker run -itd ubuntu /bin/bash
61eb9ff76e82442175c128018eda7958f278b742adf3fb9e98b5d68039f34c52
# 2. 查看正在运行的容器
[root@dk ~]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS     NAMES
61eb9ff76e82   ubuntu    "/bin/bash"   6 seconds ago   Up 3 seconds       	    my_shk
# 3. 重新进去容器
[root@dk ~]# docker exec -it 61eb9ff76e82 /bin/bash
# 4. 进入ubuntu容器后创建文件
root@61eb9ff76e82:/# ls
bin  boot  dev  etc  home  lib  lib32  lib64  libx32  media  mnt  opt  proc  root  run  sbin  srv  sys  tmp  usr  var
root@61eb9ff76e82:/# cd home
root@61eb9ff76e82:/home# echo hello ubuntu >> ubuntu.txt
root@61eb9ff76e82:/home# cat ubuntu.txt 
hello ubuntu
# 5. 退出ubuntu容器,此操作并不会真正退出容器。容器会继续在后台运行
root@61eb9ff76e82:/home# exit
exit
# 6. 执行复制的操作
[root@dk ~]# docker cp 61eb9ff76e82:/home/ubuntu.txt /home
# 7. 查看结果
[root@dk ~]# cat /home/ubuntu.txt 
hello ubuntu

示例:

#将物理主机中的/data/index.html拷贝到容器bd96d72ed9c7:/web/目录下
[root@dk ~]# docker cp /data/index.html bd96d72ed9c7:/web/

#将物理主机中的/data/index.html拷贝到容器bd96d72ed9c7:/web/目录下并改名为index.php
[root@dk ~]# docker cp /data/index.html bd96d72ed9c7:/web/index.php

#拷贝容器bd96d72ed9c7:/web/目录到物理主机中的/data/目录下
[root@dk ~]# docker cp  bd96d72ed9c7:/web  /data/
3.3.8 导出和导入容器
  • 导出容器:如果要导出本地某个容器,可以使用 docker export 命令。
# 语法格式如下:
[root@dk ~]# docker export 容器ID > 文件名.tar
# 案例
# 1. 运行镜像创建容器
[root@dk ~]# docker run -itd ubuntu /bin/bash
450d0315404c5042a4f7ebad233b9ce5235897c6069e234e5c5b4cfaa816c025
# 2. 查看正在运行的容器
[root@dk ~]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS     NAMES
450d0315404c   ubuntu    "/bin/bash"   5 seconds ago   Up 3 seconds             gallant_raman
# 3. 导出容器
[root@dk ~]# docker export 450d0315404c > ubuntu.tar
# 4. 查看导出文件
[root@dk ~]# ls
anaconda-ks.cfg  ubuntu.tar
  • 导入容器快照:可以使用 docker import 从容器快照文件中再导入为镜像,以下实例将快照文件 ubuntu.tar 导入到镜像 test/ubuntu:v1:
# 语法格式如下:
[root@dk ~]# cat 文件名.tar | docker import - 镜像用户/镜像名:镜像版本号
# 案例
# 1. 可以先删除ubuntu镜像,也可以不删除。因为后面导入时重新设置了版本不冲突
[root@dk ~]# docker rmi -f ubuntu
# 2. 导入镜像
[root@dk ~]# cat /root/ubuntu.tar | docker import - test/ubuntu:v1
sha256:ede897f1dc208a1f738d4afd1639ebcfc34f91e49845f2ac5dc4f94a0a8ddfc9
# 3. 查看导入的镜像
[root@dk ~]# docker images
REPOSITORY    TAG       IMAGE ID       CREATED         SIZE
test/ubuntu   v1        ede897f1dc20   6 seconds ago   77.8MB
tomcat        latest    ab37a470285a   27 hours ago    474MB
mysql         5.7       eef0fab001e8   3 days ago      495MB
mysql         latest    2a04bf34fdf0   3 days ago      535MB
ubuntu        latest    a8780b506fa4   6 days ago      77.8MB
hello-world   latest    feb5d9fea6a5   13 months ago   13.3kB
centos        latest    5d0da3dc9764   13 months ago   231MB
3.3.9 常用命令总结
attach    Attach to a running container                 # 当前 shell 下 attach 连接指定运行容器
build     Build an image from a Dockerfile              # 通过 Dockerfile 定制镜像
commit    Create a new image from a container changes   # 提交当前容器为新的镜像
cp        Copy files/folders from the containers filesystem to the host path   #从容器中拷贝指定文件或者目录到宿主机中
create    Create a new container                        # 创建一个新的容器,同 run,但不启动容器
diff      Inspect changes on a container's filesystem   # 查看 docker 容器变化
events    Get real time events from the server          # 从 docker 服务获取容器实时事件
exec      Run a command in an existing container        # 在已存在的容器上运行命令
export    Stream the contents of a container as a tar archive   # 导出容器的内容流作为一个 tar 归档文件[对应 import ]
history   Show the history of an image                  # 展示一个镜像形成历史
images    List images                                   # 列出系统当前镜像
import    Create a new filesystem image from the contents of a tarball # 从tar包中的内容创建一个新的文件系统映像[对应export]
info      Display system-wide information               # 显示系统相关信息
inspect   Return low-level information on a container   # 查看容器详细信息
kill      Kill a running container                      # kill 指定 docker 容器
load      Load an image from a tar archive              # 从一个 tar 包中加载一个镜像[对应 save]
login     Register or Login to the docker registry server    # 注册或者登陆一个 docker 源服务器
logout    Log out from a Docker registry server          # 从当前 Docker registry 退出
logs      Fetch the logs of a container                 # 输出当前容器日志信息
port      Lookup the public-facing port which is NAT-ed to PRIVATE_PORT    # 查看映射端口对应的容器内部源端口
pause     Pause all processes within a container        # 暂停容器
ps        List containers                               # 列出容器列表
pull      Pull an image or a repository from the docker registry server   # 从docker镜像源服务器拉取指定镜像或者库镜像
push      Push an image or a repository to the docker registry server    # 推送指定镜像或者库镜像至docker源服务器
restart   Restart a running container                   # 重启运行的容器
rm        Remove one or more containers                 # 移除一个或者多个容器
rmi       Remove one or more images       # 移除一个或多个镜像[无容器使用该镜像才可删除,否则需删除相关容器才可继续或 -f 强制删除]
run       Run a command in a new container              # 创建一个新的容器并运行一个命令
save      Save an image to a tar archive                # 保存一个镜像为一个 tar 包[对应 load]
search    Search for an image on the Docker Hub         # 在 docker hub 中搜索镜像
start     Start a stopped containers                    # 启动容器
stop      Stop a running containers                     # 停止容器
tag       Tag an image into a repository                # 给源中镜像打标签
top       Lookup the running processes of a container   # 查看容器中运行的进程信息
unpause   Unpause a paused container                    # 取消暂停容器
version   Show the docker version information           # 查看 docker 版本号
wait      Block until a container stops, then print its exit code   # 截取容器停止时的退出状态值

4. Docker镜像

4.1 什么是镜像?
  • 是什么?

镜像是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包好形成一个可交付的运行环境(包括代码、运行时需要的库、环境变量和配置文件等),这个打包好的运行环境就是image镜像文件。只有通过这个镜像文件才能生成Docker容器实例(类似Java中new出来一个对象)。

  • 分层的镜像

以我们的pull为例,在下载的过程中我们可以看到docker的镜像好像是在一层一层的在下载

  • UnionFS(联合文件系统)

UnionFS(联合文件系统):Union文件系统(UnionFS)是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtual filesystem)。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 等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如Ubuntu,Centos等等。

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

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

  • 为什么 Docker 镜像要采用这种分层结构呢

镜像分层最大的一个好处就是共享资源,方便复制迁移,就是为了复用。比如说有多个镜像都从相同的 base 镜像构建而来,那么 Docker Host 只需在磁盘上保存一份 base 镜像;同时内存中也只需加载一份 base 镜像,就可以为所有容器服务了。而且镜像的每一层都可以被共享。

4.2 重点理解

Docker镜像层都是只读的,容器层是可写的。当容器启动时,一个新的可写层被加载到镜像的顶部。 这一层通常被称作“容器层”,“容器层”之下的都叫“镜像层”。所有对容器的改动 - 无论添加、删除、还是修改文件都只会发生在容器层中。只有容器层是可写的,容器层下面的所有镜像层都是只读的。

4.3 Docker镜像commit操作案例

docker commit提交容器副本使之成为一个新的镜像

# 语法格式为:
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名]
# 案例演示ubuntu安装vim
# 1. 从Hub上下载ubuntu镜像到本地并成功运行,原始的默认Ubuntu镜像是不带着vim命令的
[root@dk ~]# docker run -itd ubuntu /bin/bash
[root@dk ~]# docker ps
CONTAINER ID   IMAGE     COMMAND       CREATED         STATUS         PORTS     NAMES
5ad2545ddc9f   ubuntu    "/bin/bash"   8 seconds ago   Up 5 seconds             laughing_wilson
# 2. 外网连通的情况下,安装vim
[root@dk ~]# docker exec -it 5ad2545ddc9f /bin/bash
# 3. 更新包管理工具
root@5ad2545ddc9f:/# apt-get update
# 4. 安装vim
root@5ad2545ddc9f:/# apt-get -y install vim
# 5. 安装完成后commit我们自己的新镜像
[root@dk ~]# docker commit -m="add my images" -a="yaochuang" 5ad2545ddc9f yao/myubuntu:1.1
# 6. 查看自己的镜像,可以看到yao/myubuntu已经发布成功了
[root@dk ~]# docker images
REPOSITORY      TAG       IMAGE ID       CREATED         SIZE
yao/myubuntu   1.1       5ea55b97dc36   5 seconds ago   177MB
test/ubuntu     v1        ede897f1dc20   2 hours ago     77.8MB
tomcat          latest    ab37a470285a   29 hours ago    474MB
mysql           5.7       eef0fab001e8   4 days ago      495MB
mysql           latest    2a04bf34fdf0   4 days ago      535MB
ubuntu          latest    a8780b506fa4   6 days ago      77.8MB
hello-world     latest    feb5d9fea6a5   13 months ago   13.3kB
centos          latest    5d0da3dc9764   13 months ago   231MB
# 7. 重新运行自己发布的镜像文件
[root@dk ~]# docker run -itd yao/mybubuntu:1.1 /bin/bash
668079fcda31acaddd39396b6e013ac8fad90591ad9047f1b3df61d363d12717
# 8. 进入自己发布的容器
[root@dk ~]# docker exec -it 668079fcda31 /bin/bash
# 9. 此时再使用vim编辑器
root@668079fcda31:/# vim a
  1. 官网是默认下载的Ubuntu没有vim命令

  2. 我们自己commit构建的镜像,新增加了vim功能,可以成功使用。

总结

Docker中的镜像分层,支持通过扩展现有镜像,创建新的镜像。类似Java继承于一个Base基础类,自己再按需扩展。新镜像是从 base 镜像一层一层叠加生成的。每安装一个软件,就在现有镜像的基础上增加一层。

4.4 生命周期

Docker 容器经历以下阶段:

  • 创建容器
  • 运行容器
  • 暂停容器(可选)
  • 取消暂停容器(可选)
  • 启动容器
  • 停止容器
  • 重启容器
  • 杀死容器
  • 销毁容器

5. 本地镜像发布到阿里云

5.1 本地镜像发布到阿里云流程

5.2 镜像的生成方法

  • 上一章节已经讲过操作
  • 反复练习生成两个镜像centos + ubuntu
  • 请大家务必多动手练习、
  • 后面的DockerFile章节,第2种方法

5.3 将本地镜像推送到阿里云

  • 本地镜像素材原型
  • 阿里云开发者平台

(1)打开链接:https://promotion.aliyun.com/ntms/act/kubernetes.html,登录阿里云开发者平台

  • 创建镜像仓库

(1)登录阿里云

(2)选择容器镜像服务

(3)创建命名空间

(4)创建镜像仓库

(5)选择管理获取脚本信息

  • 管理界面脚本
  • 脚本实例,将镜像推送到Registry
$ docker login --username=no***@live.com registry.cn-hangzhou.aliyuncs.com
$ docker tag [ImageId] registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu:[镜像版本号]
$ docker push registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu:[镜像版本号]
注意:此命令是姚哥自己的,请根据自己的地址进行修改,不要复制粘贴我的

具体运行效果如下图所示:

检查阿里云上的镜像仓库

5.4 将阿里云上的镜像下载到本地

# 先删除本地已存在的镜像
[root@dk ~]# docker rmi -f 5ea55b97dc36
Untagged: yao/mybubuntu:1.1
Untagged: registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu:1.1
Untagged: registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu@sha256:102746278de36b7c9093a5b2b56abd6ffa66f656192127e912c7d4f070b46e2a
Deleted: sha256:5ea55b97dc36e138a84efaea893ab904c7c36e7963114eedb7ddeafb512c88a9
[root@dk ~]# docker pull registry.cn-hangzhou.aliyuncs.com/noyao/myubuntu:1.1

6. 本地镜像发布到私有库

6.1 本地镜像发布到私有库流程

6.2 是什么?

  1. 官方Docker Hub地址:https://hub.docker.com/,中国大陆访问太慢了且有被阿里云取代的趋势,不太主流。
  2. Docker Hub、阿里云这样的公共镜像仓库可能不太方便,涉及机密的公司不可能提供镜像给公网,所以需要创建一个本地私人仓库供给团队使用,基于公司内部项目构建镜像。
  3. Docker Registry是官方提供的工具,可以用于构建私有镜像仓库。

6.3 将本地镜像推送到私有库

  1. 下载镜像Docker Registry
[root@dk ~]# docker pull registry
Using default tag: latest
latest: Pulling from library/registry
213ec9aee27d: Pull complete 
4583459ba037: Pull complete 
6f6a6c5733af: Pull complete 
b136d5c19b1d: Pull complete 
fd4a5435f342: Pull complete 
Digest: sha256:de4b7d974220231c29828d2a643b7dac3186746eb91f4d688438680bf0ebaf60
Status: Downloaded newer image for registry:latest
docker.io/library/registry:latest
  1. 运行私有库Registry,相当于本地有个私有Docker Hub
# 此命令现在暂时无需了解,后面第7章再做说明
[root@dk ~]# docker run -d -p 5000:5000 -v /noyao/myregistry/:/tmp/registry --privileged=true registry
8756168da5688dbe17c61fcfed5f72b9e084ba404e872024d0201bcadce92e0d

默认情况,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器卷映射,方便于宿主机联调

  1. 案例演示:创建一个新镜像,ubuntu安装ifconfig命令

ifconfig(接口配置)是一个网络管理工具。它用于在Linux操作系统中配置和查看网络接口的状态。使用ifconfig可以分配IP地址、启用或禁用接口、管理ARP缓存、路由等。

  • 从Hub上下载ubuntu镜像到本地并成功运行
[root@dk ~]# docker run -it ubuntu /bin/bash
  • 原始的Ubuntu镜像是不带着ifconfig命令的
root@605767afd7aa:/# ipconfig
bash: ipconfig: command not found
root@605767afd7aa:/# apt-get update
root@605767afd7aa:/# apt-get install net-tools
root@605767afd7aa:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.3  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:03  txqueuelen 0  (Ethernet)
        RX packets 9878  bytes 25245740 (25.2 MB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 8793  bytes 480864 (480.8 KB)
        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
# 退出容器,不记得命令请回顾3.4章节
  • 安装完成后,commit我们自己的新镜像
# 公式:
docker commit -m="提交的描述信息" -a="作者" 容器ID 要创建的目标镜像名:[标签名]
# 命令:在容器外执行,记得
[root@dk ~]# docker commit -m="添加ifconfig命令" -a="yaochuang" abdef7cbf113 ycubuntu:1.2
  • 启动我们的新镜像并和原来的对比
  1. 官网是默认下载的Ubuntu没有ifconfig命令

  2. 我们自己commit构建的新镜像,新增加了ifconfig功能,可以成功使用。

[root@dk ~]# docker images
[root@dk ~]# docker run -it ycubuntu:1.2 /bin/bash
root@6fbe42982759:/# ifconfig
  • curl验证私服库上有什么镜像1

curl命令 是一个利用URL规则在命令行下工作的文件传输工具。它支持文件的上传和下载,所以是综合传输工具,但按传统,习惯称curl为下载工具。作为一款强力工具,curl支持包括HTTP、HTTPS、ftp等众多协议,还支持POST、cookies、认证、从指定偏移处下载部分文件、用户代理字符串、限速、文件大小、进度条等特征。做网页处理流程和数据检索自动化,curl可以助你一臂之力。

# -X:(HTTP)指定与服务器通信使用的请求方法,如:GET、PUT、POST、DELETE等,默认GET
[root@dk ~]# curl -XGET http://192.168.170.151:5000/v2/_catalog
{"repositories":[]}

可以看到目前私服库没有任何镜像上传过,继续看下面的步骤!

  • 将新镜像ycubuntu:1.2修改符合私服规范的Tag
# 公式:
docker tag 镜像:Tag Host:Port/Repository:Tag
# 自己host主机IP地址,填写同学你们自己的,不要粘贴错误
# 使用命令 docker tag 将ycubuntu:1.2 这个镜像修改为192.168.170.151:5000/ycubuntu:1.2
[root@dk ~]# docker tag ycubuntu:1.2 192.168.170.151:5000/ycubuntu:1.2
  • 修改配置文件使之支持http
# registry-mirrors 配置的是国内阿里提供的镜像加速地址,不用加速的话访问官网的会很慢。
# 2个配置中间有个逗号 ','别漏了,这个配置是json格式的。
[root@dk ~]# cat /etc/docker/daemon.json
{
	"registry-mirrors":["https://aa25jngu.mirror.aliyuncs.com"],
	"insecure-registries":["192.168.170.151:5000"]
}

上述理由:docker默认不允许http方式推送镜像,通过配置选项来取消这个限制。 修改完后重启docker。

# 重启docker
[root@dk ~]# systemctl restart docker
# 运行私有库
[root@dk ~]# docker run -d -p 5000:5000 -v /noyao/myregistry/:/tmp/registry --privileged=true registry
  • push推送到私服库
[root@dk ~]# docker push 192.168.170.151:5000/ycubuntu:1.2
The push refers to repository [192.168.170.151:5000/ycubuntu]
d2126fedd4bd: Pushed 
f4a670ac65b6: Pushed 
1.2: digest: sha256:7cd62eb5e172572d793a343834adeaa722d2fa0f1990da54e9e7ceddfd04312c size: 741
  • curl验证私服库上有什么镜像2
[root@dk ~]# curl -XGET http://192.168.170.151:5000/v2/_catalog
{"repositories":["ycubuntu"]}
  • pull到本地并运行
[root@dk ~]# docker pull 192.168.170.151:5000/ycubuntu:1.2
# 语法如下:
[root@dk ~]# docker run -it 镜像ID /bin/bash
# 镜像ID记得更换为自己的
[root@dk ~]# docker run -it 466572ffe865 /bin/bash
# 输入ifconfig查看信息
root@8097570fad7a:/# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 172.17.0.3  netmask 255.255.0.0  broadcast 172.17.255.255
        ether 02:42:ac:11:00:03  txqueuelen 0  (Ethernet)
        RX packets 8  bytes 656 (656.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

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
  • 删除本地仓库中的镜像
# 删除仓库容器/var/lib/registry/docker/registry/v2/repositories/下面的镜像目录
docker exec 容器仓库ID rm -rf /var/lib/registry/docker/registry/v2/repositories/镜像名称
# 垃圾回收
docker exec 容器仓库ID bin/registry garbage-collect /etc/docker/registry/config.yml
# 重启仓库
docker restart 容器仓库ID
# 查看镜像

7. Docker容器数据卷

7.1 坑:容器卷记得加入

--privileged=true

Docker挂载主机目录访问如果出现cannot open directory .: Permission denied

解决办法:在挂载目录后多加一个--privileged=true参数即可

如果是CentOS7安全模块会比之前系统版本加强,不安全的会先禁止,所以目录挂载的情况被默认为不安全的行为,在SELinux里面挂载目录被禁止掉了额,如果要开启,我们一般使用--privileged=true命令,扩大容器的权限解决挂载目录没有权限的问题,也即使用该参数,container内的root拥有真正的root权限,否则,container内的root只是外部的一个普通用户权限。

7.2 回顾知识点

[root@dk ~]# docker run -d -p 5000:5000 -v /noyao/myregistry/:/tmp/registry --privileged=true registry
8756168da5688dbe17c61fcfed5f72b9e084ba404e872024d0201bcadce92e0d

默认情况,仓库被创建在容器的/var/lib/registry目录下,建议自行用容器卷映射,方便与宿主机联调。

7.3 是什么

卷(volume)就是目录或文件,存在于一个或多个容器中,由docker挂载到容器,但不属于联合文件系统,因此能够绕过Union File System提供一些用于持续存储或共享数据的特性;卷的设计目的就是数据的持久化,完全独立于容器的生存周期,因此Docker不会在容器删除时删除其挂载的数据卷。

  • 将docker容器内的数据保存进宿主机的磁盘中

  • 运行一个带有容器卷存储功能的容器实例

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

7.4 能做什么

将运用与运行的环境打包镜像,run后形成容器实例运行 ,但是我们对数据的要求希望是持久化的。Docker容器产生的数据,如果不备份,那么当容器实例删除后,容器内的数据自然也就没有了。为了能保存数据在docker中我们使用卷。举个生活中例子帮助理解:假设我们有一台笔记本电脑,现在我需要备份笔记本电脑中的数据,那么我可以将数据保存外外置硬盘或U盘中,还可以备份到百度云盘或者阿里云盘。假设我现在有一台docker容器,里面有一些重要的数据,突然有一天有个人执行了docker stop或者docker rm -f,容器一旦被删除则里面的数据也会消失,这时候我们就需要用容器数据卷的方式完成数据持久化(备份重要的数据),docker可以实现容器的目录和主机的目录做一个映射,可以把容器内的数据备份+持久化到本地主机目录,这样就形成了对重要数据的保护,不容易丢失。

特点:

  1. 数据卷可在容器之间共享或重用数据

  2. 卷中的更改可以直接实时生效

  3. 数据卷中的更改不会包含在镜像的更新中

  4. 数据卷的生命周期一直持续到没有容器使用它为止

7.5 数据卷案例

案例1:匿名卷

# 直接指定容器内的路径,会随机生成一个宿主机目录地址
docker run -d --name nx1 -p 80:80 -v /nginx/dir --privileged=true nginx

案例2:具名卷

# 指定数据卷的名称,本地卷在/var/lib/docker/volumes/数据卷的名称/_data
docker run -d -p 8080:80 --name nx2 --privileged=true -v nginx:/nginx/dir nginx
  1. 宿主机与容器之间添加数据卷
# 1. 运行镜像并添加数据卷
[root@dk ~]# docker run -it --name yaoge --privileged=true -v /yc/myData:/tmp/ubuntu ubuntu /bin/bash
# 2. 在ubuntu中创建文件并写入内容
root@21cd326d8bfa:/# cd /tmp/ubuntu
root@21cd326d8bfa:/tmp/ubuntu# echo 'hello ubuntu' >> test.txt
# 3. 写入完毕后退出ubuntu

# 4. 在宿主机CentOS中查看/yc/myData/test.txt的文件内容
[root@dk ~]# cd /yc/myData/
[root@dk myData]# cat test.txt 
hello ubuntu
# 5. 重写追加文字到test.txt文件中
[root@dk myData]# echo 'hello docker!!' >> test.txt 
# 6. 查看运行的容器
[root@dk myData]# docker ps
CONTAINER ID   IMAGE      COMMAND                  CREATED              STATUS              PORTS                                       NAMES
21cd326d8bfa   ubuntu     "/bin/bash"              About a minute ago   Up About a minute                                               yaoge
d5d543121c4b   registry   "/entrypoint.sh /etc…"   30 hours ago         Up 30 hours         0.0.0.0:5000->5000/tcp, :::5000->5000/tcp   keen_ramanujan
# 7. 重新进去容器,并查看文件内容
[root@dk myData]# docker exec -it 21cd326d8bfa /bin/bash
root@21cd326d8bfa:/# cd /tmp/ubuntu
root@21cd326d8bfa:/tmp/ubuntu# cat test.txt 
hello ubuntu
hello docker!!

# 8. 可以看到在宿主机中添加的内容也能够在ubuntu容器中看到了

查看数据卷是否挂载成功

# 查看运行的容器
[root@dk ~]# docker ps
[root@dk ~]# docker inspect d5d543121c4b
        "Mounts": [
            {
                "Type": "bind",
                "Source": "/noyao/myregistry",
                "Destination": "/tmp/registry",
                "Mode": "",
                "RW": true,
                "Propagation": "rprivate"
            },
  1. 练习:容器和宿主机之间数据共享
  • docker修改,主机同步获得

  • 主机修改,docker同步获得

  • docker容器stop,主机修改,docker容器重启看数据是否同步

    总结:

    1. 如果是开发或测试环境建议使用-v指定具体的目录地址
    2. 如果是生产环境,建议使用具名卷;-v nginxhtml:容器内的地址;容器外的地址:/var/lib/docker/volumes/数据卷名称/_data
    3. 如果要指定具体的路径,例如:/var/lib/mysql/mysql.sock,建议使用指定具体的目录
  1. 读写规则映射添加说明

读写规则分为读写和只读两种,在默认情况下,就是容器可以读和写

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

下面我们进行只读的限制:容器实例内部被限制,容器只能读取不能写

# 此时如果宿主机写入内容,可以同步给容器内,容器可以读取到,但是不能写入。
docker run -it --privileged=true -v /宿主机绝对路径目录:/容器内目录:ro 镜像名
  1. 卷的继承和共享

我们在上一个容器yaoge的基础上进行配置,对于卷的继承,就是相当于使用原来的容器生成了一个新的一样的容器实例。

语法:

docker run -it  --privileged=true --volumes-from 父类  --name yaoge ubuntu

容器yaoge2继承容器yaoge的卷规则

docker run -it --name=yaoge2 --privileged=true --volumes-from yaoge ubuntu bash
  1. 多个MySQL实现数据共享(等待验证)
[root@localhost ~]# docker run -d -p 6603:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
[root@localhost ~]# docker run -d -p 6604:3306 -e MYSQL_ROOT_PASSWORD=123456 --name mysql02 --volumes-from mysql01 mysql:5.7
  1. 总结

容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器为止。

但是一旦持久化到了本地,本地的数据是不会删除的!

8. Docker常规安装简介

8.1 总体步骤

  1. 搜索镜像
  2. 拉取镜像
  3. 查看镜像
  4. 运行镜像
  5. 服务端口映射
  6. 停止容器
  7. 移除容器

8.2 安装Tomcat

  1. 搜索Tomcat
[root@dk ~]# docker search tomcat
  1. 从docker hub上拉取tomcat镜像到本地
[root@dk ~]# docker pull tomcat
  1. 查看镜像
[root@dk ~]# docker images
REPOSITORY                      TAG       IMAGE ID       CREATED         SIZE
ycubuntu                        1.2       466572ffe865   2 days ago      119MB
test/ubuntu                     v1        ede897f1dc20   5 days ago      77.8MB
ubuntu                          latest    a8780b506fa4   11 days ago     77.8MB
registry                        latest    dcb3d42c1744   5 weeks ago     24.1MB
tomcat                          latest    fb5657adc892   10 months ago   680MB
hello-world                     latest    feb5d9fea6a5   13 months ago   13.3kB
centos                          latest    5d0da3dc9764   14 months ago   231MB
  1. 运行镜像(使用tomcat镜像创建容器实例)
# -p 小写,主机端口:docker容器端口
# -P 大写,随机分配端口
# i:交互
# t:终端
# d:后台
[root@dk ~]# docker run -it -p 8080:8080 tomcat
  1. 打开浏览器输入地址:http://192.168.170.151:8080/,打开时会报404错误,如下图所示:
# 1. 查看容器ID
[root@dk ~]# docker ps
CONTAINER ID   IMAGE     COMMAND             CREATED       STATUS       PORTS                                       NAMES
2432606c447f   tomcat    "catalina.sh run"   2 hours ago   Up 2 hours   0.0.0.0:8080->8080/tcp, :::8080->8080/tcp   zen_gagarin
# 2. 重新进入容器
[root@dk ~]# docker exec -it 2432606c447f /bin/bash
# 3. 查看webapps文件夹
root@2432606c447f:/usr/local/tomcat# ls -l
total 132
-rw-r--r--. 1 root root 18994 Dec  2  2021 BUILDING.txt
-rw-r--r--. 1 root root  6210 Dec  2  2021 CONTRIBUTING.md
-rw-r--r--. 1 root root 60269 Dec  2  2021 LICENSE
-rw-r--r--. 1 root root  2333 Dec  2  2021 NOTICE
-rw-r--r--. 1 root root  3378 Dec  2  2021 README.md
-rw-r--r--. 1 root root  6905 Dec  2  2021 RELEASE-NOTES
-rw-r--r--. 1 root root 16517 Dec  2  2021 RUNNING.txt
drwxr-xr-x. 2 root root  4096 Dec 22  2021 bin
drwxr-xr-x. 1 root root    22 Nov 14 09:25 conf
drwxr-xr-x. 2 root root  4096 Dec 22  2021 lib
drwxrwxrwx. 1 root root    80 Nov 14 09:25 logs
drwxr-xr-x. 2 root root   159 Dec 22  2021 native-jni-lib
drwxrwxrwx. 2 root root    30 Dec 22  2021 temp
drwxr-xr-x. 2 root root     6 Dec 22  2021 webapps
drwxr-xr-x. 7 root root    81 Dec  2  2021 webapps.dist
drwxrwxrwx. 2 root root     6 Dec  2  2021 work
# 4. 删除webapps文件夹
root@2432606c447f:/usr/local/tomcat# rm -r webapps
# 5. 将webapps.dist文件夹改名为webapps
root@2432606c447f:/usr/local/tomcat# mv webapps.dist webapps

重新刷新浏览器,可以看到Tomcat可以正常访问了!

  1. 免修改版本
[root@dk ~]# docker pull billygoo/tomcat8-jdk8
[root@dk ~]# docker run -d -p 8080:8080 --name mytomcat8 billygoo/tomcat8-jdk8

8.3 安装MySQL

  1. docker hub上面查找mysql镜像
[root@dk ~]# docker search mysql
  1. 从docker hub上(阿里云加速器)拉取mysql镜像到本地标签为5.7
[root@dk ~]# docker pull mysql:5.7
  1. 查看镜像
[root@dk ~]# docker images
  1. 运行镜像(使用mysql5.7镜像创建容器)
# 1. 运行镜像方式1
[root@dk ~]# docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
# 运行镜像方式2
[root@dk ~]# docker run -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7

# 2. 查看有docker运行了哪些容器
[root@dk ~]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                 NAMES
fe02e9067ea2   mysql:5.7   "docker-entrypoint.s…"   5 seconds ago   Up 3 seconds   3306/tcp, 33060/tcp   some-mysql
# 3. 重新进入容器
[root@dk ~]# docker exec -it fe02e9067ea2 /bin/bash
root@fe02e9067ea2:/# mysql -uroot -p
Enter password: (输入MYSQL_ROOT_PASSWORD上定义的密码)

登录成功后,建库建表插入数据。

mysql> create database studentDB;
Query OK, 1 row affected (0.00 sec)

mysql> use studentDB;
Database changed
mysql> create table student(id int primary key auto_increment,name varchar(50));
Query OK, 0 rows affected (0.05 sec)

mysql> insert into student values(0,'aaa');
Query OK, 1 row affected (0.07 sec)

mysql> select * from student;
+----+------+
| id | name |
+----+------+
|  1 | aaa  |
+----+------+
1 row in set (0.00 sec)

Win10使用外部工具SQLyog也可以连接MySQL,如果连接不上记得防火墙端口放行,或者在运行时使用的是默认端口,注意使用docker ps查看容器运行的端口是多少!

插入中文数据时会有乱码问题,如下图:

为什么报错?

  • docker上默认字符集编码隐患
  • client客户端的编码不是utf8
  • server端的编码不是utf8
  • database数据库的编码不是utf8
  • 数据库的表的编码不是utf8
  • 表中的列字段编码不是utf8

docker里面的mysql容器实例查看,内容如下:

SHOW VARIABLES LIKE 'character%'

删除此容器重新开始配置!

重新新建容器:

[root@dk ~]# docker run -d -p 3306:3306 --privileged=true -v /yaochuang/mysql/log:/var/log/mysql -v /yaochuang/mysql/data:/var/lib/mysql -v /yaochuang/mysql/conf:/etc/mysql/conf.d -e MYSQL_ROOT_PASSWORD=123456 --name mysql mysql:5.7
1d25eb44bf8f77d63508bc086249b0b67b3df2f014757f7403f9c885ae2e8c3e
[root@dk ~]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED         STATUS         PORTS                                                  NAMES
1d25eb44bf8f   mysql:5.7   "docker-entrypoint.s…"   7 seconds ago   Up 4 seconds   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   mysql
[root@dk ~]# cd /yaochuang/mysql/conf/
[root@dk conf]# vi my.cnf
[root@dk conf]# cat my.cnf
[mysqld]
# 添加此配置跳过登录
skip-grant-tables
collation_server=utf8_general_ci
character-set-server=utf8
[client]
default-character-set=utf8
[root@dk conf]# docker restart mysql
# 重新进入mysql
[root@dk ~]# docker exec -it mysql bash
# 不用输入密码,直接回车登录
root@23c664f57c61:/# mysql -uroot
# 注意:接下来的代码都是MySQL
mysql> use mysql;
# 必须先刷新权限
mysql> flush privileges;
# 修改密码
mysql> alter user 'root'@localhost identified by '123456';
# 修改完成后退出MySQL,退出容器
mysql> exit
root@23c664f57c61:/# exit
# 重新打开my.cnf文件,删除skip-grant-tables,然后重启mysql
# 测试:使用SQLyog连接MySQL,插入中文试试是否成功!

总结:

docker安装完MySQL并run出容器后,建议先修改字符集编码后再新建库-表-插数据。

问题:假如将当前容器实例删除,再重新来一次,之前建的studentDB实例还有吗?

  1. 停止容器
[root@dk ~]# docker stop fe02e9067ea2
[root@dk ~]# docker rm -f fe02e9067ea2
  1. 移除镜像
[root@dk ~]# docker rmi -f mysql:5.7

8.4 安装Redis(待整理,考虑删除。)

Redis 是一个开源的使用 ANSI C 语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value 的 NoSQL 数据库,并提供多种语言的 API。

8.5 安装Nginx

8.6 安装PHP

8.7 安装MongoDB

8.8 安装Node.js

8.9 安装Python

二、高级篇

1. Docker复杂安装详说

1.1 安装MySQL主从复制

  • 复习主从复制原理
  • 主从搭建步骤

第1步:新建主服务器容器实例3307

# 注意:以下命令是整段,\为换行。此处应也应该要注意MYSQL_ROOT_PASSWORD设置密码时,可能后面会多一个空格
[root@dk ~]# docker run -p 3307:3306 --name mysql-master \
-v /mydata/mysql-master/log:/var/log/mysql \
-v /mydata/mysql-master/data:/var/lib/mysql \
-v /mydata/mysql-master/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7
[root@dk ~]# docker ps
CONTAINER ID   IMAGE       COMMAND                  CREATED          STATUS          PORTS                                                  
c1648630914c   mysql:5.7   "docker-entrypoint.s…"   28 seconds ago   Up 26 seconds   33060/tcp, 0.0.0.0:3307->3306/tcp, :::3307->3306/tcp
[root@dk ~]# docker exec -it mysql-master bash
# 检查 MYSQL_ROOT_PASSWORD 是否设置正确
root@c1648630914c:/# echo $MYSQL_ROOT_PASSWORD
# 尝试是否能够登陆成功
root@c1648630914c:/# mysql -uroot -p

第2步:进入/mydata/mysql-master/conf目录下新建my.cnf

[root@dk ~]# cd /mydata/mysql-master/conf
[root@dk conf]# cat my.cnf
[mysqld]
# 设置server_id,同一局域网中需要唯一
server-id=101
# 指定不需要同步的数据库名称
binlog-ignore-db=mysql
# 开启二进制日志功能
log-bin=mall-mysql-bin
# 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
# 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
# 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
# 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
# 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062

第3步:修改完配置后重启master实例

[root@dk conf]# docker restart mysql-master

第4步:重新进入mysql-master容器

[root@dk conf]# docker exec -it mysql-master bash
root@c1648630914c:/# mysql -uroot -p

第5步:master容器实例内创建数据同步用户

mysql> CREATE USER 'slave'@'%' IDENTIFIED BY '123456';
mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'slave'@'%';

第6步:新建从服务器容器实例3308

[root@dk ~]# docker run -p 3308:3306 --name mysql-slave \
-v /mydata/mysql-slave/log:/var/log/mysql \
-v /mydata/mysql-slave/data:/var/lib/mysql \
-v /mydata/mysql-slave/conf:/etc/mysql \
-e MYSQL_ROOT_PASSWORD=123456 \
-d mysql:5.7

第7步:进入/mydata/mysql-slave/conf目录下新建my.cnf

[root@dk ~]# cd /mydata/mysql-slave/conf
[root@dk ~]# cat my.cnf
[mysqld]
# 设置server_id,同一局域网中需要唯一
server-id=102
# 指定不需要同步的数据库名称
binlog-ignore-db=mysql
# 开启二进制日志功能,以备Slave作为其它数据库实例的Master时使用
log-bin=mall-mysql-slave1-bin
# 设置二进制日志使用内存大小(事务)
binlog_cache_size=1M
# 设置使用的二进制日志格式(mixed,statement,row)
binlog_format=mixed
# 二进制日志过期清理时间。默认值为0,表示不自动清理。
expire_logs_days=7
# 跳过主从复制中遇到的所有错误或指定类型的错误,避免slave端复制中断。
# 如:1062错误是指一些主键重复,1032错误是因为主从数据库数据不一致
slave_skip_errors=1062
# relay_log配置中继日志
relay_log=mall-mysql-relay-bin
# log_slave_updates表示slave将复制事件写进自己的二进制日志
log_slave_updates=1
# slave设置为只读(具有super权限的用户除外)
read-only=1

第8步:修改完配置后重启slave实例

# 重启完成后记得查看后台是否重启成功
[root@dk conf]# docker restart mysql-slave

第9步:在主数据库中查看主从同步状态

# 此处应当重新进入主库容器,重新进入数据库
mysql> show master status;
+-----------------------+----------+--------------+------------------+-------------------+
| File                  | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-----------------------+----------+--------------+------------------+-------------------+
| mall-mysql-bin.000001 |      617 |              | mysql            |                   |
+-----------------------+----------+--------------+------------------+-------------------

第10步:进入mysql-slave容器

[root@dk ~]# docker exec -it mysql-slave bash
root@75c733a68748:/# mysql -uroot -p

第11步:在从数据库中配置主从复制

参数说明:

  • master_host:主数据库的IP地址;
  • master_port:主数据库的运行端口;
  • master_user:在主数据库创建的用于同步数据的用户账号;
  • master_password:在主数据库创建的用于同步数据的用户密码;
  • master_log_file:指定从数据库要复制数据的日志文件,通过查看主数据的状态,获取File参数;
  • master_log_pos:指定从数据库从哪个位置开始复制数据,通过查看主数据的状态,获取Position参数;
  • master_connect_retry:连接失败重试的时间间隔,单位为秒。
mysql> change master to master_host='192.168.170.148',master_user='slave',master_password='123456',master_port=3307, master_log_file='mall-mysql-bin.000001', master_log_pos=1821, master_connect_retry=30;

第12步:在从数据库中开启主从同步

mysql> start slave;

第13步:在从数据库中查看主从同步状态

mysql> show slave status \G;

第14步:查看从数据库状态发现已经同步

第15步:主从复制测试

主机新建库—使用库—新建表—插入数据

从机使用库—查看记录

1.2 MySQL搭建双主双从

建议学习完Docker网络再来配置此内容
1.2.1 架构

一个主机 m1 用于处理所有写请求,它的从机 s1 和另一台主机 m2 还有它的从机 s2 负责所有读请求。当 m1 主机宕机后,m2 主机负责写请求,m1 和 m2 互为备机,架构图如下:

1.2.2 准备工作

首先会创建一个自定义网络 mysql ,然后会启动 4 台 MySQL 容器实例,其中 2 台作为主机,2 台作为从机。

编号 主机名 备注
1 m1 主机
2 s1
3 m2 主机(备)
4 s2 从机(备)
1.2.3 自定义网络

创建自定义网络,以便容器之间可以通过主机名和端口互相通信:

docker network create mysql
1.2.4 创建四台MySQL容器实例

新建 4 台 MySQL 容器实例,2 台容器实例为 MySQL 的主机,2 台容器实例为 MySQL 的从机:

docker run -d -p 3306:3306 --network mysql \
      --name m1 \
      -v m1:/etc/mysql/conf.d \
      -v /var/m1/logs:/var/log/mysql \
      -v /var/m1/data:/var/lib/mysql \
      -e MYSQL_ROOT_PASSWORD=123456 \
      -e TZ=Asia/Shanghai \
      --restart=always mysql:8.0.26 \
      --lower_case_table_names=1 \
      --character-set-server=utf8mb4 \
      --collation-server=utf8mb4_general_ci \
      --default-authentication-plugin=mysql_native_password

docker run -d -p 3307:3306 --network mysql \
    --name s1 \
    -v s1:/etc/mysql \
    -v /var/s1/logs:/var/log/mysql \
    -v /var/s1/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -e TZ=Asia/Shanghai \
    --restart=always mysql:8.0.26 \
    --lower_case_table_names=1 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_general_ci \
    --default-authentication-plugin=mysql_native_password
    
docker run -d -p 3308:3306 --network mysql \
    --name m2 \
    -v m2:/etc/mysql \
    -v /var/m2/logs:/var/log/mysql \
    -v /var/m2/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -e TZ=Asia/Shanghai \
    --restart=always mysql:8.0.26 \
    --lower_case_table_names=1 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_general_ci \
    --default-authentication-plugin=mysql_native_password
    
docker run -d -p 3309:3306 --network mysql \
    --name s2 \
    -v s2:/etc/mysql \
    -v /var/s2/logs:/var/log/mysql \
    -v /var/s2/data:/var/lib/mysql \
    -e MYSQL_ROOT_PASSWORD=123456 \
    -e TZ=Asia/Shanghai \
    --restart=always mysql:8.0.26 \
    --lower_case_table_names=1 \
    --character-set-server=utf8mb4 \
    --collation-server=utf8mb4_general_ci \
    --default-authentication-plugin=mysql_native_password
1.2.5 查看自定义网络

查看自定义网络是否有 MySQL 的主机和从机:

docker network ls
docker network inspect mysql
1.2.6 查看卷

列出所有卷:

docker volume ls

查看卷的详情:

docker volume inspect m1
cd /var/lib/docker/volumes/m1/_data

其余信息,以此类推即可。

1.2.7 主机搭建(m1)

修改主机 m1 容器实例的配置文件,并重启主机容器 m1 实例:

vim my.cnf
[mysqld]
# 以下是增加的配置
# [必须] 主服务器唯一ID,保证整个集群环境中唯一,取值范围在 1 - 2^32 -1 ,默认为 1
server-id=1
# 在作为从库的时候,有写入操作也需要更新二进制日志文件
log-slave-updates

重启容器:docker restart m1

1.2.8 主机搭建(m2)

修改主机 m2 容器实例的配置文件,并重启主机容器 m2 实例:

vim my.cnf
[mysqld]
# 以下是增加的配置
# [必须] 主服务器唯一ID,保证整个集群环境中唯一,取值范围在 1 - 2^32 -1 ,默认为 1
server-id=3
# 在作为从库的时候,有写入操作也需要更新二进制日志文件
log-slave-updates

重启容器:docker restart m2

1.2.9 从机搭建(s1)

修改从机 s1 容器实例的配置文件,并重启从机容器 s1 实例:

vim my.cnf
[mysqld]
# 以下是增加的配置
# [必须] 从服务器唯一ID
server-id=2
# [可选] 启用中继日志
relay-log=mysql-relay

重启容器:docker restart s1

1.2.10 从机搭建(s2)

修改从机 s2 容器实例的配置文件,并重启从机容器 s2 实例:

vim my.cnf
[mysqld]
# 以下是增加的配置
# [必须] 从服务器唯一ID
server-id=4
# [可选] 启用中继日志
relay-log=mysql-relay

重启容器:docker restart s2

1.2.11 双主:建立账户并授权

MySQL 主机(m1)创建账号并授权:

CREATE USER 'slave1'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'%';
ALTER USER 'slave1'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

MySQL 主机(m2)创建账号并授权:

CREATE USER 'slave1'@'%' IDENTIFIED BY '123456';
GRANT REPLICATION SLAVE ON *.* TO 'slave1'@'%';
ALTER USER 'slave1'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

查看 MySQL 主机(m1)的状态,并记录 File 和 Position 的值:

SHOW MASTER STATUS;

查看 MySQL 主机(m2)的状态,并记录 File 和 Position 的值:

SHOW MASTER STATUS;
1.2.12 双从:配置需要复制的主机

步骤 ①:从机上复制主机的命令

CHANGE MASTER TO 
MASTER_HOST='主机的IP地址',
MASTER_USER='主机用户名',
MASTER_PORT='主机的端口号',
MASTER_PASSWORD='主机用户名的密码',
MASTER_LOG_FILE='binlog.具体数字',
MASTER_LOG_POS=具体值;

步骤②:配置从库1(s1),指定主机m1

步骤③:配置从库2(s2),指定主机m2

步骤④:启动从库1和从库2的主从同步

start slave;

步骤⑤:查看同步状态

到此为止,我们只是配置了两台主从复制,还没有配置主主复制,请看下面的操作。

1.2.13 双主之间的相互复制

步骤 ① :查看 Master 的状态,并记录 File 和 Position 的值:

SHOW MASTER STATUS;

之所以再次执行,是为了防止 File 和 Position 有变化。

步骤 ②:m2 复制 m1,m1 复制 m2。

CHANGE MASTER TO 
MASTER_HOST='主机的IP地址',
MASTER_USER='主机用户名',
MASTER_PORT='主机的端口号',
MASTER_PASSWORD='主机用户名的密码',
MASTER_LOG_FILE='binlog.具体数字',
MASTER_LOG_POS=具体值;

示例:m1

CHANGE MASTER TO 
MASTER_HOST='m2',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='binlog.000003',
MASTER_LOG_POS=1114;

示例:m2

CHANGE MASTER TO 
MASTER_HOST='m1',
MASTER_USER='slave1',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='binlog.000003',
MASTER_LOG_POS=1114;

步骤 ③:启动 Master 同步

两个主库都要执行!

START SLAVE;

步骤④:查看同步状态

1.2.14 测试

在 m1 和 m2 上执行 DDL 和 DML 语句,查看涉及到的数据库服务器的数据同步情况。

示例:m1

# 执行 DDL 操作
CREATE DATABASE IF NOT EXISTS dbtest CHARACTER SET 'utf8mb4' COLLATE 'utf8mb4_general_ci';

USE dbtest;

CREATE TABLE student(id INT,`name` VARCHAR(16));

示例:m2

# 执行 DML 操作
USE dbtest;

INSERT INTO student VALUES(1, 'xx');

INSERT INTO student VALUES(2, 'yy');

示例:m1

# 查看数据
USE dbtest;
SELECT * FROM student;

示例:m2

# 查看数据
USE dbtest;
SELECT * FROM student;

示例:s1

# 查看数据
USE dbtest;
SELECT * FROM student;

示例:s2

# 查看数据
USE dbtest;
SELECT * FROM student;

1.3 安装redis集群

此处内容不做讲解

2. DockerFile解析

2.1 是什么

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

概念:

官网:

构建三步骤:

  • 编写Dockerfile文件
  • docker build命令构建镜像
  • docker run依镜像运行容器实例

2.2 DockerFile构建过程解析

Dockerfile内容基础知识

  1. 每条指令都必须为大写字母且后面要跟随至少一个参数
  2. 指令按照从上到下,顺序执行
  3. #表示注释
  4. 每条指令都会创建一个新的镜像层并对镜像进行提交

Docker执行Dockerfile的大致流程

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

总结

从应用软件的角度来看,Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段:

  • Dockerfile是软件的原材料

  • Docker镜像是软件的交付品

  • Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例

Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,合力充当Docker体系的基石。

1)Dockerfile,需要定义一个Dockerfile,Dockerfile定义了进程需要的一切东西。Dockerfile涉及的内容包括执行代码或者是文件、环境变量、依赖包、运行时环境、动态链接库、操作系统的发行版、服务进程和内核进程(当应用进程需要和系统服务和内核进程打交道,这时需要考虑如何设计namespace的权限控制)等等;

2)Docker镜像,在用Dockerfile定义一个文件之后,docker build时会产生一个Docker镜像,当运行 Docker镜像时会真正开始提供服务;

3)Docker容器,容器是直接提供服务的。

2.3 DockerFile常用保留字指令

  • 参考tomcat8的dockerfile入门

https://github.com/docker-library/tomcat

  • 一般而言,Dockerfile 可以分为四个部分:基础镜像信息维护者信息镜像操作指令启动时执行指令
2.3.1 FROM

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

  • 推荐使用 alpineslim 之类的基础小镜像。

  • scratch 镜像是一个空镜像,常常用于多阶段构建。

  • 『问』:如何确定我们需要的基础镜像?

  • 『答』:

    • ① 如果是 Java 应用,可以选择 Java 基础镜像或 Tomcat 基础镜像。
    • ② 如果是 JS 模块化应用,可以选择 nodejs 基础镜像。
    • ③ 每种语言都有自己的服务器或基础环境镜像,如:Python、Golang、Java、PHP 等。
2.3.2 MAINTAINER & LABEL

镜像维护者的姓名和邮箱地址,已过期;可以使用 LABEL xxx=yyy 来代替。

LABEL 用来标注镜像的一些说明信息,常常用来指定维护者的信息。

# 下面的这种格式是可以的
LABEL multi.label1="value1" multi.label2="value2" other="value3"

# 下面的这种格式也是可以的
LABEL multi.label1="value1" \
      multi.label2="value2" \
      other="value3"
2.3.3 RUN
  • RUN 指令在当前镜像层顶部的新层执行任何命令,并提交结果,生成新的镜像层。

  • 生成的提交镜像将用于 Dockerfile 中的下一步,分层运行 RUN 指令并生成提交符合 Docker 的核心概念,就像 Git 管理源代码一样。

  • 注意:多个 RUN 指令并没有上下文的关系,换言之,多个 RUN 指令都是在同一个目录操作的。

  • 容器构建时需要运行的命令,有两种格式

    Shell格式:

    RUN <命令行命令>
    # <命令行命令> 等同于,在终端操作的shell名。
    RUN yum -y install vim

    exec格式

    RUN["可执行文件","参数1","参数2"]
    # 例如:
    # RUN["./test.sh","dev","offline"] 等价于 RUN ./test.sh dev offline

    RUN 是在docker build 时运行

  • 在 RUN 中可以使用 \ 将一条 RUN 指令继续到下一行。
RUN /bin/bash -c 'source $HOME/.bashrc; \
echo $HOME'

# 等同于
RUN /bin/bash -c 'source $HOME/.bashrc; echo $HOME'
  • 案例
[root@dk ~]# vim Dockerfile
# 测试案例
FROM alpine
LABEL maintainer=yaoge
ENV msg='hello Docker'
RUN echo $msg
RUN ["echo","$msg"]
RUN /bin/sh -c 'echo $msg'
RUN ["/bin/sh","-c","echo $msg"]
CMD sleep 1000

运行

docker build -t test --force-rm --no-cache .
  • 总结
    1. 由于 [] 不是 shell 形式,所以不能输出变量信息,而是输出 $msg 。其他任何 /bin/sh -c 的形式都可以输出变量信息。
    1. shell 是 RUN /bin/sh -c <command> 的方式,RUN ["/bin/sh","-c",command] 的 exec 方式等同于 shell 方式,而 RUN ["/bin/sh",command] 的 exec 默认不会进行变量替换。
2.3.4 构建期(build)和运行期(run)
  • 构建期是指使用 Dockerfile 构建镜像的整个过程时期,如:docker build 等。

  • 运行期是指使用之前构建的镜像启动容器的过程,如:docker start 、docker run 等。

2.3.5 ARG

语法:

ARG name=defaultValue
  • ARG 指令定义了一个变量,用户可以在构建的时候使用 --build-arg name=value 传递,docker build 命令会将其传递给构建器。

  • --build-arg 指定参数会覆盖 Dockerfile 中指定的同名参数。

  • 如果用户指定了 未在 Dockerfile 中定义的构建参数 ,则构建会输出 警告

  • ARG 只在构建时期有效,运行时期无效。

  • 不建议使用构建时变量来传递注入 github 密码、用户凭据等机密,因为构建时变量的值可以通过 docker history 来观察到。

  • ARG 变量定义从 Dockerfile 定义的行开始生效。

  • 使用 ENV 指定定义的环境变量始终会覆盖同名的 ARG 指令。

案例:

[root@dk ~]# vim Dockerfile
# 选择基础镜像
FROM alpine
# 维护者信息
LABEL maintainer="yaoge"
# ARG 指令定义了一个变量,用户可以在构建的时候使用 `--build-arg name=value` 传递,docker build 命令会将其传递给构建器。
# `--build-arg` 指定参数会覆盖 Dockerfile 中指定的同名参数。
# 如果用户指定了 `未在 Dockerfile 中定义的构建参数` ,则构建会输出 `警告` 。
# ARG 只在构建时期有效,运行时期无效。
# 不建议使用构建时变量来传递注入 github 密码、用户凭据等机密,因为构建时变量的值可以通过 docker history 来观察到。
# ARG 变量定义从 Dockerfile 定义的行开始生效。
ARG param="Hi Docker"

# 在构建时期会运行的指令(根据 Dockerfile 创建一个镜像的整个过程时期)
RUN echo 1111
RUN echo ${param}

# 在运行时候会执行的指令(根据之前创建的镜像启动一个容器,容器启动默认运行的命令)
# docker start 或 docker run
CMD ["/bin/sh","-c","echo 2222;echo $param"]

运行:

docker build -t test01 --force-rm --no-cache .
# 或者
docker build -t test02 --force-rm --no-cache --build-arg param=test .
2.3.6 ENV

语法:

ENV name=value
  • ENV 和 ARG 很类似,但是 ENV 在构建期和运行期都有效,并且使用 ENV 指定定义的环境变量始终会覆盖同名的 ARG 指令。

  • 可以使用 docker run -e name=value 修改 ENV 定义的环境变量。

示例:

[root@dk ~]# vim Dockerfile
# 选择基础镜像
FROM alpine
# 维护者信息
LABEL maintainer="姚大哥"
# ARG 指令定义了一个变量,用户可以在构建的时候使用 `--build-arg name=value` 传递,docker build 命令会将其传递给构建器。
# `--build-arg` 指定参数会覆盖 Dockerfile 中指定的同名参数。
# 如果用户指定了 `未在 Dockerfile 中定义的构建参数` ,则构建会输出 `警告` 。
# ARG 只在构建时期有效,运行时期无效。
# 不建议使用构建时变量来传递注入 github 密码、用户凭据等机密,因为构建时变量的值可以通过 docker history 来观察到。
# ARG 变量定义从 Dockerfile 定义的行开始生效。
ARG param="Hi Docker"

# ENV 在构建期和运行期都有效,但是只能在运行期进行修改,修改通过 docker run -e name=value 命令。
ENV app=taobao

# 在构建时期会运行的指令(根据 Dockerfile 创建一个镜像的整个过程时期)
RUN echo 1111
RUN echo ${param}
RUN echo ${app}

# 在运行时候会运行的指令(根据之前创建的镜像启动一个容器,容器启动默认运行的命令)
# docker start 或 docker run
CMD ["/bin/sh","-c","echo 2222;echo $param;echo app_$app"]

运行:

docker build -t test --force-rm --no-cache .
# 或者
docker run -it test
  • :ENV 在构建期就会被解析并持久化,可以通过 docker inspect image 查看。
[root@dk ~]# vim Dockerfile
# ENV 的坑
FROM alpine
LABEL maintainer="姚哥"
ENV msg1="hello"
ENV msg2=${msg1}
RUN echo ${msg1}
RUN echo ${msg2}
# 如果运行期修改了 msg1=666,那么 msg1 和 msg2 的值是 666 和 hello ,因为 ENV 在构建期就会被解析并持久化。
CMD ["/bin/sh","-c","echo $msg1;echo $msg2;"]

运行:

docker build -t test --force-rm --no-cache .
# 或者
docker run -it -e msg1=666 test
2.3.7 ADD
  • ADD 可以将上下文指定的内容添加(复制)到镜像中,如果是压缩包,ADD 会自动解压;如果是远程 URL ,ADD 会自动下载;但是,ADD 并没有自动下载远程压缩文件并解压的功能

语法:

ADD src dest

注意

    1. src 路径必须在构建的上下文,不能使用 ../../xxx 这种方式,因为 Docker 构建的第一步是将上下文目录(包括子目录)发送给 Docker 的守护进程。
    1. 如果 src 是 URL ,并且 dest 不以 / 结尾,那么就会从 URL 下载文件并将其复制为 dest(名称)。
    1. 如果 src 是 URL ,并且 dest 以 / 结尾,会自动推断出文件的名称(URL 的最后一部分)并保存到 dest(目录)中。
    1. 如果 src 是目录,则将复制目录的整个内容,包括文件系统元数据。

示例1:

[root@dk ~]# vim Dockerfile
FROM alpine
LABEL maintainer="姚哥"
# 如果是远程文件,自动下载
# 如果是压缩文件,自动解压
# 注意:ADD 并没有自动下载远程压缩文件并解压的功能
# 将当前内容复制到 alpine 中,并且以dest为文件名称
ADD https://download.redis.io/releases/redis-6.2.6.tar.gz /dest
# 注意,RUN 指令上下并没有上下文的关系。
RUN ls -l

运行:

docker build -t test --force-rm --no-cache .

示例2:

[root@dk ~]# vim Dockerfile
FROM alpine
LABEL maintainer="姚哥"
# 如果是远程文件,自动下载
# 如果是压缩文件,自动解压
# 注意:ADD 并没有自动下载远程压缩文件并解压的功能
ADD https://download.redis.io/releases/redis-6.2.6.tar.gz /dest/
# 注意,RUN 指令上下并没有上下文的关系。
RUN ls -l
RUN cd /dest && ls -l

运行:

docker build -t test --force-rm --no-cache .

示例3:

# 先下载文件,放入与Dockerfile同目录下
[root@dk ~]# wget https://download.redis.io/releases/redis-6.2.6.tar.gz
[root@dk ~]# vim Dockerfile
FROM alpine
LABEL maintainer="姚哥"
# 如果是远程文件,自动下载
# 如果是压缩文件,自动解压
# 注意:ADD 并没有自动下载远程压缩文件并解压的功能
ADD redis-6.2.6.tar.gz /dest/
# 注意,RUN 指令上下并没有上下文的关系。
RUN ls -l
RUN cd /dest && ls -l

运行:

docker build -t test --force-rm --no-cache .
2.3.8 COPY

语法:

COPY [--chown=<user>:<group>] <src>... <dest>
COPY [--chown=<user>:<group>] ["<src>",... "<dest>"]
# <src源路径>:源文件或者源目录
# <dest目标路径>:容器内的指定路径,该路径不用事先建好,路径不存在的话,会自动创建。
  • COPY 和 ADD 类似,都有将上下文指定的内容添加(复制)到镜像中的功能,只不过 ADD 有自动下载或解压压缩文件的功能。

  • --chown 功能仅在用于构建 Linux 容器的 Dockerfile 上受支持,而在 Windows 容器上不起作用。

案例:省略,根据自己的理解复制文件到容器内;

2.3.9 USER

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

语法:

USER <user>[:<group>]
USER <UID>[:<GID>]
  • USER 指令和 WORKDIR 指令类似,都是改变环境状态并影响以后的层,WORKDIR 是改变工作目录,USER 则是改变之后层的执行 RUN 、CMD 、以及 ENTRYPOINT 这类命令的身份。

  • 注意:USER 只是帮助我们切换到指定的用户而已,这个用户必须事先建立好的,否则无法切换。

示例:

[root@dk ~]# touch user.txt
[root@dk ~]# vim Dockerfile
FROM alpine
LABEL maintainer="姚哥"
# 创建用户和组
RUN addgroup -S test && adduser -S test -G test -h /home/test
# USER 只是帮准我们切换到指定的用户而已,这个用户必须事先建立好的,否则无法切换。USER 则是改变之后层的执行 RUN、CMD、以及 ENTRYPOINT 这类命令的身份。
USER test:test
COPY *.txt /test/
# 注意:一旦声明了 USER 之后,USER 后面的 RUN、CMD、ENTRYPOINT 的身份就是 test ,而 a.txt 是主机生成的,身份是 root ,必然会报错,权限不对。
RUN cd /test && ls -l && echo 1111 > user.txt

运行:

docker build -t test --force-rm --no-cache .

报错信息如下:

/bin/sh: can't create user.txt: Permission denied

示例:使用 USER + COPY 解决上面示例的权限等问题

[root@dk ~]# touch user.txt
[root@dk ~]# vim Dockerfile
FROM alpine
LABEL maintainer="姚哥"
# 创建用户和组
RUN addgroup -S test && adduser -S test -G test -h /home/test
# USER 只是帮准我们切换到指定的用户而已,这个用户必须事先建立好的,否则无法切换。USER 则是改变之后层的执行 RUN、CMD、以及 ENTRYPOINT 这类命令的身份。
USER test:test
# 通过 COPY 指定的 chown 功能改变复制文件的权限
COPY --chown=test:test *.txt /test/
# 注意:一旦声明了 USER 之后,USER 后面的 RUN、CMD、ENTRYPOINT 的身份就是 test ,而 a.txt 是主机生成的,身份是 root ,但是,因为使用了 COPY --chown=test:test ,所以文件的权限是 test
RUN cd /test && ls -l && echo 1111 > user.txt

运行:

docker build -t test --force-rm --no-cache .
2.3.10 WORKDIR

指定在创建容器后,终端默认登陆的进来工作目录,一个落脚点。(类似于cd命令)

语法:

WORKDIR /a/b/c
  • WORKDIR 指令为 Dockerfile 中跟随它后面的 RUN 、CMD 、ENTRYPOINT、 COPY、ADD 指令设置工作目录。

  • WORKDIR 指令可在 Dockerfile 中多次使用。 如果提供了相对路径,则它将相对于上一个 WORKDIR 指令的路径,如:

WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd

# 效果:/a/b/c
  • WORKDIR 指令也可以用在环境变量上,如:
ENV DIRPATH=/path
WORKDIR $DIRPATH/$DIRNAME
RUN pwd

# 效果:/path/$DIRNAME

示例:

[root@dk ~]# touch user.txt
[root@dk ~]# vim Dockerfile
FROM alpine
RUN pwd && ls -l
WORKDIR /app
RUN pwd && ls -l
COPY *.txt ./
RUN ls -l

运行:

docker build -t test --force-rm --no-cache .

执行完进入容器后。能够看到进入了目录/app,并且复制了txt文件到此目录下

2.3.11 VOLUME

语法:

#可以是JSON数组
VOLUME ["/var/log/","/var/db"]

#可以直接写
VOLUME /var/log

#可以空格分割多个
VOLUME /var/log /var/db
  • 注意:用 VOLUME 声明了卷,那么以后对于卷内容的修改会被丢弃,所以,一定要在 volume 声明之前修改内容 。

示例:

[root@dk ~]# vim Dockerfile
FROM alpine
# 挂载 容器指定的文件夹,如果不存在,会自动创建。
# 指定了 VOLUME 指令后,即使启动容器的时候没有指定 -v 参数,也会自动进行匿名卷挂载。
VOLUME [ "/demo","/app" ]
CMD ping www.baidu.com

运行:

# 此命令运行完后,会以匿名卷的方式在容器内创建/demo和/app文件夹,主机映射地址使用inspect查看
docker build -t test --force-rm --no-cache .

示例:用 VOLUME 声明了卷,那么以后对于卷内容的修改会被丢弃,所以,一定要在 volume 声明之前修改内容

[root@dk ~]# vim Dockerfile
FROM alpine
RUN mkdir /demo && mkdir /app
RUN echo 111 > /demo/a.txt
RUN echo 222 > /app/b.txt
# 挂载 容器指定的文件夹,如果不存在,会自动创建。
# 指定了 VOLUME 指令后,即使启动容器的时候没有指定 -v 参数,也会自动进行匿名卷挂载。容器内的 /demo 和 /app ,需要在启动容器的时候,需要使用 -v 参数进行挂载。
# VOLUME 挂载出去的东西,容器改变也不会最终在 docker commit 的时候生效。
# -v 和 VOLUME 挂载出去的目录,主机变,容器里面也会发生变化,但是 
# ① docker commit 提交当前容器的所有变化为镜像,就会丢弃。
# ② VOLUME [ "/demo","/app" ] 容器会自动挂载,在之后对这些目录所操作的变化,也会丢弃
# ③ 挂载仅仅是为了将外边的数据同步到容器里面
# VOLUME 的最佳实践是写在 CMD 或 ENTRYPOINT 前面
VOLUME [ "/demo","/app" ]
# 下面的 2 个 RUN 指令没有生效,因为 VOLUME 指定的挂载目录是固化配置,当执行到 VOLUME 的时候就已经写入到容器中了,即使后面容器怎么变,也不会改变。
RUN echo 333 > /demo/a.txt
RUN echo 444 > /app/b.txt
CMD ping www.baidu.com

运行:

# 运行完后自行做测试
docker build -t test --force-rm --no-cache .
2.3.12 EXPOSE

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

语法:

EXPOSE <port> [<port>/<protocol>...]
EXPOSE [80,443]
EXPOSE 80/tcp
EXPOSE 80/udp
  • EXPOSE 指令通知 Docker 容器在运行的时候在指定的网络端口上进行侦听,可以指定端口是侦听 TCP 还是 UDP ,如果没有指定,默认就是 TCP 。

  • EXPOSE 指令实际上不会发布端口,它充当了构建镜像人员和运行容器人员之间的一种文档,即打算发布那些端口的信息,要在运行容器时映射端口,需要使用 docker run -p xxx:xxxdocker run -P 的命令。

示例:省略

2.3.13 CMD 和 ENTRYPOINT

CMD语法:

# exec 方式, 首选方式
CMD ["executable","param1","param2"]

# 为 ENTRYPOINT 提供默认参数
CMD ["param1","param2"]

# shell 形式
CMD command param1 param2

ENTRYPOINT 的语法:

# exec 方式, 首选方式
ENTRYPOINT ["executable", "param1", "param2"]

# shell 形式
ENTRYPOINT command param1 param2

注意

  • ① 如果 Dockerfile 文件中,使用多个 CMD 或 ENTRYPOINT 作为唯一的入口,即写多个 CMD 或 ENTRYPOINT ,则会产生覆盖现象,只有最后一个生效。

  • ② shell 方式是可以读取环境变量的值的(如:${xxx}),默认情况下,exec 的方式是读取不了环境变量值的,但是 exec 方式的 [“/bin/sh”,”-c”,”xxx”] 等同于 shell 方式,也是可以读取环境变量值。

  • ③ 官方推荐使用 RUN 、CMD 以及 ENTRYPOINT 使用 exec 的方式。

  • ④ 如果既有 CMD 的 exec 方式,又有 ENTRYPOINT 的 exec 方式,那么 CMD 是作为 ENTRYPOINT 的参数的(最佳实践)。

  • ⑤ 使用 docker run -d xxx CMD 命令是可以覆盖 Dockerfile 中的 CMD 指令的,不是覆盖 exec 方式数组中的一个,而是全部。

示例:证明注意 ①

[root@dk ~]# vim Dockerfile
FROM alpine
#  CMD 和 ENTRYPOINT 作为唯一入口,写多个,只有最后一个生效
CMD ping baidu.com
CMD ping bing.com

运行:

docker build -t test --force-rm --no-cache .

# --rm 表示容器退出,自动删除
docker run -it --rm test

示例:证明注意 ④

[root@dk ~]# vim Dockerfile
FROM alpine
# java -jar xxx.jar --spring.profile.active=dev --server.port=8888
# CMD [ "-jar","xxx.jar","--spring.profile.active=dev","--server.port=8888"]
# ENTRYPOINT [ "java" ]
CMD ["baidu.com"]
ENTRYPOINT ["ping"]

运行:

docker build -t test --force-rm --no-cache .

# --rm 表示容器退出,自动删除
docker run -it --rm test

示例:证明注意 ⑤

[root@dk ~]# vim Dockerfile
FROM alpine
# java -jar xxx.jar --spring.profile.active=dev --server.port=8888
# CMD [ "-jar","xxx.jar","--spring.profile.active=dev","--server.port=8888"]
# ENTRYPOINT [ "java" ]
CMD ["baidu.com"]
ENTRYPOINT ["ping"]

运行:

docker build -t test --force-rm --no-cache .
# --rm 表示容器退出,自动删除
docker run -it --rm test bing.com

CMD和RUN命令的区别

CMD是在docker run 时运行。RUN是在 docker build时运行。

ENTRYPOINT:也是用来指定一个容器启动时要运行的命令,类似于 CMD 指令,但是ENTRYPOINT不会被docker run后面的命令覆盖, 而且这些命令行参数会被当作参数送给 ENTRYPOINT 指令指定的程序

命令格式:ENTRYPOINT ["<executeable>","param1","param2",......]

ENTRYPOINT可以和CMD一起用,一般是变参才会使用 CMD ,这里的 CMD 等于是在给 ENTRYPOINT 传参。

当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合会变成:

<ENTRYPOINT> <CMD>

案例如下:假设已通过 Dockerfile 构建了 nginx:test 镜像:

FROM nginx
ENTRYPOINT ["nginx","-c"] # 定参
CMD ["/etc/nginx/nginx.conf"] #变参
是否传参 按照dockerfile编写执行 传参运行
Docker命令 docker run nginx:test docker run nginx:test -c /etc/nginx/new.conf
衍生出的实际命令 nginx -c /etc/nginx/nginx.conf nginx -c /etc/nginx/new.conf

**优点:**在执行docker run的时候可以指定 ENTRYPOINT 运行所需的参数。

**注意:**如果 Dockerfile 中如果存在多个 ENTRYPOINT 指令,仅最后一个生效。

2.4 案例

2.4.1 构建Centos7镜像具备vim + ifconfig + jdk8

JDK下载地址:https://www.oracle.com/java/technologies/downloads/#java8

或者也可以安装之前我给你们的jdk-8u333-linux-x64.tar.gz,这里演示案例为8u333

第1步:创建文件夹/mywork,在此目录下创建文件Dockerfile

[root@dk ~]# mkdir /myfile
[root@dk ~]# touch /myfile/Dockerfile
[root@dk ~]# ll /myfile
总用量 144540
-rw-r--r-- 1 root root       791 11月 01 22:14 Dockerfile
-rw-r--r-- 1 root root 148003421 6月  10 07:52 jdk-8u333-linux-x64.tar.gz

第2步:编辑Dockerfile文件,文件内容如下:

注意:如果默认安装的是centos8,则会报一下错误:

Error: Failed to download metadata for repo 'appstream': Cannot prepare internal mirrorlist: No URLs in mirrorlist

解决方法1:将系统改为centos7,如FROM centos:7

解决方法2:百度、谷歌

[root@dk myfile]# cat Dockerfile 
FROM centos:7
MAINTAINER yaochuang<2788459@qq.com>
 
ENV MYPATH /usr/local
WORKDIR $MYPATH
 
# 安装vim编辑器
RUN yum -y install vim
# 安装ifconfig命令查看网络IP
RUN yum -y install net-tools
# 安装java8及lib库,类似于gcc依赖包,不安装会有问题
RUN yum -y install glibc.i686
RUN mkdir /usr/local/java
# ADD 是相对路径jar,把jdk-8u333-linux-x64.tar.gz添加到容器中,安装包必须要和Dockerfile文件在同一位置
ADD jdk-8u333-linux-x64.tar.gz /usr/local/java/
# 配置java环境变量
ENV JAVA_HOME /usr/local/java/jdk1.8.0_333
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
 
 
CMD echo $MYPATH
CMD echo "安装成功......"
CMD /bin/bash

第3步:执行安装命令,注意,myos:1.5后面有个空格,空格后有个点

# docker build --no-cache --force-rm -t 镜像名称:TAG .
[root@dk myfile]# docker build -t myos:1.5 .

说明:

  • --no-cache : 表示构建的时候不使用之前的缓存。

  • --force-rm:删除构建过程中的中间容器层。

  • .:表示构建环境的上下文,通常而言是 . ,表示以 Dockerfile 所在的目录作为构建的起点。

安装成功,结果如下:

第4步:测试

[root@dk ~]# docker run -it 93c5a653993f /bin/bash
2.4.2 Dockerfile构建Tomcat

接下来,我们通过构建一个Tomcat镜像,来演示Dockerfile的使用方法

步骤①:准备jdk.tar.gz和tomcat.tar.gz两个文件

步骤②:编写Dockerfile文件

[root@dk df]# vim Dockerfile
#config file start#
FROM centos
LABEL maintainer="yaoge"

#添加并解压压缩包
ADD jdk-8u144-linux-x64.tar.gz /usr/local/
ADD apache-tomcat-8.5.31.tar.gz /usr/local/
ADD index.jsp /usr/local/apache-tomcat-8.5.31/webapps/ROOT/

#配置环境变量
ENV JAVA_HOME /usr/local/jdk1.8.0_144
ENV CLASSPATH $JAVA_HOME/lib/dt.jar:$JAVA_HOME/lib/tools.jar
ENV CATALINA_HOME /usr/local/apache-tomcat-8.5.31/
ENV PATH $PATH:$JAVA_HOME/bin:$CATALINA_HOME/bin

#设置tomcat监听端口
EXPOSE 8080

#设置运行时执行的启动命令
CMD /usr/local/apache-tomcat-8.5.31/bin/catalina.sh run

步骤③:构建镜像

docker build -t tomcat-web .

步骤④:运行容器测试

2.5. 虚悬镜像

仓库名、标签都是<none>的镜像,俗称dangling image。通俗点讲就是在删除镜像时出了点错误导致删除失败,这时导致镜像仓库名、标签等都是<none>

案例:

1.新建一个Dockerfile文件

[root@dk ~]# cat Dockerfile
from ubuntu
CMD echo 'action is success'

2.docker build

3.查看

[root@dk ~]# docker image ls -f dangling=true

4.删除,虚悬镜像已经失去存在价值,可以删除

[root@dk ~]# docker image prune

2.6 作业

使用Dockerfile构建myubuntu

2.7 镜像瘦身

  1. 选择最小的基础镜像。
  2. 合并 RUN 环节的所有指令,少生成一些镜像层。
  3. RUN 期间可能安装其它程序会生成临时缓存,要自行删除,如:
# 开发期间,逐层验证正确的
RUN xxx
RUN xxx
RUN aaa \
aaa \
vvv \
# 生产环境
RUN apt-get update && apt-get install -y \
	bzr \
	cvs \
	git \
	mercurial \
	subversion \
	&& rm -rf /var/lib/apt/lists/*
  1. 使用 .dockerignore 文件,排除上下文中无需参与构建的资源。

  2. 合理使用多阶段构建。

  3. 合理使用构建缓存加速构建,但是有时也会有坑,开发的时候建议还是 docker build -t xxx --no-cache --force-rm . 来构建镜像。

2.8 总结

回顾UnionFS(联合文件系统)

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

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

3. Docker微服务实战(此章节不学)

docker微服务指的是使用一套小服务来开发单个应用的方式;每个服务运行在独立的进程中,一般采用轻量级的通讯机制互联,并且他们可以通过自动化的方式部署;微服务思想是将传统的单体系统按照业务拆分成多个职责单一并且可独立运行的接口服务。

3.1 通过IDEA新建一个普通微服务模块

  • 建Module:docker_boot

  • 改POM,内容如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/>
    </parent>

    <groupId>com.atguigu.docker</groupId>
    <artifactId>docker_boot</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <junit.version>4.12</junit.version>
        <log4j.version>1.2.17</log4j.version>
        <lombok.version>1.16.18</lombok.version>
        <mysql.version>5.1.47</mysql.version>
        <druid.version>1.1.16</druid.version>
        <mapper.version>4.1.5</mapper.version>
        <mybatis.spring.boot.version>1.3.0</mybatis.spring.boot.version>
    </properties>

    <dependencies>
        <!--SpringBoot通用依赖模块-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <!--test-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>3.1.0</version>
            </plugin>
        </plugins>
    </build>

</project>
  • 写YML
server.port=6001
  • 主启动
package com.atguigu.docker;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DockerBootApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(DockerBootApplication.class, args);
    }

}
  • 业务类
package com.atguigu.docker.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

import java.util.UUID;

/**
 * @auther zzyy
 * @create 2021-10-25 17:43
 */
@RestController
public class OrderController
{
    @Value("${server.port}")
    private String port;

    @RequestMapping("/order/docker")
    public String helloDocker()
    {
        return "hello docker"+"\t"+port+"\t"+ UUID.randomUUID().toString();
    }

    @RequestMapping(value ="/order/index",method = RequestMethod.GET)
    public String index()
    {
        return "服务端口号: "+"\t"+port+"\t"+UUID.randomUUID().toString();
    }
}

3.2 通过dockerfile发布微服务部署到docker容器

  • IDEA工具里面搞定微服务jar包

docker_boot-编写Dockerfile0.0.1-SNAPSHOT.jar

  • 编写Dockerfile
# 基础镜像使用java
FROM java:8
# 作者
MAINTAINER yao
# VOLUME 指定临时文件目录为/tmp,在主机/var/lib/docker目录下创建了一个临时文件并链接到容器的/tmp
VOLUME /tmp
# 将jar包添加到容器中并更名为yao_docker.jar
ADD docker_boot-0.0.1-SNAPSHOT.jar yao_docker.jar
# 运行jar包
RUN bash -c 'touch /yao_docker.jar'
ENTRYPOINT ["java","-jar","/yao_docker.jar"]
# 暴露6001端口作为微服务
EXPOSE 6001
  • 将微服务jar包和Dockerfile文件上传到同一个目录下/mydocker

将docker_boot-0.0.1-SNAPSHOT.jar和Dockerfile文件放在同一个目录下,然后执行

[root@dk ~]# docker build -t yao_docker:1.6 .
  • 运行容器
[root@dk ~]# docker run -d -p 6001:6001 yao_docker:1.6
  • 访问测试

http://地址:6001/order/docker

4. Docker网络

# 先安装ifconfig命令
[root@dk ~]# yum install net-tools

4.1 是什么

docker不启动,默认网络情况

桥接、NAT、仅主机、用户自定义。这里就不重复地说这些有啥区别了,但是咱们这节要说的docker网络模式与之有一定的相似之处。

  • Docker不启动时默认的网络情况
[root@dk ~]# docker images
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
# 先关闭服务
[root@dk ~]# ifconfig
ens33: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500
        inet 192.168.170.151  netmask 255.255.255.0  broadcast 192.168.170.255
        inet6 fe80::deb2:feb0:9aa9:1e4d  prefixlen 64  scopeid 0x20<link>
        ether 00:0c:29:f1:1f:ed  txqueuelen 1000  (Ethernet)
        RX packets 150627  bytes 118946590 (113.4 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 102470  bytes 18277759 (17.4 MiB)
        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
        inet6 ::1  prefixlen 128  scopeid 0x10<host>
        loop  txqueuelen 1000  (Local Loopback)
        RX packets 1113  bytes 97944 (95.6 KiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 1113  bytes 97944 (95.6 KiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0

ens33不用多说了,lo为回环。这里可能还会有一个virbr0,在CentOS7的安装过程中如果有选择相关虚拟化的的服务安装系统后,启动网卡时会发现有一个以网桥连接的私网地址的virbr0网卡(virbr0网卡:它还有一个固定的默认IP地址192.168.122.1),是做虚拟机网桥的使用的,其作用是为连接其上的虚机网卡提供 NAT访问外网的功能。当然你也可以选择把它删了,命令如下:

[root@dk ~]# yum remove libvirt-libs.x86_64
  • Docker启动时的网络情况
# 这时候docker0这个虚拟网桥就冒出来了。 
[root@dk ~]# ifconfig
docker0: flags=4099<UP,BROADCAST,MULTICAST>  mtu 1500
        inet 172.17.0.1  netmask 255.255.0.0  broadcast 172.17.255.255
        inet6 fe80::42:63ff:fe46:2803  prefixlen 64  scopeid 0x20<link>
        ether 02:42:63:46:28:03  txqueuelen 0  (Ethernet)
        RX packets 6845  bytes 8204943 (7.8 MiB)
        RX errors 0  dropped 0  overruns 0  frame 0
        TX packets 9832  bytes 14104901 (13.4 MiB)
        TX errors 0  dropped 0 overruns 0  carrier 0  collisions 0
  • 查看docker网络模式命令
[root@dk ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
8d40433c88ed   bridge    bridge    local
99c28be75fe0   host      host      local
913260e62b5d   none      null      local

4.2 能做什么

  • 容器间的互联和通信以及端口映射
  • 容器IP变动时可以通过服务名直接进行网络通信而不受到影响

4.3 常用基本命令

  1. asda
[root@dk ~]# docker network --help
Usage:  docker network COMMAND

Manage networks

Commands:
  connect     Connect a container to a network
  create      Create a network
  disconnect  Disconnect a container from a network
  inspect     Display detailed information on one or more networks
  ls          List networks
  prune       Remove all unused networks
  rm          Remove one or more networks

Run 'docker network COMMAND --help' for more information on a command.
  1. 查看网络,这里可以看到有 bridge host none
[root@dk ~]# docker network ls
NETWORK ID     NAME      DRIVER    SCOPE
8d40433c88ed   bridge    bridge    local
99c28be75fe0   host      host      local
913260e62b5d   none      null      local
  1. 显示完整的网络ID
[root@dk ~]# docker network ls --no-trunc
NETWORK ID                                     NAME                      DRIVER    SCOPE
9f8332ff0fffc6a393867d71f12ed003a2762a7ae378   bridge                    bridge    local
d50ebcc074d6dd570bdbec9bf958ffc6c7d6184d4866   cig_default               bridge    local
29aaf2bf1e84d0963445706d2001ab4e6fda4f97314e   composetest_atguigu_net   bridge    local
bebc5090e899abfc864356bc7f4192cd14ba51376143   composetest_default       bridge    local
99c28be75fe0e242f93bb254ab674912794f6b281d2b   host                      host      local
b8d9199e67fe7dc5457e1a427ecc0cb62fffd8d67b37   mywp_default              bridge    local
d16438fc94795de2770878dc2a66ba33c027dbcebda8   mywp_mywordpress          bridge    local
913260e62b5d22b0a2f2e01c1e6ab802c4e63525306a   none                      null      local
  1. 驱动程序过滤器根据驱动程序匹配网络。以下示例将网络与桥驱动程序相匹配
[root@dk ~]# docker network ls --filter driver=bridge
NETWORK ID     NAME                      DRIVER    SCOPE
9f8332ff0fff   bridge                    bridge    local
d50ebcc074d6   cig_default               bridge    local
29aaf2bf1e84   composetest_atguigu_net   bridge    local
bebc5090e899   composetest_default       bridge    local
b8d9199e67fe   mywp_default              bridge    local
d16438fc9479   mywp_mywordpress          bridge    local
  1. 匹配网络ID的全部或部分。需要注意以下自己的ID
[root@dk ~]#  docker network ls --filter id=9f8332ff0fffc6a393867d71f12ed003a2762a7ae378544973eeada72e884921
NETWORK ID     NAME      DRIVER    SCOPE
9f8332ff0fff   bridge    bridge    local
  1. 这里使用不带标题的模板,并输出用冒号分隔所有网络的ID和驱动程序条目:
[root@dk ~]# docker network ls --format "{{.ID}}: {{.Driver}}"
9f8332ff0fff: bridge
d50ebcc074d6: bridge
29aaf2bf1e84: bridge
bebc5090e899: bridge
99c28be75fe0: host
b8d9199e67fe: bridge
d16438fc9479: bridge
913260e62b5d: null
  1. 查看网络源数据
[root@dk ~]# docker network inspect  XXX网络名字
  1. 删除网络
[root@dk ~]# docker network rm XXX网络名字
  1. 案例
# 创建网络
[root@dk ~]# docker network create yao_network
ab0e49bb367fad508703d56a3ebf64e62891e18099fda65979696404cf5956b2
# 查看网络
[root@dk ~]# docker network ls
NETWORK ID     NAME                      DRIVER    SCOPE
9f8332ff0fff   bridge                    bridge    local
d50ebcc074d6   cig_default               bridge    local
29aaf2bf1e84   composetest_atguigu_net   bridge    local
bebc5090e899   composetest_default       bridge    local
99c28be75fe0   host                      host      local
b8d9199e67fe   mywp_default              bridge    local
d16438fc9479   mywp_mywordpress          bridge    local
913260e62b5d   none                      null      local
ab0e49bb367f   yao_network               bridge    local
# 删除网络
[root@dk ~]# docker network rm yao_network
yao_network
# 重新查看网络
[root@dk ~]# docker network ls
NETWORK ID     NAME                      DRIVER    SCOPE
9f8332ff0fff   bridge                    bridge    local
d50ebcc074d6   cig_default               bridge    local
29aaf2bf1e84   composetest_atguigu_net   bridge    local
bebc5090e899   composetest_default       bridge    local
99c28be75fe0   host                      host      local
b8d9199e67fe   mywp_default              bridge    local
d16438fc9479   mywp_mywordpress          bridge    local
913260e62b5d   none                      null      local

4.4 网络模式

网络模式 简介
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指定

容器实例内默认网络IP生产规则:

第1步:先运行两个ubuntu容器实例

[root@dk ~]# docker run -it --name u1 ubuntu bash
[root@dk ~]# docker run -it --name u2 ubuntu bash

如果安装此命令报此错误—>WARNING: IPv4 forwarding is disabled. Networking will not work

# 第一步:在宿主机上执行
[root@dk ~]# echo "net.ipv4.ip_forward=1" >>/usr/lib/sysctl.d/00-system.conf
# 第二步:重启network和docker服务
[root@dk ~]# systemctl restart network && systemctl restart docker

第2步:docker inspect 容器ID 或者 容器名字

[root@dk ~]# docker inspect u1 | tail -n 20

第3步:关闭u2实例,新建u3,查看ip变化

总结:docker容器内部的ip是有可能会发生改变的

  • bridge是什么

Docker 服务默认会创建一个 docker0 网桥(其上有一个 docker0 内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker 默认指定了 docker0 接口 的 IP 地址和子网掩码,让主机和容器之间可以通过网桥相互通信。

# 查看 bridge 网络的详细信息,并通过 grep 获取名称项
[root@dk ~]# docker network inspect bridge | grep name
            "com.docker.network.bridge.name": "docker0",
[root@dk ~]# ifconfig | grep docker
docker0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST>  mtu 1500

说明

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

  2. docker run 的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是docker0。在宿主机ifconfig,就可以看到docker0和自己create的network(后面讲)eth0,eth1,eth2……代表网卡一,网卡二,网卡三……,lo代表127.0.0.1,即localhost,inet addr用来表示网卡的IP地址

  3. 网桥docker0创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。

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

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

代码:

docker run -d -p 8081:8080  --name tomcat81 billygoo/tomcat8-jdk8
docker run -d -p 8082:8080  --name tomcat82 billygoo/tomcat8-jdk8

两两匹配验证:

  • host是什么

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

说明:

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

案例:

[root@dk ~]# docker run -d -p 8083:8080 --network host --name tomcat83 billygoo/tomcat8-jdk8
WARNING: Published ports are discarded when using host network mode
db97ce1707f8d602db16108ee75fc57503a0cf4e4b0b6b8f1b8e2009d67edc88

**问题:**docker启动时总是遇见标题中的警告

**原因:**docker启动时指定--network=host或-net=host,如果还指定了-p映射端口,那这个时候就会有此警告,并且通过-p设置的参数将不会起到任何作用,端口号会以主机端口号为主,重复时则递增。

**解决:**使用docker的其他网络模式,例如--network=bridge,这样就可以解决问题,或者直接无视。

[root@dk ~]# docker run -d --network host --name tomcat83 billygoo/tomcat8-jdk8

无之前的配对显示了,看容器实例内部

没有设置-p的端口映射了,如何访问启动的tomcat83??

http://宿主机IP:8080/

在CentOS里面用默认的火狐浏览器访问容器内的tomcat83看到访问成功,因为此时容器的IP借用主机的,所以容器共享宿主机网络IP,这样的好处是外部主机与容器可以直接通信。

  • none 是什么

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

禁用网络功能,只有lo标识(就是127.0.0.1表示本地回环)

案例:

[root@dk ~]# docker run -d -p 8084:8080 --network none --name tomcat84 billygoo/tomcat8-jdk8
3af3372d8a3140b6436548bf0cb6adb5b9abff3a6b49962b354754497cbc74da
# 进入容器内部查看
[root@dk ~]# docker exec -it tomcat84 bash
root@3af3372d8a31:/usr/local/tomcat# ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
# 在容器外部查看
[root@dk ~]# docker inspect tomcat84 | tail -n 20
  • container 是什么

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

案例1:

[root@dk ~]# docker run -d -p 8085:8080 --name tomcat85 billygoo/tomcat8-jdk8
2cef03f7fe8020d19877708e1ae3ec69d0c9eeeac7cd72f37431e69854add531
[root@dk ~]# docker run -d -p 8086:8080 --network container:tomcat85 --name tomcat86 billygoo/tomcat8-jdk8
docker: Error response from daemon: conflicting options: port publishing and the container type network mode.
See 'docker run --help'.

相当于tomcat86和tomcat85公用同一个ip同一个端口,导致端口冲突。本案例用tomcat演示有坑,换其他镜像。

案例2:

Alpine操作系统是一个面向安全的轻型 Linux发行版,是一款独立的、非商业的通用 Linux 发行版,专为追求安全性、简单性和资源效率的用户而设计。 可能很多人没听说过这个 Linux 发行版本,但是经常用 Docker 的朋友可能都用过,因为他小,简单,安全而著称,所以作为基础镜像是非常好的一个选择,可谓是麻雀虽小但五脏俱全,镜像非常小巧,不到 6M的大小,所以特别适合容器打包。

[root@dk ~]# docker run -it --name alpine1 alpine /bin/sh
[root@dk ~]# docker run -it --network container:alpine1 --name alpine2 alpine /bin/sh

运行结果,验证共用搭桥

假如此时关闭alpine1,再看看alpine2,会发现38: eth0@if39: 消失了

  • 自定义网络

docker官网不建议使用此网络,最终将会被移除,所以这里不做讲解!

4.5 Docker平台架构图解

从其架构和运行流程来看,Docker 是一个 C/S 模式的架构,后端是一个松耦合架构,众多模块各司其职。

Docker 运行的基本流程为:

  1. 用户是使用 Docker Client 与 Docker Daemon 建立通信,并发送请求给后者。
  2. Docker Daemon 作为 Docker 架构中的主体部分,首先提供 Docker Server 的功能使其可以接受 Docker Client 的请求。
  3. Docker Engine 执行 Docker 内部的一系列工作,每一项工作都是以一个 Job 的形式的存在。
  4. Job 的运行过程中,当需要容器镜像时,则从 Docker Registry 中下载镜像,并通过镜像管理驱动 Graph driver将下载镜像以Graph的形式存储。
  5. 当需要为 Docker 创建网络环境时,通过网络管理驱动 Network driver 创建并配置 Docker 容器网络环境。
  6. 当需要限制 Docker 容器运行资源或执行用户指令等操作时,则通过 Execdriver 来完成。
  7. Libcontainer是一项独立的容器管理包,Network driver以及Exec driver都是通过Libcontainer来实现具体对容器进行的操作。

5. Docker-compose容器编排

5.1 是什么

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

Docker-Compose是Docker官方的开源项目, 负责实现对Docker容器集群的快速编排。

5.2 能做什么

docker建议我们每一个容器中只运行一个服务,因为docker容器本身占用资源极少,所以最好是将每个服务单独的分割开来但是这样我们又面临了一个问题?

如果我需要同时部署好多个服务,难道要每个服务单独写Dockerfile然后在构建镜像,构建容器,这样累都累死了,所以docker官方给我们提供了docker-compose多服务部署的工具

例如要实现一个Web微服务项目,除了Web服务容器本身,往往还需要再加上后端的数据库mysql服务容器,redis服务器,注册中心eureka,甚至还包括负载均衡容器等等。

Compose允许用户通过一个单独的docker-compose.yml模板文件(YAML 格式)来定义一组相关联的应用容器为一个项目(project)。

可以很容易地用一个配置文件定义一个多容器的应用,然后使用一条指令安装这个应用的所有依赖,完成构建。Docker-Compose 解决了容器与容器之间如何管理编排的问题。

5.3 下载地址

  • 官网

https://docs.docker.com/compose/compose-file/compose-file-v3/

  • 官网下载

https://docs.docker.com/compose/install/

  • 安装步骤
# 下载链接1
[root@dk ~]# curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
# 下载链接2
[root@dk ~]# curl -L https://get.daocloud.io/docker/compose/releases/download/v2.14.0/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
[root@dk ~]# chmod +x /usr/local/bin/docker-compose
[root@dk ~]# docker-compose --version
  • 卸载
[root@dk ~]# sudo rm /usr/local/bin/docker-compose

5.4 Compose核心概念

一个文件:docker-compose.yml

两个要素:

  1. 服务(service)

一个个应用容器实例,比如订单微服务、库存微服务、mysql容器、nginx容器或者redis容器

  1. 工程(project)

由一组关联的应用容器组成的一个完整业务单元,在 docker-compose.yml 文件中定义。

5.5 Compose使用的三个步骤

  1. 编写Dockerfile定义各个微服务应用并构建出对应的镜像文件
  2. 使用 docker-compose.yml 定义一个完整业务单元,安排好整体应用中的各个容器服务。
  3. 最后,执行docker-compose up命令 来启动并运行整个应用程序,完成一键部署上线

5.6 Compose常用命令

docker-compose -h # 查看帮助

docker-compose up # 启动所有docker-compose服务

docker-compose up -d # 启动所有docker-compose服务并后台运行

docker-compose down # 停止并删除容器、网络、卷、镜像。

docker-compose exec yml里面的服务id # 进入容器实例内部 docker-compose exec docker-compose.yml文件中写的服务id /bin/bash

docker-compose ps # 展示当前docker-compose编排过的运行的所有容器

docker-compose top # 展示当前docker-compose编排过的容器进程

docker-compose logs yml里面的服务id # 查看容器输出日志

docker-compose config # 检查配置

docker-compose config -q # 检查配置,有问题才有输出

docker-compose restart # 重启服务

docker-compose start # 启动服务

docker-compose stop # 停止服务

5.7 Compose编排微服务

忽略,待整理,直接看5.8

5.8 Compose编排案例

什么是YAML

YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。

YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。

YAML 的配置文件后缀为 .yml,如:docker-compose.yml 。

  • 基本语法
  • 大小写敏感
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • #表示注释

案例1:使用Compose部署Tomcat + MySQL

第1步:准备镜像tomcat和mysql,如果已经有此镜像请忽略

[root@dk ~]# docker pull tomcat
[root@dk ~]# docker pull mysql:5.7

第2步:创建文件夹/composetest,并在该目录下创建文件docker-compose.yml。

[root@dk ~]# mkdir /composetest
[root@dk ~]# cd /composetest
[root@dk composetest]# cat docker-compose.yml 
version: '3.1'
services:
  tomcat:
    restart: always
    image: tomcat
    container_name: tomcat
    ports:
      - 8080:8080
    volumes:
      - /usr/local/docker/tomcat/webapps:/usr/local/tomcat/webapps
    environment:
      TZ: Asia/Shanghai
  mysql:
    restart: always
    image: mysql:5.7
    container_name: mysql
    ports:
      - 3306:3306
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 123456
    command:
      --character-set-server=utf8mb4
      --collation-server=utf8mb4_general_ci
      --explicit_defaults_for_timestamp=true
      --lower_case_table_names=1
      --max_allowed_packet=128M
      --sql-mode="STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO"
    volumes:
      - /usr/local/docker/mysql:/var/lib/mysql
[root@dk composetest]# docker-compose up -d
# 如果出现done、done则表示成功!

第3步:访问tomcat地址

注意:此步骤会出现404错误,8.2章节的内容不再适合修改,因为删除webapps时已经有容器数据卷,所以删除webapps文件夹时会报错,应当把webapps.dist里的内容拷贝到webapps目录下。

第4步:检查MySQL是否可以正常登陆

  • 案例2:

第1步:准备镜像mysql5.7和wordpress,不提供代码,代码自行编写。

[root@dk ~]# docker images
REPOSITORY              TAG       IMAGE ID       CREATED         SIZE
nginx                   latest    605c77e624dd   10 months ago   141MB
wordpress               latest    c3c92cc3dcb1   11 months ago   616MB
mysql                   5.7       c20987f18b13   11 months ago   448MB

第2步:创建yml文件

[root@dk mywp]# cat docker-compose.yml 
version: '3.1'           #compose文件版本

services:
   mysql:                   # 服务1:mysql
     image: mysql:5.7    # 使用镜像 mysql:5.7版本
     volumes:
       - /use/local/docker/mysql:/var/lib/mysql   # 数据持久化
     restart: always     # 容器服务宕机后总是重启
     ports:
       - "3307:3306"
     environment:        # 环境配置
       MYSQL_ROOT_PASSWORD: root12345 #数据库root用户密码
       MYSQL_DATABASE: wordpress #wordpress数据库名称
       MYSQL_USER: wordpress      #数据库用户名
       MYSQL_PASSWORD: 123456 #数据库密码

   wordpress:          # 服务2:wordpress
     depends_on:       # wordpress服务启动时依赖mysql服务,所以会自动先启动mysql服务
       - mysql
     image: wordpress:latest    # 使用镜像 wordpress:latest最新版
     ports:
       - "8080:80"          #端口映射8000:80
     restart: always
     environment:        # 环境
       WORDPRESS_DB_HOST: mysql:3306     # wordpress连接db的3306端口
       WORDPRESS_DB_USER: wordpress    # 数据库用户为wordpress
       WORDPRESS_DB_PASSWORD: 123456   # 数据库密码是123456
       WORDPRESS_DB_NAME: wordpress    # 数据库名字是wordpress

第3步:执行

[root@dk mywp]# docker-compose up -d

第4步:访问地址看结果

  • 案例3:docker-compose + Jira

JIRA是Atlassian公司出品的项目与事务跟踪工具,被广泛应用于缺陷跟踪、客户服务、需求收集、流程审批、任务跟踪、项目跟踪和敏捷管理等工作领域。

分为以下x个步骤:

1、先下载两个jar包,放入libs下

2、mysql、jira用于存放配置文件、日志文件、数据文件

3、编写docker-compose.yml文件

4、创建数据库

4、测试运行

[root@dk jira]# tree ./
./
├── docker-compose.yml
├── jira
├── libs
│   ├── atlassian-agent.jar
│   └── mysql-connector-java-5.7.36.jar
└── mysql

3 directories, 3 files

编写docker-compose.yml文件:

[root@dk jira]# cat docker-compose.yml 
version: '3.1'
services:
    mysql:
        container_name: mysql
        image: mysql
        restart: always
        ports:
            - 3308:3306
        environment:
            MYSQL_ROOT_PASSWORD: 123456
        volumes:
            - ./mysql/conf:/etc/mysql/conf.d
            - ./mysql/logs:/var/log/mysql
            - ./mysql/data:/var/lib/mysql
        command:
            --character-set-server=utf8mb4
    jira:
        container_name: jira
        image: atlassian/jira-software:latest
        restart: "no"
        ports:
            - 8080:8080
        environment:
            CATALINA_OPTS: -javaagent:/opt/atlassian/jira/atlassian-agent.jar
        volumes:
            - ./jira/data:/var/atlassian/application-data/jira
            - ./libs/atlassian-agent.jar:/opt/atlassian/jira/atlassian-agent.jar
            - ./libs/mysql-connector-java-8.0.27.jar:/opt/atlassian/jira/lib/mysql-connector-java.jar

启动容器:

[root@dk jira]# docker-compose up -d

创建数据库:

CREATE DATABASE jiradb;
create user 'jira'@'%' identified by 'jira';
grant all privileges on jiradb.* to 'jira'@'%';
flush privileges;

设置Jira:

在浏览器上打开:http://IP:8080

1、点击右上角Language,设置中文

2、选择我将设置它自己,点击下一步

3、数据库设置,选择其它数据库,根据文本提示填入相应内容,点击测试连接,通过后点击下一步

4、设置应用程序的属性,采用默认值就行,直接点击下一步

5、设置许可证,如果本地有Java环境,可以在本地生成许可证,或者进入jira容器生成

执行命令,带上服务器ID,服务器ID写自己电脑上的,生成许可证:

root@61bf39400f8a:/opt/atlassian/jira# java -jar atlassian-agent.jar -d -m test@test.com -n BAT -p jira -o lewis2951 -s 服务器ID

6、设置管理员账户,根据自己的需求设置,设置Email地址

7、设置电子邮件通知,可以选择以后再说

8、完成部署

9、查看许可证

  • 案例4:使用Docker-Compose部署nginx代理Tomcat集群,实现负载均衡

大体步骤分为以下四步

1、下载所需的文件tomcat,jdk 2、编写dockerfile来布署tomcat与java环境,生成镜像文件 3、编写docker-compose.yml配置文件,启动所有容器服务 4、测试负载均衡

[root@localhost java]# tree ./
./
├── docker-compose.yml
├── nginx
│   └── nginx.conf
├── tomcat
│   ├── apache-tomcat-8.5.79.tar.gz
│   ├── Dockerfile
│   └── jdk-8u333-linux-x64.tar.gz
└── webserver
    ├── tomcatA
    │   └── index.jsp
    └── tomcatB
        └── index.jsp

5 directories, 7 files
[root@localhost java]# cat webserver/tomcatA/index.jsp
welcome to Tomcat-A server
[root@localhost java]# cat webserver/tomcatB/index.jsp
welcome to Tomcat-B server
# 编写docker-compose.yml文件
[root@master java]# cat docker-compose.yml 
version: "3"
services:
  nginx:
    image: nginx:1.14
    restart: always
    ports:
      - 80:80
    links:
      - tomcat1:tomcat1
      - tomcat2:tomcat2
    volumes:
      - ./webserver:/webserver
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
    environment:
      TZ: Asia/Shanghai
    depends_on:
      - tomcat1
      - tomcat2
  tomcat1:
    hostname: tomcat1
    build: ./tomcat
    volumes:
      - ./webserver/tomcatA:/usr/local/apache-tomcat-8.5.79/webapps/ROOT
    environment:
      TZ: Asia/Shanghai
  tomcat2:
    hostname: tomcat2
    build: ./tomcat
    volumes:
      - ./webserver/tomcatB:/usr/local/apache-tomcat-8.5.79/webapps/ROOT
    environment:
      TZ: Asia/Shanghai

#安装JAVA环境
[root@localhost java]# cat tomcat/Dockerfile 
FROM centos
ADD jdk-8u333-linux-x64.tar.gz /usr/local
ENV JAVA_HOME /usr/local/jdk1.8.0_333
ADD apache-tomcat-8.5.79.tar.gz /usr/local
EXPOSE 8080
ENTRYPOINT ["/usr/local/apache-tomcat-8.5.31/bin/catalina.sh", "run"]

编写nginx配置文件

[root@localhost java]# cat ./nginx/nginx.conf 
user  nginx;
worker_processes  1;
error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;
events {
    worker_connections  1024;
}

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';
    access_log  /var/log/nginx/access.log  main;
    client_max_body_size 10m;
    sendfile        on;
    #tcp_nopush     on;
    keepalive_timeout  65;
    #gzip  on;

upstream tomcat_client {
         server tomcat1:8080 weight=1;
         server tomcat2:8080 weight=1;
    }
    
    server {
        server_name "";
        listen 80 default_server;
        listen [::]:80 default_server ipv6only=on;
        location / {
            proxy_pass http://tomcat_client;
            proxy_redirect default;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
        }
    }
}

启动所有容器服务

[root@localhost java]# docker-compose up -d

查看启动情况

[root@localhost java]# docker-compose ps

检测负载均衡

[root@localhost java]# curl http://localhost
welcome to tomcat-A server
[root@localhost java]# curl http://localhost
welcome to tomcat-B server
[root@localhost java]# curl http://localhost
welcome to tomcat-A server
[root@localhost java]# curl http://localhost
welcome to tomcat-B server

浏览器访问测试负载均衡

在浏览器上输入服务器地址就可以测试了,多刷新几次试试!!

6. Docker轻量级可视化工具Portainer

6.1 是什么

Portainer 是一款轻量级的应用,它提供了图形化界面,用于方便地管理Docker环境,包括单机环境和集群环境。

6.2 安装

  • 官网

https://www.portainer.io/

https://docs.portainer.io/v/ce-2.9/start/install/server/docker/linux

  • docker命令安装
[root@dk ~]# 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
  • 登录演示

第一次登录需创建admin,访问地址:xxx.xxx.xxx.xxx:9000

设置admin用户和密码后首次登陆(设置密码为12345678,以防忘记)

选择local选项卡后本地docker详细信息展示

上一步的图形展示,能想得起对应命令吗?

[root@dk ~]# docker system df
TYPE            TOTAL     ACTIVE    SIZE      RECLAIMABLE
Images          10        10        3.185GB   77.79MB (2%)
Containers      49        1         202.7MB   202.7MB (100%)
Local Volumes   13        9         1.094GB   828MB (75%)
Build Cache     0         0         0B        0B

6.3 常用操作

点击镜像看效果,这里的删除、新建、导入导出都很明确。

咱们再回去看看刚刚首页上的Stack(左侧列表中):

可以看到这里是用来存放咱们compose编排的界面,继续点开看看:

常规操作

# 先docker ps看一眼有什么正在运行:
[root@dk ~]# docker ps
CONTAINER ID   IMAGE                 COMMAND                  CREATED          STATUS          PORTS                                                                                  NAMES
13fbca891039   mysql:5.7             "docker-entrypoint.s…"   6 seconds ago    Up 4 seconds    3306/tcp, 33060/tcp                                                                    serene_gauss
9dc6e689c519   portainer/portainer   "/portainer"             27 minutes ago   Up 26 minutes   0.0.0.0:8000->8000/tcp, :::8000->8000/tcp, 0.0.0.0:9000->9000/tcp, :::9000->9000/tcp   portainer

演示Logs操作、演示监控信息、演示命令行(Exec Console)

添加一个容器实例,内容随便填写一下发布即可。

还可以配置容器数据卷

7. Docker容器监控值CAdvisor+InfluxDB+Granfana

7.1 原生命令

通过docker stats命令可以很方便的看到当前宿主机上所有容器的CPU,内存以及网络流量等数据。docker stats统计结果只能是当前宿主机的全部容器,数据资料是实时的,没有地方存储、没有健康指标过线预警等功能。

7.2 是什么

容器监控3剑客:CAdvisor监控收集+InfluxDB存储数据+Granfana展示图表

  • CAdvisor监控收集

CAdvisor是一个非常好用的容器监控工具,还可以和Prometheus整合,安装和使用都很简单。CAdvisor是一个容器资源监控工具,包括容器的内存CPU,网络IO,磁盘I0等监控,同时提供了一个WEB页面用于查看容器的实时运行状态。CAdvisor默认存储2分钟的数据, 而且只是针对单物理机。不过,CAdvisor提供了很多数据集成接口,支持InfluxDB,Redis,Kafka,Elasticsearch等集成,可以加上对应配置将监控数据发往这些数据库存储起来。

CAdvisor功能主要有两点:

  1. 展示Host和容器两个层次的监控数据
  2. 展示历史变化数据
  • InfluxDB存储数据

InfluxDB是用Go语言编写的一个开源分布式时序、事件和指标数据库,无序外部依赖。

CADvisor默认只在本机保存最近2分钟的数据,为了持久化存储数据和统一收集展示监控数据,需要将数据存储到InfluxDB中。InfluxDB是一个时序数据库,专门用于存储时序相关数据,很适合存储CADvisor的数据,而且,CADvisor本身已经提供了InfluxDB的集成方法,启动容器时指定配置即可。

主要功能:

  1. 基于时间序列,支持与时间有关的相关函数(如最大、最小、求和等)
  2. 可度量性:你可以实时对大量数据进行计算
  3. 基于事件:它支持任意的事件数据
  • Granfana展示图表

Grafana是一个开源的数据监控分析可视化平台,支持多种数据源配置和丰富的插件及模板功能,支持图表权限控制和报警。

主要功能:

  1. 灵活丰富的图形化选项
  2. 可以混合多种风格
  3. 支持白天和夜间模式
  4. 多个数据源

7.3 Compose容器编排

第1步:新建目录

[root@dk ~]# mkdir /cig && cd /cig

第2步:新建docker-compose.yml文件,内容如下:

version: '3.1'

volumes:
  grafana_data: {}

services:
 influxdb:
  image: tutum/influxdb:0.9
  restart: always
  environment:
    - PRE_CREATE_DB=cadvisor
  ports:
    - "8083:8083"
    - "8086:8086"
  volumes:
    - ./data/influxdb:/data

 cadvisor:
  image: google/cadvisor
  links:
    - influxdb:influxsrv
  command: -storage_driver=influxdb -storage_driver_db=cadvisor -storage_driver_host=influxsrv:8086
  restart: always
  ports:
    - "8080:8080"
  volumes:
    - /:/rootfs:ro
    - /var/run:/var/run:rw
    - /sys:/sys:ro
    - /var/lib/docker/:/var/lib/docker:ro
    
 grafana:
  user: "104"
  image: grafana/grafana
  restart: always
  links:
    - influxdb:influxsrv
  ports:
    - "3000:3000"
  volumes:
    - grafana_data:/var/lib/grafana
  environment:
    - HTTP_USER=admin
    - HTTP_PASS=admin
    - INFLUXDB_HOST=influxsrv
    - INFLUXDB_PORT=8086
    - INFLUXDB_NAME=cadvisor
    - INFLUXDB_USER=root
    - INFLUXDB_PASS=root

第3步:输入网址测试,第一次加载较慢

http://192.168.170.151:8080/containers/

第4步:

cadvisor也有基础的图形展现功能,这里主要用它来作数据采集

ip+3000端口的方式访问,默认帐户密码(admin/admin)

第5步:配置数据源—>选择influxdb数据源

点击Save & test,出现Data source is working则表示成功!

第6步:配置面板panel

到这里CAdvisor + InfluxDB + Grafana容器监控系统就部署完成了

8. Windows安装Docker

9. 项目实战

9.1 OA办公系统

步骤1:拷贝o2server.zip文件,或者在Linux里使用wget命令下载压缩包,下载地址为:

https://download.o2oa.net/download/o2server-7.2.6-linux-x64.zip

步骤2:使用unzip命令解压安装包

步骤3:制作Dockerfile文件

  • FROM为基础镜像名称,镜像中包括Tomcat、JDK、Linux操作系统
  • PaaS平台不允许使用root用户启动应用,所以新建新用户 yaoge
  • 将解压后的o2server拷贝到容器中
  • 为新建的用户yaoge赋予o2server目录的管理权限,能够读、写、执行权限。
  • 容器启动的时候将用户切换为 yaoge(1000为yaoge的用户编号,测试PaaS平台要求使用"user 用户编号"方式切换用户)
  • 容器启动后的ENTRYPOINT为执行启动o2oa的脚本 start_linux.sh
FROM tomcat:8.5
RUN cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo 'Asiz/Shanghai' > /etc/timezone
RUN groupadd yaoge
RUN useradd yaoge -g yaoge -u 1000
RUN mkdir -p /data/o2server
COPY o2server /data/o2server/
RUN chown -R yaoge:yaoge /data
ENTRYPOINT ["/bin/sh","-c","/data/o2server/start_linux.sh && tailf /etc/localtime"]

步骤4:docker build 制作镜像,镜像名为oa

步骤5:镜像制作完毕后将镜像存储为tar包进行保存,gzip没有请先安装,保存命令:

docker save oa | gzip > oa.tar

步骤6:docker run检查镜像是否能够正常启动

8. 其他问题

8.1 停止警告

执行 systemctl stop docker 后提示“Warning: Stopping docker.service, but it can still be activated by: docker.socket”

解释: 这是docker在关闭状态下被访问自动唤醒机制,很人性化,即这时再执行任意docker命令会直接启动

注:如果真的不希望docker被访问自动唤醒,执行 systemctl stop docker 后再执行systemctl stop docker.socket即可

8.2 Docker报错解决

  • 【解决】Got permission denied while trying to connect to the Docker daemon socket at......dial unix /var/run/docker.sock: permission denied

https://www.cnblogs.com/leozhanggg/p/11730189.html

  • 【解决】error pulling image configuration: Get https:// ...... x509: certificate has expired or is not yet valid

https://www.cnblogs.com/leozhanggg/p/12031433.html

  • 【解决】image ... could not be accessed on a registry to record its digest.

https://www.cnblogs.com/leozhanggg/p/12023531.html

  • 【解决】http: server gave HTTP response to HTTPS client

https://www.cnblogs.com/leozhanggg/p/12023456.html

  • 【解决】OCI runtime exec failed......executable file not found in $PATH": unknown

https://www.cnblogs.com/leozhanggg/

  • 【解决】 WARNING: IPv4 forwarding is disabled. Networking will not work.
echo "net.ipv4.ip_forward=1" >>/usr/lib/sysctl.d/00-system.conf
# 重启网卡、docker
systemctl restart network && systemctl restart docker

9. 最终总结

加 油!

空文件

简介

个人整理的Docker笔记!!! 希望可以帮助到你 展开 收起
Docker
取消

发行版

暂无发行版

贡献者

全部

近期动态

加载更多
不能加载更多了
Docker
1
https://gitee.com/zhangzhihuai/docker.git
git@gitee.com:zhangzhihuai/docker.git
zhangzhihuai
docker
Docker
master

搜索帮助