Docker
原理与安装
原理
容器技术的核心有以下几个内核技术组成:
- Cgroups (Control Groups) -资源管理
- SELinux 安全
- NameSpeace - 命名空间
Linux的NameSpace:
UTS主机名、NETWORK网络、MOUNT文件系统、USER用户、PID进程、IPC进程信号
Docker是什么
- Docker是一款软件
- Docker是完整的一套容器管理系统
- Docker可以让用户非常方便的使用容器技术,而不需要过多关心底层内核的实现
Docker的优缺点
优点
相比于传统虚拟化技术,容器更加简洁高效
传统虚拟机需要给每个VM安装造作系统
容器使用的共享公共库和程序
缺点
容器的隔离性没有虚拟化强
共用Linux内核,安全性有先天缺陷
安装环境
- 需要64位操作系统
- 至少RHEL6.5以上的版本,强烈推荐RHEL7
- 关闭防火墙(不是必须)
- 2G 2CPU 20G
Yum仓库配置
系统包位于光盘CentOS7-1804.iso中
Docker软件包位于云盘kubernetes/docker中
Docker依赖软件包位于云盘kubernetes/extras中
1 | yum install httpd createrepo |
给客户机配置yum源
1 | baseurl=file:///var/centos-1804 |
关闭防火墙和SELinux
1 | systemctl disable --now firewalld |
安装docker
- 开启路由转发
1 | vim /etc/sysctl.conf |
- 安装软件包
1 | yum install -y docker-ce |
镜像管理
什么是镜像?
- 镜像是启动容器的核心
- 在docker中容器是基于镜像启动的
- 镜像采用分层设计
- 使用COW技术
镜像来源
镜像可以从官方仓库下载,也可以自己制作
官方镜像仓库:
查看本机镜像:
- docker images
获取镜像
查找镜像
docker search 关键字
docker search busybox
下载镜像
docker pull 镜像名称:标签
docker pull docker.io/busybox
镜像的备份与恢复
备份镜像(导出镜像)
docker save 镜像名称:镜像标签 -o 备份文件名 (tar格式)
docker save docker.io/busybox:latest -o busybox.tar
恢复镜像(导入镜像)
docker load -i 备份文件名称
docker load -i busybox.tar
镜像的名称和标签
- 指定镜像的方法
- 每一个镜像都对应唯一的镜像id
- 镜像名称(文件名称)+标签(路径)==唯一
- 每一个镜像都有标签,如果没写就是默认标签latest
- 我们在调用镜像的时候,如果没有指定默认也是latest
导入已有镜像,镜像的备份包位于kubernetes/docker-images下
- centos
- nginx
- redis
- Ubuntu
docker容器管理
运行容器
docker run 命令
docker run -参数 镜像名称:镜像标签 启动命令
查看run的参数
docker help run
man docker-run
run = 创建+启动+进入
启动centos容器,并进入容器
- 参数-i,交互式
- 参数-t,终端
- 参数-d,后台运行
- 参数–name,容器名字
docker run -it centos:latest /bin/bash
镜像管理命令
查看镜像
- docker images
查找镜像(在官方仓库查找)
- docker search
删除镜像
- docker rmi 镜像名称:镜像标签
上传下载镜像
docker pull 镜像名称:镜像标签
docker push 要上传的镜像名称:镜像标签
备份恢复镜像
- docker save 镜像名称:镜像标签 -o 备份文件名称
- docker load -i 备份文件名称
查看镜像的制作历史
- docker history 镜像名称:镜像标签
查看镜像的信息
- docker inspect 镜像名称:镜像标签
镜像的新名称和标签
- docker tag 镜像名称:镜像标签 新镜像名称:新的标签
容器管理命令
启动容器
- docker run -参数 镜像名称:镜像标签 启动命令
查看容器
- docker ps [-a 所有容] [-q 只显示容器id]
删除容器
docker rm 容器id
docker rm -f $(docker ps -aq)
将容器保存为镜像
- docker commit -a “作者” -m “文字说明” 容器id 镜像名:标签
容器管理命令启动、停止、重启
- docker start|stop|restart 容器id
查看容器内进程
- docker top 容器id
拷贝文件
- docker cp 本机文件路径 容器id:容器内路径(上传)
- docker cp 容器id:容器内路径 本机文件路径(下载)
查看容器信息
- docker inspect 容器id
连接容器启动进程
- docker attach 容器id
- exit 退出后容器关闭
- 有些进程无法与用户交互 不能管理容器
连接容器,启动新进程
- docker exec -it 容器id 启动命令(/bin/bash)
容器与应用
前台服务/后台服务
容器的启动进程后台运行 ≠ 容器在后台运行
容器的服务
前台服务(-it)
一般能与用户交互的程序,比如/bin/bash,/bin/sh等
后台服务(-itd)
一般是一个程序,比如apache、nginx、redis等
容器服务安装
在centos容器中安装apache
启动一个容器
docker run -it –name myapache centos:latest
配置yum仓库(与宿主机使用相同的仓库)
–删除容器内默认的仓库配置(容器内执行)
rm -f /etc/yum.repos.d/*.repo
–把宿主机上的仓库配置文件拷贝到容器内(在宿主机上执行)
docker cp local.repo myapache:/etc/yum.repo.d/
安装apache (容器内执行)
yum install -y httpd php
查看启动文件及变量(容器内执行)
–文件路径/usr/lib/systemd/httpd.service
–启动命令可以查看service文件中的ExecStart
–环境变量查询服务文件中EnvironmenrFile指定的文件内容
cat /usr/lib/systemd/system/httpd.service
cat /etc/sysconfig/httpd
启动服务(容器内执行)
1 | 设置默认首页 |
验证服务
curl http://容器IP/ #访问
自定义镜像
COW技术原理
- Copy On Write 写时复制技术
- 直接映射原始盘的数据内容
- 当数据有写入需求时,写入之前自动将数据块拷贝存入前端盘中后,对前端盘进行修改
- 原始盘始终是只读的
自定义镜像原理
- 镜像采用分层设计
- 创建读写层
- 修改配置
- 重新打包
基础镜像
- 容器(运行各种应用)
- 应用层镜像(多个)
- 基础镜像 (在此制作)
- 原始镜像(官方下载版)
制作基础镜像
commit命令
使用commit制作基础镜像
- 启动原始镜像,配置yum源,并安装软件包
- 使用commit制作新的镜像
1 | docker run -it centos:lastest |
commit的局限
- 很容易制作简单的镜像,但碰到复杂的境况就十分不方便,例如碰到以下情况:
- 需要设置默认的启动命令
- 需要设置环境变量
- 需要指定镜像开放某些特定的端口
- Dockerfile是一种更强大的镜像制作方式
- 编写类似脚本的Dockerfile文件,通过该文件制作镜像
Dockerfile语法
- FROM:基础镜像
- MAINTAINER:镜像维护者姓名
- RUN:制作镜像时执行的命令
- ADD:复制文件到镜像,自动解压
- COPY:复制文件到镜像,不解压
- EXPOSE:声明开放的端口
- ENV:声明容器启动后的环境变量
- WORKDIR:定义容器默认工作目录(等于cd)
- CMD:容器启动时执行的命令,仅可以有一条CMD
- ENTERYPOINT : 类似于 CMD 指令,但其不会被 docker run 的命令行参数指定的指令所覆盖
- VOLUME : 定义匿名数据卷
- ONBUILD : 用于延迟构建命令的执行就是用此镜像构建新的镜像时执行的命令
- LABEL : 用来给镜像添加一些元数据(metadata),以键值对的形式
- HEALTHCHECK : 用于指定某个程序或者指令来监控 docker 容器服务的运行状态。
- ARG : 构建参数,与 ENV 作用一至。不过作用域不一样。ARG 设置的环境变量仅对 Dockerfile 内有效
docker build 命令
- docker build 根据Dockerfile里的内容生成镜像
- 使用Dockerfile工作流程
- 编写Dockerfile
- 生成镜像
- docker build -t 镜像名称:标签 Dockerfile所在目录
服务镜像案例
- 创建apache + php 服务镜像
- 核心配置CMD [“/usr/bin/httpd”,”-DFOREGROUND”]
1 | vim Dockerfile |
- 验证镜像服务
1 | 创建镜像,并验证 |
docker与微服务
微服务是什么
- 微服务是一种架构模式,它提倡将单一应用程序划分成一组小的服务,服务之间互相协调、互相配合,为用户提供最终价值
- 微服务架构有别于更为传统的单体式方案,将应用拆分为多个核心功能,可以单独架构和部署
- 微服务架构不只是应用核心功能间的这种松散耦合,它还涉及如何进行服务间通信以应对不可避免的故障、满足未来的可拓展性并实现新的功能集成
微服务优点
它解决了复杂性的问题。它将单体应用分解为一组服务。虽然功能总量不变,但应用程序已被分解为可管理的模块或服务,这种体系结构使得每个服务都可以独立开发、运行,降低了服务的耦合性更适合CI/CD
微服务的优点是
高度可拓展性、出色的弹性、易于部署、易于访问、更加易于开发
松耦合高内聚
如何构建微服务
- 微服务的核心就是“拆”
- 如何拆分服务?
- 要想构建微服务就要理清各个服务之间的关系
- 保持服务的持续演进,使服务能够快速、低成本地拆分和合并,以快速响应业务的变化,持续迭代
- docker这种应用的管理模式正是微服务的思想,每个容器承载一个服务。一台计算机同时运行多个容器,从而就能很轻松地模拟出复杂的微服务架构
高级服务镜像概述
制作一个Nginx + php-fpm 服务
容器服务拆分
拆分Nginx与php-fpm服务
nginx镜像
nginx只负责前端静态页面
php-fpm镜像
php-fpm通过内部网络为nginx提供解析服务
创建php-fpm镜像
- www.conf 配置文件(/etc/php-fpm.d/)
- listen = 0.0.0.0:9090 # 修改
- listen.allwoed_cllents=127.0.0.1 #删除
Dockerfile
1 | FROM myos:latest |
查询系统服务
cat /usr/lib/systemd/systemd/php-fpm
环境变量文件
EnvironmentFile=/etc/sysconfig/php-fpm
查询是否有需要定义变量参数
/etc/sysconfig/php-fpm
启动命令
ExecStart=/usr/sbin/php-fpm –nodaemonize
nginx镜像
- nginx镜像创建技巧
- nginx一般采用编译安装,在容器内编译不容易排错也不便于管
- Dockerfile中ADD可以将一个压缩包在容器内解压释放
- 利用这一特性,我们可以在外部编译nginx,并把编译好的文件打包,使用打包文件构建nginx镜像服务
- nginx进程默认在后台进行,我们必须强制在前台运行
- 使用参数daemon off;
nginx安装包
- 编译nginx并打包
1 | yum install -y gcc make pcre-devel openssl-devel #依赖包 |
nginx镜像创建
- Dockerfile
1 | FROM myos:latest |
创建镜像
docker build -t myos:nginx .
容器服务管理
对外发布容器服务
怎么访问docker服务
- 默认容器可以访问外网
- 但外部网络的主机不可以访问容器内的资源
- 容器每次创建IP地址都会改变
- 解决这个问题的最佳方法是端口绑定
- 容器可以与宿主机的端口进行端口绑定
- 从而把宿主机变成对应的服务,不用关心容器的IP地址
发布docker任务
我们使用-p参数把容器端口和宿主机端口绑定
同一宿主机端口只能绑定一个容器服务
-p [可选ip]:宿主机端口:容器端口 (可有多个)
例如:把宿主机变成apache
docker run -itd -p 80:80 myos:httpd
例如:把宿主机变成nginx
docker run -itd -p 80:80 myos:nginx
容器共享卷
nginx解析PHP
nginx不能解析PHP怎么办?
修改nginx配置文件,把PHP交给后端服务解析
php-fpm容器作为后端解析php程序
如何修改nginx配置文件
进入容器修改
卷的用途
- Docker容器不适合保存任何数据
- 数据文件与配置文件频发更改
- 修改多个容器内的数据非常困难
- 多容器之间有数据共享、同步需求
- 重要数据在容器内不方便管理易丢失
- 解决这些问题请使用主机卷映射功能
主机卷的映射
docker可以映射宿主机文件或目录到容器中
- 目标对象不存在就自动创建
- 目标文件存在就直接覆盖掉
- 多容器可以映射同一目标对象来达到数据共享目的
启动容器时,使用-v映射参数(可以有多个)
- docker run -itd -v 宿主机对象:容器内对象 镜像名称:标签
- docker run -itd -v 宿主机:容器:ro centos(容器内只读)
卷映射案例
在宿主机上修改nginx配置文件,把配置文件通过卷映射到容器
1 | 启动PHP后端容器 |
docker 的数据卷容器
- 使用 dockerfile 构建包含数据卷的镜像
1 | vim dockerfile |
挂载数据卷容器的方法
- docker run –volumes-from [container name]
1 | volume这个镜像是上面创建的带两个数据卷/datavolume和/ddatavolume6的镜像 |
- 创建一个新容器挂载刚才 data-volume 这个容器创建的数据卷
1 | docker run --name data-volume2 --volumes-from data-volume -itd centos /bin/bash |
docker 数据卷的备份和还原
- 数据备份方法
- docker run –volumes-from [container name] -v $(pwd):/backup centos tar czvf /backup/backup.tar [container data volume]
1 | docker run --volumes-from data-volume2 -v /root/backup:/backup --name datavolume-copy centos tar zcvf /backup/data-volume2.tar.gz /datavolume6 |
- 数据还原方法
- docker run –volumes-from [container name] -v $(pwd):/backup centos tar xzvf /backup/backup.tar.gz [container data volume]
1 | 删除lucky.txt文件 |
容器网络通信
nginx如何调用后端php-fpm
- php-fpm监听网络端口,从网络把服务传递进来
php-fpm与nginx如何共享网页文件
后端解析传递的文件是文件路径,可以使用宿主机的共享卷
在nginx和php-fpm中共享目录
php-fpm经常变化怎么办
docker 容器的网络基础
安装 docker 的时候,会生成一个 docker0 的虚拟网桥
Linux 虚拟网桥的特点,可以设置 ip 地址,相当于拥有一个隐藏的虚拟网卡
1 | ip addr |
每运行一个 docker 容器都会生成一个 veth 设备对,这个 veth 一个接口在容器里,一个接口在物理机上。
- 安装网桥管理工具
1 | brctl show可以查看到有一个docker0的网桥设备,下面有很多接口,每个接口都表示一个启动的docker容器 |
docker 容器的互联
- 下面用到的镜像的 dockerfile 文件
1 | FROM centos |
允许所有容器间互联
- 第一种方法
1 | 基于inter-image镜像启动第一个容器test1 |
上述方法假如 test1 容器重启,那么在启动就会重新分配 ip 地址,所以为了使 ip 地址变了也可以访问,可以采用给目标主机设置别名的方法
docker link 设置网络别名
- 第二种方法
可以给容器起一个代号,这样可以直接以代号访问,避免了容器重启 ip 变化带来的问题
- docker run –link=[CONTAINER_NAME]:[ALIAS] [IMAGE] [COMMAND]
1 | 启动一个 test3 容器 |
docker的网络通信模式
- host模式,与宿主机共享网络,使用–net =host 指定
- container模式,共享其他容器的网络命名空间,使用–net =container:NAME orID 指定
- none模式,无网络模式,使用–net =container:NAME orID 指定
- bridge模式,默认模式
- 自定义网络,自由创建桥接网络或者overlay网络
none 模式
Docker 网络 none 模式是指创建的容器没有网络地址,只有 lo 网卡
1 | docker run -itd --name none --net=none --privileged=true centos |
container 模式
Docker 网络 container 模式是指,创建新容器的时候,通过–net container 参数,指定其和已经存在的某个容器共享一个 Network Namespace。
因此新建的容器就不会拥有自己独立的 IP,而是共享左边容器的 IP ,端口范围等网络资源,两个容器的进程通过 lo 网卡设备通信。
- 和已经存在的 none 容器共享网络
1 | docker run --name container2 --net=container:none -it --privileged=true centos |
bridge 模式
默认选择 bridge 的情况下,容器启动后会通过 DHCP 获取一个地址
- 创建桥接网络
1 | docker run --name bridge -it --privileged=true centos bash |
host 模式
Docker 网络 host 模式是指共享宿主机的网络
- 共享宿主机网络
1 | docker run --name host -it --net=host --privileged=true centos |
容器网络共享
1 | mkdir /var/webroot # 创建共享网页目录 |
docker 资源配额
docker 容器控制 cpu
Docker 通过 cgroup 来控制容器使用的资源限制,可以对 docker 限制的资源包括 CPU、内存、磁盘
查看配置份额的帮助命令:
docker run –help | grep cpu-shares
-c, –cpu-shares int CPU shares (relative weight)
CPU shares (relative weight) 在创建容器时指定容器所使用的 CPU 份额值。cpu-shares 的值不能保证可以获得 1 个 vcpu 或者多少 GHz 的 CPU 资源,仅仅只是一个弹性的加权值。
默认每个 docker 容器的 cpu 份额值都是 1024。在同一个 CPU 核心上,同时运行多个容器时,容器的cpu 加权的效果才能体现出来。
两个容器 A、B 的 cpu 份额分别为 1000 和 500,结果会怎么样
情况 1:A 和 B 正常运行,占用同一个 CPU,在 cpu 进行时间片分配的时候,容器 A 比容器 B 多一倍的机会获得 CPU 的时间片。
情况 2:分配的结果取决于当时其他容器的运行状态。比如容器 A 的进程一直是空闲的,那么容器 B是可以获取比容器 A 更多的 CPU 时间片的; 比如主机上只运行了一个容器,即使它的 cpu 份额只有50,它也可以独占整个主机的 cpu 资源。
cgroups 只在多个容器同时争抢同一个 cpu 资源时,cpu 配额才会生效。因此,无法单纯根据某个容器的 cpu 份额来确定有多少 cpu 资源分配给它,资源分配结果取决于同时运行的其他容器的 cpu 分配和容器中进程运行情况。
1 | 给容器实例分配 512 权重的 cpu 使用份额 |
CPU core 核心控制
- 参数:–cpuset 可以绑定 CPU
对多核 CPU 的服务器,docker 还可以控制容器运行限定使用哪些 cpu 内核和内存节点,即使用–cpuset-cpus 和–cpuset-mems 参数。对具有 NUMA 拓扑(具有多 CPU、多内存节点)的服务器尤其有用,可以对需要高性能计算的容器进行性能最优的配置。如果服务器只有一个内存节点,则–cpuset-mems 的配置基本上不会有明显效果。
- CPU 配额控制参数的混合使用
在上面这些参数中,cpu-shares 控制只发生在容器竞争同一个 cpu 的时间片时有效。 如果通过 cpuset-cpus 指定容器 A 使用 cpu 0,容器 B 只是用 cpu1,在主机上只有这两个容器使用对应内核的情况,它们各自占用全部的内核资源,cpu-shares 没有明显效果。
容器 A 和容器 B 配置上 cpuset-cpus 值并都绑定到同一个 cpu 上,然后同时抢占 cpu 资源,就可以看出效果了。
测试 cpu-shares 和 cpuset-cpus 混合使用运行效果,就需要一个压缩力测试工具stress 来让容器实例把 cpu 跑满。
- stress 命令
- linux 系统压力测试软件 Stress 。
1 | yum install -y epel-release |
测试 cpuset-cpus 和 cpu-shares 混合使用运行效果,就需要一个压缩力测试工具 stress 来让容器实例把 cpu 跑满。 当跑满后,会不会去其他 cpu 上运行。 如果没有在其他 cpu 上运行,说明cgroup 资源限制成功。
创建两个容器实例:docker10 和 docker20。 让 docker10 和 docker20 只运行在 cpu0 和cpu1 上,最终测试一下 docker10 和 docker20 使用 cpu 的百分比。
1 | 指定docker10只能在cpu0和cpu1上运行,而且docker10的使用cpu的份额512 |
- 进入 docker10,使用 stress 测试进程是不是只在 cpu0,1 上运行
1 | docker exec -it docker10 /bin/bash |
- 在物理机另外一个虚拟终端上运行 top 命令,按 1 快捷键,查看每个 cpu 使用情况,可看到正常。只在 cpu0,1 上运行
1 | top - 11:08:13 up 1:56, 3 users, load average: 1.33, 0.34, 0.15 |
- 然后进入 docker20,使用 stress 测试进程是不是只在 cpu0,1 上运行,且 docker20 上运行的 stress 使用 cpu 百分比是 docker10 的 2 倍
1 | docker exec -it docker20 /bin/bash |
- 在另外一个虚拟终端上运行 top 命令,按 1 快捷键,查看每个 cpu 使用情况
注:两个容器只在 cpu0,1 上运行,说明 cpu 绑定限制成功。而 docker20 是 docker10 使用 cpu 的 2倍。说明–cpu-shares 限制资源成功。
1 | top - 11:10:37 up 1:59, 3 users, load average: 3.76, 1.75, 0.70 |
docker 容器控制内存
- Docker 提供参数-m, –memory=””限制容器的内存使用量
1 | 允许容器使用的内存上限为 128M: |
docker 容器控制 IO
- 防止某个 Docker 容器吃光你的磁盘 I / O 资源
1 | docker run --help | grep write-bps |
docker 容器运行结束自动释放资源
- 当容器命令运行结束后,自动删除容器,自动释放资源
1 | docker run --help | grep rm |
私有仓库
Harbor
另起一台主机
为 Harbor 自签发证书
1 | mkdir /data/ssl -p |
安装Harbor
- 初始化操作
1 | 关闭防火墙 |
- 安装docker-ce
1 | 配置docker-ce国内yum源 |
- 修改内核参数
1 | br_netfilter 模块用于将桥接流量转发至 iptables 链,br_netfilter 内核参数需要开启转发。 |
- 安装harbor
- 下载位置https://github.com/goharbor/harbor/releases/tag/
1 | 创建安装目录 |
修改docker配置
1 | vim /etc/docker/daemon.json |
docker-distribution
安装私有仓库(服务端)
- yum install -y docker-distribution
启动私有仓库,并设置开机自启动
- systemctl enable –now docker-disrtibution
私有仓库的配置
/etc/docker-distribution/registry/config.yml
/var/lib/registry
默认端口号5000
我们可以通过curl命令访问仓库
- curl http://仓库ip:5000/v2/_catalog
docker配置文件
1 | vim /etc/docker/daemon.json |
重启docker服务(停止所有容器,容器太多可能会卡死)
docker rm -f $(docker ps -aq)
systenctl restart docker
上传镜像
为镜像创建标签后上传:
- docker tag 镜像:标签 私有仓库ip:5000/镜像:标签
- docker push 私有仓库ip:5000/镜像:标签#上传
- docker tag myos:latest 192.168.1.100:5000/myos:latest
- docker push 192.168.1.100:5000/myos:latest
管理仓库
查看私有仓库中的镜像名称或标签
- 名称:curl http://192.168.1.100:5000/v2/_catalog
- 标签:curl http://192.168.1.100:5000/v2/myos/tags/list
- 数据目录:/var/lib/registry
下载镜像
docker pull 镜像:标签
在客户端上启动docker并配置好daemon.json
在内网中配置好私有仓库文件不用下载镜像可直接运行启动
docker run -itd 192.168.1.100:5000/myos:latest