【DOCKER容器实战】-1Nginx 与 Tomcat
文章目录
容器化应用实战:Nginx 与 Tomcat
[!note] 核心摘要
- 端口映射:
-p 宿主机端口:容器端口可将服务暴露给局域网;不加-p只能本机/容器网络访问- 数据卷挂载:
-v 宿主机目录:容器目录让网页目录、配置文件与宿主机共享,修改即时生效- 只读挂载:
:ro表示容器内只读、宿主机可写,静态页面与配置文件生产环境推荐- Tomcat 差异:新版 tomcat 镜像默认不带 ROOT 首页,访问根路径返回 404,需自行挂载部署
一、Nginx 容器实战
1. 运行 Nginx 容器(不暴露端口)
不在 docker host 暴露端口,仅容器内网可访问:
[root@localhost ~]# docker run --name some-nginx -v /some/content:/usr/share/nginx/html:ro -d nginx
fd8c7de2e666ac60e4b098c4fc770c2c8bd7ef6aff3c7462a9de369d1deed4f7
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fd8c7de2e666 nginx "/docker-entrypoint.…" 43 seconds ago Up 41 seconds 80/tcp some-nginx
[root@localhost ~]# docker inspect fd8c7de2e666 | grep IPAddress
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
命令要点解读
docker run --name some-nginx -v /some/content:/usr/share/nginx/html:ro -d nginx--name some-nginx:给容器命名为some-nginx-v /some/content:/usr/share/nginx/html:ro:挂载卷,宿主机/some/content映射到容器 nginx 网页目录,ro = read-only 只读- 挂载卷不需要自己创建,Docker 会自动创建
-d:后台守护模式运行- 没有
-p端口映射:不会把容器 80 端口暴露到宿主机,外部无法直接访问,只能在 Docker 内部网络访问。
docker ps:PORTS 列只显示80/tcp,代表容器内部开放 80 端口,但没有映射宿主机端口。docker inspect | grep IPAddress:获取容器 docker 网桥(bridge)内网 IP172.17.0.2。同一台宿主机上其他容器可以用这个 IP 访问 nginx,宿主机本机也可以访问该 IP,外部机器不能访问。
[!warning] 访问范围
没有-p端口映射时,外部其他机器无法访问172.17.0.2,只有本机宿主机、同 docker 网桥的其他容器可以访问。
验证挂载生效
[root@localhost ~]# curl http://172.17.0.2
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.29.0</center>
</body>
</html>
[root@localhost ~]# ls /some/
content
[root@localhost ~]# echo "nginx is start" > /some/content/index.html
[root@localhost ~]# curl http://172.17.0.2
nginx is start
过程解读:
- 通过
docker inspect拿到容器内网 IP172.17.0.2,宿主机可以直接访问这个容器网桥 IP。 - 第一次 curl 访问返回 403 Forbidden:宿主机
/some/content目录下没有 index.html 首页文件,nginx 找不到默认主页,报 403。 - 在宿主机挂载目录写入
index.html内容nginx is start。 - 再次 curl 访问容器 IP,直接读取到我们写入的页面内容,验证宿主机目录和容器目录挂载生效。
2. 运行 Nginx 容器(暴露 80 端口)
在 docker host 暴露 80 端口:
[root@localhost ~]# docker run -d -p 80:80 --name some-nginx-port -v /some/content-port:/usr/share/nginx/html:ro nginx
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
fe78fd48861a nginx "/docker-entrypoint.…" 39 seconds ago Up 38 seconds 0.0.0.0:80->80/tcp, :::80->80/tcp some-nginx-port
[root@localhost ~]# ls /some/
content content-port
[root@localhost ~]# echo "nginx is running" > /some/content-port/index.html
在宿主机上访问:浏览器地址 192.168.100.152,页面输出 nginx is running。
命令要点解读
-p 80:80:端口映射,格式宿主机端口:容器端口- 宿主机 80 端口映射到容器内部 80 端口
0.0.0.0:80->80/tcp代表监听本机所有网卡,外部局域网机器可以访问宿主机 IP 的 80 端口访问 nginx 服务。
-v /some/content-port:/usr/share/nginx/html:ro:挂载新的宿主机目录/some/content-port为 nginx 网页目录,只读。- 在宿主机挂载目录写入
index.html,浏览器访问宿主机 IP192.168.100.152,即可读取页面内容。
[!tip] 与上一例对比
不加-p只能本机/容器网络访问;加-p端口映射后,局域网其他机器也可以访问服务。
3. 挂载配置文件运行
挂载配置文件,需要先创建 1 个 nginx 容器,把配置文件复制出来修改后使用。先查看容器运行的进程,nginx worker 进程数量默认与 CPU 核心数一致:
[root@localhost ~]# docker top some-nginx-port
UID PID PPID C STIME TTY TIME CMD
root 2674 2654 0 09:55 ? 00:00:00 nginx: master process nginx -g daemon off;
101 2718 2674 0 09:55 ? 00:00:00 nginx: worker process
101 2719 2674 0 09:55 ? 00:00:00 nginx: worker process
创建 nginx 配置文件修改目录:
[root@localhost ~]# docker cp some-nginx-port:/etc/nginx/nginx.conf /opt/nginx_conf/
Successfully copied 2.56kB to /opt/nginx_conf/
修改配置文件,把进程数 auto 修改为 4:
[root@localhost ~]# ls /opt/nginx_conf/
nginx.conf
[root@localhost ~]# vim /opt/nginx_conf/nginx.conf
worker_processes 4;
重新创建容器,同时挂载网页目录与修改后的配置文件:
[root@localhost ~]# docker run -d \
> -p 82:80 --name nginx-server-conf \
> -v /opt/nginx-server-conf:/usr/share/nginx/html:ro \
> -v /opt/nginx_conf/nginx.conf:/etc/nginx/nginx.conf:ro \
> nginx
311116230fe29bf6a49dc007d07decf311b2e785889cf5d9951a5e61e190f662
# 后续验证 docker top nginx-server-conf 查看 worker 进程的数量
命令解读
docker top some-nginx-port:查看容器内部进程,nginx 分为 master 主进程(root)+ worker 工作进程(uid=101),worker 进程默认跟随 CPU 核心数。docker cp:把正在运行容器内的/etc/nginx/nginx.conf配置文件拷贝到宿主机/opt/nginx_conf/目录,拿到原始配置再修改。- 修改
worker_processes 4;,指定 nginx 工作进程数为 4。 - 新建容器,同时挂载网页目录 + 挂载修改后的 nginx 主配置文件(ro 只读)
-p 82:80:宿主机 82 端口映射容器 80-v 网页目录:网页静态资源挂载-v 宿主机nginx.conf:/etc/nginx/nginx.conf:ro:把修改后的配置文件注入容器,只读模式防止容器内篡改配置。
[!warning] 配置文件热更新限制
直接挂载单个配置文件,宿主机修改后容器不会自动生效,需要重启容器;如果要热更新,需要挂载整个 conf 目录。
二、数据卷 ro 参数详解
1. 含义
ro = read-only,只读挂载
-v /opt/nginx-server-conf:/usr/share/nginx/html:ro
含义:宿主机 /opt/nginx-server-conf 挂载到容器内 /usr/share/nginx/html,容器内部只能读,不能写这个目录。
2. 对比两种权限
| 权限 | 宿主机 | 容器内部 | 适用场景 |
|---|---|---|---|
:ro 只读 | ✅ 可正常增删改文件 | ❌ 不能新建、修改、删除任何文件 | nginx 静态页面、配置文件(推荐) |
默认读写 rw | ✅ 可正常增删改文件 | ✅ 拥有读写权限 | 需要容器内写数据的场景 |
3. 为什么 nginx 网页目录要加 :ro
- 安全防护:如果 nginx 出现漏洞被入侵,黑客也无法通过 nginx 进程篡改页面文件,容器内没有写权限,页面只能由宿主机侧更新。
- nginx 业务本身只需要读取 html,完全不需要写这个目录。
- ro 限制的是容器内部,宿主机不受影响:在宿主机修改
index.html,容器立刻就能读到新内容。
[!warning] 容易踩坑点
ro 是容器视角只读,不是宿主机只读。
- 宿主机:随便改文件
- 容器内部:
echo xxx > /usr/share/nginx/html/a.html会报错Read-only file system
4. 补充:对应参数 rw
:rw = read-write 读写,是 docker volume 挂载的默认值,可以省略不写。
# 只读,容器不能写
-v /opt/nginx-server-conf:/usr/share/nginx/html:ro
# 等价读写,容器可以写(不推荐给静态网页目录)
-v /opt/nginx-server-conf:/usr/share/nginx/html:rw
# 和下面完全一样,rw 默认省略
-v /opt/nginx-server-conf:/usr/share/nginx/html
本套案例里,nginx 配置文件挂载也加了 :ro:
-v /opt/nginx_conf/nginx.conf:/etc/nginx/nginx.conf:ro
同样目的:容器内部 nginx 进程不能修改配置文件,防止容器内篡改配置;宿主机修改文件后,重启容器生效。
[!tip] 记忆口诀
ro:容器只读,宿主机随便改;rw:容器读写,风险更高,业务不需要写就尽量用 ro。
三、Tomcat 容器实战
1. 不暴露端口运行
下载 Tomcat 镜像
[root@localhost ~]# docker pull tomcat
Using default tag: latest
latest: Pulling from library/tomcat
32f112e3802c: Pull complete
ea8f5ca39c1a: Pull complete
a4f9b8cc7730: Pull complete
4e45d9955da7: Pull complete
59425f3d7529: Pull complete
66c77c59c8e6: Pull complete
4f4fb700ef54: Pull complete
ccaa96572930: Pull complete
Digest: sha256:88c2a4ee830b56baec6cd6502935d96876a375889e51bdfa4c390d74df891d30
Status: Downloaded newer image for tomcat:latest
docker.io/library/tomcat:latest
如果下载慢,可以把备份的镜像文件 tomcat_latest.tar 导入:
[root@localhost ~]# docker load -i tomcat_latest.tar
运行容器
[root@localhost ~]# docker run -d --rm tomcat:latest
da3c37fd7e710ed3003f8de2ce3c5516aadd29bb2376906e166de3dbc9bf0884
查看容器
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
da3c37fd7e71 tomcat:latest "shturl.cc/d run" 6 minutes ago Up 6 minutes 8080/tcp objective_mirzakhani
命令解读
docker pull tomcat:拉取 tomcat 镜像,不带 tag 默认拉取tomcat:latest。docker load -i tomcat_latest.tar:离线导入镜像,网络差的时候使用。docker run -d --rm tomcat:latest-d:后台守护运行--rm:容器停止之后自动删除容器,测试环境临时用,生产不要用- 没有
-p端口映射,不会把 8080 端口暴露到宿主机
docker ps输出:PORTS列只显示8080/tcp,表示容器内部监听 8080 端口,没有映射宿主机端口。
[!note] 容器命名
objective_mirzakhani:没有用--name指定容器名,docker 自动随机生成容器名字。
补充:tomcat 默认端口是 8080,和 nginx 的 80 端口区分开。
获取容器 IP 并验证
[root@localhost ~]# docker inspect da3 | grep "IPAddress"
"SecondaryIPAddresses": null,
"IPAddress": "172.17.0.2",
"IPAddress": "172.17.0.2",
查看服务,运行的是 Tomcat 11.0 版本:
[root@localhost ~]# curl http://172.17.0.2:8080
<!doctype html><html lang="en"><head><title>HTTP Status 404 - Not Found</title><style type="text/css">body {font-family:Tahoma,Arial,sans-serif;} h1, h2, h3, b {color:white;background-color:#525D76;} h1 {font-size:22px;} h2 {font-size:16px;} h3 {font-size:14px;} p {font-size:12px;} a {color:black;} .line {height:1px;background-color:#525D76;border:none;}</style>
</head><body><h1>HTTP Status 404 - Not Found</h1><hr class="line" /><p><b>Type</b> Status Report</p><p><b>Description</b> The origin server did not find a current representation for the target resource or is not willing to disclose that one exists.</p>
<hr class="line" /><h3>Apache Tomcat/11.0.9</h3></body></html>
解读
docker inspect da3 | grep "IPAddress":截取容器内网 IP 为172.17.0.2,仅宿主机本机可访问该 IP。curl http://172.17.0.2:8080:访问 tomcat 容器的 8080 端口。- 返回 404-Not Found:新版 tomcat 镜像默认没有自带 ROOT 默认首页项目,服务本身已经正常启动,但是没有部署 web 应用,所以报 404。页面底部可以看到版本
Apache Tomcat/11.0.9。
[!note] 与 nginx 对比
nginx 镜像自带默认页面;tomcat 新版本官方镜像不再内置示例网页,访问根路径直接 404。
- 没有做
-p端口映射,其他局域网机器无法访问172.17.0.2:8080。
2. 暴露端口运行
[root@localhost ~]# docker run -d -p 8080:8080 tomcat:latest
dfc6850532ef43cdf5b4073b5eb008588a4f236a67d16c8608d91f7cb36313cc
查看容器:
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
dfc6850532ef tomcat:latest "shturl.cc/H run" 25 seconds ago Up 25 seconds 0.0.0.0:8080->8080/tcp, :::8080->8080/tcp musing_mahavira
da3c37fd7e71 tomcat:latest "shturl.cc/H run" 10 minutes ago Up 10 minutes 8080/tcp objective_mirzakhani
命令解读
-p 8080:8080:格式宿主机端口:容器内部端口,把宿主机的 8080 端口映射到 tomcat 容器内部的 8080 端口。0.0.0.0:8080->8080/tcp:监听本机所有网卡,局域网其他机器可以访问宿主机IP:8080访问 tomcat 服务。docker ps两个容器对比:dfc6850532ef:做了端口映射,PORTS显示0.0.0.0:8080->8080/tcp,外部网络可访问。da3c37fd7e71:没有-p映射,PORTS只有8080/tcp,仅宿主机本机可通过容器内网 IP 访问。
- 没有写
--name,docker 自动随机生成容器名字musing_mahavira。
[!warning] 新版 tomcat 镜像即便端口映射成功,直接访问根路径依旧是 404,需要部署 war 包/web 项目到 webapps 目录。
查看站点内容是否为空:
[root@localhost ~]# docker exec dfc6850532ef ls /usr/local/tomcat/webapps
3. 暴露端口并挂载网站目录
[root@localhost ~]# docker run -d -p 8081:8080 -v /opt/tomcat-server:/usr/local/tomcat/webapps/ROOT tomcat:latest
7cb2ab2b6d15154043f03eaf5523a6dee870fbb0740ee3b96840cf64f98ea63d
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7cb2ab2b6d15 tomcat:latest "catalina.sh run" 47 seconds ago Up 46 seconds 0.0.0.0:8081->8080/tcp, :::8081->8080/tcp sharp_cori
写入文件内容:
[root@localhost ~]# echo "tomcat running" > /opt/tomcat-server/index.html
浏览器访问 宿主机IP:8081,页面输出 tomcat running。
命令解析
docker exec dfc6850532ef ls /usr/local/tomcat/webapps:进入容器执行 ls 命令,查看 webapps 目录,新版 tomcat 镜像 webapps 下面是空的,所以访问根路径 404。-v /opt/tomcat-server:/usr/local/tomcat/webapps/ROOT:- 宿主机目录
/opt/tomcat-server挂载到 tomcat 容器的 ROOT 应用目录(tomcat 根访问路径/对应的项目目录) -p 8081:8080:宿主机 8081 端口映射容器 8080 端口,外部访问宿主机IP:8081
- 宿主机目录
echo "tomcat running" > /opt/tomcat-server/index.html:在宿主机挂载目录写入首页文件,容器内立刻可以读到,访问IP:8081就会输出tomcat running。
[!note] 挂载权限
这里挂载没有写:ro,是默认rw读写模式。
ROOT 是 tomcat 特殊目录:访问http://ip:8081/等价读取webapps/ROOT下面的资源。
四、命令速查
| 命令 | 作用 |
|---|---|
docker run -d -p 80:80 --name xxx nginx | 运行 nginx 并映射端口 |
docker cp 容器:路径 宿主机路径 | 拷贝容器内文件到宿主机 |
docker top 容器名 | 查看容器内进程(宿主机视角) |
docker pull tomcat / docker load -i tomcat.tar | 在线拉取 / 离线导入镜像 |
docker run -d --rm tomcat | 运行 tomcat,停止后自动删除容器 |
-v 宿主机目录:容器目录:ro | 只读挂载数据卷 |
更多推荐


所有评论(0)