?
This article is included in the Rust practical column. The relevant code in this column comes from the note-taking system I developed. It starts on September 14, 2023. Relevant technology stacks currently include: Rust, Javascript. Follow me, I will share with you relevant practical technologies through the development of this project.
Foreword
Last week, I wrote an article about the pitfalls of Rust-backend service debugging. Now it seems that the pitfalls are nothing at all. This time the pit is the real pit. At the moment of writing this article, I have only identified the simplest code to reproduce this problem (refer to Directory Verification 4: Creating the simplest Rust http service based on the Debian:11 image). But still no solution to this problem has been found.
Now I will sort out this problem and my verification process so that friends passing by can understand it. I believe there must be a master who knows the reason and solution. I hope you can leave a message and give me some advice.
Problem description
I created a Restful service api (hereinafter referred to as api) based on the Rocket framework (0.5-rc) using the Rust language and deployed it in a docker container for debugging.
Container related description:
- Container name: notes-api
- Network mode: bridge
- Port mapping: 8003:8000
When the container is successfully started, the problem and related situations are described:
- The api in the container cannot be accessed from the host machine, error code 56, command line: curl http://localhost:8003/api/notes
- Able to ping the container from the host, command line: ping 172.22.0.2
- Log in to the container and be able to run the command line to successfully access the API. Command line: sudo docker exec -it notes-api curl http://localhost:8000/api/notes
- If you change the network mode to host, you can access the API in the container normally from the host. Command line: curl http://localhost:8000/api/notes
Doubts about oneself
This question caused me to question my Docker experience and related memory. Is it possible that the Docker container cannot be accessed from the host machine when the network mode is bridge? Soon, this problem was denied, because I had configured routing for multiple APIs on the Nginx server earlier. These APIs were deployed on one server through Docker and accessed through different ports deployed in these containers. api.
Since it was earlier, my next judgment was whether it was because the Docker version installed on my current server was relatively new, and whether the new version of Docker Engine had some settings regarding port mapping, causing the deployment in Docker What if the API in the container cannot be accessed normally?
The road to verification
1. Python application
To be honest, my experience with using Docker containers to deploy APIs mainly comes from Python. This is my first time deploying a Rust-built API into a Docker container. Therefore, I decided to create a simple api in Python and deploy it into a Docker container to see if it could be accessed normally from the host machine.
docker-compose.yml
version: '3' services: web: build: context: ./ dockerfile: Dockerfile container_name: python-api network_mode: bridge ports: - 8004:5000
Dockerfile
from python:latest workdir /app copy ./main.py ./requirements.txt /app/ run pip install -r requirements.txt cmd ["python3", "main.py"]
Result: The API in the container can be successfully accessed from the host.
So, is it a problem with the mirror itself?
I compared the Linux systems used by the Python image and the Rust image. The Python image uses “Debian GNU/Linux 11”, and the Rust image uses “Debian GNU/Linux 12”.
2. Create a Rust image from the Debian:11 image
Therefore, I decided to make an image of the Rust api container based on the Debian:11 image.
docker-compose.yml
version: '3' services: web: build: context: ./ dockerfile: Dockerfile container_name: notes-api1 network_mode: bridge ports: - 8004:8000
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"]
Result: The api in the Docker container cannot be accessed from the host machine.
Could it be that although the Python image is based on “Debian GNU/Linux 11”, there are some settings regarding port mapping when making the image?
3. Create a Python image from the Debian:11 image
Therefore, in order to eliminate the guesswork about “settings”, I decided to make an image of the Python api container based on the Debian:11 image.
If it can be accessed normally, it means there is no guess about “settings”.
docker-compose.yml
version: '3' services: web: build: context: ./ dockerfile: Dockerfile container_name: my-python2 network_mode: bridge ports: - 8009:5000
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"]
Result: The API in the container can be successfully accessed from the host.
Note that in the Python image, the relevant “settings” conjecture about port mapping does not exist.
Therefore, my attention returned to the Rust project. My api was developed based on Rocket 0.5-rc. Is it because of this framework?
4. Create the simplest Rust http service based on Debian:11 image
Therefore, I asked AI to help me write a Rust http service without any dependencies, and then deployed this service to a Debian:11-based image.
docker-compose.yml
version: '3' services: web: build: context: ./ dockerfile: Dockerfile container_name: rust-simple-http network_mode: bridge ports: - 8012:8080
Dockerfile
from debian:11 run apt update run apt install -y curl gcc # Install 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"]
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( & amp;mut buffer).unwrap(); let response = "HTTP/1.1 200 OK\r\\ \r\\ Hello, 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); } } } }
entry.sh
#!/bin/bash echo "cargo build" cargo build --release echo "cp bin" cp target/release/app ./ echo "run app" ./app
Result: The API in the Docker container cannot be accessed from the host machine.
Therefore, it seems to have nothing to do with the Rocket 0.5-rc framework.
The current situation can be summarized as:
- The API created by Rust is deployed into the container. The network mode of the container is bridge. The API running in the container cannot be accessed from the host through the port of the container.
- The API created by Python is deployed into the container. The network mode of the container is bridge. The API running in the container can be accessed from the host through the port of the container.
Does this special phenomenon only exist between Rust and Python? If we find another application and deploy it to a Docker container with the network mode being bridge, can we access the application running in the container from the host through the container port? If the answer is “yes”, then we can more or less conclude:
The api created by Rust is deployed into a Docker container with a bridge network mode. The api running in the container cannot be accessed from the host through the port of the container.
5. Install a simple http service from apt based on Debian:11 image
docker-compose.yml
version: '3' services: web: build: context: ./ dockerfile: Dockerfile container_name: simple-http-server network_mode: bridge ports: - 8013:8080
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"]
entry.sh
#!/bin/bash perl -MHTTP::Server::Simple -e 'my $server = HTTP::Server::Simple->new(); $server->run()'
Result: The API in the container can be successfully accessed from the host.
Conclusion
It seems that the only conclusion we can draw is this:
The api created by Rust is deployed into a Docker container with a bridge network mode. The api running in the container cannot be accessed from the host through the port of the container.
The questions have been sorted out, so I’ll put them here first.
How to carry out the subsequent work
Temporarily set the container’s network mode to host for debugging. If there are many services configured later, you may need to create a cli to manage the configuration of each service port (this is my strength, haha).
If you have any questions, please leave a message to communicate. Follow me and I will bring you more sharing about the actual development of Rust in the Rust practical column.
?