31
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

docker stack deploy で立ち上げたサービスのポートが分からん件

Posted at

INTRODUCING EXPERIMENTAL DISTRIBUTED APPLICATION BUNDLES
https://blog.docker.com/2016/06/docker-app-bundle/

とか

What's New in Docker 1.12 (June 20, 2016) by Mike Goelzer & Andrea Luzzardi
http://www.slideshare.net/MikeGoelzer/whats-new-in-docker-112-goelzerluzzardi

とか

Docker Stacks and Distributed Application Bundles
https://github.com/docker/docker/blob/master/experimental/docker-stacks-and-bundles.md

あたりを見ると分かるんですが、 docker stack コマンドが新しくできまして、この stack をつかうことで「サービスのまとまり」を可搬性高く管理することができるようになります。
例えばマイクロサービスな開発をしてるときに、自分の担当外のサービスとの連携をローカル環境で試したい、みたいな状況のときに、その担当外のサービスの構成がかいてある foo_service.dab ( 現状だと .dsb 形式になってるけど間違いなのでそのうち .dab になるはず )を受け取るだけで、自分のローカルにそのサービスの環境を構築できる、みたいな未来が待ってるわけです。

現在でも docker-compose.yml を使えば似たようなことができはしますが、 docker-compose だとポートが重複してたりするので、自分のサービスとそのサービスの両方が80をLISTENしてるような状況の場合に問題が発生するわけですが、この docker stack を使うと、その辺のことを何かいい感じにしてくれます。

使い方は比較的簡単で

  • docker-compose bundle コマンドを実行して hoge.dab ファイルを作る
  • docker stack deploy hogehoge.dab ファイルが読まれてサービスが展開される
  • すごい

これだけなんですが、 docker-compose の感覚でいるといくつか失敗します。

links未対応とその解決策

まず、 docker-compose.yml が対応している構文の全てに対応しているわけじゃないです。
実際に docker-compose bundle を実行すると以下のようなWARNINGがでてくるはずです。

$ docker-compose bundle
WARNING: Unsupported key 'network_mode' in services.web - ignoring
WARNING: Unsupported key 'links' in services.web - ignoring
WARNING: Unsupported key 'network_mode' in services.app - ignoring

つまり、 docker-compose のウリである勝手にhosts書き換えちゃいましたテヘペロ links が使えないという、超残念仕様なのであります。

「え・・・じゃぁどうすんの・・・・コンテナ間のIPとか分からんよ・・・・」と思うんですが、そこは流石に対策があり、 docker-compose.yml のルートのコンテナ名の代わりに、サービス名でアクセスすることができます。

たとえば以下みたいな docker-compose.yml があったとして(中身は適当なので雰囲気を感じ取ってください)

web:
  image: nginx
  ports:
    - 80:80
  expose:
    - 80
  links:
    - app:php
app:
  image: php
  expose:
    - 9000

nginx->appへの通信は tcp://php:9000/ みたいな感じで行えるのが docker-compose なわけですが、これに対して docker stack の場合は(仮にstack名がpiyoだったとすると)、 tcp://piyo_app:9000/ でアクセスできるようになってます。

つまり app:php のようなかたちで links の設定はできませんが、この例でいうと(仮にstack名がpiyoだったとすると) piyo_web というサービスと piyo_app というサービスが作られ、そのサービス名がDocker内部のDNSに登録されている、というようなイメージです。

立ち上がったStackのポートが分からん件

というわけで (仮にpiyoという名前の) stack を docker stack deploy piyo みたいにすると、内部でいい感じに piyo_web piyo_app みたいに スタック名_サービス名 形式のサービスが立ち上がります。

これで大勝利かと思いきや、先ほど申したとおり、stackはポートの重複が起こらないように何かいい感じにしてくれるので、たとえ以下のようにまるで80をLISTENしてそうな雰囲気でもLISTENしてくれません。

piyo.dsbの例(ふんいき)
{
  "services": {
    "app": {
      "Image": "xxxxxx", 
      "Networks": [], 
      "Ports": [
        {
          "Port": 9000, 
          "Protocol": "tcp"
        }
      ]
    }, 
    "web": {
      "Image": "yyyyyy", 
      "Networks": [], 
      "Ports": [
        {
          "Port": 80, 
          "Protocol": "tcp"
        }
      ]
    }
  }, 
  "version": "0.1"
}

「OKなるほどね。んじゃ何かいい感じのGatewayが設定されてるんだね。きっとそうだね。」
と思っていろいろ調べたけど全然見当たらなくて、ぐぬぬったんですが、何のことはなくて80じゃないPortをLISTENしてました。

というわけで困ったときのinspectコマンドですので、docker service inspect piyo_web みたいな感じでinspectすると以下みたいな結果が返ってきます。(内容は適当ですので雰囲気)

$ docker service inspect mystack_web
[
    {
        "ID": "abcdefghijklmn",
        "Version": {
            "Index": 18
        },
        "CreatedAt": "2016-06-25T08:20:01.126619833Z",
        "UpdatedAt": "2016-06-25T08:20:01.141201786Z",
        "Spec": {
            "Name": "piyo_web",
            "Labels": {
                "com.docker.stack.namespace": "piyo"
            },
            "TaskTemplate": {
                "ContainerSpec": {
                    "Image": "yyyyyy",
                }
            },
            "Mode": {
                "Replicated": {
                    "Replicas": 1
                }
            },
            "EndpointSpec": {
                "Mode": "vip",
                "Ports": [
                    {
                        "Protocol": "tcp",
                        "TargetPort": 80
                    }
                ]
            }
        },
        "Endpoint": {
            "Spec": {},
            "Ports": [
                {
                    "Protocol": "tcp",
                    "TargetPort": 80,
                    "PublishedPort": 30001
                }
            ],
            "VirtualIPs": [
                {
                    "NetworkID": "abcdefg",
                    "Addr": "10.255.0.6/16"
                }
            ]
        }
    }
]

見るべきは EndpointPortsPublishedPort で、外に露出してるポートが30001なことがわかります。このポートは30001固定なのではなく、docker taskさんがdeploy時にいい感じに決めてくれるので、例えばこれと同じ設定のstackを複数個デプロイしてもポートの重複は起こりません。

というわけで、今回の例でいうと http://localhost:30001/ にアクセスすることで piyo_web にアクセスすることができました。

サービスのPublishedPortを取得するコマンド

GoTemplate が独特で覚えられる気がしないんでメモがてら。

docker service inspect -f '{{with index .Endpoint.Ports 0}}{{.PublishedPort}}{{end}}' piyo_web

こんな感じで、たぶんサービスのPublishedPortの取得はできると思うので、Dockerネットワークの外から何かしようとした場合も、動的に処理できそうな感じはしてます。

感想

マジまだドキュメントとか無いようなもんだし、いろいろ辛いけど夢はあるので、全てのサービスが *.dab ファイルを持つ時代がくるといいなと思いました。まる。

あとたぶんデプロイが楽になるんじゃって噂もあるんだけど、それにしては *.dab の構文が弱い(レプリカ数とかの設定どうすんの・・・?)んで、早く整備されるといいなと思いましたとさ。

31
30
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
31
30

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?