index.ts 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301
  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 encryptKey = config.get('encryptionKey') || '1234'
  49. const subkeyContainers = getSubkeyContainers(numberOfValidators, chainDataPath)
  50. const nfsVolume = new NFSServiceDeployment('nfs-server', { namespace: namespaceName }, resourceOptions)
  51. const pvcClaimName = nfsVolume.pvc.metadata.apply((m) => m.name)
  52. const jsonModifyConfig = new configMapFromFile(
  53. 'json-modify-config',
  54. {
  55. filePath: 'json_modify.py',
  56. namespaceName: namespaceName,
  57. },
  58. resourceOptions
  59. ).configName
  60. const chainDataPrepareJob = new k8s.batch.v1.Job(
  61. 'chain-data',
  62. {
  63. metadata: {
  64. namespace: namespaceName,
  65. },
  66. spec: {
  67. backoffLimit: 0,
  68. template: {
  69. spec: {
  70. containers: [
  71. ...subkeyContainers,
  72. {
  73. name: 'builder-node',
  74. image: nodeImage,
  75. command: ['/bin/sh', '-c'],
  76. args: [
  77. `/joystream/chain-spec-builder generate -a ${numberOfValidators} \
  78. --chain-spec-path ${chainDataPath}/chainspec.json --deployment live \
  79. --endowed 1 --keystore-path ${chainDataPath}/data > ${chainDataPath}/seeds.txt`,
  80. ],
  81. volumeMounts: [
  82. {
  83. name: 'config-data',
  84. mountPath: chainDataPath,
  85. },
  86. ],
  87. },
  88. {
  89. name: 'json-modify',
  90. image: 'python',
  91. command: ['python'],
  92. args: [
  93. '/scripts/json_modify.py',
  94. '--path',
  95. `${chainDataPath}`,
  96. '--prefix',
  97. networkSuffix,
  98. '--validators',
  99. `${numberOfValidators}`,
  100. ],
  101. volumeMounts: [
  102. {
  103. mountPath: '/scripts/json_modify.py',
  104. name: 'json-modify-script',
  105. subPath: 'fileData',
  106. },
  107. {
  108. name: 'config-data',
  109. mountPath: chainDataPath,
  110. },
  111. ],
  112. },
  113. {
  114. name: 'raw-chain-spec',
  115. image: nodeImage,
  116. command: ['/bin/sh', '-c'],
  117. args: [`/joystream/node build-spec --chain ${chainDataPath}/chainspec.json --raw > ${chainSpecPath}`],
  118. volumeMounts: [
  119. {
  120. name: 'config-data',
  121. mountPath: chainDataPath,
  122. },
  123. ],
  124. },
  125. {
  126. name: '7z',
  127. image: 'danielwhatmuff/7z-docker',
  128. command: ['/bin/sh', '-c'],
  129. args: [`7z a -p${encryptKey} ${chainDataPath}/chain-data.7z ${chainDataPath}/*`],
  130. volumeMounts: [
  131. {
  132. name: 'config-data',
  133. mountPath: chainDataPath,
  134. },
  135. ],
  136. },
  137. ],
  138. volumes: [
  139. {
  140. name: 'json-modify-script',
  141. configMap: {
  142. name: jsonModifyConfig,
  143. },
  144. },
  145. {
  146. name: 'config-data',
  147. persistentVolumeClaim: {
  148. claimName: pvcClaimName,
  149. },
  150. },
  151. ],
  152. restartPolicy: 'Never',
  153. },
  154. },
  155. },
  156. },
  157. { ...resourceOptions }
  158. )
  159. // Create N validator service deployments
  160. const validators = []
  161. for (let i = 1; i <= numberOfValidators; i++) {
  162. const validator = new ValidatorServiceDeployment(
  163. `node-${i}`,
  164. { namespace: namespaceName, index: i, chainSpecPath, dataPath: chainDataPath, pvc: pvcClaimName, nodeImage },
  165. { ...resourceOptions, dependsOn: chainDataPrepareJob }
  166. )
  167. validators.push(validator)
  168. }
  169. const deployment = new k8s.apps.v1.Deployment(
  170. `rpc-node`,
  171. {
  172. metadata: {
  173. namespace: namespaceName,
  174. labels: appLabels,
  175. },
  176. spec: {
  177. replicas: 1,
  178. selector: { matchLabels: appLabels },
  179. template: {
  180. metadata: {
  181. labels: appLabels,
  182. },
  183. spec: {
  184. initContainers: [],
  185. containers: [
  186. {
  187. name: 'rpc-node',
  188. image: nodeImage,
  189. ports: [
  190. { name: 'rpc-9944', containerPort: 9944 },
  191. { name: 'rpc-9933', containerPort: 9933 },
  192. { name: 'rpc-30333', containerPort: 30333 },
  193. ],
  194. args: [
  195. '--chain',
  196. chainSpecPath,
  197. '--ws-external',
  198. '--rpc-cors',
  199. 'all',
  200. '--pruning',
  201. 'archive',
  202. '--ws-max-connections',
  203. '512',
  204. '--telemetry-url',
  205. 'wss://telemetry.joystream.org/submit/ 0',
  206. '--telemetry-url',
  207. 'wss://telemetry.polkadot.io/submit/ 0',
  208. ],
  209. volumeMounts: [
  210. {
  211. name: 'config-data',
  212. mountPath: chainDataPath,
  213. },
  214. ],
  215. },
  216. ],
  217. volumes: [
  218. {
  219. name: 'config-data',
  220. persistentVolumeClaim: {
  221. claimName: pvcClaimName,
  222. },
  223. },
  224. ],
  225. },
  226. },
  227. },
  228. },
  229. { ...resourceOptions, dependsOn: validators }
  230. )
  231. // Export the Deployment name
  232. export const deploymentName = deployment.metadata.name
  233. // Create a Service for the RPC Node
  234. const service = new k8s.core.v1.Service(
  235. name,
  236. {
  237. metadata: {
  238. labels: appLabels,
  239. namespace: namespaceName,
  240. name: 'node-network',
  241. },
  242. spec: {
  243. type: isMinikube ? 'NodePort' : 'ClusterIP',
  244. ports: [
  245. { name: 'port-1', port: 9944 },
  246. { name: 'port-2', port: 9933 },
  247. ],
  248. selector: appLabels,
  249. },
  250. },
  251. resourceOptions
  252. )
  253. // Export the Service name and public LoadBalancer Endpoint
  254. export const serviceName = service.metadata.name
  255. const lbReady = config.get('isLoadBalancerReady') === 'true'
  256. const caddyEndpoints = [
  257. `/ws-rpc {
  258. reverse_proxy node-network:9944
  259. }`,
  260. `/http-rpc {
  261. reverse_proxy node-network:9933
  262. }`,
  263. ]
  264. export let endpoint1: pulumi.Output<string>
  265. export let endpoint2: pulumi.Output<string>
  266. if (!isMinikube) {
  267. const caddy = new CaddyServiceDeployment(
  268. 'caddy-proxy',
  269. { lbReady, namespaceName: namespaceName, isMinikube, caddyEndpoints },
  270. resourceOptions
  271. )
  272. endpoint1 = pulumi.interpolate`${caddy.primaryEndpoint}`
  273. endpoint2 = pulumi.interpolate`${caddy.secondaryEndpoint}`
  274. }