一、引言
相信不少开发者都遇到过这样的场景:自己在本地运行完全正常的项目,打包发给别人后却无法运行,甚至出现各种难以预料的bug。这类问题往往并非代码本身有误,而是由于运行环境或依赖项的版本不一致所导致的。为了解决环境差异带来的兼容性问题,通常需要投入大量时间进行环境适配和部署调试,整个过程既耗时又耗力。
正是为了应对这一痛点,Docker应运而生。简而言之,Docker 通过将应用程序及其完整的运行环境(包括系统库、依赖项、配置文件等)一同打包,从而确保了应用在任何支持 Docker 的环境中都能以一致的方式运行。这相当于将“软件”与“运行环境”一体交付,从根本上避免了因环境差异导致的兼容性问题。
下面我们再来展开讨论一下Docker!
二、Docker是什么?
Docker 是一种容器化技术,其目标与虚拟机类似——实现环境的一致性与可移植性。例如,我们可以将一个配置好的 Ubuntu 系统打包发给其他人,并在 VMware 上运行,从而保证环境统一。然而,Docker 并非虚拟机,其本质是一种更加轻量级的进程隔离与封装技术。
它的优势在于
- 环境一致性:一次构建,到处运行
- 轻量级:秒级启动,资源占用小
- 标准化:将应用和环境打包成标准单元
Docker具有三大核心概念,分别是:镜像、容器、Dockerfile。镜像的意思有点类同于一套环境模板,我们可以向这个模板里面安装需要的各种运行环境,比如Python、C++等,同时映射到嵌入式领域,它同样可以去保证Gcc工具链的一致性,从而确保了一个整体环境的一致性。而容器就是基于镜像这个模板运行起来的实体化程序,比如我基于python完成了一个websocket服务器源码,同时这个脚本在镜像环境下运行,这个进程就被叫做容器。而Dockerfile相当于就是一套规则,这个规则指的是搭建镜像环境的一套规则文件。
打一个很通俗的比喻:
- Dockerfile = 菜谱
- 镜像 = 做好的菜品(可以复制)
- 容器 = 客人吃这份菜的过程
现在我们对于Docker已经有了一个初步的了解了。接下来我将介绍一下Docker的基本用法。
三、Docker常用指令
1.docker version——查看Docker版本
# 基本用法
docker version
# 示例输出:
Client: Docker Engine - Community
Version: 20.10.7
API version: 1.41
Go version: go1.13.15
Git commit: f0df350
Built: Wed Jun 2 11:56:40 2021
OS/Arch: linux/amd64
Context: default
Experimental: true
Server: Docker Engine - Community
Engine:
Version: 20.10.7
API version: 1.41 (minimum version 1.12)
Go version: go1.13.15
Git commit: b0f5bc3
Built: Wed Jun 2 11:54:50 2021
OS/Arch: linux/amd64
Experimental: false
containerd:
Version: 1.4.6
GitCommit: d71fcd7d8303cbf684402823e425e9dd2e99285d
runc:
Version: 1.0.0-rc95
GitCommit: b9ee9c6314599f1b4a7f497e1f1f856fe433d3b7
docker-init:
Version: 0.19.0
GitCommit: de40ad0
2.docker images——查看本地镜像
# 基本用法
docker images
# 示例输出:
REPOSITORY TAG IMAGE ID CREATED SIZE
ubuntu 20.04 ba6acccedd29 2 weeks ago 72.8MB
nginx latest 08b152afcfae 3 weeks ago 133MB
python 3.9 28a4c88cdbbf 4 weeks ago 882MB
# 常用选项:
docker images -a # 显示所有镜像(包括中间层镜像)
docker images --digests # 显示镜像摘要(digest)
docker images --no-trunc # 显示完整的IMAGE ID
docker images -q # 只显示镜像ID
# 格式化输出:
docker images --format "table {{.Repository}}\t{{.Tag}}\t{{.ID}}\t{{.Size}}"
3.docker build——构建镜像
# 基本语法
docker build [OPTIONS] PATH
# 示例:
docker build -t myapp:1.0 . # 使用当前目录的Dockerfile
docker build -f Dockerfile.dev -t dev:latest . # 指定Dockerfile
docker build --no-cache -t myapp . # 不使用缓存构建
# 常用选项:
-t, --tag string # 设置镜像标签(name:tag)
-f, --file string # 指定Dockerfile路径
--build-arg list # 设置构建时变量
--no-cache # 构建时不使用缓存
--pull # 总是尝试拉取新版本的基础镜像
4.docker pull——拉取镜像
# 基本用法
docker pull ubuntu:20.04 # 拉取指定版本
docker pull nginx # 拉取latest标签
docker pull python:3.9-slim # 拉取精简版
# 常用选项:
docker pull --all-tags ubuntu # 拉取所有标签(慎用!)
docker pull --quiet ubuntu # 安静模式,减少输出
# 从特定仓库拉取
docker pull myregistry.local:5000/myimage:1.0
5.docker push——推送镜像
docker push username/myapp:1.0
# 推送到私有仓库
docker tag myapp:1.0 myregistry.local:5000/myapp:1.0
docker push myregistry.local:5000/myapp:1.0
6.docker rmi——删除镜像
# 删除单个镜像
docker rmi ubuntu:20.04
# 删除多个镜像
docker rmi image1 image2 image3
7.docker run——运行容器
# 基本语法
docker run [OPTIONS] IMAGE [COMMAND] [ARG...]
# 常用选项:
-d, --detach # 后台运行
-it # 交互模式(-i保持STDIN打开,-t分配伪终端)
--name string # 为容器指定名称
-p, --publish list # 端口映射(主机端口:容器端口)
-v, --volume list # 数据卷挂载
-e, --env list # 设置环境变量
--rm # 容器退出时自动删除
--restart string # 重启策略(no, on-failure, always, unless-stopped)
-m, --memory bytes # 内存限制
--cpus decimal # CPU限制
8.docker ps——查看容器
# 查看运行中的容器
docker ps
# 示例输出:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 nginx "/docker-entrypoint.…" 2 hours ago Up 2 hours 0.0.0.0:8080->80/tcp webserver
# 查看所有容器(包括已停止的)
docker ps -a
# 只显示容器ID
docker ps -q
# 查看最近创建的容器
docker ps -l
# 格式化输出
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}"
# 按条件过滤
docker ps -f status=running # 只显示运行中的
docker ps -f status=exited # 只显示已退出的
docker ps -f name=web # 按名称过滤
9.docker stop——停止容器
# 停止单个容器
docker stop mycontainer # 使用容器名
docker stop a1b2c3d4e5 # 使用容器ID(前几位即可)
# 停止多个容器
docker stop container1 container2
# 停止所有运行中的容器
docker stop $(docker ps -q)
# 设置停止超时时间(默认10秒)
docker stop -t 30 mycontainer # 30秒后强制停止
10.docker restart——重启容器
# 重启容器
docker restart mycontainer
# 重启所有运行中的容器
docker restart $(docker ps -q)
# 设置重启前的等待时间
docker restart -t 10 mycontainer # 等待10秒后重启
四、dockerfile基本语法
dockerfile 结构:指令 参数
核心指令指令包括:
1. FROM——指定基础镜像
FROM <image>[:<tag>] [AS <name>]
#示例:
FROM ubuntu:20.04 # 使用Ubuntu 20.04
2. RUN——执行命令
RUN <command>
# 示例
RUN apt-get update && apt-get install -y \
python3 \
python3-pip \
&& rm -rf /var/lib/apt/lists/*
RUN pip install --no-cache-dir flask
RUN ["/bin/bash", "-c", "echo Hello"]
3.COPY——复制文件
COPY [--chown=<user>:<group>] <src>... <dest>
# 示例
COPY app.py /app/ # 复制单个文件
COPY . /app/ # 复制当前目录所有文件
4. CMD——容器启动命令
# 语法
CMD ["executable","param1","param2"] # exec格式(推荐)
CMD command param1 param2 # shell格式
CMD ["param1","param2"] # 作为ENTRYPOINT参数
CMD ["python", "app.py"] # 直接运行Python脚本
CMD ["nginx", "-g", "daemon off;"] # Nginx前台运行
CMD python app.py # shell格式(不推荐)
5.WORKDIR——设置工作目录
# 语法
WORKDIR /path/to/workdir
# 示例 WORKDIR /app RUN pwd # 输出: /app
6.ENV——设置环境变量
# 语法
ENV <key>=<value> ...
# 示例
ENV PYTHON_VERSION=3.9.5
ENV APP_HOME=/app \
NODE_ENV=production \
DEBIAN_FRONTEND=noninteractive
7.EXPOSE——声明端口
# 语法
EXPOSE <port> [<port>/<protocol>...]
# 示例
EXPOSE 80 # 默认TCP
EXPOSE 80/tcp # 明确协议
EXPOSE 80/udp
EXPOSE 8080 3000 # 多个端口
#注意
只是声明并不会自动映射,实际映射需要在docker run -p时指定
五、Docker环境搭建与验证测试
1.环境安装
#1.安装Docker引擎
sudo apt-get update
sudo apt-get install docker.io
#2.验证安装正确性
sudo docker version
#3.配置国内镜像加速器(可以拉区别人的镜像,从而快速开发)
sudo tee /etc/docker/daemon.json << 'EOF'
{
"registry-mirrors": ["https://docker.m.daocloud.io"]
}
EOF
#4.重启docker
sudo systemctl restart docker
2.第一个Hello World测试
#1.创建最简单的Dockerfile
cat > Dockerfile << 'EOF'
FROM busybox:latest #基于远程镜像busybox环境进行开发
CMD ["echo", "Hello Docker!"] #终端输出"Hello Docker!"
EOF
#2.构建镜像
sudo docker build -t hello-test . #默认基于当前路径的Dockerfile进行构建
sudo docker run --rm hello-test #运行容器
运行效果:

这个“Hello Docker!”就是基于busybox这个环境输出的,把当前的Dockerfile进行打包发送到存在Docker环境的另一个主机,从而可以实现了Hello Docker这段内容一定可以正常进行输出。
现在用的是别人的docker环境,当然我们也可以自己搭建一个Docker环境,他的重点就是在于Dockerfile的书写,从而建立起不同的运行环境!


