当前位置: 首页 > article >正文

docker的前世今生

docker来自哪里?

从我们运维部署的历史来看,宿主机从最初的物理机到虚拟机,再到docker,一步步演进到现在。技术演进其实是为了解决当前技术的痛点,那我们来看看有哪些痛点以及如何克服痛点的。

物理机

一般来说,一台物理服务器的配置都很高,都会部署较多的服务以充分利用物理机的资源,这就会带来许多的问题

  1. 服务之间互相影响,如A服务占满了CPU或内存,就会把机器上所有服务都卡死
  2. 资源浪费,若服务对基础环境的要求不一致,如linux或java版本等,那么服务就得放在别的机器上
  3. 故障恢复困难,一旦系统出现故障或卡死,需要重启机器上的所有服务,经历过的都知道,时间漫长且困难

虚拟机

既然物理机有以上问题,那么怎么解决呢?从解决问题的思路上来说,就是隔离,把基础环境隔离,服务隔离,这就引入了虚拟机,通过虚拟化技术,将一台物理机隔离成多个独立的虚拟机,互不受影响,这样就能部分解决物理机的问题,所有的影响都限制到了单台虚拟机,而不是整台物理机。当然,引入虚拟机也有对应的问题,以及提出了更高的要求

  1. 虚拟机的管理复杂,存在管理虚机的消耗
  2. 虚机的依赖管理复杂,常常遇到测试环境没问题,而生产环境报错的情况

docker

那么,虚拟机上面的问题,该如何解决呢?再次隔离,把每个服务都进行隔离,互不影响,这样就将影响降低到最小。那么docker就应运而生,每个服务就是一个容器,互相隔离,环境版本都可以根据自己的需求进行配置,服务出现故障不会影响其他服务,也不会因为资源占用问题,夯死整台机器。此外容器的故障恢复速度非常快,基本是秒级。

关于解决虚拟机的问题,docker解决了虚机的依赖管理复杂的问题,这是毋庸置疑的。此外,docker是不需要虚拟机的,若直接部署在物理机上,那么就解决虚拟机管理复杂的问题(因为实际原因,docker可能是部署在虚机上的)。当然任何的新技术都会引入新的问题,关于docker的问题,后面介绍k8s的时候再介绍。

docker现在何处

docker的基本使用

docker可以简单分为两块,一个是镜像,一个是容器。镜像是静态的,容器是运行时。那么我们先看看镜像是如何创建的。

编写dockerfile
# 基础镜像
FROM dockerhub.kubekey.local/public/openjdk:8
# 对外暴露的端口
EXPOSE 8082
EXPOSE 9998
#启动时接受的参数
ARG profile
ENV profile ${profile}
RUN echo "profile: ${profile}"
# 将原路径复制到镜像内的目录下
COPY ./OaCenter-1.0-SNAPSHOT.jar /jar/OaCenter-1.0-SNAPSHOT.jar

##统一内部时区
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai  /etc/localtime
RUN echo 'Asia/Shanghai' > /etc/timezone

# 启动时执行命令
ENTRYPOINT java -server -Xms4g -Xmx4g -Xss512M -XX:+UseG1GC -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/logs/outOfMemory.hprof -XX:+PrintGCDateStamps -XX:+PrintGCDetails -XX:+UseGCLogFileRotation -XX:NumberOfGCLogFiles=5 -XX:GCLogFileSize=20m -Xloggc:/logs/gc.log -jar /jar/OaCenter-1.0-SNAPSHOT.jar --spring.profiles.active=${profile}

构建镜像
docker build --build-arg profile=uat -f Dockerfile -t $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BUILD_NUMBER .
运行镜像
docker run -d $REGISTRY/$DOCKERHUB_NAMESPACE/$APP_NAME:$BUILD_NUMBER --name xxx

至此,容器就运行起来,对外提供服务了。

docker运维的常用命令

# 停止容器
docker stop xxx
# 重启容器
docker restart xxx
#进入容器
docker exec -it xxx bash
# 进入 shell启动的容器
docker exec -it xxx sh
# 查看所有容器
docker ps
# 查看所有容器,包括停止的
docker ps -a
# 容器的状态
docker stats
# 查看某个容器的详细信息
docker inspect xxx
# 复制容器的文件到宿主机
docker cp xxx:src_path dest_path
# 复制宿主机文件到容器
docker cp src_path xxx:dest_path
# 查看容器的日志
docker logs -f xxx
# 清理未使用的镜像、容器、卷和网络。
docker system prune

docker三板斧

一个容器服务突然报错,该如何处理?

第一步:docker ps

docker ps查看所有容器,若没看到,那么docker ps -a,如果docker ps -a也没看到,那么恭喜你,三板斧失败,你需要找到镜像文件,用启动命令或脚本去启动了。反之,如遇到下面三种情况,那么直接docker restart xxx,一般80%的情况下都能解决问题。

  1. docker ps没看到,docker ps -a看到了
  2. status为Exited
  3. status为UP,括号里unhealthy
docker ps
CONTAINER ID   IMAGE          COMMAND                  PORTS                status    NAMES
09b93464c2f7   nginx:latest   "nginx -g 'daemon off"   80/tcp, 443/tcp       Up 16 hours (healthy)         myrunoob
96f7f14e99ab   mysql:5.6      "docker-entrypoint.sh"   0.0.0.0:3306->3306/tcp Exited(0) 16 hours ago       mymysql

Up:表示容器正在运行中。

Created:表示容器已经被创建,但还未启动。

Exited:表示容器已经停止运行

第二步:docker logs -f xxx

容器若重启失败,那么就得看容器的日志了,从容器的日志查看失败的原因,可能内存不足,配置缺失等等,如果找到服务失败的原因,并解决掉,然后docker restart xxx,应该也能解决问题。

第三步:docker inspect xxx

主要检查State,Env,HostConfig里面的信息,一般来说就能看到错误的原因,针对性的去处理即可。

docker inspect xxx
[
    {
        "Id": "d2f5e3f19a6a",
        "Created": "2024-07-23T00:00:00Z",
        "Path": "bash",
        "Args": [],
        "State": {
            "Status": "running",
            "Running": true,
            "Paused": false,
            "Restarting": false,
            "OOMKilled": false,
            "Dead": false,
            "Pid": 12345,
            "ExitCode": 0,
            "Error": "",
            "StartedAt": "2024-07-23T00:00:00Z",
            "FinishedAt": "0001-01-01T00:00:00Z"
        },
        "Image": "sha256:abc123",
        "ResolvConfPath": "/var/lib/docker/containers/d2f5e3f19a6a/resolv.conf",
        "HostnamePath": "/var/lib/docker/containers/d2f5e3f19a6a/hostname",
        "HostsPath": "/var/lib/docker/containers/d2f5e3f19a6a/hosts",
        "LogPath": "/var/lib/docker/containers/d2f5e3f19a6a/d2f5e3f19a6a-json.log",
        "Name": "/my_container",
        "RestartCount": 0,
        "Driver": "overlay2",
        "Platform": "linux",
        "MountLabel": "",
        "ProcessLabel": "",
        "AppArmorProfile": "",
        "ExecIDs": null,
        "HostConfig": {
            "Binds": null,
            "ContainerIDFile": "",
            "LogConfig": {
                "Type": "json-file",
                "Config": {}
            },
            "NetworkMode": "default",
            "PortBindings": {},
            "RestartPolicy": {
                "Name": "no",
                "MaximumRetryCount": 0
            },
            "AutoRemove": false,
            "VolumeDriver": "",
            "VolumesFrom": null,
            "CapAdd": null,
            "CapDrop": null,
            "Dns": [],
            "DnsOptions": [],
            "DnsSearch": [],
            "ExtraHosts": null,
            "GroupAdd": null,
            "IpcMode": "private",
            "Cgroup": "",
            "Links": null,
            "OomScoreAdj": 0,
            "PidMode": "",
            "Privileged": false,
            "PublishAllPorts": false,
            "ReadonlyRootfs": false,
            "SecurityOpt": null,
            "UTSMode": "",
            "UsernsMode": "",
            "ShmSize": 67108864,
            "Runtime": "runc",
            "ConsoleSize": [
                0,
                0
            ],
            "Isolation": "",
            "CpuShares": 0,
            "Memory": 0,
            "NanoCpus": 0,
            "CgroupParent": "",
            "BlkioWeight": 0,
            "BlkioWeightDevice": null,
            "BlkioDeviceReadBps": null,
            "BlkioDeviceWriteBps": null,
            "BlkioDeviceReadIOps": null,
            "BlkioDeviceWriteIOps": null,
            "CpuPeriod": 0,
            "CpuQuota": 0,
            "CpuRealtimePeriod": 0,
            "CpuRealtimeRuntime": 0,
            "CpusetCpus": "",
            "CpusetMems": "",
            "Devices": null,
            "DiskQuota": 0,
            "KernelMemory": 0,
            "MemoryReservation": 0,
            "MemorySwap": 0,
            "MemorySwappiness": null,
            "OomKillDisable": false,
            "PidsLimit": null,
            "Ulimits": null,
            "CpuCount": 0,
            "CpuPercent": 0,
            "IOMaximumIOps": 0,
            "IOMaximumBandwidth": 0,
            "Mounts": [],
            "MaskedPaths": [
                "/proc/asound",
                "/proc/acpi",
                "/proc/kcore",
                "/proc/keys",
                "/proc/latency_stats",
                "/proc/timer_list",
                "/proc/timer_stats",
                "/proc/sched_debug",
                "/proc/scsi",
                "/sys/firmware"
            ],
            "ReadonlyPaths": [
                "/proc/bus",
                "/proc/fs",
                "/proc/irq",
                "/proc/sys",
                "/proc/sysrq-trigger"
            ]
        },
        "GraphDriver": {
            "Data": {
                "LowerDir": "/var/lib/docker/overlay2/l/abc123/diff",
                "MergedDir": "/var/lib/docker/overlay2/merged",
                "UpperDir": "/var/lib/docker/overlay2/upper",
                "WorkDir": "/var/lib/docker/overlay2/work"
            },
            "Name": "overlay2"
        },
        "Mounts": [],
        "Config": {
            "Hostname": "d2f5e3f19a6a",
            "Domainname": "",
            "User": "",
            "AttachStdin": false,
            "AttachStdout": false,
            "AttachStderr": false,
            "ExposedPorts": {},
            "Tty": false,
            "OpenStdin": false,
            "StdinOnce": false,
            "Env": [
                "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
            ],
            "Cmd": [
                "bash"
            ],
            "Image": "ubuntu",
            "Volumes": null,
            "WorkingDir": "",
            "Entrypoint": null,
            "OnBuild": null,
            "Labels": {}
        },
        "NetworkSettings": {
            "Bridge": "",
            "SandboxID": "abc123",
            "HairpinMode": false,
            "LinkLocalIPv6Address": "",
            "LinkLocalIPv6PrefixLen": 0,
            "Ports": {},
            "SandboxKey": "/var/run/docker/netns/abc123",
            "SecondaryIPAddresses": null,
            "SecondaryIPv6Addresses": null,
            "EndpointID": "abc123",
            "Gateway": "172.17.0.1",
            "GlobalIPv6Address": "",
            "GlobalIPv6PrefixLen": 0,
            "IPAddress": "172.17.0.2",
            "IPPrefixLen": 16,
            "IPv6Gateway": "",
            "MacAddress": "02:42:ac:11:00:02",
            "Networks": {
                "bridge": {
                    "IPAMConfig": null,
                    "Links": null,
                    "Aliases": null,
                    "NetworkID": "abc123",
                    "EndpointID": "abc123",
                    "Gateway": "172.17.0.1",
                    "IPAddress": "172.17.0.2",
                    "IPPrefixLen": 16,
                    "IPv6Gateway": "",
                    "GlobalIPv6Address": "",
                    "GlobalIPv6PrefixLen": 0,
                    "MacAddress": "02:42:ac:11:00:02",
                    "DriverOpts": null
                }
            }
        }
    }
]

一般来说,三板斧能解决95%以上的问题。当然还有一些情况,比如我近期遇到过修改宿主机iptables,导致容器挂掉的情况,重启也会失败,这种情况,需要重启宿主机就能解决此问题。

docker的原理和优势

原理

主要是利用了Linux内核的特性:namespacecgrouprootfs

  1. namespace将文件,网络,进程和权限进行隔离。
  2. cgroup限制namespace可使用的资源,如cpu,内存等
  3. rootfs用以创建新的根文件系统
优势
  1. 独立性,每个容器完全隔离,不会互相影响
  2. 启动快,容器因为简单和资源少,启动速度在秒级
  3. 运行环境一致,不会出现测试环境可以,生产环境失败的情况
  4. 迁移快,更换机器或者扩展节点,只需保存容器,在新机器上启动即可

docker去向何方

docker已经很方便了,为何现在k8s更如火如荼呢?这时候就得说到docker的缺点了。在大型系统中,采用微服务+docker部署方式之后,同时可能存在几百上千的容器在同时运行,同时产生的就是管理问题。我怎么把上千的容器部署到对应的节点上,怎么对容器进行部署重启,总不能一台机器一台机器上去运维部署,这显然不符合技术自动化发展的趋势。在这种情况下,k8s应运而生(其实不仅仅有k8s,还有docker swam等,最后k8s脱颖而出)。k8s简单来说就是容器编排工具,那么我们看看k8s的架构图。

在这里插入图片描述

Controller Manager 负责维护集群的状态,比如故障检测、自动扩展、滚动更新等;

API Server 提供了资源操作的唯一入口,并提供认证、授权、访问控制、API 注册和发现等机制;

Scheduler 负责资源的调度,按照预定的调度策略将 Pod 调度到相应的机器上;

Kube-proxy 负责为 Service 提供 cluster 内部的服务发现和负载均衡;

etcd 保存了整个集群的状态;

Kubelet 负责维护容器的生命周期,同时也负责 Volume(CVI)和网络(CNI)的管理;

总结

k8s为使用大规模容器服务提供了极大的便利,对于持续部署和自动化运维几乎都是一键操作。但是在最新的k8s版本已经舍弃了docker,后面可以继续聊聊这个的原因以及k8s的详细介绍和运维


http://www.kler.cn/a/515924.html

相关文章:

  • 数学规划问题2 .有代码(非线性规划模型,最大最小化模型,多目标规划模型)
  • Android SystemUI——通知栏构建流程(十六)
  • 计算机网络介质访问控制全攻略:从信道划分到协议详解!!!
  • SentencePiece和 WordPiece tokenization 的含义和区别
  • [答疑]这个消息名是写发送数据还是接收数据
  • 1月21日星期二今日早报简报微语报早读
  • 数据结构初阶之双向链表的介绍与双向链表的实现
  • SpringAI 之AI 模型输出与 POJO 映射
  • 数据分析 six库
  • 步入响应式编程篇(二)之Reactor API
  • 每天五分钟深度学习pytorch:基于VGG神经网络完成CAFIR10的识别
  • Kafak 单例生产者实现-C#操作
  • 工厂模式 - 工厂方法模式、抽象工厂模式
  • 软件测试丨SDK 功能测试
  • 【软件测试入门】测试工作总结
  • 蓝桥杯例题一
  • 使用 Element-UI 中的 el-button 添加防抖指令防止重复提交
  • 备赛蓝桥杯之第十五届职业院校组省赛第三题:产品360度展示
  • Alibaba Spring Cloud 四 Seata 的核心组件:TC
  • 【浙江省乡镇界】面图层shp格式arcgis数据+乡镇名称和编码+wgs84坐标无偏移内容测评
  • 在 Windows 11 中为 SMB 3.x 文件共享协议提供 RDMA 支持
  • 【C++图论 并集查找】2492. 两个城市间路径的最小分数|1679
  • TOGAF之架构标准规范-信息系统架构 | 数据架构
  • 小利特惠源码/生活缴费/电话费/油卡燃气/等充值业务类源码附带承兑系统
  • c语言贪吃蛇(极简版,基本能玩)
  • 【豆包MarsCode蛇年编程大作战】花样贪吃蛇