物理机上部署:
1 部署复杂,成本高; 会造成资源的浪费 ; 不支持跨平台部署 ;无法支持进行服务的迁移
虚拟化:
1 在一台宿主机上虚拟出多台虚拟机, 虚拟机会部署一个完整的操作系统,要分配系统资源。
部署缺点: 部署复杂,无法进行迁移
3 虚拟化与容器对比
- 容器直接使用的宿主机的资源性能 , 而kvm 是分配的虚拟的的宿主机资源,性能弱
- 同样配置宿主机,最多可以器起十台虚拟机,但是可以跑100个容器
- 启动一个虚拟机有一个启动过程,启动时间较长
- kvm需要硬件cpu支持虚拟化技术, 而容器不需要
- 容器实现快速的扩缩容
kvm 虚拟化技术,kvm已将虚拟化集成到linux内核当中,基于硬件虚拟化来实现,因此只能在linux上使用
所以虚拟出一台完整的虚拟机的成本要远远高于一个容器的所需资源, 而容器知识虚拟出一个隔离的环境。
容器: 隔离的进程独立于宿主机和其他进程, docker 是一个容器引擎,容器内的应用程序直接运行在宿主机上,容器内没有自己的内核,也没有对硬件进行虚拟化,因此容器比虚拟机更加轻便。
基于内核的lxc 容器技术,容器作用就是创建一个名称空间环境,实现对资源进程,网络,文件系统的隔离,而容器是直接使用宿主机的硬件配置。通过ps -ef 可以看到容器的这个进程。
虚拟化核心解决问题就是资源隔离和资源限制:
虚拟化硬件虚拟化技术,通过hypervisor层实现对资源的彻底隔离
容器则是操作系统级别的虚拟化,利用内核的cgroup 与namespace 来实现,通过软件实现
namespace 资源隔离
命名空间是全局资源的一种抽象,将资源放在不同的命名空间中,各个命名空间的资源相互隔离,就是docker在启动一个容器时候,会调内核的namespace接口,来创建一块虚拟空间,创建时候可以支持设置下面几种
pid: 用于进程隔离
net: 用于管理网络接口
ipc:管理对ipc资源的访问(ipc进程之间通信)
mnt:管理文件系统挂载点
uts:隔离主机名和域名
user: 隔离用户和用户组
cgroup资源限制
通过namespace可以保证容器之间的隔离,但是无法控制每个容器占用多少资源,如果其中一个容器正在执行cpu密集型任务,那么就会影响其他容器中任务的性能,导致多个容器相互影响并且之间抢占资源 ,cgroup 就是能隔离宿主机上的物理资源如cpu,内存,磁盘。io和网络带宽,每一个cgroup都是一组相同的标准和参数限制的进程,而我们做的只是把容器这个进程加入到指定的cgroup中
docker 网络
docker 具有自己独立的网络空间。
1 那么多个容器之间时咋么通信的?
2 容器和宿主机之间是则么通信的?
3 使用- p 是如何实现端口映射的?
网络模型:docker 有以下4中模型
1 bridge:默认设置 网桥,我们可以把网桥看成一个二层交换机
2 host模式:使用--net = host 指定,容器内部网络空间共享宿主机的网络空间,类似在宿主机上直接启动了一个进程,端口信息和宿主机共用
3 container :使用--net=container:name 指定容器与特定容器共享名称空间
4 none : 使用 --net=none 指定 网络模式为空,仅保留网络名称空间,但是不做任务网络相关的配置
bridge 网桥:
docker创建一个容器时会做如下网络相关操作:
1 创一对虚拟网卡,也就是veth pair
2 本地主机一端桥接到默认的docker0 网桥或指定的网桥上面,并有一个唯一的名字veth998345
3 容器一端放到新起的容器内部并修改名称为eth0,这个网卡只在容器内部可见
4 从网桥可用地址中分配一个地址给容器的eth0
5 配置默认路由到网桥
网桥相关命令:
brctl show 查看网桥
在容器中查看路由网关指向docker0
在docker0 会维护一张mac表,mac地址对应的端口,容器之间通过docker0的mac表就可以通信了
这个mac地址对应的veth的mac
查看网卡对对应的另一个网卡 ,网卡对一直可以更加索引号去找
在容器内查看eth0 的网卡索引号 索引号4 与上面的veth2d0a9c7@if4 网卡对应
端口映射是如何实现的:
iptables 规则:
docker run -d -p 80:80 --name nginx2 nginx 创建一个容器做端口映射
访问本机的80端口,数据包进入到宿主机,因此涉及到PREROUTING 和INPUT 链,我们是通过做宿主机和容器端口之间的映射,所以会涉及到端口的转换, nat表负责维护网络地址的转换
查看nat 表
# iptables -t nat -nvL PREROUTING
有一条docker的数据
规则利用iptables的addrtype 拓展,匹配网络类型为本地的包,查看确定哪些是匹配本地
也就是说目标地址类型匹配到这些的,会执行iptables 规则中的target ,target一般时一指对数据包进行什么样的操作,常见的为ACCEPT 或者DROP 此处target 值为docker ,docker 是自定义的一个docker 链,会把某类规则放在docker的自定义链中
查看docker链 这条规则主机收到目的端口80的tcp流量包进行DNAT 转换发往172.17.0.2 的80
数据包在出方向走POSTROUING
iptables -t nat -nvL POSTROUTING
MASQUERADE 其实是一种更灵活的SNAT 把原地址转化为宿主机出口的ip
这条规则是将原地址172.17.0.0/16 的包(也就是从容器产生的包,并不是docker0网桥发出的包) 进行原地址转换,转为宿主机的网卡地址,包从容器发出,会路由到docker0,网桥根据宿主机的路由规则转给宿主机的eth0接口,eth0网口发出这条规则会生效进行原地址的转换。
抓包查看宿主机的eth0 和容器的eth0 端口
#tcpdump -i eth0 port 80 -w host.casp
4 docker 安装部署
4,1 docker的组成架构
三大核心要素: 镜像 , 容器 ,仓库
docker的工作流:
1 docker 客户端执行的命令调用docker 进程服务
2 docker 服务进程会去仓库拉去镜像
image: 镜像 用于构建容器 , 也可以通过dockerfile文件来描述镜像
containcer: 容器, 用于跑应用程序的,本质是利用namespace 与cgroup等技术在宿主机上创建独立的虚拟空间
network : docker 网络
volumes: docker 存储卷
docker 有两个版本: 企业版EE 社区版 CE
docker 网络数据包流程
当安装docker 后会有一个docker0的网桥, 172.16.17.1/16
docker 做端口映射会生成一个虚拟网桥接口, 这个接口与docker 容器中的网络接口进行了绑定
宿主机上通过iptables 进行数据转发给docker0网桥,网桥再转发到这个虚拟的网桥接口上, 再转发到容器
lsmodbe br_netfilter 加载内核防火墙功能
lsmod|grep br_netfilter 查看是否开启流量转发功能
启动一个nginx 容器发现
docker run -d --name nginx -p 80:80 nginx
ps -ef |grep nginx 会在系统跑一个nginx进程
再查看这个进程的父进程好103305 发现这个进程是容器所创建的
查看容器
当该容器停止后,nginx进程会停止掉同时端口映射创建的虚拟网卡接口也会消失,但是只要docker ps 可以查看这容器则数据不会丢
4.2 yum仓库安装
centos的基础创库
wget -o /etc/yum.repos.d/Centos.Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo
epel仓库
wget -o /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo
关闭防火墙
systemctl disabale firewalld
systemctl stop firewalld
iptables -F 清空下现有的规则
getenforce 查看是否关闭seliun
4.3 软件安装
内核最好不要低于3.10
开启内核转发功能 :
# cat < /etc/sysctl.d/docker.conf
> net.bridge.bridge-nf-call-ip6tables =1
> net.bridge.bridge-nf-call-iptables = 1
> net.ipv4.conf.default.rp_filter = 0
> net.ipv4.conf.all.rp_filter = 0
> net.ipv4.ip_forward=1
> EOF
# sysctl -p /etc/sysctl.d/docker.conf 重新加载内核
遇到报错信息:
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-ip6tables: 没有那个文件或目录
sysctl: cannot stat /proc/sys/net/bridge/bridge-nf-call-iptables: 没有那个文件或目录
解决:
# modprobe br_netfilter
yum list docker-ce --showduplicates |sort -r 查看环境中yum仓库里面的docker 包
下载dcoker-ce的yum仓库
# curl -o /etc/yum.repos.d/docker-ce.repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
# yum install docker-ce-20.10.20-3.el7 安装指定版本docker-ce
配置docker 镜像加速器:
https://cr.console.aliyun.com/cn-hangzhou/instances.mirrors aliyun地址上注册一个账号,里面有免费的镜像仓库
# vim /etc/docker/daemon.json
{
"registry-mirrors": [
"https://8xpk5wnt.mirror.aliyuncs.com",
“https://dockerhub.azk8s.cn”,
"https://registry.docker-cn.com",
"https://0t2k4d59.mirror.aliyuncs.com",
"https://uy35zvn6.mirror.aliyuncs.com"
]
}
]# systemctl daemon-reload
# systemctl restart docker
#systemctl enable docker
启动后查看docker版本
# docker version
5 docker 基本操作
镜像操作
docker search nginx 搜索镜像
docker pull nginx 下载镜像
docker images 本地镜像查看 -q 只列出镜像id
docker images --format “{{.ID}}” 指定格式显示 , 这里只显示出镜像id
docker images --format ‘’table {{.ID}}\t{{.Tag}}” 表格形式显示
docker rmi id 删除镜像
docker rmi `docker images -aq` 批量删除镜像
docker image save centos:last > centos.tar 导出镜像
docker load -i centos.tar 导入镜像
dcoker save 镜像名 > name.tar 保存本地镜像
docker image inspect id 查看详情
docker history reids 查看redis镜像的构建过程 :
容器操作命令:
docker run -d -p 80:80 容器名 镜像id 运行一个容器 -d:运行在后端 -p 端口映射; -P 随机给宿主机暴露一个端口 ; run 创建加启动,如果镜像本地不存在则会去下载镜像 --name: 制定容器名
-v : 存储卷挂载到容器 -e : 配置一个环境变量
docker ps -a 查看本地运行的容器 -a : 显示所有的容器包括退出的 ; -q 只显示id号
docker exec -it dockername bash 进入docker终端, -i :交互式 -t: 开启一个终端 --rm 容器退出时删除容器
docker logs 容器id 查看容器日志 -f 实时刷新日志 --tail 5 最新的5条日志
docker inspect 容器id 查看容器详细信息
docker commit 容器id 新镜像名 容器的提交为镜像,在本地images 可以查看到该镜像
docker port 容器id 查看容器端口的 转发情况
docker build -t 'xiao/flask_web' . 构建镜像 -t 加上标签 ; . 表示基于当前目录
docker stats 容器id 查看容器资源使用
docker top 容器id 查看容器中跑的进程
注意:
容器内部必须有一个运行在前台的进程,不然容器的退出
docker的生命周期:
镜像的原理:
docker镜像时分层构建的,他是不包括内核的
dockerfile 实践
基于dockerfile 自行编写指令,创建出一个镜像
FROM : 指定一个基础镜像 FROM nginx
MAINTAINER: 指定作者的信息
RUN : 执行的linux 命令
USER: 用指定用户去操作
ADD: 添加一个宿主机文件到容器内 并自动解压
COPY: 复制一个宿主机文件到容器内 保留原文件的原属性 权限 ( copy 宿主机文件 容器目录路径 )
WORKDIR: 当前工作目录 就像cd 一样进行目录切换
VOLUME: 定义存储卷路径 ( VOLUME ["/data1","/data2"]) 可以定义多个目录 容器启动会自动挂载指定的目录
EXPOSE: 在容器内暴漏一个端口
ENV : 定义环境变量 (ENV NAME="yuchao") 后面操作通过$NAME 来调用使用
ARG: 定义变量, 他只是用于镜像构建时候用的变量, 容器运行变量就会失效
CMD: 容器启动时运行的指令 CMD [ "bin/bash"] CMD ["sh","-c","echo $PATH" ] , 如果在运行容器run时 传一个参数,则会覆盖他
ENTRYPOINT: 作用和cmd 作用一样,但是当指定了ENTRYPOINT 之后,CMD 的语句就有变化了,而是把CMD 当做参数传递给ENTRYPOINT
构建docker 镜像 : docker build . . 点表示当前目录下 --no-cache 不使用历史构建缓存
docker tag 镜像id 镜像名 : 重新给镜像打标签
docker的无状态与有状态:
无状态:是指容器运行后,容器内产生数据,不重要的数据,可以随意删除的容器
有状态: 将容器内的数据,持久化的存储在宿主机上面 ,一般会在dockerfile 中定义了volume的参数