"在我机器上能跑啊!"——这是开发者和运维人员之间最经典的对话之一。代码在开发环境能跑,到了测试环境就出问题;测试环境没问题,到了生产环境又崩溃。环境不一致、依赖冲突、配置繁琐,这些问题困扰着每一个开发者和运维人员。

Docker的出现,彻底改变了这一切。Docker通过容器化技术,把应用和它的所有依赖打包成一个标准化的单元(容器),可以在任何支持Docker的环境中一致运行。有了Docker,"在我机器上能跑"不再是借口,因为容器在任何机器上都能跑。

Docker在2013年由dotCloud公司发布,2014年Docker 1.0正式发布,2016年已经成为开发者和运维人员的必备技能。越来越多的公司采用Docker进行应用部署和微服务架构,Docker生态也越来越完善(Docker Compose、Docker Swarm、Kubernetes等)。

作为一个后端开发者,我从2015年开始接触Docker,2016年已经在项目中大量使用。Docker让我的开发和部署效率提升了很多,也让我对运维和架构有了更深的理解。今天就来系统地分享一下Docker容器化入门与实践,从基本概念到实战部署,帮助你快速上手Docker。

一、Docker基本概念

在开始使用Docker之前,我们需要了解一些核心概念。

1. 什么是Docker

Docker是一个开源的容器化平台,允许开发者将应用及其依赖打包成一个轻量级、可移植的容器,然后发布到任何支持Docker的机器上运行。

Docker的核心思想是:"Build, Ship, and Run Any App, Anywhere"(构建、交付、运行任何应用,在任何地方)。

Docker解决的问题:

  • 环境不一致:开发、测试、生产环境统一
  • 依赖冲突:每个应用有独立的依赖环境
  • 快速部署:容器秒级启动,快速扩缩容
  • 资源隔离:应用之间互不影响
  • 易于迁移:容器可以在任何机器上运行
  • 版本管理:镜像可以版本化,回滚方便

2. 镜像(Image)

镜像是Docker容器的只读模板,包含运行应用所需的一切:代码、运行时、库、环境变量、配置文件等。

镜像的特点:

  • 只读:镜像一旦构建就不能修改
  • 分层:镜像由多层组成,每层是一个文件系统的变更
  • 可复用:基础镜像可以被多个镜像复用
  • 可分享:镜像可以推送到仓库,分享给他人

常见的基础镜像:

  • ubuntu/debian/centos:操作系统基础镜像
  • php/python/node/java/go:语言运行时镜像
  • nginx/apache/mysql/redis:服务镜像
  • alpine:极简Linux镜像,体积小

3. 容器(Container)

容器是镜像的运行实例。镜像相当于"类",容器相当于"对象"。一个镜像可以创建多个容器。

容器的特点:

  • 可读写:容器在镜像之上添加了一个可写层
  • 隔离性:容器之间相互隔离,有独立的文件系统、网络、进程
  • 轻量级:容器共享宿主机内核,启动快,资源占用少
  • 可移植:容器可以在任何支持Docker的机器上运行
  • 生命周期:容器可以创建、启动、停止、删除

容器 vs 虚拟机: | 对比项 | 容器 | 虚拟机 | |--------|------|--------| | 虚拟化级别 | 操作系统级 | 硬件级 | | 启动速度 | 秒级 | 分钟级 | | 资源占用 | 小(共享内核) | 大(完整操作系统) | | 性能 | 接近原生 | 有损耗 | | 隔离性 | 进程级隔离 | 完全隔离 | | 可移植性 | 高 | 中 |

4. 仓库(Registry)

仓库是存储和分发镜像的地方。

  • Docker Hub:官方公共仓库,有大量官方和社区镜像
  • 私有仓库:企业内部搭建的仓库,如Docker Registry、Harbor
  • 国内镜像加速:由于网络原因,国内访问Docker Hub较慢,可以使用阿里云、腾讯云、DaoCloud等提供的镜像加速服务

5. Dockerfile

Dockerfile是一个文本文件,包含一系列构建镜像的指令。通过Dockerfile,可以自动化构建镜像。

常见指令:

  • FROM:指定基础镜像
  • MAINTAINER/LABEL:维护者信息
  • RUN:执行命令
  • COPY/ADD:复制文件
  • WORKDIR:设置工作目录
  • ENV:设置环境变量
  • EXPOSE:暴露端口
  • CMD/ENTRYPOINT:容器启动命令

6. Docker Compose

Docker Compose是用于定义和运行多容器Docker应用的工具。通过一个YAML文件配置应用的服务,然后用一条命令启动所有服务。

Docker Compose适合:

  • 多容器应用(如Web+MySQL+Redis)
  • 开发和测试环境
  • 单机部署

二、Docker安装与配置

1. Linux安装(Ubuntu/Debian)

# 更新包索引
sudo apt-get update

# 安装依赖
sudo apt-get install -y apt-transport-https ca-certificates curl software-properties-common

# 添加Docker官方GPG密钥
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

# 添加Docker仓库
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"

# 更新包索引
sudo apt-get update

# 安装Docker
sudo apt-get install -y docker-ce

# 启动Docker
sudo systemctl start docker
sudo systemctl enable docker

# 验证安装
sudo docker --version
sudo docker run hello-world

2. Linux安装(CentOS)

# 安装依赖
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

# 添加Docker仓库
sudo yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo

# 安装Docker
sudo yum install -y docker-ce

# 启动Docker
sudo systemctl start docker
sudo systemctl enable docker

# 验证安装
sudo docker --version

3. Windows/Mac安装

2016年,Docker for Windows和Docker for Mac已经发布,可以直接从Docker官网下载安装包安装。

  • Windows:需要Windows 10专业版或企业版,启用Hyper-V
  • Mac:需要OS X 10.10.3+

也可以使用Docker Toolbox(旧版本,通过VirtualBox运行)。

4. 配置镜像加速(国内)

由于国内访问Docker Hub较慢,建议配置镜像加速。

# 创建配置目录
sudo mkdir -p /etc/docker

# 编辑配置文件
sudo tee /etc/docker/daemon.json <<-'EOF'
{
  "registry-mirrors": [
    "https://registry.docker-cn.com",
    "https://docker.mirrors.ustc.edu.cn"
  ]
}
EOF

# 重启Docker
sudo systemctl daemon-reload
sudo systemctl restart docker

# 验证
sudo docker info | grep -A 5 "Registry Mirrors"

也可以使用阿里云、腾讯云等提供的专属镜像加速地址。

5. 非root用户运行Docker

# 创建docker组
sudo groupadd docker

# 将用户加入docker组
sudo usermod -aG docker $USER

# 重新登录或执行以下命令使组生效
newgrp docker

# 验证(不需要sudo)
docker run hello-world

三、Docker常用命令

1. 镜像相关命令

# 搜索镜像
docker search nginx

# 拉取镜像
docker pull nginx
docker pull nginx:1.11
docker pull ubuntu:16.04

# 列出本地镜像
docker images
docker image ls

# 删除镜像
docker rmi nginx
docker image rm nginx
docker rmi -f nginx  # 强制删除

# 查看镜像详情
docker inspect nginx

# 查看镜像历史
docker history nginx

# 给镜像打标签
docker tag nginx:latest my-nginx:1.0

# 推送镜像到仓库
docker push username/my-nginx:1.0

2. 容器相关命令

# 创建并启动容器
docker run nginx
docker run -d nginx  # 后台运行
docker run -d -p 8080:80 nginx  # 端口映射
docker run -d -p 8080:80 --name my-nginx nginx  # 指定名称
docker run -d -p 8080:80 -v /host/path:/container/path nginx  # 挂载卷
docker run -it ubuntu /bin/bash  # 交互式运行

# 列出运行中的容器
docker ps

# 列出所有容器(包括停止的)
docker ps -a

# 启动/停止/重启容器
docker start my-nginx
docker stop my-nginx
docker restart my-nginx

# 进入容器
docker exec -it my-nginx /bin/bash
docker attach my-nginx  # 附加到容器(不推荐)

# 查看容器日志
docker logs my-nginx
docker logs -f my-nginx  # 实时查看
docker logs --tail 100 my-nginx

# 查看容器详情
docker inspect my-nginx

# 查看容器资源使用
docker stats my-nginx

# 查看容器进程
docker top my-nginx

# 复制文件到/从容器
docker cp /host/path my-nginx:/container/path
docker cp my-nginx:/container/path /host/path

# 删除容器
docker rm my-nginx
docker rm -f my-nginx  # 强制删除运行中的容器
docker container prune  # 删除所有停止的容器

# 暂停/恢复容器
docker pause my-nginx
docker unpause my-nginx

3. 其他常用命令

# 查看Docker版本
docker --version
docker version

# 查看Docker系统信息
docker info

# 清理未使用的资源
docker system prune
docker system prune -a  # 清理所有未使用的(包括镜像)

# 查看磁盘使用
docker system df

四、Dockerfile编写与镜像构建

1. Dockerfile基本结构

# 基础镜像
FROM ubuntu:16.04

# 维护者
MAINTAINER Your Name <your@email.com>

# 环境变量
ENV APP_HOME /var/www/app
ENV DEBIAN_FRONTEND noninteractive

# 工作目录
WORKDIR $APP_HOME

# 安装依赖
RUN apt-get update && \
    apt-get install -y nginx php-fpm php-mysql && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 复制文件
COPY . $APP_HOME
COPY nginx.conf /etc/nginx/nginx.conf

# 暴露端口
EXPOSE 80 443

# 数据卷
VOLUME ["/var/www/app/uploads"]

# 启动命令
CMD ["nginx", "-g", "daemon off;"]

2. Dockerfile指令详解

  • FROM:指定基础镜像,必须是第一条指令。尽量使用官方镜像,使用固定版本标签。
  • MAINTAINER/LABEL:维护者信息,推荐使用LABEL。
  • RUN:执行命令,在构建时运行。每条RUN指令会创建一个新的镜像层,建议合并多条命令减少层数。
  • COPY:复制文件到镜像中。比ADD更简单,推荐使用COPY。
  • ADD:复制文件,支持URL和自动解压tar包。除非需要自动解压,否则用COPY。
  • WORKDIR:设置工作目录,后续的RUN、CMD、ENTRYPOINT、COPY、ADD都在这个目录下执行。
  • ENV:设置环境变量,可以在构建时和运行时使用。
  • ARG:构建参数,只在构建时有效,用--build-arg传递。
  • EXPOSE:声明容器运行时监听的端口。只是声明,不会自动映射,需要用-p映射。
  • VOLUME:声明数据卷,用于持久化数据。
  • USER:设置运行容器的用户。
  • CMD:容器启动时执行的命令。可以被docker run的参数覆盖。一个Dockerfile只有最后一个CMD生效。
  • ENTRYPOINT:容器启动时执行的命令。不会被docker run的参数覆盖,参数会追加到ENTRYPOINT后面。

3. 构建镜像

# 构建镜像
docker build -t my-app:1.0 .

# 使用指定Dockerfile
docker build -t my-app:1.0 -f Dockerfile.prod .

# 构建时传递参数
docker build -t my-app:1.0 --build-arg ENV=production .

# 不使用缓存构建
docker build -t my-app:1.0 --no-cache .

# 查看构建历史
docker history my-app:1.0

4. Dockerfile最佳实践

  • 使用官方基础镜像
  • 使用固定版本标签,不要用latest
  • 合并RUN指令,减少镜像层数
  • 把变化少的指令放在前面,利用构建缓存
  • 使用.dockerignore排除不需要的文件
  • 不要在镜像中存储敏感信息(密码、密钥)
  • 使用多阶段构建减小镜像体积(Docker 17.05+)
  • 清理apt缓存和临时文件
  • 使用非root用户运行容器
  • 一个容器只运行一个进程
  • 镜像尽量小,使用alpine基础镜像

.dockerignore示例

.git
node_modules
vendor
*.log
.env
.DS_Store
tests
docs

五、Docker Compose多容器编排

1. 安装Docker Compose

# Linux安装
sudo curl -L "https://github.com/docker/compose/releases/download/1.9.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
sudo chmod +x /usr/local/bin/docker-compose

# 验证
docker-compose --version

Windows/Mac的Docker Desktop自带Docker Compose。

2. docker-compose.yml示例(LNMP)

version: '2'

services:
  nginx:
    image: nginx:1.11
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./www:/var/www/html
      - ./logs/nginx:/var/log/nginx
    depends_on:
      - php
      - mysql
    restart: always

  php:
    build: ./php
    volumes:
      - ./www:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/php.ini
    depends_on:
      - mysql
      - redis
    restart: always

  mysql:
    image: mysql:5.7
    ports:
      - "3306:3306"
    volumes:
      - ./mysql/data:/var/lib/mysql
      - ./mysql/conf.d:/etc/mysql/conf.d
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: myapp
      MYSQL_USER: myapp
      MYSQL_PASSWORD: myapp123
    restart: always

  redis:
    image: redis:3.2
    ports:
      - "6379:6379"
    volumes:
      - ./redis/data:/data
      - ./redis/redis.conf:/usr/local/etc/redis/redis.conf
    command: redis-server /usr/local/etc/redis/redis.conf
    restart: always

3. Docker Compose常用命令

# 启动所有服务
docker-compose up
docker-compose up -d  # 后台运行

# 停止并删除所有服务
docker-compose down

# 启动/停止/重启服务
docker-compose start
docker-compose stop
docker-compose restart

# 查看服务状态
docker-compose ps

# 查看日志
docker-compose logs
docker-compose logs -f nginx
docker-compose logs --tail 100

# 构建服务
docker-compose build
docker-compose build php

# 进入服务容器
docker-compose exec php /bin/bash
docker-compose exec mysql mysql -u root -p

# 拉取镜像
docker-compose pull

# 查看配置
docker-compose config

# 暂停/恢复
docker-compose pause
docker-compose unpause

六、实战:用Docker部署WordPress

1. 项目结构

wordpress-docker/
├── docker-compose.yml
├── nginx/
│   ├── nginx.conf
│   └── conf.d/
│       └── wordpress.conf
├── php/
│   ├── Dockerfile
│   └── php.ini
├── mysql/
│   └── conf.d/
│       └── my.cnf
└── www/
    └── (WordPress文件)

2. docker-compose.yml

version: '2'

services:
  nginx:
    image: nginx:1.11
    ports:
      - "80:80"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/nginx.conf
      - ./nginx/conf.d:/etc/nginx/conf.d
      - ./www:/var/www/html
    depends_on:
      - php
      - mysql
    restart: always

  php:
    build: ./php
    volumes:
      - ./www:/var/www/html
      - ./php/php.ini:/usr/local/etc/php/php.ini
    depends_on:
      - mysql
    restart: always

  mysql:
    image: mysql:5.7
    volumes:
      - ./mysql/data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: root123
      MYSQL_DATABASE: wordpress
      MYSQL_USER: wordpress
      MYSQL_PASSWORD: wp123
    restart: always

3. php/Dockerfile

FROM php:7.0-fpm

RUN docker-php-ext-install mysqli pdo pdo_mysql

RUN apt-get update && \
    apt-get install -y libpng-dev libjpeg-dev && \
    docker-php-ext-configure gd --with-jpeg-dir=/usr/include && \
    docker-php-ext-install gd && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

WORKDIR /var/www/html

4. nginx/conf.d/wordpress.conf

server {
    listen 80;
    server_name localhost;
    root /var/www/html;
    index index.php index.html;

    location / {
        try_files $uri $uri/ /index.php?$args;
    }

    location ~ \.php$ {
        fastcgi_pass php:9000;
        fastcgi_index index.php;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
        include fastcgi_params;
    }

    location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg)$ {
        expires 30d;
    }
}

5. 启动

# 下载WordPress到www目录
cd www
wget https://wordpress.org/latest.tar.gz
tar -xzf latest.tar.gz
mv wordpress/* .
rm -rf wordpress latest.tar.gz

# 启动所有服务
cd ..
docker-compose up -d

# 查看状态
docker-compose ps

# 访问 http://localhost 进行WordPress安装
# 数据库主机填 mysql,用户名 wordpress,密码 wp123,数据库名 wordpress

七、Docker最佳实践

1. 镜像最佳实践

  • 使用官方基础镜像,安全有保障
  • 使用固定版本标签,可复现构建
  • 镜像尽量小,使用alpine或slim版本
  • 利用构建缓存,变化少的指令放前面
  • 合并RUN指令,减少层数
  • 清理缓存和临时文件
  • 使用多阶段构建(Docker 17.05+)
  • 不要在镜像中包含敏感信息
  • 定期更新基础镜像,修复安全漏洞

2. 容器最佳实践

  • 一个容器只运行一个进程
  • 容器是无状态的,数据持久化到数据卷
  • 使用非root用户运行容器
  • 限制容器的资源使用(CPU、内存)
  • 设置健康检查(HEALTHCHECK)
  • 容器崩溃自动重启(restart: always)
  • 不要在容器中存储重要数据
  • 日志输出到stdout/stderr,不要写文件

3. 安全最佳实践

  • 使用官方镜像,避免未知来源的镜像
  • 定期扫描镜像漏洞(Docker Security Scanning、Clair等)
  • 不要以root用户运行容器
  • 限制容器的系统调用(seccomp、AppArmor)
  • 不要挂载敏感目录(如/var/run/docker.sock)
  • 使用Docker Content Trust验证镜像签名
  • 敏感信息使用环境变量或secrets,不要硬编码
  • 定期更新Docker和镜像

4. 生产环境最佳实践

  • 使用编排工具(Kubernetes、Docker Swarm、Mesos等)
  • 服务发现和负载均衡
  • 自动扩缩容
  • 滚动更新和回滚
  • 集中式日志收集(ELK、Fluentd等)
  • 监控和告警(Prometheus、Grafana等)
  • 数据备份和恢复
  • 蓝绿部署或金丝雀发布
  • CI/CD自动化部署

八、常见问题与解决方案

1. 容器启动后立即退出

原因:容器的主进程退出了。Docker容器运行的前提是主进程在前台运行。

解决方案:

  • 检查CMD/ENTRYPOINT命令是否正确
  • 确保主进程在前台运行,不要用daemon模式
  • 使用docker logs查看日志
  • 对于nginx,使用CMD ["nginx", "-g", "daemon off;"]
  • 对于调试,可以用docker run -it image /bin/bash进入容器手动运行命令

2. 端口映射不生效

原因:

  • 容器内的服务没有监听0.0.0.0,只监听了127.0.0.1
  • 端口被占用
  • 防火墙阻止

解决方案:

  • 确保容器内服务监听0.0.0.0
  • 检查端口是否被占用:netstat -tlnp | grep 端口
  • 检查防火墙规则
  • 使用docker port container查看端口映射

3. 数据丢失

原因:容器是无状态的,删除容器后数据会丢失。

解决方案:

  • 使用数据卷(VOLUME/-v)持久化数据
  • 数据库的数据目录一定要挂载到宿主机
  • 定期备份数据卷
  • 不要用docker rm -v删除有数据的容器

4. 镜像构建慢

原因:

  • 没有利用构建缓存
  • 每次都重新下载依赖
  • 基础镜像拉取慢

解决方案:

  • 变化少的指令放前面,利用缓存
  • 依赖安装指令放前面,代码复制放后面
  • 配置国内镜像加速
  • 使用本地缓存的基础镜像
  • 合并RUN指令,减少层数

5. 容器之间无法通信

原因:

  • 容器不在同一个网络
  • 使用了容器名称但不在同一个网络
  • 防火墙阻止

解决方案:

  • 使用Docker Compose,服务在同一个默认网络
  • 使用自定义网络:docker network create mynet,启动容器时--network mynet
  • 同一网络中的容器可以用容器名互相访问
  • 检查防火墙规则

6. Docker磁盘占用大

原因:镜像、容器、数据卷占用大量磁盘空间。

解决方案:

# 查看磁盘使用
docker system df

# 清理停止的容器
docker container prune

# 清理未使用的镜像
docker image prune -a

# 清理未使用的数据卷
docker volume prune

# 清理所有未使用的资源
docker system prune -a --volumes

总结

Docker是2013年发布的容器化技术,2016年已经成为开发者和运维人员的必备技能。Docker通过容器化技术,让应用可以在任何环境中一致运行,解决了环境不一致、依赖冲突、部署繁琐等问题。

Docker核心概念:

  • 镜像(Image):容器的只读模板,包含应用和所有依赖
  • 容器(Container):镜像的运行实例,轻量级、可移植、隔离
  • 仓库(Registry):存储和分发镜像的地方
  • Dockerfile:构建镜像的文本文件
  • Docker Compose:多容器编排工具

Docker常用命令:

  • 镜像:search、pull、images、rmi、build、tag、push
  • 容器:run、ps、start、stop、restart、exec、logs、rm、inspect

Dockerfile最佳实践:使用官方基础镜像、固定版本标签、合并RUN指令、利用构建缓存、清理缓存、镜像尽量小。

Docker Compose适合多容器应用的开发和测试环境,通过YAML文件配置服务,一条命令启动所有服务。

生产环境建议使用编排工具(Kubernetes、Docker Swarm等),配合服务发现、负载均衡、自动扩缩容、监控告警、集中日志等。

Docker改变了软件开发和部署的方式,是每个开发者和运维人员都应该掌握的技能。希望本文能帮助你快速上手Docker,享受容器化带来的便利和效率。

最后,Docker生态在快速发展,从Docker到Kubernetes,从容器到微服务,技术在不断进步。保持学习,跟上技术的步伐,才能在这个快速变化的时代立于不败之地。