少湖说 | 科技自媒体

互联网,科技,数码,鸿蒙

语言

  1. 两者都是胶水语言, 不需要编译, 逐行运行, 因此报错时可以追中到行
  2. 主流都是用C实现的, 因此底层仍然是C

格式

  1. PHP与Python在代码格式上有非常大的不同, 首先最明显的一个区别就是, 没有大括号包裹函数和类库.
  2. Python使用缩进来替代大括号的功能, 开始的时候可能会让你不太习惯, 不过时间长了, 也就慢慢接受了

依赖管理

  1. PHP使用composer进行依赖管理,既可以全局安装,也可以安装到当前项目
  2. Python使用Pip进行依赖管理,只能全局安装

创建文件

在根目录新建.gitlab-ci.yml文件.

该文件与项目其他文件一样, 同样受到版本控制, 所以可以在不同的分支下, 配置不同的持续集成脚本

YAML语法

配置文件遵循YAML语法, 关于该语法的内容, 自行搜索

参考 YAML 语言教程

关键词

根主要关键词一览

关键词 含义 可选 备注
image 声明使用的Docker镜像 为空时使用默认镜像 该镜像应当满足脚本执行的环境依赖
services Docker镜像使用的服务, 通过链接的方式来调用所需服务 可空 常用于链接数据库
stages 定义构建阶段 为空时, 单纯定义jobs 项目的构建分为多个阶段, 例如: 安装依赖/准备, 编译, 测试, 发布等, 同时每个阶段包含若干任务
before_script 定义每个job之前执行的脚本 可空 每个job启动时会先执行该脚本
after_script 定义每个job之后执行的脚本 可空 同上
variables 定义变量 可空 同上
cache 定义与后续job之间应缓存的文件 可空 同上

Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
image: aipline
services:
- mysql
- redis
stages:
- build
- test
- deploy
before_script:
- bundle install
after_script:
- rm secrets
cache:
paths:
- binaries/
- .config

Jobs中的关键词

jobs中存在一些与根中相同的关键词, 这些一旦定义, 则会向前覆盖, 即根中定义的则不会在该job执行

job 这里译为任务

关键词 含义 可选 备注
image 声明任务使用的Docker镜像 为空时使用根中的定义 该镜像应当满足脚本执行的环境依赖
services 任务中Docker镜像使用的服务, 通过链接的方式来调用所需服务 可空 常用于链接数据库
stage 所属构建阶段 为空时则不使用stages 一个任务属于一个构建阶段
before_script 定义每个job之前执行的脚本 可选 如果在job中定义则会覆盖根中的内容
script 定义每个job执行的脚本 必须
after_script 定义每个job之后执行的脚本 可选 同上
variables 定义任务中使用的变量 可选 同上
cache 定义与后续job之间应缓存的文件 可选 同上
only 指定应用的Git分支 可选 可以是分支名称, 可用正则匹配分支, 也可是tags来指定打过标签的分支
except 排除应用的Git分支 可选 同上
tags 指定执行的GitLab-Runners 可选 通过匹配Runners的标签选定
allow_failure 允许失败 默认为false 如果允许失败, 本次任务不会影响整个构建的结果
when 定义合适执行任务 默认为always on_success, on_failure, always or manual可选
dependencies 定义合任务所需要的工件 可空 需要首先定义工件
artifacts 定义工件 可空 工件中指定的目录会在任务执行成功后压缩传到GitLab, 后面需要该工件的任务执行时, 再自行下载解压
environment 定义环境 可空 在部署任务中, 定义该任务所属的环境

Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
installing-dependencies:
script:
- composer install --prefer-dist --optimize-autoloader -n --no-interaction -v --no-suggest
- composer dump-autoload --optimize
artifacts:
name: "vendor"
untracked: true
expire_in: 60 mins
paths:
- vendor/
docker-build-image:
stage: test
only:
- master
except:
- develop
tags:
- ruby
- postgres
allow_failure: true
dependencies:
- installing-dependencies
script:
- docker build -t registry.com/mops/image:latest .
- docker push registry.com/mops/image:latest

注意:

  1. jobs的名称不能重名
  2. 同一阶段中的任务, 是并行执行的
  3. 上一阶段所有任务执行完后, 才会进入下一阶段
  4. 定义工件时, 务必定义工件的过期时间, 否则工件会一直寸在GitLab上, 占用空间
  5. 如果需要在任务中传递文件, 优先选择使用 dependencies (结合artifacts)

验证配置文件合法性

在GitLab中, 打开 /ci/lint网址, 将配置文件粘贴在些, 进行验证

相关文档

假定已经安装好了GitLab-Runners

Hello World !

在仓库根目录创建 .gitlab-ci.yml 文件, 内容如下

1
2
3
job-1:
script:
- echo "Hello World"

这样, 在每次提交代码后, 都会自动执行以上脚本. 其中job-1是任务名称, 可以定义多个任务,

script下面是 shell 命令, 只要命令执行成功, 就代表本次构建通过(出现passed标记)

如图

这样, 一次简单的持续集成已经搞定了.

远程拉取代码

使用ssh远程登录服务器, 然后执行git pull 拉取代码, 实现代码热更新

由于ssh无密码登录需要用到密钥, 所以首先需要注入私钥

release-doc:
    stage: deploy
    script:
        - ssh root@$DEPLOY_SERVER "cd /mnt/data/docker-gollum/wiki && git pull origin master"

一个更详细的例子 [[通过gitlab-ci实现文件的自动部署]]

通过Docker镜像实现自动部署

见文章 [[GitLab-CI使用Docker进行持续部署]]

参考资料

概述

持续集成(CI)和 持续交付(CD) 是一种流行的软件开发实践,每次提交都通过自动化的构建(测试、编译、发布)来验证,从而尽早的发现错误。

持续集成实现了DevOps, 使开发人员和运维人员从繁琐的工作中解放出来。另外,这种形式极大地提高了开发者的开发效率和开发质量。
持续集成有多种工具,如Jenkins. GitLab内置了GitLab-CI,通过配置一段YAML脚本来实现持续集成.

功能

持续集成可以实现的功能:

  • 代码审核: 自动化代码规范审查, 甚至代码质量检查
  • 自动化测试: 单元测试, 功能测试和验收测试
  • 编译发布: 将源代码编译成可执行程序, 并将程序上传到托管发布平台实现自动发布
  • 构建部署: 通过构建Docker镜像, 或登录远程服务器执行相关部署命令和脚本, 实现自动化部署

原理

GitLab-CI 检测每次代码变动, 通过.gitlab-ci.yml脚本执行构建命令, 将命令发布到GitLab-Runners(运行机)上, 进而执行命令.

GitLab-Runners 基于Docker执行持续集成的每项任务, 这样就解决了环境依赖问题.

GitLab-Runners把实时将执行结果输出到GitLab网页上, 任务执行完后, 通过徽章标记和邮箱告知执行结果.

下一章: [[GitLab 快速开始]]

Docker镜像通过私有仓库进行发布(如阿里云), 发布命令为:

1
2
3
4
docker login -u username -p password registry.demo.com
docker build -t registry.demo.com/repos/$CI_PROJECT_NAME:latest .
docker push registry.demo.com/repos/$CI_PROJECT_NAME:latest

其中 username是用户名, password是密码, registry.demo.com是私有镜像库地址,

$CI_PROJECT_NAME 是GitLab-CI内置变量, 会自动替换为项目的名称, 这里也可以直接写死, 如

docker build -t registry.demo.com/repos/image-name:latest .

image-name, 就是要构建的镜像名称, latest是TAG标签, repos是仓库的空间名称

在下面的例子中, 首先通过composer安装依赖库, 然后通过artifacts传递给构建任务, 构建完镜像将镜像发布到私有库,
部署时通过拉取最新的镜像库, 进行部署

项目的deploy目录中, 放置一些配置文件, 如Dockerfile, docker-compose.yml等, 通过rsync同步到部署服务器上, 用于部署所需

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
image: zacksleo/docker-composer:1.1

before_script:
- 'which ssh-agent || ( apk update && apk add openssh-client)'
- apk add rsync
- eval $(ssh-agent -s)
- echo "$SSH_PRIVATE_KEY" > ~/deploy.key
- chmod 0600 ~/deploy.key
- ssh-add ~/deploy.key
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'
- export APP_ENV=testing

stages:
- prepare
- build
- deploy

variables:
COMPOSER_CACHE_DIR: "/cache/composer"
DOCKER_DRIVER: overlay

installing-dependencies:
stage: prepare
script:
- composer install --prefer-dist -n --no-interaction -v --no-suggest
artifacts:
name: "vendor"
untracked: true
expire_in: 60 mins
paths:
- $CI_PROJECT_DIR/vendor
test-image:
stage: build
image: docker:latest
services:
- docker:dind
dependencies:
- installing-dependencies
script:
- docker login -u username -p password registry.demo.com
- docker build -t registry.demo.com/repos/$CI_PROJECT_NAME:latest .
- docker push registry.demo.com/repos/$CI_PROJECT_NAME:latest
testing-server:
stage: deploy
image: alpine
variables:
DEPLOY_SERVER: "server-host"
script:
- cd deploy
- rsync -rtvhze ssh . root@$DEPLOY_SERVER:/data/$CI_PROJECT_NAME --stats
- ssh root@$DEPLOY_SERVER "docker login -u username -p password registry.demo.com"
- ssh root@$DEPLOY_SERVER "cd /data/$CI_PROJECT_NAME && docker-compose stop && docker-compose rm -f && docker-compose pull && docker-compose up -d"
- ssh root@$DEPLOY_SERVER "docker exec -i $CI_PROJECT_NAME chown www-data:www-data web/assets"
- ssh root@$DEPLOY_SERVER "docker exec -i $CI_PROJECT_NAME ./yii migrate/up --interactive=0"

相关文档

Using Docker Build

在GitLab-CI中, cacheartifacts比较容易混淆.

其中 cache 指的是缓存, 常用于依赖安装中, 如几个jobs都需要安装相同的依赖, 可以使用依赖, 此时可以加快依赖的安装进度;
对于artifacts则是将某个工件上传到GitLab提供下载或后续操作使用, 由于每个job启动时, 都会自动删除.gitignore中指定的文件, 因此对于依赖安装目录, 即可以使用cache, 也可以使用artifacts.

两个主要有以下几个区别:

  1. cache不一定命中,artifacts肯定命中, 能否使用cache取决当当前机器是否生成过cache, artifacts则每次都会从GitLab下载
  2. 重新安装时因为使用的是缓存, 所以很有可能不是最新的
  3. 特别是开发环境, 如果每次都希望使用最新的更新, 应当删除cache, 使用artifacts, 这样可以保证确定的更新
    4.artifacts中定义的部分, 会自动生成, 并可以传到下面的job中解压使用, 避免了重复依赖安装等工作
  4. 如果使用Docker运行Gitlab-Runner, cache会生成一些临时容器, 不容易清理
  5. artifacts可以设置自动过期时间, 过期自动删除,cache不会自动清理
  6. artifacts会先传到GitLab服务器, 然后需要时再重新下载, 所以这部分也可以在GitLab下载和浏览

artifacts 的依赖使用

下面是一个使用artifacts的例子, 首先有一个安装依赖的工作, 然后工作完成后, 会将安装文件转移到后续的工作时

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
installing-dependencies:
stage: prepare
script:
- composer install --prefer-dist --optimize-autoloader -n --no-interaction -v --no-suggest
- composer dump-autoload --optimize
artifacts:
name: "vendor"
untracked: true
expire_in: 60 mins
paths:
- $CI_PROJECT_DIR/vendor
code-review:
stage: testing
dependencies:
- installing-dependencies
script:
- php vendor/bin/phpcs --config-set ignore_warnings_on_exit 1
- php vendor/bin/phpcs --standard=PSR2 -w --colors ./
test-image:
stage: build
image: docker:latest
services:
- docker:dind
dependencies:
- installing-dependencies
script:
- docker build -t $CI_PROJECT_NAME:latest .
- docker push domain.com/repos/$CI_PROJECT_NAME:latest
only:
- develop

如果上述过程使用cache, 则会变成下面这样子, 注意, 此时每次都要执行composer install这样的依赖安装工作, 即before_script

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
cache:
paths:
- vendor
before_scritp:
- composer install --prefer-dist --optimize-autoloader -n --no-interaction -v --no-suggest
- composer dump-autoload --optimize
code-review:
stage: testing
script:
- php vendor/bin/phpcs --config-set ignore_warnings_on_exit 1
- php vendor/bin/phpcs --standard=PSR2 -w --colors ./
test-image:
stage: build
image: docker:latest
services:
- docker:dind
script:
- docker build -t $CI_PROJECT_NAME:latest .
- docker push domain.com/repos/$CI_PROJECT_NAME:latest
only:
- develop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
cache:
paths:
- vendor
code-review:
stage: testing
script:
- composer install --prefer-dist --optimize-autoloader -n --no-interaction -v --no-suggest
- composer dump-autoload --optimize
- php vendor/bin/phpcs --config-set ignore_warnings_on_exit 1
- php vendor/bin/phpcs --standard=PSR2 -w --colors ./
test-image:
stage: build
image: docker:latest
services:
- docker:dind
script:
- composer install --prefer-dist --optimize-autoloader -n --no-interaction -v --no-suggest
- composer dump-autoload --optimize
- docker build -t $CI_PROJECT_NAME:latest .
- docker push domain.com/repos/$CI_PROJECT_NAME:latest
only:
- develop

否则, 会出现类似 vendor not found的问题

禁用artifacts

默认artifacts会自动在不同的stage中传输, 如果该stage中的job不需要artifacts, 则可以禁用artifacts, 以加速构建速度

1
2
dependencies: []

注意

使用cache会出现一个问题, 就是缓存有可能使用上次执行该job时的缓存, 不能保证某些文件最新

相关文档

Introduction to job artifacts

需求

通过gitlab-ci实现文件的自动部署

实现过程

文档托管在gitlab上, 每次代码更新, 会自动出发gitlab-ci构建
在构建脚本中, 通过ssh 登录远程服务器执行git拉取文档的命令

过程

首先需要在服务器上生成ssh证书

注意该证书的用户必须与ssh远程登录的用户一样, 例如我们的用户名是root

将公钥添加到gitlab上, 以便于该用于可以拉取代码

CI/CD Piplines中设置 Secret Variables, 包括 DEPLOY_SERVERSSH_PRIVATE_KEY

其中 SSH_PRIVATE_KEY 的内容是服务器上的私钥, DEPLOY_SERVER 是服务器地址

编写 .gitlab-ci.yml 文件, 注入密钥, 通过ssh执行远程命令

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# 使用alpine镜像, 该镜像很少,只有几兆
image: alpine
stages:
- deploy
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
# 配置较低权限
- chmod 0600 deploy.key
# 注入密钥
- ssh-add deploy.key
- mkdir -p ~/.ssh
- '[[ -f /.dockerenv ]] && echo -e "Host *\n\tStrictHostKeyChecking no\n\n" > ~/.ssh/config'

release-doc:
stage: deploy
script:
# 连接远程服务器并执行拉取代码的命令
- ssh root@$DEPLOY_SERVER "cd /path/to/wiki && git pull origin master"
only:
- master
environment:
name: production
url: http://$DEPLOY_SERVER

相关文档

Using SSH keys

Gollum是一个开源的Wiki系统, 该系统基于Git, 支持 Markdown, RDoc 等多种排版格式.

下面是在搭建的过程中经常会遇到一些问题

UTF-8 问题

  • 安装 gollum-rugged_adapter
  • 通过参数 --adapter rugged 启动gollum
1
2
3
4
sudo apt-get install cmake
sudo gem install gollum-rugged_adapter
gollum --adapter rugged

如何设置只读

启动Gollum时, 设置 --no-edit 来禁止编辑

使用 Docker

使用Docker 安装gullum的内容见 gollum

参考文档

incompatible character encodings: UTF-8 and ASCII-8BIT

When use gollum , you may be got this error, below can helps you solves the problem.

  • install gollum-rugged_adapter
  • start gullom with --adapter rugged
1
2
3
4
sudo apt-get install cmake
sudo gem install gollum-rugged_adapter
gollum --adapter rugged

Here is a detailed demo for Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

FROM ruby
RUN apt-get -y update && apt-get -y install libicu-dev
RUN gem install gollum
RUN gem install github-markdown org-ruby
# RUN gem install --pre gollum-rugged_adapter
RUN apt-get -y install cmake
RUN gem install gollum-rugged_adapter
VOLUME /docs
WORKDIR /docs
CMD ["gollum", "--port", "80", "--adapter", "rugged"]
#CMD ["gollum", "--port", "80"]

EXPOSE 80

More Infomation

两个不同的Token

OAuth2 中主要有两个不同的Token, 其中的区别为是否与用户相关联, 即与用户相关的用户Token, 和与客户端相关的客户端Token,
可以通过用户Token, 查询到用户的相关信息, 客户端Token与用户无关, 一般只用于客户端认证

用户Token

获取用户Token一般有两个方式, 授权码模式和密码模式

授权码模式

授权码模式通过跳转到授权中心来获取token

  • 跳转到认证服务器
  • 认证服务器需要用户登录
  • 用户选择是否授权
  • 授权同意后, 自动跳转回原来的页面, 客户端拿到授权码
  • 客户端凭借授权码, 在服务器上通过接口向认证服务器申请令牌

密码模式

密码模式通过接口直接申请到Token

该接口需要几个参数, client_id, client_secret, grant_type, username, password

1
2
3
4
5
6
7
8
{
"client_id": "客户端ID",
"client_secret": "客户端密码",
"grant_type": "授权模式, 此外为 password",
"username": "用户名",
"password": "用户密码"
}

客户端Token

通过客户端ID和客户端密码来获取Token

该Token与客户端相关, 与用于无关, 只用于客户端认证, 避免了接口泄露和滥用

0%