notes

k8s实战

这里以我们双十一的项目为例子。

总的部署流程如下:

在使用 golang 起一个 https 服务,编写好业务逻辑后,通过 gitlab CI 去自动部署到 k8s 上。

  1. 使用 golang 起一个 https 服务,编写好业务逻辑。
  2. 提交代码到 gitlab ,并合并到 master 分支
  3. 合并到 master 分支后,会自动触发 CI ,执行构建和部署流程。部署项目到测试环境。
  4. 通过打 tag 的方式,部署到正式环境。

gitlab-ci.yml 配置如下:

image: zacksleo/golang

before_script:
  # 预先装 ssh-agent
  - 'which ssh-agent || ( apk update && apk add openssh-client)'
  # 启动服务
  - eval $(ssh-agent -s)
  # 将私钥写入deploy.key 文件
  - echo "$SSH_PRIVATE_KEY" > ~/deploy.key
  - echo "$SSH_PRIVATE_PRIVATE_REPO_KEY" > ~/deploy_private_repo.key
  # 配置较低权限
  - chmod 0600 ~/deploy.key
  - chmod 0600 ~/deploy_private_repo.key
  # 注入密钥
  - ssh-add ~/deploy.key
  - ssh-add ~/deploy_private_repo.key
  - mkdir -p ~/.ssh
  - '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'


stages:
  - install_dep
  - build
  - deploy


# 打包
build-package:
  stage: install_dep
  script:
    # 项目中用到了私有库
    - git config --global url."git@your.gitlab.host.com:".insteadOf "https://your.gitlab.host.com/"
    - go mod tidy
    # 这里把编译的文件放到子目录避免把 .git 目录也打包进来
    - go build -o build/app
  only:
    - master
    - tags
  artifacts:
    name: "app"
    untracked: true     # true表示打包没有被git跟踪的文件
    expire_in: 60 mins  # 在 gitlab 上保存一小时后自动删除
    paths:
      - ./build
    when: on_success
  tags:
    - hongkong


# 构建镜像
build-webserver-image:
  stage: build
  image: docker:latest
  dependencies:
    - build-package
  cache: {}
  before_script: []
  script:
    - mv build/app app
    - cp deploy/testing/.env .env
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:8} .
    - docker push $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:8}
    - docker rmi $CI_REGISTRY_IMAGE:${CI_COMMIT_SHA:0:8}
  only:
    - master
  tags:
    - mops-temp


build-webserver-image-prd:
  stage: build
  image: docker:latest
  dependencies:
    - build-package
  cache: {}
  before_script: []
  script:
    - mv build/app app
    - cp deploy/production/.env .env
    - docker login -u gitlab-ci-token -p $CI_JOB_TOKEN $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
    - docker rmi $CI_REGISTRY_IMAGE:$CI_COMMIT_TAG
  only:
    - tags
  tags:
    - mops-temp

deploy-testing:
  stage: deploy
  variables:
    SERVER: $DEPLOY_SERVER
    SERVER_PATH: "/opt/k8s/apps/gitlab"
  dependencies: []
  cache: {}
  script:
    - ssh $SERVER "mkdir -p $SERVER_PATH/$CI_PROJECT_NAME"
    - scp -r deploy/testing/* $SERVER:$SERVER_PATH/$CI_PROJECT_NAME
    - ssh $SERVER "cd $SERVER_PATH/$CI_PROJECT_NAME/manifest && sed -i 's/__APP_VERSION__/${CI_COMMIT_SHA:0:8}/' deployment.yaml"
    - ssh $SERVER "cd $SERVER_PATH/$CI_PROJECT_NAME/manifest && kubectl apply -f deployment.yaml && kubectl apply -f service.yaml && kubectl apply -f ingress.yaml "
  only:
    - master
  tags:
    - mops-temp

deploy-prd:
  stage: deploy
  variables:
    SERVER: $DEPLOY_SERVER
    SERVER_PATH: "/opt/k8s/apps/business"
  dependencies: []
  cache: {}
  script:
    - ssh $SERVER "mkdir -p $SERVER_PATH/$CI_PROJECT_NAME"
    - scp -r deploy/production/* $SERVER:$SERVER_PATH/$CI_PROJECT_NAME
    - ssh $SERVER "cd $SERVER_PATH/$CI_PROJECT_NAME/manifest && sed -i 's/__APP_VERSION__/$CI_COMMIT_TAG/' deployment.yaml"
    - ssh $SERVER "cd $SERVER_PATH/$CI_PROJECT_NAME/manifest && kubectl apply -f deployment.yaml && kubectl apply -f service.yaml && kubectl apply -f ingress.yaml "
  only:
    - tags
  tags:
    - mops-temp

在 install_dep 这个 stage 的时候,build 后的 app 使用 artifact 的方式传输到下一个 stage。值得一提的是,使用 artifact 的方式好像只能打包整个目录,如果目录中有 .git 文件夹的话也会打包进来,这显然不太合理。所以我们把构建好的 app 文件放在另一个目录下,就避免了这个问题。

下一个 stage 是 build。

在 build 阶段,runner 会把项目从 git 仓库再拉一遍,也会把 artifact 下载下来,所以当前目录下除了 git 仓库的文件,还有一个 build/app 文件。所以我们要把 app 移动到项目根目录,再把 testing 中的配置文件也复制到项目根目录。就可以执行 docker build 命令制作镜像了。

这里务必要创建 .dockerignore 文件,把 .git 文件加进去,不然会发现构建的镜像体积会很大。

最后一个阶段是 deploy,顾名思义,就是部署阶段。

在这个阶段,我们会把做好的镜像部署到 k8s 上。

先看一下 $SERVER_PATH 这个变量,他的值是 /opt/k8s/apps/gitlab,前面的 /opt/k8s/apps 是固定的,后面的 gitlab 是命名空间的名字。在 k8s 中,我们创建了一个叫做 gitlab 的命名空间,用于部署测试环境的业务服务(不要问我为什么名字叫做 gitlab,而不叫做 test 等更容易理解的名字,因为不是我创建的)。与之对应的,正式环境的命名空间是 business ,后面会把正式环境的应用部署到这个命名空间中。

在这个阶段,实际执行部署的命令就是最后一句的 kubectl apply -f xxx.yaml 文件,当然,我们现在要一个个去分析 yaml 文件。