iT邦幫忙

2019 iT 邦幫忙鐵人賽

DAY 11
1
Kubernetes

在地端建置Angular+ASP.NET Core的DevOps環境系列 第 11

day11_docker09_AngularTaiwan讀書會[S03E08]CI Pipeline for Angular

前言

科科,這樣標題會不會太長了?

當初下題目時雄心壯志,把範圍定太大,本來想宇宙大融合的
想前後端測試一條龍
結果完蛋,根基不足,不只整合不起來,還沒有Angular

故多補這一篇~~,應付一下,代表我有提到Angular喔~~

今天一樣沒有原創,只是把以前看不懂的讀書會理解,分享「讀書」心得
這一集我以前看了2次吧,都看不懂,
直到參加鐵人賽,學了一些dockerfile突然就看懂六成了,

明年一定要再參加鐵人賽!!

本篇參考:
[S03E08] CI Pipeline for Angular
https://www.youtube.com/watch?v=YZC0HyZdV5o&t=3360s
https://github.com/Gogistics/prjNgCIPipeline
此次讀書會分參考
https://www.trion.de/news/
https://github.com/trion-development # 裡面有很多好料的喔~站在巨人的肩往上往麻~~

Angular讀書會都會錄影,小弟都會挑著看,
大部分看高手們展現武林新絕學,其實都跟不上進度
感謝第5季回到rxjs重新打底、第6季回歸初新者系列,
照顧我們這些中下階層的,把一些後段班的朋友們再往上帶

感謝KEVIN YANG大大跟每週的講者 m(.)m

其實有的也不一定是新手,而是長期卡等上不去

====前言打好長,科科====
請先clone Alan Tai大大的專案

$ git clone https://github.com/Gogistics/prjNgCIPipeline.git

目錄說明:

-scripts/
-docker_files/ # 一起看
- 本專案的起點
build_ng_cli_docker_img.sh # 此專案的基礎image,配/docker_files/Dockerfile.ng-cli

build_ng_cli_karma_docker_img.sh # 單元測試環境(會用到xvfb-chromium),配/docker_files/Dockerfile.ng-cli-karma

build_ng_cli_e2e_docker_img.sh # e2e,沒接觸過的領域,感覺主要是JDK環境

-pipeline/ # Alan Tai大大貼心準備的Angular專案
也可以用./script/generate_ng_project.sh自己建一個

-koa_app/ # 略過,不解釋
一個很小的koa專案
koa 就是 node.js 的一種開發框架,就像 Express 一樣

因為小弟本身是用 Express,koa入門可參考:
https://github.com/nodejs-tw/nodejs-book-best-practice/blob/master/draft/koa.md

========================

README.md

目錄看完,我們先來看README.md,最後再看scripts跟docker_files裡的檔案

摘錄部分內容,只有中文的部分才是我打的喔

###Prerequistes:
...
2. get all infrastructures ready:
    2-1. version control
可用
gitlab(參考day08~day11)
jenkins(參考day05)
    2-2. docker registry
自架private docker registry(參考day03_docker02_基礎工具)
所以你可以跑gitlab、gitlab-ci、registry這幾個container,來達到基本要求
...

### These steps are done by DevOps:   (DevOps的建置步驟)
- 基本上這個部分,公司可找1位專人維護DevOps環境及images

1. login docker registry 如果你private docker registry建不起來,可以用docker hub

$ docker login docker-registry.gogistics-tw.com(作者自己架的private registry)
or with user name and password

$ docker login -u <username> -p <password> docker-registry.gogistics-tw.com
(你應該己建一個,登入自己的private docker registry)

2. create docker image by running scripts and then tag the docker image which is created and tested successfully. The commands below demo how to generate the docker image of ng-cli
2-1. create the docker image of ng-cli

#本案專的main(){...}起點喔!!
$ ./scripts/build_ng_cli_docker_img.sh # 這個檔案內容後面再說明
#新的image build好後,加tab跟push到你的registry,主要考驗你的private docker registry能不能用。

2-2. tag the docker image of ng-cli
$ docker tag atai/ng-cli:v1 docker-registry.gogistics-tw.com/atai/ng-cli:v1
3. push the docker image of ng-cli to the docker registry
$ docker push docker-registry.gogistics-tw.com/atai/ng-cli:v1

###Development (開發、測試)
###這邊就是每位開發人員,新人一來,只要裝docker,pull image就能開始開發@@~
$ cd pipeline # host先進到pipeline裡(Alan Tai大大貼心準備的Angular專案)
如果你有自己的Angular專案,要丟到container裡面跑,就進到你專案目錄
...
# unit testing
$ docker run --rm -v "$PWD":/pipeline docker-registry.gogistics-tw.com/atai/ng-cli-karma:v1 ng test --single-run
$PWD就是你目前的目錄,假設你的host現在應該在pipeline
那-v  "$PWD":/pipeline就是 host的pipeline mount到 container的pipeline
所以編譯完,dist/ 的結果在host的pipeline就看得到

ng-cli-karma:v1 就是用image  ng-cli-karma的tag  v1這個版本,建container
最後面的 ng test --single-run,有學過Angular應該都很熟了,就是angular-cli的指令

# e2e testing
$ docker run --rm -v "$PWD":/pipeline docker-registry.gogistics-tw.com/atai/ng-cli-e2e:v1 ng e2e

# once developers build features and want to generate bundle files, run the command below
$ docker run --rm -v "$PWD":/pipeline docker-registry.gogistics-tw.com/atai/ng-cli:v1 ng build (-prod) (-aot)
$ docker run --rm -v "$PWD":/pipeline docker-registry.gogistics-tw.com/atai/ng-cli:v1 ng build -prod -aot
小弟不知道(-prod) (-aot)跟-prod -aot的差別??

如果遇到Node Sass could not find a binding for your current environment這個問題時,要rebuild node-sass
好像常發生在一個專案,要同時在windows、macOS兩種環境上開發
Note: if you encounter the build issue, **Node Sass could not find a binding for your current environment**, run change command to:
$ docker run --rm -v "$PWD":/pipeline docker-registry.gogistics-tw.com/atai/ng-cli-e2e:v1 sh -c 'npm rebuild node-sass --force && ng build (-prod) (-aot)'

# 只是比較有-prod跟沒-prod的檔案大小
# check size of folders
$ du -h dist/ # 看編譯出來的dist有多大
4.7M dist/

========================

scripts/底下的檔案

build_ng_cli_docker_img.sh

build docker image的script只要看懂1個其他都類似

source $(pwd)/scripts/envVariables 
# 利用 source 來執行腳本:在父程序中執行
# 可以改變目前的 BASH 環境變數
# http://linux.vbird.org/linux_basic/0340bashshell-scripts.php#some_ex_run
# 如果用bash或sh跑script,則只作用在sub-process

bold=$(tput bold) # 進入粗體模式
normal=$(tput sgr0) # 結束所有的模式

docker build \
  -t $DOCKER_ACCOUNT_NAME/$DOCKER_IMG_TAG_NG_CLI:$DOCKER_IMG_NG_CLI_VERSION \
  # 一般我們要commit到Docker Hub的話,
  # 會需要針對該Docker Image製作一個TAG,之後使用這個TAG做push...
  # 透過"-t"可以指定該build動作到一個TAG上

  --build-arg NG_CLI_VERSION=$NG_CLI_VERSION \
  --build-arg USER_HOME_DIR=$USER_HOME_DIR \
  --build-arg APP_DIR=$APP_DIR \
  --build-arg USER_ID=$USER_ID \
  --build-arg NPM_CONFIG_LOGLEVEL=$NPM_CONFIG_LOGLEVEL \
  --build-arg USER_HOME_DIR=$USER_HOME_DIR \
  -f $(pwd)/docker_files/Dockerfile.ng-cli . # 若build成功會回覆一個image id
# $(pwd) 目前所在目錄
# --build-arg :
# 在docker build 的時候藉由 --build-arg 指定
# 讓build的動作可結合外部的指令給定一些建構時候所需的參數
# 這邊就是給參數arg,給Dockerfile.ng-cli

========================

generate_ng_project.sh

產生一個ng new <>(有餵envVariables裡的參數)

直接學shell script會睡著,但有真實案例學就好多了,至少有個目的
如果您跟我一樣是shell白紙,剛開始會比較辛苦喔~
一起進步吧,gogo~~

set -ex
# -e : 只要bash script中的任何一command失敗,它就會退出
# -x : 印出每個將要執行的命令

# set environment variables
source $(pwd)/scripts/envVariables
# 利用 source 來執行腳本:在父程序中執行
# 可以改變目前的 BASH 環境變數
# http://linux.vbird.org/linux_basic/0340bashshell-scripts.php#some_ex_run
# 如果用bash或sh跑script,則只作用在sub-process

[[ $# -gt 0 ]] || (echo "No options" && exit $ERR_OPERATOR)
# [[ expression ]] 回傳 expression 為 true | false
# 參數個數大於0,且 source $(pwd)/scripts/envVariables 環境變數有餵成功
# $ERR_OPERATOR=1 是 envVariables 裡的最後1行
while [[ $# -gt 0 ]]; do
  key="$1"  # var $key[] = 第1個參數,opt1
  case $key in
  #第1個CASE
    -n|--name)  # 抓出command參數是 -n 或 --name 的
    NAME="$2" # 例如 ./xx.sh -n  yyy,讓$NAME=yyy
    # 這個$NAME就是,最後跑 docker run 要用到的 angular project name
    shift # past argument
    shift # past value
    # shift掉2個參數
    # 例如 ./xx.sh -opt1 value1 -opt2 value2 -opt3 value3
    # 會變成 -opt2 value2 -opt3 value3
    ;;    # 第1個CASE的CASE結尾
  #其他CASE
    *)    # unknown option,最後一個變數內容都會用 * 來代表所有其他職
    echo "ERROR: unknown options"
    exit $ERR_OPERATOR
    ;;    # 第2個CASE的CASE結尾
  esac
done
echo "ready to generate a new Angular project"
# 這個要用各個擊破法
# $# :代表後面接的參數『個數』
# -gt 0 :大於0
# ./xxx.sh    opt1    opt2    opt3    opt4
#   $0        $1       $2      $3      $4

# case...in...esac
# http://linux.vbird.org/linux_basic/0340bashshell-scripts.php#case

# shift 造成參數變數號碼偏移
# http://linux.vbird.org/linux_basic/0340bashshell-scripts.php#shift

docker run \
  --rm \
  -v $PWD:$APP_DIR \
  $DOCKER_ACCOUNT_NAME/$DOCKER_IMG_TAG_NG_CLI:$DOCKER_IMG_NG_CLI_VERSION \
  ng new $NAME

# 所以這個script只作了3件事
# 1、source envVariables
# 2、抓出$NAME
# 3、跑docker run(有餵一些envVariables),在container裡跑你的新專案
#   也可以用pipeline裡的專案

========================

spin_up_koa.sh

因為個人沒用koa,所以就略過囉,只補充一個指令

inspection=$(docker inspect $KOA_APP_NAME)
# KOA_APP_NAME="ng-koa",定義在envVariables
# docker inspect
# 找到 Mapping 到實體主機的資料夾路徑,確認是否有看到之前寫在 Container 裡面的檔案
# 參考:https://ithelp.ithome.com.tw/articles/10192703

xvfb-chromium、xvfb-chromium-webgl

這2個檔案是Dockerfile.ng-cli-karma跑單元測試時,會用到的
在Dockerfile.ng-cli-karma,會把這2個script copy到container

ADD ./scripts/xvfb-chromium /usr/bin/xvfb-chromium
ADD ./scripts/xvfb-chromium-webgl /usr/bin/xvfb-chromium-webgl

xvfb的說明:virtual framebuffer X server for X Version 11
可以處理 XWindow 的圖形化功能,在 shell 底下跑圖形程式
請參考:
-XVFB實現selenium在linux上無界面運行安裝篇
https://hk.saowen.com/a/f6ffd3f36a5d7bfa253ed45f35e089fe2f5e46c47ba69fa5972164e39767406a
就是用chrome去測專案

#這段看不懂: -f /tmp/.X$i-lock
#網路上是找到:
#Find a free server number by looking at .X*-lock files in /tmp.
#另外「好像」這個程式碼不能同時跑2份以上,有可能資源會打架
#如果大家有使用經驗可留言指教喔
find_free_servernum() {
  i=0
  while [ -f /tmp/.X$i-lock ]; do
    i=$(($i + 1))
  done
  echo $i
}

export DISPLAY=":$(find_free_servernum)"
# 把 X11 這個程序將顯示圖像輸出的結果丟到第 $DISPLAY 號螢幕,例如  :2  用第2號螢幕
Xvfb "$DISPLAY" -ac -screen 0 "${XVFB_WHD:-1280x720x16}" -nolisten tcp +render &
xvfb_pid=$!

最後我們來看...Dockerfile
相信大家對Dockerfile都已經很熟了

docker_files/

略的部分就不打了,請直接看作者Alan Tai大大的github喔

Dockerfile.ng-cli

...中間略...
ARG NG_CLI_VERSION="$NG_CLI_VERSION"
ARG USER_HOME_DIR="$USER_HOME_DIR"
ARG APP_DIR="$APP_DIR"
ARG USER_ID="$USER_ID" # optional

# ARG 補充說明:
# 參考:Dockerfile - ENV與ARG參數
# https://peihsinsu.gitbooks.io/docker-note-book/dockerfile-env-vs-arg.html
# ARG 可以在 docker build 的時候藉由 --build-arg 指定
# 讓build的動作可結合外部的指令給定一些建構時候所需的參數
...中間略...

# 把script copy到node8裡面
COPY ./scripts/dumb-init /usr/bin/dumb-init
...中間略...
ENTRYPOINT ["/usr/bin/dumb-init", "--"]
# dumb-init
# https://github.com/Yelp/dumb-init
# 由Yelp開發的,扮演PID 1 (runs as PID 1) 跟 把你的command作為child process,
# 就像簡單的 init system
# 處理(handle)及forward(轉發) 它所收到的 信號 signals
 It launches a single process and then proxies all received signals 
 to a session rooted at that child process.
# 為什麼需要init?用途範例:
# 在輕量級的container裡面
# 使用docker運行my-container腳本的CI作業:
# 將SIGTERM發送到docker run通常會終止docker run命令,但讓container跑在後台(背景)。
# 造成孤兒的殭屍程序(Orphaned zombie processes)不會去closed掉

# SIGTERM process結束(terminate)信號,與SIGKILL不同的是,
# SIGTERM可以被截獲。通常用來要求process自己正常退出(自我了斷)
# The init system is thus responsible for wait()-ing on orphaned zombie processes.
# 更進一步請參考官網囉~

# dumb-init怎麼用在 Docker containers?官網也有說
https://github.com/Yelp/dumb-init#installing-inside-docker-containers
# Runs "/usr/bin/dumb-init -- /my/script --with --args"
# ENTRYPOINT ["/usr/bin/dumb-init", "--"] # 所以這一行是標準用法

========================

###Dockerfile.ng-cli-alpine
# node:8-alpine is the most recent and stable alpine image that allows to run rebuild node-sass, headless chrome, and Angular CLI operations.
FROM node:8-alpine
# 輕量化版本,適合用來只跑單純的測試
LABEL maintainer="Alan Tai <alan.tai@riverbed.com>"
# Install alpine dependencies

# 安裝相依套件
RUN apk add --update --no-cache make gcc g++ git python dumb-init
# 像 dumb-init 就有很多安裝方式
1、用deb檔安裝
apt install dumb-init
RUN wget https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64.deb
RUN dpkg -i dumb-init_*.deb
# 看到dpkg就想到openWRT
2、下載binary file
RUN wget -O /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v1.2.2/dumb-init_1.2.2_amd64
RUN chmod +x /usr/local/bin/dumb-init
3、用pip裝(Debian/Ubuntu/macOS)
pip install dumb-init

========================

Dockerfile.ng-cli-karma

# 在既有的ng-cli:v1,再加測試環境
# 一個常見的單元測試 (Unit Test)環境: Jasmine + Karma + Webpack
# 請參考 John Wu - Angular 4 教學 - 單元測試 (Unit Test) Jasmine + Karma + Webpack
# https://blog.johnwu.cc/article/angular-unit-test-jasmine-karma-webpack.html
# 所以他應該比e2e還後面

# ln -s A(真實目標) B(捷徑)
# 建立指向 A的symbolic link,名稱為B
# 為什麼要這麼大費周章呢? 
#「我猜」會不會像Windows一樣,把程式裝在Program Files/裡面,程式捷徑放桌面? 的意思??

結語:

有沒有參賽朋友跟我一樣,一直編輯,會害怕按到「發表文章」的?

借由Alan Tai大大的專案
偶們才得可以把前面各自獨立的主題融合在一起

-Docker private registry
放docker image的,要有人維護

docker-compose(寫個dockerfile把下面image包進來)
-gitlab(含gitlab-ci)
類似今天這樣的專案放gitlab
-gitlab-ci-runner
-angular-cli
$ ng build => dist/
-.NetCore SDK(如果是全端開發,angular併在.Net Core專案就好了)
把 dist/ copy到.Net Core專案
$ dotnet test

這樣幾乎就是CI啦,對於大部分的開發人員來說應該就夠用了吧
而且環境只依賴docker,愈簡單愈好
只要有1位能建出環境並維護image,
其他同仁不太需要學什麼
(只要裝docker、git,再學一下docker run、docker login)

#CD(自動佈署+自動維運)
感覺就是更深的課題啦
以海賊王的角度來看,CD就類似「新世界」 :P
初部使用CI可能只能算剛學會霸氣

感覺CD就得靠Docker Swarm或Kubernetes囉
One Piece就藏在那裡囉

各位勇者們

你準備好要前往「新世界」了嗎?

「新世界」的文章很亂喔(還在趕稿ing XD )


上一篇
day10_docker08_GitLab-CI-(Multi)Runner
下一篇
day12_Vagrant基本安裝&說明
系列文
在地端建置Angular+ASP.NET Core的DevOps環境31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言