Deploy Angular SSR docker app to own server using GitLab CI/CD

Deploy Angular SSR docker app to own server using GitLab CI/CD

Santhosh Thomas
·Aug 15, 2021·

Subscribe to my newsletter and never miss my upcoming articles

This blog describes how to enable server-side rendering and deploy to your own server using gitlab CI/CD.

Add angular universal

ng add @nguniversal/express-engine

Build ssr in local

npm run build:ssr

This will generate outpute folder in dist/appname

Folder Strutucre

-dist
 - browser
    - index.html
    - favicon.ico
    - styles.css
    - common.js
    - scripts.js
    - common.js
    - runtime.js
    - polyfills.js
    - assets/
 - server
   - main.js
   - 3rdpartylicenses.txt

Run SSR in localhost

npm run serve:ssr

Add User

Run below commands in your server. Create a new user gitlab deploy and add sudo permission for gitlab-deployer user.

usermod -aG sudo gitlab-deployer
adduser gitlab-deployer
su -  gitlab-deployer
docker ps

Generate Private Key

ssh-keygen -t rsa -b 4096
cd ~/.ssh
cat gitlab

store this generated private key in gitlab variable.

Execute below command on server with root access:

cp -r ~/.ssh/ /home/gitlabdeployer
cd /home/gitlabdeployer/.ssh
chown gitlabdeployer:gitlabdeployer *
chmod 644 authorized_keys

Run below commands in your server. Create a new user gitlab deploy and add sudo permission for gitlab-deployer user.

usermod -aG sudo gitlab-deployer
adduser gitlab-deployer
su -  gitlab-deployer
docker ps

Generate DOCKER_CI_TOKEN

They can generated from gitlab.com/-/profile/personal_access_tokens

select scopes -> api, read_repository, read_registry, write_registry

Then create personal access token.

So this can acts as to login docker without password.

Add gitlab variables

gitlab variables.PNG

CI_REGISTRY_USER - gitlab username

IMAGE_NAME - gitlab.com/project_name/image_name

Here in test environment, it will deploy into aws s3 bucket and in production evironment, it will deploy docker image into own server.

Gitlab container registry is used for building and storing docker image, then finally pull docker image into server.

.gitlab.-ci.yml

image: node:14

variables:
  GIT_DEPTH: '0'
  DOCKER_HOST: tcp://docker:2375/
before_script:
  - apt-get update
  - apt-get install zip

stages:
  - install_dependencies
  - build
  - docker-image-creation
  - deploy


install_dependencies:
  stage: install_dependencies
  cache:
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/
      - dist/
  script:
    - npm ci
  only:
    changes:
      - package-lock.json

build:
  stage: build
  script:
    - npm run build:ssr
    - cd dist/${PROJECT_NAME}
    - ls -al -F
    - echo "BUILD SUCCESSFUL"
  dependencies:
    - install_dependencies
  cache: 
    key: ${CI_COMMIT_REF_SLUG}
    paths:
      - node_modules/
    policy: pull
  only:
    changes:
      - src/*
      - angular.json  
  artifacts:
    when: on_success
    paths:
      - dist/${PROJECT_NAME}
  only:
    - develop
    - main

test-deploy_to_s3_bucker:
  image: python:latest  
  stage: deploy
  cache:
    policy: pull
  dependencies:
    - build
    - install_dependencies
  before_script:
    - pip install awscli 
  script:
    - ls -lh
    - find  dist/${PROJECT_NAME}/browser \( -name '*.*' \) -exec gzip --verbose --best --force {} \; -exec mv "{}.gz" "{}" \; # Gzip recursively all files in the directory.
    - aws   s3 rm s3://${TEST_S3_BUCKET_NAME} --recursive
    - aws  s3 cp ./dist/${PROJECT_NAME}/browser S3://${TEST_S3_BUCKET_NAME}/ --recursive --acl public-read --content-encoding gzip
    - echo "Deployed Successfully"
  only:
    - develop
  environment:
    name: test

deploy_production_to_server:
  stage: deploy
  cache:
    policy: pull
  before_script:
    - 'which ssh-agent || ( apt-get update -y && apt-get install openssh-client -y )'
    - eval $(ssh-agent -s)
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    - chmod 400 $PRIVATE_KEY
    - echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config
    - apt-get update -y
    - apt-get -y install rsync
  script:
    - ssh -i $PRIVATE_KEY ubuntu@$SERVER_IP_ADDRESS
    - rsync -zvhr -auv -e "ssh -i $PRIVATE_KEY" dist/${PROJECT_NAME}/browser ubuntu@$SERVER_IP_ADDRESS:/var/www/html/angular/
  only: ['main']
  environment:
    name: production


docker_image-creation:
  image: docker:git # image with docker installed to execute docker commands
  stage: docker-image-creation # notice a new 
  cache:
    policy: pull
  services:
    - docker:dind #used to be able to execute docker commands inside of a docker container
  before_script:
    - docker ps #overrides previous docker script
  script:
    - docker login -u $CI_REGISTRY_USER -p $DOCKER_CI_TOKEN registry.gitlab.com #logs into gitlab docker registery, make sure to have this variables defined
    - docker build -t registry.gitlab.com/****/*** . # creates a docker image
    - docker push registry.gitlab.com/*****/*** # pushes the create docker image to docker registry
  dependencies:
    - build 


deploy_docker_image_to_server:
  image: ubuntu
  cache:
    policy: pull
  before_script: #checks if ssh installed and if not, attempts to install it
    - "which ssh-agent || ( apt-get update -y && apt-get install openssh-client git -y )"
    - eval $(ssh-agent -s)
    # Inject the remote's private key
    - echo "$PRIVATE_KEY" | tr -d '\r' | ssh-add - > /dev/null #adding a ssh private key from variables, pair of the one registered on digital ocean
    - mkdir -p ~/.ssh
    - chmod 700 ~/.ssh
    # Append keyscan output into known hosts
    - ssh-keyscan $SERVER_IP_ADDRESS >> ~/.ssh/known_hosts
    - chmod 644 ~/.ssh/known_hosts
  stage: deploy #new stage after release
  script:
    - ssh $SERVER_USERNAME@$SERVER_IP_ADDRESS  ls
    - ssh $SERVER_USERNAME@$SERVER_IP_ADDRESS "docker login -u ${CI_REGISTRY_USER} -p ${DOCKER_CI_TOKEN} registry.gitlab.com;
     docker stop aapp_name;
     docker rm app_name; 
     docker rmi "$(docker images -aq)"
     docker pull registry.gitlab.com/${PROJECT_NAME};
     docker run --name app_name -d -p 80:4000 ${PROJECT_NAME}"

Dockerfile (for angular universal)

FROM node:alpine

COPY nginx/nginx.conf /etc/nginx/nginx.conf

COPY package.json .
COPY dist dist/
EXPOSE 4000
ENV NODE_ENV production
CMD ["npm", "run", "serve:ssr"]

Did you find this article valuable?

Support Santhosh Thomas by becoming a sponsor. Any amount is appreciated!

See recent sponsors Learn more about Hashnode Sponsors
 
Share this