1

I do not know how to achieve that. Now all the ports are exposed to the host machine but I just want to expose one container port (80), not the other (8080). Here is the docker-compose file:

---
version: "3.9"

services:
  app:
    image: sandbox/app
    container_name: app
    volumes:
      - ./src/app:/app/
    expose:
      - "8080"
    restart: unless-stopped
    networks:
      custom-net:
        ipv4_address: 10.0.0.7
  web_server:
    image: nginx:latest
    container_name: proxy
    ports:
      - "80:80"
    networks:
      custom-net:
        ipv4_address: 10.0.0.6
networks:
  custom-net:
    name: custom-net
    driver: bridge
    ipam:
      driver: default
      config:
        - subnet: 10.0.0.0/8

If I run from the local machine nmap 10.0.0.6, it shows port as open in port 80. This container exposure is the desired one. But when I run nmap 10.0.0.7, it also shows as open 8080 port, how it could be that one? Checking some stackoverflow thread, ports is defined like that:

Expose ports. Either specify both ports (HOST:CONTAINER), or just the container port (a random host port will be chosen).

and expose:

Expose ports without publishing them to the host machine - they’ll only be accessible to linked services. Only the internal port can be specified.

Do I miss some network concepts or do I have wrong docker-compose file?

DustInTheSilence
  • 575
  • 1
  • 7
  • 15

1 Answers1

1

You must be on a native-Linux host. If you happen to know the Docker-internal IP addresses, and you're on a native-Linux host, then you can always connect to a container using those addresses; you can't prevent this (without iptables magic) but it's also not usually harmful. This trick doesn't work in other environments (on MacOS or Windows hosts, or if Docker is in a Linux VM, or from a different host from the container) and it's much more portable to connect only to containers' published ports:.

You should be able to use a much simpler Compose file. Delete all of the networks: blocks and the expose: blocks. You also do not need container_name:, and you should not need to inject code using volumes:. Trimming out all of the unnecessary options leaves you with

version: '3.8' # last version supported by standalone docker-compose tool
services:
  app:
    image: sandbox/app # may want `build: .` _instead of_ this line
    restart: unless-stopped
  web_server:
    image: nginx:latest # needs some custom configuration?
    ports:
      - "80:80"

That should literally be the entire file.

From outside Docker but on the same machine, http://localhost:80 matches the first ports: of the web_server container, so forwards to the second ports:, on which the Nginx server is listening. The Nginx configuration should include a line like proxy_pass http://app:8080 which will forward to the application container.

Compared to your original file:

  • expose: is an artifact of first-generation Docker networking. In a Compose file it does absolutely nothing at all and it's always safe to delete it.
  • Connections between containers (where web_server uses app as a host name) connect directly to the specified port; they do not use or require expose: or ports: settings, and they ignore ports: if they're present.
  • Compose assigns container names on its own, and there are docker-compose CLI equivalents to almost all Docker commands that can figure out the right mapping. You don't need to manually specify container_name:.
  • Docker automatically assigns IP addresses to containers. These are usually an internal implementation detail; it's useful to know that containers do have their own IP addresses (and so you can have multiple containers that internally listen on the same port) but you never need to know these addresses, look them up, or manually specify them.
  • Compose automatically creates a network named default for you and attaches containers to it, so in most common cases you don't need networks: at all.

Networking in Compose in the Docker documentation describes how to make connections between containers (again, you do not need to know the container-private IP addresses). Container networking discusses these concepts separately from Compose.

David Maze
  • 130,717
  • 29
  • 175
  • 215
  • Actually, I am in a Ubuntu VM not in a native-linux host when I exec `nmap` command. The reason to use `image` is just for tagging purposes. The usage of the `volume`, it is because the container is the development environment so, once I change some code in the local machine, I want that it would be reflected in the container and re-run the app. If it would be in an other environment, I will just use the COPY directive in the *Dockerfile*. – DustInTheSilence Jul 12 '22 at 04:08
  • Lastly, the reason to create a custom network is because I might hook up more containers in that network. I just want to let the access to the nginx container inside app and other requests block. Would it be possible to use container name to block the request as docker engine will resolve that name through an IP? – DustInTheSilence Jul 12 '22 at 04:08
  • btw thanks for that extensive answer, many thanks!! – DustInTheSilence Jul 12 '22 at 04:09