Kubernetes をベースとした CI/CD というと、Jenkins、Gitlab CI、Drone など使えるツールは数多くありますが、ここでは CI/CD ツールとして最も皆さんに馴染みのある Jenkins を使います。 インストールKubernetes をベースに CI/CD を行うので、Kubernetes クラスターに Jenkins をインストールするのが最適です。インストール方法はいろいろあります。詳細を理解するために、ここでは手動の方法を使用します。対応するリソース リスト ファイルは次のとおりです。 # jenkins.yaml apiVersion: v1 kind: PersistentVolume metadata: name: jenkins-local labels: app: jenkins spec: accessModes: - ReadWriteOnce capacity: storage: 5Gi storageClassName: local-storage local: path: /data/k8s/jenkins persistentVolumeReclaimPolicy: Retain nodeAffinity: required: nodeSelectorTerms: - matchExpressions: - key: kubernetes.io/hostname operator: In values: - node2 --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: jenkins-pvc namespace: kube-ops spec: storageClassName: local-storage accessModes: - ReadWriteOnce resources: requests: storage: 5Gi --- apiVersion: v1 kind: ServiceAccount metadata: name: jenkins namespace: kube-ops --- kind: ClusterRole apiVersion: rbac.authorization.k8s.io/v1 metadata: name: jenkins rules: - apiGroups: ["extensions", "apps"] resources: ["deployments", "ingresses"] verbs: ["create", "delete", "get", "list", "watch", "patch", "update"] - apiGroups: [""] resources: ["services"] verbs: ["create", "delete", "get", "list", "watch", "patch", "update"] - apiGroups: [""] resources: ["pods"] verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] - apiGroups: [""] resources: ["pods/exec"] verbs: ["create", "delete", "get", "list", "patch", "update", "watch"] - apiGroups: [""] resources: ["pods/log", "events"] verbs: ["get", "list", "watch"] - apiGroups: [""] resources: ["secrets"] verbs: ["get"] --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: jenkins namespace: kube-ops roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: jenkins subjects: - kind: ServiceAccount name: jenkins namespace: kube-ops --- apiVersion: apps/v1 kind: Deployment metadata: name: jenkins namespace: kube-ops spec: selector: matchLabels: app: jenkins template: metadata: labels: app: jenkins spec: serviceAccount: jenkins initContainers: - name: fix-permissions image: busybox:1.35.0 command: ["sh", "-c", "chown -R 1000:1000 /var/jenkins_home"] securityContext: privileged: true volumeMounts: - name: jenkinshome mountPath: /var/jenkins_home containers: - name: jenkins image: jenkins/jenkins:lts-jdk11 imagePullPolicy: IfNotPresent env: - name: JAVA_OPTS value: -Dhudson.model.DownloadService.noSignatureCheck=true ports: - containerPort: 8080 name: web protocol: TCP - containerPort: 50000 name: agent protocol: TCP readinessProbe: httpGet: path: /login port: 8080 initialDelaySeconds: 60 timeoutSeconds: 5 failureThreshold: 12 volumeMounts: - name: jenkinshome mountPath: /var/jenkins_home volumes: - name: jenkinshome persistentVolumeClaim: claimName: jenkins-pvc --- apiVersion: v1 kind: Service metadata: name: jenkins namespace: kube-ops labels: app: jenkins spec: selector: app: jenkins ports: - name: web port: 8080 targetPort: web - name: agent port: 50000 targetPort: agent --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: jenkins namespace: kube-ops spec: ingressClassName: nginx rules: - host: jenkins.k8s.local http: paths: - path: / pathType: Prefix backend: service: name: jenkins port: name: web ここでは、Jenkins の公式 Docker イメージである jenkins/jenkins:lts-jdk11 イメージを使用します。環境変数もいくつかあります。もちろん、自分のニーズに合わせて画像をカスタマイズすることもできます。たとえば、いくつかのプラグインをカスタム イメージにパッケージ化できます。ドキュメントを参照してください:https://github.com/jenkinsci/docker。ここではデフォルトの公式イメージを使用します。もう一つ注意すべき点は、データの永続性です。コンテナの /var/jenkins_home ディレクトリをそのまま保持します。ここではローカル PV メソッドを使用します。 ここで使用するイメージ内ではユーザー uid=1000 が実行されているため、ここでマウントすると権限の問題が発生します。この問題を解決するために、マウントするデータ ディレクトリを変更するために、依然として単純な initContainer を使用します。 また、jenkens は update-center.json に対して署名検証セキュリティチェックを実行するため、事前にこれを閉じておく必要があります。そうしないと、プラグインソースの次の変更が失敗する可能性があります。環境変数 JAVA_OPTS=-Dhudson.model.DownloadService.noSignatureCheck=true を設定できます。 さらに、関連する権限を持つ serviceAccount: jenkins も使用する必要があります。ここでは、Jenkins に必要な権限のみを付与します。もちろん、serviceAccount の権限に詳しくない場合は、この sa に cluster-admin クラスター ロール権限をバインドできます。もちろん、これには一定のセキュリティ上のリスクが伴います。最後のステップは、Ingress を通じてサービスを公開することです。これは比較的簡単です。 Jenkins のリソース リストを直接作成できます。 $ kubectl apply -f jenkins.yaml $ kubectl get pods -n kube-ops -l app=jenkins NAME READY STATUS RESTARTS AGE jenkins-55c4676f4d-fhmw2 1/1 Running 0 3m5s $ kubectl logs -f jenkins-55c4676f4d-fhmw2 -n kube-ops Running from: /usr/share/jenkins/jenkins.war webroot: /var/jenkins_home/war # ...... 2023-09-07 06:56:26.123+0000 [id=33] 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: 278e9dcdcab04d11ae671f7f81f517ba This may also be found at: /var/jenkins_home/secrets/initialAdminPassword ************************************************************* ************************************************************* ************************************************************* 2023-09-07 06:56:38.572+0000 [id=29] INFO jenkins.InitReactorRunner$1#onAttained: Completed initialization 2023-09-07 06:56:38.583+0000 [id=23] INFO hudson.lifecycle.Lifecycle#onReady: Jenkins is fully up and running 2023-09-07 06:57:02.555+0000 [id=49] INFO hmDownloadService$Downloadable#load: Obtained the updated data file for hudson.tasks.Maven.MavenInstaller 2023-09-07 06:57:02.556+0000 [id=49] INFO hudson.util.Retrier#start: Performed the action check updates server successfully at the attempt #1 上記の「run: Jenkins is completely up and running」というメッセージは、Jenkins アプリケーションが起動されたことを証明します。 次に、Ingress で定義されたドメイン名 jenkins.k8s.local を介して jenkins サービスにアクセスできるようになります (DNS 解決を行うか、ローカルの /etc/hosts にマッピングを追加する必要があります)。 ジェンキンス ロック解除 次に、次のコマンドを実行して、ロック解除された管理者パスワードを取得します。 $ kubectl exec -it jenkins-55c4676f4d-fhmw2 -n kube-ops -- cat /var/jenkins_home/secrets/initialAdminPassword 278e9dcdcab04d11ae671f7f81f517ba # jenkins启动日志里面也有 次に、プラグインのインストールをスキップし、デフォルトのプラグインのインストール プロセスを選択します。このプロセスは非常に遅くなります (推奨されるプラグインをインストールすることもできます)。右上隅をクリックしてプラグインの選択を閉じます。 プラグインのインストールを無視 スキップした後は、Jenkins の準備完了ページに直接入り、クリックするだけで使用を開始できます。 ジェンキンス準備完了 その後、Jenkins のホームページに入ることができます。 ジェンキンスホーム まず中国語プラグインをインストールし(中国語インターフェースが必要な場合)、Localization: Chineseを検索します。 ローカライズ: 中国語 インストールと再起動が完了すると、自動的にログイン ページに移動します。 ジェンキンスログイン ここでも、admin と初期パスワードを使用してログインします。次に、ユーザー管理ページ http://jenkins.k8s.local/user/admin/configure に移動して、ユーザー パスワードを変更できます。 パスワードを変更する その後、新しいパスワードを使用してログインできます。 次に、Pipeline プラグインなどのその他の必要なプラグインをインストールできます。 Pipeline は Jenkins のコア プラグインです。コードの構築からデプロイメントまでのプロセス全体を実装できる Pipeline スクリプトの記述に使用できる DSL 言語を定義します。 Pipeline タイプのプロジェクトを使用する場合は、事前に Jenkins Pipeline プラグインをインストールする必要があります。 パイプラインプラグイン プラグインをインストールしたら、新しいパイプライン タイプのジョブを作成します。 新しいジョブを作成する フリースタイルプロジェクトとパイプライン型プロジェクトの違いは、ビルド部分の操作がすべてページ上で完結することです。パイプライン ビルド タスクの説明はすべてコード形式です。 こんにちは 保存後、「今すぐビルド」をクリックしてこのタスクを実行するか、このタスクの実行結果の出力を表示できます。 実行結果 建築Jenkins がインストールされたので、すぐに使用する必要はありません。 Kubernetes 環境で Jenkins を使用する利点を理解する必要があります。 私たちは、継続的な構築とリリースが日々の業務において不可欠なステップであることを認識しています。現在、ほとんどの企業は Jenkins クラスターを使用して、ニーズを満たす CI/CD プロセスを構築しています。ただし、従来の Jenkins スレーブの 1 つのマスターと複数のスレーブのアプローチには、次のような問題点があります。 - マスターで単一点障害が発生すると、プロセス全体が利用できなくなります。
- 各スレーブには、さまざまな言語のコンパイルやパッケージ化などの操作を完了するための異なる構成環境があります。しかし、これらの差別化された構成は管理を非常に不便にし、メンテナンスを困難にします。
- リソースの割り当てが不均衡です。奴隷の中には、仕事が実行されるのを待っている者もいれば、何もせずに待っている者もいます。
- 資源の無駄遣いです。各スレーブは物理マシンまたは仮想マシンである可能性があります。スレーブがアイドル状態の場合、リソースは完全に解放されません。
上記の問題点のため、私たちは CI/CD プロセスを完了するためのより効率的で信頼性の高い方法を切望しています。 Docker 仮想化コンテナ テクノロジーは、特に Kubernetes クラスター環境において、この問題点を非常にうまく解決できます。上記の問題をより良く解決できます。次の図は、Kubernetes に基づいて Jenkins クラスターを構築する簡単な概略図です。 k8s ジェンキンス スレーブ 図から、Jenkins Master と Jenkins Slave が Pod の形で Kubernetes クラスターのノード上で実行されていることがわかります。マスターはノードの 1 つで実行され、その構成データをボリュームに保存します。スレーブは各ノード上で実行され、常に実行状態にあるとは限りません。必要に応じて動的に作成され、自動的に削除されます。 この方法のワークフローは、おおよそ次のようになります。Jenkins マスターはビルド要求を受信すると、構成されたラベルに従ってポッドで実行される Jenkins スレーブを動的に作成し、マスターに登録します。ジョブが終了すると、スレーブは登録解除され、ポッドは自動的に削除され、元の状態に復元されます。 では、このアプローチを使用するとどのようなメリットが得られるのでしょうか? - 高いサービス可用性: Jenkins マスターに障害が発生すると、Kubernetes は自動的に新しい Jenkins マスター コンテナを作成し、新しく作成されたコンテナにボリュームを割り当てて、データが失われないようにすることで、クラスター サービスの高可用性を実現します。
- 動的なスケーリングとリソースの合理的な使用。ジョブが実行されるたびに、Jenkins スレーブが自動的に作成されます。ジョブが完了すると、スレーブは自動的にログアウトしてコンテナを削除し、リソースは自動的に解放されます。さらに、Kubernetes は各リソースの使用状況に基づいて、アイドル ノードにスレーブを動的に割り当てて作成するため、ノードのリソース使用率が高いためにスレーブがノード上で順番に待機しなければならない状況が軽減されます。
- 優れたスケーラビリティ。 Kubernetes クラスターのリソースが著しく不足し、ジョブがキューに入れられている場合は、Kubernetes ノードをクラスターに簡単に追加して拡張できます。これまで直面した問題はすべて、Kubernetes クラスター環境では不要になるのでしょうか?完璧ですね。
エージェントノード上記では動的ノードの利点について説明しましたが、静的ノードにこだわることを好む人もいます。静的または動的 Jenkins エージェント ノードを選択できます。次に、Kubernetes クラスターで Jenkins の動的および静的エージェント ノードを提供する方法を紹介します。 静的ノードまず、Jenkins ページ http://jenkins.k8s.local/computer/new に新しいノードを作成します。 新しいノードを作成する 「作成」をクリックしてノード情報を設定し、「保存」をクリックします。 ノードを構成する 保存すると、ノードが正常に作成されたことがわかります。 ノードリスト 次に、リスト内の agent1 の名前をクリックしてノードの詳細ページに入り、そこでノードを実行するための重要な情報を取得します。 重要な情報 次に、次のようなリソース マニフェスト ファイルを作成します。 # jenkins-agent.yaml apiVersion: apps/v1 kind: Deployment metadata: name: jenkins-agent namespace: kube-ops spec: selector: matchLabels: app: jenkins-agent template: metadata: labels: app: jenkins-agent spec: containers: - name: agent image: jenkins/inbound-agent securityContext: privileged: true imagePullPolicy: IfNotPresent env: - name: JENKINS_URL value: http://jenkins.k8s.local - name: JENKINS_SECRET value: 9c4c5159b111083705eed5802ceb021cfad002a18dd59c692aa59a9616e6285a - name: JENKINS_AGENT_NAME value: agent1 - name: JENKINS_AGENT_WORKDIR value: /home/jenkins/workspace 上記マニフェストファイル内の環境変数 JENKINS_URL、JENKINS_SECRET、JENKINS_AGENT_WORKDIR の値は、上記でノード詳細ページで取得した情報です。次に、このファイルをクラスターに適用します。 $ kubectl apply -f jenkins-agent.yaml 作成後、エージェントのポッドはエラーで起動します。エラーログは次のとおりです。 INFO: Locating server among [http://jenkins.k8s.local/] Sep 07, 2023 7:55:51 AM hudson.remoting.jnlp.Main$CuiListener error SEVERE: Failed to connect to http://jenkins.k8s.local/tcpSlaveAgentListener/: jenkins.k8s.local java.io.IOException: Failed to connect to http://jenkins.k8s.local/tcpSlaveAgentListener/: jenkins.k8s.local at org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver.resolve(JnlpAgentEndpointResolver.java:216) at hudson.remoting.Engine.innerRun(Engine.java:760) at hudson.remoting.Engine.run(Engine.java:543) Caused by: java.net.UnknownHostException: jenkins.k8s.local at java.base/java.net.AbstractPlainSocketImpl.connect(Unknown Source) at java.base/java.net.Socket.connect(Unknown Source) at java.base/sun.net.NetworkClient.doConnect(Unknown Source) at java.base/sun.net.www.http.HttpClient.openServer(Unknown Source) at java.base/sun.net.www.http.HttpClient.openServer(Unknown Source) at java.base/sun.net.www.http.HttpClient.<init>(Unknown Source) at java.base/sun.net.www.http.HttpClient.New(Unknown Source) at java.base/sun.net.www.http.HttpClient.New(Unknown Source) at java.base/sun.net.www.protocol.http.HttpURLConnection.getNewHttpClient(Unknown Source) at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect0(Unknown Source) at java.base/sun.net.www.protocol.http.HttpURLConnection.plainConnect(Unknown Source) at java.base/sun.net.www.protocol.http.HttpURLConnection.connect(Unknown Source) at org.jenkinsci.remoting.engine.JnlpAgentEndpointResolver.resolve(JnlpAgentEndpointResolver.java:213) ... 2 more これは実際には、設定した jenkins.k8s.local ドメイン名がカスタム ドメイン名であるためです。 K8s クラスターで解決する必要がある場合は、CoreDNS にホスト マッピングも追加する必要があります。 $ kubectl edit cm coredns -n kube-system # Please edit the object below. Lines beginning with a '#' will be ignored, # and an empty file will abort the edit. If an error occurs while saving this file will be # reopened with the relevant failures. # apiVersion: v1 data: Corefile: | .:53 { errors health { lameduck 5s } hosts { 10.206.16.10 jenkins.k8s.local fallthrough } # ...... kind: ConfigMap しかし、実際にはもっと簡単な方法があり、それは JENKINS_URL 値を Jenkins のサービス アドレス http://jenkins.kube-ops.svc.cluster.local:8080 に直接置き換えることです。この方法では、CoreDNS にホスト マッピングを追加する必要はありません。 通常、Jenkins エージェント ポッドは正常に実行されるはずです。次のコマンドで確認できます。 $ kubectl get pods -n kube-ops -l app=jenkins-agent NAME READY STATUS RESTARTS AGE jenkins-agent-76884cd44c-dd9ds 1/1 Running 0 2m32s ノード リストを再度確認すると、ノードがオンラインであることがわかります。 エージェント 次に、パイプライン タイプのジョブを作成し、パイプライン スクリプトに次のコンテンツを追加します。 組立ライン ここで定義したパイプライン スクリプトでは、パイプラインの実行環境を指定するために使用される agent キーワードを使用しました。ここで、ビルド ラベル (上記で作成した agent1 ノード) を指定して、パイプラインがこのノードで実行されるようにします。 [保存] をクリックした後、[今すぐビルド] をクリックしてパイプラインを実行し、パイプラインの実行結果を表示できます。 実行結果 このようにして、この静的ノードを使用して Jenkins でタスクを構築できます。 動的ノード静的ノードに加えて、動的ノードを使用してタスクを構築することもできます。これにより、リソースをより有効に活用できます。ここでは、Kubernetes を使用して動的ノードを作成し、動的ノードを使用して Jenkins でタスクを構築できるようにします。 ステップ 1. まず、Kubernetes プラグインをインストールする必要があります。 Kubernetesプラグイン ステップ 2. インストールが完了したら、http://jenkins.k8s.local/manage/cloud/ に移動します。 新しい Kubernetes プラグイン設定 新しいクラウド サービスを作成するには、このページの [新しいクラウド] をクリックします。 Kubernetes プラグイン設定 タイプとして必ず Kubernetes を選択し、「作成」をクリックしてください。すると、次の構成ページが表示されます。 クラウドを作成する まず、Kubernetes API サーバーに接続するためのアドレスを設定します。 Jenkins は Kubernetes クラスターで実行されるため、サービスの DNS 形式を使用して https://kubernetes.default.svc.cluster.local に接続できます。 ジェンキンス k8s apiサーバー [名前空間] フィールドに「kube-ops」と入力し、[接続テスト] をクリックします。 「Connected to Kubernetes v1.26.2」などのメッセージが表示された場合、Jenkins は Kubernetes システムと正常に通信できることを意味します。 以下の Jenkins URL アドレスは http://jenkins.kube-ops.svc.cluster.local:8080 です。上記で作成した Jenkins サービス名に従って入力します。次の Jenkins チャネルを含みます。デフォルトのポートは 50000 です (TCP なので、http を入力しないでください)。 ジェンキンス URL 次に、最後の保存ボタンをクリックして設定を保存します。この時点で、Kubernetes プラグインの構成は完了です。 テストKubernetes プラグインの設定が完了しました。次に、ジョブ タスクを追加して、それがスレーブ ポッドで実行できるかどうか、またタスクの完了後にポッドが破棄されるかどうかを確認します。 テスト タスクを作成するには、Jenkins ホームページで [新しいタスク] をクリックします。再度、パイプライン タイプのタスクを選択します。今回、使用する必要があるパイプライン スクリプトは、次に示すように、より複雑です。 pipeline{ agent{ kubernetes{ label "test01" cloud 'Kubernetes' yaml ''' --- kind: Pod apiVersion: v1 metadata: labels: k8s-app: jenkins-agent name: jenkins-agent namespace: kube-ops spec: containers: - name: jenkinsagent image: jenkins/inbound-agent imagePullPolicy: IfNotPresent ''' } } stages{ stage("Hello"){ steps{ script{ echo "Hello Slave Pod on Kubernetes!" } } } } } このスクリプトで定義されている実行エージェントはより複雑です。パイプラインの実行環境を指定するために kubernetes プロパティが使用され、実行中の Pod のマニフェスト ファイルを定義するために yaml プロパティが使用されます。ここでは、単純な Pod を定義し、この Pod を kube-ops 名前空間にデプロイして、この Pod で Jenkins スレーブを実行できるようにします。 cloud の後の値は、前に定義したクラウド サービス名と一致している必要があることに注意してください。 最後に、「保存」をクリックします。同様に、左側の [今すぐビルド] をクリックしてこのタスクを実行し、このタスクの実行結果を表示できます。 スレーブポッド実行コマンド ここでスクリプトで定義した Pod は非常にシンプルですが、Jenkins がいくつかのデフォルトの環境変数の設定に役立つことがわかります。タスクの実行中に、Kubernetes クラスター内の Pod の変更を観察することもできます。 $ kubectl get pods -n kube-ops -w NAME READY STATUS RESTARTS AGE jenkins-55c4676f4d-fhmw2 1/1 Running 3 (12m ago) 91m jenkins-agent-76884cd44c-dd9ds 1/1 Running 0 22m test01-jnzmb-ht0n7 0/1 Pending 0 0s test01-jnzmb-ht0n7 0/1 Pending 0 0s test01-jnzmb-ht0n7 0/1 ContainerCreating 0 0s test01-jnzmb-ht0n7 1/1 Running 0 1s test01-jnzmb-ht0n7 1/1 Terminating 0 3s test01-jnzmb-ht0n7 0/1 Terminating 0 4s test01-jnzmb-ht0n7 0/1 Terminating 0 4s test01-jnzmb-ht0n7 0/1 Terminating 0 4s 「今すぐビルド」をクリックすると、新しいポッドtest01-jnzmb-ht0n7 表示されます。 作成された、これが Jenkins スレーブです。タスクがビルドされると、スレーブ ポッドは自動的に削除されます。 ここまでで、Kubernetes を使って Jenkins Slave を動的に生成する方法は完了です。 |