Docker从入门到精通<2>-Docker的架构、内部组件、底层原理 Docker架构 Docker 内部组件 docker启动快速构建zabbix server

Docker从入门到精通<2>-Docker的架构、内部组件、底层原理
Docker架构
Docker 内部组件
docker启动快速构建zabbix server

Docker从入门到精通<2>-Docker的架构、内部组件、底层原理
Docker架构
Docker 内部组件
docker启动快速构建zabbix server

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从入门到精通<2>-Docker的架构、内部组件、底层原理
Docker架构
Docker 内部组件
docker启动快速构建zabbix server

从 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呢?

之前:

Docker从入门到精通<2>-Docker的架构、内部组件、底层原理
Docker架构
Docker 内部组件
docker启动快速构建zabbix server

之后:

Docker从入门到精通<2>-Docker的架构、内部组件、底层原理
Docker架构
Docker 内部组件
docker启动快速构建zabbix server

还要从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: 隔离进程的ID
  • Network: 隔离网络资源
  • 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

镜像加速

为了加快docker拉取镜像的速度,这里我们配置了docker镜像加速:
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库也需要初始化,如果有报错,主要看日志排错。

Docker从入门到精通<2>-Docker的架构、内部组件、底层原理
Docker架构
Docker 内部组件
docker启动快速构建zabbix server

 输入默认的用户名:Admin  密码:zabbix

Docker从入门到精通<2>-Docker的架构、内部组件、底层原理
Docker架构
Docker 内部组件
docker启动快速构建zabbix server