Docker是2016年最火爆的技术之一,它彻底改变了应用部署的方式。在Docker出现之前,部署一个应用需要安装各种依赖、配置环境、处理兼容性问题,过程繁琐且容易出错。有了Docker,我们可以把应用和它的所有依赖打包成一个镜像,然后在任何支持Docker的环境中一键运行,真正实现"一次构建,到处运行"。

2016年,Docker已经从一个新兴技术发展成为容器化的事实标准,被越来越多的公司采用,从创业公司到大型企业,都在使用Docker进行应用部署和运维。微服务架构的兴起,更是让Docker成为必备技能。作为程序员,掌握Docker已经不是加分项,而是必备项。

今天就来详细讲解Docker入门与实战,从基础概念到生产环境部署,帮助你快速掌握容器化部署技术。

一、Docker基础概念

1. 什么是Docker

Docker是一个开源的容器化平台,它可以让开发者把应用和它的所有依赖打包成一个轻量级、可移植的容器,然后在任何支持Docker的环境中运行。

Docker的核心思想是"Build, Ship, and Run Any App, Anywhere"(构建、交付、运行任何应用,在任何地方)。它解决了"在我机器上能跑"的问题,让应用部署变得简单、快速、可移植。

2. 容器 vs 虚拟机

很多人会把Docker容器和虚拟机混淆,其实它们有本质的区别:

对比项容器(Docker)虚拟机(VM)
虚拟化级别操作系统级虚拟化硬件级虚拟化
内核共享宿主机内核每个VM有独立内核
体积小(MB级)大(GB级)
启动速度快(秒级)慢(分钟级)
性能接近原生有性能损耗
资源占用
隔离性进程级隔离完全隔离

简单来说,虚拟机是在硬件层面模拟一台完整的计算机,而容器是在操作系统层面隔离进程,共享宿主机的内核。容器更轻量、更快速、更高效,但是隔离性不如虚拟机。

3. Docker的核心组件

  • 镜像(Image):只读模板,包含运行应用所需的代码、运行时、库、环境变量、配置文件等。镜像是容器的基础,一个镜像可以创建多个容器。
  • 容器(Container):镜像的运行实例,是一个独立的、可执行的应用环境。容器可以被创建、启动、停止、删除,每个容器之间是隔离的。
  • 仓库(Registry):存储和分发镜像的地方。Docker Hub是官方的公共仓库,也可以搭建私有仓库(如Harbor)。
  • Dockerfile:用来构建镜像的文本文件,包含一系列指令,描述如何构建镜像。
  • 数据卷(Volume):用于持久化数据,容器删除后数据卷中的数据不会丢失。
  • 网络(Network):容器之间的通信网络,Docker提供了多种网络模式。

4. Docker的优势

  • 快速部署:容器启动只需几秒,大大缩短部署时间
  • 环境一致性:开发、测试、生产环境一致,避免"在我机器上能跑"的问题
  • 轻量高效:容器体积小、启动快、资源占用低
  • 可移植性:一次构建,到处运行,支持公有云、私有云、物理机、虚拟机
  • 微服务友好:每个微服务可以打包成一个容器,独立部署、独立扩展
  • 版本管理:镜像支持版本管理,可以回滚到任意版本
  • 生态丰富:Docker Hub上有大量现成的镜像,可以直接使用

二、Docker安装与配置

1. 安装Docker

Docker支持Linux、Windows、macOS等多种操作系统。2016年,Docker在Linux上的支持最完善,Windows和macOS通过Docker Toolbox或Docker for Windows/Mac运行。

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"

# 更新包索引并安装Docker
sudo apt-get update
sudo apt-get install -y docker-ce

# 启动Docker并设置开机自启
sudo systemctl start docker
sudo systemctl enable docker

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

CentOS/RHEL安装

# 安装依赖
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

2. 配置Docker

配置镜像加速器(国内必配): 由于国内网络原因,从Docker Hub拉取镜像很慢,建议配置国内镜像加速器。

# 创建或修改Docker配置文件
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",
    "https://hub-mirror.c.163.com"
  ]
}
EOF

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

将当前用户加入docker组(免sudo)

sudo usermod -aG docker $USER
# 重新登录后生效

三、Docker基本操作

1. 镜像操作

# 搜索镜像
docker search nginx

# 拉取镜像(默认latest标签)
docker pull nginx
docker pull nginx:1.21
docker pull ubuntu:16.04

# 查看本地镜像
docker images

# 删除镜像
docker rmi nginx:1.21
docker rmi -f nginx  # 强制删除

# 导出镜像为tar文件
docker save -o nginx.tar nginx:latest

# 从tar文件导入镜像
docker load -i nginx.tar

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

2. 容器操作

# 运行容器
docker run nginx                          # 前台运行
docker run -d nginx                       # 后台运行(守护进程)
docker run -d --name mynginx nginx        # 指定容器名
docker run -d -p 8080:80 nginx           # 端口映射(宿主机8080 -> 容器80)
docker run -d -p 8080:80 -v /data:/usr/share/nginx/html nginx  # 挂载数据卷
docker run -it ubuntu /bin/bash           # 交互式运行(进入容器)
docker run --rm nginx                     # 容器停止后自动删除

# 查看运行中的容器
docker ps

# 查看所有容器(包括已停止的)
docker ps -a

# 停止容器
docker stop mynginx
docker stop <容器ID>

# 启动已停止的容器
docker start mynginx

# 重启容器
docker restart mynginx

# 进入运行中的容器
docker exec -it mynginx /bin/bash
docker exec -it mynginx nginx -t          # 在容器中执行命令

# 查看容器日志
docker logs mynginx
docker logs -f mynginx                     # 实时跟踪日志
docker logs --tail 100 mynginx            # 查看最后100行

# 查看容器详情
docker inspect mynginx

# 查看容器资源使用情况
docker stats

# 删除容器(容器必须先停止)
docker rm mynginx
docker rm -f mynginx                       # 强制删除(即使在运行)
docker rm $(docker ps -aq)                 # 删除所有已停止的容器

# 复制文件到容器/从容器复制
docker cp index.html mynginx:/usr/share/nginx/html/
docker cp mynginx:/etc/nginx/nginx.conf ./

四、Dockerfile编写

Dockerfile是用来构建镜像的文本文件,包含一系列指令。掌握Dockerfile编写是Docker的核心技能。

1. Dockerfile常用指令

指令说明示例
FROM指定基础镜像FROM ubuntu:16.04
MAINTAINER维护者信息(已弃用,用LABEL)MAINTAINER name <email>
LABEL元数据标签LABEL maintainer="name"
RUN构建时执行命令RUN apt-get update && apt-get install -y nginx
CMD容器启动时默认执行的命令CMD ["nginx", "-g", "daemon off;"]
ENTRYPOINT容器启动时执行的命令(不可被覆盖)ENTRYPOINT ["nginx"]
EXPOSE声明容器监听的端口EXPOSE 80 443
ENV设置环境变量ENV NODE_ENV=production
ADD复制文件到容器(支持URL和解压)ADD app.tar.gz /app/
COPY复制文件到容器(推荐)COPY index.html /var/www/html/
VOLUME声明数据卷VOLUME ["/data"]
WORKDIR设置工作目录WORKDIR /app
USER设置运行用户USER www-data
ARG构建参数ARG VERSION=1.0
.dockerignore排除不需要的文件类似.gitignore

2. Dockerfile示例:Nginx

# 基础镜像
FROM ubuntu:16.04

# 维护者
LABEL maintainer="dev@example.com"

# 安装Nginx
RUN apt-get update && \
    apt-get install -y nginx && \
    apt-get clean && \
    rm -rf /var/lib/apt/lists/*

# 复制配置文件和网站文件
COPY nginx.conf /etc/nginx/nginx.conf
COPY html/ /var/www/html/

# 声明端口
EXPOSE 80

# 启动Nginx(前台运行)
CMD ["nginx", "-g", "daemon off;"]

3. Dockerfile示例:PHP+Apache

FROM php:7.4-apache

# 安装PHP扩展
RUN docker-php-ext-install pdo pdo_mysql mysqli

# 启用Apache重写模块
RUN a2enmod rewrite

# 复制应用代码
COPY src/ /var/www/html/

# 设置权限
RUN chown -R www-data:www-data /var/www/html/

EXPOSE 80

CMD ["apache2-foreground"]

4. Dockerfile最佳实践

  • 使用官方基础镜像:优先使用Docker Hub上的官方镜像,安全可靠
  • 固定镜像版本:不要用latest,用具体版本号如ubuntu:16.04,确保可重现
  • 合并RUN指令:多个RUN指令合并成一个,减少镜像层数
  • 清理缓存:apt-get install后执行apt-get clean和rm -rf /var/lib/apt/lists/*,减小镜像体积
  • 使用COPY而非ADD:COPY更简单透明,ADD有额外功能(URL、解压)容易混淆
  • 利用构建缓存:把不常变化的指令放在前面,常变化的放在后面,充分利用缓存
  • 使用.dockerignore:排除不需要的文件(如.git、node_modules、日志等),减小构建上下文
  • 多阶段构建:编译环境和运行环境分离,减小最终镜像体积(Docker 17.05+支持)
  • 不要在镜像中存储敏感信息:密码、密钥等用环境变量或配置文件注入,不要写在Dockerfile中
  • 以非root用户运行:创建普通用户,用USER指令切换,提高安全性

5. 构建镜像

# 构建镜像(.是Dockerfile所在目录)
docker build -t myapp:1.0 .

# 指定Dockerfile路径
docker build -f /path/to/Dockerfile -t myapp:1.0 .

# 构建时传递参数
docker build --build-arg VERSION=1.0 -t myapp:1.0 .

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

五、数据卷(Volume)

容器是临时的,容器删除后数据会丢失。对于需要持久化的数据(如数据库数据、用户上传文件、日志等),需要使用数据卷。

1. 数据卷类型

  • 匿名卷:只指定容器内路径,Docker自动在宿主机创建目录

``bash docker run -d -v /var/lib/mysql mysql ``

  • 命名卷:给卷起个名字,方便管理

``bash docker run -d -v mysql_data:/var/lib/mysql mysql ``

  • 绑定挂载:直接挂载宿主机目录到容器

``bash docker run -d -v /host/data:/container/data nginx ``

2. 数据卷操作

# 创建数据卷
docker volume create myvolume

# 查看数据卷
docker volume ls

# 查看数据卷详情
docker volume inspect myvolume

# 删除数据卷
docker volume rm myvolume

# 删除所有未使用的数据卷
docker volume prune

# 运行容器时挂载数据卷
docker run -d -v myvolume:/data nginx

# 挂载宿主机目录(绑定挂载)
docker run -d -v /host/path:/container/path nginx

# 只读挂载
docker run -d -v /host/path:/container/path:ro nginx

3. 数据卷注意事项

  • 数据卷独立于容器,删除容器不会删除数据卷
  • 多个容器可以共享同一个数据卷
  • 绑定挂载可以方便地在宿主机和容器之间共享文件,适合开发环境
  • 命名卷更适合生产环境,由Docker管理,更安全
  • 数据库等有状态应用一定要用数据卷持久化数据

六、Docker网络

Docker提供了多种网络模式,容器之间可以通过网络通信。

1. Docker网络模式

  • bridge(默认):每个容器连接到默认的docker0网桥,容器之间可以通过IP通信
  • host:容器共享宿主机的网络栈,性能高但隔离性差
  • none:容器没有网络,完全隔离
  • container:容器共享另一个容器的网络栈
  • 自定义网络:用户创建的bridge网络,支持容器名解析(DNS),推荐使用

2. 网络操作

# 查看网络
docker network ls

# 创建自定义网络
docker network create mynetwork

# 查看网络详情
docker network inspect mynetwork

# 运行容器时指定网络
docker run -d --name app --network mynetwork nginx

# 连接容器到网络
docker network connect mynetwork container2

# 断开容器与网络的连接
docker network disconnect mynetwork container2

# 删除网络
docker network rm mynetwork

# 删除所有未使用的网络
docker network prune

3. 容器间通信

在同一个自定义网络中,容器可以通过容器名互相访问(Docker内置DNS):

# 创建网络
docker network create appnet

# 运行MySQL容器
docker run -d --name db --network appnet -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7

# 运行Web容器,连接到同一个网络,可以用容器名db访问MySQL
docker run -d --name web --network appnet -p 8080:80 mywebapp
# 在web容器中,数据库主机名就是db

七、Docker Compose

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

1. 安装Docker Compose

# 下载Docker Compose
sudo curl -L "https://github.com/docker/compose/releases/download/1.29.2/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose

# 添加执行权限
sudo chmod +x /usr/local/bin/docker-compose

# 验证安装
docker-compose --version

2. docker-compose.yml示例

一个典型的Web应用(Nginx + PHP + MySQL):

version: '3'

services:
  # Nginx服务
  nginx:
    image: nginx:1.21
    ports:
      - "8080:80"
    volumes:
      - ./nginx.conf:/etc/nginx/nginx.conf
      - ./html:/var/www/html
    depends_on:
      - php
    networks:
      - appnet

  # PHP服务
  php:
    build: ./php
    volumes:
      - ./html:/var/www/html
    depends_on:
      - mysql
    networks:
      - appnet

  # MySQL服务
  mysql:
    image: mysql:5.7
    environment:
      MYSQL_ROOT_PASSWORD: 123456
      MYSQL_DATABASE: appdb
      MYSQL_USER: appuser
      MYSQL_PASSWORD: apppass
    volumes:
      - mysql_data:/var/lib/mysql
    networks:
      - appnet

# 数据卷
volumes:
  mysql_data:

# 网络
networks:
  appnet:
    driver: bridge

3. Docker Compose常用命令

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

# 启动并构建镜像
docker-compose up -d --build

# 查看服务状态
docker-compose ps

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

# 停止服务
docker-compose stop

# 启动已停止的服务
docker-compose start

# 重启服务
docker-compose restart
docker-compose restart nginx

# 停止并删除容器、网络(保留数据卷)
docker-compose down

# 停止并删除容器、网络、数据卷(危险!数据会丢失)
docker-compose down -v

# 进入服务容器
docker-compose exec php /bin/bash

# 构建镜像
docker-compose build
docker-compose build --no-cache

# 查看配置
docker-compose config

# 拉取镜像
docker-compose pull

4. Docker Compose最佳实践

  • 用环境变量管理配置(如数据库密码),不要硬编码
  • 用dependson定义服务启动顺序(注意:dependson只保证启动顺序,不保证服务就绪)
  • 用数据卷持久化数据库等有状态服务的数据
  • 用自定义网络隔离不同应用
  • 开发环境用绑定挂载,生产环境用命名卷
  • 不同环境用不同的compose文件(docker-compose.yml + docker-compose.prod.yml)

八、生产环境部署

1. 镜像管理

  • 搭建私有仓库:生产环境建议搭建私有Docker仓库(如Harbor),存储自己的镜像
  • 镜像版本管理:用Git commit hash或版本号作为镜像标签,不要用latest
  • 安全扫描:用Trivy、Clair等工具扫描镜像漏洞
  • 最小化镜像:用Alpine等轻量基础镜像,减小镜像体积和攻击面

2. 容器编排

单机用Docker Compose,多机/集群用Kubernetes(K8s)。2016年,Kubernetes已经成为容器编排的事实标准,但是对于中小团队,Docker Compose已经足够。

3. 日志管理

  • 容器日志输出到stdout/stderr,用docker logs查看
  • 生产环境用ELK(Elasticsearch + Logstash + Kibana)或EFK收集和分析日志
  • 用log driver把日志发送到远程日志服务

4. 监控

  • 用docker stats查看容器资源使用
  • 生产环境用Prometheus + Grafana监控容器和宿主机
  • 设置告警,及时发现问题

5. 安全

  • 以非root用户运行容器
  • 不要在镜像中存储敏感信息,用环境变量或Secret注入
  • 限制容器资源(CPU、内存),防止单个容器占用过多资源

``bash docker run -d --memory=512m --cpus=1 nginx ``

  • 只读挂载根文件系统,只给需要写的目录挂载可写卷
  • 定期更新基础镜像,修复安全漏洞

6. CI/CD集成

  • 代码提交后,CI自动构建镜像、运行测试
  • 测试通过后,推送镜像到私有仓库
  • CD自动部署到生产环境(蓝绿部署、滚动更新)
  • 常见工具:Jenkins、GitLab CI、GitHub Actions、Drone

九、常见问题与解决方案

1. 容器启动后立即退出

  • 原因:容器没有前台进程,Docker容器需要一个前台进程保持运行
  • 解决:确保CMD或ENTRYPOINT启动的是前台进程,如nginx -g "daemon off;"

2. 端口映射不生效

  • 检查容器内应用是否监听0.0.0.0而不是127.0.0.1
  • 检查防火墙是否开放端口
  • 用docker ps确认端口映射是否正确

3. 数据丢失

  • 原因:没有用数据卷持久化数据,容器删除后数据丢失
  • 解决:数据库等有状态服务一定要用数据卷

4. 镜像体积太大

  • 用Alpine等轻量基础镜像
  • 合并RUN指令,清理包缓存
  • 用多阶段构建,只把编译产物复制到最终镜像
  • 用.dockerignore排除不需要的文件

5. 容器时间不对

  • 原因:容器默认用UTC时间
  • 解决:挂载宿主机时区文件

``bash docker run -v /etc/localtime:/etc/localtime:ro nginx ``

总结

Docker是2016年最火爆的技术之一,它彻底改变了应用部署的方式。本文从基础概念、安装配置、基本操作、Dockerfile编写、数据卷、网络、Docker Compose、生产环境部署等方面,详细讲解了Docker入门与实战。

核心要点总结:

  1. 镜像和容器:镜像是只读模板,容器是镜像的运行实例
  2. Dockerfile:用Dockerfile构建镜像,遵循最佳实践(合并RUN、清理缓存、利用缓存、.dockerignore)
  3. 数据卷:有状态服务用数据卷持久化数据,容器删除数据不丢失
  4. 网络:用自定义网络,容器之间可以通过容器名通信
  5. Docker Compose:用YAML文件定义多容器应用,一条命令启动所有服务
  6. 生产环境:私有仓库、镜像版本管理、日志监控、安全加固、CI/CD集成

Docker的学习曲线不算陡峭,但是要精通需要大量实践。建议从简单的应用开始,逐步尝试更复杂的场景。Docker不仅仅是一个工具,更是一种思维方式——它改变了我们开发、测试、部署应用的方式。

在微服务和云原生的时代,Docker已经成为程序员的必备技能。希望本文能帮助你快速入门Docker,在实际项目中用好容器化技术。

最后,记住Docker的核心理念:"Build, Ship, and Run Any App, Anywhere"。一次构建,到处运行。这就是Docker的魅力所在。