[{"data":1,"prerenderedAt":5727},["ShallowReactive",2],{"navigation":3,"posts-undefined-Docker-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,3950,4115,4152,4764,5597],{"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":3933,"coverSize":1939,"date":3934,"description":3935,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":3936,"navigation":596,"path":3937,"readingTime":3938,"seo":3943,"sitemap":3944,"stem":3945,"tags":3946,"time":1939,"weather":3948,"__hash__":3949},"posts\u002Fposts\u002F2020\u002F20200227.k8s-cert-manager-tls.md","k8s 上利用 cert-manager 自动签发 TLS 证书",{"type":25,"value":1963,"toc":3931},[1964,1971,1978,1988,1996,2005,2010,2017,2039,2042,2146,2151,2180,2186,2271,2275,2361,2365,2388,2543,2548,2566,2569,2605,2608,2615,2755,2759,2777,2780,2826,2840,2847,2855,3312,3335,3353,3362,3365,3371,3374,3378,3381,3419,3464,3469,3534,3542,3547,3758,3762,3778,3780,3810,3813,3816,3862,3880,3891,3928],[28,1965,1966,1967,1970],{},"很多博主的 ",[75,1968,1969],{},"https"," 证书经常容易忘记更新，虽说证书过期前都会有邮件提醒，但是万一确实忙得没时间去处理，忘记了，就会出现证书过期的情况了。",[28,1972,1973,1974,1977],{},"之前在服务器上自己搭博客服务的时候，用 ",[75,1975,1976],{},"Let's Encrypt"," 来自动创建并续签证书，确实省了不少事。",[28,1979,1980,1981,1984,1985,1987],{},"在我的博客部署到 ",[75,1982,1983],{},"k8s"," 之后，就一直用的一年一签的免费证书，每年更新一次，也不算特别麻烦，但是总归不够高端，我又怀念起了 ",[75,1986,1976],{},"。",[28,1989,1990,1992,1993,1995],{},[75,1991,1976],{}," 是个好东西，",[75,1994,1983],{}," 也是个好东西，两个好东西怎么结合呢？搜寻了一番确实有方案，经过几天的尝试，终于弄好了。花了几天是因为第一天因为有个粗心导致的问题，导致搞了好久没成功，休息了几天再次尝试，才找到问题。",[28,1997,1998,1999,2001,2002,2004],{},"有关 ",[75,2000,1983],{}," 的基础知识，这里不做赘述，网上教程很多，这里假设大家对 ",[75,2003,1983],{}," 都有一定了解。",[2006,2007,2009],"h4",{"id":2008},"安装-cert-manager","安装 cert-manager",[28,2011,2012,2013,2016],{},"安装 ",[75,2014,2015],{},"helm"," 到本地",[86,2018,2022],{"className":2019,"code":2020,"language":2021,"meta":94,"style":94},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","$ brew install helm\n","bash",[75,2023,2024],{"__ignoreMap":94},[134,2025,2026,2030,2033,2036],{"class":136,"line":137},[134,2027,2029],{"class":2028},"sbgvK","$",[134,2031,2032],{"class":165}," brew",[134,2034,2035],{"class":165}," install",[134,2037,2038],{"class":165}," helm\n",[28,2040,2041],{},"添加仓库和命名空间",[86,2043,2045],{"className":2019,"code":2044,"language":2021,"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,2046,2047,2067,2088,2110,2132],{"__ignoreMap":94},[134,2048,2049,2051,2054,2057,2060,2063],{"class":136,"line":137},[134,2050,2029],{"class":2028},[134,2052,2053],{"class":165}," kubectl",[134,2055,2056],{"class":165}," create",[134,2058,2059],{"class":165}," namespace",[134,2061,2062],{"class":165}," cert-manager",[134,2064,2066],{"class":2065},"sutJx"," # 创建 cert-manager 命名空间\n",[134,2068,2069,2071,2073,2076,2078,2080,2083,2085],{"class":136,"line":144},[134,2070,2029],{"class":2028},[134,2072,2053],{"class":165},[134,2074,2075],{"class":165}," label",[134,2077,2059],{"class":165},[134,2079,2062],{"class":165},[134,2081,2082],{"class":165}," certmanager.io\u002Fdisable-validation=",[134,2084,69],{"class":147},[134,2086,2087],{"class":2065}," # 标记 cert-manager 命名空间以禁用资源验证\n",[134,2089,2090,2092,2094,2097,2101,2104,2107],{"class":136,"line":174},[134,2091,2029],{"class":2028},[134,2093,2053],{"class":165},[134,2095,2096],{"class":165}," apply",[134,2098,2100],{"class":2099},"stzsN"," --validate=false",[134,2102,2103],{"class":2099}," -f",[134,2105,2106],{"class":165}," https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml",[134,2108,2109],{"class":2065}," # 安装 CustomResourceDefinition 资源，注意 k8s 版本低于 1.15 需要用 legacy 版本\n",[134,2111,2112,2114,2117,2120,2123,2126,2129],{"class":136,"line":218},[134,2113,2029],{"class":2028},[134,2115,2116],{"class":165}," helm",[134,2118,2119],{"class":165}," repo",[134,2121,2122],{"class":165}," add",[134,2124,2125],{"class":165}," jetstack",[134,2127,2128],{"class":165}," https:\u002F\u002Fcharts.jetstack.io",[134,2130,2131],{"class":2065}," # 添加 Jetstack Helm repository\n",[134,2133,2134,2136,2138,2140,2143],{"class":136,"line":233},[134,2135,2029],{"class":2028},[134,2137,2116],{"class":165},[134,2139,2119],{"class":165},[134,2141,2142],{"class":165}," update",[134,2144,2145],{"class":2065}," # 更新本地 Helm chart repository\n",[28,2147,2012,2148],{},[75,2149,2150],{},"cert-manager",[86,2152,2154],{"className":2019,"code":2153,"language":2021,"meta":94,"style":94},"$ helm install cert-manager --namespace cert-manager --version v0.14.1 jetstack\u002Fcert-manager\n",[75,2155,2156],{"__ignoreMap":94},[134,2157,2158,2160,2162,2164,2166,2169,2171,2174,2177],{"class":136,"line":137},[134,2159,2029],{"class":2028},[134,2161,2116],{"class":165},[134,2163,2035],{"class":165},[134,2165,2062],{"class":165},[134,2167,2168],{"class":2099}," --namespace",[134,2170,2062],{"class":165},[134,2172,2173],{"class":2099}," --version",[134,2175,2176],{"class":165}," v0.14.1",[134,2178,2179],{"class":165}," jetstack\u002Fcert-manager\n",[28,2181,2182,2183,2185],{},"查看 ",[75,2184,2150],{}," 安装情况",[86,2187,2189],{"className":2019,"code":2188,"language":2021,"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,2190,2191,2208,2225,2243,2257],{"__ignoreMap":94},[134,2192,2193,2195,2197,2200,2203,2205],{"class":136,"line":137},[134,2194,2029],{"class":2028},[134,2196,2053],{"class":165},[134,2198,2199],{"class":165}," get",[134,2201,2202],{"class":165}," pods",[134,2204,2168],{"class":2099},[134,2206,2207],{"class":165}," cert-manager\n",[134,2209,2210,2213,2216,2219,2222],{"class":136,"line":144},[134,2211,2212],{"class":2028},"NAME",[134,2214,2215],{"class":165},"                                       READY",[134,2217,2218],{"class":165},"   STATUS",[134,2220,2221],{"class":165},"    RESTARTS",[134,2223,2224],{"class":165},"   AGE\n",[134,2226,2227,2230,2233,2236,2240],{"class":136,"line":174},[134,2228,2229],{"class":2028},"cert-manager-6cff8dc7b9-8vxws",[134,2231,2232],{"class":165},"              1\u002F1",[134,2234,2235],{"class":165},"     Running",[134,2237,2239],{"class":2238},"srdBf","   0",[134,2241,2242],{"class":165},"          4d10h\n",[134,2244,2245,2248,2251,2253,2255],{"class":136,"line":218},[134,2246,2247],{"class":2028},"cert-manager-cainjector-795c46858f-txczb",[134,2249,2250],{"class":165},"   1\u002F1",[134,2252,2235],{"class":165},[134,2254,2239],{"class":2238},[134,2256,2242],{"class":165},[134,2258,2259,2262,2265,2267,2269],{"class":136,"line":233},[134,2260,2261],{"class":2028},"cert-manager-webhook-5dfc77cd74-skgsv",[134,2263,2264],{"class":165},"      1\u002F1",[134,2266,2235],{"class":165},[134,2268,2239],{"class":2238},[134,2270,2242],{"class":165},[2006,2272,2274],{"id":2273},"更新-cert-manager","更新 cert-manager",[86,2276,2278],{"className":2019,"code":2277,"language":2021,"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,2279,2280,2305,2309,2324,2328,2339],{"__ignoreMap":94},[134,2281,2282,2284,2286,2289,2292,2294,2297,2299,2302],{"class":136,"line":137},[134,2283,2029],{"class":2028},[134,2285,2053],{"class":165},[134,2287,2288],{"class":165}," delete",[134,2290,2291],{"class":2099}," -n",[134,2293,2062],{"class":165},[134,2295,2296],{"class":165}," deployment",[134,2298,2062],{"class":165},[134,2300,2301],{"class":165}," cert-manager-cainjector",[134,2303,2304],{"class":165}," cert-manager-webhook\n",[134,2306,2307],{"class":136,"line":144},[134,2308,597],{"emptyLinePlaceholder":596},[134,2310,2311,2313,2315,2317,2319,2321],{"class":136,"line":174},[134,2312,2029],{"class":2028},[134,2314,2053],{"class":165},[134,2316,2096],{"class":165},[134,2318,2100],{"class":2099},[134,2320,2103],{"class":2099},[134,2322,2323],{"class":165}," https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Freleases\u002Fdownload\u002Fv0.14.1\u002Fcert-manager-legacy.crds.yaml\n",[134,2325,2326],{"class":136,"line":218},[134,2327,597],{"emptyLinePlaceholder":596},[134,2329,2330,2332,2334,2336],{"class":136,"line":233},[134,2331,2029],{"class":2028},[134,2333,2116],{"class":165},[134,2335,2119],{"class":165},[134,2337,2338],{"class":165}," update\n",[134,2340,2341,2343,2345,2348,2350,2352,2354,2357,2359],{"class":136,"line":239},[134,2342,2029],{"class":2028},[134,2344,2116],{"class":165},[134,2346,2347],{"class":165}," upgrade",[134,2349,2173],{"class":2099},[134,2351,2176],{"class":165},[134,2353,2062],{"class":165},[134,2355,2356],{"class":165}," jetstack\u002Fcert-manager",[134,2358,2291],{"class":2099},[134,2360,2207],{"class":165},[2006,2362,2364],{"id":2363},"创建-clusterissuer","创建 ClusterIssuer",[28,2366,2367,2368,2370,2371,104,2374,2377,2378,2380,2381,2383,2384,2387],{},"我们需要创建一个签发机构，",[75,2369,2150],{}," 提供了",[75,2372,2373],{},"Issuer",[75,2375,2376],{},"ClusterIssuer"," 两种类型的签发机构，",[75,2379,2373],{}," 只能用来签发自己所在命名空间下的证书，ClusterIssuer 可以签发任意命名空间下的证书，我这里用 ",[75,2382,2376],{}," 为例，创建 ",[75,2385,2386],{},"letsencrypt-prod.yaml"," 文件：",[86,2389,2391],{"className":563,"code":2390,"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,2392,2393,2403,2413,2420,2427,2437,2450,2457,2464,2477,2484,2494,2501,2511,2518,2530],{"__ignoreMap":94},[134,2394,2395,2398,2400],{"class":136,"line":137},[134,2396,2397],{"class":572},"apiVersion",[134,2399,158],{"class":140},[134,2401,2402],{"class":165}," cert-manager.io\u002Fv1alpha2\n",[134,2404,2405,2408,2410],{"class":136,"line":144},[134,2406,2407],{"class":572},"kind",[134,2409,158],{"class":140},[134,2411,2412],{"class":165}," ClusterIssuer\n",[134,2414,2415,2418],{"class":136,"line":174},[134,2416,2417],{"class":572},"metadata",[134,2419,576],{"class":140},[134,2421,2422,2425],{"class":136,"line":218},[134,2423,2424],{"class":572},"  labels",[134,2426,576],{"class":140},[134,2428,2429,2432,2434],{"class":136,"line":233},[134,2430,2431],{"class":572},"    name",[134,2433,158],{"class":140},[134,2435,2436],{"class":165}," letsencrypt-prod\n",[134,2438,2439,2442,2444,2447],{"class":136,"line":239},[134,2440,2441],{"class":572},"  name",[134,2443,158],{"class":140},[134,2445,2446],{"class":165}," letsencrypt-prod",[134,2448,2449],{"class":2065}," # 自定义的签发机构名称，后面会引用\n",[134,2451,2452,2455],{"class":136,"line":264},[134,2453,2454],{"class":572},"spec",[134,2456,576],{"class":140},[134,2458,2459,2462],{"class":136,"line":296},[134,2460,2461],{"class":572},"  acme",[134,2463,576],{"class":140},[134,2465,2466,2469,2471,2474],{"class":136,"line":311},[134,2467,2468],{"class":572},"    email",[134,2470,158],{"class":140},[134,2472,2473],{"class":165}," yourname@youremail.com",[134,2475,2476],{"class":2065}," # 你的邮箱，证书快过期的时候会邮件提醒，不过我们可以设置自动续期\n",[134,2478,2479,2482],{"class":136,"line":317},[134,2480,2481],{"class":572},"    solvers",[134,2483,576],{"class":140},[134,2485,2486,2489,2492],{"class":136,"line":322},[134,2487,2488],{"class":140},"      -",[134,2490,2491],{"class":572}," http01",[134,2493,576],{"class":140},[134,2495,2496,2499],{"class":136,"line":343},[134,2497,2498],{"class":572},"          ingress",[134,2500,576],{"class":140},[134,2502,2503,2506,2508],{"class":136,"line":365},[134,2504,2505],{"class":572},"            class",[134,2507,158],{"class":140},[134,2509,2510],{"class":165}," nginx\n",[134,2512,2513,2516],{"class":136,"line":385},[134,2514,2515],{"class":572},"    privateKeySecretRef",[134,2517,576],{"class":140},[134,2519,2520,2523,2525,2527],{"class":136,"line":390},[134,2521,2522],{"class":572},"      name",[134,2524,158],{"class":140},[134,2526,2446],{"class":165},[134,2528,2529],{"class":2065}," # 指示此签发机构的私钥将要存储到哪个 Secret 对象中\n",[134,2531,2532,2535,2537,2540],{"class":136,"line":395},[134,2533,2534],{"class":572},"    server",[134,2536,158],{"class":140},[134,2538,2539],{"class":165}," https:\u002F\u002Facme-v02.api.letsencrypt.org\u002Fdirectory",[134,2541,2542],{"class":2065}," # acme 协议的服务端，我们用 Let's Encrypt\n",[28,2544,2545,2546],{},"应用 ",[75,2547,565],{},[86,2549,2551],{"className":2019,"code":2550,"language":2021,"meta":94,"style":94},"$ kubectl create -f letsencrypt-prod.yaml\n",[75,2552,2553],{"__ignoreMap":94},[134,2554,2555,2557,2559,2561,2563],{"class":136,"line":137},[134,2556,2029],{"class":2028},[134,2558,2053],{"class":165},[134,2560,2056],{"class":165},[134,2562,2103],{"class":2099},[134,2564,2565],{"class":165}," letsencrypt-prod.yaml\n",[28,2567,2568],{},"查看状态",[86,2570,2572],{"className":2019,"code":2571,"language":2021,"meta":94,"style":94},"$ kubectl get clusterissuer\nNAME               READY   AGE\nletsencrypt-prod   True    51s\n",[75,2573,2574,2585,2594],{"__ignoreMap":94},[134,2575,2576,2578,2580,2582],{"class":136,"line":137},[134,2577,2029],{"class":2028},[134,2579,2053],{"class":165},[134,2581,2199],{"class":165},[134,2583,2584],{"class":165}," clusterissuer\n",[134,2586,2587,2589,2592],{"class":136,"line":144},[134,2588,2212],{"class":2028},[134,2590,2591],{"class":165},"               READY",[134,2593,2224],{"class":165},[134,2595,2596,2599,2602],{"class":136,"line":174},[134,2597,2598],{"class":2028},"letsencrypt-prod",[134,2600,2601],{"class":165},"   True",[134,2603,2604],{"class":165},"    51s\n",[2006,2606,2607],{"id":2607},"手动签发证书",[28,2609,2610,2611,2614],{},"手动签发证书，创建 ",[75,2612,2613],{},"test-monkeyrun-net-cert.yaml"," 文件",[86,2616,2618],{"className":563,"code":2617,"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,2619,2620,2628,2637,2643,2652,2662,2668,2681,2694,2707,2714,2721,2728,2736,2745],{"__ignoreMap":94},[134,2621,2622,2624,2626],{"class":136,"line":137},[134,2623,2397],{"class":572},[134,2625,158],{"class":140},[134,2627,2402],{"class":165},[134,2629,2630,2632,2634],{"class":136,"line":144},[134,2631,2407],{"class":572},[134,2633,158],{"class":140},[134,2635,2636],{"class":165}," Certificate\n",[134,2638,2639,2641],{"class":136,"line":174},[134,2640,2417],{"class":572},[134,2642,576],{"class":140},[134,2644,2645,2647,2649],{"class":136,"line":218},[134,2646,2441],{"class":572},[134,2648,158],{"class":140},[134,2650,2651],{"class":165}," test-monkeyrun-net-cert\n",[134,2653,2654,2657,2659],{"class":136,"line":233},[134,2655,2656],{"class":572},"  namespace",[134,2658,158],{"class":140},[134,2660,2661],{"class":165}," test\n",[134,2663,2664,2666],{"class":136,"line":239},[134,2665,2454],{"class":572},[134,2667,576],{"class":140},[134,2669,2670,2673,2675,2678],{"class":136,"line":264},[134,2671,2672],{"class":572},"  secretName",[134,2674,158],{"class":140},[134,2676,2677],{"class":165}," tls-test-monkeyrun-net",[134,2679,2680],{"class":2065}," # 证书保存的 secret 名\n",[134,2682,2683,2686,2688,2691],{"class":136,"line":296},[134,2684,2685],{"class":572},"  duration",[134,2687,158],{"class":140},[134,2689,2690],{"class":165}," 2160h",[134,2692,2693],{"class":2065}," # 90d\n",[134,2695,2696,2699,2701,2704],{"class":136,"line":311},[134,2697,2698],{"class":572},"  renewBefore",[134,2700,158],{"class":140},[134,2702,2703],{"class":165}," 720h",[134,2705,2706],{"class":2065}," # 30d\n",[134,2708,2709,2712],{"class":136,"line":317},[134,2710,2711],{"class":572},"  dnsNames",[134,2713,576],{"class":140},[134,2715,2716,2718],{"class":136,"line":322},[134,2717,625],{"class":140},[134,2719,2720],{"class":165}," test.monkeyrun.net\n",[134,2722,2723,2726],{"class":136,"line":343},[134,2724,2725],{"class":572},"  issuerRef",[134,2727,576],{"class":140},[134,2729,2730,2732,2734],{"class":136,"line":365},[134,2731,2431],{"class":572},[134,2733,158],{"class":140},[134,2735,2436],{"class":165},[134,2737,2738,2741,2743],{"class":136,"line":385},[134,2739,2740],{"class":572},"    kind",[134,2742,158],{"class":140},[134,2744,2412],{"class":165},[134,2746,2747,2750,2752],{"class":136,"line":390},[134,2748,2749],{"class":572},"    group",[134,2751,158],{"class":140},[134,2753,2754],{"class":165}," cert-manager.io\n",[28,2756,2545,2757],{},[75,2758,565],{},[86,2760,2762],{"className":2019,"code":2761,"language":2021,"meta":94,"style":94},"$ kubectl apply -f test-monkeyrun-net-cert.yaml\n",[75,2763,2764],{"__ignoreMap":94},[134,2765,2766,2768,2770,2772,2774],{"class":136,"line":137},[134,2767,2029],{"class":2028},[134,2769,2053],{"class":165},[134,2771,2096],{"class":165},[134,2773,2103],{"class":2099},[134,2775,2776],{"class":165}," test-monkeyrun-net-cert.yaml\n",[28,2778,2779],{},"检查是否生成证书文件",[86,2781,2783],{"className":2019,"code":2782,"language":2021,"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,2784,2785,2800,2813],{"__ignoreMap":94},[134,2786,2787,2789,2791,2793,2796,2798],{"class":136,"line":137},[134,2788,2029],{"class":2028},[134,2790,2053],{"class":165},[134,2792,2199],{"class":165},[134,2794,2795],{"class":165}," certificate",[134,2797,2291],{"class":2099},[134,2799,2661],{"class":165},[134,2801,2802,2804,2807,2810],{"class":136,"line":144},[134,2803,2212],{"class":2028},[134,2805,2806],{"class":165},"                      READY",[134,2808,2809],{"class":165},"   SECRET",[134,2811,2812],{"class":165},"                   AGE\n",[134,2814,2815,2818,2820,2823],{"class":136,"line":174},[134,2816,2817],{"class":2028},"test-monkeyrun-net-cert",[134,2819,2601],{"class":165},[134,2821,2822],{"class":165},"    test-monkeyrun-net-tls",[134,2824,2825],{"class":165},"   99m\n",[28,2827,2828,2829,2832,2833,2836,2837,2839],{},"将该证书配置到 ",[75,2830,2831],{},"test.monkeyrun.net"," 的 ",[75,2834,2835],{},"ingress"," 上，测试 ",[75,2838,1969],{}," 访问，成功。",[2006,2841,2843],{"id":2842},"创建deployment时自动签发证书",[2844,2845,2846],"del",{},"创建Deployment时自动签发证书",[28,2848,2849],{},[2844,2850,2851,2852],{},"创建 ",[75,2853,2854],{},"test-nginx.yaml",[86,2856,2858],{"className":563,"code":2857,"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,2859,2860,2869,2878,2884,2893,2901,2907,2917,2924,2931,2938,2947,2954,2961,2973,2982,2989,3002,3007,3016,3025,3031,3039,3047,3053,3062,3068,3075,3086,3096,3105,3112,3121,3125,3133,3142,3148,3156,3164,3171,3180,3195,3204,3210,3217,3228,3235,3242,3252,3261,3270,3280,3287,3299,3306],{"__ignoreMap":94},[134,2861,2862,2864,2866],{"class":136,"line":137},[134,2863,2397],{"class":572},[134,2865,158],{"class":140},[134,2867,2868],{"class":165}," extensions\u002Fv1beta1\n",[134,2870,2871,2873,2875],{"class":136,"line":144},[134,2872,2407],{"class":572},[134,2874,158],{"class":140},[134,2876,2877],{"class":165}," Deployment\n",[134,2879,2880,2882],{"class":136,"line":174},[134,2881,2417],{"class":572},[134,2883,576],{"class":140},[134,2885,2886,2888,2890],{"class":136,"line":218},[134,2887,2441],{"class":572},[134,2889,158],{"class":140},[134,2891,2892],{"class":165}," test-nginx\n",[134,2894,2895,2897,2899],{"class":136,"line":233},[134,2896,2656],{"class":572},[134,2898,158],{"class":140},[134,2900,2661],{"class":165},[134,2902,2903,2905],{"class":136,"line":239},[134,2904,2454],{"class":572},[134,2906,576],{"class":140},[134,2908,2909,2912,2914],{"class":136,"line":264},[134,2910,2911],{"class":572},"  replicas",[134,2913,158],{"class":140},[134,2915,2916],{"class":2238}," 1\n",[134,2918,2919,2922],{"class":136,"line":296},[134,2920,2921],{"class":572},"  template",[134,2923,576],{"class":140},[134,2925,2926,2929],{"class":136,"line":311},[134,2927,2928],{"class":572},"    metadata",[134,2930,576],{"class":140},[134,2932,2933,2936],{"class":136,"line":317},[134,2934,2935],{"class":572},"      labels",[134,2937,576],{"class":140},[134,2939,2940,2943,2945],{"class":136,"line":322},[134,2941,2942],{"class":572},"        run",[134,2944,158],{"class":140},[134,2946,2892],{"class":165},[134,2948,2949,2952],{"class":136,"line":343},[134,2950,2951],{"class":572},"    spec",[134,2953,576],{"class":140},[134,2955,2956,2959],{"class":136,"line":365},[134,2957,2958],{"class":572},"      containers",[134,2960,576],{"class":140},[134,2962,2963,2966,2969,2971],{"class":136,"line":385},[134,2964,2965],{"class":140},"        -",[134,2967,2968],{"class":572}," name",[134,2970,158],{"class":140},[134,2972,2892],{"class":165},[134,2974,2975,2978,2980],{"class":136,"line":390},[134,2976,2977],{"class":572},"          image",[134,2979,158],{"class":140},[134,2981,2510],{"class":165},[134,2983,2984,2987],{"class":136,"line":395},[134,2985,2986],{"class":572},"          ports",[134,2988,576],{"class":140},[134,2990,2991,2994,2997,2999],{"class":136,"line":416},[134,2992,2993],{"class":140},"            -",[134,2995,2996],{"class":572}," containerPort",[134,2998,158],{"class":140},[134,3000,3001],{"class":2238}," 80\n",[134,3003,3004],{"class":136,"line":448},[134,3005,3006],{"class":2028},"---\n",[134,3008,3009,3011,3013],{"class":136,"line":465},[134,3010,2397],{"class":572},[134,3012,158],{"class":140},[134,3014,3015],{"class":165}," v1\n",[134,3017,3018,3020,3022],{"class":136,"line":489},[134,3019,2407],{"class":572},[134,3021,158],{"class":140},[134,3023,3024],{"class":165}," Service\n",[134,3026,3027,3029],{"class":136,"line":495},[134,3028,2417],{"class":572},[134,3030,576],{"class":140},[134,3032,3033,3035,3037],{"class":136,"line":501},[134,3034,2441],{"class":572},[134,3036,158],{"class":140},[134,3038,2892],{"class":165},[134,3040,3041,3043,3045],{"class":136,"line":522},[134,3042,2656],{"class":572},[134,3044,158],{"class":140},[134,3046,2661],{"class":165},[134,3048,3049,3051],{"class":136,"line":541},[134,3050,2424],{"class":572},[134,3052,576],{"class":140},[134,3054,3055,3058,3060],{"class":136,"line":736},[134,3056,3057],{"class":572},"    app",[134,3059,158],{"class":140},[134,3061,2892],{"class":165},[134,3063,3064,3066],{"class":136,"line":742},[134,3065,2454],{"class":572},[134,3067,576],{"class":140},[134,3069,3070,3073],{"class":136,"line":748},[134,3071,3072],{"class":572},"  ports",[134,3074,576],{"class":140},[134,3076,3077,3079,3082,3084],{"class":136,"line":754},[134,3078,625],{"class":140},[134,3080,3081],{"class":572}," port",[134,3083,158],{"class":140},[134,3085,3001],{"class":2238},[134,3087,3088,3091,3093],{"class":136,"line":760},[134,3089,3090],{"class":572},"      protocol",[134,3092,158],{"class":140},[134,3094,3095],{"class":165}," TCP\n",[134,3097,3098,3100,3102],{"class":136,"line":766},[134,3099,2522],{"class":572},[134,3101,158],{"class":140},[134,3103,3104],{"class":165}," http\n",[134,3106,3107,3110],{"class":136,"line":772},[134,3108,3109],{"class":572},"  selector",[134,3111,576],{"class":140},[134,3113,3114,3117,3119],{"class":136,"line":778},[134,3115,3116],{"class":572},"    run",[134,3118,158],{"class":140},[134,3120,2892],{"class":165},[134,3122,3123],{"class":136,"line":784},[134,3124,3006],{"class":2028},[134,3126,3127,3129,3131],{"class":136,"line":789},[134,3128,2397],{"class":572},[134,3130,158],{"class":140},[134,3132,2868],{"class":165},[134,3134,3135,3137,3139],{"class":136,"line":795},[134,3136,2407],{"class":572},[134,3138,158],{"class":140},[134,3140,3141],{"class":165}," Ingress\n",[134,3143,3144,3146],{"class":136,"line":801},[134,3145,2417],{"class":572},[134,3147,576],{"class":140},[134,3149,3150,3152,3154],{"class":136,"line":807},[134,3151,2441],{"class":572},[134,3153,158],{"class":140},[134,3155,2892],{"class":165},[134,3157,3158,3160,3162],{"class":136,"line":813},[134,3159,2656],{"class":572},[134,3161,158],{"class":140},[134,3163,2661],{"class":165},[134,3165,3166,3169],{"class":136,"line":818},[134,3167,3168],{"class":572},"  annotations",[134,3170,576],{"class":140},[134,3172,3173,3176,3178],{"class":136,"line":823},[134,3174,3175],{"class":572},"    kubernetes.io\u002Fingress.class",[134,3177,158],{"class":140},[134,3179,2510],{"class":165},[134,3181,3182,3185,3187,3190,3192],{"class":136,"line":829},[134,3183,3184],{"class":572},"    kubernetes.io\u002Ftls-acme",[134,3186,158],{"class":140},[134,3188,3189],{"class":161}," '",[134,3191,69],{"class":165},[134,3193,3194],{"class":161},"'\n",[134,3196,3197,3200,3202],{"class":136,"line":835},[134,3198,3199],{"class":572},"    certmanager.io\u002Fcluster-issuer",[134,3201,158],{"class":140},[134,3203,2436],{"class":165},[134,3205,3206,3208],{"class":136,"line":841},[134,3207,2454],{"class":572},[134,3209,576],{"class":140},[134,3211,3212,3215],{"class":136,"line":847},[134,3213,3214],{"class":572},"  rules",[134,3216,576],{"class":140},[134,3218,3219,3221,3224,3226],{"class":136,"line":852},[134,3220,625],{"class":140},[134,3222,3223],{"class":572}," host",[134,3225,158],{"class":140},[134,3227,2720],{"class":165},[134,3229,3230,3233],{"class":136,"line":857},[134,3231,3232],{"class":572},"      http",[134,3234,576],{"class":140},[134,3236,3237,3240],{"class":136,"line":863},[134,3238,3239],{"class":572},"        paths",[134,3241,576],{"class":140},[134,3243,3244,3247,3250],{"class":136,"line":869},[134,3245,3246],{"class":140},"          -",[134,3248,3249],{"class":572}," backend",[134,3251,576],{"class":140},[134,3253,3254,3257,3259],{"class":136,"line":875},[134,3255,3256],{"class":572},"              serviceName",[134,3258,158],{"class":140},[134,3260,2892],{"class":165},[134,3262,3263,3266,3268],{"class":136,"line":881},[134,3264,3265],{"class":572},"              servicePort",[134,3267,158],{"class":140},[134,3269,3001],{"class":2238},[134,3271,3272,3275,3277],{"class":136,"line":887},[134,3273,3274],{"class":572},"            path",[134,3276,158],{"class":140},[134,3278,3279],{"class":165}," \u002F\n",[134,3281,3282,3285],{"class":136,"line":893},[134,3283,3284],{"class":572},"  tls",[134,3286,576],{"class":140},[134,3288,3289,3291,3294,3296],{"class":136,"line":898},[134,3290,625],{"class":140},[134,3292,3293],{"class":572}," secretName",[134,3295,158],{"class":140},[134,3297,3298],{"class":165}," tls-test-monkeyrun-net\n",[134,3300,3301,3304],{"class":136,"line":904},[134,3302,3303],{"class":572},"      hosts",[134,3305,576],{"class":140},[134,3307,3308,3310],{"class":136,"line":910},[134,3309,2965],{"class":140},[134,3311,2720],{"class":165},[28,3313,3314],{},[2844,3315,3316,3317,3320,3321,3324,3325,104,3328,3331,3332,3334],{},"删除之前手动创建的 ",[75,3318,3319],{},"Deployment","、",[75,3322,3323],{},"Service"," 、 ",[75,3326,3327],{},"Ingress",[75,3329,3330],{},"Secret"," 后， 应用 ",[75,3333,565],{}," 来自动创建",[86,3336,3338],{"className":2019,"code":3337,"language":2021,"meta":94,"style":94},"$ kubectl apply -f test-nginx.yaml\n",[75,3339,3340],{"__ignoreMap":94},[134,3341,3342,3344,3346,3348,3350],{"class":136,"line":137},[134,3343,2029],{"class":2028},[134,3345,2053],{"class":165},[134,3347,2096],{"class":165},[134,3349,2103],{"class":2099},[134,3351,3352],{"class":165}," test-nginx.yaml\n",[28,3354,3355],{},[2844,3356,3357,3358,3361],{},"打开 ",[75,3359,3360],{},"https:\u002F\u002Ftest.monkeyrun.net"," 测试，成功！",[28,3363,3364],{},"不知为何再次使用自动签发证书的时候会报错：",[86,3366,3369],{"className":3367,"code":3368,"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,3370,3368],{"__ignoreMap":94},[28,3372,3373],{},"解决了半天都没能找到问题，所以还是用手动签发吧，反正也是一次性的操作。",[2006,3375,3377],{"id":3376},"通过-dns-验证域名","通过 DNS 验证域名",[28,3379,3380],{},"刚才通过 http01 的方式验证域名会有个问题，对于已经部署上线的项目，没办法去验证，所以可以通过 dns 的方式来验证。",[28,3382,3383],{},[2844,3384,3385,3386,3393,3394,3399,3400,3403,3404,3406,3407,3412,3413,3418],{},"经过搜寻，找到了几篇文章，都是利用 ",[3387,3388,3392],"a",{"href":3389,"rel":3390},"https:\u002F\u002Fgithub.com\u002Fkevinniu666",[3391],"nofollow","kevinniu666"," 这位仁兄基于  ",[3387,3395,3398],{"href":3396,"rel":3397},"https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager-webhook-example",[3391],"jetstack\u002Fcert-manager-webhook-example"," 改成 ",[75,3401,3402],{},"alidns"," 的版本来搞的，不过尝试了下，他这里面 ",[75,3405,2150],{}," 版本太老已经跑不起来了，从 GitHub 的 forks 树里面找到了最新的一个 fork，",[3387,3408,3411],{"href":3409,"rel":3410},"https:\u002F\u002Fgithub.com\u002Fcolprog\u002Fcert-manager-webhook-alidns",[3391],"colprog\u002Fcert0manager-webhooks-alidns","，尝试了下，也不行，他应该是改了镜像，但是不可用了。重新尝试了下上一代 fork ",[3387,3414,3417],{"href":3415,"rel":3416},"https:\u002F\u002Fgithub.com\u002Fpangzineng\u002Fcert-manager-webhook-alidns",[3391],"pangzineng\u002Fcert-manager-webhook-alidns","，可用。",[86,3420,3422],{"className":2019,"code":3421,"language":2021,"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,3423,3424,3437,3447],{"__ignoreMap":94},[134,3425,3426,3428,3431,3434],{"class":136,"line":137},[134,3427,2029],{"class":2028},[134,3429,3430],{"class":165}," git",[134,3432,3433],{"class":165}," clone",[134,3435,3436],{"class":165}," https:\u002F\u002Fgithub.com\u002Fpangzineng\u002Fcert-manager-webhook-alidns.git\n",[134,3438,3439,3441,3444],{"class":136,"line":144},[134,3440,2029],{"class":2028},[134,3442,3443],{"class":165}," cd",[134,3445,3446],{"class":165}," cert-manager-webhook-alidns\n",[134,3448,3449,3451,3453,3455,3458,3461],{"class":136,"line":174},[134,3450,2029],{"class":2028},[134,3452,2116],{"class":165},[134,3454,2035],{"class":165},[134,3456,3457],{"class":165}," cert-manager-webhook-alidns",[134,3459,3460],{"class":2099}," --namespace=cert-manager",[134,3462,3463],{"class":165}," .\u002Fdeploy\u002Fwebhook-alidns\n",[28,3465,3466],{},[2844,3467,3468],{},"创建 alidns AccessKey Id 和 Secret",[86,3470,3472],{"className":2019,"code":3471,"language":2021,"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,3473,3474,3506],{"__ignoreMap":94},[134,3475,3476,3478,3480,3482,3484,3486,3489,3492,3495,3498,3501,3504],{"class":136,"line":137},[134,3477,2029],{"class":2028},[134,3479,2053],{"class":165},[134,3481,2291],{"class":2099},[134,3483,2062],{"class":165},[134,3485,2056],{"class":165},[134,3487,3488],{"class":165}," secret",[134,3490,3491],{"class":165}," generic",[134,3493,3494],{"class":165}," alidns-access-key-id",[134,3496,3497],{"class":2099}," --from-literal=accessKeyId=",[134,3499,3500],{"class":161},"'",[134,3502,3503],{"class":165},"xxxxxxx",[134,3505,3194],{"class":161},[134,3507,3508,3510,3512,3514,3516,3518,3520,3522,3525,3528,3530,3532],{"class":136,"line":144},[134,3509,2029],{"class":2028},[134,3511,2053],{"class":165},[134,3513,2291],{"class":2099},[134,3515,2062],{"class":165},[134,3517,2056],{"class":165},[134,3519,3488],{"class":165},[134,3521,3491],{"class":165},[134,3523,3524],{"class":165}," alidns-access-key-secret",[134,3526,3527],{"class":2099}," --from-literal=accessKeySecret=",[134,3529,3500],{"class":161},[134,3531,3503],{"class":165},[134,3533,3194],{"class":161},[28,3535,3536,3537],{},"更新：使用 ",[3387,3538,3541],{"href":3539,"rel":3540},"https:\u002F\u002Fgithub.com\u002Fpragkent\u002Falidns-webhook\u002Ftree\u002Fmaster",[3391],"pragkent\u002Falidns-webhook",[28,3543,3544,3545],{},"修改我们之前创建的 ",[75,3546,2386],{},[86,3548,3550],{"className":563,"code":3549,"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,3551,3552,3561,3569,3575,3581,3589,3599,3605,3611,3621,3627,3636,3643,3653,3663,3670,3680,3687,3697,3707,3714,3722,3731,3737,3748],{"__ignoreMap":94},[134,3553,3554,3556,3558],{"class":136,"line":137},[134,3555,2397],{"class":572},[134,3557,158],{"class":140},[134,3559,3560],{"class":165}," cert-manager.io\u002Fv1\n",[134,3562,3563,3565,3567],{"class":136,"line":144},[134,3564,2407],{"class":572},[134,3566,158],{"class":140},[134,3568,2412],{"class":165},[134,3570,3571,3573],{"class":136,"line":174},[134,3572,2417],{"class":572},[134,3574,576],{"class":140},[134,3576,3577,3579],{"class":136,"line":218},[134,3578,2424],{"class":572},[134,3580,576],{"class":140},[134,3582,3583,3585,3587],{"class":136,"line":233},[134,3584,2431],{"class":572},[134,3586,158],{"class":140},[134,3588,2436],{"class":165},[134,3590,3591,3593,3595,3597],{"class":136,"line":239},[134,3592,2441],{"class":572},[134,3594,158],{"class":140},[134,3596,2446],{"class":165},[134,3598,2449],{"class":2065},[134,3600,3601,3603],{"class":136,"line":264},[134,3602,2454],{"class":572},[134,3604,576],{"class":140},[134,3606,3607,3609],{"class":136,"line":296},[134,3608,2461],{"class":572},[134,3610,576],{"class":140},[134,3612,3613,3615,3617,3619],{"class":136,"line":311},[134,3614,2468],{"class":572},[134,3616,158],{"class":140},[134,3618,2473],{"class":165},[134,3620,2476],{"class":2065},[134,3622,3623,3625],{"class":136,"line":317},[134,3624,2481],{"class":572},[134,3626,576],{"class":140},[134,3628,3629,3631,3634],{"class":136,"line":322},[134,3630,2488],{"class":140},[134,3632,3633],{"class":572}," dns01",[134,3635,576],{"class":140},[134,3637,3638,3641],{"class":136,"line":343},[134,3639,3640],{"class":572},"          webhook",[134,3642,576],{"class":140},[134,3644,3645,3648,3650],{"class":136,"line":365},[134,3646,3647],{"class":572},"            groupName",[134,3649,158],{"class":140},[134,3651,3652],{"class":165}," yourgroup.com\n",[134,3654,3655,3658,3660],{"class":136,"line":385},[134,3656,3657],{"class":572},"            solverName",[134,3659,158],{"class":140},[134,3661,3662],{"class":165}," alidns\n",[134,3664,3665,3668],{"class":136,"line":390},[134,3666,3667],{"class":572},"            config",[134,3669,576],{"class":140},[134,3671,3672,3675,3677],{"class":136,"line":395},[134,3673,3674],{"class":572},"              region",[134,3676,158],{"class":140},[134,3678,3679],{"class":161}," ''\n",[134,3681,3682,3685],{"class":136,"line":416},[134,3683,3684],{"class":572},"              accessKeySecretRef",[134,3686,576],{"class":140},[134,3688,3689,3692,3694],{"class":136,"line":448},[134,3690,3691],{"class":572},"                name",[134,3693,158],{"class":140},[134,3695,3696],{"class":165}," alidns-secret\n",[134,3698,3699,3702,3704],{"class":136,"line":465},[134,3700,3701],{"class":572},"                key",[134,3703,158],{"class":140},[134,3705,3706],{"class":165}," access-key\n",[134,3708,3709,3712],{"class":136,"line":489},[134,3710,3711],{"class":572},"              secretKeySecretRef",[134,3713,576],{"class":140},[134,3715,3716,3718,3720],{"class":136,"line":495},[134,3717,3691],{"class":572},[134,3719,158],{"class":140},[134,3721,3696],{"class":165},[134,3723,3724,3726,3728],{"class":136,"line":501},[134,3725,3701],{"class":572},[134,3727,158],{"class":140},[134,3729,3730],{"class":165}," secret-key\n",[134,3732,3733,3735],{"class":136,"line":522},[134,3734,2515],{"class":572},[134,3736,576],{"class":140},[134,3738,3739,3741,3743,3746],{"class":136,"line":541},[134,3740,2522],{"class":572},[134,3742,158],{"class":140},[134,3744,3745],{"class":165}," letsencrypt-prod-account-key",[134,3747,2529],{"class":2065},[134,3749,3750,3752,3754,3756],{"class":136,"line":736},[134,3751,2534],{"class":572},[134,3753,158],{"class":140},[134,3755,2539],{"class":165},[134,3757,2542],{"class":2065},[28,3759,2545,3760],{},[75,3761,565],{},[86,3763,3764],{"className":2019,"code":2550,"language":2021,"meta":94,"style":94},[75,3765,3766],{"__ignoreMap":94},[134,3767,3768,3770,3772,3774,3776],{"class":136,"line":137},[134,3769,2029],{"class":2028},[134,3771,2053],{"class":165},[134,3773,2056],{"class":165},[134,3775,2103],{"class":2099},[134,3777,2565],{"class":165},[28,3779,2568],{},[86,3781,3782],{"className":2019,"code":2571,"language":2021,"meta":94,"style":94},[75,3783,3784,3794,3802],{"__ignoreMap":94},[134,3785,3786,3788,3790,3792],{"class":136,"line":137},[134,3787,2029],{"class":2028},[134,3789,2053],{"class":165},[134,3791,2199],{"class":165},[134,3793,2584],{"class":165},[134,3795,3796,3798,3800],{"class":136,"line":144},[134,3797,2212],{"class":2028},[134,3799,2591],{"class":165},[134,3801,2224],{"class":165},[134,3803,3804,3806,3808],{"class":136,"line":174},[134,3805,2598],{"class":2028},[134,3807,2601],{"class":165},[134,3809,2604],{"class":165},[28,3811,3812],{},"重新手动签发证书，验证，成功！",[28,3814,3815],{},"PS：需要注意的是，从 http01 认证修改到 dns01 认证后，有个坑，会一直失败，查看 cert-manager 的 Pod 日志，会发现如下错误：",[86,3817,3821],{"className":3818,"code":3819,"language":3820,"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","log",[75,3822,3823],{"__ignoreMap":94},[134,3824,3825,3829,3832,3835,3838,3841,3843,3846,3849,3851,3854,3857,3859],{"class":136,"line":137},[134,3826,3828],{"class":3827},"su5hD","cert-manager\u002Fcontroller\u002Forders ",[134,3830,3831],{"class":165},"\"msg\"",[134,3833,3834],{"class":3827},"=",[134,3836,3837],{"class":165},"\"Failed to determine the list of Challenge resources needed for the Order\"",[134,3839,3840],{"class":165}," \"error\"",[134,3842,3834],{"class":3827},[134,3844,3845],{"class":165},"\"no configured challenge solvers can be used for this challenge\"",[134,3847,3848],{"class":165}," \"resource_kind\"",[134,3850,3834],{"class":3827},[134,3852,3853],{"class":165},"\"Order\"",[134,3855,3856],{"class":165}," \"resource_name\"",[134,3858,3834],{"class":3827},[134,3860,3861],{"class":165},"\"xxx\"\n",[28,3863,3864,3865,3870,3871,3876,3877,3879],{},"研究了半天都没成功，后来在 GitHub 上找到了这个 ",[3387,3866,3869],{"href":3867,"rel":3868},"https:\u002F\u002Fgithub.com\u002Fjetstack\u002Fcert-manager\u002Fissues\u002F2494#issuecomment-585391545",[3391],"Issue","，按照 ",[3387,3872,3875],{"href":3873,"rel":3874},"https:\u002F\u002Fgithub.com\u002Fdemisx",[3391],"demisx"," 这位仁兄的建议，把所有和 ",[75,3878,2150],{}," 相关的东西全部删除重新用 dns01 的方式部署一遍就 OK 了。",[28,3881,3882,3883,3886,3887,3890],{},"另外，cert-manager 的 API group 从 ",[75,3884,3885],{},"certmanager.k8s.io"," 改到 ",[75,3888,3889],{},"certmanager.io"," 了，不少老教程里面仍然是前者，需要改为后者才能正常执行。",[3892,3893,3894,3897],"blockquote",{},[28,3895,3896],{},"参考链接",[3898,3899,3900,3907,3914,3921],"ul",{},[48,3901,3902],{},[3387,3903,3906],{"href":3904,"rel":3905},"https:\u002F\u002Fdocs.bitnami.com\u002Fkubernetes\u002Fhow-to\u002Fsecure-kubernetes-services-with-ingress-tls-letsencrypt\u002F",[3391],"Secure Kubernetes Services With Ingress, TLS And Let's Encrypt",[48,3908,3909],{},[3387,3910,3913],{"href":3911,"rel":3912},"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",[3391],"使用 cert-manager 实现 Ingress https",[48,3915,3916],{},[3387,3917,3920],{"href":3918,"rel":3919},"https:\u002F\u002Fyq.aliyun.com\u002Farticles\u002F718711",[3391],"使用 cert-manager 给阿里云的 DNS 域名授权 SSL 证书",[48,3922,3923],{},[3387,3924,3927],{"href":3925,"rel":3926},"https:\u002F\u002Fcert-manager.io\u002Fdocs\u002F",[3391],"cert-manager docs",[1932,3929,3930],{},"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":3932},[],"png","2020-02-27","很多博主的 https 证书经常容易忘记更新，虽说证书过期前都会有邮件提醒，但是万一确实忙得没时间去处理，忘记了，就会出现证书过期的情况了。",{},"\u002Fposts\u002F2020\u002Fk8s-cert-manager-tls",{"text":3939,"minutes":3940,"time":3941,"words":3942},"8 min read",7.465,447900,1493,{"title":1961,"description":3935},{"loc":3937},"posts\u002F2020\u002F20200227.k8s-cert-manager-tls",[1954,3947,1983,1955,1957],"阿里云","天气晴","2aJ6T7QGEjJQr4Yy8PkK08lqxa4n-rxsCy0mGJw2oBY",{"id":3951,"title":3952,"body":3953,"class":1939,"cover":3933,"coverSize":1939,"date":4101,"description":3957,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":4102,"navigation":596,"path":4103,"readingTime":4104,"seo":4109,"sitemap":4110,"stem":4111,"tags":4112,"time":1939,"weather":4113,"__hash__":4114},"posts\u002Fposts\u002F2019\u002F20191201.docker-registry-auth-with-same-domain.md","Docker 同一域名下多个 Registry 保存凭证的方式",{"type":25,"value":3954,"toc":4099},[3955,3958,3985,3992,4017,4028,4089,4096],[28,3956,3957],{},"阿里云的容器镜像服务是个好东西，配合在阿里云上容器服务，速度非常快。",[28,3959,3960,3961,3964,3965,3967,3968,112,3971,3974,3975,3977,3978,3980,3981,3984],{},"但是阿里云的容器服务不支持自定义域名，都是在同一个域名下，通过不同的 ",[75,3962,3963],{},"namespace"," 来实现的。当需要管理多个账户下的不同 ",[75,3966,3963],{}," 的时候，Docker 默认的认证存储方式就不太适用了。默认的 ",[75,3969,3970],{},"~\u002F.docker\u002Fconfig.json",[75,3972,3973],{},"auths"," 是根据域名来区分的，会出现登录了这个 ",[75,3976,3963],{}," 之后，另一个 ",[75,3979,3963],{}," 认证会失效的情况。经过一番搜索，发现可以通过 ",[75,3982,3983],{},"docker --config"," 来实现。",[28,3986,3987,3988,3991],{},"通过如下方式来创建一个名为 ",[75,3989,3990],{},"config-a"," 的配置",[86,3993,3995],{"className":2019,"code":3994,"language":2021,"meta":94,"style":94},"docker --config ~\u002F.docker\u002Fconfig-a login --username=config-a-username registry.cn-hangzhou.aliyuncs.com\n",[75,3996,3997],{"__ignoreMap":94},[134,3998,3999,4002,4005,4008,4011,4014],{"class":136,"line":137},[134,4000,4001],{"class":2028},"docker",[134,4003,4004],{"class":2099}," --config",[134,4006,4007],{"class":165}," ~\u002F.docker\u002Fconfig-a",[134,4009,4010],{"class":165}," login",[134,4012,4013],{"class":2099}," --username=config-a-username",[134,4015,4016],{"class":165}," registry.cn-hangzhou.aliyuncs.com\n",[28,4018,4019,4020,4023,4024,4027],{},"之后 ",[75,4021,4022],{},"push"," 之类的命令，在前面加个 ",[75,4025,4026],{},"--config ~\u002F.docker\u002Fconfig-a"," 即可，例如：",[86,4029,4031],{"className":2019,"code":4030,"language":2021,"meta":94,"style":94},"docker build -t registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:$npm_package_version -t registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:latest . && docker --config ~\u002F.docker\u002Fconfig-a push registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:$npm_package_version && docker --config ~\u002F.docker\u002Fconfig-a push registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:latest\n",[75,4032,4033],{"__ignoreMap":94},[134,4034,4035,4037,4040,4043,4046,4049,4052,4055,4058,4061,4064,4066,4068,4071,4073,4075,4078,4080,4082,4084,4086],{"class":136,"line":137},[134,4036,4001],{"class":2028},[134,4038,4039],{"class":165}," build",[134,4041,4042],{"class":2099}," -t",[134,4044,4045],{"class":165}," registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:",[134,4047,4048],{"class":3827},"$npm_package_version ",[134,4050,4051],{"class":2099},"-t",[134,4053,4054],{"class":165}," registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:latest",[134,4056,4057],{"class":165}," .",[134,4059,4060],{"class":140}," &&",[134,4062,4063],{"class":2028}," docker",[134,4065,4004],{"class":2099},[134,4067,4007],{"class":165},[134,4069,4070],{"class":165}," push",[134,4072,4045],{"class":165},[134,4074,4048],{"class":3827},[134,4076,4077],{"class":140},"&&",[134,4079,4063],{"class":2028},[134,4081,4004],{"class":2099},[134,4083,4007],{"class":165},[134,4085,4070],{"class":165},[134,4087,4088],{"class":165}," registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002Fhblb-web:latest\n",[28,4090,4091,4092,4095],{},"同理，可以增加其他的 ",[75,4093,4094],{},"config"," 来完成同一域名下多个账号的认证存储。",[1932,4097,4098],{},"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 .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 .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}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":4100},[],"2019-12-01",{},"\u002Fposts\u002F2019\u002Fdocker-registry-auth-with-same-domain",{"text":4105,"minutes":4106,"time":4107,"words":4108},"2 min read",1.16,69600,232,{"title":3952,"description":3957},{"loc":4103},"posts\u002F2019\u002F20191201.docker-registry-auth-with-same-domain",[1954,1957],"天气小雨","wt0A2lhDXspLwF_D24FDYIYXkEH3UndrqDQcKEdEBjs",{"id":4116,"title":4117,"body":4118,"class":1939,"cover":3933,"coverSize":1939,"date":4137,"description":4122,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":4138,"navigation":596,"path":4139,"readingTime":4140,"seo":4144,"sitemap":4145,"stem":4146,"tags":4147,"time":1939,"weather":4150,"__hash__":4151},"posts\u002Fposts\u002F2019\u002F20190109.ghost-docker-mail-config.md","Ghost Docker 部署方式配置邮箱",{"type":25,"value":4119,"toc":4135},[4120,4123,4126,4132],[28,4121,4122],{},"很久没登录博客了，今天登录时，发现忘记密码了，之前都是自动登录的，估计是自动登录过期了，没办法自动登录了，试了几次，账号被锁定了。",[28,4124,4125],{},"尝试找回密码，发现好像没有配置 SMTP 邮箱。于是找了下配置项，用 Docker 部署的话，在编排模板的 environment 中添加如下配置：",[86,4127,4130],{"className":4128,"code":4129,"language":91},[89],"- 'mail__transport=SMTP'\n- 'mail__from=Ghost \u003Cxx@xxx.com>'\n- 'mail__options__host=smtp.qiye.aliyun.com'\n- 'mail__options__secureConnection=true'\n- 'mail__options__port=465'\n- 'mail__options__auth__user=xx@xxx.com'\n- 'mail__options__auth__pass=YOUR_PASSWORD'\n",[75,4131,4129],{"__ignoreMap":94},[28,4133,4134],{},"重新部署一下即可。",{"title":94,"searchDepth":144,"depth":144,"links":4136},[],"2019-01-09",{},"\u002Fposts\u002F2019\u002Fghost-docker-mail-config",{"text":4141,"minutes":4142,"time":4143,"words":1296},"1 min read",0.62,37200,{"title":4117,"description":4122},{"loc":4139},"posts\u002F2019\u002F20190109.ghost-docker-mail-config",[1954,4148,4149,1957],"博客","Ghost","天气阴","yJjcNS7QIvqk0sWEB0_f7Ds-et57mbeIjVP1lrbEJOE",{"id":4153,"title":4154,"body":4155,"class":1939,"cover":1939,"coverSize":1939,"date":4748,"description":4159,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":4749,"navigation":596,"path":4750,"readingTime":4751,"seo":4756,"sitemap":4757,"stem":4758,"tags":4759,"time":4761,"weather":4762,"__hash__":4763},"posts\u002Fposts\u002F2018\u002F20180414.frontend-components-and-docker-deploy.md","前端跨项目组件化及基于 Docker 的快速部署方案",{"type":25,"value":4156,"toc":4744},[4157,4160,4164,4167,4191,4194,4245,4270,4289,4299,4347,4354,4360,4407,4413,4422,4425,4466,4526,4544,4548,4551,4554,4666,4676,4685,4731,4738,4741],[28,4158,4159],{},"最近静下心来写了几个项目，花了些时间重新整理了整套组件化方案和部署方案，记录一下。",[4161,4162,4163],"h3",{"id":4163},"跨项目组件化",[28,4165,4166],{},"前端的组件化不用多说了，发展到现在，无论是 React 的还是 Vue，都提供了相当方便的组件化实现。在日常项目中，有些组件其实是可以跨多个项目使用的，将这些组件抽离出来作为单独项目，并复用到其他项目中去，一来可以避免重复造轮子，加快开发速度，二来维护效率也高，一些 bugfix 或者新特性直接在组件中更新，项目中只需要更新引用版本号即可，方便快捷。",[28,4168,4169,4170,4173,4174,4177,4178,4180,4181,4183,4184,4187,4188,4190],{},"跨项目的组件化方式也很多，开发阶段可以用 ",[75,4171,4172],{},"npm link","，相当于在主项目的 ",[75,4175,4176],{},"node_modules"," 目录中创建了一个链向组件项目的软链，方便是挺方便，但是有几个问题。一是 Eslint 的目录递归检查是基于最终实际目录的，也就是说虽然 Eslint 默认排除 ",[75,4179,4176],{}," 目录，但它依然会对该目录中的软链项目进行检查，一旦组件项目的 Eslint 规则和主项目的 Eslint 不一致的话，主项目 Eslint 就没法通过，这个比较蛋疼，就得临时禁用 Eslint 或者修改组件项目的规则。作为组件项目应该保证少依赖，而且要服务多个项目，没办法保证匹配各个项目的 Eslint 规则。第二个问题是，通过 ",[75,4182,4172],{}," 实现的依赖，不会体现在 ",[75,4185,4186],{},"package.json"," 中，如果通过 Docker 去部署，在 Docker 上是不知道你这个软链的，即便能够把软链写进去，在 Docker 中构建的时候，由于目录问题，也不能保证可以把组件项目文件拷过去。因此，在生产中，",[75,4189,4172],{}," 这个方案是没办法用的。",[28,4192,4193],{},"之前在国资的时候，采用的是通过组件项目的 git 地址来定位包，使用起来也很方便。如下：",[86,4195,4197],{"className":128,"code":4196,"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,4198,4199,4203,4217,4236,4241],{"__ignoreMap":94},[134,4200,4201],{"class":136,"line":137},[134,4202,141],{"class":140},[134,4204,4205,4207,4210,4212,4214],{"class":136,"line":144},[134,4206,148],{"class":147},[134,4208,4209],{"class":151},"dependencies",[134,4211,155],{"class":147},[134,4213,158],{"class":140},[134,4215,4216],{"class":140}," {\n",[134,4218,4219,4222,4225,4227,4229,4231,4234],{"class":136,"line":174},[134,4220,4221],{"class":147},"    \"",[134,4223,4224],{"class":245},"example-component",[134,4226,155],{"class":147},[134,4228,158],{"class":140},[134,4230,162],{"class":161},[134,4232,4233],{"class":165},"git+ssh:\u002F\u002Fgit@xxx.com\u002Fexample-component.git#v1.0.0",[134,4235,382],{"class":161},[134,4237,4238],{"class":136,"line":218},[134,4239,4240],{"class":140},"  }\n",[134,4242,4243],{"class":136,"line":233},[134,4244,544],{"class":140},[28,4246,4247,4248,4251,4252,4255,4256,4258,4259,4262,4263,4265,4266,4269],{},"npm 支持通过 tag 来定位版本，组件项目发布的时候打一个 tag，对应的在项目中更新最后的版本号重新安装就可以完成升级了。由于之前没有用 Docker 部署，所以也没发现有什么问题。用了 Docker 的话，就会有些小问题。一般为了精简，都会采用基于 ",[75,4249,4250],{},"Alpine"," 的基础镜像，我目前的前端项目都是基于 ",[75,4253,4254],{},"node:8-alpine"," 来构建的。要知道，",[75,4257,4250],{}," 镜像本身只有 4.8M，",[75,4260,4261],{},"node:8-apline"," 也只有 20 几兆，非常精简。但是通过上面的这种方式，需要依赖 git，而 ",[75,4264,4250],{}," 显然是没有安装 ",[75,4267,4268],{},"git"," 的，也没有必要为了部署专门去安装一个 git。",[28,4271,4272,4273,4276,4277,4280,4281,4284,4285,4288],{},"于是便有了第三种方案，基于 Nexus 的 npm repository 方案。把包发布到 npm 上不太现实，大部分公司项目还是希望私有，和 maven 一样，Nexus 也支持 npm。创建一个 ",[75,4274,4275],{},"hosted"," 类型的 npm 仓库，例如 ",[75,4278,4279],{},"npm-hosted","，具体教程自行谷歌。但是我们又不希望给该仓库增加过多的压力，不想把所有 npm 或者 yarn 默认的 registry 改为 Nexus，没必要，因为即便改为 Nexus，在国内的网络环境，还是 proxy 到 ",[75,4282,4283],{},"https:\u002F\u002Fregistry.npm.taobao.org"," 上去了，而且会在 Nexus 上留下大量缓存，也经过了两层的下载，我尝试过，很蛋疼，在 Nexus 没有缓存第一次去下载的时候，还会有很多失败。后来发现 npm 也支持直接通过 ",[75,4286,4287],{},"tgz"," 文件的方式来引用，这样就好办了。",[28,4290,4291,4292,4295,4296,4298],{},"首先执行 ",[75,4293,4294],{},"npm adduser --registry=https:\u002F\u002Fmyregistry.example.com","，输入 Nexus 上具有上传 npm 包权限的用户名和密码，会在本地记录该用户的登录认证。然后在组件项目的 ",[75,4297,4186],{}," 中加入：",[86,4300,4302],{"className":128,"code":4301,"language":130,"meta":94,"style":94},"{\n  \"publishConfig\": {\n    \"registry\": \"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002F\"\n  }\n}\n",[75,4303,4304,4308,4321,4339,4343],{"__ignoreMap":94},[134,4305,4306],{"class":136,"line":137},[134,4307,141],{"class":140},[134,4309,4310,4312,4315,4317,4319],{"class":136,"line":144},[134,4311,148],{"class":147},[134,4313,4314],{"class":151},"publishConfig",[134,4316,155],{"class":147},[134,4318,158],{"class":140},[134,4320,4216],{"class":140},[134,4322,4323,4325,4328,4330,4332,4334,4337],{"class":136,"line":174},[134,4324,4221],{"class":147},[134,4326,4327],{"class":245},"registry",[134,4329,155],{"class":147},[134,4331,158],{"class":140},[134,4333,162],{"class":161},[134,4335,4336],{"class":165},"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002F",[134,4338,382],{"class":161},[134,4340,4341],{"class":136,"line":218},[134,4342,4240],{"class":140},[134,4344,4345],{"class":136,"line":233},[134,4346,544],{"class":140},[28,4348,4349,4350,4353],{},"然后执行 ",[75,4351,4352],{},"npm publish","，组件项目就会被上传到 Nexus 了。",[28,4355,4356,4357,4359],{},"在具体项目中，需要用到改组件的时候，在 ",[75,4358,4186],{}," 中这样引用：",[86,4361,4363],{"className":128,"code":4362,"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,4364,4365,4369,4381,4399,4403],{"__ignoreMap":94},[134,4366,4367],{"class":136,"line":137},[134,4368,141],{"class":140},[134,4370,4371,4373,4375,4377,4379],{"class":136,"line":144},[134,4372,148],{"class":147},[134,4374,4209],{"class":151},[134,4376,155],{"class":147},[134,4378,158],{"class":140},[134,4380,4216],{"class":140},[134,4382,4383,4385,4388,4390,4392,4394,4397],{"class":136,"line":174},[134,4384,4221],{"class":147},[134,4386,4387],{"class":245},"vue-footer",[134,4389,155],{"class":147},[134,4391,158],{"class":140},[134,4393,162],{"class":161},[134,4395,4396],{"class":165},"https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002Fexample-component\u002F-\u002Fexample-component-1.0.0.tgz",[134,4398,382],{"class":161},[134,4400,4401],{"class":136,"line":218},[134,4402,4240],{"class":140},[134,4404,4405],{"class":136,"line":233},[134,4406,544],{"class":140},[28,4408,4409,4410,1987],{},"或者直接 ",[75,4411,4412],{},"npm install https:\u002F\u002Fmyregistry.example.com\u002Frepository\u002Fnpm-hosted\u002Fexample-component\u002F-\u002Fexample-component-1.0.0.tgz --save",[28,4414,4415,4416,4418,4419,4421],{},"组件更新通过修改 ",[75,4417,4186],{}," 里的版本号，然后 ",[75,4420,4352],{},"，然后具体项目中修改最后的版本号，重新安装即可。",[28,4423,4424],{},"另外由于这次都是用的最新的组件，也遇到了一个很大的坑。",[28,4426,4427,4428,4431,4432,4435,4436,4439,4440,4443,4444,2832,4447,4450,4451,4454,4455,4459,4460,4465],{},"我的项目都是基于 Nuxt.js 来搞的，Nuxt.js 的框架用起来更爽，ssr 性能也比 vue 原生的要高。有一个问题，组件项目在 webpack 打包的时候，默认是不支持 Nuxt 的 SSR 的，用了 ",[75,4429,4430],{},"vue-style-loader"," 之后，里面会有多个地方用到了 ",[75,4433,4434],{},"document","。如果需要同时支持 Browser 和 SSR，需要再建一个 SSR 的 webpack config，将 ",[75,4437,4438],{},"target"," 设为 ",[75,4441,4442],{},"node","，并且 ",[75,4445,4446],{},"vue-loader",[75,4448,4449],{},"options"," 中需要加入 ",[75,4452,4453],{},"optimizeSSR: false","，这个是因为尤大在最近的某个版本中针对 SSR 做了一些优化，但是在 Nuxt 的 SSR 中会有些问题，具体可以参见 ",[3387,4456,4457],{"href":4457,"rel":4458},"https:\u002F\u002Fgithub.com\u002Fnuxt\u002Fnuxt.js\u002Fissues\u002F2565",[3391]," ，找了很久找到了这个 issue，追踪了下，尤大貌似在最近的 ",[3387,4461,4464],{"href":4462,"rel":4463},"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fvue\u002Fcommit\u002F9b22d86ab315a3c6061a6a4776eab1964304f92e",[3391],"v2.5.17-beta.0"," 中已经修复了这个问题，具体等 release 版发布之后再试下。在 Nuxt 中，创建一个 plugin，直接引用生成的 SSR 版本的文件即可。",[86,4467,4471],{"className":4468,"code":4469,"language":4470,"meta":94,"style":94},"language-javascript shiki shiki-themes material-theme-lighter github-light github-dark","import ExampleComponent from 'example-component\u002Fdist\u002Fssr.js'\nimport Vue from 'vue'\n\nVue.use(ExampleComponent)\n","javascript",[75,4472,4473,4491,4507,4511],{"__ignoreMap":94},[134,4474,4475,4478,4481,4484,4486,4489],{"class":136,"line":137},[134,4476,4477],{"class":727},"import",[134,4479,4480],{"class":3827}," ExampleComponent ",[134,4482,4483],{"class":727},"from",[134,4485,3189],{"class":161},[134,4487,4488],{"class":165},"example-component\u002Fdist\u002Fssr.js",[134,4490,3194],{"class":161},[134,4492,4493,4495,4498,4500,4502,4505],{"class":136,"line":144},[134,4494,4477],{"class":727},[134,4496,4497],{"class":3827}," Vue ",[134,4499,4483],{"class":727},[134,4501,3189],{"class":161},[134,4503,4504],{"class":165},"vue",[134,4506,3194],{"class":161},[134,4508,4509],{"class":136,"line":174},[134,4510,597],{"emptyLinePlaceholder":596},[134,4512,4513,4516,4519,4523],{"class":136,"line":218},[134,4514,4515],{"class":3827},"Vue",[134,4517,4518],{"class":140},".",[134,4520,4522],{"class":4521},"sGLFI","use",[134,4524,4525],{"class":3827},"(ExampleComponent)\n",[28,4527,4528,4529,2832,4532,4535,4536,4539,4540,4543],{},"在 ",[75,4530,4531],{},"nuxt.config.js",[75,4533,4534],{},"plugins"," 中直接加入 ",[75,4537,4538],{},"'~plugins\u002Fcomponents-plugin.js'"," 即可。网上大部分解决方案是引用的时候将组件项目设置为 ",[75,4541,4542],{},"ssr: false","，其实是治标不治本，放弃了该组件在服务端的渲染，不可取。",[4161,4545,4547],{"id":4546},"基于-docker-的快速部署","基于 Docker 的快速部署",[28,4549,4550],{},"使用 Docker 也快 1 年了，基本上从开始用上 Docker 之后，就爱不释手了，大大缩短了发布时间，减少了运维成本。",[28,4552,4553],{},"目前我的项目都是部署在阿里云上，基于阿里云的容器集群方案，前面通过 SLB，后面横向部署多台机器。不多说，贴下 Dockerfile：",[86,4555,4559],{"className":4556,"code":4557,"language":4558,"meta":94,"style":94},"language-dockerfile shiki shiki-themes material-theme-lighter github-light github-dark","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","dockerfile",[75,4560,4561,4570,4574,4582,4586,4594,4601,4609,4616,4623,4627,4635,4643],{"__ignoreMap":94},[134,4562,4563,4567],{"class":136,"line":137},[134,4564,4566],{"class":4565},"sw1J6","FROM",[134,4568,4569],{"class":3827}," node:8-alpine\n",[134,4571,4572],{"class":136,"line":144},[134,4573,597],{"emptyLinePlaceholder":596},[134,4575,4576,4579],{"class":136,"line":174},[134,4577,4578],{"class":4565},"WORKDIR",[134,4580,4581],{"class":3827}," \u002Fapp\n",[134,4583,4584],{"class":136,"line":218},[134,4585,597],{"emptyLinePlaceholder":596},[134,4587,4588,4591],{"class":136,"line":233},[134,4589,4590],{"class":4565},"COPY",[134,4592,4593],{"class":3827}," package.json \u002Fapp\n",[134,4595,4596,4598],{"class":136,"line":239},[134,4597,4590],{"class":4565},[134,4599,4600],{"class":3827}," yarn.lock \u002Fapp\n",[134,4602,4603,4606],{"class":136,"line":264},[134,4604,4605],{"class":4565},"RUN",[134,4607,4608],{"class":3827}," npm config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn config set registry https:\u002F\u002Fregistry.npm.taobao.org && yarn install\n",[134,4610,4611,4613],{"class":136,"line":296},[134,4612,4590],{"class":4565},[134,4614,4615],{"class":3827}," . \u002Fapp\n",[134,4617,4618,4620],{"class":136,"line":311},[134,4619,4605],{"class":4565},[134,4621,4622],{"class":3827}," npm run build\n",[134,4624,4625],{"class":136,"line":317},[134,4626,597],{"emptyLinePlaceholder":596},[134,4628,4629,4632],{"class":136,"line":322},[134,4630,4631],{"class":4565},"EXPOSE",[134,4633,4634],{"class":3827}," 6002\n",[134,4636,4637,4640],{"class":136,"line":343},[134,4638,4639],{"class":4565},"ENV",[134,4641,4642],{"class":3827}," SERVER_ENV $SERVER_ENV\n",[134,4644,4645,4648,4650,4653,4656,4659,4661,4664],{"class":136,"line":365},[134,4646,4647],{"class":4565},"CMD",[134,4649,186],{"class":3827},[134,4651,4652],{"class":165},"\"npm\"",[134,4654,4655],{"class":3827},", ",[134,4657,4658],{"class":165},"\"run\"",[134,4660,4655],{"class":3827},[134,4662,4663],{"class":165},"\"start\"",[134,4665,486],{"class":3827},[28,4667,4668,4669,4672,4673,4675],{},"记得在 ",[75,4670,4671],{},".dockerignore"," 文件中把 ",[75,4674,4176],{}," 目录加上，在 COPY 的时候不进行复制，而是在 docker 环境中重新获取。",[28,4677,4678,4679,4681,4682,4684],{},"在项目的 ",[75,4680,4186],{}," 中配置好 ",[75,4683,651],{}," 命令：",[86,4686,4688],{"className":128,"code":4687,"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,4689,4690,4694,4706,4723,4727],{"__ignoreMap":94},[134,4691,4692],{"class":136,"line":137},[134,4693,141],{"class":140},[134,4695,4696,4698,4700,4702,4704],{"class":136,"line":144},[134,4697,148],{"class":147},[134,4699,115],{"class":151},[134,4701,155],{"class":147},[134,4703,158],{"class":140},[134,4705,4216],{"class":140},[134,4707,4708,4710,4712,4714,4716,4718,4721],{"class":136,"line":174},[134,4709,4221],{"class":147},[134,4711,651],{"class":245},[134,4713,155],{"class":147},[134,4715,158],{"class":140},[134,4717,162],{"class":161},[134,4719,4720],{"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,4722,382],{"class":161},[134,4724,4725],{"class":136,"line":218},[134,4726,4240],{"class":140},[134,4728,4729],{"class":136,"line":233},[134,4730,544],{"class":140},[28,4732,4733,4734,4737],{},"在阿里云的容器镜像服务中建好镜像，便可以部署上去了。之前我是直接通过绑定 gitlab，在代码更新后，触发阿里云镜像服务在线构建，但是我们采用了私有的 npm 仓库的话，就比较麻烦了，还得配置 Nexus 账号，索性直接在本地构建，上传的网速也不是问题。在阿里云的容器服务中创建好应用，设置好触发器，在镜像更新后触发重新部署，就大功告成了。如果有多台机器，在调度配置中配置好“平滑升级”，会在同一服务的多个容器升级的时候，保证当前一批或者一个容器升级或者更新成功（健康检查成功）之后，再来更新或者升级下一批容器，也就是“滚动发布”。之后只需要 ",[75,4735,4736],{},"npm run deploy","，喝杯咖啡 ☕️，就完成了所有的部署工作了。方便、快捷、不易出错。",[28,4739,4740],{},"最近看了篇文章，里面有句话：“这世界上肯定存在让人上瘾的代码”，提出了“技术多巴胺”这个说法。而此刻的我，就仿佛打了几针“技术多巴胺”一样，很兴奋，一点睡意都没有。",[1932,4742,4743],{},"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":4745},[4746,4747],{"id":4163,"depth":174,"text":4163},{"id":4546,"depth":174,"text":4547},"2018-04-14",{},"\u002Fposts\u002F2018\u002Ffrontend-components-and-docker-deploy",{"text":4752,"minutes":4753,"time":4754,"words":4755},"10 min read",9.455,567300,1891,{"title":4154,"description":4159},{"loc":4750},"posts\u002F2018\u002F20180414.frontend-components-and-docker-deploy",[1954,4760,1957,1955],"前端","凌晨","天气🌧","9_mH8lcdetq97RzpovxCwJ60jWmSz7r17jZvBEuf42s",{"id":4765,"title":4766,"body":4767,"class":1939,"cover":1939,"coverSize":1939,"date":5583,"description":5584,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":5585,"navigation":596,"path":5586,"readingTime":5587,"seo":5591,"sitemap":5592,"stem":5593,"tags":5594,"time":1939,"weather":5595,"__hash__":5596},"posts\u002Fposts\u002F2018\u002F20180412.dockerfile-maven.md","Maven 项目 Docker 一键发布配置",{"type":25,"value":4768,"toc":5581},[4769,4784,4787,4895,4901,4919,5474,5481,5572,5578],[28,4770,4771,4772,4777,4778,4783],{},"Docker 用了很久了，之前 Maven 项目一直用的",[3387,4773,4776],{"href":4774,"rel":4775},"https:\u002F\u002Fgithub.com\u002Fspotify\u002Fdocker-maven-plugin",[3391],"docker-maven-plugin","，但是作者目前已经不推荐使用这种方式了，该项目已经不再更新功能，只提供 bugfix。他们的新项目叫做",[3387,4779,4782],{"href":4780,"rel":4781},"https:\u002F\u002Fgithub.com\u002Fspotify\u002Fdockerfile-maven",[3391],"dockerfile-maven","，配置上有些不同，之前一直没时间去更新，最近的一个项目中，采用了最新的插件，中间也踩过不少坑，刚刚终于都搞定了，记录一下。",[28,4785,4786],{},"Dockerfile 无需多说，整理了一个通用的，可以用在任意 Spring Boot 项目中，如下：",[86,4788,4790],{"className":4556,"code":4789,"language":4558,"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,4791,4792,4799,4803,4810,4814,4822,4826,4834,4842,4852,4862,4871],{"__ignoreMap":94},[134,4793,4794,4796],{"class":136,"line":137},[134,4795,4566],{"class":4565},[134,4797,4798],{"class":3827}," frolvlad\u002Falpine-oraclejdk8:slim\n",[134,4800,4801],{"class":136,"line":144},[134,4802,597],{"emptyLinePlaceholder":596},[134,4804,4805,4807],{"class":136,"line":174},[134,4806,4605],{"class":4565},[134,4808,4809],{"class":3827}," ln -sf \u002Fusr\u002Fshare\u002Fzoneinfo\u002FAsia\u002FShanghai \u002Fetc\u002Flocaltime\n",[134,4811,4812],{"class":136,"line":218},[134,4813,597],{"emptyLinePlaceholder":596},[134,4815,4816,4819],{"class":136,"line":233},[134,4817,4818],{"class":4565},"VOLUME",[134,4820,4821],{"class":3827}," \u002Ftmp\n",[134,4823,4824],{"class":136,"line":239},[134,4825,597],{"emptyLinePlaceholder":596},[134,4827,4828,4831],{"class":136,"line":264},[134,4829,4830],{"class":4565},"ARG",[134,4832,4833],{"class":3827}," JAR_FILE\n",[134,4835,4836,4839],{"class":136,"line":296},[134,4837,4838],{"class":4565},"ADD",[134,4840,4841],{"class":3827}," ${JAR_FILE} app.jar\n",[134,4843,4844,4846,4849],{"class":136,"line":311},[134,4845,4605],{"class":4565},[134,4847,4848],{"class":3827}," sh -c ",[134,4850,4851],{"class":165},"'touch \u002Fapp.jar'\n",[134,4853,4854,4856,4859],{"class":136,"line":317},[134,4855,4639],{"class":4565},[134,4857,4858],{"class":3827}," JAVA_OPTS=",[134,4860,4861],{"class":165},"\"\"\n",[134,4863,4864,4866,4869],{"class":136,"line":322},[134,4865,4639],{"class":4565},[134,4867,4868],{"class":3827}," ENV=",[134,4870,4861],{"class":165},[134,4872,4873,4876,4879,4882,4884,4887,4889,4892],{"class":136,"line":343},[134,4874,4875],{"class":4565},"ENTRYPOINT",[134,4877,4878],{"class":3827}," [ ",[134,4880,4881],{"class":165},"\"sh\"",[134,4883,4655],{"class":3827},[134,4885,4886],{"class":165},"\"-c\"",[134,4888,4655],{"class":3827},[134,4890,4891],{"class":165},"\"java $JAVA_OPTS -Djava.security.egd=file:\u002Fdev\u002F.\u002Furandom -jar \u002Fapp.jar --spring.profiles.active=$ENV\"",[134,4893,4894],{"class":3827}," ]\n",[28,4896,4897,4898,4900],{},"在用阿里云容器服务的时候，这里的",[75,4899,4639],{},"可以直接显示到配置项中进行配置，根据不同的配置选择不同的 profiles 文件。",[28,4902,4903,4906,4907,4910,4911,4914,4915,4918],{},[75,4904,4905],{},"pom.xml","文件中，加入",[75,4908,4909],{},"plugin","，会自动添加版本号和",[75,4912,4913],{},"latest","两个",[75,4916,4917],{},"tag","，并推送。",[86,4920,4924],{"className":4921,"code":4922,"language":4923,"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,4925,4926,4936,4957,4975,4993,5002,5012,5031,5049,5058,5077,5086,5095,5103,5120,5136,5144,5160,5176,5184,5193,5210,5218,5226,5234,5251,5267,5275,5291,5307,5315,5323,5339,5347,5355,5364,5372,5390,5406,5423,5432,5450,5458,5466],{"__ignoreMap":94},[134,4927,4928,4931,4933],{"class":136,"line":137},[134,4929,4930],{"class":140},"\u003C",[134,4932,4909],{"class":572},[134,4934,4935],{"class":140},">\n",[134,4937,4938,4941,4944,4947,4950,4953,4955],{"class":136,"line":144},[134,4939,4940],{"class":140},"    \u003C",[134,4942,4943],{"class":572},"groupId",[134,4945,4946],{"class":140},">",[134,4948,4949],{"class":3827},"com.spotify",[134,4951,4952],{"class":140},"\u003C\u002F",[134,4954,4943],{"class":572},[134,4956,4935],{"class":140},[134,4958,4959,4961,4964,4966,4969,4971,4973],{"class":136,"line":174},[134,4960,4940],{"class":140},[134,4962,4963],{"class":572},"artifactId",[134,4965,4946],{"class":140},[134,4967,4968],{"class":3827},"dockerfile-maven-plugin",[134,4970,4952],{"class":140},[134,4972,4963],{"class":572},[134,4974,4935],{"class":140},[134,4976,4977,4979,4982,4984,4987,4989,4991],{"class":136,"line":218},[134,4978,4940],{"class":140},[134,4980,4981],{"class":572},"version",[134,4983,4946],{"class":140},[134,4985,4986],{"class":3827},"1.4.0",[134,4988,4952],{"class":140},[134,4990,4981],{"class":572},[134,4992,4935],{"class":140},[134,4994,4995,4997,5000],{"class":136,"line":233},[134,4996,4940],{"class":140},[134,4998,4999],{"class":572},"executions",[134,5001,4935],{"class":140},[134,5003,5004,5007,5010],{"class":136,"line":239},[134,5005,5006],{"class":140},"        \u003C",[134,5008,5009],{"class":572},"execution",[134,5011,4935],{"class":140},[134,5013,5014,5017,5020,5022,5025,5027,5029],{"class":136,"line":264},[134,5015,5016],{"class":140},"            \u003C",[134,5018,5019],{"class":572},"id",[134,5021,4946],{"class":140},[134,5023,5024],{"class":3827},"build-image",[134,5026,4952],{"class":140},[134,5028,5019],{"class":572},[134,5030,4935],{"class":140},[134,5032,5033,5035,5038,5040,5043,5045,5047],{"class":136,"line":296},[134,5034,5016],{"class":140},[134,5036,5037],{"class":572},"phase",[134,5039,4946],{"class":140},[134,5041,5042],{"class":3827},"package",[134,5044,4952],{"class":140},[134,5046,5037],{"class":572},[134,5048,4935],{"class":140},[134,5050,5051,5053,5056],{"class":136,"line":311},[134,5052,5016],{"class":140},[134,5054,5055],{"class":572},"goals",[134,5057,4935],{"class":140},[134,5059,5060,5063,5066,5068,5071,5073,5075],{"class":136,"line":317},[134,5061,5062],{"class":140},"                \u003C",[134,5064,5065],{"class":572},"goal",[134,5067,4946],{"class":140},[134,5069,5070],{"class":3827},"build",[134,5072,4952],{"class":140},[134,5074,5065],{"class":572},[134,5076,4935],{"class":140},[134,5078,5079,5082,5084],{"class":136,"line":322},[134,5080,5081],{"class":140},"            \u003C\u002F",[134,5083,5055],{"class":572},[134,5085,4935],{"class":140},[134,5087,5088,5091,5093],{"class":136,"line":343},[134,5089,5090],{"class":140},"        \u003C\u002F",[134,5092,5009],{"class":572},[134,5094,4935],{"class":140},[134,5096,5097,5099,5101],{"class":136,"line":365},[134,5098,5006],{"class":140},[134,5100,5009],{"class":572},[134,5102,4935],{"class":140},[134,5104,5105,5107,5109,5111,5114,5116,5118],{"class":136,"line":385},[134,5106,5016],{"class":140},[134,5108,5019],{"class":572},[134,5110,4946],{"class":140},[134,5112,5113],{"class":3827},"tag-image-version",[134,5115,4952],{"class":140},[134,5117,5019],{"class":572},[134,5119,4935],{"class":140},[134,5121,5122,5124,5126,5128,5130,5132,5134],{"class":136,"line":390},[134,5123,5016],{"class":140},[134,5125,5037],{"class":572},[134,5127,4946],{"class":140},[134,5129,651],{"class":3827},[134,5131,4952],{"class":140},[134,5133,5037],{"class":572},[134,5135,4935],{"class":140},[134,5137,5138,5140,5142],{"class":136,"line":395},[134,5139,5016],{"class":140},[134,5141,5055],{"class":572},[134,5143,4935],{"class":140},[134,5145,5146,5148,5150,5152,5154,5156,5158],{"class":136,"line":416},[134,5147,5062],{"class":140},[134,5149,5065],{"class":572},[134,5151,4946],{"class":140},[134,5153,4917],{"class":3827},[134,5155,4952],{"class":140},[134,5157,5065],{"class":572},[134,5159,4935],{"class":140},[134,5161,5162,5164,5166,5168,5170,5172,5174],{"class":136,"line":448},[134,5163,5062],{"class":140},[134,5165,5065],{"class":572},[134,5167,4946],{"class":140},[134,5169,4022],{"class":3827},[134,5171,4952],{"class":140},[134,5173,5065],{"class":572},[134,5175,4935],{"class":140},[134,5177,5178,5180,5182],{"class":136,"line":465},[134,5179,5081],{"class":140},[134,5181,5055],{"class":572},[134,5183,4935],{"class":140},[134,5185,5186,5188,5191],{"class":136,"line":489},[134,5187,5016],{"class":140},[134,5189,5190],{"class":572},"configuration",[134,5192,4935],{"class":140},[134,5194,5195,5197,5199,5201,5204,5206,5208],{"class":136,"line":495},[134,5196,5062],{"class":140},[134,5198,4917],{"class":572},[134,5200,4946],{"class":140},[134,5202,5203],{"class":3827},"${project.version}",[134,5205,4952],{"class":140},[134,5207,4917],{"class":572},[134,5209,4935],{"class":140},[134,5211,5212,5214,5216],{"class":136,"line":501},[134,5213,5081],{"class":140},[134,5215,5190],{"class":572},[134,5217,4935],{"class":140},[134,5219,5220,5222,5224],{"class":136,"line":522},[134,5221,5090],{"class":140},[134,5223,5009],{"class":572},[134,5225,4935],{"class":140},[134,5227,5228,5230,5232],{"class":136,"line":541},[134,5229,5006],{"class":140},[134,5231,5009],{"class":572},[134,5233,4935],{"class":140},[134,5235,5236,5238,5240,5242,5245,5247,5249],{"class":136,"line":736},[134,5237,5016],{"class":140},[134,5239,5019],{"class":572},[134,5241,4946],{"class":140},[134,5243,5244],{"class":3827},"tag-image-latest",[134,5246,4952],{"class":140},[134,5248,5019],{"class":572},[134,5250,4935],{"class":140},[134,5252,5253,5255,5257,5259,5261,5263,5265],{"class":136,"line":742},[134,5254,5016],{"class":140},[134,5256,5037],{"class":572},[134,5258,4946],{"class":140},[134,5260,651],{"class":3827},[134,5262,4952],{"class":140},[134,5264,5037],{"class":572},[134,5266,4935],{"class":140},[134,5268,5269,5271,5273],{"class":136,"line":748},[134,5270,5016],{"class":140},[134,5272,5055],{"class":572},[134,5274,4935],{"class":140},[134,5276,5277,5279,5281,5283,5285,5287,5289],{"class":136,"line":754},[134,5278,5062],{"class":140},[134,5280,5065],{"class":572},[134,5282,4946],{"class":140},[134,5284,4917],{"class":3827},[134,5286,4952],{"class":140},[134,5288,5065],{"class":572},[134,5290,4935],{"class":140},[134,5292,5293,5295,5297,5299,5301,5303,5305],{"class":136,"line":760},[134,5294,5062],{"class":140},[134,5296,5065],{"class":572},[134,5298,4946],{"class":140},[134,5300,4022],{"class":3827},[134,5302,4952],{"class":140},[134,5304,5065],{"class":572},[134,5306,4935],{"class":140},[134,5308,5309,5311,5313],{"class":136,"line":766},[134,5310,5081],{"class":140},[134,5312,5055],{"class":572},[134,5314,4935],{"class":140},[134,5316,5317,5319,5321],{"class":136,"line":772},[134,5318,5016],{"class":140},[134,5320,5190],{"class":572},[134,5322,4935],{"class":140},[134,5324,5325,5327,5329,5331,5333,5335,5337],{"class":136,"line":778},[134,5326,5062],{"class":140},[134,5328,4917],{"class":572},[134,5330,4946],{"class":140},[134,5332,4913],{"class":3827},[134,5334,4952],{"class":140},[134,5336,4917],{"class":572},[134,5338,4935],{"class":140},[134,5340,5341,5343,5345],{"class":136,"line":784},[134,5342,5081],{"class":140},[134,5344,5190],{"class":572},[134,5346,4935],{"class":140},[134,5348,5349,5351,5353],{"class":136,"line":789},[134,5350,5090],{"class":140},[134,5352,5009],{"class":572},[134,5354,4935],{"class":140},[134,5356,5357,5360,5362],{"class":136,"line":795},[134,5358,5359],{"class":140},"    \u003C\u002F",[134,5361,4999],{"class":572},[134,5363,4935],{"class":140},[134,5365,5366,5368,5370],{"class":136,"line":801},[134,5367,4940],{"class":140},[134,5369,5190],{"class":572},[134,5371,4935],{"class":140},[134,5373,5374,5376,5379,5381,5384,5386,5388],{"class":136,"line":807},[134,5375,5006],{"class":140},[134,5377,5378],{"class":572},"repository",[134,5380,4946],{"class":140},[134,5382,5383],{"class":3827},"registry.cn-hangzhou.aliyuncs.com\u002Fxxx\u002F${project.artifactId}",[134,5385,4952],{"class":140},[134,5387,5378],{"class":572},[134,5389,4935],{"class":140},[134,5391,5392,5394,5396,5398,5400,5402,5404],{"class":136,"line":813},[134,5393,5006],{"class":140},[134,5395,4917],{"class":572},[134,5397,4946],{"class":140},[134,5399,5203],{"class":3827},[134,5401,4952],{"class":140},[134,5403,4917],{"class":572},[134,5405,4935],{"class":140},[134,5407,5408,5410,5413,5415,5417,5419,5421],{"class":136,"line":818},[134,5409,5006],{"class":140},[134,5411,5412],{"class":572},"useMavenSettingsForAuth",[134,5414,4946],{"class":140},[134,5416,69],{"class":3827},[134,5418,4952],{"class":140},[134,5420,5412],{"class":572},[134,5422,4935],{"class":140},[134,5424,5425,5427,5430],{"class":136,"line":823},[134,5426,5006],{"class":140},[134,5428,5429],{"class":572},"buildArgs",[134,5431,4935],{"class":140},[134,5433,5434,5436,5439,5441,5444,5446,5448],{"class":136,"line":829},[134,5435,5016],{"class":140},[134,5437,5438],{"class":572},"JAR_FILE",[134,5440,4946],{"class":140},[134,5442,5443],{"class":3827},"target\u002F${project.build.finalName}.jar",[134,5445,4952],{"class":140},[134,5447,5438],{"class":572},[134,5449,4935],{"class":140},[134,5451,5452,5454,5456],{"class":136,"line":835},[134,5453,5090],{"class":140},[134,5455,5429],{"class":572},[134,5457,4935],{"class":140},[134,5459,5460,5462,5464],{"class":136,"line":841},[134,5461,5359],{"class":140},[134,5463,5190],{"class":572},[134,5465,4935],{"class":140},[134,5467,5468,5470,5472],{"class":136,"line":847},[134,5469,4952],{"class":140},[134,5471,4909],{"class":572},[134,5473,4935],{"class":140},[28,5475,5476,5477,5480],{},"想要直接用",[75,5478,5479],{},"mvn deploy","完成整个部署的话，还需要加一下 Nexus 的发布配置",[86,5482,5484],{"className":4921,"code":5483,"language":4923,"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,5485,5486,5495,5503,5520,5538,5556,5564],{"__ignoreMap":94},[134,5487,5488,5490,5493],{"class":136,"line":137},[134,5489,4930],{"class":140},[134,5491,5492],{"class":572},"distributionManagement",[134,5494,4935],{"class":140},[134,5496,5497,5499,5501],{"class":136,"line":144},[134,5498,4940],{"class":140},[134,5500,5378],{"class":572},[134,5502,4935],{"class":140},[134,5504,5505,5507,5509,5511,5514,5516,5518],{"class":136,"line":174},[134,5506,5006],{"class":140},[134,5508,5019],{"class":572},[134,5510,4946],{"class":140},[134,5512,5513],{"class":3827},"monkey-run-maven-release",[134,5515,4952],{"class":140},[134,5517,5019],{"class":572},[134,5519,4935],{"class":140},[134,5521,5522,5524,5527,5529,5532,5534,5536],{"class":136,"line":218},[134,5523,5006],{"class":140},[134,5525,5526],{"class":572},"name",[134,5528,4946],{"class":140},[134,5530,5531],{"class":3827},"MonkeyRun Maven Release Repository",[134,5533,4952],{"class":140},[134,5535,5526],{"class":572},[134,5537,4935],{"class":140},[134,5539,5540,5542,5545,5547,5550,5552,5554],{"class":136,"line":233},[134,5541,5006],{"class":140},[134,5543,5544],{"class":572},"url",[134,5546,4946],{"class":140},[134,5548,5549],{"class":3827},"你的nexus仓库地址",[134,5551,4952],{"class":140},[134,5553,5544],{"class":572},[134,5555,4935],{"class":140},[134,5557,5558,5560,5562],{"class":136,"line":239},[134,5559,5359],{"class":140},[134,5561,5378],{"class":572},[134,5563,4935],{"class":140},[134,5565,5566,5568,5570],{"class":136,"line":264},[134,5567,4952],{"class":140},[134,5569,5492],{"class":572},[134,5571,4935],{"class":140},[28,5573,5574,5575,5577],{},"阿里云容器上配置好镜像更新重新部署的触发器，之后就是直接",[75,5576,5479],{},"等编译好上传完就发布完成啦！",[1932,5579,5580],{},"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":5582},[],"2018-04-12","Docker 用了很久了，之前 Maven 项目一直用的docker-maven-plugin，但是作者目前已经不推荐使用这种方式了，该项目已经不再更新功能，只提供 bugfix。他们的新项目叫做dockerfile-maven，配置上有些不同，之前一直没时间去更新，最近的一个项目中，采用了最新的插件，中间也踩过不少坑，刚刚终于都搞定了，记录一下。",{},"\u002Fposts\u002F2018\u002Fdockerfile-maven",{"text":4105,"minutes":5588,"time":5589,"words":5590},1.81,108600,362,{"title":4766,"description":5584},{"loc":5586},"posts\u002F2018\u002F20180412.dockerfile-maven",[1954,1957,1955],"天气小雨🌦","cvFl_OQs7LBBHpi45-gE5m-7kKeSxsJyCWuH8fPZRM8",{"id":5598,"title":5599,"body":5600,"class":1939,"cover":3933,"coverSize":1939,"date":5714,"description":5604,"draft":1941,"extension":1942,"hideComments":1941,"location":1939,"meta":5715,"navigation":596,"path":5716,"readingTime":5717,"seo":5722,"sitemap":5723,"stem":5724,"tags":5725,"time":1939,"weather":1939,"__hash__":5726},"posts\u002Fposts\u002F2017\u002F20171102.transfer-blog-to-aliyun-docker.md","Ghost 博客迁移至阿里云 Docker",{"type":25,"value":5601,"toc":5712},[5602,5605,5608,5611,5614,5637,5640,5648,5651,5654,5657,5660,5663,5709],[28,5603,5604],{},"刚刚，将 Ghost 博客迁移到了阿里云 Docker 上。",[28,5606,5607],{},"由于近期网络问题，导致家里的 NAS 已经无法提供 443 端口的服务了，之前的临时解决方案是将 hadb.me 的域名解析到 DigitalOcean 的一台机器上，然后用 nginx 转发到 NAS 的 20443 端口，通过海外的服务器做了中转，访问速度可想而知。并且近期海外网络极不稳定，最终决定还是老老实实备案，迁移到阿里云上来。",[28,5609,5610],{},"近年来，Docker 容器化越来越火，我最近的几个项目也都是通过 Docker 来部署的，非常方便。",[28,5612,5613],{},"域名备案经历了几波周折，提交备案后，阿里云初检未通过，有如下问题：",[45,5615,5616,5625,5628,5631,5634],{},[48,5617,5618,5619,5624],{},"根据要求已经取得备案号的网站最下方必须显示您的备案号，并能链接到工信部网站",[3387,5620,5623],{"href":5621,"rel":5622},"http:\u002F\u002Fwww.miitbeian.gov.cn\u002F",[3391],"www.miitbeian.gov.cn","，目前您网站“monkeyrun.net”最下方备案号无法链接工信部网站，请您修改",[48,5626,5627],{},"根据要求网站名称必须与主办单位名称有一定的关联性。您备案的网站名称“HADB 的博客”与主办单位名称“上海猿奋网络科技有限公司”没有关联性，请修改",[48,5629,5630],{},"根据要求域名持有者必须与主办单位名称一致，经查询您的域名“hadb.me”持有者与您备案信息中“邓斌 ”单位名称\u002F法人姓名不一致，请您先办理域名过户",[48,5632,5633],{},"根据管局要求域名有效期需要大于 6 个月，您的域名“hadb.me”有效期不足 6 个月，请您修改",[48,5635,5636],{},"“邓斌”证件号码在多个单位\u002F个人备案中重复出现多次，根据要求，一个证件号码只能出现在一个单位\u002F个人备案下，请您更换其它证件",[28,5638,5639],{},"问题 1、2 改起来都还好，很快改完了。",[28,5641,5642,5643,5647],{},"问题 3 操作的过程中遇到了一个很蛋疼的问题。域名原先在 Godaddy 上购买的，在过户前，手贱把域名里的持有者信息修改了下，从英文名改成了中文品拼音，然后就尴尬了，Godaddy 禁止域名转出了，锁定期貌似 60 天，后来给 Godaddy 打中文客服，一个妹子客服跟我说可以给",[3387,5644,5646],{"href":5645},"mailto:review60@goaddy.com","review60@goaddy.com","发邮件申请解锁，发了邮件一天没回复。又打电话过去，这次是个男客服接的，他跟我说，这个锁定期是没办法解锁的，巴拉巴拉，口径竟然不一样。后来 Godaddy 的 review60 团队回复我邮件了，说已经解锁了 60 天的锁定期。如果有遇到同样问题的朋友，可以尝试给 review60 团队发邮件就可以解锁了。但是在万网进行域名过户的时候，一直提示“该域名产品暂时不允许转入，无法进行转入操作”，查了下，万网目前不支持.me 域名的新注册和转入。后来就尝试了下直接提交，没有做过户操作，也通过了初审。阿里云这里要求的过户其实是非必要的，只需要把持有者信息修改就可以了。",[28,5649,5650],{},"问题 4 续费了下就可以了。",[28,5652,5653],{},"问题 5，也费了些功夫。几年前上大学时，一个外包项目中用的我自己的身份证作为网站负责人备案的。之前一直没有要求说一个证件号码只能出现在一个备案下，不过既然现在提示了这个问题，那就去处理下。由于这个外包项目已经停止了，并且甲方的网站也已经不做了。所以处理起来很简单，直接登录原备案账号，把备案号注销掉就可以了。",[28,5655,5656],{},"几经周折，备案号终于下来了，接下来开始处理部署的问题。",[28,5658,5659],{},"在阿里云上部署与在自己的机器上部署 Docker 有些区别。",[28,5661,5662],{},"具体流程如下：",[45,5664,5665,5668,5675,5685,5703,5706],{},[48,5666,5667],{},"购买阿里云文件存储 NAS 服务，用来存放 Docker 数据卷",[48,5669,5670,5671,5674],{},"在 ECS 上挂在 NAS，将以前的博客数据复制到 NAS 中的",[75,5672,5673],{},"\u002Fghost-hadb-data","目录下",[48,5676,5677,5678,5681,5682,5684],{},"容器服务中创建 NAS 类型的数据卷",[75,5679,5680],{},"ghost-hadb-data","，指向",[75,5683,5673],{},"目录",[48,5686,5687,5688,5691,5692,5695,5696,5699,5700],{},"创建应用，简单路由配置，将",[75,5689,5690],{},"hadb.me","指向容器端口",[75,5693,5694],{},"2368","，选择刚刚创建的数据卷，容器路径为",[75,5697,5698],{},"\u002Fvar\u002Flib\u002Fghost\u002Fcontent","，在环境变量中配置 url 为",[75,5701,5702],{},"https:\u002F\u002Fhadb.me\u002F",[48,5704,5705],{},"配置负载均衡，添加 https 协议 443 端口监听，导入证书",[48,5707,5708],{},"将域名解析切换到负载均衡 ip 地址",[28,5710,5711],{},"Done！以后可以愉快的写博客啦！",{"title":94,"searchDepth":144,"depth":144,"links":5713},[],"2017-11-02",{},"\u002Fposts\u002F2017\u002Ftransfer-blog-to-aliyun-docker",{"text":5718,"minutes":5719,"time":5720,"words":5721},"6 min read",5.875,352500,1175,{"title":5599,"description":5604},{"loc":5716},"posts\u002F2017\u002F20171102.transfer-blog-to-aliyun-docker",[1954,4148,4149,1957],"Q9OzmZ88ueumMvvOGN6bGncIsjTRbiAg-qgAHmieR4k",1777579134709]