WHCSRL 技术网

Docker从入门到初步掌握

提纲:Docker和Kubernetes关系

zheg

本人开始今天分享之前想提以前简单的问题,大家可以试试回答。Kubernetes和Docker关系?

Kubernetes的作用是管理容器,docker一旦变多,重启,容器通信等一系列过程就需要一个工具来进行管理,这就是Kubernetes做的事情

在这里插入图片描述

K8S 调度的基本单位为 pod(一个POD表示一个或多个容器),为什么基本单位不是一个容器?

Docker概述

Docker是什么?

​ Docker 是一个开源项目,诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目。它基于 Google 公司推出的Go语言实现。项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在GitHub 上进行维护。

​ Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。Docker 的基础是 Linux 容器(LXC)等技术,在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。

既然是都是虚拟化技术, 为什么不直接使用虚拟机?

序号Docker虚拟机
1占用资源少资源占用多
2操作简单冗余步骤多
3启动快启动慢

备注:区分容器和虚拟机概念(容器起源自Linux Containers(LXC)一种新的虚拟化技术)

  • 容器内应用进程直接运行于宿主内核是对进程进行隔离的技术、包含完整文件系统,并区分计算资源、自身无内核、无需硬件虚拟。

  • 虚拟机在虚拟出一套硬件后在其基础上搭建套操作系统,后运行软件。

(补充)为什么Docker比较 VM 快?

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

2 、docker利用的是宿主机的内核,而不需要Guest OS。因此,当新建一个容器时,docker不需要和虚拟机一样重新加载一个操作系统内核。仍而避免引寻、加载操作系统内核返个比较费时费资源的过程,当新建一个虚拟机时,虚拟机软件需要加载Guest OS,返个新建过程是分钟级别的。而docker由于直接利用宿主机的操作系统,则省略了返个过程,因此新建一个docker容器只需要几秒钟。

容器化技术给开发/运维(DevOps)/部署能带来哪些便捷?

序号维度Docker开发/运维传统开发/运维
1应用交付和部署少量容器镜像文件(配置文件内置)复杂的安装程序和配置说明文档
2升级、迁移、扩缩容通过镜像运行新的容器进行快速扩容重新部署上线
3系统运维生产、开发、测试环境高度一致;
配置文件封装;
测试快速定位、修复;
更快速的交付和部署;
更简单的管理;
更快速的启动时间
生产、开发、测试环境高不一致;
管理、交付、部署难度大;
虚拟机启动时间慢
4资源利用内核级虚拟化;
一台物理机上可以运行很多个容器实例;
提升物理服务器的CPU和内存的利用率;
传统的虚拟化技术需要额外Hypervisor

Docker是怎么工作的?

Docker是一个Client-Server结构的系统,Docker守护进程运行在主机上, 然后通过Socket连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。 容器,是一个运行时环境,就是我们 前面说到的集装箱。

备注:

  • Docker官网:http://www.docker.com
  • Docker中文网站:https://www.docker-cn.com
  • Docker Hub官网:https://hub.docker.com (仓库)

Docker安装

Docker组成

在这里插入图片描述

备注:区分镜像和容器

  • 镜像(image):Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。 就好似 Java 中的 类和对象,类就是镜像,容器就是对象!

  • 容器(container):Docker 利用容器(Container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。 它可以被启动、开始、停止、删除。每个容器都是相互隔离的,保证安全的平台。可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等) 和运行在其中的应用程序。

  • 仓库(repository):仓库(Repository)是集中存放镜像文件的场所。 仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。 仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是 Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。 国内的公开仓库包括阿里云 、网易云等

补充:容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。

Docker安装

官网安装参考手册:https://docs.docker.com/engine/install/centos/

1、yum安装gcc相关环境(需要确保 虚拟机可以上外网 )

yum -y install gcc
yum -y install gcc-c++
  • 1
  • 2

2 、卸载旧版本

yum remove docker 
    docker-client 
    docker-client-latest 
    docker-common 
    docker-latest 
    docker-latest-logrotate 
    docker-logrotate 
    docker-engine
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

3、安装需要的YUM软件包

yum install -y yum-utils
  • 1

4、设置镜像仓库

# 正确推荐使用国内的
yum-config-manager --add-repo http://mirrors.aliyun.com/docker- ce/linux/centos/docker-ce.repo
  • 1
  • 2

5、更新yum软件包索引

yum makecache fast
  • 1

6、安装 Docker CE

yum install docker-ce docker-ce-cli containerd.io
  • 1

7、启动Docker

systemctl start docker
  • 1

8、卸载Docker

systemctl stop docker
yum -y remove docker-ce docker-ce-cli containerd.io
rm -rf /var/lib/docker
  • 1
  • 2
  • 3

Docker常用命令

基本命令

docker version 				# 显示 Docker 版本信息。
docker info  				# 显示 Docker 系统信息,包括镜像和容器数。。
docker --help 				# 帮助
docker images 				# 列出本地主机上的镜像
# 备注:同一个仓库源可以有多个 TAG,代表这个仓库源的不同版本,我们使用REPOSITORY:TAG 定义不同 的镜像,如果你不定义镜像的标签版本,docker将默认使用 lastest 镜像!

docker search mysql 		# 搜索镜像
docker pull mysql/mysql:8.0			# 下载镜像
docker pull centos					 # 下载容器

docker rmi -f 镜像id					 # 删除单个
docker rmi -f 镜像名:tag 镜像名:tag 	 # 删除多个 
docker rmi -f $(docker images -qa)   # 删除全部


docker run -it centos /bin/bash		# 新建容器并启动
docker ps							  # 列出所有运行的容器

docker start (容器id or 容器名) 		# 启动容器
docker restart (容器id or 容器名)	# 重启容器
docker stop (容器id or 容器名) 		# 停止容器
docker kill (容器id or 容器名)		# 强制停止容器

docker rm 容器id 						# 删除指定容器 
docker rm -f $(docker ps -a -q) 	# 删除所有容器 
docker ps -a -q|xargs docker rm 	# 删除所有容器

# 从容器内拷贝文件到主机上
docker cp 容器id:容器内路径 目的主机路径

docker commit 提交容器副本使之成为一个新的镜像!
# 语法
docker commit -m="提交的描述信息" -a="作者" 容器id 要创建的目标镜像名:[标签名]

# 例子
docker run -d centos 	# 启动centos,使用后台方式启动
docker inspect 容器id

# 语法:
# -d 	后台运行 
# -p 	端口 
# -v 	容器数据卷
# -i	以交互模式运行容器,通常与 -t 同时使用
# -t	为容器重新分配一个伪输入终端,通常与 -i 同时使用
# -d	后台运行容器,并返回容器ID
# -itd 	组合命

# 容器进入:
docker exec -it --user root docker-nginx /bin/bash

# 文件向容器复制:
# 语法:docker cp 容器外路径 容器ID:容器内路径
docker cp 外部文件地址 e7f25fc3bd48:容器内文件地址

# Nginx:
docker run -d -p 80:80 -v 外部文件地:容器内文件地址 --name docker-nginx nginx

# MySQL:
docker run -d -p 3306:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 --name docker-mysql mysql:8.0

# Redis:
docker run -itd --name docker-redis -p 6379:6379 redis

# RabbitMQ:
docker pull rabbitmq 
docker pull rabbitmq:management
docker run -d --name docker-rabbitmq -p 5671:5671 -p 5672:5672 -p 4369:4369 -p 25672:25672 -p 15671:15671 -p 15672:15672 rabbitmq:management
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67

练习:

docker search nginx
docker pull nginx
docker images
docker run -d --name mynginx -p 3500:80 nginx
docker ps
curl localhost:3500

docker exec -it mynginx /bin/bash
whereis nginx # 寻找nginx
cd /usr/share/nginx # nginx 的路径
vim  index.html #修改index
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

可视化/管理工具

在这里插入图片描述

Portainer

docker run -d -p 8088:9000 
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --
privileged=true portainer/portainer
  • 1
  • 2
  • 3

Portainer是Docker的图形化管理工具,提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作)、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录用户管理和控制等功能。功能十分全面,面向中小型单位对容器管理的全部需求。

访问方式:http://IP:8088

首次登陆需要注册用户,给admin用户设置密码:单机版这里选择local即可,选择完毕,点击Connect即可连接到本地docker:

Docker Dasktop

在这里插入图片描述

Docker镜像原理

镜像是什么?

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

Docker镜像加载原理

在聊这个话题时候需要提前了解两个知识点,什么是UnionFS ?有兴趣可以去了解一下,这里只做简单阐述。

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

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

UnionFS和Docker镜像加载原理有什么关系?

在这里插入图片描述

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

备注: Linuxkernel是一个一体化内核(monolithic kernel)系统,提供硬件抽象层、磁盘及文件系统控制、多任务等功能的系统软件,不是一套完整的操作系统。有兴趣可以去阅读: Linux内核的工作原理

Docker文件分层原理

分层的镜像:我们可以去下载一个镜像,注意观察下载的日志输出,可以看到是一层一层的在下载!

在这里插入图片描述

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

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

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

拓展:所有的 Docker 镜像都起始于一个基础镜像层,当进行修改或增加新的内容时,就会在当前镜像层上,创建新的镜像层。举一个简单的例子,假如基于 Ubuntu Linux 16.04 创建一个新的镜像,这就是新镜像的第一层;如果在该镜像中添加 Python包,就会在基础镜像层之上创建第二个镜像层;如果继续添加一个安全补丁,就会创建第三个镜像层。该镜像当前已经包含 3 个镜像层,如下图所示(这只是一个用于演示的很简单的例子)。

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

在这里插入图片描述

上图中的镜像层跟之前图中的略有区别,主要目的是便于展示文件。下图中展示了一个稍微复杂的三层镜像,在外部看来整个镜像只有 6 个文件,这是因为最上层中的文件7 是文件 5 的一个更新版本。这种情况下,上层镜像层中的文件覆盖了底层镜像层中的文件。这样就使得文件的更新版本作为一个新镜像层添加到镜像当中。

Docker 通过存储引擎(新版本采用快照机制)的方式来实现镜像层堆栈,并保证多镜像层对外展示为统一的文件系统。

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

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

(img-GW34mpWa-1634551438154)(https://i.loli.net/2021/08/30/NVgljJnYWadeCqD.png)]https://i.loli.net/2021/08/30/NVgljJnYWadeCqD.png

特点:Docker镜像都是只读的,当容器启动时,一个新的可写层被加载到镜像的顶部!这一层就是我们通常说的容器层,容器之下的都叫镜像层!

Docker数据持久

容器数据卷

什么是容器数据卷,为什么要用容器数据卷?

将应用和运行的环境打包形成容器运行,运行可以伴随着容器,但是我们对于数据的要求,是希望能够持久化的!就好比,你安装一个MySQL,结果你把容器删了,就相当于删库跑路了,这TM也太扯了吧!所以我们希望容器之间有可能可以共享数据,Docker容器产生的数据,如果不通过docker commit 生成新的镜像,使得数据作为镜像的一部分保存下来,那么当容器删除后,数据自然也就没有了!这样是行不通的!为了能保存数据在Docker中我们就可以使用卷!让数据挂载到我们本地!这样数据就不会因为容器删除而丢失了!

容器数据卷作用是什么?

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

容器数据卷特点?

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

备注:总结一句话: 就是容器的持久化,以及容器间的继承和数据共享!

容器数据卷如何使用?

docker run -it -v 宿主机绝对路径目录:容器内目录 镜像名
# 测试
[root@kuangshen ~]# docker run -it -v /home/ceshi:/home centos /bin/bash
  • 1
  • 2
  • 3

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f8SWyTLg-1634551438155)(https://i.loli.net/2021/08/30/qnVfBJiOzKskuav.png)]

容器挂载

mysql 数据持久化的问题!

# 1、搜索镜像
[root@kuangshen ~]# docker search mysql
NAME DESCRIPTION
STARS
mysql MySQL is a widely used, open-source
relation... 9488

# 2、拉取镜像
[root@kuangshen ~]# docker pull mysql:5.7
5 .7: Pulling from library/mysql
54fec2fa59d0: Already exists
bcc6c6145912: Pull complete
951c3d959c9d: Pull complete
05de4d0e206e: Pull complete
319f0394ef42: Pull complete
d9185034607b: Pull complete
013a9c64dadc: Pull complete
e745b3361626: Pull complete
03145d87b451: Pull complete
3991a6b182ee: Pull complete
62335de06f7d: Pull complete
Digest:
sha256:e821ca8cc7a44d354486f30c6a193ec6b70a4eed8c8362aeede4e9b8d74b8ebb
Status: Downloaded newer image for mysql:5.7
docker.io/library/mysql:5.7


# 3、启动容器 -e 环境变量!
# 注意: mysql的数据应该不丢失!先体验下 -v 挂载卷! 参考官方文档
[root@kuangshen home]# docker run -d -p 3310:3306 -v
/home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e
MYSQL_ROOT_PASSWORD=123456 --name mysql01 mysql:5.7
4763fa5c68c4323688102f57938fb10996a0fb902d2812349286529f9378f16c


# 4、使用本地的sqlyog连接测试一下 3310

# 5、查看本地的 /home/mysql 目录 [root@kuangshen data]# pwd /home/mysql/data [root@kuangshen data]# ls
.. ... . test # 可以看到我们刚刚建立的mysql数据库在本地存储着


# 6、删除mysql容器
[root@kuangshen data]# docker rm -f mysql01 # 删除容器,然后发现远程连接失败! mysql01
[root@kuangshen data]# ls
.. ... . test # 可以看到我们刚刚建立的mysql数据库在本地存储着
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45

匿/具名挂载

什么是匿名和具名挂载?

# 匿名挂载
-v 容器内路径
docker run -d -P --name nginx01 -v /etc/nginx nginx
# 匿名挂载的缺点,就是不好维护,通常使用命令 docker volume维护 docker volume ls
# 具名挂载
-v 卷名:/容器内路径
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx nginx
# 查看挂载的路径
[root@kuangshen ~]# docker volume inspect nginxconfig [
    {
        "CreatedAt": "2020-05-13T17:23:00+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/nginxconfig/_data",
        "Name": "nginxconfig",
        "Options": null,
        "Scope": "local"
} ]
# 怎么判断挂载的是卷名而不是本机目录名? 不是/开始就是卷名,是/开始就是目录名
# 改变文件的读写权限
# ro: readonly
# rw: readwrite
# 指定容器对我们挂载出来的内容的读写权限
docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx:ro nginx docker run -d -P --name nginx02 -v nginxconfig:/etc/nginx:rw nginx
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

Docker File

DockerFile 是用来构建Docker镜像的构建文件,是由一些列命令和参数构成的脚本。我们在这里,先体验下,后面我们会详细讲解 DockerFile !

测试:

# 1、我们在宿主机 /home 目录下新建一个 docker-test-volume文件夹
[root@kuangshen home]# mkdir docker-test-volume

# 说明:在编写DockerFile文件中使用 VOLUME 指令来给镜像添加一个或多个数据卷 
VOLUME["/dataVolumeContainer1","/dataVolumeContainer2","/dataVolumeContainer 3"]

# 出于可移植和分享的考虑,我们之前使用的 -v 主机目录:容器目录 这种方式不能够直接在 DockerFile中实现。
# 由于宿主机目录是依赖于特定宿主机的,并不能够保证在所有宿主机上都存在这样的特定目录.

# 2、编写DockerFile文件
[root@kuangshen docker-test-volume]# pwd /home/docker-test-volume
[root@kuangshen docker-test-volume]# vim dockerfile1 [root@kuangshen docker-test-volume]# cat dockerfile1
# volume test
FROM centos
VOLUME ["/dataVolumeContainer1","/dataVolumeContainer2"] CMD echo "-------end------"
CMD /bin/bash

# 3、build后生成镜像,获得一个新镜像 kuangshen/centos
docker build -f /home/docker-test-volume/dockerfile1 -t kuangshen/centos . # 注意最后有个.

# 4、启动容器
[root@kuangshen docker-test-volume]# docker run -it 0e97e1891a3d /bin/bash # 启动容器
[root@f5824970eefc /]# ls -l
total 56
# 问题:通过上述步骤,容器内的卷目录地址就已经知道了,但是对应的主机目录地址在哪里呢?


# 5、我们在数据卷中新建一个文件
[root@f5824970eefc dataVolumeContainer1]# pwd /dataVolumeContainer1
[root@f5824970eefc dataVolumeContainer1]# touch container.txt [root@f5824970eefc dataVolumeContainer1]# ls -l
total 0
-rw-r--r-- 1 root root 0 May 11 11:58 container.txt


# 6、查看下这个容器的信息
[root@kuangshen ~]# docker inspect 0e97e1891a3d # 查看输出的Volumes
"Volumes": {
    "/dataVolumeContainer1": {},
    "/dataVolumeContainer2": {}
},

# 7、这个卷在主机对应的默认位置

# 注意:如果访问出现了 cannot open directory: Permission denied 解决办法:在挂载目录后多加一个 --privileged=true参数即可
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

数据卷容器

有人会问数据卷容器和容器数据卷有什么区别?

​ 它们都是一种同步、持久化技术。容器数据卷是为了防止删除容器的时候数据丢失,构建起镜像和外部文件系统同步机制;数据卷容器是其他的容器通过挂载这个父容器实现数据共享,挂载数据卷的容器,是容器之间数据共享同步机制(类似于单项目集群思想)。

在这里插入图片描述

数据卷容器如何使用?

我们使用上一步的镜像:kuangshen/centos 为模板,运行容器 docker01,docker02,docker03,他们都会具有容器卷,我们来测试下,容器间传递共享

docker run -it - -name parentContainer  镜像名 	#建立父容器

#语法:--volumes-from
docker run -it - -name sonContainer1 --volumes -from parentContainer 镜像名
docker run -it - -name sonContainer2 --volumes -from parentContainer 镜像名

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

结论:容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用它为止。存储在本机的文件则会一直保留!

DockerFile

DockerFile基础

大家想想,Nginx,tomcat,mysql 这些镜像都是哪里来的?官方能写,我们不能写吗?如何实现自己的docker镜像,么就需要用到DockerFile

  • 微服务单体springboot应用打包成镜像(包含数据库、APP、消息队列…)

  • 流程:开发应用=>DockerFile=>打包为镜像=>上传到仓库(私有仓库,公有仓库)=> 下载镜像 => 启动

什么是DockerFile?

DockerFile是用来构建Docker镜像的构建文件,是由一系列命令和参数构成的脚本。

如何实现一个dockerFile?

基础知识:

FROM 		# 基础镜像,当前新镜像是基于哪个镜像的 MAINTAINER # 镜像维护者的姓名混合邮箱地址
RUN 		# 容器构建时需要运行的命令
EXPOSE 		# 当前容器对外保留出的端口
WORKDIR 	# 指定在创建容器后,终端默认登录的进来工作目录,一个落脚点
ENV 		# 用来在构建镜像过程中设置环境变量
ADD			# 将宿主机目录下的文件拷贝进镜像且ADD命令会自动处理URL和解压tar压缩包 COPY # 类似ADD,拷贝文件和目录到镜像中!
VOLUME 		# 容器数据卷,用于数据保存和持久化工作
CMD 		# 指定一个容器启动时要运行的命令,dockerFile中可以有多个CMD指令,但只有最 后一个生效!
ENTRYPOINT 	# 指定一个容器启动时要运行的命令!和CMD一样
ONBUILD 	# 当构建一个被继承的DockerFile时运行命令,父镜像在被子镜像继承后,父镜像的 ONBUILD被触发
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

DockerFile测试

建构步骤:编写DockerFile文件>docker build 构建镜像>docker run

例子地址:https://hub.docker.com/_/centos

创建app.py

#ipython
#ipython os
#mkdir python_app
#cd python_app
#vim app.py:
from flask import Flask             	  	#from模块调用
import socket                            	#为了获取主机名称用
import os                                	#获取环境变量用的
 
app = Flask(__name__)                         
 
@app.route('/')                             
def hello():
    html = "Hello {name} Hostname: {hostname} This is Dockerfile Demo"
    return html.format(name=os.getenv("NAME", "world"), hostname=socket.gethostname())  
    
if __name__ == "__main__":           #主函数
    app.run(host='0.0.0.0', port=80)     #监控本地所有的网卡、监控80端口
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

创建requirements.txt

Flask
  • 1

创建Dockerfile

FROM python:3.7-alpine
WORKDIR /app
ADD . /app
RUN pip install --trusted-host pypi.python.org -r requirements.txt
# 使用 pip 命令安装这个应用所需要的依赖
EXPOSE 80
# 允许外界访问容器的 80 端口
ENV NAME World
# 设置环境变量
CMD ["python", "app.py"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

DockerFile拓展

除上述的功能点以外Dockerfile还提供了HEALTHCHECK(状态服务监控)、ENV/ARG(环境配置)、ENTRYPOINT(入口指令)等功能,有兴趣的小伙伴可以去研究一下。

Docker Compose

什么是Docker Compose?

​ Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 **YML **文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

Docker Compose安装

安装地址:https://docs.docker.com/compose/install/

 sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
#备注:要安装其他版本的 Compose,请替换 1.24.1。


# 将可执行权限应用于二进制文件:
sudo chmod +x /usr/local/bin/docker-compose


# 创建软链:
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose


# 测试是否安装成功:
docker-compose --version
#输出:cker-compose version 1.24.1, build 4667896b

# 卸载
sudo rm /usr/local/bin/docker-compose
pip uninstall docker-compose

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

Docker Compose测试

mkdir composetest
cd composetest
  • 1
  • 2

建一个py.app文件

import time
 
import redis
from flask import Flask
app = Flask(__name__)
cache = redis.Redis(host='redis', port=6379)
def get_hit_count():
    retries = 5
    while True:
        try:
            return cache.incr('hits')
        except redis.exceptions.ConnectionError as exc:
            if retries == 0:
                raise exc
            retries -= 1
            time.sleep(0.5)
@app.route('/')
def hello():
    count = get_hit_count()
    return 'Hello World! I have been seen {} times.
'.format(count)
 
if __name__ == "__main__":
    app.run(host="0.0.0.0", debug=True)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

创建一个txt文件requirements.txt

flask
redis
  • 1
  • 2

创建一个Dockerfile

FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
  • 1
  • 2
  • 3
  • 4
  • 5

创建 docker-compose.yml

# yaml 配置
version: '3'
services:
  web:
    build: .
    ports:
     - "8848:8848"
  redis:
    image: "redis:alpine"
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

项目构建

# 启动应用程序:
docker-compose up

#后台执行该服务可以加上 -d 参数:
docker-compose up -d
  • 1
  • 2
  • 3
  • 4
  • 5

Docker Compose拓展

除了上面常见的配置,Docker Compose yaml 配置还提供了 endpoint_mode(集群模式vip和轮询机制dnsrr)、healthcheck、logging、network_mode、restart、secrets(存储敏感数据)、devices、dns、env_file、extra_hosts(主机映射)…等等,大家有兴趣的可以去研究一下。

Docker 网络讲解

在这里插入图片描述

Docker容器互联

Docker0(IP)

在这里插入图片描述

问题:什么是Docker0?

​ Docker启动的时候会在主机上自动创建一个docker0网桥,实际上是一个Linux网桥,所有容器的启动如果在docker run的时候没有指定网络模式的情况下都会挂载到docker0网桥上。这样容器就可以和主机甚至是其他容器之间通讯了。

docker network ls					  # Docker网络(bridge:桥接模式 host:主机模式 none:无网络模式)
docker rm -f $(docker ps -a -q) 	# 删除所有容器
docker rmi -f $(docker images -qa) 	# 删除全部镜像
  • 1
  • 2
  • 3

问题:Docker 是如何处理容器网络访问的?

每一个安装了Docker的linux主机都有一个docker0的虚拟网卡。这是个桥接网卡,使用了veth-pair技术!

小结:

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

Docker容器网络就很好的利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);Docker中的网络接口默认都是虚拟的接口。虚拟接口的优势就是转发效率极高(因为Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多。

Docker–Link(容器名)

??思考一个场景,我们编写一个微服务,数据库连接地址原来是使用ip的,如果ip变化就不行了,那我们能不能使用服务名访问呢?jdbc:mysql://mysql:3306,这样的话哪怕mysql重启,我们也不需要修改配置了!docker提供了 --link的操作!

docker run --name mysql --restart=always  -d  -e MYSQL_ROOT_PASSWORD=123456 mysql:5.6
docker run -d --name  接收容器名(#一般是tomcat) --link 源容器(被连接容器)(#一般是mysql) --restart=always  源容器镜像名
     	
docker run -d -p 8080:8080 --name tomcat --link mysql --restart=always -v /data/tomcat/webapps:/usr/local/tomcat/webapps tomcat

  • 1
  • 2
  • 3
  • 4
  • 5

备注:–link早都过时了,我们不推荐使用!我们可以使用自定义网络的方式

自定义网络

在这里插入图片描述

# 命令
[root@kuangshen ~]# docker network inspect 4eb2182ac4b2 [
    {
        "Name": "bridge",
        "Id":
"4eb2182ac4b23487e1eb6e06df56c71ab6f0adc7ccc0962b4747d0eed5ad6690",
        "Created": "2020-05-11T15:44:20.131441544+08:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
{
// 默认docker0是管理这个子网范围内的。0~16,也就是 255*255,去掉0个255,我们有65534可以分配的ip
// docker0网络默认可以支持创建6万多个容器ip不重复
] },
    "Subnet": "172.18.0.0/16",
    "Gateway": "172.18.0.1"
}
"Internal": false,
"Attachable": false,
"Ingress": false,
"ConfigFrom": {
    "Network": ""
},
"ConfigOnly": false,
"Containers": {
 "a3a4a17a2b707766ad4f2bb967ce1d94f658cd4cccef3bb8707395cdc71fa1e7": {
                "Name": "tomcat03",
                "EndpointID":
"5fe62d0be3a1343aa74f6bb77885d3657b191210cafad200e5054e1bdfe56be9",
                "MacAddress": "02:42:ac:12:00:04",
                "IPv4Address": "172.18.0.4/16",
                "IPv6Address": ""
},
 "b80da266a3ad85fcc874c5f0d3c897246ebc40533cb745bb24180237cc3167b1": {
                "Name": "tomcat02",
                "EndpointID":
"6232cac0c4e7fed30c9fa5eebc9238f9fc44f548f257344b5440d6d486825256",
                "MacAddress": "02:42:ac:12:00:03",
                "IPv4Address": "172.18.0.3/16",
                "IPv6Address": ""
},
 "f38239e2b405bccf8c93bd1052f336f661033b9b27ef9b3f99a018c108e06f87": {
                "Name": "tomcat01",
                "EndpointID":
"31dc174b8f3f15f217acdc3ac7e8a235a03d59438f863706c0143b4426772c5e",
                "MacAddress": "02:42:ac:12:00:02",
                "IPv4Address": "172.18.0.2/16",
                "IPv6Address": ""
} },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
},
        "Labels": {}
    }
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
# 默认我们不配置网络,也就相当于默认值 --net bridge 使用的docker0 
docker run -d -P --name tomcat01 --net bridge tomcat
# docker0网络的特点 
1.它是默认的
2.域名访问不通
3.--link 域名通了,但是删了又不行
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

备注:聊了这么多,我们现在应该可以深刻理解docker的网络了!

实战:部署Redis集群

# 创建网卡
docker network create redis --subnet 172.38.0.0/16
# 通过脚本创建六个redis配置
for port in $(seq 1 6); 
do 
mkdir -p /mydata/redis/node-${port}/conf
touch /mydata/redis/node-${port}/conf/redis.conf
cat << EOF >/mydata/redis/node-${port}/conf/redis.conf port 6379
bind 0.0.0.0
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 5000
cluster-announce-ip 172.38.0.1${port}
cluster-announce-port 6379
cluster-announce-bus-port 16379
appendonly yes
EOF
done
docker run -p 637${port}:6379 -p 1637${port}:16379 --name redis-${port} 
-v /mydata/redis/node-${port}/data:/data 
-v /mydata/redis/node-${port}/conf/redis.conf:/etc/redis/redis.conf 
-d --net redis --ip 172.38.0.1${port} redis:5.0.9-alpine3.11 redis-server
/etc/redis/redis.conf; 
# 启动6个容器
docker run -p 6371:6379 -p 16371:16379 --name redis-1 
-v /mydata/redis/node-1/data:/data 
-v /mydata/redis/node-1/conf/redis.conf:/etc/redis/redis.conf 
-d --net redis --ip 172.38.0.11 redis:5.0.9-alpine3.11 redis-server /etc/redis/redis.conf
docker run -p 6376:6379 -p 16376:16379 --name redis-6 
-v /mydata/redis/node-6/data:/data 
-v /mydata/redis/node-6/conf/redis.conf:/etc/redis/redis.conf 
-d --net redis --ip 172.38.0.16 redis:5.0.9-alpine3.11 redis-server
/etc/redis/redis.conf
# 进入一个redis,注意这里是 sh命令 docker exec -it redis-1 /bin/sh
# 创建集群
redis-cli --cluster create 172.38.0.11:6379 172.38.0.12:6379 172.38.0.13:6379 172.38.0.14:6379 172.38.0.15:6379 172.38.0.16:6379 -- cluster-replicas 1
# 连接集群 redis-cli -c
# 查看集群信息 cluster info
# 查看节点 cluster nodes
# set a b
# 停止到存值的容器
# 然后再次get a,发现依旧可以获取值 # 查看节点,发现高可用完全没问题
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

文章参考

[1]Docker官网.https://www.docker.com/[EB/OL].
[2]菜鸟Docker.https://www.runoob.com/docker/docker-tutorial.html[EB/OL].
[3]遇见狂神说.Docker最新超详细版教程通俗易懂[EB/OL].2020-05-16/至今.

推荐阅读