index.ts 9.1 KB

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