跳至主要內容

Docker 问题

程序员李某某大约 10 分钟

Docker 问题

下载地址

AMD架构open in new window

ARM架构open in new window

打包报错

ERROR: failed to solve: DeadlineExceeded: DeadlineExceeded: DeadlineExceeded: openjdk:17: failed to resolve source metadata for docker.io/library/openjdk:17: failed to authorize: DeadlineExceeded: failed to fetch oauth token: Post "https://auth.docker.io/token": dial tcp 54.196.99.49:443: i/o timeout

需要管理员权限

  • Windows 以管理员运行 CMD
  • Linux 以 root 用户运行或者加 sudo 前缀

启动服务报错

Error response from daemon: driver failed programming external connectivity on endpoint office (5eef8b3c39e7ef1462aeeab2236fd7b1bb1a92ef6a390c03df2362f0ad0ac7c2):  (iptables failed: iptables --wait -t nat -A DOCKER -p tc
p -d 0/0 --dport 9988 -j DNAT --to-destination 172.17.0.2:80 ! -i docker0: iptables: No chain/target/match by that name.
  • ip 有变化 重启docker即可service docker restart

交叉编译 Docker BuildX

docker 在19.03的版本支持了一个实验性的功能 - - buildx,大大地减少了编译工作,但也还是有些坑,慢慢道来

利用 Docker 19.03 引入的插件 buildxopen in new window,可以很轻松地构建多平台 Docker 镜像。buildx 是 docker build ... 命令的下一代替代品,它利用 BuildKitopen in new window 的全部功能扩展了 docker build 的功能

这里不详细介绍该如此开启buildx了,大家可参考官方的handbook,这里假设开启了”experimental”: true

使用以下命令确认开启了:

docker info | grep -i exp
Experimental: true  

Docker buildx需要开启binfmt_misc, 开启方法如下

验证是否启用了相应的处理器:

cat /proc/sys/fs/binfmt_misc/qemu-aarch64
enabled
interpreter /usr/bin/qemu-aarch64
flags: OCF
offset 0
magic 7f454c460201010000000000000000000200b7mask 
ffffffffffffff00fffffffffffffffffeffff

Docker 默认会使用不支持多 CPU 架构的构建器,我们需要手动切换。

先创建一个新的构建器:

# 新建构建器
docker buildx create --use --name mybuilder 
# 启动构建器
docker buildx inspect mybuilder --bootstrap

准备工作就绪后,下面以例子来说明,既然是交叉编译,那就得先有一个初始的x86 的Dockerfile,然后将其编译出arm64的镜像

时区问题

# 运行容器增加 -e 参数
docker run -e TZ=Asia/Shanghai --name your_container_name -d your_image
# 容器内修改
docker exec -it 1560e87cb43e /bin/bash 
echo "Asia/Shanghai" > /etc/timezone

排查大容器

有时候经常会有个别容器占用磁盘空间特别大,这个时候就需要通过docker overlay2 目录名查找对应容器名:

## 首先进入到 /var/lib/docker/overlay2 目录下,查看谁占用的较多
cd /var/lib/docker/overlay2 
du -sc * | sort -rn  | more
## 再通过目录名查找容器名,便可以确定具体的容器
docker ps -q | xargs docker inspect --format '{{.State.Pid}}, {{.Id}}, {{.Name}}, {{.GraphDriver.Data.WorkDir}}' | grep "bbed8c4cd32f640601c976229c610a5d3ef0ddc5be274acda3699f425dff884a"

网络配置

四种网络模式: bridge、 host、 container、 none

  • bridge模式:--net=bridge 桥接模式(默认设置,自己创建也使用bridge 模式)
  • host模式:--net=host 和宿主即共享网络
  • container模式:--net=container:NAME_or_ID 容器网络连通!(很少用,局限性很大!)
  • none模式:--net=none 不配置网络

bridge 网桥模式

Docker安装启动后会在宿主主机上创建一个名为 docker0 的虚拟网桥,处于七层网络模型的数据链路层, 后续每当我们创建一个新的 docker 容器,在不指定容器网络模式的情况下,docker 会通过 docker0 与主机的网络连接,docker0 相当于网桥。

  • --net=bridge 可省略
  • 网桥默认 IP 范围是一般都是 172.17.x.x,docker默认的内网网段为172.17.0.0/16,如果公司内网网段也是172.17.x.x的话,就会发生路由冲突

host 模式

如果指定的host模式容器不会拥有一个独立network namesace,而是与宿主主机共用network namesace。 也就说明容器本身不会有的网卡信息,而是使用宿主主机的网络信息。 容器除了网络,其他比如文件系统、进程等依然都是隔离的。

  • --net=host 指定
  • 容器和宿主主机共享 network namespace
  • host模式因为和宿主主机共享network namespace,会有可能出现端口冲突的情况。

container 模式

container 模式和 host 模式很类似: host 模式和宿主主机共享network namespace; container 模式和指定的容器共享,两者之间除了网络共享(网卡、主机名、IP 地址),其他方面还是隔离的。

命令:docker run -d -name tomcat02 --net=container:name/id -p 8000:80 tomcat:latest

  • –-net={容器id 或容器name} 指定
  • 当前容器和另外一个容器共享 network namespace

none 模式

如果dockers容器指定的网络模式为none,该容器没有办法联网,外界也无法访问它,可以用来本次测试。

命令:docker run -d -name tomcat02 --net=none -p 8000:80 tomcat:latest

  • --net=none 指定
  • 容器有独立的Network namespace,但并没有对其进行任何网络设置,如果需要的话,需要自定义配置网络

自定义网络-解决冲突

## 创建网络
docker network create --subnet=192.168.222.0/24 网络名
## 查看网络
docker network ls
## 查看网络详情
docker network inspect 网络名


## 解除网络绑定
docker network disconnect 网络名 容器名
## 为容器重新指定网络
docker network connect 网络名 容器名
## 删除网络
docker network rm 网络名

## 运行容器时指定网络
docker run --network mynet --ip 172.18.0.22 -d nginx
## 验证
docker inspect 容器名 | grep "IPAddress"

想要修改正在运行的jms_redis容器IP

## 查看网络配置
docker inspect 容器名 | grep IPAddress
## 需要进入容器的网络命名空间。可以使用以下命令获取容器的PID(Process ID)号
docker inspect -f '{{.State.Pid}}' 容器名
## 可以使用以下命令进入容器的网络命名空间
# AI给的命令 nsenter -t PID -n -i -u -p
nsenter --target PID --net /bin/bash
### 在容器内部,可以使用ip addr命令查看容器的网络配置信息
## 修改容器IP
ip addr add 172.18.0.22/16 dev eth0
## 使用ip link set命令将eth0接口打开
ip link set eth0 up

跨主机容器互访

需求如下

服务器IP容器分配网段容器IP
192.168.1.105172.172.0.0/24172.172.0.10
192.168.1.106172.172.1.0/24172.172.1.10

分别创建网络,运行容器

docker network create --subnet=172.172.0.0/24 net1
docker run -d --net=net1 --ip=172.172.0.10 --name=net1_1 nginx

docker network create --subnet=172.172.1.0/24 net2
docker run -d --net=net2 --ip=172.172.1.10 --name=net2_1 nginx

此时容器net1_1和net2_1网络是不通的, 需要分别添加对方的路由表

##  ip route add 对方容器所在的ip网段/子网掩码 via 对方虚拟机ip dev 通过哪个网卡通信
docker exec net1_1 ip route add 172.172.1.0/24 via 192.168.1.106 dev eno16777736 
docker exec net2_1 ip route add 172.172.0.0/24 via 192.168.1.105 dev eno16777736 
## eno16777736 是
## ip route del 172.172.1.0/24  用来移除路由表
  • 早期使用的是eth0, eth1 等名称,但随着 Predictable Network Interface Names(可预测网络接口名称)规则的引入,接口的名称变得更加灵活和具有一定的规范性
  • eno 通常代表以太网(Ethernet)接口(可能是 "Ethernet Over" 的缩写)
  • 16777736 是一个由操作系统自动分配的唯一标识符,通常基于硬件信息(比如网卡的MAC地址)来生成。
  • 可以通过ifconfig命令, 或ip a, 或ip addr命令查看

关于 -t、-i 选项

-i(--interactive): 使容器保持标准输入(stdin)打开

  • 即使没有连接终端,容器仍然能够接受输入。它的作用是允许你与容器进行交互。
  • 如果你想在容器内运行交互式程序(如 bash 或其他 shell),就需要用到这个选项 -t(--tty):分配一个伪终端(pseudo-terminal)
  • 容器的输出在终端上能够得到适当的格式化。这通常用于运行像 bash 这样的交互式命令时,使得输出在终端中呈现得更清晰和易于理解
  • 如果你希望运行一个命令并希望它的输出能像在本地终端上一样格式化显示,那么你需要使用 -t 通常,-i 和 -t 是一起使用的,因为它们互补

java17-maven-springboot3打包问题

dockerfile

# 使用 Maven 官方镜像作为基础镜像
FROM maven:3.8.5-openjdk-17 AS build

# 设置工作目录
WORKDIR /app

# 从指定代码仓库拉取代码
# 请将 YOUR_GIT_REPO_URL 替换为你的 Git 仓库地址
# RUN git clone YOUR_GIT_REPO_URL .
COPY ../ /app

# 使用 Maven 打包应用
RUN cd /app/lhzzzhmx-common && mvn clean install -DskipTests
RUN cd /app/lhzzzhmx-model && mvn clean package -DskipTests

# 使用 JDK 作为运行环境
#FROM openjdk:17-ea-jdk-slim
FROM openjdk:17-jdk-slim

# 将构建的 Jar 文件复制到新的镜像中
COPY --from=build /app/lhzzzhmx-model/target/*.jar /app/app.jar

# 设置环境变量
ENV SPRING_PROFILES_ACTIVE=prod

# 暴露服务端口
EXPOSE 8080

# 启动 Spring Boot 应用
ENTRYPOINT ["java", "-jar", "/app/app.jar"]
## VOLUME ["/app/conf"]
## ENTRYPOINT ["java", "-jar","-Dspring.config.location=/app/conf/", "/app/app.jar"]

问题:Lombok 插件版本问题

在使用 Spring Boot 3.0.0-M5 和 Lombok 1.18.16 时,可能会出现以下错误:

46.58 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.10.1:compile (default-compile) on project lhzzzhmx-common: Fatal error compiling: java.lang.IllegalAccessError: class lombok.javac.apt.LombokProce ssor (in unnamed module @0x174721c4) cannot access class com.sun.tools.javac.processing.JavacProcessingEnvironment (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.processing to unnamed module @0x174721c4 -> [Help 1]

Lombok 和 Java 模块系统之间的兼容性。具体来说,Lombok 尝试访问 JDK 内部的类 com.sun.tools.javac.processing.JavacProcessingEnvironment,但这些类在 Java 9 及更高版本中受到模块系统的限制,不再公开可见。

确保你使用的是最新版本的 Lombok,因为较新版本的 Lombok 已经修复了与 Java 模块系统的兼容性问题。你可以在父项目的 pom.xml 中更新 Lombok 的版本:

<properties>
    <lombok.version>1.18.24</lombok.version> <!-- 或者更高版本 -->
</properties>

问题:工具包找不到主类

 [ERROR] Failed to execute goal org.springframework.boot:spring-boot-maven-plugin:3.0.12:repackage (repackage) on project lhzzzhmx-common: Execution repackage of goal org.springframework.boot:spring-boot-maven-plugin:3.0.12:repackage failed: Unable to find main class -> [Help 1]

这个错误是因为 Spring Boot 3.0.12 版本的 repackage 插件无法找到主类。 解决这个问题的关键是确保你的 Spring Boot 应用程序有一个有效的主类,并且这个主类被正确地配置了。 在 pom.xml 文件中,确保有正确的主类配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.10.1</version>
            <configuration>
                <encoding>UTF-8</encoding>
            </configuration>
        </plugin>
        <!-- 移除或注释掉 spring-boot-maven-plugin -->
        <!--
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <version>${spring-boot.version}</version>
        </plugin>
        -->
    </plugins>
</build>

确保父 POM 不强制应用 spring-boot-maven-plugin 检查父项目的 pom.xml 文件 (be-lhzzzhmx/pom.xml),确保没有在 <build><dependencyManagement> 中全局配置 spring-boot-maven-plugin。如果有的话,可以将其限制为仅应用于特定的子模块

 <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <executions>
                <execution>
                    <id>repackage</id>
                    <goals>
                        <goal>repackage</goal>
                    </goals>
                    <configuration>
                        <skip>true</skip> <!-- 默认跳过 repackage -->
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>

然后在需要使用 spring-boot-maven-plugin 的子模块中覆盖该配置:

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <skip>false</skip> <!-- 明确不跳过 repackage -->
            </configuration>
        </plugin>
    </plugins>
</build>

问题:找不到本地common包

Non-resolvable parent POM for com.hykc.hqgy:lhzzzhmx-model:0.0.1-SNAPSHOT: Could not find artifact com.hykc.hqgy:lhzzzhmx:pom:0.0.1-SNAPSHOT and 'parent.relativePath' points at no local POM @ line 9, column 13 -> [Help 2]
  1. 确保父项目已安装到本地仓库 确保父项目的 POM 文件(即 be-lhzzzhmx/pom.xml)已经成功构建并安装到本地 Maven 仓库。可以在父项目的根目录下执行以下命令
mvn clean install
  1. 检查父 POM 的相对路径配置 虽然你在 lhzzzhmx-model/pom.xml 中设置了 ,但有时显式指定相对路径可以帮助解决问题。尝试将 修改为明确的路径指向父 POM 文件的位置,例如:
<relativePath>../pom.xml</relativePath>
  1. 检查父 POM 是否包含正确的模块声明
<modules>
    <module>lhzzzhmx-common</module>
    <module>lhzzzhmx-model</module>
    <module>lhzzzhmx-api</module>
    <module>lhzzzhmx-gateway</module>
</modules>

防火墙导致docker启动不了

docker:Error response from daemon: driver failed programming external connectivity on endpoint xxx (xxxxxx):(iptables [--wait -t nat -A DOCKER -p tcp -d 0/0 --dport 80 -j DNAT --to-destination 172.17.0.2:80] failed: (iptables failed: iptables --wait -t nat -A DOCKER -p tcp -d 0/0 --dport 80 -j DNAT --to-destination))

开启防火墙情况下的解决方式

  • 配置开放端口
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --reload
## 配置ipv4转发
vi /etc/sysctl.conf # 改 net.ipv4.ip_forward=1
systemctl restart network # 或 sysctl -p

## 检查
cat /proc/sys/net/ipv4/ip_forward # 或 sysctl net.ipv4.ip_forward

Dockerfile 构建镜像没有权限

COPY test.sh /usr/local/bin/
RUN chmod +x /usr/local/bin/test.sh

执行docker build命令时,会提示operation not permitted 原因是外部环境是root用户,而Dockerfile中的用户是其他用户,因此无法修改文件权限。

改Dockerfile

COPY --chown=root:root test.sh /usr/local/bin/
USER root
RUN chmod +x /usr/local/bin/test.sh
## 切换回原用户
USER your_user
ENTRYPOINT ["/usr/local/bin/test.sh"]

docker-compose 相关

加hosts映射

配extra_hosts

 penpot-backend:
    image: "penpotapp/backend:${PENPOT_VERSION:-latest}"
    restart: always
    extra_hosts:
      - "raw.githubusercontent.com:10.10.2.31"
      - "fonts.gstatic.com:10.10.2.31"

Dockerfile 示例


报错

错误1

Failed to start thread - pthread_create failed (EPERM) for attributes: stacksize: 2048k, guardsize: 64k, detached.
#
# There is insufficient memory for the Java Runtime Environment to continue.
# Cannot create worker GC thread. Out of system resources.
# An error report file with more information is saved as:
# /app/hs_err_pid8.log

将--security-opt seccomp=unconfined参数添加到docker run命令修复了我的问题。

相关问题解决:通过升级docker版本进行解决,但并未亲自尝试。https://github.com/GoogleCloudPlatform/pgadapter/issues/405open in new window

上次编辑于:
贡献者: 李元昊,liyuanhao