From 5bbe0b4d0256afe00b44dcc3971bd403c204d8dc Mon Sep 17 00:00:00 2001 From: Ronaldo Macapobre Date: Fri, 6 Sep 2024 16:27:47 -0700 Subject: [PATCH] API CI and CD Workflow Pipelines (#19) * - Added api Dockerfile - Update API CICD - Added build-api * Fixed indention in build-api * Added shell bash and lint * Ignore existing errors * Add API workflow action * - Updated wf to pass .NET version to use when building docker image - Moved Dockerfile to /docker/api for consistency * Fixed assigning of SHORT_SHA * - Fixed incorrect Dockerfile path - Use lowercase arg * - Try major.minor version - Fixed SHORT_SHA * Fixed SHORT_SHA * Pass minor version=0 * Added jc-interface-client * Fixed short_sha * Finalize changes to api ci/cd * Added deploy2test and deploy2prod stages * - Pass tier name to deploy-to-aws action - Update aws deployment to trigger ECS Task Definition when a new image is deployed * Moved env vars to output vars * Added missing shell: bash * Try manual docker commands to retag and push since we the image is already built. * Append sha to tag an image * Pass SHORT_SHA to use for retagging * REname to main to be able to test feature branch * - Use build-push-action rather than docker commands - Update gh repo url to make concat easier to understand * Fixed Docker file path * Removed target * Include short_sha to pull docker image in ghcr * Pull image via tag instead of digest * Changed docker cli commands to GH actions * Cahnged web runtime's context * Update image and tags props * set push to true * Removed metadata acton * Try different tag * add debug log * fixed docker command * add more docker image ls * Add load=true * Removed :latest * Try pushing the image * Changed driver=docker * Changed context for build-push-action step * - Renamed main to Deploy API - Re-add push:true to deploy to web package to GCHR * - Added metadata gh action - Added image names env variables * Fixed incorrect arg casing * - Create a separate Dockerfile to combine web images into one - Updated web pipeline so that build-push-action is only called once * - Removed commented code - Changed context to . * Fixed paths for the pipeline * - Pass NODE_VERSION - Removed other commands that may not be necessary for the pipeline * Fixed permissions to use root user * Copy run script from nginx-runtime as it appears to be missing as per the Cloudwatch error. * Pass folder path to make Dockerfile.release reusable by ./manage script and publish-web pipeline * Fixed missing dir folder --------- Co-authored-by: Ronaldo Macapobre --- .../workflows/actions/build-api/action.yml | 36 ++++ .../actions/deploy-to-aws/action.yml | 51 ++++-- .github/workflows/build-and-test-api.yml | 33 ++++ .github/workflows/main.yml | 29 ---- .github/workflows/publish-api.yml | 164 ++++++++++++++++++ .github/workflows/publish-web.yml | 67 ++++--- docker/api/Dockerfile.release | 33 ++++ docker/manage | 32 +--- docker/web/Dockerfile.release | 103 +++++++++++ 9 files changed, 458 insertions(+), 90 deletions(-) create mode 100644 .github/workflows/actions/build-api/action.yml create mode 100644 .github/workflows/build-and-test-api.yml delete mode 100644 .github/workflows/main.yml create mode 100644 .github/workflows/publish-api.yml create mode 100644 docker/api/Dockerfile.release create mode 100644 docker/web/Dockerfile.release diff --git a/.github/workflows/actions/build-api/action.yml b/.github/workflows/actions/build-api/action.yml new file mode 100644 index 00000000..23525e2b --- /dev/null +++ b/.github/workflows/actions/build-api/action.yml @@ -0,0 +1,36 @@ +name: Build API +description: Builds the API codebase + +inputs: + working_directory: + description: The working directory where the code will be built. + required: true + dotnet_version: + description: The .NET version that will be used. + required: true + +runs: + using: composite + + steps: + - name: Setup .NET Core + uses: actions/setup-dotnet@v4 + with: + dotnet-version: ${{ inputs.dotnet_version }} + + - run: dotnet format --verify-no-changes --severity info + shell: bash + working-directory: ${{ inputs.working_directory }} + continue-on-error: true + + - run: dotnet restore + shell: bash + working-directory: ${{ inputs.working_directory }} + + - run: dotnet build --configuration Release --no-restore + shell: bash + working-directory: ${{ inputs.working_directory }} + + - run: dotnet test --no-restore --verbosity normal + shell: bash + working-directory: ${{ inputs.working_directory }} diff --git a/.github/workflows/actions/deploy-to-aws/action.yml b/.github/workflows/actions/deploy-to-aws/action.yml index 2551b748..ceb11873 100644 --- a/.github/workflows/actions/deploy-to-aws/action.yml +++ b/.github/workflows/actions/deploy-to-aws/action.yml @@ -17,8 +17,6 @@ inputs: aws_role_arn: description: The AWS Role ARN to assume. required: true - - # Image parameters ghcr_token: description: The token to use to login to the GHCR. required: true @@ -28,13 +26,27 @@ inputs: image_name: description: The name of the image to be deployed. required: true - image_digest: - description: The digest of the image to be deployed. Identifies the unique image tag in the GHCR. + tier_name: + description: The tier/layer name such as web or api. + required: true + short_sha: + description: The short SHA used to tag image in GCHR. required: true runs: using: composite + steps: + - name: Set reusable variables + id: vars + shell: bash + run: | + echo "task_definition_name=${{ inputs.app_name }}-${{ inputs.tier_name }}-task-definition-${{ inputs.environment }}" >> $GITHUB_OUTPUT + echo "container_name=${{ inputs.app_name }}-${{ inputs.tier_name }}-container-${{ inputs.environment }}" >> $GITHUB_OUTPUT + echo "ecs_cluster_name=${{ inputs.app_name }}-ecs-cluster-${{ inputs.environment }}" >> $GITHUB_OUTPUT + echo "ecs_service_name=${{ inputs.app_name }}-ecs-${{ inputs.tier_name }}-service-${{ inputs.environment }}" >> $GITHUB_OUTPUT + echo "full_ecr_repo_url=${{ inputs.aws_account }}.dkr.ecr.${{ inputs.region }}.amazonaws.com/${{ inputs.app_name }}-ecr-repo-${{ inputs.environment }}" >> $GITHUB_OUTPUT + - name: Log in to the GHCR uses: docker/login-action@v2 with: @@ -51,14 +63,33 @@ runs: role-duration-seconds: 1800 role-session-name: ci-deployment - - name: Login to AWS CLI + - name: Login to Amazon ECR + uses: aws-actions/amazon-ecr-login@v2 + + - name: Pull Docker image from GHCR shell: bash run: | - aws ecr get-login-password --region ${{ inputs.region }} | docker login --username AWS --password-stdin ${{ inputs.aws_account }}.dkr.ecr.${{ inputs.region }}.amazonaws.com/${{ inputs.app_name }}-ecr-repo-${{ inputs.environment }} + docker pull ${{ inputs.github_image_repo }}/${{ inputs.image_name }}:${{ inputs.short_sha}} + docker tag ${{ inputs.github_image_repo }}/${{ inputs.image_name }}:${{ inputs.short_sha}} ${{ steps.vars.outputs.full_ecr_repo_url }}:${{ inputs.image_name }}-${{ inputs.short_sha }} + docker push ${{ steps.vars.outputs.full_ecr_repo_url }}:${{ inputs.image_name }}-${{ inputs.short_sha }} - - name: Tag the image in the GHCR as ${{ inputs.environment }} + - name: Download task definition shell: bash run: | - docker pull ${{ inputs.github_image_repo }}${{ inputs.image_name }}@${{ inputs.image_digest }} - docker tag ${{ inputs.github_image_repo }}${{ inputs.image_name }}@${{ inputs.image_digest }} ${{ inputs.aws_account }}.dkr.ecr.${{ inputs.region }}.amazonaws.com/${{ inputs.app_name }}-ecr-repo-${{ inputs.environment }}:${{ inputs.image_name }} - docker push ${{ inputs.aws_account }}.dkr.ecr.${{ inputs.region }}.amazonaws.com/${{ inputs.app_name }}-ecr-repo-${{ inputs.environment }}:${{ inputs.image_name }} + aws ecs describe-task-definition --task-definition ${{ steps.vars.outputs.task_definition_name }} --query taskDefinition > ${{ steps.vars.outputs.task_definition_name }}.json + + - name: Fill in new image ID in task definition + id: task-def + uses: aws-actions/amazon-ecs-render-task-definition@v1 + with: + task-definition: ${{ steps.vars.outputs.task_definition_name }}.json + container-name: ${{ steps.vars.outputs.container_name }} + image: "${{ steps.vars.outputs.full_ecr_repo_url }}:${{ inputs.image_name }}-${{ inputs.short_sha }}" + + - name: Deploy Amazon ECS task definition + uses: aws-actions/amazon-ecs-deploy-task-definition@v2 + with: + task-definition: ${{ steps.task-def.outputs.task-definition }} + service: ${{ steps.vars.outputs.ecs_service_name }} + cluster: ${{ steps.vars.outputs.ecs_cluster_name }} + wait-for-service-stability: true diff --git a/.github/workflows/build-and-test-api.yml b/.github/workflows/build-and-test-api.yml new file mode 100644 index 00000000..e5a76c5e --- /dev/null +++ b/.github/workflows/build-and-test-api.yml @@ -0,0 +1,33 @@ +name: Build and Test API + +on: + pull_request: + branches: + - master + paths: + - "api/**" + - "db/**" + + workflow_dispatch: + +env: + WORKING_DIRECTORY: ./api + +jobs: + build-and-test: + runs-on: ubuntu-latest + + strategy: + matrix: + dotnet-major-version: [8] + dotnet-minor-version: [0] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Building Web codebase + uses: ./.github/workflows/actions/build-api + with: + working_directory: ${{ env.WORKING_DIRECTORY }} + dotnet_version: ${{ matrix.dotnet-major-version }}.${{ matrix.dotnet-minor-version }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml deleted file mode 100644 index 36a57138..00000000 --- a/.github/workflows/main.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: API (.NET Core) - -on: - push: - branches: [master] - pull_request: - branches: [master] - -jobs: - build: - runs-on: ubuntu-latest - env: - working-directory: ./api - - steps: - - uses: actions/checkout@v2 - - name: Setup .NET Core - uses: actions/setup-dotnet@v1 - with: - dotnet-version: 8.0.101 - - name: Install dependencies - run: dotnet restore - working-directory: ${{env.working-directory}} - - name: Build - run: dotnet build --configuration Release --no-restore - working-directory: ${{env.working-directory}} - - name: Test - run: dotnet test --no-restore --verbosity normal - working-directory: ${{env.working-directory}} diff --git a/.github/workflows/publish-api.yml b/.github/workflows/publish-api.yml new file mode 100644 index 00000000..0c41b848 --- /dev/null +++ b/.github/workflows/publish-api.yml @@ -0,0 +1,164 @@ +name: Deploy API + +on: + push: + branches: + - master + paths: + - "api/**" + - "db/**" + + workflow_dispatch: + +env: + WORKING_DIRECTORY: ./api + IMAGE_NAME: api + GITHUB_IMAGE_REPO: ghcr.io/bcgov/jasper + +jobs: + build: + name: Build, Create and Push Image + runs-on: ubuntu-latest + outputs: + short_sha: ${{ steps.short_sha.outputs.SHORT_SHA }} + + strategy: + matrix: + dotnet-major-version: [8] + dotnet-minor-version: [0] + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Build API codebase + uses: ./.github/workflows/actions/build-api + with: + working_directory: ${{ env.WORKING_DIRECTORY }} + dotnet_version: ${{ matrix.dotnet-major-version }}.${{ matrix.dotnet-minor-version }} + + - name: Log in to the GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Get short SHA + id: short_sha + run: | + echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT + + - name: Setup Image Metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.GITHUB_IMAGE_REPO }}/${{ env.IMAGE_NAME }} + tags: | + type=raw,value=${{ steps.short_sha.outputs.SHORT_SHA }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and Push Image to ghcr.io + id: build_image + uses: docker/build-push-action@v5 + with: + push: true + context: . + file: ./docker/api/Dockerfile.release + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + load: true + build-args: | + dotnet_version=${{ matrix.dotnet-major-version }}.${{ matrix.dotnet-minor-version }} + + deploy2dev: + name: Deploy to DEV + needs: build + env: + ENVIRONMENT: dev + permissions: + id-token: write + packages: write + runs-on: ubuntu-latest + environment: dev + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Deploy to ${{ env.ENVIRONMENT }} + uses: ./.github/workflows/actions/deploy-to-aws + with: + environment: ${{ env.ENVIRONMENT }} + aws_account: ${{ vars.AWS_ACCOUNT }} + region: ${{ vars.AWS_REGION }} + app_name: ${{ vars.APP_NAME }} + aws_role_arn: ${{ vars.AWS_ROLE_ARN }} + ghcr_token: ${{ secrets.GITHUB_TOKEN }} + github_image_repo: ${{ env.GITHUB_IMAGE_REPO }} + image_name: ${{ env.IMAGE_NAME }} + tier_name: api + short_sha: ${{ needs.build.outputs.short_sha }} + + deploy2test: + name: Deploy to TEST + needs: [build, deploy2dev] + env: + ENVIRONMENT: test + permissions: + id-token: write + packages: write + runs-on: ubuntu-latest + environment: test + + steps: + - name: Checkout + uses: actions/checkout@v4 + + # Uncomment when infra in AWS in TEST environment has been configured + # - name: Deploy to ${{ env.ENVIRONMENT }} + # uses: ./.github/workflows/actions/deploy-to-aws + # with: + # environment: ${{ env.ENVIRONMENT }} + # aws_account: ${{ vars.AWS_ACCOUNT }} + # region: ${{ vars.AWS_REGION }} + # app_name: ${{ vars.APP_NAME }} + # aws_role_arn: ${{ vars.AWS_ROLE_ARN }} + # ghcr_token: ${{ secrets.GITHUB_TOKEN }} + # github_image_repo: ${{ env.GITHUB_IMAGE_REPO }} + # image_name: ${{ env.IMAGE_NAME }} + # tier_name: api + # short_sha: ${{ needs.build.outputs.short_sha }} + + deploy2prod: + name: Deploy to PROD + needs: [build, deploy2dev, deploy2test] + env: + ENVIRONMENT: prod + permissions: + id-token: write + packages: write + runs-on: ubuntu-latest + environment: prod + + steps: + - name: Checkout + uses: actions/checkout@v4 + + # Uncomment when infra in AWS in PROD environment has been configured + # - name: Deploy to ${{ env.ENVIRONMENT }} + # uses: ./.github/workflows/actions/deploy-to-aws + # with: + # environment: ${{ env.ENVIRONMENT }} + # aws_account: ${{ vars.AWS_ACCOUNT }} + # region: ${{ vars.AWS_REGION }} + # app_name: ${{ vars.APP_NAME }} + # aws_role_arn: ${{ vars.AWS_ROLE_ARN }} + # ghcr_token: ${{ secrets.GITHUB_TOKEN }} + # github_image_repo: ${{ env.GITHUB_IMAGE_REPO }} + # image_name: ${{ env.IMAGE_NAME }} + # tier_name: api + # short_sha: ${{ needs.build.outputs.short_sha }} diff --git a/.github/workflows/publish-web.yml b/.github/workflows/publish-web.yml index 5cba3af0..02de75d3 100644 --- a/.github/workflows/publish-web.yml +++ b/.github/workflows/publish-web.yml @@ -11,8 +11,10 @@ on: env: WORKING_DIRECTORY: ./web - IMAGE_NAME: web - GITHUB_IMAGE_REPO: ghcr.io/bcgov/jasper/ + WEB_IMAGE_NAME: web + WEB_RUNTIME_IMAGE_NAME: web-runtime + WEB_ARTIFACTS_IMAGE_NAME: web-artifacts + GITHUB_IMAGE_REPO: ghcr.io/bcgov/jasper WEB_BASE_HREF: / jobs: @@ -20,7 +22,7 @@ jobs: name: Build, Create and Push Image runs-on: ubuntu-latest outputs: - image_digest: ${{ steps.docker_push.outputs.digest }} + short_sha: ${{ steps.short_sha.outputs.SHORT_SHA }} strategy: matrix: @@ -47,24 +49,36 @@ jobs: - name: Get short SHA id: short_sha run: | - echo "::set-output name=SHORT_SHA::$(git rev-parse --short HEAD)" - echo "Short SHA: $SHORT_SHA" + echo "SHORT_SHA=$(git rev-parse --short HEAD)" >> $GITHUB_OUTPUT - - name: Build Docker Image - working-directory: ${{env.WORKING_DIRECTORY}}/../ - run: | - docker build --tag web-runtime -f docker/nginx-runtime/Dockerfile ./docker/nginx-runtime/ - docker build --tag web-artifacts --build-arg node_version=${{ matrix.node-major-version }} --build-arg WEB_BASE_HREF=${{ env.WEB_BASE_HREF }} -f docker/web/Dockerfile . - docker build --tag ${{ env.IMAGE_NAME }} -f docker/vue-on-nginx/Dockerfile ./docker/vue-on-nginx/ - docker tag ${{ env.IMAGE_NAME }} ${{ env.GITHUB_IMAGE_REPO }}${{ env.IMAGE_NAME }}:${{ steps.short_sha.outputs.SHORT_SHA }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver: docker - - name: Push Image to GCHR - id: docker_push - run: | - output=$(docker push ${{ env.GITHUB_IMAGE_REPO }}${{ env.IMAGE_NAME }}:${{ steps.short_sha.outputs.SHORT_SHA }}) - echo $output - digest=$(echo "$output" | grep "digest: sha256" | awk '{ print $3 }') - echo "digest=$digest" >> $GITHUB_OUTPUT + - name: Setup ${{ env.WEB_IMAGE_NAME }} Image Metadata + id: meta + uses: docker/metadata-action@v5 + with: + images: | + ${{ env.GITHUB_IMAGE_REPO }}/${{ env.WEB_IMAGE_NAME }} + tags: | + type=raw,value=${{ steps.short_sha.outputs.SHORT_SHA }} + + - name: Build ${{ env.WEB_IMAGE_NAME }} image + uses: docker/build-push-action@v5 + with: + push: true + context: . + file: ./docker/web/Dockerfile.release + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + build-args: | + NODE_VERSION=${{ matrix.node-major-version }} + WEB_BASE_HREF=${{ env.WEB_BASE_HREF }} + NGINX_RUNTIME_SRC=../../docker/nginx-runtime + VUE_ON_NGINX_SRC=../../docker/vue-on-nginx + WEB_SRC=../../web deploy2dev: name: Deploy to DEV @@ -91,8 +105,9 @@ jobs: aws_role_arn: ${{ vars.AWS_ROLE_ARN }} ghcr_token: ${{ secrets.GITHUB_TOKEN }} github_image_repo: ${{ env.GITHUB_IMAGE_REPO }} - image_name: ${{ env.IMAGE_NAME }} - image_digest: ${{ needs.build.outputs.image_digest }} + image_name: ${{ env.WEB_IMAGE_NAME }} + tier_name: web + short_sha: ${{ needs.build.outputs.short_sha }} deploy2test: name: Deploy to TEST @@ -120,8 +135,9 @@ jobs: # aws_role_arn: ${{ vars.AWS_ROLE_ARN }} # ghcr_token: ${{ secrets.GITHUB_TOKEN }} # github_image_repo: ${{ env.GITHUB_IMAGE_REPO }} - # image_name: ${{ env.IMAGE_NAME }} - # image_digest: ${{ needs.build.outputs.image_digest }} + # image_name: ${{ env.WEB_IMAGE_NAME }} + # tier_name: web + # short_sha: ${{ needs.build.outputs.short_sha }} deploy2prod: name: Deploy to PROD @@ -149,5 +165,6 @@ jobs: # aws_role_arn: ${{ vars.AWS_ROLE_ARN }} # ghcr_token: ${{ secrets.GITHUB_TOKEN }} # github_image_repo: ${{ env.GITHUB_IMAGE_REPO }} - # image_name: ${{ env.IMAGE_NAME }} - # image_digest: ${{ needs.build.outputs.image_digest }} + # image_name: ${{ env.WEB_IMAGE_NAME }} + # tier_name: web + # short_sha: ${{ needs.build.outputs.short_sha }} diff --git a/docker/api/Dockerfile.release b/docker/api/Dockerfile.release new file mode 100644 index 00000000..df7383e3 --- /dev/null +++ b/docker/api/Dockerfile.release @@ -0,0 +1,33 @@ +# Dockerfile used by Github Action +ARG dotnet_version=8.0 +FROM mcr.microsoft.com/dotnet/aspnet:${dotnet_version} AS base +WORKDIR /app +EXPOSE 8080 +ENV ASPNETCORE_URLS=http://*:8080 +ENV ASPNETCORE_FORWARDEDHEADERS_ENABLED=true +ENV DOTNET_gcServer=1 +ARG VERSION +ENV VERSION=$VERSION + +FROM mcr.microsoft.com/dotnet/sdk:${dotnet_version} AS build + +WORKDIR /src + +COPY ["api/api.csproj", "api/"] +COPY ["db/db.csproj", "db/"] +COPY ["jc-interface-client/jc-interface-client.csproj", "jc-interface-client/"] +RUN dotnet restore api/api.csproj +RUN dotnet restore db/db.csproj +RUN dotnet restore jc-interface-client/jc-interface-client.csproj +COPY . . +RUN dotnet build "api/api.csproj" -c Release +# build +FROM build AS publish +RUN dotnet publish "api/api.csproj" -c Release -o /app/publish --runtime linux-musl-x64 --no-self-contained + +FROM base AS final + +# copy app +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "api.dll"] \ No newline at end of file diff --git a/docker/manage b/docker/manage index e79546e7..52270e2d 100755 --- a/docker/manage +++ b/docker/manage @@ -89,38 +89,18 @@ build-web() { # # web # - # The web-runtime image is used for the final runtime image. - # The artifacts image is used to build the artifacts for the vue distribution. - # The vue-on-nginx image is copy of the nginx-runtime image complete with a copy of the build artifacts. - # - echo -e "\n\n====================================================================================================" - echo -e "Building the ${COMPOSE_PROJECT_NAME}-web-runtime (nginx-runtime) image using Docker ..." - echo -e "----------------------------------------------------------------------------------------------------" - docker build \ - -t "${COMPOSE_PROJECT_NAME}-web-runtime" \ - -f './nginx-runtime/Dockerfile' './nginx-runtime/' - echo -e "====================================================================================================" - - # Apparently vue-cli-tools wants WEB_BASE_HREF -> vue.config.js -> publicPath at compile time. - # I tried using __webpack_public_path__, but the CSS file path and JS file path weren't correctly updated. - # Also note we don't load in environment variables from the arguments here. - echo -e "\n\n====================================================================================================" - echo -e "Building the ${COMPOSE_PROJECT_NAME}-web-artifacts image using s2i ..." - echo -e "----------------------------------------------------------------------------------------------------" - ${S2I_EXE} build \ - --copy \ - '../web' \ - 'quay.io/centos7/nodejs-12-centos7:master' \ - "${COMPOSE_PROJECT_NAME}-web-artifacts" - echo -e "====================================================================================================" - echo -e "\n\n====================================================================================================" echo -e "Building the ${COMPOSE_PROJECT_NAME}-web image using Docker ..." echo -e "----------------------------------------------------------------------------------------------------" docker build \ -t "${COMPOSE_PROJECT_NAME}-web" \ --build-arg IMAGE_PREFIX=${COMPOSE_PROJECT_NAME}- \ - -f './vue-on-nginx/Dockerfile' './vue-on-nginx/' + --build-arg NODE_VERSION=12 \ + --build-arg NGINX_RUNTIME_SRC='../docker/nginx-runtime' \ + --build-arg VUE_ON_NGINX_SRC='../docker/vue-on-nginx' \ + --build-arg WEB_SRC='./web' \ + --build-arg NPM_INSTALL_ARGS='--unsafe-perm' \ + -f './web/Dockerfile.release' '..' echo -e "====================================================================================================" } diff --git a/docker/web/Dockerfile.release b/docker/web/Dockerfile.release new file mode 100644 index 00000000..9504e06b --- /dev/null +++ b/docker/web/Dockerfile.release @@ -0,0 +1,103 @@ +# Define ARG at the top level +ARG NODE_VERSION=10 +ARG WEB_BASE_HREF=/ +ARG NGINX_RUNTIME_SRC=./nginx-runtime +ARG VUE_ON_NGINX_SRC=./vue-on-nginx +ARG WEB_SRC=./web +ARG NPM_INSTALL_ARGS="" + +################################################################################### +# 1. Build web-runtime +################################################################################### +# Use the offical nginx (based on debian) +FROM nginx:stable as runtime +ARG NGINX_RUNTIME_SRC +ARG VUE_ON_NGINX_SRC + +ENV STI_SCRIPTS_PATH=/usr/libexec/s2i + +# Required for HTTP Basic feature +RUN apt-get update -y && \ + apt-get install -y openssl ca-certificates && \ + rm -rf /var/lib/apt/lists/* + +# Copy our OpenShift s2i scripts over to default location +COPY ${VUE_ON_NGINX_SRC}/s2i/bin/ /usr/libexec/s2i/ + +# Expose this variable to OpenShift +LABEL io.openshift.s2i.scripts-url=image:///usr/libexec/s2i + +# Copy config from source to container +COPY ${NGINX_RUNTIME_SRC}/nginx.conf.template /tmp/ + +# Copy run script +COPY ${NGINX_RUNTIME_SRC}/s2i/bin/run /usr/libexec/s2i/run + +# ================================================================================= +# Fix up permissions +# ref: https://torstenwalter.de/openshift/nginx/2017/08/04/nginx-on-openshift.html +# - S2I scripts must be executable +# - Make sure nginx can read and write it's working directories. +# - The container dynamically configures nginx on startup +# - The application artifacts live in /tmp +# --------------------------------------------------------------------------------- +RUN chmod -R g+rwx $STI_SCRIPTS_PATH +RUN chmod g+rw /var/cache/nginx \ + /var/run \ + /var/log/nginx \ + /etc/nginx/nginx.conf \ + /tmp + +# ================================================================================= + +# Work-around for issues with S2I builds on Windows +WORKDIR /tmp + +# Nginx runs on port 8080 by default +EXPOSE 8080 + +# Switch to usermode +USER 104 + +################################################################################### +# 2. Build web-artifacts +################################################################################### +FROM quay.io/centos7/nodejs-${NODE_VERSION}-centos7:${NODE_VERSION} as artifacts +ARG NODE_VERSION +ARG WEB_SRC +ARG NPM_INSTALL_ARGS + +USER root +WORKDIR /opt/app-root/src + +COPY ${WEB_SRC} . + +RUN npm install ${NPM_INSTALL_ARGS} +RUN npm run build + +################################################################################### +# 3. Build web image +################################################################################### +FROM runtime +ARG VUE_ON_NGINX_SRC + +# Copy the build artifacts from the 'builder' image +# to the distribution folder on the runtime image. +COPY --from=artifacts /opt/app-root/src/dist/. /tmp/app/dist/ + +# # Ensure S2I script copied over is runnable. +COPY ${VUE_ON_NGINX_SRC}/s2i/bin/fix-base-url /usr/libexec/s2i/fix-base-url + +# Fix permissions. +USER root +RUN chmod 674 /usr/libexec/s2i/fix-base-url +RUN chmod -R 674 /tmp/app/dist/ + +# From nginx-runtime. +USER 104 + +# Since the runtime image is itself an s2i image we need to +# short circuit the s2i lifecycle. +# The runtime image "loses" its s2i runtime voodoo when it +# is used in a dockerSrategy, which is why the explicit `CMD` is necessary +CMD /usr/libexec/s2i/fix-base-url \ No newline at end of file