Skip to content

Docker 容器

虚拟机与容器对比

虚拟机容器
优点- 使用简单 - 有成熟管理工具,如 VMware - 可以随意定制- 快速部署 - 大部分环境都有现成镜像 - 让我们不再关注系统基础设施,把关注点放在配置、升级、优化 - 不依赖硬件 - 相当于一个进程
缺点- 需要硬件支持虚拟化技术 - 资源利用率不高 - 同一台虚拟机跑多个服务,可能有冲突 - 占用资源较多 - 不满足目前升级、快速扩容、快速部署,回滚不方便- 使用较为复杂 - 共享 Linux 系统内核,推荐使用较新 Linux 内核

本地安装 Docker 并运行 Nginx 测试

环境准备

bash
[root@docker01.oldboylinux.cn ~]# rz -E
rz waiting to receive.
[root@docker01.oldboylinux.cn ~]# ll
总用量 72104
-rw------- 1 root root     3208  2月 20 11:39 anaconda-ks.cfg
-rw-r--r-- 1 root root 73823487  3月 18 12:11 docker-27.0.3.tgz
-rw-r--r-- 1 root root     3779  2月 20 14:56 initial-setup-ks.cfg
[root@docker01.oldboylinux.cn ~]# ll docker-27.0.3.tgz 
-rw-r--r-- 1 root root 73823487  3月 18 12:11 docker-27.0.3.tgz
[root@docker01.oldboylinux.cn ~]# tar -xzvf docker-27.0.3.tgz
docker/
docker/docker-init
docker/runc
docker/containerd-shim-runc-v2
docker/ctr
docker/docker
docker/dockerd
docker/containerd
docker/docker-proxy
[root@docker01.oldboylinux.cn ~]# ll
总用量 72104
-rw------- 1 root   root       3208  2月 20 11:39 anaconda-ks.cfg
drwxrwxr-x 2 oldboy oldboy      146  6月 29  2024 docker
-rw-r--r-- 1 root   root   73823487  3月 18 12:11 docker-27.0.3.tgz
-rw-r--r-- 1 root   root       3779  2月 20 14:56 initial-setup-ks.cfg
[root@docker01.oldboylinux.cn ~]# chown  root.root docker/*
[root@docker01.oldboylinux.cn ~]# ll docker
总用量 194132
-rwxr-xr-x 1 root root 39161856  6月 29  2024 containerd
-rwxr-xr-x 1 root root 12496896  6月 29  2024 containerd-shim-runc-v2
-rwxr-xr-x 1 root root 19361792  6月 29  2024 ctr
-rwxr-xr-x 1 root root 38498840  6月 29  2024 docker
-rwxr-xr-x 1 root root 71298872  6月 29  2024 dockerd
-rwxr-xr-x 1 root root   708448  6月 29  2024 docker-init
-rwxr-xr-x 1 root root  1979949  6月 29  2024 docker-proxy
-rwxr-xr-x 1 root root 15275560  6月 29  2024 runc
[root@docker01.oldboylinux.cn ~]# mv docker/* /bin/
[root@docker01.oldboylinux.cn ~]# rz -E
rz waiting to receive.
[root@docker01.oldboylinux.cn ~]# mv docker.service  /usr/lib/systemd/system
[root@docker01.oldboylinux.cn ~]# systemctl daemon-reload
[root@docker01.oldboylinux.cn ~]# systemctl start docker
[root@docker01.oldboylinux.cn ~]# systemctl enable docker
Created symlink /etc/systemd/system/multi-user.target.wants/docker.service /usr/lib/systemd/system/docker.service.
[root@docker01.oldboylinux.cn ~]# systemctl status docker
 docker.service - Docker Application Container Engine
   Loaded: loaded (/usr/lib/systemd/system/docker.service; enabled; vendor preset: disabled)
   Active: active (running) since Tue 2025-03-18 12:17:27 CST; 11s ago
     Docs: https://docs.docker.com
 Main PID: 1371 (dockerd)
    Tasks: 17
   Memory: 36.1M
   CGroup: /system.slice/docker.service
           ├─1371 /usr/bin/dockerd
           └─1378 containerd --config /var/run/docker/containerd/containerd.toml

导入常用镜像 Nginx 并运行

1. 解压镜像文件

bash
tar -xzvf oldboyedu_useful_docker_images.tar.gz

解压后,你应该会得到一个目录 oldboyedu_useful_docker_images,其中存放多个 .tar 结尾的 Docker 镜像文件。

2. 查看本地已有的 Docker 镜像

bash
docker images

这个命令会列出你当前系统中的所有 Docker 镜像。

3. 进入解压后的目录

bash
cd oldboyedu_useful_docker_images

4. 导入所有 .tar 结尾的镜像文件

bash
for n in `ls *.tar`
do
    docker load -i $n
done

解释:

  • ls *.tar 获取当前目录下所有 .tar 结尾的 Docker 镜像文件。
  • for 循环遍历每个 .tar 文件,并使用 docker load -i 逐个导入到 Docker 中。

5. 确认镜像导入成功

bash
[root@docker01.oldboylinux.cn ~/oldboyedu_useful_docker_images]# docker images
REPOSITORY   TAG           IMAGE ID       CREATED         SIZE
tomcat       9.0-jdk8      56d6933de864   20 months ago   285MB
nginx        1.24          b6c621311b44   20 months ago   142MB
mysql        5.7-debian    b5d7c63fe339   20 months ago   463MB
mysql        8.0-debian    5557a1823e30   20 months ago   602MB
debian       buster        1de12428f6f4   20 months ago   114MB
debian       bullseye      189a2f977ff1   20 months ago   124MB
debian       bookworm      3676c78a12ad   20 months ago   116MB
ubuntu       20.04         14be0685b768   20 months ago   72.8MB
ubuntu       22.04         5a81c4b8502e   20 months ago   77.8MB
nginx        1.24-alpine   55ba84d7d539   21 months ago   41.1MB
redis        6.0-alpine    26c28490dc59   21 months ago   26.6MB
alpine       latest        c1aabb73d233   21 months ago   7.33MB
php          7-fpm         38f2b691dcb8   2 years ago     443MB
redis        5.0-alpine    7558bc54e8a2   2 years ago     22.9MB
centos       7             eeb6ee3f44bd   3 years ago     204MB
redis        4.0-alpine    e3dd0e49bca5   4 years ago     20.4MB
java         8u111-jdk     d23bdf5b1b1b   8 years ago     643MB

6. 删除已麒麟自带的 Docker 环境

bash
rm -f /usr/local/bin/runc

7. 开启内核转发

bash
root@docker01.oldboylinux.cn ~/oldboyedu_useful_docker_images]# sysctl -p
kernel.sysrq = 0
net.ipv4.ip_forward = 1
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.secure_redirects = 0
net.ipv4.conf.default.secure_redirects = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_syncookies = 1
kernel.dmesg_restrict = 1
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0

8. 运行

bash
docker run -d  --name ngx_test -p 80:80 nginx:1.24

浏览器访问宿主机 ip:80 ---->容器中 80

bash
docker run -d --name ngx_test2 -p 81:80 nginx:1.24

浏览器访问宿主机 ip:81---->容器中 80

解释:

  • docker run 启动一个新的 Docker 容器。
  • -d 后台运行(Detached mode),容器启动后不会占据终端,会在后台运行。如果不加 -d,那么 Nginx 容器会在前台运行,显示 Nginx 访问日志等信息。
  • --name ngx_test2 自定义容器名称,这里把容器命名为 ngx_test2。如果不指定 --name,Docker 会自动分配一个随机名称。
  • -p 81:80 端口映射,将宿主机的 81 端口映射到容器的 80 端口,格式为:-p 宿主机端口:容器端口。宿主机访问 http://localhost:81,等同于访问容器的 http://localhost:80。nginx 服务器默认监听 80 端口,因此把宿主机的 81 端口映射过去。
  • nginx:1.24 指定使用 nginx:1.24 版本的镜像。如果本地没有这个镜像,Docker 会自动从 Docker Hub 拉取。

9. 检查是否运行

bash
docker ps -a  # 查看所有
docker ps     # 查看运行

Docker 镜像指令

镜像

查看镜像

bash
docker images docker image ls

下载镜像

bash
docker pull nginx:latest  # 避免使用latest标签,建议使用具体版本号

删除镜像

bash
docker image rm docker rmi  # 镜像在使用中时,需先删除容器

导出镜像

bash
docker save nginx:1.24 -o nginx.tar docker save nginx:1.24 | gzip > nginx.tar.gz

导入镜像

bash
docker load -i xxx.tar docker load -i xxx.tar.gz

load 导入示例:

bash
[root@docker01.oldboylinux.cn ~]# docker save nginx:1.24 -o nginx.tar
 
[root@docker01.oldboylinux.cn ~]# ll
-rw------- 1 root   root    146628096  3月 19 10:08 nginx.tar
 
[root@docker01.oldboylinux.cn ~]# scp nginx.tar 10.0.0.82:/root/
 
[root@docker02.oldboylinux.cn ~]# docker images
REPOSITORY   TAG       IMAGE ID   CREATED   SIZE
[root@docker02.oldboylinux.cn ~]# docker load -i nginx.tar 
4b3ba104e9a8: Loading layer  84.03MB/84.03MB
6310117db5a7: Loading layer  62.55MB/62.55MB
e543857b2aef: Loading layer  3.584kB/3.584kB
629fd7b81c65: Loading layer  4.608kB/4.608kB
0b27f1638f81: Loading layer  3.584kB/3.584kB
f62590d48fe5: Loading layer  7.168kB/7.168kB
Loaded image: nginx:1.24
[root@docker02.oldboylinux.cn ~]# docker images
REPOSITORY   TAG       IMAGE ID       CREATED         SIZE
nginx        1.24      b6c621311b44   20 months ago   142MB

镜像标签

bash
docker tag 原有的名字 新名字 docker tag 镜像id 新名字

查看镜像详细信息

bash
docker inspect 镜像
docker inspect nginx:1.24
docker inspect mysql:8.0-debian
bash
[root@docker01.oldboylinux.cn ~]# yum -y install jq
 
[root@docker01.oldboylinux.cn ~]# docker inspect nginx:1.24
 
[root@docker01.oldboylinux.cn ~]# docker inspect mysql:8.0-debian | jq .[].RepoTags
[
  "mysql:8.0-debian"
]
[root@docker01.oldboylinux.cn ~]# docker inspect mysql:8.0-debian | jq .[].RepoTags|jq .[]
"mysql:8.0-debian"

镜像命令总结集合

镜像操作命令选项例子
docker images-a 查看所有镜像(包括中间层镜像)docker images -a
docker pull<镜像名>:<标签>
指定镜像名和标签
docker pull ubuntu:latest
docker rmi<镜像ID或镜像名>
指定要删除的镜像
docker rmi abcdef123456
docker rmi ubuntu:latest
docker inspect<资源ID或资源名>
指定要查看的资源
docker inspect abcdef123456
(查看容器信息,替换为容器ID)或 docker inspect ubuntu:latest
(查看镜像信息)
docker save-o <导出文件名>
指定导出文件的名称
docker save -o ubuntu_latest.tar ubuntu:latest
docker load<导入文件名>
指定要导入的文件
docker load < ubuntu_latest.tar
tag通常作为 docker
命令的子命令,如 docker tag
docker tag ubuntu:latest myubuntu:latest
(给镜像打新标签)
build-t <镜像名>:<标签>
指定构建后的镜像名和标签
docker build -t myapp:latest .
(在当前目录下构建镜像)
history<镜像名或镜像ID>
查看镜像的构建历史

Docker 容器指令

获取帮助

bash
docker container help

运行容器

bash
docker run

导出容器

docker export 用于将正在运行或已停止的容器导出为一个 tar 压缩文件,以便备份或迁移。

bash
docker export -o 文件名.tar 容器ID或容器名称

导入为镜像

docker import 用于将一个 tar 文件(通常是 docker export 生成的文件)导入为一个新的 Docker 镜像。

bash
docker import 文件名.tar 镜像名:标签

查看容器详细信息

bash
docker inspect 容器

示例:

bash
docker inspect ngx_test
[root@docker01.oldboylinux.cn ~]# docker inspect ngx_test | jq '.[0].Config.Env'
[
  "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
  "NGINX_VERSION=1.24.0",
  "NJS_VERSION=0.7.12",
  "PKG_RELEASE=1~bullseye"
]

进入已经运行的容器

bash
docker exec -it 容器名字 /bin/bash

Docker 启动 Nginx

bash
[root@docker01.oldboylinux.cn ~]# docker ps -a
CONTAINER ID   IMAGE               COMMAND                   CREATED         STATUS                      PORTS                               NAMES
fa112253fb8e   nginx:1.24-alpine   "/docker-entrypoint.…"   4 minutes ago   Up 4 minutes                0.0.0.0:81->80/tcp, :::81->80/tcp   ngx_alpine
15be80e3072a   nginx:1.24          "/docker-entrypoint.…"   21 hours ago    Exited (255) 13 hours ago   0.0.0.0:80->80/tcp, :::80->80/tcp   ngx_test
[root@docker01.oldboylinux.cn ~]# docker start 15be80e3072a
15be80e3072a
[root@docker01.oldboylinux.cn ~]# docker exec -it ngx_test  /bin/bash
# echo "Test nginx">/usr/share/nginx/html/index.html
 
[root@docker01.oldboylinux.cn ~]# docker exec -it ngx_alpine  /bin/sh
/ # echo "NGINX-alpine" > /usr/share/nginx/html/index.html

Docker 启动 MySQL 查看日志解决问题

bash
[root@docker01.oldboylinux.cn ~]# docker run -d --name mysql_test mysql:8.0-debian
feae8dd0c85e469c9a525fef47b2c263a2f5eddc434811325782efc191370d9d
 
[root@docker01.oldboylinux.cn ~]# docker exec -it mysql_test /bin/bash/
Error response from daemon: container feae8dd0c85e469c9a525fef47b2c263a2f5eddc434811325782efc191370d9d is not running
 
# 数据库未运行
[root@docker01.oldboylinux.cn ~]# docker ps -a 
CONTAINER ID   IMAGE               COMMAND                   CREATED          STATUS                      PORTS                               NAMES
feae8dd0c85e   mysql:8.0-debian    "docker-entrypoint.s…"   49 seconds ago   Exited (1) 43 seconds ago                                       mysql_test
fa112253fb8e   nginx:1.24-alpine   "/docker-entrypoint.…"   34 minutes ago   Up 34 minutes               0.0.0.0:81->80/tcp, :::81->80/tcp   ngx_alpine
15be80e3072a   nginx:1.24          "/docker-entrypoint.…"   21 hours ago     Up 29 minutes               0.0.0.0:80->80/tcp, :::80->80/tcp   ngx_test
 
[root@docker01.oldboylinux.cn ~]# docker logs  mysql_test
2025-03-19 04:03:07+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.33-1debian11 started.
2025-03-19 04:03:10+00:00 [Note] [Entrypoint]: Switching to dedicated user 'mysql'
2025-03-19 04:03:10+00:00 [Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.33-1debian11 started.
2025-03-19 04:03:10+00:00 [ERROR] [Entrypoint]: Database is uninitialized and password option is not specified
    You need to specify one of the following as an environment variable:
    - MYSQL_ROOT_PASSWORD
    - MYSQL_ALLOW_EMPTY_PASSWORD
    - MYSQL_RANDOM_ROOT_PASSWORD
# 缺少环境变量
 
[root@docker01.oldboylinux.cn ~]# docker rm mysql_test
 
# -e MYSQL_ROOT_PASSWORD=1:通过 -e 参数设置容器环境变量。
# 这个例子中,MYSQL_ROOT_PASSWORD=1 设置了 MySQL 容器的 root 用户密码为 1。
 
[root@docker01.oldboylinux.cn ~]# docker run -d --name mysql_test -e MYSQL_ROOT_PASSWORD=1 mysql:8.0-debian
b8c6070141c8e7ce34fb0b27247eb673247e7483a665d1be9b2c83200576bc6d
 
# 进入数据库 成功
[root@docker01.oldboylinux.cn ~]# docker exec -it mysql_test  mysql -uroot -p1
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 8.0.33 MySQL Community Server - GPL

Docker 启动系统容器

bash
docker run -itd --name debian_test2 debian:bullseye /bin/bash
  • run 命令用于创建并运行一个新的容器
  • -itd(三个选项的组合):
    • -i(interactive):保持标准输入(STDIN)打开,即使容器没有附加到终端,这样可以确保容器不会立即退出。
    • -t(tty):分配一个伪终端(TTY),让你可以使用 bash 或其他交互式命令。
    • -d(detach):让容器在后台运行,不会占用你的终端。

📌 -itd 的作用:

  • -it 使得容器能够进行交互,适用于 bash 这样的 Shell 终端。
  • -d 让容器在后台运行,否则 -it 会让你直接进入容器。

示例:

bash
[root@docker01.oldboylinux.cn ~]# docker run -itd --name ngx_test05 nginx:1.24 /bin/bash
e8fcbeb35716c78cd3f81b0fc085c412bc2950ba12a830eddad3d77eaf470cb2
 
[root@docker01.oldboylinux.cn ~]# docker exec -itd  ngx_test05 /bin/bash
 
[root@docker01.oldboylinux.cn ~]# docker exec -it  ngx_test05 /bin/bash
# nginx 检查
root@e8fcbeb35716:/# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
 
# 手动启动Nginx
root@e8fcbeb35716:/# nginx
2025/03/19 06:54:54 [notice] 20#20: using the "epoll" event method
2025/03/19 06:54:54 [notice] 20#20: nginx/1.24.0
2025/03/19 06:54:54 [notice] 20#20: built by gcc 10.2.1 20210110 (Debian 10.2.1-6) 
2025/03/19 06:54:54 [notice] 20#20: OS: Linux 4.19.90-52.22.v2207.ky10.x86_64
2025/03/19 06:54:54 [notice] 20#20: getrlimit(RLIMIT_NOFILE): 1048576:1048576
root@e8fcbeb35716:/# 2025/03/19 06:54:54 [notice] 21#21: start worker processes
2025/03/19 06:54:54 [notice] 21#21: start worker process 22
2025/03/19 06:54:54 [notice] 21#21: start worker process 23

阻塞问题

GPT 解释:
在 Docker 中,容器的生命周期是由容器内的主进程(PID 1)的生命周期决定的。简单来说,当你启动一个容器时,Docker 会运行你指定的主进程;如果这个进程退出,容器就会自动停止运行。

详细解释:

  • 主进程与容器生命周期:Docker 容器内部只有一个主进程(通常是你在 Dockerfile 中通过 CMD 或 ENTRYPOINT 指定的命令),Docker 会监控这个进程的状态。如果主进程结束,Docker 认为容器的任务已经完成,因此容器就会退出。
  • 阻塞进程的重要性:为了让容器保持运行,你需要确保主进程能够持续运行并“阻塞”住容器。例如,运行一个 Web 服务器(如 nginx 或 Apache)时,服务器会持续监听请求,这个过程不会退出,从而使容器一直处于运行状态。如果你的进程在启动后很快结束(例如执行一个短暂的脚本),容器就会随之退出。

常见做法:

  • 前台运行服务:确保服务以非守护方式在前台运行。例如,很多应用程序都提供选项让它们在前台运行,这样 Docker 就能正确地管理它们的生命周期。
  • 保持进程阻塞:在某些场景下,可以使用诸如 tail -f /dev/null 的方式保持容器前台阻塞,防止容器立即退出(不过这种方法一般只适用于调试或临时用途)。

总结:
Docker 启动时会等待容器内的主进程一直运行,这就是所谓的“启动阻塞”。只要这个进程不退出,容器就会保持运行状态;一旦该进程结束,容器也就随之终止。理解这一点对于编写 Dockerfile 和设计容器化应用非常关键,因为这直接影响到容器的稳定性和可用性。

查看容器状态指令

Docker stats

bash
[root@docker01.oldboylinux.cn ~]# docker stats
CONTAINER ID   NAME           CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O         PIDS
e8fcbeb35716   ngx_test05     0.00%     3.023MiB / 3.816GiB   0.08%     1.08kB / 0B       4.1kB / 0B        5
011e1005b6d7   debian_test2   0.00%     492KiB / 3.816GiB     0.01%     1.08kB / 0B       1.19MB / 0B       1
b8c6070141c8   mysql_test     0.51%     404.4MiB / 3.816GiB   10.35%    1.29kB / 0B       43.2MB / 265MB    38
fa112253fb8e   ngx_alpine     0.00%     2.164MiB / 3.816GiB   0.06%     7.46kB / 4.29kB   926kB / 7.17kB    3
15be80e3072a   ngx_test       0.00%     10.47MiB / 3.816GiB   0.27%     29.5kB / 11.5kB   13.6MB / 16.4kB   3

动态修改正在运行的容器的资源限制

动态修改正在运行的容器的资源限制,比如 CPU、内存、重启策略等,而无需重启容器。

bash
[root@docker01.oldboylinux.cn ~]# docker update -m 1024m --memory-swap 1024m mysql_test
CONTAINER ID   NAME           CPU %     MEM USAGE / LIMIT     MEM %     NET I/O           BLOCK I/O         PIDS
e8fcbeb35716   ngx_test05     0.00%     3.023MiB / 3.816GiB   0.08%     1.08kB / 0B       4.1kB / 0B        5
011e1005b6d7   debian_test2   0.00%     492KiB / 3.816GiB     0.01%     1.08kB / 0B       1.19MB / 0B       1
b8c6070141c8   mysql_test     0.48%     404.4MiB / 1GiB       39.49%    1.29kB / 0B       43.2MB / 265MB    38

修改容器重启策略

bash
docker update --restart=always test22

test22 容器始终自动重启。

可用策略:

  • no:不自动重启
  • always:总是重启
  • unless-stopped:除非手动停止,否则会自动重启
  • on-failure:仅在非 0 退出码时才重启

示例:

bash
[root@docker01.oldboylinux.cn ~]# docker ps -a | awk 'NR>1{print $NF}'| xargs docker update --restart always
ngx_test05
debian_test2
mysql_test
ngx_alpine
ngx_test

Docker top 查看指定容器,相当于 Linux ps 命令

  • -L 选项表示显示线程信息,即一个进程中的所有线程都会被列出。
bash
[root@docker01.oldboylinux.cn ~]# docker top ngx_test -efL
UID                 PID                 PPID                LWP                 C                   NLWP                STIME               TTY                 TIME                CMD
root                3131                3110                3131                0                   1                   11:34               ?                   00:00:00            nginx: master process nginx -g daemon off;
101                 3164                3131                3164                0                   1                   11:34               ?                   00:00:00            nginx: worker process
101                 3165                3131                3165                0                   1                   11:34               ?                   00:00:00            nginx: worker process
 
[root@docker01.oldboylinux.cn ~]# docker top ngx_test -aux
USER                PID                 %CPU                %MEM                VSZ                 RSS                 TTY                 STAT                START               TIME                COMMAND
root                3131                0.0                 0.1                 8932                5428                ?                   Ss                  11:34               0:00                nginx: master process nginx -g daemon off;
101                 3164                0.0                 0.0                 9352                2612                ?                   S                   11:34               0:00                nginx: worker process
101                 3165                0.0                 0.0                 9352                2612                ?                   S                   11:34               0:00                nginx: worker process
 
[root@docker01.oldboylinux.cn ~]# docker top ngx_test -ef
UID                 PID                 PPID                C                   STIME               TTY                 TIME                CMD
root                3131                3110                0                   11:34               ?                   00:00:00            nginx: master process nginx -g daemon off;
101                 3164                3131                0                   11:34               ?                   00:00:00            nginx: worker process
101                 3165                3131                0                   11:34               ?                   00:00:00            nginx: worker process

创建自定义镜像 Commit

bash
[root@docker01.oldboylinux.cn ~]# docker ps -aq
e8fcbeb35716
011e1005b6d7
b8c6070141c8
fa112253fb8e
15be80e3072a
 
[root@docker01.oldboylinux.cn ~]# docker ps -aq|xargs docker rm -f
e8fcbeb35716
011e1005b6d7
b8c6070141c8
fa112253fb8e
15be80e3072a
 
[root@docker01.oldboylinux.cn ~]# docker run -d --name ngx_test -p 8080:80 nginx:1.24
6211918fca3801462a7b9a72976f4af68f78b1c07db636b7769cabf21d3d428d
 
# 两个窗口操作
[root@docker01.oldboylinux.cn ~]# docker exec -it ngx_test /bin/bash
root@6211918fca38:/# 
 
# cp宿主机与容器传输数据
[root@docker01.oldboylinux.cn ~]# docker cp bird.tar.gz  ngx_test:/tmp/
Successfully copied 93.2kB to ngx_test:/tmp/
 
# 建议Tar有的时候zip没有命令
root@6211918fca38:/# tar -xf /tmp/bird.tar.gz  -C /usr/share/nginx/html/
 
root@6211918fca38:/# egrep -v '^$|#' /etc/nginx/conf.d/default.conf 
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;
    location / {
        root   /usr/share/nginx/html;
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
}
# 在宿主机修改defaut
server {
    listen       80;
    listen  [::]:80;
    server_name  localhost;
    root   /usr/share/nginx/html;
    
    location / {
        index  index.html index.htm;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   /usr/share/nginx/html;
    }
    location ~* \.(html|css|js|png)$ {
        expires 7d;
    }
}
 
# 宿主机传过去
[root@docker01.oldboylinux.cn ~]# docker cp default.conf  ngx_test:/etc/nginx/conf.d/
Successfully copied 2.05kB to ngx_test:/etc/nginx/conf.d/
# 容器检查语法
nginx -t
# 重启容器服务
[root@docker01.oldboylinux.cn ~]# docker restart ngx_test
ngx_test
 
[root@docker01.oldboylinux.cn ~]# docker commit ngx_test web:version01
sha256:4511318f044545225cdeba46bc927c4d6bb49f4990cfef4a956b8123c77859a7
 
[root@docker01.oldboylinux.cn ~]# docker  images
REPOSITORY   TAG           IMAGE ID       CREATED              SIZE
web          version01     4511318f0445   12 seconds ago       142MB
 
[root@docker01.oldboylinux.cn ~]# docker run -d --name final_bird -p 80:80 web:version01
0d2db50339e2d60f24085d6f920159e54f8df6e8577d5537594d968f85b3c7e3
 
[root@docker01.oldboylinux.cn ~]# docker ps
CONTAINER ID   IMAGE           COMMAND                   CREATED          STATUS         PORTS                                   NAMES
0d2db50339e2   web:version01   "/docker-entrypoint.…"   2 seconds ago    Up 2 seconds   0.0.0.0:80->80/tcp, :::80->80/tcp       final_bird
 
# 测试
curl 10.0.0.81

容器端口映射

docker run -p 背后发生什么?

当你执行类似 docker run -p 8080:80 的命令时,Docker 会在后台完成以下几步:

1. 容器网络隔离与网络命名空间

Docker 启动容器时,会为它创建独立的网络命名空间,这意味着容器拥有独立的 IP 地址、端口空间以及自己的网络堆栈。

2. 虚拟网络接口对接

Docker 为容器创建一对虚拟以太网设备(veth pair),一端进入容器的网络命名空间(容器内的网络接口),另一端连接到宿主机上的默认虚拟桥接网络(通常是 docker0)。

3. 端口映射的实现(iptables 规则配置)

当使用 -p 参数时,Docker 会在宿主机上选择一个端口(如8080),并建立 iptables 规则:

  • DNAT(目的地址转换):在 PREROUTING 链中设置规则,把发往宿主机 8080 端口的流量转发(DNAT)到容器内对应的 IP 地址及端口(例如 172.17.0.X:80)。
  • SNAT/MASQUERADE:对于容器对外发出的数据包,Docker 在 POSTROUTING 链中使用 MASQUERADE 规则,把容器内的源地址转换成宿主机的 IP 地址,确保返回数据能正确路由回容器。

4. 透明转发

这样,当外部访问宿主机的 8080 端口时,iptables 会将流量重定向到容器的 80 端口,整个过程对用户是透明的。

iptables 端口映射与共享上网流程原理

iptables 在实现 NAT 与端口映射时,主要分为以下两大部分:

端口映射(DNAT)

  • 工作链:PREROUTING

当外部数据包到达宿主机时,iptables 的 PREROUTING 链会首先检查是否有 DNAT 规则匹配。例如,一条规则会把目的端口为 8080 的数据包重定向到容器内某个 IP 的 80 端口。

过程概览:

  1. 外部客户端发起请求,目标地址为宿主机的公网 IP,端口为 8080。
  2. PREROUTING 规则匹配到该数据包后,将目标地址改为容器 IP,目标端口改为 80。
  3. 数据包随后通过正常路由,进入容器的网络命名空间,交由容器内的服务处理。

共享上网(SNAT/MASQUERADE)

  • 工作链:POSTROUTING

当容器发出数据包到外部网络时,数据包原本的源地址是容器的私有 IP,这个地址在公网中不可达。为了解决这个问题,iptables 在 POSTROUTING 链中应用 SNAT 或 MASQUERADE 规则:

过程概览:

  1. 容器发起外部请求,源地址为容器 IP。
  2. POSTROUTING 规则将数据包的源地址替换为宿主机的公网 IP(通常通过 MASQUERADE 动态实现)。
  3. 数据包离开宿主机时使用转换后的源地址,从而使响应数据包能正确返回。

流程总结

  • 入站流量:
    • 外部请求 → 经过 PREROUTING 的 DNAT 规则(端口映射) → 路由到容器内服务。
  • 出站流量:
    • 容器请求 → 经过 POSTROUTING 的 MASQUERADE 规则(SNAT) → 数据包的源地址改为宿主机 IP → 发送到互联网。

这种机制不仅实现了外部访问容器(端口映射),同时也使得容器内的应用能够共享上网,正常访问外部互联网。

数据卷

数据卷挂载 -v

Docker 数据卷(Volume)是用于持久化数据的一种机制,可以在容器销毁后仍然保留数据。主要用于存储数据库数据、日志文件、配置文件等。容器本质上是临时的,如果容器被删除,里面的数据也会丢失。

为了解决这个问题,可以使用数据卷(Volume)或者绑定挂载(Bind Mounts)来让数据持久化存储。

-v 实现宿主机和容器的映射

挂载数据库的目录:

bash

[root@docker02.oldboylinux.cn /test]# docker run -d -p 3306:3306 --name mysql --restart always -e MYSQL_ROOT_PASSWORD=1 mysql:8.0-debian
34c7978aae6ae4386a814b67f601ccd079b54c2b67f493c8353f98fa0f3c9bad

查看进程

bash
[root@docker02.oldboylinux.cn /test]# docker ps -a
CONTAINER ID   IMAGE              COMMAND                   CREATED          STATUS          PORTS                                                    NAMES
34c7978aae6a   mysql:8.0-debian   "docker-entrypoint.s…"   39 seconds ago   Up 38 seconds   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp     mysql
21594ed41fd0   test01             "/docker-entrypoint.…"   2 hours ago      Up 2 hours      0.0.0.0:80-81->80-81/tcp, :::80-81->80-81/tcp, 443/tcp   test01

查看日志是否成功

bash
[root@docker02.oldboylinux.cn /test]# docker logs mysql
2025-03-20T09:25:44.477023Z 0 [System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.33'  socket: '/var/run/mysqld/mysqld.sock'  port: 3306  MySQL Community Server - GPL.
2025-03-20T09:25:44.477026Z 0 [System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock

创建数据库测试

bash
[root@docker02.oldboylinux.cn /test]# docker exec -it mysql mysql -uroot -p1
mysql> create database test;
Query OK, 1 row affected (0.00 sec)
 
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| sys                |
| test               |
+--------------------+
5 rows in set (0.05 sec)

删除容器

bash
[root@docker02.oldboylinux.cn /test]# docker rm -f mysql
mysql

重新运行容器并挂载数据卷

bash
[root@docker02.oldboylinux.cn /test]# docker run -d -p 3306:3306 --name db --restart always -e MYSQL_ROOT_PASSWORD=1 -v /app/mysql/data/:/var/lib/mysql/ mysql:8.0-debian
996a1a01e75c43ae3140c2a86cef56644e1185366f42b9f65fa654392918deb0

查看挂载目录

bash
[root@docker02.oldboylinux.cn /app/mysql/data]# ll
总用量 99636
-rw-r----- 1 systemd-coredump input       56  3月 20 19:28  auto.cnf
-rw-r----- 1 systemd-coredump input  2994206  3月 20 19:28  binlog.000001
-rw-r----- 1 systemd-coredump input      157  3月 20 19:28  binlog.000002
-rw-r----- 1 systemd-coredump input       32  3月 20 19:28  binlog.index
-rw------- 1 systemd-coredump input     1680  3月 20 19:28  ca-key.pem
-rw-r--r-- 1 systemd-coredump input     1112  3月 20 19:28  ca.pem
-rw-r--r-- 1 systemd-coredump input     1112  3月 20 19:28  client-cert.pem
-rw------- 1 systemd-coredump input     1676  3月 20 19:28  client-key.pem
-rw-r----- 1 systemd-coredump input   196608  3月 20 19:28  '#ib_16384_0.dblwr'
-rw-r----- 1 systemd-coredump input  8585216  3月 20 19:28  '#ib_16384_1.dblwr'
-rw-r----- 1 systemd-coredump input     5657  3月 20 19:28  ib_buffer_pool
-rw-r----- 1 systemd-coredump input 12582912  3月 20 19:28  ibdata1
-rw-r----- 1 systemd-coredump input 12582912  3月 20 19:28  ibtmp1
drwxr-x--- 2 systemd-coredump input     4096  3月 20 19:28  '#innodb_redo'
drwxr-x--- 2 systemd-coredump input      187  3月 20 19:28  '#innodb_temp'
drwxr-x--- 2 systemd-coredump input      143  3月 20 19:28  mysql
-rw-r----- 1 systemd-coredump input 31457280  3月 20 19:28  mysql.ibd
drwxr-x--- 2 systemd-coredump input     8192  3月 20 19:28  performance_schema
-rw------- 1 systemd-coredump input     1676  3月 20 19:28  private_key.pem

创建数据库并验证持久化

bash
[root@docker02.oldboylinux.cn /app/mysql/data]# docker exec -it db  mysql -uroot -p1 -e 'create database lidao;'
mysql: [Warning] Using a password on the command line interface can be insecure.
 
# 目录多了个lidao
[root@docker02.oldboylinux.cn /app/mysql/data]# ll
总用量 99636
-rw-r----- 1 systemd-coredump input       56  3月 20 19:28  auto.cnf
-rw-r----- 1 systemd-coredump input  2994206  3月 20 19:28  binlog.000001
-rw-r----- 1 systemd-coredump input      345  3月 20 19:32  binlog.000002
-rw-r----- 1 systemd-coredump input       32  3月 20 19:28  binlog.index
-rw------- 1 systemd-coredump input     1680  3月 20 19:28  ca-key.pem
-rw-r--r-- 1 systemd-coredump input     1112  3月 20 19:28  ca.pem
-rw-r--r-- 1 systemd-coredump input     1112  3月 20 19:28  client-cert.pem
-rw------- 1 systemd-coredump input     1676  3月 20 19:28  client-key.pem
-rw-r----- 1 systemd-coredump input   196608  3月 20 19:32  '#ib_16384_0.dblwr'
-rw-r----- 1 systemd-coredump input  8585216  3月 20 19:28  '#ib_16384_1.dblwr'
-rw-r----- 1 systemd-coredump input     5657  3月 20 19:28  ib_buffer_pool
-rw-r----- 1 systemd-coredump input 12582912  3月 20 19:32  ibdata1
-rw-r----- 1 systemd-coredump input 12582912  3月 20 19:28  ibtmp1
drwxr-x--- 2 systemd-coredump input     4096  3月 20 19:28  '#innodb_redo'
drwxr-x--- 2 systemd-coredump input      187  3月 20 19:28  '#innodb_temp'
drwxr-x--- 2 systemd-coredump input        6  3月 20 19:32  lidao

删除容器并验证数据持久化

bash
[root@docker02.oldboylinux.cn /app/mysql/data]# docker rm -f db
db
 
[root@docker02.oldboylinux.cn /app/mysql/data]# docker ps 
CONTAINER ID   IMAGE     COMMAND   CREATED   STATUS    PORTS     NAMES
 
# 删除后查看目录依然存在
[root@docker02.oldboylinux.cn /app/mysql/data]# ll
总用量 99636
-rw-r----- 1 systemd-coredump input       56  3月 20 19:28  auto.cnf
-rw-r----- 1 systemd-coredump input  2994206  3月 20 19:28  binlog.000001
-rw-r----- 1 systemd-coredump input      345  3月 20 19:32  binlog.000002
-rw-r----- 1 systemd-coredump input       32  3月 20 19:28  binlog.index
-rw------- 1 systemd-coredump input     1680  3月 20 19:28  ca-key.pem
-rw-r--r-- 1 systemd-coredump input     1112  3月 20 19:28  ca.pem
-rw-r--r-- 1 systemd-coredump input     1112  3月 20 19:28  client-cert.pem
-rw------- 1 systemd-coredump input     1676  3月 20 19:28  client-key.pem
-rw-r----- 1 systemd-coredump input   196608  3月 20 19:32  '#ib_16384_0.dblwr'
-rw-r----- 1 systemd-coredump input  8585216  3月 20 19:28  '#ib_16384_1.dblwr'
-rw-r----- 1 systemd-coredump input     5657  3月 20 19:28  ib_buffer_pool
-rw-r----- 1 systemd-coredump input 12582912  3月 20 19:32  ibdata1
-rw-r----- 1 systemd-coredump input 12582912  3月 20 19:28  ibtmp1
drwxr-x--- 2 systemd-coredump input     4096  3月 20 19:28  '#innodb_redo'
drwxr-x--- 2 systemd-coredump input      187  3月 20 19:28  '#innodb_temp'
drwxr-x--- 2 systemd-coredump input        6  3月 20 19:32  lidao
drwxr-x--- 2 systemd-coredump input      143  3月 20 19:28  mysql
-rw-r----- 1 systemd-coredump input 31457280  3月 20 19:32  mysql.ibd
drwxr-x--- 2 systemd-coredump input     8192  3月 20 19:28  performance_schema
-rw------- 1 systemd-coredump input     1676  3月 20 19:28  private_key.pem
-rw-r--r-- 1 systemd-coredump input      452  3月 20 19:28  public_key.pem
-rw-r--r-- 1 systemd-coredump input     1112  3月 20 19:28  server-cert.pem
-rw------- 1 systemd-coredump input     1676  3月 20 19:28  server-key.pem
drwxr-x--- 2 systemd-coredump input       28  3月 20 19:28  sys
-rw-r----- 1 systemd-coredump input 16777216  3月 20 19:30  undo_001
-rw-r----- 1 systemd-coredump input 16777216  3月 20 19:32  undo_002

重新运行容器并验证数据库

bash
[root@docker02.oldboylinux.cn /app/mysql/data]# docker exec -it db mysql -uroot -p1 -e 'show databases;'
mysql: [Warning] Using a password on the command line interface can be insecure.
+--------------------+
| Database           |
+--------------------+
| information_schema |
| lidao              | <---- <---   <--
| mysql              |
| performance_schema |
| sys                |
+--------------------+

启动 Nginx 容器并映射配置文件和站点目录

bash
[root@docker02.oldboylinux.cn /app/mysql/data]# mkdir -p /app/code/bird/
 
[root@docker02.oldboylinux.cn /app/mysql/data]# cd  /app/code/bird/
 
[root@docker02.oldboylinux.cn /app/code/bird]# mv ~/default.conf  .
 
[root@docker02.oldboylinux.cn /app/code/bird]# vim default.conf 
 
[root@docker02.oldboylinux.cn /app/code/bird]# mv  ~/bird.tar.gz .
 
[root@docker02.oldboylinux.cn /app/code/bird]# mkdir code/
 
[root@docker02.oldboylinux.cn /app/code/bird]# ll
总用量 96
-rw-r--r-- 1 root root 91622  2月 27 11:37 bird.tar.gz
drwxr-xr-x 2 root root     6  3月 20 20:01 code
-rw-r--r-- 1 root root   361  3月 20 19:48 default.conf
 
# 查看是否占用端口
[root@docker02.oldboylinux.cn /app/code/bird]# docker ps 
CONTAINER ID   IMAGE              COMMAND                   CREATED          STATUS          PORTS                                                  NAMES
2e05998add7f   mysql:8.0-debian   "docker-entrypoint.s…"   27 minutes ago   Up 27 minutes   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   db
 
# 运行容器并映射配置文件和站点目录
[root@docker02.oldboylinux.cn /app/code/bird]# docker run -d -p 80:80 --name ngx_volume --restart always   -v /app/code/bird/default.conf:/etc/nginx/conf.d/default.conf   -v /app/code/bird/code/:/usr/share/nginx/html nginx:1.24
7c4c3d42a788878799ff34b0a7258c2c0eb0c134f8818a441b2fb48f3aced334
 
# 检查 ngx_volume 是否运行
[root@docker02.oldboylinux.cn /app/code/bird]# docker ps 
CONTAINER ID   IMAGE              COMMAND                   CREATED          STATUS          PORTS                                                  NAMES
7c4c3d42a788   nginx:1.24         "/docker-entrypoint.…"   4 seconds ago    Up 4 seconds    0.0.0.0:80->80/tcp, :::80->80/tcp                      ngx_volume
2e05998add7f   mysql:8.0-debian   "docker-entrypoint.s…"   42 minutes ago   Up 42 minutes   0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp   db

创建自定义镜像 Dockerfile

书写 Dockerfile

bash
[root@docker02.oldboylinux.cn /test]# cat Dockerfile 
-----------------------------------------------------------------
# 使用官方 Nginx 1.24 镜像作为基础镜像
FROM nginx:1.24
 
# 添加镜像元数据
LABEL author=1234 url=www.linuxpath.cn desc="FlyBird"
 
# VARS
ENV CODE bird.tar.gz
 
ENV CODE_DIR /usr/share/nginx/html/
 
ENV MSG  测试bird
 
ADD default.conf /etc/nginx/conf.d/default.conf
 
# 用变量试试
# ADD bird.tar.gz /usr/share/nginx/html
 
ADD  ${CODE} ${CODE_DIR}
 
RUN echo  {$MSG}>${CODE_DIR}lidao.html
 
# 代码目录测试
WORKDIR ${CODE_DIR}
RUN touch worktest.txt
 
# 数据卷
VOLUME ${CODE_DIR}
 
# 告诉 Docker 容器将会监听 80 端口和 443 端口,仅作声明作用
# 这两个端口是 Web 服务的默认 HTTP 和 HTTPS 端口
EXPOSE 80 443
 
# 启动 Nginx 服务,并通过 -g "daemon off;" 让 Nginx 在前台运行
# 这样容器不会退出,并且可以持续提供服务
CMD ["nginx", "-g", "daemon off;"]
------------------------------------------------------------------

创建镜像

bash
[root@docker02.oldboylinux.cn /test]# docker build -t test02:02 .

查看镜像

bash
[root@docker02.oldboylinux.cn /test]# docker images
REPOSITORY   TAG           IMAGE ID       CREATED         SIZE
test02       02            c1197d2e93d1   8 minutes ago   142MB

运行并查看环境变量是否设置成功

bash

[root@docker02.oldboylinux.cn /test]# docker build -t test02:02 .
 
# 查看 test02 镜像的环境变量
[root@docker02.oldboylinux.cn /test]# docker inspect test02:02
"Env": [
    "PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
    "NGINX_VERSION=1.24.0",
    "NJS_VERSION=0.7.12",
    "PKG_RELEASE=1~bullseye",
    "CODE=bird.tar.gz",
    "CODE_DIR=/usr/share/nginx/html/",
    "MSG=测试bird"
],
"Cmd": [
    "nginx",
    "-g",
    "daemon off"
]

Dockerfile 指令详解

ENV - 设置环境变量

ENV 用于在容器中设置环境变量,这些变量在容器运行时可用。

bash
# 示例
ENV MYSQL_ROOT_PASSWORD=1

在容器中查看环境变量:

bash
[root@docker02.oldboylinux.cn ~]# docker exec -it db /bin/bash
root@2e05998add7f:/# env
MYSQL_MAJOR=8.0
HOSTNAME=2e05998add7f
PWD=/
MYSQL_ROOT_PASSWORD=1
HOME=/root
MYSQL_VERSION=8.0.33-1debian11
GOSU_VERSION=1.16
TERM=xterm
SHLVL=1
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
_=/usr/bin/env

WORKDIR - 设置工作目录

WORKDIR 用于设置容器的工作目录,类似于 cd 命令。

bash
# 示例
WORKDIR /app/code/exam/

在容器中验证工作目录:

bash
[root@docker02.oldboylinux.cn ~]# docker exec -it 3c4e84edb854 /bin/bash
root@3c4e84edb854:/app/code/exam# pwd
/app/code/exam

数据卷

VOLUME 用于定义数据卷,使得容器中的目录可以持久化存储。

bash
# 示例
VOLUME /var/lib/mysql

查看数据卷:

bash
[root@docker02.oldboylinux.cn ~]# docker inspect db
"Volumes": {
    "/var/lib/mysql": {}
},

ADD vs COPY 的区别

特性ADDCOPY
功能支持从本地或远程 URL 复制文件,并自动解压压缩包(如 .tar.gz
仅支持从本地文件系统复制文件
解压能力✅ 自动解压压缩文件❌ 无解压功能
远程文件支持✅ 可从网络 URL 下载文件(如 ADD http://example.com/file.txt /app/
❌ 仅支持本地文件
官方推荐慎用(优先用 COPY,除非需要解压或远程文件)优先使用(简单透明,不易出错)
典型场景需要解压配置文件或下载依赖项时单纯复制本地文件到容器

示例:

bash
# ADD 示例:解压本地压缩包并复制到容器
ADD config.tar.gz /app/config/  # 自动解压到/app/config/
 
# COPY 示例:直接复制本地文件
COPY requirements.txt /app/

CMD vs ENTRYPOINT 的区别

特性CMDENTRYPOINT
命令覆盖性✅ 可被 docker run
覆盖(如 docker run image ls -l
会替换 CMD)
❌ 不可被覆盖,docker run
参数会作为 ENTRYPOINT 的子命令追加参数
默认执行定义容器启动时的默认命令定义容器必须执行的根命令(类似“入口函数”)
参数传递运行时的额外参数会替换 CMD 命令运行时的额外参数会作为 ENTRYPOINT 的参数(如 docker run image arg1 arg2
典型场景定义可替换的默认启动命令(如 CMD ["nginx"]
定义固定命令模板(如 ENTRYPOINT ["dockerize"]
+ CMD ["-template"]

示例:

bash
# CMD 示例:默认启动 Nginx(可被覆盖)
CMD ["nginx", "-g", "daemon off;"]
 
# ENTRYPOINT 示例:强制使用 curl 命令(参数通过 docker run 传递)
ENTRYPOINT ["curl"]
CMD ["-s", "http://example.com"]  # 运行时可追加参数:`docker run image -I`

ARG vs ENV 的区别

对比项ARGENV
定义位置Dockerfile 中通过 ARG
指令定义
Dockerfile 中通过 ENV
指令定义
作用阶段仅构建阶段(Build Time)构建阶段 + 运行阶段(Build & Runtime)
持久性构建完成后自动清除持久化到容器环境

业务如何容器化

业务容器化示例:基于 Tomcat 部署 WAR 包

容器化的核心是选择合适的基础镜像、优化配置、合理构建 Docker 镜像,然后推送到私有仓库以便团队协作和自动化部署。

1. 选择基础镜像

选择合适的 Linux 发行版作为基础镜像,这里使用 Tomcat 官方镜像:

bash
FROM tomcat:9.0

tomcat:9.0 是官方提供的 Tomcat 9 版本,已包含 JDK 和 Tomcat 环境。

2. 部署 JDK

如果基础镜像没有 JDK,需要手动安装:

bash
FROM ubuntu:20.04
RUN apt update && apt install -y openjdk-11-jdk

说明:

  • 适用于从零搭建 Tomcat 环境的情况。

3. 部署 Tomcat

如果使用自定义镜像,需要手动安装 Tomcat:

bash
RUN apt install -y wget && \
    wget https://downloads.apache.org/tomcat/tomcat-9/v9.0.65/bin/apache-tomcat-9.0.65.tar.gz && \
    tar -xzf apache-tomcat-9.0.65.tar.gz -C /opt && \
    mv /opt/apache-tomcat-9.0.65 /opt/tomcat

说明:

  • 适用于无法使用官方镜像,需要手动配置 Tomcat 的情况。

4. 上传 WAR 包

myapp.war 复制到 Tomcat webapps 目录:

bash
COPY myapp.war /usr/local/tomcat/webapps/

说明:

  • COPY 指令会将本地的 myapp.war 复制到容器的 webapps 目录,Tomcat 启动时会自动部署。

5. 优化 Tomcat 配置

Tomcat 默认配置可能无法满足高并发需求,因此需要调整:

bash
RUN echo "JAVA_OPTS='-Xms512m -Xmx1024m -Djava.security.egd=file:/dev/./urandom'" >> /usr/local/tomcat/bin/setenv.sh

说明:

  • -Xms512m -Xmx1024m 限制 JVM 最小 512MB,最大 1024MB。
  • -Djava.security.egd=file:/dev/./urandom 解决 Tomcat 启动慢的问题。

6. 开放端口

bash
EXPOSE 8080

说明:

  • 让容器监听 8080 端口,外部可访问 Web 服务。

7. 启动 Tomcat

bash

CMD ["/usr/local/tomcat/bin/catalina.sh", "run"]

说明:

  • 使用 CMD 指令在容器启动时运行 Tomcat。

8. 构建并运行 Docker 容器

执行以下命令:

bash
docker build -t myapp:v1 .
docker run -d -p 8080:8080 --name myapp-container myapp:v1

说明:

  • docker build 生成镜像 myapp:v1
  • docker run 以守护进程模式(-d)运行容器,映射 8080 端口。

9. 推送到私有仓库

如果有私有 Docker 仓库(如 Harbor、阿里云镜像仓库):

bash
docker tag myapp:v1 myregistry.com/myapp:v1
docker push myregistry.com/myapp:v1

说明:

  • docker tag 给镜像打上私有仓库标签。
  • docker push 上传镜像,方便团队协作。

感谢阅读,欢迎交流!