ARTICLE DETAIL

资讯详情

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

Docker入门指南:从安装到MySQL与Redis实战部署

Docker入门指南:从安装到MySQL与Redis实战部署 1. Docker到底解决了什么问题很多刚接触Docker的人都会被一系列概念绕晕镜像、容器、仓库、Dockerfile、数据卷、Compose……感觉每个词都认识放在一起就不知道在说什么。如果你也是这种状态别着急这篇就是写给你的。我当年刚接触Docker时第一反应是“这不就是个轻量级虚拟机吗”。后来用得多了才明白这个类比对也不对。虚拟机是把一整台电脑包括操作系统都模拟出来而Docker更像个“标准集装箱”——它把应用本身和它运行所需要的所有依赖代码、运行时、系统库、配置文件打包成一个标准化的单元无论丢到哪台机器上都能以完全一致的方式运行起来。举个例子。你在自己电脑上写了个Python脚本本地跑得好好的一部署到服务器就报错最常见的原因是环境不一致服务器缺某个依赖库、系统版本不同、Python版本不对。用Docker打包之后这些环境差异全部被隔离在容器内部你本地跑什么版本服务器上跑的就是什么版本不会再出现“在我的机器上是好的啊”这种尴尬局面。在具体操作之前先把三个最核心的概念说清楚镜像Image可以理解成一个只读的模板里面包含了应用和它的完整运行环境。镜像是分层构建的每一层都是一些文件系统的变更记录。容器Container镜像运行起来之后的实例。一个镜像可以启动多个容器比如同一个镜像同时跑3个副本每个容器互相隔离、互不影响。仓库Registry存放镜像的地方。Docker官方有一个公共仓库叫Docker Hub大家在里面上传、下载镜像。这三个概念的关系用饭店点餐来类比就很好懂镜像就是中央厨房预制好的半成品菜包容器就是把菜包下锅炒出来的那份菜仓库就是存放菜包的冷链仓库。你想吃哪道菜直接从仓库调货下锅炒熟端上桌就能吃。这篇“入门基础知识(一)”会从安装开始讲覆盖镜像拉取、容器启停、常用命令、数据持久化最后用MySQL和Redis两个实战案例把知识串起来。2. 环境准备Windows和Linux两种安装方式2.1 Windows安装Docker DesktopWindows用户目前主流的方案是安装Docker Desktop它是Docker官方推出的图形化管理工具集成了Docker引擎、命令行工具和可视化界面对新手非常友好。Docker Desktop的安装过程本身不复杂去官网下载安装包双击安装即可但有两个前置条件经常出问题第一Windows系统版本必须是64位的Windows 10或Windows 11并且要开启WSL 2Windows Subsystem for Linux适用于Linux的Windows子系统。Docker Desktop默认依赖WSL 2来运行Linux容器。第二必须开启硬件虚拟化。如果安装后启动时提示“Virtualization support not detected”之类的报错基本就是BIOS里的虚拟化技术Intel VT-x或AMD-V没有开启。需要重启电脑进入BIOS设置界面找到Intel Virtualization Technology或SVM Mode之类的选项把它改为Enabled保存退出后再启动Docker Desktop。一个实用的建议在安装Docker Desktop之前先在PowerShell管理员权限里执行如下命令确认WSL 2是否就绪wsl --status wsl --set-default-version 2如果系统还没有WSL可以用下面这行命令一键安装wsl --install安装完成后需要重启系统。这一步建议优先搞定否则后续Docker Desktop启动会反复报错。Docker Desktop安装好后还需要做一个关键设置在Settings - Resources里调整内存分配。默认情况下Docker Desktop只分配2GB内存如果你要同时跑MySQL、Redis、GitLab这些容器建议调到4GB以上否则机器会明显卡顿甚至出现容器自动崩溃的情况。2.2 Ubuntu服务器安装Docker Engine服务器场景下我们一般不用Docker Desktop而是直接安装Docker Engine。以Ubuntu为例最推荐使用官方维护的apt仓库来安装这样后续升级也方便。先把依赖装好并添加官方GPG密钥和软件源sudo apt update sudo apt install -y ca-certificates curl gnupg lsb-release 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接着安装Docker引擎和配套工具sudo apt update sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin安装完成后执行下面这条命令验证是否成功sudo docker run hello-world如果能看到一段“Hello from Docker!”的输出说明Docker已经正常工作。这条命令会先拉取一个测试镜像然后启动容器并输出信息。这里有个细节值得说一下hello-world这个镜像非常小拉取速度快是验证Docker环境是否可用的最佳测试品。如果这条命令失败那大概率是网络问题或Docker服务没有启动。2.3 解决权限问题不用每次输入sudoUbuntu安装完Docker后每次执行docker命令都需要加sudo否则会报“permission denied while trying to connect to the Docker daemon socket”。原因是Docker守护进程默认只允许root用户和docker用户组访问。如果你不想每次都敲sudo可以把自己加入docker用户组sudo usermod -aG docker $USER执行完之后必须注销当前会话再重新登录或者重启虚拟机组权限才会生效。不过这里要提个醒把用户加入docker组等价于给这个用户root级别的系统权限。因为docker命令本质上可以操作宿主机的所有文件系统。所以这个操作适合在开发环境、个人服务器上做生产环境的服务器要谨慎。2.4 服务启动失败的排查思路如果你的Docker服务启动失败先别急着卸载重装。按下面的顺序来排查第一步查看服务状态sudo systemctl status docker第二步查看具体的错误日志sudo journalctl -u docker.service --no-pager -n 50最常见的原因是containerd没有启动或者配置文件daemon.json写错了。如果是配置写错Docker启动时会直接报JSON解析错误这时候检查一下/etc/docker/daemon.json的格式就对了。还有一类情况是端口冲突。Docker在启动时会尝试绑定系统端口比如后续我们运行容器时映射的3306、6379等端口如果这些端口已经被宿主机上的程序占用容器会启动失败。解决办法是换个宿主端口或者先停掉占用端口的程序。注意修改了daemon.json之后一定要重启Docker服务才会生效命令是 sudo systemctl restart docker。只重载配置文件的命令 systemctl daemon-reload 在这里不顶用。3. 镜像与仓库Docker世界的核心操作3.1 镜像源配置摆脱拉取慢的烦恼很多新手第一次用docker pull拉镜像时会遇到下载很慢甚至卡住不动的情况。这是因为默认访问的是Docker Hub官方仓库网络链路跨越大半个地球速度自然不理想。解决办法是配置国内可用的公共镜像站点。Docker支持通过daemon.json配置多个镜像源Docker会按顺序尝试拉取。编辑/etc/docker/daemon.jsonWindows的Docker Desktop用户在Settings - Docker Engine里修改{ registry-mirrors: [ https://docker.1ms.run, https://docker.xuanyuan.me ] }这里我列了两个目前还算稳定的站点你可以把它们替换成自己实际可用的。保存文件后重启Docker服务。配置好之后可以用这个命令验证是否生效docker info在输出内容里找到Registry Mirrors那一栏如果能看到刚才配置的地址就说明镜像源配置成功了。3.2 镜像的拉取与查看拉取镜像是Docker使用频率最高的操作之一。基本语法是docker pull 镜像名:标签标签tag用来区分同一个镜像的不同版本。官方镜像一般用latest表示最新版但实际项目里强烈建议指定具体版本号比如mysql:8.0。因为在生产环境中镜像版本漂移带来的隐性风险非常大——你以为拉的是同一个镜像实际上内容已经变了。拉取完成后可以用下列命令查看本地已有哪些镜像docker images这个命令会展示镜像的仓库名、标签、镜像ID、创建时间和大小。镜像ID是唯一的后续操作里可以用它代替镜像名来引用镜像。3.3 容器的创建、运行与删除镜像本身只是静态的模板真正干活的是容器。运行一个容器用docker run命令docker run --name my-nginx -d -p 8080:80 nginx这条命令的每个参数都值得解释一下--name my-nginx给容器起一个名字方便后续通过名字管理容器。如果不指定Docker会自动生成一个随机名字。-d后台运行模式容器启动后终端不会占用当前命令行。-p 8080:80端口映射把宿主机的8080端口映射到容器内的80端口。访问宿主机IP:8080就相当于访问容器内的80端口。nginx指定使用的镜像。如果本地不存在Docker会自动去仓库拉取。运行起来之后访问 http://localhost:8080能看到Nginx的欢迎页面就说明你的第一个容器已经正常工作。查看正在运行的容器docker ps查看所有容器包括已停止的docker ps -a停止和启动容器docker stop my-nginx docker start my-nginx进入容器内部用于调试docker exec -it my-nginx bash-it的意思是开启一个交互式终端进入之后你就拥有一台完整的Linux环境但只有这个容器内的文件系统。调试完输入exit退出。删除容器docker rm my-nginx删除镜像docker rmi nginx注意删除容器前要确保容器已停止否则会报错。docker rm -f 可以直接强制删除运行中的容器但属于“暴力操作”有数据丢失风险不推荐平时用。3.4 日志查看与实时监控容器本质上是一台迷你服务器运行中的应用会输出日志。查看日志用docker logs my-nginx实时跟踪日志类似Linux下的tail -fdocker logs -f my-nginx查看容器内的进程、资源占用情况docker top my-nginx docker statsdocker stats是个很有用的命令它实时显示所有容器的CPU、内存、网络I/O使用情况。服务器上如果有多个容器在跑想快速定位谁在“吃内存”这个命令最直观。4. 实战案例用Docker部署MySQL 8.0基础知识讲完下面进入正题——用Docker部署MySQL 8.0。这是很多初学者第一个真正意义上用起来的容器化应用里面涉及数据持久化、环境变量配置、端口映射、字符集设置等核心知识点。4.1 为什么要用Docker跑MySQL传统方式在Ubuntu上装MySQL要走一堆步骤apt安装、初始化数据库、设置密码、开启远程访问、配置字符集……装完还要手动注册成systemd服务。用Docker只需要一条命令拉镜像一条命令启动容器就搞定了。而且Docker版本的MySQL和宿主机完全隔离卸载的时候直接删容器、删镜像不留任何残留文件。不像某些apt安装的软件卸载完还能在/var/lib里找到一堆历史数据。4.2 完整部署步骤拉取镜像docker pull mysql:8.0运行MySQL容器docker run -d \ --name mysql8 \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123456 \ -e TZAsia/Shanghai \ -v /opt/mysql/data:/var/lib/mysql \ -v /etc/localtime:/etc/localtime:ro \ mysql:8.0 \ --character-set-serverutf8mb4 \ --collation-serverutf8mb4_unicode_ci这里补充说明一下几个关键参数背后的逻辑-e MYSQL_ROOT_PASSWORDroot123456是设置MySQL root用户的密码。注意这个环境变量只在容器第一次启动时生效也就是说如果你的数据目录已经初始化过之后再改这个环境变量是不会修改root密码的。很多新手在这里踩坑其实要修改密码还是得进入容器内部执行SQL语句。-v /opt/mysql/data:/var/lib/mysql是数据卷挂载把容器内部的MySQL数据目录映射到宿主机。这一步至关重要——如果不做挂载容器一旦被删除你数据库里的所有数据都会跟着消失。--character-set-serverutf8mb4和--collation-serverutf8mb4_unicode_ci是设置MySQL的默认字符集。utf8mb4是完整的UTF-8实现支持存储emoji表情这是现在的主流选择。很多老项目还在用utf8存中文没问题一存生僻字或emoji就报错就是因为没有用utf8mb4。启动完成后验证容器是否正常运行docker ps看到mysql8容器状态为Up再通过客户端连接测试docker exec -it mysql8 mysql -uroot -p输入密码后执行SHOW VARIABLES LIKE character%;可以看到character_set_server已经是utf8mb4。4.3 数据持久化与容器生命周期管理上面的部署方案里我特意把数据目录挂载到了宿主机的/opt/mysql/data。这样做的好处是容器删除重建数据不丢失。升级镜像版本时先把旧容器停掉用新镜像重新跑一个容器数据挂载到同一目录无缝衔接。备份数据方便。直接打包宿主机上的/opt/mysql/data目录就行不需要进容器操作。排查问题方便。某些情况下需要直接查看数据文件宿主机上就能看到。对应的完整生命周期管理流程大致是这样# 停止容器 docker stop mysql8 # 删除容器数据不会丢因为数据在宿主机目录里 docker rm mysql8 # 用新镜像重新创建容器挂载点保持一致 docker run -d --name mysql8 -p 3306:3306 \ -e MYSQL_ROOT_PASSWORDroot123456 \ -v /opt/mysql/data:/var/lib/mysql \ mysql:8.04.4 修改MySQL配置的两种方式MySQL的配置文件在容器内是/etc/my.cnf。如果你想调整配置比如关闭MySQL的DNS反向解析、设置最大连接数有两种方式方式一宿主机准备配置文件启动时挂载进去-v /opt/mysql/my.cnf:/etc/my.cnf方式二进入容器直接修改然后重启容器docker exec -it mysql8 bash vi /etc/my.cnf exit docker restart mysql8生产环境推荐方式一因为配置文件和部署命令统一管理换机器重新部署的时候不容易漏东西。5. 实战案例二用Docker Compose部署Redis主从5.1 为什么推荐Docker Compose前面的MySQL是我们一条命令手动启动的当容器数量变多、配置变复杂之后手动敲docker run管理所有的参数会很痛苦。Docker Compose就是用来解决这个问题的——它用一个YAML文件描述整个应用服务一条命令完成所有容器的启动和编排。打个比方docker run像是在炒一盘菜而Docker Compose是提前定好的整套宴席菜单厨师照着菜单同时开工所有菜一起上桌。我们要部署Redis主从架构一共3个节点用Compose描述再合适不过。5.2 准备Redis配置文件先准备3个Redis节点的配置文件。主节点redis-master.confport 6379 appendonly yes protected-mode no从节点redis-slave-1.confport 6379 appendonly yes protected-mode no replicaof redis-master 6379从节点redis-slave-2.confport 6379 appendonly yes protected-mode no replicaof redis-master 6379这里的replicaof后面跟的是主节点在Docker Compose网络里的服务名不是IP地址。这是Compose部署和普通部署的核心区别——Compose会自动创建一个内部网络容器之间通过服务名互相访问。5.3 编写docker-compose.yml新建一个目录比如redis-cluster在里面创建docker-compose.ymlversion: 3.8 services: redis-master: image: redis:7.0 container_name: redis-master restart: always ports: - 6379:6379 volumes: - ./redis-master.conf:/usr/local/etc/redis/redis.conf command: [redis-server, /usr/local/etc/redis/redis.conf] redis-slave-1: image: redis:7.0 container_name: redis-slave-1 restart: always ports: - 6380:6379 volumes: - ./redis-slave-1.conf:/usr/local/etc/redis/redis.conf command: [redis-server, /usr/local/etc/redis/redis.conf] depends_on: - redis-master redis-slave-2: image: redis:7.0 container_name: redis-slave-2 restart: always ports: - 6381:6379 volumes: - ./redis-slave-2.conf:/usr/local/etc/redis/redis.conf command: [redis-server, /usr/local/etc/redis/redis.conf] depends_on: - redis-master启动命令就两条docker compose up -d查看状态docker compose ps5.4 验证主从同步进入主节点写入一条数据docker exec -it redis-master redis-cli set name docker然后进入任意一个从节点读取这条数据docker exec -it redis-slave-1 redis-cli get name如果返回docker说明主从同步已经正常工作。这个验证过程虽然简单但能确认整条链路是通的。从节点默认是只读的你可以试试在从节点执行set命令会收到(error) READONLY错误提示这是正常现象。使用Docker Compose的另外一层好处是如果某个从节点挂了重启之后无需手动干预它会自动从主节点同步数据拓扑关系通过配置文件固化下来了。6. 常见问题与排查技巧速查表实操过程中新手遇到的报错翻来覆去就那么几类。下面把高频问题整理成一张速查表建议收藏备用。问题现象可能原因排查方法Docker Desktop启动报Virtualization support not detectedBIOS未开启硬件虚拟化重启进BIOS开启Intel VT-x或AMD SVMdocker命令提示permission denied当前用户不在docker用户组执行 sudo usermod -aG docker $USER重新登录容器启动几秒后自动退出应用自身挂掉或前台进程退出查看docker logs 容器名确认应用报错信息docker pull 拉取镜像非常慢网络链路问题配置公共镜像站点重启Docker端口无法访问防火墙拦截或端口未映射检查docker ps端口映射检查系统防火墙规则容器删了数据也丢了未挂载数据卷启动容器时加-v挂载宿主目录MySQL容器启动报权限错误宿主机挂载目录权限不对chmod -R 777 /opt/mysql/data 或chown到对应用户容器时间不对容器时区是UTC-e TZAsia/Shanghai 也挂载 /etc/localtime再说几个不太容易碰到、但碰到就很头疼的问题Docker守护进程崩溃后容器状态异常。遇到这种情况先执行sudo systemctl restart docker让守护进程重新读取当前的状态。大部分容器的数据不会丢只要数据卷在。docker-compose版本语法问题。新版ComposeV2建议直接在yml文件顶部不写version字段直接用service顶层关键字。网上很多老教程里的version: 2写法在新版本里虽然兼容但没必要。宿主机代理问题。如果你在开发机上配了系统代理有时候反而会导致Docker拉取镜像失败或者容器内应用无法正常进行网络访问。排查的时候可以先把代理临时关掉对比测试一下是不是代理导致的。磁盘空间不足。Docker运行一段时间后/var/lib/docker目录会越来越大包含镜像、容器层、数据卷、构建缓存。定期用docker system prune -a清理不需要的镜像和缓存这个命令可以腾出大量磁盘空间。关于最后一点我再详细说说。Docker在构建镜像和运行容器的过程中会产生很多“悬空镜像”和中间层数据时间一长占用空间非常可观。我见过一台服务器上只跑了一个GitLab容器结果/var/lib/docker占了30多GB的情况。定期执行docker system df查看磁盘占用docker system prune清理无用数据是服务器运维的基本功课。7. 我的几点实操心得写到这里基础部分的内容基本讲完了。结合我自己这几年用Docker的经验再说几点实用心得。第一次用Docker不建议一上来就搞复杂的编排先跑通一个nginx或hello-world把镜像、容器、端口映射这些核心概念弄明白再逐步增加数据卷、环境变量、自定义配置这些维度最后再碰Compose。这个循序渐进的过程比看十篇教程都管用。镜像版本号一定要固定。标签不要用latest尤其在生产环境用latest等于给自己埋雷。某天你执行docker pull拉到的镜像可能和昨天运行的版本有差异而你自己完全没有感知。一定要在部署脚本里写明具体版本号比如mysql:8.0.33、redis:7.0.12这样每次部署的结果才是确定的。数据卷挂载路径建议统一规划。我自己的习惯是在宿主机上建立/opt/docker/{项目名}/{服务名}/{data|conf|logs}这样的目录结构一个项目一个主目录方便备份也方便删除重建。服务器上容器多了之后目录规划混乱会让运维变得非常痛苦。最后再强调一句Docker虽然很好地隔离了应用环境但虚拟化的资源占用是真实存在的。在低配服务器上跑多个容器之前先用docker stats看看内存和CPU的实际消耗再决定要不要继续加容器别等服务器卡死了才回头找原因。下一篇我会继续聊Dockerfile的编写、镜像构建的原理以及如何把你的应用打包成可供分发的镜像这些内容才真正算得上是Docker使用的进阶技能。
返回列表