イングレス
イングレスはクライアント側エグレスの受信側に当たる機能です。ワークロードの公開ポートに対するパブリックな HTTP(S) のフロントドアを要求するため、サービスはポート転送やトンネルだけでなく、実際のホスト名で到達可能になります。ワークロードの公開ポートの前段となるため、仕様は少なくとも 1 つのポートを公開しなければなりません。
イングレスはデプロイスペックの ingress: ブロックまたは Compose のポータブル拡張 x-cornus-ingress: で明示的に有効化します。暗黙に有効になることはありません。
何が実現するか
宣言するホストとパスはどの環境でも同じです。異なるのは、それを提供する主体です。
| バックエンド | 実現する主体 |
|---|---|
kubernetes | ワークロードの ClusterIP Service の前段に立つ実際の networking.k8s.io/v1 Ingress オブジェクト。クラスターのイングレスコントローラーが提供します |
dockerhost、containerd、bare、incus | cornus サーバー自身。ホストとパスのルーティングテーブルを保持し、ワークロードへプロキシします |
クラスターオブジェクトを作成するのは Kubernetes の経路だけなので、以下で説明するクラス名、アノテーション、cert-manager の TLS 設定はそこにのみ適用されます。それ以外の環境ではサーバーが同じ宣言をルーティングします。これにより Compose プロジェクトはノート PC 上でもクラスター上でも同じように振る舞います。
サーバー側フロントドアは開発用です
サーバー側のイングレスルーティングは、Compose プロジェクトがノート PC 上でもクラスター上でも同じように振る舞うようにするために存在します。本番用のイングレスではありません。自前の TLS 終端はなく、レート制限もアクセス制御も行わず、エラーページは到達できなかった内部ワークロード名とコンテナポートを表示します。デバッグ中には有用ですが、見知らぬ相手が到達できるアドレスでは望ましくありません。
開発とプレビュー用途に使ってください。恒久的なもの、あるいはパブリックなものには、実際のイングレスコントローラーを備えたクラスターへデプロイしてください。cornus は利用可能な場合そちらへ接続します。
到達方法
イングレスを宣言しただけでは、自分のマシンから到達できるようにはなりません。そのためには DNS がどこかを指している必要があり、プレビュー環境にそれがあることはまれです。この隔たりを埋める方法は 3 つあり、必要になる頻度の高い順に挙げます。
cornus ingress-tunnelは、プロジェクト全体に 1 つのパブリック URL を与え、ルーティングも代行します。イングレスをパブリックに公開するを参照してください。- SOCKS5 conduitは、宣言したホスト名を自分のマシンからルーティングします。パブリックには一切公開されません。
CORNUS_INGRESS_LISTENはサーバーのフロントドアを、DNS や/etc/hostsのエントリで指せるアドレスにバインドします。
仕組み
有効化する
以下のいずれでもイングレスが有効になります。
- デプロイスペックの
ingress: { enabled: true }、 - Compose の素の
x-cornus-ingress: {}(またはx-cornus-ingress: true)、または - 空でないホスト (
hosts:/ Composehost:)。これはenabledを暗黙に有効化します。
name: web
image: localhost:5000/web:v1
ports:
- { host: 8080, container: 80 } # the Service the Ingress fronts
ingress:
enabled: true # host auto-derived from the server domain
tls: {} # HTTPS via the server's default issuerホスト解決
- 明示的な
hosts:— 各ホスト名は独自のイングレス規則になります。すべて 1 つの TLS 項目を共有し、同じ Service の前段になります。特別なトークン@は、DNS ゾーンの慣例に従い、<name>.接頭辞のない頂点ドメイン (ベースドメイン自体) に対応します。 - 自動導出 (
hostsが空の場合) — バックエンドは<subdomain>.<domain>という単一のホスト名を構築します。domainはベースドメインのクライアント側上書きです。空ならサーバー既定のCORNUS_INGRESS_DOMAINにフォールバックします。subdomainの既定値はデプロイメント名です。Compose トランスレーターは<service>.<project>に設定するため、プロジェクトごとに異なるホスト名になります。ラベルは DNS-1123 に合わせて正規化されます。- 明示的なホスト名もベースドメインもないデプロイは拒否されます。
ルーティング
イングレスはワークロードの公開済みコンテナポートのいずれか (その ClusterIP Service) の前段になるため、仕様は少なくとも 1 つのポートを公開しなければなりません。ports: なしでイングレスを有効化したデプロイは ingress requires the deployment to publish at least one port で拒否されます。
| デプロイスペックのフィールド | Compose のキー | 既定値 | 意味 |
|---|---|---|---|
path | path | / | ルーティングする HTTP パスの接頭辞。 |
pathType | path_type | Prefix | Kubernetes のパスマッチ型: Prefix、Exact、ImplementationSpecific (大文字小文字を区別します。小文字の prefix は拒否されます)。 |
port | port | 最初の公開済みポート | ルーティング先のコンテナポート、つまりアプリが待ち受けているポートです。パブリックな HTTP/HTTPS ポートではありません (そちらは 80/443 のままです)。ゼロなら最初の公開済みポートを使います。非ゼロ値はワークロードの公開済みコンテナポートのいずれかと一致しなければならず、そうでなければ ingress: port N is not among the deployment's published container ports になります。 |
className | class_name | サーバー既定 | IngressClassName。空なら CORNUS_INGRESS_CLASS、次にクラスターの既定 IngressClass へフォールバックします。 |
annotations | annotations | — | コントローラー固有の設定 (書き換え先、本文サイズなど) のため、Ingress オブジェクトにそのまま統合します。 |
デプロイスペックは 1 列目の camelCase のフィールド名を使い、Compose の x-cornus-ingress 拡張は 2 列目の snake_case のキーを使います (Compose サービスを公開するを参照)。
サーバー側既定値とドメインポリシー
運用者はフォールバックを設定できるため、ワークロードはすべて既定値のままイングレスを有効化できます (Helm の ingress.* 値は環境変数として設定されます)。空のままにすれば各ワークロードが自身のホスト名を指定する必要があり、何も自動公開されません。
| 環境変数 | Helm 値 | 意味 |
|---|---|---|
CORNUS_INGRESS_DOMAIN | ingress.domain | ホスト自動導出の base wildcard ドメイン (例: preview.example.com)。 |
CORNUS_INGRESS_CLASS | ingress.className | 既定の IngressClassName。 |
CORNUS_INGRESS_TLS_ISSUER | ingress.tlsIssuer | TLS イングレス用の既定 cert-manager cluster-issuer。 |
CORNUS_INGRESS_ENFORCE_DOMAIN | ingress.enforceDomain | true (かつドメイン設定済み) なら、解決したホスト名が domain の外にあるワークロードを拒否します。共有コントローラーがクライアントの指定だけで任意のホスト名を提供させられることを防ぎます。 |
関連項目: デプロイスペック、Helm chart values
自動導出されたホストでワークロードを公開する
イングレスを有効にし、<subdomain>.<domain> 形式のホスト名をサーバーのベースドメイン (CORNUS_INGRESS_DOMAIN) から導出させます。
name: web
image: localhost:5000/web:v1
ports:
- { host: 8080, container: 80 }
ingress:
enabled: truesubdomainの既定値はデプロイメント名なので、これはweb.<CORNUS_INGRESS_DOMAIN>にデプロイされます (Compose トランスレーターは代わりに<service>.<project>を使います)。サーバーにベースドメインがなく、自分でも設定しなければデプロイは拒否されます。
関連項目: デプロイスペック
明示的なホスト名を設定する
一つ以上のホスト名を同じ Service にルーティングします。各ホスト名はそれぞれ独立したイングレス規則になります。
ingress:
hosts:
- app.example.com
- www.example.com- 特別なトークン
@は、ルートドメイン (ベースドメインそのもので、<name>.の接頭辞なし) に使います:hosts: ["@"]。
cert-manager で HTTPS を提供する
cert-manager の cluster-issuer に証明書を要求します。Cornus が issuer アノテーションを追加し、cert-manager がシークレットを用意します。
ingress:
hosts: ["app.example.com"]
tls:
clusterIssuer: letsencrypt-prod # 空の場合は CORNUS_INGRESS_TLS_ISSUER にフォールバックするtls:ブロックはホストに HTTPS を要求します。プレーンな HTTP では省略してください。secretNameは既存 TLS シークレットの名前です。空なら<name>-tlsが既定で、clusterIssuer(またはサーバー既定) が設定されていれば cert-manager が作成します。既存のシークレットを自分で用意する場合は、tls: { secretName: my-existing-tls }を設定し、clusterIssuerを省略します。clusterIssuerはcert-manager.io/cluster-issuerannotation を設定します。空ならサーバー既定のCORNUS_INGRESS_TLS_ISSUERにフォールバックします。
関連項目: セキュリティと認証
自前の証明書を使う
証明書の規則は、選択中の接続プロファイルに記述します。pattern は任意です。省略した場合、Cornus は証明書のすべての DNS SAN からセレクターを作成します。
contexts:
prod:
server: https://cornus.example.com
conduit:
ingress:
mode: native
certificates:
- certificate: /etc/cornus/example-com.pem
key: /etc/cornus/example-com-key.pem
- pattern: api.other.example
certificate: /etc/cornus/api.pem
key: /etc/cornus/api-key.pemパターンは厳密なホスト名か、*.example.com のような 1 ラベルのワイルドカードです。明示したパターンは証明書の SAN に含まれていなければなりません。厳密一致がワイルドカードより優先され、ワイルドカード同士では最長の接尾辞が優先されます。
emulate モードでは、ローカルのイングレスプロキシが提供する証明書を SNI が選択します。一致しないホスト名では通常の生成 CA へのフォールバックを使います。native モードでは、Cornus はデプロイ前に具体的なイングレスホストをすべて照合し、選択された証明書ごとにホストをまとめ、ワークロードの Deployment が所有する安定した kubernetes.io/tls Secret を作成して、Kubernetes Ingress に接続します。再適用すると Secret のデータをその場でローテーションし、不要になった管理対象 Secret を削除します。
native の管理対象証明書には、具体的な ingress.hosts を明示する必要があります。自動導出されるホスト名や @ の頂点ドメイントークンは、仕様の中で展開してください。すべてのホストがいずれかの証明書規則に一致しなければなりません。証明書はクライアント側 conduit のリスナーではなく永続的な Kubernetes の状態であるため、これはデタッチした Compose やデプロイの操作でも機能します。
native の経路はデプロイ要求で秘密鍵の実体を送信します。そのため Cornus は、リモートのプレーン HTTP 越しの場合は要求のシリアライズ前に拒否します。HTTPS、SSH トンネルのプロファイル、または Kubernetes のポート転送のようなループバックのエンドポイントを使ってください。鍵がステータスや診断出力に現れることはありません。フィールドの一覧は接続設定リファレンスを参照してください。
特定のパス、ポート、クラスをルーティングする
ワークロードが複数のポートを公開する場合や、クラスターに複数のイングレスコントローラーがある場合に、既定値を上書きします。
ingress:
hosts: ["api.example.com"]
path: /v1
pathType: Prefix # Exact または ImplementationSpecific も指定可能
port: 8443 # 公開済みコンテナポートと一致する必要がある
className: nginx # 空の場合は CORNUS_INGRESS_CLASS、それもなければクラスターの既定値を使う
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"関連項目: デプロイスペック
Compose サービスを公開する
プロジェクトレベルまたはサービスごとに x-cornus-ingress を使います。プロジェクトレベルのブロックは既定値を提供するだけで、イングレスは有効にしません。イングレスはサービスごとに明示的に有効化したままです。x- 接頭辞により、ファイルは標準の Compose ツールに対して有効なままです。
services:
web:
image: registry.example/web:v1
ports: ["8080:80"] # the ingress fronts a published port (here container :80)
x-cornus-ingress:
host: web.example.com # scalar sugar; unioned with hosts:
port: 80 # container port to route to; omit to use the first published
path_type: Prefix
tls: { cluster_issuer: letsencrypt-prod }ここでつまずきやすい点が 3 つあります。いずれも無言で失敗するか、デプロイ時に失敗します。
- ポートを公開すること。 サービスには
ports:の項目が必要です。イングレスは公開済みのコンテナポートの前段に配置されるためです。内部でしか待ち受けないサービスでも、それを記載しなければなりません (ports: ["80"]、またはホストポートをバインドしない長形式の- target: 80。後者は他のサービスとのホストポート衝突も避けられます)。 portはコンテナポートであり、公開ポートではありません。 これはコンテナ内でアプリが待ち受けているポート (例:3000、8000) であり、80/443ではありません。TLS とパブリックな HTTP(S) ポートはtls: {}が処理します。- キーは snake_case です。
x-cornus-ingressの中ではpath_type、class_name、tls:の下ではsecret_name/cluster_issuerと書きます。デプロイスペックの camelCase (pathType、className、secretName、clusterIssuer) ではありません。camelCase のキーは未知のフィールドとして無言で無視されます。値も大文字小文字を区別し、path_type: Prefixであってprefixではありません。サーバー既定の issuer で HTTPS を要求するだけなら、素のtls: {}で十分です。
関連項目: デプロイスペック、Compose、devcontainers、docker CLI
conduit 経由で自分のマシンからイングレスに到達する
上記のパブリックな Ingress はワークロードに実際のホスト名を与えますが、開発マシンからそこへ到達するには、クラスターのイングレスコントローラーを指す DNS が依然として必要です。SOCKS5 conduit がその隙間を埋めます。ブラウザーのプロキシ設定 1 つで、ワークロードのイングレスホストがプロキシ経由で解決されます。/etc/hosts の編集も実際の DNS も不要です。これは明示的に有効化するもので、socks5 conduit (--conduit socks5) に相乗りし、次の 2 つのモードのいずれかで動作します。
- native — クラスターの実際のイングレスコントローラー Service への透過的なトンネルです。ブラウザーの TLS ClientHello (SNI) と
Hostヘッダーはそのまま通過するため、実際のコントローラーが Host / パスのルーティングを行い、クラスター自身の証明書で TLS を終端します。Kubernetes 専用で、セッションにはクラスターへの直接アクセス (ポート転送 / kube-auth のプロファイル) が必要です。コントローラーの Service はサーバーが検出し、GET /.cornus/v1/infoで通知します (CORNUS_INGRESS_CONTROLLER=<namespace>/<service>[:http/https]で上書きできます)。 - emulate —
Host/ パスに基づいて conduit 経由でワークロードのコンテナポートへ経路する、小さなクライアント側 HTTP(S) リバースプロキシです。TLS は、一致するユーザー提供の証明書か、生成したフォールバック証明書で終端します。すべてのバックエンドで動作します (コントローラーを持たないdockerhost/containerdを含みます)。TLS の信頼はそのまま使えます。 mkcert がインストールされていてmkcert -installを実行済みなら、エミュレートしたイングレスはリーフ証明書を mkcert のすでに信頼された ローカル CA で署名するため、ブラウザーとcurlは手作業なしでhttps://<host>/を信頼します。そうでない場合は、一度だけ信頼すればよい永続的な自己署名 CA (~/.local/share/cornus/ingress-ca.pem) にフォールバックします (--cacertを渡すこともできます)。明示的な--ingress-emulate-ca/--ingress-emulate-ca-keyは両者より優先されます。
実行ごとに有効化するか、プロファイルに固定します。
# per run
cornus compose up --conduit socks5 --ingress-conduit native
cornus deploy -f app.yaml --server https://cornus.example.com \
--conduit socks5 --ingress-conduit emulate
# or pin it in the connection profile (see cornus config)
cornus config set-context prod --conduit-mode socks5 --ingress-conduit nativeブラウザーの SOCKS5 プロキシを conduit に向け (リモート DNS / socks5h を使います)、ワークロードのイングレスホスト (例: https://web.example.com/) を開きます。優先順位は --ingress-conduit > CORNUS_INGRESS_CONDUIT > プロファイルで、off は無効化します。
cornus setup はサーバーを調べて既定値を選びます。コントローラーを検出できれば native、到達可能なコントローラーがなくイングレスドメインだけがある場合は emulate、それ以外は off を提案します。
補足が 2 点あります。native と emulate は同じ x-cornus-ingress の仕様に適用されます。実際のコントローラーが存在する場所では native が優先され、emulate はポータブルなフォールバックです。コントローラーの annotations / className / cert-manager のフィールドは Kubernetes 専用で、エミュレーションでは無視されます。
関連項目: ネットワークと conduit、cornus setup
イングレスをパブリックに公開する
cornus ingress-tunnelはイングレスの前面にパブリック URL を置きます。スコープが宣言したホスト名とパスのすべてが、1 つのアドレスの背後に入ります。
cornus ingress-tunnel --project myappIngress tunnel for project/myapp ready at https://abc123.ngrok.app
serving: web.myapp.example.com, api.myapp.example.com
fronting: the cluster ingress controller
host: passed through untouchedこれがcornus tunnelとの違いです。後者は単一のワークロードポートを公開するだけで、宣言されたホストやパスについては何も関知しません。
コマンドを実行する代わりに compose up のたびに公開したい場合は、対象のイングレスの隣で宣言します。
services:
web:
image: myapp:latest
ports: ["8080:80"]
x-cornus-ingress:
host: web.myapp.example.com
tunnel: trueトンネルの資格情報は引き続きクライアント側から渡されます (接続プロファイルの authtoken-file、または CORNUS_TUNNEL_AUTHTOKEN)。リポジトリにコミットされる compose ファイルからは渡されません。各バックエンドで何ができるかはトンネルガイドを、URL を共有する前に理解しておくべき唯一の点 — アプリが認識するのがトンネルのホスト名か宣言したホスト名か — についてはHost の扱いを参照してください。