如果你是从 Kubernetes 社区转到 OpenShift Container Platform 的第一次打开官方文档大概率会看到一个高频词oc。我先说我的结论——oc不是kubectl的简单改名它是 OpenShift Container Platform 面向使用者、开发者、运维者统一暴露的命令行入口。这个工具值得你花一个下午认真过一遍因为后面所有日常操作从登录集群、创建应用、查看构建、滚动发布到给用户授权、节点维护几乎都离不开它。我最早接手的一个集群环境比较特殊控制台页面有时因为网络策略限制访问不了但跳板机上只要配好了oc所有操作照常进行。团队成员里有从kubectl转过来的也有完全不懂 K8s 的通过oc反而更快上手了。这篇文章不打算写成像文档那样逐条列命令而是按我实际使用的路径来梳理先搞懂它和kubectl的关系再把环境准备好接着跑通一个应用上线的完整链路最后聊聊排障和运维。这样一套下来你至少能应付 80% 的日常工作。1. 为什么 OpenShift 要单独维护一把oc而不是直接用kubectl1.1 命令层面oc是kubectl的超集最直观的理解方式是所有kubectl能做的事oc都能做。oc get pod、oc describe node、oc apply -f、oc logs这些命令的语法和输出习惯和kubectl保持一致所以如果你已经熟悉kubectl迁移成本几乎为零。但oc不是只做了命令改名它额外扩展了一批 OpenShift 特有资源的操作。比如oc get route查看路由规则oc get imagestream查看镜像流oc get buildconfig查看构建配置oc get deploymentconfig查看部署配置这些对象在标准 K8s 里没有对应概念用kubectl查不到。刚转换过来的同事最容易在这卡住明明oc get all里能看到一堆资源切回kubectl get all却少了很多。不是集群出问题了而是kubectl根本不认识这些扩展 API 对象。1.2 对象层面OCP 在 K8s 之上多了一层应用编排模型标准 Kubernetes 的世界里应用上线大概是这样准备镜像 - 写 Deployment YAML - 用 kubectl apply - 暴露 Service - 再搞 Ingress。这套流程没问题但遇到源码构建、镜像流跟踪、自动触发部署这类更贴近平台的需求时就得自己组装很多工具。OpenShift 把这套流程沉淀成了平台原生的对象BuildConfig定义怎么从源码或 Dockerfile 构建出镜像ImageStream相当于一个镜像仓库的抽象层标记某个镜像的 tag 变化DeploymentConfig类似 Deployment但支持更丰富的部署策略和触发器Route在 Ingress 之上封装的 HTTP/HTTPS 路由对象配置更简单oc把这些对象的操作都封装进了命令里。举个例子你不用手动写五个 YAML 再 Apply一个oc new-app就能把源码地址、构建策略、镜像流、部署配置、服务暴露串联起来。这种一条命令完成应用上线的体验是kubectl给不了的。1.3 同一套 kubeconfig心智模型却要切换oc底层用的是和kubectl一样的 kubeconfig 文件认证方式也兼容。你用oc login登录后~/.kube/config会被更新之后kubectl也能用这份配置直接访问集群的 K8s 原生资源。但注意这不代表两者可以随意混用。我见过有人把 Deployment 用kubectl建好后跑到 OpenShift 控制台里发现它确实是已部署状态但构建历史、镜像流那一栏是空的。反过来用oc new-app建出来的 DeploymentConfigkubectl rollout status是看不懂的必须用oc rollout status。要我说日常顺手用kubectl没问题但凡是涉及 OpenShift 扩展能力的操作养成先想一下这是不是 OCP 特有的功能是就用oc的习惯能省很多困惑。2. 环境准备拿 CLI、登录、项目切换这些基本功课2.1 从集群控制台拿匹配版本的oc安装oc最稳妥的路径不是从第三方网站随便下载一个二进制而是从你实际连接的集群控制台页面里拿。OpenShift 控制台的右上角有一个命令行工具入口点开后能看到对应这个集群版本的oc客户端下载链接。为什么强调版本匹配因为客户端版本太新或太旧都可能出现 API 兼容问题。我就遇到过客户端版本比集群新一个大版本的情况执行某些命令直接报GroupVersionKind不匹配排查了半天最后发现是客户端版本的问题。下载回来后是 tar.gz 压缩包解压后把oc可执行文件放到 PATH 目录里比如/usr/local/bin或者~/bin。验证是否可用oc version能看到类似于Client Version和Server Version两行信息就说明客户端能正常连接集群了。如果Server Version显示不出来通常是你还没登录或者是网络层面根本访问不到集群的 API Server 地址。2.2 三种登录方式用户名密码、token、service accountoc login是最常见的登录入口适合交互式输入用户名密码oc login https://api.example.openshift.com:6443 -u yourname -p yourpassword登录成功后系统会提示你当前所在的 project并把 context 信息写入 kubeconfig。这种做法适合开发者在本地终端里用。如果你在用多因素认证或者 SSO可能需要先通过浏览器拿 token 再粘贴回来。自动化脚本或 CI 环境里我更推荐直接用 token 登录oc login --tokensha256~xxxxxxxx --serverhttps://api.example.openshift.com:6443Token 可以是你自己账号的也可以是某个 ServiceAccount 的。用 ServiceAccount token 的好处是权限范围明确便于审计而且不依赖某个人是否离职。但要注意别把 token 写在脚本里直接提交到代码仓库我习惯用环境变量引用比如oc login --token$OC_TOKEN --server$OC_SERVER还有一种场景是集群刚装完你需要用管理员账号去初始化。这种可以直接用 kubeconfig 文件切换OpenShift 安装生成的kubeadmin用户配置文件通常在安装目录下把KUBECONFIG环境变量指过去即可export KUBECONFIG/path/to/kubeadmin.kubeconfig2.3 项目namespace的切换和身份确认OpenShift 里的项目Project本质上就是带注解的 Namespace但它在用户层面多了一点点隔离语义。用下面命令查看当前项目oc project切换项目用oc project name新建项目用oc new-project name。注意oc new-project不仅会创建 Namespace还会自动帮你把当前用户绑定成该项目的管理员这个行为在标准 K8s 里是没有的。我在用这个命令时有个习惯每次登录完先执行一条oc whoami再执行一条oc project。这不是多此一举而是因为 OpenShift 环境里经常要做身份切换有时候你以为自己在用管理员身份操作实际某个上下文中已经切成了普通用户不看这两条命令直接部署很容易把应用建到错误项目里或者因为权限不足收到一串听不懂的 403。3. 应用上线主链路用oc new-app、start-build、rollout把业务跑起来3.1 推荐用oc new-app而不是手写一堆 YAML 的场景我见过很多 K8s 背景的同行拿到 OpenShift 第一反应是继续写 YAML然后oc apply -f。这没有任何问题但如果你只是想把一个应用快速跑起来特别是还在开发阶段oc new-app的效率要高得多。oc new-app的基本逻辑是你给它一个源码仓库地址、一个镜像名、或一组基础镜像它自动帮你生成对应的 BuildConfig、DeploymentConfig、Service 等资源。举个例子把一个 Node.js 源码仓库部署到集群oc new-app https://github.com/example/nodejs-demo.git --namenodejs-demo执行完之后看输出它会告诉你已经创建了 imagestream、buildconfig、deploymentconfig、service 这些对象。然后你只需要手动创建一条 Route应用就能用域名访问了——当然 Route 也可以让oc new-app自动生成加上--as-deployment-config或指定--route相关参数即可。这个过程中 OpenShift 会先探测仓库里的语言类型然后选择对应的 Source-to-ImageS2I构建器。比如识别到 package.json 就用 Node.js 构建器识别到 pom.xml 就用 Java 构建器。如果仓库结构比较复杂可能探测不准这时候你用--image参数明确指定镜像反而更可靠oc new-app nodejs:20~https://github.com/example/nodejs-demo.git --namenodejs-demo注意nodejs:20是从 ImageStream 里引用的 tag~后面接源码仓库地址表示用这个基础镜像去构建后面的源码。3.2 构建策略的选型S2I 还是 Dockerfileoc new-app默认倾向使用 S2I 策略也就是把源码注入基础镜像的固定目录执行构建器脚本最后生成一个新的运行时镜像。这种方式对常见的 Node.js、Java、Python、Ruby 等语言很友好你不用维护自己的 Dockerfile平台帮你处理依赖安装和启动命令。但遇到特殊需求比如要定制系统库、要跑多阶段构建、要修改镜像的非默认启动参数S2I 反而变得别扭。好在你可以在源码仓库根目录放一个 Dockerfile然后显式指定这次构建走 Docker 策略oc new-app --strategydocker https://github.com/example/custom-runtime.git --namecustom-app或者先创建一个 BuildConfig再手动触发。我个人的选型经验是能用 S2I 就用 S2I特别是团队里没有专人维护镜像的场合一旦应用对基础环境有强定制要求果断切 Docker 策略别勉强在 S2I 里塞各种 hack。S2I 最大的好处是构建过程可重复、增量构建快缺点是调试构建器脚本的时候比较难受。Docker 策略则完全就是标准docker build的逻辑团队里懂的人更多。构建开始后用oc logs -f实时看构建日志oc logs -f bc/nodejs-demobc是 buildconfig 的简写。日志里能看到拉取基础镜像、注入源码、执行构建步骤、推送镜像到内部仓库的全过程。这一步是排查源码依赖安装问题的主要途径比如 npm 私服地址配错了、maven 仓库连不通都会在这里暴露。构建成功后镜像会被推送到 OpenShift 内部的 ImageStream 里。查看当前镜像流和 tagoc get imagestream oc describe imagestream nodejs-demo3.3 部署策略与版本回滚镜像流更新后如果 DeploymentConfig 配置了 ImageChange 触发器会自动发起一次新部署。查看部署状态oc rollout status dc/nodejs-demo这里的dc是 deploymentconfig 的简写。OpenShift 支持两种部署策略Rolling滚动更新和 Recreate先停旧再起新。默认是 Rolling适合无状态应用Recreate 适合那些数据库迁移类应用旧版本必须完全退出才能启动新版本避免同时存在新旧逻辑读写同一份数据。部署历史保留多少份会影响你回滚时能选到哪个版本。查看历史版本oc rollout history dc/nodejs-demo回滚到上一个版本oc rollout undo dc/nodejs-demo如果一切正常确认当前可用副本数oc get pods看到Running且 READY 列为1/1同时底下的oc rollout status输出successfully rolled out这条链路就算跑通了。这里有个容易混淆的点如果你是用oc new-app自动生成的它是 DeploymentConfig如果你自己写的 YAML 里用的是标准 Deployment那回滚命令要换成kubectl rollout undo deployment/xxx。我在文中和团队讲解时都会强调先确认对象类型再选命令不要想当然。3.4 把服务暴露成集群外可访问的地址构建和部署都完成后还需要解决外面怎么访问的问题。OpenShift 使用 Route 对象对外暴露 HTTP/HTTPS 服务。最简单的方式oc expose service/nodejs-demo它会自动用当前项目名和应用名生成一个默认域名。查看生成的路由oc get route如果你想自定义域名路径或者配置 TLS 证书可以导出现有对象再修改oc get route nodejs-demo -o yaml route.yaml然后把spec.host改成自己的域名需要 HTTPS 的话可以配置spec.tls.termination: edge、insecureEdgeTerminationPolicy: Redirect最后oc apply -f route.yaml如果域名不是集群默认的泛域名记得去 DNS 侧加一条解析记录指向集群的入口点。这一步经常被忽略导致 Route 建好了curl 却一直超时。4. 故障排查链路从日志、事件到 debug 容器4.1 日志oc logs的正确打开方式应用跑起来后看日志是日常操作里频率最高的。最基础的当然是oc logs pod/nodejs-demo-1-abcd但很多时候 Pod 名字是自动生成的随机后缀敲起来并不方便。你完全可以直接用资源名oc logs deploymentconfig/nodejs-demo或者直接用dc/前缀oc logs dc/nodejs-demo如果你确定目前在跑的只有一个 Pod这种写法会直接给你日志输出。Pod 内有多个容器时必须用-c指定容器名oc logs pod/nodejs-demo-1-abcd -c my-container调试时搭配参数很有用。实时跟踪日志用-f只看最近 200 行用--tail200只看最近 10 分钟用--since10m。oc logs dc/nodejs-demo --tail200 -f有些启动失败的场景下Pod 反复重启你看到的日志是上一个实例留下的残留内容。这时候加--previous参数能看到容器在崩溃前最后输出oc logs pod/nodejs-demo-1-abcd --previous这个参数在排查CrashLoopBackOff时特别好使。4.2 事件与 describe快速定位 ImagePullBackOff / CrashLoopBackOffPod 起不来的时候oc get pods看到的通常是ImagePullBackOff、CrashLoopBackOff、Pending这类状态。先看事件oc get events --sort-by.lastTimestamp | tail -20事件信息可能比较杂如果你心里已经有明确的 Pod 名用oc describe更直接oc describe pod/nodejs-demo-1-abcd输出里注意看两个部分Events部分明确告诉你发生了什么。比如拉取镜像失败底下会写Failed to pull image以及具体错误码调度失败会写0/3 nodes are available。Conditions部分描述 Pod 当前各条件的通过情况比如PodScheduled是否是 True。以ImagePullBackOff为例常见的根因是国内环境拉取外部镜像超时、镜像 tag 写错、或者内部镜像仓库没有开放访问。定位思路先看事件里的错误信息再查它实际拉取的镜像地址是哪一行。如果用的是 S2I 构建很可能是 BuildConfig 里配置的output.to指向了一个不存在的 ImageStreamTag。CrashLoopBackOff则意味着容器能启动但马上退出排查路径通常是oc logs --previous看最后输出 - 查看启动命令和探针配置 - 验证依赖的服务是否就绪。我曾经遇到过一次其实只是容器里的进程绑定端口和 Service 暴露端口不一致日志里却一直显示莫名其妙的内存错误这时候用oc debug进去看实际情况就特别必要。4.3oc debug给故障 Pod 开一个同款副本oc debug是我排查问题时的王牌命令它和kubectl exec不太一样。直接oc debug pod/xxx会创建一个和当前 Pod 配置几乎一样的临时副本但不会启动原来的容器而是用调试镜像启动一个 shell。这样做的好处是即使原容器已经崩溃、无法exec进去你依然可以进到 Pod 的运行时环境里检查文件、进程、网络。oc debug pod/nodejs-demo-1-abcd进去之后你会发现自己是通过一个 debug 容器在操作Pod 里的其他容器文件系统需要手动挂载路径才能访问。访问节点文件系统时用chroot /host这个命令在排查节点级问题时非常有用比如检查节点上的磁盘占用、清理残留镜像。有一个坑要提醒oc debug创建的调试 Pod 是会占用临时资源的而且有些集群开启了 Pod Security 策略可能导致 debug Pod 创建失败。要记得用完删掉别留下一堆调试用 Pod 占着调度资源。4.4 路由层跟进的检查顺序对外访问不通时我习惯按顺序排查Route 是否存在oc get route确认 Host、Path、Service 名、端口都正确。Endpoint 是否正常oc get endpoints确认 Service 后端 Pod 地址有值。如果 Endpoint 列表为空说明 Service 的 selector 和 Pod 标签不匹配。Router 是否运行OpenShift 的入口流量默认由 Ingress Operator 和 Router 处理。查看相关 Podoc get pods -n openshift-ingress如果 Router Pod 异常外部流量自然进不来。Router 日志里也经常能翻到 TLS 证书失效、后端连接被拒这类的线索。 4.Service 是否工作如果 Pod 里能 curl 到 Service 域名说明链路内部正常如果 Pod 里也 curl 不通那就是 Service 配置或 Pod 端口映射的问题。这套链路走下来大部分域名访问不了的问题都能定位到具体环节。5. 运维视角oc adm系列命令和权限管理的实操心法5.1 节点维护三部曲cordon、drain、uncordon如果你需要重启某台节点机器或者给节点打补丁千万别直接关机。先把这个节点标记为不可调度oc adm cordon node-namecordon 之后新调度的 Pod 不会跑到这个节点上但已经在节点上运行的 Pod 不会自动挪走。接下来执行 drain把已有 Pod 迁移到其他节点oc adm drain node-name --ignore-daemonsets --delete-emptydir-data--ignore-daemonsets是必需的因为 DaemonSet 类 Pod 本来就应该留在节点上--delete-emptydir-data要根据业务情况决定如果节点上用了 emptyDir 且有临时数据删除它就意味着清掉这些数据。维护完成后让节点恢复调度oc adm uncordon node-name我还习惯在 drain 之前确认一下节点上有哪些关键 Pod以及它们的 PDBPodDisruptionBudget状态。没有 PDB 的 Pod 在 drain 时会直接强制驱逐可能导致服务短暂不可用。有一个 PDB 保护的应用drain 时会被优雅地滚动迁移。5.2 常用权限操作add-role-to-user 与集群角色OpenShift 的 RBAC 和 K8s 一致但oc提供了一些更便捷的封装命令。给某个用户授予某个项目的 view 权限oc adm policy add-role-to-user view username -n project-name把用户提升为项目管理员oc adm policy add-role-to-user admin username -n project-name如果需要集群级权限用oc adm policy add-cluster-role-to-user cluster-admin username这个命令给出的权限极大操作前一定确认是你真正要授权的对象。我在生产环境里很少直接给 cluster-admin除非是平台运维团队的核心成员。平时给到特定 Namespace 的 admin 权限已经覆盖绝大多数需求。查看某用户当前权限oc policy can-i create deployment -n project-name --asusername这条命令在写自动化权限审批流程时特别有用可以提前验证某用户是否具备执行某操作的资格。新版本的 OpenShift 里部分旧的oc adm policy命令会提示改用统一的 RBAC API 管理但老集群上这些命令依然能用。作为运维两套写法都最好了解。5.3 巡检与集群诊断oc adm must-gather、top集群出问题需要报工单或者给平台团队提供现场数据时oc adm must-gather是标准的取证命令oc adm must-gather它会收集集群里的大量诊断信息包括事件、日志、资源定义、Operator 状态等打包成一个压缩目录。这个命令执行时间可能比较长数据量也大但它是排查集群级问题的关键材料。我每次执行完会顺手把产物目录名改得带有日期和集群标识方便后续追溯。快速看节点和 Pod 的资源占用oc adm top nodes oc adm top pods -n project-nameoc adm top pods的结果依赖 metrics-server如果集群没有安装监控栈可能为空。不过 OpenShift 通常默认自带监控所以绝大多数环境都能正常出数据。日常巡检脚本里我一般把这三样组合起来oc get nodes看节点状态、oc get clusteroperators看各组件健康、oc get co发现问题后再用 must-gather 深入。6. 忘不了的教训几个让老手也翻车的细节6.1new-app的语言探测有时过于自作主张oc new-app的自动探测很方便但它不是万能的。有一次我给一个 Python 项目执行oc new-app因为仓库里恰好还有一个不相关的pom.xml结果平台把项目识别成了 Java构建了半天依赖直接失败。后来我都是先检查仓库根目录确认语言特征明确后再交给oc new-app处理。如果仓库结构本身就容易误导探测干脆显式指定--image或者--strategy别把选择权完全交给自动探测。另一个相关问题是oc new-app如果给的不是源码仓库而是纯镜像名它会直接创建 DeploymentConfig 并启动镜像而不经过构建。这个行为有时候有惊喜快速部署一个现成镜像但很多时候用户本意是希望走构建流程结果资源类型完全不同了。执行完oc new-app后养成习惯看一眼输出确认有没有出现 buildconfig 对象。6.2 DeploymentConfig 和 Deployment 的 rollout 行为差异前面提到过一次这个坑值得再展开。OpenShift 里同时存在DeploymentConfig和Deployment两种部署对象触发更新和回滚的命令行为不完全一样对 DeploymentConfig用oc rollout status dc/xxx、oc rollout undo dc/xxx对 Deployment用oc rollout status deployment/xxx或kubectl rollout status deployment/xxx如果搞混了最典型的报错是Error from server (NotFound): deploymentconfigs.apps.openshift.io xxx not found。你明明知道资源存在命令却找不到就是因为资源类型写错了。用oc get all查看所有工作负载时注意区分deploymentconfig.apps.openshift.io和deployment.apps两列。另外DeploymentConfig 的部署历史默认保留 10 个版本Deployment 的revisionHistoryLimit默认也是 10。如果你期待回滚到很久以前的版本可能需要先调整保留数量否则历史版本早被清理了。6.3 客户端版本不匹配和 TLS 跳过的取舍我在前面提过从控制台下载与集群版本匹配的oc。这里再补一个细节如果你连接的是一个版本比较旧的集群新版oc登录时可能会提示配置了不兼容的 API 组或者一些命令报the server could not find the requested resource。这时候优先考虑是不是版本匹配问题。TLS 证书方面很多测试环境没有接入正式 CA用oc login时会提示证书不受信任。有些人习惯加--insecure-skip-tls-verifytrue跳过校验这确实能快速通过认证但生产环境不建议这么做。证书校验失败背后往往意味着中间人风险或者集群入口证书配置有误。正确的做法是把集群的 CA 证书下载到本地并配置到客户端信任链里。如果只是临时连测试环境跳过校验可以接受但要有意识确保这是短期的测试行为。我还踩过一次比较低级的坑登录脚本里把--insecure-skip-tls-verifytrue写死了后来这个脚本被复制到生产环境使用导致生产集群跳过证书校验了很长时间。现在我的做法是这类开关参数通过环境变量单独传递不同环境各写各的配置绝不在脚本里写死。6.4 我给团队沉淀的常用命令速查表最后把我平时最常用的命令整理成一张表内容是根据我这边的维护场景总结的你可以直接抄走放到团队文档里。场景命令说明查看当前用户oc whoami确认身份防止上下文切换错乱查看当前项目oc project确认当前所在 Namespace/Project切换项目oc project name切换后建议再跑一次oc whoami复核查看所有资源oc get all查看项目内各类资源概览查看 Pod 详情oc describe pod/name事件和条件都在这里实时日志oc logs dc/name -f --tail100适合跟踪部署过程查看崩溃前日志oc logs pod/name --previousCrashLoopBackOff 排查利器创建应用oc new-app repo --nameapp自动生成构建/部署/服务资源手动触发构建oc start-build bc/name重新执行一次构建流程查看构建日志oc logs -f bc/name构建失败时必用查看部署状态oc rollout status dc/name等待部署完成回滚部署oc rollout undo dc/name回滚到上一个可用版本暴露服务oc expose service/name创建默认 Route查看事件oc get events --sort-by.lastTimestamp全局事件排查进入调试副本oc debug pod/name容器崩溃也能进去查节点维护oc adm cordon/drain/uncordon node三步走别跳步骤用户授权oc adm policy add-role-to-user role user -n project按最小权限给集群诊断收集oc adm must-gather提交平台团队前先跑这个这张表没有覆盖所有命令但日常 90% 的操作都够了。最后分享一个小习惯。我在本地的 shell 配置里放了几个函数比如ocp-login-test、ocp-login-prod、ocp-desc分别用来切换不同环境的登录和快速描述资源。这样不仅避免重复敲一长串参数还能防止在测试环境配好的脚本不小心在生产环境执行的问题。配置方式很简单就是普通的 shell 函数加别名但用起来确实能减少很多低级失误。不同团队管理规模不一样你可以按自己的集群数量和环境命名习惯来做这层封装。总的原则只有一个能让命令少敲一半的封装都值得做能把环境区分开的封装必须做。