1. 为什么在Ubuntu上安装Docker和Docker-Compose是开发者的必修课如果你正在使用Ubuntu进行开发无论是做后端服务、机器学习还是Web应用迟早会碰到一个场景你的代码在本地跑得好好的一到服务器或者同事的电脑上就出问题。环境不一致、依赖冲突、系统库版本差异这些“玄学”问题消耗了开发者大量的调试时间。Docker的出现就是为了解决这个痛点。它通过容器化技术将应用及其所有依赖打包成一个标准化的单元确保在任何地方都能以相同的方式运行。而Docker-Compose则是一个用于定义和运行多容器Docker应用的工具用一个简单的YAML文件就能管理一套复杂的服务栈比如一个典型的Web应用可能包含Nginx、后端API、数据库和缓存服务。在Ubuntu上手动搭建这套环境虽然网上教程很多但新手很容易在几个关键步骤上踩坑比如系统内核版本与Docker的兼容性、镜像源配置不当导致下载龟速、用户权限配置错误导致每次都要sudo、以及Docker-Compose版本选择不当引发的兼容性问题。我见过不少人在安装后运行docker ps都报权限错误或者docker-compose up时因为版本太旧而语法不支持。这篇文章我会结合我多次在从Ubuntu 18.04到22.04 LTS各种版本上部署的经验手把手带你走一遍最稳妥、最高效的安装和配置流程并重点解释每个步骤背后的原因帮你避开那些隐形的坑。2. 安装前的关键准备系统环境与依赖检查很多教程一上来就让你执行安装命令但忽略了一个至关重要的前提你的系统环境是否真的准备好了盲目执行命令很可能在中间步骤报错而错误信息又往往令人困惑。我们先花几分钟做好准备工作这能节省后面大量的排查时间。2.1 确认你的Ubuntu版本与架构首先打开终端。虽然听起来很基础但Docker对64位系统的支持是最完善的。运行以下命令lsb_release -a uname -mlsb_release -a会输出类似“Ubuntu 22.04 LTS”的信息请务必确认你的版本。Docker官方支持当前的LTS长期支持版本和最近的非LTS版本。对于uname -m你需要看到x86_64、amd64或aarch64ARM架构这样的输出。i386或i686意味着是32位系统官方Docker Engine不再支持你需要考虑其他方案或升级系统。注意虽然Ubuntu 22.04 LTS是目前最主流的版本但如果你使用的是20.04甚至18.04安装过程也大同小异只是某些软件源配置可能略有不同后续步骤中我会指出差异点。2.2 卸载可能存在的旧版本或冲突软件这是一个极易被忽略但可能导致各种诡异问题的步骤。如果你的系统之前尝试安装过Docker比如通过snap或者老旧的docker.io包或者有残留的配置最好先清理干净。sudo apt-get remove docker docker-engine docker.io containerd runc这条命令会移除通过apt安装的旧版本。但仅仅这样还不够Docker的数据如镜像、容器通常存放在/var/lib/docker/配置可能在/etc/docker/。如果你确定不需要这些数据可以手动删除它们sudo rm -rf /var/lib/docker /etc/docker。不过更安全的做法是先备份再删除或者暂时不删等新安装成功后再处理。2.3 安装必要的工具和配置APT软件源为了让apt能通过HTTPS使用软件仓库我们需要先安装一些基础工具包sudo apt-get update sudo apt-get install \ ca-certificates \ curl \ gnupg \ lsb-release接下来是最关键的一步添加Docker的官方GPG密钥和软件源。很多国内用户觉得Docker官方源慢会选择国内镜像源这本身没问题但必须确保源的可靠性和与系统版本的匹配。这里我推荐使用官方源然后通过后续配置镜像加速器来解决速度问题这样最稳妥。添加Docker的官方GPG密钥。这个密钥用于验证下载的软件包是否来自Docker官方确保安全。sudo mkdir -p /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg这条命令创建了密钥环目录并下载、导入密钥。设置Docker的稳定版stable仓库。这里需要根据你的系统架构通常是x86_64/amd64来正确设置。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让我拆解一下这个命令arch$(dpkg --print-architecture)自动获取你的系统架构。signed-by/etc/apt/keyrings/docker.gpg指定验证软件包签名的密钥。https://download.docker.com/linux/ubuntuDocker官方软件源地址。$(lsb_release -cs)自动获取你的Ubuntu代号例如jammy对应22.04focal对应20.04。这是确保源与系统版本匹配的关键手动写错代号会导致apt update失败。执行完这两步后务必再次更新APT软件包索引让系统识别新添加的Docker源sudo apt-get update如果这一步没有报错并且你能在更新列表中看到来自download.docker.com的源信息那么准备工作就圆满完成了。3. 安装Docker Engine选择最适合你的版本现在来到了核心的安装环节。Docker Engine有不同的版本对于绝大多数个人开发者和测试环境我们安装最新的稳定版Stable即可。但对于生产环境你可能需要考虑与特定Kubernetes版本兼容的Docker版本。3.1 安装最新稳定版Docker Engine运行以下命令安装Docker Engine、命令行工具docker-cli以及容器运行时containerdsudo apt-get install docker-ce docker-ce-cli containerd.io docker-compose-plugin注意这个命令里的docker-compose-plugin。从Docker v20.10.0开始官方推荐将Docker Compose作为Docker CLI的一个插件docker compose来安装和使用而不是独立的docker-compose二进制文件。我们这里先装上这个插件后面会详细说明它与传统docker-compose的区别和用法。安装过程可能会提示你确认磁盘空间占用输入Y并回车继续。安装完成后Docker服务dockerd会自动启动并且设置为开机自启。你可以通过以下命令验证服务状态sudo systemctl status docker你应该看到绿色的“active (running)”状态。如果状态不是active可以尝试手动启动sudo systemctl start docker。3.2 验证安装与运行你的第一个容器安装成功不代表一切就绪。我们来运行Docker的“Hello World”镜像这是最直接的验证方式sudo docker run hello-world这条命令会做以下几件事Docker客户端联系Docker守护进程服务。守护进程发现本地没有hello-world镜像于是从默认的Docker Hub仓库拉取pull它。拉取成功后守护进程根据这个镜像创建一个新的容器并运行。容器执行一个简单的程序在终端打印出一段欢迎信息然后退出。如果你看到类似“Hello from Docker!”的输出并且下面解释了Docker的各个步骤那么恭喜你Docker Engine已经成功安装并运行了3.3 解决权限问题将用户加入docker组你可能注意到了上面的命令都加了sudo。这是因为Docker守护进程默认以root用户运行而Docker CLI通过Unix socket/var/run/docker.sock与守护进程通信该socket默认属于root用户组。为了每次运行docker命令时不输入sudo我们需要将你的普通用户添加到docker用户组中。sudo usermod -aG docker $USER重要提示执行此命令后必须完全注销当前用户并重新登录或者重启系统组权限变更才会生效。仅仅新开一个终端标签页是不够的。生效后你就可以直接运行docker ps、docker images等命令而不再需要sudo了。实操心得这是新手最容易困惑的一点。很多人添加用户组后立刻测试发现还要sudo就以为命令没生效。务必记得重新登录。另外从安全角度考虑将用户加入docker组等同于赋予了该用户root权限因为容器可以挂载主机目录、操作网络等所以在多用户共享的系统上需谨慎操作。4. 配置Docker镜像加速与守护进程调优基础的Docker安装好了但默认配置可能并不适合我们尤其是在国内网络环境下。直接使用Docker Hub拉取镜像速度可能很慢。此外默认的磁盘空间分配、日志驱动等设置也可能需要根据你的硬盘情况调整。4.1 配置国内镜像加速器镜像加速器通过将Docker Hub的镜像缓存到国内的服务器上来大幅提升拉取速度。国内常用的有阿里云、腾讯云、中科大等提供的加速器。这里以阿里云为例你需要有一个阿里云账号来获取专属加速器地址。登录阿里云容器镜像服务控制台。在左侧菜单找到“镜像工具” - “镜像加速器”。选择你的操作系统Linux你会看到属于你的专属加速器地址形如https://xxxx.mirror.aliyuncs.com以及配置步骤。我们需要修改Docker守护进程的配置文件/etc/docker/daemon.json。如果文件不存在就创建它。sudo tee /etc/docker/daemon.json -EOF { registry-mirrors: [https://你的专属加速器地址.mirror.aliyuncs.com], log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 } } EOF这个配置做了两件事registry-mirrors: 设置了镜像加速器地址。你可以配置多个用逗号分隔例如同时加上中科大的源[https://你的阿里云地址.mirror.aliyuncs.com, https://docker.mirrors.ustc.edu.cn]。Docker会按顺序尝试。log-driver和log-opts: 配置了Docker容器的日志驱动为json-file并限制了单个日志文件最大100MB最多保留3个文件。这可以防止容器日志无限增长撑爆磁盘。4.2 使配置生效并验证修改完配置后需要重启Docker服务才能使更改生效sudo systemctl daemon-reload sudo systemctl restart docker重启后运行docker info在输出结果中查找Registry Mirrors部分你应该能看到你刚刚配置的加速器地址。为了测试加速效果可以尝试拉取一个较大的镜像比如ubuntu:latest感受一下速度的提升。docker pull ubuntu:latest4.3 管理Docker磁盘空间可选但重要Docker默认将所有镜像、容器、卷和网络配置存储在/var/lib/docker/目录下。随着时间的推移这个目录可能会变得非常庞大。你可以使用以下命令查看Docker的磁盘使用情况docker system df这个命令会清晰地显示镜像、容器、本地卷和构建缓存各占用了多少空间。如果空间紧张可以运行docker system prune来清理所有未被使用的镜像、容器、网络和构建缓存系统会提示你确认。如果你想更激进地清理包括停止的容器和所有未被任何容器引用的镜像可以使用docker system prune -a但使用前请确保你真的不需要那些镜像了。对于更高级的磁盘管理比如修改默认存储路径到更大的磁盘分区需要修改Docker的存储驱动配置这涉及更复杂的操作在初期可以暂不处理但需要心中有数。5. 安装与使用Docker-Compose从独立工具到官方插件Docker-Compose是一个定义和运行多容器应用的工具。早期它是一个独立的Python工具docker-compose现在Docker官方将其集成为了CLI插件docker compose。两者命令相似一个是横杠-一个是空格 但底层实现和特性支持有差异。官方推荐使用插件版本。5.1 安装Docker Compose插件如果你在第3.1步已经安装了docker-compose-plugin那么插件实际上已经就绪了。你可以通过以下命令验证docker compose version如果输出了版本号如Docker Compose version v2.17.2说明插件安装成功。如果提示命令未找到请确保已安装该插件包sudo apt-get install docker-compose-plugin。5.2 安装传统独立的docker-compose备选方案尽管官方推荐插件但仍有大量现有的脚本、教程或CI/CD流程在使用docker-compose带横杠命令。为了兼容性你也可以安装它。注意不要与插件版本混淆它们可以共存。从Docker Compose的GitHub Release页面下载特定版本。这里以安装较新的v2版本为例# 下载最新的docker-compose稳定版到/usr/local/bin目录 sudo curl -L https://github.com/docker/compose/releases/latest/download/docker-compose-$(uname -s)-$(uname -m) -o /usr/local/bin/docker-compose # 赋予二进制文件可执行权限 sudo chmod x /usr/local/bin/docker-compose # 验证安装 docker-compose --version踩坑实录下载链接中的$(uname -s)-$(uname -m)会自动适配你的系统Linux和架构如x86_64。但有时网络问题会导致下载失败或下载的文件不完整。如果执行docker-compose --version报错“无法执行二进制文件”或“文本文件忙”很可能是文件下载不完整。请删除文件重新下载或用wget替代curl再试一次。5.3 Docker Compose核心概念与一个实战示例光安装没用得知道怎么用。Docker Compose的核心是一个名为docker-compose.yml的YAML文件。在这个文件里你定义整个应用栈需要的服务Service、网络Network、数据卷Volume。我们来创建一个最简单的实战示例一个使用Python Flask框架的Web应用它使用Redis来记录访问次数。创建项目目录和文件mkdir composetest cd composetest创建应用文件app.pyimport time import redis from flask import Flask app Flask(__name__) # 连接到名为‘redis’的服务这是Compose中定义的服务名 cache redis.Redis(hostredis, port6379) def get_hit_count(): retries 5 while True: try: return cache.incr(hits) except redis.exceptions.ConnectionError as exc: if retries 0: raise exc retries - 1 time.sleep(0.5) app.route(/) def hello(): count get_hit_count() return fHello World! 该页面已被访问 {count} 次。\n if __name__ __main__: app.run(host0.0.0.0, debugTrue)创建依赖文件requirements.txtflask redis创建Dockerfile用于构建Python应用的镜像# 使用官方Python轻量级镜像 FROM python:3.9-alpine # 设置工作目录 WORKDIR /code # 先复制依赖文件利用Docker缓存层 COPY requirements.txt . # 安装Python依赖 RUN pip install -r requirements.txt # 复制应用代码 COPY . . # 设置容器启动命令 CMD [python, app.py]创建核心的docker-compose.yml文件version: 3.8 # 指定Compose文件格式版本 services: # 定义服务 web: # 第一个服务名为web build: . # 从当前目录的Dockerfile构建镜像 ports: - 8000:5000 # 将主机8000端口映射到容器的5000端口(Flask默认端口) volumes: - .:/code # 将当前目录挂载到容器的/code实现代码热更新 environment: - FLASK_ENVdevelopment depends_on: # 声明依赖先启动redis服务 - redis redis: # 第二个服务名为redis image: redis:alpine # 直接使用官方的Redis镜像5.4 运行与管理Compose项目现在在包含docker-compose.yml的目录下运行以下命令# 使用插件版本推荐 docker compose up # 或者使用独立版本 # docker-compose updocker compose up命令会根据docker-compose.yml创建一个默认的项目网络。为redis服务拉取redis:alpine镜像如果本地没有。为web服务根据Dockerfile构建镜像。启动两个服务容器并将终端日志关联到当前窗口。你会看到大量的构建和运行日志。在浏览器中访问http://localhost:8000每次刷新页面计数都会增加。这证明了Flask应用和Redis容器正在协同工作。常用Compose命令docker compose up -d在后台守护进程模式启动服务。docker compose ps查看当前项目下运行中的容器状态。docker compose logs [service_name]查看指定服务的日志加-f可以跟踪实时日志。docker compose stop停止服务但保留容器和网络。docker compose down停止并移除所有容器、网络。加-v参数会同时删除在Compose文件中定义的匿名卷数据会丢失慎用docker compose exec web sh在名为web的服务容器中启动一个交互式Shell方便进入容器调试。当你修改了代码比如app.py由于配置了卷挂载volumesFlask开发服务器会自动重载你只需要刷新浏览器即可看到变化。这就是开发时使用Compose的便利之处。6. 进阶配置与日常运维要点安装和运行基本服务只是开始要让Docker和Compose在开发和生产中真正好用还需要了解一些进阶配置和运维技巧。6.1 使用.env文件管理环境变量在docker-compose.yml中硬编码密码、密钥等敏感信息是极不安全的。最佳实践是使用环境变量。Compose天然支持从项目目录下的.env文件加载环境变量。创建.env文件注意文件名以点开头# .env REDIS_PASSWORDmy_strong_password_here DB_USERadmin DB_PASSsecret在docker-compose.yml中引用services: redis: image: redis:alpine command: redis-server --requirepass ${REDIS_PASSWORD} # 使用环境变量 db: image: postgres environment: POSTGRES_USER: ${DB_USER} POSTGRES_PASSWORD: ${DB_PASS}确保.env文件被添加到.gitignore中避免敏感信息提交到代码仓库。6.2 多环境Compose文件配置你可能需要为开发、测试、生产环境准备不同的配置。可以使用多个Compose文件并通过-f参数指定或者使用docker-compose.override.yml进行自动合并。基础配置docker-compose.yml定义所有环境共享的服务。开发覆盖配置docker-compose.override.yml默认情况下docker compose up会自动读取这个文件与基础文件合并。可以在这里配置开发专用的设置如卷挂载、调试端口、开发镜像标签等。生产配置docker-compose.prod.yml用于生产环境可以配置不同的端口、关闭调试模式、使用生产镜像、设置资源限制等。启动生产环境服务docker compose -f docker-compose.yml -f docker-compose.prod.yml up -d6.3 Docker与Docker Compose的常用故障排查命令当遇到问题时以下命令是你的好帮手查看Docker服务日志如果Docker本身启动失败sudo journalctl -u docker.service可以查看详细的系统日志。查看容器内部docker exec -it container_name sh进入容器内部检查文件、进程、日志。检查容器配置docker inspect container_name会以JSON格式输出容器的全部配置信息包括网络、卷、环境变量等非常详细。查看容器日志docker logs container_name查看容器的标准输出/错误日志。对于Compose项目docker compose logs更便捷。清理构建缓存如果Docker镜像构建失败或层缓存有问题可以尝试docker builder prune清理构建缓存然后重新构建。网络排查使用docker network ls查看网络docker network inspect network_name查看网络详情如连接的容器、IP分配。Compose项目会创建一个以项目目录名为前缀的默认网络。6.4 资源限制与监控默认情况下容器可以使用宿主机的所有资源。为了防止某个容器耗尽资源影响其他服务或宿主机应该设置资源限制。在docker-compose.yml中可以这样为服务设置限制services: web: # ... 其他配置 deploy: # 注意在Compose v3格式中资源限制通常在deploy下指定特别是用于Swarm模式。对于单机Compose也可以使用以下格式 resources: limits: cpus: 0.5 # 最多使用0.5个CPU核心 memory: 512M # 内存限制为512MB reservations: cpus: 0.1 memory: 256M对于单机Docker运行非Swarm更常用的简单限制方式是使用docker run的--cpus和--memory参数或者在Compose文件中使用cpus和mem_limit等字段具体语法取决于Compose文件版本。监控方面除了基本的docker stats命令可以实时查看容器资源使用情况对于更复杂的监控可以考虑集成Prometheus、Grafana等工具。7. 从安装到实践构建一个简单的个人服务栈为了把前面所有的知识点串联起来我们最后来规划并搭建一个微型的个人服务栈比如一个包含WordPress博客和MySQL数据库的环境。这比之前的计数器例子更贴近真实用例。项目结构规划my-wordpress/ ├── docker-compose.yml └── wp-data/ # 用于持久化WordPress文件通过卷映射编写docker-compose.ymlversion: 3.8 services: db: image: mysql:8.0 # 使用卷持久化数据库数据避免容器删除后数据丢失 volumes: - db_data:/var/lib/mysql # 通过环境变量设置数据库root密码和新建的数据库 environment: MYSQL_ROOT_PASSWORD: some_root_password MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress_password # 设置健康检查确保数据库完全启动后再启动WordPress healthcheck: test: [CMD, mysqladmin, ping, -h, localhost] timeout: 20s retries: 10 wordpress: depends_on: db: condition: service_healthy # 依赖db服务的健康状态 image: wordpress:latest ports: - 8080:80 # 主机8080端口映射到容器80端口 volumes: - wp_data:/var/www/html # 持久化WordPress核心文件、插件、主题 - ./uploads.ini:/usr/local/etc/php/conf.d/uploads.ini # 自定义PHP配置如上传文件大小限制 environment: WORDPRESS_DB_HOST: db:3306 # 使用服务名‘db’作为主机名Compose网络内自动DNS解析 WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress_password WORDPRESS_DB_NAME: wordpress # 定义命名卷便于管理和备份 volumes: db_data: wp_data:创建自定义PHP配置文件可选 在项目根目录创建uploads.ini文件内容为upload_max_filesize 64M以修改上传文件大小限制。启动服务栈cd my-wordpress docker compose up -d访问与验证 等待片刻首次运行需要拉取镜像在浏览器访问http://你的服务器IP:8080。你应该能看到WordPress的著名“五分钟安装”界面。按照提示完成安装你的个人博客就运行起来了。日常运维备份由于数据库和WordPress文件都通过命名卷持久化备份就是备份这些卷的数据。可以使用docker run --rm -v my-wordpress_db_data:/source -v /path/to/backup:/backup alpine tar czf /backup/db_backup.tar.gz -C /source .这样的命令来打包卷数据。更新要更新WordPress或MySQL版本只需修改docker-compose.yml中的镜像标签如wordpress:latest改为wordpress:6.2然后运行docker compose up -dCompose会自动拉取新镜像并重新创建容器而你的数据卷保持不变。停止与清理运行docker compose down会停止并删除容器和网络但不会删除在volumes:部分定义的db_data和wp_data命名卷因此你的数据是安全的。如果想彻底清理数据需要运行docker compose down -v。通过这个完整的例子你应该能深刻体会到Docker Compose如何将多个相互依赖的服务编排成一个整体并通过声明式的YAML文件进行管理。这种“基础设施即代码”的方式使得搭建、复制、迁移一套复杂环境变得前所未有的简单和可靠。