前言

npm 作爲一種包管理工具,無論你是泛前端還是大前端都已經離不開它。它的出現方便了萬千少年。讓我們跨過了 Ctrl+C、Ctrl+V ,通過 ``npm install x 的方式將別人的優秀代碼模塊引入到自己的項目中。這些優秀的模塊能被共享的原因,一方面是有 npm 這麼一個包管理工具,另外就是 npm 倉庫。

對於 npm 倉庫,如果你還停留在使用 npm 或者 cnpm 這類官方源的情況下。那麼你有必要想想如何搭建一個私有的 npm 倉庫。下面從三個方面講解企業級 npm 私有倉庫搭建那些事兒,分分鐘教會你。

  • 爲什麼需要搭建公司的私有 npm 倉庫

  • 如何搭建私有倉庫

  • 搭建私有倉庫擴展篇

爲什麼需要搭建公司的私有 npm 倉庫

照慣例,先講講爲啥要搭建私有倉庫。目前已經有很多成熟的 npm 源可以使用,比如:

在已經有如此多公共倉庫的情況下,是否有必要搞一套私有 倉庫?重複"造輪子"?還是自嗨?如果從下面幾方面來考慮的話,或許能打消心中的疑問。

1. 穩定性

首先是網絡訪問穩定性,私有倉庫因爲是自己公司在維護,有什麼問題能第一時間處理,比如服務宕機…其次資源的穩定性,試想一下,如果哪天你依賴的某個很重要的模塊突然被作者刪了,那是不是完犢子了,畢竟我們很多時候都奉行的是 “拿來主義” ,一旦遇見這種情況,基本上全抓瞎。如果有私有倉庫,上面的問題可以從容面對,有效的保障了業務穩定。

2. 私密性

每個公司都有和自己業務強相關的模塊,或者對某些開源模塊進行個性化的改造,改造後的模塊只滿足本公司的業務場景,這些模塊我們並不希望發佈到公共的倉庫中去,這時就可以發佈到自己的私有倉庫在公司內部共享。

3. 安全性

有了私有倉庫後,可以在 npm 模塊的質量和安全上做文章,能夠有效的防治惡意代碼攻擊。

綜上,搭建自己公司的私有倉庫完全有必要,這並不是秀。當然,如果你所在的公司比較 mini ,對於上面的幾點需求並不是那麼迫切,使用公共倉庫也挺好。但當公司發展到一定規模,在未來可預見的情況下,那就是時候準備搭建自己的私有倉庫了。

如何搭建私有 npm 倉庫

目前已經有許多成熟開源方案,選擇站在巨人的肩膀上不失爲一種良策。這裏選擇 cnpmjs.org 方案,原因有三:

  1. 目前國內像淘寶這樣的大廠內部也是選擇的它,足以證明它的可靠行和穩定性

  2. 擴展性強

  3. 配置多樣化

當然缺點也不是沒有,就是部署有那麼一丟丟複雜。

準備工作

兵馬未動,糧草先行,既然是搭建企業級的應用,基礎環境得備好。

  • Linux 服務器

  • node 環境

  • 數據庫( Mysql )

  • nginx

大概就這些,如果你只是熟悉一下,搭着玩兒。也不一定得用 Linux 服務器,Windows 也行,但是如果是作爲線上應用,建議還是使用 Linux 服務器,畢竟 Linux 的穩定性在那裏擺着。

下面的示例是在雲服務器(Ubuntu)上完成的。

安裝

  1. 安裝cnpmjs.org

建議通過 git 將 cnpmjs.org 的項目源碼克隆到服務器本地某個目錄下。或者將代碼fork到自己git倉庫後,再基於內部倉庫進行部署,這樣方便以後對源碼進行個性化的改造。

git clone https://github.com/cnpm/cnpmjs.org.git

安裝項目依賴:

npm i

安裝完成後找到項目根目錄下的配置文件 config/index.js ,這裏配置文件非常多,剛開始可以只關注下面幾項即可, 詳細配置 戳這裏。

  • 服務訪問端口

    registryPort: 7001,         //倉庫服務訪問端口
    webPort: 7002,              //web站點訪問端口
    bindingHost: '',   //監聽綁定的 Host,默認127.0.0.1,外網訪問註釋掉此項即可,一般我們不會把我們內部端口暴露出去,可以在nginx層做一個轉發,所以這個配置可以註釋掉。如果直接外網訪問,配置爲 0.0.0.0
  • 數據庫配置

    database: {
      db: 'npm',數據庫名稱
      username: 'admin',//用戶
      password: 'admin123',//密碼
      // 數據庫類型
      // - 目前支持 'mysql', 'sqlite', 'postgres', 'mariadb'
      dialect: 'mysql',//默認是sqlite,我選擇的mysql
      host: '127.0.0.1', //數據庫服務地址
      port: 3306,    // 端口
      // 數據庫連接池使用默認配置就好
      // 目前只支持  mysql 和 postgresql (since v1.5.0)
      pool: {
        maxConnections: 10,
        minConnections: 0,
        maxIdleTime: 30000
      },
      ...//其他的暫時不用關注
    },
  • 是否啓用私有模式

    enablePrivate: false,//默認不啓用

    私有模式下,只有管理員才能發佈模塊。非管理員發佈模塊式命名必須以 scopes 字段開頭例如: @catfly/packagename

  • 發佈前綴

    scopes: ['@catfly'],

    這個和啓用非私有模式配套使用,非私有模式要發佈必須配置該項。

  • 管理員賬號配置

    admins: {
          fengmk2: '[email protected]',
          admin: '[email protected]',
          dead_horse: '[email protected]',
    }

    如果啓用私有模式,只有該配置項中的用戶可以發佈私有包。至於其他的配置項暫時不用關注,後面根據需要在逐漸配置起來。

  • 同步模式

    // 同步模式選項
    // none: 不進行同步,只管理用戶上傳的私有模塊,公共模塊直接從上游獲取
    // exist: 只同步已經存在於數據庫的模塊
    // all: 定時同步所有源registry的模塊
    syncModel:'exist'
  1. 數據庫

    我選擇的 mysql ,這裏不介紹怎麼安裝 mysql 了,有需要請 戳這裏 。當然你也可以選擇其他數據庫,目前支持mysql 、 sqlite 、 postgres 、 mariadb ,默認是 sqlite 。

先檢查一下數據庫服務狀態,確保數據庫服務沒毛病:

  • 登錄數據庫

    mysql -u root -p test123456
  • 創建數據庫

    create database npm;

    查看數據庫列表:

  • 創建數據庫表

    cnpmjs.org 項目 docs 目錄下已經給我們備好了創建數據庫的腳本 db.sql 。執行:

    source docs/db.sql;

    默認當前操作路徑就在 cnpmjs.org 項目下,如果不是,請用 db.sql 的絕對路徑。

    查看結果:

上面兩步完成後,就可以將項目跑起來一睹芳容了。因爲我們通過 git 克隆的,所以需要進入到項目目錄下執行啓動服務的命令

npm run start

啓動成功後,訪問 web 頁面,發現之前配置文件中的 web 端口 7002 訪問不了。

這是因爲服務器防火牆的原因,可以選擇關閉防火牆,但是這種方式不推薦;另外一種就是開放指定端口。

iptables -A INPUT -p tcp --drop -j 7002 DROP

如果你是使用的雲服務器,需要去雲服務控制檯,新增安全組,將暴露的端口放開。

端口開放後,訪問 web 頁面:xxx.xxx.xxx.xx:7002,就可以看見熟悉的部署在本地的 cnpm 頁面了。

在上面這張的訪問地址可以看到,用了域名,並不是用的 IP+ 端口的形式,因爲作爲一個企業級的應用,IP+ 端口的方式就如同裸奔一樣,建議採用域名的方式。我在自己的雲服務域名管理下新增了一個子域名。

然後配置 nginx 將 IP 和域名進行綁定,統一使用默認的 80 端口,儘量不要將私有倉庫服務的真實端口和 IP 暴露出來。這裏順便把 nginx 配置也說一下,如果你能接受 IP+ 端口訪問的方式,可以跳過下面這一步。

  1. nginx配置

如果沒有安裝nginx, 戳這裏 。找到 nginx 配置文件,在 conf.d 文件夾信息新增 npm.conf 配置文件,這樣功能清楚明白,因爲很多時候 nginx 不是隻代理這個一個服務。

server{
      listen  80;
       server_name www.mirrors.catfly.vip;
       #charset koi8-r;
       #access_log  logs/host.access.log  main;
       location / { 
            proxy_pass http://127.0.0.1:7002/; #代理到cnpmjs.org提供的web服務
            proxy_set_header        X-Real-IP $remote_addr;
       }
       location /registry/ {
           proxy_pass http://127.0.0.1:7001/; # 代理到cnpmjs.org提供的註冊服務
           proxy_set_header X-Real-IP $remote_addr;
           proxy_set_header Host $host;
           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
       }
       #error_page  404              /404.html;
       # redirect server error pages to the static page /50x.html
       # error_page   500 502 503 504  /50x.html;
       location = /50x.html {
           root   html;
       }
}

重啓 nginx :

service nginx restart

檢查 nginx 狀態,防止配置錯誤,導致重啓失敗。

通過上面的環境搭建和部署,基本工作就完成了,下面開始驗證功能。

功能驗證

在驗證之前推薦安裝一個 npm 源管理模塊 nrm ,有了它我們可以在各種源之間自由切換。

npm i nrm -g

安裝成功後新增我們自己的私有源到nrm源列表中。

nrm add catfly http://www.mirrors.catfly.vip/registry

切換到私有源:

nrm use catfly

這個時候本地執行 npm 操作的時候就會去找到我們自己的私有地址。

  1. 私有包發佈

    註冊用戶:

    npm adduser

登錄私有倉庫:

npm login

登錄成功後,發佈 npm ,在已準備好的模塊目錄執行:

npm publish

這個時候可能會出現各種錯誤,主要是403權限問題,因爲私有倉庫在不同模式下需要滿足不同的條件,例如:

在非私有模式( enablePrivate: false)下,當用戶不用具備管理員權限,模塊命名前綴必須帶有配置中規定的scope ,如果不存在或者 scopes 中不包含該 scope 就會報錯。

在私有模式(enablePrivate: true)下,如果用戶不在配置文件的 admins 中,則不允許執行發佈操作,反過來如果在,那麼他的權限就非常大了,不僅能發佈還能刪除。所以真實場景下不要讓管理員賬號氾濫。

  1. 包下載安裝

    發佈成功後,嘗試安裝發佈的私有包:

    npm i xxxxx

    沒毛病。

  1. Web工作臺

    訪問私有倉庫的web站點

    通過這個 站點可以對私有包的發佈、刪除以及下載進行統計,還可以私有包搜索功能。也可以對這個 web站點進行個性化改造。代碼、數據都在我們這邊,想怎麼造就怎麼造。

私有npm倉庫搭建擴展篇

在真實的企業級應用中,在上面的基礎上還可以進行擴展,下面介紹一下可以擴展的幾個方面:

進程管理

推薦使用 pm2 進行進程管理,雖然項目本身提供了 npm run startnpm run stop 的能力,但是這對於一個企業級的應用來說還是太弱了,使用 pm2 的好處如下:

  1. 隨時隨地多進程管理
  2. 完善的監控機制,我們可以清晰地看見整個集羣的模式、狀態,CPU 利用率甚至是內存大小
  3. 負責均衡
  4. 進程守護
  5. ...
  • 全局安裝pm2
npm i pm2 -g
  • 啓動項目
pm2 start ./dispatch.js //dispatch.js在cnpmjs.org項目的根目錄下

執行完後,可以看見該服務的基本信息,簡潔明瞭。

  • 查看服務進程信息
pm2 monit dispatch //diapatch爲當前進程name

這裏可以實時查看進程運行的詳細信息,方便平時項目的維護。pm2 還有好多強大的功能,這裏就不一一介紹了,有興趣的 戳這裏

私有包存儲上雲

cnpmjs.org 項目配置項裏面有一個 nfs 配置,這裏定義了一個 npm 文件系統(NFS)。私有倉庫在同步和上傳的時候,會交給 NFS 對象相應的函數去處理,NFS 對象返回處理結束之後再返回下載鏈接,所以通過自定義 NFS 模塊可以實現 npm 包的各種定製存儲。目前官方默認使用 fs-cnpm ,該模塊會將上傳或者同步的包保存在服務器本地的 /root/.cnpmjs.org/doenloads/ 目錄下。這種方式比較傳統,一方面隨着私有包數量的不斷增加,存儲資源會是一個瓶頸。另一方面需要定時的備份資源,不然哪天磁盤壞了,那就只有

這個時候將私有包或者同步的資源放到雲上就是一個非常好的方案。cnpmjs.org 官方早就爲我們想到了這點,給出了下面幾種 NFS 模塊:

這些模塊已經能夠滿足我們絕大部分的場景,如果你有特殊的需求,可以參看 nfs模塊規範 進行定製化開發。這裏拿阿里雲 oss 存儲作爲示例。

首先在 cnpmjs.org 項目目錄下安裝 oss-cnpm 模塊

cnpm i oss-cnpm

然後在雲服務控制檯 oss 管理中新增了一個 bucket 來存儲 npm 包,也可以通過上傳路徑區分來複用其他 bucket,畢竟在公司中 bucket 資源一般還是比較緊張的。然後修改項目配置文件,將默認的 fs-cnpm 模塊替換成 oss-cnpm

var oss = require("oss-cnpm");
var nfs = oss.create({
  accessKeyId: 'xxxx',
  accessKeySecret: 'xxx',
  endpoint: 'oss-cn-beijing.aliyuncs.com',
  bucket: 'catfly-xxx',
  mode: 'private',
})
var config = {
  ...,
  nfs:nfs,
  ...
}

重啓項目,這個時候再發布或者同步資源的時候,服務器本地目錄不會有新發布或同步的包了,在 oss 對應的 bucket 裏面能找到剛剛發佈或者同步的資源。

希望這篇文章對你有所幫助。

參考文獻

pm2使用介紹

部署和定製 CNPM——自定義包存儲層

❉ 作者介紹 ❉

相關文章