当前位置:   article > 正文

Rust-虽然9天过去了,结果是没有结果(Docker容器的端口映射问题)_docker-compose 检测端口是否通 延迟启动 再启动另外一个容器 rust

docker-compose 检测端口是否通 延迟启动 再启动另外一个容器 rust

这篇文章收录于Rust 实战专栏。这个专栏中的相关代码来自于我开发的笔记系统。它启动于是2023年的9月14日。相关技术栈目前包括:Rust,Javascript。关注我,我会通过这个项目的开发给大家带来相关实战技术的分享。


前言

上上周了吧,写了一篇Rust-后端服务调试入坑记,现在看来那个坑根本就不算什么。这次这个坑才是真正的坑。到写文章这一刻,我只是确定了复现这个问题的最简单代码(参考目录验证4:基于Debian:11镜像创建最简单Rust的http服务)。但依然没有找到解决这个问题的办法。

现在把这个问题以及我的验证过程整理出来,以便路过的朋友了解,我相信肯定有大神知道其中的缘由和解决办法。还望各位大神留言赐教。

问题描述

我用Rust语言创建了一个基于Rocket框架(0.5-rc)的Restful服务api(以下简称api),并将其部署在docker容器内调试。
在这里插入图片描述

容器相关描述:

  • 容器名称: notes-api
  • 网络模式: bridge
  • 端口映射: 8003:8000

当容器成功启动后,问题及相关情况描述:

  • 不能从宿住机访问容器中的api,错误码56,命令行: curl http://localhost:8003/api/notes
  • 能够从宿主机上ping通容器,命令行:ping 172.22.0.2
  • 登录到容器中,能够运行命令行成功访问api,命令行: sudo docker exec -it notes-api curl http://localhost:8000/api/notes
  • 如果将网络模式改为host,能够从宿主机上正常访问容器中的api,命令行: curl http://localhost:8000/api/notes

对自己的怀疑

这个问题导致了我对我的Docker使用经验和相关的记忆产生怀疑。难道Docker容器在网络模式为bridge时,不能从宿住机访问?很快,这个问题被否定了,因为我早些时候就在Nginx服务器上配置过多个api的路由,这些api都通过Docker部署在一台服务器上,通过不同的端口来访问部署在这些容器中的api。
即然是早些时候,我的下一个判断是,会不会是因为我现在这台服务器上安装的Docker版本比较新,新版本的Docker Engine是否有一些关于端口映射的设置,导致部署在Docker容器中的api不能够被正常访问呢?

验证之路

1. Python应用

说实话,我的关于用Docker容器部署Api的经验主要来至于Python。我还是第一次将Rust构建的api部署到Docker容器中。因此,我决定用Python创建一个简单的api,将其部署到Docker容器中,看是否能够从宿主机上正常访问。
docker-compose.yml

version: '3'

services:  
  web:
    build:
      context: ./
      dockerfile: Dockerfile
    container_name: python-api
    network_mode: bridge
    ports:
      - 8004:5000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Dockerfile

from python:latest

workdir /app
copy ./main.py ./requirements.txt /app/

run pip install -r requirements.txt
cmd ["python3", "main.py"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

结果:能够从宿主机上成功访问到容器内的api。

因此,是不是镜像本身的问题呢?
我对比了Python镜像和Rust镜像所使用的linux系统。Python镜像使用的是"Debian GNU/Linux 11",Rust使用的是"Debian GNU/Linux 12"。

2. 从Debian:11镜像来创建Rust的镜像

因此,我决定基于Debian:11的镜像来制作Rust的api容器的镜像。
docker-compose.yml

version: '3'
services:  
  web:
    build:
      context: ./
      dockerfile: Dockerfile
    container_name: notes-api1
    network_mode: bridge
    ports:
      - 8004:8000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Dockerfile

from debian:11
run apt update
run apt install -y curl gcc

run curl -s --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 
env PATH=/root/.cargo/bin:$PATH 
run rustup default nightly

workdir /app

copy ./Cargo.toml ./config.toml ./Cargo.lock /app/
copy ./src /app/src

cmd ["cargo", "run"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

结果:不能从宿主机访问Docker容器中的api。
会不会是Python的镜像虽然基于"Debian GNU/Linux 11",但是,在制作镜像的时候有一些设置关于端口映射的设置呢?

3. 从Debian:11镜像来创建Python的镜像

因此,为了排出关于“设置”的猜想,我决定基于Debian:11的镜像来制作Python的api容器的镜像。
如果能够正常访问,说明不存在关于“设置”的猜想。
docker-compose.yml

version: '3'

services:  
  web:
    build:
      context: ./
      dockerfile: Dockerfile
    container_name: my-python2 
    network_mode: bridge
    ports:
      - 8009:5000
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

Dockerfile

from debian:11
run apt update
run apt install -y curl gcc

run apt install python3 pip -y

workdir /app
copy ./main.py ./requirements.txt /app/

env PATH=/usr/local/bin:$PATH

run pip install -r requirements.txt

cmd ["python3", "main.py"]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

结果:能够从宿主机上成功访问到容器内的api。
说明,Python的镜像中,关于端口映射的相关“设置”猜想不存在。

因此,我的视线又回到了Rust项目,我的api是基于Rocket 0.5-rc开发的,是不是因为这个框架的原因呢?

4. 基于Debian:11镜像创建最简单Rust的http服务

因此,我让AI帮我写了一个没有任何依赖的Rust的http服务,然后将这个服务部署到基于Debian:11的镜像中。
docker-compose.yml

version: '3'
services:  
  web:
    build:
      context: ./
      dockerfile: Dockerfile
    container_name: rust-simple-http
    network_mode: bridge
    ports:
      - 8012:8080
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Dockerfile

from debian:11
run apt update
run apt install -y curl gcc

# 安装rust
run curl -s --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y 
env PATH=/root/.cargo/bin:$PATH 

workdir /app

copy ./Cargo.toml ./entry.sh /app/
copy ./src/main.rs /app/src/main.rs

entrypoint ["./entry.sh"]

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

main.rs

use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};

fn handle_client(mut stream: TcpStream) {
    let mut buffer = [0; 1024];
    stream.read(&mut buffer).unwrap();

    let response = "HTTP/1.1 200 OK\r\n\r\nHello, World!";
    stream.write(response.as_bytes()).unwrap();
    stream.flush().unwrap();
}

fn main() {
    let listener = TcpListener::bind("127.0.0.1:8080").unwrap();

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                std::thread::spawn(|| {
                    handle_client(stream);
                });
            }
            Err(e) => {
                eprintln!("Failed to establish a connection: {}", e);
            }
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

entry.sh

#!/bin/bash

echo "cargo build"
cargo build --release
echo "cp bin"
cp target/release/app ./
echo "run app"
./app
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

结果: 不能从宿主机访问Docker容器中的api。
因此,貌似和Rocket 0.5-rc这个框架没有关系。

目前的情况可以归纳为:

  1. Rust创建的api部署到容器中,容器的网络模式为bridge,不能够从宿主机通过容器的端口访问到容器中运行的api。
  2. Python创建的api部署到容器中,容器的网络模式为bridge,能够从宿主机通过容器的端口访问到容器中运行的api。

这个特殊现象是否只存在于Rust和Python之间呢?如果再找一个应用部署到网络模式为bridge的Docker容器中,能否从宿主机通过容器端口访问到容器中运行的应用呢?如果答案是“能”,那么,多少可以得出结论:

Rust创建的api部署到网络模式为bridge的Docker容器中,不能从宿主机通过容器的端口访问到容器中运行的api。

5. 基于Debian:11镜像从apt安装简单的http服务

docker-compose.yml

version: '3'
services:  
  web:
    build:
      context: ./
      dockerfile: Dockerfile
    container_name: simple-http-server
    network_mode: bridge
    ports:
      - 8013:8080
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

Dockerfile

from debian:11
run apt update
run apt install -y curl gcc

run apt install libhttp-server-simple-perl -y

workdir /app
copy entry.sh /app/
entrypoint ["./entry.sh"]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

entry.sh

#!/bin/bash
perl -MHTTP::Server::Simple -e 'my $server = HTTP::Server::Simple->new(); $server->run()'
  • 1
  • 2

结果:能够从宿主机上成功访问到容器内的api。

结论

看来目前只能得出这样的结论了:
Rust创建的api部署到网络模式为bridge的Docker容器中,不能从宿主机通过容器的端口访问到容器中运行的api。

问题整理好了,先放在这里。

后面的工作如何开展

暂时将容器的网络模式设置为host进行调试。如果后面配置的服务很多,估计需要做一个cli来管理各个服务端口的配置(这可是我的强项,哈哈)。

如有问题,欢迎大家留言交流。关注我,后面会在Rust 实战专栏中给大家带来更多关于Rust开发实战的分享。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/640998
推荐阅读
相关标签
  

闽ICP备14008679号