Docker是一个开源引擎,可以轻松地为任何应用创建一个轻量级、可移植、自给自足的容器。开发者在笔记本上编译测试通过的容器可以批量地在生产环境中部署,包括VMs(虚拟机)、bare metal、OpenStack 集群和其他的基础应用平台,容器完全使用沙箱机制,相互之间没有任何接口。
文章来源:水月imooc · Docker 入门教程,整理:amomini
Docker 项目诞生于 2013 年初,最初是 dotCloud 公司内部的一个业余项目,基于 Google 公司推出的 Go 语言实现。该项目后来加入了 Linux 基金会,遵从了 Apache 2.0 协议,项目代码在 GitHub 上进行维护,开发者可以打包他们的应用以及依赖包到一个可移植的镜像中,然后发布到任何流行的 Linux 或 Windows 机器上。
在软件产品的传统开发流程中,软件从开发到上线,从操作系统安装,到运行环境依赖,再到应用配置,需要消耗大量技术资源在很多琐碎无意义的运维工作上。随着虚拟机和云计算的普及,许多公司开始租用 AWS(亚马逊公司旗下云计算服务平台) 或 OpenStack 的虚拟机,用脚本在这些机器上自动化部署,但这个过程中会碰到云端虚拟机和本地环境不一致的问题, 解决起来依旧费时费力。
为了解决以上这些问题,PaaS 平台服务(Platform-as-a-service)诞生了。 PaaS 有应用托管的能力,提供与开发环境相同的运行环境。PaaS 会为每一个应用单独创建一个隔离环境,然后在隔离环境中启动这些应用进程,从而达到多个用户的应用互不干涉地在虚拟机里批量、自动运行起来的目的。
Docker 作为新一代的 PaaS 项目, 它脱胎于 Linux Container (LXC)技术,与先前的 PaaS 不同的是,Docker 把 Cgroups、Namespace 和 UnionFS 等一系列技术整合起来,极大地降低了容器技术的复杂度,提升了开发者的用户体验。Docker 公司定义了以容器镜像为标准的应用打包格式,并且建立 Docker Hub 服务进行镜像分发和协作。这些举措迅速创建了一个良好的社区和合作伙伴生态圈,其中包括 AWS、Google、Microsoft、IBM 等行业巨头和国内的众多公司。Docker具有以下优点:
Docker通常用于如下场景:
Tips:CentOS8 的推荐的新包管理工具是 dnf,所以我们使用 dnf 来安装 Docker。 CentOS7 的版本只需要将 dnf 替换成 yum 即可。
Docker系统有两个程序:docker服务端和docker客户端。其中docker服务端是一个服务进程,管理着所有的容器。docker客户端则扮演着docker服务端的远程控制器,可以用来控制docker的服务端进程。大部分情况下,docker服务端和客户端运行在一台机器上。
dnf config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
# 更新索引
dnf update
# 安装所需依赖
# 目前 Centos8 软件源中的 containerd.io 版本偏低,我们需要手动安装一个新版本,这样才能顺利安装 docker-ce 19.03,否则只能安装老版本的 docker-ce。
# Centos7 用户可以跳过此步。
dnf install -y https://download.docker.com/linux/centos/7/x86_64/edge/Packages/containerd.io-1.2.13-3.2.el7.x86_64.rpm
dnf install -y docker-ce
systemctl stop firewalld
iptables -P INPUT ACCEPT
iptables -F
echo "net.ipv4.ip_forward = 1" | tee -a /etc/sysctl.conf
sysctl -p
systemctl start firewalld
firewall-cmd --add-masquerade --permanent
firewall-cmd --reload
# 设为开机启动
systemctl enable docker
# 启动Docker服务
systemctl start docker
docker version
使用 systemctl 命令管理 Docker 服务
# 启动 Docker 服务
systemctl start docker
# 停止 Docker 服务
systemctl stop docker
# 设定 Docker 服务开机自启动
systemctl enable docker
# 取消 Docker 服务开机自启动
systemctl disable docker
Docker 是一个开源的容器引擎,它的核心就是容器技术,而容器技术其实是一种基于虚拟化的沙盒技术(沙盒(sandbox)是一种安全机制,为运行中的程序提供隔离环境,通常是作为一些来源不可信、具破坏力或无法判定程序意图的程序提供实验之用)。
在计算机中,虚拟化是一种资源管理的技术,它将计算机的各种实体资源,如CPU、网络、内存及存储等,进行抽象后展示出来,使用户更方便地使用这些资源。
容器技术的实质:通过各种手段,修改、约束一个"容器(本质就是进程)”进程的运行状态,按照用户的意图“误导”它能看到的资源,控制它的边界,从而达到环境隔离或者说虚拟化的目的。
容器核心技术之Namespace——可以为容器提供系统资源隔离能力,假如一个容器中的进程需要使用 root 权限,出于安全考虑,我们不可能把宿主机的 root 权限给他。但是通过 Namespace 机制,我们可以隔离宿主机与容器的真实用户资源,谎称一个普通用户就是 root,骗过这个程序。
容器核心技术之 CGroup——Namespace 只能做到系统资源维度的隔离,无法做到硬件资源的控制,我们需要使用机制 Cgroup指定容器应用最大占用多少资源,Linux cgroups 的全称是 Linux Control Groups,它是 Linux 内核的特性,主要作用是限制、记录和隔离进程组(process groups)使用的物理资源(CPU、Memory、IO 等)。CGroup 机制中有以下几个基本概念:
在 Linux 环境中,我们可以执行 ls -al /sys/fs/cgroup/ 查看当前系统的 Cgroup,目录中有若干个子目录,除了 systemd 目录,其他的一个子目录对应一个子系统,子系统功能如下所示。
计算机的文件系统是一种存储和组织计算机数据的方法,它使得对其访问和查找变得容易,文件系统使用文件和树形目录的抽象逻辑概念代替了硬盘和光盘等物理设备使用数据块的概念,用户使用文件系统来保存数据,不必关心数据实际保存在硬盘(或者光盘)的地址为多少的数据块上,只需要记住这个文件的所属目录和文件名。在写入新数据之前,用户不必关心硬盘上的那个块地址没有被使用,硬盘上的存储空间管理(分配和释放)功能由文件系统自动完成,用户只需要记住数据被写入到了哪个文件中。
文件系统是一套实现了数据的存储、分级组织、访问和获取等操作的抽象数据类型。 联合文件系统(Union File System)于2004 年由纽约州立大学开发,它可以把多个目录内容联合挂载到同一个目录下,而目录的物理位置是分开的。UnionFS可以把只读和可读写文件系统合并在一起,具有写时复制功能,允许只读文件系统的修改可以保存到可写文件系统当中。
图中的容器是运行在 debian 容器环境中的 apache 网页应用,这个环境还提供了 emacs 编辑器功能。Docker 引入了层(layer)的概念,将 rootfs 的内容进行了分层管理,有系统层,运行库依赖层等等,可以一层接一层进行增量式挂载叠加。启动容器的时候通过 UnionFS 把相关的层挂载到一个目录,作为容器的根 rootfs。借助于 UnionFS,容器内部的更改都被保存到了最上面的读写层,而其他层都是只读的,这样中间的只读 rootfs 是可以被多个容器复用的。UnionFS 将文件的更新挂载到老的文件之上,而不去修改那些不更新的内容,这就意味着即使虚拟的文件系统被反复修改,也能保证宿主机空间占用保持一个较低水平。
在 rootfs 的基础上,Docker 公司创新性地提出了使用 UnionFS,多个增量 rootfs 联合挂载一个完整 rootfs 的方案,通过“分层镜像”的设计,围绕 Docker 镜像,大家甚至可以协同工作,再加上 Docker 官方提供的镜像仓库,进一步减少了共享镜像的成本,这大大提高了开发部署的效率。
目前 Docker 官方维护了一个公共仓库 Docker Hub,这是一个用于管理公共镜像的地方,我们可以找到各种镜像,也可以把我们自己的镜像推送上去,根据需要也可以搭建私有的镜像仓库,用于管理自己的镜像。容器镜像的操作是增量式的,每次镜像拉取的内容会比原本多个完整的操作系统的小很多,同时镜像只要发布成功,我们就能完全复现这个镜像的完整环境,这样就使得基于容器镜像的团队协作更加便捷。
如果没有账号,需要在 Docker Hub 上免费注册一个 Docker 账号,保存好账号密码用于进入 Linux 环境。
docker login # 登入
docker logout # 登出
docker search # 搜索 redis 镜像
docker pull # 下载 redis 镜像
# 默认会拉取 latest 版本,如果要指定版本,需要在镜像后标记版本,如 dockre pull redis:3.2
docker push <本地镜像名> # 上传镜像
docker image rm (镜像名称或id)> # 删除镜像
# Docker 会自动删掉无用、没有依赖的镜像层
用 docker search 命令可以搜索 Docker Hub 中的镜像,比如搜索redis,可以看到返回很多包含 redis 关键字的信息,返回的信息中从左到右依次是:镜像名字、描述、star关注数、是否官方创建、是否自动创建。
# 搭建 Docker 私有仓库,例如使用Docker运行私有仓库服务registry
docker run -d -v /root/registry:/var/lib/registry -p 6000:5000 --restart=always --name registry registry
# 此命令会启动一个容器,设定本地的 /root/registry 目录存储上传的镜像。
# 配置/etc/docker/daemon.json,添加一行insecure-registries配置
"insecure-registries": ["127.0.0.1:6000"] # 127.0.0.1:6000是自定义镜像仓库服务的地址端口
# 测试私有镜像仓库
# 将redis:latest镜像名称改为127.0.0.1:6000/myredis:v1
# 127.0.0.1:6000/xxx是固定写法,与之前的地址对应
docker tag redis:latest 127.0.0.1:6000/myredis:v1
# 上传到私有仓库
docker push 127.0.0.1:6000/myredis:v1
# 查看私有仓库中的镜像
curl http://127.0.0.1:6000/v2/_catalog
# 删除镜像
docker rmi 127.0.0.1:6000/myredis:v1
# 把redis镜像也删除掉,这样可以清理掉相关的缓存层,使后面镜像的下载过程和结果更清楚
docker rmi redis
# 拉取镜像
docker pull 127.0.0.1:6000/myredis:v1
Docker镜像是静态的,要使用它,就是以镜像为模板,创建并运行Docker容器应用,容器的操作使得容器在不同状态间转换。
Docker 容器的生命周期里分为五种状态,其分别代表着:
# 容器操作
# 1.创建容器(Created 状态),可以通过 `--name` 这个选项来配置容器名。
docker create --name busybox busybox
# 2.启动容器(Running状态)
docker start busybox
docker run --name busybox -d -i busybox # docker run 将 docker create 和 docker start 合并,在创建完成之后会直接启动
# -d 参数, Docker 在启动后将程序与控制台分离,使其进入后台运行。
# -i ( --interactive ) 表示保持终端输入流
# 3.管理容器
docker ps # 罗列出 Docker 中处于运行中的容器,加 -a 选项列出所有状态的容器
# 4.停止和删除容器
docker stop busybox # 停止正在运行的容器
docker rm busybox # 完全删除容器
# 正在运行中的容器默认情况下是不能被删除的,我们可以通过增加 -f 选项 强制停止并删除容器
# 5. 进入容器
# docker exec 进入容器的时候,两个选项不可或缺,即 -i 和 -t ( 合并为 -it )。
# -t ( --tty ) 表示启用一个伪终端,没有它无法看到 bash 内部的执行结果。
docker exec -it busybox sh # 在正在运行的容器中运行指定命令
# 6. 查看容器日志
docker logs busybox # 导出容器的日志信息
# 7. 查看容器的配置信息
docker inspect busybox# 提供详细的容器信息(配置信息等)
Docker 镜像是多个基于 UnionFS 的镜像层依次挂载的结果,而容器的文件系统则是在以只读方式挂载镜像后增加的一个可读可写的文件系统复合而成。Docker 中提供了将容器中的可读可写环境持久化为一个镜像层的方法,即docker commit。 docker commit将容器修改的内容保存为镜像,可以理解为提交容器的更改。
生成变更后的镜像
# 重新创建一个busybox容器
docker run --name busybox -d -i busybox
# 进入容器
docker exec -it busybox sh
# 做些更改,新建个文件
echo 'something' > something
# 查看变更后的目录和文件
ls
# exit退出容器后,使用docker commit进行提交变更
docker commit -m 'something' busybox
# 执行 docker commit 将容器记录成镜像层的时候,会先暂停容器的运行,以保证容器内的文件系统处于一个相对稳定的状态,确保数据的一致性。
为镜像命名
docker tag 6858b9b172b7(镜像id) mybusybox:1.0 # 为镜像取名的命令
# 使用 docker tag能够对已有的镜像创建一个新的别名副本(旧的镜像与名称也会保留)
docker tag mybusybox:1.0 mybusybox:latest
# 也可以在提交变更的时候命名镜像
docker commit -m 'something' busybox mybusybox2:latest
# 将 something:latest镜像,导出到something-latest.tar,-o 选项用来指定输出文件
docker save -o ./something-latest.tar something:latest
# 导入镜像,会延用原有的镜像名称
docker load -i something-latest.tar
# 使用 docker export 命令可以直接导出容器,可以把它理解为 docker commit 与 docker save 的结合体
docker export -o ./something-export.tar busybox
# 使用 docker import并非直接将容器导入,而是将容器运行时的内容以镜像的形式导入。所以导入的结果其实是一个镜像,不是容器。
docker import ./something-export.tar something:export
容器与主机、容器与容器之间是互相隔离的。同时,我们可以通过配置 docker 网络,为容器创建完全独立的网络命名空间,或者使容器共享主机或者其他容器的网络命名空间,以应对不同场景的需要。四种常用的单宿主机网络模式:bridge 模式、host 模式、container 模式、none 模式。
bridge 模式——Docker 服务启动时,会自动在宿主机上创建一个 docker0 虚拟网桥 (Linux Bridge, 可以理解为一个软件虚拟出来的交换机),它会在挂载到它的网口之间进行转发。同时 Docker 随机分配一个可用的私有 IP 地址给 docker0 接口。如果容器使用默认网络参数启动,那么它的网口也会自动分配一个与 docker0 同网段的 IP 地址。
# 测试默认新建的容器是否能互相连通
# 使用 busybox 镜像分别运行 b0,b1 两个容器
docker run -d -t --name b0 busybox
docker run -d -t --name b1 busybox
# 查看两个容器的 IP 地址
docker inspect --format '{
{ .NetworkSettings.IPAddress }}' b0 # 172.17.0.3
docker inspect --format '{
{ .NetworkSettings.IPAddress }}' b1 # 172.17.0.4
# 两个容器互相 ping 一下,证明它们的网络能连通
(1)自定义网桥:使用 docker network 相关命令自定义网桥,创建一个网桥 br0,设定网段是 172.71.0.0/24,网关为 172.71.0.1
docker network create -d bridge --subnet '172.71.0.0/24' --gateway '172.71.0.1' br0
# -d 指定管理网络的驱动方式,默认为bridge
# --subnet 指定子网网段
# --gateway 指定默认网关
docker network ls # 查看当前的 docker 网络列表
# 尝试在使用网桥 br0 来新建运行两个容器,并测试它们的连通性。
docker run -d -t --network br0 --name b2 busybox
docker run -d -t --network br0 --name b3 busybox
docker exec b2 ping b3
docker exec b3 ping b2
# ping 测试过程中,输入的是容器名。在自定义网桥中,容器名会在需要的时候自动解析到对应的 IP,也解决了容器重启可能导致 IP 变动的问题。
(2)端口映射访问容器:将宿主机的本地端口,与指定容器的服务端口进行映射绑定,之后访问宿主机端口时,会将请求自动转发到容器的端口上,实现外部对容器内网络服务的访问。
# 创建名为 n0 的 nginx 容器,映射宿主机 8000 端口到它的 80 端口
docker run -d -t -p 8000:80 --name n0 nginx
# 指定的宿主机端口必须是未被占用的端口,否则操作会失败,且生成一个无法正常启动的容器 n0, 需要手动删除
# 使用 docker port n0 查看 n0 的端口映射信息
docker port n0
# 如果需要绑定多个容器端口,可以连续使用 -p 参数多次指定
docker run -d -t -p 8001:80 -p 8433:443 --name n1 ngin
# 如果不想主动指定宿主机端口,可以使用 -P 参数,宿主机随机使用一个可用端口与容器端口进行映射
docker run -d -t -P --name n2 nginx
# 如果只想使用宿主机上特定的网口与容器进行映射
docker run -d -t -p 宿主机映射网口的 IP 地址:8002:80 --name n3 nginx
iptables -t nat -nL # 查看防火墙
打开浏览器,地址栏输入 http://localhost:8000 或 http:// 宿主机 IP:8000, 都能访问到 n0 的 nginx 服务
使用端口映射访问容器是常用的方式之一,它配置简单,通用性强,可以跨宿主机访问,基本覆盖个人日常使用的场景,但它仍有一些缺陷,端口转发方式的本质是通过配置 iptables 规则转发实现的,效率较低,如果容器的服务端口数量过多,需要配置较多的映射,占用大量宿主机端口,也不便于管理。
host 模式——该模式下启动的容器,网络不再与宿主机隔离,访问容器服务可以直接使用访问宿主机对应的网络端口,且不需要端口转发。
# 以 host 模式启动 nginx 的容器 h0
docker run -d -t --network host --name h0 nginx
启动成功后,在浏览器输入任意的本机地址,都可以打开 nginx 的默认页面,访问宿主机 80 端口就是访问容器的 80 端口,它们是一致的。
host 模式下的容器与宿主机共享同一个网络环境,容器可以使用宿主机的网卡和外界通信,不需要转发拆包,性能好。但 host 模式也有非常严重的缺点:容器没有隔离的网络,会与其他服务竞争宿主机的网络,导致宿主机网络状态不可控,因此无法用在生产环境。
container 模式——可以使一个容器共享另一个已存在容器的网络,此时这两个容器共同使用同一网卡、主机名、IP 地址,容器间通讯可直接通过本地回环 lo 接口通讯。
# 新运行一个 busybox 的容器 b1,设定它共享已存在的容器 b0 的网络
docker run -d -t --network container:b0 --name b1 busybox
# 端口转发设定以已存在的容器为准,出于安全和权限控制的角度,container 模式下运行的容器设定端口转发不生效。
# 查看 b0,b1 的网络配置,验证两者的网络配置是否相同
docker exec b0 ifconfig
docker exec b1 ifconfig
nginx 镜像自带的网络命令非常少,查看网络不方便,而 busybox 的网络命令比较齐全,使用 container 模式,可以快速解决这个问题。
# 运行一个名为 n0 的 nginx 容器,再将它的网络共享给 busybox 容器 n0-net
docker run -d -t --name n0 nginx
docker run -d -t --network container:n0 --name n0-net busybox
# 通过 localhost 访问 n0 的 web 服务,说明通过 container 模式下,共享的网络中的容器能够使用 lo 访问其他容器的服务。
docker exec n0-net telnet localhost 80
# 在交互中输入
# GET /
在 container 模式下的容器,会使用其他容器的网络命名空间,其网络隔离性会处于 bridge 桥接模式(最高)与 host 模式(最低)之间:当容器共享其他容器的网络命名空间,则在容器之间不存在网络隔离;而它们又与宿主机以及其他不在此共享中的容器存在网络隔离。
none 模式——容器有自己的网络命名空间,但不做任何配置,它与宿主机、与其他容器都不连通。
# 新建一个 none 模式的 busybox 镜像 b0
docker run -d -t --network none --name b0 busybox
# 查看它的网络状态, 验证它仅有 lo 接口,不能与容器外通信
docker exec b0 ip a
两个容器之间可以直连通信,但不通过主机网桥进行桥接。解决的办法是创建一对 peer 接口,分别放到两个容器中,配置成点到点链路类型即可。
# 启动 2 个容器
docker run -it -d --net=none --name=none1 busybox
docker run -it -d --net=none --name=none2 busybox
# 找到这两个容器的进程号
docker inspect -f '{
{.State.Pid}}' none1 # 26613
docker inspect -f '{
{.State.Pid}}' none2 # 26681
# 创建网络命名空间的跟踪文件
sudo mkdir -p /var/run/netns
sudo ln -s /proc/26613/ns/net /var/run/netns/26613
sudo ln -s /proc/26681/ns/net /var/run/netns/26681
# 创建一对 peer 接口,然后配置路由
sudo ip link add A type veth peer name B
sudo ip link set A netns 26613
sudo ip netns exec 26613 ip addr add 10.1.1.1/32 dev A
sudo ip netns exec 26613 ip link set A up
sudo ip netns exec 26613 ip route add 10.1.1.2/32 dev A
sudo ip link set B netns 26681
sudo ip netns exec 26681 ip addr add 10.1.1.2/32 dev B
sudo ip netns exec 26681 ip link set B up
sudo ip netns exec 26681 ip route add 10.1.1.1/32 dev B
使用Docker时,产生的数据默认是保存到容器的UnionFS的读写层中的。那么,我们不妨思考下这两个问题:如果不启动容器却想访问数据该怎么办?容器被销毁或损坏,数据也就消失了,这合理吗?
是的,我想我们的答案应该是一样的,容器和数据不应该被绑定在一起。为此,Docker 提供了两类数据管理的方式:挂载宿主机目录或文件;使用数据卷。
通过挂载宿主机的目录或文件,可以在宿主机和容器间方便地共享数据,包括将提前准备好的配置文件挂载到容器,或者在开发调试过程中将代码移入 Docker 环境试运行等。
# 新建一个目录
mkdir -p ~/mydir/tmp
# 在目录中新建一个文件,填充内容 hello docker
echo "hello docker" > ~/mydir/tmp/text.txt
# 新建容器 busybox,将 /mydir/tmp 目录挂载到容器的 /tmp/ 目录,挂载宿主操作系统目录的参数是 -v <宿主机目录路径>:<容器目录路径>
docker run -d -it --name busybox -v ~/mydir/tmp/:/tmp/ busybox
# 查看容器对应的文件内容
docker exec -it busybox cat /tmp/text.txt
# 新建一个容器 busybox2,将 /mydir/tmp/text.txt 文件挂载到容器的 /tmp/text.txt
docker run -d -it --name busybox2 -v ~/mydir/tmp/text.txt:/tmp/text.txt busybox
# 挂载宿主操作系统文件的参数是 -v <宿主机文件路径>:<容器文件路径>
# 查看容器对应的文件内容
docker exec -it busybox2 cat /tmp/text.txt
使用数据卷的好处在于:我们不必自己维护一个外部路径挂载和存储的关系,借助Docker管理数据,并且通过语义化数据卷命名,更加方便直观地使用它来数据共享。
# 挂载数据卷到容器
docker run -d -it --name busybox -v mydata:/tmp busybox
# 查看容器中数据卷挂载的信息
docker inspect busybox
# 一些常用的数据卷操作
# 删除数据卷
docker volume rm mydata
# 手动创建数据卷
docker volume create mydata
# 删除那些没有被容器引用的数据卷
docker volume prune
数据卷是受控存储,是由 Docker 引擎进行管理维护的。使用卷可以不必处理 uid、SELinux 等各种权限问题,Docker 引擎在建立卷时会自动添加安全规则以及根据挂载点调整权限,并且可以统一列表、添加、删除。另外,除了本地卷外,还支持网络卷、分布式卷。而挂载目录属于用户自行维护,必须手动处理所有权限问题,好处在于这种方式与宿主机的文件交换更方便一些。
数据卷实际上也是宿主机的一个目录,这个目录由 Docker 管理,利用数据卷可以方便地将数据在多个容器中共享。按照 Docker 最佳实践的要求,容器不应该向其存储层内写入任何数据,容器存储层要保持无状态化。所有的文件写入操作,都应该使用数据卷或者绑定挂载宿主目录,在这些位置的读写会跳过容器存储层,直接对宿主发生读写,其性能和稳定性更高。
Docker官方镜像仓库提供了众多高质量的镜像和使用文档,生态也非常活跃。这种简单的部署方式更接近获取软件的方式,在平时的开发测试中,个人的开发机上安装这些服务经常费时费力,到时寻找安装包,甚至可能还要编译源码,结果出现一些意想不到的问题。熟练掌握这种方法部署应用服务,可以快速且高效地获取一致的开发部署环境。
获取Docker 服务:部署时默认使用最新稳定的官方镜像版本,如有版本要求自行在镜像名后标注 :tag 即可。
# 1. Redis:一个使用 ANSIC 编写的开源、支持网络、基于内存、可选持久性的键值对存储数据库
# ①使用 Docker 启动 redis 服务,端口默认,使用host网络模式保障性能
docker run --restart=always --network host -d -it --name myredis redis
# ②将 redis 数据保存到宿主机目录
mkdir -p ~/docker/redis/data # 新建宿主机目录
docker run --restart=always --network host -d -it -v ~/docker/redis/data:/data --name myredis redis
# ③指定自己的配置文件,先将配置文件放到 ~/docker/redis/redis.conf
docker run --restart=always --network host -d -it -v ~/docker/redis/redis.conf:/usr/local/etc/redis/redis.conf --name myredis redis redis-server /usr/local/etc/redis/redis.conf
# 2. Nginx:异步框架的网页服务器,也可以用作反向代理、负载平衡器和HTTP缓存
# ①使用 Docker 启动 redis 服务,端口默认,使用host网络模式保障性能
# 使用自己的html目录,ro设定宿主机目录挂载到容器后,容器对此目录只读
docker run --restart=always --network host -d -it -v ~/docker/nginx/html:/usr/share/nginx/html:ro --name mynginx nginx
# ②指定自己的配置文件,先将配置文件放到 ~/docker/nginx/etc/nginx 目录下
docker run --restart=always --network host -d -it -v ~/docker/nginx/html:/usr/share/nginx/html:ro -v ~/docker/nginx/etc/nginx:/etc/nginx --name mynginx nginx
# 3. MySQL:一个开放源码的关系数据库管理系统
# ①使用 Docker 启动 redis 服务,端口默认,使用host网络模式保障性能。
# my-secret-pw 指定mysql的root用户密码
docker run --restart=always --network host -d -it --name mymysql -e MYSQL_ROOT_PASSWORD=my-secret-pw mysql
# ②将mysql数据保存到宿主机目录
mkdir -p ~/docker/mysql/data # 新建宿主机目录
docker run --restart=always --network host -d -it -v ~/docker/mysql/data:/var/lib/mysql --name mymysql -e MYSQL_ROOT_PASSWORD=my-secret-pw mysql
# ③指定自己的配置文件
# 将配置文件放入 ~/docker/mysql/conf.d/
docker run --restart=always --network host -d -it -v ~/docker/mysql/conf.d/:/etc/mysql/conf.d --name mymysql -e MYSQL_ROOT_PASSWORD=my-secret-pw mysql
实战:使用 flask 调用 redis 容器应用
通过一个flask web应用,实现主页访问计数功能,使用 redis 服务帮助我们实现计数统计功能。这个 redis 服务运行在 Docker 容器中。
# 1. 在 CentOS 中安装 Python3 和flask框架,以及 Python 的 redis 客户端库
# 安装 python3
sudo dnf install -y python3
# 安装flask与redis python客户端
pip3 install redis flask --user
# 2. 将下面的代码保存到~/test/app.py
import flask
import os
from flask import Flask
app = Flask(__name__)
from redis import StrictRedis
from redis import ConnectionPool
# 指定redis服务地址
REDIS_HOST = os.getenv('REDIS_HOST','127.0.0.1')
# 指定redis端口号
REDIS_PORT = os.getenv('REDIS_PORT', '6379')
# 指定redis的数据库
REDIS_DB = os.getenv('REDIS_DB', '0')
# 指定redis的密码
REDIS_PASSWORD = os.getenv('REDIS_PASSWORD', '12345678')
class Redis:
def __init__(self):
self.cli = None
def connect(self):
pool = ConnectionPool(host=REDIS_HOST,
port=REDIS_PORT,
db=REDIS_DB,
password=REDIS_PASSWORD)
return StrictRedis(connection_pool=pool)
def add_pv(self):
self.connection.incr('pv', 1)
def get_pv(self):
count = self.connection.get('pv')
return int(count)
@property
def connection(self):
if self.cli:
return self.cli
else:
self.cli = self.connect()
return self.cli
redis = Redis()
@app.route('/')
def index():
redis.add_pv()
return "Hello World, 本页已访问{}次。
".format(redis.get_pv())
# 3. 在 ~/docker/redis/ 目录下,配置 redis.conf 文件
cd ~/docker/redis
# 获取官方提供的redis配置文件模板
wget http://download.redis.io/redis-stable/redis.conf
# 修改redis密码
echo "requirepass 12345678" >> redis.conf
# 4. 配置文件修改好后,用它来配置 Docker 的 redis 容器应用
docker run --restart=always --network host -d -it -v ~/docker/redis/redis.conf:/usr/local/etc/redis/redis.conf --name myredis redis redis-server /usr/local/etc/redis/redis.conf
redis容器启动完成后,在 ~/test/ 目录下执行 python3 -m flask run,打开Linux系统中的浏览器,输入127.0.0.1:5000 访问这个 web 应用的主页,多次刷新查看效果。
不同的容器部署在不同的宿主机,它们之间的通信方式有3种,桥接网络;端口映射;Docker网络驱动:1. Overlay,2. Macvlan。
# 需要两台装好 Docker 服务的 Linux 虚拟机,并且虚拟机的网络要互通
# 1.创建 macvlan 网络,在两个节点上都进行此操作
docker network create -d macvlan --subnet=192.168.1.0/24 --gateway=192.168.1.1 -o parent=eth1 -o macvlan_mode=bridge macvlan_net
# macvlan 是 kernel 的模块名;
# 192.168.2.0/24是宿主机所在网络的网段;
# 192.168.2.1是网关;
# eth1 是 Docker 宿主机(两台虚拟机)接入192.168.1.0/24 的物理网口
# 2.创建容器并指定 IP
docker run -it -d --net macvlan_net --ip=192.168.1.101 --name b1 busybox #容器b1
docker run -it -d --net macvlan_net --ip=192.168.1.102 --name b2 busybox #容器b2
# 3.测试容器通信
docker exec -it b1 ping 192.168.1.102
docker exec -it b2 ping 192.168.1.101
Dockerfile 是 Docker 中用于定义镜像自动化构建流程的配置文件,在 Dockerfile 中,包含了构建镜像过程中需要执行的命令和其他操作。它可以明确设定 Docker 镜像的制作过程,帮助我们在容器体系下能够完成自动构建。优点:Dockerfile 的体积小,容易进行快速迁移部署;环境构建流程记录了 Dockerfile 中,能够直观的看到镜像构建的顺序和逻辑。
可以将 Dockerfile 理解为一个由上往下执行指令的脚本文件,当我们调用构建命令,通过给出的 Dockerfile 构建镜像时,Docker按照 指令顺序进行对应的操作。Dockerfile 的一般规范有:
初识 Dockerfile
# 在本地新建一个目录,编辑一个名为 Dockerfile 的文件
mkdir -p ~/docker/nginx/
cd ~/docker/nginx
vim Dockerfile
# Dockerfile文件内容 #
# 构建一个基于ubuntu的docker定制镜像
# 基础镜像
FROM ubuntu
# 镜像作者
MAINTAINER amo amo123@domain.com
# 执行命令
## 换成国内的软件源
RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
RUN sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
## 安装nginx
RUN apt update >/dev/null 2>&1
RUN apt install nginx -y >/dev/null 2>&1
# 暴露对外端口
EXPOSE 80
# 在当前 Dockerfile 文件所在目录执行如下命令
# --network=host 使用宿主机的网络连接代理容器的网络(在一些情况下,容器可能无法顺畅地连接外网)。
# -t ubuntu-nginx:v1 指定生产的镜像名称为 ubuntu-nginx ,版本号为 v1。
docker build --network=host -t ubuntu-nginx:v1 .
镜像构建过程
[user@centos8 nginx]$ docker build --network=host -t ubuntu-nginx:v1 .
# 将上下文求发送给Docker引擎
Sending build context to Docker daemon 2.56kB
# 下载依赖的镜像
Step 1/7 : FROM ubuntu
latest: Pulling from library/ubuntu
d51af753c3d3: Pull complete
fc878cd0a91c: Pull complete
6154df8ff988: Pull complete
fee5db0ff82f: Pull complete
Digest: sha256:747d2dbbaaee995098c9792d99bd333c6783ce56150d1b11e333bbceed5c54d7
Status: Downloaded newer image for ubuntu:latest
# 生成镜像 1d622ef86b13
---> 1d622ef86b13
Step 2/7 : MAINTAINER my_name myemail@domain.com
# 运行容器 4eec6e3094f0,在容器内运行上面的这个命令,标记维护者信息
---> Running in 4eec6e3094f0
# 移除临时容器 4eec6e3094f0
Removing intermediate container 4eec6e3094f0
# 生成镜像 6679d1c204e3
---> 6679d1c204e3
Step 3/7 : RUN sed -i 's/archive.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
# 运行容器84d38c20d8c4,在容器内运行上面的这个命令,更换软件源记录
---> Running in 84d38c20d8c4
# 移除临时容器 84d38c20d8c4
Removing intermediate container 84d38c20d8c4
# 生成镜像 83f29f7b055a
---> 83f29f7b055a
Step 4/7 : RUN sed -i 's/security.ubuntu.com/mirrors.ustc.edu.cn/g' /etc/apt/sources.list
# 运行容器 763e4493d93f, 在容器内运行上面的这个命令,更换软件源记录
---> Running in 763e4493d93f
# 移除临时容器 763e4493d93f
Removing intermediate container 763e4493d93f
# 生成镜像 6297f20605d9
---> 6297f20605d9
Step 5/7 : RUN apt update >/dev/null 2>&1
# 运行容器 2665a7e5a2e9,在容器内运行上面的这个命令, 更新软件源缓存
---> Running in 2665a7e5a2e9
# 移除临时容器 2665a7e5a2e9
Removing intermediate container 2665a7e5a2e9
# 生成镜像 fdfed940ca4d
---> fdfed940ca4d
Step 6/7 : RUN apt install nginx -y >/dev/null 2>&1
# 运行 容器 722a9a544643,在容器内运行上面的这个命令, 安装nginx
---> Running in 722a9a544643
# 移除临时容器 722a9a544643
Removing intermediate container 722a9a544643
# 生成镜像 6ee76f7df9e5
---> 6ee76f7df9e5
Step 7/7 : EXPOSE 80
# 运行容器 a12ed3216ee0,在容器内运行上面的这个命令, 暴露80端口
---> Running in a12ed3216ee0
# 移除临时容器 a12ed3216ee0
Removing intermediate container a12ed3216ee0
# 生成最终的镜像 7cf64279ba98
---> 7cf64279ba98
Successfully built 7cf64279ba98
# 将这个镜像标记命名 ubuntu-nginx 版本号v1
Successfully tagged ubuntu-nginx:v1
Dockerfile 的结构——主要包含四部分内容:基础镜像信息;维护者信息;镜像操作指令;容器启动时指令。
Dockerfile 指令详解:
(1)FROM:指明当前的镜像基于哪个镜像构建,用法:FROM <基础镜像:版本>,写上这一行指令后, Dockerfile 就可以构建镜像了,构建出的镜像未做任何修改、没有执行任何命令;
(2)LABEL: 标记镜像信息,给镜像添加标签来标记镜像信息,每个标签一行,用法:LABEL <标签>=<描述>;
(3)MAINTAINER:指定镜像的作者信息,包含镜像的所有者和联系人信息,用法:MAINTAINER < NAME >;
(4)RUN : 运行命令,用法:RUN <命令>,为了保持 Dockerfile 文件的可读性以及可维护性,可以将复杂的RUN指令用反斜杠\分割成多行;
【注】Dockerfile 构建一次之后,apt update 构建的镜像层就会缓存到本地,无论后面这个 Dockerfile 如何更新 apt install 的内容,apt update 镜像缓存也不会更新,这会导致安装的始终是第一次 Dockerfile构建时获取的软件源版本,除非手动删除这些缓存镜像层。
解决方法是用 RUN apt-get update && apt-get install -y 确保 Dockerfiles 每次安装的都是包的最新的版本。
(5)CMD:指定容器的默认执行的命令,用法:CMD [“可执行命令”, “参数1”, “参数2”…],docker run 没有指定其他命令时,CMD 指令会在容器执行。Dockerfile 中 CMD 只能有一个,如果写了多个 CMD,则以最后一个为准,ENTRYPOINT 与 CMD 类似,但不会被 docker run 指定的命令覆盖;
(6)EXPOSE:指定容器将要监听的端口,用法:EXPOSE 端口号,启动容器时,如果使用自动映射 -P 或 --net=host 宿主机网络模式,容器中 EXPOSE 标记暴露的端口与宿主机网络会自动建立关联。如果没有指定 EXPOSE,使用 -p 手动指定端口映射参数也可以访问到容器内提供服务的端口,EXPOSE 显式地标明镜像开放端口,一定程度上提供了操作的便利,也提高了 Dockerfile 的可读性和可维护性;
(7)ENV:定义环境变量,用法:ENV 环境变量名 环境变量值,通过 ENV 定义的环境变量,可以在后面的所有指令中使用,但是不能被 CMD 指令使用;通过 ENV 定义的环境变量,会永久的保存到该镜像创建的任何容器中,我们可以在 docker run 命令中通过 -e 标记来传递环境变量,启动的容器将会使用我们指定的变量值;ARG 指令与 ENV 作用基本一致,区别在于它仅在构建过程中使用,不会保留到容器中;
(8)COPY: 将宿主机文件拷贝到镜像中,用法:COPY <宿主机文件路径> <镜像文件路径>,除了指定完整的文件名外,COPY 命令还支持 Go 风格的通配符,对于目录而言,COPY 只复制目录中的内容而不包含目录自身。ADD和COPY用法类似,一般优先使用 COPY,COPY 只支持简单将本地文件拷贝到容器中,而 ADD 还有从压缩包中提取文件的功能;
(9)VOLUME:指定目录为数据卷存储方式,为了防止运行时用户忘记将需要保存数据的目录挂载为卷,可以事先指定某些目录挂载为匿名卷,这样在运行时如果用户不指定挂载,其应用也不会向容器存储层写入大量数据,用法:VOLUME ["<路径1>", “<路径2>”…] ;
(10)USER:指定运行容器时的用户名或 UID,用法:USER [:< group >] 或USER [:< GID >],当容器中运行的服务不需要管理员权限时,可以先建立一个特定的用户和用户组,为它分配必要的权限,然后通过该命令,使用 USER 切换到这个用户,也可以在docker run中使用-u参数指定用户执行命令,来替代默认设定,如果为了精确控制用户的id,也可以传入uid;
(11)WORKDIR: 切换到镜像中的指定路径,在WORKDIR中需要使用绝对路径,如果镜像中对应的路径不存在会自动创建此目录,使用 WORKDIR 来替代 RUN cd && 的这类切换目录进行操作的指令。
(12) ONBUILD:引用后构建指令, 用法:ONBUILD <其他指令>,ONBUILD 是一个特殊的指令,它后面跟的是其它指令,比如 RUN, COPY 等。
FROM alpine:latest #构建镜像
LABEL MYLABEL="First Test" #给镜像添加标签
MAINTAINER nickname@domain.com #指定镜像的作者信息
LABEL maintainer="[email protected]" #也可以用LABEL来标记
RUN echo 'text' > test.txt #运行命令
CMD ["echo" "hello"] #指定默认执行的命令
EXPOSE 8080 #指定将要监听的端口
ENV PATH /usr/local/nginx/bin:$PATH #定义环境变量
COPY app.py /web/ #将宿主机文件拷贝到镜像中
VOLUME ["/data"] #指定目录为数据卷存储方式
USER www #指定运行容器时的用户名或 UID
WORKDIR /tmp
COPY test.txt #切换到镜像中的指定路径
ONBUILD COPY . /tmp/ #引用后构建指令
Dockerfile多阶段构建镜像:使用多阶段构建可以在一个 Dockerfile 中使用多个 FROM 语句。每个 FROM 指令都可以使用不同的镜像,并表示开始一个新的构建阶段。很方便的将一个阶段的文件复制到另外一个阶段,在最终的镜像中保留下需要的内容即可。在 Dockerfile 文件的同一目录新建一个新的构建脚本,命名为 Dockerfile-multi-stage :
#从ubuntu镜像开始构建, 将第一阶段命名为`build`,在其他阶段需要引用的时候使用`--from=build`参数即可。
FROM ubuntu AS build
# 将宿主机的源码拷贝到镜像中
COPY ./code/helloworld.c .
# 安装依赖 并编译源码
RUN apt update >/dev/null 2>&1 && \
apt install -y gcc >/dev/null 2>&1 && \
cc helloworld.c -o /usr/bin/helloworld
# 第二阶段 从官方的python:alpine基础镜像开始构建
FROM python:alpine
# 镜像维护者信息
MAINTAINER user .com>
# 将第一阶段构建的helloworld 导入到此镜像中
COPY --from=build /usr/bin/helloworld /usr/bin/helloworld
# 安装flask 和 redis 的依赖
RUN pip install flask redis >/dev/null 2>&1
# 设定镜像在切换到/app目录路径
WORKDIR /app
# 将源码导入到镜像
COPY ./code/app.py .
# 设定执行用户为user
RUN useradd user
USER user
# 设定flask所需的环境变量
ENV FLASK_APP app
# 默认启动执行的命令
CMD ["flask", "run", "-h", "0.0.0.0"]
# 将flask的默认端口暴露出来
EXPOSE 5000
docker build -f Dockerfile-multi-stage -t myhello-multi-stage . #执行 build 命令
# 使用此镜像运行容器。使用--net=host,方便使用之前章节中部署的redis容器服务与之进行数据交换
docker run -dit --net=host --name myhello-multi-stage myhello-multi-stage
Docker Compose 是 Docker 官方编排项目,使用 Docker Compose 可以轻松、高效地管理容器,它是一个用于定义和运行多容器 Docker 的应用程序工具。在 Docker Compose 里,通过一个配置文件,将所有与应用系统相关的软件及它们对应的容器进行配置,之后使用 Docker Compose 提供的命令进行启动。
获取 Docker Compose:安装 Docker Compose 可以通过下面命令自动下载适应版本的 Compose,并为安装脚本添加执行权限。
# 下载 docker-compose
wget https://github.com/docker/compose/releases/download/1.26.0/docker-compose-Linux-x86_64
# 下载加速
sudo curl -L https://get.daocloud.io/docker/compose/releases/download/1.25.1/docker-compose-`uname -s`-`uname -m` -o /usr/local/bin/docker-compose
# 移到 /usr/local/bin/docker-compose
sudo mv docker-compose-Linux-x86_64 /usr/local/bin/docker-compose
# 给 docker-compose 执行权限
sudo chmod +x /usr/local/bin/docker-compose
docker-compose -v # 查看安装是否成功
使用 Docker Compose
(1)编写docker-compose.yml:进入dockerfiledir目录,新建redis目录,将之前部署redis容器用到的redis.conf拷贝到redis目录下,修改redis.conf中的bind 127.0.0.1为bind 0.0.0.0,以便其他容器访问此服务。然后新建文件docker-compose.yml,并将下面的内容写入到这个文件中。
version: "3.8"
services:
cache:
image: redis:6.0.5
container_name: my_redis
networks:
- mynetwork
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
app:
build:
context: .
dockerfile: ./Dockerfile-multi-stage
container_name: my_hello
networks:
- mynetwork
environment:
- REDIS_HOST=my_redis
depends_on:
- cache
ports:
- "5000:5000"
networks:
mynetwork:
name: my_network
driver: bridge
(2)使用docker-compose up命令启动:docker-compose up 命令类似于 Docker 中的 docker run,它会根据 docker-compose.yml 中配置的内容,创建所有的容器、网络、数据卷等等内容,并将它们启动。与 docker run 一样,默认情况下 docker-compose up 会在前台运行,我们可以用 -d 选项使其“后台”运行,大多数情况都会加上 -d 选项。
sudo docker-compose up -d #启动
(3)使用docker-compose命令重建:docker-compose 命令默认会识别当前控制台所在目录内的 docker-compose.yml 文件,而会以这个目录的名字作为组装的应用项目的名称。如果需要改变它们,可以通过选项 -f 来修改识别的 Docker Compose 配置文件,- - build 用于执行重建服务镜像,更新镜像时使用。
sudo docker-compose -f ./docker-compose.yml up -d --build #重建
(4)使用docker-compose down命令停止:docker-compose down 命令用于停止所有的容器,并将它们删除,同时消除网络等配置内容,也就是几乎将这个 Docker Compose 项目的所有影响从 Docker 中清除。
docker compose 配置文件指令详解:Docker Compose 的配置文件是一个基于 YAML 格式的文件,与 Dockerfile 采用 Dockerfile 作为默认文件名一样,Docker Compose 的配置文件也有一个缺省的文件名,就是 docker-compose.yml。Docker Compose 将所管理的容器分为三层,分别是工程(project)、服务(service)、容器(container),Docker Compose 定义了一个工程,一个工程包含多个服务,每个服务中定义了容器运行的镜像、参数、依赖,一个服务可包括多个容器实例。
# 指定配置文件的版本号
version: "3.8"
# 服务
services:
# 服务名称cache
cache:
# 标明构建的镜像
image: redis:6.0.5
# 生成的容器名称
container_name: my_redis
# 指定网络
networks:
- mynetwork
# 设定挂载
volumes:
- ./redis/redis.conf:/usr/local/etc/redis/redis.conf:ro
# 容器启动后执行命令
command: ["redis-server", "/usr/local/etc/redis/redis.conf"]
# 服务名称app
app:
# 构建镜像
build:
# 指定上下文
context: .
# 指定构建脚本
dockerfile: ./Dockerfile-multi-stage
# 生成的容器名称
container_name: my_hello
# 指定网络
networks:
- mynetwork
# 配置环境变量
environment:
- REDIS_HOST=my_redis
# 指名容器依赖关系
depends_on:
- cache
# 宿主机与容器端口映射
ports:
- "5000:5000"
# 网络配置,与services在同一层级,注意书写格式对齐
networks:
# 标识自定义的网络,对应容器中指定的网络的名称
mynetwork:
# 在容器网路中展示的名称
name: my_network
# 网络驱动类型
driver: bridge
(1)version:版本号标识我们定义的 docker-compose.yml 文件内容所采用的版本,目前 Docker Compose 的配置文件已经迭代至了第三版,其所支持的功能也越来越丰富;
(2)service: Docker Compose 把 service 作为配置的最小单元。使用时首先要为每个服务定义一个名称,用以区别不同的服务。虽然看上去每个 service 里的配置内容就像是在配置单个容器,但其实 service 代表的是一个应用集群的配置;
(3)image:类似于Dockerfile中的FROM指令。需要注意的是一定要注明镜像的具体版本号,不要使用latest版本(不标明版本即为latest),不同的执行时间latest指代的镜像会发生改变,准确执行唯一指定的容器才能使得整个工程正确运行;
(4)build:另一种指定镜像的方式,通过 build 配置能够直接采用 Dockerfile 构建镜像,定义构建的环境目录。如果通过这种方式指定镜像,那么 Docker Compose 先会执行镜像的构建,之后再通过这个镜像启动容器。Docker Compose 能够指定更多的镜像构建参数,例如 Dockerfile 的文件名,构建上下文,构建参数等等,此外,与image指令类似,希望Dockerfile的构建也是唯一可靠的;
(5)container_name:指定容器名称,如果不指定,会以<项目名称><服务名称><序号>,其中项目名称默认是当前工作目录的名字;
(6)command:如果希望修改 Redis 的启动命令,加入配置文件以便对 Redis 服务进行配置,那么我们可以直接通过 command 配置来修改,command会覆盖镜像中的CMD指令;
(7)depends_on:使用 depends_on 这个配置项,列出这个服务所有依赖的其他服务即可,在 Docker Compose 为我们启动项目的时候,会检查所有依赖,按照依赖指定的启动顺序来依次启动容器;
(8)volumes:在 Docker Compose 里定义文件挂载的方式与 Docker里也并没有太多的区别,使用 volumes 配置可以像 docker 的 -v 选项一样来指定外部挂载和数据卷挂载;
(9)ports:是用来定义端口映射的,可以利用它进行宿主机与容器端口的映射,这个配置与 docker 中 -p 选项的使用方法是近似的;
(10)environment:设置环境变量, 类似于 Dockerfile 的 ENV;
(11)networks:网络也是容器间互相访问的桥梁,网络的配置对于多个容器组成的应用系统来说也是非常重要的。在 Docker Compose 里可以为整个应用系统设置一个或多个网络,要使用网络必须先声明网络,声明网络的配置同样独立于 services 存在,是位于根配置下的 networks 配置;
(12)networks下的name:作用类似于 container_name指令,mynetwork 创建的 docker 网络被命名为 my_network, 使用命令 docker network ls 可以查看到它。
Weave Scope 这个工具可以自定义一些监控度量指标,也能够自动搜集、处理容器的信息,还带有直观的可视化界面,是一款一站式工具。它具有以下特点:直观的图形或表格模式;灵活的过滤和强大的搜索;实时展示应用和容器指标;支持多主机监管,支持 k8s。Weave Scope 监控展示了主机、容器、进程的众多常用数据和状态,并提供 WebUI 帮助我们进行基本的管理操作,并且在整个过程中不需要进行额外的配置,易于上手使用。同时,Weave Scope 提供插件和插件机制方便我们进行扩展,可以说,WeaveScope 是我们初期搭建容器监控管理系统的极佳选择。
安装部署:将 Weave Scope 安装到 Docker 容器的宿主机上,执行 docker ps 查看,发现运行的容器列表中新增了一个名为 weavescope 的容器,实际上 scope launch 命令也是借助 Docker 部署了 Weave Scope 服务。
# 下载 scope 工具
sudo curl -L https://github.com/weaveworks/scope/releases/download/latest_release/scope -o /usr/local/bin/scope
# 使 scope 具有执行权限
sudo chmod +x /usr/local/bin/scope
# 部署安装 Weave Scope, 并设定验证用户 myuser, 密码 mypassword。
sudo scope launch -app.basicAuth -app.basicAuth.password mypassword -app.basicAuth.username myuser -probe.basicAuth -probe.basicAuth.password mypassword -probe.basicAuth.username myuser
使用:WeaveScope 默认启动时在 4040 端口,可以在宿主机上打开 http://127.0.0.1:4040 进行登录查看:
监控是管理基础设施的核心工具,没有监控,我们将无法了解系统环境、进行诊断故障、制定容量计划,最糟的就是,故障发生了也不会被发现。从技术角度来看,监控是衡量和管理技术系统的工具和流程。监控将系统和应用程序生成的指标转换为对应的业务价值。监控系统需要在基于容器的微服务中自动且快速地识别生命周期,并持续地提供实时的监控检测。
上一节已经使用WeaveScope搭建了一个容器监控管理系统,并带有一些基本的监控功能。但它对于自定义数据获取、自动告警、细化监控指标和展示的时候,生态、功能性和扩展性都有欠缺,系统及需求复杂时难以满足我们的定制化的监控需求。这时,我们就需要围绕Premetheus,配合其生态及相关工具,配置我们的监控平台。
监控 Docker 主机上运行的容器状态,并监控 cAdvisor 容器,如果此容器关闭,会触发邮件告警。
编写 prometheus 的配置文件 prometheus.yml
# 全局配置
global:
# 每5s收集一次数据
scrape_interval: 5s
# 每5s执行一次告警规则检测
evaluation_interval: 5s
# 标记标签
external_labels:
monitor: 'monitor'
# 告警配置
alerting:
alertmanagers:
- static_configs:
- targets: ['alertmanager:9093']
# 指定规则配置文件
rule_files:
- rules/*.yml
# 数据抓取配置
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['prometheus:9090']
- job_name: 'cadvisor'
static_configs:
- targets: ['cadvisor:8080']
编写 Prometheus 的告警规则 rule_1.yml
groups:
- name: rule-1
rules:
- alert: "服务运行告警"
expr: up{
job="cadvisor"} < 1
# 告警等待时间
for: 1m
labels:
severity: warning
annotations:
summary: "服务名: {
{
$labels.alertname}}"
description: "容器cadvisor已停止"
编辑 alertmanager 配置文件 config.yml
alertmanager 触发告警后会发送邮件,我们需要先配置邮箱,这里以 QQ 邮箱为例。登录邮箱,选择设置:
Tips:25 端口用于转发邮件,没有考虑认证、加密等问题。587 端口专门被设计用来提交邮件,传输可以加密。这里我们使用 587 端口。
# 全局配置项
global:
resolve_timeout: 5m #处理超时时间,默认为5min
smtp_smarthost: 'smtp.qq.com:587' # 邮箱smtp服务器代理,这里以qq邮箱为例
smtp_from: '[email protected]' # 发送邮箱名称
smtp_auth_username: '[email protected]' # 邮箱账号
smtp_auth_password: 'xxxxxxxxxxxx' # 邮箱授权码
# 定义模板信息
templates:
- '/etc/alertmanager/templates/*.html'
# 定义路由树信息
route:
group_by: ['alertname'] # 报警分组依据
group_wait: 10s # 最初即第一次等待多久时间发送一组警报的通知
group_interval: 10s # 在发送新警报前的等待时间
repeat_interval: 1m # 发送重复警报的周期 对于email配置中,此项不可以设置过低,否则将会由于邮件发送太多频繁,被smtp服务器拒绝
receiver: 'email' # 发送警报的接收者的名称,以下receivers name的名称
# 定义警报接收者信息
receivers:
- name: 'email' # 警报
email_configs: # 邮箱配置
- to: '[email protected]' # 接收警报的email配置
html: '{
{ template "alert.html" . }}' # 设定邮箱的内容模板
headers: {
Subject: "[WARN] 报警邮件"} # 接收邮件的标题
编辑告警邮件模板 alert.html
{
{
define "alert.html" }}