index.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. import * as awsx from '@pulumi/awsx'
  2. import * as aws from '@pulumi/aws'
  3. import * as eks from '@pulumi/eks'
  4. import * as k8s from '@pulumi/kubernetes'
  5. import * as pulumi from '@pulumi/pulumi'
  6. import { CaddyServiceDeployment } from 'pulumi-common'
  7. import * as fs from 'fs'
  8. const awsConfig = new pulumi.Config('aws')
  9. const config = new pulumi.Config()
  10. const wsProviderEndpointURI = config.require('wsProviderEndpointURI')
  11. const isAnonymous = config.require('isAnonymous') === 'true'
  12. const lbReady = config.get('isLoadBalancerReady') === 'true'
  13. const name = 'storage-node'
  14. const colossusPort = parseInt(config.get('colossusPort') || '3000')
  15. const storage = parseInt(config.get('storage') || '40')
  16. let additionalParams: string[] | pulumi.Input<string>[] = []
  17. let volumeMounts: pulumi.Input<pulumi.Input<k8s.types.input.core.v1.VolumeMount>[]> = []
  18. let volumes: pulumi.Input<pulumi.Input<k8s.types.input.core.v1.Volume>[]> = []
  19. // Create a VPC for our cluster.
  20. const vpc = new awsx.ec2.Vpc('storage-node-vpc', { numberOfAvailabilityZones: 2, numberOfNatGateways: 1 })
  21. // Create an EKS cluster with the default configuration.
  22. const cluster = new eks.Cluster('eksctl-storage-node', {
  23. vpcId: vpc.id,
  24. subnetIds: vpc.publicSubnetIds,
  25. instanceType: 't2.medium',
  26. providerCredentialOpts: {
  27. profileName: awsConfig.get('profile'),
  28. },
  29. })
  30. // Export the cluster's kubeconfig.
  31. export const kubeconfig = cluster.kubeconfig
  32. // Create a repository
  33. const repo = new awsx.ecr.Repository('colossus-image')
  34. // Build an image and publish it to our ECR repository.
  35. export const colossusImage = repo.buildAndPushImage({
  36. dockerfile: '../../../colossus.Dockerfile',
  37. context: '../../../',
  38. })
  39. const resourceOptions = { provider: cluster.provider }
  40. // Create a Kubernetes Namespace
  41. const ns = new k8s.core.v1.Namespace(name, {}, resourceOptions)
  42. // Export the Namespace name
  43. export const namespaceName = ns.metadata.name
  44. const appLabels = { appClass: name }
  45. const pvc = new k8s.core.v1.PersistentVolumeClaim(
  46. `${name}-pvc`,
  47. {
  48. metadata: {
  49. labels: appLabels,
  50. namespace: namespaceName,
  51. name: `${name}-pvc`,
  52. },
  53. spec: {
  54. accessModes: ['ReadWriteOnce'],
  55. resources: {
  56. requests: {
  57. storage: `${storage}Gi`,
  58. },
  59. },
  60. },
  61. },
  62. resourceOptions
  63. )
  64. volumes.push({
  65. name: 'ipfs-data',
  66. persistentVolumeClaim: {
  67. claimName: `${name}-pvc`,
  68. },
  69. })
  70. const caddyEndpoints = [
  71. ` {
  72. reverse_proxy storage-node:${colossusPort}
  73. }`,
  74. ]
  75. const caddy = new CaddyServiceDeployment(
  76. 'caddy-proxy',
  77. { lbReady, namespaceName: namespaceName, caddyEndpoints },
  78. resourceOptions
  79. )
  80. export const endpoint1 = caddy.primaryEndpoint
  81. export const endpoint2 = caddy.secondaryEndpoint
  82. export let appLink: pulumi.Output<string>
  83. if (lbReady) {
  84. appLink = pulumi.interpolate`https://${endpoint1}`
  85. if (!isAnonymous) {
  86. const remoteKeyFilePath = '/joystream/key-file.json'
  87. const providerId = config.require('providerId')
  88. const keyFile = config.require('keyFile')
  89. const publicUrl = config.get('publicURL') ? config.get('publicURL')! : appLink
  90. const keyConfig = new k8s.core.v1.ConfigMap('key-config', {
  91. metadata: { namespace: namespaceName, labels: appLabels },
  92. data: { 'fileData': fs.readFileSync(keyFile).toString() },
  93. })
  94. const keyConfigName = keyConfig.metadata.apply((m) => m.name)
  95. additionalParams = ['--provider-id', providerId, '--key-file', remoteKeyFilePath, '--public-url', publicUrl]
  96. volumeMounts.push({
  97. mountPath: remoteKeyFilePath,
  98. name: 'keyfile-volume',
  99. subPath: 'fileData',
  100. })
  101. volumes.push({
  102. name: 'keyfile-volume',
  103. configMap: {
  104. name: keyConfigName,
  105. },
  106. })
  107. const passphrase = config.get('passphrase')
  108. if (passphrase) {
  109. additionalParams.push('--passphrase', passphrase)
  110. }
  111. }
  112. }
  113. if (isAnonymous) {
  114. additionalParams.push('--anonymous')
  115. }
  116. // Create a Deployment
  117. const deployment = new k8s.apps.v1.Deployment(
  118. name,
  119. {
  120. metadata: {
  121. namespace: namespaceName,
  122. labels: appLabels,
  123. },
  124. spec: {
  125. replicas: 1,
  126. selector: { matchLabels: appLabels },
  127. template: {
  128. metadata: {
  129. labels: appLabels,
  130. },
  131. spec: {
  132. hostname: 'ipfs',
  133. containers: [
  134. {
  135. name: 'ipfs',
  136. image: 'ipfs/go-ipfs:latest',
  137. ports: [{ containerPort: 5001 }, { containerPort: 8080 }],
  138. command: ['/bin/sh', '-c'],
  139. args: [
  140. 'set -e; \
  141. /usr/local/bin/start_ipfs config profile apply lowpower; \
  142. /usr/local/bin/start_ipfs config --json Gateway.PublicGateways \'{"localhost": null }\'; \
  143. /usr/local/bin/start_ipfs config Datastore.StorageMax 200GB; \
  144. /sbin/tini -- /usr/local/bin/start_ipfs daemon --migrate=true',
  145. ],
  146. volumeMounts: [
  147. {
  148. name: 'ipfs-data',
  149. mountPath: '/data/ipfs',
  150. },
  151. ],
  152. },
  153. {
  154. name: 'colossus',
  155. image: colossusImage,
  156. env: [
  157. {
  158. name: 'WS_PROVIDER_ENDPOINT_URI',
  159. // example 'wss://18.209.241.63.nip.io/'
  160. value: wsProviderEndpointURI,
  161. },
  162. {
  163. name: 'DEBUG',
  164. value: 'joystream:*',
  165. },
  166. ],
  167. volumeMounts,
  168. command: [
  169. 'yarn',
  170. 'colossus',
  171. '--ws-provider',
  172. wsProviderEndpointURI,
  173. '--ipfs-host',
  174. 'ipfs',
  175. ...additionalParams,
  176. ],
  177. ports: [{ containerPort: colossusPort }],
  178. },
  179. ],
  180. volumes,
  181. },
  182. },
  183. },
  184. },
  185. resourceOptions
  186. )
  187. // Create a LoadBalancer Service for the Deployment
  188. const service = new k8s.core.v1.Service(
  189. name,
  190. {
  191. metadata: {
  192. labels: appLabels,
  193. namespace: namespaceName,
  194. name: 'storage-node',
  195. },
  196. spec: {
  197. ports: [{ name: 'port-1', port: colossusPort }],
  198. selector: appLabels,
  199. },
  200. },
  201. resourceOptions
  202. )
  203. // Export the Service name
  204. export const serviceName = service.metadata.name
  205. // Export the Deployment name
  206. export const deploymentName = deployment.metadata.name