分分鐘教會你搭建企業級的npm私有倉庫
前言
npm 作爲一種包管理工具,無論你是泛前端還是大前端都已經離不開它。它的出現方便了萬千少年。讓我們跨過了 Ctrl+C、Ctrl+V ,通過 ``npm install x 的方式將別人的優秀代碼模塊引入到自己的項目中。這些優秀的模塊能被共享的原因,一方面是有 npm 這麼一個包管理工具,另外就是 npm 倉庫。
對於 npm 倉庫,如果你還停留在使用 npm 或者 cnpm 這類官方源的情況下。那麼你有必要想想如何搭建一個私有的 npm 倉庫。下面從三個方面講解企業級 npm 私有倉庫搭建那些事兒,分分鐘教會你。
-
爲什麼需要搭建公司的私有 npm 倉庫
-
如何搭建私有倉庫
-
搭建私有倉庫擴展篇
爲什麼需要搭建公司的私有 npm 倉庫
照慣例,先講講爲啥要搭建私有倉庫。目前已經有很多成熟的 npm 源可以使用,比如:
- npm : https://registry.npmjs.org/
- cnpm : http://r.cnpmjs.org/
- taobao: https://registry.npm.taobao.org/
- nj: https://registry.nodejitsu.com/
- npmMirror https://skimdb.npmjs.com/registry/
- ….
在已經有如此多公共倉庫的情況下,是否有必要搞一套私有 倉庫?重複"造輪子"?還是自嗨?如果從下面幾方面來考慮的話,或許能打消心中的疑問。
1. 穩定性
首先是網絡訪問穩定性,私有倉庫因爲是自己公司在維護,有什麼問題能第一時間處理,比如服務宕機…其次資源的穩定性,試想一下,如果哪天你依賴的某個很重要的模塊突然被作者刪了,那是不是完犢子了,畢竟我們很多時候都奉行的是 “拿來主義” ,一旦遇見這種情況,基本上全抓瞎。如果有私有倉庫,上面的問題可以從容面對,有效的保障了業務穩定。
2. 私密性
每個公司都有和自己業務強相關的模塊,或者對某些開源模塊進行個性化的改造,改造後的模塊只滿足本公司的業務場景,這些模塊我們並不希望發佈到公共的倉庫中去,這時就可以發佈到自己的私有倉庫在公司內部共享。
3. 安全性
有了私有倉庫後,可以在 npm 模塊的質量和安全上做文章,能夠有效的防治惡意代碼攻擊。
綜上,搭建自己公司的私有倉庫完全有必要,這並不是秀。當然,如果你所在的公司比較 mini ,對於上面的幾點需求並不是那麼迫切,使用公共倉庫也挺好。但當公司發展到一定規模,在未來可預見的情況下,那就是時候準備搭建自己的私有倉庫了。
如何搭建私有 npm 倉庫
目前已經有許多成熟開源方案,選擇站在巨人的肩膀上不失爲一種良策。這裏選擇 cnpmjs.org 方案,原因有三:
-
目前國內像淘寶這樣的大廠內部也是選擇的它,足以證明它的可靠行和穩定性
-
擴展性強
-
配置多樣化
當然缺點也不是沒有,就是部署有那麼一丟丟複雜。
準備工作
兵馬未動,糧草先行,既然是搭建企業級的應用,基礎環境得備好。
-
Linux 服務器
-
node 環境
-
數據庫( Mysql )
-
nginx
大概就這些,如果你只是熟悉一下,搭着玩兒。也不一定得用 Linux 服務器,Windows 也行,但是如果是作爲線上應用,建議還是使用 Linux 服務器,畢竟 Linux 的穩定性在那裏擺着。
下面的示例是在雲服務器(Ubuntu)上完成的。
安裝
- 安裝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'
-
數據庫
我選擇的 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+ 端口訪問的方式,可以跳過下面這一步。
- 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 操作的時候就會去找到我們自己的私有地址。
-
私有包發佈
註冊用戶:
npm adduser
登錄私有倉庫:
npm login
登錄成功後,發佈 npm ,在已準備好的模塊目錄執行:
npm publish
這個時候可能會出現各種錯誤,主要是403權限問題,因爲私有倉庫在不同模式下需要滿足不同的條件,例如:
在非私有模式( enablePrivate: false)下,當用戶不用具備管理員權限,模塊命名前綴必須帶有配置中規定的scope ,如果不存在或者 scopes 中不包含該 scope 就會報錯。
在私有模式(enablePrivate: true)下,如果用戶不在配置文件的 admins 中,則不允許執行發佈操作,反過來如果在,那麼他的權限就非常大了,不僅能發佈還能刪除。所以真實場景下不要讓管理員賬號氾濫。
-
包下載安裝
發佈成功後,嘗試安裝發佈的私有包:
npm i xxxxx
沒毛病。
-
Web工作臺
訪問私有倉庫的web站點
通過這個 站點可以對私有包的發佈、刪除以及下載進行統計,還可以私有包搜索功能。也可以對這個 web站點進行個性化改造。代碼、數據都在我們這邊,想怎麼造就怎麼造。
私有npm倉庫搭建擴展篇
在真實的企業級應用中,在上面的基礎上還可以進行擴展,下面介紹一下可以擴展的幾個方面:
進程管理
推薦使用 pm2 進行進程管理,雖然項目本身提供了 npm run start
和 npm run stop
的能力,但是這對於一個企業級的應用來說還是太弱了,使用 pm2 的好處如下:
- 隨時隨地多進程管理
- 完善的監控機制,我們可以清晰地看見整個集羣的模式、狀態,CPU 利用率甚至是內存大小
- 負責均衡
- 進程守護
- ...
- 全局安裝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 模塊:
- upyun-cnpm :又拍雲存儲插件
- fs-cnpm :本地存儲的插件
- sfs-client : SFS (Simple FIle Store)存儲插件
- qn-cnpm :七牛雲存儲插件
- oss-cnpm :阿里雲 OSS 存儲插件
這些模塊已經能夠滿足我們絕大部分的場景,如果你有特殊的需求,可以參看 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 裏面能找到剛剛發佈或者同步的資源。
希望這篇文章對你有所幫助。
參考文獻
❉ 作者介紹 ❉