Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 1140a92adc |
@@ -1,29 +0,0 @@
|
|||||||
name: ci
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- main
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
jobs:
|
|
||||||
deploy:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
- name: Configure Git Credentials
|
|
||||||
run: |
|
|
||||||
git config user.name github-actions[bot]
|
|
||||||
git config user.email 41898282+github-actions[bot]@users.noreply.github.com
|
|
||||||
- uses: actions/setup-python@v5
|
|
||||||
with:
|
|
||||||
python-version: 3.x
|
|
||||||
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
|
|
||||||
- uses: actions/cache@v4
|
|
||||||
with:
|
|
||||||
key: mkdocs-material-${{ env.cache_id }}
|
|
||||||
path: .cache
|
|
||||||
restore-keys: |
|
|
||||||
mkdocs-material-
|
|
||||||
- run: pip install -r requirements.txt
|
|
||||||
- run: mkdocs gh-deploy --force
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
.venv
|
|
||||||
docs/copypaste
|
|
||||||
docs/Заметки/vault/root-token.md
|
|
||||||
+1526
File diff suppressed because it is too large
Load Diff
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
+16
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+18
@@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* Lunr languages, `Danish` language
|
||||||
|
* https://github.com/MihaiValentin/lunr-languages
|
||||||
|
*
|
||||||
|
* Copyright 2014, Mihai Valentin
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
* based on
|
||||||
|
* Snowball JavaScript Library v0.3
|
||||||
|
* http://code.google.com/p/urim/
|
||||||
|
* http://snowball.tartarus.org/
|
||||||
|
*
|
||||||
|
* Copyright 2010, Oleg Mazko
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.da=function(){this.pipeline.reset(),this.pipeline.add(e.da.trimmer,e.da.stopWordFilter,e.da.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.da.stemmer))},e.da.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.da.trimmer=e.trimmerSupport.generateTrimmer(e.da.wordCharacters),e.Pipeline.registerFunction(e.da.trimmer,"trimmer-da"),e.da.stemmer=function(){var r=e.stemmerSupport.Among,i=e.stemmerSupport.SnowballProgram,n=new function(){function e(){var e,r=f.cursor+3;if(d=f.limit,0<=r&&r<=f.limit){for(a=r;;){if(e=f.cursor,f.in_grouping(w,97,248)){f.cursor=e;break}if(f.cursor=e,e>=f.limit)return;f.cursor++}for(;!f.out_grouping(w,97,248);){if(f.cursor>=f.limit)return;f.cursor++}d=f.cursor,d<a&&(d=a)}}function n(){var e,r;if(f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(c,32),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del();break;case 2:f.in_grouping_b(p,97,229)&&f.slice_del()}}function t(){var e,r=f.limit-f.cursor;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.find_among_b(l,4)?(f.bra=f.cursor,f.limit_backward=e,f.cursor=f.limit-r,f.cursor>f.limit_backward&&(f.cursor--,f.bra=f.cursor,f.slice_del())):f.limit_backward=e)}function s(){var e,r,i,n=f.limit-f.cursor;if(f.ket=f.cursor,f.eq_s_b(2,"st")&&(f.bra=f.cursor,f.eq_s_b(2,"ig")&&f.slice_del()),f.cursor=f.limit-n,f.cursor>=d&&(r=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,e=f.find_among_b(m,5),f.limit_backward=r,e))switch(f.bra=f.cursor,e){case 1:f.slice_del(),i=f.limit-f.cursor,t(),f.cursor=f.limit-i;break;case 2:f.slice_from("løs")}}function o(){var e;f.cursor>=d&&(e=f.limit_backward,f.limit_backward=d,f.ket=f.cursor,f.out_grouping_b(w,97,248)?(f.bra=f.cursor,u=f.slice_to(u),f.limit_backward=e,f.eq_v_b(u)&&f.slice_del()):f.limit_backward=e)}var a,d,u,c=[new r("hed",-1,1),new r("ethed",0,1),new r("ered",-1,1),new r("e",-1,1),new r("erede",3,1),new r("ende",3,1),new r("erende",5,1),new r("ene",3,1),new r("erne",3,1),new r("ere",3,1),new r("en",-1,1),new r("heden",10,1),new r("eren",10,1),new r("er",-1,1),new r("heder",13,1),new r("erer",13,1),new r("s",-1,2),new r("heds",16,1),new r("es",16,1),new r("endes",18,1),new r("erendes",19,1),new r("enes",18,1),new r("ernes",18,1),new r("eres",18,1),new r("ens",16,1),new r("hedens",24,1),new r("erens",24,1),new r("ers",16,1),new r("ets",16,1),new r("erets",28,1),new r("et",-1,1),new r("eret",30,1)],l=[new r("gd",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("elig",1,1),new r("els",-1,1),new r("løst",-1,2)],w=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],p=[239,254,42,3,0,0,0,0,0,0,0,0,0,0,0,0,16],f=new i;this.setCurrent=function(e){f.setCurrent(e)},this.getCurrent=function(){return f.getCurrent()},this.stem=function(){var r=f.cursor;return e(),f.limit_backward=r,f.cursor=f.limit,n(),f.cursor=f.limit,t(),f.cursor=f.limit,s(),f.cursor=f.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return n.setCurrent(e),n.stem(),n.getCurrent()}):(n.setCurrent(e),n.stem(),n.getCurrent())}}(),e.Pipeline.registerFunction(e.da.stemmer,"stemmer-da"),e.da.stopWordFilter=e.generateStopWordFilter("ad af alle alt anden at blev blive bliver da de dem den denne der deres det dette dig din disse dog du efter eller en end er et for fra ham han hans har havde have hende hendes her hos hun hvad hvis hvor i ikke ind jeg jer jo kunne man mange med meget men mig min mine mit mod ned noget nogle nu når og også om op os over på selv sig sin sine sit skal skulle som sådan thi til ud under var vi vil ville vor være været".split(" ")),e.Pipeline.registerFunction(e.da.stopWordFilter,"stopWordFilter-da")}});
|
||||||
+18
File diff suppressed because one or more lines are too long
+18
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+18
File diff suppressed because one or more lines are too long
+18
File diff suppressed because one or more lines are too long
+18
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hi=function(){this.pipeline.reset(),this.pipeline.add(e.hi.trimmer,e.hi.stopWordFilter,e.hi.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.hi.stemmer))},e.hi.wordCharacters="ऀ-ःऄ-एऐ-टठ-यर-िी-ॏॐ-य़ॠ-९॰-ॿa-zA-Za-zA-Z0-90-9",e.hi.trimmer=e.trimmerSupport.generateTrimmer(e.hi.wordCharacters),e.Pipeline.registerFunction(e.hi.trimmer,"trimmer-hi"),e.hi.stopWordFilter=e.generateStopWordFilter("अत अपना अपनी अपने अभी अंदर आदि आप इत्यादि इन इनका इन्हीं इन्हें इन्हों इस इसका इसकी इसके इसमें इसी इसे उन उनका उनकी उनके उनको उन्हीं उन्हें उन्हों उस उसके उसी उसे एक एवं एस ऐसे और कई कर करता करते करना करने करें कहते कहा का काफ़ी कि कितना किन्हें किन्हों किया किर किस किसी किसे की कुछ कुल के को कोई कौन कौनसा गया घर जब जहाँ जा जितना जिन जिन्हें जिन्हों जिस जिसे जीधर जैसा जैसे जो तक तब तरह तिन तिन्हें तिन्हों तिस तिसे तो था थी थे दबारा दिया दुसरा दूसरे दो द्वारा न नके नहीं ना निहायत नीचे ने पर पहले पूरा पे फिर बनी बही बहुत बाद बाला बिलकुल भी भीतर मगर मानो मे में यदि यह यहाँ यही या यिह ये रखें रहा रहे ऱ्वासा लिए लिये लेकिन व वग़ैरह वर्ग वह वहाँ वहीं वाले वुह वे वो सकता सकते सबसे सभी साथ साबुत साभ सारा से सो संग ही हुआ हुई हुए है हैं हो होता होती होते होना होने".split(" ")),e.hi.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var r=e.wordcut;r.init(),e.hi.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(r){return isLunr2?new e.Token(r.toLowerCase()):r.toLowerCase()});var t=i.toString().toLowerCase().replace(/^\s+/,"");return r.cut(t).split("|")},e.Pipeline.registerFunction(e.hi.stemmer,"stemmer-hi"),e.Pipeline.registerFunction(e.hi.stopWordFilter,"stopWordFilter-hi")}});
|
||||||
+18
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.hy=function(){this.pipeline.reset(),this.pipeline.add(e.hy.trimmer,e.hy.stopWordFilter)},e.hy.wordCharacters="[A-Za-z-֏ff-ﭏ]",e.hy.trimmer=e.trimmerSupport.generateTrimmer(e.hy.wordCharacters),e.Pipeline.registerFunction(e.hy.trimmer,"trimmer-hy"),e.hy.stopWordFilter=e.generateStopWordFilter("դու և եք էիր էիք հետո նաև նրանք որը վրա է որ պիտի են այս մեջ ն իր ու ի այդ որոնք այն կամ էր մի ես համար այլ իսկ էին ենք հետ ին թ էինք մենք նրա նա դուք եմ էի ըստ որպես ում".split(" ")),e.Pipeline.registerFunction(e.hy.stopWordFilter,"stopWordFilter-hy"),e.hy.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}(),e.Pipeline.registerFunction(e.hy.stemmer,"stemmer-hy")}});
|
||||||
+18
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.ja=function(){this.pipeline.reset(),this.pipeline.add(e.ja.trimmer,e.ja.stopWordFilter,e.ja.stemmer),r?this.tokenizer=e.ja.tokenizer:(e.tokenizer&&(e.tokenizer=e.ja.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.ja.tokenizer))};var t=new e.TinySegmenter;e.ja.tokenizer=function(i){var n,o,s,p,a,u,m,l,c,f;if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t.toLowerCase()):t.toLowerCase()});for(o=i.toString().toLowerCase().replace(/^\s+/,""),n=o.length-1;n>=0;n--)if(/\S/.test(o.charAt(n))){o=o.substring(0,n+1);break}for(a=[],s=o.length,c=0,l=0;c<=s;c++)if(u=o.charAt(c),m=c-l,u.match(/\s/)||c==s){if(m>0)for(p=t.segment(o.slice(l,c)).filter(function(e){return!!e}),f=l,n=0;n<p.length;n++)r?a.push(new e.Token(p[n],{position:[f,p[n].length],index:a.length})):a.push(p[n]),f+=p[n].length;l=c+1}return a},e.ja.stemmer=function(){return function(e){return e}}(),e.Pipeline.registerFunction(e.ja.stemmer,"stemmer-ja"),e.ja.wordCharacters="一二三四五六七八九十百千万億兆一-龠々〆ヵヶぁ-んァ-ヴーア-ン゙a-zA-Za-zA-Z0-90-9",e.ja.trimmer=e.trimmerSupport.generateTrimmer(e.ja.wordCharacters),e.Pipeline.registerFunction(e.ja.trimmer,"trimmer-ja"),e.ja.stopWordFilter=e.generateStopWordFilter("これ それ あれ この その あの ここ そこ あそこ こちら どこ だれ なに なん 何 私 貴方 貴方方 我々 私達 あの人 あのかた 彼女 彼 です あります おります います は が の に を で え から まで より も どの と し それで しかし".split(" ")),e.Pipeline.registerFunction(e.ja.stopWordFilter,"stopWordFilter-ja"),e.jp=e.ja,e.Pipeline.registerFunction(e.jp.stemmer,"stemmer-jp"),e.Pipeline.registerFunction(e.jp.trimmer,"trimmer-jp"),e.Pipeline.registerFunction(e.jp.stopWordFilter,"stopWordFilter-jp")}});
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
module.exports=require("./lunr.ja");
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.kn=function(){this.pipeline.reset(),this.pipeline.add(e.kn.trimmer,e.kn.stopWordFilter,e.kn.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.kn.stemmer))},e.kn.wordCharacters="ಀ-಄ಅ-ಔಕ-ಹಾ-ೌ಼-ಽೕ-ೖೝ-ೞೠ-ೡೢ-ೣ೦-೯ೱ-ೳ",e.kn.trimmer=e.trimmerSupport.generateTrimmer(e.kn.wordCharacters),e.Pipeline.registerFunction(e.kn.trimmer,"trimmer-kn"),e.kn.stopWordFilter=e.generateStopWordFilter("ಮತ್ತು ಈ ಒಂದು ರಲ್ಲಿ ಹಾಗೂ ಎಂದು ಅಥವಾ ಇದು ರ ಅವರು ಎಂಬ ಮೇಲೆ ಅವರ ತನ್ನ ಆದರೆ ತಮ್ಮ ನಂತರ ಮೂಲಕ ಹೆಚ್ಚು ನ ಆ ಕೆಲವು ಅನೇಕ ಎರಡು ಹಾಗು ಪ್ರಮುಖ ಇದನ್ನು ಇದರ ಸುಮಾರು ಅದರ ಅದು ಮೊದಲ ಬಗ್ಗೆ ನಲ್ಲಿ ರಂದು ಇತರ ಅತ್ಯಂತ ಹೆಚ್ಚಿನ ಸಹ ಸಾಮಾನ್ಯವಾಗಿ ನೇ ಹಲವಾರು ಹೊಸ ದಿ ಕಡಿಮೆ ಯಾವುದೇ ಹೊಂದಿದೆ ದೊಡ್ಡ ಅನ್ನು ಇವರು ಪ್ರಕಾರ ಇದೆ ಮಾತ್ರ ಕೂಡ ಇಲ್ಲಿ ಎಲ್ಲಾ ವಿವಿಧ ಅದನ್ನು ಹಲವು ರಿಂದ ಕೇವಲ ದ ದಕ್ಷಿಣ ಗೆ ಅವನ ಅತಿ ನೆಯ ಬಹಳ ಕೆಲಸ ಎಲ್ಲ ಪ್ರತಿ ಇತ್ಯಾದಿ ಇವು ಬೇರೆ ಹೀಗೆ ನಡುವೆ ಇದಕ್ಕೆ ಎಸ್ ಇವರ ಮೊದಲು ಶ್ರೀ ಮಾಡುವ ಇದರಲ್ಲಿ ರೀತಿಯ ಮಾಡಿದ ಕಾಲ ಅಲ್ಲಿ ಮಾಡಲು ಅದೇ ಈಗ ಅವು ಗಳು ಎ ಎಂಬುದು ಅವನು ಅಂದರೆ ಅವರಿಗೆ ಇರುವ ವಿಶೇಷ ಮುಂದೆ ಅವುಗಳ ಮುಂತಾದ ಮೂಲ ಬಿ ಮೀ ಒಂದೇ ಇನ್ನೂ ಹೆಚ್ಚಾಗಿ ಮಾಡಿ ಅವರನ್ನು ಇದೇ ಯ ರೀತಿಯಲ್ಲಿ ಜೊತೆ ಅದರಲ್ಲಿ ಮಾಡಿದರು ನಡೆದ ಆಗ ಮತ್ತೆ ಪೂರ್ವ ಆತ ಬಂದ ಯಾವ ಒಟ್ಟು ಇತರೆ ಹಿಂದೆ ಪ್ರಮಾಣದ ಗಳನ್ನು ಕುರಿತು ಯು ಆದ್ದರಿಂದ ಅಲ್ಲದೆ ನಗರದ ಮೇಲಿನ ಏಕೆಂದರೆ ರಷ್ಟು ಎಂಬುದನ್ನು ಬಾರಿ ಎಂದರೆ ಹಿಂದಿನ ಆದರೂ ಆದ ಸಂಬಂಧಿಸಿದ ಮತ್ತೊಂದು ಸಿ ಆತನ ".split(" ")),e.kn.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var r=e.wordcut;r.init(),e.kn.tokenizer=function(t){if(!arguments.length||null==t||void 0==t)return[];if(Array.isArray(t))return t.map(function(r){return isLunr2?new e.Token(r.toLowerCase()):r.toLowerCase()});var n=t.toString().toLowerCase().replace(/^\s+/,"");return r.cut(n).split("|")},e.Pipeline.registerFunction(e.kn.stemmer,"stemmer-kn"),e.Pipeline.registerFunction(e.kn.stopWordFilter,"stopWordFilter-kn")}});
|
||||||
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){e.multiLanguage=function(){for(var t=Array.prototype.slice.call(arguments),i=t.join("-"),r="",n=[],s=[],p=0;p<t.length;++p)"en"==t[p]?(r+="\\w",n.unshift(e.stopWordFilter),n.push(e.stemmer),s.push(e.stemmer)):(r+=e[t[p]].wordCharacters,e[t[p]].stopWordFilter&&n.unshift(e[t[p]].stopWordFilter),e[t[p]].stemmer&&(n.push(e[t[p]].stemmer),s.push(e[t[p]].stemmer)));var o=e.trimmerSupport.generateTrimmer(r);return e.Pipeline.registerFunction(o,"lunr-multi-trimmer-"+i),n.unshift(o),function(){this.pipeline.reset(),this.pipeline.add.apply(this.pipeline,n),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add.apply(this.searchPipeline,s))}}}});
|
||||||
+18
File diff suppressed because one or more lines are too long
+18
@@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* Lunr languages, `Norwegian` language
|
||||||
|
* https://github.com/MihaiValentin/lunr-languages
|
||||||
|
*
|
||||||
|
* Copyright 2014, Mihai Valentin
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
* based on
|
||||||
|
* Snowball JavaScript Library v0.3
|
||||||
|
* http://code.google.com/p/urim/
|
||||||
|
* http://snowball.tartarus.org/
|
||||||
|
*
|
||||||
|
* Copyright 2010, Oleg Mazko
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.no=function(){this.pipeline.reset(),this.pipeline.add(e.no.trimmer,e.no.stopWordFilter,e.no.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.no.stemmer))},e.no.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.no.trimmer=e.trimmerSupport.generateTrimmer(e.no.wordCharacters),e.Pipeline.registerFunction(e.no.trimmer,"trimmer-no"),e.no.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,i=new function(){function e(){var e,r=w.cursor+3;if(a=w.limit,0<=r||r<=w.limit){for(s=r;;){if(e=w.cursor,w.in_grouping(d,97,248)){w.cursor=e;break}if(e>=w.limit)return;w.cursor=e+1}for(;!w.out_grouping(d,97,248);){if(w.cursor>=w.limit)return;w.cursor++}a=w.cursor,a<s&&(a=s)}}function i(){var e,r,n;if(w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(m,29),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:n=w.limit-w.cursor,w.in_grouping_b(c,98,122)?w.slice_del():(w.cursor=w.limit-n,w.eq_s_b(1,"k")&&w.out_grouping_b(d,97,248)&&w.slice_del());break;case 3:w.slice_from("er")}}function t(){var e,r=w.limit-w.cursor;w.cursor>=a&&(e=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,w.find_among_b(u,2)?(w.bra=w.cursor,w.limit_backward=e,w.cursor=w.limit-r,w.cursor>w.limit_backward&&(w.cursor--,w.bra=w.cursor,w.slice_del())):w.limit_backward=e)}function o(){var e,r;w.cursor>=a&&(r=w.limit_backward,w.limit_backward=a,w.ket=w.cursor,e=w.find_among_b(l,11),e?(w.bra=w.cursor,w.limit_backward=r,1==e&&w.slice_del()):w.limit_backward=r)}var s,a,m=[new r("a",-1,1),new r("e",-1,1),new r("ede",1,1),new r("ande",1,1),new r("ende",1,1),new r("ane",1,1),new r("ene",1,1),new r("hetene",6,1),new r("erte",1,3),new r("en",-1,1),new r("heten",9,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",12,1),new r("s",-1,2),new r("as",14,1),new r("es",14,1),new r("edes",16,1),new r("endes",16,1),new r("enes",16,1),new r("hetenes",19,1),new r("ens",14,1),new r("hetens",21,1),new r("ers",14,1),new r("ets",14,1),new r("et",-1,1),new r("het",25,1),new r("ert",-1,3),new r("ast",-1,1)],u=[new r("dt",-1,-1),new r("vt",-1,-1)],l=[new r("leg",-1,1),new r("eleg",0,1),new r("ig",-1,1),new r("eig",2,1),new r("lig",2,1),new r("elig",4,1),new r("els",-1,1),new r("lov",-1,1),new r("elov",7,1),new r("slov",7,1),new r("hetslov",9,1)],d=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,48,0,128],c=[119,125,149,1],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,i(),w.cursor=w.limit,t(),w.cursor=w.limit,o(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return i.setCurrent(e),i.stem(),i.getCurrent()}):(i.setCurrent(e),i.stem(),i.getCurrent())}}(),e.Pipeline.registerFunction(e.no.stemmer,"stemmer-no"),e.no.stopWordFilter=e.generateStopWordFilter("alle at av bare begge ble blei bli blir blitt både båe da de deg dei deim deira deires dem den denne der dere deres det dette di din disse ditt du dykk dykkar då eg ein eit eitt eller elles en enn er et ett etter for fordi fra før ha hadde han hans har hennar henne hennes her hjå ho hoe honom hoss hossen hun hva hvem hver hvilke hvilken hvis hvor hvordan hvorfor i ikke ikkje ikkje ingen ingi inkje inn inni ja jeg kan kom korleis korso kun kunne kva kvar kvarhelst kven kvi kvifor man mange me med medan meg meget mellom men mi min mine mitt mot mykje ned no noe noen noka noko nokon nokor nokre nå når og også om opp oss over på samme seg selv si si sia sidan siden sin sine sitt sjøl skal skulle slik so som som somme somt så sånn til um upp ut uten var vart varte ved vere verte vi vil ville vore vors vort vår være være vært å".split(" ")),e.Pipeline.registerFunction(e.no.stopWordFilter,"stopWordFilter-no")}});
|
||||||
+18
File diff suppressed because one or more lines are too long
+18
File diff suppressed because one or more lines are too long
+18
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sa=function(){this.pipeline.reset(),this.pipeline.add(e.sa.trimmer,e.sa.stopWordFilter,e.sa.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sa.stemmer))},e.sa.wordCharacters="ऀ-ःऄ-एऐ-टठ-यर-िी-ॏॐ-य़ॠ-९॰-ॿ꣠-꣱ꣲ-ꣷ꣸-ꣻ꣼-ꣽꣾ-ꣿᆰ0-ᆰ9",e.sa.trimmer=e.trimmerSupport.generateTrimmer(e.sa.wordCharacters),e.Pipeline.registerFunction(e.sa.trimmer,"trimmer-sa"),e.sa.stopWordFilter=e.generateStopWordFilter('तथा अयम् एकम् इत्यस्मिन् तथा तत् वा अयम् इत्यस्य ते आहूत उपरि तेषाम् किन्तु तेषाम् तदा इत्यनेन अधिकः इत्यस्य तत् केचन बहवः द्वि तथा महत्वपूर्णः अयम् अस्य विषये अयं अस्ति तत् प्रथमः विषये इत्युपरि इत्युपरि इतर अधिकतमः अधिकः अपि सामान्यतया ठ इतरेतर नूतनम् द न्यूनम् कश्चित् वा विशालः द सः अस्ति तदनुसारम् तत्र अस्ति केवलम् अपि अत्र सर्वे विविधाः तत् बहवः यतः इदानीम् द दक्षिण इत्यस्मै तस्य उपरि नथ अतीव कार्यम् सर्वे एकैकम् इत्यादि। एते सन्ति उत इत्थम् मध्ये एतदर्थं . स कस्य प्रथमः श्री. करोति अस्मिन् प्रकारः निर्मिता कालः तत्र कर्तुं समान अधुना ते सन्ति स एकः अस्ति सः अर्थात् तेषां कृते . स्थितम् विशेषः अग्रिम तेषाम् समान स्रोतः ख म समान इदानीमपि अधिकतया करोतु ते समान इत्यस्य वीथी सह यस्मिन् कृतवान् धृतः तदा पुनः पूर्वं सः आगतः किम् कुल इतर पुरा मात्रा स विषये उ अतएव अपि नगरस्य उपरि यतः प्रतिशतं कतरः कालः साधनानि भूत तथापि जात सम्बन्धि अन्यत् ग अतः अस्माकं स्वकीयाः अस्माकं इदानीं अन्तः इत्यादयः भवन्तः इत्यादयः एते एताः तस्य अस्य इदम् एते तेषां तेषां तेषां तान् तेषां तेषां तेषां समानः सः एकः च तादृशाः बहवः अन्ये च वदन्ति यत् कियत् कस्मै कस्मै यस्मै यस्मै यस्मै यस्मै न अतिनीचः किन्तु प्रथमं सम्पूर्णतया ततः चिरकालानन्तरं पुस्तकं सम्पूर्णतया अन्तः किन्तु अत्र वा इह इव श्रद्धाय अवशिष्यते परन्तु अन्ये वर्गाः सन्ति ते सन्ति शक्नुवन्ति सर्वे मिलित्वा सर्वे एकत्र"'.split(" ")),e.sa.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var r=e.wordcut;r.init(),e.sa.tokenizer=function(t){if(!arguments.length||null==t||void 0==t)return[];if(Array.isArray(t))return t.map(function(r){return isLunr2?new e.Token(r.toLowerCase()):r.toLowerCase()});var i=t.toString().toLowerCase().replace(/^\s+/,"");return r.cut(i).split("|")},e.Pipeline.registerFunction(e.sa.stemmer,"stemmer-sa"),e.Pipeline.registerFunction(e.sa.stopWordFilter,"stopWordFilter-sa")}});
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
!function(r,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(r.lunr)}(this,function(){return function(r){r.stemmerSupport={Among:function(r,t,i,s){if(this.toCharArray=function(r){for(var t=r.length,i=new Array(t),s=0;s<t;s++)i[s]=r.charCodeAt(s);return i},!r&&""!=r||!t&&0!=t||!i)throw"Bad Among initialisation: s:"+r+", substring_i: "+t+", result: "+i;this.s_size=r.length,this.s=this.toCharArray(r),this.substring_i=t,this.result=i,this.method=s},SnowballProgram:function(){var r;return{bra:0,ket:0,limit:0,cursor:0,limit_backward:0,setCurrent:function(t){r=t,this.cursor=0,this.limit=t.length,this.limit_backward=0,this.bra=this.cursor,this.ket=this.limit},getCurrent:function(){var t=r;return r=null,t},in_grouping:function(t,i,s){if(this.cursor<this.limit){var e=r.charCodeAt(this.cursor);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},in_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e<=s&&e>=i&&(e-=i,t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},out_grouping:function(t,i,s){if(this.cursor<this.limit){var e=r.charCodeAt(this.cursor);if(e>s||e<i)return this.cursor++,!0;if(e-=i,!(t[e>>3]&1<<(7&e)))return this.cursor++,!0}return!1},out_grouping_b:function(t,i,s){if(this.cursor>this.limit_backward){var e=r.charCodeAt(this.cursor-1);if(e>s||e<i)return this.cursor--,!0;if(e-=i,!(t[e>>3]&1<<(7&e)))return this.cursor--,!0}return!1},eq_s:function(t,i){if(this.limit-this.cursor<t)return!1;for(var s=0;s<t;s++)if(r.charCodeAt(this.cursor+s)!=i.charCodeAt(s))return!1;return this.cursor+=t,!0},eq_s_b:function(t,i){if(this.cursor-this.limit_backward<t)return!1;for(var s=0;s<t;s++)if(r.charCodeAt(this.cursor-t+s)!=i.charCodeAt(s))return!1;return this.cursor-=t,!0},find_among:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o<h?o:h,_=t[a],m=l;m<_.s_size;m++){if(n+l==u){f=-1;break}if(f=r.charCodeAt(n+l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n+_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n+_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},find_among_b:function(t,i){for(var s=0,e=i,n=this.cursor,u=this.limit_backward,o=0,h=0,c=!1;;){for(var a=s+(e-s>>1),f=0,l=o<h?o:h,_=t[a],m=_.s_size-1-l;m>=0;m--){if(n-l==u){f=-1;break}if(f=r.charCodeAt(n-1-l)-_.s[m])break;l++}if(f<0?(e=a,h=l):(s=a,o=l),e-s<=1){if(s>0||e==s||c)break;c=!0}}for(;;){var _=t[s];if(o>=_.s_size){if(this.cursor=n-_.s_size,!_.method)return _.result;var b=_.method();if(this.cursor=n-_.s_size,b)return _.result}if((s=_.substring_i)<0)return 0}},replace_s:function(t,i,s){var e=s.length-(i-t),n=r.substring(0,t),u=r.substring(i);return r=n+s+u,this.limit+=e,this.cursor>=i?this.cursor+=e:this.cursor>t&&(this.cursor=t),e},slice_check:function(){if(this.bra<0||this.bra>this.ket||this.ket>this.limit||this.limit>r.length)throw"faulty slice operation"},slice_from:function(r){this.slice_check(),this.replace_s(this.bra,this.ket,r)},slice_del:function(){this.slice_from("")},insert:function(r,t,i){var s=this.replace_s(r,t,i);r<=this.bra&&(this.bra+=s),r<=this.ket&&(this.ket+=s)},slice_to:function(){return this.slice_check(),r.substring(this.bra,this.ket)},eq_v_b:function(r){return this.eq_s_b(r.length,r)}}}},r.trimmerSupport={generateTrimmer:function(r){var t=new RegExp("^[^"+r+"]+"),i=new RegExp("[^"+r+"]+$");return function(r){return"function"==typeof r.update?r.update(function(r){return r.replace(t,"").replace(i,"")}):r.replace(t,"").replace(i,"")}}}}});
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
/*!
|
||||||
|
* Lunr languages, `Swedish` language
|
||||||
|
* https://github.com/MihaiValentin/lunr-languages
|
||||||
|
*
|
||||||
|
* Copyright 2014, Mihai Valentin
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
/*!
|
||||||
|
* based on
|
||||||
|
* Snowball JavaScript Library v0.3
|
||||||
|
* http://code.google.com/p/urim/
|
||||||
|
* http://snowball.tartarus.org/
|
||||||
|
*
|
||||||
|
* Copyright 2010, Oleg Mazko
|
||||||
|
* http://www.mozilla.org/MPL/
|
||||||
|
*/
|
||||||
|
|
||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.sv=function(){this.pipeline.reset(),this.pipeline.add(e.sv.trimmer,e.sv.stopWordFilter,e.sv.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.sv.stemmer))},e.sv.wordCharacters="A-Za-zªºÀ-ÖØ-öø-ʸˠ-ˤᴀ-ᴥᴬ-ᵜᵢ-ᵥᵫ-ᵷᵹ-ᶾḀ-ỿⁱⁿₐ-ₜKÅℲⅎⅠ-ↈⱠ-ⱿꜢ-ꞇꞋ-ꞭꞰ-ꞷꟷ-ꟿꬰ-ꭚꭜ-ꭤff-stA-Za-z",e.sv.trimmer=e.trimmerSupport.generateTrimmer(e.sv.wordCharacters),e.Pipeline.registerFunction(e.sv.trimmer,"trimmer-sv"),e.sv.stemmer=function(){var r=e.stemmerSupport.Among,n=e.stemmerSupport.SnowballProgram,t=new function(){function e(){var e,r=w.cursor+3;if(o=w.limit,0<=r||r<=w.limit){for(a=r;;){if(e=w.cursor,w.in_grouping(l,97,246)){w.cursor=e;break}if(w.cursor=e,w.cursor>=w.limit)return;w.cursor++}for(;!w.out_grouping(l,97,246);){if(w.cursor>=w.limit)return;w.cursor++}o=w.cursor,o<a&&(o=a)}}function t(){var e,r=w.limit_backward;if(w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(u,37),w.limit_backward=r,e))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.in_grouping_b(d,98,121)&&w.slice_del()}}function i(){var e=w.limit_backward;w.cursor>=o&&(w.limit_backward=o,w.cursor=w.limit,w.find_among_b(c,7)&&(w.cursor=w.limit,w.ket=w.cursor,w.cursor>w.limit_backward&&(w.bra=--w.cursor,w.slice_del())),w.limit_backward=e)}function s(){var e,r;if(w.cursor>=o){if(r=w.limit_backward,w.limit_backward=o,w.cursor=w.limit,w.ket=w.cursor,e=w.find_among_b(m,5))switch(w.bra=w.cursor,e){case 1:w.slice_del();break;case 2:w.slice_from("lös");break;case 3:w.slice_from("full")}w.limit_backward=r}}var a,o,u=[new r("a",-1,1),new r("arna",0,1),new r("erna",0,1),new r("heterna",2,1),new r("orna",0,1),new r("ad",-1,1),new r("e",-1,1),new r("ade",6,1),new r("ande",6,1),new r("arne",6,1),new r("are",6,1),new r("aste",6,1),new r("en",-1,1),new r("anden",12,1),new r("aren",12,1),new r("heten",12,1),new r("ern",-1,1),new r("ar",-1,1),new r("er",-1,1),new r("heter",18,1),new r("or",-1,1),new r("s",-1,2),new r("as",21,1),new r("arnas",22,1),new r("ernas",22,1),new r("ornas",22,1),new r("es",21,1),new r("ades",26,1),new r("andes",26,1),new r("ens",21,1),new r("arens",29,1),new r("hetens",29,1),new r("erns",21,1),new r("at",-1,1),new r("andet",-1,1),new r("het",-1,1),new r("ast",-1,1)],c=[new r("dd",-1,-1),new r("gd",-1,-1),new r("nn",-1,-1),new r("dt",-1,-1),new r("gt",-1,-1),new r("kt",-1,-1),new r("tt",-1,-1)],m=[new r("ig",-1,1),new r("lig",0,1),new r("els",-1,1),new r("fullt",-1,3),new r("löst",-1,2)],l=[17,65,16,1,0,0,0,0,0,0,0,0,0,0,0,0,24,0,32],d=[119,127,149],w=new n;this.setCurrent=function(e){w.setCurrent(e)},this.getCurrent=function(){return w.getCurrent()},this.stem=function(){var r=w.cursor;return e(),w.limit_backward=r,w.cursor=w.limit,t(),w.cursor=w.limit,i(),w.cursor=w.limit,s(),!0}};return function(e){return"function"==typeof e.update?e.update(function(e){return t.setCurrent(e),t.stem(),t.getCurrent()}):(t.setCurrent(e),t.stem(),t.getCurrent())}}(),e.Pipeline.registerFunction(e.sv.stemmer,"stemmer-sv"),e.sv.stopWordFilter=e.generateStopWordFilter("alla allt att av blev bli blir blivit de dem den denna deras dess dessa det detta dig din dina ditt du där då efter ej eller en er era ert ett från för ha hade han hans har henne hennes hon honom hur här i icke ingen inom inte jag ju kan kunde man med mellan men mig min mina mitt mot mycket ni nu när någon något några och om oss på samma sedan sig sin sina sitta själv skulle som så sådan sådana sådant till under upp ut utan vad var vara varför varit varje vars vart vem vi vid vilka vilkas vilken vilket vår våra vårt än är åt över".split(" ")),e.Pipeline.registerFunction(e.sv.stopWordFilter,"stopWordFilter-sv")}});
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.ta=function(){this.pipeline.reset(),this.pipeline.add(e.ta.trimmer,e.ta.stopWordFilter,e.ta.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.ta.stemmer))},e.ta.wordCharacters="-உஊ-ஏஐ-ஙச-ட-னப-யர-ஹ-ிீ-ொ-ௐ---௩௪-௯௰-௹௺-a-zA-Za-zA-Z0-90-9",e.ta.trimmer=e.trimmerSupport.generateTrimmer(e.ta.wordCharacters),e.Pipeline.registerFunction(e.ta.trimmer,"trimmer-ta"),e.ta.stopWordFilter=e.generateStopWordFilter("அங்கு அங்கே அது அதை அந்த அவர் அவர்கள் அவள் அவன் அவை ஆக ஆகவே ஆகையால் ஆதலால் ஆதலினால் ஆனாலும் ஆனால் இங்கு இங்கே இது இதை இந்த இப்படி இவர் இவர்கள் இவள் இவன் இவை இவ்வளவு உனக்கு உனது உன் உன்னால் எங்கு எங்கே எது எதை எந்த எப்படி எவர் எவர்கள் எவள் எவன் எவை எவ்வளவு எனக்கு எனது எனவே என் என்ன என்னால் ஏது ஏன் தனது தன்னால் தானே தான் நாங்கள் நாம் நான் நீ நீங்கள்".split(" ")),e.ta.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var t=e.wordcut;t.init(),e.ta.tokenizer=function(r){if(!arguments.length||null==r||void 0==r)return[];if(Array.isArray(r))return r.map(function(t){return isLunr2?new e.Token(t.toLowerCase()):t.toLowerCase()});var i=r.toString().toLowerCase().replace(/^\s+/,"");return t.cut(i).split("|")},e.Pipeline.registerFunction(e.ta.stemmer,"stemmer-ta"),e.Pipeline.registerFunction(e.ta.stopWordFilter,"stopWordFilter-ta")}});
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,t){"function"==typeof define&&define.amd?define(t):"object"==typeof exports?module.exports=t():t()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.te=function(){this.pipeline.reset(),this.pipeline.add(e.te.trimmer,e.te.stopWordFilter,e.te.stemmer),this.searchPipeline&&(this.searchPipeline.reset(),this.searchPipeline.add(e.te.stemmer))},e.te.wordCharacters="ఀ-ఄఅ-ఔక-హా-ౌౕ-ౖౘ-ౚౠ-ౡౢ-ౣ౦-౯౸-౿఼ఽ్ౝ౷",e.te.trimmer=e.trimmerSupport.generateTrimmer(e.te.wordCharacters),e.Pipeline.registerFunction(e.te.trimmer,"trimmer-te"),e.te.stopWordFilter=e.generateStopWordFilter("అందరూ అందుబాటులో అడగండి అడగడం అడ్డంగా అనుగుణంగా అనుమతించు అనుమతిస్తుంది అయితే ఇప్పటికే ఉన్నారు ఎక్కడైనా ఎప్పుడు ఎవరైనా ఎవరో ఏ ఏదైనా ఏమైనప్పటికి ఒక ఒకరు కనిపిస్తాయి కాదు కూడా గా గురించి చుట్టూ చేయగలిగింది తగిన తర్వాత దాదాపు దూరంగా నిజంగా పై ప్రకారం ప్రక్కన మధ్య మరియు మరొక మళ్ళీ మాత్రమే మెచ్చుకో వద్ద వెంట వేరుగా వ్యతిరేకంగా సంబంధం".split(" ")),e.te.stemmer=function(){return function(e){return"function"==typeof e.update?e.update(function(e){return e}):e}}();var t=e.wordcut;t.init(),e.te.tokenizer=function(r){if(!arguments.length||null==r||void 0==r)return[];if(Array.isArray(r))return r.map(function(t){return isLunr2?new e.Token(t.toLowerCase()):t.toLowerCase()});var i=r.toString().toLowerCase().replace(/^\s+/,"");return t.cut(i).split("|")},e.Pipeline.registerFunction(e.te.stemmer,"stemmer-te"),e.Pipeline.registerFunction(e.te.stopWordFilter,"stopWordFilter-te")}});
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var r="2"==e.version[0];e.th=function(){this.pipeline.reset(),this.pipeline.add(e.th.trimmer),r?this.tokenizer=e.th.tokenizer:(e.tokenizer&&(e.tokenizer=e.th.tokenizer),this.tokenizerFn&&(this.tokenizerFn=e.th.tokenizer))},e.th.wordCharacters="[-]",e.th.trimmer=e.trimmerSupport.generateTrimmer(e.th.wordCharacters),e.Pipeline.registerFunction(e.th.trimmer,"trimmer-th");var t=e.wordcut;t.init(),e.th.tokenizer=function(i){if(!arguments.length||null==i||void 0==i)return[];if(Array.isArray(i))return i.map(function(t){return r?new e.Token(t):t});var n=i.toString().replace(/^\s+/,"");return t.cut(n).split("|")}}});
|
||||||
+18
File diff suppressed because one or more lines are too long
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r():r()(e.lunr)}(this,function(){return function(e){if(void 0===e)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===e.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");e.vi=function(){this.pipeline.reset(),this.pipeline.add(e.vi.stopWordFilter,e.vi.trimmer)},e.vi.wordCharacters="[A-Za-ẓ̀͐́͑̉̃̓ÂâÊêÔôĂ-ăĐ-đƠ-ơƯ-ư]",e.vi.trimmer=e.trimmerSupport.generateTrimmer(e.vi.wordCharacters),e.Pipeline.registerFunction(e.vi.trimmer,"trimmer-vi"),e.vi.stopWordFilter=e.generateStopWordFilter("là cái nhưng mà".split(" "))}});
|
||||||
+1
@@ -0,0 +1 @@
|
|||||||
|
!function(e,r){"function"==typeof define&&define.amd?define(r):"object"==typeof exports?module.exports=r(require("@node-rs/jieba")):r()(e.lunr)}(this,function(e){return function(r,t){if(void 0===r)throw new Error("Lunr is not present. Please include / require Lunr before this script.");if(void 0===r.stemmerSupport)throw new Error("Lunr stemmer support is not present. Please include / require Lunr stemmer support before this script.");var i="2"==r.version[0];r.zh=function(){this.pipeline.reset(),this.pipeline.add(r.zh.trimmer,r.zh.stopWordFilter,r.zh.stemmer),i?this.tokenizer=r.zh.tokenizer:(r.tokenizer&&(r.tokenizer=r.zh.tokenizer),this.tokenizerFn&&(this.tokenizerFn=r.zh.tokenizer))},r.zh.tokenizer=function(n){if(!arguments.length||null==n||void 0==n)return[];if(Array.isArray(n))return n.map(function(e){return i?new r.Token(e.toLowerCase()):e.toLowerCase()});t&&e.load(t);var o=n.toString().trim().toLowerCase(),s=[];e.cut(o,!0).forEach(function(e){s=s.concat(e.split(" "))}),s=s.filter(function(e){return!!e});var u=0;return s.map(function(e,t){if(i){var n=o.indexOf(e,u),s={};return s.position=[n,e.length],s.index=t,u=n,new r.Token(e,s)}return e})},r.zh.wordCharacters="\\w一-龥",r.zh.trimmer=r.trimmerSupport.generateTrimmer(r.zh.wordCharacters),r.Pipeline.registerFunction(r.zh.trimmer,"trimmer-zh"),r.zh.stemmer=function(){return function(e){return e}}(),r.Pipeline.registerFunction(r.zh.stemmer,"stemmer-zh"),r.zh.stopWordFilter=r.generateStopWordFilter("的 一 不 在 人 有 是 为 為 以 于 於 上 他 而 后 後 之 来 來 及 了 因 下 可 到 由 这 這 与 與 也 此 但 并 並 个 個 其 已 无 無 小 我 们 們 起 最 再 今 去 好 只 又 或 很 亦 某 把 那 你 乃 它 吧 被 比 别 趁 当 當 从 從 得 打 凡 儿 兒 尔 爾 该 該 各 给 給 跟 和 何 还 還 即 几 幾 既 看 据 據 距 靠 啦 另 么 麽 每 嘛 拿 哪 您 凭 憑 且 却 卻 让 讓 仍 啥 如 若 使 谁 誰 虽 雖 随 隨 同 所 她 哇 嗡 往 些 向 沿 哟 喲 用 咱 则 則 怎 曾 至 致 着 著 诸 諸 自".split(" ")),r.Pipeline.registerFunction(r.zh.stopWordFilter,"stopWordFilter-zh")}});
|
||||||
@@ -0,0 +1,206 @@
|
|||||||
|
/**
|
||||||
|
* export the module via AMD, CommonJS or as a browser global
|
||||||
|
* Export code from https://github.com/umdjs/umd/blob/master/returnExports.js
|
||||||
|
*/
|
||||||
|
;(function (root, factory) {
|
||||||
|
if (typeof define === 'function' && define.amd) {
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define(factory)
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
/**
|
||||||
|
* Node. Does not work with strict CommonJS, but
|
||||||
|
* only CommonJS-like environments that support module.exports,
|
||||||
|
* like Node.
|
||||||
|
*/
|
||||||
|
module.exports = factory()
|
||||||
|
} else {
|
||||||
|
// Browser globals (root is window)
|
||||||
|
factory()(root.lunr);
|
||||||
|
}
|
||||||
|
}(this, function () {
|
||||||
|
/**
|
||||||
|
* Just return a value to define the module export.
|
||||||
|
* This example returns an object, but the module
|
||||||
|
* can return a function as the exported value.
|
||||||
|
*/
|
||||||
|
|
||||||
|
return function(lunr) {
|
||||||
|
// TinySegmenter 0.1 -- Super compact Japanese tokenizer in Javascript
|
||||||
|
// (c) 2008 Taku Kudo <taku@chasen.org>
|
||||||
|
// TinySegmenter is freely distributable under the terms of a new BSD licence.
|
||||||
|
// For details, see http://chasen.org/~taku/software/TinySegmenter/LICENCE.txt
|
||||||
|
|
||||||
|
function TinySegmenter() {
|
||||||
|
var patterns = {
|
||||||
|
"[一二三四五六七八九十百千万億兆]":"M",
|
||||||
|
"[一-龠々〆ヵヶ]":"H",
|
||||||
|
"[ぁ-ん]":"I",
|
||||||
|
"[ァ-ヴーア-ン゙ー]":"K",
|
||||||
|
"[a-zA-Za-zA-Z]":"A",
|
||||||
|
"[0-90-9]":"N"
|
||||||
|
}
|
||||||
|
this.chartype_ = [];
|
||||||
|
for (var i in patterns) {
|
||||||
|
var regexp = new RegExp(i);
|
||||||
|
this.chartype_.push([regexp, patterns[i]]);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.BIAS__ = -332
|
||||||
|
this.BC1__ = {"HH":6,"II":2461,"KH":406,"OH":-1378};
|
||||||
|
this.BC2__ = {"AA":-3267,"AI":2744,"AN":-878,"HH":-4070,"HM":-1711,"HN":4012,"HO":3761,"IA":1327,"IH":-1184,"II":-1332,"IK":1721,"IO":5492,"KI":3831,"KK":-8741,"MH":-3132,"MK":3334,"OO":-2920};
|
||||||
|
this.BC3__ = {"HH":996,"HI":626,"HK":-721,"HN":-1307,"HO":-836,"IH":-301,"KK":2762,"MK":1079,"MM":4034,"OA":-1652,"OH":266};
|
||||||
|
this.BP1__ = {"BB":295,"OB":304,"OO":-125,"UB":352};
|
||||||
|
this.BP2__ = {"BO":60,"OO":-1762};
|
||||||
|
this.BQ1__ = {"BHH":1150,"BHM":1521,"BII":-1158,"BIM":886,"BMH":1208,"BNH":449,"BOH":-91,"BOO":-2597,"OHI":451,"OIH":-296,"OKA":1851,"OKH":-1020,"OKK":904,"OOO":2965};
|
||||||
|
this.BQ2__ = {"BHH":118,"BHI":-1159,"BHM":466,"BIH":-919,"BKK":-1720,"BKO":864,"OHH":-1139,"OHM":-181,"OIH":153,"UHI":-1146};
|
||||||
|
this.BQ3__ = {"BHH":-792,"BHI":2664,"BII":-299,"BKI":419,"BMH":937,"BMM":8335,"BNN":998,"BOH":775,"OHH":2174,"OHM":439,"OII":280,"OKH":1798,"OKI":-793,"OKO":-2242,"OMH":-2402,"OOO":11699};
|
||||||
|
this.BQ4__ = {"BHH":-3895,"BIH":3761,"BII":-4654,"BIK":1348,"BKK":-1806,"BMI":-3385,"BOO":-12396,"OAH":926,"OHH":266,"OHK":-2036,"ONN":-973};
|
||||||
|
this.BW1__ = {",と":660,",同":727,"B1あ":1404,"B1同":542,"、と":660,"、同":727,"」と":1682,"あっ":1505,"いう":1743,"いっ":-2055,"いる":672,"うし":-4817,"うん":665,"から":3472,"がら":600,"こう":-790,"こと":2083,"こん":-1262,"さら":-4143,"さん":4573,"した":2641,"して":1104,"すで":-3399,"そこ":1977,"それ":-871,"たち":1122,"ため":601,"った":3463,"つい":-802,"てい":805,"てき":1249,"でき":1127,"です":3445,"では":844,"とい":-4915,"とみ":1922,"どこ":3887,"ない":5713,"なっ":3015,"など":7379,"なん":-1113,"にし":2468,"には":1498,"にも":1671,"に対":-912,"の一":-501,"の中":741,"ませ":2448,"まで":1711,"まま":2600,"まる":-2155,"やむ":-1947,"よっ":-2565,"れた":2369,"れで":-913,"をし":1860,"を見":731,"亡く":-1886,"京都":2558,"取り":-2784,"大き":-2604,"大阪":1497,"平方":-2314,"引き":-1336,"日本":-195,"本当":-2423,"毎日":-2113,"目指":-724,"B1あ":1404,"B1同":542,"」と":1682};
|
||||||
|
this.BW2__ = {"..":-11822,"11":-669,"――":-5730,"−−":-13175,"いう":-1609,"うか":2490,"かし":-1350,"かも":-602,"から":-7194,"かれ":4612,"がい":853,"がら":-3198,"きた":1941,"くな":-1597,"こと":-8392,"この":-4193,"させ":4533,"され":13168,"さん":-3977,"しい":-1819,"しか":-545,"した":5078,"して":972,"しな":939,"その":-3744,"たい":-1253,"たた":-662,"ただ":-3857,"たち":-786,"たと":1224,"たは":-939,"った":4589,"って":1647,"っと":-2094,"てい":6144,"てき":3640,"てく":2551,"ては":-3110,"ても":-3065,"でい":2666,"でき":-1528,"でし":-3828,"です":-4761,"でも":-4203,"とい":1890,"とこ":-1746,"とと":-2279,"との":720,"とみ":5168,"とも":-3941,"ない":-2488,"なが":-1313,"など":-6509,"なの":2614,"なん":3099,"にお":-1615,"にし":2748,"にな":2454,"によ":-7236,"に対":-14943,"に従":-4688,"に関":-11388,"のか":2093,"ので":-7059,"のに":-6041,"のの":-6125,"はい":1073,"はが":-1033,"はず":-2532,"ばれ":1813,"まし":-1316,"まで":-6621,"まれ":5409,"めて":-3153,"もい":2230,"もの":-10713,"らか":-944,"らし":-1611,"らに":-1897,"りし":651,"りま":1620,"れた":4270,"れて":849,"れば":4114,"ろう":6067,"われ":7901,"を通":-11877,"んだ":728,"んな":-4115,"一人":602,"一方":-1375,"一日":970,"一部":-1051,"上が":-4479,"会社":-1116,"出て":2163,"分の":-7758,"同党":970,"同日":-913,"大阪":-2471,"委員":-1250,"少な":-1050,"年度":-8669,"年間":-1626,"府県":-2363,"手権":-1982,"新聞":-4066,"日新":-722,"日本":-7068,"日米":3372,"曜日":-601,"朝鮮":-2355,"本人":-2697,"東京":-1543,"然と":-1384,"社会":-1276,"立て":-990,"第に":-1612,"米国":-4268,"11":-669};
|
||||||
|
this.BW3__ = {"あた":-2194,"あり":719,"ある":3846,"い.":-1185,"い。":-1185,"いい":5308,"いえ":2079,"いく":3029,"いた":2056,"いっ":1883,"いる":5600,"いわ":1527,"うち":1117,"うと":4798,"えと":1454,"か.":2857,"か。":2857,"かけ":-743,"かっ":-4098,"かに":-669,"から":6520,"かり":-2670,"が,":1816,"が、":1816,"がき":-4855,"がけ":-1127,"がっ":-913,"がら":-4977,"がり":-2064,"きた":1645,"けど":1374,"こと":7397,"この":1542,"ころ":-2757,"さい":-714,"さを":976,"し,":1557,"し、":1557,"しい":-3714,"した":3562,"して":1449,"しな":2608,"しま":1200,"す.":-1310,"す。":-1310,"する":6521,"ず,":3426,"ず、":3426,"ずに":841,"そう":428,"た.":8875,"た。":8875,"たい":-594,"たの":812,"たり":-1183,"たる":-853,"だ.":4098,"だ。":4098,"だっ":1004,"った":-4748,"って":300,"てい":6240,"てお":855,"ても":302,"です":1437,"でに":-1482,"では":2295,"とう":-1387,"とし":2266,"との":541,"とも":-3543,"どう":4664,"ない":1796,"なく":-903,"など":2135,"に,":-1021,"に、":-1021,"にし":1771,"にな":1906,"には":2644,"の,":-724,"の、":-724,"の子":-1000,"は,":1337,"は、":1337,"べき":2181,"まし":1113,"ます":6943,"まっ":-1549,"まで":6154,"まれ":-793,"らし":1479,"られ":6820,"るる":3818,"れ,":854,"れ、":854,"れた":1850,"れて":1375,"れば":-3246,"れる":1091,"われ":-605,"んだ":606,"んで":798,"カ月":990,"会議":860,"入り":1232,"大会":2217,"始め":1681,"市":965,"新聞":-5055,"日,":974,"日、":974,"社会":2024,"カ月":990};
|
||||||
|
this.TC1__ = {"AAA":1093,"HHH":1029,"HHM":580,"HII":998,"HOH":-390,"HOM":-331,"IHI":1169,"IOH":-142,"IOI":-1015,"IOM":467,"MMH":187,"OOI":-1832};
|
||||||
|
this.TC2__ = {"HHO":2088,"HII":-1023,"HMM":-1154,"IHI":-1965,"KKH":703,"OII":-2649};
|
||||||
|
this.TC3__ = {"AAA":-294,"HHH":346,"HHI":-341,"HII":-1088,"HIK":731,"HOH":-1486,"IHH":128,"IHI":-3041,"IHO":-1935,"IIH":-825,"IIM":-1035,"IOI":-542,"KHH":-1216,"KKA":491,"KKH":-1217,"KOK":-1009,"MHH":-2694,"MHM":-457,"MHO":123,"MMH":-471,"NNH":-1689,"NNO":662,"OHO":-3393};
|
||||||
|
this.TC4__ = {"HHH":-203,"HHI":1344,"HHK":365,"HHM":-122,"HHN":182,"HHO":669,"HIH":804,"HII":679,"HOH":446,"IHH":695,"IHO":-2324,"IIH":321,"III":1497,"IIO":656,"IOO":54,"KAK":4845,"KKA":3386,"KKK":3065,"MHH":-405,"MHI":201,"MMH":-241,"MMM":661,"MOM":841};
|
||||||
|
this.TQ1__ = {"BHHH":-227,"BHHI":316,"BHIH":-132,"BIHH":60,"BIII":1595,"BNHH":-744,"BOHH":225,"BOOO":-908,"OAKK":482,"OHHH":281,"OHIH":249,"OIHI":200,"OIIH":-68};
|
||||||
|
this.TQ2__ = {"BIHH":-1401,"BIII":-1033,"BKAK":-543,"BOOO":-5591};
|
||||||
|
this.TQ3__ = {"BHHH":478,"BHHM":-1073,"BHIH":222,"BHII":-504,"BIIH":-116,"BIII":-105,"BMHI":-863,"BMHM":-464,"BOMH":620,"OHHH":346,"OHHI":1729,"OHII":997,"OHMH":481,"OIHH":623,"OIIH":1344,"OKAK":2792,"OKHH":587,"OKKA":679,"OOHH":110,"OOII":-685};
|
||||||
|
this.TQ4__ = {"BHHH":-721,"BHHM":-3604,"BHII":-966,"BIIH":-607,"BIII":-2181,"OAAA":-2763,"OAKK":180,"OHHH":-294,"OHHI":2446,"OHHO":480,"OHIH":-1573,"OIHH":1935,"OIHI":-493,"OIIH":626,"OIII":-4007,"OKAK":-8156};
|
||||||
|
this.TW1__ = {"につい":-4681,"東京都":2026};
|
||||||
|
this.TW2__ = {"ある程":-2049,"いった":-1256,"ころが":-2434,"しょう":3873,"その後":-4430,"だって":-1049,"ていた":1833,"として":-4657,"ともに":-4517,"もので":1882,"一気に":-792,"初めて":-1512,"同時に":-8097,"大きな":-1255,"対して":-2721,"社会党":-3216};
|
||||||
|
this.TW3__ = {"いただ":-1734,"してい":1314,"として":-4314,"につい":-5483,"にとっ":-5989,"に当た":-6247,"ので,":-727,"ので、":-727,"のもの":-600,"れから":-3752,"十二月":-2287};
|
||||||
|
this.TW4__ = {"いう.":8576,"いう。":8576,"からな":-2348,"してい":2958,"たが,":1516,"たが、":1516,"ている":1538,"という":1349,"ました":5543,"ません":1097,"ようと":-4258,"よると":5865};
|
||||||
|
this.UC1__ = {"A":484,"K":93,"M":645,"O":-505};
|
||||||
|
this.UC2__ = {"A":819,"H":1059,"I":409,"M":3987,"N":5775,"O":646};
|
||||||
|
this.UC3__ = {"A":-1370,"I":2311};
|
||||||
|
this.UC4__ = {"A":-2643,"H":1809,"I":-1032,"K":-3450,"M":3565,"N":3876,"O":6646};
|
||||||
|
this.UC5__ = {"H":313,"I":-1238,"K":-799,"M":539,"O":-831};
|
||||||
|
this.UC6__ = {"H":-506,"I":-253,"K":87,"M":247,"O":-387};
|
||||||
|
this.UP1__ = {"O":-214};
|
||||||
|
this.UP2__ = {"B":69,"O":935};
|
||||||
|
this.UP3__ = {"B":189};
|
||||||
|
this.UQ1__ = {"BH":21,"BI":-12,"BK":-99,"BN":142,"BO":-56,"OH":-95,"OI":477,"OK":410,"OO":-2422};
|
||||||
|
this.UQ2__ = {"BH":216,"BI":113,"OK":1759};
|
||||||
|
this.UQ3__ = {"BA":-479,"BH":42,"BI":1913,"BK":-7198,"BM":3160,"BN":6427,"BO":14761,"OI":-827,"ON":-3212};
|
||||||
|
this.UW1__ = {",":156,"、":156,"「":-463,"あ":-941,"う":-127,"が":-553,"き":121,"こ":505,"で":-201,"と":-547,"ど":-123,"に":-789,"の":-185,"は":-847,"も":-466,"や":-470,"よ":182,"ら":-292,"り":208,"れ":169,"を":-446,"ん":-137,"・":-135,"主":-402,"京":-268,"区":-912,"午":871,"国":-460,"大":561,"委":729,"市":-411,"日":-141,"理":361,"生":-408,"県":-386,"都":-718,"「":-463,"・":-135};
|
||||||
|
this.UW2__ = {",":-829,"、":-829,"〇":892,"「":-645,"」":3145,"あ":-538,"い":505,"う":134,"お":-502,"か":1454,"が":-856,"く":-412,"こ":1141,"さ":878,"ざ":540,"し":1529,"す":-675,"せ":300,"そ":-1011,"た":188,"だ":1837,"つ":-949,"て":-291,"で":-268,"と":-981,"ど":1273,"な":1063,"に":-1764,"の":130,"は":-409,"ひ":-1273,"べ":1261,"ま":600,"も":-1263,"や":-402,"よ":1639,"り":-579,"る":-694,"れ":571,"を":-2516,"ん":2095,"ア":-587,"カ":306,"キ":568,"ッ":831,"三":-758,"不":-2150,"世":-302,"中":-968,"主":-861,"事":492,"人":-123,"会":978,"保":362,"入":548,"初":-3025,"副":-1566,"北":-3414,"区":-422,"大":-1769,"天":-865,"太":-483,"子":-1519,"学":760,"実":1023,"小":-2009,"市":-813,"年":-1060,"強":1067,"手":-1519,"揺":-1033,"政":1522,"文":-1355,"新":-1682,"日":-1815,"明":-1462,"最":-630,"朝":-1843,"本":-1650,"東":-931,"果":-665,"次":-2378,"民":-180,"気":-1740,"理":752,"発":529,"目":-1584,"相":-242,"県":-1165,"立":-763,"第":810,"米":509,"自":-1353,"行":838,"西":-744,"見":-3874,"調":1010,"議":1198,"込":3041,"開":1758,"間":-1257,"「":-645,"」":3145,"ッ":831,"ア":-587,"カ":306,"キ":568};
|
||||||
|
this.UW3__ = {",":4889,"1":-800,"−":-1723,"、":4889,"々":-2311,"〇":5827,"」":2670,"〓":-3573,"あ":-2696,"い":1006,"う":2342,"え":1983,"お":-4864,"か":-1163,"が":3271,"く":1004,"け":388,"げ":401,"こ":-3552,"ご":-3116,"さ":-1058,"し":-395,"す":584,"せ":3685,"そ":-5228,"た":842,"ち":-521,"っ":-1444,"つ":-1081,"て":6167,"で":2318,"と":1691,"ど":-899,"な":-2788,"に":2745,"の":4056,"は":4555,"ひ":-2171,"ふ":-1798,"へ":1199,"ほ":-5516,"ま":-4384,"み":-120,"め":1205,"も":2323,"や":-788,"よ":-202,"ら":727,"り":649,"る":5905,"れ":2773,"わ":-1207,"を":6620,"ん":-518,"ア":551,"グ":1319,"ス":874,"ッ":-1350,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278,"・":-3794,"一":-1619,"下":-1759,"世":-2087,"両":3815,"中":653,"主":-758,"予":-1193,"二":974,"人":2742,"今":792,"他":1889,"以":-1368,"低":811,"何":4265,"作":-361,"保":-2439,"元":4858,"党":3593,"全":1574,"公":-3030,"六":755,"共":-1880,"円":5807,"再":3095,"分":457,"初":2475,"別":1129,"前":2286,"副":4437,"力":365,"動":-949,"務":-1872,"化":1327,"北":-1038,"区":4646,"千":-2309,"午":-783,"協":-1006,"口":483,"右":1233,"各":3588,"合":-241,"同":3906,"和":-837,"員":4513,"国":642,"型":1389,"場":1219,"外":-241,"妻":2016,"学":-1356,"安":-423,"実":-1008,"家":1078,"小":-513,"少":-3102,"州":1155,"市":3197,"平":-1804,"年":2416,"広":-1030,"府":1605,"度":1452,"建":-2352,"当":-3885,"得":1905,"思":-1291,"性":1822,"戸":-488,"指":-3973,"政":-2013,"教":-1479,"数":3222,"文":-1489,"新":1764,"日":2099,"旧":5792,"昨":-661,"時":-1248,"曜":-951,"最":-937,"月":4125,"期":360,"李":3094,"村":364,"東":-805,"核":5156,"森":2438,"業":484,"氏":2613,"民":-1694,"決":-1073,"法":1868,"海":-495,"無":979,"物":461,"特":-3850,"生":-273,"用":914,"町":1215,"的":7313,"直":-1835,"省":792,"県":6293,"知":-1528,"私":4231,"税":401,"立":-960,"第":1201,"米":7767,"系":3066,"約":3663,"級":1384,"統":-4229,"総":1163,"線":1255,"者":6457,"能":725,"自":-2869,"英":785,"見":1044,"調":-562,"財":-733,"費":1777,"車":1835,"軍":1375,"込":-1504,"通":-1136,"選":-681,"郎":1026,"郡":4404,"部":1200,"金":2163,"長":421,"開":-1432,"間":1302,"関":-1282,"雨":2009,"電":-1045,"非":2066,"駅":1620,"1":-800,"」":2670,"・":-3794,"ッ":-1350,"ア":551,"グ":1319,"ス":874,"ト":521,"ム":1109,"ル":1591,"ロ":2201,"ン":278};
|
||||||
|
this.UW4__ = {",":3930,".":3508,"―":-4841,"、":3930,"。":3508,"〇":4999,"「":1895,"」":3798,"〓":-5156,"あ":4752,"い":-3435,"う":-640,"え":-2514,"お":2405,"か":530,"が":6006,"き":-4482,"ぎ":-3821,"く":-3788,"け":-4376,"げ":-4734,"こ":2255,"ご":1979,"さ":2864,"し":-843,"じ":-2506,"す":-731,"ず":1251,"せ":181,"そ":4091,"た":5034,"だ":5408,"ち":-3654,"っ":-5882,"つ":-1659,"て":3994,"で":7410,"と":4547,"な":5433,"に":6499,"ぬ":1853,"ね":1413,"の":7396,"は":8578,"ば":1940,"ひ":4249,"び":-4134,"ふ":1345,"へ":6665,"べ":-744,"ほ":1464,"ま":1051,"み":-2082,"む":-882,"め":-5046,"も":4169,"ゃ":-2666,"や":2795,"ょ":-1544,"よ":3351,"ら":-2922,"り":-9726,"る":-14896,"れ":-2613,"ろ":-4570,"わ":-1783,"を":13150,"ん":-2352,"カ":2145,"コ":1789,"セ":1287,"ッ":-724,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637,"・":-4371,"ー":-11870,"一":-2069,"中":2210,"予":782,"事":-190,"井":-1768,"人":1036,"以":544,"会":950,"体":-1286,"作":530,"側":4292,"先":601,"党":-2006,"共":-1212,"内":584,"円":788,"初":1347,"前":1623,"副":3879,"力":-302,"動":-740,"務":-2715,"化":776,"区":4517,"協":1013,"参":1555,"合":-1834,"和":-681,"員":-910,"器":-851,"回":1500,"国":-619,"園":-1200,"地":866,"場":-1410,"塁":-2094,"士":-1413,"多":1067,"大":571,"子":-4802,"学":-1397,"定":-1057,"寺":-809,"小":1910,"屋":-1328,"山":-1500,"島":-2056,"川":-2667,"市":2771,"年":374,"庁":-4556,"後":456,"性":553,"感":916,"所":-1566,"支":856,"改":787,"政":2182,"教":704,"文":522,"方":-856,"日":1798,"時":1829,"最":845,"月":-9066,"木":-485,"来":-442,"校":-360,"業":-1043,"氏":5388,"民":-2716,"気":-910,"沢":-939,"済":-543,"物":-735,"率":672,"球":-1267,"生":-1286,"産":-1101,"田":-2900,"町":1826,"的":2586,"目":922,"省":-3485,"県":2997,"空":-867,"立":-2112,"第":788,"米":2937,"系":786,"約":2171,"経":1146,"統":-1169,"総":940,"線":-994,"署":749,"者":2145,"能":-730,"般":-852,"行":-792,"規":792,"警":-1184,"議":-244,"谷":-1000,"賞":730,"車":-1481,"軍":1158,"輪":-1433,"込":-3370,"近":929,"道":-1291,"選":2596,"郎":-4866,"都":1192,"野":-1100,"銀":-2213,"長":357,"間":-2344,"院":-2297,"際":-2604,"電":-878,"領":-1659,"題":-792,"館":-1984,"首":1749,"高":2120,"「":1895,"」":3798,"・":-4371,"ッ":-724,"ー":-11870,"カ":2145,"コ":1789,"セ":1287,"ト":-403,"メ":-1635,"ラ":-881,"リ":-541,"ル":-856,"ン":-3637};
|
||||||
|
this.UW5__ = {",":465,".":-299,"1":-514,"E2":-32768,"]":-2762,"、":465,"。":-299,"「":363,"あ":1655,"い":331,"う":-503,"え":1199,"お":527,"か":647,"が":-421,"き":1624,"ぎ":1971,"く":312,"げ":-983,"さ":-1537,"し":-1371,"す":-852,"だ":-1186,"ち":1093,"っ":52,"つ":921,"て":-18,"で":-850,"と":-127,"ど":1682,"な":-787,"に":-1224,"の":-635,"は":-578,"べ":1001,"み":502,"め":865,"ゃ":3350,"ょ":854,"り":-208,"る":429,"れ":504,"わ":419,"を":-1264,"ん":327,"イ":241,"ル":451,"ン":-343,"中":-871,"京":722,"会":-1153,"党":-654,"務":3519,"区":-901,"告":848,"員":2104,"大":-1296,"学":-548,"定":1785,"嵐":-1304,"市":-2991,"席":921,"年":1763,"思":872,"所":-814,"挙":1618,"新":-1682,"日":218,"月":-4353,"査":932,"格":1356,"機":-1508,"氏":-1347,"田":240,"町":-3912,"的":-3149,"相":1319,"省":-1052,"県":-4003,"研":-997,"社":-278,"空":-813,"統":1955,"者":-2233,"表":663,"語":-1073,"議":1219,"選":-1018,"郎":-368,"長":786,"間":1191,"題":2368,"館":-689,"1":-514,"E2":-32768,"「":363,"イ":241,"ル":451,"ン":-343};
|
||||||
|
this.UW6__ = {",":227,".":808,"1":-270,"E1":306,"、":227,"。":808,"あ":-307,"う":189,"か":241,"が":-73,"く":-121,"こ":-200,"じ":1782,"す":383,"た":-428,"っ":573,"て":-1014,"で":101,"と":-105,"な":-253,"に":-149,"の":-417,"は":-236,"も":-206,"り":187,"る":-135,"を":195,"ル":-673,"ン":-496,"一":-277,"中":201,"件":-800,"会":624,"前":302,"区":1792,"員":-1212,"委":798,"学":-960,"市":887,"広":-695,"後":535,"業":-697,"相":753,"社":-507,"福":974,"空":-822,"者":1811,"連":463,"郎":1082,"1":-270,"E1":306,"ル":-673,"ン":-496};
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
TinySegmenter.prototype.ctype_ = function(str) {
|
||||||
|
for (var i in this.chartype_) {
|
||||||
|
if (str.match(this.chartype_[i][0])) {
|
||||||
|
return this.chartype_[i][1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "O";
|
||||||
|
}
|
||||||
|
|
||||||
|
TinySegmenter.prototype.ts_ = function(v) {
|
||||||
|
if (v) { return v; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
TinySegmenter.prototype.segment = function(input) {
|
||||||
|
if (input == null || input == undefined || input == "") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
var result = [];
|
||||||
|
var seg = ["B3","B2","B1"];
|
||||||
|
var ctype = ["O","O","O"];
|
||||||
|
var o = input.split("");
|
||||||
|
for (i = 0; i < o.length; ++i) {
|
||||||
|
seg.push(o[i]);
|
||||||
|
ctype.push(this.ctype_(o[i]))
|
||||||
|
}
|
||||||
|
seg.push("E1");
|
||||||
|
seg.push("E2");
|
||||||
|
seg.push("E3");
|
||||||
|
ctype.push("O");
|
||||||
|
ctype.push("O");
|
||||||
|
ctype.push("O");
|
||||||
|
var word = seg[3];
|
||||||
|
var p1 = "U";
|
||||||
|
var p2 = "U";
|
||||||
|
var p3 = "U";
|
||||||
|
for (var i = 4; i < seg.length - 3; ++i) {
|
||||||
|
var score = this.BIAS__;
|
||||||
|
var w1 = seg[i-3];
|
||||||
|
var w2 = seg[i-2];
|
||||||
|
var w3 = seg[i-1];
|
||||||
|
var w4 = seg[i];
|
||||||
|
var w5 = seg[i+1];
|
||||||
|
var w6 = seg[i+2];
|
||||||
|
var c1 = ctype[i-3];
|
||||||
|
var c2 = ctype[i-2];
|
||||||
|
var c3 = ctype[i-1];
|
||||||
|
var c4 = ctype[i];
|
||||||
|
var c5 = ctype[i+1];
|
||||||
|
var c6 = ctype[i+2];
|
||||||
|
score += this.ts_(this.UP1__[p1]);
|
||||||
|
score += this.ts_(this.UP2__[p2]);
|
||||||
|
score += this.ts_(this.UP3__[p3]);
|
||||||
|
score += this.ts_(this.BP1__[p1 + p2]);
|
||||||
|
score += this.ts_(this.BP2__[p2 + p3]);
|
||||||
|
score += this.ts_(this.UW1__[w1]);
|
||||||
|
score += this.ts_(this.UW2__[w2]);
|
||||||
|
score += this.ts_(this.UW3__[w3]);
|
||||||
|
score += this.ts_(this.UW4__[w4]);
|
||||||
|
score += this.ts_(this.UW5__[w5]);
|
||||||
|
score += this.ts_(this.UW6__[w6]);
|
||||||
|
score += this.ts_(this.BW1__[w2 + w3]);
|
||||||
|
score += this.ts_(this.BW2__[w3 + w4]);
|
||||||
|
score += this.ts_(this.BW3__[w4 + w5]);
|
||||||
|
score += this.ts_(this.TW1__[w1 + w2 + w3]);
|
||||||
|
score += this.ts_(this.TW2__[w2 + w3 + w4]);
|
||||||
|
score += this.ts_(this.TW3__[w3 + w4 + w5]);
|
||||||
|
score += this.ts_(this.TW4__[w4 + w5 + w6]);
|
||||||
|
score += this.ts_(this.UC1__[c1]);
|
||||||
|
score += this.ts_(this.UC2__[c2]);
|
||||||
|
score += this.ts_(this.UC3__[c3]);
|
||||||
|
score += this.ts_(this.UC4__[c4]);
|
||||||
|
score += this.ts_(this.UC5__[c5]);
|
||||||
|
score += this.ts_(this.UC6__[c6]);
|
||||||
|
score += this.ts_(this.BC1__[c2 + c3]);
|
||||||
|
score += this.ts_(this.BC2__[c3 + c4]);
|
||||||
|
score += this.ts_(this.BC3__[c4 + c5]);
|
||||||
|
score += this.ts_(this.TC1__[c1 + c2 + c3]);
|
||||||
|
score += this.ts_(this.TC2__[c2 + c3 + c4]);
|
||||||
|
score += this.ts_(this.TC3__[c3 + c4 + c5]);
|
||||||
|
score += this.ts_(this.TC4__[c4 + c5 + c6]);
|
||||||
|
// score += this.ts_(this.TC5__[c4 + c5 + c6]);
|
||||||
|
score += this.ts_(this.UQ1__[p1 + c1]);
|
||||||
|
score += this.ts_(this.UQ2__[p2 + c2]);
|
||||||
|
score += this.ts_(this.UQ3__[p3 + c3]);
|
||||||
|
score += this.ts_(this.BQ1__[p2 + c2 + c3]);
|
||||||
|
score += this.ts_(this.BQ2__[p2 + c3 + c4]);
|
||||||
|
score += this.ts_(this.BQ3__[p3 + c2 + c3]);
|
||||||
|
score += this.ts_(this.BQ4__[p3 + c3 + c4]);
|
||||||
|
score += this.ts_(this.TQ1__[p2 + c1 + c2 + c3]);
|
||||||
|
score += this.ts_(this.TQ2__[p2 + c2 + c3 + c4]);
|
||||||
|
score += this.ts_(this.TQ3__[p3 + c1 + c2 + c3]);
|
||||||
|
score += this.ts_(this.TQ4__[p3 + c2 + c3 + c4]);
|
||||||
|
var p = "O";
|
||||||
|
if (score > 0) {
|
||||||
|
result.push(word);
|
||||||
|
word = "";
|
||||||
|
p = "B";
|
||||||
|
}
|
||||||
|
p1 = p2;
|
||||||
|
p2 = p3;
|
||||||
|
p3 = p;
|
||||||
|
word += seg[i];
|
||||||
|
}
|
||||||
|
result.push(word);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
lunr.TinySegmenter = TinySegmenter;
|
||||||
|
};
|
||||||
|
|
||||||
|
}));
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
{"version":3,"sources":["src/templates/assets/stylesheets/palette/_scheme.scss","../../../../src/templates/assets/stylesheets/palette.scss","src/templates/assets/stylesheets/palette/_accent.scss","src/templates/assets/stylesheets/palette/_primary.scss","src/templates/assets/stylesheets/utilities/_break.scss"],"names":[],"mappings":"AA2BA,cAGE,6BAME,sDAAA,CACA,6DAAA,CACA,+DAAA,CACA,gEAAA,CACA,mDAAA,CACA,6DAAA,CACA,+DAAA,CACA,gEAAA,CAGA,mDAAA,CACA,gDAAA,CACA,yDAAA,CACA,4DAAA,CAGA,0BAAA,CACA,mCAAA,CAGA,iCAAA,CACA,kCAAA,CACA,mCAAA,CACA,mCAAA,CACA,kCAAA,CACA,iCAAA,CACA,+CAAA,CACA,6DAAA,CACA,gEAAA,CACA,4DAAA,CACA,4DAAA,CACA,6DAAA,CAGA,6CAAA,CAGA,+CAAA,CAGA,uDAAA,CACA,6DAAA,CACA,2DAAA,CAGA,iCAAA,CAGA,yDAAA,CACA,iEAAA,CAGA,mDAAA,CACA,mDAAA,CAGA,qDAAA,CACA,uDAAA,CAGA,8DAAA,CAKA,8DAAA,CAKA,0DAAA,CAzEA,iBCiBF,CD6DE,kHAEE,YC3DJ,CDkFE,yDACE,4BChFJ,CD+EE,2DACE,4BC7EJ,CD4EE,gEACE,4BC1EJ,CDyEE,2DACE,4BCvEJ,CDsEE,yDACE,4BCpEJ,CDmEE,0DACE,4BCjEJ,CDgEE,gEACE,4BC9DJ,CD6DE,0DACE,4BC3DJ,CD0DE,2OACE,4BC/CJ,CDsDA,+FAGE,iCCpDF,CACF,CCjDE,2BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD6CN,CCvDE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDoDN,CC9DE,8BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD2DN,CCrEE,mCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDkEN,CC5EE,8BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDyEN,CCnFE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDgFN,CC1FE,kCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDuFN,CCjGE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD8FN,CCxGE,4BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDqGN,CC/GE,6BACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCD4GN,CCtHE,mCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDmHN,CC7HE,4BACE,4BAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCD6HN,CCpIE,8BACE,4BAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCDoIN,CC3IE,6BACE,yBAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCD2IN,CClJE,8BACE,4BAAA,CACA,2CAAA,CAIE,8BAAA,CACA,qCDkJN,CCzJE,mCACE,4BAAA,CACA,2CAAA,CAOE,yBAAA,CACA,qCDsJN,CE3JE,4BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwJN,CEnKE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgKN,CE3KE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwKN,CEnLE,oCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgLN,CE3LE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwLN,CEnME,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgMN,CE3ME,mCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwMN,CEnNE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgNN,CE3NE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwNN,CEnOE,8BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgON,CE3OE,oCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwON,CEnPE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCFmPN,CE3PE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCF2PN,CEnQE,8BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCFmQN,CE3QE,+BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAIE,+BAAA,CACA,sCF2QN,CEnRE,oCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFgRN,CE3RE,8BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCFwRN,CEnSE,6BACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCAAA,CAKA,4BF4RN,CE5SE,kCACE,6BAAA,CACA,oCAAA,CACA,mCAAA,CAOE,0BAAA,CACA,sCAAA,CAKA,4BFqSN,CEtRE,sEACE,4BFyRJ,CE1RE,+DACE,4BF6RJ,CE9RE,iEACE,4BFiSJ,CElSE,gEACE,4BFqSJ,CEtSE,iEACE,4BFySJ,CEhSA,8BACE,mDAAA,CACA,4DAAA,CACA,0DAAA,CACA,oDAAA,CACA,2DAAA,CAGA,4BFiSF,CE9RE,yCACE,+BFgSJ,CE7RI,kDAEE,0CAAA,CACA,sCAAA,CAFA,mCFiSN,CG7MI,mCD1EA,+CACE,8CF0RJ,CEvRI,qDACE,8CFyRN,CEpRE,iEACE,mCFsRJ,CACF,CGxNI,sCDvDA,uCACE,oCFkRJ,CACF,CEzQA,8BACE,kDAAA,CACA,4DAAA,CACA,wDAAA,CACA,oDAAA,CACA,6DAAA,CAGA,4BF0QF,CEvQE,yCACE,+BFyQJ,CEtQI,kDAEE,0CAAA,CACA,sCAAA,CAFA,mCF0QN,CEnQE,yCACE,6CFqQJ,CG9NI,0CDhCA,8CACE,gDFiQJ,CACF,CGnOI,0CDvBA,iFACE,6CF6PJ,CACF,CG3PI,sCDKA,uCACE,6CFyPJ,CACF","file":"palette.css"}
|
||||||
-19
@@ -1,19 +0,0 @@
|
|||||||
FROM docker.io/library/python:alpine
|
|
||||||
WORKDIR /home/ubadmin/mkdocks
|
|
||||||
COPY ./requirements.txt .
|
|
||||||
RUN apk add --update --no-cache --virtual .build-deps gcc musl-dev &&\
|
|
||||||
apk add --no-cache git ca-certificates curl &&\
|
|
||||||
pip install --no-cache-dir --requirement ./requirements.txt &&\
|
|
||||||
apk del .build-deps &&\
|
|
||||||
rm ./requirements.txt
|
|
||||||
WORKDIR /d
|
|
||||||
CMD ["mkdocs", "serve", "-a", "0.0.0.0:8000"]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# Планы для Sailor.lab
|
|
||||||
~~1) Развернуть vault~~
|
|
||||||
2) Выбрать систему мониторинга
|
|
||||||
3) Настроить бекапы
|
|
||||||
4) Установить ubuntu на старый комп
|
|
||||||
5) Подумать над переделкой шаблона as a service?
|
|
||||||
6) Добавить диск и маунт диска для логов
|
|
||||||
7) Настроить Vault
|
|
||||||
8) Сделать дашборд для веб панелей
|
|
||||||
9) Развернуть opensearch
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Стартовая страница
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
Ты devops инженер. Твоя основная специальность это автоматизация и настройка серверов.
|
|
||||||
Ты занят настройкой opensearch.
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
Инвентарь для плейбуков бывает 3-х видов
|
|
||||||
1. INI
|
|
||||||
2. YAML
|
|
||||||
3. Динамический
|
|
||||||
Я использую ini формат как самый простой. Динамический интвентарь мы пока не научились формировать
|
|
||||||
Примерный вид инвентаря
|
|
||||||
|
|
||||||
[localhost]
|
|
||||||
localhost
|
|
||||||
[web]
|
|
||||||
web1
|
|
||||||
web2
|
|
||||||
[web:vars]
|
|
||||||
ansible_user=root
|
|
||||||
|
|
||||||
|
|
||||||
В инвентаре также указываем переменные относящиеся к данному хосту, по типу юзера которым подключаемся, где лежит ключ, какой порт используем и т.д. Для подробных сведений надо смотерть документацию
|
|
||||||
https://docs.ansible.com/ansible/latest/inventory_guide/intro_inventory.html
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
Файл конфигурации с именем ansible.cfg лежит в директории пользования и задает параметры ансибла для пользователя. Для подробных разъяснений требуется смотреть документацию.
|
|
||||||
```
|
|
||||||
[defaults]
|
|
||||||
inventory = /home/ubadmin/ansible/inventory.ini
|
|
||||||
ansible_ssh_private_key_file = /home/ubadmin/.ssh/id_ed25519
|
|
||||||
ansible_user = ubadmin
|
|
||||||
roles_path = /home/ubadmin/ansible/roles
|
|
||||||
allow_world_readable_tmpfiles = false
|
|
||||||
remote_tmp = /tmp/${USER}/ansible
|
|
||||||
host_key_checking = False
|
|
||||||
timeout = 60
|
|
||||||
deprecation_warnings = False
|
|
||||||
display_skipped_hosts = False
|
|
||||||
[ssh_connection]
|
|
||||||
pipelining = True
|
|
||||||
ansible_connection=paramiko
|
|
||||||
[persistent_connection]
|
|
||||||
retries = 3
|
|
||||||
connect_timeout = 60
|
|
||||||
command_timeout = 30
|
|
||||||
```
|
|
||||||
|
|
||||||
https://docs.ansible.com/ansible/latest/reference_appendices/config.html
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# Заметки по докеру
|
|
||||||
1) Для того что бы compose перечитал файл конфигурации нужно сделать стоп, а потом старт. Рестар не перечитывает конфигурацию
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
# Альтернативный registry
|
|
||||||
Есть 2 ситуации когда нужен альтернативный реджистри докера:
|
|
||||||
1) требуется качать свое приложение из своего регистри
|
|
||||||
2) Докерхаб не доступен\ограничено кол-во скачиваний\санкции
|
|
||||||
Для решение этих проблем мы можем в конфиг файле докера указать альтернативный регистри. Данная инструкция для обычной установки докера. Если докер рутлесс, то команды будут иными
|
|
||||||
- Открываем или создаем, если нет, файл по пути
|
|
||||||
```
|
|
||||||
nano /etc/docker/daemon.json
|
|
||||||
```
|
|
||||||
- Редактируем или вставляем в свежий файл конфиг. В данном случае используется зеркало таймвеба. Ниже будет список альтернативныйх зеркал
|
|
||||||
```
|
|
||||||
{
|
|
||||||
"registry-mirrors" : [ "https://dockerhub.timeweb.cloud" ]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
- Перезапускаем демон докера
|
|
||||||
```
|
|
||||||
systemctl reload docker
|
|
||||||
```
|
|
||||||
# Список альтернативных зеркал
|
|
||||||
Адрес реестра Компания-владелец
|
|
||||||
https://mirror.gcr.io Google
|
|
||||||
https://public.ecr.aws Amazon
|
|
||||||
https://dockerhub.timeweb.cloud Timeweb Cloud
|
|
||||||
https://dh-mirror.gitverse.ru GitVerse (СберБанк)
|
|
||||||
https://dockerhub1.beget.com Beget
|
|
||||||
https://quay.io Red Hat
|
|
||||||
https://registry.access.redhat.com Red Hat
|
|
||||||
https://registry.redhat.io Red Hat
|
|
||||||
@@ -1,142 +0,0 @@
|
|||||||
# Нструкция по установке Gitea
|
|
||||||
Примитивная инструкция по установке gitea.
|
|
||||||
Ссылка на официальную документацию https://docs.gitea.com/next/installation/install-from-binary
|
|
||||||
Требования и версии
|
|
||||||
Ubuntu 22.04
|
|
||||||
postgresql 17.9
|
|
||||||
gitea main-nightly ( 1.27.0)
|
|
||||||
git => 2.0
|
|
||||||
## Подготовка базы
|
|
||||||
Установим и настроем базу данных postgresql
|
|
||||||
1) Добавим репозиторий и ключ
|
|
||||||
```
|
|
||||||
sudo sh -c 'echo "deb http://apt.postgresql.org/pub/repos/apt $(lsb_release -cs)-pgdg main" > /etc/apt/sources.list.d/pgdg.list'
|
|
||||||
curl -fsSL https://www.postgresql.org/media/keys/ACCC4CF8.asc | sudo gpg --dearmor -o /etc/apt/trusted.gpg.d/postgresql.gpg
|
|
||||||
sudo apt update
|
|
||||||
|
|
||||||
```
|
|
||||||
2) Установим postgresql, запустим и добавим в автозагрузку
|
|
||||||
```
|
|
||||||
sudo apt install postgresql-17
|
|
||||||
sudo systemctl start postgresql
|
|
||||||
sudo systemctl enable postgresql
|
|
||||||
|
|
||||||
```
|
|
||||||
3) Настроим файл postgesql.conf для подключения. Файл лежит /etc/postgresql/17/main/postgresql.conf (17 это версия postgesql)
|
|
||||||
Если сторка закоментирована, то раскоментировать и добавить адрес сервера на котором будет развернута gitea
|
|
||||||
```
|
|
||||||
listen_addresses = 'localhost, 203.0.113.3'
|
|
||||||
```
|
|
||||||
4) Создадим юзера для БД. Для создания нового юзера надо подключится к Субд под дефолтным юзером postges. После ROLE идет имя юзера. Требуется заменить дефолтные на свои.
|
|
||||||
```
|
|
||||||
su -c "psql" - postgres
|
|
||||||
CREATE ROLE gitea WITH LOGIN PASSWORD 'gitea';
|
|
||||||
```
|
|
||||||
5) Создадим БД для gitea. Владельцем базы назначим свежесозданного юзера. Кодировка базы UTF8. Сменить имя базы на свою
|
|
||||||
```
|
|
||||||
CREATE DATABASE giteadb WITH OWNER gitea TEMPLATE template0 ENCODING UTF8 LC_COLLATE 'en_US.UTF-8' LC_CTYPE 'en_US.UTF-8';
|
|
||||||
```
|
|
||||||
1) Разрешим подключаться к базе пользователю к созданной выше базе данных, добавив следующее правило аутентификации в pg_hba.conf. Файл лежит /etc/postgresql/17/main/
|
|
||||||
```
|
|
||||||
local giteadb gitea scram-sha-256 #Для локальной базы данных
|
|
||||||
host giteadb gitea 192.0.2.10/32 scram-sha-256 #Для удаленной базы данных
|
|
||||||
```
|
|
||||||
1) На сервере Gitea проверим подключение к базе данных.
|
|
||||||
|
|
||||||
Для локальной базы данных:
|
|
||||||
```
|
|
||||||
psql -U gitea -d giteadb
|
|
||||||
```
|
|
||||||
Для удаленной базы данных:
|
|
||||||
```
|
|
||||||
psql "postgres://gitea@203.0.113.3/giteadb"
|
|
||||||
```
|
|
||||||
## Установка
|
|
||||||
1) Обновим систему
|
|
||||||
```
|
|
||||||
sudo apt update && sudo apt upgrade -y
|
|
||||||
```
|
|
||||||
2) Скачаем бинарник и выдадим ему права на исполнение.
|
|
||||||
```
|
|
||||||
wget -O gitea https://dl.gitea.com/gitea/main-nightly/gitea-main-nightly-linux-amd64
|
|
||||||
|
|
||||||
```
|
|
||||||
```
|
|
||||||
sudo chmod gitea
|
|
||||||
```
|
|
||||||
3) Проверим подпись бинарника. В репозитории есть GPG ключ.
|
|
||||||
```
|
|
||||||
gpg --keyserver hkps://keys.openpgp.org --recv 7C9E68152594688862D62AF62D9AE806EC1592E2
|
|
||||||
wget https://dl.gitea.com/gitea/main-nightly/gitea-main-nightly-linux-386.asc
|
|
||||||
gpg --verify gitea-main-nightly-linux-amd64.asc gitea-main-nightly-linux-amd64
|
|
||||||
```
|
|
||||||
4) НА сервере должен быть установлен git версии => 2.0. Убедимся что git подходящей версии
|
|
||||||
```
|
|
||||||
git --version
|
|
||||||
```
|
|
||||||
5) Добавим локального юзера для запуска gitea.
|
|
||||||
```
|
|
||||||
adduser \
|
|
||||||
--system \
|
|
||||||
--shell /bin/false \
|
|
||||||
--gecos 'Git Version Control' \
|
|
||||||
--group \
|
|
||||||
--disabled-password \
|
|
||||||
--home /home/git \
|
|
||||||
gitea
|
|
||||||
```
|
|
||||||
1) Создадим необходимые для работы директории и выдадим созданному юзеру права на них. Права 770 на директорию /etc/gitea выдаются только для первоначальной настройки.
|
|
||||||
```
|
|
||||||
mkdir -p /mnt/data/gitea/{custom,data,log}
|
|
||||||
chown -R gitea:gitea /mnt/data/gitea/
|
|
||||||
chmod -R 750 /mnt/data/gitea/
|
|
||||||
mkdir /etc/gitea
|
|
||||||
chown root:gitea /etc/gitea
|
|
||||||
chmod 770 /etc/gitea
|
|
||||||
```
|
|
||||||
1) Создадим системный юнит для запуска gitea как сервиса systemd. Создадим файл юнита по пути /etc/systemd/system/gitea.service
|
|
||||||
Добавим конфиг юнита
|
|
||||||
```
|
|
||||||
[Unit]
|
|
||||||
Description=Gitea (Git with a cup of tea)
|
|
||||||
After=network.target
|
|
||||||
Wants=postgresql.service
|
|
||||||
After=postgresql.service
|
|
||||||
[Service]
|
|
||||||
# Uncomment the next line if you have repos with lots of files and get a HTTP 500 error because of that
|
|
||||||
# LimitNOFILE=524288:524288
|
|
||||||
RestartSec=2s
|
|
||||||
Type=simple
|
|
||||||
User=git
|
|
||||||
Group=git
|
|
||||||
WorkingDirectory=/var/lib/gitea/
|
|
||||||
ExecStart=/usr/local/bin/gitea web --config /etc/gitea/app.ini
|
|
||||||
Restart=always
|
|
||||||
Environment=USER=git HOME=/home/git GITEA_WORK_DIR=/var/lib/gitea
|
|
||||||
[Install]
|
|
||||||
WantedBy=multi-user.target
|
|
||||||
```
|
|
||||||
Перечитаем список юнитов
|
|
||||||
```
|
|
||||||
sudo systemctl daemon-reload
|
|
||||||
```
|
|
||||||
Запустим gitea
|
|
||||||
```
|
|
||||||
sudo systemctl enable gitea --now
|
|
||||||
```
|
|
||||||
## Первоначальная настройка
|
|
||||||
1) Для первоначальной настройки нужно зайти по адресу нашего сервера на порте 3000
|
|
||||||
http://ip-addr-or-fqdn:3000/
|
|
||||||
Откроется веб странциа первоначальной настройки, где нужно:
|
|
||||||
1) указать юзера, пароль и БД, которую мы будем использовать.
|
|
||||||
2) Указать домен и URl сервера
|
|
||||||
3) Зарегестрировать администратора.
|
|
||||||
Если забудем логи\пароль админа, то его можно сменить через консоль сервера
|
|
||||||
```
|
|
||||||
gitea admin user change-password --username user --password password --config /etc/gitea/app.ini:
|
|
||||||
```
|
|
||||||
4) Так же стоит запретить самостоятельную регистрацию на сервер
|
|
||||||
После первоначальной настройки меняем права на директорию /etc/gitea на 750
|
|
||||||
```
|
|
||||||
chmod 750 /etc/gitea
|
|
||||||
```
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
# Gitea Runner
|
|
||||||
# Включение Actions
|
|
||||||
Для установки и добавления раннера нужны включенные действия. Если они не включены по умолчанию, то включаем их в конфиге самой gitea
|
|
||||||
```
|
|
||||||
nano /etc/gitea/app.ini
|
|
||||||
|
|
||||||
[actions] ENABLED=true
|
|
||||||
```
|
|
||||||
# Где взять токен
|
|
||||||
Нужно зайти в репозиторий -> Настройки -> Действия -> Раннеры -> Создать новый раннер
|
|
||||||
## Установка docker
|
|
||||||
Раннер запускается в контейнере и запускает задачи тоже в контейнере, так что нам нужен докер.
|
|
||||||
|
|
||||||
```
|
|
||||||
curl -fsSL https://get.docker.com | sh -
|
|
||||||
```
|
|
||||||
В нужно папке создадим папку для раннеров и перейдем в нее. Если нужнго будет запускать несколько раннеров, то мы просто создадим папку со следующим номером.
|
|
||||||
```
|
|
||||||
mkdir -p gitea_runners/runner1
|
|
||||||
mkdir runner-1 && cd runner-1
|
|
||||||
```
|
|
||||||
Создадим конфиг ранера. Описание всех настроке конфига в оф документации https://gitea.com/gitea/runner/src/branch/main/internal/pkg/config/config.example.yaml
|
|
||||||
```
|
|
||||||
nano config.yaml
|
|
||||||
```
|
|
||||||
Вернемся в общую для ранеров директорию и создадим там compose файл для запуска. В этом файле мы можем описывать сразу все раннеры
|
|
||||||
```
|
|
||||||
cd ..
|
|
||||||
nano docker-compose.yaml
|
|
||||||
```
|
|
||||||
После всех настроек запускаем и ждем лога о том, что раннер зарегистрирован
|
|
||||||
```
|
|
||||||
docker compose up -d
|
|
||||||
docker logs gitea_runners-runner-1-1 -f
|
|
||||||
```
|
|
||||||
## Тест
|
|
||||||
Для теста создадим в репозитории директорию и конфиг для работы с раннером
|
|
||||||
```
|
|
||||||
mkdir -p ./.gitea/workflow/test.yaml
|
|
||||||
```
|
|
||||||
Далее запушим любое изменение и посмотрим в репозитории во вкладе действия выполнение. Первое выполнение может быть долгим, так как раннер скачивает контейнер для запуска кода.
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Заметки по синтаксису Markdown
|
|
||||||
## Зачеркнуть
|
|
||||||
Что бы сделать зачеркнутый текст поместите его в ~~ТЕКСТ~~
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
# Установка Opensearch
|
|
||||||
Ссылка на офф доку https://docs.opensearch.org/latest/install-and-configure/install-opensearch/tar/
|
|
||||||
Установка опенсерча производится из архива.
|
|
||||||
Стендэлон
|
|
||||||
Подготавливаем ноду для установки. Все это дело работает на джаве. И хоть в архиве опенсерча уже есть жаба лучше поставить ее прямо на машину, что бы было проще работать
|
|
||||||
```
|
|
||||||
sudo apt update && sudo apt upgrade -y
|
|
||||||
sudo apt install -y openjdk-25-jdk
|
|
||||||
```
|
|
||||||
Отключим свап и настроем виртуальную память хоста
|
|
||||||
```
|
|
||||||
sudo vim /etc/sysctl.conf
|
|
||||||
|
|
||||||
vm.max_map_count=262144
|
|
||||||
|
|
||||||
sudo sysctl -p
|
|
||||||
cat /proc/sys/vm/max_map_count
|
|
||||||
```
|
|
||||||
После обновления и установки жабы, лучше всего перезагрузится. Далее мы качаем архив с опенсерчем с оффсайта
|
|
||||||
```
|
|
||||||
wget https://artifacts.opensearch.org/releases/bundle/opensearch/3.6.0/opensearch-3.6.0-linux-x64.deb
|
|
||||||
sudo env OPENSEARCH_INITIAL_ADMIN_PASSWORD=5ruXurur! dpkg -i opensearch-3.6.0-linux-x64.deb
|
|
||||||
sudo systemctl enable opensearch
|
|
||||||
```
|
|
||||||
|
|
||||||
Выполним скрипт ssl.sh (лежит в доке как пример) для создания самоподписанных сертификатов и хранилища сертификатов. СОздадутся корневой сертификат ЦА. Так же создастся на его основе админский сертификат и сертификаты для каждой ноды. Это нужно что бы общение между нодами и дашбордой были зашифрованы. Так же выпустится админский серт для зашифрованного подключения админа.
|
|
||||||
После завершения подготовки требуется нарисовать кофниг. Конфиг имеет формат YAML и различается в зависимости от кластера или стендэлон опенсерча
|
|
||||||
|
|
||||||
|
|
||||||
mv /etc/opensearch/opensearch.yml /etc/opensearch/opensearch.yml.back
|
|
||||||
|
|
||||||
rm /etc/opensearch/*.pem
|
|
||||||
|
|
||||||
vim /etc/opensearch/opensearch.yml
|
|
||||||
|
|
||||||
systemctl start opensearch
|
|
||||||
|
|
||||||
|
|
||||||
wget https://artifacts.opensearch.org/releases/bundle/opensearch-dashboards/3.6.0/opensearch-dashboards-3.6.0-linux-x64.deb
|
|
||||||
|
|
||||||
$2y$12$7.7YOQi6.CEN1VSNXg3BqefgQLEeqDbsqxNBNhY1tgZ085q1eZeCK
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
Установка из тарбола завершилась тем что я не разложил бинарники логи и тд по папкам. Так же все напутал с правами, юзерами, группами и т.д. Секурити плагин не смог прочитать кейстор, видимо что то я сделал неправильно
|
|
||||||
Попробуем установить его через апт завтра. Так же откатимся в хотелках и просто сгенерим сертификаты, без кестора и трастстора
|
|
||||||
|
|
||||||
20/04/2026 Установка через апт работает. Но проблема с сертификатами остается. Смиряемся и откатываемся. Возможно я генерю сертификаты не правильно. Из важного сертификаты надо генерить на 1 ноде. А потом раскидать все это дело по узлам.
|
|
||||||
Из важного записать все ноды в файлы хостс или поднять свой днс
|
|
||||||
|
|
||||||
27.04.2026 Решил поднимать свой днс. Помучил нейронки. Из предлженных вариантов только powerdns имеет апи и тераформ провайдера. Подолбится ли с базовой инфрой? Или попытатся обойтись костылями?
|
|
||||||
04.05.2026 Днс сработал и решил 1 проблему. Как приятно держать свою инфру. Выяснил что с винды не создавалась тачка потому как был включен впн, а я забыл что прокс виртуальный с винды и трафик тунелируется через вмваре бридж прямо в тунель. Собрал кластер без секюрити плагина. Установил дашборду. Столкнулся с пробелмой авторизации. Пока отключил все настройки безопасности на серче и дашборде.
|
|
||||||
Шаг 0 выполнен. Теперь надо поднять все то же самое но с включенным секьюрити плагином. И прокинуть пользаков в опенсерч.
|
|
||||||
Так же можно задуматься какие логи я буду писать в опенсерч. Для начала можно попробовать напрямую логи самого серча
|
|
||||||
Для сбора используем fluenbit. Шаг 0 заставить это как то работать
|
|
||||||
|
|
||||||
На заметку поискать мб какую то Пошечку для автоматизации выпуска сертификатов и их доставки на сервера
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
Сделаю наверное перевод с дополнениями
|
|
||||||
|
|
||||||
В этом посте я буду использовать следующие продукты:
|
|
||||||
|
|
||||||
- Proxmox - [домашняя страница](https://www.proxmox.com/en/)
|
|
||||||
- Ubuntu - [домашняя страница](https://ubuntu.com/)
|
|
||||||
- OpenTofu - [домашняя страница](https://opentofu.org/)
|
|
||||||
- поставщик terraform для bpg/proxmox — [реестр](https://registry.terraform.io/providers/bpg/proxmox/latest) и [github](https://github.com/bpg/terraform-provider-proxmox)
|
|
||||||
- Ansible - [домашняя страница](https://www.ansible.com/community)
|
|
||||||
- Kubespray - [домашняя страница](https://kubespray.io/#/) и [github](https://github.com/kubernetes-sigs/kubespray/tree/master)
|
|
||||||
|
|
||||||
### Установить OpenTofu
|
|
||||||
|
|
||||||
Чтобы начать работу с OpenTofu, я установил его на свой компьютер с Linux с помощью Snap, но есть и несколько других альтернатив. Подробнее см. на официальной странице [документации](https://opentofu.org/docs/intro/install/snap) OpenTofu.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo snap install --classic opentofu
|
|
||||||
```
|
|
||||||
|
|
||||||
Теперь OpenTofu установлен, и я могу начать его использовать. Я также установил автозаполнение bash следующим образом:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tofu -install-autocomplete # restart shell session...
|
|
||||||
```
|
|
||||||
|
|
||||||
Я решил создать отдельную папку для своих «проектов», поэтому я создал папку в своей домашней папке под названием *tofu/proxmox, в которой у меня есть разные подпапки в зависимости от задач или ресурсов, для которых я буду использовать OpenTofu.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
tofu/proxmox/
|
|
||||||
├── k8s-cluster-02
|
|
||||||
├── proxmox-images
|
|
||||||
```
|
|
||||||
|
|
||||||
## Сетевое зеркало реестра провайдеров terraform
|
|
||||||
|
|
||||||
Так как реестры и тераформ заблокированы для РФ требуется использовать пути обхода.
|
|
||||||
Для использования зеркала добавьте в [файл настроек terraform](https://www.terraform.io/cli/config/config-file) следующий код:
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
provider_installation {
|
|
||||||
network_mirror {
|
|
||||||
url = "https://nm.tf.org.ru/"
|
|
||||||
include = ["registry.terraform.io/*/*"]
|
|
||||||
}
|
|
||||||
direct {
|
|
||||||
exclude = ["registry.terraform.io/*/*"]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
- В Windows файл должен называться `terraform.rc` и находиться в каталоге `%APPDATA%` соответствующего пользователя. Расположение каталога зависит от версии Windows. Чтобы найти этот каталог в своей системе используйте `$env:APPDATA` в PowerShell.
|
|
||||||
- В остальных системах файл должен называться `.terraformrc` и находиться в домашнем каталоге пользователя. (/home/ubadmin)
|
|
||||||
## Другие сайты
|
|
||||||
## Зеркала реестров
|
|
||||||
- [registry.comcloud.xyz](https://registry.comcloud.xyz/)
|
|
||||||
- [terraform-mirror.yandexcloud.net](https://cloud.yandex.ru/docs/tutorials/infrastructure-management/terraform-quickstart#configure-provider)
|
|
||||||
## Зеркала релизов
|
|
||||||
- [hashicorp-releases.website.yandexcloud.net](https://hashicorp-releases.website.yandexcloud.net/terraform/)
|
|
||||||
- [hc-mirror.express42.net](https://hc-mirror.express42.net/)
|
|
||||||
- [releases.comcloud.xyz](https://releases.comcloud.xyz/)
|
|
||||||
## Документация
|
|
||||||
- [docs.comcloud.xyz](https://docs.comcloud.xyz/)
|
|
||||||
|
|
||||||
### Провайдер OpenTofu Proxmox
|
|
||||||
|
|
||||||
Чтобы использовать OpenTofu с Proxmox, мне нужен провайдер, который может использовать API Proxmox. Я быстро изучил различные варианты и остановился на этом провайдере: [bpg/proxmox](https://registry.terraform.io/providers/bpg/proxmox). Он кажется очень активным и недавно обновлялся (согласно репозиторию git [здесь](https://github.com/bpg/terraform-provider-proxmox))
|
|
||||||
|
|
||||||
Поставщик OpenTofu/Terraform определяется следующим образом, а приведенный ниже пример настраивает установку поставщика bpg/proxmox, необходимого для взаимодействия с Proxmox.
|
|
||||||
terraform {
|
|
||||||
|
|
||||||
```
|
|
||||||
required_providers {
|
|
||||||
proxmox = {
|
|
||||||
source = "registry.terraform.io/bpg/proxmox"
|
|
||||||
}
|
|
||||||
local = {
|
|
||||||
source = "registry.terraform.io/hashicorp/local"
|
|
||||||
}
|
|
||||||
null = {
|
|
||||||
source = "registry.terraform.io/hashicorp/null"
|
|
||||||
}
|
|
||||||
time = {
|
|
||||||
source = "registry.terraform.io/hashicorp/time"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
provider "proxmox" {
|
|
||||||
endpoint = var.pve_api_url
|
|
||||||
api_token = "${var.pve_token_id}=${var.pve_token_secret}"
|
|
||||||
insecure = true
|
|
||||||
ssh {
|
|
||||||
agent = true
|
|
||||||
username = "root"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Я сохраню это содержимое в файле под названием _providers.tf_
|
|
||||||
Сначала краткое объяснение двух разделов выше. Разделы _terraform_ указывают OpenTofu/Terraform, какой провайдер следует загрузить и включить. Поле _version_ определяет конкретную версию для использования, а не _последнюю_, а именно эту версию. Использование этого поля гарантирует, что ваша автоматизация не сломается, если в версии провайдера произойдут какие-либо изменения в API.
|
|
||||||
Раздел _provider_ настраивает взаимодействие прокси-провайдера с Proxmox. Вместо обычного имени пользователя и пароля я решил использовать токен API. Здесь я использую значение переменной в ключах endpoint и api-token, которые определены в другом файле под названием variables.tf и credentials.auto.tfvars. В моей системе автоматизации есть задачи, для которых требуется взаимодействие с Proxmox по SSH, поэтому я также включил его, настроив поле _ssh_.
|
|
||||||
|
|
||||||
Для получения дополнительной информации о провайдере bpg/proxmox перейдите [сюда](https://registry.terraform.io/providers/bpg/proxmox/latest/docs)
|
|
||||||
### Подготовьте Proxmox с помощью токена API для поставщика OpenTofu bpg/proxmox
|
|
||||||
|
|
||||||
Чтобы использовать описанный выше провайдер с Proxmox, мне нужно подготовить Proxmox для использования токена API. Я следовал документации провайдера bpg/proxmox [здесь](https://registry.terraform.io/providers/bpg/proxmox/latest/docs#api-token-authentication)
|
|
||||||
|
|
||||||
```
|
|
||||||
# Create the user
|
|
||||||
sudo pveum user add terraform@pve
|
|
||||||
|
|
||||||
# Create a role for the user above
|
|
||||||
sudo pveum role add Terraform -privs "Datastore.Allocate Datastore.AllocateSpace Datastore.AllocateTemplate Datastore.Audit Pool.Allocate Sys.Audit Sys.Console Sys.Modify SDN.Use VM.Allocate VM.Audit VM.Clone VM.Config.CDROM VM.Config.Cloudinit VM.Config.CPU VM.Config.Disk VM.Config.HWType VM.Config.Memory VM.Config.Network VM.Config.Options VM.Migrate VM.Monitor VM.PowerMgmt User.Modify"
|
|
||||||
|
|
||||||
# Assign the terraform user to the above role
|
|
||||||
sudo pveum aclmod / -user terraform@pve -role Terraform
|
|
||||||
|
|
||||||
# Create the token
|
|
||||||
sudo pveum user token add terraform@pve provider --privsep=0
|
|
||||||
|
|
||||||
┌──────────────┬──────────────────────────────────────┐
|
|
||||||
│ key │ value │
|
|
||||||
╞══════════════╪══════════════════════════════════════╡
|
|
||||||
│ full-tokenid │ terraform@pve!provider │
|
|
||||||
├──────────────┼──────────────────────────────────────┤
|
|
||||||
│ info │ {"privsep":"0"} │
|
|
||||||
├──────────────┼──────────────────────────────────────┤
|
|
||||||
│ value │ <token> │
|
|
||||||
└──────────────┴──────────────────────────────────────┘
|
|
||||||
# make a backup of the token
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
|
|
||||||
Теперь я могу войти в свой узел Proxmox по SSH без пароля с моего Linux-шлюза. Но при использовании в сочетании с opentofu этого недостаточно. Мне нужно загрузить ключ в хранилище ключей. Если я этого не сделаю, автоматизация, требующая доступа по SSH, завершится ошибкой с этим сообщением:
|
|
||||||
```bash
|
|
||||||
Error: failed to open SSH client: unable to authenticate user "root" over SSH to "172.18.5.102:22". Please verify that ssh-agent is correctly loaded with an authorized key via 'ssh-add -L' (NOTE: configurations in ~/.ssh/config are not considered by golang's ssh implementation). The exact error from ssh.Dial: ssh: handshake failed: ssh: unable to authenticate, attempted methods [none password], no supported methods remain
|
|
||||||
```
|
|
||||||
Для того что бы избавиться от ошибки нужно выполнить команды в терминале:
|
|
||||||
```
|
|
||||||
eval `ssh-agent -s`
|
|
||||||
ssh-add /home/ubadmin/.ssh/id_ed25519
|
|
||||||
```
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
|
|
||||||
eval `ssh-agent -s`
|
|
||||||
ssh-add /home/ubadmin/.ssh/id_ed25519
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
При добавлении хранилища в прокс не ставить MTU 9000 даже если оборудование поддерживает. Потому что кривые пидарасы не смогли это реализовать. До первой проблемы\перезгарузки\мигания электричества (нужное подчеркнуть) оно работает как часы, но потом не запустишь. Кластер разваливается и пиздец.
|
|
||||||
Возможно я накосячил с MTU.
|
|
||||||
Jumbo Frames включены на всем маршруте
|
|
||||||
Опытным путем замеров и кринжа выяснен МТУ для маршрута до СХД
|
|
||||||
MTU 7940
|
|
||||||
Почему так а хуй его знает. Заголовок VLAN 4 байта. МТУ на интерфейсах свитча 9126
|
|
||||||
МТУ на СХД 9000
|
|
||||||
куда делись 1000 байт а хер его знает.
|
|
||||||
Я выяснил свой косяк. Производитель говна под названием Infortrend сделал на модели СХД jumbo frames 8000 вместо 9000 Теперь понятно куда делся косарь байтиков. Вонючие тайваньцы сожрали
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
|
|
||||||
Для создания кластера копируем мой шаблон в рабочую директорию и переименовываем. Переходим в свежую копию и готовимся к развертыванию
|
|
||||||
ЧТо нам потребуется:
|
|
||||||
- Создать 3 мастер ноды
|
|
||||||
- Создать 3 воркер ноды
|
|
||||||
- Сгенерировать инвентарь для ансибл
|
|
||||||
- Установить кубспрей
|
|
||||||
- установить зависимости
|
|
||||||
- Запустить плейбук установки кубера
|
|
||||||
Действия для выполнения.
|
|
||||||
ТАк как надо создавать 2 вида вм то создадим 2 файла где опишем ресурсы и назовем их k8s-cp-node-01 и k8s-worker-node-01
|
|
||||||
Копируем в эти файлы содержимое темплейта и меняем название ресурса.
|
|
||||||
С переменными в данной ситуации все сложно.
|
|
||||||
Можно создать набор переменных для каждой установки, но тогда на потребуется указывать 2 набора переменных под каждый тип машин.
|
|
||||||
Можно использовать переменные из шаблона не вводя новые, но так как ресурса 2 разных мы столкнемся с проблемой что переменные пересекаются в ресурсах и мешают друг другу (например Id вм должно быть индивидуальным, но без колхозинга оно таким не является и падает в ошибку )
|
|
||||||
|
|
||||||
Я предпочел колхозинг второму набору, так как его немного. А так же не надо городить второй набор практических неизменных значений. (подумать над решением такой проблемы)
|
|
||||||
|
|
||||||
Меняем значения переменных в файле значений. СЛЕДИМ ЗА ID!!!!! Так можно убить инфру. По хорошему надо добавить автотест с отдельной ручкой на случай сушествования ВМ с таким айди.
|
|
||||||
|
|
||||||
Так как у нас добавился счетчик, то для подставления значений в имя, ip и тд, нам нужно экранировать переменные, а так же добавить простой текст для того, что бы не городить второй набор. Этот текст будет различать разные виды ресурса.
|
|
||||||
Переменные которые обязательно меняются:
|
|
||||||
vm_id
|
|
||||||
vm_hostname
|
|
||||||
ip_address
|
|
||||||
Значение для них записываются в виде :
|
|
||||||
```
|
|
||||||
"${var.vm_hostname}-cp-${count.index + 1}"
|
|
||||||
```
|
|
||||||
```
|
|
||||||
"${var.vm_id}1${count.index + 1}"
|
|
||||||
```
|
|
||||||
```
|
|
||||||
"${var.ip_address}${count.index + 1}/24"
|
|
||||||
```
|
|
||||||
Мы добавляем кавычки так как внутри содержится просто текст. Текст мы добавляем для каждого ресурса свой.
|
|
||||||
Так как имя будет составное из переменной из файла и переменной счетчика то мы их экранируем друг от друга конструкцией ${}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
### Записик для изучения модулей в terraform
|
|
||||||
09.05.2026
|
|
||||||
Узнал что существуют модули
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
# Переменные в terraform
|
|
||||||
Переменные - значения, которые передаются пользователем и могут менятся в зависимости от задачи. Существует два вида переменных.
|
|
||||||
1) Входящие - переменные, которые поступают от пользователя.
|
|
||||||
2) Исходящие - определяются в результате работы
|
|
||||||
## Входящие переменные
|
|
||||||
Для использования переменных их нужно сначало объявить. Для соблюдения чистоты рекомендуется делать это в отдельном файле variable.tf
|
|
||||||
Объявив переменную мы можем вызвать ее в ресурсе используюя ее имя.
|
|
||||||
Так же мы можем записывать значения в файл с расширение *.tfvars. Тогда нужно будет при планировании указать ключ -var-file и имя файла
|
|
||||||
```
|
|
||||||
tofu plan -var-file example.tfvars -out example
|
|
||||||
```
|
|
||||||
Создавая файлы переменных с разными значениями, мы можем использовать 1 и тот же код ресурса для разных окружений
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
Манифест - этакая программа, состоящая из HCL кода, для выполнения набора действий и логики. Для читаемости разбивается на файлы.
|
|
||||||
Блок - блок кода в терраформе, который выполняет определенное действие. Если приводить аналогию с програмированием, то это функция.
|
|
||||||
Переменная - изменяемое значение, передается на вход пользователем или на выход программой.
|
|
||||||
Ресурс - блок кода, используемый для создания чего либо. Возможности определяются провайдером.
|
|
||||||
Модуль - готовый набор ресурсов, который мы можем переиспользовать в другом манифесте.
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
```
|
|
||||||
#cloud-config
|
|
||||||
package_update: true
|
|
||||||
packages:
|
|
||||||
- iptables-persistent
|
|
||||||
- fail2ban
|
|
||||||
- auditd
|
|
||||||
- qemu-guest-agent
|
|
||||||
- net-tools
|
|
||||||
groups:
|
|
||||||
- admins
|
|
||||||
users:
|
|
||||||
- name: ubadmin
|
|
||||||
primary_group: admin
|
|
||||||
groups:
|
|
||||||
- sudo
|
|
||||||
- admins
|
|
||||||
sudo: ALL=(ALL) NOPASSWD:ALL
|
|
||||||
shell: /bin/bash
|
|
||||||
ssh_authorized_keys:
|
|
||||||
- <ssh_public_key>
|
|
||||||
write_files:
|
|
||||||
- path: /etc/iptables/rules.v4
|
|
||||||
permissions: 0640
|
|
||||||
owner: root:root
|
|
||||||
content: |
|
|
||||||
*filter
|
|
||||||
:INPUT DROP [0:0]
|
|
||||||
:FORWARD DROP [0:0]
|
|
||||||
:OUTPUT ACCEPT [0:0]
|
|
||||||
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
|
|
||||||
-A INPUT -i lo -j ACCEPT
|
|
||||||
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
|
|
||||||
-A INPUT -p icmp --icmp-type 0 -j ACCEPT
|
|
||||||
-A INPUT -p icmp --icmp-type 3 -j ACCEPT
|
|
||||||
-A INPUT -p icmp --icmp-type 11 -j ACCEPT
|
|
||||||
COMMIT
|
|
||||||
- path: /etc/fail2ban/jail.local
|
|
||||||
permissions: 0640
|
|
||||||
owner: root:root
|
|
||||||
content: |
|
|
||||||
[sshd]
|
|
||||||
enabled = true
|
|
||||||
findtime = 1m
|
|
||||||
nmaxretry = 5
|
|
||||||
bantime = 15m
|
|
||||||
- path: /etc/audit/rules.d/audit.rules
|
|
||||||
permissions: 0640
|
|
||||||
owner: root:root
|
|
||||||
content: |
|
|
||||||
-D
|
|
||||||
-e 1
|
|
||||||
-f 1
|
|
||||||
-a always,exclude -F msgtype=CWD
|
|
||||||
-a always,exclude -F msgtype=PATH
|
|
||||||
-a always,exclude -F msgtype=PROCTITLE
|
|
||||||
-a always,exit -F dir=/var/log/audit/ -F perm=wa -F auid!=unset -F key=audit-trail-modification
|
|
||||||
-a always,exit -F path=/var/log/syslog -F perm=wa -F auid!=unset -F key=audit-trail-modification
|
|
||||||
-a always,exit -F path=/var/log/auth.log -F perm=wa -F auid!=unset -F key=audit-trail-modification
|
|
||||||
-a always,exit -F arch=x86_64 -S setuid -F auid!=unset -F a0=0 -F exe=/usr/bin/su -F key=elevated-privileges-session
|
|
||||||
-a always,exit -F arch=x86_64 -S setresuid -F auid!=unset -F a0=0 -F exe=/usr/bin/sudo -F key=elevated-privileges-session
|
|
||||||
-a always,exit -F arch=x86_64 -S execve -F auid!=unset -C uid!=euid -F euid=0 -F key=elevated-privileges-session
|
|
||||||
-a always,exit -F arch=x86_64 -S chmod -S fchmod -S chown -S fchown -S lchown -F auid!=unset -F key=access-rights-modification
|
|
||||||
runcmd:
|
|
||||||
- printf "%s\n""readonly TMOUT=900" "export TMOUT" >> /etc/profile
|
|
||||||
- printf "%s\n" "net.ipv6.conf.all.disable_ipv6 = 1" "net.ipv6.conf.default.disable_ipv6 = 1" "net.ipv6.conf.lo.disable_ipv6 = 1" >> /etc/sysctl.conf && sysctl -p
|
|
||||||
- sed -i -e '/^PermitRootLogin/s/^.*$/PermitRootLogin no/' /etc/ssh/sshd_config
|
|
||||||
- sed -i -e '/^PasswordAuthentication/s/^.*$/PasswordAuthentication no/' /etc/ssh/sshd_config
|
|
||||||
- sed -i -e '/^#ClientAliveInterval/s/^.*$/ClientAliveInterval 5m/' /etc/ssh/sshd_config
|
|
||||||
- sed -i -e '/^#ClientAliveCountMax/s/^.*$/ClientAliveCountMax 3/' /etc/ssh/sshd_config
|
|
||||||
- sed -i -e '$aAllowGroups admins' /etc/ssh/sshd_config
|
|
||||||
- systemctl enable fail2ban
|
|
||||||
- reboot
|
|
||||||
```
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
# Инструкция по установке Vault
|
|
||||||
Система ubuntu 22.04
|
|
||||||
Vault 2.0.0
|
|
||||||
## Скачивание
|
|
||||||
Компания Hashicorp запретила скачивать свои продукты из России. Пользуемся ВПН или скачиваем с зеркала яндекса
|
|
||||||
```
|
|
||||||
VAULT_VERSION="2.0.0"
|
|
||||||
wget https://hashicorp-releases.yandexcloud.net/vault/${VAULT_VERSION}/vault_${VAULT_VERSION}_linux_amd64.zip
|
|
||||||
```
|
|
||||||
## Установка
|
|
||||||
Установим архиватор, что бы распаковать бинарник
|
|
||||||
```
|
|
||||||
sudo apt-get install -y unzip
|
|
||||||
```
|
|
||||||
Распакуем и перенесем бинарник в папку с бинарниками
|
|
||||||
```
|
|
||||||
unzip vault_${VAULT_VERSION}_linux_amd64.zip
|
|
||||||
sudo mv vault /usr/local/bin/
|
|
||||||
```
|
|
||||||
Проверим что работает
|
|
||||||
```
|
|
||||||
vault --version
|
|
||||||
```
|
|
||||||
Далее нужно создать системного юзера и директории для конфигов и данных
|
|
||||||
```
|
|
||||||
adduser \
|
|
||||||
--system \
|
|
||||||
--shell /bin/false \
|
|
||||||
--gecos 'Vault user' \
|
|
||||||
--group \
|
|
||||||
--disabled-password \
|
|
||||||
--no-create-home \
|
|
||||||
vault
|
|
||||||
```
|
|
||||||
```
|
|
||||||
sudo nano /etc/vault/vault.hcl
|
|
||||||
```
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
Для того что бы использовать vault
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# Заметка для запоминания горячих клавиш в Vscode
|
|
||||||
1) Для рендера Markdown ctrl + shift + V. Для того что бы вернуться в редактор нужно нажать еще раз
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#comand #notes #database #mysql
|
|
||||||
mysqldump -u root -v --single-transaction --skip-lock-tables --flush-privileges --quick zabbix > /tmp/zabbix.sql
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#comand #notes
|
|
||||||
![[Pasted image 20250312073216.png]]
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
#comand #linux #iptables
|
|
||||||
```
|
|
||||||
iptables -A FORWARD -i eth1(внутрений) -o eth0(внешний) -j ACCEPT
|
|
||||||
```
|
|
||||||
```
|
|
||||||
iptables -A FORWARD -m state --state RELATED,ESTABLISHED -j ACCEPT
|
|
||||||
```
|
|
||||||
```
|
|
||||||
iptables -t nat -A POSTROUTING -s 10.2.0.0/24 -o eth1 -j SNAT --to-source 84.201.168.122
|
|
||||||
```
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#comand #notes #linux
|
|
||||||
```
|
|
||||||
apt list --instaled (name)
|
|
||||||
```
|
|
||||||
Имя пакета, можно использовать звездочку если не помнишь точное имя
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#comand #linux #notes
|
|
||||||
**du** — команда в Linux, которая отображает размер директории. [1](https://winitpro.ru/index.php/2021/02/20/svobodnoe-mesta-na-diske-v-linux/) Расшифровывается как disk usage (использование диска). [2](https://rebrainme.com/blog/linux/kak-uznat-skolko-mesta-zanimayut-fajly-i-direktorii-v-linux/)
|
|
||||||
|
|
||||||
**Некоторые дополнительные опции для du**:
|
|
||||||
|
|
||||||
- **du -h** — показывает информацию в читабельном формате. Можно указать путь к конкретной директории, если применить без этого, покажет информацию в текущем каталоге. [1](https://winitpro.ru/index.php/2021/02/20/svobodnoe-mesta-na-diske-v-linux/)
|
|
||||||
- **du -sh** — покажет размер директории, в которой пользователь находится. Можно указать путь к директории. [1](https://winitpro.ru/index.php/2021/02/20/svobodnoe-mesta-na-diske-v-linux/)
|
|
||||||
- **du -m** — вывод информации в мегабайтах. [1](https://winitpro.ru/index.php/2021/02/20/svobodnoe-mesta-na-diske-v-linux/)
|
|
||||||
- **du -h --time** — выведет информацию с дополнительным столбцом по времени изменения директории или файлов.
|
|
||||||
|
|
||||||
Если запустить du без указания параметров, то она выведет все директории и поддиректории текущей директории.
|
|
||||||
(https://winitpro.ru/index.php/2021/02/20/svobodnoe-mesta-na-diske-v-linux/)
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
#comand #notes #linux
|
|
||||||
```
|
|
||||||
ip addr flush dev eth0 (указать имя сетевого интерфеса)
|
|
||||||
ifconfig <interface> 0.0.0.0
|
|
||||||
```
|
|
||||||
@@ -1,761 +0,0 @@
|
|||||||
#notes #comand #linux #docker
|
|
||||||
```
|
|
||||||
curl https://get.docker.com | sh -
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
#!/bin/sh
|
|
||||||
set -e
|
|
||||||
# Docker Engine for Linux installation script.
|
|
||||||
#
|
|
||||||
# This script is intended as a convenient way to configure docker's package
|
|
||||||
# repositories and to install Docker Engine, This script is not recommended
|
|
||||||
# for production environments. Before running this script, make yourself familiar
|
|
||||||
# with potential risks and limitations, and refer to the installation manual
|
|
||||||
# at https://docs.docker.com/engine/install/ for alternative installation methods.
|
|
||||||
#
|
|
||||||
# The script:
|
|
||||||
#
|
|
||||||
# - Requires `root` or `sudo` privileges to run.
|
|
||||||
# - Attempts to detect your Linux distribution and version and configure your
|
|
||||||
# package management system for you.
|
|
||||||
# - Doesn't allow you to customize most installation parameters.
|
|
||||||
# - Installs dependencies and recommendations without asking for confirmation.
|
|
||||||
# - Installs the latest stable release (by default) of Docker CLI, Docker Engine,
|
|
||||||
# Docker Buildx, Docker Compose, containerd, and runc. When using this script
|
|
||||||
# to provision a machine, this may result in unexpected major version upgrades
|
|
||||||
# of these packages. Always test upgrades in a test environment before
|
|
||||||
# deploying to your production systems.
|
|
||||||
# - Isn't designed to upgrade an existing Docker installation. When using the
|
|
||||||
# script to update an existing installation, dependencies may not be updated
|
|
||||||
# to the expected version, resulting in outdated versions.
|
|
||||||
#
|
|
||||||
# Source code is available at https://github.com/docker/docker-install/
|
|
||||||
#
|
|
||||||
# Usage
|
|
||||||
# ==============================================================================
|
|
||||||
#
|
|
||||||
# To install the latest stable versions of Docker CLI, Docker Engine, and their
|
|
||||||
# dependencies:
|
|
||||||
#
|
|
||||||
# 1. download the script
|
|
||||||
#
|
|
||||||
# $ curl -fsSL https://get.docker.com -o install-docker.sh
|
|
||||||
#
|
|
||||||
# 2. verify the script's content
|
|
||||||
#
|
|
||||||
# $ cat install-docker.sh
|
|
||||||
#
|
|
||||||
# 3. run the script with --dry-run to verify the steps it executes
|
|
||||||
#
|
|
||||||
# $ sh install-docker.sh --dry-run
|
|
||||||
#
|
|
||||||
# 4. run the script either as root, or using sudo to perform the installation.
|
|
||||||
#
|
|
||||||
# $ sudo sh install-docker.sh
|
|
||||||
#
|
|
||||||
# Command-line options
|
|
||||||
# ==============================================================================
|
|
||||||
#
|
|
||||||
# --version <VERSION>
|
|
||||||
# Use the --version option to install a specific version, for example:
|
|
||||||
#
|
|
||||||
# $ sudo sh install-docker.sh --version 23.0
|
|
||||||
#
|
|
||||||
# --channel <stable|test>
|
|
||||||
#
|
|
||||||
# Use the --channel option to install from an alternative installation channel.
|
|
||||||
# The following example installs the latest versions from the "test" channel,
|
|
||||||
# which includes pre-releases (alpha, beta, rc):
|
|
||||||
#
|
|
||||||
# $ sudo sh install-docker.sh --channel test
|
|
||||||
#
|
|
||||||
# Alternatively, use the script at https://test.docker.com, which uses the test
|
|
||||||
# channel as default.
|
|
||||||
#
|
|
||||||
# --mirror <Aliyun|AzureChinaCloud>
|
|
||||||
#
|
|
||||||
# Use the --mirror option to install from a mirror supported by this script.
|
|
||||||
# Available mirrors are "Aliyun" (https://mirrors.aliyun.com/docker-ce), and
|
|
||||||
# "AzureChinaCloud" (https://mirror.azure.cn/docker-ce), for example:
|
|
||||||
#
|
|
||||||
# $ sudo sh install-docker.sh --mirror AzureChinaCloud
|
|
||||||
#
|
|
||||||
# ==============================================================================
|
|
||||||
|
|
||||||
|
|
||||||
# Git commit from https://github.com/docker/docker-install when
|
|
||||||
# the script was uploaded (Should only be modified by upload job):
|
|
||||||
SCRIPT_COMMIT_SHA="4c94a56999e10efcf48c5b8e3f6afea464f9108e"
|
|
||||||
|
|
||||||
# strip "v" prefix if present
|
|
||||||
VERSION="${VERSION#v}"
|
|
||||||
|
|
||||||
# The channel to install from:
|
|
||||||
# * stable
|
|
||||||
# * test
|
|
||||||
DEFAULT_CHANNEL_VALUE="stable"
|
|
||||||
if [ -z "$CHANNEL" ]; then
|
|
||||||
CHANNEL=$DEFAULT_CHANNEL_VALUE
|
|
||||||
fi
|
|
||||||
|
|
||||||
DEFAULT_DOWNLOAD_URL="https://download.docker.com"
|
|
||||||
if [ -z "$DOWNLOAD_URL" ]; then
|
|
||||||
DOWNLOAD_URL=$DEFAULT_DOWNLOAD_URL
|
|
||||||
fi
|
|
||||||
|
|
||||||
DEFAULT_REPO_FILE="docker-ce.repo"
|
|
||||||
if [ -z "$REPO_FILE" ]; then
|
|
||||||
REPO_FILE="$DEFAULT_REPO_FILE"
|
|
||||||
fi
|
|
||||||
|
|
||||||
mirror=''
|
|
||||||
DRY_RUN=${DRY_RUN:-}
|
|
||||||
while [ $# -gt 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
--channel)
|
|
||||||
CHANNEL="$2"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--dry-run)
|
|
||||||
DRY_RUN=1
|
|
||||||
;;
|
|
||||||
--mirror)
|
|
||||||
mirror="$2"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--version)
|
|
||||||
VERSION="${2#v}"
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--*)
|
|
||||||
echo "Illegal option $1"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
shift $(( $# > 0 ? 1 : 0 ))
|
|
||||||
done
|
|
||||||
|
|
||||||
case "$mirror" in
|
|
||||||
Aliyun)
|
|
||||||
DOWNLOAD_URL="https://mirrors.aliyun.com/docker-ce"
|
|
||||||
;;
|
|
||||||
AzureChinaCloud)
|
|
||||||
DOWNLOAD_URL="https://mirror.azure.cn/docker-ce"
|
|
||||||
;;
|
|
||||||
"")
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
>&2 echo "unknown mirror '$mirror': use either 'Aliyun', or 'AzureChinaCloud'."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
case "$CHANNEL" in
|
|
||||||
stable|test)
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
>&2 echo "unknown CHANNEL '$CHANNEL': use either stable or test."
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
command_exists() {
|
|
||||||
command -v "$@" > /dev/null 2>&1
|
|
||||||
}
|
|
||||||
|
|
||||||
# version_gte checks if the version specified in $VERSION is at least the given
|
|
||||||
# SemVer (Maj.Minor[.Patch]), or CalVer (YY.MM) version.It returns 0 (success)
|
|
||||||
# if $VERSION is either unset (=latest) or newer or equal than the specified
|
|
||||||
# version, or returns 1 (fail) otherwise.
|
|
||||||
#
|
|
||||||
# examples:
|
|
||||||
#
|
|
||||||
# VERSION=23.0
|
|
||||||
# version_gte 23.0 // 0 (success)
|
|
||||||
# version_gte 20.10 // 0 (success)
|
|
||||||
# version_gte 19.03 // 0 (success)
|
|
||||||
# version_gte 26.1 // 1 (fail)
|
|
||||||
version_gte() {
|
|
||||||
if [ -z "$VERSION" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
version_compare "$VERSION" "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# version_compare compares two version strings (either SemVer (Major.Minor.Path),
|
|
||||||
# or CalVer (YY.MM) version strings. It returns 0 (success) if version A is newer
|
|
||||||
# or equal than version B, or 1 (fail) otherwise. Patch releases and pre-release
|
|
||||||
# (-alpha/-beta) are not taken into account
|
|
||||||
#
|
|
||||||
# examples:
|
|
||||||
#
|
|
||||||
# version_compare 23.0.0 20.10 // 0 (success)
|
|
||||||
# version_compare 23.0 20.10 // 0 (success)
|
|
||||||
# version_compare 20.10 19.03 // 0 (success)
|
|
||||||
# version_compare 20.10 20.10 // 0 (success)
|
|
||||||
# version_compare 19.03 20.10 // 1 (fail)
|
|
||||||
version_compare() (
|
|
||||||
set +x
|
|
||||||
|
|
||||||
yy_a="$(echo "$1" | cut -d'.' -f1)"
|
|
||||||
yy_b="$(echo "$2" | cut -d'.' -f1)"
|
|
||||||
if [ "$yy_a" -lt "$yy_b" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
if [ "$yy_a" -gt "$yy_b" ]; then
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
mm_a="$(echo "$1" | cut -d'.' -f2)"
|
|
||||||
mm_b="$(echo "$2" | cut -d'.' -f2)"
|
|
||||||
|
|
||||||
# trim leading zeros to accommodate CalVer
|
|
||||||
mm_a="${mm_a#0}"
|
|
||||||
mm_b="${mm_b#0}"
|
|
||||||
|
|
||||||
if [ "${mm_a:-0}" -lt "${mm_b:-0}" ]; then
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
return 0
|
|
||||||
)
|
|
||||||
|
|
||||||
is_dry_run() {
|
|
||||||
if [ -z "$DRY_RUN" ]; then
|
|
||||||
return 1
|
|
||||||
else
|
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
is_wsl() {
|
|
||||||
case "$(uname -r)" in
|
|
||||||
*microsoft* ) true ;; # WSL 2
|
|
||||||
*Microsoft* ) true ;; # WSL 1
|
|
||||||
* ) false;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
is_darwin() {
|
|
||||||
case "$(uname -s)" in
|
|
||||||
*darwin* ) true ;;
|
|
||||||
*Darwin* ) true ;;
|
|
||||||
* ) false;;
|
|
||||||
esac
|
|
||||||
}
|
|
||||||
|
|
||||||
deprecation_notice() {
|
|
||||||
distro=$1
|
|
||||||
distro_version=$2
|
|
||||||
echo
|
|
||||||
printf "\033[91;1mDEPRECATION WARNING\033[0m\n"
|
|
||||||
printf " This Linux distribution (\033[1m%s %s\033[0m) reached end-of-life and is no longer supported by this script.\n" "$distro" "$distro_version"
|
|
||||||
echo " No updates or security fixes will be released for this distribution, and users are recommended"
|
|
||||||
echo " to upgrade to a currently maintained version of $distro."
|
|
||||||
echo
|
|
||||||
printf "Press \033[1mCtrl+C\033[0m now to abort this script, or wait for the installation to continue."
|
|
||||||
echo
|
|
||||||
sleep 10
|
|
||||||
}
|
|
||||||
|
|
||||||
get_distribution() {
|
|
||||||
lsb_dist=""
|
|
||||||
# Every system that we officially support has /etc/os-release
|
|
||||||
if [ -r /etc/os-release ]; then
|
|
||||||
lsb_dist="$(. /etc/os-release && echo "$ID")"
|
|
||||||
fi
|
|
||||||
# Returning an empty string here should be alright since the
|
|
||||||
# case statements don't act unless you provide an actual value
|
|
||||||
echo "$lsb_dist"
|
|
||||||
}
|
|
||||||
|
|
||||||
echo_docker_as_nonroot() {
|
|
||||||
if is_dry_run; then
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
if command_exists docker && [ -e /var/run/docker.sock ]; then
|
|
||||||
(
|
|
||||||
set -x
|
|
||||||
$sh_c 'docker version'
|
|
||||||
) || true
|
|
||||||
fi
|
|
||||||
|
|
||||||
# intentionally mixed spaces and tabs here -- tabs are stripped by "<<-EOF", spaces are kept in the output
|
|
||||||
echo
|
|
||||||
echo "================================================================================"
|
|
||||||
echo
|
|
||||||
if version_gte "20.10"; then
|
|
||||||
echo "To run Docker as a non-privileged user, consider setting up the"
|
|
||||||
echo "Docker daemon in rootless mode for your user:"
|
|
||||||
echo
|
|
||||||
echo " dockerd-rootless-setuptool.sh install"
|
|
||||||
echo
|
|
||||||
echo "Visit https://docs.docker.com/go/rootless/ to learn about rootless mode."
|
|
||||||
echo
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
echo "To run the Docker daemon as a fully privileged service, but granting non-root"
|
|
||||||
echo "users access, refer to https://docs.docker.com/go/daemon-access/"
|
|
||||||
echo
|
|
||||||
echo "WARNING: Access to the remote API on a privileged Docker daemon is equivalent"
|
|
||||||
echo " to root access on the host. Refer to the 'Docker daemon attack surface'"
|
|
||||||
echo " documentation for details: https://docs.docker.com/go/attack-surface/"
|
|
||||||
echo
|
|
||||||
echo "================================================================================"
|
|
||||||
echo
|
|
||||||
}
|
|
||||||
|
|
||||||
# Check if this is a forked Linux distro
|
|
||||||
check_forked() {
|
|
||||||
|
|
||||||
# Check for lsb_release command existence, it usually exists in forked distros
|
|
||||||
if command_exists lsb_release; then
|
|
||||||
# Check if the `-u` option is supported
|
|
||||||
set +e
|
|
||||||
lsb_release -a -u > /dev/null 2>&1
|
|
||||||
lsb_release_exit_code=$?
|
|
||||||
set -e
|
|
||||||
|
|
||||||
# Check if the command has exited successfully, it means we're in a forked distro
|
|
||||||
if [ "$lsb_release_exit_code" = "0" ]; then
|
|
||||||
# Print info about current distro
|
|
||||||
cat <<-EOF
|
|
||||||
You're using '$lsb_dist' version '$dist_version'.
|
|
||||||
EOF
|
|
||||||
|
|
||||||
# Get the upstream release info
|
|
||||||
lsb_dist=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'id' | cut -d ':' -f 2 | tr -d '[:space:]')
|
|
||||||
dist_version=$(lsb_release -a -u 2>&1 | tr '[:upper:]' '[:lower:]' | grep -E 'codename' | cut -d ':' -f 2 | tr -d '[:space:]')
|
|
||||||
|
|
||||||
# Print info about upstream distro
|
|
||||||
cat <<-EOF
|
|
||||||
Upstream release is '$lsb_dist' version '$dist_version'.
|
|
||||||
EOF
|
|
||||||
else
|
|
||||||
if [ -r /etc/debian_version ] && [ "$lsb_dist" != "ubuntu" ] && [ "$lsb_dist" != "raspbian" ]; then
|
|
||||||
if [ "$lsb_dist" = "osmc" ]; then
|
|
||||||
# OSMC runs Raspbian
|
|
||||||
lsb_dist=raspbian
|
|
||||||
else
|
|
||||||
# We're Debian and don't even know it!
|
|
||||||
lsb_dist=debian
|
|
||||||
fi
|
|
||||||
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
|
|
||||||
case "$dist_version" in
|
|
||||||
12)
|
|
||||||
dist_version="bookworm"
|
|
||||||
;;
|
|
||||||
11)
|
|
||||||
dist_version="bullseye"
|
|
||||||
;;
|
|
||||||
10)
|
|
||||||
dist_version="buster"
|
|
||||||
;;
|
|
||||||
9)
|
|
||||||
dist_version="stretch"
|
|
||||||
;;
|
|
||||||
8)
|
|
||||||
dist_version="jessie"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
do_install() {
|
|
||||||
echo "# Executing docker install script, commit: $SCRIPT_COMMIT_SHA"
|
|
||||||
|
|
||||||
if command_exists docker; then
|
|
||||||
cat >&2 <<-'EOF'
|
|
||||||
Warning: the "docker" command appears to already exist on this system.
|
|
||||||
|
|
||||||
If you already have Docker installed, this script can cause trouble, which is
|
|
||||||
why we're displaying this warning and provide the opportunity to cancel the
|
|
||||||
installation.
|
|
||||||
|
|
||||||
If you installed the current Docker package using this script and are using it
|
|
||||||
again to update Docker, you can ignore this message, but be aware that the
|
|
||||||
script resets any custom changes in the deb and rpm repo configuration
|
|
||||||
files to match the parameters passed to the script.
|
|
||||||
|
|
||||||
You may press Ctrl+C now to abort this script.
|
|
||||||
EOF
|
|
||||||
( set -x; sleep 20 )
|
|
||||||
fi
|
|
||||||
|
|
||||||
user="$(id -un 2>/dev/null || true)"
|
|
||||||
|
|
||||||
sh_c='sh -c'
|
|
||||||
if [ "$user" != 'root' ]; then
|
|
||||||
if command_exists sudo; then
|
|
||||||
sh_c='sudo -E sh -c'
|
|
||||||
elif command_exists su; then
|
|
||||||
sh_c='su -c'
|
|
||||||
else
|
|
||||||
cat >&2 <<-'EOF'
|
|
||||||
Error: this installer needs the ability to run commands as root.
|
|
||||||
We are unable to find either "sudo" or "su" available to make this happen.
|
|
||||||
EOF
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
if is_dry_run; then
|
|
||||||
sh_c="echo"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# perform some very rudimentary platform detection
|
|
||||||
lsb_dist=$( get_distribution )
|
|
||||||
lsb_dist="$(echo "$lsb_dist" | tr '[:upper:]' '[:lower:]')"
|
|
||||||
|
|
||||||
if is_wsl; then
|
|
||||||
echo
|
|
||||||
echo "WSL DETECTED: We recommend using Docker Desktop for Windows."
|
|
||||||
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop/"
|
|
||||||
echo
|
|
||||||
cat >&2 <<-'EOF'
|
|
||||||
|
|
||||||
You may press Ctrl+C now to abort this script.
|
|
||||||
EOF
|
|
||||||
( set -x; sleep 20 )
|
|
||||||
fi
|
|
||||||
|
|
||||||
case "$lsb_dist" in
|
|
||||||
|
|
||||||
ubuntu)
|
|
||||||
if command_exists lsb_release; then
|
|
||||||
dist_version="$(lsb_release --codename | cut -f2)"
|
|
||||||
fi
|
|
||||||
if [ -z "$dist_version" ] && [ -r /etc/lsb-release ]; then
|
|
||||||
dist_version="$(. /etc/lsb-release && echo "$DISTRIB_CODENAME")"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
debian|raspbian)
|
|
||||||
dist_version="$(sed 's/\/.*//' /etc/debian_version | sed 's/\..*//')"
|
|
||||||
case "$dist_version" in
|
|
||||||
12)
|
|
||||||
dist_version="bookworm"
|
|
||||||
;;
|
|
||||||
11)
|
|
||||||
dist_version="bullseye"
|
|
||||||
;;
|
|
||||||
10)
|
|
||||||
dist_version="buster"
|
|
||||||
;;
|
|
||||||
9)
|
|
||||||
dist_version="stretch"
|
|
||||||
;;
|
|
||||||
8)
|
|
||||||
dist_version="jessie"
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
;;
|
|
||||||
|
|
||||||
centos|rhel)
|
|
||||||
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
|
|
||||||
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
*)
|
|
||||||
if command_exists lsb_release; then
|
|
||||||
dist_version="$(lsb_release --release | cut -f2)"
|
|
||||||
fi
|
|
||||||
if [ -z "$dist_version" ] && [ -r /etc/os-release ]; then
|
|
||||||
dist_version="$(. /etc/os-release && echo "$VERSION_ID")"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Check if this is a forked Linux distro
|
|
||||||
check_forked
|
|
||||||
|
|
||||||
# Print deprecation warnings for distro versions that recently reached EOL,
|
|
||||||
# but may still be commonly used (especially LTS versions).
|
|
||||||
case "$lsb_dist.$dist_version" in
|
|
||||||
centos.8|centos.7|rhel.7)
|
|
||||||
deprecation_notice "$lsb_dist" "$dist_version"
|
|
||||||
;;
|
|
||||||
debian.buster|debian.stretch|debian.jessie)
|
|
||||||
deprecation_notice "$lsb_dist" "$dist_version"
|
|
||||||
;;
|
|
||||||
raspbian.buster|raspbian.stretch|raspbian.jessie)
|
|
||||||
deprecation_notice "$lsb_dist" "$dist_version"
|
|
||||||
;;
|
|
||||||
ubuntu.bionic|ubuntu.xenial|ubuntu.trusty)
|
|
||||||
deprecation_notice "$lsb_dist" "$dist_version"
|
|
||||||
;;
|
|
||||||
ubuntu.mantic|ubuntu.lunar|ubuntu.kinetic|ubuntu.impish|ubuntu.hirsute|ubuntu.groovy|ubuntu.eoan|ubuntu.disco|ubuntu.cosmic)
|
|
||||||
deprecation_notice "$lsb_dist" "$dist_version"
|
|
||||||
;;
|
|
||||||
fedora.*)
|
|
||||||
if [ "$dist_version" -lt 40 ]; then
|
|
||||||
deprecation_notice "$lsb_dist" "$dist_version"
|
|
||||||
fi
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
# Run setup for each distro accordingly
|
|
||||||
case "$lsb_dist" in
|
|
||||||
ubuntu|debian|raspbian)
|
|
||||||
pre_reqs="ca-certificates curl"
|
|
||||||
apt_repo="deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] $DOWNLOAD_URL/linux/$lsb_dist $dist_version $CHANNEL"
|
|
||||||
(
|
|
||||||
if ! is_dry_run; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
$sh_c 'apt-get -qq update >/dev/null'
|
|
||||||
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq install $pre_reqs >/dev/null"
|
|
||||||
$sh_c 'install -m 0755 -d /etc/apt/keyrings'
|
|
||||||
$sh_c "curl -fsSL \"$DOWNLOAD_URL/linux/$lsb_dist/gpg\" -o /etc/apt/keyrings/docker.asc"
|
|
||||||
$sh_c "chmod a+r /etc/apt/keyrings/docker.asc"
|
|
||||||
$sh_c "echo \"$apt_repo\" > /etc/apt/sources.list.d/docker.list"
|
|
||||||
$sh_c 'apt-get -qq update >/dev/null'
|
|
||||||
)
|
|
||||||
pkg_version=""
|
|
||||||
if [ -n "$VERSION" ]; then
|
|
||||||
if is_dry_run; then
|
|
||||||
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
|
|
||||||
else
|
|
||||||
# Will work for incomplete versions IE (17.12), but may not actually grab the "latest" if in the test channel
|
|
||||||
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/~ce~.*/g' | sed 's/-/.*/g')"
|
|
||||||
search_command="apt-cache madison docker-ce | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
|
|
||||||
pkg_version="$($sh_c "$search_command")"
|
|
||||||
echo "INFO: Searching repository for VERSION '$VERSION'"
|
|
||||||
echo "INFO: $search_command"
|
|
||||||
if [ -z "$pkg_version" ]; then
|
|
||||||
echo
|
|
||||||
echo "ERROR: '$VERSION' not found amongst apt-cache madison results"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if version_gte "18.09"; then
|
|
||||||
search_command="apt-cache madison docker-ce-cli | grep '$pkg_pattern' | head -1 | awk '{\$1=\$1};1' | cut -d' ' -f 3"
|
|
||||||
echo "INFO: $search_command"
|
|
||||||
cli_pkg_version="=$($sh_c "$search_command")"
|
|
||||||
fi
|
|
||||||
pkg_version="=$pkg_version"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
(
|
|
||||||
pkgs="docker-ce${pkg_version%=}"
|
|
||||||
if version_gte "18.09"; then
|
|
||||||
# older versions didn't ship the cli and containerd as separate packages
|
|
||||||
pkgs="$pkgs docker-ce-cli${cli_pkg_version%=} containerd.io"
|
|
||||||
fi
|
|
||||||
if version_gte "20.10"; then
|
|
||||||
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
|
|
||||||
fi
|
|
||||||
if version_gte "23.0"; then
|
|
||||||
pkgs="$pkgs docker-buildx-plugin"
|
|
||||||
fi
|
|
||||||
if ! is_dry_run; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
$sh_c "DEBIAN_FRONTEND=noninteractive apt-get -y -qq install $pkgs >/dev/null"
|
|
||||||
)
|
|
||||||
echo_docker_as_nonroot
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
centos|fedora|rhel)
|
|
||||||
repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE"
|
|
||||||
(
|
|
||||||
if ! is_dry_run; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
if command_exists dnf5; then
|
|
||||||
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
|
|
||||||
$sh_c "dnf5 config-manager addrepo --overwrite --save-filename=docker-ce.repo --from-repofile='$repo_file_url'"
|
|
||||||
|
|
||||||
if [ "$CHANNEL" != "stable" ]; then
|
|
||||||
$sh_c "dnf5 config-manager setopt \"docker-ce-*.enabled=0\""
|
|
||||||
$sh_c "dnf5 config-manager setopt \"docker-ce-$CHANNEL.enabled=1\""
|
|
||||||
fi
|
|
||||||
$sh_c "dnf makecache"
|
|
||||||
elif command_exists dnf; then
|
|
||||||
$sh_c "dnf -y -q --setopt=install_weak_deps=False install dnf-plugins-core"
|
|
||||||
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
|
|
||||||
$sh_c "dnf config-manager --add-repo $repo_file_url"
|
|
||||||
|
|
||||||
if [ "$CHANNEL" != "stable" ]; then
|
|
||||||
$sh_c "dnf config-manager --set-disabled \"docker-ce-*\""
|
|
||||||
$sh_c "dnf config-manager --set-enabled \"docker-ce-$CHANNEL\""
|
|
||||||
fi
|
|
||||||
$sh_c "dnf makecache"
|
|
||||||
else
|
|
||||||
$sh_c "yum -y -q install yum-utils"
|
|
||||||
$sh_c "rm -f /etc/yum.repos.d/docker-ce.repo /etc/yum.repos.d/docker-ce-staging.repo"
|
|
||||||
$sh_c "yum-config-manager --add-repo $repo_file_url"
|
|
||||||
|
|
||||||
if [ "$CHANNEL" != "stable" ]; then
|
|
||||||
$sh_c "yum-config-manager --disable \"docker-ce-*\""
|
|
||||||
$sh_c "yum-config-manager --enable \"docker-ce-$CHANNEL\""
|
|
||||||
fi
|
|
||||||
$sh_c "yum makecache"
|
|
||||||
fi
|
|
||||||
)
|
|
||||||
pkg_version=""
|
|
||||||
if command_exists dnf; then
|
|
||||||
pkg_manager="dnf"
|
|
||||||
pkg_manager_flags="-y -q --best"
|
|
||||||
else
|
|
||||||
pkg_manager="yum"
|
|
||||||
pkg_manager_flags="-y -q"
|
|
||||||
fi
|
|
||||||
if [ -n "$VERSION" ]; then
|
|
||||||
if is_dry_run; then
|
|
||||||
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
|
|
||||||
else
|
|
||||||
if [ "$lsb_dist" = "fedora" ]; then
|
|
||||||
pkg_suffix="fc$dist_version"
|
|
||||||
else
|
|
||||||
pkg_suffix="el"
|
|
||||||
fi
|
|
||||||
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/\\\\.ce.*/g' | sed 's/-/.*/g').*$pkg_suffix"
|
|
||||||
search_command="$pkg_manager list --showduplicates docker-ce | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
|
|
||||||
pkg_version="$($sh_c "$search_command")"
|
|
||||||
echo "INFO: Searching repository for VERSION '$VERSION'"
|
|
||||||
echo "INFO: $search_command"
|
|
||||||
if [ -z "$pkg_version" ]; then
|
|
||||||
echo
|
|
||||||
echo "ERROR: '$VERSION' not found amongst $pkg_manager list results"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
if version_gte "18.09"; then
|
|
||||||
# older versions don't support a cli package
|
|
||||||
search_command="$pkg_manager list --showduplicates docker-ce-cli | grep '$pkg_pattern' | tail -1 | awk '{print \$2}'"
|
|
||||||
cli_pkg_version="$($sh_c "$search_command" | cut -d':' -f 2)"
|
|
||||||
fi
|
|
||||||
# Cut out the epoch and prefix with a '-'
|
|
||||||
pkg_version="-$(echo "$pkg_version" | cut -d':' -f 2)"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
(
|
|
||||||
pkgs="docker-ce$pkg_version"
|
|
||||||
if version_gte "18.09"; then
|
|
||||||
# older versions didn't ship the cli and containerd as separate packages
|
|
||||||
if [ -n "$cli_pkg_version" ]; then
|
|
||||||
pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io"
|
|
||||||
else
|
|
||||||
pkgs="$pkgs docker-ce-cli containerd.io"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if version_gte "20.10"; then
|
|
||||||
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
|
|
||||||
fi
|
|
||||||
if version_gte "23.0"; then
|
|
||||||
pkgs="$pkgs docker-buildx-plugin"
|
|
||||||
fi
|
|
||||||
if ! is_dry_run; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
$sh_c "$pkg_manager $pkg_manager_flags install $pkgs"
|
|
||||||
)
|
|
||||||
echo_docker_as_nonroot
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
sles)
|
|
||||||
if [ "$(uname -m)" != "s390x" ]; then
|
|
||||||
echo "Packages for SLES are currently only available for s390x"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
repo_file_url="$DOWNLOAD_URL/linux/$lsb_dist/$REPO_FILE"
|
|
||||||
pre_reqs="ca-certificates curl libseccomp2 awk"
|
|
||||||
(
|
|
||||||
if ! is_dry_run; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
$sh_c "zypper install -y $pre_reqs"
|
|
||||||
$sh_c "rm -f /etc/zypp/repos.d/docker-ce-*.repo"
|
|
||||||
$sh_c "zypper addrepo $repo_file_url"
|
|
||||||
|
|
||||||
opensuse_factory_url="https://download.opensuse.org/repositories/security:/SELinux/openSUSE_Factory/"
|
|
||||||
if ! zypper lr -d | grep -q "${opensuse_factory_url}"; then
|
|
||||||
opensuse_repo="${opensuse_factory_url}security:SELinux.repo"
|
|
||||||
if ! is_dry_run; then
|
|
||||||
cat >&2 <<- EOF
|
|
||||||
WARNING!!
|
|
||||||
openSUSE repository ($opensuse_repo) will be enabled now.
|
|
||||||
Do you wish to continue?
|
|
||||||
You may press Ctrl+C now to abort this script.
|
|
||||||
EOF
|
|
||||||
( set -x; sleep 20 )
|
|
||||||
fi
|
|
||||||
$sh_c "zypper addrepo $opensuse_repo"
|
|
||||||
fi
|
|
||||||
$sh_c "zypper --gpg-auto-import-keys refresh"
|
|
||||||
$sh_c "zypper lr -d"
|
|
||||||
)
|
|
||||||
pkg_version=""
|
|
||||||
if [ -n "$VERSION" ]; then
|
|
||||||
if is_dry_run; then
|
|
||||||
echo "# WARNING: VERSION pinning is not supported in DRY_RUN"
|
|
||||||
else
|
|
||||||
pkg_pattern="$(echo "$VERSION" | sed 's/-ce-/\\\\.ce.*/g' | sed 's/-/.*/g')"
|
|
||||||
search_command="zypper search -s --match-exact 'docker-ce' | grep '$pkg_pattern' | tail -1 | awk '{print \$6}'"
|
|
||||||
pkg_version="$($sh_c "$search_command")"
|
|
||||||
echo "INFO: Searching repository for VERSION '$VERSION'"
|
|
||||||
echo "INFO: $search_command"
|
|
||||||
if [ -z "$pkg_version" ]; then
|
|
||||||
echo
|
|
||||||
echo "ERROR: '$VERSION' not found amongst zypper list results"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
search_command="zypper search -s --match-exact 'docker-ce-cli' | grep '$pkg_pattern' | tail -1 | awk '{print \$6}'"
|
|
||||||
# It's okay for cli_pkg_version to be blank, since older versions don't support a cli package
|
|
||||||
cli_pkg_version="$($sh_c "$search_command")"
|
|
||||||
pkg_version="-$pkg_version"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
(
|
|
||||||
pkgs="docker-ce$pkg_version"
|
|
||||||
if version_gte "18.09"; then
|
|
||||||
if [ -n "$cli_pkg_version" ]; then
|
|
||||||
# older versions didn't ship the cli and containerd as separate packages
|
|
||||||
pkgs="$pkgs docker-ce-cli-$cli_pkg_version containerd.io"
|
|
||||||
else
|
|
||||||
pkgs="$pkgs docker-ce-cli containerd.io"
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
if version_gte "20.10"; then
|
|
||||||
pkgs="$pkgs docker-compose-plugin docker-ce-rootless-extras$pkg_version"
|
|
||||||
fi
|
|
||||||
if version_gte "23.0"; then
|
|
||||||
pkgs="$pkgs docker-buildx-plugin"
|
|
||||||
fi
|
|
||||||
if ! is_dry_run; then
|
|
||||||
set -x
|
|
||||||
fi
|
|
||||||
$sh_c "zypper -q install -y $pkgs"
|
|
||||||
)
|
|
||||||
echo_docker_as_nonroot
|
|
||||||
exit 0
|
|
||||||
;;
|
|
||||||
*)
|
|
||||||
if [ -z "$lsb_dist" ]; then
|
|
||||||
if is_darwin; then
|
|
||||||
echo
|
|
||||||
echo "ERROR: Unsupported operating system 'macOS'"
|
|
||||||
echo "Please get Docker Desktop from https://www.docker.com/products/docker-desktop"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo
|
|
||||||
echo "ERROR: Unsupported distribution '$lsb_dist'"
|
|
||||||
echo
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
|
|
||||||
# wrapped up in a function so that we have some protection against only getting
|
|
||||||
# half the file during "curl | sh"
|
|
||||||
do_install
|
|
||||||
```
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#comand #notes #linux
|
|
||||||
```
|
|
||||||
sudo hostnamectl set-hostname linuxconfig
|
|
||||||
```
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
#comand #cloud-init #note
|
|
||||||
|
|
||||||
```
|
|
||||||
echo (пароль) | mkpasswd -m sha-512 -s
|
|
||||||
```
|
|
||||||
|
|
||||||
echo lGoI9ltSpDJDL91zYaEy | mkpasswd -m sha-512 -s
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
#note #imap #mail
|
|
||||||
Порт для подключения по протоколу IMAP с шифрованием TLS 993 Без
|
|
||||||
##### SMTP
|
|
||||||
- 25/tcp SMTP (стандартный порт)
|
|
||||||
- 465/tcp SMTPS (устаревший)
|
|
||||||
- 587/tcp submission (порт для обслуживания клиентов)
|
|
||||||
##### POP3
|
|
||||||
- 110/tcp POP3 (стандартный порт)
|
|
||||||
- 995/tcp POP3S (порт с предварительной установкой SSL/TLS соединения)
|
|
||||||
##### IMAP
|
|
||||||
- 143/tcp IMAP (стандартный порт)
|
|
||||||
- 993/tcp IMAPS (порт с предварительной установкой SSL/TLS соединения)
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
#reglament
|
|
||||||
Для введения нового оборудования в эксплуатацию требуется:
|
|
||||||
1) Заявка
|
|
||||||
2) Оборудование
|
|
||||||
3) Предварительные настройки
|
|
||||||
4) Монтаж
|
|
||||||
5) Коммутация
|
|
||||||
6) Введение в работу
|
|
||||||
7) Отчет о выполненных работах
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
#reglament
|
|
||||||
Для введения нового оборудования в эксплуатацию требуется:
|
|
||||||
1) Заявка
|
|
||||||
2) Оборудование
|
|
||||||
3) Предварительные настройки
|
|
||||||
4) Монтаж
|
|
||||||
5) Коммутация
|
|
||||||
6) Введение в работу
|
|
||||||
7) Отчет о выполненных работах
|
|
||||||
|
|
||||||
## Заявка
|
|
||||||
Перед тем как приступать к работе требуется принять заявку на введение в эксплуатацию оборудования. Если такой нет требуется ее создать.
|
|
||||||
В заявке должно быть указано:
|
|
||||||
- Производитель оборудования и его модель
|
|
||||||
- Серийный номер
|
|
||||||
- Хостнейм и ip адрес
|
|
||||||
- Куда монтируется Стойка Юнит
|
|
||||||
- Куда будет подключено Стойка Юнит Порт (линки для управления, uplink)
|
|
||||||
- Назначение оборудования
|
|
||||||
- Ответственный за выполнение работ (Если разные задачи выполняют разные они все должны быть указаны в исполнителях)
|
|
||||||
|
|
||||||
## Оборудование
|
|
||||||
Для понимания какие настройки делать нужно понимать что это за оборудование и как оно будет использоваться.
|
|
||||||
Для этого следует ответить на вопросы:
|
|
||||||
- Сюда будут подключаться конечные узлы?
|
|
||||||
- Какой трафик будет идти через оборудование?
|
|
||||||
- На каком уровне будет работать оборудование?
|
|
||||||
- Есть ли отдельный линк управления?
|
|
||||||
Если подключается конечное оборудование, то скорее всего магистральный трафик идет только по аплинку, но если подключены сервера, то возможно деление трафика в канале на вланы или оверлейный трафик. Это относится и как L2 фабрике, так и к L3.
|
|
||||||
Если линк не предусмотрен то оно будет управляться через uplink.
|
|
||||||
Это дает нам понять какие настройки мы сделаем.
|
|
||||||
|
|
||||||
## Предварительные настройки
|
|
||||||
Прежде всего на оборудовании требуется настроить доступ. Без доступа мы управление идет через консоль, что мешает делать дальнейшие настройки.
|
|
||||||
Для получения доступа требуется настроить такие параметры как:
|
|
||||||
- Хостнейм (из заявки)
|
|
||||||
- Время
|
|
||||||
- Юзер, пароль и привелегии
|
|
||||||
- Разрешить доступ по SSH (если надо ACL)
|
|
||||||
- Создать влан через который будет осуществляться доступ
|
|
||||||
- Настроить порты во влан доступа и включить их
|
|
||||||
- Если управление идет через аплинк, то настроить uplink. А именно агрегацию или STP, транковый канал (канал доступа), назначить влан на uplink
|
|
||||||
Как только у нас появился удаленный доступ, то предварительные настройки завершены.
|
|
||||||
Все дальнейшие настройки являются уже индивидуальными и зависят от задачи эксплуатации.
|
|
||||||
|
|
||||||
## Монтаж
|
|
||||||
Монтаж производится после настройки удаленного доступа и uplink.
|
|
||||||
Размещение оборудования мы берем из заявки.
|
|
||||||
В монтаж входит прокладка кабеля между оборудованием. Откуда и куда будет подключено тоже берем из заявки.
|
|
||||||
Кабель тянем либо сами, либо заводим на СКС, если своих сил не достаточно.
|
|
||||||
На СКС заводятся межзальная, и клиентская прокладка линков. В остальных случаях ( внутри стойки и между стоек) можно протянуть кабель своими силами.
|
|
||||||
Тип и длинна кабеля завися от подключения и расстояния между устройствами. Это понятно из заявки.
|
|
||||||
Перед прокладкой кабеля должны быть промаркированы в соответствии с регламентом маркировки
|
|
||||||
[[Регламент маркировки]]
|
|
||||||
|
|
||||||
## Коммутация
|
|
||||||
Коммутация выполняется после прокладки всех линков и предварительных настроек. Если оборудование не настроено, то сработает защита на другом конце и порты не включатся. Кабеля заводим через специальные отверстия в стойках и включаем в нужные порты. (Какие именно указано в зявке)
|
|
||||||
После подключения кабелей между узлами сети можно выполнять настройки для эксплуатации.
|
|
||||||
Не забываем про STP и если между узлами образовались петли их надо разрешить.
|
|
||||||
|
|
||||||
## Введение в работу
|
|
||||||
Производим настройки портов для пользования оборудованием. Из заявки нам известно назначение оборудования и мы можем приступить к его эксплуатации.
|
|
||||||
В данном пункте не будут описываться параметры и настройки, так как все зависит от назначения.
|
|
||||||
Не забываем сохраняться и указывать дескрипшены и подсказки.
|
|
||||||
Так же лучше всего вести лог действий, которые выполняются
|
|
||||||
После настройки обязательно тестируем
|
|
||||||
## Отчет о выполнении работ
|
|
||||||
В отчете о выполнении требуется указать какие работы и настройки были сделаны, сколько времени потрачено и кто что сделал
|
|
||||||
Данную информацию заполняет указанный ответственный и пинает остальных для указания нужной информации.
|
|
||||||
Если в исходных данных были изменения они обязательно должны быть отражены в заявке.
|
|
||||||
@@ -1,420 +0,0 @@
|
|||||||
Оглавление
|
|
||||||
|
|
||||||
[Инструкция по введению в эксплуатацию серверов. 1](#_Toc182279328)
|
|
||||||
|
|
||||||
[Часть 1. Приемка сервера и коммутация сервера. 1](#_Toc182279329)
|
|
||||||
|
|
||||||
[Этап 1: Приемка сервера. 1](#_Toc182279330)
|
|
||||||
|
|
||||||
[Этап 2. Коммутация и включение сервера. 1](#_Toc182279331)
|
|
||||||
|
|
||||||
[Часть 2. BIOS, порт удаленного управления, мониторинг 2](#_Toc182279332)
|
|
||||||
|
|
||||||
[Этап 1. BIOS. 2](#_Toc182279333)
|
|
||||||
|
|
||||||
[Этап 2. Порт удаленного управления. 3](#_Toc182279334)
|
|
||||||
|
|
||||||
[Этап 3. Мониторинг 4](#_Toc182279335)
|
|
||||||
|
|
||||||
[Установка ОС и подготовка к тестам.. 4](#_Toc182279336)
|
|
||||||
|
|
||||||
[Нагрузочное тестирование компонентов. 4](#_Toc182279337)
|
|
||||||
|
|
||||||
[Тестирование отказоустойчивости компонентов. 5](#_Toc182279338)
|
|
||||||
|
|
||||||
[Отключение и извлечение блока питания: имитация отказа блока питания. 5](#_Toc182279339)
|
|
||||||
|
|
||||||
[Извлечение накопителей: имитация отказа накопителей. 6](#_Toc182279340)
|
|
||||||
|
|
||||||
[Тестирование сетевых интерфейсов и пропускной способности. 8](#_Toc182279341)
|
|
||||||
|
|
||||||
# Инструкция по введению в эксплуатацию серверов
|
|
||||||
|
|
||||||
Для введения серверов в эксплуатацию и использования их в продакшене требуется выполнить обязательный порядок действий. Эти шаги являются обязательными и требуют внимания.
|
|
||||||
|
|
||||||
Для каждого сервера требуется заполнить чек-лист по пунктам инструкции. Сервер помечается как испорченный, если хотя бы один из критичных критериев не проходит проверку
|
|
||||||
|
|
||||||
## Часть 1. Приемка сервера и коммутация сервера
|
|
||||||
|
|
||||||
Приемка сервера является критичным критерием.
|
|
||||||
При поступлении сервера требуется провести:
|
|
||||||
|
|
||||||
### Этап 1: Приемка сервера
|
|
||||||
|
|
||||||
1. Проведение внешнего осмотра:
|
|
||||||
|
|
||||||
1.1. Осмотрите корпус сервера на наличие видимых повреждений (вмятин, царапин, сколов).
|
|
||||||
|
|
||||||
1.2. Проверьте кабели, блоки питания, кнопки и разъемы на наличие повреждений и целостность креплений.
|
|
||||||
|
|
||||||
1.3. Убедитесь, что на корпусе и внутренних компонентах нет признаков ударов, трещин или коррозии.
|
|
||||||
|
|
||||||
2. Проверка внутренних компонентов:
|
|
||||||
|
|
||||||
2.1. Откройте корпус и осмотрите плату, процессоры, модули памяти, накопители и кабели.
|
|
||||||
|
|
||||||
2.2. Проверьте крепление всех модулей, особенно оперативной памяти и процессора: они должны быть надежно зафиксированы.
|
|
||||||
|
|
||||||
2.3. Проверьте вентиляторы – они должны быть установлены плотно и вращаться без заеданий.
|
|
||||||
|
|
||||||
2.4. Если сервер прибыл в разобранном виде компоненты проверяются ДО и ПОСЛЕ установки в сервер.
|
|
||||||
|
|
||||||
3. Устранение неполадок (если обнаружены):
|
|
||||||
|
|
||||||
3.1. В случае обнаружения повреждений, зафиксируйте их и сделайте фотографии.
|
|
||||||
|
|
||||||
3.2. Оповестите руководство или службу поддержки, если повреждения серьезные или присутствуют недостающие компоненты.
|
|
||||||
|
|
||||||
4. Сборка сервера.
|
|
||||||
|
|
||||||
4.1. Сборка сервера осуществляется, если сервер поступил в разобранном виде.
|
|
||||||
|
|
||||||
4.2. Компоненты сервера являются модульными и подключаются согласно документации.
|
|
||||||
|
|
||||||
4.3. Сборка осуществляется аккуратно, без применения силы.
|
|
||||||
|
|
||||||
Этап 2. Коммутация и включение сервера.
|
|
||||||
ЗАПРЕЩАЕТСЯ УСТАНАВЛИВАТЬ СЕРВЕР В СТОЙКУ С ПРОДАКШН СЕРВЕРАМИ.
|
|
||||||
|
|
||||||
После осмотра и сборки сервера необходимо установить его в стойку и скоммутировать для тестирования.
|
|
||||||
|
|
||||||
1. Установка сервера в стойку:
|
|
||||||
|
|
||||||
1.1. Установите сервер в стойку, используя рекомендованные производителем монтажные крепления или шасси, чтобы обеспечить надежность крепления.
|
|
||||||
|
|
||||||
2. Подключение сетевых и периферийных устройств:
|
|
||||||
|
|
||||||
2.1. Подключите кабели Ethernet к сетевым портам сервера. Обычно подключают два порта: основной и резервный.
|
|
||||||
|
|
||||||
2.2. Подключите кабель к порту удаленного управления (ILO/DRAC/IPMI) для удаленного контроля и диагностики.
|
|
||||||
|
|
||||||
2.3. Подключите PSU используя кабели питания, подключив их к PDU (подключение производится симметрично, подключение PSU производится в разные PDU – один основной луч, другой резервный).
|
|
||||||
|
|
||||||
2.4. Подсоедините монитор, клавиатуру и мышь к серверу для доступа к начальной настройке.
|
|
||||||
|
|
||||||
3. Включение сервера
|
|
||||||
|
|
||||||
3.1. Произвести включение сервера нажатием на кнопку на передней панели
|
|
||||||
|
|
||||||
3.2. Проверьте, что все индикаторы на компонентах (диски, питание, сеть) работают нормально.
|
|
||||||
|
|
||||||
3.3. Ожидайте завершения POST (Power-On Self Test). Если система не проходит POST, зафиксируйте код ошибки и устраните причину.
|
|
||||||
|
|
||||||
3.4. Выключаем сервер
|
|
||||||
|
|
||||||
Часть 2. BIOS, порт удаленного управления, мониторинг
|
|
||||||
|
|
||||||
### Этап 1. BIOS
|
|
||||||
|
|
||||||
1. Вход в BIOS:
|
|
||||||
|
|
||||||
1.1. Нажмите необходимую клавишу (обычно F2, F10, Delete) при загрузке сервера, чтобы войти в BIOS.
|
|
||||||
|
|
||||||
2. Проверка конфигурации:
|
|
||||||
|
|
||||||
2.1. Убедитесь, что BIOS корректно распознает процессоры, модули памяти и диски.
|
|
||||||
|
|
||||||
2.2. Проверьте наличие и корректность кода версии BIOS и BMC.
|
|
||||||
|
|
||||||
3. Обновление прошивок:
|
|
||||||
|
|
||||||
3.1. Если необходимо, обновите BIOS и прошивки BMC. Используйте только версии, рекомендованные производителем.
|
|
||||||
|
|
||||||
3.2. Проверьте наличие обновлений для RAID-контроллера и PCIe-карт. Убедитесь, что обновление завершилось без ошибок.
|
|
||||||
|
|
||||||
4. Настройка базовых параметров:
|
|
||||||
|
|
||||||
4.1. Настройте системное время и дату для синхронизации с сервером мониторинга. Если для времени используется NTP сервер, то по возможности использовать его.
|
|
||||||
|
|
||||||
4.2. Включите опции виртуализации и Hyper-Threading (если необходимо для задач сервера).
|
|
||||||
|
|
||||||
4.3. Включаем параметр для сервера включаться всегда, когда есть питание.
|
|
||||||
|
|
||||||
4.4. Настройте последовательность загрузки, указав на USB в приоритете для загрузки установщика тестовой ОС.
|
|
||||||
|
|
||||||
4.5. Настройте сетевые параметры (ip, маска, шлюз) для порта удаленного управления (ILO/DRAC/IPMI) и, по возможности, учетную запись администратора (логин и пароль)
|
|
||||||
|
|
||||||
5. Настройка загрузки в режиме UEFI:
|
|
||||||
|
|
||||||
5.1. Включите режим загрузки UEFI, если система поддерживает его и планируется установка UEFI-совместимой ОС.
|
|
||||||
|
|
||||||
5.2. Если совместимость с Legacy обязательна для некоторых компонентов, включите режим Dual Boot или Legacy.
|
|
||||||
|
|
||||||
### Этап 2. Порт удаленного управления.
|
|
||||||
|
|
||||||
1. Подключение к порту управления (ILO, DRAC, IPMI):
|
|
||||||
|
|
||||||
1.1. Проверьте сетевую связность с портом управления.
|
|
||||||
|
|
||||||
1.2. Проверьте что по указанному адресу порта управления есть доступ через браузер и ssh.
|
|
||||||
|
|
||||||
1.3. Войдите в систему удаленного управления (ILO, DRAC, IPMI) через браузер и ssh с настроенными ранее логином и паролем. В иных случаях используйте логин и пароль указанные производителем.
|
|
||||||
|
|
||||||
2. Проверка аппаратной конфигурации и состояния компонентов:
|
|
||||||
|
|
||||||
2.1. Убедитесь, что на экране управления отображаются корректные сведения о сервере: серийный номер, модель, версия микрокода BIOS и BMC.
|
|
||||||
|
|
||||||
2.2. Проверьте статус установленных компонентов (процессор, ОЗУ, накопители, модули PCIe).
|
|
||||||
|
|
||||||
2.3. Проверьте показания датчиков температуры, напряжения, частоты вращения вентиляторов. Если есть возможность настройте пороговые значения для данных датчиков.
|
|
||||||
|
|
||||||
2.4. Просмотрите журналы событий BMC, чтобы убедиться в отсутствии ошибок, связанных с оборудованием.
|
|
||||||
|
|
||||||
3. Проверка функциональных возможностей системы удаленного управления (ILO, DRAC, IPMI):
|
|
||||||
|
|
||||||
3.1. Протестируйте функцию управления включением, выключением, перезагрузкой сервера
|
|
||||||
|
|
||||||
3.2. Создание новых пользователей и групп с определенными правами доступа (администратор, оператор, только чтение).
|
|
||||||
|
|
||||||
3.3. Использование HTML KVM-консоли или Java-консоли для установки и управления ОС.
|
|
||||||
|
|
||||||
3.4. Обновление версии микрокода BMC без влияния на работу ОС.
|
|
||||||
|
|
||||||
3.5. Генерация нового или добавление существующего SSL-сертификата.
|
|
||||||
|
|
||||||
3.6. Экспорт и импорт конфигурации интерфейса управления.
|
|
||||||
|
|
||||||
3.7. Сбросом порта управления до заводских настроек;
|
|
||||||
|
|
||||||
3.8. Отправка сообщений о событиях с использованием SMTP, syslog и SNMP Trap.
|
|
||||||
|
|
||||||
Дополнительные функциональные возможности можно узнать из документации к серверу от производителя.
|
|
||||||
|
|
||||||
### Этап 3. Мониторинг
|
|
||||||
|
|
||||||
Перед проведением тестов компонентов нам требуется настроить мониторинг сервера. От мониторинга мы получим статистку и проверим работоспособность мониторинга со стороны сервера.
|
|
||||||
|
|
||||||
1. Импорт и настройка MIB-файлов:
|
|
||||||
|
|
||||||
1.1. Загрузите и импортируйте MIB-файлы производителя в систему мониторинга (например, Zabbix, Nagios).
|
|
||||||
|
|
||||||
2. Создание шаблонов мониторинга:
|
|
||||||
|
|
||||||
2.1. Настройте шаблоны для мониторинга температур, энергопотребления, состояния компонентов (процессор, ОЗУ, диски).
|
|
||||||
|
|
||||||
3. Настройка оповещений:
|
|
||||||
|
|
||||||
3.1. Задайте пороговые значения для предупреждений и оповещений.
|
|
||||||
|
|
||||||
3.2. Убедитесь, что уведомления отправляются при перегрузке процессора, перегреве или отказе дисков.
|
|
||||||
|
|
||||||
## Часть 3. Нагрузочное тестирование.
|
|
||||||
|
|
||||||
### Этап 1. Установка ОС и подготовка к тестам
|
|
||||||
|
|
||||||
Закончив с предварительными настройками, мы можем переходить к установке ОС и запуске нагрузочного тестирования сервера.
|
|
||||||
|
|
||||||
1. Подключите носитель с установочным образом к серверу
|
|
||||||
|
|
||||||
2. Создайте RAID массив (если сервер содержит RAID контроллер)
|
|
||||||
|
|
||||||
2.1. Уровень RAID выбирается в зависимости от количества дисков и задач сервера
|
|
||||||
|
|
||||||
2.2. В основном используются уровни RAID 1 или 10, но также допускается уровень RAID5
|
|
||||||
|
|
||||||
3. Установка ОС
|
|
||||||
|
|
||||||
3.1. Для установки ОС используется дистрибутив Linux, который поддерживает утилиты из списка далее.
|
|
||||||
|
|
||||||
3.2. В основном используются дистрибутивы Ubuntu и Debian
|
|
||||||
|
|
||||||
3.3. Произведите установку ОС. При установке используйте LVM и выделите под корневой раздел все доступное пространство. Создайте файловую систему.
|
|
||||||
|
|
||||||
3.4. Настройте тестовую сеть
|
|
||||||
|
|
||||||
4. После установки
|
|
||||||
|
|
||||||
4.1. Обновите список пакетов и скачайте, и установите обновления системы.
|
|
||||||
|
|
||||||
4.2. Установите необходимые для тестирования утилиты:
|
|
||||||
|
|
||||||
· **badblocks** – для проверки дисков на наличие поврежденных секторов.
|
|
||||||
|
|
||||||
· **stress-ng** – для создания нагрузки на процессор и оперативную память.
|
|
||||||
|
|
||||||
· **fio** – для тестирования производительности дисковой подсистемы.
|
|
||||||
|
|
||||||
· **dd** – для измерения скорости записи данных на диск.
|
|
||||||
|
|
||||||
· **htop** – для мониторинга загрузки процессора, памяти и других системных ресурсов в реальном времени.
|
|
||||||
|
|
||||||
· **smartmontools** (включает smartctl) – для проверки состояния жестких дисков и SSD, поддержки S.M.A.R.T.
|
|
||||||
|
|
||||||
· **iperf****3** – Для тестирования пропускной способности сети
|
|
||||||
|
|
||||||
## Нагрузочное тестирование компонентов
|
|
||||||
|
|
||||||
После всех подготовок можно приступать к тестированию.
|
|
||||||
|
|
||||||
1) Процессор, память и температура.
|
|
||||||
|
|
||||||
Для проверки работы процессора, оперативной памяти и охлаждения запускаем стресс тест через утилиту stress-ng. Данный тест загружает каждый поток процессора на максимум и 98% оперативной памяти. Процент памяти надо указывать такой, чтобы осталось памяти для работы операционной системы. Иначе операционная система отключит выполнение тестов. Во время теста следим за ОС и сервером через утилиту htop. Смотрим что бы система не зависала и утилизировала указанные ресурсы. Сервер все 2 часа должен проработать без перезагрузок. Так же наблюдаем за сервером через менджмент порт. Смотрим температуру системы и отчеты в журналах (нагрев, перезагрузки и тд). Записываем показатель bogo ops/s (real time) и температуры.
|
|
||||||
|
|
||||||
**stress-ng --cpu 0 --cache 0 -m 0 --vm-bytes (объем RAM в сервере в %) --io 0 --hdd 0 -t (время теста) s --metrics-brief --tz**
|
|
||||||
|
|
||||||
2) Дисковая подсистема.
|
|
||||||
|
|
||||||
Перед запуском тестов проверим здоровье дисков. Для этого используем утилиту smartmontools
|
|
||||||
|
|
||||||
Так как диски находятся в RAID, то наша система видит его как логический диск. Для того что бы определить физические диски просканируем систему.
|
|
||||||
|
|
||||||
**smartctl --scan**
|
|
||||||
|
|
||||||
Данная команда отразит рейд контроллер и номер канала. Эти данные мы используем в проверке
|
|
||||||
|
|
||||||
**smartctl -****H -****d** **megaraid,5 (рейд контроллер и номер канала из сканирования) /****dev/****sda**
|
|
||||||
|
|
||||||
Вывод будет формата ок или не ок.
|
|
||||||
|
|
||||||
Для тестов дисковой подсистемы мы используем утилиты badblock, fio, dd
|
|
||||||
badblock проверяет блоки жесткого диска.
|
|
||||||
**badblocks -v****s /dev/sda (указываем диск или раздел который хотим протестировать)**
|
|
||||||
|
|
||||||
После проверки блоков если все блоки целые проверяем скорость чтения/записи. Пишем временный файл, состоящий из блоков размером в 1m количеством 40960 скидывая кэш. Скорость записи должна быть большой так как файл сначала пишется в кэш потом на диск
|
|
||||||
|
|
||||||
**dd status=progress if=/dev/zero of=/tmp/tempfile bs=1M count=40960 oflag=sync**
|
|
||||||
|
|
||||||
**sync; dd if=/dev/zero of=/tmp/tempfile bs=1M count=1024; sync**
|
|
||||||
|
|
||||||
Прогоняем данный тест 10 раз. Скорость не должна опускаться меньше 90 MB/s.
|
|
||||||
|
|
||||||
## Тестирование отказоустойчивости компонентов
|
|
||||||
|
|
||||||
После нагрузочного тестирования требуется протестировать отработку отказа компонентов с горячей заменой. А именно блоки питания и диски в RAID. Для данного тестирования мы поочередно отключаем компоненты, а потом возвращаем обратно. Во время тестов смотрим на работоспособность системы и отчеты в журнале менеджмент порта.
|
|
||||||
|
|
||||||
## Отключение и извлечение блока питания: имитация отказа блока питания
|
|
||||||
|
|
||||||
Этапы:
|
|
||||||
|
|
||||||
1. Перед проведением тестирования для получения эталонных результатов запускаем утилиту Stress-ng на 2 часа с параметрами, которые были описаны в разделе «Нагрузочное тестирование процессора». Фиксируем результаты для последующего сравнения.
|
|
||||||
|
|
||||||
2. Для создания нагрузки на сервер при выполнении тестирования повторно запускаем утилиту Stress-ng на 2 часа с параметрами, описанными в разделе «Утилита Stress-ng».
|
|
||||||
|
|
||||||
3. От блока питания PSU1 отключаем кабель питания и наблюдаем за BMC сервера с использованием IPMI на предмет появления уведомлений об ошибках и создания записей в системном журнале.
|
|
||||||
|
|
||||||
4. Блок питания PSU1 извлекаем из сервера и наблюдаем за BMC сервера, как описано в пункте 3.
|
|
||||||
|
|
||||||
5. По истечении 2 часов работы сервера под нагрузкой на одном блоке питания PSU2 фиксируем полученные результаты работы утилиты Stress-ng, чтобы сравнить с результатами, полученными при выполнении пункта 1.
|
|
||||||
|
|
||||||
6. Устанавливаем обратно блок питания PSU1. Наблюдаем за BMC сервера, как описано в пункте 3.
|
|
||||||
|
|
||||||
7. К блоку питания PSU1 подключаем кабель питания и наблюдаем за BMC сервера, как описано в пункте 3.
|
|
||||||
|
|
||||||
8. Повторно выполняем пункты со 2 по 7 включительно для блока питания PSU2.
|
|
||||||
|
|
||||||
Критерии успешного прохождения теста
|
|
||||||
|
|
||||||
· Во время работы под нагрузкой сервер доступен по ssh.
|
|
||||||
|
|
||||||
· При отключении от блока питания в BMC появилось уведомление, в системном журнале создалась запись о событии, датчики зафиксировали отсутствие напряжения на блоке питания.
|
|
||||||
|
|
||||||
· При извлечении блока питания из сервера в BMC создалось уведомление, определяется только один блок питания.
|
|
||||||
|
|
||||||
· При возврате блока питания в сервер и подключении кабеля питания в BMC определяются все блоки питания, все датчики с нормальными значениями, в системном журнале создалась запись о наличии напряжения на блоке питания.
|
|
||||||
|
|
||||||
· Результаты утилиты Stress-ng при работе сервера под нагрузкой и на одном блоке питания не отличаются в меньшую сторону больше чем на 10% по сравнению с результатами, полученными при работе сервера под нагрузкой на двух блоках питания.
|
|
||||||
|
|
||||||
## Извлечение накопителей: имитация отказа накопителей
|
|
||||||
|
|
||||||
Этапы
|
|
||||||
|
|
||||||
На свободных накопителях в сервере создаем дисковое хранилище с уровнем резервирования RAID1, RAID6 или RAID10 через BMC, BIOS или с использованием, например, утилиты StorCLI. Тестирование проводим для каждого уровня резервирования дискового хранилища.
|
|
||||||
|
|
||||||
При возможности один из свободных накопителей настраиваем как резервный (Global Hot Spare). Зависит от количества дисков и уровня RAID
|
|
||||||
|
|
||||||
Для дискового хранилища без файловой системы
|
|
||||||
|
|
||||||
1. Запускаем утилиту FIO с профилем нагрузки: 40% случайных операций записи и 60% случайных операций чтения блоком 8 КБ, 16 потоков. Фиксируем производительность в IOPS на чтение и запись.
|
|
||||||
|
|
||||||
2. Извлекаем из сервера один из накопителей. До начала перестроения дискового хранилища снова фиксируем производительность в IOPS на чтение и запись. Наблюдаем за BMC сервера на предмет появления уведомлений и создания записей в системном журнале.
|
|
||||||
|
|
||||||
3. Контролируем статус и процесс перестроения (rebuild) дискового хранилища, если есть свободный резервный накопитель в сервере (Global Hot Spare). Фиксируем производительность в IOPS на чтение и запись во время перестроения дискового хранилища.
|
|
||||||
|
|
||||||
4. При использовании дискового хранилища с уровнем резервирования RAID6 или RAID10 извлекаем второй накопитель из сервера. Фиксируем производительность в IOPS на чтение и запись.
|
|
||||||
|
|
||||||
5. По завершении перестроения дискового хранилища все ранее извлеченные накопители устанавливаем обратно. Отслеживаем статус всех накопителей, а также наблюдаем за BMC сервера, как описано в пункте 2.
|
|
||||||
|
|
||||||
6. При необходимости через BMC или с помощью утилиты StorCLI в ОС удаляем стороннюю конфигурацию на накопителях и меняем их статусы.
|
|
||||||
|
|
||||||
7. По завершении второго перестроения дискового хранилища отслеживаем статус и состав дискового хранилища, а также статус самих накопителей.
|
|
||||||
|
|
||||||
Для дискового хранилища с файловой системой:
|
|
||||||
|
|
||||||
1. На дисковом хранилище создаем один раздел максимального размера и файловую систему ext4, которую подключаем к директории test1..
|
|
||||||
|
|
||||||
2. Запускаем копирование большого файла (несколько десятков ГБ) в директорию test1.
|
|
||||||
|
|
||||||
3. Извлекаем из сервера один из накопителей. Отслеживаем статус дискового хранилища и наблюдаем за BMC сервера на предмет появления уведомлений и создания записей в системном журнале.
|
|
||||||
|
|
||||||
4. Контролируем процесс перестроения дискового хранилища, если есть свободный накопитель в сервере (Global Hot Spare), и отслеживаем скорость копирования файла.
|
|
||||||
|
|
||||||
5. Если используем дисковое хранилище с уровнем резервирования RAID6 или RAID10, извлекаем второй накопитель из сервера.
|
|
||||||
|
|
||||||
6. После перестроения дискового хранилища и окончания копирования файла устанавливаем накопители обратно. Отслеживаем статус всех накопителей, а также наблюдаем за BMC сервера, как описано в пункте 3.
|
|
||||||
|
|
||||||
7. Вычисляем и сравниваем контрольные суммы файла-источника и скопированного файла.
|
|
||||||
|
|
||||||
8. При необходимости через BMC или с помощью утилиты StorCLI в ОС удаляем стороннюю конфигурацию на установленных накопителях с последующим изменением статусов установленных накопителей.
|
|
||||||
|
|
||||||
9. После второго перестроения дискового хранилища отслеживаем статус и состав дискового хранилища, а также статус самих накопителей.
|
|
||||||
|
|
||||||
Критерии выполнения теста
|
|
||||||
|
|
||||||
Дисковое хранилище без файловой системы
|
|
||||||
|
|
||||||
· Работа утилиты FIO не прерывалась во время тестирования, и дисковое хранилище было постоянно доступно на чтение и запись.
|
|
||||||
|
|
||||||
· Сработала автоматическая замена извлеченного накопителя на резервный (Global Hot Spare).
|
|
||||||
|
|
||||||
· Дисковое хранилище успешно выполнило все перестроения.
|
|
||||||
|
|
||||||
· Возврат к начальной конфигурации дискового хранилища (состав и статус накопителей) происходит без перезагрузки сервера.
|
|
||||||
|
|
||||||
Дисковое хранилище с файловой системой
|
|
||||||
|
|
||||||
· Файловая система на дисковом хранилище была доступна на протяжении всего времени тестирования.
|
|
||||||
|
|
||||||
· Копирование файла завершилось без прерываний.
|
|
||||||
|
|
||||||
· Сработала автоматическая замена извлеченного накопителя на резервный (Global Hot Spare).
|
|
||||||
|
|
||||||
· Дисковое хранилище успешно выполнило все перестроения.
|
|
||||||
|
|
||||||
· Контрольные суммы файла-источника и скопированного файла остались одинаковыми.
|
|
||||||
|
|
||||||
· Конфигурация дискового хранилища (состав и статус накопителей) вернулась в начальное состояние без перезагрузки сервера.
|
|
||||||
|
|
||||||
## Тестирование сетевых интерфейсов и пропускной способности
|
|
||||||
|
|
||||||
Для тестирования сетевых интерфейсов на потребуется приемник трафика. Приемник должен обладать достаточной пропускной способностью и иметь возможность запускать утилиту iperf3. То есть для интерфейса скоростью 10 Гбит\с у приемника должен быть такой же по скорости интерфейс.
|
|
||||||
|
|
||||||
При подключении интерфейса должна загораться индикация на нем. На интерфейсах и приемнике настраиваем ip адреса. Тестируемый интерфейс и приемник должны обладать сетевой связностью. Пропингуем интерфейс с приёмника. Он должен быть доступен и передавать пакеты без потерь.
|
|
||||||
|
|
||||||
|
|
||||||
Для тестирования пропускной способности сети воспользуемся утилитой iperf3.
|
|
||||||
|
|
||||||
На приемнике запускаем утилиту iperf3 –s
|
|
||||||
|
|
||||||
-s Запуск в режиме сервера. В данном режиме машина принимает трафик по порту 5201
|
|
||||||
|
|
||||||
На тестируемом сервере так же запускаем утилиту iperf3
|
|
||||||
|
|
||||||
iperf3 -c (ip адрес приемника) -t 1800 -f g -P 20
|
|
||||||
|
|
||||||
-с Запуск в режиме клиента с указанием ip адреса сервера
|
|
||||||
|
|
||||||
-t Время работы утилиты
|
|
||||||
|
|
||||||
-f формат вывода пропускной способности k, m, g килобиты, мегабиты, гигабиты. K, M, G килобайты, мегабайты, гигабайты
|
|
||||||
|
|
||||||
-P Количество потоков генерации трафика.
|
|
||||||
|
|
||||||
Значение ключа –P требуется подобрать такое что бы утилизация интерфеса была максимальная.
|
|
||||||
|
|
||||||
Критерии выполнения теста:
|
|
||||||
|
|
||||||
· Сетевые интерфейсы включатся и имеют индикацию
|
|
||||||
|
|
||||||
· Между приемником и передатчиком ходят пакеты
|
|
||||||
|
|
||||||
· Пропускная способность интерфейса во время теста больше 90 % от заявленной производителем
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
#note #error #ILO
|
|
||||||
|
|
||||||
Текст ошибки
|
|
||||||
iLO Self-Test reports a problem with: Embedded Flash/SD-CARD
|
|
||||||
Такая ошибка возникает при использовании ILO4 на серверах HP DL360 G9. Это впаянная SD карта. Из за большокго количество операций чтения записи карта кораптится. Для сброса ошибки требуется отформатировать карту и перезагрузить сервер.
|
|
||||||
Для форматирования карты заходим в ILO с правами администратора. Видим ошибку в категории
|
|
||||||
Status
|
|
||||||
ILO Health Degraded
|
|
||||||
Кликаем на ILO Health, открывается окно диагностики.
|
|
||||||
![[Pasted image 20250502153656.png]]
|
|
||||||
Пробуем перезагрузить ILO
|
|
||||||
![[Pasted image 20250502153811.png]]
|
|
||||||
Если это не помогло, то кликаем на ILO Health на окне диагностики и нажимаем кнопку форматирования Флеш карты
|
|
||||||
![[Pasted image 20250502154443.png]]
|
|
||||||
Если кнопка не активна то это другая проблема, требуется погуглить
|
|
||||||
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
#note #RAID #firmware
|
|
||||||
Для обновления прошивки нам нужна утилита для управления рейдом из ОС или ILO.
|
|
||||||
[[Установка утилиты для управление рейдом]]
|
|
||||||
Они нужны для того что бы узнать текущую версию прошивки и надо ли вообще обновляться
|
|
||||||
Найти прошивку нужно на сайте поддержки HP.
|
|
||||||
https://support.hpe.com/connect/s/softwaredetails?language=en_US&softwareId=MTX_26880c85fb3e4f5897e03283cc&tab=releaseNotes
|
|
||||||
Для Debiana к сожалению готового пакета нет поэтому мы будем распаковывать RPM пакет для Red Hat Enterprise Linux 7.1
|
|
||||||
Выберем в меню нужные параметры и получим ссылку на скачивание.
|
|
||||||
![[Pasted image 20250502234053.png]]
|
|
||||||
Установим пакет rpm2cpio для распаковки rpm-пакета
|
|
||||||
```
|
|
||||||
apt install rpm2cpio
|
|
||||||
```
|
|
||||||
После установки скачаем прошивку по ссылке с сайта HP
|
|
||||||
Распаковываем
|
|
||||||
```
|
|
||||||
rpm2cpio firmware-smartarray-ea3138d8e8-7.20-1.1.x86_64.rpm | cpio -id
|
|
||||||
```
|
|
||||||
|
|
||||||
Переходим в директорию с прошивкой
|
|
||||||
```
|
|
||||||
cd /usr/lib/x86_64-linux-gnu/firmware-smartarray-ea3138d8e8-7.20-1.1
|
|
||||||
```
|
|
||||||
|
|
||||||
И запускаем скрипт установки
|
|
||||||
```
|
|
||||||
./setup
|
|
||||||
```
|
|
||||||
|
|
||||||
Скрипт предложит ввести цифру с нужным нам Рейд контроллером
|
|
||||||
После завершения требуется перезагрузить компьютер
|
|
||||||
@@ -1,74 +0,0 @@
|
|||||||
#note #raid #utils
|
|
||||||
Все действия выполняются на ОС Proxmox 8.4 (Debian). Рейд контроллер P440ar на сервере HP Dl 360 G9
|
|
||||||
Для установки утилиты требуется ее скачать или закинуть из файлопомойки на сервер
|
|
||||||
Скачиваем утилиту
|
|
||||||
```
|
|
||||||
wget https://downloads.linux.hpe.com/sdr/repo/mcp/pool/non-free/hpssacli-2.40-13.0_amd64.deb
|
|
||||||
```
|
|
||||||
|
|
||||||
Устанавливаем пакет
|
|
||||||
```
|
|
||||||
dpkg -i hpssacli-2.40-13.0_amd64.deb
|
|
||||||
```
|
|
||||||
Далее мы можем использовать команды:
|
|
||||||
|
|
||||||
Посмотреть статус контроллера
|
|
||||||
```
|
|
||||||
hpssacli ctrl all show status
|
|
||||||
```
|
|
||||||
Статус покажет текущее состояние контроллера и его слот
|
|
||||||
Далее мы будем использовать параметр слота в качестве указателя для дальнейших команд
|
|
||||||
Посмотреть физические диски
|
|
||||||
```
|
|
||||||
hpssacli ctrl slot=0 pd all show status
|
|
||||||
```
|
|
||||||
Посмотрев диски можно узнать их расположение в сервере вида
|
|
||||||
```
|
|
||||||
physicaldrive 1I:1:1 (port 1I:box 1:bay 1, 300 GB): OK
|
|
||||||
```
|
|
||||||
Указывается что это физический диск находящийся в порте 1i в боксе 1 слоте 1
|
|
||||||
Эти данные понадобиться нам для дальнейшей настройки
|
|
||||||
|
|
||||||
Посмотреть логические диски
|
|
||||||
```
|
|
||||||
hpssacli ctrl slot=0 ld all show status
|
|
||||||
```
|
|
||||||
|
|
||||||
Полная информация по контроллеру
|
|
||||||
```
|
|
||||||
hpssacli ctrl all show detail
|
|
||||||
```
|
|
||||||
Собрать рейд
|
|
||||||
```
|
|
||||||
hpssacli ctrl slot=0 create type=ld drives=1I:1:3 raid=0 stripsize=16
|
|
||||||
```
|
|
||||||
Указываем контроллер в нужном слоте. Указываем тип логический диск кеазываем через запятую диски в формате порт:бокс:слот. Указываем уровень рейда и размер страйпа.
|
|
||||||
Страйп это блок данных который записывается за одну итерацию IO. Для больших файлов лучше большой блок, для маленьких маленький.
|
|
||||||
Размеры страйпов бывают:
|
|
||||||
8
|
|
||||||
16
|
|
||||||
32
|
|
||||||
64
|
|
||||||
128
|
|
||||||
256
|
|
||||||
512
|
|
||||||
1024
|
|
||||||
Лучше всего смотреть в спеках рейд котроллера.
|
|
||||||
Для удаления рейда
|
|
||||||
```
|
|
||||||
hpssacli ctrl slot=0 logicaldrive 2 delete
|
|
||||||
```
|
|
||||||
Так же указываем контроллер, какой логический диск и команду удалить. Если диск используется из ОС, то он не даст его удалить.
|
|
||||||
|
|
||||||
Включения кеша контроллера при работе с SSD.
|
|
||||||
Отключаем функцию SSDSmartPath.
|
|
||||||
Это функция позволяет увеличить производительность дисков при большом их количестве, так как кеш маленький по размеру. При малом количестве дисков следует включить кеш контроллера для увеличения производительности.
|
|
||||||
Эти функции надо включить и перевести систему в ХБА режим если мы планируем строить SDS
|
|
||||||
```
|
|
||||||
hpssacli ctrl slot=2 array A modify ssdsmartpath=disable
|
|
||||||
```
|
|
||||||
Выбираем слот контроллера и какой массив будем использовать
|
|
||||||
|
|
||||||
```
|
|
||||||
hpssacli ctrl slot=2 logicaldrive 1 modify arrayaccelerator=enable
|
|
||||||
```
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
#network #stp
|
|
||||||
## STP
|
|
||||||
Протокол STP и иные его реализации служит для предотвращения появления петель в топологии и широковещательного шторма.
|
|
||||||
Такое может случиться из за избыточных линков по которым будут ходит и размножаться широковещательные запросы
|
|
||||||
Для разрыва колец Замкнутая топология приобретает как бы вид дерева путем блокирования избыточных линков.
|
|
||||||
Принцип работы:
|
|
||||||
- Выбирается Корневой коммутатор. Процесс выбора корневого коммутаторы может быть ручным или автоматическим. В автоматическом режиме выбирается коммутатор с наименьшим MAC адресом. **Это влечет за собой проблемы так как наименьший мак у самого старого оборудования!**
|
|
||||||
-
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
Сброс коммутатора cisco 2960
|
|
||||||
|
|
||||||
1. Выключить питание
|
|
||||||
2. Зажать кнопку **mode**
|
|
||||||
3. Подключить кабель питания
|
|
||||||
4. Держать кнопку mode пока индикатор sys не перестанет мигать
|
|
||||||
5. Подключатся через консольный порт
|
|
||||||
6. Проинициализировать файлы в памяти командой <br> ```flash_init ```
|
|
||||||
7. Удалить файл конфигурации <br> ``` del flash:config.text ```
|
|
||||||
8. Удалить файл с таблицей вланов <br> ```del flash:vlan.dat```
|
|
||||||
9. Загрузить систему из консоли командой <br> ```boot```
|
|
||||||
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
|
|
||||||
На ядро сети можно добавить до 5000 агрегаций
|
|
||||||
Сколько каналов в 1 агрегацию неизвестно
|
|
||||||
Перед добавлением агрегации следует узнать какие каналы уже существуют.
|
|
||||||
```
|
|
||||||
Show etherchannel summary
|
|
||||||
```
|
|
||||||
Для добавления выберем порты
|
|
||||||
```
|
|
||||||
Int range (ports number)
|
|
||||||
```
|
|
||||||
Выключим их
|
|
||||||
```
|
|
||||||
sh
|
|
||||||
```
|
|
||||||
Создаем интрефейс агрегации
|
|
||||||
```
|
|
||||||
Int port-channel (номер от 1 до 5000)
|
|
||||||
```
|
|
||||||
В настройках порта задаем дескрипшон
|
|
||||||
```
|
|
||||||
Description (надпись)
|
|
||||||
```
|
|
||||||
И указываем его как L2 порт
|
|
||||||
```
|
|
||||||
Switchport
|
|
||||||
```
|
|
||||||
Так же указываем на портах, которые будут входить в групп что они L2
|
|
||||||
```
|
|
||||||
Int range (ports number)
|
|
||||||
Switchport
|
|
||||||
```
|
|
||||||
После добавляем в группу
|
|
||||||
```
|
|
||||||
Channel-group (номер агрегации) mode active
|
|
||||||
```
|
|
||||||
Если надо LACP, на том конце тоже должен быть активный LACP
|
|
||||||
Переходим в настройки интерфеса агрегации.
|
|
||||||
```
|
|
||||||
Int port-channel (номер)
|
|
||||||
```
|
|
||||||
Можем указывать транк или аксес и какие вланы принимать. Настройка аналогична физическому интерфейсу
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
|
|
||||||
Порты маркируем в соответствии со стандартом ОТКУДА Стойка Юнит Порт - КУДА Стойка Юнит Порт
|
|
||||||
Для блейд серверов и маршрутизаторов добавляется обозначение B (Bay) оно же лезвие. Номер лезвия указывается на корзине и на маркировке
|
|
||||||
Порты считаем слева направо сверху вниз если не указаны специальные обозначения
|
|
||||||
3 5 7
|
|
||||||
1 2 4 6 8
|
|
||||||
Если специальные обозначение есть (тип LAN1 или ETH1) то ориентируемся по ним
|
|
||||||
Порты IPMI (ilo, iDRAC, Irmc) не считаем и указываем их как менеджмент порты отдельно
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
#links #notes
|
|
||||||
[https://github.com/awesome-selfhosted/awesome-selfhosted?tab=readme-ov-file#automation](https://github.com/awesome-selfhosted/awesome-selfhosted?tab=readme-ov-file#automation)
|
|
||||||
@@ -1,977 +0,0 @@
|
|||||||
|
|
||||||
[Открыть главное меню](https://yourcmc.ru/wiki/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:%D0%9C%D0%BE%D0%B1%D0%B8%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D0%BC%D0%B5%D0%BD%D1%8E "Открыть главное меню")
|
|
||||||
|
|
||||||
- Наблюдать за этой страницей
|
|
||||||
|
|
||||||
# Производительность Ceph
|
|
||||||
|
|
||||||
Ссылки сюда (2) →
|
|
||||||
|
|
||||||
[](https://yourcmc.ru/wiki/%D0%A4%D0%B0%D0%B9%D0%BB:Ceph-funnel.svg)
|
|
||||||
|
|
||||||
Ceph — это SDS (по-русски — программная СХД), которая по некоторым параметрам является уникальной в своём роде, и в целом, умеет очень многое — S3, диски виртуалок, кластерную FS + огромный багаж дополнительных фич.
|
|
||||||
|
|
||||||
И всё было бы хорошо — бери, ставь, запускай своё облако и руби бабло — если бы не один маленький нюанс: ПРОИЗВОДИТЕЛЬНОСТЬ. Терять 95 % производительности в Production-е разумным людям обычно жалко. «Облакам» типа AWS, GCP, Яндекса, по-видимому, не жалко — у них тоже собственные крафтовые SDS и они тоже тормозят примерно так же :-) но этот вопрос оставим — кто мы такие, чтобы их судить.
|
|
||||||
|
|
||||||
В данной статье описано, каких показателей производительности можно добиться от цефа и как. Если вкратце, то примерным ориентиром служит доклад Nick Fisk «Low-Latency Ceph», в его исполнении Low Latency это 0.7 мс (на запись). Лучший результат с Ceph-ом получить практически невозможно (худший — легко). При этом 0.7 мс — это всего лишь примерно ~1500 iops в 1 поток. На чтение в идеальной ситуации можно получить где-то раза в 2 больше, то есть где-то до 3000 iops в 1 поток.
|
|
||||||
|
|
||||||
Для сравнения: любой самый дешёвый серверный SSD-диск раз в 10 быстрее, средний порядок задержки SSD на запись — 0.01-0.04 мс, на чтение — 0.1 мс.
|
|
||||||
|
|
||||||
**UPDATE: Догнать (почти догнать) SDS-кой локальной диск можно, я это сделал в своём собственном проекте — Vitastor: [https://vitastor.io](https://vitastor.io/) :-) это блочная SDS с архитектурой, похожей на Ceph, но при этом БЫСТРАЯ — в тесте на SATA SSD кластере задержка и чтения, и записи составила 0.14 мс. На том же кластере задержка записи у Ceph была 1 мс, а чтения — 0.57 мс. Детали в [README](https://yourcmc.ru/git/vitalif/vitastor/src/branch/master/README.md) — смотрите по ссылке.**
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Содержание
|
|
||||||
|
|
||||||
- [Бенчмаркинг](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.91.D0.B5.D0.BD.D1.87.D0.BC.D0.B0.D1.80.D0.BA.D0.B8.D0.BD.D0.B3)
|
|
||||||
- [Тестирование дисков](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A2.D0.B5.D1.81.D1.82.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B5_.D0.B4.D0.B8.D1.81.D0.BA.D0.BE.D0.B2)
|
|
||||||
- [Лирическое отступление](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9B.D0.B8.D1.80.D0.B8.D1.87.D0.B5.D1.81.D0.BA.D0.BE.D0.B5_.D0.BE.D1.82.D1.81.D1.82.D1.83.D0.BF.D0.BB.D0.B5.D0.BD.D0.B8.D0.B5)
|
|
||||||
|
|
||||||
- [Тестирование кластера Ceph](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A2.D0.B5.D1.81.D1.82.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B5_.D0.BA.D0.BB.D0.B0.D1.81.D1.82.D0.B5.D1.80.D0.B0_Ceph)
|
|
||||||
- [RBD](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#RBD)
|
|
||||||
|
|
||||||
- [Отдельные OSD](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9E.D1.82.D0.B4.D0.B5.D0.BB.D1.8C.D0.BD.D1.8B.D0.B5_OSD)
|
|
||||||
|
|
||||||
- [CephFS](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#CephFS)
|
|
||||||
|
|
||||||
- [S3 (rgw)](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#S3_.28rgw.29)
|
|
||||||
|
|
||||||
- [Что использовать не надо](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A7.D1.82.D0.BE_.D0.B8.D1.81.D0.BF.D0.BE.D0.BB.D1.8C.D0.B7.D0.BE.D0.B2.D0.B0.D1.82.D1.8C_.D0.BD.D0.B5_.D0.BD.D0.B0.D0.B4.D0.BE)
|
|
||||||
|
|
||||||
- [Про RBD и параллелизм](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9F.D1.80.D0.BE_RBD_.D0.B8_.D0.BF.D0.B0.D1.80.D0.B0.D0.BB.D0.BB.D0.B5.D0.BB.D0.B8.D0.B7.D0.BC)
|
|
||||||
|
|
||||||
- [Тестирование сети](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A2.D0.B5.D1.81.D1.82.D0.B8.D1.80.D0.BE.D0.B2.D0.B0.D0.BD.D0.B8.D0.B5_.D1.81.D0.B5.D1.82.D0.B8)
|
|
||||||
|
|
||||||
- [О транзакционности записи](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9E_.D1.82.D1.80.D0.B0.D0.BD.D0.B7.D0.B0.D0.BA.D1.86.D0.B8.D0.BE.D0.BD.D0.BD.D0.BE.D1.81.D1.82.D0.B8_.D0.B7.D0.B0.D0.BF.D0.B8.D1.81.D0.B8)
|
|
||||||
|
|
||||||
- [Конденсаторы](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9A.D0.BE.D0.BD.D0.B4.D0.B5.D0.BD.D1.81.D0.B0.D1.82.D0.BE.D1.80.D1.8B)
|
|
||||||
|
|
||||||
- [Bluestore vs Filestore](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#Bluestore_vs_Filestore)
|
|
||||||
- [Тест на 1 NVMe](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A2.D0.B5.D1.81.D1.82_.D0.BD.D0.B0_1_NVMe)
|
|
||||||
|
|
||||||
- [Про размер block.db](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9F.D1.80.D0.BE_.D1.80.D0.B0.D0.B7.D0.BC.D0.B5.D1.80_block.db)
|
|
||||||
|
|
||||||
- [RGW vs Minio](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#RGW_vs_Minio)
|
|
||||||
|
|
||||||
- [Снапшоты](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A1.D0.BD.D0.B0.D0.BF.D1.88.D0.BE.D1.82.D1.8B)
|
|
||||||
|
|
||||||
- [EC](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#EC)
|
|
||||||
- [Про вероятность потери данных](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9F.D1.80.D0.BE_.D0.B2.D0.B5.D1.80.D0.BE.D1.8F.D1.82.D0.BD.D0.BE.D1.81.D1.82.D1.8C_.D0.BF.D0.BE.D1.82.D0.B5.D1.80.D0.B8_.D0.B4.D0.B0.D0.BD.D0.BD.D1.8B.D1.85)
|
|
||||||
|
|
||||||
- [Контроллеры](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9A.D0.BE.D0.BD.D1.82.D1.80.D0.BE.D0.BB.D0.BB.D0.B5.D1.80.D1.8B)
|
|
||||||
|
|
||||||
- [Процессоры](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9F.D1.80.D0.BE.D1.86.D0.B5.D1.81.D1.81.D0.BE.D1.80.D1.8B)
|
|
||||||
|
|
||||||
- [Сеть](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A1.D0.B5.D1.82.D1.8C)
|
|
||||||
|
|
||||||
- [Настройка виртуалок и ФС](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9D.D0.B0.D1.81.D1.82.D1.80.D0.BE.D0.B9.D0.BA.D0.B0_.D0.B2.D0.B8.D1.80.D1.82.D1.83.D0.B0.D0.BB.D0.BE.D0.BA_.D0.B8_.D0.A4.D0.A1)
|
|
||||||
- [Драйвер виртуального диска и ФС](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.94.D1.80.D0.B0.D0.B9.D0.B2.D0.B5.D1.80_.D0.B2.D0.B8.D1.80.D1.82.D1.83.D0.B0.D0.BB.D1.8C.D0.BD.D0.BE.D0.B3.D0.BE_.D0.B4.D0.B8.D1.81.D0.BA.D0.B0_.D0.B8_.D0.A4.D0.A1)
|
|
||||||
|
|
||||||
- [cache=writeback](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#cache.3Dwriteback)
|
|
||||||
|
|
||||||
- [ФС](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A4.D0.A1)
|
|
||||||
|
|
||||||
- [Оценка производительности кластера](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9E.D1.86.D0.B5.D0.BD.D0.BA.D0.B0_.D0.BF.D1.80.D0.BE.D0.B8.D0.B7.D0.B2.D0.BE.D0.B4.D0.B8.D1.82.D0.B5.D0.BB.D1.8C.D0.BD.D0.BE.D1.81.D1.82.D0.B8_.D0.BA.D0.BB.D0.B0.D1.81.D1.82.D0.B5.D1.80.D0.B0)
|
|
||||||
|
|
||||||
- [Картина маслом «Тормозящий кэш»](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9A.D0.B0.D1.80.D1.82.D0.B8.D0.BD.D0.B0_.D0.BC.D0.B0.D1.81.D0.BB.D0.BE.D0.BC_.C2.AB.D0.A2.D0.BE.D1.80.D0.BC.D0.BE.D0.B7.D1.8F.D1.89.D0.B8.D0.B9_.D0.BA.D1.8D.D1.88.C2.BB)
|
|
||||||
- [O_SYNC vs fsync vs hdparm -W 0](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#O_SYNC_vs_fsync_vs_hdparm_-W_0)
|
|
||||||
|
|
||||||
- [Серверные SSD](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A1.D0.B5.D1.80.D0.B2.D0.B5.D1.80.D0.BD.D1.8B.D0.B5_SSD)
|
|
||||||
|
|
||||||
- [Ceph HDD+SSD](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#Ceph_HDD.2BSSD)
|
|
||||||
- [Примечания](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9F.D1.80.D0.B8.D0.BC.D0.B5.D1.87.D0.B0.D0.BD.D0.B8.D1.8F)
|
|
||||||
|
|
||||||
- [Почему вообще Bluestore такой медленный?](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9F.D0.BE.D1.87.D0.B5.D0.BC.D1.83_.D0.B2.D0.BE.D0.BE.D0.B1.D1.89.D0.B5_Bluestore_.D1.82.D0.B0.D0.BA.D0.BE.D0.B9_.D0.BC.D0.B5.D0.B4.D0.BB.D0.B5.D0.BD.D0.BD.D1.8B.D0.B9.3F)
|
|
||||||
|
|
||||||
- [DPDK и SPDK](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#DPDK_.D0.B8_SPDK)
|
|
||||||
|
|
||||||
- [RAID WRITE HOLE](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#RAID_WRITE_HOLE)
|
|
||||||
|
|
||||||
- [Краткий экскурс в устройство SSD и флеш-памяти](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9A.D1.80.D0.B0.D1.82.D0.BA.D0.B8.D0.B9_.D1.8D.D0.BA.D1.81.D0.BA.D1.83.D1.80.D1.81_.D0.B2_.D1.83.D1.81.D1.82.D1.80.D0.BE.D0.B9.D1.81.D1.82.D0.B2.D0.BE_SSD_.D0.B8_.D1.84.D0.BB.D0.B5.D1.88-.D0.BF.D0.B0.D0.BC.D1.8F.D1.82.D0.B8)
|
|
||||||
- [Бонус: USB-флешки](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.91.D0.BE.D0.BD.D1.83.D1.81:_USB-.D1.84.D0.BB.D0.B5.D1.88.D0.BA.D0.B8)
|
|
||||||
|
|
||||||
- [Пример теста от Micron](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9F.D1.80.D0.B8.D0.BC.D0.B5.D1.80_.D1.82.D0.B5.D1.81.D1.82.D0.B0_.D0.BE.D1.82_Micron)
|
|
||||||
- [Апдейт](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.90.D0.BF.D0.B4.D0.B5.D0.B9.D1.82)
|
|
||||||
|
|
||||||
- [Бонус: висян (vSAN)](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.91.D0.BE.D0.BD.D1.83.D1.81:_.D0.B2.D0.B8.D1.81.D1.8F.D0.BD_.28vSAN.29)
|
|
||||||
|
|
||||||
- [Модели](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9C.D0.BE.D0.B4.D0.B5.D0.BB.D0.B8)
|
|
||||||
|
|
||||||
- [Резюме](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A0.D0.B5.D0.B7.D1.8E.D0.BC.D0.B5)
|
|
||||||
|
|
||||||
- [Примечание](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9F.D1.80.D0.B8.D0.BC.D0.B5.D1.87.D0.B0.D0.BD.D0.B8.D0.B5)
|
|
||||||
|
|
||||||
- [См. также](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A1.D0.BC._.D1.82.D0.B0.D0.BA.D0.B6.D0.B5)
|
|
||||||
|
|
||||||
- [Советы лучших собаководов](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.A1.D0.BE.D0.B2.D0.B5.D1.82.D1.8B_.D0.BB.D1.83.D1.87.D1.88.D0.B8.D1.85_.D1.81.D0.BE.D0.B1.D0.B0.D0.BA.D0.BE.D0.B2.D0.BE.D0.B4.D0.BE.D0.B2)
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Бенчмаркинг
|
|
||||||
|
|
||||||
Основные направления тестирования:
|
|
||||||
|
|
||||||
- Линейное чтение/запись (большими блоками)
|
|
||||||
- Пиковая производительность высоко-параллельного случайного чтения/записи мелкими блоками
|
|
||||||
- Задержка однопоточного случайного чтения мелкими блоками (4-8 Кб)
|
|
||||||
- Задержка однопоточной транзакционной записи мелкими блоками (4-8 Кб) — обычно последовательной, как в журнал СУБД, но в один поток это обычно слабо отличается от случайной
|
|
||||||
|
|
||||||
Задержки обычно важнее простой пиковой производительности случайного чтения/записи, так как далеко не каждое приложение может загрузить диск при большом параллелизме / глубокой очереди (32-128 запросов).
|
|
||||||
|
|
||||||
### Тестирование дисков
|
|
||||||
|
|
||||||
[SSD Bench Google Docs](https://docs.google.com/spreadsheets/d/1E9-eXjzsKboiCCX-0u0r5fAjjufLKayaut_FOPxYZjc)
|
|
||||||
|
|
||||||
Сначала прогоните fio на голом диске:
|
|
||||||
|
|
||||||
 ВНИМАНИЕ! Для тех, кто в танке — fio-тест записи на диск ДЕСТРУКТИВНЫЙ. Не вздумайте запускать его на дисках/разделах, на которых есть нужные данные… например, журналы OSD (был прецедент).
|
|
||||||
|
|
||||||
- Перед тестированием попробуйте отключить кэш записи диска: hdparm -W 0 /dev/sdX (SATA-диски через SATA или HBA), sdparm --set WCE=0 /dev/sdX (SAS-диски). Не совсем ясно, почему, но эта операция на серверных SSD может увеличить IOPS-ы на 2 порядка (а может НЕ увеличить, поэтому пробуйте оба варианта — и W0, и W1). Также см.ниже [#Картина маслом «Тормозящий кэш»](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#.D0.9A.D0.B0.D1.80.D1.82.D0.B8.D0.BD.D0.B0_.D0.BC.D0.B0.D1.81.D0.BB.D0.BE.D0.BC_.C2.AB.D0.A2.D0.BE.D1.80.D0.BC.D0.BE.D0.B7.D1.8F.D1.89.D0.B8.D0.B9_.D0.BA.D1.8D.D1.88.C2.BB).
|
|
||||||
- Линейное чтение: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4M -iodepth=32 -rw=read -runtime=60 -filename=/dev/sdX
|
|
||||||
- Линейная запись: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4M -iodepth=32 -rw=write -runtime=60 -filename=/dev/sdX
|
|
||||||
- Пиковые IOPS случайного чтения: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randread -runtime=60 -filename=/dev/sdX
|
|
||||||
- Задержка случайного чтения: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randread -runtime=60 -filename=/dev/sdX
|
|
||||||
- Пиковые IOPS случайной записи: fio -ioengine=libaio -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -runtime=60 -filename=/dev/sdX
|
|
||||||
- Задержка записи в журнал: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=write -runtime=60 -filename=/dev/sdX — также стоит повторить тот же тест с -fsync=1 вместо -sync=1 и принять худший результат, так как иногда бывает, что одним из методов sync игнорируется (зависит от контроллера).
|
|
||||||
- Задержка случайной записи: fio -ioengine=libaio -sync=1 -direct=1 -invalidate=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=/dev/sdX
|
|
||||||
|
|
||||||
«А почему так мало…» — см.ниже сагу про конденсаторы.
|
|
||||||
|
|
||||||
 Когда разворачиваете Ceph OSD на SSD — очень разумно не отдавать её под Ceph целиком, а оставить небольшой раздел (10-20 гб) пустым для будущего использования под бенчмаркинг. Ибо SSD имеют свойство со временем (или при забивании данными под 80%) начинать тормозить. Очень удобно иметь возможность гонять fio на пустом никем не используемом разделе.
|
|
||||||
|
|
||||||
#### Лирическое отступление
|
|
||||||
|
|
||||||
Почему нужно тестировать именно так? Ведь в целом производительность диска зависит от многих параметров:
|
|
||||||
|
|
||||||
- Размер блока
|
|
||||||
- Режим — чтение, запись или смешанный режим чтение+запись в разных пропорциях
|
|
||||||
- Параллелизм — размер очереди и число потоков, то есть, в целом число одновременно запрашиваемых у диска операций
|
|
||||||
- Длительность теста
|
|
||||||
- Исходное состояние — пуст, заполнен линейной записью, заполнен случайной записью, заполнен случайной записью на протяжении какого-то времени и т. п.
|
|
||||||
- Распределение данных — например, 10% горячих данных и 90% холодных — или, например, определённое расположение горячих данных (в начале диска)
|
|
||||||
- Другие смешанные режимы тестов, например, тестирование одновременно с разными размерами блоков
|
|
||||||
|
|
||||||
Также и результаты можно интерпретировать с разной степенью детализации — вместо простого среднего числа операций или мегабайт в секунду можно также приводить графики, гистограммы, перцентили и так далее — это, естественно, даст больше информации о поведении тестируемого образца.
|
|
||||||
|
|
||||||
Есть и философская сторона тестов — например, производители серверных SSD иногда заявляют о необходимости подготовки диска к тестам путём 2-х кратной полной случайной перезаписи, чтобы нагрузить слой трансляции адресов диска, а я считаю, что это на самом деле ставит SSD в неправдоподобно плохие по сравнению с реальной нагрузкой условия; есть сторонники рисования графиков формата «задержка в зависимости от числа операций в секунду», что я считаю немного странным, но тоже возможным подходом — в нём, по сути, строится график F1(q) в зависимости от F2(q) и график обычно получается достаточно замысловатый — но для каких-то применений, может быть, и тоже разумный.
|
|
||||||
|
|
||||||
В общем, бенчмаркингом заниматься можно бесконечно, и уж несколько дней, чтобы предоставить полную информацию, точно уйдёт. Этим обычно и занимаются ресурсы типа 3dnews в своих обзорах SSD. А мы не хотим сидеть несколько дней. Мы хотим обозначить набор тестов, которые можно провести быстро и сразу составить примерное представление о производительности.
|
|
||||||
|
|
||||||
Посему общая идея — выделить несколько наиболее «крайних» режимов, протестировать диск в них и представить, что остальная часть «амплитудно-скоростной характеристики» диска является некоторой гладкой функцией в пределах изменения параметров между крайними точками. Тем более, что каждому из крайних режимов соответствует и реальное применение в своей категории приложений:
|
|
||||||
|
|
||||||
1. Использующих в основном линейный или крупноблочный доступ. Для таких приложений наиболее важная характеристика — производительность линейного доступа в мегабайтах в секунду. Отсюда режим тестирования линейным доступом 4 МБ блоком со средней очередью — 16-32 операции. Результаты — только в МБ/с.
|
|
||||||
2. Использующих случайный доступ мелким блоком и при этом способных к распараллеливанию. Отсюда — режимы тестирования случайным доступом 4 КБ блоком (стандартный блок для большинства ФС и, плюс-минус, СУБД) с большой очередью — 128 операций или, если диск не удаётся нагрузить одним потоком CPU с глубиной очереди 128 — тогда в несколько (2-4-8 или больше) потоков по 128 операций. Результаты — только в iops. Задержку (latency) указывать не нужно, так как в данном тесте её можно произвольно увеличить, просто подняв размер очереди — задержка жёстко связана с iops формулой latency=queue/iops.
|
|
||||||
3. Использующих случайный доступ мелким блоком и при этом НЕспособных к распараллеливанию. Таких приложений больше, чем вы могли подумать — например, в части записи сюда относятся все транзакционные СУБД. Отсюда вытекают режимы тестирования случайным доступом 4 КБ блоком с очередью 1 и, для записи, с fsync после каждой операции, чтобы диск/СХД не могли нас обмануть и положить запись во внутренний кэш. Результаты — iops или latency, по желанию — но выберите что-то одно, так как числа, опять же, жёстко связанные.
|
|
||||||
|
|
||||||
### Тестирование кластера Ceph
|
|
||||||
|
|
||||||
Как тестировать Ceph после сборки.
|
|
||||||
|
|
||||||
#### RBD
|
|
||||||
|
|
||||||
fio -ioengine=rbd. Нужно сделать следующее:
|
|
||||||
|
|
||||||
1. fio -ioengine=rbd -direct=1 -name=test -bs=4M -iodepth=16 -rw=write -pool=rpool_hdd -runtime=60 -rbdname=testimg
|
|
||||||
2. fio -ioengine=rbd -direct=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -pool=rpool_hdd -runtime=60 -rbdname=testimg
|
|
||||||
3. fio -ioengine=rbd -direct=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -pool=rpool_hdd -runtime=60 -rbdname=testimg
|
|
||||||
4. ...и потом то же самое для read/randread.
|
|
||||||
|
|
||||||
Смысл в том, чтобы протестировать а) задержку в идеальных условиях б) линейную пропускную способность в) случайные iops-ы.
|
|
||||||
|
|
||||||
Перед тестами чтения образ сначала нужно заполнить линейной записью, так как чтение из пустого образа очень быстрое :)
|
|
||||||
|
|
||||||
Запускать нужно оттуда, где будут реальные пользователи RBD. В целом, с другого узла результаты обычно лучше.
|
|
||||||
|
|
||||||
Также всё то же самое можно повторить изнутри виртуалки или через krbd:
|
|
||||||
|
|
||||||
1. fio -ioengine=libaio -direct=1 -name=test -bs=4M -iodepth=16 -rw=write -runtime=60 -filename=/dev/rbdX
|
|
||||||
2. fio -ioengine=libaio -direct=1 -sync=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=/dev/rbdX
|
|
||||||
3. fio -ioengine=libaio -direct=1 -name=test -bs=4k -iodepth=128 -rw=randwrite -runtime=60 -filename=/dev/rbdX
|
|
||||||
|
|
||||||
Заметьте, что при тестировании задержки через libaio добавилась опция -sync=1. Это не случайно, а соответствует режиму работы СУБД (транзакционная запись в 1 поток). В ioengine=rbd понятие sync отсутствует, там всё всегда «sync».
|
|
||||||
|
|
||||||
#### Отдельные OSD
|
|
||||||
|
|
||||||
ceph-gobench: [https://github.com/rumanzo/ceph-gobench/](https://github.com/rumanzo/ceph-gobench/)
|
|
||||||
|
|
||||||
Либо [https://github.com/vitalif/ceph-bench](https://github.com/vitalif/ceph-bench), что примерно то же самое. Родоначальник идеи — @socketpair Марк Коренберг ([оригинал](https://github.com/socketpair/ceph-bench)). Бенчилка тестирует _отдельные OSD_, что очень помогает понять, кто же из них тупит-то.
|
|
||||||
|
|
||||||
Перед запуском надо создать пул без репликации ceph osd pool create bench 128 replicated; ceph osd pool set bench size 1; ceph osd pool set bench min_size 1 и с числом PG, достаточным, чтобы при случайном выборе туда попали все OSD (ну или прибить их вручную к каждому OSD upmap-ами).
|
|
||||||
|
|
||||||
#### CephFS
|
|
||||||
|
|
||||||
«Нормальных» инструментов для тестирования ФС, сцуко, нет!!!
|
|
||||||
|
|
||||||
«Нормальный» инструмент — это такой инструмент, который вёл бы себя, как файловый сервер: случайно открывал, создавал/писал/читал и закрывал маленькие файлы среди большого общего количества, разбитые по набору каталогов
|
|
||||||
|
|
||||||
Всё, что есть, какое-то кривожопое: bonnie++, например, зачем-то тестирует запись по 1 байту. iometer, fs_mark не обновлялись лет по 10, но и паттерн файл сервера не умеют. Лучшее, что умеют — это тест создания файлов.
|
|
||||||
|
|
||||||
Пришлось написать свой ioengine для fio: [https://github.com/vitalif/libfio_fileserver](https://github.com/vitalif/libfio_fileserver) :)
|
|
||||||
|
|
||||||
#### S3 (rgw)
|
|
||||||
|
|
||||||
Предпочтительный вариант: [hsbench](https://github.com/vitalif/hsbench) — ссылка дана на исправленную версию (!). Максимально простой, консольное Golang приложение. Оригинальная версия пока что имеет 2 неприятных бага: во-первых, вместо чтения объектов целиком читает только первые 64 КБ, во-вторых, производит последовательное, а не случайное, чтение. Что, например, с minio приводит к слишком оптимистичным результатам тестов. В моей данные баги исправлены.
|
|
||||||
|
|
||||||
[cosbench](https://github.com/intel-cloud/cosbench) — очень толстый, Java с Web-интерфейсом, XML-настройки.
|
|
||||||
|
|
||||||
[minio warp](https://github.com/minio/warp) — тестов чуть больше, чем в hsbench, но зато тестирует только 1 бакет и при каждом тесте загружает данные заново.
|
|
||||||
|
|
||||||
#### Что использовать не надо
|
|
||||||
|
|
||||||
- dd и hdparm для бенчмаркинга не использовать вообще никогда!!!
|
|
||||||
- rados bench использовать тоже не надо, так как он создаёт для тестирования очень мало объектов (в 1 поток всего 2, в 128 — несколько сотен). «Случайная» запись в такое число объектов не очень-то и случайная.
|
|
||||||
- rbd bench лучше тоже не использовать. В принципе, он адекватен, но fio всё равно лучше.
|
|
||||||
- Не надо удивляться, если Ceph не может загрузить диски на 100 % при случайной записи. Он тормоз :)
|
|
||||||
|
|
||||||
#### Про RBD и параллелизм
|
|
||||||
|
|
||||||
 Тестировать запись несколькими параллельными процессами (fio numjobs > 1) в один RBD-образ бесполезно. Из-за особенностей реализации RBD, в частности, из-за object-map, при параллельной записи из нескольких источников производительность СИЛЬНО проседает (в 2-10 раз). Можно отключить object-map, но это будет некорректный тест, т.к. в реальной эксплуатации в 99% случаев он нужен, так что с отключенным object-map вы лишь получите неправильный (слишком хороший) результат.
|
|
||||||
|
|
||||||
Если вы не можете загрузить кластер одним процессом fio, то нужно создать несколько отдельных RBD-образов и запустить несколько процессов fio параллельно, каждый на своём RBD-образе.
|
|
||||||
|
|
||||||
### Тестирование сети
|
|
||||||
|
|
||||||
sockperf. На одной ноде запускаем сервер: sockperf sr -i IP --tcp. На другой клиент в режиме ping-pong: sockperf pp -i SERVER_IP --tcp -m 4096. ВНИМАНИЕ: В выводе фигурирует **половина** задержки (задержка в одну сторону). Таким образом, для получения RTT её стоит умножить на 2. Нормальный средний RTT - в районе 30-50 микросекунд (0.05ms).
|
|
||||||
|
|
||||||
~~Также qperf. На одной ноде просто qperf. На второй qperf -vvs -m 4096 SERVER_IP tcp_lat.~~
|
|
||||||
|
|
||||||
qperf написан криво: 1) всегда использует для tcp_lat размер сообщения 1 байт!!! 2) не использует TCP_NODELAY. Так что его юзать, только если возьмёте его с моим патчем отсюда: [Мой Debian репозиторий](https://yourcmc.ru/wiki/%D0%9C%D0%BE%D0%B9_Debian_%D1%80%D0%B5%D0%BF%D0%BE%D0%B7%D0%B8%D1%82%D0%BE%D1%80%D0%B8%D0%B9 "Мой Debian репозиторий").
|
|
||||||
|
|
||||||
 Внимание: в Ubuntu на сетевую задержку негативно влияет AppArmor, его лучше отключить. Картина примерно такая (Intel X520-DA2):
|
|
||||||
|
|
||||||
- centos 3.10: rtt min/avg/max/mdev = 0.039/0.053/0.132/0.012 ms
|
|
||||||
- ubuntu 4.x + apparmor: rtt min/avg/max/mdev = 0.068/0.163/0.230/0.029 ms
|
|
||||||
- ubuntu 4.x: rtt min/avg/max/mdev = 0.037/0.071/0.157/0.018 ms
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
О транзакционности записи
|
|
||||||
|
|
||||||
 Плохая новость!
|
|
||||||
|
|
||||||
Важная особенность Ceph — _вся запись, даже та, для которой никто этого явно не просит, ведётся транзакционно_. То есть, никакая операция записи не завершается, пока она не записана в журналы всех OSD и не сделан fsync() диска. Так сделано, чтобы предотвращать [#RAID WRITE HOLE](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#RAID_WRITE_HOLE)-подобные ситуации рассинхронизации данных между репликами при отключении питания, потере сети и т.п…
|
|
||||||
|
|
||||||
Если конкретизировать сильнее, это означает, что Ceph не использует никакие буферы записи дисков (наоборот, он делает всё, чтобы эти буферы всё время очищать). Это не значит, что буферизации записи нет вообще — она есть на уровне клиентов (page cache в linux, кэш RBD устройства на уровне драйвера librbd qemu…). Но именно внутренние дисковые буферы не используются.
|
|
||||||
|
|
||||||
Это приводит к тому, что типичная настольная SSD под журналом в Ceph выдаёт **неприлично низкие IOPS-ы** — обычно от 500 до 2000. И это при том, что при обычном тестировании почти любая SSD выдаёт > 20000 iops. Даже самый паршивый китайский noname выдаёт не менее 10000 iops. NVMe легко выжимает 150000 и больше. Но стоит начать использовать fsync… и та же NVMe выдаёт 600 iops (на 2.5 порядка меньше).
|
|
||||||
|
|
||||||
В общем, чтобы понять, сколько у вас теоретически может быть IOPS-ов на запись в Ceph, диски под него нужно тестировать с опциями fio **sync=1 iodepth=1**. Это даст «журнальные» иопсы (производительность последовательного коммита операций по одной).
|
|
||||||
|
|
||||||
Другие почти идентичные варианты: fdatasync=1 (в файле поверх ФС) либо fsync=1 (на голом девайсе). Разница между опциями:
|
|
||||||
|
|
||||||
- fsync=1 синхронизирует данные и метаданные тестируемого файла отдельным запросом после каждой операции записи. Так работает BlueStore.
|
|
||||||
- fdatasync=1 синхронизирует только данные (но не метаданные) тестируемого файла после каждой операции записи. Соответственно, от fsync=1 это отличается, только если тестируется **файл в ФС**, а не блочное устройство.
|
|
||||||
 fdatasync=1 надо использовать, когда на диске уже есть ФС, а прогнать тест хочется. Результаты будут достаточно корректными.
|
|
||||||
- sync=1 использует O_SYNC и синхронный ввод/вывод, то есть, каждая операция начинается только после завершения предыдущей. Так работает FileStore.
|
|
||||||
|
|
||||||
Но ещё нужна опция iodepth=1, иначе в очередь диска до синхронизации «пролезает» несколько операций и IOPS-ы растут, тест перестаёт быть тестом журнала.
|
|
||||||
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Конденсаторы
|
|
||||||
|
|
||||||
Нас спасёт такое чудо инженерной мысли, как **SSD с конденсаторами** (или с суперконденсаторами — ионисторами). Которые на M.2 SSD, кстати, прекрасно видны невооружённым глазом (только тут это не ионисторы :)):
|
|
||||||
|
|
||||||
[](https://yourcmc.ru/wiki/%D0%A4%D0%B0%D0%B9%D0%BB:Micron_5100_sata_m2.jpg)
|
|
||||||
|
|
||||||
Конденсаторы работают фактически как встроенный в SSD ИБП и позволяют SSD успеть сбросить кэш во флеш-память при потере питания. Таким образом кэш становится «энергонезависимым» — и таким образом SSD может просто игнорировать запросы fsync, так как точно знает, что данные из кэша в любом случае доедут до постоянной памяти.
|
|
||||||
|
|
||||||
При этом **IOPS-ы транзакционной записи становятся равны IOPS-ам нетранзакционной**.
|
|
||||||
|
|
||||||
Конденсаторы в официальных описаниях SSD-шек обычно называются «enhanced/advanced power loss protection». Этой характеристикой обладают, как правило, только «серверные» SSD, да и то не все. Например, в Intel DC S3100 конденсаторов нет, а в Intel DC S4600 есть.
|
|
||||||
|
|
||||||
 Это и является главным отличием серверных SSD от настольных. Обычному пользователю транзакции нужны редко — а вот на серверах живут СУБД, которым транзакции как раз нужны позарез.
|
|
||||||
|
|
||||||
То есть, под Ceph следует закупать **только** SSD с конденсаторами. Даже если рассматривать NVMe — NVMe без конденсаторов хуже, чем SATA с оными.
|
|
||||||
|
|
||||||
И ещё один вариант — Intel Optane. Это тоже SSD, но они основаны не на Flash памяти (не NAND и не NOR), а на Phase-Change-Memory «3D XPoint». По спецификации заявляются 550000 iops при полном отсутствии необходимости в стирании блоков, кэше и конденсаторах. Но если даже задержка такого диска и равна 0.005мс (она действительно равна), то задержка Ceph всё равно 0.5-1мс, соответственно, с Ceph оптаны использовать чуть менее, чем бессмысленно — за большие деньги (1500$ за 960 гб, 500$ за 240 гб) вы получите не сильно лучший результат.
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Bluestore vs Filestore
|
|
||||||
|
|
||||||
Блюстор — «новое» хранилище. От «нового» хранилища честно ожидаешь лучшей или хотя бы не худшей производительности во всех сценариях. Однако это, увы, не совсем так.
|
|
||||||
|
|
||||||
Что лучше в Bluestore?
|
|
||||||
|
|
||||||
- Ликвидирована двойная запись при линейной записи. Линейная запись быстрее в честных 2 раза практически в любых конфигурациях.
|
|
||||||
- Отложенная запись, эффективная для HDD (и, частично, для очень плохих SSD). iops случайной записи в HDD-only конфигурациях почти в 2 раза больше, чем в Filestore. Правда, речь об iops на HDD не идёт в принципе, поэтому вся разница — 33 или 66 iops на 1 HDD.
|
|
||||||
- Возможность использования EC под CephFS и RBD благодаря реализованной частичной перезаписи объектов в EC-пулах.
|
|
||||||
- Эффективные снапшоты (благодаря «виртуальным клонам»): после снятия снапшота iops практически не падают, в отличие от Filestore, в котором они падают до считанных сотен даже на NVMe, так как при перезаписи даже 4 КБ после снятия снапшота в Filestore копируется целый 4 МБ объект.
|
|
||||||
|
|
||||||
 Увы, это относится только к **rbd snapshot**, но не к **rbd clone**. Клоны неэффективны в Bluestore точно так же, как и в Filestore. Запись 4 КБ в клон точно так же выливается в копирование 4 МБ.
|
|
||||||
|
|
||||||
- Это не так критично, но в Bluestore есть поддержка сжатия и контрольных сумм данных.
|
|
||||||
|
|
||||||
А что хуже? В целом, претензии сводятся к производительности:
|
|
||||||
|
|
||||||
- Гораздо хуже производительность случайной записи на SSD+HDD, SSD-раздел не работает как буфер для быстрой записи. Bluestore не пишет быстрее, чем может в среднем сам HDD. То есть, с SSD-журналом и Filestore будет 1000—2000 иопс случайной записи, а с Bluestore (и без bcache) — 200—300. 1000—2000, конечно, упадёт до 100 или даже ниже, когда у Filestore забьётся журнал и его придётся сбрасывать — но тем не менее, «буфер» для сглаживания пиков Filestore предоставляет. А Bluestore — нет.
|
|
||||||
|
|
||||||
И проблема не только в том, что параметры по умолчанию — deferred_batch_ops и max_deferred_txc — задают частый сброс операций на медленный диск (раз в 64 операции). Проблема ещё в том, что в Bluestore отсутствуют механизмы фоновой очистки «журнала» (очереди отложенной записи). Поэтому, когда очередь забивается, производительность просто падает до HDD-шной до перезапуска OSD. Ну и сама очередь находится в RocksDB, поэтому сильно поднимать её размер, по идее, неполезно.
|
|
||||||
|
|
||||||
- До 1.5-2 раз хуже latency случайной записи на SSD/NVMe (All-Flash), ибо накладных расходов на каждую операцию записи у Bluestore больше.
|
|
||||||
- Жор памяти больше. Да, у Filestore много занимал pagecache, но Bluestore меньше 2 ГБ памяти не жрёт вообще никогда. Причиной тому — RocksDB (одни только memtable-ы с дефолтными настройками съедают 1 ГБ памяти) и собственный кэш метаданных и данных (Bluestore не может использовать pagecache).
|
|
||||||
- Фрагментация приводит к снижению скорости чтения.
|
|
||||||
|
|
||||||
Также BlueStore делает огромное количество fsync-ов (что очень смешно — даже больше, чем запросов записи), из-за чего не терпит десктопных SSD под журналом. Но FileStore работает похоже, и кардинальных различий производительности это не вносит.
|
|
||||||
|
|
||||||
Как полечить высокие задержки на SSD+HDD?
|
|
||||||
|
|
||||||
- Либо вместо журнала (или рядом с журналом) сделать на SSD bcache для HDD.
|
|
||||||
- Либо использовать HDD с SSD Cache, Media Cache или аналогом (перманентным кэшем случайной записи на пластинах). Например, в старых дисках HGST это включается при отключении волатильного кэша командой `hdparm -W 0 /dev/sdXX`. В новых, похоже, включено всё время.
|
|
||||||
|
|
||||||
### Тест на 1 NVMe
|
|
||||||
|
|
||||||
Threadripper 2920X, NVMe Intel P4500, localhost. 1 OSD без репликации, 8 PG, чтобы не упираться в блокировки, 1 маленький RBD образ 10 Гб.
|
|
||||||
|
|
||||||
Журнал Filestore 1 GB, чтобы в тестах успевал начинаться сброс. Bluestore 4k — это min_alloc_size и prefer_deferred_size = 4096 (4k запись идёт через redirect-write), Bluestore 16k — 16384 (4k запись идёт через deferred).
|
|
||||||
|
|
||||||
||Filestore|Bluestore 16k|Bluestore 4k||
|
|
||||||
|---|---|---|---|---|
|
|
||||||
|bs=4M iodepth=16 rw=write|950 MB/s|1700 MB/s|1700 MB/s||
|
|
||||||
|bs=4M iodepth=16 rw=read|1250 MB/s|1300 MB/s|1300 MB/s|После полной линейной перезаписи + drop_caches|
|
|
||||||
|bs=4M iodepth=16 rw=read|1250 MB/s|450 MB/s|320 MB/s|После 33 % случайной перезаписи блоком min_alloc_size|
|
|
||||||
|bs=4k iodepth=1 rw=randwrite|3900 iops|3200 iops|2500 iops||
|
|
||||||
|bs=4k iodepth=128 rw=randwrite|19100 iops|19500 iops|25500 iops||
|
|
||||||
|bs=4k iodepth=1 rw=randwrite|180 iops|2800 iops|2500 iops|Сразу после снятия snapshot-а RBD|
|
|
||||||
|bs=4k iodepth=128 rw=randwrite|180 iops|8800 iops|15600 iops|Сразу после снятия snapshot-а RBD|
|
|
||||||
|bs=4k iodepth=1 rw=randread|3900 iops|4500 iops|4500 iops|После drop_caches / перезапуска OSD|
|
|
||||||
|bs=4k iodepth=1 rw=randread|6300 iops|4500 iops|4500 iops|Прогретый кэш|
|
|
||||||
|bs=4k iodepth=128 rw=randread|33000 iops|32000 iops|33000 iops||
|
|
||||||
|RAM|270 MB|2 GB +||Filestore также использует произвольный объём page cache|
|
|
||||||
|CPU randwrite Q=128|600 %|550 %|||
|
|
||||||
|
|
||||||
### Про размер block.db
|
|
||||||
|
|
||||||
**Внимание:** актуально до Ceph 14. Начиная с Ceph 15, благодаря добавленным «allocation hints» RocksDB, Bluestore стал нормально утилизировать раздел block.db. Для истории — это коммит 5f72c376deb64562e5e88be2f22339135ac7372b, там добавили опцию bluestore_volume_selection_policy.
|
|
||||||
|
|
||||||
Дальше стоит читать, только если у вас всё ещё проблемы со spillover-ами.
|
|
||||||
|
|
||||||
Спилловер — это когда вы собрали Bluestore на SSD+HDD, выделив SSD под базу (block.db), но при этом эта самая база постоянно частично утекает на HDD. При этом она, вроде бы, даже влезает в SSD с запасом — но всё равно утекает. Начиная с Ceph 14 Nautilus о спилловерах предупреждает ceph -s.
|
|
||||||
|
|
||||||
Когда случается спилловер в SSD+HDD конфигурациях, работа кластера замедляется — в большей или меньшей степени, в зависимости от размеров RocksDB и паттерна нагрузки, так как когда метаданных не очень много, они влезают в кэш OSD — либо onode cache, либо rocksdb cache, либо, если включено bluefs buffered io — то ещё и в системный page cache. Если кэш-промахов достаточно много, или если OSD упирается в compaction RocksDB, могут даже появляться slow ops-ы.
|
|
||||||
|
|
||||||
Так в чём же дело и как это победить? А дело в том, что с выбором раздела для очередного файла БД (RocksDB организована в виде набора файлов) «есть нюанс», точнее, даже два.
|
|
||||||
|
|
||||||
**Нюанс № 1:** RocksDB кладёт файл на быстрый диск только когда считает, что на быстром диске хватит места под все файлы этого же уровня (для тех, кто ещё не в курсе — RocksDB это [LSM база](https://github.com/facebook/rocksdb/wiki/Leveled-Compaction)).
|
|
||||||
|
|
||||||
Дефолтные настройки цефа:
|
|
||||||
|
|
||||||
- 1 Гб WAL = 4x256 Мб
|
|
||||||
- max_bytes_for_level_base и max_bytes_for_level_multiplier не изменены, поэтому равны 256 Мб и 10 соответственно
|
|
||||||
- соответственно, L1 = 256 Мб
|
|
||||||
- L2 = 2560 Мб
|
|
||||||
- L3 = 25600 Мб и т. д.
|
|
||||||
|
|
||||||
…Соответственно!
|
|
||||||
|
|
||||||
Rocksdb положит L2 на block.db, только если раздел имеет размер хотя бы 2560+256+1000 Мб — округлим вверх до **4 ГБ**. А L3 она положит на block.db, только если block.db размером хотя бы 25600+2560+256+1000 МБ = около **30 ГБ**. А L4, соответственно, если ещё +256 ГБ, то есть итого **286 ГБ**.
|
|
||||||
|
|
||||||
Иными словами, имеют смысл только размеры раздела block.db 4 ГБ, 30 ГБ, 286 ГБ. Все промежуточные значения бессмысленны — место сверх предыдущего граничного значения использоваться не будет. Например, если БД занимает 10 ГБ, а раздел SSD — 20 ГБ, то фактически на SSD ляжет только WAL (1 ГБ), L1 и L2 (256 МБ + 2.56 ГБ). L3, составляющий бОльшую часть базы, уедет на HDD и будет тормозить работу.
|
|
||||||
|
|
||||||
При этом 4 ГБ — слишком мало, 286 ГБ — слишком много. Так что, по сути, правильно делать block.db размером 30 ГБ для OSD любого размера. Ещё раз повторюсь: это актуально до Ceph 14, с Ceph 15 уже не актуально.
|
|
||||||
|
|
||||||
Кстати, из этого же следует то, что официальная рекомендация — выделять под block.db то ли 2 %, то ли 4 % от размера устройства данных — полный отстой.
|
|
||||||
|
|
||||||
Но что делать, если у вас разделы другого размера? Например, 80 ГБ, и вы по каким-то причинам не хотите делать bcache, но хотите использовать эти 80 ГБ по максимуму. В этом случае можно поменять базовый размер уровня RocksDB (max_bytes_for_level_base). multiplier менять не будем, оставим по умолчанию 10 — его значение влияет на итоговое количество уровней RocksDB, а это уже более тонкая материя. Теоретически, меньшее число уровней снижает read и space amplification, но замедляет compaction и из-за этого может сильно повысить итоговый write amplification. Также есть тема с уменьшением размера отдельных memtable и кратным увеличением общего их числа, то есть, например, установки 32*32 МБ вместо дефолтных 4*256 МБ и min_write_buffer_to_merge=8, но эффект от этого тоже не совсем понятен (возможно, немного экономится CPU при compaction-е), так что это тоже пока лучше не трогать.
|
|
||||||
|
|
||||||
Так как каждый уровень отличается от предыдущего в 10 раз, общий размер раздела БД должен быть равен k*X, где k — коэффициенты из ряда: 1, 11, 111, 1111 и т. п. (по числу уровней RocksDB). Значит, мы можем взять размер нашего block.db, вычесть из него 1 ГБ WAL (лучше даже вычесть с запасом 2 ГБ) и делить его последовательно на каждую из цифр до тех пор, пока не получим значение, близкое к 256 МБ … 1 ГБ. Это значение округлить вниз, принять за базовый размер уровня RocksDB и прописать в конфиг как max_bytes_for_level_base. База компактится по 256 МБ за раз, так что меньше 256 МБ размер первого уровня ставить точно смысла нет. Например, для 80 ГБ раздела это будет 719 МБ, только не забываем считать всё в двоичных мегабайтах — MiB. Остаётся прописать это значение в конфигурацию (bluestore_rocksdb_options = …,max_bytes_for_level_base=719MB), перезапустить OSD и сделать ручной compaction (можно дважды).
|
|
||||||
|
|
||||||
**Нюанс № 2:** При ручном compaction-е RocksDB переписывает уровни целиком. Если при этом на SSD нет запаса места в размере этого уровня, то уровень, опять-таки, утечёт на HDD и так там и останется, ибо перемещать после compaction-а его обратно она не умеет. Теоретически, если после этого сделать compaction ещё раз, то уровень должен вернуться на SSD (поэтому выше дана рекомендация делать ручной compaction дважды). Однако по сведениям из чата якобы бывает так, что один-два файла *.sst на SSD не возвращается. Чтобы это побороть на 100 %, можно предусмотреть на SSD-разделе ещё и запас в размере первого + последнего уровня БД. В этом случае коэффициенты вместо 1-11-111-1111 превращаются в 2-22-212-2112 и т. п.
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
RGW vs Minio
|
|
||||||
|
|
||||||
Вопрос частый, так как Ceph и Minio — две наиболее распространённые реализации S3.
|
|
||||||
|
|
||||||
Сравнение, как всегда, не совсем честное, так как в Minio «бесконечного масштабирования» и произвольных схем избыточности нет. Есть только erasure коды, которые оперируют группами дисков, кратными по количеству 4 или 16 дискам. Расширения кластера в Minio раньше не было вообще, потом в каком-то смысле появилось через создание дополнительных зон.
|
|
||||||
|
|
||||||
Таких же гарантий целостности, как в Ceph, в Minio тоже нет. Minio работает поверх обычных ФС, даже не делая fsync данных. На практике ext4, правда, делает sync автоматически раз в 5 секунд, да и Minio пишет с O_DIRECT, так что не совсем всё плохо — но тем не менее, потенциально небольшие потери при отключении питания возможны.
|
|
||||||
|
|
||||||
Особенно классный перл был в баге [https://github.com/minio/minio/issues/3478](https://github.com/minio/minio/issues/3478):
|
|
||||||
|
|
||||||
> Minio in this case is working as intended, minio cannot be expanded or shrinkable in this manner. Minio is different by design. It is designed to solve all the needs of a single tenant. Spinning minio per tenant is the job of external orchestration layer. Any addition and removal means one has to rebalance the nodes. When Minio does it internally, it behaves like blackbox. It also adds significant complexity to Minio. Minio is designed to be deployed once and forgotten. We dont even want users to be replacing failed drives and nodes. Erasure code has enough redundancy built it. By the time half the nodes or drives are gone, it is time to refresh all the hardware. If the user still requires rebalancing, one can always start a new minio server on the same system on a different port and simply migrate the data over. It is essentially what minio would do internally. Doing it externally means more control and visibility. Minio is meant to be deployed in static units per tenant.
|
|
||||||
|
|
||||||
Короче, всё работает как надо, в минио нет возможности расширения, если у вас будут ломаться диски — не меняйте, просто дождитесь, пока из строя выйдет половина дисков и пересоздайте кластер. На самом деле всё не так печально, можно заменить диск и запустить heal, но, конечно, без той же прозрачности, что в Ceph — оно будет просто сканировать все объекты и проверять отсутствующие. Если дисков много, это очень накладно.
|
|
||||||
|
|
||||||
Ещё Minio хранит объекты в виде обычных файлов, даже не шардируя каталоги (соответствующие бакетам) по подкаталогам, плюс на каждый объект ещё создаёт директорию с парой файлов метаданных. Ну а директории в ФС по миллиону файлов — это, естественно, удовольствие ниже среднего. Хотя просто для раздачи оно, благодаря всяким dir_index-ам, работает.
|
|
||||||
|
|
||||||
Для представления о производительности проведём простой тест Ceph (bluestore) vs Minio (ext4) на 1 HDD. Да, я знаю, что это тупо и нужно ещё хотя бы посравнивать их на SSD. Но всё-таки результаты довольно показательны. Да и объектное хранилище чаще холодное/прохладное и строится на HDD, а не на SSD.
|
|
||||||
|
|
||||||
Тест делался через [hsbench](https://github.com/vitalif/hsbench). Заключался в заливке примерно 1.1 миллиона объектов в 1 бакет, потом сброса кэшей и перезапуска Ceph/Minio, и потом — их раздачи в случайном порядке, а также проверки скорости выполнения операций листингов. Результаты:
|
|
||||||
|
|
||||||
- Заливка в 32 потока: Minio — 305 объектов в секунду, RGW — 135 объектов в секунду. RGW indexless — 288 объектов в секунду.
|
|
||||||
- Раздача в 32 потока: Minio — 45 объектов в секунду, RGW — 78 объектов в секунду
|
|
||||||
- Листинги в 32 потока: Minio — после сброса кэша 35 сек, после прогрева — 2.9 сек с разбросом от 0.5 до 16 сек. RGW — стабильно — 0.4 сек
|
|
||||||
|
|
||||||
Да, заливка в Minio быстрее. Но, во-первых, меньшая скорость заливки — это цена, во-первых, консистентности (fsync), а во-вторых, bucket index-а и bucket index log-а, которые позволяют RGW, например, делать геосинхронизацию (multisite), чего в Minio нет.
|
|
||||||
|
|
||||||
Кроме того, индексы в RGW можно положить на отдельные SSD (как обычно все и делают), а если же вам совсем не нужны листинги, синхронизация и прочее, в Ceph бакеты можно сделать безиндексными (indexless), и тогда оверхед bucket index-а вообще исчезает, как и возможные проблемы с его шардированием.
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Снапшоты
|
|
||||||
|
|
||||||
В реализации снапшотов в Ceph есть целый ворох проблем из-за количества параллельных реализаций и общего накопленного архитектурного бардака:
|
|
||||||
|
|
||||||
- На уровне RADOS снапшоты свои и их уже 2 вида: снапшоты пулов и снапшоты объектов.
|
|
||||||
- На уровне RBD тоже свои снапшоты, причём они отличаются от RADOS-снапшотов, хоть внутри и реализованы частично через них. RBD снапшоты, можно сказать, не юзабельны ни для чего, кроме быстрого снятия бэкапов, за которым тут же следует удаление снапшота. Откат к RBD снапшотам очень медленный и реализован не просто ужасно, а отвратительно — копированием содержимого снапшота поверх образа, _даже неизменённых частей_. Кроме того, при откате цепочка последующих снапшотов уничтожается. Кроме того, есть прикол с жором места — см. на 2 пункта ниже.
|
|
||||||
- В Bluestore снапшоты эффективны — после снятия снапшота случайная запись практически не замедляется, в отличие от Filestore, где принцип работы снапшотов схож со старым LVM и при записи даже 4 КБ после снятия снапшота соответствующий 4 МБ объект копируется целиком.
|
|
||||||
- Но при этом эта оптимизация снапшотов — «виртуальные клоны» — реализована именно на уровне Bluestore, ни клиент, ни OSD о ней ничего не знают и поэтому она… ломается при ребалансе. Выглядит это так: ты добавляешь в кластер диск, он заполняется, а доступного места больше не становится. Почему? Потому что изначально, при записи, Bluestore получил запрос «клонировать 4 МБ объект в новую версию» и сделал это внутри себя, реально не копируя данные, а потом перезаписал 4 КБ. А при ребалансе эта связь порвалась и объект стал занимать все 4 МБ… Fail.
|
|
||||||
- Также на уровне RBD есть клоны, которые, с одной стороны, более юзабельны — реализованы они через ссылку на родительский образ, соответственно, откат к такому «снапшоту» быстрый — достаточно просто создать новый клон. С другой стороны, для клонов не работает эта самая блюсторовская оптимизация, поэтому они опять-таки копируют объекты целиком при любой записи… что выливается в 40 иопс на запись (QD=1) в свежий клон даже в NVMe кластере.
|
|
||||||
- В CephFS ещё одна реализация снапшотов, и там отката нет вообще.
|
|
||||||
|
|
||||||
В общем, наговнокодили — мама не горюй…
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
EC
|
|
||||||
|
|
||||||
EC ещё больше снижает iops-ы, так как добавляется цикл Read-Modify-Write. [#RAID WRITE HOLE](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_mobile#RAID_WRITE_HOLE) в цефе закрыт, поэтому при записи все OSD сначала делают вторые копии объекта (по-видимому, виртуальные, через тот же механизм virtual clone bluestore), а потом удаляют старые.
|
|
||||||
|
|
||||||
В моём примере на NVMe-шках — write iops с репликацией Q=1 примерно 1500, а с EC — примерно 500. Q=128 — примерно 25000 с репликацией и 10000 с EC.
|
|
||||||
|
|
||||||
### Про вероятность потери данных
|
|
||||||
|
|
||||||
Смотрите мой калькулятор вероятности потери данных в кластере: [https://yourcmc.ru/afr-calc/](https://yourcmc.ru/afr-calc/)
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Контроллеры
|
|
||||||
|
|
||||||
- SATA — это нормально, SAS не обязателен от слова «совсем». SATA за счёт того, что «не умничает», достаточно быстрая и точно лучше, чем старые RAID контроллеры.
|
|
||||||
- Разница в IOPS между RAID и HBA/SATA может быть колоссальна. В производительность не самого нового RAID контроллера легко упереться. Плохо даже не то, что на 1 диск вы получите 48000 iops вместо 60000, хуже то, что при подключении 8 дисков вы получите 6000 iops на каждый диск вместо 60000, так как 48000 поделятся на всех. Также в RAID режиме увеличивается задержка в 1 поток.
|
|
||||||
- Так что свой RAID контроллер либо переключите в режим passthrough (если он умеет), либо перепрошейте, чтобы умел, либо выкиньте в помойку и купите HBA («RAID без RAID-функционала», например, LSI 9300-8i). Это актуально для всех видов программных хранилок — Ceph, ZFS и т. п.
|
|
||||||
- Если не выкинули RAID — отключайте все кэши контроллера, чтобы уменьшить влияние прослойки и не страдать при разряде батарейки / перемещении диска в другой сервер... и молитесь :). Наверное, в теории можно выжить и с включенным кэшем, но это стрельба себе в ногу.
|
|
||||||
- Даже если у вас HBA — имейте в виду, что некоторые HBA (в частности, Adaptec) могут всё равно не сбросить кэш корректно и устроить вам Cloudmouse при отключении питания. Но по крайней мере точно известно, что LSI ведут себя нормально.
|
|
||||||
- Хороший пост про RAID-кэши в списке рассылки: [http://lists.ceph.com/pipermail/ceph-users-ceph.com/2019-July/036237.html](http://lists.ceph.com/pipermail/ceph-users-ceph.com/2019-July/036237.html) - если вкратце - человек, админивший 6000 OSD, пишет, что никогда больше не свяжется с RAID0-режимами.
|
|
||||||
- У HBA тоже есть предел IOPS. К примеру, у LSI 9211-8i это ~280000 iops на весь контроллер.
|
|
||||||
- При подключении через SATA или HBA контроллер не забывайте для **серверных** SATA дисков сделать hdparm -W 0 /dev/sdX, для SAS — sdparm --set WCE=0 /dev/sdX.
|
|
||||||
- Для SAS и NVMe включайте blk-mq (ну или юзайте свежие ядра, в районе 4.18 оно включается по умолчанию). Но для SATA blk-mq обычно бесполезен или почти бесполезен.
|
|
||||||
- Фактическая глубина очереди, используемая Ceph OSD при случайной записи, редко больше 10 (посмотреть можно при работе утилитой iostat -xmt 1).
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Процессоры
|
|
||||||
|
|
||||||
- На SSD Ceph ОЧЕНЬ СИЛЬНО упирается в процессор. Можно сказать, что процессор — основной bottleneck.
|
|
||||||
- Как сказано в презентации Ника Фиска — Ceph is a Software-Defined Storage **and every piece of Ceph «Software»** will run faster with every GHz of CPU frequency.
|
|
||||||
- Кроме частоты, на серверных процессорах часто наличествует NUMA (Non-Uniform Memory Access). То есть, часть памяти и оборудования доступна процессору напрямую, а часть — только через другой процессор.
|
|
||||||
- Для максимизации производительности конфигураций с NUMA лучше избегать, а процессорам с бОльшим числом ядер и меньшей частотой лучше предпочитать бОльшую частоту и меньшее число ядер…
|
|
||||||
- …но в пределах разумного, так как даже один OSD на серверном SSD под нагрузкой может спокойно выжрать на 100 % ядер 6.
|
|
||||||
- Под частотой подразумевается номинальная частота, а не Turbo Boost, так как оный актуален только для однопоточных нагрузок.
|
|
||||||
- Рекомендации по привязке OSD к отдельным CPU (taskset), можно сказать, неактуальны, так как Ceph OSD сильно многопоточные — при записи постоянно активно как минимум 4 потока, и ограничение их несколькими ядрами сильно урезает производительность.
|
|
||||||
- Есть два параметра, которые регулируют число рабочих потоков OSD — osd_op_num_shards и osd_op_num_threads_per_shard…
|
|
||||||
- …Но менять их бесполезно, поднять производительность таким образом не получается абсолютно, дефолтные значения (1x5 на HDD и 2x8 на SSD) оптимальны. kv_sync_thread-то всё равно только один.
|
|
||||||
- Есть одна мера, которая помогает поднять производительность сразу раза в 2-3: отключение экономии энергии процессором:
|
|
||||||
- cpupower idle-set -D 0 — отключает C-States (либо опции ядра processor.max_cstate=1 intel_idle.max_cstate=0)
|
|
||||||
- cpupower frequency-set -g performance или (старое) for i in $(seq 0 $((`nproc`-1))); do cpufreq-set -c $i -g performance; done — отключает снижение частоты через множитель
|
|
||||||
- После этих двух команд процессор начинает греться как ПЕЧ, но iops-ы увеличиваются сразу раза в 2 (а то и 3)
|
|
||||||
- Также жор CPU — одна из причин НЕ делать из Ceph «гиперконвергентное облако» (в котором совмещены узлы хранения и запуска виртуальных машин)
|
|
||||||
- Ещё можно отключить все mitigation-ы аппаратных уязвимостей: noibrs noibpb nopti nospectre_v2 nospectre_v1 l1tf=off nospec_store_bypass_disable no_stf_barrier
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Сеть
|
|
||||||
|
|
||||||
- Разумеется, 10 Гбит/с или быстрее
|
|
||||||
- MTU 9000: ip l set enp3s0f0 mtu 9000
|
|
||||||
- Отключить оффлоады: ethtool -K enp3s0f0 gro off gso off tso off lro off sg off
|
|
||||||
- Отключить объединение прерываний: ethtool -C enp3s0f0 rx-usecs 0
|
|
||||||
- Самый дешёвый 10G свитч с Ebay: Quanta LB6M / Brocade TurboIron 24X — но говно старое унылое и жрёт под 200 ватт, и греется и жужжит соответственно
|
|
||||||
- UPD Самый дешёвый свитч с Aliexpress: [TP-LINK TL-ST5008F](https://aliexpress.ru/item/1005004429524441.html). На чипе [Realtek RTL9303-CG](https://www.realtek.com/en/products/communications-network-ics/item/rtl9303-cg)
|
|
||||||
|
|
||||||
Если совсем задолбала латенси, как отключить ВСЕ оффлоады?
|
|
||||||
|
|
||||||
for i in rx tx tso ufo gso gro lro tx nocache copy sg txvlan rxvlan; do
|
|
||||||
/sbin/ethtool -K eth3 $i off 2>&1 > /dev/null;
|
|
||||||
done
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Настройка виртуалок и ФС
|
|
||||||
|
|
||||||
С дефолтными опциями qemu подключает RBD, увы, криво.
|
|
||||||
|
|
||||||
Криво — это значит, что:
|
|
||||||
|
|
||||||
а) используется медленная эмуляция lsi-контроллера
|
|
||||||
|
|
||||||
б) неправильно настроен кэш
|
|
||||||
|
|
||||||
### Драйвер виртуального диска и ФС
|
|
||||||
|
|
||||||
В qemu есть следующие способы эмуляции дисков:
|
|
||||||
|
|
||||||
- lsi — самый медленный
|
|
||||||
- virtio — самый быстрый, но до QEMU 4.0 не умел TRIM. Юзать надо его.
|
|
||||||
- virtio-scsi — достаточно быстрый. На самом деле, умеет multiqueue и поэтому на быстром хранилище (например, при прямом доступе к локальной NVMe) должен быть быстрее virtio — но в случае с Ceph это не так, так как цеф не настолько быстрый, чтобы multiqueue играл роль
|
|
||||||
- nvme — тоже достаточно быстрый, но немного медленнее virtio. Принцип работы похожий — и там, и там кольцевые буферы. Можно использовать для тех образцов ПО, которые не умеют virtio, потому что не уметь nvme сейчас совершенно точно не может никто. Например, если хочется потестировать VMWare ESXi с вложенной виртуализацией внутри QEMU, то nvme — для вас!
|
|
||||||
|
|
||||||
### cache=writeback
|
|
||||||
|
|
||||||
Кэш дисков в qemu регулируется опцией, собственно, cache. Бывает <не указано>, writethrough, writeback, none, unsafe, directsync.
|
|
||||||
|
|
||||||
С Ceph RBD эта опция регулирует работу rbd cache, то есть кэша на стороне клиентской библиотеки Ceph (librbd). RBD cache маленький (32 МБ) и является аналогом буфера записи на HDD, то есть, предназначен исключительно для группировки операций записи перед, собственно, отправкой их в хранилище.
|
|
||||||
|
|
||||||
Режимы writethrough, <не указано> и directsync с RBD, по сути, эквивалентны и означают отсутствие кэширования записи (каждая операция отправляется сразу в Ceph).
|
|
||||||
|
|
||||||
Режим writeback означает «честное» (безопасное) кэширование записи. То есть, операции записи без fsync ложатся в кэш и отправляются оттуда либо раз в rbd_cache_max_dirty_age (по умолчанию 1 сек), либо когда ВМ просит fsync.
|
|
||||||
|
|
||||||
Режим unsafe означает «нечестное» (чреватое потерей данных) кэширование записи. Операции записи также ложатся в кэш, но fsync от ВМ игнорируются. Транзакционность в виртуалке отсутствует, но и производительность повышается. Использовать этот режим можно только для виртуалок, не содержащих ценные данные (например, для каких-нибудь тестов или CI).
|
|
||||||
|
|
||||||
При среднем применении правильный режим — **cache=writeback**. Грубо говоря, cache=writeback увеличивает результат следующего теста:
|
|
||||||
|
|
||||||
fio -name=test -ioengine=libaio -direct=1 -bs=4k -iodepth=1 -rw=randwrite -filename=/dev/vdb # без -fsync и без -sync
|
|
||||||
|
|
||||||
…примерно приводя его к результату того же теста с iodepth=32.
|
|
||||||
|
|
||||||
 ОДНАКО, есть НЮАНСЫ (как всегда с цефом…):
|
|
||||||
|
|
||||||
- Если RBD-образ пустой и при этом на нём включён object-map (по умолчанию включён, см. rbd info <pool>/<image>), то сброс кэша становится однопоточным И ФИГ ВЫ ВИДИТЕ улучшение результата fio.
|
|
||||||
- Если опция rbd_cache_writethrough_until_flush равна true (так по умолчанию), то до первого fsync кэш не работает. То есть, если вы монтируете к виртуалке диск и сразу тестируете его вышеприведённой командой — вы опять-таки не увидите хороших результатов.
|
|
||||||
- Из-за той же опции, стоящей в true, cache=unsafe не работает вообще. В Proxmox включен патч, который заставляет qemu ставить эту опцию автоматически. Если у вас не Proxmox — опцию нужно прописать в конфиг глобально: rbd_cache_writethrough_until_flush=false.
|
|
||||||
- Сам кэш может являться тормозом в SSD-кластерах. Что-то там сделано с блокировками, что-то там однопоточное, всё это оптимизируют, но пока не оптимизировали. Например, 4K randwrite Q128 с rbd_cache=false может достигать 20000 iops, а с rbd_cache=true — только 11000 iops. Для говнософта, который пишет на диск абы как, это торможение оправдано, а для, например, СУБД — нет. Поэтому в случае, если у вас SSD и вам нужны в первую очередь random iops, то правильный режим — **cache=none**.
|
|
||||||
|
|
||||||
### ФС
|
|
||||||
|
|
||||||
А ещё тормозит файловая система! Конкретно, если у вас не включена опция mount -o lazytime, то при каждой мелкой записи ФС обновляет mtime, то есть время модификации inode-а. Так как это метаданные, а ФС журналируемые — это изменение журналируется. Из-за этого при тесте fio -sync=1 -iodepth=1 -direct=1 поверх ФС без lazytime iops-ы уменьшаются в 3-4 раза.
|
|
||||||
|
|
||||||
Для lazytime нужны свежие ядра: с ext4 — хотя бы 4.0, с XFS — хотя бы 4.17. Также нужны соответствующие (свежие) util-linux (подсказка: в протухшей 7-й центоси их нет, поставить можно только из исходников).
|
|
||||||
|
|
||||||
А если у вас (не дай бог) внутри Oracle, то ему надо обязательно поставить опцию FILESYSTEMIO_OPTIONS=SETALL.
|
|
||||||
|
|
||||||
Производительность случайной записи в CephFS почти не отличается от RBD.
|
|
||||||
|
|
||||||
Производительность случайной записи в CephFS через mount -t cephfs и через ceph-fuse… при iodepth=1 почти не отличается. А вот при iodepth=128 ядерный клиент ведёт себя нормально, а ceph-fuse выдаёт столько же, сколько при iodepth=1 (то есть на порядок/порядки меньше, чем ядерный клиент).
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Оценка производительности кластера
|
|
||||||
|
|
||||||
Оценка производительности кластера просто по спецификациям входящих в него дисков — абсолютно неправильна.
|
|
||||||
|
|
||||||
Речь о Bluestore. Под iops понимаются iops случайного чтения/записи блоками по 4 кб.
|
|
||||||
|
|
||||||
1 HDD (обычный, 7200 rpm, не SMR, без ssd-кэша) — это:
|
|
||||||
|
|
||||||
- ~100-120 iops при QD=128
|
|
||||||
- ~66 iops при QD=1
|
|
||||||
- ~40 MB/s линейного чтения/записи
|
|
||||||
- При недостатке памяти показатели будут хуже, так как будут cache miss по метаданным
|
|
||||||
|
|
||||||
1 быстрый SSD или NVMe SSD (с конденсаторами, write iops >= 25000):
|
|
||||||
|
|
||||||
- ~1000 iops на запись при QD=1, в зависимости от частоты CPU и настроек может быть от 300 до, в самом идеальном случае, ~2500 iops.
|
|
||||||
- Максимум до ~10000-20000 iops на запись при QD=128 на 1 OSD.
|
|
||||||
- На чтение показатели примерно в 2-2.5 раза лучше: QD=1 ~2000 iops (максимум до ~4000), QD=128 ~20000, максимум до 50000.
|
|
||||||
- Естественно, показатель QD=128 не может быть больше, чем показатель самого диска :). Но так как хорошие SSD в параллельном режиме обычно быстрые — этого не замечаешь.
|
|
||||||
- Создав несколько OSD на одном диске, можно умножить число QD=128 iops на число OSD (пока диск позволяет). Естественно, ценой аналогичного увеличения жора CPU.
|
|
||||||
- Скорость линейного чтения/записи примерно равна скорости линейного чтения/записи самого диска.
|
|
||||||
- Кардинальной разницы по iops между SATA SSD и даже NVMe, если у обоих есть конденсаторы — нет. Это не отменяет того, что NVMe лучше, но на случайном доступе это заметить трудно.
|
|
||||||
- На актуальных SSD чтение QD=1 часто бывает медленнее записи QD=1, так как писать можно в быстрый кэш, а читать можно только из реальной памяти. Разница бывает примерно такая: на чтение при QD=1 8000 iops, а на запись 40000 iops.
|
|
||||||
|
|
||||||
Итоговые показатели:
|
|
||||||
|
|
||||||
- Линейное чтение из кластера = Число OSD * MB/s одного OSD
|
|
||||||
- Линейная запись в реплицированный пул = Число OSD / Репликация * MB/s одного OSD
|
|
||||||
- Линейная запись в EC-пул = Число OSD / (K+M) * K * MB/s одного OSD
|
|
||||||
- С iops всё сложнее, так как репликация вносит нелинейный эффект в масштабирование. Но, условно говоря:
|
|
||||||
- iops QD=1 — усредняются по всем OSD, а iops QD=128 — суммируются
|
|
||||||
- При этом на 1 RBD клиента можно получить лишь до ~30000 iops на чтение и ~15000 iops на запись
|
|
||||||
- Естественно, если что-то упирается в сеть — соответствующая цифра обрезается показателем сети
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Картина маслом «Тормозящий кэш»
|
|
||||||
|
|
||||||
### O_SYNC vs fsync vs hdparm -W 0
|
|
||||||
|
|
||||||
У SATA и SCSI дисков есть два способа сброса кэша: команда FLUSH CACHE и флаг FUA (Force Unit Access) на команде записи. Первый — это явный сброс кэша, второй — это указание записать операцию на диск, минуя кэш. Точнее, у SCSI оно есть, а с SATA ситуация точно не ясна: в спецификации NCQ бит FUA есть, но по факту FUA большинством дисков вроде как не поддерживается и, соответственно, эмулируется ядром/контроллером (если контроллер не кривой).
|
|
||||||
|
|
||||||
По всей видимости, fsync() отправляет диску команду FLUSH CACHE, а открытие файла с O_SYNC устанавливает бит FUA на все команды записи.
|
|
||||||
|
|
||||||
Есть ли разница? Обычно нет. Но на некоторых контроллерах и/или с некоторыми настройками различия по неустановленным причинам встречаются. И тогда fio -sync=1 и fio -fsync=1 начинают давать разные результаты — возможно, даже на порядки разные результаты.
|
|
||||||
|
|
||||||
Кроме того, у дисков есть команда отключения кэша. Когда он отключен, запросы сброса (fsync) Linux диску не отправляет. Казалось бы, такой режим тоже должен быть эквивалентен выполнению fsync и/или O_SYNC после каждой команды. Но и это не всегда так! На SSD с конденсаторами (то есть серверных моделях с Advanced Power Loss Protection) при отключении кэша iops-ы случайной записи часто вырастают на порядок (например, с 5000 до 40000). Но не всегда, так как это, опять-таки, зависит от контроллера.
|
|
||||||
|
|
||||||
Почему? По-видимому, потому, что команда FLUSH CACHE трактуется диском как «сбрось все кэши» (включая энергонезависимый), а отключение кэша — как «отключи энергозависимый кэш» (а энергонезависимый можешь оставить включенным). Соответственно, запись со сбросом кэша становится медленнее, чем просто отключённый кэш.
|
|
||||||
|
|
||||||
А что с NVMe? В NVMe разнообразие чуть меньше — возможность отключить кэш в спецификации не предусмотрена вообще, но точно так же есть команды FLUSH CACHE и бит FUA. При этом по личным наблюдениям FUA часто игнорируется то ли диском, то ли Linux-ом, и fio -sync=1 выдаёт с NVMe такие же результаты, как и без sync вообще. -fsync=1 при этом ведёт себя как надо и приземляет производительность туда, где ей самое место (на десктопных NVMe — до тех же 1000—2000 iops).
|
|
||||||
|
|
||||||
P.S: Bluestore использует fsync. Filestore использует O_SYNC.
|
|
||||||
|
|
||||||
### Серверные SSD
|
|
||||||
|
|
||||||
Disabling cache is not a joke!
|
|
||||||
|
|
||||||
fio -ioengine=libaio -name=test -filename=/dev/sdb -(sync|fsync)=1 -direct=1 -bs=(4k|4M) -iodepth=(1|32|128) -rw=(write|randwrite)
|
|
||||||
|
|
||||||
**Micron 5100 Eco 960GB**
|
|
||||||
|
|
||||||
**Запись**
|
|
||||||
|
|
||||||
|sync или fsync|bs|iodepth|rw|hdparm -W 1|hdparm -W 0|
|
|
||||||
|---|---|---|---|---|---|
|
|
||||||
|sync|4k|1|write|612 iops|22200 iops|
|
|
||||||
|sync|4k|1|randwrite|612 iops|22200 iops|
|
|
||||||
|sync|4k|32|randwrite|6430 iops|59100 iops|
|
|
||||||
|sync|4k|128|randwrite|6503 iops|59100 iops|
|
|
||||||
|sync|4M|32|write|469 MB/s|485 MB/s|
|
|
||||||
|fsync|4k|1|write|659 iops|25100 iops|
|
|
||||||
|fsync|4k|1|randwrite|671 iops|25100 iops|
|
|
||||||
|fsync|4k|32|randwrite|695 iops|59100 iops|
|
|
||||||
|fsync|4k|128|randwrite|701 iops|59100 iops|
|
|
||||||
|fsync|4M|32|write|384 MB/s|486 MB/s|
|
|
||||||
|
|
||||||
**Чтение**
|
|
||||||
|
|
||||||
|bs|iodepth|rw|результат|
|
|
||||||
|---|---|---|---|
|
|
||||||
|4k|1|randread|6000 iops|
|
|
||||||
|4k|4|randread|15900 iops|
|
|
||||||
|4k|8|randread|18500 iops|
|
|
||||||
|4k|16|randread|24800 iops|
|
|
||||||
|4k|32|randread|37400 iops|
|
|
||||||
|4M|1|read|460 MB/s|
|
|
||||||
|4M|16|read|514 MB/s|
|
|
||||||
|
|
||||||
Результаты по чтению не отличаются на hdparm -W 0 и 1.
|
|
||||||
|
|
||||||
**Seagate Nytro 1351 XA3840LE10063**
|
|
||||||
|
|
||||||
Диск заполнен почти полностью, на 90-100 %.
|
|
||||||
|
|
||||||
**Запись**
|
|
||||||
|
|
||||||
|sync или fsync|bs|iodepth|rw|hdparm -W 1|hdparm -W 0|
|
|
||||||
|---|---|---|---|---|---|
|
|
||||||
|sync|4k|1|randwrite|18700 iops|18700 iops|
|
|
||||||
|sync|4k|4|randwrite|49400 iops|54700 iops|
|
|
||||||
|sync|4k|32|randwrite|51800 iops|65700 iops|
|
|
||||||
|sync|4M|32|write|516 MB/s|516 MB/s|
|
|
||||||
|fsync=1|4k|1|randwrite|288 iops|18100 iops|
|
|
||||||
|fsync=1|4k|4|randwrite|288 iops|52800 iops|
|
|
||||||
|fsync=4|4k|4|randwrite|1124 iops|53500 iops|
|
|
||||||
|fsync=1|4k|32|randwrite|288 iops|65700 iops|
|
|
||||||
|fsync=32|4k|32|randwrite|7802 iops|65700 iops|
|
|
||||||
|fsync=1|4M|32|write|336 MB/s|516 MB/s|
|
|
||||||
|
|
||||||
**Чтение**
|
|
||||||
|
|
||||||
|bs|iodepth|rw|результат|
|
|
||||||
|---|---|---|---|
|
|
||||||
|4k|1|randread|8600 iops|
|
|
||||||
|4k|4|randread|21900 iops|
|
|
||||||
|4k|8|randread|30500 iops|
|
|
||||||
|4k|16|randread|39200 iops|
|
|
||||||
|4k|32|randread|50000 iops|
|
|
||||||
|4M|1|read|508 MB/s|
|
|
||||||
|4M|16|read|536 MB/s|
|
|
||||||
|
|
||||||
Если не хотите 288 иопс — отключайте кэш.
|
|
||||||
|
|
||||||
### Ceph HDD+SSD
|
|
||||||
|
|
||||||
Дано: 3 компа с 4x 7200rpm SATA HDD, с 1 SSD (десктопным) под систему и ceph-mon и с 1 SSD (старым, но серверным, 25000 iops) под журналы. Не самая быстрая 10-гигабитная сеть — флуд пингом средний RTT (задержка) 0.098ms. Развёрнут Ceph + OpenNebula с KVM. Диски под Ceph отформатированы в Bluestore утилитой ceph-volume (то есть используется LVM). Диски виртуалок лежат в обычном реплицированном ceph pool с size=3.
|
|
||||||
|
|
||||||
Создаём Debian-виртуалку (настройки диска kvm по умолчанию — bus=virtio, cache=none), ставим fio, запускаем в ней тест на задержку транзакционной случайной записи: fio -ioengine=libaio -size=10G -sync=1 -direct=1 -name=test -bs=4k -iodepth=1 -rw=randwrite -runtime=60 -filename=./testfile (или можно не случайной, тогда rw=write, но результат идентичный).
|
|
||||||
|
|
||||||
1. Настройки по умолчанию — все кэши дисков включены (везде hdparm -W 1, в /sys/block/*/queue/write_cache везде write back) — Ж О П А, iops=59, avg lat = 16.88ms
|
|
||||||
2. Отключаю кэш записи SSD с журналами: hdparm -W 0 /dev/sdb — остаётся Ж О П А, iops=58, avg lat = 16.99ms
|
|
||||||
3. Всем LVM-девайсам отключаю кэш записи: for i in /sys/block/dm-*; do echo write through > $i/queue/write_cache; done` — А Ф И Г Е Т Ь, iops=584, avg lat = 1.7ms
|
|
||||||
4. Обратно включаю кэш SSDшке с журналами: hdparm -W 1 /dev/sdb — остаётся iops=582, avg lat = 1.7ms
|
|
||||||
5. Откручиваю все отключения кэшей LVM: for i in /sys/block/dm-*; do echo write back > $i/queue/write_cache; done — обратно жопа, 57 iops, avg lat = 17.2ms
|
|
||||||
6. Опять отключаю кэш журнальным LVM-девайсам: for i in `ls /dev/ceph-journals/lvol*`; do j=readlink $i; echo write through > /sys/block/${j##../}/queue/write_cache; done — никакого улучшения, всё та же жопа (но с ними это точно безопасно, так как они с конденсаторами :))
|
|
||||||
7. Отключаю кэш HDD LVM-разделам (for i in `ls /dev/ceph-*/osd-block*`; do j=readlink $i; echo write through > /sys/block/${j##../}/queue/write_cache; done) — бинго, iops=603, avg lat = 1.65ms
|
|
||||||
8. Ага. Простите. Обнаруживаю, что просто писать куда-то write through небезопасно без hdparm -W 0 /dev/sd*, так как [https://www.kernel.org/doc/Documentation/block/queue-sysfs.txt](https://www.kernel.org/doc/Documentation/block/queue-sysfs.txt) - Writing to this file can change the kernels view of the device, but it doesn’t alter the device state. ок, добавляю for i in /dev/sd?; do hdparm -W 0 $i; done (отключаю все кэши) — результат похуже, iops=405, avg lat = 2.47ms — но это всё равно лучше, чем изначальная жопа.
|
|
||||||
|
|
||||||
Виртуалку, в которой тестировал — даже не перезапускал между тестами.
|
|
||||||
|
|
||||||
 Мораль: отключайте кэш записи всем дискам!
|
|
||||||
|
|
||||||
Частичная разгадка:
|
|
||||||
|
|
||||||
1. ~~В жёстких дисках HGST есть Media Cache~~ (энергонезависимый кэш случайной записи прямо на пластинах) — во-первых, там не HGST, во-вторых, в HGST медиакэш включён всегда и не зависит от hdparm -W 0.
|
|
||||||
2. В жёстких дисках Seagate Enterprise Capacity ST8000NM0055 (коих из общего числа 3) есть встроенный SSD-кэш. И вот он, видимо, действительно включается только при hdparm -W 0.
|
|
||||||
3. Блюстор блокирует запись в журнал записью на HDD. Когда включен спец.кэш, HDD начинает рандомно писать сильно быстрее, и блокировки при сбросе уходят. Действует кэш, естественно, временно — когда он кончится, производительность случайной записи опять упадёт. Однако плюс в том, что в Ceph-е этого, скорее всего, не произойдёт, так как скорость случайной записи ограничивается, собственно, самим Ceph-ом и распределяется по всем дискам кластера :).
|
|
||||||
4. Однако, для обычных дисков без SSD-кэша отключение кэша тоже даёт выигрыш… есть гипотеза, что из-за того же тормоза с bluefs (проверю).
|
|
||||||
|
|
||||||
Более свежие тесты бенчилкой ceph-gobench на том же самом стенде. Версия Ceph Mimic 13.2.2, Bluestore, журналы на SSD.
|
|
||||||
|
|
||||||
osd.1 и osd.3 без выноса журналов на SSD оба показывали 75-80 iops вне зависимости от отключения кэша.
|
|
||||||
|
|
||||||
В таблице показаны однопоточные IOPS с глубиной очереди Q=1 без репликации.
|
|
||||||
|
|
||||||
|Номер OSD|Патч + кэш|Кэш выкл|Кэш вкл|Модель диска|Номер модели|
|
|
||||||
|---|---|---|---|---|---|
|
|
||||||
|osd.0|365|308|226|Seagate Constellation ES.2|ST32000645NS|
|
|
||||||
|osd.1|322|325|234|Hitachi Ultrastar 7K3000|HUA723020ALA640|
|
|
||||||
|osd.2|1278|1392|230|Seagate Enterprise Capacity|ST8000NM0055|
|
|
||||||
|osd.4|198|244|140|Hitachi Ultrastar A7K2000|HUA722020ALA330|
|
|
||||||
|osd.5|251|185|154|Hitachi Ultrastar A7K2000|HUA722020ALA330|
|
|
||||||
|osd.7|1351|1129|253|Seagate Enterprise Capacity|ST8000NM0055|
|
|
||||||
|osd.9|399|315|184|Hitachi Ultrastar 7K3000|HUA723020ALA640|
|
|
||||||
|osd.13|226|212|142|Hitachi Ultrastar A7K2000|HUA722020ALA330|
|
|
||||||
|osd.3|393|386|241|Hitachi Ultrastar 7K3000|HUA723020ALA640|
|
|
||||||
|osd.8|340|323|156|Hitachi Ultrastar A7K2000|HUA722020ALA330|
|
|
||||||
|osd.10|1461|1319|273|Seagate Enterprise Capacity|ST8000NM0055|
|
|
||||||
|osd.14|302|229|135|Hitachi Ultrastar A7K2000|HUA722020ALA330|
|
|
||||||
|
|
||||||
#### Примечания
|
|
||||||
|
|
||||||
Toshiba MG07ACA14TE тоже замечены в подобном поведении, см. обсуждение в списке рассылки: [https://www.spinics.net/lists/ceph-users/msg60753.html](https://www.spinics.net/lists/ceph-users/msg60753.html)
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Почему вообще Bluestore такой медленный?
|
|
||||||
|
|
||||||
 Третья редакция hatespeech’а.
|
|
||||||
|
|
||||||
Вкратце ситуация такова: После того, как хранилище Ceph переписали с упоротой архитектуры с хранением объектов в ФС XFS и дополнительным журналом, в которой, по идее, был дикий оверхед — быстрее (в смысле random write iops) оно практически не стало. **Логичный ответ на вопрос «какого х#ра» такой: то ли XFS на самом деле написана очень хорошо, то ли Bluestore на самом деле написан очень плохо.** Сразу скажу: оба ответа верны. :-)
|
|
||||||
|
|
||||||
Все мы держим в уме, что 1x 7200rpm HDD может выдать примерно 100—120 iops. Дальше нам говорят — ну, там типа журналирование. Ну ок, как мы рассуждаем — ну, типа, есть журнал, есть диск. Значит типа вроде как синхронно записало в журнал, потом асинхронно постепенно перенесло на диск. Значит, берём 100, умножаем на число дисков в кластере, делим на фактор репликации (3), делим на 1.5-2 (данные+журнал), мы же держим в уме, что наверняка там всё асинхронно и оптимизировано… Получаем, скажем, 100 * 9 дисков / 1.5-2 / 3 = 150—200 iops. Запускаем fio iodepth=128 на собранном кластере — ОЙ, 30 iops. Как так?
|
|
||||||
|
|
||||||
Отчаиваемся и по советам знатоков прикручиваем туда SSD под wal+db. И думаем: ну, теперь у нас быстрая SSD с конденсаторами под журналом, латенси записи 50 микросекунд, значит должно быть много иопсов — ну, в устоявшемся режиме хотя бы 300 (9 * 100 / 3). Тестируем. В 1 поток получаем ну… 60 иопс. Во много — где-то 200. Опять плохо.
|
|
||||||
|
|
||||||
Смысл в том, что собственной реализации журнала у блюстора нет, есть очередь отложенной записи, живущая прямо в RocksDB. RocksDB — это LSM keyvalue база, по сути база-журнал. В принципе, это достаточно разумно, так как всё равно нужно журналировать изменения метаданных, которые там держит блюстор. Когда очередь отложенной записи засунута туда же, изменение можно коммитить одной транзакцией (соответственно, одним fsync-ом).
|
|
||||||
|
|
||||||
И в этой схеме есть одно большое отличие от filestore — оно заключается в том, что в filestore журнал работал как буфер для временного повышения нагрузки на запись. Пока в журнале было место, случайная запись была очень быстрой, а журналы обычно делали размеров в несколько гигабайт. В bluestore же «очередь отложенной записи» очень маленькая и сбрасывается через каждые 64 запроса. То есть, bluestore не пишет быстрее, чем в среднем может медленное устройство (HDD).
|
|
||||||
|
|
||||||
Плюс к этому на практике (при просмотре strace) оказывается, что fsync-ов на каждую операцию записи делается не 1, а 2. Второй fsync — это лишняя транзакция записи в журнал BlueFS, сводящаяся к обновлению размера лог-файла RocksDB. Это нафиг не нужно, так как в опциях RocksDB в цефе по умолчанию стоит wal_recovery_mode=kTolerateCorruptedTailRecords и recycle_log_number=4, но это так, потому что без этого у них из-за другого бага корраптятся данные при падении OSD. Что на самом деле исправляется легко, я им даже отправил фикс — [https://tracker.ceph.com/issues/38559](https://tracker.ceph.com/issues/38559) [https://github.com/ceph/ceph/pull/26909](https://github.com/ceph/ceph/pull/26909) - и они даже вроде как обещают его влить. С фиксом на HDD ускорение случайной записи при глубине очереди 1 — двукратное (с 33 % до 66 % возможностей самого HDD, обычно как раз с 33 до 66 иопс). При глубине очереди 128 — почти нулевое.
|
|
||||||
|
|
||||||
ОК, ладно. В конце концов мы решаем — гулять так гулять и собираем кластер на серверных SSD (или вообще NVMe). Думаем — ну теперь-то?!… Бенчим в 1 поток. 300 иопс. Охреневаем окончательно и идём гуглить эту статью :)
|
|
||||||
|
|
||||||
Здесь смысл в том, что в голове у всех сидит мысль «а, ну да, оно медленное, потому что слишком много пишет на диск — диск же относительно медленный, а софт быстрый». А вот хрен. :) оказывается, Ceph довольно сложно разогнать до latency < 1 ms, и виной тому не диски, а сам Ceph. То есть да, Ceph при записи мелкими блоками порождает WA (Write Amplification) 3..5 на каждой OSD — это легко посчитать через тот же strace. Но на хороших SSD это практически не занимает времени, одна операция 4кб записи занимает условно 20 микросекунд. Проблема именно в С++ коде Ceph.
|
|
||||||
|
|
||||||
Причём даже не до конца понятно, что конкретно там тормозит — такое ощущение, что всё целиком. Выявить какие-то «горячие точки» при профилировании трудно, просто при записи выполняется много всякой C++ной мелочи, которая суммарно отъедает достаточно много времени. Одно горячее место — вычисление цифровых подписей пакетов (включено по умолчанию, можно отключить), другое — сериализация/десериализация (код обрабатывает каждое поле пакета, чуть ли не каждый байт, отдельным вызовом функции). Дальше идут уже malloc-и, которых тоже происходят тонны. Причём всё это происходит в несколько потоков. На это ещё навёрнута какая-то странная смесь буферизованного и прямого I/O.
|
|
||||||
|
|
||||||
RocksDB не виновата — её я пробовал бенчить, она быстрая, ~8000 транзакций в секунду на NVMe в 1 поток она даёт и даже масштабируется до ~120000 tps в 256 потоков. А Ceph OSD даже с максимальным параллелизмом на той же NVMe даёт только 10-20 тысяч iops.
|
|
||||||
|
|
||||||
Сеть тоже не виновата — её я пробовал бенчить с помощью nbd (network block device). При прямом доступе диск выдаёт 50000 iops, при пробросе диска с одного сервера на другой через nbd — 8000 iops. То есть, добавленная latency сети — примерно 0.1ms. Это не много.
|
|
||||||
|
|
||||||
И даже Bluestore не совсем виноват. На NVMe Bluestore с некоторыми тюнами всё-таки осиливает завершить запись примерно за 0.3мс. И в то же время код самого Ceph сжирает ещё 0.4мс.
|
|
||||||
|
|
||||||
По сути, нужно было бы добиться ситуации, в которой Ceph бы отъедал 0.1ms и Bluestore ещё 0.1ms. Сеть съест ещё 0.1ms и тогда на хорошей NVMe получится latency ~ 0.33ms, что в 1 поток соответствует 3000 операциям записи в секунду. До локальной SSD это всё равно не дотянуло бы, но, тем не менее, было бы уже очень-очень неплохо. Тогда, скажем, можно будет бороться за CPU: поставив самолётные CPU по 20000$ каждый, настроив ядро и снизив время Ceph+Bluestore ещё в 2 раза, вы получите уже 0.23ms = ~4350 iops. А допилив поддержку Infiniband, может, получите и все 6000 iops. Пока же код не оптимизирован… всё это — мёртвому припарки.
|
|
||||||
|
|
||||||
Авторы сейчас пытаются пилить новую реализацию OSD на асинхронном фреймворке Seastar (Crimson OSD), так что у нас есть теоретические шансы увидеть Ceph, который будет в несколько раз быстрее. Хотя, конечно, там вопрос далеко не только в многопоточности и блокировках — по идее, много съедает именно программная логика. Но так как её они фактически тоже частично переписывают с нуля — она тоже может улучшиться.
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
DPDK и SPDK
|
|
||||||
|
|
||||||
- DPDK = Data Plane Developer Kit, быстрая библиотека от Intel для работы с (в основном) сетевыми устройствами в userspace, без задействования драйверов ядра
|
|
||||||
- SPDK = Storage Performance Developer Kit, основанная на DPDK библиотека для работы с NVMe SSD, тоже в пространстве пользователя. Ещё есть форк libnvme — SPDK, отвязанный от DPDK
|
|
||||||
- DPDK включается через ms_type=async+dpdk
|
|
||||||
- SPDK включается для NVMe-шек передачей в качестве пути девайса spdk:<серийный номер pcie устройства> и ручным созданием OSD по инструкции Manual Deployment
|
|
||||||
- Это в теории — на практике (проверялся Mimic 13.2.x) НИ ХРЕНА не работает, ни DPDK, ни SPDK
|
|
||||||
- С DPDK Ceph «из коробки» даже не собирается — это в общем-то довольно легко исправить, но даже когда добиваешься сборки и запуска — OSD падают после обработки ~50 пакетов
|
|
||||||
- С SPDK Ceph собирается и даже собран по умолчанию — но оно опять-таки не работает — вскоре после запуска OSD просто виснет в пространстве
|
|
||||||
- Code is there, так что, вероятно, всё это можно исправить, если подебажить подольше
|
|
||||||
- Есть сообщения, что SPDK всё-таки работает из коробки, просто не даёт никакого выигрыша производительности. Но мне пока завести его не удалось
|
|
||||||
- Однако в силу неоптимальной реализации самого Ceph ни от DPDK, ни от RDMA ожидать ускорения не приходится. Задержка Ceph в 10-100 раз выше сетевой задержки, так что, уменьшая сетевую задержку, добиться практически нечего. Один чувак даже проводил эксперимент — отрезал код AsyncMessenger-а от всего остального цефа и пробовал бенчить его отдельно: [https://www.spinics.net/lists/ceph-devel/msg43555.html](https://www.spinics.net/lists/ceph-devel/msg43555.html) — и получил всего лишь ~80000 iops.
|
|
||||||
- В перспективе SPDK будет на хрен не нужен, так как в ядро приняли штуку под названием io_uring: [https://lore.kernel.org/linux-block/20190116175003.17880-1-axboe@kernel.dk/](https://lore.kernel.org/linux-block/20190116175003.17880-1-axboe@kernel.dk/) - с ней обычный код прокачивает через Optane-ы практически столько же iops, сколько и SPDK, при заметно меньшем объёме геморроя на поддержку работы с SPDK/DPDK
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
RAID WRITE HOLE
|
|
||||||
|
|
||||||
В RAID-е есть один интересный момент: при отказе диска и одновременном отключении питания RAID 5 может кораптить данные.
|
|
||||||
|
|
||||||
Суть такая: допустим, есть три диска в рейд5. Есть какая-то пара блоков данных A и B. Соответственно на дисках хранятся: A, B, A xor B.
|
|
||||||
|
|
||||||
Теперь представим, что мы пишем в блок B данные B2. Для этого нам надо обновить данные на двух дисках: B -> B2, A+B -> A+B2. Теперь представим, что один из них успел записать, а второй не успел. Тут вырубилось питание и одновременно сдох диск A (или диск сдох от умирания питания, или контроллер повис и ядро в панику упало...). Что мы имеем на дисках?
|
|
||||||
|
|
||||||
?, B2, A+B либо ?, B, A+B2.
|
|
||||||
|
|
||||||
Теперь при попытке восстановить A мы получим A+B+B2 => опа! Покорраптились данные, которые даже не записывались!
|
|
||||||
|
|
||||||
Из-за этого raid всегда делает полный resync после нештатного вырубания питания. И, собственно, такая же потеря данных возможна при отказе диска до завершения resync. mdadm RAID5 в таких ситуациях (когда одновременно потерян диск и массив помечен как грязный) просто отказывается стартовать.
|
|
||||||
|
|
||||||
И именно чтоб этого избежать, в цефе сделано полное журналирование всех данных на уровне отдельных дисков (т.е. OSD). Потому что других способов борьбы с этой проблемой НЕТ, а при работе по сети отказы гораздо более вероятны, чем при работе RAID-массива на локальных дисках. Даже write intent bitmap может только сказать вам, потеряли вы какие-то данные или нет, но не может помочь их восстановить, если они потеряны.
|
|
||||||
|
|
||||||
Так что Ceph надёжнее RAID-а. :) медленнее (на SSD). Но надёжнее и не требует resync-а.
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Краткий экскурс в устройство SSD и флеш-памяти
|
|
||||||
|
|
||||||
Особенность NAND флеш-памяти заключается в том, что пишется она мелкими блоками, а стирается большими. Актуальное соотношение для Micron 3D NAND — страница (page) 16 КБ, блок стирания (block) — 16 или 24 МБ (1024 или 1536 страниц, MLC/TLC соответственно). Случайное чтение страницы быстрое. Запись тоже, но писать можно только в предварительно стёртую область — а стирание медленное, да ещё и число стираний каждого блока ограничено — после нескольких тысяч (типичное значение для MLC) блок физически выходит из строя. В более дешёвых и плотных (MLC, TLC, QLC — 2-4 бита на ячейку) чипах лимит стираний меньше, в более дорогих и менее плотных (SLC, один бит на ячейку) — больше. Соответственно, при «тупом» подходе — если при записи каждого блока его просто стирать и перезаписывать — случайная запись во флеш-память, во-первых, будет очень медленной, а во-вторых, она будет быстро выводить её из строя.
|
|
||||||
|
|
||||||
Но почему тогда SSD быстрые? А потому, что внутри SSD на самом деле есть очень мощный и умный контроллер (1-2 гигагерца, типично 4 ядра или больше, примерно как процессоры мобильников), и на нём выполняется нечто, называемое Flash Translation Layer — прошивка, которая переназначает каждый мелкий логический сектор в произвольное место диска. FTL всё время поддерживает некоторое количество свободных стёртых блоков и направляет каждую мелкую случайную запись в новое место диска, в заранее стёртую область. Поэтому запись быстрая. Одновременно FTL делает дефрагментацию свободного места и Wear Leveling (распределение износа), направляя запись и перемещая данные так, чтобы все блоки диска стирались примерно одинаковое количество раз. Кроме того, во всех SSD некоторый % реального места зарезервирован под Wear Leveling и не виден пользователю («overprovision»), а в хороших серверных SSD этот процент весьма большой — например, в Micron 5100 Max это +60 % ёмкости (в Micron 5100 Eco — всего лишь +7.5 %, 1.92 ТБ SSD содержит 10 чипов NAND по 1.5 терабита и 2 по 768 гигабит).
|
|
||||||
|
|
||||||
Именно из наличия FTL вытекает и проблема с энергонезависимостью и «power loss protection»-ом. Карты отображения секторов — это метаданные, которые при сбросе кэша тоже нужно сбрасывать в постоянную память, и именно этот сброс и вносит торможение в работу настольных SSD с fsync.
|
|
||||||
|
|
||||||
Кстати, из размера страницы 16 КБ следует, что когда вы используете десктопную SSD под мелкую транзакционную перезапись, вы ещё и сильно снижаете срок её жизни, так как при перезаписи по 4 КБ с sync-ами write amplification составляет не менее 16 КБ / 4 КБ = 4. _Примечание: в современных SSD распространена такая вещь, как SLC Cache — использование части той же самой флеш-памяти в SLC-режиме для первых X гигабайт записи. При SLC-записи размер той же страницы, вероятно, уменьшается в 3 раза, так как вместо 3 бит в каждой ячейке хранится только 1. С другой стороны, это всё равно та же страница, которая номинально вмещает 16 КБ, так что как тут считать WA, не совсем ясно._
|
|
||||||
|
|
||||||
Дополнение: когда я попытался кого-то в списке рассылки полечить на тему, что «все SSD делают fsync», мне в ответ кинули статью: [https://www.usenix.org/system/files/conference/fast13/fast13-final80.pdf](https://www.usenix.org/system/files/conference/fast13/fast13-final80.pdf). В общем, суть статьи в том, что в 2013 году нормой было то, что SSD вообще не сбрасывали метаданные на диск при fsync, и при отключении питания это приводило к разным весёлым вещам вплоть до (!!!) полного отказа SSD.
|
|
||||||
|
|
||||||
Есть экземпляры старых SSD без конденсаторов (OCZ Vector/Vertex), которые при этом выдают большие iops на запись с fsync. Как это возможно? Неизвестно, но есть предположение, что суть как раз в небезопасности записи. Принцип работы флеш-памяти за последние годы вроде как не менялся — в SSD как раньше был FTL, так и сейчас FTL. Как достигнуть быстрой записи, если постоянно сбрасывать на диск карты трансляции — хз… наверное, если только сделать некое подобие лог-структурированной ФС внутри — писать всё время вперемешку метаданные и данные. Но при этом, по идее, при старте всё это «добро» придётся сканировать и старт/монтирование станет долгим. А в SSD долгого монтирования вроде как нет.
|
|
||||||
|
|
||||||
Ну и, собственно, «power loss protection», видимо, бывает простой, а бывает advanced. Простой означает просто «мы корректно делаем fsync и не сдохнем при отключении питания», а advanced означает наличие конденсаторов и быструю безопасную запись с fsync. Сейчас, в 2018—2019 годах, «обычный» PLP, похоже, всё-таки стал нормой и при отключении питания большая часть SSD терять данные и умирать уже не должна.
|
|
||||||
|
|
||||||
### Бонус: USB-флешки
|
|
||||||
|
|
||||||
А почему тогда USB-флешки такие медленные? Случайная запись на флешку 512-байтными (или 4 Кб) блоками обычно идёт со скоростью 2-3 iops. А флеш-память там примерно та же, что в SSD — ну, более дешёвые и мелкие чипы с меньшими размерами страницы и блока (часто 4 КБ страница и 4 МБ блок), но принцип тот же и разница в скорости не на порядки. Ответ кроется в том, что на флешках тоже есть FTL (и даже Wear Leveling), но по сравнению с SSD-шным он маленький и тупой. У него слабый процессор и мало памяти. Из-за малого объёма RAM контроллеру флешки, в отличие от контроллера SSD, негде хранить полную таблицу сопоставления виртуальных и реальных секторов — поэтому отображаются не сектора, а крупные блоки где-то по мегабайту или больше, и при записи есть лимит на количество «открытых» блоков. Как это происходит:
|
|
||||||
|
|
||||||
- Допустим, вы пишете в сектор X.
|
|
||||||
- Контроллер отображает блок, которому принадлежит этот сектор, на реальный блок, и «открывает» его — выделяет пустой блок, запоминает, что он «дочерний» для открытого и записывает туда один изменённый вами сектор.
|
|
||||||
- Таким макаром можно открыть максимум N разных блоков; число N обычно очень маленькое — от 3 до 6.
|
|
||||||
- Дальше если вы пишете следующий сектор из уже открытого блока — он просто записывается в его дочерний блок (что быстро).
|
|
||||||
- Если же следующий записываемый сектор принадлежит другому блоку — какой-то из открытых блоков приходится закрывать и «сливать» содержимое дочернего блока с оригинальным.
|
|
||||||
|
|
||||||
Для копирования больших файлов на флешку, отформатированную в любую из стандартных файловых систем, двух блоков достаточно: в один открытый блок пишутся данные, во второй — метаданные записываемого файла. Запись последовательная, всё быстро. А вот при случайной записи вы перестаёте попадать в уже «открытые» блоки и каждая операция записи превращается в полное стирание. Тут-то и начинаются тормоза…
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Пример теста от Micron
|
|
||||||
|
|
||||||
Пример самолётного сетапа от Micron с процами по полляма (2x Xeon Platinum 8168), 2x100-гбит сетью (точнее 2x2x100, так как 2 карты по 2 порта) и 10x топовыми NVMe (с конденсаторами, ага) в каждом узле, 4 узла, репликация 2x: [https://www.micron.com/resource-details/30c00464-e089-479c-8469-5ecb02cfe06f](https://www.micron.com/resource-details/30c00464-e089-479c-8469-5ecb02cfe06f)
|
|
||||||
|
|
||||||
Всего 367011 iops на запись в пике на весь кластер, при 100 % загрузке CPU. Казалось бы, довольно много, но если поделить 367000/40 osd — получится 9175 иопс на 1 osd. С учётом репликации на диски нагрузка двойная, выходит, 18350 иопс. Ок, журналы тоже удваивают нагрузку, итого — 36700 iops на запись смог выжать ceph из одной NVMe… которая сама по спеке может 260000 иопс в одиночку. Вот такой вот overhead.
|
|
||||||
|
|
||||||
Данных по задержкам в 1 поток нет (а было бы интересно узнать).
|
|
||||||
|
|
||||||
Примечание: PDF-ка по ссылке обновлена, новый результат с теми же тюнами, что ниже, составил 479882 iops на запись и 2277453 iops на чтение. Старая [тут](https://yourcmc.ru/wiki/images/c/c0/Micron_9200_ceph_3.0_reference_architecture.pdf "Micron 9200 ceph 3.0 reference architecture.pdf"). Что и подтверждает, что разница между Micron 9200 и 9300 для Ceph незаметна.
|
|
||||||
|
|
||||||
### Апдейт
|
|
||||||
|
|
||||||
[https://www.micron.com/-/media/client/global/documents/products/other-documents/micron_9300_and_red_hat_ceph_reference_architecture.pdf](https://www.micron.com/-/media/client/global/documents/products/other-documents/micron_9300_and_red_hat_ceph_reference_architecture.pdf)
|
|
||||||
|
|
||||||
NVMe обновились до Micron 9300 (максимальной ёмкости 12.8 ТБ). iops-ов такие диски дают даже не 260 тыс., а 310 тыс. Всё остальное осталось прежним. Итого 100 клиентами на запись сняли 477029 iops. Однако надо понимать, что каждому клиенту при этом досталось лишь 4770 iops. 10 клиентами сняли 294000 iops — то есть на 1 клиента 29400 иопс, что всё-таки поприличней.
|
|
||||||
|
|
||||||
Почему стало лучше? Предположительно, благодаря тюнингу. По сравнению с прошлым тестом они:
|
|
||||||
|
|
||||||
- отключили чексуммы мессенджера (ms_crc_data=false) и чексуммы блюстора (bluestore_csum_type=none)
|
|
||||||
- затюнили rocksdb: bluestore_rocksdb_options = compression=kNoCompression,max_write_buffer_number=64,min_write_buffer_number_to_merge=32,recycle_log_file_num=64,compaction_style=kCompactionStyleLevel,
|
|
||||||
write_buffer_size=4MB,target_file_size_base=4MB,max_background_compactions=64,level0_file_num_compaction_trigger=64,level0_slowdown_writes_trigger=128,
|
|
||||||
level0_stop_writes_trigger=256,max_bytes_for_level_base=6GB,compaction_threads=32,flusher_threads=8,compaction_readahead_size=2MB
|
|
||||||
- 64x32x4 MB memtable (number x merge x size) вместо стандартных 4x1x256 MB. Скорее всего, это и сыграло основную роль. Эффект не совсем очевиден, но, вероятно, это снижает нагрузку на CPU, потому что поиск в 64 маленьких memtable быстрее, чем поиск в 1 (или 4) больших.
|
|
||||||
- сильно изменён max_bytes_for_level_base — с 256 мб он поднят до 6 гб!
|
|
||||||
- добавлены потоки compaction-а.
|
|
||||||
- выдали 14 гб RAM каждому OSD
|
|
||||||
- osd_max_pg_log_entries=osd_min_pg_log_entries=osd_pg_log_dups_tracked=osd_pg_log_trim_min = 10 (хз, по-моему, ничего не даёт)
|
|
||||||
|
|
||||||
Также надо отметить, что:
|
|
||||||
|
|
||||||
- cephx у них уже был отключён. В этот раз зачем-то добавили и отключение подписей — видимо, читали мою статью. Но это нафиг не надо, при отключенном cephx подписи можно уже не отключать.
|
|
||||||
- debug objecter = 0/0 и вообще отключенные дебаги у них тоже уже были
|
|
||||||
- с prefer_deferred_size и min_alloc_size они, видимо, не игрались
|
|
||||||
- обновлённые диски не играют никакой роли гарантированно. 260000 или 310000 iops — для цефа никакой разницы нет.
|
|
||||||
|
|
||||||
### Бонус: висян (vSAN)
|
|
||||||
|
|
||||||
[Micron Accelerated All-Flash SATA vSAN 6.7 Solution](https://media-www.micron.com/-/media/client/global/documents/products/other-documents/micron_vsan_6,-d-,7_on_x86_smc_reference_architecture.pdf)
|
|
||||||
|
|
||||||
Конфигурация серверов:
|
|
||||||
|
|
||||||
- 384 GB RAM 2667 MHz
|
|
||||||
- 2X Micron 5100 MAX 960 GB (randread: 93k iops, randwrite: 74k iops)
|
|
||||||
- 8X Micron 5200 ECO 3.84TB (randread: 95k iops, randwrite: 17k iops)
|
|
||||||
- 2x Xeon Gold 6142 (16c 2.6GHz)
|
|
||||||
- Mellanox ConnectX-4 Lx
|
|
||||||
- Connected to 2x Mellanox SN2410 25GbE switches
|
|
||||||
|
|
||||||
«Соответствует конфигурации VMWare AF-6, по заявлениям дающей от 50K iops чтения на каждый сервер»
|
|
||||||
|
|
||||||
- 2 реплики (аналог size=2 в цефе)
|
|
||||||
- 4 сервера
|
|
||||||
- 4 ВМ на каждом сервере
|
|
||||||
- 8 диска на каждую ВМ
|
|
||||||
- 4 потока на каждый диск
|
|
||||||
|
|
||||||
Суммарный параллелизм ввода/вывода: 512
|
|
||||||
|
|
||||||
100%/70%/50%/30%/0% write
|
|
||||||
|
|
||||||
- «Baseline» (данные умещаются в кэш): 121k/178k/249k/314k/486k iops
|
|
||||||
- «Capacity» (не умещаются): 51k/66k/90k/134k/363k
|
|
||||||
- Задержка равна 1000*512/IOPS миллисекунд во всех тестах (1000мс * параллелизм / iops)
|
|
||||||
- **Нет тестов задержки с низким параллелизмом**
|
|
||||||
- **Нет тестов линейного чтения/записи**
|
|
||||||
|
|
||||||
Заключение:
|
|
||||||
|
|
||||||
- ~3800 iops на запись в пересчёте на каждый диск данных
|
|
||||||
- ~1600 iops на запись в пересчёте на диск, если данные не влезают в кэш
|
|
||||||
- ~15000 iops на чтение в пересчёте на каждый диск данных
|
|
||||||
- ~11400 iops на чтение в пересчёте на диск, если данные не влезают в кэш
|
|
||||||
- Итого: при параллельной нагрузке в данном тесте vSAN смотрится хуже, чем Ceph
|
|
||||||
- С другой стороны, vSAN гиперконвергентный, а Ceph, в идеале, нет — или тормозит сильнее, чем мог бы
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Модели
|
|
||||||
|
|
||||||
- Micron 5100/5200, 9300. Возможно также 5300, 7300
|
|
||||||
- HGST SN260
|
|
||||||
- Intel P4500
|
|
||||||
|
|
||||||
[https://docs.google.com/spreadsheets/d/1E9-eXjzsKboiCCX-0u0r5fAjjufLKayaut_FOPxYZjc](https://docs.google.com/spreadsheets/d/1E9-eXjzsKboiCCX-0u0r5fAjjufLKayaut_FOPxYZjc)
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Резюме
|
|
||||||
|
|
||||||
Резюмируя вышесказанное, для random write iops:
|
|
||||||
|
|
||||||
- Использовать только SSD с конденсаторами. NVMe это тоже касается. Подсказка: 99 % desktop SSD и NVMe конденсаторов не имеют.
|
|
||||||
- …и надо отключать этим SSD кэш (hdparm -W 0), если они SATA!
|
|
||||||
- В случае HDD — полезны HDD со встроенным SSD-кэшем. Например, почти все большие Seagate EXOS таковы, хоть на них это часто и не заявлено.
|
|
||||||
- …и им тоже бывает полезно отключить кэш (hdparm -W 0). Только проверьте, что это улучшает iops-ы, а не ухудшает.
|
|
||||||
- SMR HDD не использовать под Ceph никогда.
|
|
||||||
- Отключить powersave: cpupower frequency-set -g performance, cpupower idle-set -D 0
|
|
||||||
- Отключить электронные подписи:
|
|
||||||
cephx_require_signatures = false
|
|
||||||
cephx_cluster_require_signatures = false
|
|
||||||
cephx_sign_messages = false
|
|
||||||
(и монтировать rbd map / cephfs ядерным драйвером с опциями -o nocephx_require_signatures,nocephx_sign_messages)
|
|
||||||
- min_alloc_size=16384 (так и было по умолчанию, в последних версиях поменяли на 4096 и я рекомендовал 4096, а похоже, что зря)
|
|
||||||
- Актуально для версий до Nautilus включительно — [global] debug objecter = 0/0 (там большой тормоз на клиентской стороне)
|
|
||||||
- В QEMU юзать virtio
|
|
||||||
- Если у вас All-flash кластер и вам критичны либо iops-ы случайной _синхронной_ записи (fsync/O_SYNC, например в случае СУБД), либо суммарные iops-ы _параллельной_ случайной записи, то нужно отключить rbd cache (в qemu опция cache=none). Если не критичны или у вас HDD, лучше поставить cache=writeback.
|
|
||||||
- Чтобы мог работать cache=unsafe, поставить [global] rbd cache writethrough until flush = false
|
|
||||||
- ~~Для HDD-only или Bad-SSD-Only и версий до Nautilus включительно — снять ручник [https://github.com/ceph/ceph/pull/26909](https://github.com/ceph/ceph/pull/26909)~~ - уже влит
|
|
||||||
- Внутри ВМ: mount -o lazytime
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Примечание
|
|
||||||
|
|
||||||
Написанное в статье актуально для версий Ceph, доступных на момент последней правки (см. «История» вверху страницы). Конкретно — 12-15 luminous-octopus. Различия между версиями минимальны, всё пока что актуально. Если вдруг в будущем что-то пофиксят и всё вдруг станет чудесно быстрым — сам побегу обновляться первым и поправлю статью :).
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
См. также
|
|
||||||
|
|
||||||
- [https://www.ixsystems.com/community/resources/list-of-known-smr-drives.141/](https://www.ixsystems.com/community/resources/list-of-known-smr-drives.141/) - список SMR дисков (вероятно, не полный)
|
|
||||||
- [http://vasilisc.com/bluestore-ceph-2017](http://vasilisc.com/bluestore-ceph-2017) - кое-что про bluestore, местами не совсем верно, но тем не менее
|
|
||||||
- [https://amarao-san.livejournal.com/3437997.html](https://amarao-san.livejournal.com/3437997.html) - «IOPS не существует» — сказ о latency
|
|
||||||
|
|
||||||
_Прим.вред — iops существуют, но с обязательным указанием режима тестирования и параллелизма_
|
|
||||||
|
|
||||||
- [https://yourcmc.ru/afr-calc/](https://yourcmc.ru/afr-calc/) - мой калькулятор вероятности потери данных в кластере Ceph в зависимости от размера кластера и схемы отказоустойчивости
|
|
||||||
|
|
||||||
##
|
|
||||||
|
|
||||||
Советы лучших собаководов
|
|
||||||
|
|
||||||
Офлайн балансер:
|
|
||||||
|
|
||||||
ceph osd getmap -o om; osdmaptool om --upmap upmap.sh --upmap-deviation 0; bash upmap.sh; rm -f upmap.sh om
|
|
||||||
|
|
||||||
[Читать на другом языке](https://yourcmc.ru/wiki/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:MobileLanguages/%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph)
|
|
||||||
|
|
||||||
[Последняя правка сделана 2 года назад](https://yourcmc.ru/wiki/%D0%A1%D0%BB%D1%83%D0%B6%D0%B5%D0%B1%D0%BD%D0%B0%D1%8F:History/%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph) участником [VitaliyFilippov](https://yourcmc.ru/wiki/Special:UserProfile/VitaliyFilippov)
|
|
||||||
|
|
||||||
- ## YourcmcWiki
|
|
||||||
|
|
||||||
|
|
||||||
- Мобильный
|
|
||||||
- [Стационарный](https://yourcmc.ru/wiki/index.php?title=%D0%9F%D1%80%D0%BE%D0%B8%D0%B7%D0%B2%D0%BE%D0%B4%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D1%8C_Ceph&mobileaction=toggle_view_desktop)
|
|
||||||
- Содержимое доступно в соответствии с [CC-BY-SA](http://creativecommons.org/licenses/by-sa/3.0/), если не указано иное.
|
|
||||||
|
|
||||||
- [Конфиденциальность](https://yourcmc.ru/wiki/YourcmcWiki:%D0%9F%D0%BE%D0%BB%D0%B8%D1%82%D0%B8%D0%BA%D0%B0_%D0%BA%D0%BE%D0%BD%D1%84%D0%B8%D0%B4%D0%B5%D0%BD%D1%86%D0%B8%D0%B0%D0%BB%D1%8C%D0%BD%D0%BE%D1%81%D1%82%D0%B8 "YourcmcWiki:Политика конфиденциальности")
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user