diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..fd00eaa96 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,34 @@ +# Include any files or directories that you don't want to be copied to your +# container here (e.g., local build artifacts, temporary files, etc.). +# +# For more help, visit the .dockerignore file reference guide at +# https://docs.docker.com/go/build-context-dockerignore/ + +**/.classpath +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.next +**/.cache +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/charts +**/docker-compose* +**/compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +**/build +**/dist +LICENSE +README.md diff --git a/.github/workflows/aws-deploy.yml b/.github/workflows/aws-deploy.yml new file mode 100644 index 000000000..d38df5257 --- /dev/null +++ b/.github/workflows/aws-deploy.yml @@ -0,0 +1,81 @@ +name: Deploy to EC2 + +on: + push: + branches: + - develop + - main + - feat/deploy-on-aws # Remove this after testing it, Do not merge without removeing this line + +jobs: + build-and-push: + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: ${{ github.ref == 'refs/heads/main' && 'production' || 'staging' }} + + steps: + - name: Checkout Repository + uses: actions/checkout@v4 + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and push + uses: docker/build-push-action@v5 + with: + context: . + file: ./Dockerfile + platforms: linux/arm64 + push: true + tags: | + ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:${{ github.sha }} + ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Deploy to EC2 + uses: appleboy/ssh-action@master + with: + host: ${{ secrets.AWS_EC2_HOST }} + username: ${{ secrets.AWS_EC2_USERNAME }} + key: ${{ secrets.AWS_EC2_SSH_PRIVATE_KEY }} + script: | + docker pull ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }}:latest + docker stop ${{ github.event.repository.name }}-${{vars.ENV}} || true + docker rm ${{ github.event.repository.name }}-${{vars.ENV}} || true + docker run -d -p ${{vars.PORT}}:${{vars.PORT}} \ + --name ${{ github.event.repository.name }}-${{vars.ENV}} \ + --network=${{vars.DOCKER_NETWORK}} \ + -e BOT_PUBLIC_KEY=${{secrets.BOT_PUBLIC_KEY}} \ + -e CLOUDINARY_API_KEY=${{secrets.CLOUDINARY_API_KEY}} \ + -e CLOUDINARY_API_SECRET_KEY=${{secrets.CLOUDINARY_API_SECRET_KEY}} \ + -e CLOUDINARY_CLOUD_NAME=${{secrets.CLOUDINARY_CLOUD_NAME}} \ + -e CRON_JOB_PUBLIC_KEY=${{secrets.CRON_JOB_PUBLIC_KEY}} \ + -e DISCORD_BASE_URL=${{vars.DISCORD_BASE_URL}} \ + -e ENABLE_CONSOLE_LOGS=${{vars.ENABLE_CONSOLE_LOGS}} \ + -e EVENT_100MS_APP_ACCESS_KEY=${{secrets.EVENT_100MS_APP_ACCESS_KEY}} \ + -e EVENT_100MS_APP_SECRET=${{secrets.EVENT_100MS_APP_SECRET}} \ + -e EXTERNAL_SERVICE_PUBLIC_KEY=${{secrets.EXTERNAL_SERVICE_PUBLIC_KEY}} \ + -e FIRESTORE_CONFIG=${{secrets.FIRESTORE_CONFIG}} \ + -e GITHUB_CLIENT_ID=${{secrets.GITHUB_CLIENT_ID}} \ + -e GITHUB_CLIENT_SECRET=${{secrets.GITHUB_CLIENT_SECRET}} \ + -e GITHUB_PERSONAL_ACCESS_TOKEN=${{secrets.GITHUB_PERSONAL_ACCESS_TOKEN}} \ + -e IDENTITY_SERVICE_URL=${{secrets.IDENTITY_SERVICE_URL}} \ + -e INTEGRATIONS_NEWRELIC_APPNAME=${{secrets.INTEGRATIONS_NEWRELIC_APPNAME}} \ + -e INTEGRATIONS_NEWRELIC_LICENSEKEY=${{secrets.INTEGRATIONS_NEWRELIC_LICENSEKEY}} \ + -e NODE_ENV=${{vars.ENV}} \ + -e PRIVATE_KEY=${{secrets.PRIVATE_KEY}} \ + -e PUBLIC_KEY=${{secrets.PUBLIC_KEY}} \ + -e RDS_SERVERLESS_PRIVATE_KEY=${{secrets.RDS_SERVERLESS_PRIVATE_KEY}} \ + -e RDS_SERVERLESS_PUBLIC_KEY=${{secrets.RDS_SERVERLESS_PUBLIC_KEY}} \ + -e RDS_SERVERLESS_TTL=${{secrets.RDS_SERVERLESS_TTL}} \ + -e SERVICES_RDSAPI_BASEURL=${{secrets.SERVICES_RDSAPI_BASEURL}} \ + -e USER_TOKEN_REFRESH_TTL=${{secrets.USER_TOKEN_REFRESH_TTL}} \ + -e USER_TOKEN_TTL=${{secrets.USER_TOKEN_TTL}} \ + ${{ secrets.DOCKERHUB_USERNAME }}/${{ github.event.repository.name }} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..520cdf7e8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,53 @@ +# syntax=docker/dockerfile:1 + +ARG NODE_VERSION=20.11.1 + +FROM node:${NODE_VERSION} AS build + +# Run the application as a non-root user. +USER node + +WORKDIR /usr/src/app + +# Download dependencies as a separate step to take advantage of Docker's caching. +# Leverage a cache mount to /root/.yarn to speed up subsequent builds. +# Leverage a bind mounts to package.json and yarn.lock to avoid having to copy them into +# into this layer. +RUN --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=yarn.lock,target=yarn.lock \ + --mount=type=cache,target=/root/.yarn \ + yarn install --frozen-lockfile --ignore-scripts + +# Copy the rest of the source files into the image. +COPY . . + +# Build the application. +RUN yarn tsc + +################################################################################ + +#Production stage +FROM node:${NODE_VERSION}-alpine AS production + +# Run the application as a non-root user. +USER node + +# Use production node environment by default. +ENV NODE_ENV production + +WORKDIR /usr/src/app + +RUN --mount=type=bind,source=package.json,target=package.json \ + --mount=type=bind,source=yarn.lock,target=yarn.lock \ + --mount=type=cache,target=/root/.yarn \ + yarn install --production --frozen-lockfile --ignore-scripts + +# Copy the build 'dist/' into the image. +COPY --from=build /usr/src/app/dist . + +# Expose the port that the application listens on. +EXPOSE 3000 + +# Run the application. +CMD ["node", "server.js"] + diff --git a/README.Docker.md b/README.Docker.md new file mode 100644 index 000000000..fa3048f63 --- /dev/null +++ b/README.Docker.md @@ -0,0 +1,22 @@ +### Building and running your application + +When you're ready, start your application by running: +`docker compose up --build`. + +Your application will be available at http://localhost:3000. + +### Deploying your application to the cloud + +First, build your image, e.g.: `docker build -t myapp .`. +If your cloud uses a different CPU architecture than your development +machine (e.g., you are on a Mac M1 and your cloud provider is amd64), +you'll want to build the image for that platform, e.g.: +`docker build --platform=linux/amd64 -t myapp .`. + +Then, push it to your registry, e.g. `docker push myregistry.com/myapp`. + +Consult Docker's [getting started](https://docs.docker.com/go/get-started-sharing/) +docs for more detail on building and pushing. + +### References +* [Docker's Node.js guide](https://docs.docker.com/language/nodejs/) \ No newline at end of file diff --git a/compose.yaml b/compose.yaml new file mode 100644 index 000000000..60ad62c5c --- /dev/null +++ b/compose.yaml @@ -0,0 +1,21 @@ +# Comments are provided throughout this file to help you get started. +# If you need more help, visit the Docker compose reference guide at +# https://docs.docker.com/go/compose-spec-reference/ + +# Here the instructions define your application as a service called "server". +# This service is built from the Dockerfile in the current directory. +# You can add other services your application may depend on here, such as a +# database or a cache. For examples, see the Awesome Compose repository: +# https://github.com/docker/awesome-compose +services: + server: + build: + context: . + environment: + NODE_ENV: production + DOCKER_BUILDKIT: 1 + ENABLE_FILE_LOGS: true + ENABLE_CONSOLE_LOGS: false + ports: + - 3000:3000 +