Oracle Identity Cloud Service(IDCS)のアクセストークンを有効期間の短い OpenAI アクセストークンと交換することで、Oracle Cloud Infrastructure(OCI)をワークロード ID プロバイダーとして使用できます。OCI インスタンスプリンシパルは、同じテナンシー内のアイデンティティドメインへのトークン交換リクエストに署名します。OpenAI は取得したトークンを検証し、OCI ワークロードがマッピング先の OpenAI サービスアカウントとして動作することを認可します。
Codex で使用する場合は、このページの手順に従って Oracle トークンを取得し、内容を確認します。その後、Codex のワークロード ID を設定し、トークンをファイルに書き込んで Codex から参照できるようにします。このページのサービスアカウントのマッピングと SDK の例は、OpenAI API 向けです。
この設定では、OpenAI API キー、カスタムの Oracle OAuth リソースアプリケーション、カスタムアプリケーションに対する動的グループへの権限付与は必要ありません。
インスタンスプリンシパルを使用して、OCI Compute インスタンスでワークロードを実行します。Oracle Kubernetes Engine(OKE)では、どの ID でリクエストに署名するのかを確認してください。標準のインスタンスプリンシパル署名機能が通常識別するのは、個々の Kubernetes Pod ではなくワーカーノードです。
署名機能は、OCI インスタンスメタデータサービスから認証情報を取得します。ワークロードがリンクローカルのメタデータエンドポイントに到達できることを確認します。
123curl --fail --silent \
--header "Authorization: Bearer Oracle" \
http://169.254.169.254/opc/v2/instance/id
ワークロードは、所属するテナンシーのアイデンティティドメインに対して、アウトバウンドの HTTPS リクエストを送信できる必要もあります。メタデータエンドポイント自体には、NAT ゲートウェイやインターネット接続は不要です。
OCI Python SDK の InstancePrincipalsSecurityTokenSigner を使用して、アイデンティティドメインへの OAuth トークン交換リクエストに署名します。
POST https://<identity-domain>/oauth2/v1/token
Content-Type: application/x-www-form-urlencoded;charset=utf-8
grant_type=urn:ietf:params:oauth:grant-type:token-exchange
scope=urn:opc:idm:__myscopes__
requested_token_type=urn:ietf:params:oauth:token-type:access_token
urn:opc:idm:__myscopes__ スコープは、インスタンスプリンシパルにすでに付与されている権限を使用します。返された IDCS アクセストークンを、OpenAI ワークロード ID フェデレーションのサブジェクトトークンとして使用します。Oracle トークンのオーディエンスを https://api.openai.com/v1 に置き換えないでください。OpenAI プロバイダーには、実際の Oracle トークンに含まれるオーディエンスを設定します。
TOKEN に実際の OCI ワークロードが生成したアクセストークンを設定し、既存のローカル JWT デコーダーでクレームを確認します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18const parts = process.env.TOKEN?.split(".") ?? [];
if (parts.length !== 3) {
throw new Error("Expected a compact JWT with three segments");
}
if (!/^[A-Za-z0-9_-]+$/.test(parts[1]) || parts[1].length % 4 === 1) {
throw new Error("JWT payload is not valid Base64URL");
}
const bytes = Buffer.from(parts[1], "base64url");
if (bytes.toString("base64url") !== parts[1]) {
throw new Error("JWT payload is not valid Base64URL");
}
const decoded = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
const claims = JSON.parse(decoded);
if (claims === null || Array.isArray(claims) || typeof claims !== "object") {
throw new Error("JWT payload is not a JSON object");
}
console.log(decoded);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26import base64
import json
import os
import re
def reject_non_json_constant(value):
raise ValueError(f"JWT payload contains non-JSON constant: {value}")
parts = os.environ.get("TOKEN", "").split(".")
if len(parts) != 3:
raise ValueError("Expected a compact JWT with three segments")
payload = parts[1]
if re.fullmatch(r"[A-Za-z0-9_-]+", payload) is None or len(payload) % 4 == 1:
raise ValueError("JWT payload is not valid Base64URL")
padded_payload = payload + "=" * (-len(payload) % 4)
decoded = base64.b64decode(padded_payload, altchars=b"-_", validate=True)
if base64.urlsafe_b64encode(decoded).rstrip(b"=").decode("ascii") != payload:
raise ValueError("JWT payload is not valid Base64URL")
decoded_text = decoded.decode("utf-8")
claims = json.loads(decoded_text, parse_constant=reject_non_json_constant)
if not isinstance(claims, dict):
raise ValueError("JWT payload is not a JSON object")
print(decoded_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69package main
import (
"bytes"
"encoding/base64"
"encoding/json"
"fmt"
"os"
"strings"
"unicode/utf8"
)
func decodeSegment(segment string) (json.RawMessage, error) {
if !isBase64URLSegment(segment) {
return nil, fmt.Errorf("JWT segment is not valid Base64URL")
}
decoded, err := base64.RawURLEncoding.DecodeString(segment)
if err != nil {
return nil, err
}
if base64.RawURLEncoding.EncodeToString(decoded) != segment {
return nil, fmt.Errorf("JWT segment is not valid Base64URL")
}
if !utf8.Valid(decoded) {
return nil, fmt.Errorf("JWT segment is not valid UTF-8")
}
var value json.RawMessage
if err := json.Unmarshal(decoded, &value); err != nil {
return nil, err
}
if trimmed := bytes.TrimSpace(value); len(trimmed) == 0 || trimmed[0] != '{' {
return nil, fmt.Errorf("JWT segment is not a JSON object")
}
return value, nil
}
func isBase64URLSegment(segment string) bool {
if segment == "" || len(segment)%4 == 1 {
return false
}
for _, character := range segment {
if !('A' <= character && character <= 'Z') &&
!('a' <= character && character <= 'z') &&
!('0' <= character && character <= '9') &&
character != '-' &&
character != '_' {
return false
}
}
return true
}
func main() {
parts := strings.Split(os.Getenv("TOKEN"), ".")
if len(parts) != 3 {
panic("Expected a compact JWT with three segments")
}
payload, err := decodeSegment(parts[1])
if err != nil {
panic(err)
}
formatted, err := json.MarshalIndent(payload, "", " ")
if err != nil {
panic(err)
}
fmt.Println(string(formatted))
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77// Add Jackson (com.fasterxml.jackson.core:jackson-databind) to your project.
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
public final class DecodeJwtPayloadExample {
private static final ObjectMapper JSON =
new ObjectMapper().enable(DeserializationFeature.FAIL_ON_TRAILING_TOKENS);
private DecodeJwtPayloadExample() {}
static String decodeUtf8(byte[] bytes) throws IOException {
try {
return StandardCharsets.UTF_8
.newDecoder()
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT)
.decode(ByteBuffer.wrap(bytes))
.toString();
} catch (CharacterCodingException exception) {
throw new IOException("JWT segment is not valid UTF-8", exception);
}
}
static String decodeSegment(String segment) throws IOException {
if (!isBase64UrlSegment(segment)) {
throw new IllegalArgumentException("JWT segment is not valid Base64URL");
}
byte[] bytes = Base64.getUrlDecoder().decode(segment);
if (!Base64.getUrlEncoder().withoutPadding().encodeToString(bytes).equals(segment)) {
throw new IllegalArgumentException("JWT segment is not valid Base64URL");
}
String decoded = decodeUtf8(bytes);
JsonNode value = JSON.readTree(decoded);
if (value == null || value.isMissingNode() || !value.isObject()) {
throw new IOException("JWT segment is not a JSON object");
}
return decoded;
}
static boolean isBase64UrlSegment(String segment) {
if (segment.isEmpty() || segment.length() % 4 == 1) {
return false;
}
return segment
.chars()
.allMatch(
character ->
character >= 'A' && character <= 'Z'
|| character >= 'a' && character <= 'z'
|| character >= '0' && character <= '9'
|| character == '-'
|| character == '_');
}
static String[] requireCompactJwt(String token) {
if (token == null) {
throw new IllegalArgumentException("Expected a compact JWT with three segments");
}
String[] parts = token.split("\\.", -1);
if (parts.length != 3) {
throw new IllegalArgumentException("Expected a compact JWT with three segments");
}
return parts;
}
public static void main(String[] args) throws IOException {
String[] parts = requireCompactJwt(System.getenv("TOKEN"));
System.out.println(decodeSegment(parts[1]));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59using System.Text;
using System.Text.Json;
static string DecodeSegment(string segment)
{
if (
segment.Length % 4 == 1 ||
segment.Any(
character =>
!(
character is >= 'A' and <= 'Z' ||
character is >= 'a' and <= 'z' ||
character is >= '0' and <= '9' ||
character is '-' or '_'
)
)
)
{
throw new FormatException("JWT segment is not valid Base64URL");
}
byte[] decoded = Convert.FromBase64String(
segment.Replace('-', '+').Replace('_', '/') +
new string('=', (4 - segment.Length % 4) % 4)
);
string canonicalSegment = Convert
.ToBase64String(decoded)
.TrimEnd('=')
.Replace('+', '-')
.Replace('/', '_');
if (canonicalSegment != segment)
{
throw new FormatException("JWT segment is not valid Base64URL");
}
string decodedJson = new UTF8Encoding(false, true).GetString(decoded);
using JsonDocument document = JsonDocument.Parse(decodedJson);
if (document.RootElement.ValueKind is not JsonValueKind.Object)
{
throw new FormatException("JWT segment is not a JSON object");
}
return decodedJson;
}
string? token = Environment.GetEnvironmentVariable("TOKEN");
if (token is null)
{
throw new InvalidOperationException(
"Expected a compact JWT with three segments"
);
}
string[] parts = token.Split('.');
if (parts.Length != 3)
{
throw new InvalidOperationException(
"Expected a compact JWT with three segments"
);
}
Console.WriteLine(DecodeSegment(parts[1]));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26require "base64"
require "json"
parts = ENV.fetch("TOKEN", "").split(".", -1)
raise "Expected a compact JWT with three segments" unless parts.length == 3
unless parts[1].match?(/\A[A-Za-z0-9_-]+\z/) && parts[1].length % 4 != 1
raise "JWT payload is not valid Base64URL"
end
begin
payload = Base64.urlsafe_decode64(parts[1].ljust((parts[1].length + 3) & ~3, "="))
rescue ArgumentError
raise "JWT payload is not valid Base64URL"
end
unless Base64.urlsafe_encode64(payload, padding: false) == parts[1]
raise "JWT payload is not valid Base64URL"
end
payload.force_encoding(Encoding::UTF_8)
raise "JWT payload is not valid UTF-8" unless payload.valid_encoding?
claims = JSON.parse(payload)
raise "JWT payload is not a JSON object" unless claims.is_a?(Hash)
puts(payload)
このデコーダーは、署名を検証せずにトークンの内容を確認します。生のトークンは機密情報として扱い、ログに記録しないでください。また、本番環境のトークンをサードパーティ製の JWT デコーダーに貼り付けないでください。
デコードした Oracle アクセストークンには、次のクレームが含まれる場合があります。
123456789101112131415{
"iss": "https://identity.oraclecloud.com/",
"aud": [
"https://idcs-example.us-phoenix-1.identity.oraclecloud.com",
"https://idcs-example.identity.oraclecloud.com"
],
"sub_type": "instance",
"ipst_instance": "ocid1.instance.oc1.phx.<instance-id>",
"ipst_compartment": "ocid1.compartment.oc1..<compartment-id>",
"domain_id": "ocid1.domain.oc1..<domain-id>",
"ca_ocid": "ocid1.tenancy.oc1..<tenancy-id>",
"tenant": "idcs-example",
"exp": 1782369434,
"iat": 1782365834
}
自分のアイデンティティドメインが発行したトークンを正確な情報源として使用してください。iss の値をそのまま設定し、トークンの aud の値から 1 つを設定します。ワークロードを認可する際は、不変のクレームである ipst_instance、ipst_compartment、domain_id、ca_ocid を優先して使用してください。
Oracle アイデンティティドメイン用のワークロード ID プロバイダーを作成し、対象の OpenAI サービスアカウントを使用できる OCI インスタンスまたはコンパートメントのマッピングを追加します。
-
ワークロード ID プロバイダーを作成します。 名前 には、oracle-cloud-prod などの一意の値を設定します。 説明には、Production OCI instance principal など、信頼するワークロードを識別できる内容を入力します。
-
発行者とオーディエンスを設定します。 OIDC 発行者 URL には、https://identity.oraclecloud.com/ など、トークンの iss クレームの値を設定します。 オーディエンス には、同じトークンの aud の値から 1 つを設定します。
-
利用できる場合は、テナント固有の OIDC ディスカバリーを設定します。 上級に OIDC ディスカバリーにカスタム URL を使用 が表示される場合は、有効にします。 カスタム OIDC ディスカバリー URL には、https://idcs-example.identity.oraclecloud.com など、テナント固有のアイデンティティドメインを設定します。OpenAI は https://idcs-example.identity.oraclecloud.com/.well-known/openid-configuration を取得し、ディスカバリードキュメントの jwks_uri を使用して、テナントの署名用公開鍵を取得します。カスタムディスカバリーのオプションが表示されない場合は、代わりに トークンの検証にアップロードした JWKS を使用 を有効にし、https://<identity-domain>/admin/v1/SigningCert/jwk から取得した公開 JWKS をアップロードします。
-
派生属性が必要な場合にのみ、属性変換を追加します。 ipst_instance、ipst_compartment、domain_id、ca_ocid などの Oracle クレームは、サービスアカウントのマッピングのアサーションでそのまま使用できます。インスタンスの派生属性を明示的に作成する場合は、instance と式 assertion.ipst_instance を入力して openai.instance を作成します。
Oracle の OpenID Connect ディスカバリーリファレンスは、カスタムディスカバリーが重要な理由を示しています。ディスカバリードキュメントでは、グローバルな発行者として https://identity.oraclecloud.com/ を宣言しながら、トークンエンドポイントと jwks_uri をテナント固有のアイデンティティドメインで公開できます。 OIDC 発行者 URL にはグローバルな発行者を設定したままにし、 カスタム OIDC ディスカバリー URLにはテナントのドメインを使用してください。
アイデンティティドメインがトークンの発行者 URL でディスカバリーメタデータを公開している場合は、
カスタムディスカバリーを無効にしたまま、標準の OIDC ディスカバリーを使用します。
OpenAI がテナントのディスカバリードキュメントまたは署名鍵エンドポイントに到達できない場合は、
カスタムディスカバリーを無効にし、 トークンの検証にアップロードした JWKS を使用を有効にします。
そのうえで、テナントの公開 JWKS を
https://<identity-domain>/admin/v1/SigningCert/jwk から取得してアップロードします。
カスタムディスカバリーとアップロードした JWKS は、同時に有効にできません。
Oracle が署名証明書をローテーションした際は、アップロードした鍵を更新してください。
-
サービスアカウントのマッピングを作成します。 名前 には、oracle-instance-prod などの一意の値を設定し、信頼する OCI ワークロードを識別できる説明を追加します。
-
安定して識別できる、範囲が最も狭い OCI ID を照合条件にします。 1 つのインスタンスにアクセスを許可するには、 キー を ipst_instance に設定し、 値 には確認済みのトークンに含まれるインスタンス OCID を正確に設定します。1 つのコンパートメント内のインスタンスにアクセスを許可するには、 キー を ipst_compartment に設定し、 値 にはコンパートメント OCID を正確に設定します。
-
必要に応じて、ドメインとテナンシーの境界を設定します。 domain_id または ca_ocid のマッピング行を追加し、ワークロードを特定の Oracle アイデンティティドメインまたはテナンシーに限定します。トークンに sub_type クレームが含まれていて、インスタンスプリンシパルを必須にする場合は、そのクレームを値 instance で追加します。すべてのマッピング行が一致する必要があります。
-
OpenAI 側の対象を選択します。 プロジェクト には、サービスアカウントが属するプロジェクトを設定します。次に、信頼する OCI ワークロードが使用できる サービスアカウント を選択します。
-
必要に応じて API 権限を制限します。 ワークロードに必要な 権限 のみを選択します。マッピングの権限設定では、選択したサービスアカウントの権限を制限できますが、そのサービスアカウントが持っていない権限を付与することはできません。
標準のインスタンスプリンシパル署名機能を使用する OKE ワークロードは、ワーカーノードの ID を継承します。インスタンス単位のマッピングで認可されるのは、1 つの Pod だけではなく、そのノードです。同じワーカーノードを共有する Pod 間の分離が必要な場合は、サポートされている OCI ワークロード ID のうち、より細かく対象を指定できるものを使用してください。
OpenAI、OCI、Requests の Python パッケージをインストールします。
pip install openai oci requests
Ruby の場合は、OpenAI と OCI の gem をインストールします。
gem install openai oci
OCI_IDENTITY_DOMAIN_URL には、ワークロードと同じテナンシーにあるアイデンティティドメインのベース URL を設定します。OPENAI_IDENTITY_PROVIDER_ID と OPENAI_SERVICE_ACCOUNT_ID には、OpenAI プロバイダーとサービスアカウントのマッピングから取得した ID をそれぞれ設定します。
次の例では、OCI インスタンスプリンシパルで Oracle トークン交換リクエストに署名し、IDCS アクセストークンを OpenAI SDK に返します。SDK は必要に応じて、そのトークンを有効期間の短い OpenAI アクセストークンと交換します。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53import os
import oci
import requests
from openai import OpenAI
from openai.auth import SubjectTokenProvider
def oracle_instance_principal_token_provider(
identity_domain_url: str,
) -> SubjectTokenProvider:
def get_token() -> str:
signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
response = requests.post(
f"{identity_domain_url.rstrip('/')}/oauth2/v1/token",
data={
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"scope": "urn:opc:idm:__myscopes__",
"requested_token_type": "urn:ietf:params:oauth:token-type:access_token",
},
headers={
"Content-Type": "application/x-www-form-urlencoded;charset=utf-8",
},
auth=signer,
timeout=30,
)
response.raise_for_status()
token = response.json().get("access_token")
if not isinstance(token, str) or not token:
raise RuntimeError("Oracle IDCS did not return an access token.")
return token
return {"token_type": "jwt", "get_token": get_token}
client = OpenAI(
workload_identity={
"identity_provider_id": os.environ["OPENAI_IDENTITY_PROVIDER_ID"],
"service_account_id": os.environ["OPENAI_SERVICE_ACCOUNT_ID"],
"provider": oracle_instance_principal_token_provider(
os.environ["OCI_IDENTITY_DOMAIN_URL"]
),
},
)
response = client.responses.create(
model="gpt-5.6-terra",
input="Say hello from Oracle Cloud Infrastructure workload identity federation.",
)
print(response.output_text)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105require "json"
require "net/http"
require "oci"
require "openai"
require "uri"
class OracleInstancePrincipalTokenProvider
include OpenAI::Auth::SubjectTokenProvider
def initialize(identity_domain_url:)
@identity_domain_url = identity_domain_url.sub(%r{/+\z}, "")
end
def token_type
OpenAI::Auth::TokenType::JWT
end
def get_token
uri = URI("#{@identity_domain_url}/oauth2/v1/token")
unless uri.is_a?(URI::HTTPS)
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity domain URL must use HTTPS",
provider: "oracle-instance-principal"
)
end
body = URI.encode_www_form(
grant_type: "urn:ietf:params:oauth:grant-type:token-exchange",
scope: "urn:opc:idm:__myscopes__",
requested_token_type: "urn:ietf:params:oauth:token-type:access_token"
)
headers = {
"content-type": "application/x-www-form-urlencoded;charset=utf-8"
}
signer = OCI::Auth::Signers::InstancePrincipalsSecurityTokenSigner.new
signer.sign(:post, uri.to_s, headers, body)
request = Net::HTTP::Post.new(uri)
headers.each { |name, value| request[name.to_s] = value }
request.body = body
response = Net::HTTP.start(
uri.hostname,
uri.port,
use_ssl: true,
open_timeout: 10,
read_timeout: 30
) do |http|
http.request(request)
end
unless response.is_a?(Net::HTTPSuccess)
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity token request failed with status #{response.code}",
provider: "oracle-instance-principal"
)
end
token = JSON.parse(response.body).fetch("access_token")
unless token.is_a?(String) && !token.empty?
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity domain did not return an access token",
provider: "oracle-instance-principal"
)
end
token
rescue JSON::ParserError
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity token response was not valid JSON",
provider: "oracle-instance-principal"
), cause: nil
rescue KeyError
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Oracle identity domain did not return an access token",
provider: "oracle-instance-principal"
), cause: nil
rescue SystemCallError, Timeout::Error => error
raise OpenAI::Errors::SubjectTokenProviderError.new(
message: "Failed to request Oracle identity token: #{error.message}",
provider: "oracle-instance-principal",
cause: error
)
end
end
provider = OracleInstancePrincipalTokenProvider.new(
identity_domain_url: ENV.fetch("OCI_IDENTITY_DOMAIN_URL")
)
workload_identity = OpenAI::Auth::WorkloadIdentity.new(
identity_provider_id: ENV.fetch("OPENAI_IDENTITY_PROVIDER_ID"),
service_account_id: ENV.fetch("OPENAI_SERVICE_ACCOUNT_ID"),
provider: provider
)
client = OpenAI::Client.new(workload_identity: workload_identity)
response = client.responses.create(
model: "gpt-5.6-terra",
input: "Say hello from Oracle Cloud Infrastructure workload identity federation."
)
puts(response.output_text)
OpenAI SDK がワークロード ID の認証情報を更新する必要がある場合、サブジェクトトークンプロバイダーは新しい Oracle トークンをリクエストします。Oracle サブジェクトトークンや、交換によって取得した OpenAI アクセストークンを出力したり、永続化したりしないでください。
- 1 つのワークロードだけにアクセスを許可する場合は、
ipst_instance で 1 つのインスタンスをマッピングします。
- コンパートメント内の対象となるすべてのインスタンスでマッピングを共有する場合にのみ、
ipst_compartment を使用します。
domain_id または ca_ocid を追加して、アイデンティティドメインとテナンシーの境界による制限を適用します。
- アプリケーションと環境ごとに、別々の OpenAI サービスアカウントを使用します。
- Pod 単位の分離を前提にする前に、OKE トークンがワーカーノードを表しているかどうかを確認してください。
- OpenAI 固有のオーディエンスを想定せず、発行された Oracle トークンに含まれるオーディエンスを使用します。
- アイデンティティドメインで OIDC ディスカバリーを使用できない場合は、Oracle が署名鍵をローテーションする際に、アップロードした公開鍵もローテーションしてください。