[{"data":1,"prerenderedAt":557},["ShallowReactive",2],{"\u002Fbefore\u002Fdocker":3,"surround-\u002Fbefore\u002Fdocker":546},{"id":4,"title":5,"body":6,"categories":509,"date":511,"description":512,"draft":513,"extension":514,"image":515,"meta":516,"navigation":517,"path":521,"permalink":522,"published":522,"readingTime":523,"recommend":528,"references":529,"seo":536,"sitemap":537,"stem":538,"tags":539,"type":544,"updated":511,"__hash__":545},"content\u002Fposts\u002Fbefore\u002Fdocker.md","Docker 折腾记：国内镜像源失效之后，我是怎么把环境重新拉起来的",{"type":7,"value":8,"toc":500},"minimark",[9,16,20,23,26,38,41,44,71,81,84,87,112,119,129,132,135,138,143,146,150,153,168,173,176,183,186,189,200,203,206,212,222,229,238,241,247,250,257,260,263,266,269,277,280,283,286,289,292,295,298,305,308,311,342,345,348,351,354,361,367,370,377,380,387,398,401,404,411,441,444,457,460,467,470,477,480,483,486,494,497],[10,11,12],"quote",{},[13,14,15],"p",{},"有些问题并不是你不会用 Docker，而是你刚准备开始用，环境就先给你一拳。",[17,18,19],"h2",{"id":19},"写在前面",[13,21,22],{},"其实最近一段时间，我并没有频繁折腾 Docker。",[13,24,25],{},"本地开发时我基本都挂着网络工具，拉镜像虽然偶尔慢一点，但通常还能忍；真正让我重新把这套东西捡起来，是前几天想把一个项目部署到云服务器上，结果第一步就出了问题：",[27,28,35],"pre",{"className":29,"code":31,"filename":32,"language":33,"meta":34},[30],"language-bash","docker pull xxx\n","terminal","bash","",[36,37,31],"code",{"__ignoreMap":34},[13,39,40],{},"卡住，失败，重试，继续失败。",[13,42,43],{},"那一刻就很真实地意识到，之前那种“Docker 挺省心”的印象，很大程度上建立在本地网络还过得去的前提上。一旦到了国内云服务器环境，很多事情立刻就变味了。",[45,46,49,52],"alert",{"title":47,"type":48},"这篇文章适合谁","warning",[13,50,51],{},"如果你也遇到过下面这些情况，这篇大概能对上胃口：",[53,54,55],"card-list",{},[56,57,58,62,65,68],"ul",{},[59,60,61],"li",{},"本地能拉镜像，服务器就是拉不下来",[59,63,64],{},"Docker 装好了，但一跑项目就开始补配置",[59,66,67],{},"日志不设限制，磁盘迟早被打满",[59,69,70],{},"记不住常用命令，每次都得翻之前的笔记",[13,72,73,74,77],{},"所以这篇文章不打算讲太多“Docker 的伟大原理”，而是更偏向一次真实的整理：",[75,76],"br",{},[78,79,80],"strong",{},"我被环境折腾了一遍之后，最后是怎么把 Docker 重新收拾顺手的。",[17,82,83],{"id":83},"这次到底踩了什么坑",[13,85,86],{},"从 2024 年中开始，国内很多 Docker Hub 镜像加速服务陆续失效，一些原本还能用的高校缓存服务也开始不可用。对平时只在本地开发的人来说，这种变化未必很明显；但只要你开始在国内服务器上部署项目，这个问题几乎绕不过去。",[88,89,90,93,96,99,102,105],"timeline",{},[13,91,92],{},"{之前}",[13,94,95],{},"本地偶尔用 Docker，挂着网络工具，基本没什么感觉。",[13,97,98],{},"{后来}",[13,100,101],{},"想把项目部署到云服务器上，结果镜像拉取失败。",[13,103,104],{},"{再后来}",[13,106,107,108,111],{},"开始重新翻资料、找镜像源、改 ",[36,109,110],{"code":110},"daemon.json","、顺手把日志和 IPv6 也一并收拾了。",[13,113,114,115,118],{},"更烦的还不只是“拉不下来”本身，而是那种很典型的消耗感：你明明不是在解决业务问题，只是想把环境先跑起来，结果一个 ",[36,116,117],{"code":117},"docker pull"," 就能拖走你半天。",[45,120,123,126],{"title":121,"type":122},"先说结论","info",[13,124,125],{},"能用非大陆服务器折腾 Docker，就尽量别在大陆服务器上硬扛。",[13,127,128],{},"不是说完全不能用，而是同样的事情，在不同网络环境下，体验差距会大得离谱。",[13,130,131],{},"很多时候大家不是不会 Docker，而是被外部环境拖住了。再叠加国内云服务器普遍不算大方的带宽，拉镜像这件事就更容易变成耐心测试。",[13,133,134],{},"当然，现实是现实。多数时候机器已经买了，项目也得上，那就只能接受条件有限，然后尽量把配置做得稳一点。",[13,136,137],{},"如果非要用一句话概括 Docker，我自己更喜欢这种不太严谨但够直观的说法：",[10,139,140],{},[13,141,142],{},"Docker 就像一个能搬来搬去的运行盒子。你不需要到一台新机器上再手动配一遍环境，直接把盒子拿过去，启动，就能跑。",[13,144,145],{},"至于镜像、容器、仓库这些概念，真要展开讲当然也能讲很多，但这篇我不准备往教程文的方向写太深。够用就行，先把问题解决，比把概念背熟更重要。",[17,147,149],{"id":148},"装好还不够配置得一次到位","装好还不够，配置得一次到位",[13,151,152],{},"因为后面主要是服务器场景，所以这里还是以 Ubuntu 20.04 为例。",[13,154,155,156,159,160,163,164,167],{},"如果系统里已经有旧版 Docker，稳妥起见可以先卸一下。包名不一定叫 ",[36,157,158],{"code":158},"docker","，也可能是 ",[36,161,162],{"code":162},"docker.io","、",[36,165,166],{"code":166},"docker-engine"," 之类：",[169,170],"copy",{":prompt":171,"code":172},"true","sudo apt-get remove docker docker-engine docker.io containerd runc",[13,174,175],{},"卸不掉也不用太慌，很多时候可以直接继续往下装。安装过程我还是沿用官方源这一套：",[27,177,181],{"className":178,"code":179,"filename":180,"language":33,"meta":34},[30],"sudo apt update\nsudo apt install apt-transport-https ca-certificates curl gnupg-agent software-properties-common\n\ncurl -fsSL https:\u002F\u002Fdownload.docker.com\u002Flinux\u002Fubuntu\u002Fgpg | sudo apt-key add -\n\nsudo add-apt-repository \"deb [arch=amd64] https:\u002F\u002Fdownload.docker.com\u002Flinux\u002Fubuntu $(lsb_release -cs) stable\"\n\nsudo apt update\nsudo apt install docker-ce docker-ce-cli containerd.io\n","install-docker.sh",[36,182,179],{"__ignoreMap":34},[13,184,185],{},"装完之后，先看服务有没有正常起来：",[169,187],{":prompt":171,"code":188},"sudo systemctl status docker",[13,190,191,192,195,196,199],{},"正常情况下会看到 ",[36,193,194],{"code":194},"active (running)","。如果你不想以后每次都在前面加 ",[36,197,198],{"code":198},"sudo","，还可以顺手把当前用户加进 Docker 用户组：",[169,201],{":prompt":171,"code":202},"sudo usermod -aG docker $USER",[13,204,205],{},"然后重新登录一次，让权限生效。",[13,207,208,209,211],{},"不过说实话，真正麻烦的其实不是“装上 Docker”，而是“装完以后别留下隐患”。我现在基本会顺手把 ",[36,210,110],{"code":110}," 一次配好，主要处理三件事：配置镜像源、限制日志大小、顺手打开 IPv6。这里面最值得强调的其实是日志限制，因为这玩意平时不响不动，一旦没管住，最后很容易把磁盘慢慢吃满。",[45,213,216],{"title":214,"type":215},"这是踩过的坑","error",[13,217,218,219,221],{},"Docker 默认日志如果不做限制，真的会把硬盘慢慢吃满。",[75,220],{},"\n这种问题平时不响不动，等你发现的时候，往往已经开始影响服务了。",[13,223,224,225,228],{},"我当时记录下来还能用的镜像源是这些。注意，这只是 ",[78,226,227],{},"2024-11-03 当时的可用情况","，不是永久有效清单：",[27,230,236],{"className":231,"code":233,"filename":234,"language":235,"meta":34},[232],"language-json","{\n  \"registry-mirrors\": [\n    \"https:\u002F\u002Fdocker.m.daocloud.io\",\n    \"https:\u002F\u002Fdockerhub.icu\",\n    \"https:\u002F\u002Fdocker.anyhub.us.kg\",\n    \"https:\u002F\u002Fdocker.1panel.live\"\n  ]\n}\n","mirrors.json","json",[36,237,233],{"__ignoreMap":34},[13,239,240],{},"我最后实际使用的配置如下：",[27,242,245],{"className":243,"code":244,"filename":110,"language":235,"meta":34},[232],"{\n  \"log-driver\": \"json-file\",\n  \"log-opts\": {\n    \"max-size\": \"20m\",\n    \"max-file\": \"3\"\n  },\n  \"ipv6\": true,\n  \"fixed-cidr-v6\": \"fd00:dead:beef:c0::\u002F80\",\n  \"experimental\": true,\n  \"ip6tables\": true,\n  \"registry-mirrors\": [\n    \"https:\u002F\u002Fdocker.m.daocloud.io\",\n    \"https:\u002F\u002Fdockerhub.icu\",\n    \"https:\u002F\u002Fdocker.anyhub.us.kg\",\n    \"https:\u002F\u002Fdocker.1panel.live\"\n  ]\n}\n",[36,246,244],{"__ignoreMap":34},[13,248,249],{},"如果懒得手动编辑，可以直接这样写进去：",[27,251,255],{"className":252,"code":253,"filename":254,"language":33,"meta":34},[30],"cat > \u002Fetc\u002Fdocker\u002Fdaemon.json \u003C\u003C'EOF'\n{\n  \"log-driver\": \"json-file\",\n  \"log-opts\": {\n    \"max-size\": \"20m\",\n    \"max-file\": \"3\"\n  },\n  \"ipv6\": true,\n  \"fixed-cidr-v6\": \"fd00:dead:beef:c0::\u002F80\",\n  \"experimental\": true,\n  \"ip6tables\": true,\n  \"registry-mirrors\": [\n    \"https:\u002F\u002Fdocker.m.daocloud.io\",\n    \"https:\u002F\u002Fdockerhub.icu\",\n    \"https:\u002F\u002Fdocker.anyhub.us.kg\",\n    \"https:\u002F\u002Fdocker.1panel.live\"\n  ]\n}\nEOF\n","write-daemon-json.sh",[36,256,253],{"__ignoreMap":34},[13,258,259],{},"写完之后记得重载并重启：",[169,261],{":prompt":171,"code":262},"sudo systemctl daemon-reload && sudo systemctl restart docker",[13,264,265],{},"当然，镜像站这件事也别抱太大幻想。刚开始遇到拉取失败时，第一反应通常都是“找个国内镜像站不就行了”，但真折腾下来就会发现，很多镜像站只同步热门镜像，稍微冷门一点的就未必有；就算有，也可能限流、限速，高峰期体验很差；再往深一点说，第三方镜像服务的同步和维护策略，普通用户其实也很难完全验证。",[13,267,268],{},"所以我现在的态度很简单：",[10,270,271],{},[13,272,273,274,276],{},"镜像站可以救急，但别把它当成唯一依赖。",[75,275],{},"\n对稳定性要求高的时候，还是得想办法把仓库、网络、部署链路做得更可控。",[13,278,279],{},"顺便提一句，阿里云以前那套镜像源我现在基本不考虑了。老早就有“不再更新”的说法，很多不热门镜像的版本确实偏旧，用起来总让人不踏实。",[17,281,282],{"id":282},"顺手记一份常用命令",[13,284,285],{},"前面解决的是“拉不拉得下来”的问题，下面这部分更像我自己的日常备忘录。不是面面俱到，但基本覆盖了平时最常用的操作。",[13,287,288],{},"先说镜像。查看本地镜像最简单：",[169,290],{":prompt":171,"code":291},"docker images",[169,293],{":prompt":171,"code":294},"docker images -q",[13,296,297],{},"搜索和拉取镜像的命令我最常用下面这几条：",[27,299,303],{"className":300,"code":301,"filename":302,"language":33,"meta":34},[30],"docker search mysql\ndocker search mysql --filter=STARS=3000\ndocker pull mysql\ndocker pull mysql:5.7\n","image-commands.sh",[36,304,301],{"__ignoreMap":34},[13,306,307],{},"删除镜像也很直接：",[169,309],{":prompt":171,"code":310},"docker rmi -f 镜像ID",[13,312,313,314,317,318,321,322,325,326,329,330,333,334,337,338,341],{},"到了容器这一层，最常见的还是 ",[36,315,316],{"code":316},"docker run","。虽然参数很多，但平时高频也就那么几个：",[36,319,320],{"code":320},"--name"," 指定名字，",[36,323,324],{"code":324},"-d"," 后台运行，",[36,327,328],{"code":328},"-it"," 交互进入，",[36,331,332],{"code":332},"-p"," 做端口映射，",[36,335,336],{"code":336},"-v"," 做数据挂载，",[36,339,340],{"code":340},"-e"," 传环境变量。记住这些，日常已经够用了。",[13,343,344],{},"查看容器：",[169,346],{":prompt":171,"code":347},"docker ps",[169,349],{":prompt":171,"code":350},"docker ps -a",[13,352,353],{},"控制容器的生命周期：",[27,355,359],{"className":356,"code":357,"filename":358,"language":33,"meta":34},[30],"docker start 容器ID\ndocker restart 容器ID\ndocker stop 容器ID\ndocker kill 容器ID\ndocker rm 容器ID\n","container-lifecycle.sh",[36,360,357],{"__ignoreMap":34},[13,362,363,364],{},"这里有个很基础但经常忘的点：",[78,365,366],{},"正在运行的容器不能直接删，先停掉。",[13,368,369],{},"查问题时我最常看的三条命令也放一起：",[27,371,375],{"className":372,"code":373,"filename":374,"language":33,"meta":34},[30],"docker logs -tf --tail 10 容器ID\ndocker top 容器ID\ndocker inspect 容器ID\n","inspect.sh",[36,376,373],{"__ignoreMap":34},[13,378,379],{},"进入容器一般我都用：",[27,381,385],{"className":382,"code":383,"filename":384,"language":33,"meta":34},[30],"docker exec -it 容器ID \u002Fbin\u002Fbash\ndocker attach 容器ID\n","enter-container.sh",[36,386,383],{"__ignoreMap":34},[13,388,389,390,393,394,397],{},"其中 ",[36,391,392],{"code":392},"docker exec -it"," 明显更顺手，",[36,395,396],{"code":396},"attach"," 我平时其实用得不多。至于容器和宿主机之间拷文件，也偶尔会用到：",[169,399],{":prompt":171,"code":400},"docker cp 容器ID:\u002Fxxx\u002Faaa.txt \u002Fxxx",[13,402,403],{},"另外，数据挂载这件事我建议别偷懒。很多人刚开始图省事，容器跑起来就行，不挂卷；等容器删了、数据没了，才开始后悔，数据库尤其如此。下面这个 MySQL 例子，基本就是我自己平时会直接照着改的版本：",[27,405,409],{"className":406,"code":407,"filename":408,"language":33,"meta":34},[30],"docker run -d \\\n  -p 3310:3306 \\\n  -v \u002Fhome\u002Fmysql\u002Fconf:\u002Fetc\u002Fmysql\u002Fconf.d \\\n  -v \u002Fhome\u002Fmysql\u002Fdata:\u002Fvar\u002Flib\u002Fmysql \\\n  -e MYSQL_ROOT_PASSWORD=123456 \\\n  --name mysql01 \\\n  mysql:5.7\n","mysql-run.sh",[36,410,407],{"__ignoreMap":34},[45,412,414],{"title":413,"type":122},"这条命令做了什么",[56,415,416,426,429,432,435],{},[59,417,418,419,422,423],{},"把宿主机 ",[36,420,421],{"code":421},"3310"," 映射到容器 ",[36,424,425],{"code":425},"3306",[59,427,428],{},"把 MySQL 配置目录挂出来",[59,430,431],{},"把数据库数据目录挂出来",[59,433,434],{},"设置 root 密码",[59,436,437,438],{},"后台运行，并命名为 ",[36,439,440],{"code":440},"mysql01",[13,442,443],{},"对于本地开发和轻量部署来说，这种方式已经够用了。至少容器删了还能再建，数据不会跟着一起没。",[13,445,446,447,449,450,452,453,456],{},"如果项目稍微复杂一点，比如同时要起 MySQL、Redis、Nginx、前后端服务，那就别继续硬写一长串 ",[36,448,316],{"code":316}," 了。我后来基本默认：单容器临时测试用 ",[36,451,316],{"code":316},"，多容器项目直接上 ",[36,454,455],{"code":455},"docker compose","。最常用的命令也就这一条：",[169,458],{":prompt":171,"code":459},"docker compose up -d",[13,461,462,463,466],{},"关键不在命令本身，而在 ",[36,464,465],{"code":465},"docker-compose.yml"," 写得好不好。小项目手写，大一点的项目参考现成部署方案再改，这样通常更省事。",[13,468,469],{},"如果以后真要彻底卸载 Docker，也可以顺手记一下：",[27,471,475],{"className":472,"code":473,"filename":474,"language":33,"meta":34},[30],"docker container stop $(docker container ls -aq)\ndocker system prune -a --volumes\n\nsudo apt purge docker-ce\nsudo apt autoremove\n","remove-docker.sh",[36,476,473],{"__ignoreMap":34},[17,478,479],{"id":479},"写在最后",[13,481,482],{},"这篇其实不是“我来教你 Docker”，而更像是一次被环境教育之后的收拾现场。",[13,484,485],{},"我原本只是想在服务器上顺手部署个项目，结果从拉镜像开始就一路磕绊。到最后回头看，真正浪费时间的并不是 Docker 本身，而是那些明明不属于业务、却又不得不处理的环境问题。",[10,487,488],{},[13,489,490,491,493],{},"很多技术问题最烦的地方，不是难，",[75,492],{},"\n而是它不创造价值，却持续消耗你。",[13,495,496],{},"所以我现在的做法很简单：只要准备长期用 Docker，就把镜像源、日志限制、卷挂载这些基础项先配好。这样后面真正开始跑项目时，至少不会再被这些低级但高频的小坑反复绊住。",[13,498,499],{},"至于国内 Docker 环境什么时候能重新变得省心一点——老实说，我不太乐观。所以与其等环境变好，不如先把自己的工具链收拾利索。",{"title":34,"searchDepth":501,"depth":501,"links":502},4,[503,505,506,507,508],{"id":19,"depth":504,"text":19},2,{"id":83,"depth":504,"text":83},{"id":148,"depth":504,"text":149},{"id":282,"depth":504,"text":282},{"id":479,"depth":504,"text":479},[510],"技术","2024-11-03 18:00:00","本来只是想把一个项目顺手丢到云服务器上跑起来，结果第一步 `docker pull` 就卡死了。于是又把 Docker 的安装、换源、日志限制和常用命令重新收拾了一遍。",false,"md","https:\u002F\u002Fwmimg.com\u002Fi\u002F1546\u002F2026\u002F03\u002F69ae58ea0b569.webp",{"coverDim":513,"zoom":517,"aside":518,"slots":520},true,[519],"toc",{},"\u002Fbefore\u002Fdocker",null,{"text":524,"minutes":525,"time":526,"words":527},"13 min read",12.73,763800,2546,0,[530,533],{"link":531,"title":532},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Finstall\u002Fubuntu\u002F","Docker 官方文档：在 Ubuntu 上安装 Docker Engine",{"link":534,"title":535},"https:\u002F\u002Fdocs.docker.com\u002Fengine\u002Fdaemon\u002F","Docker 官方文档：配置 Docker daemon",{"title":5,"description":512},{"loc":521},"posts\u002Fbefore\u002Fdocker",[540,541,542,543],"Docker","Linux","部署","踩坑记录","story","z6Ho6qyKKMCBYQ3pj7wJ1hR9i56Fv7BYLqFRSL-ceqQ",[547,552],{"title":548,"path":549,"stem":550,"date":551,"type":544,"children":-1},"路径规划：一个基于 Qt 的 A* 算法演示器","\u002Fbefore\u002Fastar-qt","posts\u002Fbefore\u002Fastar-qt","2024-03-15 22:00:00",{"title":553,"path":554,"stem":555,"date":556,"type":544,"children":-1},"时隔两年，关于博客，我有了新的答案。","\u002F2026\u002Freturn","posts\u002F2026\u002Freturn","2026-03-09 12:00:00",1789965463337]