• Docker ambassador pattern

    Ambassador

    При проектировании информационной системы очень важна ясность. Нет ничего лучше, чем элегантно построенное приложение, в котором нет лишних деталей, в том числе — лишних связей между компонентами.

    К счастью, Docker позволяет изолировать процессы друг от друга и организовывать связь между ними посредством заранее указанных портов и общих директорий. В большинстве случаев, для успеха мероприятия необходима только однонаправленная связь контейнеров, например: веб-сервер использует php-fpm, либо php-fpm использует базу данных. В этом простом случае, никаких проблем не возникает, установить такую связь между несколькими контейнерами, используя Docker Compose, очень просто:

    # ~/docker-apps/testapp/test.yml
    fpm:
      build: ./build/fpm
    nginx:
      build: ./build/nginx
      links:
        - fpm

    Запускаем. Как видно, всё работает:

    docker-compose -f test.yml up -d
    Creating testapp_fpm_1...
    Creating testapp_nginx_1...
    
    docker-compose ps
    
            Name                     Command                State      Ports   
    -------------------------------------------------------------------------     
    testapp_fpm_1          /usr/sbin/php5-fpm -F           Up         9000/tcp 
    testapp_nginx_1        /usr/sbin/nginx                 	Up         80/tcp   

    Из контейнера testappnginx1 можно адресовать testappfpm1 посредством прописанных самим Docker хостов. Посмотрим на /etc/hosts:

    docker exec testapp_nginx_1 cat /etc/hosts
    ...
    172.17.0.5	testapp_fpm_1 2bae5bf71e09
    172.17.0.5	fpm 2bae5bf71e09 testapp_fpm_1
    172.17.0.5	fpm_1 2bae5bf71e09 testapp_fpm_1

    В принципе, возможна ситуация, когда оба связанных процесса должны знать друг о друге. Например, если поднят Selenium: необходимо из него иметь доступ до веб-сервера, чтобы исполнять тестовые задания; но также от веб-сервера нужен доступ до самого Selenium, чтобы запускать эти задания посредством BDD-фреймворка. Для ясности примера относительно предыдущего, допустим, что нам нужен доступ из nginx в php-fpm и наоборот.

    # ~/docker-apps/testapp/test2.yml
    fpm:
      build: ./build/fpm
      links:
        - nginx
    nginx:
      build: ./build/nginx
      links:
        - fpm

    Запускаем?

    docker-compose -f test2.yml up -d
    Circular import between fpm and nginx

    К счастью, решение есть. Невозможно добавить перекрестные ссылки только на этапе старта, добавить их после старта — никто запретить не может. На помощь придет ambassador — образ процесса-прокси, единственная задача которого — создание перекрестных ссылок между контейнерами.

    # ~/docker-apps/testapp/test3.yml
    fpm:
      build: ./build/fpm
      links:
        - ambassador:nginx
    nginx:
      build: ./build/nginx
      links:
        - ambassador:fpm
    ambassador:
      image: cpuguy83/docker-grand-ambassador
      volumes:
        - "/var/run/docker.sock:/var/run/docker.sock"
      command: "-name testapp_fpm_1 -name testapp_nginx_1"

    Запускаем:

    docker-compose -f test3.yml up -d
    Creating testapp_ambassador_1...
    Creating testapp_nginx_1...
    Creating testapp_fpm_1...

    Что произошло?

    docker exec astgo_nginx_1 cat /etc/hosts
    ...
    172.17.0.11	ambassador_1 f3437e869aff testapp_ambassador_1
    172.17.0.11	testapp_ambassador_1 f3437e869aff
    172.17.0.11	fpm f3437e869aff testapp_ambassador_1

    Как видно, Docker добавил стандартные записи для ambassador, а уже ambassador добавил запись, указывающую на контейнер fpm. Стоит заметить, не напрямую — а через тот же самый ambassador.