index.ts 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  1. import * as awsx from '@pulumi/awsx'
  2. import * as eks from '@pulumi/eks'
  3. import * as pulumi from '@pulumi/pulumi'
  4. import * as k8s from '@pulumi/kubernetes'
  5. import { configMapFromFile } from './configMap'
  6. import { CaddyServiceDeployment } from 'pulumi-common'
  7. import { getSubkeyContainers } from './utils'
  8. import { ValidatorServiceDeployment } from './validator'
  9. import { NFSServiceDeployment } from './nfsVolume'
  10. // const { exec } = require('child_process')
  11. const config = new pulumi.Config()
  12. const awsConfig = new pulumi.Config('aws')
  13. const isMinikube = config.getBoolean('isMinikube')
  14. export let kubeconfig: pulumi.Output<any>
  15. let provider: k8s.Provider
  16. if (isMinikube) {
  17. provider = new k8s.Provider('local', {})
  18. } else {
  19. // Create a VPC for our cluster.
  20. const vpc = new awsx.ec2.Vpc('joystream-node-vpc', { numberOfAvailabilityZones: 2 })
  21. // Create an EKS cluster with the default configuration.
  22. const cluster = new eks.Cluster('eksctl-node-network', {
  23. vpcId: vpc.id,
  24. subnetIds: vpc.publicSubnetIds,
  25. desiredCapacity: 2,
  26. maxSize: 2,
  27. instanceType: 't2.medium',
  28. providerCredentialOpts: {
  29. profileName: awsConfig.get('profile'),
  30. },
  31. })
  32. provider = cluster.provider
  33. // Export the cluster's kubeconfig.
  34. kubeconfig = cluster.kubeconfig
  35. }
  36. const resourceOptions = { provider: provider }
  37. const name = 'node-network'
  38. // Create a Kubernetes Namespace
  39. const ns = new k8s.core.v1.Namespace(name, {}, resourceOptions)
  40. // Export the Namespace name
  41. export const namespaceName = ns.metadata.name
  42. const appLabels = { appClass: name }
  43. const networkSuffix = config.get('networkSuffix') || '8129'
  44. const numberOfValidators = config.getNumber('numberOfValidators') || 1
  45. const chainDataPath = '/chain-data'
  46. const chainSpecPath = `${chainDataPath}/chainspec-raw.json`
  47. const nodeImage = config.get('nodeImage') || 'joystream/node:latest'
  48. const subkeyContainers = getSubkeyContainers(numberOfValidators, chainDataPath)
  49. let pvcClaimName: pulumi.Output<any>
  50. if (isMinikube) {
  51. const pvc = new k8s.core.v1.PersistentVolumeClaim(
  52. `${name}-pvc`,
  53. {
  54. metadata: {
  55. labels: appLabels,
  56. namespace: namespaceName,
  57. name: `${name}-pvc`,
  58. },
  59. spec: {
  60. accessModes: ['ReadWriteMany'],
  61. resources: {
  62. requests: {
  63. storage: `1Gi`,
  64. },
  65. },
  66. },
  67. },
  68. resourceOptions
  69. )
  70. const pv = new k8s.core.v1.PersistentVolume(`${name}-pv`, {
  71. metadata: {
  72. labels: { ...appLabels, type: 'local' },
  73. namespace: namespaceName,
  74. name: `${name}-pv`,
  75. },
  76. spec: {
  77. accessModes: ['ReadWriteMany'],
  78. capacity: {
  79. storage: `1Gi`,
  80. },
  81. hostPath: {
  82. path: '/mnt/data/',
  83. },
  84. },
  85. })
  86. pvcClaimName = pvc.metadata.apply((m) => m.name)
  87. } else {
  88. const nfsVolume = new NFSServiceDeployment('nfs-server', { namespace: namespaceName }, resourceOptions)
  89. pvcClaimName = nfsVolume.pvc.metadata.apply((m) => m.name)
  90. }
  91. const jsonModifyConfig = new configMapFromFile(
  92. 'json-modify-config',
  93. {
  94. filePath: 'json_modify.py',
  95. namespaceName: namespaceName,
  96. },
  97. resourceOptions
  98. ).configName
  99. const chainDataPrepareJob = new k8s.batch.v1.Job(
  100. 'chain-data',
  101. {
  102. metadata: {
  103. namespace: namespaceName,
  104. },
  105. spec: {
  106. backoffLimit: 0,
  107. template: {
  108. spec: {
  109. containers: [
  110. ...subkeyContainers,
  111. {
  112. name: 'builder-node',
  113. image: nodeImage,
  114. command: ['/bin/sh', '-c'],
  115. args: [
  116. `/joystream/chain-spec-builder generate -a ${numberOfValidators} \
  117. --chain-spec-path ${chainDataPath}/chainspec.json --deployment live \
  118. --endowed 1 --keystore-path ${chainDataPath}/data > ${chainDataPath}/seeds.txt`,
  119. ],
  120. volumeMounts: [
  121. {
  122. name: 'config-data',
  123. mountPath: chainDataPath,
  124. },
  125. ],
  126. },
  127. {
  128. name: 'json-modify',
  129. image: 'python',
  130. command: ['python'],
  131. args: [
  132. '/scripts/json_modify.py',
  133. '--path',
  134. `${chainDataPath}`,
  135. '--prefix',
  136. networkSuffix,
  137. '--validators',
  138. `${numberOfValidators}`,
  139. ],
  140. volumeMounts: [
  141. {
  142. mountPath: '/scripts/json_modify.py',
  143. name: 'json-modify-script',
  144. subPath: 'fileData',
  145. },
  146. {
  147. name: 'config-data',
  148. mountPath: chainDataPath,
  149. },
  150. ],
  151. },
  152. {
  153. name: 'raw-chain-spec',
  154. image: nodeImage,
  155. command: ['/bin/sh', '-c'],
  156. args: [`/joystream/node build-spec --chain ${chainDataPath}/chainspec.json --raw > ${chainSpecPath}`],
  157. volumeMounts: [
  158. {
  159. name: 'config-data',
  160. mountPath: chainDataPath,
  161. },
  162. ],
  163. },
  164. ],
  165. volumes: [
  166. {
  167. name: 'json-modify-script',
  168. configMap: {
  169. name: jsonModifyConfig,
  170. },
  171. },
  172. {
  173. name: 'config-data',
  174. persistentVolumeClaim: {
  175. claimName: pvcClaimName,
  176. },
  177. },
  178. ],
  179. restartPolicy: 'Never',
  180. },
  181. },
  182. },
  183. },
  184. { ...resourceOptions }
  185. )
  186. // Create N validator service deployments
  187. const validators = []
  188. for (let i = 1; i <= numberOfValidators; i++) {
  189. const validator = new ValidatorServiceDeployment(
  190. `node-${i}`,
  191. { namespace: namespaceName, index: i, chainSpecPath, dataPath: chainDataPath, pvc: pvcClaimName, nodeImage },
  192. { ...resourceOptions, dependsOn: chainDataPrepareJob }
  193. )
  194. validators.push(validator)
  195. }
  196. const deployment = new k8s.apps.v1.Deployment(
  197. `rpc-node`,
  198. {
  199. metadata: {
  200. namespace: namespaceName,
  201. labels: appLabels,
  202. },
  203. spec: {
  204. replicas: 1,
  205. selector: { matchLabels: appLabels },
  206. template: {
  207. metadata: {
  208. labels: appLabels,
  209. },
  210. spec: {
  211. initContainers: [],
  212. containers: [
  213. {
  214. name: 'rpc-node',
  215. image: nodeImage,
  216. ports: [
  217. { name: 'rpc-9944', containerPort: 9944 },
  218. { name: 'rpc-9933', containerPort: 9933 },
  219. { name: 'rpc-30333', containerPort: 30333 },
  220. ],
  221. args: [
  222. '--chain',
  223. chainSpecPath,
  224. '--ws-external',
  225. '--rpc-cors',
  226. 'all',
  227. '--pruning',
  228. 'archive',
  229. '--ws-max-connections',
  230. '512',
  231. '--telemetry-url',
  232. 'wss://telemetry.joystream.org/submit/ 0',
  233. '--telemetry-url',
  234. 'wss://telemetry.polkadot.io/submit/ 0',
  235. ],
  236. volumeMounts: [
  237. {
  238. name: 'config-data',
  239. mountPath: chainDataPath,
  240. },
  241. ],
  242. },
  243. ],
  244. volumes: [
  245. {
  246. name: 'config-data',
  247. persistentVolumeClaim: {
  248. claimName: pvcClaimName,
  249. },
  250. },
  251. ],
  252. },
  253. },
  254. },
  255. },
  256. { ...resourceOptions, dependsOn: validators }
  257. )
  258. // Export the Deployment name
  259. export const deploymentName = deployment.metadata.name
  260. // Create a Service for the RPC Node
  261. const service = new k8s.core.v1.Service(
  262. name,
  263. {
  264. metadata: {
  265. labels: appLabels,
  266. namespace: namespaceName,
  267. name: 'node-network',
  268. },
  269. spec: {
  270. ports: [
  271. { name: 'port-1', port: 9944 },
  272. { name: 'port-2', port: 9933 },
  273. ],
  274. selector: appLabels,
  275. },
  276. },
  277. resourceOptions
  278. )
  279. // Export the Service name and public LoadBalancer Endpoint
  280. export const serviceName = service.metadata.name
  281. const lbReady = config.get('isLoadBalancerReady') === 'true'
  282. const caddyEndpoints = [
  283. `/ws-rpc {
  284. reverse_proxy node-network:9944
  285. }`,
  286. `/http-rpc {
  287. reverse_proxy node-network:9933
  288. }`,
  289. ]
  290. const caddy = new CaddyServiceDeployment(
  291. 'caddy-proxy',
  292. { lbReady, namespaceName: namespaceName, isMinikube, caddyEndpoints },
  293. resourceOptions
  294. )
  295. export const endpoint1 = caddy.primaryEndpoint
  296. export const endpoint2 = caddy.secondaryEndpoint