> Docker入门教程:容器化部署你的第一个应用

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镜像管理

Docker 容器化 部署
cd ..