分散システムメモ

# 分散システム
分散システムを構築する場合、Python Go Mysql Redis 多様なプロトコルがある
それぞれのサーバが稼働しているか、安全かは信頼することができない
常に落ちている可能性を考慮すべきだ

自分のサービスには興味を持ち、他のサービスには関心を持たない分散が重要

## サービスメッシュとサイドカーのRailsアプリケーションへの適応
例えば、一般的なRailsアプリケーションにおいてサービスメッシュを適用する場合を考えてみましょう。

この場合、アプリケーションのコンテナ（appコンテナと呼ぶことにしましょう）とは別に、サービスメッシュが提供するサイドカープロキシ（Envoy Proxyなど）が配置されます。

サイドカープロキシはappコンテナと一緒にPod内で動作し、appコンテナが発生させる全てのネットワークトラフィック（発信もしくは受信）を透過的にインターセプトします。

これにより、appコンテナは他のサービスと直接通信することなく、自身のローカルホスト上のサイドカープロキシと通信します。
サイドカープロキシがこれをサービスメッシュ内の他のすべてのプロキシと通信するための中継として機能します。

このセットアップにより、トラフィックのルーティング、ロードバランシング、エラーリトライ、ログ記録、メトリクス収集、トレーシング、セキュリティポリシーの強制など、ネットワーク関連の多くの事項をappコンテナから分離して、サービスメッシュの機能として提供することが可能になります。

## Envoy （DataPlane)
"Envoy"は、高性能でプログラム可能な、サービスメッシュやマイクロサービス設計に適しているモダンなエッジとサービスプロキシです。Envoyは、独立したプロキシとしても、あるいはサービスメッシュのデータプレーンとしても利用できます。

### アプリケーションコンテナ
Railsアプリケーション

### Data Plane
サービスメッシュアーキテクチャで、実際のデータ通信（リクエストとレスポンス）を処理する部分。Data Plane内では通常、サービスプロキシまたはサイドカープロキシが動作します。

### Service Proxy
特定のサービスのインスタンス間で通信を行い、調整する役割を果たします。

### Sidecar Proxy
アプリケーションコンテナと共にデプロイされ、そのコンテナの全ネットワークトラフィックを透過的にハンドリングします。リクエストの監視、ルーティング、セキュリティポリシー強制などを担当します。

## Railsが通信する例

Railsアプリケーションは、アプリケーションコンテナ内で動作します。

Railsアプリケーションの全てのネットワーク通信は、
Data Plane内のサイドカープロキシにより透過的にインターセプトされます。

Railsアプリケーションが他のサービスと通信を試みる場合、直接通信するのではなく、ローカルホスト上のサイドカープロキシと通信します。
サイドカープロキシはこの通信を適切な宛先のサービスプロキシあるいはサイドカープロキシへルーティングし、その結果をRailsアプリケーションに返します。

こうすることで、アプリケーションのコードからネットワーク通信の面倒な詳細を抽象化することができ、エラーハンドリング、リトライロジック、セキュリティ、観測性などの側面を強化できます。なお、Service Proxyという用語は、一部のコンテキストではSidecar Proxyと同義に使われることもあります。


## docker-composeで表現するなら

```yaml
version: "3"
services:
  rails:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    volumes:
      - .:/myapp
    links:
      - db
    ports:
      - "3000:3000"
    depends_on:
      - envoy
  db:
    image: postgres:latest
    volumes:
      - ./tmp/db:/var/lib/postgresql/data
  envoy:
    image: envoyproxy/envoy:latest
    volumes:
      - ./envoy/envoy.yaml:/etc/envoy/envoy.yaml
    ports:
      - "10000:10000"
    depends_on:
      - rails
```

Envoyが8080ポートでリッスンし、すべての受信リクエストをローカルのrailsアプリケーション（localhostの3000ポート）に転送します。

```yaml:/etc/envoy/envoy.yaml
static_resources:
  listeners:
    - name: listener_0
      address:
        socket_address: { address: 0.0.0.0, port_value: 8080 }
      filter_chains:
        - filters:
            - name: envoy.filters.network.http_connection_manager
              typed_config:
                "@type": type.googleapis.com/envoy.extensions.filters.network.http_connection_manager.v3.HttpConnectionManager
                codec_type: AUTO
                stat_prefix: ingress_http
                route_config:
                  name: local_route
                  virtual_hosts:
                    - name: local_service
                      domains: ["*"]
                      routes:
                        - match: { prefix: "/" }
                          route: { host_rewrite_literal: localhost, cluster: web_service_proxy, timeout: 0s }
                http_filters:
                  - name: envoy.filters.http.router
  clusters:
    - name: web_service_proxy
      connect_timeout: 0.25s
      type: STATIC
      lb_policy: ROUND_ROBIN
      load_assignment:
        cluster_name: web_service_proxy
        endpoints:
          - lb_endpoints:
              - endpoint:
                  address:
                    socket_address:
                      address: host.docker.internal
                      port_value: 3000

```



## 死活監視機能を入れるなら

```yaml:/etc/envoy/envoy.yaml に追加
clusters:
- name: web_service_proxy
  connect_timeout: 0.25s
  type: STRICT_DNS
  lb_policy: ROUND_ROBIN
  http2_protocol_options: {}
  load_assignment:
    cluster_name: web_service_proxy
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address:
              address: localhost
              port_value: 3000
  health_checks:
    - timeout: 1s
      interval: 10s
      unhealthy_threshold: 2
      healthy_threshold: 2
      http_health_check: { path: "/health_check" }

```

## prometheus
```yaml
admin:
  access_log_path: "/var/log/envoy/admin_access.log"
  address:
    socket_address:
      address: 0.0.0.0
      port_value: 8001
  stats_config:
    stats_tags:
    - tag_name: "listening_port"
      regex: "^listener.((?:[_.[:digit:]]*)).http"
    - tag_name: "cluster_name"
      regex: "^cluster.((?:[_.[:digit:]]*))"
  stats_sink:
    name: envoy.stat_sinks.metrics_service
    typed_config:
      "@type": type.googleapis.com/envoy.config.metrics.v2.MetricsServiceConfig
      grpc_service:
        envoy_grpc: { cluster_name: stats_sink_cluster }
clusters:
- name: stats_sink_cluster
  type: LOGICAL_DNS
  load_assignment:
    cluster_name: stats_sink_cluster
    endpoints:
    - lb_endpoints:
      - endpoint:
          address:
            socket_address: { address: localhost, port_value: 1234 }
```

## 死活状況のチェック
```sh
curl http://localhost:8001/stats
```

## 参考
https://deeeet.com/writing/2018/05/22/service-mesh/