搭建 NPM Proxy

工欲善其事,必先利其器

背景是公司内部服务器是网络隔离的, 但是要做自动化构建、测试等,通常都需要从外部拉取依赖。所以,我又要搭建内部的镜像源了。(为什么说又呢。。。)

毫无疑问,是要用到一个服务器专门做镜像源的服务器,它有特殊的网络策略,内网的服务器可以和它连通,用作内外网的转发,以下假设使用 Nginx 做反向代理。

那么,当前公司内部之前已经搭建了 Private NPM Registry(使用 cnpm 搭建),私有包自然是从内部服务器下载,而 Public 包,则是通过 301 重定向到 taobao registry

npm install xxxx -dd  # verbose mode 分析npm下载过程

一开始的思路是希望在 Nginx 内部做重定向(serverfault),防止在客户端做重定向,而客户端网络隔离。以下为参考配置,主要方法是将 301/302/307 的 HTTP 请求定义为错误并内部重定向到特定 uri ,然后再通过 proxy_pass 反向代理至公共源。

server {
    ...
    
    location / {
        proxy_pass http://reg.cnpm.internal;  # 内部搭建的CNPM
        proxy_intercept_errors on;
        error_page 301 302 307 = @handle_redirect;
    }

    location @handle_redirect {
        set $saved_redirect_location '$upstream_http_location';
        proxy_pass http://registry.taobao.org;  # 镜像服务器能够访问的NPM
    }
}

配置好之后使用 npm install 测试,发现依然失败,分析 verbose 得知,registry info 已经成功获取到数据,但是 info 里面的 tarball 地址却还是原来注册服务器的地址。通过 npm info xxx 或者直接 curl 可以验证:

{
  _id:"compare",
  _rev:"6-d647d4531309933813e678fc321bb664",
  name:"compare",
  description:"Compare primitives the right way (using `<`, `>` and `==`)",
  dist-tags:{
    latest:"2.0.0"
  },
  versions:{
    #...
    2.0    .0:{
      #...
      dist:{
        integrity:"sha512-FXeLLVm09Uh7Updmmx2NCCRG2nMq+mdY3DR9PqhVeOrie3IFU+occFQoqziFkHlTUDw8mDgmdblIZ+J9tsSAUA==",
        shasum:"8090b34dcb288f629e905972da69ea7a6d5922a0",
        tarball:"https://registry.npmjs.org/compare/-/compare-2.0.0.tgz",
        fileCount:4,
        unpackedSize:2682,
        npm-signature:"... "
      },
      #...
    }
  },
  #...
}

在开发人员的工作电脑上,自然是可以正常使用的,但在内部服务器通向互联网的流量都被阻止了。

所以,“代理”不能够单纯地转发响应,而且要“修改”响应。我选择了 Verdaccio

  • It's a web app based on Node.js
  • It's a private npm registry
  • It's a local network proxy
  • It's a Pluggable application
  • It's a fairly easy install and use
  • We offer Docker and Kubernetes support

从官方仓库下载默认配置文件:https://github.com/verdaccio/verdaccio/blob/master/conf/docker.yaml,按照官方配置文档配置多个 uplink (类似 Nginxupstream ),然后在 packages 下配置包匹配规则。

uplinks:
  npmjs:
    url: https://registry.npmjs.org/
    cache: true
  internal_npm:
    url: http://reg.cnpm.internal
    cache: false
packages:
  '@private_scope/*': # 根据情况配置私有scope,可以配置多条
    access: $all
    proxy: internal_npm # 转发到上游
  '**':
    # 匹配所有包
    access: $all
    #publish: $authenticated
    proxy: npmjs

然后启动:

docker run --name verdaccio \
-v /path-to/verdaccio/config.yaml:/verdaccio/conf/config.yaml:ro \
-v /data/verdaccio/storage:/verdaccio/storage \
-v /data/verdaccio/plugins:/verdaccio/plugins \
-d \
-p 4873:4873 \
--restart always \
verdaccio/verdaccio

至此,这个 NPM 代理即可正常地在内部使用, info 响应里面的 tarball 也会被替换成代理的链接,并且同时支持内部包、外部包。

2019-02-25 23:00