上一篇文章 云原生之旅 - 10)手把手教你安装 Jenkins on Kubernetes 我们介绍了在 Kubernetes 上安装 Jenkins,本文介绍下如何设置k8s pod作为Jenkins 构建job的 agent。
Jenkins master 和 agent 均以 pod 的形式运行在 Kubernetes 节点上。Master 运行在其中一个节点上,其配置数据 Jenkins home 使用存储卷挂载,master pod重启不会导致数据丢失。agent 运行在各个节点上,根据需求动态创建并自动释放。这样做的好处很多,比如高可用,高伸缩性,资源利用率高。
关键词:Jenkins on Kubernetes 实践,Jenkins 和 Kubernetes,在Kubernetes上安装Jenkins,Jenkins 高可用安装,Jenkins 动态伸缩构建, Kubernetes Pod as Jenkins build agent
10. Click `Add Pod Template` then `Pod Template Details`
Ensure that you remove the sleep and 9999999 default argument from the container template.
Credentials 会在Jenkinsfile里面用到。
### 本文首发于博客园 https://www.cnblogs.com/wade-xu/p/16863955.html
这个label 和我们上面创建 pod template时用的label一致. 这样的话 Jenkins就知道用哪个 pod template 作为 agent container.
点Build Now
Agent jenkins-agent-l7hw9 is provisioned from template jenkins-agent
......
Building remotely on jenkins-agent-l7hw9 (kubeagent) in workspace /home/jenkins/agent/workspace/quick-test
[quick-test] $ /bin/sh -xe /tmp/jenkins17573873264046707236.sh
+ echo test pipeline
test pipeline
Finished: SUCCESS
### 本文首发于博客园 https://www.cnblogs.com/wade-xu/p/16863955.html
kind: Pod
spec:
containers: # list of containers that you want present for your build, you can define a default container in the Jenkinsfile
- name: maven
image: maven:3.5.4-jdk-8-slim
command: ["tail", "-f", "/dev/null"] # this or any command that is bascially a noop is required, this is so that you don't overwrite the entrypoint of the base container
imagePullPolicy: Always # use cache or pull image for agent
resources: # request and limit the resources your build contaienr
requests:
memory: 4Gi
cpu: 2
limits:
memory: 4Gi
cpu: 2
volumeMounts:
- mountPath: /root/.m2 # maven .m2 cache directory
name: maven-home
- name: git
image: bitnami/git:2.38.1
imagePullPolicy: IfNotPresent
command: ["tail", "-f", "/dev/null"]
resources: # limit the resources your build contaienr
limits:
cpu: 100m
memory: 256Mi
- name: kubectl-kustomize
image: line/kubectl-kustomize:1.25.3-4.5.7
imagePullPolicy: IfNotPresent
command: ["tail", "-f", "/dev/null"]
resources: # limit the resources your build contaienr
limits:
cpu: 100m
memory: 256Mi
- name: docker
image: docker:18.06.1
command: ["tail", "-f", "/dev/null"]
imagePullPolicy: Always
volumeMounts:
- name: docker
mountPath: /var/run/docker.sock # We use the k8s host docker engine
volumes:
- name: docker
hostPath:
path: /var/run/docker.sock
- name: maven-home
persistentVolumeClaim:
claimName: maven-repo-storage
build-pod.yaml在Jenkinsfile里面定义agent 使用这个yaml file
agent {
kubernetes {
idleMinutes 3 // how long the pod will live after no jobs have run on it
yamlFile './build-pod.yaml' // path to the pod definition relative to the root of our project
defaultContainer 'docker' // define a default container if more than a few stages use it, otherwise default to jnlp container
}
下面步骤是 docker login/build/tag/push
stage('Build and Push Docker Image') {
steps {
script {
dir(dir_path) {
container('docker') {
// docker login, Using single-quotes instead of double-quotes when referencing these sensitive environment variables prevents this type of leaking.
sh 'echo $DOCKER_HUB_CREDS_PSW | docker login -u $DOCKER_HUB_CREDS_USR --password-stdin $DOCKER_HUB_REGISTRY'
// build image with git tag
sh """
docker build -t $PROJECT_IMAGE_WITH_TAG .
docker tag $PROJECT_IMAGE_WITH_TAG $DOCKER_HUB_CREDS_USR/$PROJECT_IMAGE_WITH_TAG
"""
// push image_tag to docker hub
sh """
docker push $DOCKER_HUB_CREDS_USR/$PROJECT_IMAGE_WITH_TAG
"""
}
}
}
}
}
我这里没有选择用 docker.withRegistry
docker.withRegistry("$DOCKER_HUB_REGISTRY", "$DOCKER_HUB_CREDENTIAL") {}
因为会有不安全的log提示
WARNING! Using --password via the CLI is insecure. Use --password-stdin.
这个例子是上面的 CI 之后 加上 - 利用 Kustomize build K8S resource manifests 然后 CD 到一个 Cluster
Kustomize 可以参考 云原生之旅 - 6)不能错过的一款 Kubernetes 应用编排管理神器 Kustomize
// assume your k8s manifests in another repo, mine is same repo, just in order to show git clone step
stage('Checkout K8S manifests') {
steps {
script {
dir(dir_path) {
container('git') {
if (! fileExists('learning_by_doing/README.md')) {
sh """
git clone https://github.com/wadexu007/learning_by_doing.git
ls -lhrt
"""
} else {
sh 'echo manifes repo already exist.'
}
}
}
}
}
}
stage('Build manifests with Kustomize') {
steps {
script {
dir(dir_path) {
container('kubectl-kustomize') {
sh """
cd learning_by_doing/Kustomize/demo-manifests/services/demo-app/dev/
kustomize edit set image $DOCKER_HUB_CREDS_USR/$PROJECT_IMAGE_WITH_TAG
kustomize build > $WORKSPACE/$dir_path/deployment.yaml
"""
}
}
}
}
}
stage('Deploy to GKE test cluster') {
environment{
PROJECT_ID = 'xperiences-eng-cn-dev'
CLUSTER_NAME = 'xpe-spark-test-gke'
REGION = 'asia-east2'
CREDENTIALS_ID = 'gcp_sa_json_key'
}
steps {
script {
dir(dir_path) {
container('kubectl-kustomize') {
sh """
chown 1000:1000 deployment.yaml
echo start to deploy to cluster $CLUSTER_NAME
"""
step([
$class: 'KubernetesEngineBuilder',
projectId: env.PROJECT_ID,
clusterName: env.CLUSTER_NAME,
location: env.REGION,
manifestPattern: 'deployment.yaml',
credentialsId: env.CREDENTIALS_ID,
verifyDeployments: false])
// verifyDeployments does not work for non-default namespace
}
}
}
}
}
View Code stage('Wait for SRE Approval') {
steps {
timeout(time:72, unit:'HOURS') {
input message: "Approved Prod deployment?", submitter: 'sre-team'
}
}
}
// deployment to multipe k8s clusters
stage('Deploy to GKE Prod cluster') {
environment{
PROJECT_ID = 'sre-cn-dev'
CREDENTIALS_ID = 'gcp_sa_json_key'
CLUSTER_COMMON_NAME = 'demo-gke-prod'
}
steps {
script {
env.REGION = input message: 'Choose which region you want to deploy?',
parameters: [choice(name: 'Region',
description: 'Select Region to Deloy',
choices: ['europe-west1', 'us-central1'])
]
dir(dir_path) {
if ( env.REGION == "europe-west1" ) {
def eu_cluster_name = env.CLUSTER_COMMON_NAME + "-eu"
container('kubectl-kustomize') {
sh "echo deploy to cluster $eu_cluster_name in region: $REGION"
}
}
if ( env.REGION == "us-central1" ) {
def us_cluster_name = env.CLUSTER_COMMON_NAME + "-us"
container('kubectl-kustomize') {
sh "echo deploy to cluster $us_cluster_name in region: $REGION"
}
}
}
}
}
}
所有例子均在我的 github repo。
### 本文首发于博客园 https://www.cnblogs.com/wade-xu/p/16863955.html

导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵
C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.
运行bundleinstall后出现此错误:Gem::Package::FormatError:nometadatafoundin/Users/jeanosorio/.rvm/gems/ruby-1.9.3-p286/cache/libv8-3.11.8.13-x86_64-darwin-12.gemAnerroroccurredwhileinstallinglibv8(3.11.8.13),andBundlercannotcontinue.Makesurethat`geminstalllibv8-v'3.11.8.13'`succeedsbeforebundling.我试试gemin
需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc
有没有办法在Ruby中动态创建数组?例如,假设我想遍历用户输入的书籍数组:books=gets.chomp用户输入:"TheGreatGatsby,CrimeandPunishment,Dracula,Fahrenheit451,PrideandPrejudice,SenseandSensibility,Slaughterhouse-Five,TheAdventuresofHuckleberryFinn"我把它变成一个数组:books_array=books.split(",")现在,对于用户输入的每一本书,我想用Ruby创建一个数组。伪代码来做到这一点:x=0books_array.
我想在IRB中浏览文件系统并让提示更改以反射(reflect)当前工作目录,但我不知道如何在每个命令后进行提示更新。最终,我想在日常工作中更多地使用IRB,让bash溜走。我在我的.irbrc中试过这个:require'fileutils'includeFileUtilsIRB.conf[:PROMPT][:CUSTOM]={:PROMPT_N=>"\e[1m:\e[m",:PROMPT_I=>"\e[1m#{pwd}>\e[m",:PROMPT_S=>"FOO",:PROMPT_C=>"\e[1m#{pwd}>\e[m",:RETURN=>""}IRB.conf[:PROMPT_MO
首先,我使用的是rails3.1.3和来自master的carrierwavegithub仓库的分支。我使用after_init钩子(Hook)来确定基于属性的字段页面模型实例并为这些字段定义属性访问器将值存储在序列化哈希中(希望它清楚我是什么谈论)。这是我正在做的事情的精简版:classPage省略mount_uploader命令让我可以访问我想要的属性。但是当我安装uploader时出现错误消息说“nil类的未定义新方法”我在源代码中读到有方法read_uploader和扩展模块中的write_uploader。我如何必须覆盖这些来制作mount_uploader命令使用我的“虚拟
我正在运行Ubuntu11.10并像这样安装Ruby1.9:$sudoapt-getinstallruby1.9rubygems一切都运行良好,但ri似乎有空文档。ri告诉我文档是空的,我必须安装它们。我执行此操作是因为我读到它会有所帮助:$rdoc--all--ri现在,当我尝试打开任何文档时:$riArrayNothingknownaboutArray我搜索的其他所有内容都是一样的。 最佳答案 这个呢?apt-getinstallri1.8编辑或者试试这个:(非rvm)geminstallrdocrdoc-datardoc-da
我正在寻找用于Rails的优质管理插件。似乎大多数现有的插件/gem(例如“restful_authentication”、“acts_as_authenticated”)都围绕着self注册等展开。但是,我正在寻找一种功能齐全的基于管理/管理角色的解决方案——但不是简单地附加到另一个非基于角色的解决方案。如果我找不到,我想我会自己动手......只是不想重新发明轮子。 最佳答案 RyanBates最近做了两个关于授权的railscast(注意身份验证和授权之间的区别;身份验证检查用户是否如她所说的那样,授权检查用户是否有权访问资源
我正在尝试动态构建一个多维数组。我想要的基本上是这样的(为简单起见写出来):b=0test=[[]]test[b]这给了我错误:NoMethodError:undefinedmethod`test=[[],[],[]]而且它工作正常,但在我的实际使用中,我不会事先知道需要多少个数组。有一个更好的方法吗?谢谢 最佳答案 不需要像您正在使用的索引变量。只需将每个数组附加到您的test数组:irb>test=[]=>[]irb>test[["a","b","c"]]irb>test[["a","b","c"],["d","e","f"]]