Docker,是目前世界上,最流行、最强大的容器化技术。
对于程序员来说,Docker,已经成为了必备技能。无论是开发环境搭建,还是应用部署,还是微服务架构,Docker,都能帮助我们,更高效地完成工作。可以说,不会用Docker的程序员,在2015年,已经开始落伍了。
我自己,从2014年开始接触Docker,到现在,已经用了一年多了。从最初的,觉得Docker很神奇,到后来,能够熟练使用Docker,搭建开发环境,部署应用,构建镜像,编排容器,一路走来,踩了不少坑,也总结了不少经验。我见过太多的开发者,因为环境问题,而浪费大量时间;因为部署问题,而导致应用无法运行;因为依赖冲突,而导致各种奇怪的bug。其实,只要用了Docker,这些问题,大部分都能解决。
2015年,Docker,已经成为了容器化技术的绝对主流。Docker公司,在2015年,获得了巨额融资,估值超过10亿美元;Docker 1.7、1.8、1.9版本,陆续发布,功能越来越强大;Docker Compose、Docker Swarm、Docker Machine等工具,也越来越成熟;各大云服务商,包括AWS、Google Cloud、Microsoft Azure、阿里云、腾讯云,都开始支持Docker。Docker,已经从一个,小众的技术,变成了,主流的技术。
今天,分享Docker入门,从什么是Docker,到Docker的基本概念,到Docker的安装和使用,到Dockerfile,到Docker Compose,到最佳实践,全方位讲解Docker的使用方法和技巧,帮你掌握这个强大的容器化技术。
什么是Docker
在讲Docker之前,先简单介绍一下,什么是容器,什么是虚拟机,它们有什么区别。
1. 虚拟机
虚拟机(Virtual Machine),是通过虚拟化技术,在一台物理机上,模拟出多台虚拟的计算机。每台虚拟机,都有自己的操作系统(Guest OS),有自己的CPU、内存、硬盘、网卡等虚拟硬件。
虚拟机的优点是,隔离性好,每台虚拟机,都是完全独立的,互不影响。缺点是,资源占用大,每台虚拟机,都需要运行一个完整的操作系统,启动慢,占用大量的CPU、内存、硬盘资源。
常见的虚拟机软件有,VMware、VirtualBox、KVM、Xen等。
2. 容器
容器(Container),是一种轻量级的虚拟化技术。它,不像虚拟机那样,需要运行一个完整的操作系统,而是,共享宿主机的操作系统内核,只隔离进程和资源。
容器的优点是,轻量级,启动快(秒级启动),资源占用小(一个容器,只占用几十MB的内存),可以在一台物理机上,运行成百上千个容器。缺点是,隔离性不如虚拟机,所有容器,共享宿主机的内核,如果内核有漏洞,可能会影响所有容器。
Docker,就是目前最流行的容器技术。它,让容器的使用,变得非常简单和方便。
3. Docker是什么
Docker,是一个开源的应用容器引擎,基于Go语言开发。它,可以让开发者,把应用和依赖,打包到一个轻量级、可移植的容器中,然后,发布到任何流行的Linux机器上,也可以实现虚拟化。
简单来说,Docker,就是一个,用来装应用的"集装箱"。你把你的应用,和它需要的所有依赖(库、配置文件、环境变量等),都打包到一个Docker镜像里。然后,这个镜像,可以在任何安装了Docker的机器上,运行起来,而且,运行环境,完全一致,不会出现"在我机器上能跑,在你机器上跑不了"的问题。
Docker的口号是:"Build, Ship, and Run Any App, Anywhere."(构建、交付、运行任何应用,在任何地方。)
4. Docker的优势
Docker,有以下优势:
- 快速交付和部署:开发者,可以用Docker,快速创建一个容器,运行应用;测试和运维,可以直接用同一个镜像,部署到测试环境和生产环境。整个流程,快速、一致、可重复。
- 环境一致性:Docker镜像,包含了应用运行所需的所有依赖,确保了,应用在开发、测试、生产环境,运行环境完全一致,不会出现"在我机器上能跑"的问题。
- 轻量级和高性能:Docker容器,共享宿主机内核,不需要运行完整的操作系统,启动快(秒级),资源占用小(几十MB内存),性能接近原生。
- 隔离性:Docker容器,之间是相互隔离的,每个容器,有自己的文件系统、进程空间、网络空间。一个容器的崩溃,不会影响其他容器。
- 可移植性:Docker镜像,可以在任何安装了Docker的机器上运行,无论是笔记本电脑,还是服务器,还是云服务器,都可以。一次构建,到处运行。
- 版本控制和回滚:Docker镜像,有版本控制,可以像Git一样,管理镜像的版本。如果新版本有问题,可以快速回滚到旧版本。
- 微服务架构:Docker,非常适合微服务架构。每个微服务,可以打包成一个镜像,独立部署,独立扩展,互不影响。
- 生态丰富:Docker,有丰富的生态系统。Docker Hub,上有大量的官方镜像和社区镜像,可以直接使用。还有Docker Compose、Docker Swarm、Kubernetes等工具,帮助管理和编排容器。
Docker的基本概念
要学好Docker,首先要理解Docker的几个基本概念:镜像、容器、仓库、Dockerfile、数据卷、网络。
1. 镜像(Image)
Docker镜像,是一个只读的模板,包含了运行应用所需的所有内容:代码、运行时、库、环境变量、配置文件等。
镜像,是用来创建容器的。一个镜像,可以创建多个容器。镜像,是只读的,不能修改;容器,是可写的,可以修改。
镜像,是分层存储的。一个镜像,由多个层(Layer)组成,每一层,对应Dockerfile中的一条指令。这种分层设计,让镜像的复用、共享、构建,都非常高效。比如,多个镜像,可以共享同一个基础层;修改镜像时,只需要修改对应的层,不需要重新构建整个镜像。
镜像,可以通过Dockerfile构建,也可以从Docker Hub等仓库,拉取现成的镜像。
常见的镜像命名格式:仓库名/镜像名:标签。比如,nginx:latest、ubuntu:14.04、mysql:5.6。如果不指定标签,默认是latest。
2. 容器(Container)
Docker容器,是镜像运行时的实例。它,是一个独立的、可执行的应用环境,包含了镜像的内容,以及,运行时的可写层。
容器,可以被启动、停止、删除、重启。每个容器,都是相互隔离的,有自己的文件系统、进程空间、网络空间。
容器的生命周期:
- 创建(create):用镜像,创建一个容器,但不启动
- 启动(start):启动一个已创建的容器
- 运行(run):创建并启动一个容器(相当于create + start)
- 停止(stop):停止一个运行中的容器
- 重启(restart):重启一个容器
- 删除(rm):删除一个容器(容器必须是停止状态,或者用-f强制删除)
- 暂停(pause):暂停容器中的所有进程
- 恢复(unpause):恢复暂停的容器
容器,是临时的。容器删除后,容器中的数据(可写层),也会被删除。如果,需要持久化数据,需要使用数据卷(Volume),或者,把数据目录,挂载到宿主机。
3. 仓库(Repository)
Docker仓库,是用来存储和分发镜像的地方。它,类似于代码的GitHub,是镜像的"代码托管平台"。
Docker仓库,分为公共仓库和私有仓库:
- 公共仓库:如Docker Hub(hub.docker.com),是最大的公共镜像仓库,有大量的官方镜像和社区镜像,可以免费拉取和使用。国内,也有一些公共镜像仓库,如阿里云容器镜像服务、网易云镜像服务、DaoCloud镜像市场等,可以加速镜像拉取。
- 私有仓库:企业或个人,可以搭建自己的私有仓库,用来存储不公开的镜像。Docker官方提供了
registry镜像,可以快速搭建私有仓库。也可以使用Harbor、Nexus等更强大的私有仓库解决方案。
仓库的操作:
docker pull:从仓库,拉取镜像docker push:把本地镜像,推送到仓库docker search:在仓库,搜索镜像
4. Dockerfile
Dockerfile,是一个文本文件,包含了一系列的指令,用来构建Docker镜像。
每条指令,对应镜像的一层。Docker,会按照Dockerfile中的指令顺序,逐层构建镜像。
常见的Dockerfile指令:
FROM:指定基础镜像。每条Dockerfile,第一条指令,必须是FROM。MAINTAINER:指定维护者信息(已过时,推荐用LABEL)。LABEL:给镜像添加元数据(如维护者、版本、描述等)。RUN:在镜像构建过程中,执行命令。每条RUN指令,会在当前镜像的基础上,执行命令,然后,提交为新的一层。CMD:指定容器启动时,默认执行的命令。如果,docker run时,指定了命令,会覆盖CMD。Dockerfile中,只能有一条CMD指令(如果有多条,只有最后一条生效)。ENTRYPOINT:指定容器启动时,执行的命令。和CMD类似,但ENTRYPOINT,不会被docker run的命令参数覆盖,而是,会把命令参数,作为ENTRYPOINT的参数。EXPOSE:声明容器运行时,监听的端口。这只是一个声明,不会自动映射端口。需要在docker run时,用-p参数,映射端口。ENV:设置环境变量。ADD:复制文件到镜像中。和COPY类似,但ADD,支持自动解压tar文件,以及,从URL下载文件。COPY:复制文件到镜像中。推荐使用COPY,而不是ADD,除非需要自动解压或下载。VOLUME:声明数据卷,用于持久化数据。WORKDIR:设置工作目录。后续的RUN、CMD、ENTRYPOINT、COPY、ADD等指令,都会在这个目录下执行。USER:设置运行容器的用户。ARG:设置构建参数。可以在docker build时,用--build-arg传递参数。ONBUILD:设置触发器。当这个镜像,被用作其他镜像的基础镜像时,会执行ONBUILD中的指令。
5. 数据卷(Volume)
数据卷,是Docker中,用来持久化数据的机制。它,是一个或多个容器,专门用来存放数据的目录,绕过了容器的可写层,直接存在于宿主机上。
数据卷的特点:
- 数据卷,在容器创建时初始化
- 数据卷,可以在容器之间,共享和重用
- 对数据卷的修改,直接生效,不会影响镜像
- 数据卷,不会随着容器的删除而删除
- 数据卷,可以挂载到宿主机的指定目录
数据卷的使用方式:
docker run -v /data:创建一个数据卷,挂载到容器的/data目录,宿主机目录由Docker自动分配docker run -v /host/path:/container/path:把宿主机的指定目录,挂载到容器的指定目录docker run -v name:/container/path:创建一个命名数据卷,挂载到容器的指定目录- Dockerfile中的
VOLUME指令:声明数据卷
6. 网络(Network)
Docker,提供了网络功能,让容器之间,可以相互通信,也可以和宿主机、外部网络通信。
Docker的网络模式:
- bridge(默认):Docker会创建一个docker0的虚拟网桥,所有容器,默认连接到这个网桥。容器之间,可以通过IP相互通信;容器,可以通过网桥,访问外部网络;外部网络,需要通过端口映射,才能访问容器。
- host:容器,共享宿主机的网络命名空间。容器,直接使用宿主机的IP和端口,性能最好,但隔离性差,端口容易冲突。
- none:容器,没有网络,只能通过本地回环(lo)通信,不能访问外部网络,外部网络也不能访问它。用于安全要求高的场景。
- container:容器,共享另一个容器的网络命名空间。两个容器,可以通过localhost相互通信。
Docker的网络操作:
docker network ls:列出网络docker network create:创建网络docker network inspect:查看网络详情docker network connect:把容器连接到网络docker network disconnect:把容器从网络断开docker network rm:删除网络
Docker的安装
Docker,支持在Linux、Windows、macOS上安装。但Docker,原生是基于Linux的,在Windows和macOS上,需要通过虚拟机运行。
1. Linux安装
在Linux上安装Docker,非常简单。以Ubuntu为例:
# 更新软件源
sudo apt-get update
# 安装依赖
sudo apt-get install -y apt-transport-https ca-certificates
# 添加Docker官方GPG密钥
sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
# 添加Docker软件源
echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | sudo tee /etc/apt/sources.list.d/docker.list
# 更新软件源
sudo apt-get update
# 安装Docker
sudo apt-get install -y docker-engine
# 启动Docker服务
sudo service docker start
# 设置开机自启
sudo update-rc.d docker defaults
# 验证安装
docker --version
docker info也可以用更简单的方式,使用官方的安装脚本:
curl -fsSL https://get.docker.com/ | sh2. 配置国内镜像加速
由于Docker Hub在国外,拉取镜像的速度,可能比较慢。可以配置国内的镜像加速器,提升拉取速度。
以阿里云为例:
# 创建Docker配置目录
sudo mkdir -p /etc/docker
# 配置镜像加速器
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": ["https://你的加速器地址.mirror.aliyuncs.com"]
}
EOF
# 重启Docker服务
sudo service docker restart阿里云的加速器地址,可以在阿里云容器镜像服务的控制台,免费获取。
也可以使用其他国内镜像源,如网易云、DaoCloud等。
3. Windows和macOS安装
在Windows和macOS上,可以安装Docker Toolbox,它包含了Docker Engine、Docker Compose、Docker Machine、Kitematic等工具,以及一个VirtualBox虚拟机。
下载地址:https://www.docker.com/products/docker-toolbox
安装完成后,运行Docker Quickstart Terminal,会自动创建一个默认的虚拟机,并启动Docker。
注意:在Windows和macOS上,Docker是运行在虚拟机里的,所以,容器的IP,是虚拟机的IP(通常是192.168.99.100),而不是localhost。访问容器时,需要用虚拟机的IP。
2016年以后,Docker推出了Docker for Windows和Docker for Mac,原生支持,不需要VirtualBox,性能更好。但在2015年,还是Docker Toolbox为主。
Docker常用命令
1. 镜像相关命令
# 搜索镜像
docker search nginx
# 拉取镜像
docker pull nginx
docker pull nginx:1.9
docker pull ubuntu:14.04
# 列出本地镜像
docker images
docker images -a # 包括中间层镜像
docker images -q # 只显示镜像ID
# 查看镜像详情
docker inspect nginx
# 查看镜像历史
docker history nginx
# 给镜像打标签
docker tag nginx:latest mynginx:1.0
# 删除镜像
docker rmi nginx
docker rmi -f nginx # 强制删除
docker rmi $(docker images -q) # 删除所有镜像
# 构建镜像(在Dockerfile所在目录)
docker build -t myapp:1.0 .
docker build -t myapp:1.0 -f /path/to/Dockerfile .
docker build --no-cache -t myapp:1.0 . # 不使用缓存
# 导出镜像为tar文件
docker save -o myapp.tar myapp:1.0
# 从tar文件导入镜像
docker load -i myapp.tar
# 推送镜像到仓库
docker push myrepo/myapp:1.02. 容器相关命令
# 创建并启动容器
docker run nginx
docker run -d nginx # 后台运行
docker run -d -p 8080:80 nginx # 端口映射,宿主机8080映射到容器80
docker run -d -p 8080:80 --name mynginx nginx # 指定容器名
docker run -d -v /data:/var/www/html nginx # 挂载数据卷
docker run -d -e MYSQL_ROOT_PASSWORD=123456 mysql # 设置环境变量
docker run -it ubuntu /bin/bash # 交互式运行,进入容器
docker run --rm nginx # 容器停止后自动删除
# 列出容器
docker ps # 运行中的容器
docker ps -a # 所有容器(包括停止的)
docker ps -q # 只显示容器ID
docker ps -a -q # 所有容器ID
# 启动/停止/重启容器
docker start mynginx
docker stop mynginx
docker restart mynginx
# 暂停/恢复容器
docker pause mynginx
docker unpause mynginx
# 进入容器
docker exec -it mynginx /bin/bash
docker exec -it mynginx nginx -v # 在容器中执行命令
# 查看容器日志
docker logs mynginx
docker logs -f mynginx # 实时跟踪日志
docker logs --tail 100 mynginx # 查看最后100行
# 查看容器详情
docker inspect mynginx
# 查看容器进程
docker top mynginx
# 查看容器资源使用情况
docker stats
docker stats mynginx
# 复制文件到容器/从容器复制
docker cp /host/path mynginx:/container/path
docker cp mynginx:/container/path /host/path
# 删除容器
docker rm mynginx
docker rm -f mynginx # 强制删除(运行中的也可以删)
docker rm $(docker ps -a -q) # 删除所有容器
docker rm $(docker ps -a -q -f status=exited) # 删除所有已停止的容器
# 查看容器端口映射
docker port mynginx
# 重命名容器
docker rename oldname newname3. 仓库相关命令
# 登录仓库
docker login
docker login registry.example.com
# 登出仓库
docker logout
# 搜索镜像
docker search nginx
# 拉取镜像
docker pull nginx
# 推送镜像
docker push myrepo/myapp:1.04. 数据卷相关命令
# 创建数据卷
docker volume create myvolume
# 列出数据卷
docker volume ls
# 查看数据卷详情
docker volume inspect myvolume
# 删除数据卷
docker volume rm myvolume
# 删除所有未使用的数据卷
docker volume prune
# 使用数据卷启动容器
docker run -d -v myvolume:/data nginx5. 网络相关命令
# 列出网络
docker network ls
# 创建网络
docker network create mynetwork
docker network create --driver bridge mynetwork
docker network create --subnet 172.20.0.0/16 mynetwork
# 查看网络详情
docker network inspect mynetwork
# 把容器连接到网络
docker network connect mynetwork mycontainer
# 把容器从网络断开
docker network disconnect mynetwork mycontainer
# 删除网络
docker network rm mynetwork
# 删除所有未使用的网络
docker network prune
# 使用网络启动容器
docker run -d --network mynetwork --name mycontainer nginxDockerfile最佳实践
Dockerfile,是构建Docker镜像的核心。写好Dockerfile,能让镜像更小、更安全、构建更快。
1. 选择合适的基础镜像
- 尽量使用官方镜像,安全可靠
- 尽量使用小的基础镜像,如alpine(只有5MB左右),而不是ubuntu(几百MB)
- 指定基础镜像的版本标签,不要用latest,避免不可预测的更新
# 好的例子
FROM node:4.2-alpine
# 不好的例子
FROM ubuntu:latest2. 合并RUN指令
每条RUN指令,都会创建一层。过多的层,会让镜像变大,构建变慢。应该把相关的命令,合并到一条RUN指令中。
# 不好的例子(4层)
RUN apt-get update
RUN apt-get install -y curl
RUN apt-get install -y git
RUN apt-get clean
# 好的例子(1层)
RUN apt-get update && apt-get install -y \
curl \
git \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*3. 利用构建缓存
Docker,会缓存已经构建过的层。如果Dockerfile的指令,和之前一样,并且,上下文没有变化,就会使用缓存,跳过构建。
为了利用缓存,应该把变化少的指令,放在前面;变化多的指令,放在后面。比如,先安装依赖,再复制代码。
# 好的例子:先复制依赖文件,安装依赖,再复制代码
COPY package.json /app/
RUN cd /app && npm install
COPY . /app/
# 不好的例子:先复制所有代码,再安装依赖(每次代码变化,都会导致npm install重新执行)
COPY . /app/
RUN cd /app && npm install4. 使用.dockerignore
.dockerignore文件,类似于.gitignore,用来指定,在构建镜像时,哪些文件,不需要发送给Docker守护进程。
使用.dockerignore,可以减少构建上下文的大小,加快构建速度,也可以避免,把敏感文件(如密码、密钥)打包到镜像中。
# .dockerignore
node_modules
npm-debug.log
.git
.gitignore
.env
*.log
dist
build5. 不要在镜像中存储敏感信息
不要把密码、密钥、证书等敏感信息,写在Dockerfile中,或者打包到镜像中。应该使用环境变量,或者,在运行时,通过数据卷挂载。
# 不好的例子
ENV DB_PASSWORD=123456
# 好的例子:在运行时,通过环境变量传入
# docker run -e DB_PASSWORD=123456 myapp6. 使用多阶段构建(Multi-stage Build)
多阶段构建,可以在一个Dockerfile中,使用多个FROM指令。每个阶段,都是一个独立的构建环境。最后,只需要把最终产物,复制到最后的镜像中,不需要把构建工具和中间产物,打包到镜像中。
注意:多阶段构建,是Docker 17.05以后才支持的。2015年的Docker 1.9,还不支持。但这个概念,很重要,提前了解。
# 构建阶段
FROM golang:1.5 AS builder
WORKDIR /app
COPY . .
RUN go build -o myapp .
# 最终镜像
FROM alpine:3.4
WORKDIR /app
COPY --from=builder /app/myapp .
CMD ["./myapp"]7. 设置非root用户
默认情况下,容器中的进程,是以root用户运行的。这有安全风险。应该在Dockerfile中,创建一个普通用户,并切换到这个用户运行。
RUN useradd -m appuser
USER appuser8. 合理使用CMD和ENTRYPOINT
- CMD:指定容器启动时,默认执行的命令,可以被docker run的参数覆盖
- ENTRYPOINT:指定容器启动时,执行的命令,不会被docker run的参数覆盖,而是把参数作为ENTRYPOINT的参数
如果,容器是用来运行一个固定的应用,推荐使用ENTRYPOINT;如果,容器是用来运行命令的,推荐使用CMD。
也可以组合使用:ENTRYPOINT指定固定的命令,CMD指定默认的参数。
ENTRYPOINT ["nginx"]
CMD ["-g", "daemon off;"]9. 健康检查
Docker 1.12以后,支持HEALTHCHECK指令,用来检查容器的健康状态。2015年的Docker 1.9,还不支持,但可以了解。
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost/ || exit 110. 完整的Dockerfile示例
以一个Node.js应用为例:
# 基础镜像
FROM node:4.2-alpine
# 维护者
LABEL maintainer="your@email.com"
# 工作目录
WORKDIR /app
# 复制依赖文件
COPY package.json /app/
# 安装依赖
RUN npm install --production \
&& npm cache clean
# 复制代码
COPY . /app/
# 暴露端口
EXPOSE 3000
# 设置环境变量
ENV NODE_ENV=production
# 创建非root用户
RUN adduser -D appuser
USER appuser
# 启动命令
CMD ["node", "server.js"]Docker Compose
Docker Compose,是Docker官方的编排工具,用来定义和运行多容器的Docker应用。
通过一个docker-compose.yml文件,可以定义应用需要的所有服务(容器),然后,用一条命令,启动所有服务。
1. 安装Docker Compose
在Linux上:
# 下载Docker Compose
curl -L https://github.com/docker/compose/releases/download/1.5.2/docker-compose-`uname -s`-`uname -m` > /usr/local/bin/docker-compose
# 添加执行权限
chmod +x /usr/local/bin/docker-compose
# 验证安装
docker-compose --version在Windows和macOS上,Docker Toolbox已经包含了Docker Compose,不需要单独安装。
2. docker-compose.yml示例
以一个WordPress应用为例(Nginx + PHP + MySQL):
web:
image: nginx:latest
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/conf.d/default.conf
- ./www:/var/www/html
links:
- php
php:
image: php:5.6-fpm
volumes:
- ./www:/var/www/html
links:
- mysql
mysql:
image: mysql:5.6
environment:
MYSQL_ROOT_PASSWORD: 123456
MYSQL_DATABASE: wordpress
volumes:
- ./mysql:/var/lib/mysql这个文件,定义了三个服务:web(Nginx)、php(PHP-FPM)、mysql(MySQL)。它们之间,通过links连接,共享数据卷。
3. Docker Compose常用命令
# 启动所有服务(后台运行)
docker-compose up -d
# 启动所有服务(前台运行,查看日志)
docker-compose up
# 停止所有服务
docker-compose stop
# 停止并删除所有容器、网络
docker-compose down
# 查看服务状态
docker-compose ps
# 查看服务日志
docker-compose logs
docker-compose logs -f web # 查看web服务的日志
# 构建服务
docker-compose build
docker-compose build web # 只构建web服务
# 拉取服务镜像
docker-compose pull
# 在服务中执行命令
docker-compose exec web /bin/bash
docker-compose exec mysql mysql -u root -p
# 重启服务
docker-compose restart
docker-compose restart web
# 暂停/恢复服务
docker-compose pause
docker-compose unpause
# 缩放服务(运行多个实例)
docker-compose scale web=34. Docker Compose最佳实践
- 使用环境变量,管理配置(如数据库密码、端口等),不要硬编码
- 使用数据卷,持久化数据
- 合理使用links,连接服务
- 为每个服务,指定镜像版本,不要用latest
- 使用extends,复用配置
- 使用多个docker-compose文件,区分不同环境(开发、测试、生产)
Docker常见问题和解决方法
1. 容器启动后立即退出
原因:容器的主进程,执行完后就退出了。Docker容器,需要有一个前台进程,持续运行。
解决方法:确保容器的CMD或ENTRYPOINT,是一个前台运行的命令。比如,Nginx要用nginx -g "daemon off;",MySQL要用mysqld,而不是后台运行的命令。
2. 无法访问容器端口
原因:没有做端口映射,或者,映射的端口不对。
解决方法:用docker run -p 宿主机端口:容器端口,做端口映射。用docker port 容器名,查看端口映射情况。用docker ps,查看PORTS列。
注意:在Windows和macOS上,Docker运行在虚拟机里,需要用虚拟机的IP(通常是192.168.99.100)访问,而不是localhost。
3. 容器之间无法通信
原因:容器不在同一个网络,或者,没有用links连接。
解决方法:用--link参数,连接容器;或者,创建自定义网络,把容器都连接到这个网络。在同一个网络中的容器,可以用容器名,相互访问。
4. 数据丢失
原因:数据存在容器的可写层,容器删除后,数据也丢失了。
解决方法:使用数据卷(-v参数),把数据目录,挂载到宿主机,或者,使用命名数据卷。这样,容器删除后,数据还在。
5. 镜像太大
原因:基础镜像太大,或者,Dockerfile写得不好,层数太多,没有清理缓存。
解决方法:
- 使用小的基础镜像,如alpine
- 合并RUN指令,减少层数
- 在RUN指令中,清理缓存(如apt-get clean、yum clean、npm cache clean)
- 使用多阶段构建,只把最终产物,打包到镜像中
- 用.dockerignore,排除不需要的文件
6. 构建镜像很慢
原因:没有利用构建缓存,或者,构建上下文太大。
解决方法:
- 把变化少的指令,放在前面,利用缓存
- 用.dockerignore,减少构建上下文
- 使用国内镜像源,加速依赖下载
- 使用本地的依赖缓存,挂载到容器中
7. 权限问题
原因:容器中的进程,以root用户运行,或者,挂载的宿主机目录,权限不对。
解决方法:
- 在Dockerfile中,创建非root用户,用USER指令切换
- 挂载宿主机目录时,确保目录权限正确
- 用
--user参数,指定运行容器的用户
8. 时区问题
原因:Docker容器,默认时区是UTC,和国内的东八区,差8小时。
解决方法:
- 在Dockerfile中,设置时区:
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime - 或者,在运行容器时,挂载宿主机的时区文件:
-v /etc/localtime:/etc/localtime:ro - 或者,设置环境变量:
-e TZ=Asia/Shanghai
9. DNS问题
原因:容器无法解析域名。
解决方法:
- 检查宿主机的DNS配置
- 在运行容器时,指定DNS:
--dns 8.8.8.8 --dns 114.114.114.114 - 或者,在Docker守护进程配置中,设置默认DNS
10. 磁盘空间不足
原因:Docker的镜像、容器、数据卷,占用了大量磁盘空间。
解决方法:
- 清理无用的镜像:
docker rmi $(docker images -f "dangling=true" -q) - 清理已停止的容器:
docker rm $(docker ps -a -q -f status=exited) - 清理无用的数据卷:
docker volume rm $(docker volume ls -qf dangling=true) - 清理无用的网络:
docker network prune - Docker 1.13以后,可以用
docker system prune,一键清理所有无用资源
Docker最佳实践
1. 镜像管理
- 一个容器,只运行一个进程(单一职责)
- 镜像要小,使用alpine等小基础镜像
- 指定镜像版本,不要用latest
- 定期更新基础镜像,修复安全漏洞
- 不要在镜像中,存储敏感信息
- 使用.dockerignore,排除不需要的文件
2. 容器管理
- 容器是临时的,不要在容器中存储数据
- 使用数据卷,持久化数据
- 容器启动要快,应用要支持快速启动和优雅停止
- 为容器设置资源限制(CPU、内存),避免一个容器占用所有资源
- 为容器设置健康检查
- 容器出问题时,直接删除重建,不要在容器中修改(不可变基础设施)
3. 安全
- 容器中的进程,以非root用户运行
- 不要在容器中,运行sshd等不必要的服务
- 只暴露需要的端口
- 使用官方镜像,或可信的镜像
- 定期扫描镜像漏洞
- 限制容器的系统调用和权限(--cap-drop、--read-only等)
4. 日志
- 应用日志,输出到stdout和stderr,不要写文件
- 用
docker logs查看日志 - 用日志收集工具(如ELK、Fluentd),集中收集和管理日志
- 不要在容器中,存储日志文件
5. 配置
- 配置,通过环境变量传入,不要硬编码在镜像中
- 不同环境(开发、测试、生产),使用不同的环境变量
- 敏感配置(密码、密钥),使用密钥管理工具(如Vault),不要明文存储
6. 开发流程
- 开发环境,用Docker,统一开发环境
- CI/CD流程中,用Docker构建和测试
- 镜像构建后,推送到私有仓库
- 部署时,从私有仓库拉取镜像,部署到生产环境
- 使用Docker Compose或Kubernetes,编排和管理容器
7. 监控
- 监控容器的CPU、内存、磁盘、网络使用情况
- 监控容器的健康状态
- 监控应用的性能和错误
- 使用监控工具(如Prometheus、Grafana、cAdvisor)
学习资源
推荐书籍
- 《Docker技术入门与实战》:国内作者写的Docker书籍,非常详细,适合入门和深入学习。
- 《第一本Docker书》(The Docker Book):James Turnbull写的,Docker入门经典,通俗易懂。
- 《Docker实战》(Docker in Action):深入讲解Docker的使用和最佳实践,适合有一定基础的读者。
在线教程
- Docker官方文档:https://docs.docker.com/ ,最权威、最全面的Docker文档。
- Docker官方入门教程:https://docs.docker.com/engine/getstarted/
- 阮一峰的Docker入门教程:http://www.ruanyifeng.com/blog/2016/04/dockercontainerguide.html
- 菜鸟教程Docker教程:https://www.runoob.com/docker/docker-tutorial.html
视频教程
- Docker官方YouTube频道:https://www.youtube.com/user/dockerrun
- 极客学院Docker教程
- 慕课网Docker教程
社区和工具
- Docker Hub:https://hub.docker.com/ ,最大的Docker镜像仓库。
- Docker GitHub:https://github.com/docker ,Docker的开源项目。
- Docker论坛:https://forums.docker.com/
- Stack Overflow Docker标签:https://stackoverflow.com/questions/tagged/docker
- Kitematic:Docker的图形化管理工具,适合新手。
- Portainer:轻量级的Docker管理UI,功能强大。
写在最后
Docker,是目前世界上,最流行、最强大的容器化技术。对于程序员来说,Docker,已经成为了必备技能。掌握了Docker,你就能更高效地搭建开发环境,部署应用,构建微服务架构。
学习Docker,不要死记硬背命令,而要理解它的基本原理。理解了镜像、容器、数据卷、网络等基本概念,很多命令,就自然而然地理解了。
另外,学习Docker,最好的方式,就是多实践。在实际项目中,多使用Docker,遇到问题,多查文档,多尝试,慢慢就熟练了。
2015年,Docker,还在快速发展中。Docker Compose、Docker Swarm等工具,还在不断完善;Kubernetes,也在2015年,由Google捐赠给CNCF,开始快速发展。容器化技术,正在改变软件开发和部署的方式。
"工欲善其事,必先利其器。"希望这篇文章,能帮你掌握Docker这个强大的工具,让你的开发工作,更加高效、更加顺畅。
最后,记住:Docker,只是工具。好的工具,能提升效率,但真正决定项目质量的,还是架构设计和代码本身。 不要为了用Docker而用Docker,要根据项目的实际需求,合理使用。
祝你,在Docker的世界里,游刃有余,高效开发!
评论(0)
暂无评论,快来抢沙发~
评论功能仅对会员开放,请先登录
登录