Docker - 第一个Hello World容器运行实操

👋 大家好,欢迎来到我的技术博客!
📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。
🎯 本文将围绕Docker这个话题展开,希望能为你带来一些启发或实用的参考。
🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获!
文章目录
Docker - 第一个Hello World容器运行实操 🐳✨
“Hello, world.” —— 这句简短却充满仪式感的问候,不仅是程序员踏入编程世界的第一个脚印,更是我们与容器化时代握手的第一声回响。当
System.out.println("Hello, World!");在 JVM 中轻盈跃出,而docker run hello-world在终端中迸发绿色字符时,两种范式在此刻交汇:代码的逻辑之美 与 环境的确定之稳,正悄然重构现代软件交付的底层契约。
本文将带你从零开始,亲手启动你的第一个 Docker 容器——不止是 hello-world 镜像的“开箱即用”,更将深入构建一个可编译、可运行、可调试、可观察的 Java Hello World 应用容器。你将亲手编写 Java 代码、编写 Dockerfile、构建镜像、运行容器、查看日志、进入交互式 Shell,并延伸至多阶段构建优化、端口映射、环境变量注入等核心实践。全程无需虚拟机、不依赖特定云平台,仅需一台装有 Docker 的 Linux/macOS/Windows(WSL2)机器,即可完成一次完整、真实、可复现的容器初体验 🌟。
一、前置准备:安装与验证 Docker 环境 ✅
在开始编码之前,请确保 Docker 引擎已在本地正确安装并运行。
1.1 检查 Docker 是否就绪
打开终端(Terminal / PowerShell / WSL),执行:
docker --version
预期输出类似:
Docker version 24.0.7, build afdd53b
再运行经典健康检查:
docker info | head -n 10
若无报错(如 Cannot connect to the Docker daemon),说明守护进程(Docker Engine)正在后台运行 ✅。
💡 小贴士:
- macOS 用户推荐使用 Docker Desktop for Mac(含 Kubernetes 支持)
- Windows 用户请启用 WSL2 后端(非旧版 Hyper-V),官方文档详述配置步骤
- Linux 用户(Ubuntu/Debian/CentOS)可直接通过包管理器安装,例如 Ubuntu:
sudo apt update && sudo apt install docker.io sudo systemctl enable --now docker sudo usermod -aG docker $USER # 免 sudo 运行 docker 命令(需重新登录终端)
1.2 运行首个容器:hello-world 验证链路 🌈
执行这行命令,它将自动拉取官方镜像、启动容器、打印欢迎信息、立即退出:
docker run hello-world
你将看到一段彩色文字,结尾类似:
Hello from Docker!
This message shows that your installation appears to be working correctly.
...
✅ 这意味着:
- Docker 客户端能与守护进程通信
- 镜像仓库(Docker Hub)网络可达
- 容器运行时(containerd/runc)工作正常
🔗 扩展阅读:Docker 官方入门指南 → https://docs.docker.com/get-started/
这里不仅涵盖安装,还包含交互式教程(含浏览器内嵌终端),是极佳的补充学习资源。
二、动手写 Java:一个真正“可容器化”的 Hello World 🧾
现在,让我们告别“玩具镜像”,进入真实开发场景:用 Java 编写一个带 HTTP 接口的微服务,然后将其打包为容器镜像。
2.1 创建项目结构
新建目录 java-hello-docker,并构建标准 Maven 目录结构:
mkdir -p java-hello-docker/src/main/java/com/example/hello
cd java-hello-docker
2.2 编写核心 Java 类:HelloWorldServlet.java
创建文件 src/main/java/com/example/hello/HelloWorldServlet.java:
package com.example.hello;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// ✨ 一个极简但完整的 Servlet:返回 HTML 格式的 Hello World + 当前时间
public class HelloWorldServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
private static final DateTimeFormatter FORMATTER =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String name = req.getParameter("name");
if (name == null || name.trim().isEmpty()) {
name = "World";
}
String time = LocalDateTime.now().format(FORMATTER);
String hostName = System.getenv("HOSTNAME"); // 容器 hostname,用于标识实例
String appName = System.getProperty("app.name", "JavaHelloApp");
resp.setContentType("text/html;charset=UTF-8");
resp.setStatus(HttpServletResponse.SC_OK);
StringBuilder html = new StringBuilder();
html.append("<!DOCTYPE html>")
.append("<html><head><title>Hello from Java</title>")
.append("<style>body{font-family:system-ui, sans-serif; padding:4rem 2rem; text-align:center; background:#f8f9fa;}")
.append("h1{color:#2c3e50;} .meta{color:#7f8c8d; font-size:0.9rem; margin-top:2rem;}</style>")
.append("</head><body>")
.append("<h1>🎉 Hello, ").append(name).append("!</h1>")
.append("<p>This response is served by <strong>").append(appName).append("</strong></p>")
.append("<p>Time: <strong>").append(time).append("</strong></p>")
.append("<p>Running in container: <code>").append(hostName != null ? hostName : "unknown").append("</code></p>")
.append("<div class='meta'>Built with Java 17 + Jetty • Dockerized ✅</div>")
.append("</body></html>");
resp.getWriter().write(html.toString());
}
}
📌 关键设计说明:
- 使用
javax.servlet(非 Spring Boot),降低依赖复杂度,突出容器本质 - 读取
HOSTNAME环境变量 → 后续将在容器中验证其值(每个容器有唯一 hostname) - 支持
?name=Alex查询参数 → 展示请求动态性 - 内嵌 CSS 实现轻量响应式页面 → 无需外部资源,纯静态 HTML 渲染
2.3 编写主启动类:HelloWorldServer.java
创建 src/main/java/com/example/hello/HelloWorldServer.java:
package com.example.hello;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
// ⚙️ 使用嵌入式 Jetty(轻量、无 Tomcat 依赖、启动快)
public class HelloWorldServer {
public static void main(String[] args) throws Exception {
int port = Integer.parseInt(System.getProperty("server.port", "8080"));
Server server = new Server(port);
ServletContextHandler context = new ServletContextHandler();
context.setContextPath("/");
server.setHandler(context);
context.addServlet(new ServletHolder(new HelloWorldServlet()), "/hello");
context.addServlet(new ServletHolder(new HelloWorldServlet()), "/"); // 根路径也响应
System.out.printf("🚀 Java Hello World server starting on http://localhost:%d/\n", port);
System.out.println("💡 Try: curl http://localhost:" + port + "/?name=Docker");
System.out.println("💡 Or open browser → http://localhost:" + port + "/");
server.start();
server.join(); // 阻塞主线程,保持 JVM 运行
}
}
📚 Jetty 是 Apache 顶级项目,以轻量、高性能、易嵌入著称。相比 Spring Boot 的
spring-boot-starter-web,它更贴近“裸容器”哲学——我们只容器化业务逻辑,而非框架全家桶。
🔗 官方文档:https://www.eclipse.org/jetty/documentation/
2.4 编写 Maven 构建配置:pom.xml
在项目根目录创建 pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>java-hello-docker</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jetty.version>11.0.20</jetty.version>
</properties>
<dependencies>
<!-- Jetty Server Core -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-server</artifactId>
<version>${jetty.version}</version>
</dependency>
<!-- Jetty Servlet Support -->
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-servlet</artifactId>
<version>${jetty.version}</version>
</dependency>
<!-- Jakarta Servlet API (required by Jetty 11+) -->
<dependency>
<groupId>jakarta.servlet</groupId>
<artifactId>jakarta.servlet-api</artifactId>
<version>5.0.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- Shade plugin to create fat jar -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>3.4.1</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass>com.example.hello.HelloWorldServer</mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
✅ 此 pom.xml 实现:
- JDK 17 编译兼容(Docker 官方 OpenJDK 17 镜像已成主流)
- 使用
maven-shade-plugin打包为 fat jar(含所有依赖),避免容器内 ClassNotFound - 显式声明
jakarta.servlet-api(Jetty 11+ 已迁移至 Jakarta EE 9+ 命名空间)
2.5 本地构建与运行(非容器)🧪
确保你已安装 JDK 17+ 和 Maven 3.8+:
java -version # 应输出 openjdk version "17.x.x"
mvn -v # 应输出 Apache Maven 3.8+
执行构建:
mvn clean package -DskipTests
成功后,生成 target/java-hello-docker-1.0.0.jar。
运行它:
java -jar target/java-hello-docker-1.0.0.jar
终端将输出:
🚀 Java Hello World server starting on http://localhost:8080/
💡 Try: curl http://localhost:8080/?name=Docker
💡 Or open browser → http://localhost:8080/
✅ 打开浏览器访问 http://localhost:8080/?name=Docker,你将看到一个清爽的 HTML 页面,显示:
🎉 Hello, Docker!
This response is served by JavaHelloApp
Time: 2024-06-15 14:22:35.123
Running in container: unknown
🎯 注意最后一行:“Running in container: unknown” —— 因为当前仍在宿主机 JVM 中运行,
HOSTNAME环境变量非容器上下文。这个字段,将在我们进入 Docker 世界后,变成独一无二的容器 ID 或自定义名称。
三、编写 Dockerfile:定义容器蓝图 🗺️
Dockerfile 是容器的“施工图纸”。它声明了:基于哪个操作系统?安装什么依赖?复制哪些文件?以什么用户运行?监听什么端口?启动什么命令?
在项目根目录(与 pom.xml 同级)创建文件 Dockerfile:
# 🐘 多阶段构建第一阶段:构建 Java 应用(Build Stage)
FROM maven:3.9.6-openjdk-17-slim AS builder
# 设置工作目录
WORKDIR /app
# 复制 pom.xml 先(利用 Docker layer cache 加速)
COPY pom.xml .
# 下载依赖(此层缓存率极高)
RUN mvn dependency:go-offline -B
# 复制源码
COPY src ./src
# 执行编译与打包(生成 fat jar)
RUN mvn clean package -DskipTests
# 🐳 第二阶段:运行时镜像(Runtime Stage)
FROM openjdk:17-jre-slim
# 创建非 root 用户增强安全性(最佳实践!)
RUN addgroup -g 1001 -f appgroup && adduser -S appuser -u 1001
# 设定工作目录
WORKDIR /app
# 从 builder 阶段复制 fat jar
COPY --from=builder /app/target/java-hello-docker-1.0.0.jar /app/app.jar
# 暴露端口(仅声明,不自动映射)
EXPOSE 8080
# 切换到非 root 用户
USER appuser
# 设置 JVM 参数(合理堆内存、禁用 JMX 等)
ENV JAVA_OPTS="-Xms64m -Xmx128m -Djava.security.egd=file:/dev/./urandom"
# 启动命令:java -jar + 环境变量覆盖端口
ENTRYPOINT ["sh", "-c", "java $JAVA_OPTS -Dserver.port=$PORT -jar /app/app.jar"]
🔍 Dockerfile 关键特性解析:
| 行号 | 指令 | 说明 | ✨ 为什么重要 |
|---|---|---|---|
1 |
FROM maven:... AS builder |
使用官方 Maven 镜像作为构建阶段基础 | 避免在最终镜像中携带 mvn、git、gcc 等构建工具,减小攻击面 |
13 |
FROM openjdk:17-jre-slim |
生产镜像仅含 JRE(非 JDK),体积更小(≈ 120MB vs 450MB) | 减少漏洞数量、加快拉取、提升启动速度 |
17 |
adduser -S appuser |
创建系统用户并切换 | 遵循 CIS Docker Benchmark 安全基线:禁止 root 运行容器进程 |
23 |
EXPOSE 8080 |
文档化容器监听端口 | docker ps 可见,且是 docker run -P 自动映射的依据 |
27 |
ENTRYPOINT [...] |
使用 shell 形式支持环境变量插值($PORT) |
允许运行时灵活指定端口,如 docker run -e PORT=9090 ... |
📌 多阶段构建(Multi-stage Build)是现代 Docker 最佳实践之一:它将“构建环境”与“运行环境”彻底隔离,最终镜像不含任何编译器、测试框架或源码,仅保留最小运行时依赖。
四、构建并运行容器:见证 Hello World 的容器化诞生 🌟
4.1 构建镜像
在 java-hello-docker/ 目录下执行:
docker build -t java-hello-app .
你会看到逐层构建日志,最后出现:
=> exporting to image 0.1s
=> => exporting layers 0.0s
=> => writing image sha256:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx 0.0s
=> => naming to docker.io/library/java-hello-app 0.0s
✅ 镜像已构建完成。查看它:
docker images | grep java-hello-app
输出类似:
java-hello-app latest xxxxxxxx 47 seconds ago 128MB
💡 镜像大小仅 128MB —— 对比传统虚拟机(GB 级)、甚至某些臃肿的 Spring Boot 镜像(300MB+),这是精简构建带来的真实收益。
4.2 运行容器(基础模式)
docker run -p 8080:8080 java-hello-app
-p 8080:8080:将宿主机8080端口映射到容器8080端口- 终端将打印 Jetty 启动日志,最后停在
Started Server@xxxxxx行
✅ 现在访问 http://localhost:8080/?name=Docker,你将看到完全相同的页面,但注意最后一行变化:
Running in container: 3a7b9c1d2e4f
👉 这个 3a7b9c1d2e4f 就是 Docker 分配给该容器的 随机 hostname(即容器 ID 前缀),证明你已真正在容器中运行!
4.3 运行容器(生产增强模式)⚡
添加更多实用参数,模拟真实部署场景:
docker run -d \
--name my-java-hello \
-p 8080:8080 \
-e PORT=8080 \
-e app.name="My Dockerized Java App 🐋" \
-v $(pwd)/logs:/app/logs \
--restart unless-stopped \
--memory=256m \
--cpus=0.5 \
java-hello-app
参数详解:
-d:后台守护进程模式(detached)--name my-java-hello:赋予容器可读名称,便于管理-e PORT=8080:覆盖默认端口(虽此处同值,但体现可配置性)-e app.name="...":注入自定义应用名,HTML 页面将显示该值-v $(pwd)/logs:/app/logs:挂载宿主机logs/目录到容器/app/logs(本例未写日志,但为扩展预留)--restart unless-stopped:容器异常退出后自动重启(除手动docker stop外)--memory=256m --cpus=0.5:资源限制,防止单个容器耗尽主机资源
✅ 查看运行中容器:
docker ps -f name=my-java-hello
输出:
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
a1b2c3d4e5f6 java-hello-app "sh -c 'java $JAVA_…" 3 seconds ago Up 2 seconds 0.0.0.0:8080->8080/tcp my-java-hello
✅ 查看实时日志:
docker logs -f my-java-hello
你将看到每笔 HTTP 请求的 Jetty 访问日志(如 172.17.0.1 - - [15/Jun/2024:14:30:22 +0000] "GET /?name=Docker HTTP/1.1" 200 523)。
五、深入容器内部:交互式调试与诊断 🔍
容器不是黑盒。Docker 提供强大工具助你“登船检查”。
5.1 进入容器 Bash(需镜像含 sh)
我们的 openjdk:17-jre-slim 镜像自带 sh,执行:
docker exec -it my-java-hello sh
你将进入容器内部 Shell,路径为 /app:
/app # ls -l
total 12
-rw-r--r-- 1 appuser appgroup 11234656 Jun 15 14:25 app.jar
/app # echo $HOSTNAME
a1b2c3d4e5f6
/app # cat /proc/1/cmdline | tr '\0' '\n'
sh
-c
java $JAVA_OPTS -Dserver.port=$PORT -jar /app/app.jar
✅ 验证了:
HOSTNAME环境变量正是容器 ID- PID 1 进程即为 Java 启动命令
app.jar存在于/app/目录
输入 exit 退出。
5.2 查看容器详细信息(Inspect)
docker inspect my-java-hello
输出为 JSON,包含 IP 地址、挂载点、网络配置、状态、启动命令等全部元数据。重点关注:
"NetworkSettings": {
"IPAddress": "172.17.0.2",
"Ports": { "8080/tcp": [{ "HostIp": "0.0.0.0", "HostPort": "8080" }] }
},
"Config": {
"Env": ["PORT=8080", "app.name=My Dockerized Java App 🐋"],
"Cmd": null,
"Entrypoint": ["sh", "-c", "java $JAVA_OPTS -Dserver.port=$PORT -jar /app/app.jar"]
}
✅ 这就是容器的“数字身份证”,是自动化运维、Kubernetes 调度的基础数据源。
六、可视化理解:Docker 核心组件协作流程 🧩
Docker 不是单体软件,而是由多个松耦合组件协同工作的平台。以下 Mermaid 图表清晰展示一次 docker run 命令背后发生了什么:
📌 图解说明:
- Docker CLI(蓝色):用户入口,发送 REST API 请求
- Docker Daemon(橙色):核心守护进程,接收 CLI 请求,协调全局资源
- Containerd(紫色):符合 OCI(Open Container Initiative)标准的工业级容器管理器,负责镜像分发、容器生命周期(create/start/stop)
- runc(青色):轻量级 CLI 工具,直接调用 Linux kernel 特性(namespaces 隔离进程视图,cgroups 限制资源)
- Docker Hub(灰色):公共镜像仓库,
docker pull默认源 - Linux Kernel(红色):一切容器化的根基 —— 没有 namespace/cgroup,就没有容器 🌐
✅ 正因这种分层架构,Docker 可被其他运行时(如 Podman、CRI-O)替代,而上层工具(如 Kubernetes)仍可无缝工作 —— 这就是 标准化的力量。
七、进阶实战:同一镜像,多环境部署 🌐
Docker 的核心价值之一:Build Once, Run Anywhere。我们用同一镜像,在不同场景下运行:
7.1 场景一:本地开发调试(已演示)
docker run -p 8080:8080 -e app.name="Dev Env 🛠️" java-hello-app
7.2 场景二:测试环境(绑定固定端口 + 日志卷)
docker run -d \
--name hello-test \
-p 8081:8080 \
-e PORT=8080 \
-v $(pwd)/test-logs:/app/logs \
java-hello-app
7.3 场景三:CI/CD 流水线中的健康检查(Health Check)
修改 Dockerfile,在末尾添加:
# 添加健康检查:每10秒调用一次 /hello 接口,3次失败则标记 unhealthy
HEALTHCHECK --interval=10s --timeout=3s --start-period=30s --retries=3 \
CMD curl -f http://localhost:8080/hello || exit 1
重建镜像后,运行:
docker run -d --name hello-health -p 8082:8080 java-hello-app
docker ps --filter name=hello-health --format "table {{.ID}}\t{{.Status}}\t{{.Names}}"
你会看到状态列含 healthy 字样 ✅。健康检查是 Kubernetes/Liveness Probe 的前提。
7.4 场景四:多容器协作(Docker Compose 简介)
创建 docker-compose.yml(同级目录):
version: '3.8'
services:
web:
image: java-hello-app
ports:
- "8080:8080"
environment:
- app.name=Hello World Web 🌐
- PORT=8080
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/hello"]
interval: 15s
timeout: 5s
retries: 3
start_period: 40s
nginx:
image: nginx:alpine
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
depends_on:
- web
配套 nginx.conf(反向代理):
events { worker_connections 1024; }
http {
upstream backend { server web:8080; }
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
}
启动整套服务:
docker compose up -d
✅ 访问 http://localhost/,流量经 Nginx 代理至 Java 容器 —— 这就是微服务网关的雏形。
🔗 Docker Compose 官方文档(含大量生产案例)→ https://docs.docker.com/compose/
八、常见问题排障指南 ❓
遇到问题?别慌,以下是高频场景与解决方案:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
docker: command not found |
Docker CLI 未安装或未加入 PATH | 重新安装 Docker Desktop / 检查 which docker |
Cannot connect to the Docker daemon |
Docker Engine 未运行 | macOS/Windows:打开 Docker Desktop;Linux:sudo systemctl start docker |
docker run hello-world 报 TLS 错误 |
Docker 客户端与守护进程版本不匹配 | 升级 Docker Desktop 或重置 daemon 配置 |
容器启动后立即退出(STATUS Exited (1)) |
ENTRYPOINT 或 CMD 命令执行失败/退出 |
docker logs <container> 查看错误;docker run -it <image> sh 进入调试 |
访问 http://localhost:8080 超时 |
端口未正确映射或应用未监听 0.0.0.0 |
检查 docker ps 的 PORTS 列;确认 Java 代码中 Jetty 绑定的是 0.0.0.0:8080(默认即是) |
ClassNotFoundException |
fat jar 未正确构建或路径错误 | 进入容器 sh,执行 java -jar /app/app.jar 手动测试;检查 Dockerfile COPY 路径 |
Permission denied 写日志 |
容器用户 appuser 对挂载目录无写权限 |
宿主机创建目录后 chmod 777 logs/(开发临时方案)或 chown 1001:1001 logs/(生产推荐) |
💡 黄金排障三步法:
docker logs <container>→ 查看应用 stdout/stderrdocker inspect <container>→ 查看配置、网络、状态docker exec -it <container> sh→ 登录容器,手动执行命令验证
九、安全与生产就绪建议 🔐
容器 ≠ 免疫病毒。生产环境需额外加固:
9.1 镜像扫描(免费方案)
使用 Docker 官方 docker scan(基于 Snyk):
docker scan java-hello-app
它将检测基础镜像(openjdk:17-jre-slim)中的已知 CVE 漏洞,并给出修复建议(如升级到更新的 patch 版本)。
9.2 最小权限原则(已实践)
- ✅ 使用非 root 用户(
USER appuser) - ✅ 使用
jre-slim而非jdk或full镜像 - ✅ 多阶段构建移除构建工具
9.3 禁用不必要能力
运行时显式放弃危险 Linux capability:
docker run --cap-drop=ALL --read-only --tmpfs /tmp java-hello-app
--cap-drop=ALL:丢弃所有 Linux capabilities(如CAP_NET_ADMIN)--read-only:根文件系统只读(需确保应用日志写入/tmp或挂载卷)--tmpfs /tmp:提供可写临时目录
9.4 使用 .dockerignore(防意外泄露)
在项目根目录创建 .dockerignore:
.git
.gitignore
target/
README.md
Dockerfile
pom.xml
src/
防止 COPY . . 时把 target/(含旧 jar)、.git 等无用目录打入镜像,加速构建并减小体积。
十、总结:Hello World 之后,你已掌握的容器核心能力 🎯
回顾整个实操旅程,你已亲手完成了:
| 能力维度 | 具体实践 | ✅ 已掌握 |
|---|---|---|
| 环境一致性 | 同一 Dockerfile,在 macOS/Linux/Windows 上构建相同镜像 |
✔️ |
| 依赖隔离 | Java 应用与宿主机 JDK 版本解耦,镜像内含完整 JRE | ✔️ |
| 进程封装 | java -jar 进程被 namespace 隔离,拥有独立 PID/Network/UTS 空间 |
✔️ |
| 资源可控 | 通过 --memory, --cpus 限制容器资源使用 |
✔️ |
| 配置外置 | 通过 -e 注入环境变量,实现 app.name、PORT 动态配置 |
✔️ |
| 持久化扩展 | 通过 -v 挂载日志目录,实现容器内/外数据共享 |
✔️ |
| 生命周期管理 | docker run/start/stop/rm/logs/exec 全链路操作 |
✔️ |
| 安全基线 | 非 root 用户、最小基础镜像、多阶段构建 | ✔️ |
| 可观测性 | 健康检查、标准日志输出、inspect 元数据查询 |
✔️ |
🌈 这不再是教科书上的概念,而是你指尖敲出的每一行命令、浏览器中刷新出的每一个
Hello, Docker!。容器技术从未如此触手可及。
十一、下一步学习路径 🚀
你的容器之旅才刚刚启航。推荐沿着以下方向深化:
- 编排进阶:学习 Docker Swarm(原生集群)或 Kubernetes(行业标准),管理成百上千容器
- CI/CD 集成:将
docker build嵌入 Jenkins/GitLab CI,实现提交即构建、构建即部署 - 镜像仓库私有化:搭建 Harbor,实现镜像签名、漏洞扫描、权限管控
- 服务网格:引入 Istio,为 Java 微服务增加熔断、限流、分布式追踪能力
- 无服务器容器:探索 AWS Fargate、Google Cloud Run,按需付费、免运维容器运行
🌟 最后,送你一句来自 Docker 官方的箴言:
“Containers are a standard unit of software that packages up code and all its dependencies so the application runs quickly and reliably from one computing environment to another.”
—— 容器,是代码与其所有依赖打包而成的标准软件单元,让应用得以快速、可靠地在任意计算环境中运行。
现在,关掉这篇博客,打开你的终端,输入:
docker run --rm -it alpine sh -c "echo 'Hello, World! 🐳✨' && echo 'You are now a Docker practitioner.'"
你,已经是一名 Docker 实践者了。恭喜!🎉
愿每一次 docker run,都成为你交付确定性的庄严仪式。
🙌 感谢你读到这里!
🔍 技术之路没有捷径,但每一次阅读、思考和实践,都在悄悄拉近你与目标的距离。
💡 如果本文对你有帮助,不妨 👍 点赞、📌 收藏、📤 分享 给更多需要的朋友!
💬 欢迎在评论区留下你的想法、疑问或建议,我会一一回复,我们一起交流、共同成长 🌿
🔔 关注我,不错过下一篇干货!我们下期再见!✨
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)