ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Docker安装部署实战指南:从环境准备到容器编排排坑

Docker安装部署实战指南:从环境准备到容器编排排坑 先把这个“Dock”说清楚先说个容易踩的坑。你搜Dock的安装部署网上能翻出两拨东西一拨是苹果Mac电脑底下的那个Dock栏怎么调自动隐藏、怎么改图标大小另一拨是真正的容器引擎Docker用来跑Nginx、MySQL、Redis这些服务。结合热度榜里docker安装部署k8s安装部署jenkins安装部署这些词我判断你要的是后者——哪怕把字母打成了Dock也不影响它火到今天这个程度。Docker能干的事说白了就一句话把你的应用连同它依赖的运行环境一起打包成一个标准盒子扔到哪台机器上都能直接跑不用再为我这环境怎么跟你那儿不一样头疼。它的安装部署并不复杂基础命令也就二三十条但很多人栽在几个隐蔽的地方比如内核版本不对、镜像源没配、权限没处理、容器退出找不着原因。这篇文章就按我从零部署一台机器到跑起真实项目的完整流程来写把我踩过的坑和验证过的命令全部放进来照着敲基本能一次过。这套东西适合谁一是刚接触云原生、准备折腾CI/CD或者Kubernetes的运维和开发Docker是绕不开的第一道门槛二是自己买了个云服务器、想在上面跑个小网站或应用但受够了环境配置的人。老手也能扫一眼命令排查部分当个速查表用。1. 安装前的准备先搞清楚Docker在什么环境下才能跑1.1 系统要求和内核检查Docker对Linux的依赖比大多数人想象得深。它不是像普通软件那样装完就完事而是要借助Linux内核的namespace命名空间、cgroups控制组、overlayfs联合文件系统这些机制来隔离进程和文件。所以内核版本太旧功能就是缺的装上也跑不起来。不同系统版本对应关系大概是这样的操作系统支持版本要求说明Ubuntu内核3.10及以上官方推荐20.04/22.04/24.04老版本Ubuntu 16.04也能装但建议不要用CentOS/RedHat7.x以上内核3.10初始版本可能不够CentOS 7需要额外开启overlay2存储驱动Debian8.0老版本建议升级12.x体验最好WindowsWin10/11 64位必须开启WSL2或Hyper-V只能跑在Docker Desktop里macOS10.14Intel11.0Apple Silicon同样借助Docker Desktop查看Linux内核版本用下面这条命令uname -r如果输出类似3.10.0-1160.el7.x86_64或者5.15.0-91-generic这种3.10以上的版本号基本没问题。CentOS 7还有个隐藏操作——需要额外确认overlay2驱动被正确加载因为老内核默认走的可能是devicemapper性能差一个量级。用docker info可以查看当前存储驱动。1.2 清理旧版本避免新老冲突如果你机器上曾经装过Docker或者系统自带了Podman、containerd这些容器运行时有的Linux发行版确实默认装了一部分建议先清理干净再装新的不然经常出现命令找不到、服务起不来的奇怪问题。# 查看是否装了旧版docker which docker docker --version # 如果存在先停服务再卸载 sudo systemctl stop docker sudo apt-get remove docker docker-engine docker.io containerd runc # Ubuntu/Debian sudo yum remove docker docker-client docker-common docker-engine # CentOS这一步不做也行但如果后面装完docker run hello-world报command not found或者cannot connect to the Docker daemon回来检查这个地方。1.3 选对安装方式官方脚本最省心Docker的安装方式有好几种官方推荐的是设置仓库后用包管理器安装这样后续升级、卸载都方便还有一个Docker官方提供的一键安装脚本适合新手快速上手curl -fsSL https://get.docker.com | bash这个脚本会自动识别系统发行版、配置好官方仓库、装上所有依赖然后启动服务。我试过在Ubuntu 20.04、22.04、Debian 11/12上执行成功率很高。唯一的问题是网络不好的时候下载慢解决办法是给这个脚本指定镜像源curl -fsSL https://get.docker.com | sudo sh -s -- --mirror Aliyun注意用--mirror Aliyun只是下载脚本和安装包时会从国内镜像拉并不等于后面拉镜像就快了。镜像加速是另一回事后面专门讲。2. 主流系统上的完整安装部署2.1 Ubuntu/Debian 系仓库安装法如果你想一步步来不走一键脚本那用仓库安装最标准。先装几个依赖工具sudo apt-get update sudo apt-get install -y ca-certificates curl gnupg lsb-release然后添加Docker官方GPG密钥和仓库sudo install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg sudo chmod ar /etc/apt/keyrings/docker.gpg echo \ deb [arch$(dpkg --print-architecture) signed-by/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) stable | sudo tee /etc/apt/sources.list.d/docker.list /dev/null这里有个坑$(lsb_release -cs)会返回你的系统代号比如jammy。如果你用的是Ubuntu 22.04但内核/软件源是老的那个代号可能解析不对仓库就404了。遇到这种情况直接把代号写死就行。更新源并安装sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin注意现在Docker官方已经把docker-compose插件、buildx构建工具都拆开单独打包了如果后续要跑docker-compose记得把最后的docker-compose-plugin一起装。装完之后启动服务并验证sudo systemctl start docker sudo systemctl enable docker sudo docker run hello-worldhello-world是个只有几KB的小镜像如果输出Hello from Docker!就说明整套流程通了。2.2 CentOS/RHEL 系yum安装法CentOS 7虽然已经过了主流支持期但存量服务器还很多所以单独说。先装依赖sudo yum install -y yum-utils sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo安装Docker CEsudo yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-pluginCentOS 7上如果报缺少container-selinux依赖手动装上再重试sudo yum install -y container-selinux启动服务sudo systemctl start docker sudo systemctl enable dockerCentOS 7有个特殊问题默认存储驱动可能是devicemapper性能很差而且经常出现镜像层损坏。解决办法是在/etc/docker/daemon.json里明确指定用overlay2这个文件一会儿细说。改完重启Docker服务。2.3 Windows 和 macOSDocker Desktop 方案Windows系统不能直接跑Linux容器必须靠WSL2或者Hyper-V在虚拟机里跑一个轻量Linux内核。Docker Desktop就是个图形化的壳帮你在底层自动搞定这些事。安装步骤很简单下载Docker Desktop安装包一般100MB左右双击安装勾选Use WSL 2 instead of Hyper-V这个选项然后重启电脑。装完后打开Docker Desktop设置里把Start Docker Desktop when you sign in勾上平时就不会忘了启动。macOS也是同样思路Apple Silicon芯片M1/M2/M3有专门版下载别下错成Intel版。装完以后Linux容器在后台跑得顺滑基本感知不到虚拟机的存在。Windows下有个常见的坑如果你同时装了VMware WorkstationWSL2和Hyper-V可能跟它冲突导致虚拟机起不来。要么用Docker Desktop的配置改成WSL2后端要么开Hyper-V后VMware得用15.5版本才能共存。2.4 全局配置启动需要的都在 daemon.json装完Docker后绝大多数生产环境的初始化配置都会落在/etc/docker/daemon.json这个文件里。它相当于Docker守护进程的总配置常见项包括镜像加速、日志大小限制、存储驱动、数据目录位置。我自己的服务器上一般长这样{ registry-mirrors: [https://docker.m.daocloud.io], log-driver: json-file, log-opts: { max-size: 50m, max-file: 3 }, data-root: /data/docker, exec-opts: [native.cgroupdriversystemd] }解释一下每一项的作用registry-mirrors镜像加速地址拉镜像的时候会先去这里找找不到再回官方源。这一步非常关键不配置的话国内拉个MySQL镜像都能看到进度条以个位数KB/s在爬。log-driver和log-opts限制容器日志文件大小。没有这个配置一个容器长期跑下来日志文件能占满整个磁盘太常见了。>sudo systemctl restart docker2.5 非root用户权限别整天sudoDocker刚装完只有root用户能直接执行docker命令其他用户要加sudo才能跑。每次都打sudo docker太痛苦正确做法是把当前用户加入docker组sudo usermod -aG docker $USER newgrp docker执行完newgrp docker或者重新登录终端当前用户就能直接敲docker ps了。这里要注意一个安全隐患能操作Docker的用户实际上等同于拥有root权限因为可以挂载宿主机任意目录到容器里再进去改文件。所以在多用户服务器上加docker组的操作要谨慎只给真正需要的人加。3. 基础命令全集从镜像到容器的一次完整理解3.1 镜像相关命令应用的源代码包Docker镜像就是应用运行时的模板它包含了代码、运行时、系统库、配置文件等等。几乎所有操作都从镜像开始。命令作用常见参数/说明docker pull 镜像名:标签从仓库拉取镜像标签不写默认latestdocker images查看本地已有镜像-a查看全部--digests显示摘要docker search 关键词在仓库搜索镜像一般用的少docker rmi 镜像ID/名称删除镜像-f强制删除docker tag 原镜像 新名称:新标签给镜像打标签常用于推送到私有仓库docker save -o 文件.tar 镜像名导出镜像为tar文件离线环境迁移用docker load -i 文件.tar导入tar文件为镜像配合save使用拉镜像举例docker pull nginx:1.25-alpinealpine版本比完整版小很多适合生产环境用完整版的nginx有100多MBalpine版只有不到50MB功能基本一样。在没有外网的环境里docker save和docker load是迁移镜像的救命稻草。删镜像有个坑如果某个镜像正被容器使用哪怕容器已停止直接删会报冲突提示你image is being used by stopped container。需要先删除对应的容器或者用-f强制删但强制删容易留下悬空镜像不推荐。一般流程是先docker rm 容器ID再docker rmi 镜像ID。3.2 容器生命周期命令应用运行时的沙盒镜像跑到内存中就变成了容器。容器的启动、停止、删除是使用频率最高的命令。命令作用说明docker run [参数] 镜像 [命令]创建并启动容器最重要参数极多docker ps查看运行中的容器-a看所有含已停止docker start 容器ID启动已存在的容器不会重新创建docker stop 容器ID优雅停止容器默认等待10秒再killdocker restart 容器ID重启容器等同stopstartdocker rm 容器ID删除容器需先stopdocker logs 容器ID查看容器日志-f实时跟踪docker run是最核心的命令常见参数组合如下docker run -it -d --name mynginx -p 8080:80 -v /data/nginx:/usr/share/nginx/html nginx拆开看每个参数-it用交互模式运行。-t分配一个伪终端-i保持标准输入打开。如果只是想跑后台服务可以只写-d。-d后台运行detached容器启动后回到宿主机命令行。--name给容器起个名字后面操作就不用记那一长串容器ID了。-p 8080:80端口映射。宿主机8080端口转发到容器的80端口。左边是宿主机右边是容器别搞反了。-v /data/nginx:/usr/share/nginx/html数据卷挂载。把宿主机/data/nginx目录直接挂到容器内部那个目录改宿主机文件相当于改容器文件容器删了数据还在。最后一个nginx是镜像名。3.3 进入容器和查看详情排查问题的基础容器跑起来后如果想进去里面看环境、敲命令用execdocker exec -it mynginx bash-it同样表示交互模式。如果容器里没有bash很多alpine镜像只有sh就改成docker exec -it mynginx sh。查看容器详情用inspectdocker inspect mynginx这个命令会输出一长串JSON格式的信息包括容器IP、端口映射、挂载卷、环境变量、启动参数等等。排查问题的时候非常有用尤其是看容器内部的IP地址和环境变量。docker inspect --format{{.NetworkSettings.IPAddress}} mynginx这种--format的写法可以直接提取某个字段不用翻整段JSON。看资源占用用top和statsdocker top mynginx docker statsdocker stats会实时显示每个容器的CPU、内存、网络、磁盘IO情况和宿主机上top的效果类似。排查内存泄漏、CPU飙高问题时第一个就会想到它。3.4 网络和数据卷容器间通信与数据持久化默认情况下Docker容器是隔离的网络环境容器和宿主机、容器和容器之间网络都是独立的。要让外界访问容器里的服务最常用的就是前面提到的-p端口映射。Docker里自带几种网络模式docker network ls默认有bridge、host、none三种。单机部署时用bridge就够了如果想多个容器之间互相访问比如Nginx反向代理后面的Node.js容器可以把它们放到同一个自定义网络里用容器名直接互相ping通docker network create my-net docker run -d --name nginx --network my-net nginx docker run -d --name app --network my-net myapp:latest在自定义网络里app容器可以直接通过nginx这个名字访问Nginx容器不需要查IP这比手动管理IP方便太多了。数据卷Volume是Docker持久化存储的一个官方方案docker volume create>docker run -d \ --name web-nginx \ -p 80:80 \ -v /data/wwwroot:/usr/share/nginx/html:ro \ -v /data/nginx/conf.d:/etc/nginx/conf.d:ro \ nginx:1.25-alpine这里我额外挂载了conf.d目录用来放Nginx的站点配置文件。:ro表示只读挂载宿主机上的文件容器内只能读不能改杜绝了容器里被人篡改的可能。启动完成后用curl -I http://localhost看返回的状态码200就说明服务正常。用docker logs web-nginx可以看访问日志Nginx的访问日志默认输出到标准输出正好被Docker日志机制收集。4.2 部署MySQL并实现数据持久化有状态服务比无状态服务麻烦得多核心就是数据不能丢。这里我演示一个生产环境中比较稳妥的MySQL 8.0部署方案docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD你的密码 \ -e TZAsia/Shanghai \ -v mysql-data:/var/lib/mysql \ -v /etc/localtime:/etc/localtime:ro \ mysql:8.0 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ci几个关键点MYSQL_ROOT_PASSWORD环境变量是初始化时设置root密码用的要设置一个足够复杂的密码。-v mysql-data:/var/lib/mysql这里用的是命名数据卷数据存到宿主机Docker管理的卷里容器删了重建数据还在。TZAsia/Shanghai和挂载/etc/localtime防止容器内时间跟宿主机差8小时。时间错乱会导致日志时间不对排查问题容易误判。最后面的--character-set-server参数不是Docker的而是传给MySQL服务的启动参数。MySQL 8.0默认字符集已经是utf8mb4但显式指定更稳妥。验证服务是否正常docker exec -it mysql8 mysql -uroot -p输入密码后能进入MySQL命令行就说明一切正常。4.3 用docker compose编排多容器当容器变多手动敲docker run就不现实了。docker compose可以把所有容器定义在一个docker-compose.yml文件里一条命令全部搞定。举个NginxMySQL的简单例子version: 3.8 services: web: image: nginx:1.25-alpine ports: - 8080:80 volumes: - /data/wwwroot:/usr/share/nginx/html:ro depends_on: - db db: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: 你的密码 TZ: Asia/Shanghai volumes: - mysql-data:/var/lib/mysql volumes: mysql-data:文件放在项目的根目录然后执行docker compose up -d-d表示后台运行。之后用docker compose ps看服务状态docker compose logs -f看日志docker compose stop停止所有服务不用的环境下线后还能用docker compose down把容器和默认网络一起清掉。第一次用compose的人经常犯一个错误改了docker-compose.yml里的配置后直接执行docker compose up -d发现容器没变化。实际上要加--build参数重新构建或者先down再up才能让改动生效。5. 常见问题与排查技巧实录5.1 拉取镜像非常慢这个问题十有八九是没配镜像加速或者加速地址失效。检查当前配置docker info | grep -A 6 Registry Mirrors如果没有输出说明没配。按前面说的在/etc/docker/daemon.json里加入registry-mirrors重启Docker即可。还有一种情况即使配了加速某些大镜像比如pytorch/pytorch、mysql:latest体积动辄几个GB网络差的时候还是要等。建议尽量选择带alpine或slim标签的镜像体积能小一半以上。5.2 Permission denied 权限被拒绝现象是执行docker ps报错Got permission denied while trying to connect to the Docker daemon socket原因就是当前用户不在docker组里。按前面的usermod -aG docker操作即可。如果加完组还在报错可能是当前终端会话没有刷新组权限重新登录一次就好了。5.3 端口被占用bind地址已在用docker: Error response from daemon: driver failed programming external connectivity意味着宿主机那个端口已经被别的进程占用了常见的有MySQL占了3306、Nginx占了80。用ss -tlnp看看谁占了端口要么换一个宿主机端口要么把占用端口的进程处理掉。5.4 容器启动后立刻退出这是新手最容易蒙圈的问题。用docker ps -a看到容器状态是Exited (0)或者Exited (1)不一定是坏事要分情况如果镜像本身是跑一次性任务的比如hello-world执行完就退出正常。如果确实是常驻服务比如MySQL、Nginx那说明配置有问题。先看日志docker logs 容器IDMySQL报权限、Nginx报配置文件错误、环境变量没配全日志里基本都有明确提示按提示修就好。还有一个常见原因是前台没有进程驻留比如某些Dockerfile的最后是CMD [./start.sh]如果脚本执行完就退出了容器也会跟着退出。这时候可以临时用docker run --entrypoint sh 镜像名进去手动排查。5.5 宿主机重启后容器没自动启动重启服务器后发现服务全挂了这是Docker默认策略的问题。解决方法是启动容器时加--restart参数docker run -d --name nginx --restart unless-stopped nginxunless-stopped的意思是除非你手动docker stop否则Docker服务启动时它也跟着启动。这种策略最适合长期运行的服务。如果是已经创建的容器可以用docker update补上docker update --restart always nginx5.6 daemon.json写错导致Docker起不来改完/etc/docker/daemon.json后执行sudo systemctl restart docker发现服务起不来了也不要慌先看Docker有没有报错信息sudo systemctl status docker sudo journalctl -xu docker.service最常见的问题就是JSON里多了一个逗号、少了一个引号。用这个命令验证JSON格式是否合法python3 -m json.tool /etc/docker/daemon.json如果输出的不是解析后的内容而是报Expecting property name enclosed in double quotes之类的错说明JSON格式有问题改回去再重启即可。5.7 容器日志把磁盘塞满了跑了一段时间后发现服务器磁盘被占满了很大概率就是容器日志。检查方法du -sh /var/lib/docker/containers/*/*.log按前面说的在daemon.json里限制max-size和max-file然后手动清掉已有的大日志truncate -s 0 /var/lib/docker/containers/*/*-json.logtruncate直接清空文件而不删除文件Docker还在写这个文件不能直接rm否则文件句柄还占着磁盘。6. 一段写给自己的经验记录在我维护的几台服务器上Docker现在承担了Nginx、MySQL、Redis、MinIO、Jenkins、监控面板这些常驻服务以及大批跑完即删的构建任务。一年的使用过程中最深的体会是Docker真的把部署这个事从写一堆文档、录视频教人装依赖、配置环境变量变成了给一条run命令或一个docker-compose文件而这个文件可以放进代码库跟项目一起版本化。团队里任何人拿到它都能在五分钟内拉起一个和线上几乎一模一样的环境。但也要说点冷水。容器不等于银弹有状态服务数据库、消息队列在容器里跑对运维的要求不是降低了而是换了种难度——你得理解数据卷、网络、存储驱动这些底层概念才能在出问题时快速定位。尤其是生产环境的数据库如果你没有足够的时间和精力去维护容器化数据库的备份、恢复、高可用方案那我还是建议数据库单独跑在物理机或云厂商托管实例上让Docker先服务好那些无状态应用。最实用的一条经验给每个容器都加上--restart unless-stopped所有日志都限制大小所有数据都落到命名卷或宿主机目录。这三条规矩立住Docker基本能给你省下百分之八十的半夜守护时间。别嫌麻烦等你经历过一次睡一觉起来服务全没了、查了半天发现是日志把磁盘写满了的深夜你就明白这些规矩为什么值得坚持。
返回列表