Kubernetes migration practice of VPGAME

Author | Wu Chong Bin VPGAME operation and maintenance Development Engineer

REVIEW : VPGAME is a set of event operations, information media, big data analytics, social players, game peripherals, such as an integrated gaming platform. Headquartered in Hangzhou, China, in Shanghai and Seattle were set up gaming large data centers and R & D AI research and development center. This article describes VPGAME migrate server to process the Kubernetes.

background

As the technology matures container, the company's recent plan to migrate services to the container environment, scheduling, organization and management of the vessel by Kubernetes. And take this opportunity to standardize services, streamline business processes throughout the CI / CD, improve the efficiency of service deployment.

Select CI / CD tools

The CI / CD tools, we chose GitLab-CI. GitLab-CI is a complex integration system gitlab continuous use, to complete the installation-dependent code after submission, compilation, unit testing, lint, and the like to build the mirror release.

GitLab-CI and GitLab perfect integration, when using only need to install configuration gitlab-runner. GitLab-Runner After completion of the registration to the environment may be provided gitlab CI / CD operation, is responsible for taking the code from the pull gitlab The gitlab-ci.yml code repository configured, perform the corresponding command CI / CD work.

Compared to Jenkins, GitLab-CI configuration as simple configuration file gitlab-ci.yml complete CI / CD writing process in the project, do not like the configuration webhook callback address as in Jenkins, the new project does not need Jenkins the package is built. And personally I think GitLab of CI / CD process shows more beautiful than Jenkins. Of course, Jenkins rely on its rich plug-ins, you can configure many GitLab-CI function does not exist. Now according to our needs, GitLab-CI easy to use, the function is also to meet our needs.

Service Runtime Environment

Environmental advantages of container

Traditional service deployment is installed in the operating system good a corresponding application-dependent, and then install the application service, the disadvantage of this deployment is to program services, configuration, and dependencies life cycle and host operating system closely coupled together, upgrade services, scalable capacity, migration operation is not very convenient.

Deployment container is based on the image as the core, when compiled builds the application and the application needs to run depends packaged into a mirror in the code, during the deployment phase, deployment and operation of services to create a container instance by mirroring. Enabling application-centric management, isolation container provides isolation of resources, since the container does not depend on the host operating system environment, it can well ensure the consistency of development, test and production environments. Further, since the constructed image is immutable and may be controlled by Tag versions, it is possible to provide a reliable and frequent image build and deploy the container, it can be easily and quickly rollback.

Kubernetes platform features

Kubernetes (referred k8s), as a container scheduling, orchestration and management platform, you can schedule and run the application on a vessel in a physical or virtual machine cluster, provides the infrastructure to a container as the core. On the container for scheduling and management through Kubernetes, you can:

  • Rapid, predictable deployment services
  • Ability to have immediate extension services
  • Rolling upgrade, completion of the new feature releases
  • Optimizing hardware resources, reduce costs

Ali cloud container service advantages

We use the services of the migration service container Ali cloud, it adapts and enhanced based on native Kubernetes, simplifying cluster setup and expansion work, the integration of Ali cloud virtualization, storage, networking and security capabilities, to build the best cloud Kubernetes container application runtime environment. On the convenience, can be done through a Web interface to create Kubernetes key cluster, upgrades and node scaling capacity. Functionally, the integrated network, storage, load balancing, and surveillance and Ali cloud resources can be minimized to reduce the impact caused by migration during the migration process.

In addition, the choice of cluster creation, we chose the hosted version Kubernetes, simply create a node Worker, Master node is created and hosted by container service. In this way, we are in the planning and resource isolation Worker nodes still have the autonomy and flexibility at the same time does not require operation and maintenance management Kubernetes Master cluster node, you can focus more energy on application services.

GitLab Runner deployment

GitLab CI workflow

2

GitLab CI Basic Concepts

Before introducing GitLab CI, first briefly explain some of the basic concepts in GitLab CI, as follows:

  • Pipeline: Gitlab CI in the pipeline, each commit code to trigger GitLab CI will have a Pipeline;
  • Stage: Each Pipeline Stage a plurality, and each is a sequential Stage;
  • Job: GitLab CI in the smallest task unit, complete with a charge of one thing, such as compiling, testing, building mirroring. Each Job will need to specify the Stage, so the order of execution of Job can be achieved through the development of different Stage;
  • GitLab Runner: Job specific execution environments, each Runner at the same time can only execute a Job;
  • Executor: Executor needs to be specified for each Runner at the time of registration to GitLab, to determine what types of actuators to complete the Job through.

GitLab CI workflow

When the code push to GitLab, it will trigger a Pipeline. Operation then compiled, tested and mirror constructed like operation, wherein each step are a Job. In the CD stage, the stage will be to build out the results CI deployed to the test or production environments according to the situation.

GitLab Runner Introduction

Gitlab Runner Category

There are three types of GitLab Runner, respectively:

  • shared: All items Use
  • group: group under the project uses
  • specific: Use the specified item

We can register different types of Runner to GitLab required, registration is the same way.

Gitlab Runner working process

Runner will first launch to GitLab registration request, the request contained in the content token, tag and other information, GitLab returns a token to the Runner After successful registration, subsequent requests, Runner will carry this request.

After successful registration, Runner will keep GitLab request to Job, the time interval is 3s. If no request to Job, GitLab returns 204 No Content. If the request to Job, Job information gitlab will return back, Runner after receiving Job, it will send a confirmation request to GitLab, and update status of the task. After, Job Runner started, and the intermediate data will periodically, in the manner Patch request to the GitLab.

3

GitLab Runner 的 Executor

Runner in the actual implementation of Job, is done by calling Executor. Runner offers different types of SSH, Shell, Docker, docker-ssh, VirtualBox, Kubernetes etc. Executor at the time of registration to meet different scenarios and requirements.

Which we often have Shell and Docker such as Executor, Shell is the main type of use Job execution environment where the host Runner. Docker type and the beginning of each Job Executor, producing a mirrored pull container, container in the Job is completed, after the Job is completed, the corresponding container is destroyed. Since Docker isolation and strong, lightweight and recycling, we use Docker type of Executor when used to perform Job, as long as we needed to do Docker mirror Job environment ahead of time, the definition of a good image can be used in each of the Job corresponding environment , convenient operation.

Installation and Configuration GitLab Runner

Docker installation

由于我们需要使用 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 安装与启动

执行下面的命令进行 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 Runner 后还需要向 GitLab 进行注册,在注册前需要从 GitLab 里查询 token。不同类型的 Runner 对应的 token 获取的路径不同。shared Runner 需要 admin 权限的账号,按如下方式可以获取对应的 token。

4

其他两种类型在对应的页面下( 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 的信息。

5

此外,如果一台服务需要注册多个 Runner ,可以修改 /etc/gitlab-runner/config.toml 中的 concurrent 值增加 Runner 的并发数,修改完之后同样需要重启 Runner。

Docker 基础镜像制作

为了满足不同服务对运行环境的多样化需求,我们需要为不同语言的服务提前准备不同的基础镜像用于构建镜像阶段使用。此外,CI/CD 所需要的工具镜像也需要制作,作为 Runner 执行 Job 时生成容器所需要的 Docker 镜像。
所有的镜像都以编写 Dockerfile 的形式通过 GitLab 进行管理,并且我们编写了 .gitlab-ci.yml 文件,使得每次有 Dockerfile 新增或者修改就会触发 Pipeline 进行镜像的构建并上传到 Harbor 上。这种管理方式有以下优点:

  • 按照一定规则自动构建镜像,可以快速便捷地新建和更新镜像
  • 根据规则可以找到镜像对应的 Dockerfile,明确镜像的具体组成
  • 团队成员可以通过提交 Merge Request 自由地构建自己需要的镜像

镜像分类

6

  • 运行时基础镜像:提供各个语言运行时必须的工具和相应的 package。
  • CI 镜像:基于运行时基础镜像,添加单元测试、lint、静态分析等功能,用在 CI/CD 流程中的 test 环节。
  • 打包上线镜像:用在 CI/CD 流程中的 build 和 deploy 环节。

Dockerfile 目录结构

每个文件夹都有 Dockerfile 来描述镜像的基本情况,其中包含了 Java、PHP、Node 和 Go 等不同语言的运行时基础镜像和 CI 镜像,还有 docker-kubectl 这类工具镜像的 Dockerfile。

7

以 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 上。

8

镜像自动构建基本原理

由于各个镜像通过 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 命令找出合并前后有差异的文件。

加速 tips

  • Alpine Linux Package Management(APK)镜像地址:http://mirrors.aliyun.com
  • 一些海外软件下载会比较慢,可以先下载下来上传至阿里云 OSS 后下载。Dockerfile 使用阿里云 OSS 作为下载源,减少构建镜像时间。

基于 .gitlab-ci.yml 的 CI/CD 流程

在完成 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

stages 是一个非常重要的概念, 在 .gitlab-ci.yml 中进行全局定义, 在定义 Job 时指定其中的值来表明 Job 所处的 stage。而在 stages 里元素的顺序定义了 Job 的执行顺序:所有在相同 stage 的 Job 会并行执行,只有当前 stage 的所有成功完成后,后面 stage 的 Job 才会去执行。
例如,定义如下 stages:

stages:
  - build
  - test
  - deploy
  1. 首先,所有 build 里的 Job 会并行执行;
  2. 当 build 里所有 Job 执行成功, test 里所有 Job 会并行执行;
  3. 如果 test 里所有 Job 执行成功, deploy 里所有 Job 会并行执行;
  4. 如果 deploy 里所有 Job 执行成功, 当前 Pipeline 会被标记为 passed;
  5. 当某个 stage 的 Job 执行失败, Pipeline 会标记为为 failed,其后续stage 的 Job 都不会被执行。

Job 的描述

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 每个关键字的具体含义。

  • stage,定义了 Job 所处的 stage,值为定义在全局中 stages 里的值;

  • image,指定了 Runner 运行所需要的镜像,这个镜像是我们之前制作的基本镜像。通过该镜像运行的 Docker 即是 Job 运行的环境;

  • services,Runner 所运行的 Docker 所需要的连接依赖,在这边分别定义了 MySQL 和 Redis,在 Job 运行时会去连接这两个镜像生成的 Docker;

  • script,Job 运行的具体的命令 ,通过 Shell 来描述。此 Job 中的 script 主要完成了代码的编译和单元测试;

  • artifacts,主要是将此 Job 中完成的结果打包保存下来,可以通过 when 指定何时保存,path 定义了保存的文件路径, expire_in 指定了结果保存的有效期。与之对应的是 dependencies 参数,如果其他 Job 需要此 Job 的 artifacts ,只需要在 Job 按照如下定义即可;

dependencies:
  - unittest
  • only 关键字指定了 Job 触发的时机,该例子中说明只有分支合并或者打 tag 的情况下,该 Job 才会被触发;

  • 与 only 相对还有 except 关键字来排除触发 Job 某些情况。此外 only 还支持正则表达式,比如;

job:
  only:
    - /^issue-.*$/
  except:
    - branches

这个例子中,只有以 issue- 开头 tag 标记才会触发 Job。如果不加 except 参数,以 issue- 开头的分支或者 tag 标记会触发 Job。

  • tags,tags关键字主要是用来指定运行的 Runner 类型。在我们实际运用中,部署测试环境和生产环境所采用的 Runner 是不一样的,它们是通过不同的 tag 去标识区分。

9

所以,我们在 Job 定义中,通过 tags 指定 Runner 的值,来指定所需要的 Runner。

我们可以看到 Job 的定义非常的清晰和灵活,关于 Job 的使用远不止这些功能,更详细的用法可以参考 GitLab CI/CD 官方文档。

CI/CD 流程编排

在清楚了如何描述一个 Job 之后,我们通过定义一个个 Job,并进行编排形成 Pipelines。因为我们可以描述设定 Job 的触发条件,所以通过不同的条件可以触发形成不一样的 Pipelines。
在 PHP 项目 Kubernetes 上线过程中,我们规定了合并 Master 分支会进行 lint、unitest、build-test 以及 deploy-test 四个 Job。

10

在测试环境验证通过之后,我们再通过打 tag 进行正式环境的上线。此处的 Pipelines 包含了 unittest、build-pro 和 deploy-pro 三个 Job。

11

在 .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 中查看每次部署的一些信息。除了查看每次部署的一些信息之外,我们还可以很方便地进行重新部署和回滚。

12

可以看到,通过对 Job 的关键字进行配置,我们可以灵活地编排出我们所需要的 CI/CD 流程,非常好地满足多样化的场景。

Deployment 与 Service 配置

在 CI/CD 流程中完成 Docker 镜像的打包任务之后需要将服务所对应的镜像部署到 Kubernetes 集群中。Kubernetes 提供了多种可以编排调度的资源对象。首先,我们简单了解一下 Kubernetes 中的一些基本资源。

Kubernetes 基本资源对象概览

Pod

Pod 作为无状态应用的运行实体是其中最常用的一种资源对象,Kubernetes 中资源调度最小的基本单元,它包含一个或多个紧密联系的容器。这些容器共享存储、网络和命名空间,以及如何运行的规范。

在 Kubernetes 中,Pod 是非持久的,会因为节点故障或者网络不通等情况而被销毁和重建。所以我们在 Kubernetes 中一般不会直接创建一个独立的 Pod,而是通过多个 Pod 对外提供服务。

ReplicaSet

ReplicaSet 是 Kubernetes 中的一种副本控制器,控制由其管理的 Pod,使 Pod 副本的数量维持在预设的个数。ReplicaSets 可以独立使用,但是在大多数场景下被 Deployments 作为协调 Pod 创建,删除和更新的机制。

Deployment

Deployment 为 Pod 和 ReplicaSet 提供了一个声明式定义方法。通过在 Deployment 中进行目标状态的描述,Deployment controller 会将 Pod 和 ReplicaSet 的实际状态改变为所设定的目标状态。Deployment 典型的应用场景包括:

  • 定义 Deployment 来创建 Pod 和 ReplicaSet
  • 滚动升级和回滚应用
  • 扩容和缩容
  • 暂停和继续 Deployment

Service

在 Kubernetes 中,Pod 会被随时创建或销毁,每个 Pod 都有自己的 IP,这些 IP 也无法持久存在,所以需要 Service 来提供服务发现和负载均衡能力。

Service 是一个定义了一组 Pod 的策略的抽象,通过 Label Selector 来确定后端访问的 Pod,从而为客户端访问服务提供了一个入口。每个 Service 会对应一个集群内部的 ClusterIP,集群内部可以通过 ClusterIP 访问一个服务。如果需要对集群外部提供服务,可以通过 NodePort 或 LoadBalancer 方式。

deployment.yml 配置

deployment.yml 文件用来定义 Deployment。首先通过一个简单的 deployment.yml 配置文件熟悉 Deployment 的配置格式。

13

上图中 deployment.yml 分为 8 个部分,分别如下:

  1. apiVersion 为当前配置格式的版本;
  2. kind 指定了资源类型,这边当然是 Deployment;
  3. metadata 是该资源的元数据,其中 name 是必需的数据项,还可以指定 label 给资源加上标签;
  4. spec 部分是该 Deployment 的规格说明;
  5. spec.replicas 指定了 Pod 的副本数量;
  6. spec.template 定义 Pod 的基本信息,通过 spec.template.metadata 和 spec.template.spec 指定;
  7. spec.template.metadata 定义 Pod 的元数据。至少需要定义一个 label 用于 Service 识别转发的 Pod, label 是通过 key-value 形式指定的;
  8. spec.template.spec 描述 Pod 的规格,此部分定义 Pod 中每一个容器的属性,name 和 image 是必需项。

在实际应用中,还有更多灵活个性化的配置。我们在 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__

APPNAMEGROUPNAMENAMESPACE 这种形式的变量都是在 CI/CD 流程中会被替换成 GitLab 每个 project 所对应的变量,目的是为了多了 project 用相同的 deployment.yml 文件,以便在进行 Kubernetes 迁移时可以快速复制,提高效率。

服务名称

  • Kubernetes 中运行的 Service 以及 Deployment 名称由 GitLab 中的 groupname 和 projectname 组成,即 {{groupname}}-{{projectname}},例:microservice-common;
    此名称记为 app_name,作为每个服务在 Kubernetes 中的唯一标识。这些变量可以通过 GitLab-CI 的内置变量中进行获取,无需对每个 project 进行特殊的配置。

  • Lables 中用于识别服务的标签与 Deployment 名称保持一致,统一设置为 app:{{app_name}}。

资源分配

节点配置策略,以项目组作为各项目 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)配置

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。

14

我们还可以设置 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.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 请求流量的入口。

15

如上图所示,EXTERNAL-IP 即为 SLB 的 IP。

总结

On the basis of the above work, we divide the various types of services (now largely divided according to language), then .gitlab-ci.yml develop a unified CI / CD processes for each type of service by, this same, the same class of service and Deployment service share a template. In this way we can achieve fast and efficient service during the migration when migrating to Kubernetes environment.

Of course, this is only the first step in the migration path of practice, but also more in-depth exploration and research in stability, performance, automatic retractable Kubernetes of services and so on.

"Alibaba Cloud native micro-channel public number (ID: Alicloudnative) focus on micro service, Serverless, container, Service Mesh and other technical fields, focusing popular technology trends in cloud native, cloud native large-scale landing practice, do most understand cloud native developers technology public number. "

This article from the blog article multiple platforms OpenWrite release!

Guess you like

Origin www.cnblogs.com/alisystemsoftware/p/11714993.html
Recommended