DevSpace:在 Kubernetes 中同步代码、调试与恢复工作负载
DevSpace 把本地编辑器连接到 Kubernetes 中的应用:代码保存在电脑上,程序和依赖运行在集群里,文件同步、端口转发与终端连接由一个开发会话管理。项目需要集群内的数据库、消息服务或特定运行资源时,这种方式可以减少本地环境的搭建工作。
它通过 kubeconfig 访问 Kubernetes,CLI 本身无需安装常驻服务端。项目里的 devspace.yaml 决定构建哪些镜像、部署哪些资源、连接哪个容器。已有 Helm Chart 或 YAML 可以继续使用。项目架构与适用环境
安装并准备一个独立开发环境
CLI、集群和执行身份
以下命令在 Linux x86-64 的 Bash 中执行,需要 curl、Docker Engine、kubectl 和 kind。当前用户应能执行 docker info;应用进程使用 UID/GID 10001:10001。尚未准备环境时,先完成 Docker 安装、kubectl 配置和 kind 安装与建群。Docker 控制权限接近宿主机管理权限,不要把生产 Docker socket 交给普通开发容器。
固定使用 DevSpace 6.3.21 与 v2beta1 配置。该发行版已停止 NPM/Yarn 分发,并修复 WebSocket 来源检查;不要继续套用旧的 npm install -g devspace 安装方式。其他系统从同一发行页选择对应架构,并核对资产摘要。发行说明与下载
set -euo pipefail
devspace_install=$(mktemp -d)
mkdir -p "$devspace_install/bin"
curl -q --fail --location --proto '=https' --tlsv1.2 \
https://github.com/devspace-sh/devspace/releases/download/v6.3.21/devspace-linux-amd64 \
-o "$devspace_install/devspace"
printf '%s %s\n' \
'9720a6c87a2465f6d73b4db5a908e15e1971158443b5de9162d94aa5f87f79f0' \
"$devspace_install/devspace" | sha256sum --check
install -m 0755 "$devspace_install/devspace" "$devspace_install/bin/devspace"
export PATH="$devspace_install/bin:$PATH"
devspace version
docker info
kubectl version --client
kind version摘要检查应为 OK,CLI 显示 6.3.21。安装目录只加入当前终端的 PATH;另开终端时要重新设置,或将已校验文件安装到自己的工具目录。校验失败就停止,不运行下载文件,也不要关闭 HTTPS 校验来绕过网络错误。
创建专属实验目录和 kubeconfig。以下集群名 tooling-dev 必须尚未被其他实验使用;若已有同名集群,选择新名称,并同步修改后面的 context 参数和配置检查。
devspace_project=$(mktemp -d)
cd "$devspace_project"
export KUBECONFIG="$devspace_project/kubeconfig"
kind create cluster --name tooling-dev \
--image kindest/node:v1.35.5@sha256:ce977ae6d65918d0b58a5f8b5e940429c2ce42fa3a5619ec2bbc60b949c0ac95 \
--kubeconfig "$KUBECONFIG" --wait 120s
kubectl --context kind-tooling-dev create namespace devspace-lab
kubectl --context kind-tooling-dev -n devspace-lab auth can-i create deployments.apps
kubectl --context kind-tooling-dev -n devspace-lab auth can-i create pods/exec
kubectl --context kind-tooling-dev -n devspace-lab auth can-i create pods/portforward
mkdir web三个权限检查应返回 yes。独立 kind 的管理凭证只用于本地实验。共享集群还需要读取资源、修改控制器、读取日志等权限;不能用这三项检查推导出所有操作均获授权。
项目中的五个文件
应用使用 BusyBox HTTP 服务直接读取 HTML,便于观察“文件已上传”和“页面已更新”的关系。Java、Go 等编译语言还需要构建步骤,后面单独说明。
当前目录
├── Dockerfile 构建初始镜像
├── .dockerignore 排除本地状态和凭证
├── k8s.yaml Deployment 与 Service
├── devspace.yaml 部署流水线及开发连接
└── web/index.html 唯一同步的源码目录保存 Dockerfile:
FROM busybox:1.37.0
RUN mkdir -p /www && chown 10001:10001 /www
COPY --chown=10001:10001 web/ /www/
USER 10001:10001
EXPOSE 8080
CMD ["httpd", "-f", "-p", "8080", "-h", "/www"]保存 .dockerignore,避免构建上下文包含工具缓存和本例位于项目内的 kubeconfig:
.git
.devspace
bin
effective.yaml
*.log
kubeconfig保存 web/index.html:
<!doctype html>
<html lang="en"><meta charset="utf-8"><title>DevSpace lab</title><h1>devspace-v1</h1></html>保存 k8s.yaml:
apiVersion: apps/v1
kind: Deployment
metadata:
name: devspace-web
namespace: devspace-lab
spec:
replicas: 1
selector:
matchLabels: {app: devspace-web}
template:
metadata:
labels: {app: devspace-web}
spec:
securityContext:
runAsUser: 10001
runAsGroup: 10001
runAsNonRoot: true
containers:
- name: web
image: local/devspace-web
imagePullPolicy: IfNotPresent
ports:
- containerPort: 8080
readinessProbe:
httpGet: {path: /, port: 8080}
resources:
requests: {cpu: 10m, memory: 16Mi}
limits: {cpu: 200m, memory: 64Mi}
---
apiVersion: v1
kind: Service
metadata:
name: devspace-web
namespace: devspace-lab
spec:
selector: {app: devspace-web}
ports:
- port: 8080
targetPort: 8080/www 归应用用户所有,允许同步写入;没有挂载数据卷。readinessProbe 检查 HTTP 服务。这里的 CPU、内存限制针对静态页面,换成 JVM 或编译容器时需按实际运行和构建占用调整。
用配置连接部署与开发会话
deploy 构建镜像,dev 连接容器
保存 devspace.yaml:
version: v2beta1
name: devspace-web
localRegistry:
enabled: false
functions:
check_target: |-
test "${DEVSPACE_CONTEXT}" = "kind-tooling-dev" || exit 1
test "${DEVSPACE_NAMESPACE}" = "devspace-lab" || exit 1
pipelines:
deploy: |-
check_target
build_images web
create_deployments web
dev: |-
check_target
start_dev web
images:
web:
image: local/devspace-web
dockerfile: Dockerfile
skipPush: true
createPullSecret: false
docker:
useCli: true
disableFallback: true
deployments:
web:
updateImageTags: true
kubectl:
manifests: [k8s.yaml]
dev:
web:
labelSelector: {app: devspace-web}
container: web
env:
- name: DEVSPACE_LAB
value: "true"
sync:
- path: ./web:/www
initialSync: mirrorLocal
disableDownload: true
waitInitialSync: true
printLogs: true
excludePaths: [.git/, .env, '*.key']
ports:
- port: "18084:8080"
bindAddress: 127.0.0.1
logs: {}五个文件也可分别保存:Dockerfile、.dockerignore、HTML、Kubernetes 清单、DevSpace 配置。
pipelines 使用 POSIX 风格脚本,build_images、create_deployments、start_dev 是 DevSpace 提供的函数。自定义 dev 在这里仅建立开发连接,所以必须先执行一次 deploy;后续修改 HTML 无需反复构建镜像。更复杂项目可以加入测试、依赖部署和多个开发容器,但先后关系要写在流水线里。流水线及内置函数
images.web 使用本机 Docker 构建,禁止推送和回退到集群内构建。DevSpace 识别 kind 后会将构建镜像加载到节点;日志中应出现加载成功,而不是要求节点从 Docker Hub 拉取 local/devspace-web。镜像构建配置
updateImageTags: true 会在提交前替换清单中匹配的镜像引用,磁盘上的 k8s.yaml 保持原样。kubectl 负责提交 Deployment 和 Service。清单部署与标签替换
本例显式关闭 localRegistry,镜像只进入本地 kind。DevSpace 也能在缺少推送权限时启用仓库回退;其默认临时存储不能承担长期制品保存。团队远端集群应配置批准的 registry 与拉取凭证,而不是照搬这里的 skipPush 和本地镜像加载方式。本地仓库行为
首次部署、访问和身份核对
devspace print --skip-info --disable-profile-activation \
--kube-context kind-tooling-dev -n devspace-lab
devspace deploy --disable-profile-activation \
--kube-context kind-tooling-dev -n devspace-lab
kubectl --context kind-tooling-dev -n devspace-lab \
rollout status deployment/devspace-web --timeout=120s
devspace dev --disable-profile-activation \
--kube-context kind-tooling-dev -n devspace-labprint 展示变量和 profile 处理后的配置。检查 check_target 中的 context、namespace、同步路径和端口,再执行部署;无效 kubeconfig 可能令这些值为空。环境变量或 .env 也可影响配置,DEVSPACE_*_FLAGS 会添加默认参数。显式参数与目标检查可以减少误连,但仍应先检查实际展开结果。配置变量与默认参数
dev 保持前台运行。等待文件同步初始化完成和 127.0.0.1:18084 转发建立,再在另一个终端执行:
curl -q --noproxy '*' --fail-with-body --max-time 5 http://127.0.0.1:18084/响应应包含 devspace-v1。18084:8080 表示本机 18084 转发到目标容器 8080,监听地址限制为回环。转发通过 Kubernetes API 连接选定 Pod,并未经过公网 Ingress;因此这一步验证的是开发访问路径。端口转发与反向转发
原 Deployment 为什么缩成零副本
配置中的 env 修改了运行容器。DevSpace 6.x 会找到拥有 Pod 的控制器,将原控制器缩到零副本,再创建开发控制器并应用修改。应用 Service 仍可通过标签选择开发 Pod。这个机制会影响原来的流量,因此只对自己拥有的开发工作负载操作。开发容器修改过程
另一个终端切到同一项目目录,设置同一份 KUBECONFIG 后检查:
kubectl --context kind-tooling-dev -n devspace-lab get deployments,pods
devspace enter --kube-context kind-tooling-dev -n devspace-lab \
--label-selector app=devspace-web --container web --pick=false --tty=false -- id
devspace enter --kube-context kind-tooling-dev -n devspace-lab \
--label-selector app=devspace-web --container web --pick=false --tty=false -- printenv DEVSPACE_LAB原 devspace-web 的期望副本为 0,开发控制器拥有 Ready Pod;id 应显示 UID/GID 10001,环境变量返回 true。选到多个 Pod 时,先用 kubectl get pods 找到当前开发 Pod,再用 --pod 实际名称 指定,避免在交互选择中误入其他实例。
按需执行 enter 可以打开额外终端。若改用配置中的 terminal: {},默认行为还会替换容器、移除探针并改变启动命令;disableReplace 可禁止这种隐式替换。需要原应用持续运行时,优先保留 logs: {},另开 enter,不要把打开 Shell 当成启动应用。终端、attach 与日志模式
同步代码、调整项目并恢复镜像版本
看清同步方向和删除范围
./web:/www 把本地 web 映射到容器 /www。mirrorLocal 初始同步以本地目录为准:覆盖冲突文件,也会删除远端多出来的文件;disableDownload: true 禁止远端变化回传。两个选项一起使用,仍然允许上传和远端删除,并非只读同步。文件同步策略与选项
日志中的 ./web <-> /www 是路径展示;是否回传取决于配置,不能根据双向箭头判断。以初始同步完成、实际文件内容和下面的远端文件反例确认行为。
在保存项目的终端中,把 web/index.html 的 devspace-v1 改成 devspace-v2。开发会话应报告上传,再请求:
curl -q --noproxy '*' --fail-with-body --max-time 5 http://127.0.0.1:18084/
kubectl --context kind-tooling-dev -n devspace-lab get pods -l app=devspace-web \
-o custom-columns=NAME:.metadata.name,UID:.metadata.uid,IMAGE:.spec.containers[0].image页面变成 v2,Pod UID 和镜像引用应与编辑前相同。HTTP 服务直接读取文件,所以无需重启。此时 v2 只存在本地文件和开发容器可写层,初始镜像仍保存 v1。
再从开发容器创建一个临时文件,核对单向限制:
devspace enter --kube-context kind-tooling-dev -n devspace-lab \
--label-selector app=devspace-web --container web --pick=false --tty=false -- touch /www/remote-only.txt
devspace enter --kube-context kind-tooling-dev -n devspace-lab \
--label-selector app=devspace-web --container web --pick=false --tty=false -- ls /www/remote-only.txt
test ! -e web/remote-only.txt && printf 'remote file was not downloaded\n'远端文件存在,本地不应出现同名文件。重新开始一次 mirrorLocal 初始同步时,这个远端额外文件可能被删除。需要保留远端生成物时,优先将它们移到同步范围以外;preferLocal 可以保留远端额外文件,但它改变了初始合并规则,不能当成数据备份。
同步整个项目根目录容易把凭据送进容器,也可能反向覆盖本地文件。本例只同步 web,另外排除 .env、私钥和 Git 目录。数据库目录、证书挂载点和上传文件目录都应放在同步目标之外。采用 mirrorRemote 前尤其要检查本地目标,因为它会以远端为准删除本地额外文件。
编译语言需要补哪一步
同步 Java 源码后,JVM 不会自动运行新源码。一个 Maven 应用的开发镜像需要 JDK、Maven、可写依赖缓存和构建目录;上传之后要编译成功,再触发应用重载或重启。生产用的精简 JRE 镜像通常不具备这些条件,可以用 devImage 选择专门开发镜像。
DevSpace 的 onUpload.exec 可在上传后执行容器内命令,local: true 才表示在本机执行。编译命令需开启 failOnError: true,避免失败后仍重启到旧产物。onUpload.restartContainer 依赖开发配置中的启动命令和重启辅助程序;只增加这个开关不会替你编译。初次同步不会运行普通上传回调,因此启动前的首次编译还要放进应用启动流程。
调试端口同样属于开发连接:JVM 必须先按所用 JDK 配好 JDWP 监听,再把那个容器端口转发到本机。IDE 连接回环地址即可,不需要新建公网 NodePort。断点暂停会影响请求超时和探针,调试专用实例应与共享服务流量分离。
profile 与共享集群接入
多人同时运行同一项目时,可以用 profile 调整本机端口。将下面内容加入 devspace.yaml 顶层,再用 print 检查最终值:配置叠加与 profile
profiles:
- name: alternate-port
patches:
- op: replace
path: dev.web.ports[0].port
value: "18085:8080"devspace print --skip-info --disable-profile-activation -p alternate-port \
--kube-context kind-tooling-dev -n devspace-lab确认只有预期端口变为 18085:8080 后,停止旧会话,再用同样的 -p alternate-port 参数运行 dev。不要同时开启两个同步进程写同一个容器。自动激活规则可能按环境选择 profile;明确指定的 profile 更适合复现问题。
迁往远端开发集群时,要一起改动三个位置:check_target 和命令中的 context/namespace、清单中的 namespace、镜像仓库与推送/拉取配置。仅换命令里的 context 会被本例检查拒绝。每个开发者分配独立 namespace、资源名和同步目标;平台侧再配 RBAC、资源配额和网络访问限制。
远端应用会以集群身份访问数据库和其他服务。应使用开发账号与测试数据,检查 ServiceAccount、Secret 挂载和网络出口。开发模式可能缩零原控制器、变更启动命令甚至去掉探针,不能直接选中承接生产流量的 Deployment。
停止连接、恢复应用、重新部署
先在 dev 终端按 Ctrl-C,确认同步和端口转发退出。随后在项目目录显式恢复原工作负载:reset pods 命令
devspace reset pods --kube-context kind-tooling-dev -n devspace-lab
kubectl --context kind-tooling-dev -n devspace-lab \
rollout status deployment/devspace-web --timeout=120s
kubectl --context kind-tooling-dev -n devspace-lab get deployments,pods
kubectl --context kind-tooling-dev -n devspace-lab \
exec deployment/devspace-web -- cat /www/index.html原控制器应恢复为一个副本,开发控制器被移除,页面文件回到镜像里的 v1。原开发 Pod 的可写层会随删除消失;先把需要保留的生成结果复制到明确的保存位置。本例没有 PVC,不能据此推断真实项目中的持久卷也会删除或恢复。
本地文件仍是 v2。要把它变成可重复部署的版本,重新运行 devspace deploy,等待原 Deployment 就绪,再读取页面文件,应得到 v2。镜像或依赖变化同样走重新构建部署;开发连接不负责观察 Dockerfile 并自动重建。
升级 CLI 时保留原二进制,先在这个隔离 namespace 验证配置展开、上传、恢复和清理,再切换团队版本。回退 CLI 之前先用当前版本恢复开发副本。应用版本回退则需要保存旧镜像引用和对应清单,不能靠降级 DevSpace 或复制 .devspace 目录完成。
按失败阶段检查并清理实验
配置、镜像与 Pod 启动
| 现象 | 检查位置 | 下一步 |
|---|---|---|
print 报 schema 或变量错误 | CLI 版本、version、profile 和环境变量 | 对照当前 6.x 配置,先消除展开错误 |
check_target 非零退出 | 展开后的 context/namespace 与 kubeconfig | 修正实验目标,不删除检查函数来碰运气 |
| Docker 构建失败 | Dockerfile、构建上下文、基础镜像下载 | 在项目中检查构建日志,修复后重跑 deploy |
ImagePullBackOff | Pod 的实际镜像、节点是否已加载、远端拉取凭证 | 本地查 kind 加载日志;远端查 registry 和 Secret |
| 有 Pod,但没有 Ready | 容器命令、资源限制、HTTP 探针 | 先修复应用启动,不关闭所有探针掩盖错误 |
kubectl --context kind-tooling-dev -n devspace-lab get pods -o wide
kubectl --context kind-tooling-dev -n devspace-lab get events --sort-by=.metadata.creationTimestamp
kubectl --context kind-tooling-dev -n devspace-lab logs -l app=devspace-web --all-containers --tail=100事件中的拉取失败、调度失败和容器重启分别对应不同阶段。若原 Deployment 已缩零而开发 Pod 起不来,先保存失败日志,再停止开发会话并执行 reset pods 恢复原应用。不要同时让 GitOps、HPA 和开发工具反复修改同一个共享控制器。
文件已经保存,页面却没变化
先区分本地未上传、远端文件未生效和浏览器访问错误。确认开发会话仍运行、初始同步已完成,再用 enter ... -- cat /www/index.html 读取远端内容。远端还是 v1 时查路径和排除规则;远端已是 v2、curl 仍旧时查转发目标;curl 正常而浏览器旧时,再检查浏览器缓存。
同步需要向容器复制并执行辅助程序。镜像缺少 tar、临时目录不可写,或 UID 10001 无权写 /www,都会阻断这一过程。检查具体错误和目录属主,给开发镜像补所需工具或调整限定目录权限,避免把整个容器改为 root。首次运行若无法取得辅助程序,还要检查下载地址和网络访问。
端口占用时用 ss -ltnp 'sport = :18084' 查看监听者;确认是旧开发会话后正常退出它,或使用前面的端口 profile。不要随意结束未知进程。远端连接中断后,应同时检查重新选中的 Pod、同步是否恢复和 HTTP 内容,不只看 CLI 进程还在。
删除本项目创建的资源
停止 dev 后,先执行恢复,再清理该项目部署;purge 的删除范围来自项目记录和配置,因此要保留正确项目目录与 kubeconfig。purge 命令
devspace reset pods --kube-context kind-tooling-dev -n devspace-lab
devspace purge --kube-context kind-tooling-dev -n devspace-lab
kubectl --context kind-tooling-dev -n devspace-lab \
wait --for=delete pod -l app=devspace-web --timeout=120s
kubectl --context kind-tooling-dev -n devspace-lab get deployments,services,pods这里应不再有实验应用资源。删除请求返回与 Pod 完全消失之间可能有时间差,等待超时先检查终止状态。本例没有创建共享依赖或数据卷;真实项目执行 purge 前要查看部署清单,不能添加强制参数跨项目删除。
最后,仅在确认 tooling-dev 是本次创建的专属实验集群时执行:
kind delete cluster --name tooling-dev这会删除整个实验集群及节点内数据。保留本地源码;单独妥善处理不再使用的临时 kubeconfig,不要将它提交或打进镜像。
