Docker 镜像构建与容器网络配置详解

自定义 Java 项目镜像制作及容器间网络通信

1. 镜像构建基础

1.1 镜像结构

  • 镜像能实现跨操作系统快速部署应用,核心在于包含程序运行所需的系统函数库、环境、配置、依赖等所有必要文件。
  • 自定义镜像的本质是按顺序准备程序运行的基础环境、依赖、应用本身、运行配置等文件并打包。例如部署 Java 应用的镜像,对应步骤为:
    • 准备 Linux 基础运行环境(无需完整操作系统,仅需基础运行环境)
    • 安装并配置 JDK
    • 拷贝 Jar 包
    • 配置启动脚本
  • 镜像文件按操作步骤分层叠加(Layer),每一层形成的文件单独打包并标记唯一 ID。若构建时某些层已存在,可直接复用,无需重复制作。例如 Linux 运行环境通用性强,Docker 官方已提供相关基础镜像,构建 Java 镜像时可直接使用。

1.2 Dockerfile

指令 说明 示例
FROM 指定基础镜像 FROM centos:6
ENV 设置环境变量,可在后续指令中使用 ENV key value
COPY 拷贝本地文件到镜像的指定目录 COPY ./xx.jar /tmp/app.jar
RUN 执行 Linux 的 shell 命令,通常用于安装过程 RUN yum install gcc
EXPOSE 指定容器运行时监听的端口,用于提示镜像使用者 EXPOSE 8080
ENTRYPOINT 镜像中应用的启动命令,容器运行时会调用 ENTRYPOINT java -jar xx.jar
  • 基于 Ubuntu 镜像构建 Java 应用的 Dockerfile 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    # 指定基础镜像
    FROM ubuntu:16.04
    # 配置环境变量,JDK的安装目录、容器内时区
    ENV JAVA_DIR=/usr/local
    ENV TZ=Asia/Shanghai
    # 拷贝jdk和java项目的包
    COPY ./jdk8.tar.gz $JAVA_DIR/
    COPY ./docker-demo.jar /tmp/app.jar
    # 设定时区
    RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
    # 安装JDK
    RUN cd $JAVA_DIR \
    && tar -xf ./jdk8.tar.gz \
    && mv ./jdk1.8.0_144 ./java8
    # 配置环境变量
    ENV JAVA_HOME=$JAVA_DIR/java8
    ENV PATH=$PATH:$JAVA_HOME/bin
    # 指定项目监听的端口
    EXPOSE 8080
    # 入口,java项目的启动命令
    ENTRYPOINT ["java", "-jar", "/app.jar"]
  • 优化后的 Dockerfile(利用包含 JDK 的基础镜像,省去 JDK 配置步骤):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 基础镜像
    FROM openjdk:11.0-jre-buster
    # 设定时区
    ENV TZ=Asia/Shanghai
    RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
    # 拷贝jar包
    COPY docker-demo.jar /app.jar
    # 入口
    ENTRYPOINT ["java", "-jar", "/app.jar"]

1.3 构建镜像

  • 准备工作:将项目的 jar 包(如 docker-demo.jar)和 Dockerfile 拷贝到虚拟机的指定目录(如 /root/demo)。

  • 构建命令:

    1
    2
    3
    4
    5
    6
    # 进入镜像目录
    cd /root/demo
    # 开始构建,-t指定镜像名称(repository和tag),.表示Dockerfile所在路径
    docker build -t docker-demo:1.0 .
    # 也可直接指定Dockerfile目录
    docker build -t docker-demo:1.0 /root/demo
  • 查看构建的镜像:docker images,示例结果:

    1
    2
    3
    4
    REPOSITORY    TAG       IMAGE ID       CREATED          SIZE
    docker-demo 1.0 d6ab0b9e64b9 27 minutes ago 327MB
    nginx latest 605c77e624dd 16 months ago 141MB
    mysql latest 3218b38490ce 17 months ago 516MB
  • 运行镜像:

    1
    2
    3
    4
    5
    6
    7
    8
    # 创建并运行容器
    docker run -d --name dd -p 8080:8080 docker-demo:1.0
    # 查看容器
    docker ps(可简写为dps)
    # 示例结果
    CONTAINER ID IMAGE PORTS STATUS NAMES
    78a000447b49 docker-demo:1.0 0.0.0.0:8080->8080/tcp, :::8090->8090/tcp Up 2 seconds dd
    f63cfead8502 mysql 0.0.0.0:3306->3306/tcp, :::3306->3306/tcp, 33060/tcp Up 2 hours mysql
  • 访问容器应用:curl localhost:8080/hello/count,示例结果:<h5>欢迎访问黑马商城, 这是您第1次访问<h5>

2. Docker 网络配置

2.1 容器网络互联测试

  • 查看容器 IP:通过 docker inspect 容器名docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' 容器名 查看,例如查看 MySQL 容器 IP:

    1
    2
    docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql
    # 可能得到IP:172.17.0.2
  • 测试网络连通性:进入某一容器(如 dd 容器),通过 ping 目标容器IP 测试,示例:

    1
    2
    3
    4
    5
    6
    7
    # 进入dd容器
    docker exec -it dd bash
    # 测试与MySQL容器的连通性
    ping 172.17.0.2
    # 示例结果(可连通)
    PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
    64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.053 ms
  • 问题:容器虚拟 IP 不固定,若开发时写死 IP,部署时可能因 IP 变化导致连接失败。

2.2 常用网络命令

命令 说明 文档地址
docker network create 创建一个网络 docker network create
docker network ls 查看所有网络 docker network ls
docker network rm 删除指定网络 docker network rm
docker network prune 清除未使用的网络 docker network prune
docker network connect 使指定容器加入某网络 docker network connect
docker network disconnect 使指定容器离开某网络 docker network disconnect
docker network inspect 查看网络详细信息 docker network inspect

2.3 自定义网络配置示例

  • 创建自定义网络:

    1
    docker network create hmall
  • 查看网络:

    1
    2
    3
    4
    5
    6
    7
    docker network ls
    # 示例结果
    NETWORK ID NAME DRIVER SCOPE
    639bc44d0a87 bridge bridge local
    403f16ec62a2 hmall bridge local
    0dc0f72a0fbb host host local
    cd8d3e8df47b none null local
  • 让容器加入网络并设置别名(同一网络内的容器可通过别名互相访问):

    1
    2
    3
    4
    # 让mysql容器加入hmall网络,别名为db(容器默认别名是容器名)
    docker network connect hmall mysql --alias db
    # 让dd容器(Java项目)加入hmall网络
    docker network connect hmall dd
  • 测试别名访问:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    # 进入dd容器
    docker exec -it dd bash
    # 用db别名访问mysql容器
    ping db
    # 示例结果
    PING db (172.18.0.2) 56(84) bytes of data.
    64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.070 ms
    # 用容器名访问mysql容器
    ping mysql
    # 示例结果
    PING mysql (172.18.0.2) 56(84) bytes of data.
    64 bytes from mysql.hmall (172.18.0.2): icmp_seq=1 ttl=64 time=0.044 ms

2.4 网络配置总结

  • 在自定义网络中,容器可拥有多个别名,默认别名为容器名本身。
  • 同一自定义网络中的容器,可通过别名互相访问,无需依赖固定 IP,解决了 IP 不固定导致的连接问题。