- 前端
- UI组件
- 后端
【免费下载链接】uppy
The next open source file uploader for web browsers :dog:
Companion 是 Uppy 文件上传器的服务端配套组件,负责在浏览器与 Google Drive、Dropbox、Box 等云存储之间建立服务端到服务端的通信与 OAuth 授权中转。本文以仓库中的 KUBERNETES.md 为主线,完整讲解如何使用官方 Docker 镜像将 Companion 部署到 Kubernetes:从创建命名空间、用 Helm 安装 Redis,到以 Secret 注入全部环境变量、编写 Deployment 与 Service,并进一步结合仓库源码与基础设施目录,给出持久化存储、Ingress、HPA 等生产级增强方案。读完后你将获得一套可直接复制运行的 K8s 部署 YAML,以及每个配置项背后的源码级原理。
一、部署前准备:官方 Docker 镜像与默认端口
Companion 的 Kubernetes 部署建立在官方 Docker 镜像之上。仓库根目录的 Dockerfile 展示了镜像的构建过程:
- 基于
node:26.10.0-alpine分阶段构建,先用corepack yarn workspaces focus @uppy/companion安装依赖并执行build,再切到生产依赖,最终拷贝dist、package.json与node_modules到运行镜像; - 镜像入口命令为
node /app/dist/bin/companion.js(见 Dockerfile 第 33 行),即启动 standalone 模式的 Companion 服务; - 镜像默认
EXPOSE 3020(Dockerfile 第 35 行),这与源码中端口的默认值完全一致——start-server.ts 中监听端口取自process.env['COMPANION_PORT'] || process.env['PORT'] || 3020。也就是说,只要不显式设置COMPANION_PORT,容器内的服务必然监听 3020,这也是下文 Deployment 中containerPort: 3020、Service 中targetPort: 3020的依据。
在 Kubernetes 中使用的镜像是docker.io/transloadit/companion:latest。需要注意的是:latest标签适合快速验证,若要保证可复现性,建议像仓库的 gcloud-deploy.sh 一样按 commit 或 git tag 打镜像标签(该脚本同时推送transloadit/companion:$TRAVIS_COMMIT、transloadit/companion:$TRAVIS_TAG与latest三个标签),并在 Deployment 中固定引用版本化标签。
二、创建命名空间并安装 Redis
Companion 依赖 Redis 来共享会话与跨实例状态,因此部署的第一步是创建专属命名空间,并在其中安装 Redis。
kubectl create ns uppy随后通过 Helm 在uppy命名空间中安装 Redis:
helm install --name redis \ --namespace uppy \ --set password=superSecretPassword \ stable/redis说明:以上命令保留了 KUBERNETES.md 中的原始写法(Helm v2 时代的
--name语法)。若使用 Helm v3,可等价写作helm install redis stable/redis -n uppy --set auth.password=superSecretPassword(具体参数以你所用的 Redis chart 版本为准)。核心思路不变:Redis 必须与 Companion 处于同一集群,Companion 通过集群内 DNS 地址访问它。
--set password=superSecretPassword设置了 Redis 密码,这个密码稍后会以redis://:superSecretPassword@...的形式出现在COMPANION_REDIS_URL中。
为什么必须用 Redis?从 standalone/index.ts 可以看到:Companion 用express-session管理会话,会话用于 OAuth2 授权跳转过程(避免把敏感 token 暴露在 URL 中)。当配置了redisUrl时,会话存储会切换为RedisStore(第 130-137 行),会话 cookie 的maxAge为 10 分钟(第 139-146 行),足够用户完成一次授权流程。在多副本部署下,任何副本都能通过共享的 Redis 读取会话,这正是部署两个及以上副本所必需的。
三、环境变量注入:companion-env.yml(Secret)
Companion 的全部运行配置都通过环境变量读取(standalone 模式的配置解析集中在 helper.ts 的getConfigFromEnv()中)。由于其中包含大量密钥(OAuth Key/Secret、AWS 凭证、服务端 Secret 等),Kubernetes 部署时统一放进Secret中,再通过 Deployment 的envFrom注入容器。
以下为原文档提供的companion-env.yml完整内容:
apiVersion: v1 data: COMPANION_CLIENT_ORIGINS: 'localhost:3452,uppy.io' COMPANION_DATADIR: 'PATH/TO/DOWNLOAD/DIRECTORY' COMPANION_DOMAIN: 'YOUR SERVER DOMAIN' COMPANION_DOMAINS: 'sub1.domain.com,sub2.domain.com,sub3.domain.com' COMPANION_PROTOCOL: 'YOUR SERVER PROTOCOL' COMPANION_STREAMING_UPLOAD: true COMPANION_TUS_DEFERRED_UPLOAD_LENGTH: true COMPANION_REDIS_URL: redis://:superSecretPassword@uppy-redis.uppy.svc.cluster.local:6379 COMPANION_SECRET: 'shh!Issa Secret!' COMPANION_PREAUTH_SECRET: 'another secret' COMPANION_DROPBOX_KEY: 'YOUR DROPBOX KEY' COMPANION_DROPBOX_SECRET: 'YOUR DROPBOX SECRET' COMPANION_BOX_KEY: 'YOUR BOX KEY' COMPANION_BOX_SECRET: 'YOUR BOX SECRET' COMPANION_GOOGLE_KEY: 'YOUR GOOGLE KEY' COMPANION_GOOGLE_SECRET: 'YOUR GOOGLE SECRET' COMPANION_AWS_KEY: 'YOUR AWS KEY' COMPANION_AWS_SECRET: 'YOUR AWS SECRET' COMPANION_AWS_BUCKET: 'YOUR AWS S3 BUCKET' COMPANION_AWS_REGION: 'AWS REGION' COMPANION_AWS_PREFIX: 'AWS PREFIX' COMPANION_OAUTH_DOMAIN: 'sub.domain.com' COMPANION_UPLOAD_URLS: 'http://tusd.tusdemo.net/files/,https://tusd.tusdemo.net/files/' kind: Secret metadata: name: companion-env namespace: uppy type: Opaque注意:
kind: Secret的data字段在真实 Kubernetes 中要求Base64 编码的值。上面的写法是文档中的示意形式,实际落地时应对每个值执行echo -n 'value' | base64。若希望直接写明文,可将data改为stringData。
各环境变量的作用与源码对应关系如下表:
| 环境变量 | 对应配置项 | 说明与源码依据 |
|---|---|---|
COMPANION_CLIENT_ORIGINS | corsOrigins | 允许跨域访问的浏览器来源,逗号分隔。getCorsOrigins()(helper.ts)支持true/false/*三种特殊值;普通值若不带协议,会按COMPANION_PROTOCOL自动补全为http(s):// |
COMPANION_DATADIR | filePath | 文件下载/暂存目录,必须可读写。validateConfig()(companion.ts)启动时会用fs.accessSync校验目录存在且具备读写权限,否则直接拒绝启动 |
COMPANION_DOMAIN | server.host | Companion 对外暴露的域名/主机名 |
COMPANION_DOMAINS | server.validHosts | 合法主机名白名单,逗号分隔;配置解析时会被parseAllowlist()转换为RegExp或字面量(helper.ts)。以^开头的条目按正则处理,未锚定的正则会同时匹配"仅包含"该模式的值,需谨慎 |
COMPANION_PROTOCOL | server.protocol | http或https,默认http(helper.ts) |
COMPANION_STREAMING_UPLOAD | streamingUpload | 是否启用流式上传,true/false |
COMPANION_TUS_DEFERRED_UPLOAD_LENGTH | tusDeferredUploadLength | 是否启用 tus 延迟声明文件长度(服务端先不告知总长,边传边算),未设置时默认true(helper.ts) |
COMPANION_REDIS_URL | redisUrl | Redis 连接串,如redis://:密码@服务名.命名空间.svc.cluster.local:6379 |
COMPANION_SECRET | secret | 服务端签名密钥。未设置时 Companion 会自动生成并打警告日志(helper.ts),但重启会失效,生产环境务必显式设置 |
COMPANION_PREAUTH_SECRET | preAuthSecret | 预授权(preflight)签名密钥,同理建议显式设置 |
COMPANION_DROPBOX_KEY/COMPANION_DROPBOX_SECRET | providerOptions.dropbox | Dropbox OAuth 凭证 |
COMPANION_BOX_KEY/COMPANION_BOX_SECRET | providerOptions.box | Box OAuth 凭证 |
COMPANION_GOOGLE_KEY/COMPANION_GOOGLE_SECRET | providerOptions.drive | Google Drive OAuth 凭证(注意:源码中 Google Drive 的 provider 名是drive,见 helper.ts) |
COMPANION_AWS_KEY/COMPANION_AWS_SECRET/COMPANION_AWS_BUCKET/COMPANION_AWS_REGION/COMPANION_AWS_PREFIX | s3.* | S3 上传配置:AccessKey、Secret、桶名、区域与对象键前缀。前缀会拼接到getKey生成的每个对象键之前(defaultStandaloneGetKey,见 helper.ts) |
COMPANION_OAUTH_DOMAIN | server.oauthDomain | OAuth 回调使用的域名 |
COMPANION_UPLOAD_URLS | uploadUrls | tus 服务器上传地址白名单。这是安全关键项:validateConfig()在NODE_ENV=production下若未配置会直接抛错拒绝启动(companion.ts);同时会校验条目必须是带协议的绝对 URL(如https://example.com/files/),URL 中的查询串与 fragment 在匹配时会被忽略 |
仓库的 env_example 还列出了更多可选变量,按需补充到 Secret 中即可:
COMPANION_PORT=3020 # 监听端口,默认 3020 COMPANION_SELF_ENDPOINT=uppy.xxxx.com # 用于向远端上报的自身地址 COMPANION_HIDE_METRICS=false # false 时暴露 /metrics 统计端点 COMPANION_HIDE_WELCOME=false # false 时根路径返回欢迎/启动诊断信息 COMPANION_MAX_FILENAME_LENGTH=500 # 文件名最大长度,默认 500 COMPANION_AWS_ENDPOINT= # S3 兼容端点(非 AWS 时使用) COMPANION_AWS_FORCE_PATH_STYLE="false" # 是否强制 path-style 访问另一个值得注意的机制是_FILE后缀变量:getSecret()(helper.ts)会优先读取COMPANION_SECRET_FILE、COMPANION_DROPBOX_SECRET_FILE等指向的文件内容,其次才读同名普通变量。在 Kubernetes 中,这正好可以与 Secret 卷挂载结合,让密钥以文件形式进入容器,避免出现在进程环境中。
四、Deployment:companion-deployment.yml
环境变量准备好后,编写 Deployment 拉起 Companion 容器。以下为原文档的companion-deployment.yml完整内容:
apiVersion: extensions/v1beta1 kind: Deployment metadata: name: companion namespace: uppy spec: replicas: 2 minReadySeconds: 5 strategy: type: RollingUpdate rollingUpdate: maxSurge: 2 maxUnavailable: 1 template: metadata: labels: app: companion spec: containers: - image: docker.io/transloadit/companion:latest imagePullPolicy: ifNotPresent name: companion resources: limits: memory: 150Mi requests: memory: 100Mi envFrom: - secretRef: name: companion-env ports: - containerPort: 3020 volumeMounts: - name: companion-data mountPath: /mnt/companion-data volumes: - name: companion-data emptyDir: {}kubectl apply -f companion-deployment.yml这份清单的关键点:
replicas: 2:多副本 + 共享 Redis 会话,是 Companion 横向扩展的基础;- 滚动更新策略:
maxSurge: 2允许更新时最多多出 2 个新 Pod,maxUnavailable: 1允许最多 1 个旧 Pod 不可用,minReadySeconds: 5要求新 Pod 就绪后保持 5 秒才继续滚动,兼顾可用性与更新速度; envFrom.secretRef: companion-env:把上一步的 Secret 中全部键值作为环境变量注入容器,是连接"配置"与"运行"的枢纽;containerPort: 3020:与镜像EXPOSE 3020及源码默认端口一致;/mnt/companion-data挂载emptyDir:承接COMPANION_DATADIR指向的下载/暂存目录。但emptyDir的生命周期与 Pod 相同,Pod 重建即清空。原文档在此采用emptyDir便于快速起步;生产环境若需保留中间数据,应改用持久卷(见下文第五节)。
兼容性提示:原文档使用的
apiVersion: extensions/v1beta1属于旧版 Kubernetes API(1.16 之前),较新集群已移除。下文仓库自带的参考清单使用apps/v1,建议新部署直接采用。
五、Service:companion-service.yml
Deployment 只负责"跑起来",对外暴露还需要 Service。原文档的 Service 清单:
apiVersion: v1 kind: Service metadata: name: companion namespace: uppy spec: ports: - port: 80 targetPort: 3020 protocol: TCP selector: app: companionkubectl apply -f companion-service.yml要点:
port: 80是集群内访问端口,targetPort: 3020转发到 Pod 中 Companion 的实际监听端口;selector: app: companion与 Deployment 模板中的 Pod 标签一一对应,Service 据此选择后端 Pod,Kubernetes 会自动为多个副本做负载均衡;- 在集群内部,其他组件可通过
companion.uppy.svc.cluster.local:80访问它;对集群外部,则需额外配置 Ingress 或 NodePort/LoadBalancer。
六、生产级增强:仓库自带的全套参考清单
除了文档中的基础三件套,仓库在 infra/kube/companion/companion-kube.yaml 提供了一份更贴近生产环境的完整清单(Service + StatefulSet + Ingress + HPA,用---分隔的多个资源)。其关键差异正是对原文档方案的升级方向:
1. 用 StatefulSet 替代 Deployment,实现数据持久化
apiVersion: apps/v1 kind: StatefulSet metadata: name: companion namespace: companion spec: selector: matchLabels: app: companion replicas: 2 serviceName: 'companion' template: metadata: labels: app: companion spec: containers: - image: docker.io/transloadit/companion:latest imagePullPolicy: Always name: companion envFrom: - secretRef: name: companion-env ports: - containerPort: 3020 volumeMounts: - name: companion-data mountPath: /mnt/companion-data volumeClaimTemplates: - metadata: name: companion-data spec: accessModes: ['ReadWriteOnce'] resources: requests: storage: 10Gi相比原文档的emptyDir,这里的volumeClaimTemplates会为每个副本自动创建独立的 PVC(每副本 10Gi,ReadWriteOnce),Pod 被调度/重建到任意节点时数据都能随卷保留。
2. Ingress + 自动 HTTPS
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: companion namespace: companion annotations: kubernetes.io/tls-acme: 'true' kubernetes.io/ingress.class: 'nginx' certmanager.k8s.io/cluster-issuer: 'letsencrypt-prod' certmanager.k8s.io/acme-http01-edit-in-place: 'true' spec: tls: - secretName: server-tls hosts: - companion.uppy.io - secretName: uppy-tls hosts: - server.uppy.io rules: - host: companion.uppy.io http: paths: - path: / backend: serviceName: companion servicePort: 80 - host: server.uppy.io http: paths: - path: / backend: serviceName: companion servicePort: 80通过 cert-manager 注解自动签发 Let's Encrypt 证书,companion.uppy.io与server.uppy.io两个域名都路由到companionService 的 80 端口。
3. HorizontalPodAutoscaler 自动扩缩容
apiVersion: autoscaling/v1 kind: HorizontalPodAutoscaler metadata: name: companion namespace: companion spec: scaleTargetRef: apiVersion: apps/v1 kind: Statefulset name: companion minReplicas: 1 maxReplicas: 5 targetCPUUtilizationPercentage: 80当平均 CPU 使用率超过 80% 时,HPA 会在 1~5 个副本之间自动伸缩,与 StatefulSet 的滚动更新策略配合,形成弹性伸缩能力。
4. CI/CD 参考:gcloud-deploy.sh
仓库的 gcloud-deploy.sh 展示了镜像发布与集群更新的完整链路:构建并推送transloadit/companion镜像(commit/tag/latest 三标签)→ 解码KUBECONFIGVAR写入 kubeconfig → 用kubectl set image statefulset companion ...热更新镜像 → 依次kubectl get pods/service/deployment校验部署结果。这套脚本的思路可直接迁移到你自己的 CI 流水线中。
七、日志查看
部署完成后,查看生产 Pod 的日志:
kubectl logs my-pod-name如果 Pod 名不易确定,也可以直接用 Deployment/StatefulSet 级联查看:
# 查看命名空间下所有 Pod kubectl get pods -n uppy # 跟随输出某个 Deployment 的最新日志 kubectl logs deployment/companion -n uppy --follow # 查看上一个(已崩溃的)容器实例的日志 kubectl logs my-pod-name --previousCompanion 的日志系统也内置了安全处理:在 standalone/index.ts 中,请求日志(morgan)会通过censorQuery()对access_token、uppyAuthToken等敏感查询参数做掩码处理(替换为********),避免 OAuth 过程中的令牌泄漏到日志里。排查问题时若发现日志中看不到完整参数,这属于预期行为,而非日志丢失。
八、部署自检清单
综合原文档与源码,完成部署后建议按以下顺序核对:
- 命名空间与资源就位:
kubectl get ns uppy、kubectl get secret companion-env -n uppy、kubectl get pods -n uppy均正常; - Redis 可达:
COMPANION_REDIS_URL中的服务名、命名空间(uppy-redis.uppy.svc.cluster.local)、密码与 Helm 安装时--set password一致; - 生产强制项:
COMPANION_UPLOAD_URLS必须配置且为带协议的绝对 URL(含 tus 服务地址),否则NODE_ENV=production下 Companion 拒绝启动(companion.ts); - 目录可写:
COMPANION_DATADIR指向的挂载路径必须存在且可读写,否则validateConfig()在启动时抛错; - Secret 显式设置:
COMPANION_SECRET与COMPANION_PREAUTH_SECRET不要依赖自动生成,避免重启后会话/预授权签名失效; - OAuth 回调:启动日志(欢迎页或
kubectl logs)会列出每个 provider 的/redirect回调地址,需与云服务商开发者后台配置一致。
至此,从命名空间、Redis、Secret 配置到 Deployment、Service,再到 StatefulSet 持久化、Ingress、HPA 与日志排查,一套完整的 Companion on Kubernetes 部署链路已全部打通。若需要更完整的配置变量清单,可对照 env_example 与 schemas/companion.ts 按需补充。
- 前端
- UI组件
- 后端
【免费下载链接】uppy
The next open source file uploader for web browsers :dog:
相关推荐
daily.dev容器化部署:Docker+Kubernetes实战指南
daily.dev容器化部署:Docker+Kubernetes实战指南 作为开发者,你是否还在为daily.dev的部署环境配置而烦恼?服务器兼容性问题、依赖
AI 技能/插件AI Agent15分钟上手Wiki.js容器化部署:Docker与Kubernetes实战指南
15分钟上手Wiki.js容器化部署:Docker与Kubernetes实战指南 你是否还在为Wiki系统的部署环境配置而烦恼?服务器依赖冲突、版本不一致、扩展
后端前端知识库知识管理AISystem容器化:Docker与Kubernetes部署实战指南
AISystem容器化:Docker与Kubernetes部署实战指南 引言:为什么需要容器化AI系统? 在当今AI技术飞速发展的时代,AI系统的部署和管理面临
文档教程人工智能
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考