Docker架构
docker是一个典型的CS架构,docker client通过REST API、UNIX套接字或者网络接口和docker daemon进行通信,后者来执行构建、运行、分发工作。
docker client和 docker daemonset可以在同一台机器或者不同的机器上面。
下面介绍几点基本概念:
- Docker client:docker客户端
- Docker daemon:docker服务端
- Registry:保存docker镜像的仓库,Docker hub是一个任何人都可以使用的公共镜像仓库。在国外,访问速度比较慢。一般生产环境中都建设自己的镜像仓库。
- images:一个image是创建一个容器的只读模板。通常一个镜像是基于另外一个镜像,并额外加上一些自定义的内容
- contaners:容器,即一个image运行状态下就是一个容器
最核心的三个部分:容器、镜像、仓库
Docker 内部组件
从 Docker 1.11 之后,Docker Daemon 被分成了多个模块以适应 OCI 标准。拆分之后,结构分成了以下几个部分
- docker: docker的客户端
- dockerd: docker的服务端
- containerd:高性能的容器运行时(containerd 独立负责容器运行时和生命周期(如创建、启动、停止、中止、信号处理、删除等),其他一些如镜像构建、卷管理、日志等由 Docker Daemon 的其他模块处理,包含containerd-shim)
- containerd-shim:是一个真实运行的容器的真实垫片载体,每启动一个容器都会起一个新的containerd-shim的一个进程,调用runc的api操作容器
- docker-proxy:容器代理。提供docker容器的端口映射功能
关于kubernetes弃用docker一事儿(k8s 1.20正式弃用docker-shim),估计大家都被标题党给忽悠了。kubernetes当初选择docker作为其容器运行时,是因为当时docker大火,而没有几个人知道kubernetes,为了更好支持k8s运行docker容器而开发维护一个兼容的程序叫docker-shim。真正弃用的不用docker,而是docker-shim插件的维护支持。
至于为什么要弃用docker-shim呢?
之前:
之后:
还要从k8s 在2016年发布CRI(Container Runtime Interface)标准来说起,为了支持 CoreOS 领导的容器运行时项目 rkt ,写了很多的代码。由于担心以后可能会有更新容器项目需要兼容而带来更大维护工作,所以发布了这个标准。这样的话,以后不管哪个容器项目只要支持CRL标准,就可以直接作为k8s的底层运行时。
2017 年, Docker v1.11 开始引入的容器运行时 containerd,并捐献给了CNCF,同年11月,kubernetes开始支持containerd,containerd相对于docker而言更加轻量级,是一个行业标准的运行时,强调简单性、健壮性和可移植性。
Docker的底层技术
主要是基于内核提供的namespace和cgroups技术。
namespace
namespace提供了一下6种资源隔离机制:
Mount
: 隔离文件系统挂载点UTS
: 隔离主机名和域名信息IPC
: 隔离进程间通信PID
: 隔离进程的IDNetwork
: 隔离网络资源User
: 隔离用户和用户组的ID
查看当前shell 进程所在的namespace
[root@vm1 ~]# ll /proc/$$/ns 总用量 0 lrwxrwxrwx 1 root root 0 7月 6 11:44 ipc -> ipc:[4026531839] lrwxrwxrwx 1 root root 0 7月 6 11:44 mnt -> mnt:[4026531840] lrwxrwxrwx 1 root root 0 7月 6 11:44 net -> net:[4026531956] lrwxrwxrwx 1 root root 0 7月 6 11:44 pid -> pid:[4026531836] lrwxrwxrwx 1 root root 0 7月 6 11:44 user -> user:[4026531837] lrwxrwxrwx 1 root root 0 7月 6 11:44 uts -> uts:[4026531838]
cgroups
cgroups可以限制、记录任务组所使用的物理资源(包括CPU、Memory、IO等),为容器实现虚拟化提供了基本保证,是构建Docker等一系列虚拟化管理工具的基石。
实现cgroups的主要目的是为不同用户层面的资源管理,提供一个统一化的接口。从单个任务的资源控制到操作系统层面的虚拟化,
cgroups提供了以下四大功能[插图]。
❏ 资源限制:cgroups可以对任务使用的资源总额进行限制。如设定应用运行时使用内存的上限,一旦超过这个配额就发出OOM(Out of Memory)提示。
❏ 优先级分配:通过分配的CPU时间片数量及磁盘IO带宽大小,实际上就相当于控制了任务运行的优先级。
❏ 资源统计:cgroups可以统计系统的资源使用量,如CPU使用时长、内存用量等,这个功能非常适用于计费。
❏ 任务控制:cgroups可以对任务执行挂起、恢复等操作
首先,当我们登录到操作系统之后,可以通过 ps 等操作看到各式各样的进程,这些进程包括系统自带的服务和用户的应用进程。那么,这些进程都有什么样的特点?
- 第一,这些进程可以相互看到、相互通信;
- 第二,它们使用的是同一个文件系统,可以对同一个文件进行读写操作;
- 第三,这些进程会使用相同的系统资源。
这样的三个特点会带来什么问题呢?
- 因为这些进程能够相互看到并且进行通信,高级权限的进程可以攻击其他进程;
- 因为它们使用的是同一个文件系统,因此会带来两个问题:这些进程可以对于已有的数据进行增删改查,具有高级权限的进程可能会将其他进程的数据删除掉,破坏掉其他进程的正常运行;此外,进程与进程之间的依赖可能会存在冲突,如此一来就会给运维带来很大的压力;
- 因为这些进程使用的是同一个宿主机的资源,应用之间可能会存在资源抢占的问题,当一个应用需要消耗大量 CPU 和内存资源的时候,就可能会破坏其他应用的运行,导致其他应用无法正常地提供服务。
针对上述的三个问题,如何为进程提供一个独立的运行环境呢?
- 针对不同进程使用同一个文件系统所造成的问题而言,Linux 和 Unix 操作系统可以通过 chroot 系统调用将子目录变成根目录,达到视图级别的隔离;进程在 chroot 的帮助下可以具有独立的文件系统,对于这样的文件系统进行增删改查不会影响到其他进程;
- 因为进程之间相互可见并且可以相互通信,使用 Namespace 技术来实现进程在资源的视图上进行隔离。在 chroot 和 Namespace 的帮助下,进程就能够运行在一个独立的环境下了;
- 但在独立的环境下,进程所使用的还是同一个操作系统的资源,一些进程可能会侵蚀掉整个系统的资源。为了减少进程彼此之间的影响,可以通过 Cgroup 来限制其资源使用率,设置其能够使用的 CPU 以及内存量。
那么,应该如何定义这样的进程集合呢?
其实,容器就是一个视图隔离、资源可限制、独立文件系统的进程集合。所谓“视图隔离”就是能够看到部分进程以及具有独立的主机名等;控制资源使用率则是可以对于内存大小以及 CPU 使用个数等进行限制。容器就是一个进程集合,它将系统的其他资源隔离开来,具有自己独立的资源视图。
容器具有一个独立的文件系统,因为使用的是系统的资源,所以在独立的文件系统内不需要具备内核相关的代码或者工具,我们只需要提供容器所需的二进制文件、配置文件以及依赖即可。只要容器运行时所需的文件集合都能够具备,那么这个容器就能够运行起来。
docker启动快速构建zabbix server
镜像加速
vi /etc/docker/daemon.json { "registry-mirrors": ["https://registry.docker-cn.com"] } systemctl restart docker
# 规划好zabbix server容器所需要的网段 docker network create --subnet 172.20.0.0/16 --ip-range 172.20.240.0/20 zabbix-net # 起一个mysql的容器实例 docker run --name mysql-server -t -e MYSQL_DATABASE="zabbix" -e MYSQL_USER="zabbix" -e MYSQL_PASSWORD="zabbix_pwd" -e MYSQL_ROOT_PASSWORD="root_pwd" --network=zabbix-net --restart unless-stopped -d mysql:8.0 --character-set-server=utf8 --collation-server=utf8_bin --default-authentication-plugin=mysql_native_password # 启动zabbix gateway docker run --name zabbix-java-gateway -t --network=zabbix-net --restart unless-stopped -d zabbix/zabbix-java-gateway:alpine-5.4-latest # 启动zabbix server连接mysql docker run --name zabbix-server-mysql -t -e DB_SERVER_HOST="mysql-server" -e MYSQL_DATABASE="zabbix" -e MYSQL_USER="zabbix" -e MYSQL_PASSWORD="zabbix_pwd" -e MYSQL_ROOT_PASSWORD="root_pwd" -e ZBX_JAVAGATEWAY="zabbix-java-gateway" --network=zabbix-net -p 10051:10051 --restart unless-stopped -d zabbix/zabbix-server-mysql:alpine-5.4-latest # 启动zabbix web连接zabbix server和mysql docker run --name zabbix-web-nginx-mysql -t -e ZBX_SERVER_HOST="zabbix-server-mysql" -e DB_SERVER_HOST="mysql-server" -e MYSQL_DATABASE="zabbix" -e MYSQL_USER="zabbix" -e MYSQL_PASSWORD="zabbix_pwd" -e MYSQL_ROOT_PASSWORD="root_pwd" --network=zabbix-net -p 80:8080 --restart unless-stopped -d zabbix/zabbix-web-nginx-mysql:alpine-5.4-latest
细心的同学会发现,彼此独立的两个容器,他们之间是如何直接通信的呢?zabbix-server-mysql是如何连接上数据库的呢?在以前的docker版本中需要强制加上--link的功能,新版本废弃了这一做法。采用的默认桥接的自定义网络内,共享环境变量。这里容器之间直接通过环境变量可以互相通信了。比如zabbix-server-mysql 通过直接连接环境变量mysql-server就可以直接找到mysql-server的容器了。
下面我们看下容器运行状态
[root@vm1 ~]# docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES ca8d68c8cfa6 zabbix/zabbix-web-nginx-mysql:alpine-5.4-latest "docker-entrypoint.sh" 3 minutes ago Up 3 minutes 8443/tcp, 0.0.0.0:80->8080/tcp, :::80->8080/tcp zabbix-web-nginx-mysql 531a2094920f zabbix/zabbix-server-mysql:alpine-5.4-latest "/sbin/tini -- /usr/…" 3 minutes ago Up 3 minutes 0.0.0.0:10051->10051/tcp, :::10051->10051/tcp zabbix-server-mysql 6e84835cfc35 zabbix/zabbix-java-gateway:alpine-5.4-latest "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 10052/tcp zabbix-java-gateway 5a10d9352e1e mysql:8.0 "docker-entrypoint.s…" 3 minutes ago Up 3 minutes 3306/tcp, 33060/tcp mysql-server [root@vm1 ~]# [root@vm1 ~]# pstree systemd─┬─NetworkManager───2*[{NetworkManager}] ├─auditd───{auditd} ├─containerd───9*[{containerd}] ├─containerd-shim─┬─mysqld───38*[{mysqld}] │ └─14*[{containerd-shim}] ├─containerd-shim─┬─java───17*[{java}] │ └─14*[{containerd-shim}] ├─containerd-shim─┬─tini───docker-entrypoi─┬─mysql │ │ └─zcat───zcat │ └─14*[{containerd-shim}] ├─containerd-shim─┬─supervisord─┬─nginx───5*[nginx] │ │ └─php-fpm7───5*[php-fpm7] │ └─13*[{containerd-shim}] ├─crond ├─dbus-daemon───{dbus-daemon} ├─dockerd─┬─4*[docker-proxy───4*[{docker-proxy}]] │ └─13*[{dockerd}] ├─login───bash ├─lvmetad ├─master─┬─pickup │ └─qmgr ├─polkitd───6*[{polkitd}] ├─rsyslogd───2*[{rsyslogd}] ├─sshd───sshd───bash───pstree ├─systemd-journal ├─systemd-logind ├─systemd-udevd └─tuned───4*[{tuned}]
总共启动了四个容器,docker分别启动四个containerd-shim的进程来运行四个容器。此时containerd也是独立服务,独立于docker daemon之外。
需要耐心等待一会儿,此时数据库需要初始化,zabbix库也需要初始化,如果有报错,主要看日志排错。
输入默认的用户名:Admin 密码:zabbix