์๋ ํ์ธ์. Ingress๋ฅผ ํตํด WS๋ฅผ ๋ ธ์ถํ๋ Kubernetes์์ ์คํํ๋ ค๊ณ ํ ๋ #813์ ์ค๋ช ๋ ๋ฌธ์ ์ ๋น์ทํ ์ด๋ ค์์ ๊ฒช๊ณ ์์ต๋๋ค.
WebSocket connection to 'ws://apicurio-ws.192.168.1.5.nip.io/ws/designs/2?uuid=68064b1e-f839-4973-b65e-89660d4a07d9&user=admin&secret=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJxRV9
COWQ4NFZS' failed: Error during WebSocket handshake: Unexpected response code: 404
๋ฐฐํฌํ/kubernetes์์ kubernetes ๋งค๋ํ์คํธ ํ์ผ์ ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ๋ชจ๋ ์๋น์ค์ ๋ํด ํธ์คํธ๋ง ๋ณ๊ฒฝํ๊ณ ์์ต๋๋ค.
#813์ ์ ๋ณด๊ฐ ๋์์ด ๋์ง ์์์ต๋๊น? ์ด๊ฒ์ ๊ฐ์ ๋ฌธ์ ์ฒ๋ผ ๋ณด์ ๋๋ค. @0x218 ์ด ์ค์น์ ์ ์ฉํ ๋ณ๊ฒฝ ์ฌํญ์ ๋ํด ์์ธํ ์ค๋ช ํ ์ ์์ต๋๊น? ์๋๋ฉด @t-rap์ด ์๊ฐ์ด ์์ผ์ ๊ฐ์? ๋ด kubernetes ์ ๋ฌธ ์ง์์ ๊ทนํ ์ ํ์ ์ ๋๋ค.
์, ์ ์๋ Nginx Ingress @0x218 ์์ ๋ฒ์ , ์ ๊ทธ๋ ์ด๋ ๋ฐ ์ฐ๊ฒฐ ํค๋๋ฅผ ์ ์ฉํ๋ ค๊ณ ํ์ต๋๋ค. ๊ทธ๋ฌ๋ ์น ์์ผ์ ์ฐ๊ฒฐํ ์ ์์์ต๋๋ค.
๋ฌธ์ ๋ฅผ ๋ ์ ์ดํดํ๊ธฐ ์ํด ์ ๊ณตํ ์ ์๋ ๋ก๊ทธ๊ฐ ์์ต๋๊น?
-ws ํฌ๋์์ "์ ์ฒด" ๋ธ๋ผ์ฐ์ ์ฝ์ ์ถ๋ ฅ ๋ฐ ์๋ฒ ๋ก๊ทธ๋ฅผ ์ ๊ณตํ ์ ์๋ค๋ฉด ๋์์ด ๋ ์ ์์ต๋๋ค.
๋ธ๋ผ์ฐ์ ๋ก๊ทธ:
[ApiEditorPageComponent] Attempting to reconnect to the server.
main.a117148be57ecc117ff5.js:1 [ApisService] Getting an API Design: http://apicurio.192.168.1.5.nip.io/studio-api/designs/2
main.a117148be57ecc117ff5.js:1 [ApisService] Editing API Design: http://apicurio.192.168.1.5.nip.io/studio-api/designs/2/session
main.a117148be57ecc117ff5.js:1 [ApisService] Editing Session UUID: 0d50ffc8-1f69-41b0-b1f0-6c80a1771e00
main.a117148be57ecc117ff5.js:1 [ApisService] Content Version: 3
main.a117148be57ecc117ff5.js:1 [ApiEditorPageComponent] Definition loaded. Opening editing session.
main.a117148be57ecc117ff5.js:1 [ApisService] Opening editing session on URL: ws://apicurio-ws.192.168.1.5.nip.io/ws/designs/2?uuid=0d50ffc8-1f69-41b0-b1f0-6c80a1771e00&user=admin&secret=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJCNlRDTFAxdkIz
main.a117148be57ecc117ff5.js:1 WebSocket connection to 'ws://apicurio-ws.192.168.1.5.nip.io/ws/designs/2?uuid=0d50ffc8-1f69-41b0-b1f0-6c80a1771e00&user=admin&secret=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJCNlRDTFAxdkIz' failed: Error during WebSocket handshake: Unexpected response code: 404
t.openEditingSession @ main.a117148be57ecc117ff5.js:1
(anonymous) @ main.a117148be57ecc117ff5.js:1
e.invoke @ polyfills.9c67d1be23abf75fea1a.js:1
onInvoke @ main.a117148be57ecc117ff5.js:1
e.invoke @ polyfills.9c67d1be23abf75fea1a.js:1
t.run @ polyfills.9c67d1be23abf75fea1a.js:1
(anonymous) @ polyfills.9c67d1be23abf75fea1a.js:1
e.invokeTask @ polyfills.9c67d1be23abf75fea1a.js:1
onInvokeTask @ main.a117148be57ecc117ff5.js:1
e.invokeTask @ polyfills.9c67d1be23abf75fea1a.js:1
t.runTask @ polyfills.9c67d1be23abf75fea1a.js:1
y @ polyfills.9c67d1be23abf75fea1a.js:1
t.invokeTask @ polyfills.9c67d1be23abf75fea1a.js:1
_ @ polyfills.9c67d1be23abf75fea1a.js:1
m @ polyfills.9c67d1be23abf75fea1a.js:1
main.a117148be57ecc117ff5.js:1 [ApiEditingSession] WS connection to server CLOSED: CloseEventย {isTrusted: true, wasClean: false, code: 1006, reason: "", type: "close",ย โฆ}
main.a117148be57ecc117ff5.js:1 [ApiEditorPageComponent] **Notice** editing session DROPPED! Reason code: 1006
main.a117148be57ecc117ff5.js:1 [ApiEditorPageComponent] Attempting to reconnect to the server.
main.a117148be57ecc117ff5.js:1 [ApisService] Getting an API Design: http://apicurio.192.168.1.5.nip.io/studio-api/designs/2
main.a117148be57ecc117ff5.js:1 [ApisService] Editing API Design: http://apicurio.192.168.1.5.nip.io/studio-api/designs/2/session
main.a117148be57ecc117ff5.js:1 [ApisService] Editing Session UUID: 5eecf1e6-2a83-4940-bdb0-110240374093
main.a117148be57ecc117ff5.js:1 [ApisService] Content Version: 3
main.a117148be57ecc117ff5.js:1 [ApiEditorPageComponent] Definition loaded. Opening editing session.
main.a117148be57ecc117ff5.js:1 [ApisService] Opening editing session on URL: ws://apicurio-ws.192.168.1.5.nip.io/ws/designs/2?uuid=5eecf1e6-2a83-4940-bdb0-110240374093&user=admin&secret=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJCNlRDTFAxdkIz
main.a117148be57ecc117ff5.js:1 WebSocket connection to 'ws://apicurio-ws.192.168.1.5.nip.io/ws/designs/2?uuid=5eecf1e6-2a83-4940-bdb0-110240374093&user=admin&secret=eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJCNlRDTFAxdkIz' failed: Error during WebSocket handshake: Unexpected response code: 404
t.openEditingSession @ main.a117148be57ecc117ff5.js:1
(anonymous) @ main.a117148be57ecc117ff5.js:1
e.invoke @ polyfills.9c67d1be23abf75fea1a.js:1
onInvoke @ main.a117148be57ecc117ff5.js:1
e.invoke @ polyfills.9c67d1be23abf75fea1a.js:1
t.run @ polyfills.9c67d1be23abf75fea1a.js:1
(anonymous) @ polyfills.9c67d1be23abf75fea1a.js:1
e.invokeTask @ polyfills.9c67d1be23abf75fea1a.js:1
onInvokeTask @ main.a117148be57ecc117ff5.js:1
e.invokeTask @ polyfills.9c67d1be23abf75fea1a.js:1
t.runTask @ polyfills.9c67d1be23abf75fea1a.js:1
y @ polyfills.9c67d1be23abf75fea1a.js:1
t.invokeTask @ polyfills.9c67d1be23abf75fea1a.js:1
_ @ polyfills.9c67d1be23abf75fea1a.js:1
m @ polyfills.9c67d1be23abf75fea1a.js:1
main.a117148be57ecc117ff5.js:1 [ApiEditingSession] WS connection to server CLOSED: CloseEventย {isTrusted: true, wasClean: false, code: 1006, reason: "", type: "close",ย โฆ}
main.a117148be57ecc117ff5.js:1 [ApiEditorPageComponent] **Notice** editing session DROPPED! Reason code: 1006
ws ํฌ๋ ์ ์ฒด ๋ก๊ทธ:
06-10 14:46:08,780 DEBUG [io.undertow.request.security] (default task-1) Authentication result was ATTEMPTED for /ws/designs/2
2020-06-10 14:46:08,781 DEBUG [io.undertow.request] (default task-1) Matched default handler path /ws/designs/2
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) PeriodicRecovery: background thread Status <== SCANNING
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) PeriodicRecovery: background thread scanning
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Periodic recovery first pass at Wed, 10 Jun 2020 14:46:11
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) processing /StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction transactions
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.jta.recovery.arjunacore.CommitMarkableResourceRecordRecoveryModule<strong i="6">@7e04aa62</strong>' first pass processed
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) AtomicActionRecoveryModule first pass
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) processing /StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction transactions
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule<strong i="7">@3b062bc6</strong>' first pass processed
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.txoj] (Periodic Recovery) TORecoveryModule - first pass
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.txoj.recovery.TORecoveryModule<strong i="8">@29e089e5</strong>' first pass processed
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.jta.recovery.arjunacore.SubordinateAtomicActionRecoveryModule<strong i="9">@5f676b11</strong>' first pass processed
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) XARecoveryModule state change IDLE->FIRST_PASS
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) Local XARecoveryModule - first pass
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) XARecoveryModule state change FIRST_PASS->BETWEEN_PASSES
2020-06-10 14:46:11,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule<strong i="10">@5ad9faef</strong>' first pass processed
2020-06-10 14:46:20,674 DEBUG [io.undertow.request] (default I/O-2) Matched default handler path /ws/designs/2
2020-06-10 14:46:20,675 DEBUG [io.undertow.request.security] (default task-1) Attempting to authenticate /ws/designs/2, authentication required: false
2020-06-10 14:46:20,675 DEBUG [io.undertow.request.security] (default task-1) Authentication outcome was NOT_ATTEMPTED with method io.undertow.security.impl.CachedAuthenticatedSessionMechanism<strong i="11">@59a16760</strong> for /ws/designs/2
2020-06-10 14:46:20,675 DEBUG [io.undertow.request.security] (default task-1) Authentication result was ATTEMPTED for /ws/designs/2
2020-06-10 14:46:20,675 DEBUG [io.undertow.request] (default task-1) Matched default handler path /ws/designs/2
2020-06-10 14:46:21,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Periodic recovery second pass at Wed, 10 Jun 2020 14:46:21
2020-06-10 14:46:21,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.jta.recovery.arjunacore.CommitMarkableResourceRecordRecoveryModule<strong i="12">@7e04aa62</strong>' second pass processed
2020-06-10 14:46:21,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) AtomicActionRecoveryModule second pass
2020-06-10 14:46:21,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule<strong i="13">@3b062bc6</strong>' second pass processed
2020-06-10 14:46:21,291 DEBUG [com.arjuna.ats.txoj] (Periodic Recovery) TORecoveryModule - second pass
2020-06-10 14:46:21,291 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.txoj.recovery.TORecoveryModule<strong i="14">@29e089e5</strong>' second pass processed
2020-06-10 14:46:21,292 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.jta.recovery.arjunacore.SubordinateAtomicActionRecoveryModule<strong i="15">@5f676b11</strong>' second pass processed
2020-06-10 14:46:21,292 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) XARecoveryModule state change BETWEEN_PASSES->SECOND_PASS
2020-06-10 14:46:21,292 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) Local XARecoveryModule - second pass
2020-06-10 14:46:21,292 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) Local XARecoveryModule.transactionInitiatedRecovery completed
2020-06-10 14:46:21,292 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) Local XARecoveryModule.resourceInitiatedRecovery completed
2020-06-10 14:46:21,292 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) XARecoveryModule state change SECOND_PASS->IDLE
2020-06-10 14:46:21,292 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule<strong i="16">@5ad9faef</strong>' second pass processed
2020-06-10 14:46:21,292 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) PeriodicRecovery: background thread Status <== INACTIVE
2020-06-10 14:46:21,292 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) PeriodicRecovery: background thread backing off
๊ทธ๋ ๋ค๋ฉด ๋ฌด์์ด ์๋ชป๋๊ณ ์๋์ง ์ ํ ๋ชจ๋ฆ ๋๋ค. -ws ๋ฐ -api ํฌ๋๊ฐ ๋์ผํ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ฐ๊ฒฐ๋์ด ์๋์ง ํ์ธํ๋๋ก ์์ฒญํ์ต๋๋ค. ๊ทธ๋ ์ง ์์ ๊ฒฝ์ฐ ์ฐ๊ฒฐ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง๋ง ์ด ๊ฒฝ์ฐ -ws ํฌ๋ ๋ก๊ทธ์ ์ค๋ฅ๊ฐ ํ์๋๊ณ 404๊ฐ ํ์๋์ง ์์ต๋๋ค.
๊ทธ๊ฒ์ ์ผ์ข ์ Ingress ๋ฌธ์ ์ธ ๊ฒ ๊ฐ์ผ๋ฉฐ ๋์์ด ๋ ๋งํผ k8์ ๋ํด ์ ๋ชจ๋ฆ ๋๋ค! :(
๋ํ NodePort๋ฅผ ํตํด ์ง์ ws pod๋ฅผ ๋ ธ์ถํ๋ ค๊ณ ํ์ต๋๋ค. ๊ทธ๋ฌ๋ ๋๋ ์ฌ์ ํ ์ฑ๊ณตํ์ง ๋ชปํ๋ค.
๋ค์ ์๋ํ ์์ ์ ๋๋ค. ๋ก๊ทธ ์์ค์ ๋์ด๋ ๊ฒ์ด ๋์์ด ๋ ๊น์?
๊ทธ๊ฒ์ ์์ฒ๋ฅผ ์ค ์ ์์ต๋๋ค. -ws ๊ตฌ์ฑ ์์์ ํ์ฑํ๋ ์ธ์ฆ์ด ์๊ธฐ ๋๋ฌธ์ ์ธ์ฆ์ ๋ํ -ws ๋ก๊ทธ์ ์ ๊ฐ ์ ์ดํดํ์ง ๋ชปํ๋ ๋ด์ฉ์ด ์์์ต๋๋ค. ์ด์ฉ๋ฉด ๋ก๊ทธ ์์ค์ ๋์ด๋ฉด ๋ ๋ง์ ๊ฒ์ ๋ฐํ ์ ์์ต๋๋ค.
๋ก๊ทธ ์์ค์ ์ด๋ ์์ค์ผ๋ก ๋์ฌ์ผ ํ๋ค๊ณ ์๊ฐํ์ญ๋๊น?
์๋ง๋ DEBUG๋ก ์์ํ์ฌ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๋์ง ๋ณด์ญ์์ค. :)
๋๋ฒ๊ทธ๋ฅผ ์ํด ๋ก๊ทธ ์์ค์ ๋์์ต๋๋ค. ๊ทธ๋ฌ๋ ์ ์ตํ ๋ก๊ทธ๊ฐ ์์ต๋๋ค.
์ฌ
cloak.adapters.OAuthRequestAuthenticator] (default task-2) there was no code
2020-06-13 22:20:10,628 DEBUG [org.keycloak.adapters.OAuthRequestAuthenticator] (default task-2) redirecting to auth server
2020-06-13 22:20:10,628 DEBUG [org.keycloak.adapters.undertow.ServletSessionTokenStore] (default task-3) session was null, returning null
2020-06-13 22:20:10,628 DEBUG [org.keycloak.adapters.OAuthRequestAuthenticator] (default task-3) there was no code
2020-06-13 22:20:10,628 DEBUG [org.keycloak.adapters.OAuthRequestAuthenticator] (default task-3) redirecting to auth server
2020-06-13 22:20:10,628 DEBUG [org.keycloak.adapters.OAuthRequestAuthenticator] (default task-2) callback uri: http://apicurio-studio-ui.192.168.1.5.nip.io/version.js
2020-06-13 22:20:10,628 DEBUG [org.keycloak.adapters.OAuthRequestAuthenticator] (default task-3) callback uri: http://apicurio-studio-ui.192.168.1.5.nip.io/config.js
2020-06-13 22:20:10,628 DEBUG [io.undertow.request.security] (default task-2) Authentication outcome was NOT_ATTEMPTED with method org.keycloak.adapters.wildfly.WildflyAuthenticationMechanism<strong i="7">@26c8577b</strong> for /version.js
2020-06-13 22:20:10,628 DEBUG [io.undertow.request.security] (default task-2) Authentication result was ATTEMPTED for /version.js
2020-06-13 22:20:10,628 DEBUG [io.undertow.request.security] (default task-3) Authentication outcome was NOT_ATTEMPTED with method org.keycloak.adapters.wildfly.WildflyAuthenticationMechanism<strong i="8">@26c8577b</strong> for /config.js
2020-06-13 22:20:10,628 DEBUG [io.undertow.request.security] (default task-3) Sending authentication challenge for HttpServerExchange{ GET /config.js}
2020-06-13 22:20:10,628 DEBUG [io.undertow.request] (default task-2) Matched default handler path /version.js
2020-06-13 22:20:10,628 DEBUG [org.keycloak.adapters.AuthenticatedActionsHandler] (default task-2) AuthenticatedActionsValve.invoke http://apicurio-studio-ui.192.168.1.5.nip.io/version.js
2020-06-13 22:20:10,629 DEBUG [org.keycloak.adapters.AuthenticatedActionsHandler] (default task-2) Policy enforcement is disabled.
2020-06-13 22:20:10,629 DEBUG [io.undertow.session] (default task-3) Created session with id oIOfXf1RPvC2PBpI4dTuVZKYk7f8BDg0XUAFHWrC for exchange HttpServerExchange{ GET /config.js}
2020-06-13 22:20:10,629 DEBUG [org.keycloak.adapters.OAuthRequestAuthenticator] (default task-3) Sending redirect to login page: http://keycloak-microcks.192.168.1.5.nip.io/auth/realms/Apicurio/protocol/openid-connect/auth?response_type=code&client_id=apicurio-studio&redirect_uri=http%3A%2F%2Fapicurio-studio-ui.192.168.1.5.nip.io%2Fconfig.js&state=3e4e092e-bc54-4802-a517-d3462de6f343&login=true&scope=openid
2020-06-13 22:20:10,629 DEBUG [io.undertow.request.security] (default task-3) Authentication result was CHALLENGE_SENT for /config.js
2020-06-13 22:21:00,918 DEBUG [io.undertow.request] (default I/O-2) Timing out idle connection from /10.1.94.1:41180
2020-06-13 22:21:00,918 DEBUG [io.undertow.request] (default I/O-2) Timing out idle connection from /10.1.94.1:41178
2020-06-13 22:21:48,749 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) PeriodicRecovery: background thread Status <== SCANNING
2020-06-13 22:21:48,749 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) PeriodicRecovery: background thread scanning
2020-06-13 22:21:48,749 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Periodic recovery first pass at Sat, 13 Jun 2020 22:21:48
2020-06-13 22:21:48,750 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) processing /StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction transactions
2020-06-13 22:21:48,750 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.jta.recovery.arjunacore.CommitMarkableResourceRecordRecoveryModule<strong i="9">@3bc2db45</strong>' first pass processed
2020-06-13 22:21:48,750 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) AtomicActionRecoveryModule first pass
2020-06-13 22:21:48,750 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) processing /StateManager/BasicAction/TwoPhaseCoordinator/AtomicAction transactions
2020-06-13 22:21:48,750 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.arjuna.recovery.AtomicActionRecoveryModule<strong i="10">@7ec04259</strong>' first pass processed
2020-06-13 22:21:48,750 DEBUG [com.arjuna.ats.txoj] (Periodic Recovery) TORecoveryModule - first pass
2020-06-13 22:21:48,750 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.txoj.recovery.TORecoveryModule<strong i="11">@1a9ffeac</strong>' first pass processed
2020-06-13 22:21:48,751 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.jta.recovery.arjunacore.SubordinateAtomicActionRecoveryModule<strong i="12">@1aa1c748</strong>' first pass processed
2020-06-13 22:21:48,751 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) XARecoveryModule state change IDLE->FIRST_PASS
2020-06-13 22:21:48,751 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) Local XARecoveryModule - first pass
2020-06-13 22:21:48,751 DEBUG [com.arjuna.ats.jta] (Periodic Recovery) XARecoveryModule state change FIRST_PASS->BETWEEN_PASSES
2020-06-13 22:21:48,751 DEBUG [com.arjuna.ats.arjuna] (Periodic Recovery) Recovery module 'com.arjuna.ats.internal.jta.recovery.arjunacore.XARecoveryModule<strong i="13">@5b07f3c7</strong>' first pass processed
๊ทธ๋ฌ๋ ๋ฌธ์ ๋ฅผ ์ค๋ช ํ ์ ์๋ ๋ธ๋ผ์ฐ์ ๋ก๊ทธ๋ฅผ ๋ฐ๊ฒฌํ์ต๋๋ค.
main.a117148โฆ.js:1 [ApiEditingSession] WS connection to server CLOSED:
CloseEvent {isTrusted: true, wasClean: false, code: 1006, reason: "", type: "close", โฆ}
bubbles: false
cancelBubble: false
cancelable: false
code: 1006
composed: false
currentTarget: WebSocket {__zone_symbol__openfalse: Array(1), __zone_symbol__messagefalse: Array(1), __zone_symbol__ON_PROPERTYopen: ฦ, __zone_symbol__ON_PROPERTYmessage: ฦ, __zone_symbol__ON_PROPERTYclose: ฦ, โฆ}
defaultPrevented: false
eventPhase: 0
isTrusted: true
path: []
reason: ""
returnValue: true
srcElement: WebSocket {__zone_symbol__openfalse: Array(1), __zone_symbol__messagefalse: Array(1), __zone_symbol__ON_PROPERTYopen: ฦ, __zone_symbol__ON_PROPERTYmessage: ฦ, __zone_symbol__ON_PROPERTYclose: ฦ, โฆ}
target: WebSocket {__zone_symbol__openfalse: Array(1), __zone_symbol__messagefalse: Array(1), __zone_symbol__ON_PROPERTYopen: ฦ, __zone_symbol__ON_PROPERTYmessage: ฦ, __zone_symbol__ON_PROPERTYclose: ฦ, โฆ}
timeStamp: 1177278.1400000094
type: "close"
wasClean: false
__proto__: CloseEvent
์น ์์ผ ์์ฆ์ด ์ค๋ฅ ์ฝ๋ 1006์ผ๋ก ์ข ๋ฃ๋ฉ๋๋ค.
ํฌํจ๋ ์๋ฒ ๋ก๊ทธ๋ ์ค์ ๋ก $#$ -ws
-ui
$ ํฌ๋์ ๋ก๊ทธ์
๋๋ค.
์ค๋ฅ ์ฝ๋ 1006์ ์น ์์ผ์ด ๋ธ๋ผ์ฐ์ ์ ์ํด ๋น์ ์์ ์ผ๋ก ๋ซํ์์ ์๋ฏธํฉ๋๋ค. :( ๊ทธ๋ฆฌ๊ณ ์ฌํ๊ฒ๋ ๋ธ๋ผ์ฐ์ ๋ ๋น์ ์๊ฒ ์ด์ ๋ฅผ ์ฃผ์ง ์์ ๊ฒ์ ๋๋ค. ์ฌ๊ธฐ์ ๋ช ๊ฐ์ง ์ ๋ณด๊ฐ ์์ต๋๋ค:
๋น์ ์ด ์๋ํ ์ ์๋ ๊ฒ์ ์ผ์ข ์ ์น ์์ผ ํ ์คํธ ๋๊ตฌ๋ฅผ ์ฐพ์ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค. ์ฐ๋ฆฌ๊ฐ ์ฌ์ฉํ ์ ์๋ ๋ช ๊ฐ์ง ์ง๋จ ์ ๋ณด๋ฅผ ์ ๊ณตํ ์ ์์ต๋๋ค.
๋ค์ ๋ช ๋ น์ผ๋ก ๊ฒ์๋ ๋ก๊ทธ
kubectl logs apicurio-studio-ws-7c458cf8d8-cgscc
๋ด๊ฐ ๊ฒ์ํ ์ ์๋ ํน์ ๋ก๊ทธ๊ฐ ์์ต๋๊น?
์น ์์ผ ํ ์คํธ์ ๋ํ ๊ถ์ฅ ์ฌํญ์ด ์์ต๋๊น?
https://websocket.org/echo.html
๋ฅผ ํตํด ์น ์์ผ์ ํ
์คํธํ๋ ๋ฐฉ๋ฒ์ ์ฐพ์์ต๋๋ค.
๋ด ์ปดํจํฐ์ ws ์ฐ๊ฒฐ ์ฝ๋๋ฅผ ๋ณต์ฌํ์ต๋๋ค. ์ด๋ค ๋ฉ์์ง๋ฅผ ๋ณด๋ด์ผ ํฉ๋๊น?
์์ ๊ฒ์ํ ์ถ๋ ฅ์ด config.js
๋ฐ version.js
์ ๋ํ ์์ฒญ๋ฟ๋ง ์๋๋ผ keycloak ์ธ์ฆ ๋ฆฌ๋๋ ์
๋ฐ ๊ธฐํ keycloak ๊ด๋ จ ์ถ๋ ฅ์ ๋ช
ํํ๊ฒ ๋ณด์ฌ์ฃผ๊ธฐ ๋๋ฌธ์ ์ ๋ง ์ด์ํฉ๋๋ค. ์ด๊ฒ๋ค์ ๋ชจ๋ UI๊ฐ ํ๋ ์ผ์
๋๋ค. websocket ๊ตฌ์ฑ ์์์๋ Keycloak์ด ์ ํ ํฌํจ๋์ด ์์ง ์์ต๋๋ค. ์ธ ๊ฐ์ ํ(Pod) ๋ชจ๋ ์์ ๋ก๊ทธ ์ถ๋ ฅ์ ๊ฐ์ ธ์ฌ ์ ์์ต๋๊น? ํฅ๋ฏธ๋ก์ธ ๊ฒ์
๋๋ค. ์๋ง๋ ์ค์ ๋ก ํฌ๋์ ๋ฌธ์ ๊ฐ ์์ ์ ์์ต๋๋ค!
์น ์์ผ ํ ์คํธ์ ๊ดํด์. ์ฐ๊ฒฐ์ ์๋ํ๊ณ ๋ฌด์จ ์ผ์ด ์ผ์ด๋๋์ง ๋ณด์ญ์์ค. ์๋ํ๋ ๊ฒฝ์ฐ ๋ค์๊ณผ ๊ฐ์ด ํ์๋์ด์ผ ํฉ๋๋ค.
"์ฐ๊ฒฐ" ๋จ๊ณ์์ ์ฐ๊ฒฐ์ด ๋์ด์ง๋ฉด ์ด๊ธฐ ์ฐ๊ฒฐ์ด ์คํจํ ์ ์์ต๋๋ค.
websocket.org์์ ์ ๊ณตํ๋ ์ฝ๋๋ฅผ ๋ณต์ฌํ์ฌ websocket์ ํ
์คํธํ๊ณ ํ๋ ๋๋ผ์ด๋ธ์ websocket.html๋ก ์ ์ฅํ์ฌ ๋ก์ปฌ ๋คํธ์ํฌ์์ ws ํฌ๋๋ฅผ ํ
์คํธํ์ต๋๋ค. ๋ถํํ๋ ๋ก์ปฌ ๋คํธ์ํฌ์์ ws์ ์ฐ๊ฒฐํ ์ ์์์ต๋๋ค. ๊ทธ๋ฌ๋ ๊ณต๊ฐ ์น ์์ผ์ ์ฐ๊ฒฐํ ์ ์์์ต๋๋ค.
wss://studio-ws.apicur.io/designs
. ๋ฐ๋ผ์ ์น ์์ผ ํ
์คํธ๊ฐ ์คํ ๊ฐ๋ฅํ๋ค๊ณ ๊ฐ์ ํฉ๋๋ค.
์ฒจ๋ถ ํ์ผ์ ์๋ ๋ชจ๋ ๋ก๊ทธ๋ฅผ ์ ๊ณตํ์ต๋๋ค.
kubectl ๋ก๊ทธ apicurio-studio-ui-5c6f5df485-lcdxp > apicurio-studio-ui.log
kubectl ๋ก๊ทธ apicurio-studio-ws-77dc7f7b87-57dcs > apicurio-studio-ws.log
kubectl ๋ก๊ทธ apicurio-studio-api-79d9d799cb-mgsmx > apicurio-studio-api.log
apicurio-studio-api.log
apicurio-studio-ui.log
apicurio-studio-ws.log
๋๋์ด ์๋ํ๊ฒ ๋์์ต๋๋ค! NodePort๋ฅผ ํตํด ws
ํฌ๋๋ฅผ ์ง์ ๋
ธ์ถํ์ต๋๋ค. ์ด์ ๋ฌธ์ ๊ฐ ์์ ๊ตฌ์ฑ์ ์์์ ํ์ ํฉ๋๋ค.
์์ฒญ๋!! (ํฌ๊ธฐํ์ง ์๊ณ ) ์ถ์งํด ์ฃผ์ ์ ๊ฐ์ฌํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋ ๋ง์ ๋์์ ๋๋ฆฌ์ง ๋ชปํด ์ฃ์กํฉ๋๋ค. ๋ด kubernetes ์ง์ ์์ค์ด ๋ฎ์ต๋๋ค. ์๋ํ๋๋ก ์ถ๊ฐํ๋ ๋ฐ ํ์ํ YAML ๊ตฌ์ฑ์ ๊ฒ์ํ ์ ์์ต๋๊น? ์ฐ๋ฆฌ๋ ๊ฒฐ๊ตญ Apicurio Studio๋ฅผ ์ํ ์คํผ๋ ์ดํฐ๋ฅผ ๋ง๋ค๊ฒ ๋ ๊ฒ์ ๋๋ค. ๊ทธ๋ฌ๋ ๊ทธ๊ฒ์ด ์ธ์ ์ผ์ด๋ ์ง ์ ๋ ์ ํํ ๋ชจ๋ฆ ๋๋ค.
์์งํ ๋งํด์ ํน์ yaml ๊ตฌ์ฑ์ ๋ง๋ค์ง ์์์ต๋๋ค.
ws pods 8080 ํฌํธ๊ฐ ์์ ํฌํธ์ ๋จธ์ IP์ ๋ ธ์ถ๋จ์ ์๋ฏธํ๋ NodePort ์๋น์ค๋ฅผ ์์ฑํฉ๋๋ค.
์ด๊ฒ์ด ๋ด๊ฐ NodePort๋ฅผ ๋ง๋ ๋ฐฉ๋ฒ์
๋๋ค.
kubectl expose deployment apicurio-studio-ws --type NodePort --name test
์ปดํจํฐ IP:32204๋ฅผ ํตํด ์ก์ธ์คํ ์ ์๋ ์์์ ํฌํธ 32204๋ฅผ ์ ๊ณตํ์ต๋๋ค.
kubectl์ svc๋ฅผ ์ป์ต๋๋ค.
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
test NodePort 10.152.183.2 <none> 8080:32204/TCP 10h
๋๋ ์ฌ์ ํ NodePort ํฌํธ๊ฐ ๋ฌด์์์ด๊ณ apicurio-configmap.yaml
์์ ๋ฏธ๋ฆฌ ๊ตฌ์ฑํ ์ ์๊ธฐ ๋๋ฌธ์ ingress nginx๊ฐ websocket์ ๋
ธ์ถํ๋ ๋ฐ ์ ํธ๋๋ ๋ฐฉ๋ฒ์ด์ด์ผ ํ๋ค๊ณ ์๊ฐํฉ๋๋ค.
๋ด ๊ฒฝ์ฐ์๋
apicurio-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: apicurio-configmap
data:
apicurio-db-connection-url: jdbc:mysql://apicuriodb:3306/apicuriodb
apicurio-kc-client-id: apicurio-studio
apicurio-kc-realm: Apicurio
apicurio-microcks-api-url: http://microcks-microcks.192.168.1.5.nip.io/api
apicurio-microcks-client-id: microcks-serviceaccount
apicurio-ui-editing-url: ws://192.168.1.5:32204/
apicurio-ui-feature-microcks: "true"
apicurio-ui-feature-share-with-everyone: "true"
apicurio-ui-hub-api-url: http://apicurio.192.168.1.5.nip.io/studio-api
apicurio-ui-logout-redirect-uri: /
keycloak-url: http://keycloak-microcks.192.168.1.5.nip.io/auth
์์ฃผ ํฅ๋ฏธ๋กญ์ต๋๋ค. nginx ์ ๊ทผ ๋ฐฉ์์ด ํจ๊ณผ๊ฐ ์๋ ์ด์ ๋ฅผ ๋ชจ๋ฅด๊ฒ ์ต๋๋ค. :( ํ์ง๋ง ๋น์ ์ด ๋ญ๊ฐ ์๋ํ๊ฒ ๋์ด์ ๊ธฐ์ฉ๋๋ค!
ingress๋ฅผ ํตํด ws pod๋ฅผ ๋ ธ์ถํ๋ ๋ฐฉ๋ฒ์ ์ฐพ์์ต๋๋ค. ์ด ์๋ฃจ์ ์ ํจ์ฌ ๋ ๊ตฌ์ฑ์ด ๊ฐ๋ฅํฉ๋๋ค.
์ด ์๋ฃจ์ ์์๋ ์์ ์ ํตํด WS ํฌ๋์ ๋ํ ๋ณ๋์ URL์ ๋ง๋ค์์ต๋๋ค.
ํ๊ฐ๋ฅผ ์ํ PR์ ์ ๊ณตํฉ๋๋ค.
์ด๋ด, ํํฐ์ ๋ฆ์ด์ ๋ฏธ์ํด.
@cemnura ์ด๋ค ์ธ๊ทธ๋ ์ค๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ฉฐ ์ด๋ค ๋ฒ์ ์์ ์ฌ์ฉํ๊ณ ์์ต๋๊น?
nginx-ingress-controller๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ๋ง์ ์์ ๊ณผ ๋ณ๊ฒฝ์ด ์์์ต๋๋ค.
ํ ๊ฐ์ง ์ด์ํ ์ ์ ์น ์์ผ ๋์, ํนํ http/https ๊ฐ์ ์ ํํ๋ ค๊ณ ํ ๋์ ๋๋ค.
@EricWittmann ์ํ๋ ๊ฒฝ์ฐ ํจ์น ๋ฒ์ ์ด ์๋ํ๋์ง ํ ์คํธํ ์ ์์ต๋๋ค(nginx-ingress-version์ด ์ต์ ๋ฒ์ ์ผ๋ก ์ ๋ฐ์ดํธ๋จ).
์ ๊ณตํ ์ ์๋ ๋ชจ๋ ๋์์ ์ฃผ์๋ฉด ๊ฐ์ฌํ๊ฒ ์ต๋๋ค. @t-rap
์๋
ํ์ธ์ @t-rap ์ ๊ฐ ์ดํดํ๋ ํ nginx-ingress-controller-amd64
๋ฅผ ์ฌ์ฉํ๋ microk8s Kubernetes๋ฅผ ์คํํ๊ณ ์์ต๋๋ค.
Name: nginx-ingress-microk8s-controller-nd8vb
Namespace: ingress
Priority: 0
Node: mccloud/192.168.1.5
Start Time: Tue, 02 Jun 2020 23:12:28 +0300
Labels: controller-revision-hash=59cb5dd586
name=nginx-ingress-microk8s
pod-template-generation=1
Annotations: <none>
Status: Running
IP: 192.168.1.5
IPs:
IP: 192.168.1.5
Controlled By: DaemonSet/nginx-ingress-microk8s-controller
Containers:
nginx-ingress-microk8s:
Container ID: containerd://3219d168e8fbb190acd214ab651f781f6adf51adcc2302891636a5ff250ef15f
Image: quay.io/kubernetes-ingress-controller/nginx-ingress-controller-amd64:0.25.1
Image ID: sha256:2b8ed1f2046d4b37c18cca2ecc4f435b6618d2d198c0c8bf617954e863cc5832
Ports: 80/TCP, 443/TCP
Host Ports: 80/TCP, 443/TCP
Args:
/nginx-ingress-controller
--configmap=$(POD_NAMESPACE)/nginx-load-balancer-microk8s-conf
--publish-status-address=127.0.0.1
State: Running
Started: Fri, 12 Jun 2020 15:08:07 +0300
Last State: Terminated
Reason: Completed
Exit Code: 0
Started: Wed, 10 Jun 2020 23:37:02 +0300
Finished: Fri, 12 Jun 2020 15:08:03 +0300
Ready: True
Restart Count: 187
Liveness: http-get http://:10254/healthz delay=30s timeout=5s period=10s #success=1 #failure=3
Environment:
POD_NAME: nginx-ingress-microk8s-controller-nd8vb (v1:metadata.name)
POD_NAMESPACE: ingress (v1:metadata.namespace)
Mounts:
/var/run/secrets/kubernetes.io/serviceaccount from nginx-ingress-microk8s-serviceaccount-token-r6md6 (ro)
Conditions:
Type Status
Initialized True
Ready True
ContainersReady True
PodScheduled True
Volumes:
nginx-ingress-microk8s-serviceaccount-token-r6md6:
Type: Secret (a volume populated by a Secret)
SecretName: nginx-ingress-microk8s-serviceaccount-token-r6md6
Optional: false
QoS Class: BestEffort
Node-Selectors: <none>
Tolerations: node.kubernetes.io/disk-pressure:NoSchedule
node.kubernetes.io/memory-pressure:NoSchedule
node.kubernetes.io/network-unavailable:NoSchedule
node.kubernetes.io/not-ready:NoExecute
node.kubernetes.io/pid-pressure:NoSchedule
node.kubernetes.io/unreachable:NoExecute
node.kubernetes.io/unschedulable:NoSchedule
Events: <none>
@cemnura ๊ฐ์ฌํฉ๋๋ค.
๋ด๊ฐ ๋ง๋ ํฌํฌ๋ฅผ ํ
์คํธํ ์ ์์ต๋๊น(์๋ ์ฐธ์กฐ)? (๋ค์) ๋ฏธ๋ฆฌ ๊ฐ์ฌ๋๋ฆฝ๋๋ค :)
๋๋ ingress-nginx-controller ๋ฒ์ 0.24.1 ๋ฐ apicurio-studio 0.2.44.Final์ ์ฌ์ฉํ์ฌ https://github.com/t-rap/apicurio-studio-981/ ์์ ๊ตฌ์ฑ์ ํ
์คํธํ์ต๋๋ค.
๋๋ ws์์ ์ฒ๋ฆฌํด์ผ ํ๋ ํ์ ๊ฒฝ๋ก์ด๊ธฐ ๋๋ฌธ์ ingress apicurio-studio-ws์ ํ์ ๊ฒฝ๋ก "/designs"๋ฅผ ์ถ๊ฐํ์ต๋๋ค.
๋๋ ingress-controller 0.25.1๊ณผ apicurio-studio 0.2.46.Final์ ๋ํ ๋ช ๊ฐ์ง ํ
์คํธ๋ฅผ ์คํํ ๊ฒ์
๋๋ค. ์๋ง๋ ์ฃผ๋ง์ ์์ ๊ฒ์
๋๋ค.
์ธ๊ทธ๋ ์ค ์ปจํธ๋กค๋ฌ๊ฐ kubernetes์ ์ํด ์ ์ง ๊ด๋ฆฌ๋๊ธฐ ๋๋ฌธ์ ์ธ์ํด์ผ ํ ๋ณ๊ฒฝ ์ฌํญ์ด ๋ง์ต๋๋ค.
๋ ์ด์ ํ์ํ์ง ์์ผ๋ฏ๋ก ๊ตฌ์ฑ ์ค๋ํซ๋ ์ญ์ ํ์ต๋๋ค.
์๋ฃ๋๋ฉด ์ ๋ฐ์ดํธํ๊ฒ ์ต๋๋ค.
ํด๋ณผ๊ฒ @t-rap ๐
์ ๊ณต๋ Kubernetes ๋งค๋ํ์คํธ @t-rap์ ์ฌ์ฉํ์ฌ ui
ํฌ๋์ ๋์ผํ URL์ ws
ํฌ๋๋ฅผ ๋
ธ์ถํ ์ ์์์ต๋๋ค.
๊ทธ๋ฌ๋ ๋ช ๊ฐ์ง ์ฌ์ํ ๋ณ๊ฒฝ ์ฌํญ์ด ์์ต๋๋ค.
ws pod ์์ ์ด๋ฆ์ apicurio-studio-ws
๋ก ๋ณ๊ฒฝํ์ต๋๋ค.
apicurio-ui-editing-url
ํค์ apicurio-configmap.yaml
์์ /ws
์ ๋ฏธ์ฌ๋ฅผ ์ญ์ ํ์ต๋๋ค.
apicurio-studio-ingresses.yaml
์ ์์ ๊ตฌ์ฑ์์ /ws
๊ฒฝ๋ก๋ฅผ ์ญ์ ํ์ต๋๋ค.
์ฌ๊ธฐ ๋ด ๊ตฌ์ฑ์ด ์์ต๋๋ค
apicurio-configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: apicurio-configmap
data:
apicurio-db-connection-url: jdbc:mysql://apicuriodb:3306/apicuriodb
apicurio-kc-client-id: apicurio-studio
apicurio-kc-realm: Apicurio
apicurio-microcks-api-url: http://microcks-microcks.192.168.1.5.nip.io/api
apicurio-microcks-client-id: microcks-serviceaccount
apicurio-ui-editing-url: ws://apicurio.192.168.1.5.nip.io
apicurio-ui-feature-microcks: "true"
apicurio-ui-feature-share-with-everyone: "true"
apicurio-ui-hub-api-url: http://apicurio.192.168.1.5.nip.io/studio-api
apicurio-ui-logout-redirect-uri: /
keycloak-url: http://keycloak-microcks.192.168.1.5.nip.io/auth
apicurio-studio-ingresses.yaml
kind: Ingress
apiVersion: extensions/v1beta1
metadata:
name: apicurio-studio-api
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /$1
labels:
module: apicurio-studio-api
spec:
rules:
- host: apicurio.192.168.1.5.nip.io
http:
paths:
- backend:
serviceName: "apicurio-studio-api"
servicePort: 8091
path: /studio-api/?(.*)
---
kind: Ingress
apiVersion: extensions/v1beta1
metadata:
name: apicurio-studio-ui
labels:
module: apicurio-studio-ui
spec:
rules:
- host: apicurio.192.168.1.5.nip.io
http:
paths:
- backend:
serviceName: "apicurio-studio-ui"
servicePort: 8093
path: /
---
kind: Ingress
apiVersion: extensions/v1beta1
metadata:
name: apicurio-studio-ws
annotations:
labels:
module: apicurio-studio-ws
spec:
rules:
- host: apicurio.192.168.1.5.nip.io
http:
paths:
- backend:
serviceName: "apicurio-studio-ws"
servicePort: 8092
path: /designs
์ข์ ์์! ์ด๋ฌํ ๋ณ๊ฒฝ ์ฌํญ์ ๋ํด ๋ณ๋์ PR์ด ํ์ํ๋ค๊ณ ๊ฐ์ ํฉ๋๊น? @t-rap๊ณผ @jsenko ๋ ์ด๋ป๊ฒ ์๊ฐํ์ธ์?
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์ข์ ์์! ์ด๋ฌํ ๋ณ๊ฒฝ ์ฌํญ์ ๋ํด ๋ณ๋์ PR์ด ํ์ํ๋ค๊ณ ๊ฐ์ ํฉ๋๊น? @t-rap๊ณผ @jsenko ๋ ์ด๋ป๊ฒ ์๊ฐํ์ธ์?