From db06650645503670ce4315f624cd36e840460c58 Mon Sep 17 00:00:00 2001 From: Alomerry Wu Date: Sat, 17 Feb 2024 02:39:34 +0800 Subject: [PATCH] feat(blog): refine --- blog/pages/[...404].md | 2 +- blog/pages/pinned/todo/archive.md | 2 +- blog/pages/posts/2019/cn-beian-details.md | 1 - blog/pages/posts/2019/usefull-cpp-lib.md | 1 - blog/pages/posts/2020/clean-code.md | 1 - blog/pages/posts/2020/code-review.md | 1 - blog/pages/posts/2020/deploy-proxy.md | 1 - blog/pages/posts/2020/typecho-theme-plugs.md | 1 - blog/pages/posts/2021/deploy-NAT-traversal.md | 1 - blog/pages/posts/2021/postman.md | 1 - blog/pages/posts/2021/vscode-setting.md | 1 - blog/pages/posts/2022/[ci]-jenkins-zh.md | 926 ------------------ blog/pages/posts/2022/ci-gitlab.md | 15 - blog/pages/posts/2022/deploy-vscode-web.md | 1 - blog/pages/posts/2022/install-jenkins.md | 99 ++ blog/pages/posts/2022/jenkins-in-k8s.md | 277 ++++++ blog/pages/posts/2022/jenkins-pipeline.md | 613 ++++++++++++ blog/pages/posts/2022/tail-recursion.md | 1 - blog/pages/posts/2022/vps-and-home-lab.md | 1 - .../{[share]-apps-intro.md => apps-intro.md} | 1 - .../posts/2023/{[algorithm]-kmp.md => kmp.md} | 3 +- ...ond-hand-house.md => second-hand-house.md} | 4 +- ...ive]-tekton&argocd.md => tekton&argocd.md} | 1 - blog/pages/posts/2024/2023-summary.md | 131 +++ blog/pages/posts/2024/[share]-2023-summary.md | 9 - blog/pages/posts/2024/[share]-infancy-7k.md | 2 - .../{[share].encrpt.md => ansible-encrpt.md} | 3 +- blog/pages/posts/2024/infancy-7k.md | 10 + blog/src/alomerry/setting.ts | 42 +- blog/src/components/ListCategory.vue | 146 ++- blog/src/components/WrapperPost.vue | 11 +- blog/src/components/container/Book.vue | 1 - blog/src/styles/alomerry.css | 5 + blog/src/styles/container.css | 32 +- blog/src/styles/prose.css | 14 +- 35 files changed, 1304 insertions(+), 1057 deletions(-) delete mode 100644 blog/pages/posts/2022/[ci]-jenkins-zh.md delete mode 100644 blog/pages/posts/2022/ci-gitlab.md create mode 100644 blog/pages/posts/2022/install-jenkins.md create mode 100644 blog/pages/posts/2022/jenkins-in-k8s.md create mode 100644 blog/pages/posts/2022/jenkins-pipeline.md rename blog/pages/posts/2023/{[share]-apps-intro.md => apps-intro.md} (99%) rename blog/pages/posts/2023/{[algorithm]-kmp.md => kmp.md} (99%) rename blog/pages/posts/2023/{[share]-second-hand-house.md => second-hand-house.md} (80%) rename blog/pages/posts/2023/{[cloud-native]-tekton&argocd.md => tekton&argocd.md} (99%) create mode 100644 blog/pages/posts/2024/2023-summary.md delete mode 100644 blog/pages/posts/2024/[share]-2023-summary.md delete mode 100644 blog/pages/posts/2024/[share]-infancy-7k.md rename blog/pages/posts/2024/{[share].encrpt.md => ansible-encrpt.md} (93%) create mode 100644 blog/pages/posts/2024/infancy-7k.md diff --git a/blog/pages/[...404].md b/blog/pages/[...404].md index 5f13785a..0f74e8a0 100644 --- a/blog/pages/[...404].md +++ b/blog/pages/[...404].md @@ -1,3 +1,3 @@ # 404 -Nice to meet you tho! +Nice to meet you! diff --git a/blog/pages/pinned/todo/archive.md b/blog/pages/pinned/todo/archive.md index d00b8e54..cc726940 100644 --- a/blog/pages/pinned/todo/archive.md +++ b/blog/pages/pinned/todo/archive.md @@ -8,7 +8,7 @@ date: 2023-02-21 - https://github.com/tmijs/tmi.js/issues/151 - https://gist.github.com/AlcaDesign/742d8cb82e3e93ad4205 -毕业后很久没有回家好好过年,今年尤其的想家,年迈的父母,他们真的苍老了很多;工作多年来已经久不觉年味的我,突然在今天雨停之后的上海有了额外的一丝感知,往日拥堵的中环,已经清冷,夜晚同事们都在打车,只有迟迟不到、无处可循的滴滴司机 + - 犯得事 diff --git a/blog/pages/posts/2019/cn-beian-details.md b/blog/pages/posts/2019/cn-beian-details.md index 26dcbc20..f91fc2f4 100644 --- a/blog/pages/posts/2019/cn-beian-details.md +++ b/blog/pages/posts/2019/cn-beian-details.md @@ -1,7 +1,6 @@ --- date: 2019-07-18T16:00:00.000+00:00 title: 国内网站备案细节 -lang: zh type: posts duration: 1min desc: 记录 2019 年 alomerry.com 腾讯云备案流程和细节 diff --git a/blog/pages/posts/2019/usefull-cpp-lib.md b/blog/pages/posts/2019/usefull-cpp-lib.md index 5ccf87dc..98c06add 100644 --- a/blog/pages/posts/2019/usefull-cpp-lib.md +++ b/blog/pages/posts/2019/usefull-cpp-lib.md @@ -1,7 +1,6 @@ --- date: 2019-07-21T16:00:00.000+00:00 title: C++ 常用的函数 algorithm/stdlib/string/STL 库函数 -lang: zh type: posts+algorithm duration: 10min todoNext: diff --git a/blog/pages/posts/2020/clean-code.md b/blog/pages/posts/2020/clean-code.md index 6bbe2b49..ef3a5e7b 100644 --- a/blog/pages/posts/2020/clean-code.md +++ b/blog/pages/posts/2020/clean-code.md @@ -1,7 +1,6 @@ --- date: 2020-10-03T16:00:00.000+00:00 title: 代码整洁之道 / Go 箴言笔记 TODO -lang: zh type: posts duration: 25min desc: 《代码整洁之道》读书笔记和日常写代码、读代码对于代码风格的总结 diff --git a/blog/pages/posts/2020/code-review.md b/blog/pages/posts/2020/code-review.md index 46b7415c..a941dc2d 100644 --- a/blog/pages/posts/2020/code-review.md +++ b/blog/pages/posts/2020/code-review.md @@ -1,7 +1,6 @@ --- date: 2020-07-06T16:00:00.000+00:00 title: code review 注意事项 -lang: zh type: posts duration: 3min desc: 在公司中学习到一些规范和流程 diff --git a/blog/pages/posts/2020/deploy-proxy.md b/blog/pages/posts/2020/deploy-proxy.md index 4b9b6069..b48c10b0 100644 --- a/blog/pages/posts/2020/deploy-proxy.md +++ b/blog/pages/posts/2020/deploy-proxy.md @@ -1,7 +1,6 @@ --- date: 2020-02-14T16:00:00.000+00:00 title: 搭建科学上网教程 -lang: zh duration: 10min todoNext: - 探索透明代理 diff --git a/blog/pages/posts/2020/typecho-theme-plugs.md b/blog/pages/posts/2020/typecho-theme-plugs.md index 2fa3a44a..f7681b6d 100644 --- a/blog/pages/posts/2020/typecho-theme-plugs.md +++ b/blog/pages/posts/2020/typecho-theme-plugs.md @@ -1,7 +1,6 @@ --- date: 2020-01-17T16:00:00.000+00:00 title: Typecho 主题 handsome 美化插件 Skymo -lang: zh type: posts duration: 1min desc: 已停止更新 diff --git a/blog/pages/posts/2021/deploy-NAT-traversal.md b/blog/pages/posts/2021/deploy-NAT-traversal.md index 9be31685..363758d4 100644 --- a/blog/pages/posts/2021/deploy-NAT-traversal.md +++ b/blog/pages/posts/2021/deploy-NAT-traversal.md @@ -1,7 +1,6 @@ --- date: 2021-07-11T16:00:00.000+00:00 title: 搭建内网穿透教程 -lang: zh duration: 10min todoNext: - 具体代理应用配置 diff --git a/blog/pages/posts/2021/postman.md b/blog/pages/posts/2021/postman.md index e8128559..cc7b52e0 100644 --- a/blog/pages/posts/2021/postman.md +++ b/blog/pages/posts/2021/postman.md @@ -1,7 +1,6 @@ --- date: 2021-03-04T16:00:00.000+00:00 title: postman 使用技巧 -lang: zh type: posts duration: 3min todoNext: diff --git a/blog/pages/posts/2021/vscode-setting.md b/blog/pages/posts/2021/vscode-setting.md index 5b76080a..643a8935 100644 --- a/blog/pages/posts/2021/vscode-setting.md +++ b/blog/pages/posts/2021/vscode-setting.md @@ -1,7 +1,6 @@ --- date: 2021-09-28T16:00:00.000+00:00 title: vscode 常用设置与技巧 -lang: zh type: posts duration: 3min todoNext: diff --git a/blog/pages/posts/2022/[ci]-jenkins-zh.md b/blog/pages/posts/2022/[ci]-jenkins-zh.md deleted file mode 100644 index 3e1085fc..00000000 --- a/blog/pages/posts/2022/[ci]-jenkins-zh.md +++ /dev/null @@ -1,926 +0,0 @@ ---- -date: 2022-06-17T16:00:00.000+00:00 -title: 记录学习 Jenkins 过程的一些心得、笔记 -lang: zh -duration: 10min ---- - -[[toc]] - -## Jenkins 学习笔记 - -- https://dyrnq.com/jenkins/ - - -https://www.jenkinschina.com/tags/#Jenkins - -备份和迁移 https://www.jenkins.io/doc/book/system-administration/backing-up/ - -todo configuration-as-code https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos/credentials - -https://cloud.tencent.com/developer/article/1851872 - -## 升级 Jenkins - -- 兼容的情况下删除 docker image 重新 run -- 更新 docker 容器中的 jenkins war 包 - -## 制作 Jenkins 镜像 - -### 获取已安装插件 - -如果已经搭建了 Jenkins,可以在设置中的 script console 中执行以下代码,输出已安装插件和版本: - -```groovy -Jenkins.instance.pluginManager.plugins.each{ - plugin -> - println ("${plugin.getShortName()}:${plugin.getVersion()}") -} -``` - -### 构建 Jenkins 镜像 - -```dockerfile -FROM jenkins/jenkins:2.424 -USER root -COPY sources.list /etc/apt/sources.list -COPY --chown=jenkins:jenkins plugins.txt /usr/share/jenkins/ref/plugins.txt -USER jenkins -RUN jenkins-plugin-cli -f /usr/share/jenkins/ref/plugins.txt -RUN git config --global --add safe.directory "*" -``` - -基于 Jenkins 原版镜像并提前安装所需插件,执行 `docker build -t xxx .` 即可 - -## Jenkins 部署服务 - -- [](https://blog.csdn.net/qq_22648091/article/details/116424237) -- [](https://www.mafeifan.com/DevOps/Jenkins/Jenkins2-%E5%AD%A6%E4%B9%A0%E7%B3%BB%E5%88%9727----pipeline-%E4%B8%AD-Docker-%E6%93%8D%E4%BD%9C.html) - - - -- jenkins docker pipeline plugin -- stash/unstash -- Auto-commit Jenkins configuration changes with Git -- -- 使用 Jenkinsfile -- 忽略 Shell 步骤中的故障 - -## 安装和使用 - -::: tip - -最初:来自公司 TODO -起因:自动化构建博客并部署 TODO - -::: - -- 安装 https://www.jenkins.io/doc/book/installing/docker/ - - https://www.cnblogs.com/fuzongle/p/12834080.html - - https://www.jianshu.com/p/c570e0bb4926 -- 安装 docker 命令: - - 起初:blueoceam 包含 docker 命令 - - 尝试在 jenkins docker 容器内安装 docker: - - 在Docker Jenkins中安装Docker的行为被称为Docker in Docker(DinD)。虽然在某些情况下使用DinD是可行的,但通常不建议在Docker容器中安装Docker。以下是一些原因: 安全题:在容器内安装Docker将使你的容器获得与其宿主机相同的权限,这可能会将宿主机上的其他容器和应用程序置于风险之中。 性能问题:Docker容器已经具有优秀的隔离性和轻量级特性。DinD会导致额外的性能开销,包括额外的内存消耗和I/O操作。 维护问题:在容器中安装Docker也会导致维护问题,包括需要更新和维护两个Docker版本(容器内和宿主机上的)。 因此,更好法是使用Docker外部的Jenkins代理来与Docker守护进程进行通信,而不是在Docker容器中安装Docker。这种方式是推荐的,并且在生产环境中得到广泛应用。 - - https://devpress.csdn.net/cloudnative/63054028c67703293080f192.html - - https://www.zzxworld.com/posts/debian-bullseye-install-and-remove-docker-flow -- 挂载 docker -- 安装 docker 插件 -- 安装 ssh 插件 - -设置反代: - - https://cloud.tencent.com/developer/article/1953375 - -## Jenkins in k8s - -:::tip 2023.09.21 - -在很长一段时间我都是以 docker/docker-compose 的方式使用 Jenkins,在学习 k8s 的过程中我想到能否将 Jenkins 也以 pod 的形式部署呢?于是就开始了尝试,不得不说还是有些折腾,可能是我 k8s 还没入门、Jenkins 也只是会使用的原因吧 :joy: - -::: - -本文基于:`jenkins:2.424`、`k8s 1.28.1` - -### 架构 - -![架构](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/architecture.png) - -Jenkins Master 和 Jenkins Slave 以 Pod 形式运行在 Kubernetes 集群的 Node 上,Master是常驻服务,所有的配置数据都存储在一个 Volume 中,Slave 不是一直处于运行状态,它会按照需求动态的创建并自动删除。 - -当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master 上,当运行完 Job 后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态。 - -### 开始 - -Jenkins 相关资源 - -- Namespace -- ServiceAccount -- ClusterRoleBinding -- RersistentVolume -- PersistentVolumeClaim -- Service -- Deployment -- Ingress(可选) - -#### PV/PVC - -PV/PVC 对象用于持久化 Jenkins 相关的工作目录、插件等数据 - -```yml -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: jenkins-pvc - namespace: alomerry - labels: - service: jenkins -spec: - storageClassName: local-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Gi -``` - -```yml -apiVersion: v1 -kind: PersistentVolume -metadata: - name: jenkins-pv -spec: - capacity: - storage: 10Gi - accessModes: - - ReadWriteOnce - persistentVolumeReclaimPolicy: Retain - storageClassName: local-storage - local: - path: /root/apps/jenkins - nodeAffinity: - required: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/hostname - operator: In - values: - - master - - node - - node2 -``` - -#### Deployment - -Deployment 用于部署 Jenkins Pod,在 `securityContext` 配置 `runAsUser`、`runAsGroup` 和 `fsGroup` 为 `uid:1000` 以 `jenkins` 用户运行,并暴露端口 8080 和 50000 端口 - -```yml -apiVersion: apps/v1 -kind: Deployment -metadata: - name: jenkins-deployment - namespace: alomerry -spec: - replicas: 1 - selector: - matchLabels: - service: jenkins - template: - metadata: - labels: - service: jenkins - spec: - securityContext: - runAsUser: 1000 - runAsGroup: 1000 - fsGroup: 1000 - containers: - - name: jenkins - image: registry.cn-hangzhou.aliyuncs.com/alomerry/jenkins:v2.424 - imagePullPolicy: Always - env: - - name: TZ - value: Asia/Shanghai - ports: - - name: web - containerPort: 8080 - - name: wesocket - containerPort: 50000 - volumeMounts: - - name: jenkins-pv - mountPath: /var/jenkins_home - volumes: - - name: jenkins-pv - persistentVolumeClaim: - claimName: jenkins-pvc -``` - -#### Service/Ingress - -Service 用于将 Jenkins Pod 的端口以服务的形式统一暴露,如需要集群外访问,可以配置 Ingress 将 Service 暴露出去 - -#### ServiceAccount/ClusterRoleBinding - -如果需要在 Jenkins 访问和部署 Pod 需要配置对应的 ServiceAccount/ClusterRoleBinding 授权访问集群信息 - -### Case - -以下是我部署的 Jenkins 对应的资源文件: - -@[code](@_codes/vps-home/ansible/playbook/roles/jenkins/files/jenkins.yml) - -### 图示 - -执行 `kubectl apply -f jenkins.yml` 后即可进入初始化页面: - -![初始化](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/unlock-jenkins.png) - -执行 `kubectl logs -n ${namespace} ${pod name}` - -```shell -Running from: /usr/share/jenkins/jenkins.war -... -2023-09-24 15:17:21.975+0000 [id=36] INFO jenkins.install.SetupWizard#init: - -************************************************************* -************************************************************* -************************************************************* - -Jenkins initial setup is required. An admin user has been created and a password generated. -Please use the following password to proceed to installation: - -${init code} - -This may also be found at: /var/jenkins_home/secrets/initialAdminPassword - -************************************************************* -************************************************************* -************************************************************* - -... -2023-09-24 15:20:58.800+0000 [id=23] INFO hudson.lifecycle.Lifecycle#onReady: Jenkins is fully up and running -``` - -设置好用户名密码后就进入 Jenkins 主页面了 - -![jenkins-home](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-home.png) - -将内置的节点执行数量设置成 0,设置除非指定节点名时才能使用以保证主节点的稳定 - -![prevent build-in node](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/prevent-use-build-in-node.png) - -配置 k8s 环境 - -- k8s 地址:IP + 端口号或者是集群内地址 `https://kubernetes.default.svc` -- 将 kubeconfg 配置成 secret file 后设置到凭据中 - -获取 server certificate key - -```shell -cat /etc/kubernetes/admin.conf | grep certificate-authority-data -echo ${certificate-authority-data} | base64 -d -``` - -jenkins 地址可以写 jenkins-service 的地址 - -![k8s config](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-k8s-config-1.png) - -![k8s config](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-k8s-config-2.png) - -![build pipeline](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-build-by-k8s-pod.png) - -![jenkins build pod](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-build-pod.png) - -### 优势 - -相对于部署在虚拟机环境下的 Jenkins 一主多从架构,将 Jenkins 部署到 k8s 会带来以下好处: - -- 服务高可用: 当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master 容器,并且将 Volume 分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用。 -- 动态伸缩: 合理使用资源,每次运行 Job 时,会自动创建一个 Jenkins Slave,Job 完成后,Slave 自动注销并删除容器,资源自动释放,而且 Kubernetes 会根据每个资源的使用情况,动态分配 Slave 到空闲的节点上创建,降低出现因某节点资源利用率高,还排队等待在该节点的情况。 -- 扩展性:当 Kubernetes 集群的资源严重不足而导致 Job 排队等待时,可以很容易的添加一个 Kubernetes Node 到集群中,从而实现扩展。 - -### DinD - -自 1.24 版本之后,Kubernetes 社区将正式放弃对 docker CRI 的支持,所以 k8s 集群中的 Jenkins Pod 已经不包含 docker 了,无法直接在 Pod 中使用 docker 相关的指令了。 - -我使用了 DinD 的方式来解决,DinD 即 Docker in Docker,看以下资源文件: - -```yml -apiVersion: v1 -kind: Pod -spec: - containers: - - name: dind - image: docker:24.0.6-dind - tty: true - securityContext: - privileged: true - command: - - dockerd - - --host=tcp://0.0.0.0:8000 - - --host=unix:///var/run/docker.sock - - --tls=false - volumeMounts: - - mountPath: /var/run - name: docker-sock - readinessProbe: - exec: - command: ["docker", "info"] - initialDelaySeconds: 10 - failureThreshold: 6 - - name: docker-builder - image: docker:24.0.6 - tty: true - securityContext: - privileged: true - volumeMounts: - - mountPath: /var/run - name: docker-sock - volumes: - - name: docker-sock - emptyDir: {} -``` - -通过 `docker:24.0.6-dind` 启动 dockerd 服务并挂在到 `docker:24.0.6` 容器中,实现 `docker-builder` container 可以执行 docker 命令 - -### Reference - -- [kubernetes 安装 jenkins 及配置 pipeline](https://www.orchome.com/16641) -- [基于 Kubernetes 的 Jenkins 服务也去 Docker](https://www.chenshaowen.com/blog/using-podman-to-build-images-under-kubernetes-and-jenkins.html) -- [流水线中使用 docker in pod 方式构建容器镜像](https://blog.k8s.li/docker-in-pod.html) -- [在 containerd 集群中使用 docker 做镜像构建服务](https://imroc.cc/k8s/best-practice/containerd-dind/) -- [基于 Kubernetes 实现高可用的动态 Jenkins Slave](https://testerhome.com/articles/31012) -- [Kubernetes plugin for Jenkins](https://plugins.jenkins.io/kubernetes/#plugin-content-kubernetes-plugin-for-jenkins) -- [Kubernetes plugin samples](https://github.com/jenkinsci/kubernetes-plugin/blob/master/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/samples/declarative.groovy) - -## Jenkins Pipeline - -::: tip 为什么使用 pipeline? - -freestyle 主要使用配置的方式来描述一个 job,刚上手的时候我也是使用这种方式来构建项目、发布。后续熟悉了之后一些其它的构建我使用了 pipeline 之后体会到了完全不同的顺畅。简单来说虽然 freestyle 的学习成本低,但是无法将配置代码化,在各种插件杂糅在 job 后,迁移和版本控制时会增加心智负担,配置的流程很长,各项间隔很远,而不像 pipeline 的形式,皆在一个 groovy 脚本中,上下文前后前后关联都可以一览无余。 - -其次是 pipeline 中可以定义多个 stage,来获得一些 freestyle 无法实现的行为,例如并行、人工批准、复用等,最终组合成 pipeline 集。综合上面的部分心得,最后我完全废弃 freestyle,仅使用 pipeline 来构建和发布项目。[^why-pipeline] - -::: - -- https://plugins.jenkins.io/kubernetes/ -- https://github.com/jenkinsci/kubernetes-plugin/blob/master/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/declarative.groovy -- https://github.com/jenkinsci/kubernetes-plugin/blob/master/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/jenkinsSecretHidden.groovy -- https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline -- https://plugins.jenkins.io/kubernetes/ - -优化 - -- http://www.devopser.org/articles/2020/09/11/1599814292016.html - -其他 - -- https://stackoverflow.com/questions/36194316/how-to-get-the-build-user-in-jenkins-when-job-triggered-by-timer - -build info - -- https://testerhome.com/topics/13511 -- https://cloud.tencent.com/developer/article/2202789 -- https://blog.csdn.net/weixin_39918388/article/details/112462275 - -k8s - -- https://www.chenshaowen.com/blog/creating-jenkins-slave-dynamically-on-kubernetes.html -- https://www.cnblogs.com/cyleon/p/14894586.html - -### 概念[^pipeline-conception] - -先来看一个官方的声明式的 pipeline Jenkinsfile[^jenkinsfile]: - -```groovy -pipeline { [1] - agent any [2] - stages { - stage('Build') { [3] - steps { [4] - sh 'make' - } - } - stage('Test'){ - steps { - sh 'make check' - junit 'reports/**/*.xml' - } - } - stage('Deploy') { - steps { - sh 'make publish' - } - } - } -} -``` - -- `[1]` pipeline 定义了包含执行整个流水线的所有内容和指令的块。 -- `[2]` agent 指示 Jenkins 为整个流水线分配一个执行器(在节点上)和工作区。 -- `[3]` stage 可以理解为 pipeline 流程中的一个阶段,一个或多个阶段实现了整个 pipeline 的功能。例如上例中,pipeline 由构建(Build)、测试(Test)和发布(Deploy)构成。 -- `[4]` steps 中可以描述每个 stage 需要运行的功能。例如上例中的测试(Test)stage 中,需要执行 shell 命令 `make check`,然后使用 junit 上报测试。 - -简单了解 pipeline 是大概是什么之后就可以学习 pipeline 的语法。 - -### 语法[^pipeline-syntax] - -#### agent - -::: info agent - -`agent` 部分指定了整个流水线或特定的部分,将会在 Jenkins 环境中执行的位置,这取决于 `agent` 区域的位置。该部分必须在 `pipeline` 块的顶层被定义,但是 stage 级别的使用是可选的。 - -::: - -参数: - -- **any** 在任何可用的代理上执行流水线或阶段 -- **none** 当在 pipeline 块的顶部没有全局代理,该参数将会被分配到整个流水线的运行中并且每个 stage 部分都需要包含他自己的 agent 部分 -- **label** 在提供了标签的 Jenkins 环境中可用的代理上执行流水线或阶段 -- **node** `agent { node { label 'labelName' } }` 和 `agent { label 'labelName' }` 一样,但是 node 允许额外的选项 (比如 `customWorkspace` ) -- **docker** 使用给定的容器执行流水线或阶段。该容器将在预置的 node 上,或在匹配可选定义的 `label` 参数上,动态的供应来接受基于 Docker 的流水线。`docker` 也可以选择的接受 `args` 参数,该参数可能包含直接传递到 `docker run` 调用的参数,以及 `alwaysPull` 选项,该选项强制 `docker pull`,即使镜像名称已经存在。比如:`agent { docker 'maven:3-alpine' }` 或 - - ```groovy - agent { - docker { - image 'maven:3-alpine' - label 'my-defined-label' - args '-v /tmp:/tmp' - } - } - ``` - -- **dockerfile** 执行流水线或阶段,使用从源代码库包含的 `Dockerfile` 构建的容器。为了使用该选项,`Jenkinsfile` 必须从多个分支流水线中加载,或者加载“Pipeline from SCM.”通常,这是源代码仓库的根目录下的 `Dockerfile : agent { dockerfile true }`。 如果在另一个目录下构建 `Dockerfile`,使用 dir 选项:`agent { dockerfile {dir 'someSubDir' } }`。如果 `Dockerfile` 有另一个名称,你可以使用 `filename` 选项指定该文件名。你可以传递额外的参数到 `docker build ...` 使用 `additionalBuildArgs` 选项提交,比如 `agent { dockerfile {additionalBuildArgs '--build-arg foo=bar' } }`。例如,一个带有 `build/Dockerfile.build` 的仓库,期望一个构建参数 `version`: - - ```groovy - agent { - // Equivalent to "docker build -f Dockerfile.build --build-arg version=1.0.2 ./build/ - dockerfile { - filename 'Dockerfile.build' - dir 'build' - label 'my-defined-label' - additionalBuildArgs '--build-arg version=1.0.2' - } - } - ``` - -#### post - ->`post` 部分定义一个或多个 steps,这些阶段根据流水线或阶段的完成情况而 运行(取决于流水线中 `post` 部分的位置)。`post` 支持以下 post-condition 块中的其中之一:`always`、`changed`、`failure`、`success`、`unstable` 和 `aborted`。这些条件块允许在 `post` 部分的步骤的执行取决于流水线或阶段的完成状态。 - -Conditions - -- `always` 无论流水线或阶段的完成状态如何,都允许在 `post` 部分运行该步骤。 -- `changed` 只有当前流水线或阶段的完成状态与它之前的运行不同时,才允许在 `post` 部分运行该步骤。 -- `failure` 只有当前流水线或阶段的完成状态为“failure”,才允许在 `post` 部分运行该步骤。 -- `success` 只有当前流水线或阶段的完成状态为“success”,才允许在 `post` 部分运行该步骤。 -- `unstable` 只有当前流水线或阶段的完成状态为“unstable”,才允许在 `post` 部分运行该步骤,通常由于测试失败,代码违规等造成。 -- `aborted` 只有当前流水线或阶段的完成状态为“aborted”,才允许在 `post` 部分运行该步骤,通常由于流水线被手动的 aborted。 - -Case [处理故障](https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile/#handling-failure): - -测试失败后发送邮件 - -```groovy -pipeline { - agent any - stages { - stage('Test') { - steps { - sh 'make check' - } - } - } - post { - always { - junit '**/target/*.xml' - } - failure { - mail to: team@example.com, subject: 'The Pipeline failed :(' - } - } -} -``` - -#### stages - ->包含一系列一个或多个 stage 指令,`stages` 部分是流水线描述的大部分“work”的位置。建议 `stages` 至少包含一个 `stage` 指令用于连续交付过程的每个离散部分,比如构建、测试和部署。 - -#### environment - ->指令制定一个 键-值对序列,该序列将被定义为所有步骤的环境变量,或者是特定于阶段的步骤,这取决于 `environment` 指令在流水线内的位置。 -> ->该指令支持一个特殊的助手方法 `credentials()`,该方法可用于在Jenkins环境中通过标识符访问预定义的凭证。对于类型为“Secret Text”的凭证,`credentials()` 将确保指定的环境变量包含秘密文本内容。对于类型为“SStandard username and password”的凭证,指定的环境变量指定为 `username:password`,并且两个额外的环境变量将被自动定义 :分别为 `MYVARNAME_USR` 和 `MYVARNAME_PSW`。 - -- 顶层流水线块中使用的 `environment` 指令将适用于流水线中的所有步骤。 -- 在一个 `stage` 中定义的 `environment` 指令只会将给定的环境变量应用于 stage 中的步骤。 -- `environment` 块有一个 助手方法 `credentials()` 定义,该方法可以在 Jenkins 环境中用于通过标识符访问预定义的凭证。 - -:::tip - -[Jenkins 环境变量](https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile/#使用环境变量) - -::: - -#### [处理凭据](https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile/#处理凭据) - -- Secret 文本 -- 带密码的用户名 -- Secret 文件 -- 其他凭据类型(SSH 私钥、PKCS、Docker 主机证书) - -#### options - ->`options` 指令允许从流水线内部配置特定于流水线的选项。流水线提供了许多这样的选项,比如 `buildDiscarder`,但也可以由插件提供,比如 `timestamps`。 - -可用选项: - -- **buildDiscarder** 为最近的流水线运行的特定数量保存组件和控制台输出。例如:`options { buildDiscarder(logRotator(numToKeepStr: '1')) }` -- **disableConcurrentBuilds** 不允许同时执行流水线。 可被用来防止同时访问共享资源等。 例如:`options { disableConcurrentBuilds() }` -- **overrideIndexTriggers** 允许覆盖分支索引触发器的默认处理。如果分支索引触发器在多分支或组织标签中禁用, `options { overrideIndexTriggers(true) }` 将只允许它们用于促工作。否则 `options { overrideIndexTriggers(false) }` 只会禁用改作业的分支索引触发器。 -- **skipDefaultCheckout** 在`agent` 指令中,跳过从源代码控制中检出代码的默认情况。例如:`options { skipDefaultCheckout() }` -- **skipStagesAfterUnstable** 一旦构建状态变得UNSTABLE,跳过该阶段。例如:`options { skipStagesAfterUnstable() }` -- **checkoutToSubdirectory** 在工作空间的子目录中自动地执行源代码控制检出。例如:`options { checkoutToSubdirectory('foo') }` -- **timeout** 设置流水线运行的超时时间, 在此之后,Jenkins将中止流水线。例如:`options { timeout(time: 1, unit: 'HOURS') }` -- **retry** 在失败时, 重新尝试整个流水线的指定次数。 例如:`options { retry(3) }` -- **timestamps** 预谋所有由流水线生成的控制台输出,与该流水线发出的时间一致。例如:`options { timestamps() }` - -#### trigger - ->`triggers` 指令定义了流水线被重新触发的自动化方法。对于集成了源(比如 GitHub 或 BitBucket)的流水线, 可能不需要 `triggers`,因为基于 web 的集成很肯能已经存在。当前可用的触发器是 `cron`,`pollSCM` 和 `upstream`。 - -- **cron** 接收 cron 样式的字符串来定义要重新触发流水线的常规间隔,比如:`triggers { cron('H */4 * * 1-5') }` -- **pollSCM** 接收 cron 样式的字符串来定义一个固定的间隔,在这个间隔中,Jenkins 会检查新的源代码更新。如果存在更改,流水线就会被重新触发。例如:`triggers { pollSCM('H */4 * * 1-5') }` -- **upstream** 接受逗号分隔的工作字符串和阈值。当字符串中的任何作业以最小阈值结束时,流水线被重新触发。例如:`triggers { upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS) }` - -#### when - ->`when` 指令允许流水线根据给定的条件决定是否应该执行阶段。`when` 指令必须包含至少一个条件。如果 `when` 指令包含多个条件,所有的子条件必须返回 True,阶段才能执行。这与子条件在 `allOf` 条件下嵌套的情况相同。 -使用诸如 `not`、`allOf` 或 `` 的嵌套条件可以构建更复杂的条件结构 can be built 嵌套条件可以嵌套到任意深度。 - -内置条件: - -- **branch** 当正在构建的分支与模式给定的分支匹配时,执行这个阶段,例如:`when { branch 'master' }`。注意,这只适用于多分支流水线。 -- **environment** 当指定的环境变量是给定的值时,执行这个步骤,例如:`when { environment name: 'DEPLOY_TO', value: 'production' }` -- **expression** 当指定的 Groovy 表达式评估为 true 时,执行这个阶段,例如:`when { expression { return params.DEBUG_BUILD } }` -- **not** 当嵌套条件是错误时,执行这个阶段,必须包含一个条件,例如:`when { not { branch 'master' } }` -- **allOf** 当所有的嵌套条件都正确时,执行这个阶段,必须包含至少一个条件,例如:`when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }` -- **anyOf** 当至少有一个嵌套条件为真时,执行这个阶段,必须包含至少一个条件,例如:`when { anyOf { branch 'master'; branch 'staging' } }` - -[Case:在进入 stage 的 agent 前评估 when](https://www.jenkins.io/zh/doc/book/pipeline/syntax/#在进入-stage-的-agent-前评估-when) - -#### concurrent - ->声明式流水线的阶段可以在他们内部声明多隔嵌套阶段,它们将并行执行。注意,一个阶段必须只有一个 `steps` 或 `parallel` 的阶段。嵌套阶段本身不能包含进一步的 `parallel` 阶段,但是其他的阶段的行为与任何其他 `stage` 相同。任何包含 `parallel` 的阶段不能包含 `agent` 或 `tools` 阶段,因为他们没有相关 `steps`。 -> ->另外,通过添加 `failFast true` 到包含 `parallel` 的 `stage` 中,当其中一个进程失败时,你可以强制所有的 `parallel` 阶段都被终止。 - -Case: - -```groovy -pipeline { - agent any - stages { - stage('Non-Parallel Stage') { - steps { - echo 'This stage will be executed first.' - } - } - stage('Parallel Stage') { - when { - branch 'master' - } - failFast true - parallel { - stage('Branch A') { - agent { - label "for-branch-a" - } - steps { - echo "On Branch A" - } - } - stage('Branch B') { - agent { - label "for-branch-b" - } - steps { - echo "On Branch B" - } - } - } - } - } -} -``` - -### 案例 - -#### blog/algorithm - -以前刷过一段时间的 PAT,有一些经典题目记录了下来,后续也会抽空刷 LeetCode,所以使用 jekyll 搭建了一个 IOI 题解的 blog,需要一些环境,这个 case 主要记录将 github 中的代码 build 并发布到服务器。 - -由于 jekyll 需要一些环境,所以我就做了一个用于 build site 的 docker image(很简陋,后面会优化一下): - -pipeline - -::: code-tabs - -@tab blog - -@[code](@_codes/blog/Jenkinsfile) - -::: - -#### 部署 bot-huan - -:::details bot-huan pipeline - -```groovy -pipeline { - // 设置全局环境变量 - environment { - url = 'https://gitlab.com/Alomerry/bot-huan.git' - KAIHEILA_BOT_TOKEN = credentials('kaiheila-bot-token') - KAIHEILA_BOT_VERIFY_TOKEN = credentials('kaiheila-bot-verify-token') - KAIHEILA_BOT_ENCRYPT_KEY = credentials('kaiheila-bot-encrypt-key') - } - triggers { - GenericTrigger( - genericVariables: [ - [ - key: 'name', - value: '$.repository.name', - expressionType: 'JSONPath', - regularFilter: '', - defaultValue: '' - ] - ], - printContributedVariables: false, - printPostContent: false, - tokenCredentialId: 'jenkins-webhook-token', - regexpFilterText: '$name', - regexpFilterExpression: '^(B|b)ot-huan$', - causeString: ' Triggered on $ref' , - ) - } - agent any - stages { - stage('update build image') { - steps { - sh 'docker pull registry.cn-hangzhou.aliyuncs.com/alomerry/base-golang:1.18' - sh 'docker pull registry.cn-hangzhou.aliyuncs.com/alomerry/bot-huan' - } - } - stage('pull code and build') { - agent { - docker { - image 'registry.cn-hangzhou.aliyuncs.com/alomerry/base-golang:1.18' - } - } - steps { - retry(3) { - // 拉取代码 - git(url: env.url, branch: 'master') - } - // 构建 - dir("backend") { - sh "go build -mod=vendor -o main" - stash(name: "bin", includes: "main") - } - } - } - stage('run bin') { - steps { - dir("/var/jenkins_home/build") { - unstash("bin") - } - sh''' - docker rm $(docker ps -aq --filter name=bot-huan) -f || true - docker run -d --name bot-huan -p 4376:4376 -v /home/alomerry-home/apps/jenkins/build:/build -e $KAIHEILA_BOT_TOKEN_USR=$KAIHEILA_BOT_TOKEN_PSW -e $KAIHEILA_BOT_VERIFY_TOKEN_USR=$KAIHEILA_BOT_VERIFY_TOKEN_PSW -e $KAIHEILA_BOT_ENCRYPT_KEY_USR=$KAIHEILA_BOT_ENCRYPT_KEY_PSW registry.cn-hangzhou.aliyuncs.com/alomerry/bot-huan - ''' - // TODO 验证是否正常启动 否则报错 - } - } - } - post { - always { - deleteDir() - } - failure { - mail to: 'alomerry.wu@gmail.com', - subject: "Failed Pipeline: ${currentBuild.fullDisplayName}", - body: "Something is wrong with ${env.url}" - } - } -} - -``` - -::: - - - -### jenkins function - -```groovy -pipeline { - agent any - stages { - stage('Test') { - steps { - whateverFunction() - } - } - } -} - -def whateverFunction() { - sh 'ls /' -} -``` - -return value - -```groovy -def output // set as global variable -pipeline{ -... - -stage('Sum') -{ - steps - { - script - { - output = sum() - echo "The sum is ${output}" - } - } -} -... -``` - -### Reference - -[^why-pipeline]: [Why Pipeline](https://www.jenkins.io/zh/doc/book/pipeline/#why) -[^pipeline-conception]: [Pipeline Conception](https://www.jenkins.io/zh/doc/book/pipeline/#流水线概念) -[^pipeline-syntax]: [Pipeline Syntax](https://www.jenkins.io/zh/doc/book/pipeline/syntax/) -[^jenkinsfile]: [jenkinsfile](https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile/#创建-jenkinsfile) - -## Jenkins 插件 - -https://plugins.jenkins.io/build-user-vars-plugin/ - -- https://plugins.jenkins.io/build-user-vars-plugin/ - -### SSH - -- [ssh pipeline](https://plugins.jenkins.io/ssh-steps) - -### Docker - -- [docker](https://plugins.jenkins.io/docker-plugin/) -- [docker pipeline](https://plugins.jenkins.io/docker-workflow) - -### Generic Webhook Trigger[^generic-webhook-trigger] - -https://github.com/jenkinsci/generic-webhook-trigger-plugin/blob/master/src/test/resources/org/jenkinsci/plugins/gwt/bdd/ - -Case 配合 pipeline 中的 trigger 可以实现仓库有推送后即触发构建 - -```groovy -GenericTrigger( - genericVariables: [ - [ - key: 'name', - value: '$.repository.name', - expressionType: 'JSONPath', - regularFilter: '', - defaultValue: '' - ] - ], - printContributedVariables: false, - printPostContent: false, - tokenCredentialId: 'jenkins-webhook-token', - regexpFilterText: '$name', - regexpFilterExpression: '^(B|b)ot-huan$', - causeString: ' Triggered on $ref' , -) -``` - -genericVariables 中配置一些从 request.body 中获取的变量,上例中读取的是 request.body 中的 repository.name 的值赋到变量 name 中,并使用正则判断是否满足要求 - -配置后可以使用 gitlab Test push 查看 jenkins 返回值 - -```json -{ - "jobs": { - "bot-huan": { - "regexpFilterExpression": "bot-huan", - "triggered": true, - "resolvedVariables": { - "name": "bot-huan" - }, - "regexpFilterText": "bot-huan", - "id": 390, - "url": "queue/item/390/" - }, - "blog": { - "regexpFilterExpression": "^(B|b)log$", - "triggered": false, - "resolvedVariables": { - "name": "bot-huan" - }, - "regexpFilterText": "bot-huan", - "id": 0, - "url": "" - }, - "algorithm": { - "regexpFilterExpression": "^(A|a)lgorithm$", - "triggered": false, - "resolvedVariables": { - "name": "bot-huan" - }, - "regexpFilterText": "bot-huan", - "id": 0, - "url": "" - } - }, - "message": "Triggered jobs." -} -``` - -jenkins 通过流水线中配置的正则来匹配触发哪条流水线,可以查看 jenkins 给 gitlab 的返回值看出触发了 bot-huan 的构建 - -### SSH Pipeline Step[^ssh-pipeline-step] - -- sshCommand -- sshGet -- sshPut -- sshRemove -- sshScript - -Case 将构建好的静态文件发布到服务器: - -```groovy -def remote = [:] -remote.name = 'root' -remote.logLevel = 'FINEST' -remote.host = '[your host]' -remote.allowAnyHosts = true -withCredentials([usernamePassword(credentialsId: 'tencent-ubuntu-root', passwordVariable: 'password', usernameVariable: 'username')]) { - remote.user = "${username}" - remote.password = "${password}" -} -sshCommand remote: remote, command: '''#!/bin/bash - cd /www/wwwroot/[your website]/ - shopt -s extglob - rm -rf !(.htaccess|.user.ini|.well-known|favicon.ico|algorithm.tar.gz) - ''' -sshPut remote: remote, from: '/var/jenkins_home/workspace/algorithm/docs/_site/algorithm.tar.gz', into: '/www/wwwroot/[your website]/' -sshCommand remote: remote, command: "cd /www/wwwroot/[your website] && tar -xf algorithm.tar.gz" -sshRemove remote: remote, path: '/www/wwwroot/[your website]/algorithm.tar.gz' -``` - -配合 pipeline 中的 environment,配置好 remote 后,先删除非必要文件,将静态文件压缩包推送到服务器指定位置,解压后删除即可。 - -### Reference - -[^ssh-pipeline-step]: [SSH Pipeline Step](https://github.com/jenkinsci/ssh-steps-plugin#configuration) -[^generic-webhook-trigger]: [Generic Webhook Trigger](https://plugins.jenkins.io/generic-webhook-trigger) - -## Jenkinsfile - -### 通过文件变动来触发其他 job - -```groovy -stage('check and trigger resume') { - steps { - script { - def resumeChanged = 'git --no-pager diff --name-only HEAD^ HEAD | grep -q "src/about/resume/"' - if (resumeChanged != "") { - build job: 'resume', wait: true - } - } - } -} -``` - -`git diff -name-only HEAD^ HEAD` 可以输出 HEAD 与 HEAD 前一次的变动文件,通过管道和 grep 来筛选是否包含 `src/about/resume` 路径下的改动。`--no-pager` 可以直接输出结果,避免以交互式的形式展示 - - -- 美团案例 https://tech.meituan.com/2018/08/02/erp-cd-jenkins-pipeline.html -- jenkins git diff https://sinkcup.github.io/jenkins-git-diffs - - \ No newline at end of file diff --git a/blog/pages/posts/2022/ci-gitlab.md b/blog/pages/posts/2022/ci-gitlab.md deleted file mode 100644 index 510cda44..00000000 --- a/blog/pages/posts/2022/ci-gitlab.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -date: 2022-06-17T16:00:00.000+00:00 -title: gitlab -lang: zh -duration: 10min -type: todoNext ---- - -- https://docs.gitlab.cn/jh/ci/variables/index.html#%E4%BD%BF%E7%94%A8-bashsh-%E5%92%8C%E7%B1%BB%E4%BC%BC%E7%9A%84%E5%8F%98%E9%87%8F -- https://docs.gitlab.cn/jh/ci/pipeline_editor/index.html -- https://docs.gitlab.cn/jh/ci/caching/ -- https://www.php.cn/faq/505025.html -- https://forum.gitlab.com/t/ci-cd-pipeline-get-list-of-changed-files/26847/25 -- https://forum.gitlab.com/t/ci-cd-pipeline-get-list-of-changed-files/26847/11 -- https://meigit.readthedocs.io/en/latest/gitlab_ci_.gitlab-ci.yml_detail.html diff --git a/blog/pages/posts/2022/deploy-vscode-web.md b/blog/pages/posts/2022/deploy-vscode-web.md index 53c7a037..093f2d6b 100644 --- a/blog/pages/posts/2022/deploy-vscode-web.md +++ b/blog/pages/posts/2022/deploy-vscode-web.md @@ -1,7 +1,6 @@ --- date: 2022-08-23T16:00:00.000+00:00 title: 搭建 vscode web -lang: zh duration: 10min --- diff --git a/blog/pages/posts/2022/install-jenkins.md b/blog/pages/posts/2022/install-jenkins.md new file mode 100644 index 00000000..1d266119 --- /dev/null +++ b/blog/pages/posts/2022/install-jenkins.md @@ -0,0 +1,99 @@ +--- +date: 2022-06-17T16:00:00.000+00:00 +title: Jenkins 安装手册 +desc: 记录学习 Jenkins 过程的一些心得、笔记 +duration: 10min +--- + +[[toc]] + +## 制作 Jenkins 镜像 + +### 获取已安装插件 + +如果已经搭建了 Jenkins,可以在设置中的 script console 中执行以下代码,输出已安装插件和版本: + +```groovy +Jenkins.instance.pluginManager.plugins.each { + plugin -> + println("${plugin.getShortName()}:${plugin.getVersion()}") +} +``` + +### 构建 Jenkins 镜像 + +```dockerfile +FROM jenkins/jenkins:2.424 +USER root +COPY sources.list /etc/apt/sources.list +COPY --chown=jenkins:jenkins plugins.txt /usr/share/jenkins/ref/plugins.txt +USER jenkins +RUN jenkins-plugin-cli -f /usr/share/jenkins/ref/plugins.txt +RUN git config --global --add safe.directory "*" +``` + +基于 Jenkins 原版镜像并提前安装所需插件,执行 `docker build -t xxx .` 即可 + +## 安装和使用 + +- 安装 https://www.jenkins.io/doc/book/installing/docker/ + - https://www.cnblogs.com/fuzongle/p/12834080.html + - https://www.jianshu.com/p/c570e0bb4926 +- 安装 docker 命令: + - 起初:blueoceam 包含 docker 命令 + - 尝试在 jenkins docker 容器内安装 docker: + - 在Docker Jenkins中安装Docker的行为被称为Docker in + Docker(DinD)。虽然在某些情况下使用DinD是可行的,但通常不建议在Docker容器中安装Docker。以下是一些原因: + 安全题:在容器内安装Docker将使你的容器获得与其宿主机相同的权限,这可能会将宿主机上的其他容器和应用程序置于风险之中。 + 性能问题:Docker容器已经具有优秀的隔离性和轻量级特性。DinD会导致额外的性能开销,包括额外的内存消耗和I/O操作。 + 维护问题:在容器中安装Docker也会导致维护问题,包括需要更新和维护两个Docker版本(容器内和宿主机上的)。 + 因此,更好法是使用Docker外部的Jenkins代理来与Docker守护进程进行通信,而不是在Docker容器中安装Docker。这种方式是推荐的,并且在生产环境中得到广泛应用。 + - https://devpress.csdn.net/cloudnative/63054028c67703293080f192.html + - https://www.zzxworld.com/posts/debian-bullseye-install-and-remove-docker-flow +- 挂载 docker +- 安装 docker 插件 +- 安装 ssh 插件 + +设置反代: + +- https://cloud.tencent.com/developer/article/1953375 + +## 升级 Jenkins + +- 兼容的情况下删除 docker image 重新 run +- 更新 docker 容器中的 jenkins war 包 + +## Jenkins 部署服务 + +- [](https://blog.csdn.net/qq_22648091/article/details/116424237) +- [](https://www.mafeifan.com/DevOps/Jenkins/Jenkins2-%E5%AD%A6%E4%B9%A0%E7%B3%BB%E5%88%9727----pipeline-%E4%B8%AD-Docker-%E6%93%8D%E4%BD%9C.html) + + + +- jenkins docker pipeline plugin +- stash/unstash +- Auto-commit Jenkins configuration changes with + Git +- +- 使用 Jenkinsfile +- 忽略 Shell 步骤中的故障 + +## More + +- [美团案例](https://tech.meituan.com/2018/08/02/erp-cd-jenkins-pipeline.html) +- [Jenkins 总结](https://dyrnq.com/jenkins/) +- [Groovy Hook Scripts](https://www.jenkins.io/doc/book/managing/groovy-hook-scripts/) +- [ ] 构建 docker 镜像并推送到仓库 +- [ ] 集成 k8s +- [ ] Jenkins 升级 https://mirrors.jenkins.io/war +- [ ] Others +- [ ] https://www.mafeifan.com/DevOps/Jenkins/Jenkins2-%E5%AD%A6%E4%B9%A0%E7%B3%BB%E5%88%9727----pipeline-%E4%B8%AD-Docker-%E6%93%8D%E4%BD%9C.html +- [ ] https://docs.cloudbees.com/docs/admin-resources/latest/plugins/docker-workflow +- [ ] https://www.jenkins.io/zh/doc/book/pipeline/docker/ --> + https://www.jenkinschina.com/tags/#Jenkins + +备份和迁移 https://www.jenkins.io/doc/book/system-administration/backing-up/ + +todo configuration-as-code https://github.com/jenkinsci/configuration-as-code-plugin/tree/master/demos/credentials + +https://cloud.tencent.com/developer/article/1851872 diff --git a/blog/pages/posts/2022/jenkins-in-k8s.md b/blog/pages/posts/2022/jenkins-in-k8s.md new file mode 100644 index 00000000..d9cfedc9 --- /dev/null +++ b/blog/pages/posts/2022/jenkins-in-k8s.md @@ -0,0 +1,277 @@ +--- +date: 2022-06-17T16:00:00.000+00:00 +title: Jenkins in k8s +desc: 记录学习 Jenkins 过程的一些心得、笔记 +duration: 10min +--- + +[[toc]] + +## Jenkins in k8s + +:::tip 2023.09.21 + +在很长一段时间我都是以 docker/docker-compose 的方式使用 Jenkins,在学习 k8s 的过程中我想到能否将 Jenkins 也以 pod +的形式部署呢?于是就开始了尝试,不得不说还是有些折腾,可能是我 k8s 还没入门、Jenkins 也只是会使用的原因吧 :joy: + +::: + +本文基于:`jenkins:2.424`、`k8s 1.28.1` + +### 架构 + +![架构](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/architecture.png) + +Jenkins Master 和 Jenkins Slave 以 Pod 形式运行在 Kubernetes 集群的 Node 上,Master是常驻服务,所有的配置数据都存储在一个 +Volume 中,Slave 不是一直处于运行状态,它会按照需求动态的创建并自动删除。 + +当 Jenkins Master 接受到 Build 请求时,会根据配置的 Label 动态创建一个运行在 Pod 中的 Jenkins Slave 并注册到 Master +上,当运行完 Job 后,这个 Slave 会被注销并且这个 Pod 也会自动删除,恢复到最初状态。 + +### 开始 + +Jenkins 相关资源 + +- Namespace +- ServiceAccount +- ClusterRoleBinding +- RersistentVolume +- PersistentVolumeClaim +- Service +- Deployment +- Ingress(可选) + +#### PV/PVC + +PV/PVC 对象用于持久化 Jenkins 相关的工作目录、插件等数据 + +```yml +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: jenkins-pvc + namespace: alomerry + labels: + service: jenkins +spec: + storageClassName: local-storage + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 10Gi +``` + +```yml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: jenkins-pv +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + storageClassName: local-storage + local: + path: /root/apps/jenkins + nodeAffinity: + required: + nodeSelectorTerms: + - matchExpressions: + - key: kubernetes.io/hostname + operator: In + values: + - master + - node + - node2 +``` + +#### Deployment + +Deployment 用于部署 Jenkins Pod,在 `securityContext` 配置 `runAsUser`、`runAsGroup` 和 `fsGroup` 为 `uid:1000` +以 `jenkins` 用户运行,并暴露端口 8080 和 50000 端口 + +```yml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: jenkins-deployment + namespace: alomerry +spec: + replicas: 1 + selector: + matchLabels: + service: jenkins + template: + metadata: + labels: + service: jenkins + spec: + securityContext: + runAsUser: 1000 + runAsGroup: 1000 + fsGroup: 1000 + containers: + - name: jenkins + image: registry.cn-hangzhou.aliyuncs.com/alomerry/jenkins:v2.424 + imagePullPolicy: Always + env: + - name: TZ + value: Asia/Shanghai + ports: + - name: web + containerPort: 8080 + - name: wesocket + containerPort: 50000 + volumeMounts: + - name: jenkins-pv + mountPath: /var/jenkins_home + volumes: + - name: jenkins-pv + persistentVolumeClaim: + claimName: jenkins-pvc +``` + +#### Service/Ingress + +Service 用于将 Jenkins Pod 的端口以服务的形式统一暴露,如需要集群外访问,可以配置 Ingress 将 Service 暴露出去 + +#### ServiceAccount/ClusterRoleBinding + +如果需要在 Jenkins 访问和部署 Pod 需要配置对应的 ServiceAccount/ClusterRoleBinding 授权访问集群信息 + +### Case + +以下是我部署的 Jenkins 对应的资源文件: + +@[code](@_codes/vps-home/ansible/playbook/roles/jenkins/files/jenkins.yml) + +### 图示 + +执行 `kubectl apply -f jenkins.yml` 后即可进入初始化页面: + +![初始化](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/unlock-jenkins.png) + +执行 `kubectl logs -n ${namespace} ${pod name}` + +```shell +Running from: /usr/share/jenkins/jenkins.war +... +2023-09-24 15:17:21.975+0000 [id=36] INFO jenkins.install.SetupWizard#init: + +************************************************************* +************************************************************* +************************************************************* + +Jenkins initial setup is required. An admin user has been created and a password generated. +Please use the following password to proceed to installation: + +${init code} + +This may also be found at: /var/jenkins_home/secrets/initialAdminPassword + +************************************************************* +************************************************************* +************************************************************* + +... +2023-09-24 15:20:58.800+0000 [id=23] INFO hudson.lifecycle.Lifecycle#onReady: Jenkins is fully up and running +``` + +设置好用户名密码后就进入 Jenkins 主页面了 + +![jenkins-home](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-home.png) + +将内置的节点执行数量设置成 0,设置除非指定节点名时才能使用以保证主节点的稳定 + +![prevent build-in node](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/prevent-use-build-in-node.png) + +配置 k8s 环境 + +- k8s 地址:IP + 端口号或者是集群内地址 `https://kubernetes.default.svc` +- 将 kubeconfg 配置成 secret file 后设置到凭据中 + +获取 server certificate key + +```shell +cat /etc/kubernetes/admin.conf | grep certificate-authority-data +echo ${certificate-authority-data} | base64 -d +``` + +jenkins 地址可以写 jenkins-service 的地址 + +![k8s config](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-k8s-config-1.png) + +![k8s config](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-k8s-config-2.png) + +![build pipeline](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-build-by-k8s-pod.png) + +![jenkins build pod](https://cdn.alomerry.com/blog/assets/img/notes/ci/jenkins/jenkins-build-pod.png) + +### 优势 + +相对于部署在虚拟机环境下的 Jenkins 一主多从架构,将 Jenkins 部署到 k8s 会带来以下好处: + +- 服务高可用: 当 Jenkins Master 出现故障时,Kubernetes 会自动创建一个新的 Jenkins Master 容器,并且将 Volume + 分配给新创建的容器,保证数据不丢失,从而达到集群服务高可用。 +- 动态伸缩: 合理使用资源,每次运行 Job 时,会自动创建一个 Jenkins Slave,Job 完成后,Slave 自动注销并删除容器,资源自动释放,而且 + Kubernetes 会根据每个资源的使用情况,动态分配 Slave 到空闲的节点上创建,降低出现因某节点资源利用率高,还排队等待在该节点的情况。 +- 扩展性:当 Kubernetes 集群的资源严重不足而导致 Job 排队等待时,可以很容易的添加一个 Kubernetes Node 到集群中,从而实现扩展。 + +### DinD + +自 1.24 版本之后,Kubernetes 社区将正式放弃对 docker CRI 的支持,所以 k8s 集群中的 Jenkins Pod 已经不包含 docker 了,无法直接在 +Pod 中使用 docker 相关的指令了。 + +我使用了 DinD 的方式来解决,DinD 即 Docker in Docker,看以下资源文件: + +```yml +apiVersion: v1 +kind: Pod +spec: + containers: + - name: dind + image: docker:24.0.6-dind + tty: true + securityContext: + privileged: true + command: + - dockerd + - --host=tcp://0.0.0.0:8000 + - --host=unix:///var/run/docker.sock + - --tls=false + volumeMounts: + - mountPath: /var/run + name: docker-sock + readinessProbe: + exec: + command: [ "docker", "info" ] + initialDelaySeconds: 10 + failureThreshold: 6 + - name: docker-builder + image: docker:24.0.6 + tty: true + securityContext: + privileged: true + volumeMounts: + - mountPath: /var/run + name: docker-sock + volumes: + - name: docker-sock + emptyDir: { } +``` + +通过 `docker:24.0.6-dind` 启动 dockerd 服务并挂在到 `docker:24.0.6` 容器中,实现 `docker-builder` container 可以执行 +docker 命令 + +### Reference + +- [kubernetes 安装 jenkins 及配置 pipeline](https://www.orchome.com/16641) +- [基于 Kubernetes 的 Jenkins 服务也去 Docker](https://www.chenshaowen.com/blog/using-podman-to-build-images-under-kubernetes-and-jenkins.html) +- [流水线中使用 docker in pod 方式构建容器镜像](https://blog.k8s.li/docker-in-pod.html) +- [在 containerd 集群中使用 docker 做镜像构建服务](https://imroc.cc/k8s/best-practice/containerd-dind/) +- [基于 Kubernetes 实现高可用的动态 Jenkins Slave](https://testerhome.com/articles/31012) +- [Kubernetes plugin for Jenkins](https://plugins.jenkins.io/kubernetes/#plugin-content-kubernetes-plugin-for-jenkins) +- [Kubernetes plugin samples](https://github.com/jenkinsci/kubernetes-plugin/blob/master/src/main/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/samples/declarative.groovy) diff --git a/blog/pages/posts/2022/jenkins-pipeline.md b/blog/pages/posts/2022/jenkins-pipeline.md new file mode 100644 index 00000000..db56b325 --- /dev/null +++ b/blog/pages/posts/2022/jenkins-pipeline.md @@ -0,0 +1,613 @@ +--- +date: 2022-06-17T16:00:00.000+00:00 +title: Jenkins 流水线 +desc: Jenkins 流水线、插件、Jenkinsfile +duration: 10min +--- + +[[toc]] + +## Jenkins Pipeline + +::: tip 为什么使用 pipeline? + +freestyle 主要使用配置的方式来描述一个 job,刚上手的时候我也是使用这种方式来构建项目、发布。后续熟悉了之后一些其它的构建我使用了 +pipeline 之后体会到了完全不同的顺畅。简单来说虽然 freestyle 的学习成本低,但是无法将配置代码化,在各种插件杂糅在 job +后,迁移和版本控制时会增加心智负担,配置的流程很长,各项间隔很远,而不像 pipeline 的形式,皆在一个 groovy +脚本中,上下文前后前后关联都可以一览无余。 + +其次是 pipeline 中可以定义多个 stage,来获得一些 freestyle 无法实现的行为,例如并行、人工批准、复用等,最终组合成 pipeline +集。综合上面的部分心得,最后我完全废弃 freestyle,仅使用 pipeline 来构建和发布项目。[^why-pipeline] + +::: + +- https://plugins.jenkins.io/kubernetes/ +- https://github.com/jenkinsci/kubernetes-plugin/blob/master/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/declarative.groovy +- https://github.com/jenkinsci/kubernetes-plugin/blob/master/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline/jenkinsSecretHidden.groovy +- https://github.com/jenkinsci/kubernetes-plugin/tree/master/src/test/resources/org/csanchez/jenkins/plugins/kubernetes/pipeline +- https://plugins.jenkins.io/kubernetes/ + +优化 + +- http://www.devopser.org/articles/2020/09/11/1599814292016.html + +其他 + +- https://stackoverflow.com/questions/36194316/how-to-get-the-build-user-in-jenkins-when-job-triggered-by-timer + +build info + +- https://testerhome.com/topics/13511 +- https://cloud.tencent.com/developer/article/2202789 +- https://blog.csdn.net/weixin_39918388/article/details/112462275 + +k8s + +- https://www.chenshaowen.com/blog/creating-jenkins-slave-dynamically-on-kubernetes.html +- https://www.cnblogs.com/cyleon/p/14894586.html + +### 概念[^pipeline-conception] + +先来看一个官方的声明式的 pipeline Jenkinsfile[^jenkinsfile]: + +```groovy +pipeline { + [1] + agent any[2] + stages { + stage('Build') { + [3] + steps { + [4] + sh 'make' + } + } + stage('Test') { + steps { + sh 'make check' + junit 'reports/**/*.xml' + } + } + stage('Deploy') { + steps { + sh 'make publish' + } + } + } +} +``` + +- `[1]` pipeline 定义了包含执行整个流水线的所有内容和指令的块。 +- `[2]` agent 指示 Jenkins 为整个流水线分配一个执行器(在节点上)和工作区。 +- `[3]` stage 可以理解为 pipeline 流程中的一个阶段,一个或多个阶段实现了整个 pipeline 的功能。例如上例中,pipeline + 由构建(Build)、测试(Test)和发布(Deploy)构成。 +- `[4]` steps 中可以描述每个 stage 需要运行的功能。例如上例中的测试(Test)stage 中,需要执行 shell 命令 `make check`,然后使用 + junit 上报测试。 + +简单了解 pipeline 是大概是什么之后就可以学习 pipeline 的语法。 + +### 语法[^pipeline-syntax] + +#### agent + +::: info agent + +`agent` 部分指定了整个流水线或特定的部分,将会在 Jenkins 环境中执行的位置,这取决于 `agent` +区域的位置。该部分必须在 `pipeline` 块的顶层被定义,但是 stage 级别的使用是可选的。 + +::: + +参数: + +- **any** 在任何可用的代理上执行流水线或阶段 +- **none** 当在 pipeline 块的顶部没有全局代理,该参数将会被分配到整个流水线的运行中并且每个 stage 部分都需要包含他自己的 + agent 部分 +- **label** 在提供了标签的 Jenkins 环境中可用的代理上执行流水线或阶段 +- **node** `agent { node { label 'labelName' } }` 和 `agent { label 'labelName' }` 一样,但是 node 允许额外的选项 ( + 比如 `customWorkspace` ) +- **docker** 使用给定的容器执行流水线或阶段。该容器将在预置的 node 上,或在匹配可选定义的 `label` 参数上,动态的供应来接受基于 + Docker 的流水线。`docker` 也可以选择的接受 `args` 参数,该参数可能包含直接传递到 `docker run` + 调用的参数,以及 `alwaysPull` 选项,该选项强制 `docker pull` + ,即使镜像名称已经存在。比如:`agent { docker 'maven:3-alpine' }` 或 + + ```groovy + agent { + docker { + image 'maven:3-alpine' + label 'my-defined-label' + args '-v /tmp:/tmp' + } + } + ``` + +- **dockerfile** 执行流水线或阶段,使用从源代码库包含的 `Dockerfile` 构建的容器。为了使用该选项,`Jenkinsfile` + 必须从多个分支流水线中加载,或者加载“Pipeline from + SCM.”通常,这是源代码仓库的根目录下的 `Dockerfile : agent { dockerfile true }`。 如果在另一个目录下构建 `Dockerfile`,使用 + dir 选项:`agent { dockerfile {dir 'someSubDir' } }`。如果 `Dockerfile` 有另一个名称,你可以使用 `filename` + 选项指定该文件名。你可以传递额外的参数到 `docker build ...` 使用 `additionalBuildArgs` + 选项提交,比如 `agent { dockerfile {additionalBuildArgs '--build-arg foo=bar' } }` + 。例如,一个带有 `build/Dockerfile.build` 的仓库,期望一个构建参数 `version`: + + ```groovy + agent { + // Equivalent to "docker build -f Dockerfile.build --build-arg version=1.0.2 ./build/ + dockerfile { + filename 'Dockerfile.build' + dir 'build' + label 'my-defined-label' + additionalBuildArgs '--build-arg version=1.0.2' + } + } + ``` + +#### post + +> `post` 部分定义一个或多个 steps,这些阶段根据流水线或阶段的完成情况而 运行(取决于流水线中 `post` 部分的位置)。`post` +> 支持以下 post-condition 块中的其中之一:`always`、`changed`、`failure`、`success`、`unstable` 和 `aborted` +> 。这些条件块允许在 `post` 部分的步骤的执行取决于流水线或阶段的完成状态。 + +Conditions + +- `always` 无论流水线或阶段的完成状态如何,都允许在 `post` 部分运行该步骤。 +- `changed` 只有当前流水线或阶段的完成状态与它之前的运行不同时,才允许在 `post` 部分运行该步骤。 +- `failure` 只有当前流水线或阶段的完成状态为“failure”,才允许在 `post` 部分运行该步骤。 +- `success` 只有当前流水线或阶段的完成状态为“success”,才允许在 `post` 部分运行该步骤。 +- `unstable` 只有当前流水线或阶段的完成状态为“unstable”,才允许在 `post` 部分运行该步骤,通常由于测试失败,代码违规等造成。 +- `aborted` 只有当前流水线或阶段的完成状态为“aborted”,才允许在 `post` 部分运行该步骤,通常由于流水线被手动的 aborted。 + +Case [处理故障](https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile/#handling-failure): + +测试失败后发送邮件 + +```groovy +pipeline { + agent any + stages { + stage('Test') { + steps { + sh 'make check' + } + } + } + post { + always { + junit '**/target/*.xml' + } + failure { + mail to: team @example.com, subject: 'The Pipeline failed :(' + } + } +} +``` + +#### stages + +> 包含一系列一个或多个 stage 指令,`stages` 部分是流水线描述的大部分“work”的位置。建议 `stages` 至少包含一个 `stage` +> 指令用于连续交付过程的每个离散部分,比如构建、测试和部署。 + +#### environment + +> 指令制定一个 键-值对序列,该序列将被定义为所有步骤的环境变量,或者是特定于阶段的步骤,这取决于 `environment` 指令在流水线内的位置。 +> +>该指令支持一个特殊的助手方法 `credentials()`,该方法可用于在Jenkins环境中通过标识符访问预定义的凭证。对于类型为“Secret +> Text”的凭证,`credentials()` 将确保指定的环境变量包含秘密文本内容。对于类型为“SStandard username and +> password”的凭证,指定的环境变量指定为 `username:password`,并且两个额外的环境变量将被自动定义 :分别为 `MYVARNAME_USR` +> 和 `MYVARNAME_PSW`。 + +- 顶层流水线块中使用的 `environment` 指令将适用于流水线中的所有步骤。 +- 在一个 `stage` 中定义的 `environment` 指令只会将给定的环境变量应用于 stage 中的步骤。 +- `environment` 块有一个 助手方法 `credentials()` 定义,该方法可以在 Jenkins 环境中用于通过标识符访问预定义的凭证。 + +:::tip + +[Jenkins 环境变量](https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile/#使用环境变量) + +::: + +#### [处理凭据](https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile/#处理凭据) + +- Secret 文本 +- 带密码的用户名 +- Secret 文件 +- 其他凭据类型(SSH 私钥、PKCS、Docker 主机证书) + +#### options + +> `options` 指令允许从流水线内部配置特定于流水线的选项。流水线提供了许多这样的选项,比如 `buildDiscarder` +> ,但也可以由插件提供,比如 `timestamps`。 + +可用选项: + +- **buildDiscarder** + 为最近的流水线运行的特定数量保存组件和控制台输出。例如:`options { buildDiscarder(logRotator(numToKeepStr: '1')) }` +- **disableConcurrentBuilds** 不允许同时执行流水线。 可被用来防止同时访问共享资源等。 + 例如:`options { disableConcurrentBuilds() }` +- **overrideIndexTriggers** + 允许覆盖分支索引触发器的默认处理。如果分支索引触发器在多分支或组织标签中禁用, `options { overrideIndexTriggers(true) }` + 将只允许它们用于促工作。否则 `options { overrideIndexTriggers(false) }` 只会禁用改作业的分支索引触发器。 +- **skipDefaultCheckout** 在`agent` 指令中,跳过从源代码控制中检出代码的默认情况。例如:`options { skipDefaultCheckout() }` +- **skipStagesAfterUnstable** 一旦构建状态变得UNSTABLE,跳过该阶段。例如:`options { skipStagesAfterUnstable() }` +- **checkoutToSubdirectory** + 在工作空间的子目录中自动地执行源代码控制检出。例如:`options { checkoutToSubdirectory('foo') }` +- **timeout** 设置流水线运行的超时时间, 在此之后,Jenkins将中止流水线。例如:`options { timeout(time: 1, unit: 'HOURS') }` +- **retry** 在失败时, 重新尝试整个流水线的指定次数。 例如:`options { retry(3) }` +- **timestamps** 预谋所有由流水线生成的控制台输出,与该流水线发出的时间一致。例如:`options { timestamps() }` + +#### trigger + +> `triggers` 指令定义了流水线被重新触发的自动化方法。对于集成了源(比如 GitHub 或 BitBucket)的流水线, +> 可能不需要 `triggers`,因为基于 web 的集成很肯能已经存在。当前可用的触发器是 `cron`,`pollSCM` 和 `upstream`。 + +- **cron** 接收 cron 样式的字符串来定义要重新触发流水线的常规间隔,比如:`triggers { cron('H */4 * * 1-5') }` +- **pollSCM** 接收 cron 样式的字符串来定义一个固定的间隔,在这个间隔中,Jenkins + 会检查新的源代码更新。如果存在更改,流水线就会被重新触发。例如:`triggers { pollSCM('H */4 * * 1-5') }` +- **upstream** + 接受逗号分隔的工作字符串和阈值。当字符串中的任何作业以最小阈值结束时,流水线被重新触发。例如:`triggers { upstream(upstreamProjects: 'job1,job2', threshold: hudson.model.Result.SUCCESS) }` + +#### when + +> `when` 指令允许流水线根据给定的条件决定是否应该执行阶段。`when` 指令必须包含至少一个条件。如果 `when` +> 指令包含多个条件,所有的子条件必须返回 True,阶段才能执行。这与子条件在 `allOf` 条件下嵌套的情况相同。 +> 使用诸如 `not`、`allOf` 或 `` 的嵌套条件可以构建更复杂的条件结构 can be built 嵌套条件可以嵌套到任意深度。 + +内置条件: + +- **branch** 当正在构建的分支与模式给定的分支匹配时,执行这个阶段,例如:`when { branch 'master' }`。注意,这只适用于多分支流水线。 +- **environment** + 当指定的环境变量是给定的值时,执行这个步骤,例如:`when { environment name: 'DEPLOY_TO', value: 'production' }` +- **expression** 当指定的 Groovy 表达式评估为 true + 时,执行这个阶段,例如:`when { expression { return params.DEBUG_BUILD } }` +- **not** 当嵌套条件是错误时,执行这个阶段,必须包含一个条件,例如:`when { not { branch 'master' } }` +- **allOf** + 当所有的嵌套条件都正确时,执行这个阶段,必须包含至少一个条件,例如:`when { allOf { branch 'master'; environment name: 'DEPLOY_TO', value: 'production' } }` +- **anyOf** + 当至少有一个嵌套条件为真时,执行这个阶段,必须包含至少一个条件,例如:`when { anyOf { branch 'master'; branch 'staging' } }` + +[Case:在进入 stage 的 agent 前评估 when](https://www.jenkins.io/zh/doc/book/pipeline/syntax/#在进入-stage-的-agent-前评估-when) + +#### concurrent + +> 声明式流水线的阶段可以在他们内部声明多隔嵌套阶段,它们将并行执行。注意,一个阶段必须只有一个 `steps` 或 `parallel` +> 的阶段。嵌套阶段本身不能包含进一步的 `parallel` 阶段,但是其他的阶段的行为与任何其他 `stage` 相同。任何包含 `parallel` +> 的阶段不能包含 `agent` 或 `tools` 阶段,因为他们没有相关 `steps`。 +> +>另外,通过添加 `failFast true` 到包含 `parallel` 的 `stage` 中,当其中一个进程失败时,你可以强制所有的 `parallel` 阶段都被终止。 + +Case: + +```groovy +pipeline { + agent any + stages { + stage('Non-Parallel Stage') { + steps { + echo 'This stage will be executed first.' + } + } + stage('Parallel Stage') { + when { + branch 'master' + } + failFast true + parallel { + stage('Branch A') { + agent { + label "for-branch-a" + } + steps { + echo "On Branch A" + } + } + stage('Branch B') { + agent { + label "for-branch-b" + } + steps { + echo "On Branch B" + } + } + } + } + } +} +``` + +### 案例 + +#### blog/algorithm + +以前刷过一段时间的 PAT,有一些经典题目记录了下来,后续也会抽空刷 LeetCode,所以使用 jekyll 搭建了一个 IOI 题解的 +blog,需要一些环境,这个 case 主要记录将 github 中的代码 build 并发布到服务器。 + +由于 jekyll 需要一些环境,所以我就做了一个用于 build site 的 docker image(很简陋,后面会优化一下): + +pipeline + +::: code-tabs + +@tab blog + +@[code](@_codes/blog/Jenkinsfile) + +::: + +#### 部署 bot-huan + +:::details bot-huan pipeline + +```groovy +pipeline { + // 设置全局环境变量 + environment { + url = 'https://gitlab.com/Alomerry/bot-huan.git' + KAIHEILA_BOT_TOKEN = credentials('kaiheila-bot-token') + KAIHEILA_BOT_VERIFY_TOKEN = credentials('kaiheila-bot-verify-token') + KAIHEILA_BOT_ENCRYPT_KEY = credentials('kaiheila-bot-encrypt-key') + } + triggers { + GenericTrigger( + genericVariables: [ + [ + key : 'name', + value : '$.repository.name', + expressionType: 'JSONPath', + regularFilter : '', + defaultValue : '' + ] + ], + printContributedVariables: false, + printPostContent: false, + tokenCredentialId: 'jenkins-webhook-token', + regexpFilterText: '$name', + regexpFilterExpression: '^(B|b)ot-huan$', + causeString: ' Triggered on $ref', + ) + } + agent any + stages { + stage('update build image') { + steps { + sh 'docker pull registry.cn-hangzhou.aliyuncs.com/alomerry/base-golang:1.18' + sh 'docker pull registry.cn-hangzhou.aliyuncs.com/alomerry/bot-huan' + } + } + stage('pull code and build') { + agent { + docker { + image 'registry.cn-hangzhou.aliyuncs.com/alomerry/base-golang:1.18' + } + } + steps { + retry(3) { + // 拉取代码 + git(url: env.url, branch: 'master') + } + // 构建 + dir("backend") { + sh "go build -mod=vendor -o main" + stash(name: "bin", includes: "main") + } + } + } + stage('run bin') { + steps { + dir("/var/jenkins_home/build") { + unstash("bin") + } + sh ''' + docker rm $(docker ps -aq --filter name=bot-huan) -f || true + docker run -d --name bot-huan -p 4376:4376 -v /home/alomerry-home/apps/jenkins/build:/build -e $KAIHEILA_BOT_TOKEN_USR=$KAIHEILA_BOT_TOKEN_PSW -e $KAIHEILA_BOT_VERIFY_TOKEN_USR=$KAIHEILA_BOT_VERIFY_TOKEN_PSW -e $KAIHEILA_BOT_ENCRYPT_KEY_USR=$KAIHEILA_BOT_ENCRYPT_KEY_PSW registry.cn-hangzhou.aliyuncs.com/alomerry/bot-huan + ''' + // TODO 验证是否正常启动 否则报错 + } + } + } + post { + always { + deleteDir() + } + failure { + mail to: 'alomerry.wu@gmail.com', + subject: "Failed Pipeline: ${currentBuild.fullDisplayName}", + body: "Something is wrong with ${env.url}" + } + } +} + +``` + +::: + +### jenkins function + +```groovy +pipeline { + agent any + stages { + stage('Test') { + steps { + whateverFunction() + } + } + } +} + +def whateverFunction() { + sh 'ls /' +} +``` + +return value + +```groovy +def output // set as global variable +pipeline { + ... + + stage('Sum') + { + steps + { + script + { + output = sum() + echo "The sum is ${output}" + } + } + } + ... +``` + +### Reference + +[^why-pipeline]: [Why Pipeline](https://www.jenkins.io/zh/doc/book/pipeline/#why) +[^pipeline-conception]: [Pipeline Conception](https://www.jenkins.io/zh/doc/book/pipeline/#流水线概念) +[^pipeline-syntax]: [Pipeline Syntax](https://www.jenkins.io/zh/doc/book/pipeline/syntax/) +[^jenkinsfile]: [jenkinsfile](https://www.jenkins.io/zh/doc/book/pipeline/jenkinsfile/#创建-jenkinsfile) + + +## Jenkinsfile + +### 通过文件变动来触发其他 job + +```groovy +stage('check and trigger resume') { + steps { + script { + def resumeChanged = 'git --no-pager diff --name-only HEAD^ HEAD | grep -q "src/about/resume/"' + if (resumeChanged != "") { + build job: 'resume', wait: true + } + } + } +} +``` + +`git diff -name-only HEAD^ HEAD` 可以输出 HEAD 与 HEAD 前一次的变动文件,通过管道和 grep +来筛选是否包含 `src/about/resume` 路径下的改动。`--no-pager` 可以直接输出结果,避免以交互式的形式展示 + +- jenkins git diff https://sinkcup.github.io/jenkins-git-diffs + +## Jenkins 插件 + +https://plugins.jenkins.io/build-user-vars-plugin/ + +- https://plugins.jenkins.io/build-user-vars-plugin/ + +### SSH + +- [ssh pipeline](https://plugins.jenkins.io/ssh-steps) + +### Docker + +- [docker](https://plugins.jenkins.io/docker-plugin/) +- [docker pipeline](https://plugins.jenkins.io/docker-workflow) + +### Generic Webhook Trigger[^generic-webhook-trigger] + +https://github.com/jenkinsci/generic-webhook-trigger-plugin/blob/master/src/test/resources/org/jenkinsci/plugins/gwt/bdd/ + +Case 配合 pipeline 中的 trigger 可以实现仓库有推送后即触发构建 + +```groovy +GenericTrigger( + genericVariables: [ + [ + key : 'name', + value : '$.repository.name', + expressionType: 'JSONPath', + regularFilter : '', + defaultValue : '' + ] + ], + printContributedVariables: false, + printPostContent: false, + tokenCredentialId: 'jenkins-webhook-token', + regexpFilterText: '$name', + regexpFilterExpression: '^(B|b)ot-huan$', + causeString: ' Triggered on $ref', +) +``` + +genericVariables 中配置一些从 request.body 中获取的变量,上例中读取的是 request.body 中的 repository.name 的值赋到变量 +name 中,并使用正则判断是否满足要求 + +配置后可以使用 gitlab Test push 查看 jenkins 返回值 + +```json +{ + "jobs": { + "bot-huan": { + "regexpFilterExpression": "bot-huan", + "triggered": true, + "resolvedVariables": { + "name": "bot-huan" + }, + "regexpFilterText": "bot-huan", + "id": 390, + "url": "queue/item/390/" + }, + "blog": { + "regexpFilterExpression": "^(B|b)log$", + "triggered": false, + "resolvedVariables": { + "name": "bot-huan" + }, + "regexpFilterText": "bot-huan", + "id": 0, + "url": "" + }, + "algorithm": { + "regexpFilterExpression": "^(A|a)lgorithm$", + "triggered": false, + "resolvedVariables": { + "name": "bot-huan" + }, + "regexpFilterText": "bot-huan", + "id": 0, + "url": "" + } + }, + "message": "Triggered jobs." +} +``` + +jenkins 通过流水线中配置的正则来匹配触发哪条流水线,可以查看 jenkins 给 gitlab 的返回值看出触发了 bot-huan 的构建 + +### SSH Pipeline Step[^ssh-pipeline-step] + +- sshCommand +- sshGet +- sshPut +- sshRemove +- sshScript + +Case 将构建好的静态文件发布到服务器: + +```groovy +def remote = [:] +remote.name = 'root' +remote.logLevel = 'FINEST' +remote.host = '[your host]' +remote.allowAnyHosts = true +withCredentials([usernamePassword(credentialsId: 'tencent-ubuntu-root', passwordVariable: 'password', usernameVariable: 'username')]) { + remote.user = "${username}" + remote.password = "${password}" +} +sshCommand remote: remote, command: '''#!/bin/bash + cd /www/wwwroot/[your website]/ + shopt -s extglob + rm -rf !(.htaccess|.user.ini|.well-known|favicon.ico|algorithm.tar.gz) + ''' +sshPut remote: remote, from: '/var/jenkins_home/workspace/algorithm/docs/_site/algorithm.tar.gz', into: '/www/wwwroot/[your website]/' +sshCommand remote: remote, command: "cd /www/wwwroot/[your website] && tar -xf algorithm.tar.gz" +sshRemove remote: remote, path: '/www/wwwroot/[your website]/algorithm.tar.gz' +``` + +配合 pipeline 中的 environment,配置好 remote 后,先删除非必要文件,将静态文件压缩包推送到服务器指定位置,解压后删除即可。 + +### Reference + +[^ssh-pipeline-step]: [SSH Pipeline Step](https://github.com/jenkinsci/ssh-steps-plugin#configuration) +[^generic-webhook-trigger]: [Generic Webhook Trigger](https://plugins.jenkins.io/generic-webhook-trigger) + diff --git a/blog/pages/posts/2022/tail-recursion.md b/blog/pages/posts/2022/tail-recursion.md index ba65276c..fe010963 100644 --- a/blog/pages/posts/2022/tail-recursion.md +++ b/blog/pages/posts/2022/tail-recursion.md @@ -1,7 +1,6 @@ --- date: 2022-08-10T16:00:00.000+00:00 title: 尾递归 -lang: zh type: posts+algorithm duration: 10min --- diff --git a/blog/pages/posts/2022/vps-and-home-lab.md b/blog/pages/posts/2022/vps-and-home-lab.md index 10172d05..c8aa254f 100644 --- a/blog/pages/posts/2022/vps-and-home-lab.md +++ b/blog/pages/posts/2022/vps-and-home-lab.md @@ -1,7 +1,6 @@ --- date: 2022-02-23T16:00:00.000+00:00 title: cvm、vps 和 homelab 手册 -lang: zh duration: 10min desc: 记录家庭服务器和云服务器的服务笔记,用于迁移或者恢复,macOS 使用过程中的一些经验、技巧、笔记 place: 上海 diff --git a/blog/pages/posts/2023/[share]-apps-intro.md b/blog/pages/posts/2023/apps-intro.md similarity index 99% rename from blog/pages/posts/2023/[share]-apps-intro.md rename to blog/pages/posts/2023/apps-intro.md index 778ef787..f0d0cde3 100644 --- a/blog/pages/posts/2023/[share]-apps-intro.md +++ b/blog/pages/posts/2023/apps-intro.md @@ -1,7 +1,6 @@ --- date: 2020-04-26T16:00:00.000+00:00 title: 实用/特殊应用介绍 -lang: zh duration: 10min type: posts --- diff --git a/blog/pages/posts/2023/[algorithm]-kmp.md b/blog/pages/posts/2023/kmp.md similarity index 99% rename from blog/pages/posts/2023/[algorithm]-kmp.md rename to blog/pages/posts/2023/kmp.md index 3e8e6a3a..2f998234 100644 --- a/blog/pages/posts/2023/[algorithm]-kmp.md +++ b/blog/pages/posts/2023/kmp.md @@ -1,7 +1,6 @@ --- date: 2023-01-27T16:00:00.000+00:00 -title: KMP 算法 字符串匹配 -lang: en +title: 字符串匹配算法 KMP duration: 10min --- diff --git a/blog/pages/posts/2023/[share]-second-hand-house.md b/blog/pages/posts/2023/second-hand-house.md similarity index 80% rename from blog/pages/posts/2023/[share]-second-hand-house.md rename to blog/pages/posts/2023/second-hand-house.md index 3f5b9586..64d67aff 100644 --- a/blog/pages/posts/2023/[share]-second-hand-house.md +++ b/blog/pages/posts/2023/second-hand-house.md @@ -1,11 +1,11 @@ --- date: 2023-09-30T16:00:00.000+00:00 title: 二手房笔记 -lang: en +type: todoNext duration: 10min --- ## Reference - [二手房购房笔记](https://github.com/Li-Aaron/Li-Aaron.github.io/blob/master/_posts/2020-07-27-real-estate-trade.md) -- [二手房装修笔记](https://github.com/Li-Aaron/Li-Aaron.github.io/blob/master/_posts/2020-09-08-decoration-1.md) \ No newline at end of file +- [二手房装修笔记](https://github.com/Li-Aaron/Li-Aaron.github.io/blob/master/_posts/2020-09-08-decoration-1.md) diff --git a/blog/pages/posts/2023/[cloud-native]-tekton&argocd.md b/blog/pages/posts/2023/tekton&argocd.md similarity index 99% rename from blog/pages/posts/2023/[cloud-native]-tekton&argocd.md rename to blog/pages/posts/2023/tekton&argocd.md index 4d0ef326..9ae7a693 100644 --- a/blog/pages/posts/2023/[cloud-native]-tekton&argocd.md +++ b/blog/pages/posts/2023/tekton&argocd.md @@ -1,7 +1,6 @@ --- date: 2023-12-22T16:00:00.000+00:00 title: Tekton Pipeline -lang: zh duration: 10min --- diff --git a/blog/pages/posts/2024/2023-summary.md b/blog/pages/posts/2024/2023-summary.md new file mode 100644 index 00000000..f6179727 --- /dev/null +++ b/blog/pages/posts/2024/2023-summary.md @@ -0,0 +1,131 @@ +--- +date: 2024-02-17T02:34:00.000+08:00 +title: 2023 小记 +place: 上海 闵行 +duration: 5min +--- + +印象中自己好像从没好好写过一篇一年的总结,看过很多人的年度总结,终于决定落笔记录一下 2023 年的一切吧。 + +促进我提笔的瞬间是 2.3, 我像往常一样上班,雨停之后的上海,我突然在一刹那有了一丝感知,往日拥堵的中环,冷冷清清,天也不太晴朗,心里却流淌起来一股欲泪的感觉。毕业后很久没有回家好好过年,今年尤其的想家。 + +## 家庭与感情 + +父母 + +今年是 2/9 号下午四点多回到家,已经完全忘记了刚收房时设置的门锁密码了,尝试了下大拇指指纹,居然进去了,妈妈在做饭,爸爸在卧室休息。妈妈看到我回来就和我拥抱了。 + +快一年没见爸妈了,我的第一感受就是他们真的苍老了很多,他们的两鬓都斑白了,感觉第一次在书中识字识得两鬓斑白这个词才是小学初中的语文课本上,现在我的父母已经如此了他们才五十多岁,却看起来那么苍老,时间真如流水啊... + +今年没有回老家看外婆和奶奶,我只想好好陪陪他们,聊了很多。。。 + +女友 + +和女友 2020 年认识的,2021 年在一起,不知不觉中已经走过了两年多,期间真的经历了很多,细细想来真有无穷无尽之处可回味。感谢她,让我看清了很多事情,也明白了自己是一个怎样的人。这两年我有付出,却也做错了很多事,没有她的宽容和批评,我也许脱不下自己的长衫。 + +期望 2025 年在上海买一个温馨的小屋子,两人两猫。 + +自我 + +::: info 《山月记》 + +方才我说,不知为何会遭此厄运,但细想起来,倒也并非茫然无绪。在我还是人的时候,尽量避免与人交往,人们也因此说我倨傲不逊,妄自尊大。人们不知道,其实是我心中某种近似于羞耻心的东西在作怪。当然,曾被誉为乡党之鬼才的我,并非没有自尊心。然而,这种自尊心,无疑是一种怯弱的自尊心。我想以诗成名,却又不进而投师访友,相与切磋琢磨。与此同时,又不屑与凡夫俗子为伍。这都是我那怯弱的自尊心和妄自尊大的羞耻心在作怪。我深怕自己本非美玉,故而不敢加以刻苦琢磨,却又半信自己是块美玉,故又不肯庸庸碌碌,与瓦砾为伍。于是我渐渐地脱离凡尘,疏远世人,结果便是一任愤懑与羞恨日益助长内心那怯弱的自尊心。其实,任何人都是驯兽师,而那野兽,无非就是各人的性情而已。于我而言,这种妄自尊大的羞耻心就是野兽,就是猛虎。它毁了我自己,害苦了我的妻儿,伤害了我的友人,最后,又如此这般,将我的外形也变成了与内心相一致的模样。如今想来,我自己仅有的那么一点才华也都付之东流了。我常卖弄什么「无所作为,则人生太长;欲有所为,则人生太短」的格言,其实我哪有什么远大的志向,无非是害怕暴露自己才华不足之卑劣的恐惧和不肯刻苦用功的无耻之怠惰而已。才华远逊于我,却凭磨砺精进而卓然成家的诗人,不知凡几。只可惜变成老虎后的今日,我才终于明白这个道理。每念及此,我便心如刀绞,悔恨不已。到如今,我已经无法再过人的生活了,即便在脑中吟成多么出色的诗作,也无法公之于世了。更何况我的头脑正在日益趋近于猛虎。我该如何是好?我那虚掷了的往昔的光阴!每念及此,唯有跑上山巅,面对空谷咆哮。这种撕心裂肺的悲哀,我极想找人倾诉。昨夜,我还在那里对月咆哮,希望有谁能理解我心中的苦楚。野兽们听到了我的咆哮声,唯有惊恐万分,跪地求饶而已。山峦树木、明月白露,也以为仅仅是一只老虎在震怒狂吼。纵然我呼天抢地,哀叹连连,也绝无一人懂我的内心。[^山月记] + +::: + +2023 我认清了自己,我想我终于有勇气面对真实的自己,这也许这就是置之死地而后生。 + +## + +欢欢 + +## 工作与学习 + +书籍 + +毕业后到 2023 年间几乎没怎么看书,为了准备跳槽,看了几本好书,特别是 《深度探索 Go 语言》,让我和书中序言一般有种“初闻大道,不胜自喜”的感觉,感慨蹉跎了岁月啊。 + + + + + + + +后面经历了一段忙碌的找工作和入职,2023 年也是没能达成我内心期望的阅读十本书,不够入职后也因为工作内容的变化,我调整了我 2024 的书单目标 + +工作 + +在群脉工作了三年多,还算舒适的工作时间(965),没有 PUA 和绩效的压力,仿佛是温水煮青蛙,一直没有琢磨过外面 xxx,用群脉的技术栈和主流公司的不一致,要学的东西多为由麻痹自己,敷衍于换工作之事。疫情后的经济下行,公司效益不足,从六月份开始了降薪,八月份因为我犯的错几乎要和女友分手。准备了两个月后,期间投简历才深觉当下普通人就业环境之恶劣,最后终于经过三面进入了现在这家公司, + +职业规划 + +因为现在的工作内容偏向监控业务,并且项目中也涉及 JAVA,想来上次用 JAVA 还是在写毕设的时候,准备好好复习一下 JAVA 和 Spring 相关的知识以及很多前司未曾用过的组件、数据库:MySQL、Clickhouse、flink。。。。 + + +## 生活与娱乐 + +买了什么(乱花钱) + +线下、咸鱼等 + +- steam deck 3200 +- 3080 3300 + +淘宝 + +- 一些游戏 哈格沃兹之遗... +- 1T m2 +- MS 2312 采集卡 150 + +拼多多 + +- 内存条若干 + +京东 + +- skn3.0 *2 340*2 +- 先马黑洞 450 +- 佳航机箱 150 +- Gosund 智能插排 200(积分) +- Razer 巴塞利斯蛇 X 250 + + +电影 + +- [名侦探柯南:黑铁的鱼影](https://movie.douban.com/subject/35873969/) +- [再见,李可乐](https://movie.douban.com/subject/35943827/) +- [芭比](https://movie.douban.com/subject/4058939/) +- [长安三万里](https://movie.douban.com/subject/36035676/) +- [消失的她](https://movie.douban.com/subject/35660795/) +- [极寒之城](https://movie.douban.com/subject/30450557/) +- [小美人鱼](https://movie.douban.com/subject/27601044/) +- [梅根](https://movie.douban.com/subject/30277290/) +- [名侦探柯南:贝克街的亡灵](https://movie.douban.com/subject/2286642/) +- [无名](https://movie.douban.com/subject/35372742/) +- [深海](https://movie.douban.com/subject/26649682/) +- [流浪地球 2](https://movie.douban.com/subject/35267208/) + +## 健康与未来 + +- 腰 肩膀 头发 TODO + +## 其它 + +## Reference + +[^山月记]: [山月记](https://book.douban.com/subject/30394154/) diff --git a/blog/pages/posts/2024/[share]-2023-summary.md b/blog/pages/posts/2024/[share]-2023-summary.md deleted file mode 100644 index c2a45215..00000000 --- a/blog/pages/posts/2024/[share]-2023-summary.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -date: 2024-02-12T16:00:00.000+00:00 -title: 2023 总结 -lang: zh -duration: 10min ---- - -## Reference - diff --git a/blog/pages/posts/2024/[share]-infancy-7k.md b/blog/pages/posts/2024/[share]-infancy-7k.md deleted file mode 100644 index e416a213..00000000 --- a/blog/pages/posts/2024/[share]-infancy-7k.md +++ /dev/null @@ -1,2 +0,0 @@ -- game free -- 6 18 \ No newline at end of file diff --git a/blog/pages/posts/2024/[share].encrpt.md b/blog/pages/posts/2024/ansible-encrpt.md similarity index 93% rename from blog/pages/posts/2024/[share].encrpt.md rename to blog/pages/posts/2024/ansible-encrpt.md index f3b90198..b8889b2e 100644 --- a/blog/pages/posts/2024/[share].encrpt.md +++ b/blog/pages/posts/2024/ansible-encrpt.md @@ -1,9 +1,10 @@ --- date: 2023-10-07T16:00:00.000+00:00 title: 使用 Ansible 加密 git 仓库中的隐私信息 -lang: zh duration: 10min --- +TODO + ## Reference diff --git a/blog/pages/posts/2024/infancy-7k.md b/blog/pages/posts/2024/infancy-7k.md new file mode 100644 index 00000000..76405ff4 --- /dev/null +++ b/blog/pages/posts/2024/infancy-7k.md @@ -0,0 +1,10 @@ +--- +date: 2024-05-01T00:00:00.000+08:00 +title: Infancy 7K +duration: 5min +--- + +TODO + +- game free +- 6 18 diff --git a/blog/src/alomerry/setting.ts b/blog/src/alomerry/setting.ts index 8a659df9..6dba17e1 100644 --- a/blog/src/alomerry/setting.ts +++ b/blog/src/alomerry/setting.ts @@ -1,20 +1,22 @@ -export const button_english_only_enable = false +export const button_english_only_enable = true; + +export const DEFAULT_LANG = "zh"; export declare interface Categories { - name: string - to: string + name: string; + to: string; } export declare interface SubCategories { - name: string - to: string - level: number + name: string; + to: string; + level: number; } export const categories: Categories[] = [ - { name: '博文', to: '/posts' }, - { name: '笔记', to: '/docs' }, -] + { name: "博文", to: "/posts" }, + { name: "笔记", to: "/docs" }, +]; // const subCategories: Map = new Map([ // ['cloud-native', { name: '云原生', to: '/docs/cloud-native' }], @@ -23,28 +25,26 @@ export const categories: Categories[] = [ // 最多支持三级 const subCategoriesMap = new Map([ - ['docs', ['cloud-native']], - ['cloud-native', ['k8s']], -]) + ["docs", ["cloud-native"]], + ["cloud-native", ["k8s"]], +]); export function subCategories(type: string | undefined): Categories[] { - if (!type) - return [] + if (!type) return []; - const result: Categories[] = [] - const types = type.split('/') - if (types.length > 1) - types.pop() + const result: Categories[] = []; + const types = type.split("/"); + if (types.length > 1) types.pop(); // const level = 1 while (types.length > 0) { - const top = types.shift() + const top = types.shift(); if (top) { - const next = subCategoriesMap.get(top) + const next = subCategoriesMap.get(top); if (next) { // } } } - return result + return result; } diff --git a/blog/src/components/ListCategory.vue b/blog/src/components/ListCategory.vue index dc14da61..f0a6f3b2 100644 --- a/blog/src/components/ListCategory.vue +++ b/blog/src/components/ListCategory.vue @@ -1,69 +1,96 @@