十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Docker与K8S入门指南:从容器部署到监控排查的完整路径

Docker与K8S入门指南:从容器部署到监控排查的完整路径 Docker 和 K8S 一直是 Linux 运维绕不开的两个关键词。如果你刚接触运维岗位或者正准备从传统虚拟机转向容器化部署大概率会在面试和日常工作中被这两个词反复轰炸。很多人在学习时最大的问题不是找不到资料而是资料太多不知道先装哪个、先学哪个、跑到哪一步算学会。这篇文章按我实际带人的顺序来写先搞清楚 Docker 和 K8S 各自解决了什么问题再把环境装起来跑通第一个容器然后进入 K8S 的 Pod、Service、监控和故障排查最后给一份可以直接照着做的学习落地清单。如果你只有一台普通机器甚至 Windows 笔记本也可以跟着走完大部分流程。1. 先理清概念Docker 和 K8S 到底解决什么问题很多新手一上来就急着装环境结果被一堆镜像、容器、YAML 文件绕晕。我建议先花 20 分钟把概念理顺因为后面所有操作都建立在“为什么要用它们”这个基础上。1.1 没有容器之前运维最头疼的是什么传统的部署方式是直接在服务器上装环境。Java 应用要装 JDKPython 应用要装特定版本的依赖数据库要单独维护实例。第一次部署还好第二次、第三次只要机器环境稍微不一样就会出现“开发环境能跑测试环境跑不起来生产环境更跑不起来”的问题。这种“环境不一致”是最消耗时间的事情。你可能为了一个依赖版本差异排查一整天。虚拟机能解决一部分问题但虚拟机很重一个基础系统镜像要几个 GB启动要几十秒甚至几分钟每台虚拟机还要单独分配 CPU、内存和磁盘资源。容器化解决的就是“把应用和它的运行环境一起打包”。Docker 把程序、依赖、配置文件、启动命令都写进一个镜像镜像在任何装有 Docker 的机器上都能以同样的方式启动。启动一个容器通常只需要一两秒比虚拟机轻量得多。隔离性是有的但不是完整的操作系统级隔离而是共享宿主机内核。用一句话理解Docker 的核心价值是“一次构建到处运行”。它把过去繁琐的环境配置工作提前打包好了。1.2 容器多了以后为什么需要 K8S单机环境下Docker 管理几十个容器还勉强可以。容器少的时候你用docker run一条一条启动再配合 docker compose 编排基本够用。但一旦应用到多台服务器问题就来了某台机器挂了容器怎么自动迁移到另一台机器流量高峰期需要扩容是手动再启动几个容器还是能自动伸缩多个容器分布在多台机器上它们之间怎么互相访问发布新版本的时候能不能滚动更新且不中断服务这些问题 Docker 单机版都给不了完整答案。K8S 就是用来解决“多机环境下容器怎么调度、怎么管理、怎么恢复”的编排系统。K8S 里有一个核心思想叫“声明式管理”你告诉它期望的状态比如“我要运行 3 个 nginx 副本”它会自己去创建 Pod、调度到合适的节点、保持副本数始终是 3。节点挂了它会自动在另一台节点上重新创建 Pod直到集群恢复期望状态。一个容易理解的比喻是Docker 像打包和拉货的车负责把货物打包成标准箱子并启动运输K8S 像物流调度中心它不负责造箱子但负责决定箱子放哪台车、怎么分配路线、车坏了以后怎么补货。1.3 Docker 和 K8S 的关系与区别很多资料会把 Docker 和 K8S 放在一起讲导致新手误以为它们是替代关系学了 K8S 就不需要 Docker。实际不是这样。Docker 是一个容器管理工具负责构建镜像、启动容器、管理容器生命周期。K8S 是一个容器编排平台负责管理多个节点上的容器调度。K8S 本身不直接启动容器它需要一个容器运行时来执行真正的容器操作。早期 K8S 经常用 Docker 作为运行时所以“Docker K8S”成了默认组合。但现在 K8S 默认使用 containerd 作为运行时这是 Kubernetes 社区逐步演进出来的方向。containerd 更轻量专门为容器运行时设计而 Docker 更偏向端到端的开发者体验。学习时建议从 Docker 入手因为 Docker 的命令足够直观能帮你快速理解镜像、容器、数据卷、端口映射这些基础概念。进入 K8S 后再逐步切换到 containerd、Pod、Service 这些新的表达方式。记住一点K8S 和 Docker 是不同层级的东西不是同一类工具的替代而是“编排调度”和“容器管理”的分工关系。2. Docker 落地装好环境、跑通 MySQL 和 Redis概念清楚后进入动手环节。Docker 的落地路径比较固定安装、配置镜像源、跑容器、管理容器。这里我按不同系统分别说明并给出两个常见生产任务的完整案例。2.1 不同系统的安装路径如果你用的是 Windows 或 macOS最简单的方案是安装 Docker Desktop。Docker Desktop 自带图形界面适合学习阶段使用。但它对系统版本有要求安装前先确认两点系统版本是否在支持范围内。Windows 上如果提示 incompatible version of windows说明当前系统版本太旧要么升级系统要么换用 Linux 环境练习。是否开启虚拟化。Windows 常见报错是virtualization support not detectedDocker Desktop 无法启动错误信息里会直接提示没有检测到虚拟化支持。这时去 BIOS/UEFI 里找 Intel VT-x 或 AMD SVM 开关开启后重启。Linux 环境下安装 Docker 更直接。Ubuntu 上可以用 apt 安装docker.io也可以用 Docker 官方仓库安装推荐用官方仓库版本更新更及时。CentOS 7 想升级到新版 Docker不能只靠yum update docker一般需要先卸载旧包再配置 Docker 官方或镜像站仓库然后装新版本。这里给的是通用排查顺序实际命令要以当前系统的官方文档为准。安装完先跑一次验证docker --version docker run hello-worlddocker run hello-world会拉取一个很小的测试镜像并打印一段成功信息。如果这一步能跑通说明 Docker 守护进程和命令行都能正常工作。如果卡在拉取镜像说明镜像下载网络有问题先进入下一步配置镜像源。2.2 安装之后先配镜像源Docker Hub 是默认镜像仓库但拉取慢是很多新手遇到的第一个问题。一个常见解决办法是在 Docker 配置里设置镜像加速源。在 Linux 下修改/etc/docker/daemon.json{ registry-mirrors: [ https://your-mirror-source.example.com ] }修改后重启 Dockersudo systemctl restart docker在 Docker Desktop 里设置项中一般有 registry mirrors 配置填入可用地址后应用并重启。注意事项不同云厂商的镜像源地址可能变化落地时以你所用环境对应的官方文档为准不要照抄几年前文章里的地址。配置镜像源不保证所有镜像都能加速个别镜像可能仍然很慢或者不稳定。如果镜像拉取失败先看报错是超时、连接拒绝还是 404。超时优先检查镜像源连通性404 优先确认镜像名和 tag 是否正确。配好之后重新执行docker run hello-world能正常输出版本信息说明镜像源生效了。2.3 用 Docker 部署 MySQL 8.0MySQL 是运维中最高频的中间件之一。Docker 部署 MySQL 8.0 特别适合本地开发、测试环境快速起实例。先拉镜像并运行docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDyourpassword \ mysql:8.0参数含义-d后台运行。--name mysql8容器名称方便后续用 docker 命令管理。-p 3306:3306宿主机 3306 端口映射到容器 3306 端口。-e MYSQL_ROOT_PASSWORDyourpassword设置 root 密码。这是 MySQL 官方镜像支持的初始化环境变量。mysql:8.0镜像名和标签表示 MySQL 8.0 系列。启动后先不要急着连库先看状态和日志docker ps docker logs mysql8如果docker ps里没有这个容器说明容器启动后立刻退出了这时必须看日志。常见情况是密码策略、端口被占用、初始化脚本报错。日志会给出线索。进入容器执行命令验证docker exec -it mysql8 mysql -uroot -p输入上面设置的密码能进入 MySQL Shell 说明服务正常。一个容易踩的坑是数据持久化。如果直接用上面的命令启动容器删除后/var/lib/mysql里的数据也会一起删除。正确做法是挂载数据卷docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDyourpassword \ -v $PWD/mysql-data:/var/lib/mysql \ mysql:8.0这样数据会落到宿主机当前目录的mysql-data文件夹里。容器重建后只要挂载到同一个目录数据就不会丢。另一个常见问题是 3306 端口冲突。如果本机已经装了 MySQL或者别的主机占用该端口可以换个端口docker run -d \ --name mysql8 \ -p 3307:3306 \ -e MYSQL_ROOT_PASSWORDyourpassword \ -v $PWD/mysql-data:/var/lib/mysql \ mysql:8.0连接时就要连127.0.0.1:3307连接工具和代码里的端口都要跟着改。2.4 用 docker compose 部署 Redis 主从单个 MySQL 用 docker run 还能接受如果要用 Docker 部署一整套 Redis 主从我建议直接上 docker compose。compose 把多个容器的配置写进一个 YAML 文件一条命令就能启动和停止整套服务。先创建一个工作目录比如redis-cluster里面放一个docker-compose.ymlservices: redis-master: image: redis:7.0 container_name: redis-master ports: - 6379:6379 command: redis-server --appendonly yes redis-slave: image: redis:7.0 container_name: redis-slave ports: - 6380:6379 command: redis-server --slaveof redis-master 6379 depends_on: - redis-master这个配置里有两个服务master 和 slave。slave 启动时通过--slaveof redis-master 6379指定主节点地址和端口。在 compose 网络里服务名redis-master可以直接作为域名访问。启动整套服务docker compose up -d查看运行状态docker compose ps验证主从关系docker exec -it redis-master redis-cli info replication看到role:master并且有slave0连接信息说明主节点正常。再检查从节点docker exec -it redis-slave redis-cli info replication看到role:slave并且master_link_status:up说明主从同步正常。这里有一个版本差异问题老版本 compose 使用独立的docker-compose命令新版本 Docker 直接支持docker compose子命令。如果你执行docker compose报错先确认 Docker 和 Docker Compose 的安装版本不要强行套命令。数据挂载在 Redis 集群里同样重要。可以在每个服务下加volumes配置把/data目录挂载到宿主机避免容器重建后数据丢失。除此之外Redis 主从只解决读扩展问题不解决自动故障切换生产环境要考虑哨兵或 Redis Cluster这是另一个话题。3. K8S 入门从集群搭建到 Pod、Service 和启动命令Docker 跑顺之后开始进入 K8S。K8S 的入门曲线比 Docker 陡因为它引入了一批新概念。不要试图一次全看懂先掌握最常见的几个再通过实际操作加深理解。3.1 先记住核心概念K8S 里的最小调度单元不是容器而是 Pod。Pod 是“一组容器的集合”通常一个 Pod 里放一个主容器也可以放边车容器。Pod 里的容器共享网络命名空间和存储卷所以它们之间可以通过 localhost 通信。Deployment 负责管理 Pod 的副本数。它告诉你“我要运行几个副本”实际创建、更新和删除 Pod 都是 Deployment 去做。Service 是服务入口。Pod 的 IP 会随着重启变化直接访问 Pod IP 不现实。Service 提供一个固定虚拟 IP 和域名把流量转发到后端一组 Pod 上。Namespace 用于隔离资源。团队多、项目多的时候用 Namespace 区分环境或团队。不指定时默认在 default 命名空间。控制平面组件里你最先要理解 API Server、etcd 和 kubelet。API Server 是所有请求的入口etcd 保存集群状态kubelet 负责在节点上管理 Pod。理解这三个后面看任何报错都会更容易定位。一个关键思维方式是“声明式”你声明的不是一个动作而是一个期望状态。比如你写清楚“副本数 3”K8S 会自己判断当前有几个副本不够就去创建多了就回收。3.2 本地或测试环境怎么搭学习 K8S 有两类路径。如果只有一台笔记本或者只想体验基础功能推荐 minikube。minikube 会在本地创建一个小型 K8S 集群可以是单节点常用作学习环境。启动命令一般类似minikube start启动后可以用kubectl get nodes查看节点状态。如果你对资源要求不高minikube 是成本最低的入门方式。如果想模拟真实集群建议准备三台 Linux 虚拟机或云主机一台 master两台 worker。系统建议选择较新的长期支持版本。搭建流程大致是关闭 swap保证 kubelet 能正常工作。加载内核模块调整网络参数让容器流量能正常转发。安装 kubeadm、kubelet、kubectl 三个核心组件。master 节点执行kubeadm init初始化集群。初始化完成后按提示配置 kubeconfig并记录生成的 join token。worker 节点执行kubeadm join加入集群。安装一个容器网络插件让不同节点上的 Pod 能互相通信。这里有一个容易忽略的点不同网络插件对--pod-network-cidr参数的要求不同。不要盲目照搬某个教程里的 CIDR 值一定要看你实际选择的网络插件文档。我在实际测试中见过很多次初始化失败不是因为集群配置错而是 CIDR 和网络插件对不上。初始化完成后验证kubectl get nodes能看到 master 和 worker 节点状态为Ready说明集群基础可用。3.3 用 YAML 描述 Pod 和 ServiceK8S 里最常用的是 YAML 文件。下面是一个最简单的 nginx Pod 示例apiVersion: v1 kind: Pod metadata: name: my-nginx spec: containers: - name: nginx image: nginx:1.25 ports: - containerPort: 80执行kubectl apply -f pod-nginx.yaml kubectl get pods kubectl describe pod my-nginxkubectl describe是排查问题的第一入口。它会把 Pod 的事件、容器状态、镜像拉取情况全部列出来。如果 Pod 一直 Pending先看 describe 输出里有没有节点资源不足、镜像拉取失败等提示。直接访问 Pod IP 不方便所以通常再创建一个 ServiceapiVersion: v1 kind: Service metadata: name: my-nginx-service spec: selector: app: my-nginx ports: - port: 80 targetPort: 80 type: ClusterIP如果你想在本地浏览器直接访问可以把 Service 类型改成NodePort或者用kubectl port-forward做端口转发。学习阶段我常用kubectl port-forward不用改 Service 类型也不用关心集群外的防火墙规则。kubectl port-forward service/my-nginx-service 8080:80然后访问http://localhost:8080看到 nginx 默认页面说明 Pod 和 Service 都通了。3.4 entrypoint 和 cmd 在 K8S 中代表什么Dockerfile 里有 ENTRYPOINT 和 CMDK8S 的容器定义里有 command 和 args。很多人会混淆它们。Dockerfile 中的 CMD 是默认启动命令ENTRYPOINT 是固定入口。运行时最终执行的命令是 ENTRYPOINT 加上 CMD 作为默认参数。K8S 里可以通过command覆盖 Dockerfile 的 ENTRYPOINT通过args覆盖 Dockerfile 的 CMD。它们的关系是K8S 的command对应 Dockerfile 的 ENTRYPOINT。K8S 的args对应 Dockerfile 的 CMD。举个例子镜像默认通过 ENTRYPOINT 启动某个脚本你希望在 K8S 里加一个额外参数就可以在 Pod 的 YAML 里只写args不写command这样保留镜像里的 ENTRYPOINT只覆盖默认参数spec: containers: - name: my-app image: my-app:latest args: [--config-file/etc/my-app/config.yaml]如果你同时写了command和args就会完全覆盖镜像里的默认设置。遇到启动命令不生效先看容器的启动命令和日志再确认是 command 写错了还是 args 里的路径、参数名不对。4. 监控告警与故障排查进入真实运维场景后不能只会在命令行里跑 pod、看日志。容器集群一旦多起来你更需要一套监控体系来发现资源问题而不是等用户报障才知道服务挂了。4.1 常用命令速查我不建议背命令而是建议按“查状态、查日志、查资源”三个动作去记命令。Linux 系统层面查系统运行状态top、free -h、df -h、du -sh *查服务和日志systemctl status 服务名、journalctl -u 服务名 -f查端口和连接ss -tlnp、netstat -tlnpDocker 层面查容器状态docker ps -a查日志docker logs -f 容器名进入容器docker exec -it 容器名 bash查资源占用docker stats查镜像和容器详情docker inspect 容器名K8S 层面查集群节点kubectl get nodes查资源对象kubectl get pods -A查详细事件kubectl describe pod 名称查容器日志kubectl logs -f pod名称进容器调试kubectl exec -it pod名称 -- bash查资源用量kubectl top nodes、kubectl top pods实际排查时我发现很多新手会在命令本身上浪费时间比如纠结kubectl logs和kubectl describe该用哪个。原则很简单先 describe 看事件再 logs 看应用输出。事件负责告诉你“K8S 层面发生了什么”日志负责告诉你“应用内部发生了什么”。4.2 监控告警体系node-exporter Prometheus Grafana容器化环境里靠 ssh 到每台机器去看top和df不现实。标准做法是搭建一套监控告警体系常见组合是 node-exporter、Prometheus 和 Grafana。node-exporter 负责在每台节点上采集系统指标比如 CPU、内存、磁盘、网络。Prometheus 负责定时抓取这些指标并存储。Grafana 负责把指标可视化并配置告警规则。这套体系可以用 docker compose 在单机跑通也可以部署进 K8S 集群。学习阶段我建议先用 docker compose 跑一遍理解“采集、存储、展示”三个环节再考虑部署到 K8S。一个最简单的链路是在目标机器运行 node-exporter默认监听 9100 端口。Prometheus 配置文件里添加这个 node-exporter 作为 scrape target。打开 Prometheus 的 targets 页面看到该节点状态为 UP。Grafana 添加 Prometheus 数据源导入一个 Node Exporter 的仪表盘。在 Grafana 里配置告警规则或者通过 Alertmanager 发送通知。验证链路是否正常先看两个地方Prometheus 的 targets 是否 UPGrafana 的 Dashboard 是否有数据。如果 target 是 DOWN先检查 node-exporter 进程和 9100 端口而不是急着改 Grafana。4.3 磁盘告警规则怎么写磁盘告警是最常见的需求因为镜像、容器日志、数据卷都会迅速消耗磁盘空间。只靠人工巡检很被动最好配置自动告警。在 Prometheus 或 Grafana 里写告警规则时核心是判断“磁盘剩余空间是否低于阈值”。下面是 Prometheus 告警规则的常见写法示例groups: - name: node-disk-alert rules: - alert: DiskUsageHigh expr: (1 - (node_filesystem_avail_bytes{fstype!~tmpfs|overlay} / node_filesystem_size_bytes{fstype!~tmpfs|overlay})) 0.9 for: 10m labels: severity: warning annotations: summary: 磁盘使用率超过 90%这段规则的作用是筛选出非 tmpfs、非 overlay 的文件系统计算磁盘使用率是否超过 90%持续 10 分钟就触发告警。需要提醒的是node-exporter 的指标名会随版本变化。有些版本里相关指标可能叫node_filesystem_avail_bytes还有可能存在node_filesystem_avail之类的历史指标。写规则前先在 Prometheus 的 Graph 页面用 PromQL 查询确认指标名否则规则可能永远不会触发。告警通知渠道取决于你的部署方式。Grafana 支持邮件、钉钉、企业微信、Webhook 等Alertmanager 也能做更复杂的路由和收敛。关键是先保证“规则能触发、通知能发出去”不要一开始就追求复杂的告警路由。4.4 常见故障排查链路K8S 和 Docker 的故障很多但排查思路是固定的先看现象再查输入再查环境最后查参数。Docker Desktop 虚拟化报错。如果启动时提示virtualization support not detected优先检查 BIOS/UEFI 里的 CPU 虚拟化开关再检查 Windows 虚拟机平台功能是否开启。报错信息本身一般已经给了修复方向。容器启动后立刻退出。先docker logs看日志再docker ps -a看退出状态。常见原因是端口被占、数据目录权限不足、启动命令依赖的配置不存在。不要反复重启容器先看退出前的日志。K8S Pod 一直 Pending。用kubectl describe pod看事件常见原因是节点 CPU 或内存不足或者节点有 taint 导致没有可用节点。先解决资源问题或调整调度配置。K8S Pod 进入 CrashLoopBackOff。说明容器启动后又崩溃了看kubectl logs是最直接的排查方式。常见原因是启动命令参数错误、依赖服务没就绪、环境变量缺失。K8S CPU throttling。这是一个比较隐蔽的问题。表现是 Pod 的 CPU 使用率看起来不高但接口响应很慢日志里有大量延迟提升。原因是 Pod 设置了 CPU limit但业务在实际运行时超过了 limit导致 CPU 被限流。排查时可以查看 Prometheus 里container_cpu_cfs_throttled_periods_total相关指标判断是否存在大量 throttled 周期。解决思路不是简单地取消 limit而是根据业务真实负载合理设置 requests 和 limits并在容量规划时留出缓冲。权限问题。比如想给团队成员只读权限查看 K8S 资源很多人会直接把管理员 kubeconfig 发出去这是非常危险的。正确做法是创建一个只读用户或 ServiceAccount绑定只包含get、list、watch权限的 RBAC 角色。最小化权限是运维的基本功。5. 学习落地清单和边界最后这部分给新手一条可以照着走的路径也把最容易踩的坑集中说一遍。5.1 建议的学习顺序我的建议是分四个阶段走不要跳级。第一阶段Linux 基础。至少会用cd、ls、top、free、df、journalctl、systemctl能看懂系统报错。没有这个基础Docker 和 K8S 的很多问题会卡在系统层面。第二阶段Docker 单机。从安装、拉镜像、跑容器开始理解镜像、容器、数据卷、端口映射。重点练习docker run、docker exec、docker logs、docker inspect。第三阶段docker compose 和多服务编排。把 MySQL、Redis、Nginx 等常见组件用 docker compose 编排起来理解容器之间的网络通信和数据持久化。第四阶段K8S。先掌握 Pod、Deployment、Service、Namespace再往上加监控、日志、持久化存储。不要一开始就去研究 Operator、service mesh 这些进阶内容。5.2 典型踩坑记录镜像下载慢配置镜像源确认网络连通性不要反复重试同一个失败任务。容器数据丢失没有挂载数据卷。无论 MySQL、Redis 还是业务应用只要需要持久化都要先规划数据目录。端口冲突启动前先看端口占用不要等bind failed才去改端口。版本不匹配Docker Compose 新老命令差异、MySQL 8.0 初始化参数变化、K8S 镜像版本过多都会导致照搬教程失败。遇到版本报错第一反应是核对版本。权限问题K8S 不要随便发 admin kubeconfig云主机不要随意放行所有端口。权限最小化操作留日志。资源耗尽容器不设置 requests/limitsK8S 集群容易被某个异常服务拖垮。学习阶段可以不调但生产环境一定要设置。5.3 学习环境和生产环境的边界学习环境可以用单节点、可以不开资源限制、可以随便删除重建。生产环境要考虑的东西更多etcd 备份、Master 高可用、网络插件选型、Ingress 规划、数据持久化、日志采集、监控告警、权限审计、镜像仓库安全。低配置机器能跑 K8S不代表它能扛住生产流量。学习时用 minikube 或单节点集群没问题放到生产环境要按实际负载和可用性要求设计。默认配置适合入门不适合生产。很多教程里的“一键部署”只是为了教学生产环境必须有明确的参数、容量、监控和回滚方案。如果只是学习默认配置够用。如果要长期维护就要把日志、输出目录、任务队列、监控规则提前整理好。不是等出事之后才补而是第一天就把基础打牢。踩过几次之后我发现很多问题不是工具能力不够而是前置环境和输入材料没有处理干净。先把 Docker 单机跑顺再进入 K8S 编排先把监控链路搭通再谈告警阈值先学会看日志和事件再去找参数和配置。这套顺序看起来慢但实际落地时能省下大量返工时间。
返回列表