Docker到底解决了什么问题
“在我电脑上跑得好好的啊”——这句话你一定听过。Docker就是来终结这个问题的。它把应用和所有依赖打包到一个容器里,在哪里跑都是一样的环境。
我现在所有的项目部署都用Docker,环境一致性问题基本不存在了。
安装Docker
Ubuntu 22.04
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # 安装依赖
$ sudo apt update
$ sudo apt install -y ca-certificates curl gnupg
# 添加Docker官方GPG key
$ sudo install -m 0755 -d /etc/apt/keyrings
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
$ sudo chmod a+r /etc/apt/keyrings/docker.gpg
# 添加Docker仓库
$ echo \
"deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
$(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
# 安装Docker
$ sudo apt update
$ sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
# 验证安装
$ docker --version
Docker version 24.0.7, build afdd53b
|
让普通用户使用Docker
1
2
3
4
5
6
7
8
| # 把当前用户加入docker组(避免每次都sudo)
$ sudo usermod -aG docker $USER
$ newgrp docker
# 测试
$ docker run hello-world
Hello from Docker!
This message shows that your installation appears to be working correctly.
|
核心概念
| 概念 | 类比 | 说明 |
|---|
| 镜像(Image) | 安装光盘 | 只读模板,包含运行环境和代码 |
| 容器(Container) | 运行中的系统 | 镜像的运行实例 |
| Dockerfile | 安装脚本 | 定义如何构建镜像 |
| Registry | 应用商店 | 存放镜像的仓库(Docker Hub) |
常用命令速查
镜像操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 拉取镜像
$ docker pull nginx:latest
latest: Pulling from library/nginx
a2abf6c4d29d: Pull complete
Status: Downloaded newer image for nginx:latest
# 查看本地镜像
$ docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx latest a6bd71f48f68 2 days ago 187MB
hello-world latest d2c94e258dcb 8 months ago 13.3kB
# 删除镜像
$ docker rmi hello-world
|
容器操作
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| # 运行一个Nginx容器
$ docker run -d --name mynginx -p 8080:80 nginx
f3c2e1d4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2
# 查看运行中的容器
$ docker ps
CONTAINER ID IMAGE COMMAND STATUS PORTS NAMES
f3c2e1d4a5b6 nginx "/docker-entrypoint.…" Up 5 seconds 0.0.0.0:8080->80/tcp mynginx
# 查看所有容器(含已停止的)
$ docker ps -a
# 停止容器
$ docker stop mynginx
# 启动已停止的容器
$ docker start mynginx
# 删除容器
$ docker rm mynginx
# 强制删除运行中的容器
$ docker rm -f mynginx
|
| 命令 | 作用 | 常用参数 |
|---|
docker run | 创建并运行容器 | -d后台, -p端口映射, --name命名 |
docker ps | 查看容器 | -a显示所有 |
docker stop | 停止容器 | 容器名或ID |
docker rm | 删除容器 | -f强制删除 |
docker logs | 查看日志 | -f实时跟踪 |
docker exec | 进入容器 | -it交互模式 |
进入容器内部
1
2
3
4
| $ docker exec -it mynginx /bin/bash
root@f3c2e1d4a5b6:/# ls /etc/nginx/
conf.d fastcgi_params mime.types modules nginx.conf scgi_params uwsgi_params
root@f3c2e1d4a5b6:/# exit
|
查看容器日志
1
2
3
4
5
6
7
| $ docker logs mynginx
/docker-entrypoint.sh: /docker-entrypoint.d/ is not empty, will attempt to perform configuration
...
2026/01/10 10:00:00 [notice] 1#1: start worker processes
# 实时跟踪日志
$ docker logs -f mynginx
|
编写Dockerfile
来写一个简单的Node.js应用的Dockerfile:
1
| $ mkdir ~/myapp && cd ~/myapp
|
创建 app.js:
1
2
3
4
5
6
| const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('Hello from Docker!\n');
});
server.listen(3000, () => console.log('Server running on port 3000'));
|
创建 Dockerfile:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 基础镜像
FROM node:18-alpine
# 设置工作目录
WORKDIR /app
# 复制文件
COPY app.js .
# 暴露端口
EXPOSE 3000
# 启动命令
CMD ["node", "app.js"]
|
构建和运行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 构建镜像
$ docker build -t myapp:v1 .
[+] Building 12.5s (8/8) FINISHED
=> [internal] load build definition from Dockerfile
=> [1/3] FROM node:18-alpine
=> [2/3] WORKDIR /app
=> [3/3] COPY app.js .
=> exporting to image
=> => naming to docker.io/library/myapp:v1
# 运行容器
$ docker run -d --name myapp -p 3000:3000 myapp:v1
# 测试
$ curl http://localhost:3000
Hello from Docker!
|
数据持久化
容器删除后数据就没了,用Volume解决:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
| # 创建volume
$ docker volume create mydata
# 挂载volume运行容器
$ docker run -d --name mynginx \
-p 8080:80 \
-v mydata:/usr/share/nginx/html \
nginx
# 或者挂载本地目录
$ docker run -d --name mynginx \
-p 8080:80 \
-v /var/www/mysite:/usr/share/nginx/html:ro \
nginx
|
清理空间
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
| # 查看Docker占用的空间
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 5 2 1.23GB 856MB (69%)
Containers 3 1 12.5MB 12.5MB (100%)
Local Volumes 2 1 234MB 120MB (51%)
# 清理所有无用资源
$ docker system prune -a
WARNING! This will remove:
- all stopped containers
- all networks not used by at least one container
- all images without at least one container associated to them
- all build cache
Are you sure you want to continue? [y/N] y
Total reclaimed space: 1.1GB
|
常见问题
端口冲突
1
2
3
4
5
| $ docker run -d -p 80:80 nginx
Error: driver failed: Bind for 0.0.0.0:80 failed: port is already allocated
# 换个端口或者停掉占用80端口的服务
$ docker run -d -p 8080:80 nginx
|
容器自动重启
1
2
3
4
| # 加上 --restart 参数
$ docker run -d --restart=always --name mynginx -p 80:80 nginx
# 服务器重启后容器也会自动启动
|
下一步
学会了Docker基础操作后,下一步建议学习Docker Compose,用一个YAML文件定义多个容器,一键启动整套服务。如果想深入了解镜像的构建和优化,可以看Docker镜像管理。