index.ts 8.3 KB

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