本人开始今天分享之前想提以前简单的问题,大家可以试试回答。Kubernetes和Docker关系?
Kubernetes的作用是管理容器,docker一旦变多,重启,容器通信等一系列过程就需要一个工具来进行管理,这就是Kubernetes做的事情
K8S 调度的基本单位为 pod(一个POD表示一个或多个容器),为什么基本单位不是一个容器?
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连接从客户端访问,守护进程从客户端接受命令并管理运行在主机上的容器。 容器,是一个运行时环境,就是我们 前面说到的集装箱。
备注:
备注:区分镜像和容器
镜像(image):Docker 镜像(Image)就是一个只读的模板。镜像可以用来创建 Docker 容器,一个镜像可以创建很多容器。 就好似 Java 中的 类和对象,类就是镜像,容器就是对象!
容器(container):Docker 利用容器(Container)独立运行的一个或一组应用。容器是用镜像创建的运行实例。 它可以被启动、开始、停止、删除。每个容器都是相互隔离的,保证安全的平台。可以把容器看做是一个简易版的 Linux 环境(包括root用户权限、进程空间、用户空间和网络空间等) 和运行在其中的应用程序。
仓库(repository):仓库(Repository)是集中存放镜像文件的场所。 仓库(Repository)和仓库注册服务器(Registry)是有区别的。仓库注册服务器上往往存放着多个仓库,每个仓库中又包含了多个镜像,每个镜像有不同的标签(tag)。 仓库分为公开仓库(Public)和私有仓库(Private)两种形式。最大的公开仓库是 Docker Hub(https://hub.docker.com/),存放了数量庞大的镜像供用户下载。 国内的公开仓库包括阿里云 、网易云等
补充:容器的定义和镜像几乎一模一样,也是一堆层的统一视角,唯一区别在于容器的最上面那一层是可读可写的。
官网安装参考手册:https://docs.docker.com/engine/install/centos/
1、yum安装gcc相关环境(需要确保 虚拟机可以上外网 )
yum -y install gcc
yum -y install gcc-c++
2 、卸载旧版本
yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine
3、安装需要的YUM软件包
yum install -y yum-utils
4、设置镜像仓库
# 正确推荐使用国内的
yum-config-manager --add-repo http://mirrors.aliyun.com/docker- ce/linux/centos/docker-ce.repo
5、更新yum软件包索引
yum makecache fast
6、安装 Docker CE
yum install docker-ce docker-ce-cli containerd.io
7、启动Docker
systemctl start docker
8、卸载Docker
systemctl stop docker
yum -y remove docker-ce docker-ce-cli containerd.io
rm -rf /var/lib/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
练习:
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
docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --
privileged=true portainer/portainer
Portainer是Docker的图形化管理工具,提供状态显示面板、应用模板快速部署、容器镜像网络数据卷的基本操作(包括上传下载镜像,创建容器等操作)、事件日志显示、容器控制台操作、Swarm集群和服务等集中管理和操作、登录用户管理和控制等功能。功能十分全面,面向中小型单位对容器管理的全部需求。
访问方式:http://IP:8088
首次登陆需要注册用户,给admin用户设置密码:单机版这里选择local即可,选择完毕,点击Connect即可连接到本地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镜像要采用这种分层的结构呢?
最大的好处,我觉得莫过于是资源共享了!比如有多个镜像都从相同的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]。下图展示了与系统显示相同的三层镜像。所有镜像层堆叠并合并,对外提供统一的视图。
特点: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
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(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数据库在本地存储着
什么是匿名和具名挂载?
# 匿名挂载
-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
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参数即可
有人会问数据卷容器和容器数据卷有什么区别?
它们都是一种同步、持久化技术。容器数据卷是为了防止删除容器的时候数据丢失,构建起镜像和外部文件系统同步机制;数据卷容器是其他的容器通过挂载这个父容器实现数据共享,挂载数据卷的容器,是容器之间数据共享同步机制(类似于单项目集群思想)。
数据卷容器如何使用?
我们使用上一步的镜像: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 镜像名
结论:容器之间配置信息的传递,数据卷的生命周期一直持续到没有容器使用它为止。存储在本机的文件则会一直保留!
大家想想,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被触发
建构步骤:编写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端口
创建requirements.txt
Flask
创建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"]
除上述的功能点以外Dockerfile还提供了HEALTHCHECK(状态服务监控)、ENV/ARG(环境配置)、ENTRYPOINT(入口指令)等功能,有兴趣的小伙伴可以去研究一下。
什么是Docker Compose?
Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 **YML **文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。
安装地址: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
mkdir composetest
cd composetest
建一个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.\n'.format(count)
if __name__ == "__main__":
app.run(host="0.0.0.0", debug=True)
创建一个txt文件requirements.txt
flask
redis
创建一个Dockerfile
FROM python:3.4-alpine
ADD . /code
WORKDIR /code
RUN pip install -r requirements.txt
CMD ["python", "app.py"]
创建 docker-compose.yml
# yaml 配置
version: '3'
services:
web:
build: .
ports:
- "8848:8848"
redis:
image: "redis:alpine"
项目构建
# 启动应用程序:
docker-compose up
#后台执行该服务可以加上 -d 参数:
docker-compose up -d
除了上面常见的配置,Docker Compose yaml 配置还提供了 endpoint_mode(集群模式vip和轮询机制dnsrr)、healthcheck、logging、network_mode、restart、secrets(存储敏感数据)、devices、dns、env_file、extra_hosts(主机映射)…等等,大家有兴趣的可以去研究一下。
问题:什么是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) # 删除全部镜像
问题:Docker 是如何处理容器网络访问的?
每一个安装了Docker的linux主机都有一个docker0的虚拟网卡。这是个桥接网卡,使用了veth-pair技术!
小结:
Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-IP直接通信。
Docker容器网络就很好的利用了Linux虚拟网络技术,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair);Docker中的网络接口默认都是虚拟的接口。虚拟接口的优势就是转发效率极高(因为Linux是在内核中进行数据的复制来实现虚拟接口之间的数据转发,无需通过外部的网络设备交换),对于本地系统和容器系统来说,虚拟接口跟一个正常的以太网卡相比并没有区别,只是他的速度快很多。
??思考一个场景,我们编写一个微服务,数据库连接地址原来是使用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
备注:–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": {}
}
]
# 默认我们不配置网络,也就相当于默认值 --net bridge 使用的docker0
docker run -d -P --name tomcat01 --net bridge tomcat
# docker0网络的特点
1.它是默认的
2.域名访问不通
3.--link 域名通了,但是删了又不行
备注:聊了这么多,我们现在应该可以深刻理解docker的网络了!
# 创建网卡
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]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/至今.