Kubernetes持續集成:Jenkins關於java.nio.file.NoSuchFileException錯誤的緣由

背景

爲了使用Kubernetes管理並自動化部署應用程序,領導"糾結"了幾個同事搭了一個Kubernetes集羣環境。
不過,爲了減小複雜度,採用了非官方推薦的方式:java

  • API Server使用http方式
  • 不使用Admission Controllers,即API Server啓動參數admission-control設爲空

關於Admission Controllers的解釋,能夠參照官方文檔node

搭好k8s環境後,參考文檔 初試 Jenkins 使用 Kubernetes Plugin 完成持續構建與發佈 部署了Jenkins Server。git

嘗試運行pipeline腳本:github

def label = "mypod-${UUID.randomUUID().toString()}"

podTemplate(label: label, cloud: 'kubernetes', containers: [
    containerTemplate(name: 'maven', image: 'maven:3.3.9-jdk-8-alpine', ttyEnabled: true, command: 'cat'),
  ]) {

    node(label) {
        stage('Get a Maven Project') {
            git 'https://github.com/jenkins-docs/simple-java-maven-app.git'
            container('maven') {
                stage('Build a Maven project') {
                    sh 'mvn -B clean install'
                }
            }
        }
    }
}

發生了java.nio.file.NoSuchFileException: /var/run/secrets/kubernetes.io/serviceaccount/namespace的錯誤。錯誤信息:api

...
 > git checkout -f 0d85b7e1fd39bc6978511f92381aa10534ca4c1b
 > git branch -a -v --no-abbrev # timeout=10
 > git checkout -b master 0d85b7e1fd39bc6978511f92381aa10534ca4c1b
Commit message: "Amend README.md"
First time build. Skipping changelog.
[Pipeline] container
[Pipeline] // container
[Pipeline] }
[Pipeline] // stage
[Pipeline] }
[Pipeline] // node
[Pipeline] }
[Pipeline] // podTemplate
[Pipeline] End of Pipeline
java.nio.file.NoSuchFileException: /var/run/secrets/kubernetes.io/serviceaccount/namespace
    at sun.nio.fs.UnixException.translateToIOException(UnixException.java:86)
    at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:102)
    at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:107)
...

接下來就是2,3天的調查,關於這個錯誤的能查到的資料幾乎沒有,也嘗試了不少方法,無果... 很是煎熬。安全

緣由

關於這個錯誤,有幾個疑問app

  1. 文件/var/run/secrets/kubernetes.io/serviceaccount/namespace有什麼用
  2. 爲何Jenkins(Kubernetes Plugin)要去找這個文件
  3. 爲何這個文件不存在

帶着這些問題找到了官方文檔 Accessing Clusters,文檔中出現了這個文件的身姿。dom

Accessing the API from a Pod
When accessing the API from a pod, locating and authenticating to the apiserver are somewhat different.

The recommended way to locate the apiserver within the pod is with the kubernetes.default.svc DNS name, which resolves to a Service IP which in turn will be routed to an apiserver.maven

The recommended way to authenticate to the apiserver is with a service account credential. By kube-system, a pod is associated with a service account, and a credential (token) for that service account is placed into the filesystem tree of each container in that pod, at /var/run/secrets/kubernetes.io/serviceaccount/token.ui

If available, a certificate bundle is placed into the filesystem tree of each container at/var/run/secrets/kubernetes.io/serviceaccount/ca.crt , and should be used to verify the serving certificate of the apiserver.

Finally, the default namespace to be used for namespaced API operations is placed in a file at /var/run/secrets/kubernetes.io/serviceaccount/namespace in each container.

那爲何咱們搭建環境的container裏沒有這個文件呢?就連目錄 /var/run/secrets 都沒有...

調查過程當中,漸漸地把疑點定位到API Server的啓動參數 admission-control (1.10版本後被替換成 enable-admission-plugins)。官方推薦的設值有 ServiceAccount 可是搭環境的時候沒有設置,即設置爲空。

因而,把API Server的啓動admission-control參數改爲了 --admission-control=ServiceAccount
而後,重啓API Server,修改Jenkins的Deployment(爲了讓k8s從新部署Jenkins Pod)。
接着,進入新部署的Jenkins Pod(container)確認,上帝保佑,生成了目錄/var/run/secrets以及相關文件!

因爲從新部署了Jenkins,須要從新設置Jenkins(安裝Plugin、設置k8s雲、建立上述腳本的pipeline job)

運行job(item),腳本運行成功。問題解決了!

參考文檔

相關文章
相關標籤/搜索