序文
コンテナ、k8s、クラウドネイティブがますます普及している昨今、クラウドネイティブ時代のフロントエンドプロジェクトの構築・リリースにどのような変化があったのでしょうか?従来のビルドおよびデプロイ プラットフォームと比較して、クラウド ネイティブの違いは何ですか? 従来の構築および展開プラットフォームでのエンタープライズ レベルのプロジェクト構築に興味がある場合は、まず [統合展開プラットフォームでのエンタープライズ レベルのフロントエンド構築の実践] を参照してください。
今日は主に、オープンソースの車輪 (docker/k8s/rancher/gitlab ci) に基づくクラウドネイティブな構築ソリューションについて話します。
完全なプロセス
完全なプロセスを次の図に示します。
1. コードは gitlab でホストされており、gitlab の ci cd を借用しています。
2. gitlab の ci は、主にプロジェクト自体のビルド パッケージと、docker イメージ build-docker のパッケージを担当します。
3. パッケージ化された Docker イメージをプライベート ソース ハーバーにプッシュします。
4. 環境を微分し、rancher の http インターフェースを呼び出し、ハーバーに docker イメージをダウンロードして、指定したクラスターの指定したプロジェクトをデプロイします。Rancher は、kubectl を介して k8s クラスターも制御します。
┌──────────────┐ ┌────────────────┐ │ │ │ │ 港 ├────────── ────▲│ ランチャー │ │ │▼──────────────┤ ├──────────────┐ │ │ docker pull │ │ └ ──────────────┘ └───────────────┘ kubectl│ ▲ ▲ │ │ │ │ │ │ │ │ │build-docker deploy-test1 │ │ deploy-release │ │ ...... │ │ │ │ build │ │ │ │ │ ┌──────────┴────────────┐ │ ┌ ────────────┴─────────────┐ │ │ │ │ │ │ │ │ │ │ │ │ gitlab ├─────────────── ───┘ │ k8sクラスタ │ │ │ │ │ │ │ │ │ └────────────────── ────────┘ └──────────────────────────┘
CI と CD の完全なプロセス
あるプロジェクトのgitlab ci ymlファイルを例に詳細を紹介します。
1. まず、rancher と Harbor を含む必要な変数を定義します。
2. pre-script before_script を実行して、ブランチを介して環境変数とパッケージ化されたイメージの名前を決定します. 通常のブランチのイメージ名にはハッシュが含まれません. これは、基本的にテスト環境をロールバックする必要がないためです. 環境変数は RUNTIME_ENV で、後続の docker ビルドによってコンテナーに取り込まれます。
3. ステージ全体が build、build-docker、deploy-test1、deploy-online に分かれていることを定義します。プロジェクトのパッケージ化、イメージのパッケージ化、展開テスト環境、オンライン環境の展開をそれぞれ行います。
4. 各段階で実行されるジョブを定義します. たとえば、展開は rancher インターフェースを呼び出し、さまざまな変数を渡します.
image: docker:18.06
variables:
# rancher 变量
RANCHER_NAMESPACE: kef2e
RANCHER_PROJECT: kef2e
RANCHER_WORKLOAD: ke-cms
# harbor 变量
PROJECT: kef2e
IMAGE_NAME: ke-cms
# 部署重要变量
RANCHER_URL: https://someurl
RANCHER_URL_ONLINE: https://someurl
RANCHER_TOKEN: yourtoken
RANCHER_TOKEN_ONLINE: yourtoken
RANCHER_PROJECT_ID: yourid
RANCHER_PROJECT_ID_ONLINE: yourid
services:
- name: docker:18.06-dind
alias: docker
stages:
- build-spa
- build-docker
- deploy
- deploy_online
before_script:
- echo exec before_script
# 现在gitlab版本没有 CI_COMMIT_SHORT_SHA 变量(v>11.7),进行兼容
- if [ "$CI_COMMIT_SHORT_SHA" = "" ]; then CI_COMMIT_SHORT_SHA=${CI_COMMIT_SHA:0:8} ;fi;
# 将branch name里的/转换为tag name支持的-
- CI_COMMIT_REF_SAFE_NAME=$(echo $CI_COMMIT_REF_NAME | sed 's/\//-/')
# 镜像的仓库地址
- IMAGE_URL=$DOCKER_REG/$PROJECT/$IMAGE_NAME:$CI_COMMIT_REF_SAFE_NAME
- if [[ "$CI_COMMIT_REF_SAFE_NAME" != "release" ]]; then RUNTIME_ENV=development ; fi
- if [[ "$CI_COMMIT_REF_SAFE_NAME" == "release" ]] || [[ "$CI_COMMIT_REF_SAFE_NAME" == "pre" ]]; then IMAGE_URL=$DOCKER_REG/$PROJECT/$IMAGE_NAME:$CI_COMMIT_REF_SAFE_NAME-$CI_COMMIT_SHORT_SHA; fi
- echo image url:$IMAGE_URL
- echo image RUNTIME_ENV:$RUNTIME_ENV
.deploy_tpl: &deploy_def
script:
- CONTAINER_URL=$RANCHER_URL/v3/projects/$RANCHER_PROJECT_ID/workloads/deployment:$RANCHER_NAMESPACE:$RANCHER_WORKLOAD
- curl -u $RANCHER_TOKEN -X GET $CONTAINER_URL -o deployment.json -k
- TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
- sed -i 's#image\":[^,]*,#image\":\"'$IMAGE_URL'\",#' deployment.json
- sed -i 's/"cattle.io\/timestamp":"[^"]*"/"cattle.io\/timestamp":"'${TIMESTAMP}'"/g' deployment.json
- curl -u $RANCHER_TOKEN -XPUT -H "Accept:application/json" -H "Content-Type:application/json" [email protected] $CONTAINER_URL -k
- echo RANCHER_URL:$RANCHER_URL
- echo RANCHER_TOKEN:$RANCHER_TOKEN
- echo RANCHER_PROJECT_ID:$RANCHER_PROJECT_ID
- echo CONTAINER_URL:$CONTAINER_URL
# 打包spa
build-spa:
stage: build-spa
tags:
- k8s
image: harbor-registry.inner.youdao.com/ke-test/agent-base:node12
script:
- echo "package build directory"
- yarn config set strict-ssl false
- yarn config set registry http://f2enpm.inner.youdao.com/
# https://github.com/cnpm/cnpmjs.org/issues/1246, puppeteer在CI可能install有问题
- yarn config set puppeteer_download_host https://nexus3.corp.youdao.com/repository/npm-all/
- yarn config set puppeteer_skip_chromium_download true
- yarn install
# 这里为项目线上的构建命令,根据项目自行修改
- yarn release
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- node_modules/
artifacts:
name: "$CI_COMMIT_REF_SLUG"
paths:
- dist
when: always
expire_in: 6 mos
# 构建镜像
build-docker:
retry: 2
stage: build-docker
tags:
- k8s
script:
# 登陆 dockerhub
- echo $DOCKER_REG_PASSWD | docker login $DOCKER_REG -u $DOCKER_REG_USER --password-stdin
# 镜像地址
- echo $IMAGE_URL
- docker build -t $IMAGE_URL --build-arg RUNTIME_ENV=$RUNTIME_ENV .
- docker push $IMAGE_URL
- docker logout $DOCKER_REG
# 部署rancher
deploy_rancher:
stage: deploy
image: $CURL_IMAGE
tags:
- k8s
variables:
MSG: "正式部署到 $RANCHER_CLUSTER:$RANCHER_NAMESPACE \n部署地址:$RANCHER_URL/p/$RANCHER_PROJECT_ID/workload/deployment:$RANCHER_NAMESPACE:$RANCHER_WORKLOAD-$CI_COMMIT_REF_NAME \n"
script:
<<: *deploy_def
# 手动触发rancher部署
when: manual
# 部署rancher online
deploy_rancher_online:
stage: deploy_online
image: $CURL_IMAGE
tags:
- k8s
variables:
RANCHER_URL: $RANCHER_URL_ONLINE
RANCHER_PROJECT_ID: $RANCHER_PROJECT_ID_ONLINE
RANCHER_TOKEN: $RANCHER_TOKEN_ONLINE
MSG: "正式部署到 $RANCHER_CLUSTER:$RANCHER_NAMESPACE \n部署地址:$RANCHER_URL/p/$RANCHER_PROJECT_ID/workload/deployment:$RANCHER_NAMESPACE:$RANCHER_WORKLOAD-$CI_COMMIT_REF_NAME \n"
script:
<<: *deploy_def
# 手动触发rancher部署
only:
- release
when: manual
以上のプロセスにより、フロントエンド プロジェクトの構築と展開は完了しますが、まだいくつかの問題があります。エンタープライズ レベルの意味は、最高の保守性を追求し、パーソナライズを可能な限り減らすことです。また、20 以上のプロジェクトがある場合、このような構成の yml を 1 つずつ必要としますが、めちゃくちゃになりませんか? したがって、抽象的な統一された構成が不可欠です。
統合構成後の CI と CD
分析を通じて、矛盾している可能性があるビルド フェーズを除いて、ランチャーのプロジェクト ID などの変数名が異なることを除いて、他のフェーズは同じであると結論付けることができます。
統合構成後の yml ファイルは次のとおりです。
ビルド ステージを除いて、他のステージと before_script はリモート管理に抽出され、インクルードによって読み込まれます。
ビルド フェーズで行われたことの一貫性を極端に確保できる場合は、それらをすべて抽出して、変数名の構成のみを残すこともできます。私たちのプロジェクトの数が多いため、パーソナライズされた変換の費用対効果は高くないため、ビルドは一時的に保存されます。
image: docker:18.06
variables:
# rancher 变量
RANCHER_NAMESPACE: kef2e
RANCHER_PROJECT: kef2e
RANCHER_WORKLOAD: ke-cms
# harbor 变量
PROJECT: kef2e
IMAGE_NAME: ke-cms
# 部署重要变量
RANCHER_URL: https://someurl
RANCHER_URL_ONLINE: https://someurl
RANCHER_TOKEN: yourtoken
RANCHER_TOKEN_ONLINE: yourtoken
RANCHER_PROJECT_ID: yourid
RANCHER_PROJECT_ID_ONLINE: yourid
services:
- name: docker:18.06-dind
alias: docker
stages:
- build
- build-docker
- deploy-test1
- deploy-online
include:
- 'https://g.hz.netease.com/api/v4/projects/49427/repository/files/before_script.yml/raw?ref=master&private_token=YOUR_TOKEN&ext=.yml'
- 'https://g.hz.netease.com/api/v4/projects/49427/repository/files/job.yml/raw?ref=master&private_token=YOUR_TOKEN&ext=.yml'
# 打包spa
build-spa:
stage: build
tags:
- k8s
image: harbor-registry.inner.youdao.com/ke-test/agent-base:node12
script:
- echo "package build directory"
- yarn config set strict-ssl false
- yarn config set registry http://f2enpm.inner.youdao.com/
# https://github.com/cnpm/cnpmjs.org/issues/1246, puppeteer在CI可能install有问题
- yarn config set puppeteer_download_host https://nexus3.corp.youdao.com/repository/npm-all/
- yarn config set puppeteer_skip_chromium_download true
- yarn install
# 这里为项目线上的构建命令,根据项目自行修改
- yarn release
cache:
key: "$CI_COMMIT_REF_SLUG"
paths:
- node_modules/
artifacts:
name: "$CI_COMMIT_REF_SLUG"
paths:
- dist
when: always
expire_in: 6 mos
抽象化された 2 つのリモート スクリプトは次のとおりです。
before_script.yml は、条件に従ってイメージ名を定義するなど、いくつかの事前操作を行うために使用されます。
default:
before_script:
- echo exec before_script
# 现在gitlab版本没有 CI_COMMIT_SHORT_SHA 变量(v>11.7),进行兼容
- if [ "$CI_COMMIT_SHORT_SHA" = "" ]; then CI_COMMIT_SHORT_SHA=${CI_COMMIT_SHA:0:8} ;fi;
# 将branch name里的/转换为tag name支持的-
- CI_COMMIT_REF_SAFE_NAME=$(echo $CI_COMMIT_REF_NAME | sed 's/\//-/')
# 镜像的仓库地址
- IMAGE_URL=$DOCKER_REG/$PROJECT/$IMAGE_NAME:$CI_COMMIT_REF_SAFE_NAME
- if [[ "$CI_COMMIT_REF_SAFE_NAME" != "release" ]]; then RUNTIME_ENV=development ; fi
- if [[ "$CI_COMMIT_REF_SAFE_NAME" == "release" ]] || [[ "$CI_COMMIT_REF_SAFE_NAME" == "pre" ]]; then IMAGE_URL=$DOCKER_REG/$PROJECT/$IMAGE_NAME:$CI_COMMIT_REF_SAFE_NAME-$CI_COMMIT_SHORT_SHA; fi
- echo image url:$IMAGE_URL
- echo image RUNTIME_ENV:$RUNTIME_ENV
また、job.yml はいくつかのパブリック タスクを格納するために使用されます。
.deploy_tpl: &deploy_def
script:
- CONTAINER_URL=$RANCHER_URL/v3/projects/$RANCHER_PROJECT_ID/workloads/deployment:$RANCHER_NAMESPACE:$RANCHER_WORKLOAD
- curl -u $RANCHER_TOKEN -X GET $CONTAINER_URL -o deployment.json -k
- TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
- sed -i 's#image\":[^,]*,#image\":\"'$IMAGE_URL'\",#' deployment.json
- sed -i 's/"cattle.io\/timestamp":"[^"]*"/"cattle.io\/timestamp":"'${TIMESTAMP}'"/g' deployment.json
- curl -u $RANCHER_TOKEN -XPUT -H "Accept:application/json" -H "Content-Type:application/json" [email protected] $CONTAINER_URL -k
- echo RANCHER_URL:$RANCHER_URL
- echo RANCHER_TOKEN:$RANCHER_TOKEN
- echo RANCHER_PROJECT_ID:$RANCHER_PROJECT_ID
- echo CONTAINER_URL:$CONTAINER_URL
variables:
RANCHER_URL:
RANCHER_URL_ONLINE:
RANCHER_TOKEN:
RANCHER_TOKEN_ONLINE:
# 构建镜像
build-docker:
retry: 2
stage: build-docker
tags:
- k8s
script:
# 登陆 dockerhub
- echo $DOCKER_REG_PASSWD | docker login $DOCKER_REG -u $DOCKER_REG_USER --password-stdin
# 镜像地址
- echo $IMAGE_URL
- docker build -t $IMAGE_URL --build-arg RUNTIME_ENV=$RUNTIME_ENV .
- docker push $IMAGE_URL
- docker logout $DOCKER_REG
# 部署rancher
deploy_rancher_test1:
stage: deploy-test1
image: $CURL_IMAGE
tags:
- k8s
variables:
MSG: "正式部署到 $RANCHER_CLUSTER:$RANCHER_NAMESPACE \n部署地址:$RANCHER_URL/p/$RANCHER_PROJECT_ID/workload/deployment:$RANCHER_NAMESPACE:$RANCHER_WORKLOAD-$CI_COMMIT_REF_NAME \n"
script:
<<: *deploy_def
# 手动触发rancher部署
when: manual
# 部署rancher online
deploy_rancher_online:
stage: deploy-online
image: $CURL_IMAGE
tags:
- k8s
variables:
RANCHER_URL: $RANCHER_URL_ONLINE
RANCHER_PROJECT_ID: $RANCHER_PROJECT_ID_ONLINE
RANCHER_TOKEN: $RANCHER_TOKEN_ONLINE
MSG: "正式部署到 $RANCHER_CLUSTER:$RANCHER_NAMESPACE \n部署地址:$RANCHER_URL/p/$RANCHER_PROJECT_ID/workload/deployment:$RANCHER_NAMESPACE:$RANCHER_WORKLOAD-$CI_COMMIT_REF_NAME \n"
script:
<<: *deploy_def
# 手动触发rancher部署
only:
- release
when: manual
要約する
上記の統合構成を完了した後、各プロジェクトは、ci プロセスを作成してフロントエンドのビルドとリリースを完了するときに、独自のプロジェクトと独自のビルド フェーズ タスクの一意のパラメーターを構成するだけで済みます。
将来、新しい環境とクラスターが追加された場合、それらは同じ構成のスクリプトで維持するだけでよく、プロジェクトはステージと対応する変数を追加するだけで済みます。