Comunicação entre vários projetos de docker-componha

253

Eu tenho dois docker-compose.ymlarquivos separados em duas pastas diferentes:

  • ~/front/docker-compose.yml
  • ~/api/docker-compose.yml

Como posso garantir que um contêiner frontpossa enviar solicitações para um contêiner api?

Eu sei que essa --default-gatewayopção pode ser definida docker runpara um contêiner individual, de modo que um endereço IP específico possa ser atribuído a esse contêiner, mas parece que essa opção não está disponível durante o uso docker-compose.

Atualmente, acabo fazendo um docker inspect my_api_container_ide olha o gateway na saída. Funciona, mas o problema é que esse IP é atribuído aleatoriamente, então não posso confiar nele.

Outra forma dessa pergunta pode ser:

  • Posso atribuir um endereço IP fixo a um contêiner específico usando o docker-compose?

Mas no final o que eu estou cuidando é:

  • Como dois projetos diferentes de composição de encaixe podem se comunicar?
Jivan
fonte
4
Eu apenas olhei para isso hoje novamente. Os desenvolvedores finalmente cederam e permitiram nomes arbitrários de rede. Usando o arquivo de composição versão 3.5, você pode especificar um nome para a rede padrão sob a tecla 'redes'. Isto irá criar uma rede nomeado sem o habitual prefixo do nome do projeto se ele não existe ..
cstrutton

Respostas:

325

Você só precisa garantir que os contêineres com os quais você deseja conversar estejam na mesma rede. As redes são uma construção de janela de encaixe de primeira classe e não específica para compor.

# front/docker-compose.yml
version: '2'
services:
  front:
    ...
    networks:
      - some-net
networks:
  some-net:
    driver: bridge

...

# api/docker-compose.yml
version: '2'
services:
  api:
    ...
    networks:
      - front_some-net
networks:
  front_some-net:
    external: true

Nota: A rede do seu aplicativo recebe um nome com base no "nome do projeto", que se baseia no nome do diretório em que ele vive, nesse caso, um prefixo front_foi adicionado

Eles podem conversar entre si usando o nome do serviço. De frontvocê pode fazer ping apie vice-versa.

johnharris85
fonte
1
Jivan, isso não é solução. Seus contêineres não precisam saber nada sobre o host ou serem manipulados dessa maneira. Minha resposta foi bem curta, mas atualizei com mais detalhes.
precisa
3
Robert Moskal somente se você procurar o ip do host do docker nos contêineres. É melhor que eles se comuniquem em uma rede comum definida pelo docker.
precisa
2
Observe que o prefixo "front_" da rede é criado automaticamente a partir da pasta em que está sendo executado. Portanto, se o seu primeiro arquivo docker-compose estivesse localizado em "example / docker-compose.yml", seria chamado "example_default".
AngryUbuntuNerd
7
Você também pode fornecer um nome para uma rede usando a namepropriedade, o que desativará o prefixo automático com o nome do projeto. Em seguida, qualquer um dos projetos pode usar essa rede e criá-la automaticamente, se ainda não existir.
steveb
2
@SteveB - Observe que a propriedade name funciona apenas a partir dos arquivos docker-compose versão 3.5 e superior
kramer65
78

Apenas uma pequena adição à ótima resposta de @ johnharris85, quando você está executando um arquivo de composição do docker, " default" uma rede é criada para que você possa adicioná-lo ao outro arquivo de composição como uma rede externa:

# front/docker-compose.yml 
version: '2' 
  services:   
    front_service:
    ...

...

# api/docker-compose.yml
version: '2'
services:
  api_service:
    ...
    networks:
      - front_default
networks:
  front_default:
    external: true

Para mim, essa abordagem foi mais adequada porque eu não possuía o primeiro arquivo de composição de encaixe e queria me comunicar com ele.

Tal Joffe
fonte
apenas vagando da maneira correta para atribuir IP estático para esta rede externa. I manged para fazê-lo dentro de services:tag, a sintaxe seria networks:então aninhados front_default:(remover o "-") e, então, o ninho de um IP estático:ipv4_address: '172.20.0.44'
júnior Mayhé
76

ATUALIZAÇÃO: A partir da versão 3.5 do arquivo de composição:

Isso agora funciona:

version: "3.5"
services:
  proxy:
    image: hello-world
    ports:
      - "80:80"
    networks:
      - proxynet

networks:
  proxynet:
    name: custom_network

docker-compose up -dingressará em uma rede chamada 'custom_network'. Se não existir, será criado!

root@ubuntu-s-1vcpu-1gb-tor1-01:~# docker-compose up -d
Creating network "custom_network" with the default driver
Creating root_proxy_1 ... done

Agora, você pode fazer isso:

version: "2"
services:
  web:
    image: hello-world
    networks:
      - my-proxy-net
networks:
  my-proxy-net:
    external:
      name: custom_network

Isso criará um contêiner que estará na rede externa.

Ainda não encontrei nenhuma referência nos documentos, mas funciona!

cstrutton
fonte
Você precisa iniciar os dois serviços em uma ordem específica? Você pode iniciar qualquer um deles, e o primeiro criará a rede e o segundo ingressará nela?
precisa
4
O primeiro serviço (proxy acima) cria a rede. A sintaxe no segundo exemplo se junta a ela.
Cstrutton
2
@slashdottir Você não pode marcar a rede como externa no segundo serviço e ela será criada se ainda não existir.
SteveB
2
Isso funciona. Acabei de criar uma gota de DO com o último docker de composição. Eu editei o exemplo para um exemplo de trabalho real.
Cstrutton 15/05/19
1
No meu caso, isso acabou sendo uma solução mais adequada do que a resposta aceita. O problema com a rede externa era que era necessário iniciar contêineres em ordem predefinida. Para o meu cliente, isso não era aceitável. Uma rede nomeada (desde 3.5) acabou sendo a solução perfeita. Obrigado.
ygor
24

Todos os contêineres de apipodem ingressar na rede front padrão com a seguinte configuração:

# api/docker-compose.yml

...

networks:
  default:
    external:
      name: front_default

Consulte o guia de composição do docker: usando uma rede pré-existente (veja na parte inferior)

dedek
fonte
12

As informações das postagens anteriores estão corretas, mas não têm detalhes sobre como vincular contêineres, que devem ser conectados como "external_links".

Espero que este exemplo fique mais claro para você:

  • Suponha que você tenha app1 / docker-compose.yml, com dois serviços (svc11 e svc12), e app2 / docker-compose.yml com mais dois serviços (svc21 e svc22) e suponha que você precise se conectar de maneira cruzada:

  • svc11 precisa se conectar ao contêiner de svc22

  • O svc21 precisa se conectar ao contêiner do svc11.

Portanto, a configuração deve ser assim:

este é app1 / docker-compose.yml:


version: '2'
services:
    svc11:
        container_name: container11
        [..]
        networks:
            - default # this network
            - app2_default # external network
        external_links:
            - container22:container22
        [..]
    svc12:
       container_name: container12
       [..]

networks:
    default: # this network (app1)
        driver: bridge
    app2_default: # external network (app2)
        external: true

este é app2 / docker-compose.yml:


version: '2'
services:
    svc21:
        container_name: container21
        [..]
        networks:
            - default # this network (app2)
            - app1_default # external network (app1)
        external_links:
            - container11:container11
        [..]
    svc22:
       container_name: container22
       [..]

networks:
    default: # this network (app2)
        driver: bridge
    app1_default: # external network (app1)
        external: true
Daniel Blanco
fonte
6

Desde o Compose 1.18 (especificação 3.5), você pode simplesmente substituir a rede padrão usando seu próprio nome personalizado para todos os arquivos do Compose YAML necessários. É tão simples quanto anexar o seguinte a eles:

networks:
  default:
    name: my-app

O acima pressupõe que você versionconfigurou como 3.5(ou acima, se não o descontinuar em 4+).

Outras respostas apontaram o mesmo; este é um resumo simplificado.

emyller
fonte
2

Eu garantiria que todos os contêineres estivessem docker-composena mesma rede, compondo-os juntos ao mesmo tempo, usando:

docker compose --file ~/front/docker-compose.yml --file ~/api/docker-compose.yml up -d
Nauraushaun
fonte
Isso me permitirá, por exemplo, fazer um linkou depends_onde um contêiner da frente para um contêiner de API?
Jivan
na verdade, quando eu faço o que você sugere, respostas Docker-compor quer build path ~/front/api either does not exist or is not accessibleou com o contrário,build path ~/api/front either does not exist or is not accessible
Jivan
1
Se você está compondo ao mesmo tempo, não precisa. Uma rede será criada com todos os seus contêineres, todos eles poderão se comunicar através do nome do serviço do arquivo de composição ( não o nome do contêiner).
Nauraushaun
Pode ser mais fácil se os dois arquivos de composição estiverem na mesma pasta. Mas não acho que seja necessário - acho que deve funcionar de qualquer maneira.
Nauraushaun
2
Esta solução não funcionar, ver meu comentário sobre este tópico: github.com/docker/compose/issues/3530#issuecomment-222490501
johnharris85
2

ATUALIZAÇÃO: A partir da versão 3.5 do arquivo de composição:

Me deparei com o problema semelhante e o resolvi adicionando uma pequena alteração em um dos meus projetos docker-compose.yml.

Por exemplo, temos duas APIs scoringe ner. ScoringA API precisa enviar uma solicitação à nerAPI para processar a solicitação de entrada. Para fazer isso, ambos devem compartilhar a mesma rede.

Nota: Cada contêiner possui sua própria rede, criada automaticamente no momento da execução do aplicativo na janela de encaixe. Por exemplo, a rede ner api será criada como ner_defaulte a pontuação da rede api será nomeada como scoring default. Esta solução funcionará para a versão: '3'.

Como no cenário acima, minha API de pontuação deseja se comunicar com a ner api, então adicionarei as seguintes linhas. O que significa que sempre que eu crio o contêiner para ner api, ele é automaticamente adicionado à rede scoring_default.

networks:
  default:
      external:
        name: scoring_default

ner / docker-compose.yml

version: '3'
services:
  ner:
    build: .
    ...

networks:
  default:
      external:
        name: scoring_default

scoring / docker-compose.yml

version: '3'
services:
  api:
    build: .
    ...

Podemos ver isso como os contêineres acima agora fazem parte da mesma rede chamada scoring_defaultusando o comando:

docker inspeciona scoring_default

{
    "Name": "scoring_default",
        ....
    "Containers": {
    "14a6...28bf": {
        "Name": "ner_api",
        "EndpointID": "83b7...d6291",
        "MacAddress": "0....",
        "IPv4Address": "0.0....",
        "IPv6Address": ""
    },
    "7b32...90d1": {
        "Name": "scoring_api",
        "EndpointID": "311...280d",
        "MacAddress": "0.....3",
        "IPv4Address": "1...0",
        "IPv6Address": ""
    },
    ...
}
Nomiluks
fonte
1

Você pode adicionar um .envarquivo em todos os seus projetos que contêm COMPOSE_PROJECT_NAME=somename.

COMPOSE_PROJECT_NAME substitui o prefixo usado para nomear recursos, pois todos os seus projetos usarãosomename_default como sua rede, possibilitando que os serviços se comuniquem uns com os outros como estavam no mesmo projeto.

Nota: você receberá avisos para contêineres "órfãos" criados a partir de outros projetos.

Exagone313
fonte
0
version: '2'
services:
  bot:
    build: .
    volumes:
      - '.:/home/node'
      - /home/node/node_modules
    networks:
      - my-rede
    mem_limit: 100m
    memswap_limit: 100m
    cpu_quota: 25000
    container_name: 236948199393329152_585042339404185600_bot
    command: node index.js
    environment:
      NODE_ENV: production
networks:
  my-rede:
    external:
      name: name_rede_externa
Pedro
fonte
0

Para usar outra rede de docker-compose, basta fazer o seguinte (para compartilhar redes entre docker-compose):

  1. Execute o primeiro projeto de docker-compose up -d
  2. Localize o nome da rede da primeira janela de encaixe-composição por: docker network ls(Ele contém o nome do projeto do diretório raiz)
  3. Em seguida, use esse nome por essa estrutura abaixo no segundo arquivo de composição de encaixe.

segundo docker-compose.yml

version: '3'
services:
  service-on-second-compose:  # Define any names that you want.
    .
    .
    .
    networks:
      - <put it here(the network name that comes from "docker network ls")>

networks:
  - <put it here(the network name that comes from "docker network ls")>:
    external: true
Ali Hallaji
fonte
0

Outra opção é apenas executar o primeiro módulo com o 'docker-compose', verificar o ip relacionado ao módulo e conectar o segundo módulo à rede anterior, como externo, e apontar o ip interno.

exemplo app1 - nova rede criada nas linhas de serviço, marque como externo: true na parte inferior app2 - indique a "nova rede" criada pelo app1 quando subir, marque como externo: verdadeiro na parte inferior e defina na configuração para conectar, o ip que app1 tem nessa rede.

Com isso, você deve poder conversar um com o outro

* esse caminho é apenas para o foco no teste local, para não fazer uma configuração complexa demais ** eu sei que é muito 'remendado', mas funciona para mim e acho tão simples que alguns outros podem tirar proveito disso

leonardo rey
fonte
0

Se você é

  • tentando se comunicar entre dois contêineres de projetos diferentes de composição de encaixe e não deseja usar a mesma rede (porque digamos que eles teriam um contêiner PostgreSQL ou Redis na mesma porta e você prefere não alterar essas portas e não usá-lo na mesma rede)
  • desenvolvendo localmente e deseja imitar a comunicação entre dois projetos de composição do docker
  • executando dois projetos de composição de encaixe no host local
  • desenvolvendo aplicativos Django especialmente ou API do Django Rest Framework (drf) e executando o aplicativo dentro do contêiner em alguma porta exposta
  • ficando Connection refusedao tentar se comunicar entre dois contêineres

E você quer

  • contêiner se api_acomunicar com api_b(ou vice-versa) sem a mesma "rede docker"

(exemplo abaixo)

você pode usar o "host" do segundo contêiner como o IP do seu computador e a porta mapeada de dentro do contêiner do Docker. Você pode obter o IP do seu computador com este script (em: Localizando endereços IP locais usando o stdlib do Python ):

import socket
def get_ip():
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    try:
        # doesn't even have to be reachable
        s.connect(('10.255.255.255', 1))
        IP = s.getsockname()[0]
    except:
        IP = '127.0.0.1'
    finally:
        s.close()
    return IP

Exemplo:

project_api_a/docker-compose.yml:

networks:
  app-tier:
    driver: bridge

services:
  api:
    container_name: api_a
    image: api_a:latest
    depends_on:
      - postgresql
    networks:
      - app-tier

dentro api_a contêiner que você está executando no aplicativo Django: manage.py runserver 0.0.0.0:8000

e o segundo docker-compose.yml de outro projeto:

project_api_b/docker-compose-yml :

networks:
  app-tier:
    driver: bridge

services:
  api:
    container_name: api_b
    image: api_b:latest
    depends_on:
      - postgresql
    networks:
      - app-tier

dentro api_b contêiner que você está executando no aplicativo Django: manage.py runserver 0.0.0.0:8001

E tentando conectar-se do contêiner api_aao api_bURL deapi_b contêiner será: http://<get_ip_from_script_above>:8001/

Pode ser especialmente valioso se você estiver usando ainda mais de dois (três ou mais) projetos de composição de encaixe e for difícil fornecer uma rede comum para tudo isso - é uma boa solução e solução alternativa

Rafał
fonte