Docker
Docker
概述
什么是Docker
Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux或Windows操作系统的机器上,也可以实现虚拟化。容器是完全使用沙箱机制,相互之间不会有任何接口。
Docker为什么会出现
假定您在开发一个电商项目,您使用的是一台笔记本电脑,而且您的开发环境具有特定的配置。其他开发人员身处的环境配置也各有不同。您正在开发的应用依赖于您当前的配置且还要依赖于某些配置文件。此外,您的企业还拥有标准化的测试和生产环境,且具有自身的配置和一系列的支持文件。您希望尽可能多在本地模拟这些环境而不产生重新创建服务器环境的开销。请问?
您要如何确保应用能够在这些环境中运行和通过质量检测?并且在部署过程中不出现令人头疼的版本、配置问题,也无需重新编写代码和进行故障修复?
答案就是使用容器。Docker之所以发展如此迅速,也是因为它对此给出了一个标准化的解决方案—-系统平滑移植,容器虚拟化技术。
举个例子:
在以前,开发人员只需要提交源代码(jar包等等)直接发给运维人员即可。然后,运维开始完成部署。会有三种情况让运维人员头疼。
第一种情况,部署环境问题。
运维在部署的时候会有很多环境问题:mysql版本、redis补丁、java版本(开发的时候是java8,生产的时候是java7,可能一些java8的新特性,根本无法使用)
第二种情况 ,安装过于繁琐。
运维因为环境问题导致各种软件版本不一样,这时候运维人员就会要求开发人员写一张list清单(环境配置表)。开发是什么环境,我运维就怎么配。那么问题来了:在多台集群的情况下。这时候运维就疯了,或许我的mysql是1主1从,装2台。redis是3主3从,装6台。运维稍微因为版本和环境配置,装错一步,可能系统就跑不起来了,大家只能互相扯皮,运维是非常非常痛苦的。
第三种情况,弹性发布太慢。
假设服务器不够用了,运维需要扩容、缩容,符合弹性的发布。redis是六台3主3从。现在环境并发量大了,需要马上扩容,变成4主4从,瞬间要求你起来两个实例,如果按照以前的安装步骤,你把运维逼疯,也不可能秒级别的把服务跑起来。
环境配置相当麻烦,换一台机器,就要重来一次,费力费时。很多人想到,能不能从根本上解决问题,软件可以带环境安装?也就是说,安装的时候,把原始环境一模一样复制过来,开发人员利用Docker可以消除协作编码时“在我的机器上可正常工作”的问题。
假设你在北京某小区,5号楼5单元朝北的屋子501室,找到工作后要搬家到上海,绝对不可能是同样的小区,同样的楼层,同样的户型。这时候你就要打包、解压、货拉拉搬家公司,搬过去,重新收拾,非常麻烦,而且还可能适应不了新环境。
用了docker以后,陆地航空母舰,类似于推土机直拔下去,连根基都搬起来,搬到另外一个地方,也就是直接搬楼,就没有上面述说的问题了。
也就是说,docker实现了从搬家到搬楼。
之前在服务器配置一个应用的运行环境,要安装各种软件,就拿某电商项目的环境来说,Java/ RabbitMQ/ MySQL/ JDBC等驱动包等。安装和配置这些东西有多麻烦就一一举例了,它还不能跨平台使用。假如我们在windows上安装的这些环境,到了Linux又得重新装,况且就算不跨操作系统,换另一台同样操作系统的服务器,要移植应用也是非常麻烦的。
传统上认为,软甲编码开发/测试结束后,所产生的成果即是程序或是能够编译执行的二进制字节码等(java为例)。而为了让这些程序可以顺利执行,开发团队也得准备完整的部署文件,让运维团队得以部署应用程式。开发需要清楚的告诉运维部署团队,用的全部配置文件+所有软件环境。不过,即便如此,仍然常常发生部署失败的状况。Docker的出现使得Docker得以打破过去[程序及应用]的观念。通过镜像(images)将作业系统核心除外,运作应用程式所需要的系统环境,由上而下打包,达到应用程式跨平台的无缝运作。
Docker的安装
docker的基本构成
Docker由三部分构成
docker镜像就好比是一个模板,可以通过这个模板来创建容器服务,tomcat镜像===>run ==> tomcat01容器(提供服务器),通过这个镜像可以创建多个容器(最终服务运行或者项目运行就是在容器中的)。
(有点像Java 中的 类和对象的关系)
容器(Containers):Docker利用容器技术,独立运行一个或者一个组应用,通过镜像来创建的。启动,停止,删除,基本命令,目前就可以把这个容器理解为就是一个简易的linux系统
仓库(Registry):仓库就是存放镜像的地方!仓库分为公有仓库和私有仓库!Docker Hub (默认是国外的)阿里云….都有容器服务器(配置镜像加速!)
Docker安装
首先卸载之前所安装的docker
1 | yum remove docker \ |
安装docker所需的安装包
1 | yum intsall -y yum-utils |
设置镜像仓库(阿里云)
1 | yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo |
安装Docker
docker-ce 是社区版,docker-ee 企业版,正常推荐都是社区版
1 | yum install docker-ce docker-ce-cli containerd.io |
启动docker
1 | systemctl start docker |
查看docker版本号
1 | systemctl enable docker |
设置docker开机启动
1 | systemctl enable docker |
helloWorld
查看hello-world镜像
1 | docker images |
卸载依赖
1 | yum remove docker-ce docker-ce-cli containerd.io |
删除资源
1 | rm -rf /var/lib/docker # /var/lib/docker docker的默认工作路径 |
docker是如何工作的
Docker是一个Client - Server结构的系统,Docker的守护进程运行在主机上。通过Socket从客户端访问你DockerServer接收到Docker-Client的指令,就会执行这个命令!
Docker 的常用命令
帮助命令
1 | docker version #显示docker 的版本信息 |
镜像命令
docker images 查看所有本地的主机上的镜像
1 | docker images 查看所有本地的主机上的镜像 |
REPOSITORY 镜像仓库
TAG 镜像标签
IMAGE ID 镜像ID
CREATED 镜像创建时间
SIZE 镜像的大小
docker search 搜索镜像
1 | docker search 搜索镜像 |
1 | docker search mysql -f starts=3000 #过滤掉stars少于3000的结果 |
docker pull 下载镜像
1 | docker pull 镜像名 |
本次 docker pull mysql
等价于
docker pull docker.io/library/mysql:latest
指定版本下载时,需要进入docker hub 搜索所要拉取的镜像,然后查看其版本号,不能随便你填写版本号
docker rmi 镜像名 (ID) 删除镜像
1 | docker rmi mysql 删除Mysql镜像 |
我们可以结合查询出的ID来进行删除
1 | docker rmi -f $(docker images 镜像名 -aq) #-f 强制 |
删除docker镜像中所有该镜像名的镜像(通过ID),类似于SQL语句 select * from (select * from table where age=18) where id=1
容器命令
说明∶我们有了镜像才可以创建容器,linux,下载一个centos镜像来测试学习
1 | docker pull centos |
docker pull 镜像名(创建容器并启动)
1 | docker run [可选参数] images |
我们可以发现,我们进入了centos的镜像中后,我们的主机名变成了容器ID
我们在容器的交互中,可以使用CentOs中的一些基本命令
docker ps (列出所有正在运行的容器)
1 | docker ps |
退出容器
1 | exit #容器退出并停止 |
删除容器 (docker rm)
1 | docker rm 容器id #不能删除正在运行的容器,如果要删除则需要 -f |
启动容器和停止容器
1 | docker start 容器id |
常用的其他命令
后台启动容器
1 | docker run -d 镜像名 |
查看日志
docker logs 容器id
1 | docker logs 容器id |
获取容器/镜像的元数据
docker inspect name
1 | [root@zf ~]# docker inspect 28d5c397bc7a |
进入正在运行的容器
1 | docker attach 容器id |
将文件容器拷贝到主机中
1 | docker cp 容器id:复制文件的绝对路径 主机路径 |
练习
docker 安装 nginx
首先搜索nginx
1 | docker search nginx |
下载nginx镜像
1 | docker pull nginx |
创建容器并在后台运行设置其端口号和宿主机端口号为 3344:80
1 | docker run -d --name nginx01 -p 3344:80 nginx |
查看nginx是否正常运行
在宿主机中直接curl 访问3344端口
1 | curl localhost:3344 |
进入容器交互 ,查看nginx配置文件
可视化
portainer(先用这个)
1
2docker run -d -p 8088:9000 \
--restart=always -v /var/run/docker.sock:/var/run/docker.sock --privilaged-true portainer/portainer
从外部访问portainer
Docker镜像讲解
什么是镜像
镜像是一种轻量级、可执行的独立软件包,用来打包软件运行环境和基于运行环境开发的软件,它包含运行某个软件所需的所有内容,包括代码、运行时、库、环境变量和配置文件。
所有的应用,直接打包docker镜像,就可以直接跑起来!如何得到镜像︰
朋友拷贝给你
自己制作一个镜像DockerFile
从远程仓库下载
镜像加载原理
UnionFS(联合文件系统)
UnionFS ( 联合文件系统):Union文件系统( UnionFS )是一种分层、轻量级并且高性能的文件系统,它支持对文件系统的修改作为一次提交来一层层的叠加,同时可以将不同目录挂载到同一个虚拟文件系统下(unite several directories into a single virtualfilesystem)。Union文件系统是Docker镜像的基础。镜像可以通过分层来进行继承,基于基础镜像(没有父镜像),可以制作各种具体的应用镜像。
特性:一次同时加载多个文件系统,但从外面看起来,只能看到一个文件系统,联合加载会把各层文件系统叠加起来,这样最终的文件系统会包含所有底层的文件和目录
Docker镜像加载原理
docker的镜像实际上由一层一层的文件系统组成,这种层级的文件系统UnionFS。
bootis(boot flesystem)主要包含bootloader和kernel, bootloader主要是引导加载kernel, Linux刚启动时会加载bootfs文件系统,在Docker镜像的最底层是bootfs。这一层与我们典型的Linux/Unix系统是一样的,包含boot加载器和内核。当boot加载完成之后整个内核就都在内存中了,此时内存的使用权已由bootfs转交给内核,此时系统也会卸载bootfs。
roots (root file system),在bootfs之上。包含的就是典型Linux系统中的/dev,/proc, /bin, /etc等标准目录和文件。rootfs就是各种不同的操作系统发行版,比如uuntu , Centos等等。
可是为什么平时我们的CentOS都是好几个G但是,Docker这里才200MB?
对于一个精简的OS , rootfs可以很小,只需要包含最基本的命令,工具和程序库就可以了,因为底层直接用Host的kernel ,自己只需要提供rootfs就可以了。由此可见对于不同的linux发行版, bootfs基本是一致的, rootfs会有差别,因此不同的发行版可以公用bootfs。
Commit镜像
1 | docker commit #提交容器生成一个新的副本 |
容器数据卷
什么是容器数据卷
docker理念回顾:将应用和环境打包成一个镜像!但是如果数据在容器中,一旦容器被删除,那么数据就会消失
所以我们就有了需求:数据持久化
容器之间可以有一个数据共享的技术!Docker容器中产生的数据同步到本地!这就是卷技术!目录的挂载,将我们容器内的目录挂载到Linux上面
使用数据卷
方式一:直接使用命令来挂载
1
docker run -it -v 主机目录:容器内的目录 -p 宿主机端口:容器端口
当我们查看元数据时
当我们在宿主机目录中创建文件并添加内容时,容器目录中我们可以看到该内容
类似于动态绑定,宿主机目录中的内容和容器目录内容相同
具名挂载与匿名挂载
当我们进行容器运行挂载时我们可以指定挂载时,数据卷的名称
1 | #匿名挂载 |
我们将nginx配置文件相关目录进行挂载
利用对数据卷操作命令
1 | docker volume |
打印出所有数据卷的名称
查看数据卷在宿主机中的名称(具名挂载)
倘若我们不指定在宿主机中的挂载位置,docker就会将数据卷挂载到宿主机的 /var/lib/docker/(具名挂载名称或匿名挂载名称)/_data 目录中
查看(匿名挂载)数据卷在宿主机中的名称(匿名挂载的名称是一串不规律的字符串)
我们可以看到 docker下一级目录的名称与具名挂载或匿名挂载的数据卷的名称相同
设置对数据卷的读写权限
1 | #ro read only 只读 |
练习
docker安装并配置mysql
首先安装拉取mysql8.0镜像
1 | docker pull mysql:8.0 |
创建容器并实现挂载 并且 要提前设置ROOT用户的密码 将容器3306端口映射为宿主机端口3406
1 | docker run -d -p 3406:3306 -v /home/mysql/conf:/etc/mysql/conf.d -v /home/mysql/data:/var/lib/mysql -e MYSQL_ROOT_PASSWORD=123456 mysql01 mysql |
我们发现可以实现多挂载,将宿主机目录挂载到容器中
当我们将容器删除,我们发现数据卷依然存在,实现了容器的持久化
数据卷容器
多个mysql同步数据
dockerFile
dockerfile是用来构建dokcer镜像的文件!命令参数脚本,是由一条条构建镜像所需的指令和参数构成的脚本
dockerFile有什么作用
某种镜像的增强,给我个list做个清单;后续,我需要加入任何功能,
直接在list单子里面写好,相当于多次提交
‘Dockerfile,本镜像需要一次性添加
vim/ifconfig/tomcat8/java8…..
构建步骤:
1、编写一个dockerfile文件
2、docker build构建成为一个镜像3、docker run运行镜像
4、docker push 发布镜像( DockerHub、阿里云镜像仓库!|)
DockerFile的构建过程
基础知识
1、每个保留关键字(指令)都是必须是大写字母
2、执行从上到下顺序执行
3、#表示注释
4、每一个指令都会创建提交一个新的镜像层,并提交!
DockerFile的指令
以前的话就是我们使用别人的,现在我们知道了这些指令了,我们来自己配置一些镜像
docker基础知识
每条保留字指令都必须为大写字母且后面要跟随至少一个参数
指令按照从上到下,顺序执行
#表示注释
每条指令都会创建一个新的镜像层并对镜像进行提交
docker执行过程
( 1) docker从基础镜像运行一个容器
(2)执行一条指令并对容器作出修改
(3)执行类似docker commit的操作提交一个新的镜像层
( 4) docker再基于刚提交的镜像运行一个新容器
(5)执行dockerfile中的下一条指令直到所有指令都执行完成
从应用软件的角度来看,
Dockerfile、Docker镜像与Docker容器分别代表软件的三个不同阶段,*Dockerfile是软件的原材料
Docker镜像是软件的交付品
*Docker容器则可以认为是软件镜像的运行态,也即依照镜像运行的容器实例
Dockerfile面向开发,Docker镜像成为交付标准,Docker容器则涉及部署与运维,三者缺一不可,哈力充当Dopker体系的基石。
保留字
保留字就是其关键字,结合上图去理解每个关键字所执行的时刻
1 | FROM #基础镜像,一切从这里开始,制定一个已经存在的镜像作为模板,第一条必须是from |
CMD会被docker run命令替代类似于
我们在DockerFile中设置了命令
1 | CMD["catalina.sh","run"] |
当我们在运行镜像添加命令 例如 docker run tomcat /bin/bash
这时候,DockerFile中的命令变成了
1 | CMD["/bin/bash","run"] |
这样启动tomcat容器,tomcat服务并未启动
ENTRYPOINT可以和CMD一起用,一般是变参才会使用CMD,这里的CMD等于是在给ENTRYPOINT传参。
当指定了ENTRYPOINT后,CMD的含义就发生了变化,不再是直接运行其命令而是将CMD的内容作为参数传递给ENTRYPOINT指令,他两个组合就会变成
1 | <ENTRYPOINT> <CMD> |
实战测试
构建ctenos
DockerHub中99%镜像都是从这个基础镜像过来的 FROM scratch ,然后配置需要的软件和配置来进行构建
1 | FROM centos:7 #基于centos7进行构建 |
构建镜像所要执行的命令
1 | docker build -f dockerFile名称 -t 目标镜像名:版本号 . |
创建容器并运行后
直接跳转到 /usr/local目录下
dockerhub
将本地镜像推送dockerhub
首先要登录dockerhub,注册账号(记住用户名)
将本地仓库中所要推送的镜像打上标签
1 | docker tag 本地镜像名:版本号 dockerhub用户名/镜像名:版本号 |
这里注意,没有必要把dockerhub用户名/镜像名:版本号这里的镜像名和版本号要填成和本地镜像名和版本号一致,此处是对外展示的,只是打上了一个其他名字和版本号的标签。
在这里我们可以使用命令看一下
1 | docker images |
我们生成了一个带有标签名的镜像,而且版本号也是我们设置的版本号
将本地镜像推送到dockerhub中
1 | docker push 标签镜像名:版本号 |
推送成功,我们 可以去dockerhub中查看
创建本地私有仓库并将本地镜像存入其中
(49条消息) docker搭建私有仓库_docker配置私有仓库_谬也的博客-CSDN博客
Docker 微服务实战
docker网络
清空所有环境
1 | docker system prune -a |
我们可以通过在虚拟机中的命令查看虚拟网卡
其中 ens33网卡为本机的虚拟网卡,其中的ip为虚拟机外可以访问的IP地址 lo为回环地址
上图中的virbr0和virbr0-nic
virbr0 是 KVM 默认创建的一个 Bridge,其作用是为连接其上的虚机网卡提供 NAT 访问外网的功能。
virbr0 默认分配了一个IP 192.168.122.1,并为连接其上的其他虚拟网卡提供 DHCP 服务。
当开启docker服务后我们会发现有个叫docker0的网卡
Docker服务默认会创建一个docker0网桥〈其上有一个docker)内部接口),该桥接网络的名称为docker0,它在内核层连通了其他的物理或虚拟网卡,这就将所有容器和本地主机都放到同一个物理网络。Docker默认指定了docker接口的IP地址和子网掩码,让主机和容器之间可以通过网桥相互通信。
基本命令
1 | docker network |
docker network网络模式
在容器创建时,可以通过命令设定使用什么网络连接模式 ,默认是bridge模式
1 | docker run --network bridge/host/none/(contain:公用网络的容器名) |
Bridge模式
- 1、Docker使用Linux桥接,在宿主机虚拟一个Docker容器网桥(docker0),Docker启动一个容器时会根据Docker网桥的网段分配给容器一个IP地址,称为Container-IP,同时Docker网桥是每个容器的默认网关。因为在同一宿主机内的容器都接入同一个网桥,这样容器之间就能够通过容器的Container-lP直接通信。
- 2、docker run的时候,没有指定network的话默认使用的网桥模式就是bridge,使用的就是dockerO。在宿主机ifconfig,就可以看到docker0和自己createi的network(后面讲)eth0, eth1, eth2……表网卡一,网卡二,网卡三…… lo代表127.0.0.1,即localhost. inet addr用来表示网卡的IP地址
- 3、网桥dockerO创建一对对等虚拟设备接口一个叫veth,另一个叫eth0,成对匹配。
- 3.1整个宿主机的网桥模式都是dockerO,类似一个交换机有一堆接口,每个接口叫veth,在本地主机和容器内分别创建一个虚拟接口,并让他们彼此联通(这样一对接口叫veth pair) ;
- 3.2每个容器实例内部也有一块网卡,每个接口叫eth0;
- 3.3 docker0上面的每个veth匹配某个容器实例内部的eth0,两两配对,一一匹配。
- 通过上述,将宿主机上的所有容器都连接到这个内部网络上,两个容器在同一个网络下,会从这个网关下各自拿到分配的ip,此时两个容器的网络是互通的。
Container模式
新建的容器和已经存在的一个容器共享一个网络ip配置而不是和宿主机共享。新创建的容器不会创建自己的网卡,配置自己的IP,而是和一个指定的容器共享IP、端口范围等。同样,两个容器除了网络方面,其他的如文件系统、进程列表等还是隔离的。
注意,在进行Container模式连接时,不能在容器端口映射时,选择同一端口例如:
1 | docker run -p 8081:8080 --network bridge --name tomcat1 tomcat |
此时就会报错因为二者不仅使用同一IP,而且还是用同一映射端口
那我们就使用轻便的alpine
首先创建容器alpine1
再创建容器alpine2
我们可以发现二者的eth0网卡信息一致符合上图
这时我们不仅好奇,如果alpine1突然挂掉,那么alpine2会代替alpine1吗,或者说,alpine2的ip依旧不变
我们测试一下
此时我们发现,alpine2的eth0网卡没了。
所以contain连接,倘若实际带有网卡的容器宕机,那么通过contain连接的容器网卡也会一并消失
自定义网络
我们的容器在进行网络交互时,是否可以根据ip进行相互通信呢
1 | docker run -it --name alpine1 alpine /bin/sh |
alpine1 ip为:172.17.0.3
alpine2 ip为:172.17.0.4
测试说明可以进行网络通信,如果使用容器名进行通信呢
并不可以,但是我们使用DHCP协议,动态的生成ip,倘若服务重启后容器各自的ip都不同了,那么就会造成通信异常,所以我们要解决这种问题
所以要使用自定义网络
我们自定义了一个网络连接模式aa_network
然后以aa_network作为网络连接模式创建了两个容器(alpine1 alpine2)
可以使用ip进行网络通信
更可以使用容器名进行通信!!!!!!!!!!!!!!
哇哦哇哦,至于内部什么原理,俺也不知道
docker Compose 容器编排
听某位大佬说(田某某),这块儿很难嘿嘿,背着他偷偷学
Docker-Compose是Docker官方的开源项目,负责实现对Docker容器集群的快速编排。
Compose是Docker公司推出的一个工具软件,可以管理多个Docker容器组成一个应用。你需要定义一个YAML格式的配置文作docker-compose.yml,写好多个容器之间的调用关系。然后,只要一个命令,就能同时启动/关闭这些容器
安装
1 | sudo curl -L "https://github.com/docker/compose/releases/download/1.24.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose |
赋予权限
1 | sudo chmod +x /usr/local/bin/docker-compose |
查看版本
安装成功
compose的核心概念
一个文件 ,两个要素
1 | # Compose 版本 Version 2支持更多的指令。Version 1将来会被弃用。 |
常用命令
1 | docker-compose -h # 查看帮助 |