Ubuntu 22.04 Docker安装与实战:从零到生产环境部署指南

📅 2026/8/22 2:57:23
Ubuntu 22.04 Docker安装与实战:从零到生产环境部署指南
1. 项目概述为什么在Ubuntu 22上拥抱Docker如果你正在使用Ubuntu 22.04 LTSJammy Jellyfish无论是作为主力开发机、服务器还是刚刚在虚拟机里装好那么Docker几乎是你绕不开的一个工具。我见过太多新手在配置开发环境时被各种依赖、版本冲突搞得焦头烂额也见过运维同事为了将应用从测试环境迁移到生产环境需要重新配置一整套系统库和中间件。Docker的出现就是为了解决这些“在我机器上能跑”的经典难题。简单来说Docker是一个容器化平台。你可以把它理解为一个超级轻量级的“虚拟机”但它和传统虚拟机有本质区别。传统虚拟机如VMware、VirtualBox需要模拟完整的硬件并在上面运行一个完整的操作系统开销巨大。而Docker容器直接运行在宿主机的操作系统内核之上它只“打包”了应用及其运行所必需的库和环境因此启动速度极快资源占用极小并且能保证环境的一致性。你在自己笔记本的Ubuntu上构建好的一个包含Python 3.9、Redis和Nginx的容器镜像可以百分百确信它在云服务器的Ubuntu、甚至其他Linux发行版上以完全相同的方式运行。在Ubuntu 22.04上使用Docker尤其顺畅因为其较新的内核5.15对容器所需的命名空间、控制组cgroup等特性有更好的支持。无论是为了快速搭建一个MySQL数据库进行测试还是部署一个复杂的微服务应用抑或是单纯地想隔离自己的实验环境避免搞乱系统从Ubuntu 22开始学习并使用Docker都是一个绝佳的起点。接下来我将带你从零开始完成Docker的安装、基础使用并分享一些我踩过坑才总结出来的实战经验。2. 安装前的准备与方案选型在Ubuntu上安装Docker主要有三种途径使用官方仓库、使用便捷脚本、或者安装Docker Desktop for Linux。每种方式适合不同的场景和用户。2.1 不同安装方式的利弊分析1. 通过官方APT仓库安装推荐用于服务器和大多数桌面用户这是最标准、最受社区支持的方式。它的好处是安装过程透明完全由系统包管理器APT管理可以轻松地更新、卸载并且与系统其他软件包兼容性好。所有配置步骤清晰便于理解和自定义。这也是本文主要介绍的方式。2. 使用get-docker.sh便捷脚本适合快速体验不推荐用于生产Docker官方提供了一个一键安装脚本。你只需要运行curl -fsSL https://get.docker.com -o get-docker.sh然后sudo sh get-docker.sh。这个脚本会自动检测系统并完成所有安装步骤。它的优点是极其方便但缺点是不够透明。脚本在后台做了什么你并不完全清楚对于生产服务器而言这种“黑盒”操作存在潜在风险。我通常只在临时测试环境使用它。3. 安装Docker Desktop for Linux适合深度集成需求的开发者Docker Desktop是一个包含Docker Engine、Docker CLI客户端、Docker Compose、可视化界面以及Kubernetes的集成式桌面应用。如果你的工作流严重依赖图形化界面管理容器和镜像或者需要本地集成Kubernetes进行开发测试那么Docker Desktop是个不错的选择。但请注意它资源占用相对较大并且需要系统启用虚拟化支持对于某些物理机安装的Linux可能需要检查BIOS设置。在服务器环境我们绝不会使用它。注意近期一些用户遇到“virtualization support not detected”或“docker desktop failed to start”错误这通常是因为Docker Desktop依赖于一个完整的Linux虚拟机通常是qemu而你的系统可能未启用硬件虚拟化Intel VT-x / AMD-V或者嵌套虚拟化配置有问题例如在VMware虚拟机里再装Docker Desktop。对于绝大多数使用场景我强烈建议在Ubuntu上直接安装Docker Engine即第一种方式更轻量、更稳定也更能让你理解Docker的核心工作原理。2.2 系统环境检查与清理在开始安装之前进行一些准备工作能让过程更顺利。首先更新你的APT软件包索引这是一个好习惯sudo apt update接着安装一些允许APT通过HTTPS使用仓库的基础工具sudo apt install ca-certificates curl gnupg lsb-release如果你的系统上曾经通过其他方式安装过旧版本的Docker比如docker.io、docker-engine为了避免冲突最好先卸载它们sudo apt remove docker docker-engine docker.io containerd runc请注意apt remove不会删除镜像、容器、卷等数据文件它们通常位于/var/lib/docker/如果你需要全新开始可以手动删除这个目录警告此操作不可逆会删除所有容器数据。3. 通过官方仓库安装Docker Engine这是最正统的安装方法步骤清晰适合学习和生产环境。3.1 添加Docker官方GPG密钥和APT仓库Docker的软件包使用GPG密钥进行签名以确保下载的软件包未被篡改。我们需要先添加Docker的官方GPG密钥。创建用于存储GPG密钥的目录如果不存在sudo install -m 0755 -d /etc/apt/keyrings下载Docker的GPG密钥并保存到指定位置curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg这里--dearmor命令是将下载的ASCII-armor格式密钥转换为二进制gpg格式。设置密钥文件的正确权限sudo chmod ar /etc/apt/keyrings/docker.gpg接下来我们需要告诉APT系统去哪里下载Docker软件包即添加软件源。使用下面的命令添加仓库。这条命令会自动检测你的系统架构amd64, arm64等和版本代号如jammy并生成正确的源地址。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$(dpkg --print-architecture)获取系统架构如amd64。$(lsb_release -cs)获取系统版本代号对于Ubuntu 22.04就是jammy。signed-by指定用于验证此仓库的GPG密钥路径。sudo tee ...将生成的仓库配置行写入/etc/apt/sources.list.d/docker.list文件。3.2 安装Docker引擎及相关组件添加仓库后再次更新APT索引使其能获取到Docker仓库中的软件包列表sudo apt update现在可以安装Docker了。我们主要安装以下几个包docker-ce: Docker社区版引擎核心。docker-ce-cli: Docker命令行接口用于执行docker命令。containerd.io: 容器运行时Docker引擎的底层依赖。docker-buildx-plugin: 用于构建多平台镜像的插件。docker-compose-plugin: Docker Compose V2的插件版本推荐比独立的docker-compose软件包更好管理。执行安装命令sudo apt install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin安装过程中APT会解析依赖关系并提示你确认安装。输入Y并回车继续。3.3 安装后的基础验证与配置安装完成后Docker服务docker.service会自动启动。你可以通过以下命令验证Docker引擎是否运行正常sudo systemctl status docker你应该能看到状态显示为active (running)。更直接的测试是运行经典的hello-world镜像sudo docker run hello-world这个命令会做几件事1从Docker Hub默认的公共镜像仓库拉取hello-world镜像2基于该镜像创建并启动一个容器3容器执行一段简单的输出后退出。如果你看到一段欢迎信息说明Docker安装成功并可以正常工作。让普通用户无需sudo运行Docker默认情况下执行docker命令需要sudo权限因为Docker守护进程以root用户运行。每次都要输入sudo很麻烦也存在安全隐患因为加入docker用户组等同于获得了root权限。但为了方便开发我们通常会将当前用户加入docker用户组。创建docker用户组如果不存在sudo groupadd docker将当前用户加入docker组sudo usermod -aG docker $USER-aG表示将用户追加append到指定的组Group中。生效组变更用户组变更不会立即在当前已登录的会话中生效。你需要注销并重新登录或者打开一个新的终端窗口。最简单的方法是执行以下命令启动一个新的shell会话newgrp docker验证是否生效docker run hello-world如果能够不加sudo成功运行说明配置正确。重要安全提示docker组的成员权限很高因为可以通过挂载宿主机目录、操作网络等方式影响宿主机。请仅将你信任的用户或用于开发的账户加入此组。在生产服务器上应严格限制docker组用户或直接使用sudo。4. Docker核心概念与基础命令实战安装好Docker只是第一步理解其核心概念和掌握基础命令才能让它为你所用。4.1 镜像与容器理解Docker的基石这是Docker中最核心的两个概念务必分清镜像Image一个只读的模板。它包含了运行某个软件所需的所有内容代码、运行时、库、环境变量和配置文件。你可以把它看作面向对象编程中的“类”。hello-world、nginx、mysql都是镜像。镜像通过分层存储实现这使得镜像的拉取和存储非常高效。容器Container镜像的一个运行实例。当你运行一个镜像时Docker会创建一个可写的容器层让应用程序在其中运行。容器是隔离的拥有自己的文件系统、网络和进程空间。你可以把它看作由“类”镜像创建出来的“对象”。容器可以被启动、开始、停止、删除。一个简单的类比镜像就像是ISO光盘文件而容器就像是用这个光盘启动并运行起来的电脑系统。4.2 日常高频命令详解与示例让我们通过操作一个真实的镜像来学习命令。以Nginx为例它是一个流行的Web服务器。1. 拉取镜像从Docker Hub拉取Nginx镜像。如果不指定标签默认拉取latest标签。docker pull nginx你也可以拉取特定版本如docker pull nginx:1.23-alpine。alpine版本基于极简的Alpine Linux镜像体积非常小。2. 运行容器基于Nginx镜像启动一个容器docker run -d --name my-nginx -p 8080:80 nginx-d后台运行detached mode。--name my-nginx给容器起一个名字便于后续管理。如果不指定Docker会分配一个随机名字。-p 8080:80端口映射。将宿主机的8080端口映射到容器的80端口Nginx默认监听80。这样你访问http://localhost:8080就能看到Nginx的欢迎页面。nginx要使用的镜像名。3. 查看容器状态docker ps查看正在运行的容器。加上-a参数可以查看所有容器包括已停止的。4. 查看容器日志当容器在后台运行时查看其输出日志非常重要docker logs my-nginx使用-f参数可以实时跟踪日志输出类似于tail -f。5. 进入正在运行的容器有时需要进入容器内部进行调试docker exec -it my-nginx /bin/bash-it分配一个交互式终端interactive TTY。/bin/bash在容器内执行的命令这里是启动一个Bash shell。如果镜像基于Alpine使用ash则需要换成/bin/sh。6. 停止和启动容器docker stop my-nginx # 停止容器 docker start my-nginx # 启动已停止的容器 docker restart my-nginx # 重启容器7. 删除容器和镜像docker rm my-nginx # 删除已停止的容器加 -f 可强制删除运行中的容器 docker rmi nginx # 删除镜像前提是没有容器依赖它清理所有已停止的容器、未被使用的网络和构建缓存释放磁盘空间docker system prune -a慎用-a它会删除所有未被容器使用的镜像而不仅仅是悬空镜像。4.3 数据持久化让容器数据“活下去”容器默认是无状态的当容器被删除其内部产生的所有数据也会消失。为了持久化数据如数据库文件、应用程序日志我们需要使用卷Volume或绑定挂载Bind Mount。卷Volume由Docker管理的数据存储区域完全独立于容器的生命周期。是持久化数据的首选方式。# 创建一个名为my-data的卷 docker volume create my-data # 运行容器并将卷挂载到容器内的/app/data路径 docker run -d -v my-data:/app/data --name my-app some-image绑定挂载Bind Mount将宿主机上的一个特定目录或文件挂载到容器中。适合开发时同步源代码。# 将宿主机的/home/user/project目录挂载到容器的/app目录 docker run -d -v /home/user/project:/app --name my-dev-app some-image这样你在宿主机上修改代码容器内的应用能立即看到变化无需重新构建镜像。5. 使用Docker Compose编排多容器应用单独管理一个容器还行但现代应用往往由多个服务组成例如一个Web应用需要Python后端、MySQL数据库和Redis缓存。手动用docker run启动每一个并配置网络链接非常繁琐。Docker Compose就是用来定义和运行多容器Docker应用的工具。我们之前安装的docker-compose-plugin是V2版本命令是docker compose注意中间没有横线。它通过一个docker-compose.yml文件来配置所有服务。5.1 编写你的第一个docker-compose.yml假设我们要部署一个简单的WordPress网站它需要WordPress容器和MySQL数据库容器。创建一个项目目录并在其中创建docker-compose.yml文件version: 3.8 # 指定Compose文件格式版本 services: db: image: mysql:8.0 # 使用MySQL 8.0镜像 # 数据持久化使用卷db_data挂载到MySQL的数据目录 volumes: - db_data:/var/lib/mysql # 设置环境变量相当于docker run -e environment: MYSQL_ROOT_PASSWORD: some_root_password MYSQL_DATABASE: wordpress MYSQL_USER: wordpress MYSQL_PASSWORD: wordpress_password # 设置容器重启策略除非手动停止否则总是重启 restart: always # 为服务设置一个自定义网络别名其他服务可以通过db这个主机名访问它 networks: - wp-network wordpress: depends_on: - db # 指明依赖关系先启动db服务 image: wordpress:latest ports: - 8080:80 # 将宿主机的8080端口映射到WordPress容器的80端口 environment: WORDPRESS_DB_HOST: db # 这里直接使用服务名db作为数据库主机地址 WORDPRESS_DB_USER: wordpress WORDPRESS_DB_PASSWORD: wordpress_password WORDPRESS_DB_NAME: wordpress volumes: # 绑定挂载将当前目录下的wp-content目录挂载用于持久化主题和插件 - ./wp-content:/var/www/html/wp-content restart: always networks: - wp-network # 定义在文件底部声明将要使用的卷和网络 volumes: db_data: # 命名卷由Docker管理 networks: wp-network: # 自定义桥接网络服务之间可以通过服务名互相发现5.2 启动、管理与停止Compose项目在包含docker-compose.yml文件的目录下执行以下命令启动所有服务后台模式docker compose up -d-d代表后台运行。Docker Compose会自动拉取镜像如果本地没有创建网络、卷并按依赖顺序启动所有服务。查看服务状态docker compose ps这比docker ps更清晰只显示当前Compose项目下的容器。查看服务日志docker compose logs # 查看所有服务日志 docker compose logs wordpress # 只看wordpress服务的日志 docker compose logs -f db # 实时跟踪db服务的日志进入服务容器docker compose exec db mysql -u root -p这会进入db服务容器并直接启动MySQL客户端。输入前面设置的some_root_password即可登录。停止服务docker compose down此命令会停止并删除所有容器、网络默认创建的但不会删除卷如db_data因此你的MySQL数据得以保留。 如果想同时删除卷需要加-v参数docker compose down -v。警告这会删除所有数据使用Docker Compose你通过一个文件就定义了一个完整的多服务应用栈并且可以一键启停极大地简化了开发和测试环境的搭建。6. 构建自定义镜像编写Dockerfile从公共镜像仓库拉取镜像很方便但更多时候我们需要构建自己的应用镜像。这就需要编写Dockerfile。Dockerfile是一个文本文件包含了一系列指令用于指导Docker如何一步步构建镜像。6.1 Dockerfile指令精讲让我们以一个简单的Python Flask应用为例创建一个Dockerfile。假设你的项目结构如下my-flask-app/ ├── app.py ├── requirements.txt └── Dockerfileapp.py内容from flask import Flask app Flask(__name__) app.route(/) def hello(): return Hello from Dockerized Flask! if __name__ __main__: app.run(host0.0.0.0, port5000)requirements.txt内容Flask2.3.0现在编写Dockerfile# 第一阶段构建阶段可选用于多阶段构建减小最终镜像体积 # 使用官方Python运行时作为父镜像 FROM python:3.9-slim as builder # 设置工作目录后续命令都在此目录下执行 WORKDIR /app # 将依赖文件复制到工作目录 COPY requirements.txt . # 安装依赖到虚拟环境或特定目录这里使用--user安装到用户目录 RUN pip install --user -r requirements.txt # 第二阶段运行阶段 FROM python:3.9-slim # 设置环境变量确保Python输出直接显示在终端不缓冲 ENV PYTHONUNBUFFERED1 WORKDIR /app # 从构建阶段复制已安装的Python包 COPY --frombuilder /root/.local /root/.local # 将当前目录下的所有文件复制到容器的/app目录 COPY . . # 将用户目录下的bin路径添加到PATH以便系统能找到pip安装的命令 ENV PATH/root/.local/bin:$PATH # 声明容器运行时监听的端口 EXPOSE 5000 # 定义容器启动时执行的命令 CMD [python, app.py]关键指令解析FROM指定基础镜像。选择合适的基础镜像如-slim、-alpine版本能显著减小最终镜像体积。WORKDIR设置工作目录相当于cd。COPY将文件从构建上下文你运行docker build命令的目录复制到镜像中。RUN在构建镜像时执行命令常用于安装软件包、编译代码。ENV设置环境变量。EXPOSE声明容器打算使用的端口这只是一个文档说明实际映射需要在docker run时用-p指定。CMD指定容器启动时默认执行的命令。一个Dockerfile中只能有一个CMD指令。6.2 构建并运行自定义镜像在my-flask-app目录下执行构建命令docker build -t my-flask-app:latest .-t my-flask-app:latest为镜像打上标签名称:版本。.指定构建上下文为当前目录。Docker客户端会将这个目录下的所有文件发送给Docker守护进程进行构建。注意可以通过.dockerignore文件来排除不需要发送的文件如__pycache__,.git这能加速构建过程。构建成功后运行容器docker run -d -p 5000:5000 --name flask-container my-flask-app访问http://localhost:5000你应该能看到“Hello from Dockerized Flask!”的消息。7. 实战问题排查与性能优化心得即使按照教程操作在实际使用中你也难免会遇到问题。这里分享几个我高频遇到的坑和解决思路。7.1 常见错误与解决方案速查表问题现象可能原因解决方案Got permission denied while trying to connect to the Docker daemon socket当前用户不在docker组或组变更未生效。执行sudo usermod -aG docker $USER后注销重登或执行newgrp docker。docker: Error response from daemon: Ports are not available宿主机要映射的端口如8080已被其他进程占用。使用sudo netstat -tulpn | grep :8080查找占用进程并停止或更换映射端口如-p 8081:80。Unable to find image xxx:latest locally/pull access denied镜像不存在于本地且从Docker Hub拉取失败网络问题或镜像名错误。检查镜像名拼写。对于网络问题可配置国内镜像加速器见下文。对于私有镜像需要先docker login。no space left on deviceDocker默认存储目录/var/lib/docker磁盘空间不足。清理无用资源docker system prune -a。转移Docker数据目录或扩容磁盘。容器内应用无法连接其他容器如数据库容器未处于同一自定义网络或使用了错误的连接地址。使用Docker Compose它会自动创建网络。手动运行时使用--network指定相同网络并通过容器名而非IP访问。容器时间与宿主机时间不一致容器默认使用UTC时区。运行容器时挂载宿主机时区文件-v /etc/localtime:/etc/localtime:ro。或直接在Dockerfile中设置TZ环境变量。error creating overlay mount to /var/lib/docker/overlay2/...存储驱动如overlay2出现问题可能是文件系统损坏或inode耗尽。尝试重启Docker服务sudo systemctl restart docker。检查磁盘inode使用df -i。最坏情况需备份数据后重置Docker。7.2 配置国内镜像加速器从Docker Hub拉取镜像速度慢或不稳定是常见问题。我们可以配置国内镜像加速器。编辑Docker守护进程配置文件如果不存在则创建sudo nano /etc/docker/daemon.json添加以下内容以阿里云加速器为例你需要去阿里云容器镜像服务控制台获取专属加速器地址{ registry-mirrors: [ https://your-aliyun-mirror.mirror.aliyuncs.com, https://docker.mirrors.ustc.edu.cn, https://registry.docker-cn.com ] }保存退出后重新加载配置并重启Docker服务sudo systemctl daemon-reload sudo systemctl restart docker验证配置是否生效docker info在输出中查找Registry Mirrors部分应该能看到你配置的镜像地址。7.3 资源限制与监控默认情况下容器可以使用宿主机的所有资源。为了防止某个容器耗尽资源影响其他服务可以设置资源限制。运行时限制docker run -d \ --name my-container \ --memory512m \ # 限制内存为512MB --memory-swap1g \ # 内存交换分区总共1Gswap 1g - 512m --cpus1.5 \ # 限制使用1.5个CPU核心的计算时间 my-image在Compose文件中限制services: app: image: my-image deploy: # 注意在Compose V3中资源限制通常在deploy下用于Swarm单机运行有时也认。 resources: limits: cpus: 0.5 memory: 256M reservations: cpus: 0.25 memory: 128M对于单机docker-compose up更通用的写法是使用mem_limit等旧属性V2格式或直接使用docker run的参数格式services: app: image: my-image mem_limit: 256M cpus: 0.5监控容器资源使用docker stats这个命令会实时显示所有运行中容器的CPU、内存、网络IO和磁盘IO使用情况是性能排查的利器。7.4 镜像构建优化技巧使用.dockerignore文件在构建上下文目录创建该文件列出不需要打包进镜像的文件和目录如.git,node_modules,*.log,*.tmp。这能显著减少构建上下文大小加速构建。利用构建缓存Dockerfile的每条指令都会生成一层镜像且层是缓存的。最易变的指令如COPY . .应放在Dockerfile底部将不常变的指令如RUN apt-get update放在顶部以最大化利用缓存。多阶段构建对于需要编译的应用如Go、Java在第一个阶段使用完整的构建环境进行编译在第二个阶段仅复制编译好的二进制文件到一个小体积的运行时镜像如alpine中可以极大减小最终镜像体积。上面的Flask示例已经演示了基础的多阶段构建思想。选择小巧的基础镜像优先选择-alpine或-slim版本的基础镜像。一个完整的ubuntu:22.04镜像约80MB而python:3.9-alpine可能只有40MB左右。8. 进阶之路从入门到生产掌握了以上内容你已经在Ubuntu 22上玩转Docker了。但如果你想向生产环境迈进还需要了解以下方向1. 容器编排Docker Swarm与Kubernetes当容器数量成百上千时手动管理是不可能的。你需要编排工具来管理容器的部署、伸缩、网络和健康检查。Docker SwarmDocker原生的集群管理工具概念简单与Docker CLI集成度高适合中小规模部署。Kubernetes (K8s)目前容器编排领域的事实标准功能极其强大但学习曲线陡峭适合大规模、复杂的生产环境。2. 持续集成与持续部署将Docker镜像构建集成到你的Git工作流中。例如使用GitHub Actions、GitLab CI/CD或Jenkins在代码推送后自动构建镜像、运行测试并推送到镜像仓库甚至自动部署到服务器。3. 私有镜像仓库出于安全、速度和合规性考虑企业通常会搭建私有镜像仓库如Harbor、AWS ECR、Google Container Registry等用于存储和管理自己的Docker镜像。4. 安全实践避免以root用户运行容器内的应用。在Dockerfile中使用USER指令切换到非root用户。定期扫描镜像中的安全漏洞使用docker scan或第三方工具如Trivy、Clair。最小化镜像攻击面只安装必要的包。5. 日志与监控集中化使用docker logs查看单个容器日志还行但生产环境需要集中化的日志方案如ELK StackElasticsearch, Logstash, Kibana或Fluentd将所有容器的日志收集、存储和分析。监控方面可以结合Prometheus和Grafana来监控容器和宿主机的各项指标。从我个人的经验来看Docker的价值在于它提供了一种标准化的应用打包和交付方式。一旦你习惯了容器化的工作流就很难再回到过去那种“手动配环境”的混乱状态。在Ubuntu 22上Docker的安装和使用体验已经非常成熟和稳定是投入时间学习这项技能的绝佳时机。刚开始时多动手实践从运行一个简单的Nginx容器开始再到用Compose编排一个多服务应用最后尝试为自己写的应用构建镜像。每一步遇到的问题和解决方案都会成为你宝贵的经验。记住容器化的核心思想是“一次构建处处运行”而实现这一点的第一步就是确保你的基础环境——比如我们这里的Ubuntu 22上的Docker——被正确、稳固地搭建起来。