做者 | 伍衝斌 VPGAME 運維開發工程師php
導讀:VPGAME 是集賽事運營、媒體資訊、大數據分析、玩家社羣、遊戲周邊等爲一體的綜合電競服務平臺。總部位於中國杭州,在上海和美國西雅圖分別設立了電競大數據研發中心和 AI 研發中心。本文將講述 VPGAME 將服務器遷移至 Kubernetes 的過程。
隨着容器技術的日趨成熟,公司近期計劃將服務遷移至容器環境,經過 Kubernetes 對容器進行調度、編排和管理。並藉此機會,對服務進行標準化,優化整個 CI/CD 的流程,提升服務部署的效率。html
CI/CD 工具上,咱們選擇了 GitLab-CI。GitLab-CI 就是一套配合 GitLab 使用的持續集成系統,以完成代碼提交以後的安裝依賴、編譯、單元測試、lint、鏡像構建以及發佈等工做。node
GitLab-CI 完美地和 GitLab 進行集成,在使用的時候只須要安裝配置 gitlab-runner 便可。GitLab-Runner 在向 GitLab 完成註冊後能夠提供進行 CI/CD 操做的環境,負責從 GitLab 中拉取代碼,根據代碼倉庫中配置的 gitlab-ci.yml ,執行相應的命令進行 CI/CD 工做。mysql
相比於 Jenkins,GitLab-CI 配置簡單,只需在工程中配置 gitlab-ci.yml 文件完成 CI/CD 流程的編寫,不須要像在 Jenkins 裏同樣配置 webhook 回調地址,也不須要 Jenkins 新建這個項目的編譯配置。而且我的認爲 GitLab 的 CI/CD 過程顯示比 Jenkins 更加美觀。固然 Jenkins 依靠它豐富的插件,能夠配置不少 GitLab-CI 不存在的功能。按照如今咱們的需求, GitLab-CI 簡單易用,在功能也知足咱們的需求。linux
傳統的服務部署方式是在操做系統中安裝好相應的應用依賴,而後進行應用服務的安裝,這種部署方式的缺點是將服務的程序、配置、依賴庫以及生命週期與宿主機操做系統緊密地耦合在一塊兒,對服務的升級、擴縮容、遷移等操做不是十分便利。nginx
容器的部署方式則是以鏡像爲核心,在代碼進行編譯構建時,將應用程序與應用程序運行所須要的依賴打包成一個鏡像,在部署階段,經過鏡像建立容器實例完成服務的部署和運行。從而實現以應用爲中心的管理,容器的隔離性實現了資源的隔離,因爲容器不須要依賴宿主機的操做系統環境,因此能夠很好地保證開發、測試和生產環境的一致性。此外,因爲構建好的鏡像是不可變的,而且能夠經過 tag 進行版本控制,因此能夠提供可靠、頻繁的容器鏡像構建和部署,亦可方便及快速進行回滾操做。laravel
Kubernetes(簡稱 k8s),做爲一個容器調度、編排和管理平臺,能夠在物理或虛擬機集羣上調度和運行應用程序容器,提供了一個以容器爲核心的基礎架構。經過 Kubernetes,對容器進行編排和管理,能夠:git
咱們在服務遷移中選用了阿里雲的容器服務,它基於原生 Kubernetes 進行適配和加強,簡化集羣的搭建和擴容等工做,整合阿里雲虛擬化、存儲、網絡和安全能力,打造雲端最佳的 Kubernetes 容器化應用運行環境。在便捷性上,能夠經過 Web 界面一鍵完成 Kubernetes 集羣的建立、升級以及節點的擴縮容。功能上,在網絡、存儲、負載均衡和監控方面與阿里雲資源集成,在遷移過程當中能夠最小化減小遷移帶來的影響。web
此外,在選擇集羣建立時,咱們選擇了託管版 Kubernetes,只需建立 Worker 節點,Master 節點由容器服務建立並託管。如此一來,咱們在 Worker 節點的規劃與資源隔離上仍是具有自主性和靈活性的同時不須要運維管理 Kubernetes 集羣 Master 節點,能夠將更多的精力關注在應用服務上。正則表達式
在介紹 GitLab CI 以前,首先簡單介紹一下 GitLab CI 裏的一些基本概念,具體以下:
當有代碼 push 到 GitLab 時,就會觸發一個 Pipeline。而後進行編譯,測試和鏡像構建等操做等操做,其中每一步操做都爲一個 Job。在 CD 階段,會將 CI 階段構建出來的結果根據狀況部署到測試環境或生產環境。
GitLab 中有三種類型的 Runner ,分別爲:
咱們能夠根據須要向 GitLab 註冊不一樣類型的 Runner,註冊的方式是相同的。
Runner 首先會向 GitLab 發起註冊請求,請求內容中包含 token、tag 等信息,註冊成功後 GitLab 會向 Runner 返回一個 token,後續的請求,Runner 都會攜帶這個請求。
註冊成功後,Runner 就會不停的向 GitLab 請求 Job,時間間隔是 3s。若沒有請求到 Job,GitLab 返回 204 No Content。若是請求到 Job,GitLab 會把 Job 信息返回回來,Runner 在接收到 Job 以後,會向 GitLab 發送一個確認請求,同時更新任務的狀態。以後,Runner 開始 Job 的執行, 而且會定時地將中間數據,以 Patch 請求的方式發送給 GitLab。
Runner 在實際執行 Job 時,是經過調用 Executor 來完成的。Runner 在註冊時提供了 SSH、Shell、Docker、docker-ssh、VirtualBox、Kubernetes 等不一樣類型的 Executor 來知足不一樣的場景和需求。
其中咱們經常使用的有 Shell 和 Docker 等 Executor,Shell 類型主要是利用 Runner 所在主機的環境進行 Job的執行。而 Docker 類型的 Executor 在每一個 Job 開始時,拉取鏡像生產一個容器,在容器裏完成 Job,在 Job 完成後,對應的容器就會被銷燬。因爲 Docker 隔離性強、輕量且回收,咱們在使用時選用 Docker 類型的 Executor 去執行 Job,咱們只要提早作好 Job 所需環境的 Docker 鏡像,在每一個 Job 定義好 image 便可使用對應的環境,操做便捷。
因爲咱們須要使用 Docker 類型的 Executor,因此須要在運行 Runnner 的服務器上先安裝 Docker,具體步驟以下(CentOS 環境):
安裝須要的軟件包,yum-util 提供 yum-config-manager 功能,另外兩個是 DeviceMapper 驅動依賴:
yum install -y yum-utils device-mapper-persistent-data lvm2
設置 yum 源:
yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
安裝 Docker:
yum install docker-ce -y
啓動並加入開機啓動:
systemctl start docker systemctl enable docker
執行下面的命令進行 GitLab Runner 的安裝和啓動:
curl -L https://packages.gitlab.com/install/repositories/runner/gitlab-runner/script.rpm.sh | sudo bash sudo yum install gitlab-runner -y gitlab-runner start
啓動 GitLab Runner 後還須要向 GitLab 進行註冊,在註冊前須要從 GitLab 裏查詢 token。不一樣類型的 Runner 對應的 token 獲取的路徑不一樣。shared Runner 須要 admin 權限的帳號,按以下方式能夠獲取對應的 token。
其餘兩種類型在對應的頁面下( group 或 project 主頁)的 setting—>CI/CD—>Runner 能夠獲取到 token。
Runner 的註冊方式分爲交互式和非交互式兩種。其中交互式註冊方式,在輸入 gitlab-runner register 命令後,按照提示輸入註冊所須要的信息,包括 gitlab url、token 和 Runner 名字等。這邊我的比較推薦非交互式命令,能夠事先準備好命令,完成一鍵註冊,而且非交互式註冊方式提供了更多的註冊選項,能夠知足更多樣化的需求。
按以下示例便可完成一個 Runner 的註冊:
gitlab-runner register --non-interactive \ --url "http://git.xxxx.cn" \ --registration-token "xxxxxxxxxxx" \ --executor "docker" \ --docker-image alpine:latest \ --description "base-runner-docker" \ --tag-list "base-runner" \ --run-untagged="true" \ --docker-privileged="true" \ --docker-pull-policy "if-not-present" \ --docker-volumes /etc/docker/daemon.json:/etc/docker/daemon.json \ --docker-volumes /etc/gitlab-runner/key/docker-config.json:/root/.docker/config.json \ --docker-volumes /etc/gitlab-runner/find_diff_files:/usr/bin/find_diff_files \ --docker-volumes /etc/gitlab-runner/key/id_rsa:/root/.ssh/id_rsa \ --docker-volumes /etc/gitlab-runner/key/test-kube-config:/root/.kube/config
咱們能夠經過 --docker-pull-policy 指定 Executor 執行 Job 時 Dokcer 鏡像下載策略。--docker-volumes 指定容器與宿主機(即 Runner 運行的服務器)的文件掛載映射關係。上面掛載的文件主要是用於 Runner 在執行 Job 時,運用的一些 key,包括訪問 GitLab、Docker Harbor 和 Kubernetes 集羣的 key。固然,若是還有其餘文件須要共享給容器,能夠經過 --docker-volumes 去指定。
/etc/docker/daemon.json 文件主要爲了能夠以 http 方式訪問 docker horbor 所作的設置:
{ "insecure-registries" : ["http://docker.vpgame.cn"] }
完成註冊後,重啓 Runner 便可:
gitlab-runner restart
部署完成後,能夠在 GitLab 的 Web 管理界面查看到不一樣 Runner 的信息。
此外,若是一臺服務須要註冊多個 Runner ,能夠修改 /etc/gitlab-runner/config.toml 中的 concurrent 值增長 Runner 的併發數,修改完以後一樣須要重啓 Runner。
爲了知足不一樣服務對運行環境的多樣化需求,咱們須要爲不一樣語言的服務提早準備不一樣的基礎鏡像用於構建鏡像階段使用。此外,CI/CD 所須要的工具鏡像也須要製做,做爲 Runner 執行 Job 時生成容器所須要的 Docker 鏡像。
全部的鏡像都以編寫 Dockerfile 的形式經過 GitLab 進行管理,而且咱們編寫了 .gitlab-ci.yml 文件,使得每次有 Dockerfile 新增或者修改就會觸發 Pipeline 進行鏡像的構建並上傳到 Harbor 上。這種管理方式有如下優勢:
每一個文件夾都有 Dockerfile 來描述鏡像的基本狀況,其中包含了 Java、PHP、Node 和 Go 等不一樣語言的運行時基礎鏡像和 CI 鏡像,還有 docker-kubectl 這類工具鏡像的 Dockerfile。
以 PHP 鏡像爲例:
php/ ├── 1.0 │ ├── Dockerfile │ ├── ci-1.0 │ │ └── Dockerfile │ ├── php.ini │ ├── read-zk-config │ ├── start_service.sh │ └── www.conf └── nginx ├── Dockerfile ├── api.vpgame.com.conf └── nginx.conf
該目錄下有一個名爲 1.0 的文件夾,裏面有一個 Dockerfile 用來構建 php fpm 運行時基礎進行鏡像。主要是在 php:7.1.16-fpm-alpine3.4 加了咱們本身定製化的文件,並指定工做目錄和容器初始命令。
FROM php:7.1.16-fpm-alpine3.4 RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories\ && apk upgrade --update && apk add --no-cache --virtual build-dependencies $PHPIZE_DEPS \ tzdata postgresql-dev libxml2-dev libmcrypt libmcrypt-dev libmemcached-dev cyrus-sasl-dev autoconf \ && apk add --no-cache freetype libpng libjpeg-turbo freetype-dev libpng-dev libjpeg-turbo-dev libmemcached-dev \ && cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \ && echo "Asia/Shanghai" > /etc/timezone \ && docker-php-ext-configure gd \ --with-gd \ --with-freetype-dir=/usr/include/ \ --with-png-dir=/usr/include/ \ --with-jpeg-dir=/usr/include/ \ && docker-php-ext-install gd pdo pdo_mysql bcmath opcache \ && pecl install memcached apcu redis \ && docker-php-ext-enable memcached apcu redis \ && apk del build-dependencies \ && apk del tzdata \ && rm -rf /var/cache/apk/* \ && rm -rf /tmp/* \ && rm -rf /working/* \ && rm -rf /usr/local/etc/php-fpm.d/* COPY start_service.sh /usr/local/bin/start_service.sh COPY read-zk-config /usr/local/bin/read-zk-config COPY php.ini /usr/local/etc/php/php.ini COPY www.conf /usr/local/etc/php-fpm.d/www.conf WORKDIR /work CMD ["start_service.sh"]
在 1.0/ci-1.0 還有一個 Dockerfile,是用來構建 PHP 在進行單元測試和 lint 操做時所使用的 CI 鏡像。能夠看到它基於上面的基礎運行時鏡像增長其餘工具來進行構建的。
FROM docker.vpgame.cn/infra/php-1.0 ENV PATH="/root/.composer/vendor/bin:${PATH}" ENV COMPOSER_ALLOW_SUPERUSER=1 RUN mkdir -p /etc/ssh && echo "StrictHostKeyChecking no" >> /etc/ssh/ssh_config RUN apk --update add --no-cache make libc-dev autoconf gcc openssh-client git bash &&\ echo "apc.enable_cli=1" >> /usr/local/etc/php/conf.d/docker-php-ext-apcu.ini RUN pecl install xdebug && docker-php-ext-enable xdebug &&\ echo -e "\nzend_extension=xdebug.so" >> /usr/local/etc/php/php.ini RUN wget https://vp-infra.oss-cn-beijing.aliyuncs.com/gitlab-ci/software/download/1.6.5/composer.phar -O /bin/composer && \ chmod +x /bin/composer && \ composer config -g -q repo.packagist composer https://packagist.laravel-china.org RUN composer global require -q phpunit/phpunit:~5.0 squizlabs/php_codesniffer:~3.0 WORKDIR / CMD ["/bin/bash"]
另外 Nginx 目錄下一樣有 Dockerfile,來定製化咱們 PHP 項目所須要的 Nginx 鏡像。
在 GitLab 裏第一次增長新的 Dockerfile 或者更改 Dockerfile 時,會觸動 Pipeline 自動進行鏡像的構建並上傳的咱們私有的 Docker Harbor 上。
因爲各個鏡像經過 Dockerfile 進行管理, Master 分支有新的合併,能夠經過 git diff 命令找出合併先後新增或更新的 Dockerfile,而後根據這些 Dockerfile 依據必定的命名規則構建鏡像,並上傳到 Docker Harbor 上。
for FILE in `bash ./find_diff_files|grep Dockerfile|sort`; do DIR=`dirname "$FILE"`; IMAGE_NAME=`echo $DIR | sed -e 's/\//-/g'`; echo $CI_REGISTRY/$HARBOR_DIR/$IMAGE_NAME; docker build -t $CI_REGISTRY/$HARBOR_DIR/$IMAGE_NAME -f $FILE $DIR; docker push $CI_REGISTRY/$HARBOR_DIR/$IMAGE_NAME; done
上面命令中 finddifffiles 基於 git diff 命令找出合併先後有差別的文件。
在完成 GitLab Runner 以及 Docker 基礎鏡像的製做以後,咱們即可以進行 CI/CD 流程來完成代碼更新以後的單元測試、lint、編譯、鏡像打包以及部署等工做。經過 GitLab CI 進行 CI/CD 的操做只須要在代碼倉庫裏編輯和維護一個 .gitlab-ci.yml 文件,每當代碼有更新,GitLab CI 會讀取 .gitlab-ci.yml 裏的內容,生成一條 Pipeline 進行 CI/CD 的操做。
.gitlab-ci.yml 的語法比較簡單,基於 yaml 語法進行 Job 的描述。咱們把 CI/CD 流程中所須要完成的任務拆分紅文件裏的 Job,只要對每一個 Job 完成清晰的定義,即可造成一套合適高效並具備普適性的 CI/CD 流程。
stages 是一個很是重要的概念, 在 .gitlab-ci.yml 中進行全局定義, 在定義 Job 時指定其中的值來代表 Job 所處的 stage。而在 stages 裏元素的順序定義了 Job 的執行順序:全部在相同 stage 的 Job 會並行執行,只有當前 stage 的全部成功完成後,後面 stage 的 Job 纔會去執行。
例如,定義以下 stages:
stages: - build - test - deploy
Job 是 .gitlab-ci.yml 文件中最重要的組成部分,全部的 CI/CD 流程中所執行的任務都可以須要經過定義 Job 來實現。具體來講,咱們能夠經過關鍵字來對每個 Job 進行描述。因爲 Job 中的關鍵字衆多,而且用法比較豐富,這邊針對咱們本身實戰中的一個 Job 來進行說明。
unittest: stage: test image: docker.vpgame.cn/infra/php-1.0-ci-1.1 services: - name: docker.vpgame.cn/infra/mysql-5.6-multi alias: mysql - name: redis:4.0 alias: redis_default script: - mv .env.tp .env - composer install --no-dev - phpunit -v --coverage-text --colors=never --coverage-html=coverage --stderr artifacts: when: on_success paths: - vendor/ - coverage/ expire_in: 1 hour coverage: '/^\s*Lines:\s*\d+.\d+\%/' only: - branches - tags tags: - base-runner
上面的 Job 主要完成了單元測試的功能,在起始行定義了 Job 的名稱。下面咱們來解釋 Job 每一個關鍵字的具體含義。
dependencies: - unittest
job: only: - /^issue-.*$/ except: - branches
這個例子中,只有以 issue- 開頭 tag 標記纔會觸發 Job。若是不加 except 參數,以 issue- 開頭的分支或者 tag 標記會觸發 Job。
因此,咱們在 Job 定義中,經過 tags 指定 Runner 的值,來指定所須要的 Runner。
咱們能夠看到 Job 的定義很是的清晰和靈活,關於 Job 的使用遠不止這些功能,更詳細的用法能夠參考 GitLab CI/CD 官方文檔。
在清楚瞭如何描述一個 Job 以後,咱們經過定義一個個 Job,並進行編排造成 Pipelines。由於咱們能夠描述設定 Job 的觸發條件,因此經過不一樣的條件能夠觸發造成不同的 Pipelines。
在 PHP 項目 Kubernetes 上線過程當中,咱們規定了合併 Master 分支會進行 lint、unitest、build-test 以及 deploy-test 四個 Job。
在測試環境驗證經過以後,咱們再經過打 tag 進行正式環境的上線。此處的 Pipelines 包含了 unittest、build-pro 和 deploy-pro 三個 Job。
在 .gitlab-ci.yml 文件中,test 階段主要完成 lint 和 unitest 兩個 Job,不一樣的語言在進行 Job 定義時會各有不一樣。咱們重點來看一下 build 和 deploy 兩個 stage 的 Job 描述。build stage:
# Build stage .build-op: stage: build dependencies: - unittest image: docker.vpgame.cn/infra/docker-kubectl-1.0 services: - name: docker:dind entrypoint: ["dockerd-entrypoint.sh"] script: - echo "Image name:" ${DOCKER_IMAGE_NAME} - docker build -t ${DOCKER_IMAGE_NAME} . - docker push ${DOCKER_IMAGE_NAME} tags: - base-runner build-test: extends: .build-op variables: DOCKER_IMAGE_NAME: ${DOCKER_REGISTRY_PREFIX}/${CI_PROJECT_PATH}:${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA} only: - /^testing/ - master build-prod: extends: .build-op variables: DOCKER_IMAGE_NAME: ${DOCKER_REGISTRY_PREFIX}/${CI_PROJECT_PATH}:${CI_COMMIT_TAG} only: - tags
在這邊,因爲 build 階段中測試環境和生產環境進行鏡像打包時基本操做時是相同的,都是根據 Dockerfile 進行鏡像的 build 和鏡像倉庫的上傳。這裏用到了一個 extend 參數,能夠減小重複的 Job 描述,使得描述更加地簡潔清晰。
咱們先定義一個 .build-op 的 Job,而後 build-test 和 build-prod 都經過 extend 進行繼承,能夠經過定義關鍵字來新增或覆蓋 .build-op 中的配置。好比 build-prod 從新定義了變量( variables)DOCKER_IMAGE_NAME以及觸發條件(only)更改成了打 tag 。
這邊咱們還須要注意到的是在定義 DOCKER_IMAGE_NAME 時,咱們引用了 GitLab CI 自身的一些變量,好比 CI_COMMIT_TAG 表示項目的 commit 的 tag 名稱。咱們在定義 Job 變量時,可能會引用到一些 GitLab CI 自身變量,關於這些變量的說明能夠參考 GitLab CI/CD Variables 中文文檔。
deploy stage:
# Deploy stage .deploy-op: stage: deploy image: docker.vpgame.cn/infra/docker-kubectl-1.0 script: - echo "Image name:" ${DOCKER_IMAGE_NAME} - echo ${APP_NAME} - sed -i "s~__NAMESPACE__~${NAMESPACE}~g" deployment.yml service.yml - sed -i "s~__APP_NAME__~${APP_NAME}~g" deployment.yml service.yml - sed -i "s~__PROJECT_NAME__~${CI_PROJECT_NAME}~g" deployment.yml - sed -i "s~__PROJECT_NAMESPACE__~${CI_PROJECT_NAMESPACE}~g" deployment.yml - sed -i "s~__GROUP_NAME__~${GROUP_NAME}~g" deployment.yml - sed -i "s~__VERSION__~${VERSION}~g" deployment.yml - sed -i "s~__REPLICAS__~${REPLICAS}~g" deployment.yml - kubectl apply -f deployment.yml - kubectl apply -f service.yml - kubectl rollout status -f deployment.yml - kubectl get all,ing -l app=${APP_NAME} -n $NAMESPACE # Deploy test environment deploy-test: variables: REPLICAS: 2 VERSION: ${CI_COMMIT_REF_SLUG}-${CI_COMMIT_SHORT_SHA} extends: .deploy-op environment: name: test url: http://example.com only: - /^testing/ - master tags: - base-runner # Deploy prod environment deploy-prod: variables: REPLICAS: 3 VERSION: ${CI_COMMIT_TAG} extends: .deploy-op environment: name: prod url: http://example.com only: - tags tags: - pro-deploy
與 build 階段相似,先先定義一個 .deploy-op 的 Job,而後 deploy-test 和 deploy-prod 都經過 extend 進行繼承。
.deploy-op 主要完成了對 Kubernetes Deployment 和 Service 模板文件的一些變量的替換,以及根據生成的 Deployment 和 Service 文件進行 Kubernetes 服務的部署。
deploy-test 和 deploy-prod 兩個 Job 定義了不一樣變量(variables)以及觸發條件(only)。除此以外, deploy-prod 經過 tags 關鍵字來使用不一樣的 Runner,將部署的目標集羣指向給生產環境的 Kubernetes。
這裏還有一個關鍵字 environment 須要特別說明,在定義了 environment 以後,咱們能夠在 GitLab 中查看每次部署的一些信息。除了查看每次部署的一些信息以外,咱們還能夠很方便地進行從新部署和回滾。
能夠看到,經過對 Job 的關鍵字進行配置,咱們能夠靈活地編排出咱們所須要的 CI/CD 流程,很是好地知足多樣化的場景。
在 CI/CD 流程中完成 Docker 鏡像的打包任務以後須要將服務所對應的鏡像部署到 Kubernetes 集羣中。Kubernetes 提供了多種能夠編排調度的資源對象。首先,咱們簡單瞭解一下 Kubernetes 中的一些基本資源。
Pod 做爲無狀態應用的運行實體是其中最經常使用的一種資源對象,Kubernetes 中資源調度最小的基本單元,它包含一個或多個緊密聯繫的容器。這些容器共享存儲、網絡和命名空間,以及如何運行的規範。
在 Kubernetes 中,Pod 是非持久的,會由於節點故障或者網絡不通等狀況而被銷燬和重建。因此咱們在 Kubernetes 中通常不會直接建立一個獨立的 Pod,而是經過多個 Pod 對外提供服務。
ReplicaSet 是 Kubernetes 中的一種副本控制器,控制由其管理的 Pod,使 Pod 副本的數量維持在預設的個數。ReplicaSets 能夠獨立使用,可是在大多數場景下被 Deployments 做爲協調 Pod 建立,刪除和更新的機制。
Deployment 爲 Pod 和 ReplicaSet 提供了一個聲明式定義方法。經過在 Deployment 中進行目標狀態的描述,Deployment controller 會將 Pod 和 ReplicaSet 的實際狀態改變爲所設定的目標狀態。Deployment 典型的應用場景包括:
在 Kubernetes 中,Pod 會被隨時建立或銷燬,每一個 Pod 都有本身的 IP,這些 IP 也沒法持久存在,因此須要 Service 來提供服務發現和負載均衡能力。
Service 是一個定義了一組 Pod 的策略的抽象,經過 Label Selector 來肯定後端訪問的 Pod,從而爲客戶端訪問服務提供了一個入口。每一個 Service 會對應一個集羣內部的 ClusterIP,集羣內部能夠經過 ClusterIP 訪問一個服務。若是須要對集羣外部提供服務,能夠經過 NodePort 或 LoadBalancer 方式。
deployment.yml 文件用來定義 Deployment。首先經過一個簡單的 deployment.yml 配置文件熟悉 Deployment 的配置格式。
上圖中 deployment.yml 分爲 8 個部分,分別以下:
在實際應用中,還有更多靈活個性化的配置。咱們在 Kubernetes 的部署實踐中制定了相關的規範,在以上基礎結構上進行配置,獲得知足咱們實際需求的 deployment.yml 配置文件。
在 Kubernetes 的遷移實踐中,咱們主要在如下方面對 Deployment 的配置進行了規範的約定:
首先咱們的 deployment.yml 配置文件是帶有變量的模版文件,以下所示:
apiVersion: apps/v1beta2 kind: Deployment metadata: labels: app: __APP_NAME__ group: __GROUP_NAME__ name: __APP_NAME__ namespace: __NAMESPACE__
APPNAME__、__GROUPNAME 和 __NAMESPACE__ 這種形式的變量都是在 CI/CD 流程中會被替換成 GitLab 每一個 project 所對應的變量,目的是爲了多了 project 用相同的 deployment.yml 文件,以便在進行 Kubernetes 遷移時能夠快速複製,提升效率。
節點配置策略,以項目組做爲各項目 Pod 運行在哪些 Node 節點的依據,屬於同一項目組的項目的 Pod 運行在同一批 Node 節點。具體操做方式爲給每一個 Node 節點打上形如 group:__GROUP_NAME__ 的標籤,在 deployment.yml 文件中作以下設置進行 Pod 的 Node 節點選擇:
... spec: ... template: ... spec: ... affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: group operator: In values: - __GROUP_NAME__ ...
資源請求大小,對於一些重要的線上應用,limit 和 request 設置一致,資源不足時 Kubernetes 會優先保證這些 Pod 正常運行。爲了提升資源利用率。對一些非核心,而且資源不長期佔用的應用,能夠適當減小 Pod 的 request,這樣 Pod 在調度時能夠被分配到資源不是十分充裕的節點,提升使用率。可是當節點的資源不足時,也會優先被驅逐或被 oom kill。
Liveness 主要用於探測容器是否存活,若監控檢查失敗會對容器進行重啓操做。Readiness 則是經過監控檢測容器是否正常提供服務來決定是否加入到 Service 的轉發列表接收請求流量。Readiness 在升級過程能夠發揮重要的做用,防止升級時異常的新版本 Pod 替換舊版本 Pod 致使整個應用將沒法對外提供服務的狀況。
每一個服務必須提供能夠正常訪問的接口,在 deployment.yml 文件配置好相應的監控檢測策略。
... spec: ... template: ... spec: ... containers: - name: fpm livenessProbe: httpGet: path: /__PROJECT_NAME__ port: 80 initialDelaySeconds: 3 periodSeconds: 5 readinessProbe: httpGet: path: /__PROJECT_NAME__ port: 80 initialDelaySeconds: 3 periodSeconds: 5 ... ...
升級策略咱們選擇 RollingUpdate 的方式,即在升級過程當中滾動式地逐步新建新版本的 Pod,待新建 Pod 正常啓動後逐步 kill 掉老版本的 Pod,最終所有新版本的 Pod 替換爲舊版本的 Pod。
咱們還能夠設置 maxSurge 和 maxUnavailable 的值分別控制升級過程當中最多能夠比原先設置多出的 Pod 比例以及升級過程當中最多有多少比例 Pod 處於沒法提供服務的狀態。
... spec: ... strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate ...
採用 log-tail 對容器日誌進行採集,全部服務的日誌都上報到阿里雲日誌服務的一個 log-store中。在 deployment.yml 文件裏配置以下:
... spec: ... template: ... spec: ... containers: - name: fpm env: - name: aliyun_logs_vpgame value: stdout - name: aliyun_logs_vpgame_tags value: topic=__APP_NAME__ ... ...
經過設置環境變量的方式來指定上傳的 Logstore 和對應的 tag,其中 name 表示 Logstore 的名稱。經過 topic 字段區分不一樣服務的日誌。
經過在 Deployment 中增長 annotations 的方式,令 Prometheus 能夠獲取每一個 Pod 的業務監控數據。配置示例以下:
... spec: ... template: metadata: annotations: prometheus.io/scrape: "true" prometheus.io/port: "80" prometheus.io/path: /{{ project_name }}/metrics ...
其中 prometheus.io/scrape: "true" 表示能夠被 Prometheus 獲取,prometheus.io/port 表示監控數據的端口,prometheus.io/path 表示獲取監控數據的路徑。
service.yml 文件主要對 Service 進行了描述。
apiVersion: v1 kind: Service metadata: annotations: service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet labels: app: __APP_NAME__ name: __APP_NAME__ namespace: __NAMESPACE__ spec: ports: - port: 80 protocol: TCP targetPort: 80 selector: app: __APP_NAME__ type: LoadBalancer
對 Service 的定義相比於 Deoloyment 要簡單的多,經過定義 spec.ports 的相關參數能夠指定 Service 的對外暴露的端口已經轉發到後端 Pod 的端口。spec.selector 則是指定了須要轉發的 Pod 的 label。
另外,咱們這邊是經過負載均衡器類型對外提供服務,這是經過定義 spec.type 爲 LoadBalancer 實現的。經過增長 metadata.annotations 爲 service.beta.kubernetes.io/alicloud-loadbalancer-address-type: intranet 能夠在對該 Service 進行建立的同時建立一個阿里雲內網 SLB 做爲對該 Service 請求流量的入口。
如上圖所示,EXTERNAL-IP 即爲 SLB 的 IP。
在以上工做的基礎上,咱們對各個服務劃分爲幾類(目前基本上按照語言進行劃分),而後爲每一類中的服務經過 .gitlab-ci.yml 制定一套統一的 CI/CD 流程,與此相同的,同一類中的服務共用一個 Deployment 和 Service 模板。這樣咱們在進行服務遷移到 Kubernetes 環境時能夠實現快速高效地遷移。
固然,這只是遷移實踐路上邁出的第一步,在 Kubernetes 中的服務的穩定性、性能、自動伸縮等方面還須要更深刻地探索和研究。
iPhone 11 Pro、衛衣、T恤等你來抽,立刻來試試手氣 https://www.aliyun.com/1111/2019/m-lottery?utm_content=g_1000083877
本文爲雲棲社區原創內容,未經容許不得轉載。