[{"data":1,"prerenderedAt":9860},["ShallowReactive",2],{"navigation":3,"posts-undefined-DevOps-0-999":20},[4,8,12,16],{"title":5,"path":6,"stem":7},"首页","\u002F","00.index",{"title":9,"path":10,"stem":11},"文章","\u002Fposts","01.posts",{"title":13,"path":14,"stem":15},"动态","\u002Fmoments","02.moments",{"title":17,"path":18,"stem":19},"关于","\u002Fabout","09.about",[21,1959,2002,2059,2680,4674,5715,7681,7959,7999,8387,8971,9795],{"id":22,"title":23,"body":24,"class":1939,"cover":1939,"coverSize":1939,"date":1940,"description":30,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":1943,"navigation":596,"path":1944,"readingTime":1945,"seo":1950,"sitemap":1951,"stem":1952,"tags":1953,"time":1939,"weather":1939,"__hash__":1958},"posts\u002Fposts\u002F2024\u002F20241212.managing-containers-in-my-homelab.md","如何管理并自动更新 HomeLab 中的容器",{"type":25,"value":26,"toc":1935},"minimark",[27,31,34,38,41,44,54,57,60,63,66,71,79,85,95,120,126,545,556,561,1894,1904,1925,1928,1931],[28,29,30],"p",{},"经过一段时间的探索和优化，我已经找到一个特别适合我的方法，来自动化管理我 HomeLab 中的众多容器。一直想写一篇文章来记录一下，拖到今天才开始动笔。",[28,32,33],{},"首先交代一下背景，我的 HomeLab 中目前总共管理着 50 多个容器，分布在群晖 NAS 以及另外两台 NUC 的虚拟机中。从最初的直接通过 compose 文件手动管理，到后来使用了 Portainer，再到现在的基于 GitLab CI 的自动化管理方式，逐渐变得更自动化、更方便、也更不容易出错。",[35,36,37],"h2",{"id":37},"为什么要自动化管理",[28,39,40],{},"一开始上 Portainer，是为了解决手动管理多个设备中的容器，频繁 SSH 到不同设备中比较麻烦的问题。用 Portainer 确实可以方便快捷地管理多个设备中的容器，当容器数量比较少的时候，还是非常推荐的。",[28,42,43],{},"随着容器数量的变多，Portainer 上我遇到两个不太好解决的问题：",[45,46,47,51],"ol",{},[48,49,50],"li",{},"容器的自动更新，Portainer 的 Business 版是提供了自动更新的功能的，但可惜 CE 版没有",[48,52,53],{},"容器的配置文件管理、配置和 compose 文件的备份、版本记录等",[28,55,56],{},"第二个问题比较好解决，通过 git 管理 compose 文件和配置文件，结合 GitLab CI 在文件变更的时候自动 SSH 到对应宿主机上执行容器的更新操作。",[28,58,59],{},"第一个问题，是我在本博客项目上使用 renovate 来更新前端依赖的时候，从他们的文章中看到，renovate 也可以检测 Docker 镜像的更新，于是灵光一现，基于上一步所有 compose 文件都已经在 GitLab 中管理了，那再结合 renovate，就可以实现容器的自动更新了。",[35,61,62],{"id":62},"最终形态",[28,64,65],{},"中间摸索的步骤由于已经过了差不多几个月了，不太容易复盘了，就把最终的形态分享一下。",[67,68],"post-image",{":dark-supported":69,"filename":70},"true","01.png",[28,72,73,74,78],{},"如上图，所有容器的配置文件、compose 文件都放在 ",[75,76,77],"code",{},"config-files"," 这个项目中，该项目通过 GitLab 管理，renovate 在定期检测到镜像更新的时候，会自动提交一个 MR，可以通过一定规则配置成自动合并或手动合并，当然也支持手动修改配置文件或 compose 文件。MR 合并或配置文件修改后触发 GitLab CI，通过 SSH 登录对应宿主机上，执行对应的配置文件更新或容器更新的操作。",[28,80,81,82,84],{},"我的 ",[75,83,77],{}," 目录结构：",[86,87,92],"pre",{"className":88,"code":90,"language":91},[89],"language-text",".\n├── aliyun\n│   ├── gateway\n│   └── hk\n├── homelab\n│   ├── scripts\n│   ├── synology\n│   ├── vm-rocky-01\n│   └── vm-rocky-02\n├── .gitlab-ci.yml\n└── renovate.json\n","text",[75,93,90],{"__ignoreMap":94},"",[28,96,97,100,101,104,105,108,109,112,113,116,117,119],{},[75,98,99],{},"aliyun"," 目录中的 ",[75,102,103],{},"gateway"," 和 ",[75,106,107],{},"hk"," 是我在杭州和香港的两台服务器，部署一些外网项目，也是通过上述的方式管理。",[75,110,111],{},"homelab"," 中的 ",[75,114,115],{},"scripts"," 是一些脚本，与本文无关，但也是通过 ",[75,118,77],{}," 项目统一管理的。",[28,121,81,122,125],{},[75,123,124],{},"renovate.json"," 如下：",[86,127,131],{"className":128,"code":129,"language":130,"meta":94,"style":94},"language-json shiki shiki-themes material-theme-lighter github-light github-dark","{\n  \"$schema\": \"https:\u002F\u002Fdocs.renovatebot.com\u002Frenovate-schema.json\",\n  \"extends\": [\"config:recommended\", \"group:allNonMajor\", \":semanticCommitTypeAll(chore)\"],\n  \"packageRules\": [\n    {\n      \"matchManagers\": [\"docker-compose\"],\n      \"matchPackageNames\": [\"sonatype\u002Fnexus3\", \"clickhouse\u002Fclickhouse-server\"],\n      \"enabled\": false\n    },\n    {\n      \"matchManagers\": [\"docker-compose\"],\n      \"matchPackageNames\": [\"gitlab\u002Fgitlab-runner\"],\n      \"versionCompatibility\": \"^(?\u003Ccompatibility>.*)-(?\u003Cversion>.*)$\"\n    },\n    {\n      \"matchManagers\": [\"docker-compose\"],\n      \"matchUpdateTypes\": [\"minor\", \"patch\"],\n      \"automerge\": true,\n      \"schedule\": [\"before 11am on Monday\"]\n    }\n  ],\n  \"rangeStrategy\": \"bump\",\n  \"timezone\": \"Asia\u002FShanghai\"\n}\n","json",[75,132,133,142,172,216,231,237,262,294,309,315,320,341,363,383,388,393,414,446,463,487,493,499,520,539],{"__ignoreMap":94},[134,135,138],"span",{"class":136,"line":137},"line",1,[134,139,141],{"class":140},"sP7_E","{\n",[134,143,145,149,153,156,159,163,167,169],{"class":136,"line":144},2,[134,146,148],{"class":147},"s39Yj","  \"",[134,150,152],{"class":151},"sseR_","$schema",[134,154,155],{"class":147},"\"",[134,157,158],{"class":140},":",[134,160,162],{"class":161},"sjJ54"," \"",[134,164,166],{"class":165},"s_sjI","https:\u002F\u002Fdocs.renovatebot.com\u002Frenovate-schema.json",[134,168,155],{"class":161},[134,170,171],{"class":140},",\n",[134,173,175,177,180,182,184,187,189,192,194,197,199,202,204,206,208,211,213],{"class":136,"line":174},3,[134,176,148],{"class":147},[134,178,179],{"class":151},"extends",[134,181,155],{"class":147},[134,183,158],{"class":140},[134,185,186],{"class":140}," [",[134,188,155],{"class":161},[134,190,191],{"class":165},"config:recommended",[134,193,155],{"class":161},[134,195,196],{"class":140},",",[134,198,162],{"class":161},[134,200,201],{"class":165},"group:allNonMajor",[134,203,155],{"class":161},[134,205,196],{"class":140},[134,207,162],{"class":161},[134,209,210],{"class":165},":semanticCommitTypeAll(chore)",[134,212,155],{"class":161},[134,214,215],{"class":140},"],\n",[134,217,219,221,224,226,228],{"class":136,"line":218},4,[134,220,148],{"class":147},[134,222,223],{"class":151},"packageRules",[134,225,155],{"class":147},[134,227,158],{"class":140},[134,229,230],{"class":140}," [\n",[134,232,234],{"class":136,"line":233},5,[134,235,236],{"class":140},"    {\n",[134,238,240,243,247,249,251,253,255,258,260],{"class":136,"line":239},6,[134,241,242],{"class":147},"      \"",[134,244,246],{"class":245},"sZMiF","matchManagers",[134,248,155],{"class":147},[134,250,158],{"class":140},[134,252,186],{"class":140},[134,254,155],{"class":161},[134,256,257],{"class":165},"docker-compose",[134,259,155],{"class":161},[134,261,215],{"class":140},[134,263,265,267,270,272,274,276,278,281,283,285,287,290,292],{"class":136,"line":264},7,[134,266,242],{"class":147},[134,268,269],{"class":245},"matchPackageNames",[134,271,155],{"class":147},[134,273,158],{"class":140},[134,275,186],{"class":140},[134,277,155],{"class":161},[134,279,280],{"class":165},"sonatype\u002Fnexus3",[134,282,155],{"class":161},[134,284,196],{"class":140},[134,286,162],{"class":161},[134,288,289],{"class":165},"clickhouse\u002Fclickhouse-server",[134,291,155],{"class":161},[134,293,215],{"class":140},[134,295,297,299,302,304,306],{"class":136,"line":296},8,[134,298,242],{"class":147},[134,300,301],{"class":245},"enabled",[134,303,155],{"class":147},[134,305,158],{"class":140},[134,307,308],{"class":147}," false\n",[134,310,312],{"class":136,"line":311},9,[134,313,314],{"class":140},"    },\n",[134,316,318],{"class":136,"line":317},10,[134,319,236],{"class":140},[134,321,323,325,327,329,331,333,335,337,339],{"class":136,"line":322},11,[134,324,242],{"class":147},[134,326,246],{"class":245},[134,328,155],{"class":147},[134,330,158],{"class":140},[134,332,186],{"class":140},[134,334,155],{"class":161},[134,336,257],{"class":165},[134,338,155],{"class":161},[134,340,215],{"class":140},[134,342,344,346,348,350,352,354,356,359,361],{"class":136,"line":343},12,[134,345,242],{"class":147},[134,347,269],{"class":245},[134,349,155],{"class":147},[134,351,158],{"class":140},[134,353,186],{"class":140},[134,355,155],{"class":161},[134,357,358],{"class":165},"gitlab\u002Fgitlab-runner",[134,360,155],{"class":161},[134,362,215],{"class":140},[134,364,366,368,371,373,375,377,380],{"class":136,"line":365},13,[134,367,242],{"class":147},[134,369,370],{"class":245},"versionCompatibility",[134,372,155],{"class":147},[134,374,158],{"class":140},[134,376,162],{"class":161},[134,378,379],{"class":165},"^(?\u003Ccompatibility>.*)-(?\u003Cversion>.*)$",[134,381,382],{"class":161},"\"\n",[134,384,386],{"class":136,"line":385},14,[134,387,314],{"class":140},[134,389,391],{"class":136,"line":390},15,[134,392,236],{"class":140},[134,394,396,398,400,402,404,406,408,410,412],{"class":136,"line":395},16,[134,397,242],{"class":147},[134,399,246],{"class":245},[134,401,155],{"class":147},[134,403,158],{"class":140},[134,405,186],{"class":140},[134,407,155],{"class":161},[134,409,257],{"class":165},[134,411,155],{"class":161},[134,413,215],{"class":140},[134,415,417,419,422,424,426,428,430,433,435,437,439,442,444],{"class":136,"line":416},17,[134,418,242],{"class":147},[134,420,421],{"class":245},"matchUpdateTypes",[134,423,155],{"class":147},[134,425,158],{"class":140},[134,427,186],{"class":140},[134,429,155],{"class":161},[134,431,432],{"class":165},"minor",[134,434,155],{"class":161},[134,436,196],{"class":140},[134,438,162],{"class":161},[134,440,441],{"class":165},"patch",[134,443,155],{"class":161},[134,445,215],{"class":140},[134,447,449,451,454,456,458,461],{"class":136,"line":448},18,[134,450,242],{"class":147},[134,452,453],{"class":245},"automerge",[134,455,155],{"class":147},[134,457,158],{"class":140},[134,459,460],{"class":147}," true",[134,462,171],{"class":140},[134,464,466,468,471,473,475,477,479,482,484],{"class":136,"line":465},19,[134,467,242],{"class":147},[134,469,470],{"class":245},"schedule",[134,472,155],{"class":147},[134,474,158],{"class":140},[134,476,186],{"class":140},[134,478,155],{"class":161},[134,480,481],{"class":165},"before 11am on Monday",[134,483,155],{"class":161},[134,485,486],{"class":140},"]\n",[134,488,490],{"class":136,"line":489},20,[134,491,492],{"class":140},"    }\n",[134,494,496],{"class":136,"line":495},21,[134,497,498],{"class":140},"  ],\n",[134,500,502,504,507,509,511,513,516,518],{"class":136,"line":501},22,[134,503,148],{"class":147},[134,505,506],{"class":151},"rangeStrategy",[134,508,155],{"class":147},[134,510,158],{"class":140},[134,512,162],{"class":161},[134,514,515],{"class":165},"bump",[134,517,155],{"class":161},[134,519,171],{"class":140},[134,521,523,525,528,530,532,534,537],{"class":136,"line":522},23,[134,524,148],{"class":147},[134,526,527],{"class":151},"timezone",[134,529,155],{"class":147},[134,531,158],{"class":140},[134,533,162],{"class":161},[134,535,536],{"class":165},"Asia\u002FShanghai",[134,538,382],{"class":161},[134,540,542],{"class":136,"line":541},24,[134,543,544],{"class":140},"}\n",[28,546,547,548,104,550,552,553,555],{},"里面有一些自定义的规则，比如禁用了 ",[75,549,280],{},[75,551,289],{}," 的自动更新，以及适配了 ",[75,554,358],{}," 的不规则的版本号。",[28,557,558,125],{},[75,559,560],{},".gitlab-ci.yml",[86,562,566],{"className":563,"code":564,"language":565,"meta":94,"style":94},"language-yaml shiki shiki-themes material-theme-lighter github-light github-dark","stages:\n  - pass\n  - deploy\n\npass:\n  stage: pass\n  script:\n    - echo \"Current branch is $CI_COMMIT_BRANCH, pass.\"\n  except:\n    - main\n\ndeploy:\n  stage: deploy\n  before_script:\n    - eval $(ssh-agent -s)\n    - echo \"$SSH_PRIVATE_KEY\" | ssh-add -\n    - mkdir -p ~\u002F.ssh\n    - echo \"$SSH_KNOWN_HOSTS\" > ~\u002F.ssh\u002Fknown_hosts\n\n  script:\n    - MODIFIED_FILES=$(git diff --name-only --diff-filter=ACMRT $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA)\n    - DELETED_FILES=$(git diff --name-status --diff-filter=DR $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | awk '{print $2}')\n    - |\n      for FILE in $DELETED_FILES; do\n        echo \"文件移除 $FILE\"\n        # 如果文件是 compose.yaml，执行 docker compose down\n        if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n            DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n            echo \"退出容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose down\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n            echo \"退出容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n            ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose down\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n            echo \"退出容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n            ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose down\"\n          fi\n\n          # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n          #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n          #   echo \"退出容器 gateway:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n          #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose down\"\n          # fi\n        fi\n\n        # 删除对应文件\n        if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n          FILE_PATH=\"\u002Fvolume3\u002Fdocker\u002F${FILE#homelab\u002F}\"\n          echo \"从 Synology 删除文件: $FILE_PATH\"\n          ssh -p 5110 root@synology.home \"rm -f $FILE_PATH\"\n        fi\n\n        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-01\u002F}\"\n          echo \"从 vm-rocky-01 删除文件: $FILE_PATH\"\n          ssh root@vm-rocky-01.home \"rm -f $FILE_PATH\"\n        fi\n\n        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-02\u002F}\"\n          echo \"从 vm-rocky-02 删除文件: $FILE_PATH\"\n          ssh root@vm-rocky-02.home \"rm -f $FILE_PATH\"\n        fi\n\n        if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n          FILE_PATH=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F${FILE#aliyun\u002Fgateway\u002F}\"\n          echo \"从 gateway.aliyun 删除文件: $FILE_PATH\"\n          ssh root@gateway.aliyun \"rm -f $FILE_PATH\"\n        fi\n      done\n\n      for FILE in $MODIFIED_FILES; do\n        echo \"文件变更 $FILE\"\n        if [ -e \"$FILE\" ]; then\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n            DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n            ssh -p 5110 root@synology.home \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 Synology: $DEST_DIR\"\n            scp -O -P 5110 $FILE root@synology.home:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n            ssh root@vm-rocky-01.home \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 vm-rocky-01: $DEST_DIR\"\n            scp -O $FILE root@vm-rocky-01.home:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n            ssh root@vm-rocky-02.home \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 vm-rocky-02: $DEST_DIR\"\n            scp -O $FILE root@vm-rocky-02.home:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n            DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n            ssh root@gateway.aliyun \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 gateway.aliyun: $DEST_DIR\"\n            scp -O $FILE root@gateway.aliyun:$DEST_DIR\n          fi\n\n          if [[ \"$FILE\" == \"aliyun\u002Fhk\u002Fnginx\u002F*\" ]]; then\n            DEST_DIR=\"\u002Fetc\u002Fnginx\u002F$(dirname ${FILE#aliyun\u002Fhk\u002Fnginx\u002F})\"\n            ssh root@hk.aliyun \"mkdir -p $DEST_DIR\"\n            echo \"复制文件 $FILE 到 hk.aliyun: $DEST_DIR\"\n            scp -O $FILE root@hk.aliyun:$DEST_DIR\n          fi\n        fi\n      done\n\n      for FILE in $MODIFIED_FILES; do\n        if [ -e \"$FILE\" ]; then\n          # 如果文件是 compose.yaml，执行 docker compose up -d\n          if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Frenovate\u002Fcompose.yaml\" ]]; then\n              echo \"跳过部署 vm-rocky-01:renovate\"\n              continue\n            fi\n\n            if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n              DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n              echo \"部署容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n              ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose up -d\"\n            fi\n\n            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n              echo \"部署容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n              ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose up -d\"\n            fi\n\n            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n              echo \"部署容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n              ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose up -d\"\n            fi\n\n            # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n            #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n            #   echo \"部署容器 gateway.aliyun:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n            #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose up -d\"\n            # fi\n\n            echo \"容器重新部署完成\"\n            continue\n          fi\n\n          # 如果文件是 homelab\u002Fsynology\u002Fnginx\u002F*, 重新启动 nginx\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Fnginx\u002F*\" ]]; then\n            echo \"重新启动 synology:nginx 服务\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && docker exec nginx nginx -s reload\"\n          fi\n\n          # 如果文件是 aliyun\u002Fgateway\u002Fnginx\u002F*, 重新启动 nginx\n          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002Fnginx\u002F*\" ]]; then\n            echo \"重新启动 gateway.aliyun:nginx\"\n            ssh root@gateway.aliyun \"nginx -s reload\"\n          fi\n\n          # 如果文件是 aliyun\u002Fhk\u002Fnginx\u002F*, 重新启动 nginx\n          if [[ \"$FILE\" == \"aliyun\u002Fhk\u002Fnginx\u002F*\" ]]; then\n            echo \"重新启动 hk.aliyun:nginx\"\n            ssh root@hk.aliyun \"nginx -s reload\"\n          fi\n\n          # 如果文件是 rinetd.conf, 重新启动 rinetd\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Frinetd\u002Fconfig\u002Frinetd.conf\" ]]; then\n            echo \"重新启动 synology:rinetd 服务\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Frinetd && docker-compose restart\"\n          fi\n\n          # 如果文件是 gitlab.rb, 重新配置 gitlab\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fgitlab\u002Fconfig\u002Fgitlab.rb\" ]]; then\n            echo \"重新配置 vm-rocky-01:gitlab\"\n            ssh root@vm-rocky-01.home \"docker exec gitlab gitlab-ctl reconfigure\"\n          fi\n\n          # 如果文件是 prometheus.yml, 重新启动 prometheus\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fprometheus\u002Fconfig\u002Fprometheus.yml\" ]]; then\n            echo \"重新启动 vm-rocky-01:prometheus\"\n            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Fprometheus && docker compose restart\"\n          fi\n\n          # 如果文件是 telegraf.conf, 重新启动 telegraf\n          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n            echo \"重新启动 synology:telegraf\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Ftelegraf && docker-compose restart\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n            echo \"重新启动 vm-rocky-01:telegraf\"\n            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n          fi\n\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n            echo \"重新启动 vm-rocky-02:telegraf\"\n            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n          fi\n\n          # 如果文件是 bots\u002Fconfig\u002Fclash\u002F*.yaml, 更新 clash 配置\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fbots\u002Fconfig\u002Fclash\u002F*.yaml\" ]]; then\n            echo \"重新启动 vm-rocky-02:bots\"\n            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fbots && docker compose restart\"\n            echo \"等待 10 秒\"\n            sleep 10\n            echo \"重新启动 synology:clash-premium\"\n            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Fclash-premium && docker-compose restart\"\n          fi\n\n          # 如果文件是 artalk\u002Fdata\u002Fartalk.yml, 重新启动 artalk\n          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fartalk\u002Fdata\u002Fartalk.yml\" ]]; then\n            echo \"重新启动 vm-rocky-02:artalk\"\n            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fartalk && docker compose restart\"\n          fi\n\n        fi\n      done\n    - echo \"部署完成\"\n  only:\n    - main\n","yaml",[75,567,568,577,585,592,598,605,614,621,629,636,643,647,654,662,669,676,683,690,697,701,707,714,721,729,734,740,746,752,758,764,770,776,782,787,793,799,805,811,816,821,827,833,839,845,850,855,861,867,873,879,885,891,896,902,908,914,920,926,931,936,942,948,954,960,965,970,976,982,988,994,999,1004,1010,1016,1022,1028,1033,1039,1044,1050,1056,1062,1067,1072,1078,1084,1090,1095,1100,1105,1110,1116,1122,1128,1133,1138,1143,1148,1154,1160,1166,1171,1176,1182,1188,1194,1200,1206,1211,1216,1222,1228,1234,1240,1246,1251,1256,1261,1266,1271,1276,1282,1288,1294,1300,1306,1312,1317,1323,1329,1335,1341,1346,1351,1357,1363,1369,1375,1380,1385,1391,1397,1403,1409,1414,1419,1425,1431,1437,1443,1449,1454,1460,1466,1471,1476,1482,1488,1494,1500,1505,1510,1516,1522,1528,1534,1539,1544,1550,1555,1561,1567,1572,1577,1583,1589,1595,1601,1606,1611,1617,1623,1629,1635,1640,1645,1651,1657,1663,1669,1674,1679,1685,1691,1697,1703,1708,1713,1719,1725,1731,1736,1741,1747,1753,1759,1764,1769,1775,1781,1787,1793,1799,1805,1811,1817,1822,1827,1833,1839,1845,1851,1856,1861,1866,1871,1879,1887],{"__ignoreMap":94},[134,569,570,574],{"class":136,"line":137},[134,571,573],{"class":572},"sQzsp","stages",[134,575,576],{"class":140},":\n",[134,578,579,582],{"class":136,"line":144},[134,580,581],{"class":140},"  -",[134,583,584],{"class":165}," pass\n",[134,586,587,589],{"class":136,"line":174},[134,588,581],{"class":140},[134,590,591],{"class":165}," deploy\n",[134,593,594],{"class":136,"line":218},[134,595,597],{"emptyLinePlaceholder":596},true,"\n",[134,599,600,603],{"class":136,"line":233},[134,601,602],{"class":572},"pass",[134,604,576],{"class":140},[134,606,607,610,612],{"class":136,"line":239},[134,608,609],{"class":572},"  stage",[134,611,158],{"class":140},[134,613,584],{"class":165},[134,615,616,619],{"class":136,"line":264},[134,617,618],{"class":572},"  script",[134,620,576],{"class":140},[134,622,623,626],{"class":136,"line":296},[134,624,625],{"class":140},"    -",[134,627,628],{"class":165}," echo \"Current branch is $CI_COMMIT_BRANCH, pass.\"\n",[134,630,631,634],{"class":136,"line":311},[134,632,633],{"class":572},"  except",[134,635,576],{"class":140},[134,637,638,640],{"class":136,"line":317},[134,639,625],{"class":140},[134,641,642],{"class":165}," main\n",[134,644,645],{"class":136,"line":322},[134,646,597],{"emptyLinePlaceholder":596},[134,648,649,652],{"class":136,"line":343},[134,650,651],{"class":572},"deploy",[134,653,576],{"class":140},[134,655,656,658,660],{"class":136,"line":365},[134,657,609],{"class":572},[134,659,158],{"class":140},[134,661,591],{"class":165},[134,663,664,667],{"class":136,"line":385},[134,665,666],{"class":572},"  before_script",[134,668,576],{"class":140},[134,670,671,673],{"class":136,"line":390},[134,672,625],{"class":140},[134,674,675],{"class":165}," eval $(ssh-agent -s)\n",[134,677,678,680],{"class":136,"line":395},[134,679,625],{"class":140},[134,681,682],{"class":165}," echo \"$SSH_PRIVATE_KEY\" | ssh-add -\n",[134,684,685,687],{"class":136,"line":416},[134,686,625],{"class":140},[134,688,689],{"class":165}," mkdir -p ~\u002F.ssh\n",[134,691,692,694],{"class":136,"line":448},[134,693,625],{"class":140},[134,695,696],{"class":165}," echo \"$SSH_KNOWN_HOSTS\" > ~\u002F.ssh\u002Fknown_hosts\n",[134,698,699],{"class":136,"line":465},[134,700,597],{"emptyLinePlaceholder":596},[134,702,703,705],{"class":136,"line":489},[134,704,618],{"class":572},[134,706,576],{"class":140},[134,708,709,711],{"class":136,"line":495},[134,710,625],{"class":140},[134,712,713],{"class":165}," MODIFIED_FILES=$(git diff --name-only --diff-filter=ACMRT $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA)\n",[134,715,716,718],{"class":136,"line":501},[134,717,625],{"class":140},[134,719,720],{"class":165}," DELETED_FILES=$(git diff --name-status --diff-filter=DR $CI_COMMIT_BEFORE_SHA $CI_COMMIT_SHA | awk '{print $2}')\n",[134,722,723,725],{"class":136,"line":522},[134,724,625],{"class":140},[134,726,728],{"class":727},"sVHd0"," |\n",[134,730,731],{"class":136,"line":541},[134,732,733],{"class":165},"      for FILE in $DELETED_FILES; do\n",[134,735,737],{"class":136,"line":736},25,[134,738,739],{"class":165},"        echo \"文件移除 $FILE\"\n",[134,741,743],{"class":136,"line":742},26,[134,744,745],{"class":165},"        # 如果文件是 compose.yaml，执行 docker compose down\n",[134,747,749],{"class":136,"line":748},27,[134,750,751],{"class":165},"        if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n",[134,753,755],{"class":136,"line":754},28,[134,756,757],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n",[134,759,761],{"class":136,"line":760},29,[134,762,763],{"class":165},"            DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n",[134,765,767],{"class":136,"line":766},30,[134,768,769],{"class":165},"            echo \"退出容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n",[134,771,773],{"class":136,"line":772},31,[134,774,775],{"class":165},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose down\"\n",[134,777,779],{"class":136,"line":778},32,[134,780,781],{"class":165},"          fi\n",[134,783,785],{"class":136,"line":784},33,[134,786,597],{"emptyLinePlaceholder":596},[134,788,790],{"class":136,"line":789},34,[134,791,792],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n",[134,794,796],{"class":136,"line":795},35,[134,797,798],{"class":165},"            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[134,800,802],{"class":136,"line":801},36,[134,803,804],{"class":165},"            echo \"退出容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[134,806,808],{"class":136,"line":807},37,[134,809,810],{"class":165},"            ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose down\"\n",[134,812,814],{"class":136,"line":813},38,[134,815,781],{"class":165},[134,817,819],{"class":136,"line":818},39,[134,820,597],{"emptyLinePlaceholder":596},[134,822,824],{"class":136,"line":823},40,[134,825,826],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n",[134,828,830],{"class":136,"line":829},41,[134,831,832],{"class":165},"            DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[134,834,836],{"class":136,"line":835},42,[134,837,838],{"class":165},"            echo \"退出容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[134,840,842],{"class":136,"line":841},43,[134,843,844],{"class":165},"            ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose down\"\n",[134,846,848],{"class":136,"line":847},44,[134,849,781],{"class":165},[134,851,853],{"class":136,"line":852},45,[134,854,597],{"emptyLinePlaceholder":596},[134,856,858],{"class":136,"line":857},46,[134,859,860],{"class":165},"          # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[134,862,864],{"class":136,"line":863},47,[134,865,866],{"class":165},"          #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[134,868,870],{"class":136,"line":869},48,[134,871,872],{"class":165},"          #   echo \"退出容器 gateway:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[134,874,876],{"class":136,"line":875},49,[134,877,878],{"class":165},"          #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose down\"\n",[134,880,882],{"class":136,"line":881},50,[134,883,884],{"class":165},"          # fi\n",[134,886,888],{"class":136,"line":887},51,[134,889,890],{"class":165},"        fi\n",[134,892,894],{"class":136,"line":893},52,[134,895,597],{"emptyLinePlaceholder":596},[134,897,899],{"class":136,"line":898},53,[134,900,901],{"class":165},"        # 删除对应文件\n",[134,903,905],{"class":136,"line":904},54,[134,906,907],{"class":165},"        if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n",[134,909,911],{"class":136,"line":910},55,[134,912,913],{"class":165},"          FILE_PATH=\"\u002Fvolume3\u002Fdocker\u002F${FILE#homelab\u002F}\"\n",[134,915,917],{"class":136,"line":916},56,[134,918,919],{"class":165},"          echo \"从 Synology 删除文件: $FILE_PATH\"\n",[134,921,923],{"class":136,"line":922},57,[134,924,925],{"class":165},"          ssh -p 5110 root@synology.home \"rm -f $FILE_PATH\"\n",[134,927,929],{"class":136,"line":928},58,[134,930,890],{"class":165},[134,932,934],{"class":136,"line":933},59,[134,935,597],{"emptyLinePlaceholder":596},[134,937,939],{"class":136,"line":938},60,[134,940,941],{"class":165},"        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n",[134,943,945],{"class":136,"line":944},61,[134,946,947],{"class":165},"          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-01\u002F}\"\n",[134,949,951],{"class":136,"line":950},62,[134,952,953],{"class":165},"          echo \"从 vm-rocky-01 删除文件: $FILE_PATH\"\n",[134,955,957],{"class":136,"line":956},63,[134,958,959],{"class":165},"          ssh root@vm-rocky-01.home \"rm -f $FILE_PATH\"\n",[134,961,963],{"class":136,"line":962},64,[134,964,890],{"class":165},[134,966,968],{"class":136,"line":967},65,[134,969,597],{"emptyLinePlaceholder":596},[134,971,973],{"class":136,"line":972},66,[134,974,975],{"class":165},"        if [[  \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n",[134,977,979],{"class":136,"line":978},67,[134,980,981],{"class":165},"          FILE_PATH=\"\u002Froot\u002F${FILE#homelab\u002Fvm-rocky-02\u002F}\"\n",[134,983,985],{"class":136,"line":984},68,[134,986,987],{"class":165},"          echo \"从 vm-rocky-02 删除文件: $FILE_PATH\"\n",[134,989,991],{"class":136,"line":990},69,[134,992,993],{"class":165},"          ssh root@vm-rocky-02.home \"rm -f $FILE_PATH\"\n",[134,995,997],{"class":136,"line":996},70,[134,998,890],{"class":165},[134,1000,1002],{"class":136,"line":1001},71,[134,1003,597],{"emptyLinePlaceholder":596},[134,1005,1007],{"class":136,"line":1006},72,[134,1008,1009],{"class":165},"        if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[134,1011,1013],{"class":136,"line":1012},73,[134,1014,1015],{"class":165},"          FILE_PATH=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F${FILE#aliyun\u002Fgateway\u002F}\"\n",[134,1017,1019],{"class":136,"line":1018},74,[134,1020,1021],{"class":165},"          echo \"从 gateway.aliyun 删除文件: $FILE_PATH\"\n",[134,1023,1025],{"class":136,"line":1024},75,[134,1026,1027],{"class":165},"          ssh root@gateway.aliyun \"rm -f $FILE_PATH\"\n",[134,1029,1031],{"class":136,"line":1030},76,[134,1032,890],{"class":165},[134,1034,1036],{"class":136,"line":1035},77,[134,1037,1038],{"class":165},"      done\n",[134,1040,1042],{"class":136,"line":1041},78,[134,1043,597],{"emptyLinePlaceholder":596},[134,1045,1047],{"class":136,"line":1046},79,[134,1048,1049],{"class":165},"      for FILE in $MODIFIED_FILES; do\n",[134,1051,1053],{"class":136,"line":1052},80,[134,1054,1055],{"class":165},"        echo \"文件变更 $FILE\"\n",[134,1057,1059],{"class":136,"line":1058},81,[134,1060,1061],{"class":165},"        if [ -e \"$FILE\" ]; then\n",[134,1063,1065],{"class":136,"line":1064},82,[134,1066,757],{"class":165},[134,1068,1070],{"class":136,"line":1069},83,[134,1071,763],{"class":165},[134,1073,1075],{"class":136,"line":1074},84,[134,1076,1077],{"class":165},"            ssh -p 5110 root@synology.home \"mkdir -p $DEST_DIR\"\n",[134,1079,1081],{"class":136,"line":1080},85,[134,1082,1083],{"class":165},"            echo \"复制文件 $FILE 到 Synology: $DEST_DIR\"\n",[134,1085,1087],{"class":136,"line":1086},86,[134,1088,1089],{"class":165},"            scp -O -P 5110 $FILE root@synology.home:$DEST_DIR\n",[134,1091,1093],{"class":136,"line":1092},87,[134,1094,781],{"class":165},[134,1096,1098],{"class":136,"line":1097},88,[134,1099,597],{"emptyLinePlaceholder":596},[134,1101,1103],{"class":136,"line":1102},89,[134,1104,792],{"class":165},[134,1106,1108],{"class":136,"line":1107},90,[134,1109,798],{"class":165},[134,1111,1113],{"class":136,"line":1112},91,[134,1114,1115],{"class":165},"            ssh root@vm-rocky-01.home \"mkdir -p $DEST_DIR\"\n",[134,1117,1119],{"class":136,"line":1118},92,[134,1120,1121],{"class":165},"            echo \"复制文件 $FILE 到 vm-rocky-01: $DEST_DIR\"\n",[134,1123,1125],{"class":136,"line":1124},93,[134,1126,1127],{"class":165},"            scp -O $FILE root@vm-rocky-01.home:$DEST_DIR\n",[134,1129,1131],{"class":136,"line":1130},94,[134,1132,781],{"class":165},[134,1134,1136],{"class":136,"line":1135},95,[134,1137,597],{"emptyLinePlaceholder":596},[134,1139,1141],{"class":136,"line":1140},96,[134,1142,826],{"class":165},[134,1144,1146],{"class":136,"line":1145},97,[134,1147,832],{"class":165},[134,1149,1151],{"class":136,"line":1150},98,[134,1152,1153],{"class":165},"            ssh root@vm-rocky-02.home \"mkdir -p $DEST_DIR\"\n",[134,1155,1157],{"class":136,"line":1156},99,[134,1158,1159],{"class":165},"            echo \"复制文件 $FILE 到 vm-rocky-02: $DEST_DIR\"\n",[134,1161,1163],{"class":136,"line":1162},100,[134,1164,1165],{"class":165},"            scp -O $FILE root@vm-rocky-02.home:$DEST_DIR\n",[134,1167,1169],{"class":136,"line":1168},101,[134,1170,781],{"class":165},[134,1172,1174],{"class":136,"line":1173},102,[134,1175,597],{"emptyLinePlaceholder":596},[134,1177,1179],{"class":136,"line":1178},103,[134,1180,1181],{"class":165},"          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[134,1183,1185],{"class":136,"line":1184},104,[134,1186,1187],{"class":165},"            DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[134,1189,1191],{"class":136,"line":1190},105,[134,1192,1193],{"class":165},"            ssh root@gateway.aliyun \"mkdir -p $DEST_DIR\"\n",[134,1195,1197],{"class":136,"line":1196},106,[134,1198,1199],{"class":165},"            echo \"复制文件 $FILE 到 gateway.aliyun: $DEST_DIR\"\n",[134,1201,1203],{"class":136,"line":1202},107,[134,1204,1205],{"class":165},"            scp -O $FILE root@gateway.aliyun:$DEST_DIR\n",[134,1207,1209],{"class":136,"line":1208},108,[134,1210,781],{"class":165},[134,1212,1214],{"class":136,"line":1213},109,[134,1215,597],{"emptyLinePlaceholder":596},[134,1217,1219],{"class":136,"line":1218},110,[134,1220,1221],{"class":165},"          if [[ \"$FILE\" == \"aliyun\u002Fhk\u002Fnginx\u002F*\" ]]; then\n",[134,1223,1225],{"class":136,"line":1224},111,[134,1226,1227],{"class":165},"            DEST_DIR=\"\u002Fetc\u002Fnginx\u002F$(dirname ${FILE#aliyun\u002Fhk\u002Fnginx\u002F})\"\n",[134,1229,1231],{"class":136,"line":1230},112,[134,1232,1233],{"class":165},"            ssh root@hk.aliyun \"mkdir -p $DEST_DIR\"\n",[134,1235,1237],{"class":136,"line":1236},113,[134,1238,1239],{"class":165},"            echo \"复制文件 $FILE 到 hk.aliyun: $DEST_DIR\"\n",[134,1241,1243],{"class":136,"line":1242},114,[134,1244,1245],{"class":165},"            scp -O $FILE root@hk.aliyun:$DEST_DIR\n",[134,1247,1249],{"class":136,"line":1248},115,[134,1250,781],{"class":165},[134,1252,1254],{"class":136,"line":1253},116,[134,1255,890],{"class":165},[134,1257,1259],{"class":136,"line":1258},117,[134,1260,1038],{"class":165},[134,1262,1264],{"class":136,"line":1263},118,[134,1265,597],{"emptyLinePlaceholder":596},[134,1267,1269],{"class":136,"line":1268},119,[134,1270,1049],{"class":165},[134,1272,1274],{"class":136,"line":1273},120,[134,1275,1061],{"class":165},[134,1277,1279],{"class":136,"line":1278},121,[134,1280,1281],{"class":165},"          # 如果文件是 compose.yaml，执行 docker compose up -d\n",[134,1283,1285],{"class":136,"line":1284},122,[134,1286,1287],{"class":165},"          if [[ \"$FILE\" == \"*\u002Fcompose.yaml\" ]]; then\n",[134,1289,1291],{"class":136,"line":1290},123,[134,1292,1293],{"class":165},"            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Frenovate\u002Fcompose.yaml\" ]]; then\n",[134,1295,1297],{"class":136,"line":1296},124,[134,1298,1299],{"class":165},"              echo \"跳过部署 vm-rocky-01:renovate\"\n",[134,1301,1303],{"class":136,"line":1302},125,[134,1304,1305],{"class":165},"              continue\n",[134,1307,1309],{"class":136,"line":1308},126,[134,1310,1311],{"class":165},"            fi\n",[134,1313,1315],{"class":136,"line":1314},127,[134,1316,597],{"emptyLinePlaceholder":596},[134,1318,1320],{"class":136,"line":1319},128,[134,1321,1322],{"class":165},"            if [[ \"$FILE\" == \"homelab\u002Fsynology\u002F*\" ]]; then\n",[134,1324,1326],{"class":136,"line":1325},129,[134,1327,1328],{"class":165},"              DEST_DIR=\"\u002Fvolume3\u002Fdocker\u002F$(dirname ${FILE#homelab\u002F})\"\n",[134,1330,1332],{"class":136,"line":1331},130,[134,1333,1334],{"class":165},"              echo \"部署容器 synology:$(dirname ${FILE#homelab\u002Fsynology\u002F})\"\n",[134,1336,1338],{"class":136,"line":1337},131,[134,1339,1340],{"class":165},"              ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd $DEST_DIR && docker-compose up -d\"\n",[134,1342,1344],{"class":136,"line":1343},132,[134,1345,1311],{"class":165},[134,1347,1349],{"class":136,"line":1348},133,[134,1350,597],{"emptyLinePlaceholder":596},[134,1352,1354],{"class":136,"line":1353},134,[134,1355,1356],{"class":165},"            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002F*\" ]]; then\n",[134,1358,1360],{"class":136,"line":1359},135,[134,1361,1362],{"class":165},"              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[134,1364,1366],{"class":136,"line":1365},136,[134,1367,1368],{"class":165},"              echo \"部署容器 vm-rocky-01:$(dirname ${FILE#homelab\u002Fvm-rocky-01\u002F})\"\n",[134,1370,1372],{"class":136,"line":1371},137,[134,1373,1374],{"class":165},"              ssh root@vm-rocky-01.home \"cd $DEST_DIR && docker compose up -d\"\n",[134,1376,1378],{"class":136,"line":1377},138,[134,1379,1311],{"class":165},[134,1381,1383],{"class":136,"line":1382},139,[134,1384,597],{"emptyLinePlaceholder":596},[134,1386,1388],{"class":136,"line":1387},140,[134,1389,1390],{"class":165},"            if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002F*\" ]]; then\n",[134,1392,1394],{"class":136,"line":1393},141,[134,1395,1396],{"class":165},"              DEST_DIR=\"\u002Froot\u002F$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[134,1398,1400],{"class":136,"line":1399},142,[134,1401,1402],{"class":165},"              echo \"部署容器 vm-rocky-02:$(dirname ${FILE#homelab\u002Fvm-rocky-02\u002F})\"\n",[134,1404,1406],{"class":136,"line":1405},143,[134,1407,1408],{"class":165},"              ssh root@vm-rocky-02.home \"cd $DEST_DIR && docker compose up -d\"\n",[134,1410,1412],{"class":136,"line":1411},144,[134,1413,1311],{"class":165},[134,1415,1417],{"class":136,"line":1416},145,[134,1418,597],{"emptyLinePlaceholder":596},[134,1420,1422],{"class":136,"line":1421},146,[134,1423,1424],{"class":165},"            # if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002F*\" ]]; then\n",[134,1426,1428],{"class":136,"line":1427},147,[134,1429,1430],{"class":165},"            #   DEST_DIR=\"\u002Froot\u002Fconfig-files\u002Fgateway\u002F$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[134,1432,1434],{"class":136,"line":1433},148,[134,1435,1436],{"class":165},"            #   echo \"部署容器 gateway.aliyun:$(dirname ${FILE#aliyun\u002Fgateway\u002F})\"\n",[134,1438,1440],{"class":136,"line":1439},149,[134,1441,1442],{"class":165},"            #   ssh root@gateway.aliyun \"cd $DEST_DIR && docker compose up -d\"\n",[134,1444,1446],{"class":136,"line":1445},150,[134,1447,1448],{"class":165},"            # fi\n",[134,1450,1452],{"class":136,"line":1451},151,[134,1453,597],{"emptyLinePlaceholder":596},[134,1455,1457],{"class":136,"line":1456},152,[134,1458,1459],{"class":165},"            echo \"容器重新部署完成\"\n",[134,1461,1463],{"class":136,"line":1462},153,[134,1464,1465],{"class":165},"            continue\n",[134,1467,1469],{"class":136,"line":1468},154,[134,1470,781],{"class":165},[134,1472,1474],{"class":136,"line":1473},155,[134,1475,597],{"emptyLinePlaceholder":596},[134,1477,1479],{"class":136,"line":1478},156,[134,1480,1481],{"class":165},"          # 如果文件是 homelab\u002Fsynology\u002Fnginx\u002F*, 重新启动 nginx\n",[134,1483,1485],{"class":136,"line":1484},157,[134,1486,1487],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Fnginx\u002F*\" ]]; then\n",[134,1489,1491],{"class":136,"line":1490},158,[134,1492,1493],{"class":165},"            echo \"重新启动 synology:nginx 服务\"\n",[134,1495,1497],{"class":136,"line":1496},159,[134,1498,1499],{"class":165},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && docker exec nginx nginx -s reload\"\n",[134,1501,1503],{"class":136,"line":1502},160,[134,1504,781],{"class":165},[134,1506,1508],{"class":136,"line":1507},161,[134,1509,597],{"emptyLinePlaceholder":596},[134,1511,1513],{"class":136,"line":1512},162,[134,1514,1515],{"class":165},"          # 如果文件是 aliyun\u002Fgateway\u002Fnginx\u002F*, 重新启动 nginx\n",[134,1517,1519],{"class":136,"line":1518},163,[134,1520,1521],{"class":165},"          if [[ \"$FILE\" == \"aliyun\u002Fgateway\u002Fnginx\u002F*\" ]]; then\n",[134,1523,1525],{"class":136,"line":1524},164,[134,1526,1527],{"class":165},"            echo \"重新启动 gateway.aliyun:nginx\"\n",[134,1529,1531],{"class":136,"line":1530},165,[134,1532,1533],{"class":165},"            ssh root@gateway.aliyun \"nginx -s reload\"\n",[134,1535,1537],{"class":136,"line":1536},166,[134,1538,781],{"class":165},[134,1540,1542],{"class":136,"line":1541},167,[134,1543,597],{"emptyLinePlaceholder":596},[134,1545,1547],{"class":136,"line":1546},168,[134,1548,1549],{"class":165},"          # 如果文件是 aliyun\u002Fhk\u002Fnginx\u002F*, 重新启动 nginx\n",[134,1551,1553],{"class":136,"line":1552},169,[134,1554,1221],{"class":165},[134,1556,1558],{"class":136,"line":1557},170,[134,1559,1560],{"class":165},"            echo \"重新启动 hk.aliyun:nginx\"\n",[134,1562,1564],{"class":136,"line":1563},171,[134,1565,1566],{"class":165},"            ssh root@hk.aliyun \"nginx -s reload\"\n",[134,1568,1570],{"class":136,"line":1569},172,[134,1571,781],{"class":165},[134,1573,1575],{"class":136,"line":1574},173,[134,1576,597],{"emptyLinePlaceholder":596},[134,1578,1580],{"class":136,"line":1579},174,[134,1581,1582],{"class":165},"          # 如果文件是 rinetd.conf, 重新启动 rinetd\n",[134,1584,1586],{"class":136,"line":1585},175,[134,1587,1588],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Frinetd\u002Fconfig\u002Frinetd.conf\" ]]; then\n",[134,1590,1592],{"class":136,"line":1591},176,[134,1593,1594],{"class":165},"            echo \"重新启动 synology:rinetd 服务\"\n",[134,1596,1598],{"class":136,"line":1597},177,[134,1599,1600],{"class":165},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Frinetd && docker-compose restart\"\n",[134,1602,1604],{"class":136,"line":1603},178,[134,1605,781],{"class":165},[134,1607,1609],{"class":136,"line":1608},179,[134,1610,597],{"emptyLinePlaceholder":596},[134,1612,1614],{"class":136,"line":1613},180,[134,1615,1616],{"class":165},"          # 如果文件是 gitlab.rb, 重新配置 gitlab\n",[134,1618,1620],{"class":136,"line":1619},181,[134,1621,1622],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fgitlab\u002Fconfig\u002Fgitlab.rb\" ]]; then\n",[134,1624,1626],{"class":136,"line":1625},182,[134,1627,1628],{"class":165},"            echo \"重新配置 vm-rocky-01:gitlab\"\n",[134,1630,1632],{"class":136,"line":1631},183,[134,1633,1634],{"class":165},"            ssh root@vm-rocky-01.home \"docker exec gitlab gitlab-ctl reconfigure\"\n",[134,1636,1638],{"class":136,"line":1637},184,[134,1639,781],{"class":165},[134,1641,1643],{"class":136,"line":1642},185,[134,1644,597],{"emptyLinePlaceholder":596},[134,1646,1648],{"class":136,"line":1647},186,[134,1649,1650],{"class":165},"          # 如果文件是 prometheus.yml, 重新启动 prometheus\n",[134,1652,1654],{"class":136,"line":1653},187,[134,1655,1656],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Fprometheus\u002Fconfig\u002Fprometheus.yml\" ]]; then\n",[134,1658,1660],{"class":136,"line":1659},188,[134,1661,1662],{"class":165},"            echo \"重新启动 vm-rocky-01:prometheus\"\n",[134,1664,1666],{"class":136,"line":1665},189,[134,1667,1668],{"class":165},"            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Fprometheus && docker compose restart\"\n",[134,1670,1672],{"class":136,"line":1671},190,[134,1673,781],{"class":165},[134,1675,1677],{"class":136,"line":1676},191,[134,1678,597],{"emptyLinePlaceholder":596},[134,1680,1682],{"class":136,"line":1681},192,[134,1683,1684],{"class":165},"          # 如果文件是 telegraf.conf, 重新启动 telegraf\n",[134,1686,1688],{"class":136,"line":1687},193,[134,1689,1690],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fsynology\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n",[134,1692,1694],{"class":136,"line":1693},194,[134,1695,1696],{"class":165},"            echo \"重新启动 synology:telegraf\"\n",[134,1698,1700],{"class":136,"line":1699},195,[134,1701,1702],{"class":165},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Ftelegraf && docker-compose restart\"\n",[134,1704,1706],{"class":136,"line":1705},196,[134,1707,781],{"class":165},[134,1709,1711],{"class":136,"line":1710},197,[134,1712,597],{"emptyLinePlaceholder":596},[134,1714,1716],{"class":136,"line":1715},198,[134,1717,1718],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-01\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n",[134,1720,1722],{"class":136,"line":1721},199,[134,1723,1724],{"class":165},"            echo \"重新启动 vm-rocky-01:telegraf\"\n",[134,1726,1728],{"class":136,"line":1727},200,[134,1729,1730],{"class":165},"            ssh root@vm-rocky-01.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n",[134,1732,1734],{"class":136,"line":1733},201,[134,1735,781],{"class":165},[134,1737,1739],{"class":136,"line":1738},202,[134,1740,597],{"emptyLinePlaceholder":596},[134,1742,1744],{"class":136,"line":1743},203,[134,1745,1746],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Ftelegraf\u002Fconf\u002Ftelegraf.conf\" ]]; then\n",[134,1748,1750],{"class":136,"line":1749},204,[134,1751,1752],{"class":165},"            echo \"重新启动 vm-rocky-02:telegraf\"\n",[134,1754,1756],{"class":136,"line":1755},205,[134,1757,1758],{"class":165},"            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Ftelegraf && docker compose restart\"\n",[134,1760,1762],{"class":136,"line":1761},206,[134,1763,781],{"class":165},[134,1765,1767],{"class":136,"line":1766},207,[134,1768,597],{"emptyLinePlaceholder":596},[134,1770,1772],{"class":136,"line":1771},208,[134,1773,1774],{"class":165},"          # 如果文件是 bots\u002Fconfig\u002Fclash\u002F*.yaml, 更新 clash 配置\n",[134,1776,1778],{"class":136,"line":1777},209,[134,1779,1780],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fbots\u002Fconfig\u002Fclash\u002F*.yaml\" ]]; then\n",[134,1782,1784],{"class":136,"line":1783},210,[134,1785,1786],{"class":165},"            echo \"重新启动 vm-rocky-02:bots\"\n",[134,1788,1790],{"class":136,"line":1789},211,[134,1791,1792],{"class":165},"            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fbots && docker compose restart\"\n",[134,1794,1796],{"class":136,"line":1795},212,[134,1797,1798],{"class":165},"            echo \"等待 10 秒\"\n",[134,1800,1802],{"class":136,"line":1801},213,[134,1803,1804],{"class":165},"            sleep 10\n",[134,1806,1808],{"class":136,"line":1807},214,[134,1809,1810],{"class":165},"            echo \"重新启动 synology:clash-premium\"\n",[134,1812,1814],{"class":136,"line":1813},215,[134,1815,1816],{"class":165},"            ssh -p 5110 root@synology.home \"export PATH=\\$PATH:\u002Fusr\u002Flocal\u002Fbin && cd \u002Fvolume3\u002Fdocker\u002Fsynology\u002Fclash-premium && docker-compose restart\"\n",[134,1818,1820],{"class":136,"line":1819},216,[134,1821,781],{"class":165},[134,1823,1825],{"class":136,"line":1824},217,[134,1826,597],{"emptyLinePlaceholder":596},[134,1828,1830],{"class":136,"line":1829},218,[134,1831,1832],{"class":165},"          # 如果文件是 artalk\u002Fdata\u002Fartalk.yml, 重新启动 artalk\n",[134,1834,1836],{"class":136,"line":1835},219,[134,1837,1838],{"class":165},"          if [[ \"$FILE\" == \"homelab\u002Fvm-rocky-02\u002Fartalk\u002Fdata\u002Fartalk.yml\" ]]; then\n",[134,1840,1842],{"class":136,"line":1841},220,[134,1843,1844],{"class":165},"            echo \"重新启动 vm-rocky-02:artalk\"\n",[134,1846,1848],{"class":136,"line":1847},221,[134,1849,1850],{"class":165},"            ssh root@vm-rocky-02.home \"cd \u002Froot\u002Fartalk && docker compose restart\"\n",[134,1852,1854],{"class":136,"line":1853},222,[134,1855,781],{"class":165},[134,1857,1859],{"class":136,"line":1858},223,[134,1860,597],{"emptyLinePlaceholder":596},[134,1862,1864],{"class":136,"line":1863},224,[134,1865,890],{"class":165},[134,1867,1869],{"class":136,"line":1868},225,[134,1870,1038],{"class":165},[134,1872,1874,1876],{"class":136,"line":1873},226,[134,1875,625],{"class":140},[134,1877,1878],{"class":165}," echo \"部署完成\"\n",[134,1880,1882,1885],{"class":136,"line":1881},227,[134,1883,1884],{"class":572},"  only",[134,1886,576],{"class":140},[134,1888,1890,1892],{"class":136,"line":1889},228,[134,1891,625],{"class":140},[134,1893,642],{"class":165},[28,1895,1896,1897,104,1900,1903],{},"其中，",[75,1898,1899],{},"SSH_KNOWN_HOSTS",[75,1901,1902],{},"SSH_PRIVATE_KEY"," 存储在 GitLab CI\u002FCD 的变量中，用于 SSH 登录到对应的宿主机上。",[28,1905,1906,1907,1910,1911,1914,1915,1917,1918,1921,1922,1924],{},"当我需要改某个容器的配置文件时，例如当我需要修改 ",[75,1908,1909],{},"synology"," 上的 ",[75,1912,1913],{},"nginx"," 配置，我只需要去 ",[75,1916,77],{}," 项目中修改 ",[75,1919,1920],{},"homelab\u002Fsynology\u002Fnginx\u002Fnginx.conf"," 文件，然后 commit，push，等待几秒钟，GitLab CI 就会自动帮我完成配置文件的更新以及 ",[75,1923,1913],{}," 的重启。",[28,1926,1927],{},"每周一，renovate 会自动发起并合并更新容器镜像版本的 MR，然后 GitLab CI 会自动帮我更新容器。",[28,1929,1930],{},"对了，GitLab 、GitLab Runner、Renovate 也都是通过容器私有化部署在 HomeLab 中的，这些就不赘述。",[1932,1933,1934],"style",{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}",{"title":94,"searchDepth":144,"depth":144,"links":1936},[1937,1938],{"id":37,"depth":144,"text":37},{"id":62,"depth":144,"text":62},null,"2024-12-12",false,"md",{},"\u002Fposts\u002F2024\u002Fmanaging-containers-in-my-homelab",{"text":1946,"minutes":1947,"time":1948,"words":1949},"11 min read",10.24,614400,2048,{"title":23,"description":30},{"loc":1944},"posts\u002F2024\u002F20241212.managing-containers-in-my-homelab",[1954,1955,1956,1957],"技术","DevOps","HomeLab","Docker","sSWijwbQSH5iLEufQ0ZZuj2tXgOjghNQbswIr9u7b4A",{"id":1960,"title":1961,"body":1962,"class":1939,"cover":1939,"coverSize":1939,"date":1988,"description":1966,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":1989,"navigation":596,"path":1990,"readingTime":1991,"seo":1996,"sitemap":1997,"stem":1998,"tags":1999,"time":1939,"weather":1939,"__hash__":2001},"posts\u002Fposts\u002F2024\u002F20240327.wordpress-hacked.md","记录一次 WordPress 被恶意代码注入的问题",{"type":25,"value":1963,"toc":1986},[1964,1967,1970,1977,1983],[28,1965,1966],{},"今天发现之前帮一个客户维护的服务器流量近期一直比较高，是平常的几十倍。看了下请求，都是一些奇奇怪怪的 URL，并且甚至还能返回 200。访问看了下，是一些别的产品的营销页，看了下请求来源，也都是一些营销机器人。",[28,1968,1969],{},"初步怀疑是客户 WordPress 的管理员密码被撞库了，然后 WordPress 本身又有一些漏洞导致代码文件被改了。上去看了下，发现篡改了很多文件。",[28,1971,1972,1973,1976],{},"后续就是将 WordPress 的代码恢复成之前的版本，清理了一些不用的管理员账号，并且把剩下唯一的管理员密码重新修改了。然后在 ",[75,1974,1975],{},"Apache"," 上把流量较高的一些请求的路由和 UA 做了限制，直接禁止访问降低带宽。",[86,1978,1981],{"className":1979,"code":1980,"language":91},[89],"RewriteCond %{HTTP_USER_AGENT} (DataForSeoBot|SemrushBot) [NC,OR]\nRewriteCond %{REQUEST_URI} ^\u002Fgodsend\u002F [NC]\nRewriteRule .* - [F]\n",[75,1982,1980],{"__ignoreMap":94},[28,1984,1985],{},"后面流量就恢复正常了。",{"title":94,"searchDepth":144,"depth":144,"links":1987},[],"2024-03-27",{},"\u002Fposts\u002F2024\u002Fwordpress-hacked",{"text":1992,"minutes":1993,"time":1994,"words":1995},"2 min read",1.225,73500,245,{"title":1961,"description":1966},{"loc":1990},"posts\u002F2024\u002F20240327.wordpress-hacked",[1954,1955,2000],"WordPress","8M8kF_xksYyDPkt3CFRWkUA01rl8UzgjELJTcjEXnVY",{"id":2003,"title":2004,"body":2005,"class":1939,"cover":1939,"coverSize":1939,"date":2046,"description":2009,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":2047,"navigation":596,"path":2048,"readingTime":2049,"seo":2053,"sitemap":2054,"stem":2055,"tags":2056,"time":1939,"weather":1939,"__hash__":2058},"posts\u002Fposts\u002F2023\u002F20230518.use-cloudflare-speed-up-overseas-traffic.md","使用 Cloudflare 加速博客海外访问速度",{"type":25,"value":2006,"toc":2044},[2007,2010,2013,2016,2019,2022,2025,2028,2041],[28,2008,2009],{},"这几天对 hadb.me 博客又做了一次迁移和优化。",[28,2011,2012],{},"原先 hadb.me 是直接部署在阿里云的 k8s 上的，但为了统一博客的数据备份，决定迁移到 HomeLab ，数据存到 NAS 上，容器部署到 NUC 的 docker 中。这样数据可以跟着整个 NAS 的备份策略一起。所以这次架构调整的主要目的是为了方便博客数据的统一备份。",[28,2014,2015],{},"但是由于 HomeLab 无法直接暴露 443 端口，所以域名不能直接解析到家里的 IP。",[28,2017,2018],{},"一开始尝试了下直接接入 Cloudflare，发现境外访问速度很快，但境内直接访问的话，速度堪忧。于是研究了一下，最终根据访问者的位置使用不同的解析方式实现境内外的同时加速的目标。",[28,2020,2021],{},"最终的架构是这样：",[67,2023],{"description":2024,"filename":70},"hadb.me 博客网络架构",[28,2026,2027],{},"在境内，直接解析到阿里云的 SLB 上，阿里云上我是有一套 k8s 集群，里面起了个 nginx 容器，反向代理到 HomeLab 的非标端口上，HomeLab 的域名解析通过阿里云解析的 API 动态更新。",[28,2029,2030,2031,2034,2035,104,2037,2040],{},"在境外，通过 CNAME 解析到 Cloudflare 上绑定一个其他域名 ",[75,2032,2033],{},"xxx.com","，这个域名通过 Cloudflare 的 API 会动态更新解析到 HomeLab 的外网 IP 上。在这个域名的 Origin Rules 里面设置主机名为 ",[75,2036,2033],{},[75,2038,2039],{},"hadb.me"," 的时候都重写端口到 HomeLab 的非标端口上。",[28,2042,2043],{},"至此，完成架构迁移。既满足了备份需求，海外访问速度上也有提升。",{"title":94,"searchDepth":144,"depth":144,"links":2045},[],"2023-05-18",{},"\u002Fposts\u002F2023\u002Fuse-cloudflare-speed-up-overseas-traffic",{"text":1992,"minutes":2050,"time":2051,"words":2052},1.95,117000,390,{"title":2004,"description":2009},{"loc":2048},"posts\u002F2023\u002F20230518.use-cloudflare-speed-up-overseas-traffic",[1954,2057,1955],"博客","ACtCrCQ6aSo6KIRwPlOKjc7i5Yh2L3RAT_a91uJ9FWo",{"id":2060,"title":2061,"body":2062,"class":1939,"cover":2665,"coverSize":1939,"date":2666,"description":2066,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":2667,"navigation":596,"path":2668,"readingTime":2669,"seo":2674,"sitemap":2675,"stem":2676,"tags":2677,"time":1939,"weather":1939,"__hash__":2679},"posts\u002Fposts\u002F2023\u002F20230423.gitlab-ci-auto-deploy-python-lib.md","GitLab CI 配置自动化打包上传 Python 库",{"type":25,"value":2063,"toc":2663},[2064,2067,2078,2432,2437,2553,2571,2588,2591,2660],[28,2065,2066],{},"自己之前有些 python 脚本类的项目，会用到一些通用的能力，如读取配置、打日志等，每次都 copy 一份 utils 目录有些不够优雅，于是撸了一个公共库，方便自己使用。",[28,2068,2069,2070,2073,2074,2077],{},"为了能配合 GitLab CI，",[75,2071,2072],{},"setup.py"," 需要做一些小调整，版本号不需要手动输入了，直接读取 ",[75,2075,2076],{},"$CI_COMMIT_TAG","，代码如下：",[86,2079,2083],{"className":2080,"code":2081,"language":2082,"meta":94,"style":94},"language-python shiki shiki-themes material-theme-lighter github-light github-dark","import os\n\nimport setuptools\n\nwith open(\"README.md\", \"r\") as fh:\n    long_description = fh.read()\n\nsetuptools.setup(\n    name=\"yuanfen\",\n    version=os.environ.get(\"CI_COMMIT_TAG\", \"0.0.0\"),\n    author=\"Bean\",\n    author_email=\"bean@yuanfen.net\",\n    description=\"Yuanfen Python Library\",\n    long_description=long_description,\n    long_description_content_type=\"text\u002Fmarkdown\",\n    url=\"\",\n    install_requires=[\"pyyaml\", \"watchdog\"],\n    packages=setuptools.find_packages(),\n    classifiers=[\n        \"Programming Language :: Python :: 3\",\n        \"License :: OSI Approved :: MIT License\",\n        \"Operating System :: OS Independent\",\n    ],\n)\n\n","python",[75,2084,2085,2094,2098,2105,2109,2148,2169,2173,2186,2203,2245,2261,2277,2293,2305,2321,2333,2361,2378,2388,2400,2411,2422,2427],{"__ignoreMap":94},[134,2086,2087,2090],{"class":136,"line":137},[134,2088,2089],{"class":727},"import",[134,2091,2093],{"class":2092},"su5hD"," os\n",[134,2095,2096],{"class":136,"line":144},[134,2097,597],{"emptyLinePlaceholder":596},[134,2099,2100,2102],{"class":136,"line":174},[134,2101,2089],{"class":727},[134,2103,2104],{"class":2092}," setuptools\n",[134,2106,2107],{"class":136,"line":218},[134,2108,597],{"emptyLinePlaceholder":596},[134,2110,2111,2114,2118,2121,2123,2126,2128,2130,2132,2135,2137,2140,2143,2146],{"class":136,"line":233},[134,2112,2113],{"class":727},"with",[134,2115,2117],{"class":2116},"sptTA"," open",[134,2119,2120],{"class":140},"(",[134,2122,155],{"class":161},[134,2124,2125],{"class":165},"README.md",[134,2127,155],{"class":161},[134,2129,196],{"class":140},[134,2131,162],{"class":161},[134,2133,2134],{"class":165},"r",[134,2136,155],{"class":161},[134,2138,2139],{"class":140},")",[134,2141,2142],{"class":727}," as",[134,2144,2145],{"class":2092}," fh",[134,2147,576],{"class":140},[134,2149,2150,2153,2157,2159,2162,2166],{"class":136,"line":239},[134,2151,2152],{"class":2092},"    long_description ",[134,2154,2156],{"class":2155},"smGrS","=",[134,2158,2145],{"class":2092},[134,2160,2161],{"class":140},".",[134,2163,2165],{"class":2164},"slqww","read",[134,2167,2168],{"class":140},"()\n",[134,2170,2171],{"class":136,"line":264},[134,2172,597],{"emptyLinePlaceholder":596},[134,2174,2175,2178,2180,2183],{"class":136,"line":296},[134,2176,2177],{"class":2092},"setuptools",[134,2179,2161],{"class":140},[134,2181,2182],{"class":2164},"setup",[134,2184,2185],{"class":140},"(\n",[134,2187,2188,2192,2194,2196,2199,2201],{"class":136,"line":311},[134,2189,2191],{"class":2190},"s99_P","    name",[134,2193,2156],{"class":2155},[134,2195,155],{"class":161},[134,2197,2198],{"class":165},"yuanfen",[134,2200,155],{"class":161},[134,2202,171],{"class":140},[134,2204,2205,2208,2210,2213,2215,2219,2221,2224,2226,2228,2231,2233,2235,2237,2240,2242],{"class":136,"line":317},[134,2206,2207],{"class":2190},"    version",[134,2209,2156],{"class":2155},[134,2211,2212],{"class":2164},"os",[134,2214,2161],{"class":140},[134,2216,2218],{"class":2217},"skxfh","environ",[134,2220,2161],{"class":140},[134,2222,2223],{"class":2164},"get",[134,2225,2120],{"class":140},[134,2227,155],{"class":161},[134,2229,2230],{"class":165},"CI_COMMIT_TAG",[134,2232,155],{"class":161},[134,2234,196],{"class":140},[134,2236,162],{"class":161},[134,2238,2239],{"class":165},"0.0.0",[134,2241,155],{"class":161},[134,2243,2244],{"class":140},"),\n",[134,2246,2247,2250,2252,2254,2257,2259],{"class":136,"line":322},[134,2248,2249],{"class":2190},"    author",[134,2251,2156],{"class":2155},[134,2253,155],{"class":161},[134,2255,2256],{"class":165},"Bean",[134,2258,155],{"class":161},[134,2260,171],{"class":140},[134,2262,2263,2266,2268,2270,2273,2275],{"class":136,"line":343},[134,2264,2265],{"class":2190},"    author_email",[134,2267,2156],{"class":2155},[134,2269,155],{"class":161},[134,2271,2272],{"class":165},"bean@yuanfen.net",[134,2274,155],{"class":161},[134,2276,171],{"class":140},[134,2278,2279,2282,2284,2286,2289,2291],{"class":136,"line":365},[134,2280,2281],{"class":2190},"    description",[134,2283,2156],{"class":2155},[134,2285,155],{"class":161},[134,2287,2288],{"class":165},"Yuanfen Python Library",[134,2290,155],{"class":161},[134,2292,171],{"class":140},[134,2294,2295,2298,2300,2303],{"class":136,"line":385},[134,2296,2297],{"class":2190},"    long_description",[134,2299,2156],{"class":2155},[134,2301,2302],{"class":2164},"long_description",[134,2304,171],{"class":140},[134,2306,2307,2310,2312,2314,2317,2319],{"class":136,"line":390},[134,2308,2309],{"class":2190},"    long_description_content_type",[134,2311,2156],{"class":2155},[134,2313,155],{"class":161},[134,2315,2316],{"class":165},"text\u002Fmarkdown",[134,2318,155],{"class":161},[134,2320,171],{"class":140},[134,2322,2323,2326,2328,2331],{"class":136,"line":395},[134,2324,2325],{"class":2190},"    url",[134,2327,2156],{"class":2155},[134,2329,2330],{"class":161},"\"\"",[134,2332,171],{"class":140},[134,2334,2335,2338,2340,2343,2345,2348,2350,2352,2354,2357,2359],{"class":136,"line":416},[134,2336,2337],{"class":2190},"    install_requires",[134,2339,2156],{"class":2155},[134,2341,2342],{"class":140},"[",[134,2344,155],{"class":161},[134,2346,2347],{"class":165},"pyyaml",[134,2349,155],{"class":161},[134,2351,196],{"class":140},[134,2353,162],{"class":161},[134,2355,2356],{"class":165},"watchdog",[134,2358,155],{"class":161},[134,2360,215],{"class":140},[134,2362,2363,2366,2368,2370,2372,2375],{"class":136,"line":448},[134,2364,2365],{"class":2190},"    packages",[134,2367,2156],{"class":2155},[134,2369,2177],{"class":2164},[134,2371,2161],{"class":140},[134,2373,2374],{"class":2164},"find_packages",[134,2376,2377],{"class":140},"(),\n",[134,2379,2380,2383,2385],{"class":136,"line":465},[134,2381,2382],{"class":2190},"    classifiers",[134,2384,2156],{"class":2155},[134,2386,2387],{"class":140},"[\n",[134,2389,2390,2393,2396,2398],{"class":136,"line":489},[134,2391,2392],{"class":161},"        \"",[134,2394,2395],{"class":165},"Programming Language :: Python :: 3",[134,2397,155],{"class":161},[134,2399,171],{"class":140},[134,2401,2402,2404,2407,2409],{"class":136,"line":495},[134,2403,2392],{"class":161},[134,2405,2406],{"class":165},"License :: OSI Approved :: MIT License",[134,2408,155],{"class":161},[134,2410,171],{"class":140},[134,2412,2413,2415,2418,2420],{"class":136,"line":501},[134,2414,2392],{"class":161},[134,2416,2417],{"class":165},"Operating System :: OS Independent",[134,2419,155],{"class":161},[134,2421,171],{"class":140},[134,2423,2424],{"class":136,"line":522},[134,2425,2426],{"class":140},"    ],\n",[134,2428,2429],{"class":136,"line":541},[134,2430,2431],{"class":140},")\n",[28,2433,2434,2436],{},[75,2435,560],{}," 代码如下：",[86,2438,2440],{"className":563,"code":2439,"language":565,"meta":94,"style":94},"image: python:3\n\nstages:\n  - deploy\n\ndeploy:\n  stage: deploy\n  variables:\n    TWINE_USERNAME: $TWINE_USERNAME\n    TWINE_PASSWORD: $TWINE_PASSWORD\n  script:\n    - python setup.py sdist bdist_wheel\n    - pip install twine -i https:\u002F\u002Fpypi.tuna.tsinghua.edu.cn\u002Fsimple\n    - twine upload dist\u002F*\n  only:\n    - tags\n",[75,2441,2442,2452,2456,2462,2468,2472,2478,2486,2493,2503,2513,2519,2526,2533,2540,2546],{"__ignoreMap":94},[134,2443,2444,2447,2449],{"class":136,"line":137},[134,2445,2446],{"class":572},"image",[134,2448,158],{"class":140},[134,2450,2451],{"class":165}," python:3\n",[134,2453,2454],{"class":136,"line":144},[134,2455,597],{"emptyLinePlaceholder":596},[134,2457,2458,2460],{"class":136,"line":174},[134,2459,573],{"class":572},[134,2461,576],{"class":140},[134,2463,2464,2466],{"class":136,"line":218},[134,2465,581],{"class":140},[134,2467,591],{"class":165},[134,2469,2470],{"class":136,"line":233},[134,2471,597],{"emptyLinePlaceholder":596},[134,2473,2474,2476],{"class":136,"line":239},[134,2475,651],{"class":572},[134,2477,576],{"class":140},[134,2479,2480,2482,2484],{"class":136,"line":264},[134,2481,609],{"class":572},[134,2483,158],{"class":140},[134,2485,591],{"class":165},[134,2487,2488,2491],{"class":136,"line":296},[134,2489,2490],{"class":572},"  variables",[134,2492,576],{"class":140},[134,2494,2495,2498,2500],{"class":136,"line":311},[134,2496,2497],{"class":572},"    TWINE_USERNAME",[134,2499,158],{"class":140},[134,2501,2502],{"class":165}," $TWINE_USERNAME\n",[134,2504,2505,2508,2510],{"class":136,"line":317},[134,2506,2507],{"class":572},"    TWINE_PASSWORD",[134,2509,158],{"class":140},[134,2511,2512],{"class":165}," $TWINE_PASSWORD\n",[134,2514,2515,2517],{"class":136,"line":322},[134,2516,618],{"class":572},[134,2518,576],{"class":140},[134,2520,2521,2523],{"class":136,"line":343},[134,2522,625],{"class":140},[134,2524,2525],{"class":165}," python setup.py sdist bdist_wheel\n",[134,2527,2528,2530],{"class":136,"line":365},[134,2529,625],{"class":140},[134,2531,2532],{"class":165}," pip install twine -i https:\u002F\u002Fpypi.tuna.tsinghua.edu.cn\u002Fsimple\n",[134,2534,2535,2537],{"class":136,"line":385},[134,2536,625],{"class":140},[134,2538,2539],{"class":165}," twine upload dist\u002F*\n",[134,2541,2542,2544],{"class":136,"line":390},[134,2543,1884],{"class":572},[134,2545,576],{"class":140},[134,2547,2548,2550],{"class":136,"line":395},[134,2549,625],{"class":140},[134,2551,2552],{"class":165}," tags\n",[28,2554,2555,2556,2559,2560,104,2563,2566,2567,2570],{},"其中，需要在 GitLab 项目 ",[75,2557,2558],{},"Setting -> CI\u002FCD -> Variables"," 中配置 ",[75,2561,2562],{},"TWINE_USERNAME",[75,2564,2565],{},"TWINE_PASSWORD","，这个是你在 ",[75,2568,2569],{},"pypi.org"," 上的用户名和密码。",[28,2572,2573,2574,2576,2577,2580,2581,2584,2585,2587],{},"另外需要注意的是，仓库的目录结构，",[75,2575,2072],{}," 中指定的 ",[75,2578,2579],{},"name"," 是包名，而 ",[75,2582,2583],{},"__init__.py"," 所在的文件夹名是实际 ",[75,2586,2089],{}," 的导入名。上传包时，PyPI 会检查报名是否重复，但不会检查导入名，所以如果定义了一个容易导致重复的导入名的话，就有可能有冲突。所以一般情况下该文件夹尽量是与包名统一，这样不容易有冲突。",[28,2589,2590],{},"以下是 ChatGPT 给出的目录结构的建议：",[2592,2593,2594,2597,2603,2622,2628,2634,2643,2648,2654],"blockquote",{},[28,2595,2596],{},"当你创建一个 Python 库时，通常需要遵循一些标准的目录结构和文件命名规范。下面是一个常见的建议目录结构，包括了一些常见文件和目录：",[86,2598,2601],{"className":2599,"code":2600,"language":91},[89],"your_library\u002F\n   your_library\u002F\n       __init__.py\n       module1.py\n       module2.py\n       ...\n   examples\u002F\n       example1.py\n       example2.py\n       ...\n   tests\u002F\n       test_module1.py\n       test_module2.py\n       ...\n   README.md\n   LICENSE\n   setup.py\n   requirements.txt\n   .gitignore\n\n",[75,2602,2600],{"__ignoreMap":94},[28,2604,2605,2606,2609,2610,2612,2613,2615,2616,2618,2619,2621],{},"在这个目录结构中，",[75,2607,2608],{},"your_library"," 是你的库的名称，它应该与 ",[75,2611,2072],{}," 文件中定义的名称相同。",[75,2614,2608],{}," 目录包含了你的库的所有源代码和模块文件，以及 ",[75,2617,2583],{}," 文件，用于将 ",[75,2620,2608],{}," 目录转换为 Python 包。",[28,2623,2624,2627],{},[75,2625,2626],{},"examples"," 目录包含了一些示例代码，用于演示如何使用你的库。这些示例代码可以帮助用户更快地了解和使用你的库。",[28,2629,2630,2633],{},[75,2631,2632],{},"tests"," 目录包含了单元测试和集成测试代码，用于确保你的库的功能和质量。在这个目录中，你可以为每个模块编写一个或多个测试文件，并使用测试框架运行这些测试。",[28,2635,2636,2638,2639,2642],{},[75,2637,2125],{}," 文件是你的库的说明文档，用于介绍你的库的功能、用法和示例。",[75,2640,2641],{},"LICENSE"," 文件包含了你的库的许可证信息，用于明确你的库的使用和分发条件。",[28,2644,2645,2647],{},[75,2646,2072],{}," 文件是你的库的安装脚本，用于将你的库安装到 Python 环境中。在这个文件中，你需要指定库的名称、版本、作者、描述、依赖等信息。",[28,2649,2650,2653],{},[75,2651,2652],{},"requirements.txt"," 文件包含了你的库依赖的其他 Python 包的名称和版本号。在使用 pip 安装你的库时，pip 将自动安装这些依赖包。",[28,2655,2656,2659],{},[75,2657,2658],{},".gitignore"," 文件是 Git 版本控制系统的忽略文件，用于指定哪些文件或目录不需要被 Git 跟踪和提交。",[1932,2661,2662],{},"html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sptTA, html code.shiki .sptTA{--shiki-light:#6182B8;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .slqww, html code.shiki .slqww{--shiki-light:#6182B8;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":94,"searchDepth":144,"depth":144,"links":2664},[],"jpg","2023-04-23",{},"\u002Fposts\u002F2023\u002Fgitlab-ci-auto-deploy-python-lib",{"text":2670,"minutes":2671,"time":2672,"words":2673},"4 min read",3.835,230100,767,{"title":2061,"description":2066},{"loc":2668},"posts\u002F2023\u002F20230423.gitlab-ci-auto-deploy-python-lib",[1954,1955,2678],"Python","o6Yb6IDvjBzm2kTY_GeQKG92soCUwh_mh3f6D78Xm-s",{"id":2681,"title":2682,"body":2683,"class":1939,"cover":2665,"coverSize":1939,"date":4661,"description":94,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":4662,"navigation":596,"path":4663,"readingTime":4664,"seo":4668,"sitemap":4669,"stem":4670,"tags":4671,"time":1939,"weather":1939,"__hash__":4673},"posts\u002Fposts\u002F2022\u002F20220524.use-gitlab-to-deploy-ghost-theme-automatically.md","使用 GitLab CI 自动部署 Ghost 主题",{"type":25,"value":2684,"toc":4659},[2685,2690,2693,2704,3440,3450,3459,3461,3471,3474,3477,3500,3507,4420,4425,4521,4527,4639,4653,4656],[2592,2686,2687],{},[28,2688,2689],{},"居家隔离的第 N 天",[28,2691,2692],{},"今天收到了 Ghost 5.0 发布的邮件，第一时间更新了下，发现主题里有些功能已经不兼容了，于是准备对主题做下更新。在看 Ghost Integrations 的时候发现有个 GitHub 的插件特别好用，支持通过 GitHub Actions 自动部署你的主题。但是我自己的项目用的都是 GitLab，找了一圈，没有官方的插件。于是尝试自己通过 GitLab CI 来实现。",[28,2694,2695,2696,2703],{},"大致看了下基于 GitHub Actions 自动部署的实现方式，通过官方提供的一个 ",[2697,2698,2702],"a",{"href":2699,"rel":2700},"https:\u002F\u002Fgithub.com\u002FTryGhost\u002Faction-deploy-theme\u002Fblob\u002Fmain\u002Findex.js",[2701],"nofollow","TryGhost\u002Faction-deploy-theme"," 的步骤，代码很简单，总共 40 行，我们来看下它做了什么：",[86,2705,2709],{"className":2706,"code":2707,"language":2708,"meta":94,"style":94},"language-javascript shiki shiki-themes material-theme-lighter github-light github-dark","const path = require('node:path')\nconst core = require('@actions\u002Fcore')\nconst exec = require('@actions\u002Fexec')\nconst GhostAdminApi = require('@tryghost\u002Fadmin-api');\n\n(async function main() {\n  try {\n    const url = core.getInput('api-url')\n    const api = new GhostAdminApi({\n      url,\n      key: core.getInput('api-key'),\n      version: 'canary'\n    })\n\n    const basePath = process.env.GITHUB_WORKSPACE\n    const pkgPath = path.join(process.env.GITHUB_WORKSPACE, 'package.json')\n\n    let zipPath = core.getInput('file')\n\n    \u002F\u002F Zip file was not provided - zip everything up!\n    if (!zipPath) {\n      const themeName = core.getInput('theme-name') || require(pkgPath).name\n      const themeZip = `${themeName}.zip`\n      const exclude = core.getInput('exclude') || ''\n      zipPath = themeZip\n\n      \u002F\u002F Create a zip\n      await exec.exec(`zip -r ${themeZip} ${core.getInput('working-directory') || '.'} -x *.git* *.zip yarn* npm* node_modules* *routes.yaml *redirects.yaml *redirects.json ${exclude}`, [], { cwd: basePath })\n    }\n\n    zipPath = path.join(basePath, zipPath)\n\n    \u002F\u002F Deploy it to the configured site\n    await api.themes.upload({ file: zipPath })\n    console.log(`${zipPath} successfully uploaded.`)\n  }\n  catch (err) {\n    console.error(err)\n    process.exit(1)\n  }\n}())\n","javascript",[75,2710,2711,2740,2762,2784,2809,2813,2832,2839,2867,2885,2892,2918,2934,2941,2945,2967,3008,3012,3039,3043,3049,3068,3112,3136,3167,3177,3181,3186,3277,3281,3285,3309,3313,3318,3351,3377,3382,3396,3411,3429,3433],{"__ignoreMap":94},[134,2712,2713,2717,2721,2724,2728,2730,2733,2736,2738],{"class":136,"line":137},[134,2714,2716],{"class":2715},"sbsja","const",[134,2718,2720],{"class":2719},"s_hVV"," path",[134,2722,2723],{"class":2155}," =",[134,2725,2727],{"class":2726},"sGLFI"," require",[134,2729,2120],{"class":2092},[134,2731,2732],{"class":161},"'",[134,2734,2735],{"class":165},"node:path",[134,2737,2732],{"class":161},[134,2739,2431],{"class":2092},[134,2741,2742,2744,2747,2749,2751,2753,2755,2758,2760],{"class":136,"line":144},[134,2743,2716],{"class":2715},[134,2745,2746],{"class":2719}," core",[134,2748,2723],{"class":2155},[134,2750,2727],{"class":2726},[134,2752,2120],{"class":2092},[134,2754,2732],{"class":161},[134,2756,2757],{"class":165},"@actions\u002Fcore",[134,2759,2732],{"class":161},[134,2761,2431],{"class":2092},[134,2763,2764,2766,2769,2771,2773,2775,2777,2780,2782],{"class":136,"line":174},[134,2765,2716],{"class":2715},[134,2767,2768],{"class":2719}," exec",[134,2770,2723],{"class":2155},[134,2772,2727],{"class":2726},[134,2774,2120],{"class":2092},[134,2776,2732],{"class":161},[134,2778,2779],{"class":165},"@actions\u002Fexec",[134,2781,2732],{"class":161},[134,2783,2431],{"class":2092},[134,2785,2786,2788,2791,2793,2795,2797,2799,2802,2804,2806],{"class":136,"line":218},[134,2787,2716],{"class":2715},[134,2789,2790],{"class":2719}," GhostAdminApi",[134,2792,2723],{"class":2155},[134,2794,2727],{"class":2726},[134,2796,2120],{"class":2092},[134,2798,2732],{"class":161},[134,2800,2801],{"class":165},"@tryghost\u002Fadmin-api",[134,2803,2732],{"class":161},[134,2805,2139],{"class":2092},[134,2807,2808],{"class":140},";\n",[134,2810,2811],{"class":136,"line":233},[134,2812,597],{"emptyLinePlaceholder":596},[134,2814,2815,2817,2820,2823,2826,2829],{"class":136,"line":239},[134,2816,2120],{"class":2092},[134,2818,2819],{"class":2715},"async",[134,2821,2822],{"class":2715}," function",[134,2824,2825],{"class":2726}," main",[134,2827,2828],{"class":140},"()",[134,2830,2831],{"class":140}," {\n",[134,2833,2834,2837],{"class":136,"line":264},[134,2835,2836],{"class":727},"  try",[134,2838,2831],{"class":140},[134,2840,2841,2844,2847,2849,2851,2853,2856,2858,2860,2863,2865],{"class":136,"line":296},[134,2842,2843],{"class":2715},"    const",[134,2845,2846],{"class":2719}," url",[134,2848,2723],{"class":2155},[134,2850,2746],{"class":2092},[134,2852,2161],{"class":140},[134,2854,2855],{"class":2726},"getInput",[134,2857,2120],{"class":2217},[134,2859,2732],{"class":161},[134,2861,2862],{"class":165},"api-url",[134,2864,2732],{"class":161},[134,2866,2431],{"class":2217},[134,2868,2869,2871,2874,2876,2879,2881,2883],{"class":136,"line":311},[134,2870,2843],{"class":2715},[134,2872,2873],{"class":2719}," api",[134,2875,2723],{"class":2155},[134,2877,2878],{"class":2155}," new",[134,2880,2790],{"class":2726},[134,2882,2120],{"class":2217},[134,2884,141],{"class":140},[134,2886,2887,2890],{"class":136,"line":317},[134,2888,2889],{"class":2092},"      url",[134,2891,171],{"class":140},[134,2893,2894,2897,2899,2901,2903,2905,2907,2909,2912,2914,2916],{"class":136,"line":322},[134,2895,2896],{"class":2217},"      key",[134,2898,158],{"class":140},[134,2900,2746],{"class":2092},[134,2902,2161],{"class":140},[134,2904,2855],{"class":2726},[134,2906,2120],{"class":2217},[134,2908,2732],{"class":161},[134,2910,2911],{"class":165},"api-key",[134,2913,2732],{"class":161},[134,2915,2139],{"class":2217},[134,2917,171],{"class":140},[134,2919,2920,2923,2925,2928,2931],{"class":136,"line":343},[134,2921,2922],{"class":2217},"      version",[134,2924,158],{"class":140},[134,2926,2927],{"class":161}," '",[134,2929,2930],{"class":165},"canary",[134,2932,2933],{"class":161},"'\n",[134,2935,2936,2939],{"class":136,"line":365},[134,2937,2938],{"class":140},"    }",[134,2940,2431],{"class":2217},[134,2942,2943],{"class":136,"line":385},[134,2944,597],{"emptyLinePlaceholder":596},[134,2946,2947,2949,2952,2954,2957,2959,2962,2964],{"class":136,"line":390},[134,2948,2843],{"class":2715},[134,2950,2951],{"class":2719}," basePath",[134,2953,2723],{"class":2155},[134,2955,2956],{"class":2092}," process",[134,2958,2161],{"class":140},[134,2960,2961],{"class":2092},"env",[134,2963,2161],{"class":140},[134,2965,2966],{"class":2719},"GITHUB_WORKSPACE\n",[134,2968,2969,2971,2974,2976,2978,2980,2983,2985,2988,2990,2992,2994,2997,2999,3001,3004,3006],{"class":136,"line":395},[134,2970,2843],{"class":2715},[134,2972,2973],{"class":2719}," pkgPath",[134,2975,2723],{"class":2155},[134,2977,2720],{"class":2092},[134,2979,2161],{"class":140},[134,2981,2982],{"class":2726},"join",[134,2984,2120],{"class":2217},[134,2986,2987],{"class":2092},"process",[134,2989,2161],{"class":140},[134,2991,2961],{"class":2092},[134,2993,2161],{"class":140},[134,2995,2996],{"class":2719},"GITHUB_WORKSPACE",[134,2998,196],{"class":140},[134,3000,2927],{"class":161},[134,3002,3003],{"class":165},"package.json",[134,3005,2732],{"class":161},[134,3007,2431],{"class":2217},[134,3009,3010],{"class":136,"line":416},[134,3011,597],{"emptyLinePlaceholder":596},[134,3013,3014,3017,3020,3022,3024,3026,3028,3030,3032,3035,3037],{"class":136,"line":448},[134,3015,3016],{"class":2715},"    let",[134,3018,3019],{"class":2092}," zipPath",[134,3021,2723],{"class":2155},[134,3023,2746],{"class":2092},[134,3025,2161],{"class":140},[134,3027,2855],{"class":2726},[134,3029,2120],{"class":2217},[134,3031,2732],{"class":161},[134,3033,3034],{"class":165},"file",[134,3036,2732],{"class":161},[134,3038,2431],{"class":2217},[134,3040,3041],{"class":136,"line":465},[134,3042,597],{"emptyLinePlaceholder":596},[134,3044,3045],{"class":136,"line":489},[134,3046,3048],{"class":3047},"sutJx","    \u002F\u002F Zip file was not provided - zip everything up!\n",[134,3050,3051,3054,3057,3060,3063,3066],{"class":136,"line":495},[134,3052,3053],{"class":727},"    if",[134,3055,3056],{"class":2217}," (",[134,3058,3059],{"class":2155},"!",[134,3061,3062],{"class":2092},"zipPath",[134,3064,3065],{"class":2217},") ",[134,3067,141],{"class":140},[134,3069,3070,3073,3076,3078,3080,3082,3084,3086,3088,3091,3093,3095,3098,3100,3102,3105,3107,3109],{"class":136,"line":501},[134,3071,3072],{"class":2715},"      const",[134,3074,3075],{"class":2719}," themeName",[134,3077,2723],{"class":2155},[134,3079,2746],{"class":2092},[134,3081,2161],{"class":140},[134,3083,2855],{"class":2726},[134,3085,2120],{"class":2217},[134,3087,2732],{"class":161},[134,3089,3090],{"class":165},"theme-name",[134,3092,2732],{"class":161},[134,3094,3065],{"class":2217},[134,3096,3097],{"class":2155},"||",[134,3099,2727],{"class":2726},[134,3101,2120],{"class":2217},[134,3103,3104],{"class":2092},"pkgPath",[134,3106,2139],{"class":2217},[134,3108,2161],{"class":140},[134,3110,3111],{"class":2092},"name\n",[134,3113,3114,3116,3119,3121,3124,3127,3130,3133],{"class":136,"line":522},[134,3115,3072],{"class":2715},[134,3117,3118],{"class":2719}," themeZip",[134,3120,2723],{"class":2155},[134,3122,3123],{"class":161}," `${",[134,3125,3126],{"class":2092},"themeName",[134,3128,3129],{"class":161},"}",[134,3131,3132],{"class":165},".zip",[134,3134,3135],{"class":161},"`\n",[134,3137,3138,3140,3143,3145,3147,3149,3151,3153,3155,3158,3160,3162,3164],{"class":136,"line":541},[134,3139,3072],{"class":2715},[134,3141,3142],{"class":2719}," exclude",[134,3144,2723],{"class":2155},[134,3146,2746],{"class":2092},[134,3148,2161],{"class":140},[134,3150,2855],{"class":2726},[134,3152,2120],{"class":2217},[134,3154,2732],{"class":161},[134,3156,3157],{"class":165},"exclude",[134,3159,2732],{"class":161},[134,3161,3065],{"class":2217},[134,3163,3097],{"class":2155},[134,3165,3166],{"class":161}," ''\n",[134,3168,3169,3172,3174],{"class":136,"line":736},[134,3170,3171],{"class":2092},"      zipPath",[134,3173,2723],{"class":2155},[134,3175,3176],{"class":2092}," themeZip\n",[134,3178,3179],{"class":136,"line":742},[134,3180,597],{"emptyLinePlaceholder":596},[134,3182,3183],{"class":136,"line":748},[134,3184,3185],{"class":3047},"      \u002F\u002F Create a zip\n",[134,3187,3188,3191,3193,3195,3198,3200,3203,3206,3209,3212,3214,3217,3220,3222,3224,3227,3229,3232,3234,3236,3238,3240,3242,3245,3248,3250,3252,3255,3257,3260,3262,3265,3268,3270,3272,3275],{"class":136,"line":754},[134,3189,3190],{"class":727},"      await",[134,3192,2768],{"class":2092},[134,3194,2161],{"class":140},[134,3196,3197],{"class":2726},"exec",[134,3199,2120],{"class":2217},[134,3201,3202],{"class":161},"`",[134,3204,3205],{"class":165},"zip -r ",[134,3207,3208],{"class":161},"${",[134,3210,3211],{"class":2092},"themeZip",[134,3213,3129],{"class":161},[134,3215,3216],{"class":161}," ${",[134,3218,3219],{"class":2092},"core",[134,3221,2161],{"class":161},[134,3223,2855],{"class":2726},[134,3225,2120],{"class":3226},"sfo-9",[134,3228,2732],{"class":161},[134,3230,3231],{"class":165},"working-directory",[134,3233,2732],{"class":161},[134,3235,3065],{"class":3226},[134,3237,3097],{"class":2155},[134,3239,2927],{"class":161},[134,3241,2161],{"class":165},[134,3243,3244],{"class":161},"'}",[134,3246,3247],{"class":165}," -x *.git* *.zip yarn* npm* node_modules* *routes.yaml *redirects.yaml *redirects.json ",[134,3249,3208],{"class":161},[134,3251,3157],{"class":2092},[134,3253,3254],{"class":161},"}`",[134,3256,196],{"class":140},[134,3258,3259],{"class":2217}," []",[134,3261,196],{"class":140},[134,3263,3264],{"class":140}," {",[134,3266,3267],{"class":2217}," cwd",[134,3269,158],{"class":140},[134,3271,2951],{"class":2092},[134,3273,3274],{"class":140}," }",[134,3276,2431],{"class":2217},[134,3278,3279],{"class":136,"line":760},[134,3280,492],{"class":140},[134,3282,3283],{"class":136,"line":766},[134,3284,597],{"emptyLinePlaceholder":596},[134,3286,3287,3290,3292,3294,3296,3298,3300,3303,3305,3307],{"class":136,"line":772},[134,3288,3289],{"class":2092},"    zipPath",[134,3291,2723],{"class":2155},[134,3293,2720],{"class":2092},[134,3295,2161],{"class":140},[134,3297,2982],{"class":2726},[134,3299,2120],{"class":2217},[134,3301,3302],{"class":2092},"basePath",[134,3304,196],{"class":140},[134,3306,3019],{"class":2092},[134,3308,2431],{"class":2217},[134,3310,3311],{"class":136,"line":778},[134,3312,597],{"emptyLinePlaceholder":596},[134,3314,3315],{"class":136,"line":784},[134,3316,3317],{"class":3047},"    \u002F\u002F Deploy it to the configured site\n",[134,3319,3320,3323,3325,3327,3330,3332,3335,3337,3340,3343,3345,3347,3349],{"class":136,"line":789},[134,3321,3322],{"class":727},"    await",[134,3324,2873],{"class":2092},[134,3326,2161],{"class":140},[134,3328,3329],{"class":2092},"themes",[134,3331,2161],{"class":140},[134,3333,3334],{"class":2726},"upload",[134,3336,2120],{"class":2217},[134,3338,3339],{"class":140},"{",[134,3341,3342],{"class":2217}," file",[134,3344,158],{"class":140},[134,3346,3019],{"class":2092},[134,3348,3274],{"class":140},[134,3350,2431],{"class":2217},[134,3352,3353,3356,3358,3361,3363,3366,3368,3370,3373,3375],{"class":136,"line":795},[134,3354,3355],{"class":2092},"    console",[134,3357,2161],{"class":140},[134,3359,3360],{"class":2726},"log",[134,3362,2120],{"class":2217},[134,3364,3365],{"class":161},"`${",[134,3367,3062],{"class":2092},[134,3369,3129],{"class":161},[134,3371,3372],{"class":165}," successfully uploaded.",[134,3374,3202],{"class":161},[134,3376,2431],{"class":2217},[134,3378,3379],{"class":136,"line":801},[134,3380,3381],{"class":140},"  }\n",[134,3383,3384,3387,3389,3392,3394],{"class":136,"line":807},[134,3385,3386],{"class":727},"  catch",[134,3388,3056],{"class":2217},[134,3390,3391],{"class":2092},"err",[134,3393,3065],{"class":2217},[134,3395,141],{"class":140},[134,3397,3398,3400,3402,3405,3407,3409],{"class":136,"line":813},[134,3399,3355],{"class":2092},[134,3401,2161],{"class":140},[134,3403,3404],{"class":2726},"error",[134,3406,2120],{"class":2217},[134,3408,3391],{"class":2092},[134,3410,2431],{"class":2217},[134,3412,3413,3416,3418,3421,3423,3427],{"class":136,"line":818},[134,3414,3415],{"class":2092},"    process",[134,3417,2161],{"class":140},[134,3419,3420],{"class":2726},"exit",[134,3422,2120],{"class":2217},[134,3424,3426],{"class":3425},"srdBf","1",[134,3428,2431],{"class":2217},[134,3430,3431],{"class":136,"line":823},[134,3432,3381],{"class":140},[134,3434,3435,3437],{"class":136,"line":829},[134,3436,3129],{"class":140},[134,3438,3439],{"class":2092},"())\n",[28,3441,3442,3443,104,3446,3449],{},"把主题打包成 zip 包，然后提供 Ghost 上创建的 ",[75,3444,3445],{},"Admin API Key",[75,3447,3448],{},"API URL","，通过 API 去上传，那么我们应该也可以自己去实现。",[28,3451,3452,3453,104,3456,3458],{},"首先，我们也需要去 Ghost 后台创建一个自定义的 Integration，比如取名叫 GitLab CI，目的是为了获得 ",[75,3454,3455],{},"Admin API key",[75,3457,3448],{},"，后面在 GitLab CI 中需要用到。",[67,3460],{"filename":70},[28,3462,3463,3464,104,3467,3470],{},"下一步，去 GitLab CI 中，把这两个内容配置成变量，取名 ",[75,3465,3466],{},"GHOST_ADMIN_API_KEY",[75,3468,3469],{},"GHOST_API_URL"," 以便在 CI 脚本中使用。",[67,3472],{"filename":3473},"02.png",[28,3475,3476],{},"在项目中添加 Ghost Admin API 库：",[86,3478,3482],{"className":3479,"code":3480,"language":3481,"meta":94,"style":94},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","yarn add @tryghost\u002Fadmin-api --dev\n","bash",[75,3483,3484],{"__ignoreMap":94},[134,3485,3486,3490,3493,3496],{"class":136,"line":137},[134,3487,3489],{"class":3488},"sbgvK","yarn",[134,3491,3492],{"class":165}," add",[134,3494,3495],{"class":165}," @tryghost\u002Fadmin-api",[134,3497,3499],{"class":3498},"stzsN"," --dev\n",[28,3501,3502,3503,3506],{},"在 ",[75,3504,3505],{},"gulpfile.js"," 中插入部署的任务：",[86,3508,3510],{"className":2706,"code":3509,"language":2708,"meta":94,"style":94},"const GhostAdminApi = require('@tryghost\u002Fadmin-api')\nconst { series, src, dest } = require('gulp')\nconst less = require('gulp-less')\nconst zip = require('gulp-zip')\nconst pump = require('pump')\n\nconst handleError = (done) => {\n  return function (err) {\n    if (err) {\n      console.error(err)\n    }\n    return done(err)\n  }\n}\n\nfunction css(done) {\n  pump(\n    [\n      src('.\u002Fassets\u002Fcss\u002F*.less', { sourcemaps: true }),\n      less({}),\n      dest('assets\u002Fcss', { sourcemaps: '.\u002F' }),\n    ],\n    handleError(done)\n  )\n}\n\nfunction zipper(done) {\n  const targetDir = 'dist\u002F'\n  const themeName = require('.\u002Fpackage.json').name\n  const filename = `${themeName}.zip`\n\n  pump(\n    [\n      src(['**', '!node_modules', '!node_modules\u002F**', '!dist', '!dist\u002F**']),\n      zip(filename),\n      dest(targetDir),\n    ],\n    handleError(done)\n  )\n}\n\nasync function deploy(done) {\n  try {\n    const zipFile = `dist\u002F${require('.\u002Fpackage.json').name}.zip`\n    const api = new GhostAdminApi({\n      url: process.env.GHOST_API_URL,\n      key: process.env.GHOST_ADMIN_API_KEY,\n      version: `v${require('.\u002Fpackage.json').version}`,\n    })\n\n    await api.themes.upload({ file: zipFile })\n    console.log(`${zipFile} successfully uploaded.`)\n    done()\n  }\n  catch (err) {\n    console.error(err)\n    done(err)\n  }\n}\n\nconst build = series(css)\n\nexports.build = build\nexports.zip = series(build, zipper)\nexports.deploy = deploy\nexports.default = build\n",[75,3511,3512,3532,3568,3590,3612,3634,3638,3660,3675,3687,3702,3706,3720,3724,3728,3732,3748,3755,3760,3792,3806,3841,3848,3859,3864,3868,3872,3887,3904,3929,3948,3952,3958,3962,4017,4031,4044,4050,4060,4064,4068,4072,4089,4095,4134,4150,4168,4186,4220,4226,4230,4258,4281,4288,4292,4304,4318,4328,4332,4336,4340,4354,4358,4373,4394,4407],{"__ignoreMap":94},[134,3513,3514,3516,3518,3520,3522,3524,3526,3528,3530],{"class":136,"line":137},[134,3515,2716],{"class":2715},[134,3517,2790],{"class":2719},[134,3519,2723],{"class":2155},[134,3521,2727],{"class":2726},[134,3523,2120],{"class":2092},[134,3525,2732],{"class":161},[134,3527,2801],{"class":165},[134,3529,2732],{"class":161},[134,3531,2431],{"class":2092},[134,3533,3534,3536,3538,3541,3543,3546,3548,3551,3553,3555,3557,3559,3561,3564,3566],{"class":136,"line":144},[134,3535,2716],{"class":2715},[134,3537,3264],{"class":140},[134,3539,3540],{"class":2719}," series",[134,3542,196],{"class":140},[134,3544,3545],{"class":2719}," src",[134,3547,196],{"class":140},[134,3549,3550],{"class":2719}," dest",[134,3552,3274],{"class":140},[134,3554,2723],{"class":2155},[134,3556,2727],{"class":2726},[134,3558,2120],{"class":2092},[134,3560,2732],{"class":161},[134,3562,3563],{"class":165},"gulp",[134,3565,2732],{"class":161},[134,3567,2431],{"class":2092},[134,3569,3570,3572,3575,3577,3579,3581,3583,3586,3588],{"class":136,"line":174},[134,3571,2716],{"class":2715},[134,3573,3574],{"class":2719}," less",[134,3576,2723],{"class":2155},[134,3578,2727],{"class":2726},[134,3580,2120],{"class":2092},[134,3582,2732],{"class":161},[134,3584,3585],{"class":165},"gulp-less",[134,3587,2732],{"class":161},[134,3589,2431],{"class":2092},[134,3591,3592,3594,3597,3599,3601,3603,3605,3608,3610],{"class":136,"line":218},[134,3593,2716],{"class":2715},[134,3595,3596],{"class":2719}," zip",[134,3598,2723],{"class":2155},[134,3600,2727],{"class":2726},[134,3602,2120],{"class":2092},[134,3604,2732],{"class":161},[134,3606,3607],{"class":165},"gulp-zip",[134,3609,2732],{"class":161},[134,3611,2431],{"class":2092},[134,3613,3614,3616,3619,3621,3623,3625,3627,3630,3632],{"class":136,"line":233},[134,3615,2716],{"class":2715},[134,3617,3618],{"class":2719}," pump",[134,3620,2723],{"class":2155},[134,3622,2727],{"class":2726},[134,3624,2120],{"class":2092},[134,3626,2732],{"class":161},[134,3628,3629],{"class":165},"pump",[134,3631,2732],{"class":161},[134,3633,2431],{"class":2092},[134,3635,3636],{"class":136,"line":239},[134,3637,597],{"emptyLinePlaceholder":596},[134,3639,3640,3642,3646,3648,3650,3653,3655,3658],{"class":136,"line":264},[134,3641,2716],{"class":2715},[134,3643,3645],{"class":3644},"sfCm-"," handleError",[134,3647,2723],{"class":2155},[134,3649,3056],{"class":140},[134,3651,3652],{"class":2190},"done",[134,3654,2139],{"class":140},[134,3656,3657],{"class":2715}," =>",[134,3659,2831],{"class":140},[134,3661,3662,3665,3667,3669,3671,3673],{"class":136,"line":296},[134,3663,3664],{"class":727},"  return",[134,3666,2822],{"class":2715},[134,3668,3056],{"class":140},[134,3670,3391],{"class":2190},[134,3672,2139],{"class":140},[134,3674,2831],{"class":140},[134,3676,3677,3679,3681,3683,3685],{"class":136,"line":311},[134,3678,3053],{"class":727},[134,3680,3056],{"class":2217},[134,3682,3391],{"class":2092},[134,3684,3065],{"class":2217},[134,3686,141],{"class":140},[134,3688,3689,3692,3694,3696,3698,3700],{"class":136,"line":317},[134,3690,3691],{"class":2092},"      console",[134,3693,2161],{"class":140},[134,3695,3404],{"class":2726},[134,3697,2120],{"class":2217},[134,3699,3391],{"class":2092},[134,3701,2431],{"class":2217},[134,3703,3704],{"class":136,"line":322},[134,3705,492],{"class":140},[134,3707,3708,3711,3714,3716,3718],{"class":136,"line":343},[134,3709,3710],{"class":727},"    return",[134,3712,3713],{"class":2726}," done",[134,3715,2120],{"class":2217},[134,3717,3391],{"class":2092},[134,3719,2431],{"class":2217},[134,3721,3722],{"class":136,"line":365},[134,3723,3381],{"class":140},[134,3725,3726],{"class":136,"line":385},[134,3727,544],{"class":140},[134,3729,3730],{"class":136,"line":390},[134,3731,597],{"emptyLinePlaceholder":596},[134,3733,3734,3737,3740,3742,3744,3746],{"class":136,"line":395},[134,3735,3736],{"class":2715},"function",[134,3738,3739],{"class":2726}," css",[134,3741,2120],{"class":140},[134,3743,3652],{"class":2190},[134,3745,2139],{"class":140},[134,3747,2831],{"class":140},[134,3749,3750,3753],{"class":136,"line":416},[134,3751,3752],{"class":2726},"  pump",[134,3754,2185],{"class":2217},[134,3756,3757],{"class":136,"line":448},[134,3758,3759],{"class":2217},"    [\n",[134,3761,3762,3765,3767,3769,3772,3774,3776,3778,3781,3783,3786,3788,3790],{"class":136,"line":465},[134,3763,3764],{"class":2726},"      src",[134,3766,2120],{"class":2217},[134,3768,2732],{"class":161},[134,3770,3771],{"class":165},".\u002Fassets\u002Fcss\u002F*.less",[134,3773,2732],{"class":161},[134,3775,196],{"class":140},[134,3777,3264],{"class":140},[134,3779,3780],{"class":2217}," sourcemaps",[134,3782,158],{"class":140},[134,3784,460],{"class":3785},"syTEX",[134,3787,3274],{"class":140},[134,3789,2139],{"class":2217},[134,3791,171],{"class":140},[134,3793,3794,3797,3799,3802,3804],{"class":136,"line":489},[134,3795,3796],{"class":2726},"      less",[134,3798,2120],{"class":2217},[134,3800,3801],{"class":140},"{}",[134,3803,2139],{"class":2217},[134,3805,171],{"class":140},[134,3807,3808,3811,3813,3815,3818,3820,3822,3824,3826,3828,3830,3833,3835,3837,3839],{"class":136,"line":495},[134,3809,3810],{"class":2726},"      dest",[134,3812,2120],{"class":2217},[134,3814,2732],{"class":161},[134,3816,3817],{"class":165},"assets\u002Fcss",[134,3819,2732],{"class":161},[134,3821,196],{"class":140},[134,3823,3264],{"class":140},[134,3825,3780],{"class":2217},[134,3827,158],{"class":140},[134,3829,2927],{"class":161},[134,3831,3832],{"class":165},".\u002F",[134,3834,2732],{"class":161},[134,3836,3274],{"class":140},[134,3838,2139],{"class":2217},[134,3840,171],{"class":140},[134,3842,3843,3846],{"class":136,"line":501},[134,3844,3845],{"class":2217},"    ]",[134,3847,171],{"class":140},[134,3849,3850,3853,3855,3857],{"class":136,"line":522},[134,3851,3852],{"class":2726},"    handleError",[134,3854,2120],{"class":2217},[134,3856,3652],{"class":2092},[134,3858,2431],{"class":2217},[134,3860,3861],{"class":136,"line":541},[134,3862,3863],{"class":2217},"  )\n",[134,3865,3866],{"class":136,"line":736},[134,3867,544],{"class":140},[134,3869,3870],{"class":136,"line":742},[134,3871,597],{"emptyLinePlaceholder":596},[134,3873,3874,3876,3879,3881,3883,3885],{"class":136,"line":748},[134,3875,3736],{"class":2715},[134,3877,3878],{"class":2726}," zipper",[134,3880,2120],{"class":140},[134,3882,3652],{"class":2190},[134,3884,2139],{"class":140},[134,3886,2831],{"class":140},[134,3888,3889,3892,3895,3897,3899,3902],{"class":136,"line":754},[134,3890,3891],{"class":2715},"  const",[134,3893,3894],{"class":2719}," targetDir",[134,3896,2723],{"class":2155},[134,3898,2927],{"class":161},[134,3900,3901],{"class":165},"dist\u002F",[134,3903,2933],{"class":161},[134,3905,3906,3908,3910,3912,3914,3916,3918,3921,3923,3925,3927],{"class":136,"line":760},[134,3907,3891],{"class":2715},[134,3909,3075],{"class":2719},[134,3911,2723],{"class":2155},[134,3913,2727],{"class":2726},[134,3915,2120],{"class":2217},[134,3917,2732],{"class":161},[134,3919,3920],{"class":165},".\u002Fpackage.json",[134,3922,2732],{"class":161},[134,3924,2139],{"class":2217},[134,3926,2161],{"class":140},[134,3928,3111],{"class":2092},[134,3930,3931,3933,3936,3938,3940,3942,3944,3946],{"class":136,"line":766},[134,3932,3891],{"class":2715},[134,3934,3935],{"class":2719}," filename",[134,3937,2723],{"class":2155},[134,3939,3123],{"class":161},[134,3941,3126],{"class":2092},[134,3943,3129],{"class":161},[134,3945,3132],{"class":165},[134,3947,3135],{"class":161},[134,3949,3950],{"class":136,"line":772},[134,3951,597],{"emptyLinePlaceholder":596},[134,3953,3954,3956],{"class":136,"line":778},[134,3955,3752],{"class":2726},[134,3957,2185],{"class":2217},[134,3959,3960],{"class":136,"line":784},[134,3961,3759],{"class":2217},[134,3963,3964,3966,3969,3971,3974,3976,3978,3980,3983,3985,3987,3989,3992,3994,3996,3998,4001,4003,4005,4007,4010,4012,4015],{"class":136,"line":789},[134,3965,3764],{"class":2726},[134,3967,3968],{"class":2217},"([",[134,3970,2732],{"class":161},[134,3972,3973],{"class":165},"**",[134,3975,2732],{"class":161},[134,3977,196],{"class":140},[134,3979,2927],{"class":161},[134,3981,3982],{"class":165},"!node_modules",[134,3984,2732],{"class":161},[134,3986,196],{"class":140},[134,3988,2927],{"class":161},[134,3990,3991],{"class":165},"!node_modules\u002F**",[134,3993,2732],{"class":161},[134,3995,196],{"class":140},[134,3997,2927],{"class":161},[134,3999,4000],{"class":165},"!dist",[134,4002,2732],{"class":161},[134,4004,196],{"class":140},[134,4006,2927],{"class":161},[134,4008,4009],{"class":165},"!dist\u002F**",[134,4011,2732],{"class":161},[134,4013,4014],{"class":2217},"])",[134,4016,171],{"class":140},[134,4018,4019,4022,4024,4027,4029],{"class":136,"line":795},[134,4020,4021],{"class":2726},"      zip",[134,4023,2120],{"class":2217},[134,4025,4026],{"class":2092},"filename",[134,4028,2139],{"class":2217},[134,4030,171],{"class":140},[134,4032,4033,4035,4037,4040,4042],{"class":136,"line":801},[134,4034,3810],{"class":2726},[134,4036,2120],{"class":2217},[134,4038,4039],{"class":2092},"targetDir",[134,4041,2139],{"class":2217},[134,4043,171],{"class":140},[134,4045,4046,4048],{"class":136,"line":807},[134,4047,3845],{"class":2217},[134,4049,171],{"class":140},[134,4051,4052,4054,4056,4058],{"class":136,"line":813},[134,4053,3852],{"class":2726},[134,4055,2120],{"class":2217},[134,4057,3652],{"class":2092},[134,4059,2431],{"class":2217},[134,4061,4062],{"class":136,"line":818},[134,4063,3863],{"class":2217},[134,4065,4066],{"class":136,"line":823},[134,4067,544],{"class":140},[134,4069,4070],{"class":136,"line":829},[134,4071,597],{"emptyLinePlaceholder":596},[134,4073,4074,4076,4078,4081,4083,4085,4087],{"class":136,"line":835},[134,4075,2819],{"class":2715},[134,4077,2822],{"class":2715},[134,4079,4080],{"class":2726}," deploy",[134,4082,2120],{"class":140},[134,4084,3652],{"class":2190},[134,4086,2139],{"class":140},[134,4088,2831],{"class":140},[134,4090,4091,4093],{"class":136,"line":841},[134,4092,2836],{"class":727},[134,4094,2831],{"class":140},[134,4096,4097,4099,4102,4104,4107,4109,4111,4114,4116,4118,4120,4122,4124,4126,4128,4130,4132],{"class":136,"line":847},[134,4098,2843],{"class":2715},[134,4100,4101],{"class":2719}," zipFile",[134,4103,2723],{"class":2155},[134,4105,4106],{"class":161}," `",[134,4108,3901],{"class":165},[134,4110,3208],{"class":161},[134,4112,4113],{"class":2726},"require",[134,4115,2120],{"class":3226},[134,4117,2732],{"class":161},[134,4119,3920],{"class":165},[134,4121,2732],{"class":161},[134,4123,2139],{"class":3226},[134,4125,2161],{"class":161},[134,4127,2579],{"class":2092},[134,4129,3129],{"class":161},[134,4131,3132],{"class":165},[134,4133,3135],{"class":161},[134,4135,4136,4138,4140,4142,4144,4146,4148],{"class":136,"line":852},[134,4137,2843],{"class":2715},[134,4139,2873],{"class":2719},[134,4141,2723],{"class":2155},[134,4143,2878],{"class":2155},[134,4145,2790],{"class":2726},[134,4147,2120],{"class":2217},[134,4149,141],{"class":140},[134,4151,4152,4154,4156,4158,4160,4162,4164,4166],{"class":136,"line":857},[134,4153,2889],{"class":2217},[134,4155,158],{"class":140},[134,4157,2956],{"class":2092},[134,4159,2161],{"class":140},[134,4161,2961],{"class":2092},[134,4163,2161],{"class":140},[134,4165,3469],{"class":2719},[134,4167,171],{"class":140},[134,4169,4170,4172,4174,4176,4178,4180,4182,4184],{"class":136,"line":863},[134,4171,2896],{"class":2217},[134,4173,158],{"class":140},[134,4175,2956],{"class":2092},[134,4177,2161],{"class":140},[134,4179,2961],{"class":2092},[134,4181,2161],{"class":140},[134,4183,3466],{"class":2719},[134,4185,171],{"class":140},[134,4187,4188,4190,4192,4194,4197,4199,4201,4203,4205,4207,4209,4211,4213,4216,4218],{"class":136,"line":869},[134,4189,2922],{"class":2217},[134,4191,158],{"class":140},[134,4193,4106],{"class":161},[134,4195,4196],{"class":165},"v",[134,4198,3208],{"class":161},[134,4200,4113],{"class":2726},[134,4202,2120],{"class":3226},[134,4204,2732],{"class":161},[134,4206,3920],{"class":165},[134,4208,2732],{"class":161},[134,4210,2139],{"class":3226},[134,4212,2161],{"class":161},[134,4214,4215],{"class":2092},"version",[134,4217,3254],{"class":161},[134,4219,171],{"class":140},[134,4221,4222,4224],{"class":136,"line":875},[134,4223,2938],{"class":140},[134,4225,2431],{"class":2217},[134,4227,4228],{"class":136,"line":881},[134,4229,597],{"emptyLinePlaceholder":596},[134,4231,4232,4234,4236,4238,4240,4242,4244,4246,4248,4250,4252,4254,4256],{"class":136,"line":887},[134,4233,3322],{"class":727},[134,4235,2873],{"class":2092},[134,4237,2161],{"class":140},[134,4239,3329],{"class":2092},[134,4241,2161],{"class":140},[134,4243,3334],{"class":2726},[134,4245,2120],{"class":2217},[134,4247,3339],{"class":140},[134,4249,3342],{"class":2217},[134,4251,158],{"class":140},[134,4253,4101],{"class":2092},[134,4255,3274],{"class":140},[134,4257,2431],{"class":2217},[134,4259,4260,4262,4264,4266,4268,4270,4273,4275,4277,4279],{"class":136,"line":893},[134,4261,3355],{"class":2092},[134,4263,2161],{"class":140},[134,4265,3360],{"class":2726},[134,4267,2120],{"class":2217},[134,4269,3365],{"class":161},[134,4271,4272],{"class":2092},"zipFile",[134,4274,3129],{"class":161},[134,4276,3372],{"class":165},[134,4278,3202],{"class":161},[134,4280,2431],{"class":2217},[134,4282,4283,4286],{"class":136,"line":898},[134,4284,4285],{"class":2726},"    done",[134,4287,2168],{"class":2217},[134,4289,4290],{"class":136,"line":904},[134,4291,3381],{"class":140},[134,4293,4294,4296,4298,4300,4302],{"class":136,"line":910},[134,4295,3386],{"class":727},[134,4297,3056],{"class":2217},[134,4299,3391],{"class":2092},[134,4301,3065],{"class":2217},[134,4303,141],{"class":140},[134,4305,4306,4308,4310,4312,4314,4316],{"class":136,"line":916},[134,4307,3355],{"class":2092},[134,4309,2161],{"class":140},[134,4311,3404],{"class":2726},[134,4313,2120],{"class":2217},[134,4315,3391],{"class":2092},[134,4317,2431],{"class":2217},[134,4319,4320,4322,4324,4326],{"class":136,"line":922},[134,4321,4285],{"class":2726},[134,4323,2120],{"class":2217},[134,4325,3391],{"class":2092},[134,4327,2431],{"class":2217},[134,4329,4330],{"class":136,"line":928},[134,4331,3381],{"class":140},[134,4333,4334],{"class":136,"line":933},[134,4335,544],{"class":140},[134,4337,4338],{"class":136,"line":938},[134,4339,597],{"emptyLinePlaceholder":596},[134,4341,4342,4344,4347,4349,4351],{"class":136,"line":944},[134,4343,2716],{"class":2715},[134,4345,4346],{"class":2719}," build",[134,4348,2723],{"class":2155},[134,4350,3540],{"class":2726},[134,4352,4353],{"class":2092},"(css)\n",[134,4355,4356],{"class":136,"line":950},[134,4357,597],{"emptyLinePlaceholder":596},[134,4359,4360,4363,4365,4368,4370],{"class":136,"line":956},[134,4361,4362],{"class":147},"exports",[134,4364,2161],{"class":140},[134,4366,4367],{"class":2092},"build ",[134,4369,2156],{"class":2155},[134,4371,4372],{"class":2092}," build\n",[134,4374,4375,4377,4379,4382,4384,4386,4389,4391],{"class":136,"line":962},[134,4376,4362],{"class":147},[134,4378,2161],{"class":140},[134,4380,4381],{"class":2092},"zip ",[134,4383,2156],{"class":2155},[134,4385,3540],{"class":2726},[134,4387,4388],{"class":2092},"(build",[134,4390,196],{"class":140},[134,4392,4393],{"class":2092}," zipper)\n",[134,4395,4396,4398,4400,4403,4405],{"class":136,"line":967},[134,4397,4362],{"class":147},[134,4399,2161],{"class":140},[134,4401,4402],{"class":2092},"deploy ",[134,4404,2156],{"class":2155},[134,4406,591],{"class":2092},[134,4408,4409,4411,4413,4416,4418],{"class":136,"line":972},[134,4410,4362],{"class":147},[134,4412,2161],{"class":140},[134,4414,4415],{"class":2092},"default ",[134,4417,2156],{"class":2155},[134,4419,4372],{"class":2092},[28,4421,3502,4422,4424],{},[75,4423,3003],{}," 中插入脚本：",[86,4426,4428],{"className":128,"code":4427,"language":130,"meta":94,"style":94},"{\n  \u002F* ... *\u002F\n  \"scripts\": {\n    \"build\": \"gulp build\",\n    \"zip\": \"gulp zip\",\n    \"deploy\": \"gulp deploy\"\n  }\n  \u002F* ... *\u002F\n}\n",[75,4429,4430,4434,4439,4451,4472,4492,4509,4513,4517],{"__ignoreMap":94},[134,4431,4432],{"class":136,"line":137},[134,4433,141],{"class":140},[134,4435,4436],{"class":136,"line":144},[134,4437,4438],{"class":3047},"  \u002F* ... *\u002F\n",[134,4440,4441,4443,4445,4447,4449],{"class":136,"line":174},[134,4442,148],{"class":147},[134,4444,115],{"class":151},[134,4446,155],{"class":147},[134,4448,158],{"class":140},[134,4450,2831],{"class":140},[134,4452,4453,4456,4459,4461,4463,4465,4468,4470],{"class":136,"line":218},[134,4454,4455],{"class":147},"    \"",[134,4457,4458],{"class":245},"build",[134,4460,155],{"class":147},[134,4462,158],{"class":140},[134,4464,162],{"class":161},[134,4466,4467],{"class":165},"gulp build",[134,4469,155],{"class":161},[134,4471,171],{"class":140},[134,4473,4474,4476,4479,4481,4483,4485,4488,4490],{"class":136,"line":233},[134,4475,4455],{"class":147},[134,4477,4478],{"class":245},"zip",[134,4480,155],{"class":147},[134,4482,158],{"class":140},[134,4484,162],{"class":161},[134,4486,4487],{"class":165},"gulp zip",[134,4489,155],{"class":161},[134,4491,171],{"class":140},[134,4493,4494,4496,4498,4500,4502,4504,4507],{"class":136,"line":239},[134,4495,4455],{"class":147},[134,4497,651],{"class":245},[134,4499,155],{"class":147},[134,4501,158],{"class":140},[134,4503,162],{"class":161},[134,4505,4506],{"class":165},"gulp deploy",[134,4508,382],{"class":161},[134,4510,4511],{"class":136,"line":264},[134,4512,3381],{"class":140},[134,4514,4515],{"class":136,"line":296},[134,4516,4438],{"class":3047},[134,4518,4519],{"class":136,"line":311},[134,4520,544],{"class":140},[28,4522,4523,4524,4526],{},"添加 ",[75,4525,560],{}," 文件：",[86,4528,4530],{"className":563,"code":4529,"language":565,"meta":94,"style":94},"image: node:14-slim # 注意：不要用 alpine 的镜像，上传至 https 站点会有问题\n\nstages:\n  - deploy\n\ndeploy:\n  stage: deploy\n  script:\n    - yarn install\n    - yarn zip\n    - yarn deploy\n  only:\n    - tags\n  cache:\n    paths:\n      - node_modules\u002F\n",[75,4531,4532,4544,4548,4554,4560,4564,4570,4578,4584,4591,4598,4605,4611,4617,4624,4631],{"__ignoreMap":94},[134,4533,4534,4536,4538,4541],{"class":136,"line":137},[134,4535,2446],{"class":572},[134,4537,158],{"class":140},[134,4539,4540],{"class":165}," node:14-slim",[134,4542,4543],{"class":3047}," # 注意：不要用 alpine 的镜像，上传至 https 站点会有问题\n",[134,4545,4546],{"class":136,"line":144},[134,4547,597],{"emptyLinePlaceholder":596},[134,4549,4550,4552],{"class":136,"line":174},[134,4551,573],{"class":572},[134,4553,576],{"class":140},[134,4555,4556,4558],{"class":136,"line":218},[134,4557,581],{"class":140},[134,4559,591],{"class":165},[134,4561,4562],{"class":136,"line":233},[134,4563,597],{"emptyLinePlaceholder":596},[134,4565,4566,4568],{"class":136,"line":239},[134,4567,651],{"class":572},[134,4569,576],{"class":140},[134,4571,4572,4574,4576],{"class":136,"line":264},[134,4573,609],{"class":572},[134,4575,158],{"class":140},[134,4577,591],{"class":165},[134,4579,4580,4582],{"class":136,"line":296},[134,4581,618],{"class":572},[134,4583,576],{"class":140},[134,4585,4586,4588],{"class":136,"line":311},[134,4587,625],{"class":140},[134,4589,4590],{"class":165}," yarn install\n",[134,4592,4593,4595],{"class":136,"line":317},[134,4594,625],{"class":140},[134,4596,4597],{"class":165}," yarn zip\n",[134,4599,4600,4602],{"class":136,"line":322},[134,4601,625],{"class":140},[134,4603,4604],{"class":165}," yarn deploy\n",[134,4606,4607,4609],{"class":136,"line":343},[134,4608,1884],{"class":572},[134,4610,576],{"class":140},[134,4612,4613,4615],{"class":136,"line":365},[134,4614,625],{"class":140},[134,4616,2552],{"class":165},[134,4618,4619,4622],{"class":136,"line":385},[134,4620,4621],{"class":572},"  cache",[134,4623,576],{"class":140},[134,4625,4626,4629],{"class":136,"line":390},[134,4627,4628],{"class":572},"    paths",[134,4630,576],{"class":140},[134,4632,4633,4636],{"class":136,"line":395},[134,4634,4635],{"class":140},"      -",[134,4637,4638],{"class":165}," node_modules\u002F\n",[28,4640,4641,4642,4644,4645,4648,4649,4652],{},"注意，为了避免每次提交代码都部署，",[75,4643,651],{}," 任务限制了只有打了 ",[75,4646,4647],{},"tag"," 的 ",[75,4650,4651],{},"commit"," 才会触发。",[28,4654,4655],{},"好了，更新代码，打个 tag 就会自动打包上传至 Ghost 后台了！",[1932,4657,4658],{},"html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_hVV, html code.shiki .s_hVV{--shiki-light:#90A4AE;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .skxfh, html code.shiki .skxfh{--shiki-light:#E53935;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .sfo-9, html code.shiki .sfo-9{--shiki-light:#90A4AE;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sfCm-, html code.shiki .sfCm-{--shiki-light:#90A4AE;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}",{"title":94,"searchDepth":144,"depth":144,"links":4660},[],"2022-05-24",{},"\u002Fposts\u002F2022\u002Fuse-gitlab-to-deploy-ghost-theme-automatically",{"text":2670,"minutes":4665,"time":4666,"words":4667},3.575,214500,715,{"title":2682,"description":94},{"loc":4663},"posts\u002F2022\u002F20220524.use-gitlab-to-deploy-ghost-theme-automatically",[1954,4672,1955,2057],"Ghost","2cB9yXMzvXfyQ8XEXx9VDbtCvf4CKC-2FRBZ833gE-Q",{"id":4675,"title":4676,"body":4677,"class":1939,"cover":5699,"coverSize":1939,"date":5700,"description":5701,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":5702,"navigation":596,"path":5703,"readingTime":5704,"seo":5709,"sitemap":5710,"stem":5711,"tags":5712,"time":1939,"weather":1939,"__hash__":5714},"posts\u002Fposts\u002F2020\u002F20201227.devops-gitlab-ci-aliyun-k8s.md","基于 GitLab CI 和阿里云 k8s 的持续交付解决方案",{"type":25,"value":4678,"toc":5694},[4679,4686,4689,4703,4706,4709,4713,4718,5032,5047,5050,5055,5059,5164,5167,5332,5336,5339,5433,5436,5674,5678,5681,5683,5686,5688,5691],[28,4680,4681,4682,4685],{},"今年对于我个人而言，在 DevOps 上的最大收获，莫过于摸索了这套基于 GitLab CI 和 k8s 的持续交付解决方案，其实原理都很简单，在我去年的方案里又做了改进，实现了基于 ",[75,4683,4684],{},"git tag"," 的触发方式，并且把原先的本地打包推镜像改为在 GitLab Runner 上打包推镜像。",[28,4687,4688],{},"这套解决方案大致流程是这样的：",[45,4690,4691,4697,4700],{},[48,4692,4693,4694],{},"推送代码，在代码中配置 ",[75,4695,4696],{},"gitlab-ci.yml",[48,4698,4699],{},"推送 tag，触发 GitLab Runner 编译 docker 镜像，并推送至阿里云镜像仓库",[48,4701,4702],{},"在阿里云 k8s 上基于镜像仓库创建应用，并创建重新部署的触发器，在镜像更新时触发该触发器",[28,4704,4705],{},"这样，以后每次推送新的 tag 上去，就可以实现自动打包&部署了。",[28,4707,4708],{},"下面，我来详细讲解下所有步骤。",[35,4710,4712],{"id":4711},"配置-gitlab-runner","配置 GitLab Runner",[4714,4715,4717],"h5",{"id":4716},"configtoml","config.toml",[86,4719,4723],{"className":4720,"code":4721,"language":4722,"meta":94,"style":94},"language-toml shiki shiki-themes material-theme-lighter github-light github-dark","concurrent = 1\ncheck_interval = 0\n\n[session_server]\nsession_timeout = 1800\n\n[[runners]]\nname = \"common-runner\"\nurl = \"https:\u002F\u002Fgit.xxx.xxx\"\ntoken = \"TOKEN\"\nexecutor = \"docker\"\n\n[runners.custom_build_dir]\n\n[runners.cache]\n\n[runners.cache.s3]\n\n[runners.cache.gcs]\n\n[runners.docker]\ntls_verify = false\nimage = \"docker:latest\"\nprivileged = false\ndisable_entrypoint_overwrite = false\noom_kill_disable = false\ndisable_cache = false\nvolumes = [\n  \"\u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock\",\n  \"\u002Fxxx\u002Fgitlab-runner\u002Fcache:\u002Fcache\"\n]\nshm_size = 0\n","toml",[75,4724,4725,4735,4745,4749,4758,4768,4772,4783,4797,4811,4825,4839,4843,4856,4860,4873,4877,4894,4898,4915,4919,4931,4940,4954,4963,4972,4981,4990,4999,5010,5019,5023],{"__ignoreMap":94},[134,4726,4727,4730,4732],{"class":136,"line":137},[134,4728,4729],{"class":2092},"concurrent ",[134,4731,2156],{"class":140},[134,4733,4734],{"class":3425}," 1\n",[134,4736,4737,4740,4742],{"class":136,"line":144},[134,4738,4739],{"class":2092},"check_interval ",[134,4741,2156],{"class":140},[134,4743,4744],{"class":3425}," 0\n",[134,4746,4747],{"class":136,"line":174},[134,4748,597],{"emptyLinePlaceholder":596},[134,4750,4751,4753,4756],{"class":136,"line":218},[134,4752,2342],{"class":140},[134,4754,4755],{"class":3488},"session_server",[134,4757,486],{"class":140},[134,4759,4760,4763,4765],{"class":136,"line":233},[134,4761,4762],{"class":2092},"session_timeout ",[134,4764,2156],{"class":140},[134,4766,4767],{"class":3425}," 1800\n",[134,4769,4770],{"class":136,"line":239},[134,4771,597],{"emptyLinePlaceholder":596},[134,4773,4774,4777,4780],{"class":136,"line":264},[134,4775,4776],{"class":140},"[[",[134,4778,4779],{"class":3488},"runners",[134,4781,4782],{"class":140},"]]\n",[134,4784,4785,4788,4790,4792,4795],{"class":136,"line":296},[134,4786,4787],{"class":2092},"name ",[134,4789,2156],{"class":140},[134,4791,162],{"class":161},[134,4793,4794],{"class":165},"common-runner",[134,4796,382],{"class":161},[134,4798,4799,4802,4804,4806,4809],{"class":136,"line":311},[134,4800,4801],{"class":2092},"url ",[134,4803,2156],{"class":140},[134,4805,162],{"class":161},[134,4807,4808],{"class":165},"https:\u002F\u002Fgit.xxx.xxx",[134,4810,382],{"class":161},[134,4812,4813,4816,4818,4820,4823],{"class":136,"line":317},[134,4814,4815],{"class":2092},"token ",[134,4817,2156],{"class":140},[134,4819,162],{"class":161},[134,4821,4822],{"class":165},"TOKEN",[134,4824,382],{"class":161},[134,4826,4827,4830,4832,4834,4837],{"class":136,"line":322},[134,4828,4829],{"class":2092},"executor ",[134,4831,2156],{"class":140},[134,4833,162],{"class":161},[134,4835,4836],{"class":165},"docker",[134,4838,382],{"class":161},[134,4840,4841],{"class":136,"line":343},[134,4842,597],{"emptyLinePlaceholder":596},[134,4844,4845,4847,4849,4851,4854],{"class":136,"line":365},[134,4846,2342],{"class":140},[134,4848,4779],{"class":3488},[134,4850,2161],{"class":2092},[134,4852,4853],{"class":3488},"custom_build_dir",[134,4855,486],{"class":140},[134,4857,4858],{"class":136,"line":385},[134,4859,597],{"emptyLinePlaceholder":596},[134,4861,4862,4864,4866,4868,4871],{"class":136,"line":390},[134,4863,2342],{"class":140},[134,4865,4779],{"class":3488},[134,4867,2161],{"class":2092},[134,4869,4870],{"class":3488},"cache",[134,4872,486],{"class":140},[134,4874,4875],{"class":136,"line":395},[134,4876,597],{"emptyLinePlaceholder":596},[134,4878,4879,4881,4883,4885,4887,4889,4892],{"class":136,"line":416},[134,4880,2342],{"class":140},[134,4882,4779],{"class":3488},[134,4884,2161],{"class":2092},[134,4886,4870],{"class":3488},[134,4888,2161],{"class":2092},[134,4890,4891],{"class":3488},"s3",[134,4893,486],{"class":140},[134,4895,4896],{"class":136,"line":448},[134,4897,597],{"emptyLinePlaceholder":596},[134,4899,4900,4902,4904,4906,4908,4910,4913],{"class":136,"line":465},[134,4901,2342],{"class":140},[134,4903,4779],{"class":3488},[134,4905,2161],{"class":2092},[134,4907,4870],{"class":3488},[134,4909,2161],{"class":2092},[134,4911,4912],{"class":3488},"gcs",[134,4914,486],{"class":140},[134,4916,4917],{"class":136,"line":489},[134,4918,597],{"emptyLinePlaceholder":596},[134,4920,4921,4923,4925,4927,4929],{"class":136,"line":495},[134,4922,2342],{"class":140},[134,4924,4779],{"class":3488},[134,4926,2161],{"class":2092},[134,4928,4836],{"class":3488},[134,4930,486],{"class":140},[134,4932,4933,4936,4938],{"class":136,"line":501},[134,4934,4935],{"class":2092},"tls_verify ",[134,4937,2156],{"class":140},[134,4939,308],{"class":3785},[134,4941,4942,4945,4947,4949,4952],{"class":136,"line":522},[134,4943,4944],{"class":2092},"image ",[134,4946,2156],{"class":140},[134,4948,162],{"class":161},[134,4950,4951],{"class":165},"docker:latest",[134,4953,382],{"class":161},[134,4955,4956,4959,4961],{"class":136,"line":541},[134,4957,4958],{"class":2092},"privileged ",[134,4960,2156],{"class":140},[134,4962,308],{"class":3785},[134,4964,4965,4968,4970],{"class":136,"line":736},[134,4966,4967],{"class":2092},"disable_entrypoint_overwrite ",[134,4969,2156],{"class":140},[134,4971,308],{"class":3785},[134,4973,4974,4977,4979],{"class":136,"line":742},[134,4975,4976],{"class":2092},"oom_kill_disable ",[134,4978,2156],{"class":140},[134,4980,308],{"class":3785},[134,4982,4983,4986,4988],{"class":136,"line":748},[134,4984,4985],{"class":2092},"disable_cache ",[134,4987,2156],{"class":140},[134,4989,308],{"class":3785},[134,4991,4992,4995,4997],{"class":136,"line":754},[134,4993,4994],{"class":2092},"volumes ",[134,4996,2156],{"class":140},[134,4998,230],{"class":140},[134,5000,5001,5003,5006,5008],{"class":136,"line":760},[134,5002,148],{"class":161},[134,5004,5005],{"class":165},"\u002Fvar\u002Frun\u002Fdocker.sock:\u002Fvar\u002Frun\u002Fdocker.sock",[134,5007,155],{"class":161},[134,5009,171],{"class":140},[134,5011,5012,5014,5017],{"class":136,"line":766},[134,5013,148],{"class":161},[134,5015,5016],{"class":165},"\u002Fxxx\u002Fgitlab-runner\u002Fcache:\u002Fcache",[134,5018,382],{"class":161},[134,5020,5021],{"class":136,"line":772},[134,5022,486],{"class":140},[134,5024,5025,5028,5030],{"class":136,"line":778},[134,5026,5027],{"class":2092},"shm_size ",[134,5029,2156],{"class":140},[134,5031,4744],{"class":3425},[28,5033,5034,5035,5038,5039,5042,5043,5046],{},"其中 ",[75,5036,5037],{},"token"," 从 ",[75,5040,5041],{},"GitLab Admin Area \u002F Overview \u002F Runners"," 中可以找到，或者也可以从 ",[75,5044,5045],{},"Project \u002F Settings \u002F CI\u002FCD"," 中找到项目专用的 Runner token。",[35,5048,5049],{"id":5049},"代码配置",[5051,5052,5054],"h4",{"id":5053},"前端node","前端（node）",[4714,5056,5058],{"id":5057},"dockerfile","Dockerfile",[86,5060,5063],{"className":5061,"code":5062,"language":5057,"meta":94,"style":94},"language-dockerfile shiki shiki-themes material-theme-lighter github-light github-dark","FROM node:10-alpine\n\nWORKDIR \u002Fapp\n\nCOPY package.json \u002Fapp\nCOPY yarn.lock \u002Fapp\nRUN yarn install\nCOPY . \u002Fapp\nRUN yarn build\n\nEXPOSE 8888\nENV APP_ENV $APP_ENV\nCMD [\"yarn\", \"start\"]\n",[75,5064,5065,5074,5078,5086,5090,5098,5105,5112,5119,5126,5130,5138,5146],{"__ignoreMap":94},[134,5066,5067,5071],{"class":136,"line":137},[134,5068,5070],{"class":5069},"sw1J6","FROM",[134,5072,5073],{"class":2092}," node:10-alpine\n",[134,5075,5076],{"class":136,"line":144},[134,5077,597],{"emptyLinePlaceholder":596},[134,5079,5080,5083],{"class":136,"line":174},[134,5081,5082],{"class":5069},"WORKDIR",[134,5084,5085],{"class":2092}," \u002Fapp\n",[134,5087,5088],{"class":136,"line":218},[134,5089,597],{"emptyLinePlaceholder":596},[134,5091,5092,5095],{"class":136,"line":233},[134,5093,5094],{"class":5069},"COPY",[134,5096,5097],{"class":2092}," package.json \u002Fapp\n",[134,5099,5100,5102],{"class":136,"line":239},[134,5101,5094],{"class":5069},[134,5103,5104],{"class":2092}," yarn.lock \u002Fapp\n",[134,5106,5107,5110],{"class":136,"line":264},[134,5108,5109],{"class":5069},"RUN",[134,5111,4590],{"class":2092},[134,5113,5114,5116],{"class":136,"line":296},[134,5115,5094],{"class":5069},[134,5117,5118],{"class":2092}," . \u002Fapp\n",[134,5120,5121,5123],{"class":136,"line":311},[134,5122,5109],{"class":5069},[134,5124,5125],{"class":2092}," yarn build\n",[134,5127,5128],{"class":136,"line":317},[134,5129,597],{"emptyLinePlaceholder":596},[134,5131,5132,5135],{"class":136,"line":322},[134,5133,5134],{"class":5069},"EXPOSE",[134,5136,5137],{"class":2092}," 8888\n",[134,5139,5140,5143],{"class":136,"line":343},[134,5141,5142],{"class":5069},"ENV",[134,5144,5145],{"class":2092}," APP_ENV $APP_ENV\n",[134,5147,5148,5151,5153,5156,5159,5162],{"class":136,"line":365},[134,5149,5150],{"class":5069},"CMD",[134,5152,186],{"class":2092},[134,5154,5155],{"class":165},"\"yarn\"",[134,5157,5158],{"class":2092},", ",[134,5160,5161],{"class":165},"\"start\"",[134,5163,486],{"class":2092},[4714,5165,560],{"id":5166},"gitlab-ciyml",[86,5168,5170],{"className":563,"code":5169,"language":565,"meta":94,"style":94},"image: docker:latest\n\nvariables:\n  REGISTRY: registry.cn-hangzhou.aliyuncs.com\n  USERNAME: your username\n  PASSWORD: your password\n  NAMESPACE: your namespace\n  PROJECT_NAME: your project name\n\nstages:\n  - build\n\ndocker-build:\n  stage: build\n  image: docker:latest\n  script:\n    - docker login --username=$USERNAME $REGISTRY -p $PASSWORD\n    - docker build -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest .\n    - docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME\n    - docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest\n  only:\n    - tags\n",[75,5171,5172,5181,5185,5192,5202,5212,5222,5232,5242,5246,5252,5258,5262,5269,5277,5286,5292,5299,5306,5313,5320,5326],{"__ignoreMap":94},[134,5173,5174,5176,5178],{"class":136,"line":137},[134,5175,2446],{"class":572},[134,5177,158],{"class":140},[134,5179,5180],{"class":165}," docker:latest\n",[134,5182,5183],{"class":136,"line":144},[134,5184,597],{"emptyLinePlaceholder":596},[134,5186,5187,5190],{"class":136,"line":174},[134,5188,5189],{"class":572},"variables",[134,5191,576],{"class":140},[134,5193,5194,5197,5199],{"class":136,"line":218},[134,5195,5196],{"class":572},"  REGISTRY",[134,5198,158],{"class":140},[134,5200,5201],{"class":165}," registry.cn-hangzhou.aliyuncs.com\n",[134,5203,5204,5207,5209],{"class":136,"line":233},[134,5205,5206],{"class":572},"  USERNAME",[134,5208,158],{"class":140},[134,5210,5211],{"class":165}," your username\n",[134,5213,5214,5217,5219],{"class":136,"line":239},[134,5215,5216],{"class":572},"  PASSWORD",[134,5218,158],{"class":140},[134,5220,5221],{"class":165}," your password\n",[134,5223,5224,5227,5229],{"class":136,"line":264},[134,5225,5226],{"class":572},"  NAMESPACE",[134,5228,158],{"class":140},[134,5230,5231],{"class":165}," your namespace\n",[134,5233,5234,5237,5239],{"class":136,"line":296},[134,5235,5236],{"class":572},"  PROJECT_NAME",[134,5238,158],{"class":140},[134,5240,5241],{"class":165}," your project name\n",[134,5243,5244],{"class":136,"line":311},[134,5245,597],{"emptyLinePlaceholder":596},[134,5247,5248,5250],{"class":136,"line":317},[134,5249,573],{"class":572},[134,5251,576],{"class":140},[134,5253,5254,5256],{"class":136,"line":322},[134,5255,581],{"class":140},[134,5257,4372],{"class":165},[134,5259,5260],{"class":136,"line":343},[134,5261,597],{"emptyLinePlaceholder":596},[134,5263,5264,5267],{"class":136,"line":365},[134,5265,5266],{"class":572},"docker-build",[134,5268,576],{"class":140},[134,5270,5271,5273,5275],{"class":136,"line":385},[134,5272,609],{"class":572},[134,5274,158],{"class":140},[134,5276,4372],{"class":165},[134,5278,5279,5282,5284],{"class":136,"line":390},[134,5280,5281],{"class":572},"  image",[134,5283,158],{"class":140},[134,5285,5180],{"class":165},[134,5287,5288,5290],{"class":136,"line":395},[134,5289,618],{"class":572},[134,5291,576],{"class":140},[134,5293,5294,5296],{"class":136,"line":416},[134,5295,625],{"class":140},[134,5297,5298],{"class":165}," docker login --username=$USERNAME $REGISTRY -p $PASSWORD\n",[134,5300,5301,5303],{"class":136,"line":448},[134,5302,625],{"class":140},[134,5304,5305],{"class":165}," docker build -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest .\n",[134,5307,5308,5310],{"class":136,"line":465},[134,5309,625],{"class":140},[134,5311,5312],{"class":165}," docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME\n",[134,5314,5315,5317],{"class":136,"line":489},[134,5316,625],{"class":140},[134,5318,5319],{"class":165}," docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest\n",[134,5321,5322,5324],{"class":136,"line":495},[134,5323,1884],{"class":572},[134,5325,576],{"class":140},[134,5327,5328,5330],{"class":136,"line":501},[134,5329,625],{"class":140},[134,5331,2552],{"class":165},[5051,5333,5335],{"id":5334},"后端spring-boot","后端（spring boot）",[4714,5337,5058],{"id":5338},"dockerfile-1",[86,5340,5342],{"className":5061,"code":5341,"language":5057,"meta":94,"style":94},"FROM openjdk:11-jre-slim\n\nRUN ln -sf \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FShanghai \u002Fetc\u002Flocaltime\n\nVOLUME \u002Ftmp\n\nCOPY target\u002Fxxx-api.jar app.jar\nENV SPRING_PROFILES_ACTIVE=\"prd\"\nENV JAVA_OPTS=\"-Xmx256m\"\nENTRYPOINT [ \"java\", \"-Djava.security.egd=file:\u002Fdev\u002F.\u002Furandom\", \"-jar\", \"\u002Fapp.jar\"]\n",[75,5343,5344,5351,5355,5362,5366,5374,5378,5385,5395,5405],{"__ignoreMap":94},[134,5345,5346,5348],{"class":136,"line":137},[134,5347,5070],{"class":5069},[134,5349,5350],{"class":2092}," openjdk:11-jre-slim\n",[134,5352,5353],{"class":136,"line":144},[134,5354,597],{"emptyLinePlaceholder":596},[134,5356,5357,5359],{"class":136,"line":174},[134,5358,5109],{"class":5069},[134,5360,5361],{"class":2092}," ln -sf \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FShanghai \u002Fetc\u002Flocaltime\n",[134,5363,5364],{"class":136,"line":218},[134,5365,597],{"emptyLinePlaceholder":596},[134,5367,5368,5371],{"class":136,"line":233},[134,5369,5370],{"class":5069},"VOLUME",[134,5372,5373],{"class":2092}," \u002Ftmp\n",[134,5375,5376],{"class":136,"line":239},[134,5377,597],{"emptyLinePlaceholder":596},[134,5379,5380,5382],{"class":136,"line":264},[134,5381,5094],{"class":5069},[134,5383,5384],{"class":2092}," target\u002Fxxx-api.jar app.jar\n",[134,5386,5387,5389,5392],{"class":136,"line":296},[134,5388,5142],{"class":5069},[134,5390,5391],{"class":2092}," SPRING_PROFILES_ACTIVE=",[134,5393,5394],{"class":165},"\"prd\"\n",[134,5396,5397,5399,5402],{"class":136,"line":311},[134,5398,5142],{"class":5069},[134,5400,5401],{"class":2092}," JAVA_OPTS=",[134,5403,5404],{"class":165},"\"-Xmx256m\"\n",[134,5406,5407,5410,5413,5416,5418,5421,5423,5426,5428,5431],{"class":136,"line":317},[134,5408,5409],{"class":5069},"ENTRYPOINT",[134,5411,5412],{"class":2092}," [ ",[134,5414,5415],{"class":165},"\"java\"",[134,5417,5158],{"class":2092},[134,5419,5420],{"class":165},"\"-Djava.security.egd=file:\u002Fdev\u002F.\u002Furandom\"",[134,5422,5158],{"class":2092},[134,5424,5425],{"class":165},"\"-jar\"",[134,5427,5158],{"class":2092},[134,5429,5430],{"class":165},"\"\u002Fapp.jar\"",[134,5432,486],{"class":2092},[4714,5434,560],{"id":5435},"gitlab-ciyml-1",[86,5437,5439],{"className":563,"code":5438,"language":565,"meta":94,"style":94},"image: docker:latest\n\nvariables:\n  MAVEN_OPTS: -Dmaven.repo.local=\u002Fcache\u002F.m2\u002Frepository\n  REGISTRY: registry.cn-hangzhou.aliyuncs.com\n  USERNAME: your username\n  PASSWORD: your password\n  NAMESPACE: your namespace\n  PROJECT_NAME: your project name\n\nstages:\n  - package\n  - build\n\nmaven-package:\n  image: maven:3.6-jdk-11-slim\n  stage: package\n  script:\n    - mvn $MAVEN_OPTS clean package -Dmaven.test.skip=true\n    - cp target\u002F$PROJECT_NAME.jar \u002Fcache\u002Fjars\u002F\n  only:\n    - tags\n\ndocker-build:\n  stage: build\n  image: docker:latest\n  script:\n    - docker login --username=$USERNAME $REGISTRY -p $PASSWORD\n    - mkdir target\n    - cp \u002Fcache\u002Fjars\u002F$PROJECT_NAME.jar target\n    - docker build -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME -t $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest .\n    - docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:$CI_COMMIT_REF_NAME\n    - docker push $REGISTRY\u002F$NAMESPACE\u002F$PROJECT_NAME:latest\n  only:\n    - tags\n",[75,5440,5441,5449,5453,5459,5469,5477,5485,5493,5501,5509,5513,5519,5526,5532,5536,5543,5552,5560,5566,5573,5580,5586,5592,5596,5602,5610,5618,5624,5630,5637,5644,5650,5656,5662,5668],{"__ignoreMap":94},[134,5442,5443,5445,5447],{"class":136,"line":137},[134,5444,2446],{"class":572},[134,5446,158],{"class":140},[134,5448,5180],{"class":165},[134,5450,5451],{"class":136,"line":144},[134,5452,597],{"emptyLinePlaceholder":596},[134,5454,5455,5457],{"class":136,"line":174},[134,5456,5189],{"class":572},[134,5458,576],{"class":140},[134,5460,5461,5464,5466],{"class":136,"line":218},[134,5462,5463],{"class":572},"  MAVEN_OPTS",[134,5465,158],{"class":140},[134,5467,5468],{"class":165}," -Dmaven.repo.local=\u002Fcache\u002F.m2\u002Frepository\n",[134,5470,5471,5473,5475],{"class":136,"line":233},[134,5472,5196],{"class":572},[134,5474,158],{"class":140},[134,5476,5201],{"class":165},[134,5478,5479,5481,5483],{"class":136,"line":239},[134,5480,5206],{"class":572},[134,5482,158],{"class":140},[134,5484,5211],{"class":165},[134,5486,5487,5489,5491],{"class":136,"line":264},[134,5488,5216],{"class":572},[134,5490,158],{"class":140},[134,5492,5221],{"class":165},[134,5494,5495,5497,5499],{"class":136,"line":296},[134,5496,5226],{"class":572},[134,5498,158],{"class":140},[134,5500,5231],{"class":165},[134,5502,5503,5505,5507],{"class":136,"line":311},[134,5504,5236],{"class":572},[134,5506,158],{"class":140},[134,5508,5241],{"class":165},[134,5510,5511],{"class":136,"line":317},[134,5512,597],{"emptyLinePlaceholder":596},[134,5514,5515,5517],{"class":136,"line":322},[134,5516,573],{"class":572},[134,5518,576],{"class":140},[134,5520,5521,5523],{"class":136,"line":343},[134,5522,581],{"class":140},[134,5524,5525],{"class":165}," package\n",[134,5527,5528,5530],{"class":136,"line":365},[134,5529,581],{"class":140},[134,5531,4372],{"class":165},[134,5533,5534],{"class":136,"line":385},[134,5535,597],{"emptyLinePlaceholder":596},[134,5537,5538,5541],{"class":136,"line":390},[134,5539,5540],{"class":572},"maven-package",[134,5542,576],{"class":140},[134,5544,5545,5547,5549],{"class":136,"line":395},[134,5546,5281],{"class":572},[134,5548,158],{"class":140},[134,5550,5551],{"class":165}," maven:3.6-jdk-11-slim\n",[134,5553,5554,5556,5558],{"class":136,"line":416},[134,5555,609],{"class":572},[134,5557,158],{"class":140},[134,5559,5525],{"class":165},[134,5561,5562,5564],{"class":136,"line":448},[134,5563,618],{"class":572},[134,5565,576],{"class":140},[134,5567,5568,5570],{"class":136,"line":465},[134,5569,625],{"class":140},[134,5571,5572],{"class":165}," mvn $MAVEN_OPTS clean package -Dmaven.test.skip=true\n",[134,5574,5575,5577],{"class":136,"line":489},[134,5576,625],{"class":140},[134,5578,5579],{"class":165}," cp target\u002F$PROJECT_NAME.jar \u002Fcache\u002Fjars\u002F\n",[134,5581,5582,5584],{"class":136,"line":495},[134,5583,1884],{"class":572},[134,5585,576],{"class":140},[134,5587,5588,5590],{"class":136,"line":501},[134,5589,625],{"class":140},[134,5591,2552],{"class":165},[134,5593,5594],{"class":136,"line":522},[134,5595,597],{"emptyLinePlaceholder":596},[134,5597,5598,5600],{"class":136,"line":541},[134,5599,5266],{"class":572},[134,5601,576],{"class":140},[134,5603,5604,5606,5608],{"class":136,"line":736},[134,5605,609],{"class":572},[134,5607,158],{"class":140},[134,5609,4372],{"class":165},[134,5611,5612,5614,5616],{"class":136,"line":742},[134,5613,5281],{"class":572},[134,5615,158],{"class":140},[134,5617,5180],{"class":165},[134,5619,5620,5622],{"class":136,"line":748},[134,5621,618],{"class":572},[134,5623,576],{"class":140},[134,5625,5626,5628],{"class":136,"line":754},[134,5627,625],{"class":140},[134,5629,5298],{"class":165},[134,5631,5632,5634],{"class":136,"line":760},[134,5633,625],{"class":140},[134,5635,5636],{"class":165}," mkdir target\n",[134,5638,5639,5641],{"class":136,"line":766},[134,5640,625],{"class":140},[134,5642,5643],{"class":165}," cp \u002Fcache\u002Fjars\u002F$PROJECT_NAME.jar target\n",[134,5645,5646,5648],{"class":136,"line":772},[134,5647,625],{"class":140},[134,5649,5305],{"class":165},[134,5651,5652,5654],{"class":136,"line":778},[134,5653,625],{"class":140},[134,5655,5312],{"class":165},[134,5657,5658,5660],{"class":136,"line":784},[134,5659,625],{"class":140},[134,5661,5319],{"class":165},[134,5663,5664,5666],{"class":136,"line":789},[134,5665,1884],{"class":572},[134,5667,576],{"class":140},[134,5669,5670,5672],{"class":136,"line":795},[134,5671,625],{"class":140},[134,5673,2552],{"class":165},[35,5675,5677],{"id":5676},"阿里云-k8s-配置","阿里云 k8s 配置",[28,5679,5680],{},"应用创建触发器：",[67,5682],{"filename":70},[28,5684,5685],{},"复制触发器 URL 到镜像仓库中创建推送触发器：",[67,5687],{"filename":3473},[28,5689,5690],{},"完成。",[1932,5692,5693],{},"html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}",{"title":94,"searchDepth":144,"depth":144,"links":5695},[5696,5697,5698],{"id":4711,"depth":144,"text":4712},{"id":5049,"depth":144,"text":5049},{"id":5676,"depth":144,"text":5677},"png","2020-12-27","今年对于我个人而言，在 DevOps 上的最大收获，莫过于摸索了这套基于 GitLab CI 和 k8s 的持续交付解决方案，其实原理都很简单，在我去年的方案里又做了改进，实现了基于 git tag 的触发方式，并且把原先的本地打包推镜像改为在 GitLab Runner 上打包推镜像。",{},"\u002Fposts\u002F2020\u002Fdevops-gitlab-ci-aliyun-k8s",{"text":5705,"minutes":5706,"time":5707,"words":5708},"3 min read",2.635,158100,527,{"title":4676,"description":5701},{"loc":5703},"posts\u002F2020\u002F20201227.devops-gitlab-ci-aliyun-k8s",[1954,1955,5713],"k8s","Jz27CSI5_8F6FrpDl81aWz-abbc2KGOJHbYI2yw83E8",{"id":5716,"title":5717,"body":5718,"class":1939,"cover":5699,"coverSize":1939,"date":7665,"description":7666,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":7667,"navigation":596,"path":7668,"readingTime":7669,"seo":7674,"sitemap":7675,"stem":7676,"tags":7677,"time":1939,"weather":7679,"__hash__":7680},"posts\u002Fposts\u002F2020\u002F20200227.k8s-cert-manager-tls.md","k8s 上利用 cert-manager 自动签发 TLS 证书",{"type":25,"value":5719,"toc":7663},[5720,5727,5734,5743,5751,5760,5764,5771,5790,5793,5894,5899,5928,5934,6018,6022,6108,6112,6134,6287,6292,6310,6313,6349,6352,6359,6499,6503,6521,6524,6570,6583,6590,6598,7052,7075,7093,7102,7105,7111,7114,7118,7121,7157,7202,7207,7271,7279,7284,7494,7498,7514,7516,7546,7549,7552,7595,7613,7624,7660],[28,5721,5722,5723,5726],{},"很多博主的 ",[75,5724,5725],{},"https"," 证书经常容易忘记更新，虽说证书过期前都会有邮件提醒，但是万一确实忙得没时间去处理，忘记了，就会出现证书过期的情况了。",[28,5728,5729,5730,5733],{},"之前在服务器上自己搭博客服务的时候，用 ",[75,5731,5732],{},"Let's Encrypt"," 来自动创建并续签证书，确实省了不少事。",[28,5735,5736,5737,5739,5740,5742],{},"在我的博客部署到 ",[75,5738,5713],{}," 之后，就一直用的一年一签的免费证书，每年更新一次，也不算特别麻烦，但是总归不够高端，我又怀念起了 ",[75,5741,5732],{},"。",[28,5744,5745,5747,5748,5750],{},[75,5746,5732],{}," 是个好东西，",[75,5749,5713],{}," 也是个好东西，两个好东西怎么结合呢？搜寻了一番确实有方案，经过几天的尝试，终于弄好了。花了几天是因为第一天因为有个粗心导致的问题，导致搞了好久没成功，休息了几天再次尝试，才找到问题。",[28,5752,5753,5754,5756,5757,5759],{},"有关 ",[75,5755,5713],{}," 的基础知识，这里不做赘述，网上教程很多，这里假设大家对 ",[75,5758,5713],{}," 都有一定了解。",[5051,5761,5763],{"id":5762},"安装-cert-manager","安装 cert-manager",[28,5765,5766,5767,5770],{},"安装 ",[75,5768,5769],{},"helm"," 到本地",[86,5772,5774],{"className":3479,"code":5773,"language":3481,"meta":94,"style":94},"$ brew install helm\n",[75,5775,5776],{"__ignoreMap":94},[134,5777,5778,5781,5784,5787],{"class":136,"line":137},[134,5779,5780],{"class":3488},"$",[134,5782,5783],{"class":165}," brew",[134,5785,5786],{"class":165}," install",[134,5788,5789],{"class":165}," helm\n",[28,5791,5792],{},"添加仓库和命名空间",[86,5794,5796],{"className":3479,"code":5795,"language":3481,"meta":94,"style":94},"$ kubectl create namespace cert-manager # 创建 cert-manager 命名空间\n$ kubectl label namespace cert-manager certmanager.io\u002Fdisable-validation=true # 标记 cert-manager 命名空间以禁用资源验证\n$ kubectl apply --validate=false -f https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml # 安装 CustomResourceDefinition 资源，注意 k8s 版本低于 1.15 需要用 legacy 版本\n$ helm repo add jetstack https:\u002F\u002Fcharts.jetstack.io # 添加 Jetstack Helm repository\n$ helm repo update # 更新本地 Helm chart repository\n",[75,5797,5798,5817,5838,5859,5880],{"__ignoreMap":94},[134,5799,5800,5802,5805,5808,5811,5814],{"class":136,"line":137},[134,5801,5780],{"class":3488},[134,5803,5804],{"class":165}," kubectl",[134,5806,5807],{"class":165}," create",[134,5809,5810],{"class":165}," namespace",[134,5812,5813],{"class":165}," cert-manager",[134,5815,5816],{"class":3047}," # 创建 cert-manager 命名空间\n",[134,5818,5819,5821,5823,5826,5828,5830,5833,5835],{"class":136,"line":144},[134,5820,5780],{"class":3488},[134,5822,5804],{"class":165},[134,5824,5825],{"class":165}," label",[134,5827,5810],{"class":165},[134,5829,5813],{"class":165},[134,5831,5832],{"class":165}," certmanager.io\u002Fdisable-validation=",[134,5834,69],{"class":147},[134,5836,5837],{"class":3047}," # 标记 cert-manager 命名空间以禁用资源验证\n",[134,5839,5840,5842,5844,5847,5850,5853,5856],{"class":136,"line":174},[134,5841,5780],{"class":3488},[134,5843,5804],{"class":165},[134,5845,5846],{"class":165}," apply",[134,5848,5849],{"class":3498}," --validate=false",[134,5851,5852],{"class":3498}," -f",[134,5854,5855],{"class":165}," https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml",[134,5857,5858],{"class":3047}," # 安装 CustomResourceDefinition 资源，注意 k8s 版本低于 1.15 需要用 legacy 版本\n",[134,5860,5861,5863,5866,5869,5871,5874,5877],{"class":136,"line":218},[134,5862,5780],{"class":3488},[134,5864,5865],{"class":165}," helm",[134,5867,5868],{"class":165}," repo",[134,5870,3492],{"class":165},[134,5872,5873],{"class":165}," jetstack",[134,5875,5876],{"class":165}," https:\u002F\u002Fcharts.jetstack.io",[134,5878,5879],{"class":3047}," # 添加 Jetstack Helm repository\n",[134,5881,5882,5884,5886,5888,5891],{"class":136,"line":233},[134,5883,5780],{"class":3488},[134,5885,5865],{"class":165},[134,5887,5868],{"class":165},[134,5889,5890],{"class":165}," update",[134,5892,5893],{"class":3047}," # 更新本地 Helm chart repository\n",[28,5895,5766,5896],{},[75,5897,5898],{},"cert-manager",[86,5900,5902],{"className":3479,"code":5901,"language":3481,"meta":94,"style":94},"$ helm install cert-manager --namespace cert-manager --version v0.14.1 jetstack\u002Fcert-manager\n",[75,5903,5904],{"__ignoreMap":94},[134,5905,5906,5908,5910,5912,5914,5917,5919,5922,5925],{"class":136,"line":137},[134,5907,5780],{"class":3488},[134,5909,5865],{"class":165},[134,5911,5786],{"class":165},[134,5913,5813],{"class":165},[134,5915,5916],{"class":3498}," --namespace",[134,5918,5813],{"class":165},[134,5920,5921],{"class":3498}," --version",[134,5923,5924],{"class":165}," v0.14.1",[134,5926,5927],{"class":165}," jetstack\u002Fcert-manager\n",[28,5929,5930,5931,5933],{},"查看 ",[75,5932,5898],{}," 安装情况",[86,5935,5937],{"className":3479,"code":5936,"language":3481,"meta":94,"style":94},"$ kubectl get pods --namespace cert-manager\nNAME                                       READY   STATUS    RESTARTS   AGE\ncert-manager-6cff8dc7b9-8vxws              1\u002F1     Running   0          4d10h\ncert-manager-cainjector-795c46858f-txczb   1\u002F1     Running   0          4d10h\ncert-manager-webhook-5dfc77cd74-skgsv      1\u002F1     Running   0          4d10h\n",[75,5938,5939,5956,5973,5990,6004],{"__ignoreMap":94},[134,5940,5941,5943,5945,5948,5951,5953],{"class":136,"line":137},[134,5942,5780],{"class":3488},[134,5944,5804],{"class":165},[134,5946,5947],{"class":165}," get",[134,5949,5950],{"class":165}," pods",[134,5952,5916],{"class":3498},[134,5954,5955],{"class":165}," cert-manager\n",[134,5957,5958,5961,5964,5967,5970],{"class":136,"line":144},[134,5959,5960],{"class":3488},"NAME",[134,5962,5963],{"class":165},"                                       READY",[134,5965,5966],{"class":165},"   STATUS",[134,5968,5969],{"class":165},"    RESTARTS",[134,5971,5972],{"class":165},"   AGE\n",[134,5974,5975,5978,5981,5984,5987],{"class":136,"line":174},[134,5976,5977],{"class":3488},"cert-manager-6cff8dc7b9-8vxws",[134,5979,5980],{"class":165},"              1\u002F1",[134,5982,5983],{"class":165},"     Running",[134,5985,5986],{"class":3425},"   0",[134,5988,5989],{"class":165},"          4d10h\n",[134,5991,5992,5995,5998,6000,6002],{"class":136,"line":218},[134,5993,5994],{"class":3488},"cert-manager-cainjector-795c46858f-txczb",[134,5996,5997],{"class":165},"   1\u002F1",[134,5999,5983],{"class":165},[134,6001,5986],{"class":3425},[134,6003,5989],{"class":165},[134,6005,6006,6009,6012,6014,6016],{"class":136,"line":233},[134,6007,6008],{"class":3488},"cert-manager-webhook-5dfc77cd74-skgsv",[134,6010,6011],{"class":165},"      1\u002F1",[134,6013,5983],{"class":165},[134,6015,5986],{"class":3425},[134,6017,5989],{"class":165},[5051,6019,6021],{"id":6020},"更新-cert-manager","更新 cert-manager",[86,6023,6025],{"className":3479,"code":6024,"language":3481,"meta":94,"style":94},"$ kubectl delete -n cert-manager deployment cert-manager cert-manager-cainjector cert-manager-webhook\n\n$ kubectl apply --validate=false -f https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml\n\n$ helm repo update\n$ helm upgrade --version v0.14.1 cert-manager jetstack\u002Fcert-manager -n cert-manager\n",[75,6026,6027,6052,6056,6071,6075,6086],{"__ignoreMap":94},[134,6028,6029,6031,6033,6036,6039,6041,6044,6046,6049],{"class":136,"line":137},[134,6030,5780],{"class":3488},[134,6032,5804],{"class":165},[134,6034,6035],{"class":165}," delete",[134,6037,6038],{"class":3498}," -n",[134,6040,5813],{"class":165},[134,6042,6043],{"class":165}," deployment",[134,6045,5813],{"class":165},[134,6047,6048],{"class":165}," cert-manager-cainjector",[134,6050,6051],{"class":165}," cert-manager-webhook\n",[134,6053,6054],{"class":136,"line":144},[134,6055,597],{"emptyLinePlaceholder":596},[134,6057,6058,6060,6062,6064,6066,6068],{"class":136,"line":174},[134,6059,5780],{"class":3488},[134,6061,5804],{"class":165},[134,6063,5846],{"class":165},[134,6065,5849],{"class":3498},[134,6067,5852],{"class":3498},[134,6069,6070],{"class":165}," https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml\n",[134,6072,6073],{"class":136,"line":218},[134,6074,597],{"emptyLinePlaceholder":596},[134,6076,6077,6079,6081,6083],{"class":136,"line":233},[134,6078,5780],{"class":3488},[134,6080,5865],{"class":165},[134,6082,5868],{"class":165},[134,6084,6085],{"class":165}," update\n",[134,6087,6088,6090,6092,6095,6097,6099,6101,6104,6106],{"class":136,"line":239},[134,6089,5780],{"class":3488},[134,6091,5865],{"class":165},[134,6093,6094],{"class":165}," upgrade",[134,6096,5921],{"class":3498},[134,6098,5924],{"class":165},[134,6100,5813],{"class":165},[134,6102,6103],{"class":165}," jetstack\u002Fcert-manager",[134,6105,6038],{"class":3498},[134,6107,5955],{"class":165},[5051,6109,6111],{"id":6110},"创建-clusterissuer","创建 ClusterIssuer",[28,6113,6114,6115,6117,6118,104,6121,6124,6125,6127,6128,6130,6131,4526],{},"我们需要创建一个签发机构，",[75,6116,5898],{}," 提供了",[75,6119,6120],{},"Issuer",[75,6122,6123],{},"ClusterIssuer"," 两种类型的签发机构，",[75,6126,6120],{}," 只能用来签发自己所在命名空间下的证书，ClusterIssuer 可以签发任意命名空间下的证书，我这里用 ",[75,6129,6123],{}," 为例，创建 ",[75,6132,6133],{},"letsencrypt-prod.yaml",[86,6135,6137],{"className":563,"code":6136,"language":565,"meta":94,"style":94},"apiVersion: cert-manager.io\u002Fv1alpha2\nkind: ClusterIssuer\nmetadata:\n  labels:\n    name: letsencrypt-prod\n  name: letsencrypt-prod # 自定义的签发机构名称，后面会引用\nspec:\n  acme:\n    email: yourname@youremail.com # 你的邮箱，证书快过期的时候会邮件提醒，不过我们可以设置自动续期\n    solvers:\n      - http01:\n          ingress:\n            class: nginx\n    privateKeySecretRef:\n      name: letsencrypt-prod # 指示此签发机构的私钥将要存储到哪个 Secret 对象中\n    server: https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory # acme 协议的服务端，我们用 Let's Encrypt\n",[75,6138,6139,6149,6159,6166,6173,6182,6195,6202,6209,6222,6229,6238,6245,6255,6262,6274],{"__ignoreMap":94},[134,6140,6141,6144,6146],{"class":136,"line":137},[134,6142,6143],{"class":572},"apiVersion",[134,6145,158],{"class":140},[134,6147,6148],{"class":165}," cert-manager.io\u002Fv1alpha2\n",[134,6150,6151,6154,6156],{"class":136,"line":144},[134,6152,6153],{"class":572},"kind",[134,6155,158],{"class":140},[134,6157,6158],{"class":165}," ClusterIssuer\n",[134,6160,6161,6164],{"class":136,"line":174},[134,6162,6163],{"class":572},"metadata",[134,6165,576],{"class":140},[134,6167,6168,6171],{"class":136,"line":218},[134,6169,6170],{"class":572},"  labels",[134,6172,576],{"class":140},[134,6174,6175,6177,6179],{"class":136,"line":233},[134,6176,2191],{"class":572},[134,6178,158],{"class":140},[134,6180,6181],{"class":165}," letsencrypt-prod\n",[134,6183,6184,6187,6189,6192],{"class":136,"line":239},[134,6185,6186],{"class":572},"  name",[134,6188,158],{"class":140},[134,6190,6191],{"class":165}," letsencrypt-prod",[134,6193,6194],{"class":3047}," # 自定义的签发机构名称，后面会引用\n",[134,6196,6197,6200],{"class":136,"line":264},[134,6198,6199],{"class":572},"spec",[134,6201,576],{"class":140},[134,6203,6204,6207],{"class":136,"line":296},[134,6205,6206],{"class":572},"  acme",[134,6208,576],{"class":140},[134,6210,6211,6214,6216,6219],{"class":136,"line":311},[134,6212,6213],{"class":572},"    email",[134,6215,158],{"class":140},[134,6217,6218],{"class":165}," yourname@youremail.com",[134,6220,6221],{"class":3047}," # 你的邮箱，证书快过期的时候会邮件提醒，不过我们可以设置自动续期\n",[134,6223,6224,6227],{"class":136,"line":317},[134,6225,6226],{"class":572},"    solvers",[134,6228,576],{"class":140},[134,6230,6231,6233,6236],{"class":136,"line":322},[134,6232,4635],{"class":140},[134,6234,6235],{"class":572}," http01",[134,6237,576],{"class":140},[134,6239,6240,6243],{"class":136,"line":343},[134,6241,6242],{"class":572},"          ingress",[134,6244,576],{"class":140},[134,6246,6247,6250,6252],{"class":136,"line":365},[134,6248,6249],{"class":572},"            class",[134,6251,158],{"class":140},[134,6253,6254],{"class":165}," nginx\n",[134,6256,6257,6260],{"class":136,"line":385},[134,6258,6259],{"class":572},"    privateKeySecretRef",[134,6261,576],{"class":140},[134,6263,6264,6267,6269,6271],{"class":136,"line":390},[134,6265,6266],{"class":572},"      name",[134,6268,158],{"class":140},[134,6270,6191],{"class":165},[134,6272,6273],{"class":3047}," # 指示此签发机构的私钥将要存储到哪个 Secret 对象中\n",[134,6275,6276,6279,6281,6284],{"class":136,"line":395},[134,6277,6278],{"class":572},"    server",[134,6280,158],{"class":140},[134,6282,6283],{"class":165}," https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory",[134,6285,6286],{"class":3047}," # acme 协议的服务端，我们用 Let's Encrypt\n",[28,6288,6289,6290],{},"应用 ",[75,6291,565],{},[86,6293,6295],{"className":3479,"code":6294,"language":3481,"meta":94,"style":94},"$ kubectl create -f letsencrypt-prod.yaml\n",[75,6296,6297],{"__ignoreMap":94},[134,6298,6299,6301,6303,6305,6307],{"class":136,"line":137},[134,6300,5780],{"class":3488},[134,6302,5804],{"class":165},[134,6304,5807],{"class":165},[134,6306,5852],{"class":3498},[134,6308,6309],{"class":165}," letsencrypt-prod.yaml\n",[28,6311,6312],{},"查看状态",[86,6314,6316],{"className":3479,"code":6315,"language":3481,"meta":94,"style":94},"$ kubectl get clusterissuer\nNAME               READY   AGE\nletsencrypt-prod   True    51s\n",[75,6317,6318,6329,6338],{"__ignoreMap":94},[134,6319,6320,6322,6324,6326],{"class":136,"line":137},[134,6321,5780],{"class":3488},[134,6323,5804],{"class":165},[134,6325,5947],{"class":165},[134,6327,6328],{"class":165}," clusterissuer\n",[134,6330,6331,6333,6336],{"class":136,"line":144},[134,6332,5960],{"class":3488},[134,6334,6335],{"class":165},"               READY",[134,6337,5972],{"class":165},[134,6339,6340,6343,6346],{"class":136,"line":174},[134,6341,6342],{"class":3488},"letsencrypt-prod",[134,6344,6345],{"class":165},"   True",[134,6347,6348],{"class":165},"    51s\n",[5051,6350,6351],{"id":6351},"手动签发证书",[28,6353,6354,6355,6358],{},"手动签发证书，创建 ",[75,6356,6357],{},"test-monkeyrun-net-cert.yaml"," 文件",[86,6360,6362],{"className":563,"code":6361,"language":565,"meta":94,"style":94},"apiVersion: cert-manager.io\u002Fv1alpha2\nkind: Certificate\nmetadata:\n  name: test-monkeyrun-net-cert\n  namespace: test\nspec:\n  secretName: tls-test-monkeyrun-net # 证书保存的 secret 名\n  duration: 2160h # 90d\n  renewBefore: 720h # 30d\n  dnsNames:\n    - test.monkeyrun.net\n  issuerRef:\n    name: letsencrypt-prod\n    kind: ClusterIssuer\n    group: cert-manager.io\n",[75,6363,6364,6372,6381,6387,6396,6406,6412,6425,6438,6451,6458,6465,6472,6480,6489],{"__ignoreMap":94},[134,6365,6366,6368,6370],{"class":136,"line":137},[134,6367,6143],{"class":572},[134,6369,158],{"class":140},[134,6371,6148],{"class":165},[134,6373,6374,6376,6378],{"class":136,"line":144},[134,6375,6153],{"class":572},[134,6377,158],{"class":140},[134,6379,6380],{"class":165}," Certificate\n",[134,6382,6383,6385],{"class":136,"line":174},[134,6384,6163],{"class":572},[134,6386,576],{"class":140},[134,6388,6389,6391,6393],{"class":136,"line":218},[134,6390,6186],{"class":572},[134,6392,158],{"class":140},[134,6394,6395],{"class":165}," test-monkeyrun-net-cert\n",[134,6397,6398,6401,6403],{"class":136,"line":233},[134,6399,6400],{"class":572},"  namespace",[134,6402,158],{"class":140},[134,6404,6405],{"class":165}," test\n",[134,6407,6408,6410],{"class":136,"line":239},[134,6409,6199],{"class":572},[134,6411,576],{"class":140},[134,6413,6414,6417,6419,6422],{"class":136,"line":264},[134,6415,6416],{"class":572},"  secretName",[134,6418,158],{"class":140},[134,6420,6421],{"class":165}," tls-test-monkeyrun-net",[134,6423,6424],{"class":3047}," # 证书保存的 secret 名\n",[134,6426,6427,6430,6432,6435],{"class":136,"line":296},[134,6428,6429],{"class":572},"  duration",[134,6431,158],{"class":140},[134,6433,6434],{"class":165}," 2160h",[134,6436,6437],{"class":3047}," # 90d\n",[134,6439,6440,6443,6445,6448],{"class":136,"line":311},[134,6441,6442],{"class":572},"  renewBefore",[134,6444,158],{"class":140},[134,6446,6447],{"class":165}," 720h",[134,6449,6450],{"class":3047}," # 30d\n",[134,6452,6453,6456],{"class":136,"line":317},[134,6454,6455],{"class":572},"  dnsNames",[134,6457,576],{"class":140},[134,6459,6460,6462],{"class":136,"line":322},[134,6461,625],{"class":140},[134,6463,6464],{"class":165}," test.monkeyrun.net\n",[134,6466,6467,6470],{"class":136,"line":343},[134,6468,6469],{"class":572},"  issuerRef",[134,6471,576],{"class":140},[134,6473,6474,6476,6478],{"class":136,"line":365},[134,6475,2191],{"class":572},[134,6477,158],{"class":140},[134,6479,6181],{"class":165},[134,6481,6482,6485,6487],{"class":136,"line":385},[134,6483,6484],{"class":572},"    kind",[134,6486,158],{"class":140},[134,6488,6158],{"class":165},[134,6490,6491,6494,6496],{"class":136,"line":390},[134,6492,6493],{"class":572},"    group",[134,6495,158],{"class":140},[134,6497,6498],{"class":165}," cert-manager.io\n",[28,6500,6289,6501],{},[75,6502,565],{},[86,6504,6506],{"className":3479,"code":6505,"language":3481,"meta":94,"style":94},"$ kubectl apply -f test-monkeyrun-net-cert.yaml\n",[75,6507,6508],{"__ignoreMap":94},[134,6509,6510,6512,6514,6516,6518],{"class":136,"line":137},[134,6511,5780],{"class":3488},[134,6513,5804],{"class":165},[134,6515,5846],{"class":165},[134,6517,5852],{"class":3498},[134,6519,6520],{"class":165}," test-monkeyrun-net-cert.yaml\n",[28,6522,6523],{},"检查是否生成证书文件",[86,6525,6527],{"className":3479,"code":6526,"language":3481,"meta":94,"style":94},"$ kubectl get certificate -n test\nNAME                      READY   SECRET                   AGE\ntest-monkeyrun-net-cert   True    test-monkeyrun-net-tls   99m\n",[75,6528,6529,6544,6557],{"__ignoreMap":94},[134,6530,6531,6533,6535,6537,6540,6542],{"class":136,"line":137},[134,6532,5780],{"class":3488},[134,6534,5804],{"class":165},[134,6536,5947],{"class":165},[134,6538,6539],{"class":165}," certificate",[134,6541,6038],{"class":3498},[134,6543,6405],{"class":165},[134,6545,6546,6548,6551,6554],{"class":136,"line":144},[134,6547,5960],{"class":3488},[134,6549,6550],{"class":165},"                      READY",[134,6552,6553],{"class":165},"   SECRET",[134,6555,6556],{"class":165},"                   AGE\n",[134,6558,6559,6562,6564,6567],{"class":136,"line":174},[134,6560,6561],{"class":3488},"test-monkeyrun-net-cert",[134,6563,6345],{"class":165},[134,6565,6566],{"class":165},"    test-monkeyrun-net-tls",[134,6568,6569],{"class":165},"   99m\n",[28,6571,6572,6573,4648,6576,6579,6580,6582],{},"将该证书配置到 ",[75,6574,6575],{},"test.monkeyrun.net",[75,6577,6578],{},"ingress"," 上，测试 ",[75,6581,5725],{}," 访问，成功。",[5051,6584,6586],{"id":6585},"创建deployment时自动签发证书",[6587,6588,6589],"del",{},"创建Deployment时自动签发证书",[28,6591,6592],{},[6587,6593,6594,6595],{},"创建 ",[75,6596,6597],{},"test-nginx.yaml",[86,6599,6601],{"className":563,"code":6600,"language":565,"meta":94,"style":94},"apiVersion: extensions\u002Fv1beta1\nkind: Deployment\nmetadata:\n  name: test-nginx\n  namespace: test\nspec:\n  replicas: 1\n  template:\n    metadata:\n      labels:\n        run: test-nginx\n    spec:\n      containers:\n        - name: test-nginx\n          image: nginx\n          ports:\n            - containerPort: 80\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: test-nginx\n  namespace: test\n  labels:\n    app: test-nginx\nspec:\n  ports:\n    - port: 80\n      protocol: TCP\n      name: http\n  selector:\n    run: test-nginx\n---\napiVersion: extensions\u002Fv1beta1\nkind: Ingress\nmetadata:\n  name: test-nginx\n  namespace: test\n  annotations:\n    kubernetes.io\u002Fingress.class: nginx\n    kubernetes.io\u002Ftls-acme: 'true'\n    certmanager.io\u002Fcluster-issuer: letsencrypt-prod\nspec:\n  rules:\n    - host: test.monkeyrun.net\n      http:\n        paths:\n          - backend:\n              serviceName: test-nginx\n              servicePort: 80\n            path: \u002F\n  tls:\n    - secretName: tls-test-monkeyrun-net\n      hosts:\n        - test.monkeyrun.net\n",[75,6602,6603,6612,6621,6627,6636,6644,6650,6659,6666,6673,6680,6689,6696,6703,6715,6724,6731,6744,6749,6758,6767,6773,6781,6789,6795,6804,6810,6817,6828,6838,6847,6854,6863,6867,6875,6884,6890,6898,6906,6913,6922,6935,6944,6950,6957,6968,6975,6982,6992,7001,7010,7020,7027,7039,7046],{"__ignoreMap":94},[134,6604,6605,6607,6609],{"class":136,"line":137},[134,6606,6143],{"class":572},[134,6608,158],{"class":140},[134,6610,6611],{"class":165}," extensions\u002Fv1beta1\n",[134,6613,6614,6616,6618],{"class":136,"line":144},[134,6615,6153],{"class":572},[134,6617,158],{"class":140},[134,6619,6620],{"class":165}," Deployment\n",[134,6622,6623,6625],{"class":136,"line":174},[134,6624,6163],{"class":572},[134,6626,576],{"class":140},[134,6628,6629,6631,6633],{"class":136,"line":218},[134,6630,6186],{"class":572},[134,6632,158],{"class":140},[134,6634,6635],{"class":165}," test-nginx\n",[134,6637,6638,6640,6642],{"class":136,"line":233},[134,6639,6400],{"class":572},[134,6641,158],{"class":140},[134,6643,6405],{"class":165},[134,6645,6646,6648],{"class":136,"line":239},[134,6647,6199],{"class":572},[134,6649,576],{"class":140},[134,6651,6652,6655,6657],{"class":136,"line":264},[134,6653,6654],{"class":572},"  replicas",[134,6656,158],{"class":140},[134,6658,4734],{"class":3425},[134,6660,6661,6664],{"class":136,"line":296},[134,6662,6663],{"class":572},"  template",[134,6665,576],{"class":140},[134,6667,6668,6671],{"class":136,"line":311},[134,6669,6670],{"class":572},"    metadata",[134,6672,576],{"class":140},[134,6674,6675,6678],{"class":136,"line":317},[134,6676,6677],{"class":572},"      labels",[134,6679,576],{"class":140},[134,6681,6682,6685,6687],{"class":136,"line":322},[134,6683,6684],{"class":572},"        run",[134,6686,158],{"class":140},[134,6688,6635],{"class":165},[134,6690,6691,6694],{"class":136,"line":343},[134,6692,6693],{"class":572},"    spec",[134,6695,576],{"class":140},[134,6697,6698,6701],{"class":136,"line":365},[134,6699,6700],{"class":572},"      containers",[134,6702,576],{"class":140},[134,6704,6705,6708,6711,6713],{"class":136,"line":385},[134,6706,6707],{"class":140},"        -",[134,6709,6710],{"class":572}," name",[134,6712,158],{"class":140},[134,6714,6635],{"class":165},[134,6716,6717,6720,6722],{"class":136,"line":390},[134,6718,6719],{"class":572},"          image",[134,6721,158],{"class":140},[134,6723,6254],{"class":165},[134,6725,6726,6729],{"class":136,"line":395},[134,6727,6728],{"class":572},"          ports",[134,6730,576],{"class":140},[134,6732,6733,6736,6739,6741],{"class":136,"line":416},[134,6734,6735],{"class":140},"            -",[134,6737,6738],{"class":572}," containerPort",[134,6740,158],{"class":140},[134,6742,6743],{"class":3425}," 80\n",[134,6745,6746],{"class":136,"line":448},[134,6747,6748],{"class":3488},"---\n",[134,6750,6751,6753,6755],{"class":136,"line":465},[134,6752,6143],{"class":572},[134,6754,158],{"class":140},[134,6756,6757],{"class":165}," v1\n",[134,6759,6760,6762,6764],{"class":136,"line":489},[134,6761,6153],{"class":572},[134,6763,158],{"class":140},[134,6765,6766],{"class":165}," Service\n",[134,6768,6769,6771],{"class":136,"line":495},[134,6770,6163],{"class":572},[134,6772,576],{"class":140},[134,6774,6775,6777,6779],{"class":136,"line":501},[134,6776,6186],{"class":572},[134,6778,158],{"class":140},[134,6780,6635],{"class":165},[134,6782,6783,6785,6787],{"class":136,"line":522},[134,6784,6400],{"class":572},[134,6786,158],{"class":140},[134,6788,6405],{"class":165},[134,6790,6791,6793],{"class":136,"line":541},[134,6792,6170],{"class":572},[134,6794,576],{"class":140},[134,6796,6797,6800,6802],{"class":136,"line":736},[134,6798,6799],{"class":572},"    app",[134,6801,158],{"class":140},[134,6803,6635],{"class":165},[134,6805,6806,6808],{"class":136,"line":742},[134,6807,6199],{"class":572},[134,6809,576],{"class":140},[134,6811,6812,6815],{"class":136,"line":748},[134,6813,6814],{"class":572},"  ports",[134,6816,576],{"class":140},[134,6818,6819,6821,6824,6826],{"class":136,"line":754},[134,6820,625],{"class":140},[134,6822,6823],{"class":572}," port",[134,6825,158],{"class":140},[134,6827,6743],{"class":3425},[134,6829,6830,6833,6835],{"class":136,"line":760},[134,6831,6832],{"class":572},"      protocol",[134,6834,158],{"class":140},[134,6836,6837],{"class":165}," TCP\n",[134,6839,6840,6842,6844],{"class":136,"line":766},[134,6841,6266],{"class":572},[134,6843,158],{"class":140},[134,6845,6846],{"class":165}," http\n",[134,6848,6849,6852],{"class":136,"line":772},[134,6850,6851],{"class":572},"  selector",[134,6853,576],{"class":140},[134,6855,6856,6859,6861],{"class":136,"line":778},[134,6857,6858],{"class":572},"    run",[134,6860,158],{"class":140},[134,6862,6635],{"class":165},[134,6864,6865],{"class":136,"line":784},[134,6866,6748],{"class":3488},[134,6868,6869,6871,6873],{"class":136,"line":789},[134,6870,6143],{"class":572},[134,6872,158],{"class":140},[134,6874,6611],{"class":165},[134,6876,6877,6879,6881],{"class":136,"line":795},[134,6878,6153],{"class":572},[134,6880,158],{"class":140},[134,6882,6883],{"class":165}," Ingress\n",[134,6885,6886,6888],{"class":136,"line":801},[134,6887,6163],{"class":572},[134,6889,576],{"class":140},[134,6891,6892,6894,6896],{"class":136,"line":807},[134,6893,6186],{"class":572},[134,6895,158],{"class":140},[134,6897,6635],{"class":165},[134,6899,6900,6902,6904],{"class":136,"line":813},[134,6901,6400],{"class":572},[134,6903,158],{"class":140},[134,6905,6405],{"class":165},[134,6907,6908,6911],{"class":136,"line":818},[134,6909,6910],{"class":572},"  annotations",[134,6912,576],{"class":140},[134,6914,6915,6918,6920],{"class":136,"line":823},[134,6916,6917],{"class":572},"    kubernetes.io\u002Fingress.class",[134,6919,158],{"class":140},[134,6921,6254],{"class":165},[134,6923,6924,6927,6929,6931,6933],{"class":136,"line":829},[134,6925,6926],{"class":572},"    kubernetes.io\u002Ftls-acme",[134,6928,158],{"class":140},[134,6930,2927],{"class":161},[134,6932,69],{"class":165},[134,6934,2933],{"class":161},[134,6936,6937,6940,6942],{"class":136,"line":835},[134,6938,6939],{"class":572},"    certmanager.io\u002Fcluster-issuer",[134,6941,158],{"class":140},[134,6943,6181],{"class":165},[134,6945,6946,6948],{"class":136,"line":841},[134,6947,6199],{"class":572},[134,6949,576],{"class":140},[134,6951,6952,6955],{"class":136,"line":847},[134,6953,6954],{"class":572},"  rules",[134,6956,576],{"class":140},[134,6958,6959,6961,6964,6966],{"class":136,"line":852},[134,6960,625],{"class":140},[134,6962,6963],{"class":572}," host",[134,6965,158],{"class":140},[134,6967,6464],{"class":165},[134,6969,6970,6973],{"class":136,"line":857},[134,6971,6972],{"class":572},"      http",[134,6974,576],{"class":140},[134,6976,6977,6980],{"class":136,"line":863},[134,6978,6979],{"class":572},"        paths",[134,6981,576],{"class":140},[134,6983,6984,6987,6990],{"class":136,"line":869},[134,6985,6986],{"class":140},"          -",[134,6988,6989],{"class":572}," backend",[134,6991,576],{"class":140},[134,6993,6994,6997,6999],{"class":136,"line":875},[134,6995,6996],{"class":572},"              serviceName",[134,6998,158],{"class":140},[134,7000,6635],{"class":165},[134,7002,7003,7006,7008],{"class":136,"line":881},[134,7004,7005],{"class":572},"              servicePort",[134,7007,158],{"class":140},[134,7009,6743],{"class":3425},[134,7011,7012,7015,7017],{"class":136,"line":887},[134,7013,7014],{"class":572},"            path",[134,7016,158],{"class":140},[134,7018,7019],{"class":165}," \u002F\n",[134,7021,7022,7025],{"class":136,"line":893},[134,7023,7024],{"class":572},"  tls",[134,7026,576],{"class":140},[134,7028,7029,7031,7034,7036],{"class":136,"line":898},[134,7030,625],{"class":140},[134,7032,7033],{"class":572}," secretName",[134,7035,158],{"class":140},[134,7037,7038],{"class":165}," tls-test-monkeyrun-net\n",[134,7040,7041,7044],{"class":136,"line":904},[134,7042,7043],{"class":572},"      hosts",[134,7045,576],{"class":140},[134,7047,7048,7050],{"class":136,"line":910},[134,7049,6707],{"class":140},[134,7051,6464],{"class":165},[28,7053,7054],{},[6587,7055,7056,7057,7060,7061,7064,7065,104,7068,7071,7072,7074],{},"删除之前手动创建的 ",[75,7058,7059],{},"Deployment","、",[75,7062,7063],{},"Service"," 、 ",[75,7066,7067],{},"Ingress",[75,7069,7070],{},"Secret"," 后， 应用 ",[75,7073,565],{}," 来自动创建",[86,7076,7078],{"className":3479,"code":7077,"language":3481,"meta":94,"style":94},"$ kubectl apply -f test-nginx.yaml\n",[75,7079,7080],{"__ignoreMap":94},[134,7081,7082,7084,7086,7088,7090],{"class":136,"line":137},[134,7083,5780],{"class":3488},[134,7085,5804],{"class":165},[134,7087,5846],{"class":165},[134,7089,5852],{"class":3498},[134,7091,7092],{"class":165}," test-nginx.yaml\n",[28,7094,7095],{},[6587,7096,7097,7098,7101],{},"打开 ",[75,7099,7100],{},"https:\u002F\u002Ftest.monkeyrun.net"," 测试，成功！",[28,7103,7104],{},"不知为何再次使用自动签发证书的时候会报错：",[86,7106,7109],{"className":7107,"code":7108,"language":91},[89],"E0330 07:46:30.070412       1 sync.go:57] cert-manager\u002Fcontroller\u002Fingress-shim \"msg\"=\"failed to determine issuer to be used for ingress resource\" \"error\"=\"failed to determine issuer name to be used for ingress resource\" \"resource_kind\"=\"Ingress\" \"resource_name\"=\"xxx\" \"resource_namespace\"=\"xxx\"\n",[75,7110,7108],{"__ignoreMap":94},[28,7112,7113],{},"解决了半天都没能找到问题，所以还是用手动签发吧，反正也是一次性的操作。",[5051,7115,7117],{"id":7116},"通过-dns-验证域名","通过 DNS 验证域名",[28,7119,7120],{},"刚才通过 http01 的方式验证域名会有个问题，对于已经部署上线的项目，没办法去验证，所以可以通过 dns 的方式来验证。",[28,7122,7123],{},[6587,7124,7125,7126,7131,7132,7137,7138,7141,7142,7144,7145,7150,7151,7156],{},"经过搜寻，找到了几篇文章，都是利用 ",[2697,7127,7130],{"href":7128,"rel":7129},"https:\u002F\u002Fgithub.com\u002Fkevinniu666",[2701],"kevinniu666"," 这位仁兄基于  ",[2697,7133,7136],{"href":7134,"rel":7135},"https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager-webhook-example",[2701],"jetstack\u002Fcert-manager-webhook-example"," 改成 ",[75,7139,7140],{},"alidns"," 的版本来搞的，不过尝试了下，他这里面 ",[75,7143,5898],{}," 版本太老已经跑不起来了，从 GitHub 的 forks 树里面找到了最新的一个 fork，",[2697,7146,7149],{"href":7147,"rel":7148},"https:\u002F\u002Fgithub.com\u002Fcolprog\u002Fcert-manager-webhook-alidns",[2701],"colprog\u002Fcert0manager-webhooks-alidns","，尝试了下，也不行，他应该是改了镜像，但是不可用了。重新尝试了下上一代 fork ",[2697,7152,7155],{"href":7153,"rel":7154},"https:\u002F\u002Fgithub.com\u002Fpangzineng\u002Fcert-manager-webhook-alidns",[2701],"pangzineng\u002Fcert-manager-webhook-alidns","，可用。",[86,7158,7160],{"className":3479,"code":7159,"language":3481,"meta":94,"style":94},"$ git clone https:\u002F\u002Fgithub.com\u002Fpangzineng\u002Fcert-manager-webhook-alidns.git\n$ cd cert-manager-webhook-alidns\n$ helm install cert-manager-webhook-alidns --namespace=cert-manager .\u002Fdeploy\u002Fwebhook-alidns\n",[75,7161,7162,7175,7185],{"__ignoreMap":94},[134,7163,7164,7166,7169,7172],{"class":136,"line":137},[134,7165,5780],{"class":3488},[134,7167,7168],{"class":165}," git",[134,7170,7171],{"class":165}," clone",[134,7173,7174],{"class":165}," https:\u002F\u002Fgithub.com\u002Fpangzineng\u002Fcert-manager-webhook-alidns.git\n",[134,7176,7177,7179,7182],{"class":136,"line":144},[134,7178,5780],{"class":3488},[134,7180,7181],{"class":165}," cd",[134,7183,7184],{"class":165}," cert-manager-webhook-alidns\n",[134,7186,7187,7189,7191,7193,7196,7199],{"class":136,"line":174},[134,7188,5780],{"class":3488},[134,7190,5865],{"class":165},[134,7192,5786],{"class":165},[134,7194,7195],{"class":165}," cert-manager-webhook-alidns",[134,7197,7198],{"class":3498}," --namespace=cert-manager",[134,7200,7201],{"class":165}," .\u002Fdeploy\u002Fwebhook-alidns\n",[28,7203,7204],{},[6587,7205,7206],{},"创建 alidns AccessKey Id 和 Secret",[86,7208,7210],{"className":3479,"code":7209,"language":3481,"meta":94,"style":94},"$ kubectl -n cert-manager create secret generic alidns-access-key-id --from-literal=accessKeyId='xxxxxxx'\n$ kubectl -n cert-manager create secret generic alidns-access-key-secret --from-literal=accessKeySecret='xxxxxxx'\n",[75,7211,7212,7243],{"__ignoreMap":94},[134,7213,7214,7216,7218,7220,7222,7224,7227,7230,7233,7236,7238,7241],{"class":136,"line":137},[134,7215,5780],{"class":3488},[134,7217,5804],{"class":165},[134,7219,6038],{"class":3498},[134,7221,5813],{"class":165},[134,7223,5807],{"class":165},[134,7225,7226],{"class":165}," secret",[134,7228,7229],{"class":165}," generic",[134,7231,7232],{"class":165}," alidns-access-key-id",[134,7234,7235],{"class":3498}," --from-literal=accessKeyId=",[134,7237,2732],{"class":161},[134,7239,7240],{"class":165},"xxxxxxx",[134,7242,2933],{"class":161},[134,7244,7245,7247,7249,7251,7253,7255,7257,7259,7262,7265,7267,7269],{"class":136,"line":144},[134,7246,5780],{"class":3488},[134,7248,5804],{"class":165},[134,7250,6038],{"class":3498},[134,7252,5813],{"class":165},[134,7254,5807],{"class":165},[134,7256,7226],{"class":165},[134,7258,7229],{"class":165},[134,7260,7261],{"class":165}," alidns-access-key-secret",[134,7263,7264],{"class":3498}," --from-literal=accessKeySecret=",[134,7266,2732],{"class":161},[134,7268,7240],{"class":165},[134,7270,2933],{"class":161},[28,7272,7273,7274],{},"更新：使用 ",[2697,7275,7278],{"href":7276,"rel":7277},"https:\u002F\u002Fgithub.com\u002Fpragkent\u002Falidns-webhook\u002Ftree\u002Fmaster",[2701],"pragkent\u002Falidns-webhook",[28,7280,7281,7282],{},"修改我们之前创建的 ",[75,7283,6133],{},[86,7285,7287],{"className":563,"code":7286,"language":565,"meta":94,"style":94},"apiVersion: cert-manager.io\u002Fv1\nkind: ClusterIssuer\nmetadata:\n  labels:\n    name: letsencrypt-prod\n  name: letsencrypt-prod # 自定义的签发机构名称，后面会引用\nspec:\n  acme:\n    email: yourname@youremail.com # 你的邮箱，证书快过期的时候会邮件提醒，不过我们可以设置自动续期\n    solvers:\n      - dns01:\n          webhook:\n            groupName: yourgroup.com\n            solverName: alidns\n            config:\n              region: ''\n              accessKeySecretRef:\n                name: alidns-secret\n                key: access-key\n              secretKeySecretRef:\n                name: alidns-secret\n                key: secret-key\n    privateKeySecretRef:\n      name: letsencrypt-prod-account-key # 指示此签发机构的私钥将要存储到哪个 Secret 对象中\n    server: https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory # acme 协议的服务端，我们用 Let's Encrypt\n",[75,7288,7289,7298,7306,7312,7318,7326,7336,7342,7348,7358,7364,7373,7380,7390,7400,7407,7416,7423,7433,7443,7450,7458,7467,7473,7484],{"__ignoreMap":94},[134,7290,7291,7293,7295],{"class":136,"line":137},[134,7292,6143],{"class":572},[134,7294,158],{"class":140},[134,7296,7297],{"class":165}," cert-manager.io\u002Fv1\n",[134,7299,7300,7302,7304],{"class":136,"line":144},[134,7301,6153],{"class":572},[134,7303,158],{"class":140},[134,7305,6158],{"class":165},[134,7307,7308,7310],{"class":136,"line":174},[134,7309,6163],{"class":572},[134,7311,576],{"class":140},[134,7313,7314,7316],{"class":136,"line":218},[134,7315,6170],{"class":572},[134,7317,576],{"class":140},[134,7319,7320,7322,7324],{"class":136,"line":233},[134,7321,2191],{"class":572},[134,7323,158],{"class":140},[134,7325,6181],{"class":165},[134,7327,7328,7330,7332,7334],{"class":136,"line":239},[134,7329,6186],{"class":572},[134,7331,158],{"class":140},[134,7333,6191],{"class":165},[134,7335,6194],{"class":3047},[134,7337,7338,7340],{"class":136,"line":264},[134,7339,6199],{"class":572},[134,7341,576],{"class":140},[134,7343,7344,7346],{"class":136,"line":296},[134,7345,6206],{"class":572},[134,7347,576],{"class":140},[134,7349,7350,7352,7354,7356],{"class":136,"line":311},[134,7351,6213],{"class":572},[134,7353,158],{"class":140},[134,7355,6218],{"class":165},[134,7357,6221],{"class":3047},[134,7359,7360,7362],{"class":136,"line":317},[134,7361,6226],{"class":572},[134,7363,576],{"class":140},[134,7365,7366,7368,7371],{"class":136,"line":322},[134,7367,4635],{"class":140},[134,7369,7370],{"class":572}," dns01",[134,7372,576],{"class":140},[134,7374,7375,7378],{"class":136,"line":343},[134,7376,7377],{"class":572},"          webhook",[134,7379,576],{"class":140},[134,7381,7382,7385,7387],{"class":136,"line":365},[134,7383,7384],{"class":572},"            groupName",[134,7386,158],{"class":140},[134,7388,7389],{"class":165}," yourgroup.com\n",[134,7391,7392,7395,7397],{"class":136,"line":385},[134,7393,7394],{"class":572},"            solverName",[134,7396,158],{"class":140},[134,7398,7399],{"class":165}," alidns\n",[134,7401,7402,7405],{"class":136,"line":390},[134,7403,7404],{"class":572},"            config",[134,7406,576],{"class":140},[134,7408,7409,7412,7414],{"class":136,"line":395},[134,7410,7411],{"class":572},"              region",[134,7413,158],{"class":140},[134,7415,3166],{"class":161},[134,7417,7418,7421],{"class":136,"line":416},[134,7419,7420],{"class":572},"              accessKeySecretRef",[134,7422,576],{"class":140},[134,7424,7425,7428,7430],{"class":136,"line":448},[134,7426,7427],{"class":572},"                name",[134,7429,158],{"class":140},[134,7431,7432],{"class":165}," alidns-secret\n",[134,7434,7435,7438,7440],{"class":136,"line":465},[134,7436,7437],{"class":572},"                key",[134,7439,158],{"class":140},[134,7441,7442],{"class":165}," access-key\n",[134,7444,7445,7448],{"class":136,"line":489},[134,7446,7447],{"class":572},"              secretKeySecretRef",[134,7449,576],{"class":140},[134,7451,7452,7454,7456],{"class":136,"line":495},[134,7453,7427],{"class":572},[134,7455,158],{"class":140},[134,7457,7432],{"class":165},[134,7459,7460,7462,7464],{"class":136,"line":501},[134,7461,7437],{"class":572},[134,7463,158],{"class":140},[134,7465,7466],{"class":165}," secret-key\n",[134,7468,7469,7471],{"class":136,"line":522},[134,7470,6259],{"class":572},[134,7472,576],{"class":140},[134,7474,7475,7477,7479,7482],{"class":136,"line":541},[134,7476,6266],{"class":572},[134,7478,158],{"class":140},[134,7480,7481],{"class":165}," letsencrypt-prod-account-key",[134,7483,6273],{"class":3047},[134,7485,7486,7488,7490,7492],{"class":136,"line":736},[134,7487,6278],{"class":572},[134,7489,158],{"class":140},[134,7491,6283],{"class":165},[134,7493,6286],{"class":3047},[28,7495,6289,7496],{},[75,7497,565],{},[86,7499,7500],{"className":3479,"code":6294,"language":3481,"meta":94,"style":94},[75,7501,7502],{"__ignoreMap":94},[134,7503,7504,7506,7508,7510,7512],{"class":136,"line":137},[134,7505,5780],{"class":3488},[134,7507,5804],{"class":165},[134,7509,5807],{"class":165},[134,7511,5852],{"class":3498},[134,7513,6309],{"class":165},[28,7515,6312],{},[86,7517,7518],{"className":3479,"code":6315,"language":3481,"meta":94,"style":94},[75,7519,7520,7530,7538],{"__ignoreMap":94},[134,7521,7522,7524,7526,7528],{"class":136,"line":137},[134,7523,5780],{"class":3488},[134,7525,5804],{"class":165},[134,7527,5947],{"class":165},[134,7529,6328],{"class":165},[134,7531,7532,7534,7536],{"class":136,"line":144},[134,7533,5960],{"class":3488},[134,7535,6335],{"class":165},[134,7537,5972],{"class":165},[134,7539,7540,7542,7544],{"class":136,"line":174},[134,7541,6342],{"class":3488},[134,7543,6345],{"class":165},[134,7545,6348],{"class":165},[28,7547,7548],{},"重新手动签发证书，验证，成功！",[28,7550,7551],{},"PS：需要注意的是，从 http01 认证修改到 dns01 认证后，有个坑，会一直失败，查看 cert-manager 的 Pod 日志，会发现如下错误：",[86,7553,7556],{"className":7554,"code":7555,"language":3360,"meta":94,"style":94},"language-log shiki shiki-themes material-theme-lighter github-light github-dark","cert-manager\u002Fcontroller\u002Forders \"msg\"=\"Failed to determine the list of Challenge resources needed for the Order\" \"error\"=\"no configured challenge solvers can be used for this challenge\" \"resource_kind\"=\"Order\" \"resource_name\"=\"xxx\"\n",[75,7557,7558],{"__ignoreMap":94},[134,7559,7560,7563,7566,7568,7571,7574,7576,7579,7582,7584,7587,7590,7592],{"class":136,"line":137},[134,7561,7562],{"class":2092},"cert-manager\u002Fcontroller\u002Forders ",[134,7564,7565],{"class":165},"\"msg\"",[134,7567,2156],{"class":2092},[134,7569,7570],{"class":165},"\"Failed to determine the list of Challenge resources needed for the Order\"",[134,7572,7573],{"class":165}," \"error\"",[134,7575,2156],{"class":2092},[134,7577,7578],{"class":165},"\"no configured challenge solvers can be used for this challenge\"",[134,7580,7581],{"class":165}," \"resource_kind\"",[134,7583,2156],{"class":2092},[134,7585,7586],{"class":165},"\"Order\"",[134,7588,7589],{"class":165}," \"resource_name\"",[134,7591,2156],{"class":2092},[134,7593,7594],{"class":165},"\"xxx\"\n",[28,7596,7597,7598,7603,7604,7609,7610,7612],{},"研究了半天都没成功，后来在 GitHub 上找到了这个 ",[2697,7599,7602],{"href":7600,"rel":7601},"https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Fissues\u002F2494#issuecomment-585391545",[2701],"Issue","，按照 ",[2697,7605,7608],{"href":7606,"rel":7607},"https:\u002F\u002Fgithub.com\u002Fdemisx",[2701],"demisx"," 这位仁兄的建议，把所有和 ",[75,7611,5898],{}," 相关的东西全部删除重新用 dns01 的方式部署一遍就 OK 了。",[28,7614,7615,7616,7619,7620,7623],{},"另外，cert-manager 的 API group 从 ",[75,7617,7618],{},"certmanager.k8s.io"," 改到 ",[75,7621,7622],{},"certmanager.io"," 了，不少老教程里面仍然是前者，需要改为后者才能正常执行。",[2592,7625,7626,7629],{},[28,7627,7628],{},"参考链接",[7630,7631,7632,7639,7646,7653],"ul",{},[48,7633,7634],{},[2697,7635,7638],{"href":7636,"rel":7637},"https:\u002F\u002Fdocs.bitnami.com\u002Fkubernetes\u002Fhow-to\u002Fsecure-kubernetes-services-with-ingress-tls-letsencrypt\u002F",[2701],"Secure Kubernetes Services With Ingress, TLS And Let's Encrypt",[48,7640,7641],{},[2697,7642,7645],{"href":7643,"rel":7644},"https:\u002F\u002Fxuchao918.github.io\u002F2019\u002F03\u002F14\u002F%E2%95%A9%E2%95%A3%E2%95%99%E2%94%9Ccert-manager%E2%95%A9%E2%95%A1%E2%95%A7%E2%95%93Ingress-https\u002F",[2701],"使用 cert-manager 实现 Ingress https",[48,7647,7648],{},[2697,7649,7652],{"href":7650,"rel":7651},"https:\u002F\u002Fyq.aliyun.com\u002Farticles\u002F718711",[2701],"使用 cert-manager 给阿里云的 DNS 域名授权 SSL 证书",[48,7654,7655],{},[2697,7656,7659],{"href":7657,"rel":7658},"https:\u002F\u002Fcert-manager.io\u002Fdocs\u002F",[2701],"cert-manager docs",[1932,7661,7662],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}",{"title":94,"searchDepth":144,"depth":144,"links":7664},[],"2020-02-27","很多博主的 https 证书经常容易忘记更新，虽说证书过期前都会有邮件提醒，但是万一确实忙得没时间去处理，忘记了，就会出现证书过期的情况了。",{},"\u002Fposts\u002F2020\u002Fk8s-cert-manager-tls",{"text":7670,"minutes":7671,"time":7672,"words":7673},"8 min read",7.465,447900,1493,{"title":5717,"description":7666},{"loc":7668},"posts\u002F2020\u002F20200227.k8s-cert-manager-tls",[1954,7678,5713,1955,1957],"阿里云","天气晴","2aJ6T7QGEjJQr4Yy8PkK08lqxa4n-rxsCy0mGJw2oBY",{"id":7682,"title":7683,"body":7684,"class":1939,"cover":1939,"coverSize":1939,"date":7947,"description":94,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":7948,"navigation":596,"path":7949,"readingTime":7950,"seo":7954,"sitemap":7955,"stem":7956,"tags":7957,"time":1939,"weather":1939,"__hash__":7958},"posts\u002Fposts\u002F2019\u002F20191229.aliyun-k8s-setup.md","阿里云 k8s 集群搭建",{"type":25,"value":7685,"toc":7943},[7686,7691,7697,7702,7707,7773,7782,7787,7791,7794,7866,7869,7872,7928,7940],[7687,7688,7690],"h3",{"id":7689},"为-vpc-配置-snat","为 VPC 配置 SNAT",[28,7692,7693],{},[7694,7695,7696],"strong",{},"注：SNAT 已关闭，看起来两个 ECS 节点都有公网 IP，不需要了。（2024-06-04）",[28,7698,7699],{},[6587,7700,7701],{},"阿里云的 NAT 网关太贵，考虑自行搭建 SNAT。",[28,7703,7704],{},[6587,7705,7706],{},"购买最廉价 ECS，配置如下设置",[86,7708,7710],{"className":3479,"code":7709,"language":3481,"meta":94,"style":94},"sysctl net.ipv4.ip_forward # 查看当前 IP 转发配置，0 为关闭，1 为打开\nsysctl -w net.ipv4.ip_forward=1 # 打开 IP 转发\niptables -t nat -I POSTROUTING -s 172.16.0.0\u002F16 -j SNAT --to-source 172.16.117.66\n",[75,7711,7712,7723,7738],{"__ignoreMap":94},[134,7713,7714,7717,7720],{"class":136,"line":137},[134,7715,7716],{"class":3488},"sysctl",[134,7718,7719],{"class":165}," net.ipv4.ip_forward",[134,7721,7722],{"class":3047}," # 查看当前 IP 转发配置，0 为关闭，1 为打开\n",[134,7724,7725,7727,7730,7733,7735],{"class":136,"line":144},[134,7726,7716],{"class":3488},[134,7728,7729],{"class":3498}," -w",[134,7731,7732],{"class":165}," net.ipv4.ip_forward=",[134,7734,3426],{"class":3425},[134,7736,7737],{"class":3047}," # 打开 IP 转发\n",[134,7739,7740,7743,7746,7749,7752,7755,7758,7761,7764,7767,7770],{"class":136,"line":174},[134,7741,7742],{"class":3488},"iptables",[134,7744,7745],{"class":3498}," -t",[134,7747,7748],{"class":165}," nat",[134,7750,7751],{"class":3498}," -I",[134,7753,7754],{"class":165}," POSTROUTING",[134,7756,7757],{"class":3498}," -s",[134,7759,7760],{"class":165}," 172.16.0.0\u002F16",[134,7762,7763],{"class":3498}," -j",[134,7765,7766],{"class":165}," SNAT",[134,7768,7769],{"class":3498}," --to-source",[134,7771,7772],{"class":3425}," 172.16.117.66\n",[28,7774,7775],{},[6587,7776,7777,7778,7781],{},"去 VPC 路由表中添加 ",[75,7779,7780],{},"0.0.0.0\u002F0"," 下一跳为上述 ECS",[28,7783,7784],{},[6587,7785,7786],{},"设置 iptasbles 开机启动：",[7687,7788,7790],{"id":7789},"dnat","DNAT",[28,7792,7793],{},"通过 公网 IP 访问集群管理 API",[86,7795,7797],{"className":3479,"code":7796,"language":3481,"meta":94,"style":94},"iptables -t nat -I PREROUTING -p tcp --dport 6443 -j DNAT --to 172.16.117.67:6443\niptables -t nat -I POSTROUTING -d 172.16.117.67\u002F32 -p tcp --dport 6443 -j MASQUERADE\n",[75,7798,7799,7835],{"__ignoreMap":94},[134,7800,7801,7803,7805,7807,7809,7812,7815,7818,7821,7824,7826,7829,7832],{"class":136,"line":137},[134,7802,7742],{"class":3488},[134,7804,7745],{"class":3498},[134,7806,7748],{"class":165},[134,7808,7751],{"class":3498},[134,7810,7811],{"class":165}," PREROUTING",[134,7813,7814],{"class":3498}," -p",[134,7816,7817],{"class":165}," tcp",[134,7819,7820],{"class":3498}," --dport",[134,7822,7823],{"class":3425}," 6443",[134,7825,7763],{"class":3498},[134,7827,7828],{"class":165}," DNAT",[134,7830,7831],{"class":3498}," --to",[134,7833,7834],{"class":165}," 172.16.117.67:6443\n",[134,7836,7837,7839,7841,7843,7845,7847,7850,7853,7855,7857,7859,7861,7863],{"class":136,"line":144},[134,7838,7742],{"class":3488},[134,7840,7745],{"class":3498},[134,7842,7748],{"class":165},[134,7844,7751],{"class":3498},[134,7846,7754],{"class":165},[134,7848,7849],{"class":3498}," -d",[134,7851,7852],{"class":165}," 172.16.117.67\u002F32",[134,7854,7814],{"class":3498},[134,7856,7817],{"class":165},[134,7858,7820],{"class":3498},[134,7860,7823],{"class":3425},[134,7862,7763],{"class":3498},[134,7864,7865],{"class":165}," MASQUERADE\n",[28,7867,7868],{},"记得开启安全组规则允许 6443 端口",[28,7870,7871],{},"在 k8s 集群信息中设置 自定义证书 SAN 为 47.111.247.217 配置证书，解决以下证书问题：",[86,7873,7875],{"className":3479,"code":7874,"language":3481,"meta":94,"style":94},"Unable to connect to the server: x509: certificate is valid for 172.21.0.1, 127.0.0.1, 7.20.49.48, 172.16.117.67, not 47.111.247.217\n",[75,7876,7877],{"__ignoreMap":94},[134,7878,7879,7882,7885,7888,7890,7893,7896,7899,7901,7904,7907,7910,7913,7916,7919,7922,7925],{"class":136,"line":137},[134,7880,7881],{"class":3488},"Unable",[134,7883,7884],{"class":165}," to",[134,7886,7887],{"class":165}," connect",[134,7889,7884],{"class":165},[134,7891,7892],{"class":165}," the",[134,7894,7895],{"class":165}," server:",[134,7897,7898],{"class":165}," x509:",[134,7900,6539],{"class":165},[134,7902,7903],{"class":165}," is",[134,7905,7906],{"class":165}," valid",[134,7908,7909],{"class":165}," for",[134,7911,7912],{"class":165}," 172.21.0.1,",[134,7914,7915],{"class":165}," 127.0.0.1,",[134,7917,7918],{"class":165}," 7.20.49.48,",[134,7920,7921],{"class":165}," 172.16.117.67,",[134,7923,7924],{"class":165}," not",[134,7926,7927],{"class":3425}," 47.111.247.217\n",[2592,7929,7930,7933],{},[28,7931,7932],{},"参考链接：",[28,7934,7935],{},[2697,7936,7939],{"href":7937,"rel":7938},"https:\u002F\u002Fyq.aliyun.com\u002Farticles\u002F112497",[2701],"如何通过 EIP 实现 VPC 下的 SNAT 以及 DNAT",[1932,7941,7942],{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":94,"searchDepth":144,"depth":144,"links":7944},[7945,7946],{"id":7689,"depth":174,"text":7690},{"id":7789,"depth":174,"text":7790},"2019-12-29",{},"\u002Fposts\u002F2019\u002Faliyun-k8s-setup",{"text":1992,"minutes":7951,"time":7952,"words":7953},1.17,70200,234,{"title":7683,"description":94},{"loc":7949},"posts\u002F2019\u002F20191229.aliyun-k8s-setup",[1954,7678,5713,1955],"nx96pApv8oyqcXahwKsOs9Y-8UfgHxl4bCTvPo9djqQ",{"id":7960,"title":7961,"body":7962,"class":1939,"cover":5699,"coverSize":1939,"date":7987,"description":7966,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":7988,"navigation":596,"path":7989,"readingTime":7990,"seo":7994,"sitemap":7995,"stem":7996,"tags":7997,"time":1939,"weather":1939,"__hash__":7998},"posts\u002Fposts\u002F2019\u002F20190730.qiniu-ssl-certificate-expire-problem.md","七牛 SSL 证书过期不刷新的坑",{"type":25,"value":7963,"toc":7985},[7964,7967,7970,7973,7976,7979,7982],[28,7965,7966],{},"最近一个七牛上的 SSL 证书到期了，导致 CDN 上的图片访问的时候提示证书失效。",[28,7968,7969],{},"但其实早在一个多月前，我已经针对那个域名重新签发了新的证书。",[28,7971,7972],{},"在发现提示证书失效后，我查看了 CDN 上的 HTTPS 证书，发现已经显示为了最新的证书，并且有效期都是正常的。",[28,7974,7975],{},"初步猜测是主域的 SSL 证书虽然更新了，但各个节点上的 CDN 证书没更新。",[28,7977,7978],{},"于是在 CDN 上的 HTTPS 配置中，重新强制更新下证书，提示 8~15 分钟生效。",[28,7980,7981],{},"果然，更新完之后，图片访问正常了。",[28,7983,7984],{},"七牛竟然没有在证书过期后自动去强制更新所有节点的证书，有点坑。",{"title":94,"searchDepth":144,"depth":144,"links":7986},[],"2019-07-30",{},"\u002Fposts\u002F2019\u002Fqiniu-ssl-certificate-expire-problem",{"text":7991,"minutes":7992,"time":7993,"words":1733},"1 min read",1.005,60300,{"title":7961,"description":7966},{"loc":7989},"posts\u002F2019\u002F20190730.qiniu-ssl-certificate-expire-problem",[1954,1955],"6OMV-zbYVn1ddXCucrl6oWSYC9NJLYllpY8DUrii1fA",{"id":8000,"title":8001,"body":8002,"class":1939,"cover":5699,"coverSize":1939,"date":8375,"description":8006,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":8376,"navigation":596,"path":8377,"readingTime":8378,"seo":8382,"sitemap":8383,"stem":8384,"tags":8385,"time":1939,"weather":1939,"__hash__":8386},"posts\u002Fposts\u002F2019\u002F20190710.centos-wire-ieee8021x-config.md","CentOS 有线网卡配置 IEEE 802.1X 上网",{"type":25,"value":8003,"toc":8373},[8004,8007,8010,8353,8370],[28,8005,8006],{},"公司网络使用 IEEE 802.1X 认证网络接入，手机端和 Windows 端都很方便，Linux 上稍微麻烦一些。最近有个测试服务器（CentOS 7.6）需要接入办公网络测试，折腾了一番。",[28,8008,8009],{},"中间走的弯路就不讲了，直接讲最终的解决方案吧。",[45,8011,8012,8027,8041,8245,8331,8341,8347],{},[48,8013,8014,8015,8018,8019,8022,8023,8026],{},"取消 ",[75,8016,8017],{},"\u002Fetc\u002Fsysconfig\u002Fnetwork-scripts\u002F"," 中活动网卡（本例中是 ",[75,8020,8021],{},"ifcfg-em1","）的任何配置，例如 ",[75,8024,8025],{},"ONBOOT"," 等。",[48,8028,8029,8032,8033,8036,8037,8040],{},[75,8030,8031],{},"chkconfig --list"," ，查看是否有 ",[75,8034,8035],{},"network"," 的服务，如果有，执行 ",[75,8038,8039],{},"chkconfig --del network"," 删除",[48,8042,8043,8044,8047,8048,8147,8150,8151,8153,8154,8157,8158],{},"修改 ",[75,8045,8046],{},"\u002Fetc\u002Fwpa_supplicant\u002Fwpa_supplicant.conf","，写入如下内容：",[86,8049,8051],{"className":3479,"code":8050,"language":3481,"meta":94,"style":94},"ctrl_interface=\u002Fvar\u002Frun\u002Fwpa_supplicant\nap_scan=0\nnetwork={\n    key_mgmt=IEEE8021X\n    eap=PEAP\n    identity=\"YOUR_USER_NAME\"\n    password=\"YOUR_PASSWORD\"\n    phase2=\"autheap=MSCHAPV2\"\n}\n",[75,8052,8053,8063,8073,8081,8091,8101,8115,8129,8143],{"__ignoreMap":94},[134,8054,8055,8058,8060],{"class":136,"line":137},[134,8056,8057],{"class":2092},"ctrl_interface",[134,8059,2156],{"class":2155},[134,8061,8062],{"class":165},"\u002Fvar\u002Frun\u002Fwpa_supplicant\n",[134,8064,8065,8068,8070],{"class":136,"line":144},[134,8066,8067],{"class":2092},"ap_scan",[134,8069,2156],{"class":2155},[134,8071,8072],{"class":165},"0\n",[134,8074,8075,8077,8079],{"class":136,"line":174},[134,8076,8035],{"class":2092},[134,8078,2156],{"class":2155},[134,8080,141],{"class":2092},[134,8082,8083,8086,8088],{"class":136,"line":218},[134,8084,8085],{"class":2092},"    key_mgmt",[134,8087,2156],{"class":2155},[134,8089,8090],{"class":165},"IEEE8021X\n",[134,8092,8093,8096,8098],{"class":136,"line":233},[134,8094,8095],{"class":2092},"    eap",[134,8097,2156],{"class":2155},[134,8099,8100],{"class":165},"PEAP\n",[134,8102,8103,8106,8108,8110,8113],{"class":136,"line":239},[134,8104,8105],{"class":2092},"    identity",[134,8107,2156],{"class":2155},[134,8109,155],{"class":161},[134,8111,8112],{"class":165},"YOUR_USER_NAME",[134,8114,382],{"class":161},[134,8116,8117,8120,8122,8124,8127],{"class":136,"line":264},[134,8118,8119],{"class":2092},"    password",[134,8121,2156],{"class":2155},[134,8123,155],{"class":161},[134,8125,8126],{"class":165},"YOUR_PASSWORD",[134,8128,382],{"class":161},[134,8130,8131,8134,8136,8138,8141],{"class":136,"line":296},[134,8132,8133],{"class":2092},"    phase2",[134,8135,2156],{"class":2155},[134,8137,155],{"class":161},[134,8139,8140],{"class":165},"autheap=MSCHAPV2",[134,8142,382],{"class":161},[134,8144,8145],{"class":136,"line":311},[134,8146,544],{"class":2092},[8148,8149],"br",{},"认证的账号和加密方式需要根据具体需求做更改。",[8148,8152],{},"可通过如下代码来测试是否成功（根据网卡名称 ",[75,8155,8156],{},"em1"," 需要根据实际使用的网卡做调整）：",[86,8159,8161],{"className":3479,"code":8160,"language":3481,"meta":94,"style":94},"$ ifdown em1\n$ wpa_supplicant -B -i em1 -c \u002Fetc\u002Fwpa_supplicant\u002Fwpa_supplicant.conf -D wired\n$ ifup em1\n$ dhclient em1\n$ ip addr # 查看IP地址\n$ ping baidu.com # 检查是否可以 Ping 通百度\n",[75,8162,8163,8173,8201,8210,8219,8232],{"__ignoreMap":94},[134,8164,8165,8167,8170],{"class":136,"line":137},[134,8166,5780],{"class":3488},[134,8168,8169],{"class":165}," ifdown",[134,8171,8172],{"class":165}," em1\n",[134,8174,8175,8177,8180,8183,8186,8189,8192,8195,8198],{"class":136,"line":144},[134,8176,5780],{"class":3488},[134,8178,8179],{"class":165}," wpa_supplicant",[134,8181,8182],{"class":3498}," -B",[134,8184,8185],{"class":3498}," -i",[134,8187,8188],{"class":165}," em1",[134,8190,8191],{"class":3498}," -c",[134,8193,8194],{"class":165}," \u002Fetc\u002Fwpa_supplicant\u002Fwpa_supplicant.conf",[134,8196,8197],{"class":3498}," -D",[134,8199,8200],{"class":165}," wired\n",[134,8202,8203,8205,8208],{"class":136,"line":174},[134,8204,5780],{"class":3488},[134,8206,8207],{"class":165}," ifup",[134,8209,8172],{"class":165},[134,8211,8212,8214,8217],{"class":136,"line":218},[134,8213,5780],{"class":3488},[134,8215,8216],{"class":165}," dhclient",[134,8218,8172],{"class":165},[134,8220,8221,8223,8226,8229],{"class":136,"line":233},[134,8222,5780],{"class":3488},[134,8224,8225],{"class":165}," ip",[134,8227,8228],{"class":165}," addr",[134,8230,8231],{"class":3047}," # 查看IP地址\n",[134,8233,8234,8236,8239,8242],{"class":136,"line":239},[134,8235,5780],{"class":3488},[134,8237,8238],{"class":165}," ping",[134,8240,8241],{"class":165}," baidu.com",[134,8243,8244],{"class":3047}," # 检查是否可以 Ping 通百度\n",[48,8246,8247,8248,8251,8252,8047,8255],{},"下面设置开机启动，在 ",[75,8249,8250],{},"\u002Fetc\u002Finit.d\u002F"," 中创建 ",[75,8253,8254],{},"wpa_network",[86,8256,8258],{"className":3479,"code":8257,"language":3481,"meta":94,"style":94},"#!\u002Fbin\u002Fbash\n# chkconfig: 2345 10 90\n# description: wpa network\n\nifdown em1\n\nwpa_supplicant -B -i em1 -c \u002Fetc\u002Fwpa_supplicant\u002Fwpa_supplicant.conf -D wired\n\nifup em1\n\ndhclient em1\n",[75,8259,8260,8265,8270,8275,8279,8286,8290,8309,8313,8320,8324],{"__ignoreMap":94},[134,8261,8262],{"class":136,"line":137},[134,8263,8264],{"class":3047},"#!\u002Fbin\u002Fbash\n",[134,8266,8267],{"class":136,"line":144},[134,8268,8269],{"class":3047},"# chkconfig: 2345 10 90\n",[134,8271,8272],{"class":136,"line":174},[134,8273,8274],{"class":3047},"# description: wpa network\n",[134,8276,8277],{"class":136,"line":218},[134,8278,597],{"emptyLinePlaceholder":596},[134,8280,8281,8284],{"class":136,"line":233},[134,8282,8283],{"class":3488},"ifdown",[134,8285,8172],{"class":165},[134,8287,8288],{"class":136,"line":239},[134,8289,597],{"emptyLinePlaceholder":596},[134,8291,8292,8295,8297,8299,8301,8303,8305,8307],{"class":136,"line":264},[134,8293,8294],{"class":3488},"wpa_supplicant",[134,8296,8182],{"class":3498},[134,8298,8185],{"class":3498},[134,8300,8188],{"class":165},[134,8302,8191],{"class":3498},[134,8304,8194],{"class":165},[134,8306,8197],{"class":3498},[134,8308,8200],{"class":165},[134,8310,8311],{"class":136,"line":296},[134,8312,597],{"emptyLinePlaceholder":596},[134,8314,8315,8318],{"class":136,"line":311},[134,8316,8317],{"class":3488},"ifup",[134,8319,8172],{"class":165},[134,8321,8322],{"class":136,"line":317},[134,8323,597],{"emptyLinePlaceholder":596},[134,8325,8326,8329],{"class":136,"line":322},[134,8327,8328],{"class":3488},"dhclient",[134,8330,8172],{"class":165},[48,8332,8333,8336,8337,8340],{},[75,8334,8335],{},"chkconfig --add wpa-network","，加入到 ",[75,8338,8339],{},"chkconfig"," 中",[48,8342,8343,8346],{},[75,8344,8345],{},"chkconfig wpa-network on","，开启",[48,8348,8349,8352],{},[75,8350,8351],{},"reboot"," 重启检测是否成功自动联网",[2592,8354,8355],{},[28,8356,8357,8358,8360,8361,112,8363,8365,8366,8369],{},"后记：后来发现运维可以直接通过 MAC 地址配置上网，于是又取消了 wpa 自动联网，直接在 ",[75,8359,8017],{}," 中把 ",[75,8362,8021],{},[75,8364,8025],{}," 设为 ",[75,8367,8368],{},"yes"," 即可。",[1932,8371,8372],{},"html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .stzsN, html code.shiki .stzsN{--shiki-light:#91B859;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}",{"title":94,"searchDepth":144,"depth":144,"links":8374},[],"2019-07-10",{},"\u002Fposts\u002F2019\u002Fcentos-wire-ieee8021x-config",{"text":1992,"minutes":8379,"time":8380,"words":8381},1.635,98100,327,{"title":8001,"description":8006},{"loc":8377},"posts\u002F2019\u002F20190710.centos-wire-ieee8021x-config",[1954,1955],"sWdr68ZZC2gy6xqIzSOLUKcTSd2AzXUEPvVwCE_6gEY",{"id":8388,"title":8389,"body":8390,"class":1939,"cover":1939,"coverSize":1939,"date":8955,"description":8394,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":8956,"navigation":596,"path":8957,"readingTime":8958,"seo":8963,"sitemap":8964,"stem":8965,"tags":8966,"time":8968,"weather":8969,"__hash__":8970},"posts\u002Fposts\u002F2018\u002F20180414.frontend-components-and-docker-deploy.md","前端跨项目组件化及基于 Docker 的快速部署方案",{"type":25,"value":8391,"toc":8951},[8392,8395,8398,8401,8424,8427,8475,8500,8519,8529,8577,8584,8590,8637,8643,8652,8655,8695,8750,8767,8771,8774,8777,8873,8883,8892,8938,8945,8948],[28,8393,8394],{},"最近静下心来写了几个项目，花了些时间重新整理了整套组件化方案和部署方案，记录一下。",[7687,8396,8397],{"id":8397},"跨项目组件化",[28,8399,8400],{},"前端的组件化不用多说了，发展到现在，无论是 React 的还是 Vue，都提供了相当方便的组件化实现。在日常项目中，有些组件其实是可以跨多个项目使用的，将这些组件抽离出来作为单独项目，并复用到其他项目中去，一来可以避免重复造轮子，加快开发速度，二来维护效率也高，一些 bugfix 或者新特性直接在组件中更新，项目中只需要更新引用版本号即可，方便快捷。",[28,8402,8403,8404,8407,8408,8411,8412,8414,8415,8417,8418,8420,8421,8423],{},"跨项目的组件化方式也很多，开发阶段可以用 ",[75,8405,8406],{},"npm link","，相当于在主项目的 ",[75,8409,8410],{},"node_modules"," 目录中创建了一个链向组件项目的软链，方便是挺方便，但是有几个问题。一是 Eslint 的目录递归检查是基于最终实际目录的，也就是说虽然 Eslint 默认排除 ",[75,8413,8410],{}," 目录，但它依然会对该目录中的软链项目进行检查，一旦组件项目的 Eslint 规则和主项目的 Eslint 不一致的话，主项目 Eslint 就没法通过，这个比较蛋疼，就得临时禁用 Eslint 或者修改组件项目的规则。作为组件项目应该保证少依赖，而且要服务多个项目，没办法保证匹配各个项目的 Eslint 规则。第二个问题是，通过 ",[75,8416,8406],{}," 实现的依赖，不会体现在 ",[75,8419,3003],{}," 中，如果通过 Docker 去部署，在 Docker 上是不知道你这个软链的，即便能够把软链写进去，在 Docker 中构建的时候，由于目录问题，也不能保证可以把组件项目文件拷过去。因此，在生产中，",[75,8422,8406],{}," 这个方案是没办法用的。",[28,8425,8426],{},"之前在国资的时候，采用的是通过组件项目的 git 地址来定位包，使用起来也很方便。如下：",[86,8428,8430],{"className":128,"code":8429,"language":130,"meta":94,"style":94},"{\n  \"dependencies\": {\n    \"example-component\": \"git+ssh:\u002F\u002Fgit@xxx.com\u002Fexample-component.git#v1.0.0\"\n  }\n}\n",[75,8431,8432,8436,8449,8467,8471],{"__ignoreMap":94},[134,8433,8434],{"class":136,"line":137},[134,8435,141],{"class":140},[134,8437,8438,8440,8443,8445,8447],{"class":136,"line":144},[134,8439,148],{"class":147},[134,8441,8442],{"class":151},"dependencies",[134,8444,155],{"class":147},[134,8446,158],{"class":140},[134,8448,2831],{"class":140},[134,8450,8451,8453,8456,8458,8460,8462,8465],{"class":136,"line":174},[134,8452,4455],{"class":147},[134,8454,8455],{"class":245},"example-component",[134,8457,155],{"class":147},[134,8459,158],{"class":140},[134,8461,162],{"class":161},[134,8463,8464],{"class":165},"git+ssh:\u002F\u002Fgit@xxx.com\u002Fexample-component.git#v1.0.0",[134,8466,382],{"class":161},[134,8468,8469],{"class":136,"line":218},[134,8470,3381],{"class":140},[134,8472,8473],{"class":136,"line":233},[134,8474,544],{"class":140},[28,8476,8477,8478,8481,8482,8485,8486,8488,8489,8492,8493,8495,8496,8499],{},"npm 支持通过 tag 来定位版本，组件项目发布的时候打一个 tag，对应的在项目中更新最后的版本号重新安装就可以完成升级了。由于之前没有用 Docker 部署，所以也没发现有什么问题。用了 Docker 的话，就会有些小问题。一般为了精简，都会采用基于 ",[75,8479,8480],{},"Alpine"," 的基础镜像，我目前的前端项目都是基于 ",[75,8483,8484],{},"node:8-alpine"," 来构建的。要知道，",[75,8487,8480],{}," 镜像本身只有 4.8M，",[75,8490,8491],{},"node:8-apline"," 也只有 20 几兆，非常精简。但是通过上面的这种方式，需要依赖 git，而 ",[75,8494,8480],{}," 显然是没有安装 ",[75,8497,8498],{},"git"," 的，也没有必要为了部署专门去安装一个 git。",[28,8501,8502,8503,8506,8507,8510,8511,8514,8515,8518],{},"于是便有了第三种方案，基于 Nexus 的 npm repository 方案。把包发布到 npm 上不太现实，大部分公司项目还是希望私有，和 maven 一样，Nexus 也支持 npm。创建一个 ",[75,8504,8505],{},"hosted"," 类型的 npm 仓库，例如 ",[75,8508,8509],{},"npm-hosted","，具体教程自行谷歌。但是我们又不希望给该仓库增加过多的压力，不想把所有 npm 或者 yarn 默认的 registry 改为 Nexus，没必要，因为即便改为 Nexus，在国内的网络环境，还是 proxy 到 ",[75,8512,8513],{},"https:\u002F\u002Fregistry.npm.taobao.org"," 上去了，而且会在 Nexus 上留下大量缓存，也经过了两层的下载，我尝试过，很蛋疼，在 Nexus 没有缓存第一次去下载的时候，还会有很多失败。后来发现 npm 也支持直接通过 ",[75,8516,8517],{},"tgz"," 文件的方式来引用，这样就好办了。",[28,8520,8521,8522,8525,8526,8528],{},"首先执行 ",[75,8523,8524],{},"npm adduser --registry=https:\u002F\u002Fmyregistry.example.com","，输入 Nexus 上具有上传 npm 包权限的用户名和密码，会在本地记录该用户的登录认证。然后在组件项目的 ",[75,8527,3003],{}," 中加入：",[86,8530,8532],{"className":128,"code":8531,"language":130,"meta":94,"style":94},"{\n  \"publishConfig\": {\n    \"registry\": \"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002F\"\n  }\n}\n",[75,8533,8534,8538,8551,8569,8573],{"__ignoreMap":94},[134,8535,8536],{"class":136,"line":137},[134,8537,141],{"class":140},[134,8539,8540,8542,8545,8547,8549],{"class":136,"line":144},[134,8541,148],{"class":147},[134,8543,8544],{"class":151},"publishConfig",[134,8546,155],{"class":147},[134,8548,158],{"class":140},[134,8550,2831],{"class":140},[134,8552,8553,8555,8558,8560,8562,8564,8567],{"class":136,"line":174},[134,8554,4455],{"class":147},[134,8556,8557],{"class":245},"registry",[134,8559,155],{"class":147},[134,8561,158],{"class":140},[134,8563,162],{"class":161},[134,8565,8566],{"class":165},"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002F",[134,8568,382],{"class":161},[134,8570,8571],{"class":136,"line":218},[134,8572,3381],{"class":140},[134,8574,8575],{"class":136,"line":233},[134,8576,544],{"class":140},[28,8578,8579,8580,8583],{},"然后执行 ",[75,8581,8582],{},"npm publish","，组件项目就会被上传到 Nexus 了。",[28,8585,8586,8587,8589],{},"在具体项目中，需要用到改组件的时候，在 ",[75,8588,3003],{}," 中这样引用：",[86,8591,8593],{"className":128,"code":8592,"language":130,"meta":94,"style":94},"{\n  \"dependencies\": {\n    \"vue-footer\": \"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002Fexample-component\u002F-\u002Fexample-component-1.0.0.tgz\"\n  }\n}\n",[75,8594,8595,8599,8611,8629,8633],{"__ignoreMap":94},[134,8596,8597],{"class":136,"line":137},[134,8598,141],{"class":140},[134,8600,8601,8603,8605,8607,8609],{"class":136,"line":144},[134,8602,148],{"class":147},[134,8604,8442],{"class":151},[134,8606,155],{"class":147},[134,8608,158],{"class":140},[134,8610,2831],{"class":140},[134,8612,8613,8615,8618,8620,8622,8624,8627],{"class":136,"line":174},[134,8614,4455],{"class":147},[134,8616,8617],{"class":245},"vue-footer",[134,8619,155],{"class":147},[134,8621,158],{"class":140},[134,8623,162],{"class":161},[134,8625,8626],{"class":165},"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002Fexample-component\u002F-\u002Fexample-component-1.0.0.tgz",[134,8628,382],{"class":161},[134,8630,8631],{"class":136,"line":218},[134,8632,3381],{"class":140},[134,8634,8635],{"class":136,"line":233},[134,8636,544],{"class":140},[28,8638,8639,8640,5742],{},"或者直接 ",[75,8641,8642],{},"npm install https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002Fexample-component\u002F-\u002Fexample-component-1.0.0.tgz --save",[28,8644,8645,8646,8648,8649,8651],{},"组件更新通过修改 ",[75,8647,3003],{}," 里的版本号，然后 ",[75,8650,8582],{},"，然后具体项目中修改最后的版本号，重新安装即可。",[28,8653,8654],{},"另外由于这次都是用的最新的组件，也遇到了一个很大的坑。",[28,8656,8657,8658,8661,8662,8665,8666,8365,8669,8672,8673,4648,8676,8679,8680,8683,8684,8688,8689,8694],{},"我的项目都是基于 Nuxt.js 来搞的，Nuxt.js 的框架用起来更爽，ssr 性能也比 vue 原生的要高。有一个问题，组件项目在 webpack 打包的时候，默认是不支持 Nuxt 的 SSR 的，用了 ",[75,8659,8660],{},"vue-style-loader"," 之后，里面会有多个地方用到了 ",[75,8663,8664],{},"document","。如果需要同时支持 Browser 和 SSR，需要再建一个 SSR 的 webpack config，将 ",[75,8667,8668],{},"target",[75,8670,8671],{},"node","，并且 ",[75,8674,8675],{},"vue-loader",[75,8677,8678],{},"options"," 中需要加入 ",[75,8681,8682],{},"optimizeSSR: false","，这个是因为尤大在最近的某个版本中针对 SSR 做了一些优化，但是在 Nuxt 的 SSR 中会有些问题，具体可以参见 ",[2697,8685,8686],{"href":8686,"rel":8687},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt.js\u002Fissues\u002F2565",[2701]," ，找了很久找到了这个 issue，追踪了下，尤大貌似在最近的 ",[2697,8690,8693],{"href":8691,"rel":8692},"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fvue\u002Fcommit\u002F9b22d86ab315a3c6061a6a4776eab1964304f92e",[2701],"v2.5.17-beta.0"," 中已经修复了这个问题，具体等 release 版发布之后再试下。在 Nuxt 中，创建一个 plugin，直接引用生成的 SSR 版本的文件即可。",[86,8696,8698],{"className":2706,"code":8697,"language":2708,"meta":94,"style":94},"import ExampleComponent from 'example-component\u002Fdist\u002Fssr.js'\nimport Vue from 'vue'\n\nVue.use(ExampleComponent)\n",[75,8699,8700,8717,8733,8737],{"__ignoreMap":94},[134,8701,8702,8704,8707,8710,8712,8715],{"class":136,"line":137},[134,8703,2089],{"class":727},[134,8705,8706],{"class":2092}," ExampleComponent ",[134,8708,8709],{"class":727},"from",[134,8711,2927],{"class":161},[134,8713,8714],{"class":165},"example-component\u002Fdist\u002Fssr.js",[134,8716,2933],{"class":161},[134,8718,8719,8721,8724,8726,8728,8731],{"class":136,"line":144},[134,8720,2089],{"class":727},[134,8722,8723],{"class":2092}," Vue ",[134,8725,8709],{"class":727},[134,8727,2927],{"class":161},[134,8729,8730],{"class":165},"vue",[134,8732,2933],{"class":161},[134,8734,8735],{"class":136,"line":174},[134,8736,597],{"emptyLinePlaceholder":596},[134,8738,8739,8742,8744,8747],{"class":136,"line":218},[134,8740,8741],{"class":2092},"Vue",[134,8743,2161],{"class":140},[134,8745,8746],{"class":2726},"use",[134,8748,8749],{"class":2092},"(ExampleComponent)\n",[28,8751,3502,8752,4648,8755,8758,8759,8762,8763,8766],{},[75,8753,8754],{},"nuxt.config.js",[75,8756,8757],{},"plugins"," 中直接加入 ",[75,8760,8761],{},"'~plugins\u002Fcomponents-plugin.js'"," 即可。网上大部分解决方案是引用的时候将组件项目设置为 ",[75,8764,8765],{},"ssr: false","，其实是治标不治本，放弃了该组件在服务端的渲染，不可取。",[7687,8768,8770],{"id":8769},"基于-docker-的快速部署","基于 Docker 的快速部署",[28,8772,8773],{},"使用 Docker 也快 1 年了，基本上从开始用上 Docker 之后，就爱不释手了，大大缩短了发布时间，减少了运维成本。",[28,8775,8776],{},"目前我的项目都是部署在阿里云上，基于阿里云的容器集群方案，前面通过 SLB，后面横向部署多台机器。不多说，贴下 Dockerfile：",[86,8778,8780],{"className":5061,"code":8779,"language":5057,"meta":94,"style":94},"FROM node:8-alpine\n\nWORKDIR \u002Fapp\n\nCOPY package.json \u002Fapp\nCOPY yarn.lock \u002Fapp\nRUN npm config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn install\nCOPY . \u002Fapp\nRUN npm run build\n\nEXPOSE 6002\nENV SERVER_ENV $SERVER_ENV\nCMD [\"npm\", \"run\", \"start\"]\n",[75,8781,8782,8789,8793,8799,8803,8809,8815,8822,8828,8835,8839,8846,8853],{"__ignoreMap":94},[134,8783,8784,8786],{"class":136,"line":137},[134,8785,5070],{"class":5069},[134,8787,8788],{"class":2092}," node:8-alpine\n",[134,8790,8791],{"class":136,"line":144},[134,8792,597],{"emptyLinePlaceholder":596},[134,8794,8795,8797],{"class":136,"line":174},[134,8796,5082],{"class":5069},[134,8798,5085],{"class":2092},[134,8800,8801],{"class":136,"line":218},[134,8802,597],{"emptyLinePlaceholder":596},[134,8804,8805,8807],{"class":136,"line":233},[134,8806,5094],{"class":5069},[134,8808,5097],{"class":2092},[134,8810,8811,8813],{"class":136,"line":239},[134,8812,5094],{"class":5069},[134,8814,5104],{"class":2092},[134,8816,8817,8819],{"class":136,"line":264},[134,8818,5109],{"class":5069},[134,8820,8821],{"class":2092}," npm config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn install\n",[134,8823,8824,8826],{"class":136,"line":296},[134,8825,5094],{"class":5069},[134,8827,5118],{"class":2092},[134,8829,8830,8832],{"class":136,"line":311},[134,8831,5109],{"class":5069},[134,8833,8834],{"class":2092}," npm run build\n",[134,8836,8837],{"class":136,"line":317},[134,8838,597],{"emptyLinePlaceholder":596},[134,8840,8841,8843],{"class":136,"line":322},[134,8842,5134],{"class":5069},[134,8844,8845],{"class":2092}," 6002\n",[134,8847,8848,8850],{"class":136,"line":343},[134,8849,5142],{"class":5069},[134,8851,8852],{"class":2092}," SERVER_ENV $SERVER_ENV\n",[134,8854,8855,8857,8859,8862,8864,8867,8869,8871],{"class":136,"line":365},[134,8856,5150],{"class":5069},[134,8858,186],{"class":2092},[134,8860,8861],{"class":165},"\"npm\"",[134,8863,5158],{"class":2092},[134,8865,8866],{"class":165},"\"run\"",[134,8868,5158],{"class":2092},[134,8870,5161],{"class":165},[134,8872,486],{"class":2092},[28,8874,8875,8876,8879,8880,8882],{},"记得在 ",[75,8877,8878],{},".dockerignore"," 文件中把 ",[75,8881,8410],{}," 目录加上，在 COPY 的时候不进行复制，而是在 docker 环境中重新获取。",[28,8884,8885,8886,8888,8889,8891],{},"在项目的 ",[75,8887,3003],{}," 中配置好 ",[75,8890,651],{}," 命令：",[86,8893,8895],{"className":128,"code":8894,"language":130,"meta":94,"style":94},"{\n  \"scripts\": {\n    \"deploy\": \"docker build -t registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:$npm_package_version -t registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:latest . && docker push registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:$npm_package_version && docker push registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:latest\"\n  }\n}\n",[75,8896,8897,8901,8913,8930,8934],{"__ignoreMap":94},[134,8898,8899],{"class":136,"line":137},[134,8900,141],{"class":140},[134,8902,8903,8905,8907,8909,8911],{"class":136,"line":144},[134,8904,148],{"class":147},[134,8906,115],{"class":151},[134,8908,155],{"class":147},[134,8910,158],{"class":140},[134,8912,2831],{"class":140},[134,8914,8915,8917,8919,8921,8923,8925,8928],{"class":136,"line":174},[134,8916,4455],{"class":147},[134,8918,651],{"class":245},[134,8920,155],{"class":147},[134,8922,158],{"class":140},[134,8924,162],{"class":161},[134,8926,8927],{"class":165},"docker build -t registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:$npm_package_version -t registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:latest . && docker push registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:$npm_package_version && docker push registry.cn-hangzhou.aliyuncs.com\u002Fyour-name\u002Fexample-project:latest",[134,8929,382],{"class":161},[134,8931,8932],{"class":136,"line":218},[134,8933,3381],{"class":140},[134,8935,8936],{"class":136,"line":233},[134,8937,544],{"class":140},[28,8939,8940,8941,8944],{},"在阿里云的容器镜像服务中建好镜像，便可以部署上去了。之前我是直接通过绑定 gitlab，在代码更新后，触发阿里云镜像服务在线构建，但是我们采用了私有的 npm 仓库的话，就比较麻烦了，还得配置 Nexus 账号，索性直接在本地构建，上传的网速也不是问题。在阿里云的容器服务中创建好应用，设置好触发器，在镜像更新后触发重新部署，就大功告成了。如果有多台机器，在调度配置中配置好“平滑升级”，会在同一服务的多个容器升级的时候，保证当前一批或者一个容器升级或者更新成功（健康检查成功）之后，再来更新或者升级下一批容器，也就是“滚动发布”。之后只需要 ",[75,8942,8943],{},"npm run deploy","，喝杯咖啡 ☕️，就完成了所有的部署工作了。方便、快捷、不易出错。",[28,8946,8947],{},"最近看了篇文章，里面有句话：“这世界上肯定存在让人上瘾的代码”，提出了“技术多巴胺”这个说法。而此刻的我，就仿佛打了几针“技术多巴胺”一样，很兴奋，一点睡意都没有。",[1932,8949,8950],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sseR_, html code.shiki .sseR_{--shiki-light:#9C3EDA;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZMiF, html code.shiki .sZMiF{--shiki-light:#E2931D;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":94,"searchDepth":144,"depth":144,"links":8952},[8953,8954],{"id":8397,"depth":174,"text":8397},{"id":8769,"depth":174,"text":8770},"2018-04-14",{},"\u002Fposts\u002F2018\u002Ffrontend-components-and-docker-deploy",{"text":8959,"minutes":8960,"time":8961,"words":8962},"10 min read",9.455,567300,1891,{"title":8389,"description":8394},{"loc":8957},"posts\u002F2018\u002F20180414.frontend-components-and-docker-deploy",[1954,8967,1957,1955],"前端","凌晨","天气🌧","9_mH8lcdetq97RzpovxCwJ60jWmSz7r17jZvBEuf42s",{"id":8972,"title":8973,"body":8974,"class":1939,"cover":1939,"coverSize":1939,"date":9781,"description":9782,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":9783,"navigation":596,"path":9784,"readingTime":9785,"seo":9789,"sitemap":9790,"stem":9791,"tags":9792,"time":1939,"weather":9793,"__hash__":9794},"posts\u002Fposts\u002F2018\u002F20180412.dockerfile-maven.md","Maven 项目 Docker 一键发布配置",{"type":25,"value":8975,"toc":9779},[8976,8991,8994,9096,9102,9119,9673,9680,9770,9776],[28,8977,8978,8979,8984,8985,8990],{},"Docker 用了很久了，之前 Maven 项目一直用的",[2697,8980,8983],{"href":8981,"rel":8982},"https:\u002F\u002Fgithub.com\u002Fspotify\u002Fdocker-maven-plugin",[2701],"docker-maven-plugin","，但是作者目前已经不推荐使用这种方式了，该项目已经不再更新功能，只提供 bugfix。他们的新项目叫做",[2697,8986,8989],{"href":8987,"rel":8988},"https:\u002F\u002Fgithub.com\u002Fspotify\u002Fdockerfile-maven",[2701],"dockerfile-maven","，配置上有些不同，之前一直没时间去更新，最近的一个项目中，采用了最新的插件，中间也踩过不少坑，刚刚终于都搞定了，记录一下。",[28,8992,8993],{},"Dockerfile 无需多说，整理了一个通用的，可以用在任意 Spring Boot 项目中，如下：",[86,8995,8997],{"className":5061,"code":8996,"language":5057,"meta":94,"style":94},"FROM frolvlad\u002Falpine-oraclejdk8:slim\n\nRUN ln -sf \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FShanghai \u002Fetc\u002Flocaltime\n\nVOLUME \u002Ftmp\n\nARG JAR_FILE\nADD ${JAR_FILE} app.jar\nRUN sh -c 'touch \u002Fapp.jar'\nENV JAVA_OPTS=\"\"\nENV ENV=\"\"\nENTRYPOINT [ \"sh\", \"-c\", \"java $JAVA_OPTS -Djava.security.egd=file:\u002Fdev\u002F.\u002Furandom -jar \u002Fapp.jar --spring.profiles.active=$ENV\" ]\n",[75,8998,8999,9006,9010,9016,9020,9026,9030,9038,9046,9056,9065,9074],{"__ignoreMap":94},[134,9000,9001,9003],{"class":136,"line":137},[134,9002,5070],{"class":5069},[134,9004,9005],{"class":2092}," frolvlad\u002Falpine-oraclejdk8:slim\n",[134,9007,9008],{"class":136,"line":144},[134,9009,597],{"emptyLinePlaceholder":596},[134,9011,9012,9014],{"class":136,"line":174},[134,9013,5109],{"class":5069},[134,9015,5361],{"class":2092},[134,9017,9018],{"class":136,"line":218},[134,9019,597],{"emptyLinePlaceholder":596},[134,9021,9022,9024],{"class":136,"line":233},[134,9023,5370],{"class":5069},[134,9025,5373],{"class":2092},[134,9027,9028],{"class":136,"line":239},[134,9029,597],{"emptyLinePlaceholder":596},[134,9031,9032,9035],{"class":136,"line":264},[134,9033,9034],{"class":5069},"ARG",[134,9036,9037],{"class":2092}," JAR_FILE\n",[134,9039,9040,9043],{"class":136,"line":296},[134,9041,9042],{"class":5069},"ADD",[134,9044,9045],{"class":2092}," ${JAR_FILE} app.jar\n",[134,9047,9048,9050,9053],{"class":136,"line":311},[134,9049,5109],{"class":5069},[134,9051,9052],{"class":2092}," sh -c ",[134,9054,9055],{"class":165},"'touch \u002Fapp.jar'\n",[134,9057,9058,9060,9062],{"class":136,"line":317},[134,9059,5142],{"class":5069},[134,9061,5401],{"class":2092},[134,9063,9064],{"class":165},"\"\"\n",[134,9066,9067,9069,9072],{"class":136,"line":322},[134,9068,5142],{"class":5069},[134,9070,9071],{"class":2092}," ENV=",[134,9073,9064],{"class":165},[134,9075,9076,9078,9080,9083,9085,9088,9090,9093],{"class":136,"line":343},[134,9077,5409],{"class":5069},[134,9079,5412],{"class":2092},[134,9081,9082],{"class":165},"\"sh\"",[134,9084,5158],{"class":2092},[134,9086,9087],{"class":165},"\"-c\"",[134,9089,5158],{"class":2092},[134,9091,9092],{"class":165},"\"java $JAVA_OPTS -Djava.security.egd=file:\u002Fdev\u002F.\u002Furandom -jar \u002Fapp.jar --spring.profiles.active=$ENV\"",[134,9094,9095],{"class":2092}," ]\n",[28,9097,9098,9099,9101],{},"在用阿里云容器服务的时候，这里的",[75,9100,5142],{},"可以直接显示到配置项中进行配置，根据不同的配置选择不同的 profiles 文件。",[28,9103,9104,9107,9108,9111,9112,9115,9116,9118],{},[75,9105,9106],{},"pom.xml","文件中，加入",[75,9109,9110],{},"plugin","，会自动添加版本号和",[75,9113,9114],{},"latest","两个",[75,9117,4647],{},"，并推送。",[86,9120,9124],{"className":9121,"code":9122,"language":9123,"meta":94,"style":94},"language-xml shiki shiki-themes material-theme-lighter github-light github-dark","\u003Cplugin>\n    \u003CgroupId>com.spotify\u003C\u002FgroupId>\n    \u003CartifactId>dockerfile-maven-plugin\u003C\u002FartifactId>\n    \u003Cversion>1.4.0\u003C\u002Fversion>\n    \u003Cexecutions>\n        \u003Cexecution>\n            \u003Cid>build-image\u003C\u002Fid>\n            \u003Cphase>package\u003C\u002Fphase>\n            \u003Cgoals>\n                \u003Cgoal>build\u003C\u002Fgoal>\n            \u003C\u002Fgoals>\n        \u003C\u002Fexecution>\n        \u003Cexecution>\n            \u003Cid>tag-image-version\u003C\u002Fid>\n            \u003Cphase>deploy\u003C\u002Fphase>\n            \u003Cgoals>\n                \u003Cgoal>tag\u003C\u002Fgoal>\n                \u003Cgoal>push\u003C\u002Fgoal>\n            \u003C\u002Fgoals>\n            \u003Cconfiguration>\n                \u003Ctag>${project.version}\u003C\u002Ftag>\n            \u003C\u002Fconfiguration>\n        \u003C\u002Fexecution>\n        \u003Cexecution>\n            \u003Cid>tag-image-latest\u003C\u002Fid>\n            \u003Cphase>deploy\u003C\u002Fphase>\n            \u003Cgoals>\n                \u003Cgoal>tag\u003C\u002Fgoal>\n                \u003Cgoal>push\u003C\u002Fgoal>\n            \u003C\u002Fgoals>\n            \u003Cconfiguration>\n                \u003Ctag>latest\u003C\u002Ftag>\n            \u003C\u002Fconfiguration>\n        \u003C\u002Fexecution>\n    \u003C\u002Fexecutions>\n    \u003Cconfiguration>\n        \u003Crepository>registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002F${project.artifactId}\u003C\u002Frepository>\n        \u003Ctag>${project.version}\u003C\u002Ftag>\n        \u003CuseMavenSettingsForAuth>true\u003C\u002FuseMavenSettingsForAuth>\n        \u003CbuildArgs>\n            \u003CJAR_FILE>target\u002F${project.build.finalName}.jar\u003C\u002FJAR_FILE>\n        \u003C\u002FbuildArgs>\n    \u003C\u002Fconfiguration>\n\u003C\u002Fplugin>\n\n","xml",[75,9125,9126,9136,9157,9175,9192,9201,9211,9230,9248,9257,9275,9284,9293,9301,9318,9334,9342,9358,9375,9383,9392,9409,9417,9425,9433,9450,9466,9474,9490,9506,9514,9522,9538,9546,9554,9563,9571,9589,9605,9622,9631,9649,9657,9665],{"__ignoreMap":94},[134,9127,9128,9131,9133],{"class":136,"line":137},[134,9129,9130],{"class":140},"\u003C",[134,9132,9110],{"class":572},[134,9134,9135],{"class":140},">\n",[134,9137,9138,9141,9144,9147,9150,9153,9155],{"class":136,"line":144},[134,9139,9140],{"class":140},"    \u003C",[134,9142,9143],{"class":572},"groupId",[134,9145,9146],{"class":140},">",[134,9148,9149],{"class":2092},"com.spotify",[134,9151,9152],{"class":140},"\u003C\u002F",[134,9154,9143],{"class":572},[134,9156,9135],{"class":140},[134,9158,9159,9161,9164,9166,9169,9171,9173],{"class":136,"line":174},[134,9160,9140],{"class":140},[134,9162,9163],{"class":572},"artifactId",[134,9165,9146],{"class":140},[134,9167,9168],{"class":2092},"dockerfile-maven-plugin",[134,9170,9152],{"class":140},[134,9172,9163],{"class":572},[134,9174,9135],{"class":140},[134,9176,9177,9179,9181,9183,9186,9188,9190],{"class":136,"line":218},[134,9178,9140],{"class":140},[134,9180,4215],{"class":572},[134,9182,9146],{"class":140},[134,9184,9185],{"class":2092},"1.4.0",[134,9187,9152],{"class":140},[134,9189,4215],{"class":572},[134,9191,9135],{"class":140},[134,9193,9194,9196,9199],{"class":136,"line":233},[134,9195,9140],{"class":140},[134,9197,9198],{"class":572},"executions",[134,9200,9135],{"class":140},[134,9202,9203,9206,9209],{"class":136,"line":239},[134,9204,9205],{"class":140},"        \u003C",[134,9207,9208],{"class":572},"execution",[134,9210,9135],{"class":140},[134,9212,9213,9216,9219,9221,9224,9226,9228],{"class":136,"line":264},[134,9214,9215],{"class":140},"            \u003C",[134,9217,9218],{"class":572},"id",[134,9220,9146],{"class":140},[134,9222,9223],{"class":2092},"build-image",[134,9225,9152],{"class":140},[134,9227,9218],{"class":572},[134,9229,9135],{"class":140},[134,9231,9232,9234,9237,9239,9242,9244,9246],{"class":136,"line":296},[134,9233,9215],{"class":140},[134,9235,9236],{"class":572},"phase",[134,9238,9146],{"class":140},[134,9240,9241],{"class":2092},"package",[134,9243,9152],{"class":140},[134,9245,9236],{"class":572},[134,9247,9135],{"class":140},[134,9249,9250,9252,9255],{"class":136,"line":311},[134,9251,9215],{"class":140},[134,9253,9254],{"class":572},"goals",[134,9256,9135],{"class":140},[134,9258,9259,9262,9265,9267,9269,9271,9273],{"class":136,"line":317},[134,9260,9261],{"class":140},"                \u003C",[134,9263,9264],{"class":572},"goal",[134,9266,9146],{"class":140},[134,9268,4458],{"class":2092},[134,9270,9152],{"class":140},[134,9272,9264],{"class":572},[134,9274,9135],{"class":140},[134,9276,9277,9280,9282],{"class":136,"line":322},[134,9278,9279],{"class":140},"            \u003C\u002F",[134,9281,9254],{"class":572},[134,9283,9135],{"class":140},[134,9285,9286,9289,9291],{"class":136,"line":343},[134,9287,9288],{"class":140},"        \u003C\u002F",[134,9290,9208],{"class":572},[134,9292,9135],{"class":140},[134,9294,9295,9297,9299],{"class":136,"line":365},[134,9296,9205],{"class":140},[134,9298,9208],{"class":572},[134,9300,9135],{"class":140},[134,9302,9303,9305,9307,9309,9312,9314,9316],{"class":136,"line":385},[134,9304,9215],{"class":140},[134,9306,9218],{"class":572},[134,9308,9146],{"class":140},[134,9310,9311],{"class":2092},"tag-image-version",[134,9313,9152],{"class":140},[134,9315,9218],{"class":572},[134,9317,9135],{"class":140},[134,9319,9320,9322,9324,9326,9328,9330,9332],{"class":136,"line":390},[134,9321,9215],{"class":140},[134,9323,9236],{"class":572},[134,9325,9146],{"class":140},[134,9327,651],{"class":2092},[134,9329,9152],{"class":140},[134,9331,9236],{"class":572},[134,9333,9135],{"class":140},[134,9335,9336,9338,9340],{"class":136,"line":395},[134,9337,9215],{"class":140},[134,9339,9254],{"class":572},[134,9341,9135],{"class":140},[134,9343,9344,9346,9348,9350,9352,9354,9356],{"class":136,"line":416},[134,9345,9261],{"class":140},[134,9347,9264],{"class":572},[134,9349,9146],{"class":140},[134,9351,4647],{"class":2092},[134,9353,9152],{"class":140},[134,9355,9264],{"class":572},[134,9357,9135],{"class":140},[134,9359,9360,9362,9364,9366,9369,9371,9373],{"class":136,"line":448},[134,9361,9261],{"class":140},[134,9363,9264],{"class":572},[134,9365,9146],{"class":140},[134,9367,9368],{"class":2092},"push",[134,9370,9152],{"class":140},[134,9372,9264],{"class":572},[134,9374,9135],{"class":140},[134,9376,9377,9379,9381],{"class":136,"line":465},[134,9378,9279],{"class":140},[134,9380,9254],{"class":572},[134,9382,9135],{"class":140},[134,9384,9385,9387,9390],{"class":136,"line":489},[134,9386,9215],{"class":140},[134,9388,9389],{"class":572},"configuration",[134,9391,9135],{"class":140},[134,9393,9394,9396,9398,9400,9403,9405,9407],{"class":136,"line":495},[134,9395,9261],{"class":140},[134,9397,4647],{"class":572},[134,9399,9146],{"class":140},[134,9401,9402],{"class":2092},"${project.version}",[134,9404,9152],{"class":140},[134,9406,4647],{"class":572},[134,9408,9135],{"class":140},[134,9410,9411,9413,9415],{"class":136,"line":501},[134,9412,9279],{"class":140},[134,9414,9389],{"class":572},[134,9416,9135],{"class":140},[134,9418,9419,9421,9423],{"class":136,"line":522},[134,9420,9288],{"class":140},[134,9422,9208],{"class":572},[134,9424,9135],{"class":140},[134,9426,9427,9429,9431],{"class":136,"line":541},[134,9428,9205],{"class":140},[134,9430,9208],{"class":572},[134,9432,9135],{"class":140},[134,9434,9435,9437,9439,9441,9444,9446,9448],{"class":136,"line":736},[134,9436,9215],{"class":140},[134,9438,9218],{"class":572},[134,9440,9146],{"class":140},[134,9442,9443],{"class":2092},"tag-image-latest",[134,9445,9152],{"class":140},[134,9447,9218],{"class":572},[134,9449,9135],{"class":140},[134,9451,9452,9454,9456,9458,9460,9462,9464],{"class":136,"line":742},[134,9453,9215],{"class":140},[134,9455,9236],{"class":572},[134,9457,9146],{"class":140},[134,9459,651],{"class":2092},[134,9461,9152],{"class":140},[134,9463,9236],{"class":572},[134,9465,9135],{"class":140},[134,9467,9468,9470,9472],{"class":136,"line":748},[134,9469,9215],{"class":140},[134,9471,9254],{"class":572},[134,9473,9135],{"class":140},[134,9475,9476,9478,9480,9482,9484,9486,9488],{"class":136,"line":754},[134,9477,9261],{"class":140},[134,9479,9264],{"class":572},[134,9481,9146],{"class":140},[134,9483,4647],{"class":2092},[134,9485,9152],{"class":140},[134,9487,9264],{"class":572},[134,9489,9135],{"class":140},[134,9491,9492,9494,9496,9498,9500,9502,9504],{"class":136,"line":760},[134,9493,9261],{"class":140},[134,9495,9264],{"class":572},[134,9497,9146],{"class":140},[134,9499,9368],{"class":2092},[134,9501,9152],{"class":140},[134,9503,9264],{"class":572},[134,9505,9135],{"class":140},[134,9507,9508,9510,9512],{"class":136,"line":766},[134,9509,9279],{"class":140},[134,9511,9254],{"class":572},[134,9513,9135],{"class":140},[134,9515,9516,9518,9520],{"class":136,"line":772},[134,9517,9215],{"class":140},[134,9519,9389],{"class":572},[134,9521,9135],{"class":140},[134,9523,9524,9526,9528,9530,9532,9534,9536],{"class":136,"line":778},[134,9525,9261],{"class":140},[134,9527,4647],{"class":572},[134,9529,9146],{"class":140},[134,9531,9114],{"class":2092},[134,9533,9152],{"class":140},[134,9535,4647],{"class":572},[134,9537,9135],{"class":140},[134,9539,9540,9542,9544],{"class":136,"line":784},[134,9541,9279],{"class":140},[134,9543,9389],{"class":572},[134,9545,9135],{"class":140},[134,9547,9548,9550,9552],{"class":136,"line":789},[134,9549,9288],{"class":140},[134,9551,9208],{"class":572},[134,9553,9135],{"class":140},[134,9555,9556,9559,9561],{"class":136,"line":795},[134,9557,9558],{"class":140},"    \u003C\u002F",[134,9560,9198],{"class":572},[134,9562,9135],{"class":140},[134,9564,9565,9567,9569],{"class":136,"line":801},[134,9566,9140],{"class":140},[134,9568,9389],{"class":572},[134,9570,9135],{"class":140},[134,9572,9573,9575,9578,9580,9583,9585,9587],{"class":136,"line":807},[134,9574,9205],{"class":140},[134,9576,9577],{"class":572},"repository",[134,9579,9146],{"class":140},[134,9581,9582],{"class":2092},"registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002F${project.artifactId}",[134,9584,9152],{"class":140},[134,9586,9577],{"class":572},[134,9588,9135],{"class":140},[134,9590,9591,9593,9595,9597,9599,9601,9603],{"class":136,"line":813},[134,9592,9205],{"class":140},[134,9594,4647],{"class":572},[134,9596,9146],{"class":140},[134,9598,9402],{"class":2092},[134,9600,9152],{"class":140},[134,9602,4647],{"class":572},[134,9604,9135],{"class":140},[134,9606,9607,9609,9612,9614,9616,9618,9620],{"class":136,"line":818},[134,9608,9205],{"class":140},[134,9610,9611],{"class":572},"useMavenSettingsForAuth",[134,9613,9146],{"class":140},[134,9615,69],{"class":2092},[134,9617,9152],{"class":140},[134,9619,9611],{"class":572},[134,9621,9135],{"class":140},[134,9623,9624,9626,9629],{"class":136,"line":823},[134,9625,9205],{"class":140},[134,9627,9628],{"class":572},"buildArgs",[134,9630,9135],{"class":140},[134,9632,9633,9635,9638,9640,9643,9645,9647],{"class":136,"line":829},[134,9634,9215],{"class":140},[134,9636,9637],{"class":572},"JAR_FILE",[134,9639,9146],{"class":140},[134,9641,9642],{"class":2092},"target\u002F${project.build.finalName}.jar",[134,9644,9152],{"class":140},[134,9646,9637],{"class":572},[134,9648,9135],{"class":140},[134,9650,9651,9653,9655],{"class":136,"line":835},[134,9652,9288],{"class":140},[134,9654,9628],{"class":572},[134,9656,9135],{"class":140},[134,9658,9659,9661,9663],{"class":136,"line":841},[134,9660,9558],{"class":140},[134,9662,9389],{"class":572},[134,9664,9135],{"class":140},[134,9666,9667,9669,9671],{"class":136,"line":847},[134,9668,9152],{"class":140},[134,9670,9110],{"class":572},[134,9672,9135],{"class":140},[28,9674,9675,9676,9679],{},"想要直接用",[75,9677,9678],{},"mvn deploy","完成整个部署的话，还需要加一下 Nexus 的发布配置",[86,9681,9683],{"className":9121,"code":9682,"language":9123,"meta":94,"style":94},"\u003CdistributionManagement>\n    \u003Crepository>\n        \u003Cid>monkey-run-maven-release\u003C\u002Fid>\n        \u003Cname>MonkeyRun Maven Release Repository\u003C\u002Fname>\n        \u003Curl>你的nexus仓库地址\u003C\u002Furl>\n    \u003C\u002Frepository>\n\u003C\u002FdistributionManagement>\n",[75,9684,9685,9694,9702,9719,9736,9754,9762],{"__ignoreMap":94},[134,9686,9687,9689,9692],{"class":136,"line":137},[134,9688,9130],{"class":140},[134,9690,9691],{"class":572},"distributionManagement",[134,9693,9135],{"class":140},[134,9695,9696,9698,9700],{"class":136,"line":144},[134,9697,9140],{"class":140},[134,9699,9577],{"class":572},[134,9701,9135],{"class":140},[134,9703,9704,9706,9708,9710,9713,9715,9717],{"class":136,"line":174},[134,9705,9205],{"class":140},[134,9707,9218],{"class":572},[134,9709,9146],{"class":140},[134,9711,9712],{"class":2092},"monkey-run-maven-release",[134,9714,9152],{"class":140},[134,9716,9218],{"class":572},[134,9718,9135],{"class":140},[134,9720,9721,9723,9725,9727,9730,9732,9734],{"class":136,"line":218},[134,9722,9205],{"class":140},[134,9724,2579],{"class":572},[134,9726,9146],{"class":140},[134,9728,9729],{"class":2092},"MonkeyRun Maven Release Repository",[134,9731,9152],{"class":140},[134,9733,2579],{"class":572},[134,9735,9135],{"class":140},[134,9737,9738,9740,9743,9745,9748,9750,9752],{"class":136,"line":233},[134,9739,9205],{"class":140},[134,9741,9742],{"class":572},"url",[134,9744,9146],{"class":140},[134,9746,9747],{"class":2092},"你的nexus仓库地址",[134,9749,9152],{"class":140},[134,9751,9742],{"class":572},[134,9753,9135],{"class":140},[134,9755,9756,9758,9760],{"class":136,"line":239},[134,9757,9558],{"class":140},[134,9759,9577],{"class":572},[134,9761,9135],{"class":140},[134,9763,9764,9766,9768],{"class":136,"line":264},[134,9765,9152],{"class":140},[134,9767,9691],{"class":572},[134,9769,9135],{"class":140},[28,9771,9772,9773,9775],{},"阿里云容器上配置好镜像更新重新部署的触发器，之后就是直接",[75,9774,9678],{},"等编译好上传完就发布完成啦！",[1932,9777,9778],{},"html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sQzsp, html code.shiki .sQzsp{--shiki-light:#E53935;--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sw1J6, html code.shiki .sw1J6{--shiki-light:#F76D47;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}",{"title":94,"searchDepth":144,"depth":144,"links":9780},[],"2018-04-12","Docker 用了很久了，之前 Maven 项目一直用的docker-maven-plugin，但是作者目前已经不推荐使用这种方式了，该项目已经不再更新功能，只提供 bugfix。他们的新项目叫做dockerfile-maven，配置上有些不同，之前一直没时间去更新，最近的一个项目中，采用了最新的插件，中间也踩过不少坑，刚刚终于都搞定了，记录一下。",{},"\u002Fposts\u002F2018\u002Fdockerfile-maven",{"text":1992,"minutes":9786,"time":9787,"words":9788},1.81,108600,362,{"title":8973,"description":9782},{"loc":9784},"posts\u002F2018\u002F20180412.dockerfile-maven",[1954,1957,1955],"天气小雨🌦","cvFl_OQs7LBBHpi45-gE5m-7kKeSxsJyCWuH8fPZRM8",{"id":9796,"title":9797,"body":9798,"class":1939,"cover":1939,"coverSize":1939,"date":9847,"description":9848,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":9849,"navigation":596,"path":9850,"readingTime":9851,"seo":9855,"sitemap":9856,"stem":9857,"tags":9858,"time":1939,"weather":1939,"__hash__":9859},"posts\u002Fposts\u002F2016\u002F20161109.aliyun-cdn-not-support-sni.md","解决阿里云 CDN 回源 https 返回 503 错误的问题",{"type":25,"value":9799,"toc":9845},[9800,9815,9822,9825,9828,9831,9839,9842],[28,9801,9802,9803,9808,9809,9814],{},"最近打算把",[2697,9804,9807],{"href":9805,"rel":9806},"https:\u002F\u002Fwww.monkeyrun.net",[2701],"www.monkeyrun.net","改成全站 https，使用的",[2697,9810,9813],{"href":9811,"rel":9812},"https:\u002F\u002Fletsencrypt.org\u002F",[2701],"Let’s Encrypt","的证书。然而在设置阿里云 CDN 的时候，阿里云 CDN 回源一直返回 503 错误，发工单，来来回回经过整整两天，终于把问题解决。容我娓娓道来。",[28,9816,9817,9818,9821],{},"最一开始，我先开启了阿里云的 CDN，源站设置为",[2697,9819,9807],{"href":9805,"rel":9820},[2701],"，通过 80 端口回源，没有任何问题。",[28,9823,9824],{},"后来当时配置好证书，站点也开启了 https 之后，将回源端口改为 443，开始出问题了，CDN 资源全部返回 503。而直接通过浏览器访问 https 的源站内容，都是没有问题的。",[28,9826,9827],{},"发工单，经过漫长的等待和提供链接等更详细的信息之后，阿里云的工作人员首先认为这个问题可能是由于我开启了防火墙或者一些安全软件导致，拦截或阻止了 CDN 节点的回源请求。我关闭了防火墙，问题依旧存在。",[28,9829,9830],{},"又经过漫长的等待以及转交专项处理人员处理之后，给我发了个抓的包，说是 CDN 回源请求被源站给 RST 了，让我检查我的服务器在网络层面是不是做了什么限制。看了半天抓包的数据，也不大看得懂，各种谷歌，最后感觉可能是协议不同，握手的时候有一个是 TLS 1.0，有一个是 TLS 1.2，谷歌了一通，被带入了另一个未知领域，尝试了各种 cipher suites，随后还是无果。",[28,9832,9833,9834,9838],{},"后来找到一个网站，测试 SSL 兼容性的，",[2697,9835,9836],{"href":9836,"rel":9837},"https:\u002F\u002Fwww.ssllabs.com\u002Fssltest\u002F",[2701],"，测试了一下网站 SSL 兼容性，发现不支持 SNI 的请求会直接 close connection。于是又问阿里工作人员，得知他们 CDN 回源时，SSL 握手不支持发送 SNI。",[28,9840,9841],{},"定位到问题了，在 IIS 站点里面，编辑网站绑定，取消勾选“需要服务器名称指示”，问题解决！",[28,9843,9844],{},"可以愉快的开启全站 https 了！",{"title":94,"searchDepth":144,"depth":144,"links":9846},[],"2016-11-09","最近打算把www.monkeyrun.net改成全站 https，使用的Let’s Encrypt的证书。然而在设置阿里云 CDN 的时候，阿里云 CDN 回源一直返回 503 错误，发工单，来来回回经过整整两天，终于把问题解决。容我娓娓道来。",{},"\u002Fposts\u002F2016\u002Faliyun-cdn-not-support-sni",{"text":5705,"minutes":9852,"time":9853,"words":9854},2.565,153900,513,{"title":9797,"description":9848},{"loc":9850},"posts\u002F2016\u002F20161109.aliyun-cdn-not-support-sni",[1954,1955,7678],"nHBSr5cQSJtMEotBzDat8oOGoidlnu2hPDoy8mW_Blo",1777579134115]