このトピックでは、Jenkins を使用して、ソースコードからイメージをビルドし、リポジトリにプッシュして、アプリケーションをデプロイするエンドツーエンドの自動化されたパイプラインを実装する方法について説明します。GitLab でソースコードをコミットすると、Container Registry が自動的にイメージをビルドし、Container Service for Kubernetes (ACK) がそのイメージをプルしてアプリケーションをデプロイし、イベント通知が DingTalk グループに送信されます。
前提条件
-
Git、GitLab、Jenkins がインストールされていること。
説明GitLab には JDK 8 が必要です。一部のプラグインは JDK 11 では実行できません。
-
Alibaba Cloud Container Registry (ACR) Enterprise Edition インスタンスが作成され、パブリックアクセスが有効になっていること。詳細については、「Enterprise Edition インスタンスの作成」および「インターネット経由のアクセスの設定」をご参照ください。
-
ACR インスタンスと同じリージョンに Container Service for Kubernetes (ACK) クラスターが作成されていること。詳細については、「マネージド Kubernetes クラスターの作成」をご参照ください。
-
DingTalk ロボットを作成し、その Webhook URL とシークレットトークンを記録していること。詳細については、「手順 1: DingTalk ロボットの作成」をご参照ください。
-
ACR の配信チェーン機能を使用するには、インスタンスを ACR Advanced Edition にアップグレードする必要があります。詳細については、「課金」をご参照ください。
Jenkins を使用したイメージの CI 実装
ソースコードを GitLab にコミットすると、Container Registry はソースコードから自動的にイメージをビルドします。その後、イメージに対してセキュリティスキャンを実行できます。スキャンが完了すると、DingTalk グループでイベント通知を自動的に受信します。
-
GitLab でプロジェクトを作成します。
-
GitLab にログインします。
-
上部メニューで、[Projects > Your projects] を選択します。
-
[Projects] ページで、右上隅にある [New Project] をクリックし、次に [Create blank project] をクリックします。
-
[Create blank project] ページで、[Project name]、[Project URL]、[Project slug] を設定します。[Visibility Level] を [Private] に設定し、[Create project] をクリックします。
この例では、[Project name] と [Project slug] の両方を
java-webに設定します。[Project URL] には、名前空間として [shoppingmall] を選択します。 -
ローカルマシンで、次の内容の Dockerfile、pom.xml、DemoApplication.java、HelloController.java を作成します。
-
Dockerfile
FROM registry.cn-hangzhou.aliyuncs.com/public-toolbox/maven:3.8.3-openjdk-8-aliyun AS build COPY src /home/app/src COPY pom.xml /home/app RUN ["/usr/local/bin/mvn-entrypoint.sh","mvn","-f","/home/app/pom.xml","clean","package","-Dmaven.test.skip=true"] FROM registry.cn-hangzhou.aliyuncs.com/public-toolbox/openjdk:8-jdk-alpine COPY --from=build /home/app/target/demo-0.0.1-SNAPSHOT.jar /usr/local/lib/demo-0.0.1-SNAPSHOT.jar EXPOSE 8080 ENTRYPOINT ["java","-jar","/usr/local/lib/demo-0.0.1-SNAPSHOT.jar"] -
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.1</version> <relativePath/> <!-- リポジトリから親を検索 --> </parent> <groupId>com.example</groupId> <artifactId>demo</artifactId> <version>0.0.1-SNAPSHOT</version> <name>demo</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.logging.log4j</groupId> <artifactId>log4j-core</artifactId> <version>2.13.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build> </project> -
DemoApplication.java
package com.example.demo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import java.util.TimeZone; @SpringBootApplication public class DemoApplication { public static void main(String[] args) { TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai")); SpringApplication.run(DemoApplication.class, args); } } -
HelloController.java
package com.example.demo; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; import java.text.SimpleDateFormat; import java.util.Date; @RestController @Slf4j public class HelloController { @RequestMapping({"/hello", "/"}) public String hello(HttpServletRequest request) { return "Hello World at " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); } }
-
-
次のコマンドを実行して、ビルドファイルを GitLab にアップロードします。
cd java-web # ビルドファイルがあるディレクトリに移動します。 git remote set-url origin http://8.218.20*.***/shoppingmall/java-web.git git push origin master * [new branch] master -> master
-
-
Jenkins でパイプラインを設定してイメージをビルドします。
-
Jenkins で GitLab SSH キーを設定します。
-
Jenkins にログインします。
-
左側メニューで、[Manage Jenkins] をクリックします。
-
[Security] セクションで、[Manage Credentials] をクリックします。
-
[Stores scoped to Jenkins] セクションで、[Store] 列の [Jenkins] をクリックし、次に [Global credentials] をクリックします。
-
左側メニューで、[Add Credentials] をクリックします。
-
[Kind] を [SSH Username with private key] に設定し、[Description] と [Username] を入力し、[Enter directly] を選択して、[OK] をクリックします。
[Global credentials] ページで、Jenkins が自動的に認証情報 ID を生成します。この認証情報 ID を記録しておきます。
-
-
パイプラインを作成します。
-
左側メニューで、[New Item] をクリックします。
-
項目名を入力し、[Pipeline] を選択して、[OK] をクリックします。
-
[Build Triggers] タブをクリックし、[Build when a change is pushed to GitLab] を選択し、次に [Push Events] を選択します。
[Build when a change is pushed to GitLab] の右側に表示される Webhook URL を記録します。
-
[Advanced] をクリックし、[Secret token] フィールドの右下隅にある [Generate] をクリックします。
Jenkins がシークレットトークンを生成します。このトークンを記録しておきます。
-
[Pipeline] タブをクリックし、実際の情報に基づいて次のテンプレートを変更し、内容をテキストボックスに貼り付けてから [Save] をクリックします。
def git_auth_id = "6d5a2c06-f0a7-43c8-9b79-37b8c266****" # 認証情報 ID。 def git_branch_name = "master" # ブランチ名。 def git_url = "git@172.16.1*.***:shoppingmall/java-web.git" # GitLab リポジトリの URL。 def acr_url = "s*****-devsecops-registry.cn-hongkong.cr.aliyuncs.com" # イメージリポジトリの URL。 def acr_username = "acr_test_*****@test.aliyunid.com" # Container Registry のユーザー名。 def acr_password = "HelloWorld2021" # Container Registry のパスワード。 def acr_namespace = "ns" # 名前空間。 def acr_repo_name = "test" # リポジトリ名。 def tag_version = "0.0.1" # イメージタグ。 node { stage('checkout git repo') { checkout([$class: 'GitSCM', branches: [[name: "*/${git_branch_name}"]], extensions: [], userRemoteConfigs: [[credentialsId: "${git_auth_id}", url: "${git_url}"]]]) } stage('build image') { sh "sudo docker build -t java-web:${tag_version} ." sh "sudo docker tag java-web:${tag_version} ${acr_url}/${acr_namespace}/${acr_repo_name}:${tag_version}" } stage('push image') { sh "sudo docker login --username=${acr_username} --password=${acr_password} ${acr_url}" sh "sudo docker push ${acr_url}/${acr_namespace}/${acr_repo_name}:${tag_version}" } }
-
-
-
Webhook URL を GitLab に追加します。
-
GitLab にログインします。
-
[Projects] ページで、作成したプロジェクトをクリックします。
-
左側メニューで、[Settings > Webhooks] を選択します。Webhook URL とシークレットトークンを入力し、[Enable SSL verification] の選択を解除して、[Add webhooks] をクリックします。
-
-
イベント通知を作成します。
Container Registryコンソールにログインします。
上部のナビゲーションバーで、リージョンを選択します。
左側のナビゲーションウィンドウで、[インスタンス] をクリックします。
[インスタンス] ページで、管理するEnterprise Editionインスタンスをクリックします。
-
インスタンス詳細ページの左側メニューで、 を選択します。
-
イベントルール タブで、ルールの作成 をクリックします。
-
イベントの範囲 ステップで、ルール名 を設定します。イベントタイプ を イメージをスキャンしました。 に設定し、スキャンしました。 を選択し、適用範囲 を 名前空間 に設定し、名前空間として [ns] を選択して、次へ をクリックします。
-
インシデント通知 ステップで、通知方法 を DingTalk に設定し、DingTalk の Webhook URL とシークレットトークンを入力して、保存 をクリックします。
-
イメージビルドをトリガーします。
次のコマンドを実行して HelloController.java ファイルを変更し、ファイルを GitLab にコミットします。これにより、イメージビルドがトリガーされます。
vim java/com/example/demo/HelloController.java # 必要に応じて、ローカルマシンの HelloController.java ファイルを変更します。 git add . && git commit -m 'commit' && git push origin # ファイルを GitLab にコミットします。しばらくして、Enterprise Edition インスタンスの詳細ページで、左側メニューの を選択します。表示されたページで、ターゲットリポジトリ
testをクリックします。リポジトリ ページの左側メニューで、タグ をクリックします。タグ ページでイメージが生成されていることを確認できます。 -
セキュリティスキャンを設定します。
-
[Tags] ページで、ターゲットのイメージタグを見つけ、[Actions] 列の セキュリティスキャン をクリックします。
-
セキュリティスキャン ページで、スキャン をクリックします。
セキュリティスキャンが完了すると、DingTalk グループに通知が届きます。
-
Jenkins を使用したイメージの CD 実装
イメージをビルドすると、配信チェーンが自動的にトリガーされます。完了すると、配信チェーンは Jenkins に HTTP リクエストを送信します。このリクエストにより、ACK の Deployment がトリガーされ、最新のイメージがプルされてアプリケーションが再デプロイされます。
-
アプリケーションを作成します。
ACKコンソールにログインします。
ACKコンソールの左側のナビゲーションウィンドウで、[クラスター] をクリックします。
[クラスター] ページで、管理するクラスターを見つけ、クラスターの名前をクリックするか、[操作] 列の [詳細] をクリックします。 クラスターの詳細ページが表示されます。
詳細ページの左側のナビゲーションウィンドウで、 を選択します。
-
デプロイメント ページで、名前空間 を選択し、次に YAML のリソースの作成 をクリックします。
-
YAML のリソースの作成 パネルで、サンプルテンプレート を カスタム に設定し、次の内容をテンプレートエディターに貼り付けてから デプロイ をクリックします。
説明パスワード不要のイメージプルを設定しない場合は、Container Registry でパブリックアクセスを有効にし、リポジトリをパブリックに設定する必要があります。詳細については、「インターネット経由のアクセスの設定」をご参照ください。
apiVersion: apps/v1 kind: Deployment metadata: creationTimestamp: null labels: app: demo name: demo spec: replicas: 3 minReadySeconds: 5 progressDeadlineSeconds: 60 revisionHistoryLimit: 5 selector: matchLabels: app: demo strategy: rollingUpdate: maxUnavailable: 1 type: RollingUpdate template: metadata: annotations: prometheus.io/port: "9797" prometheus.io/scrape: "true" creationTimestamp: null labels: app: demo spec: containers: - image: s*****-devsecops-registry.cn-hongkong.cr.aliyuncs.com/ns/test:0.0.1 imagePullPolicy: Always name: demo ports: - containerPort: 8080 name: http protocol: TCP readinessProbe: initialDelaySeconds: 5 tcpSocket: port: 8080 timeoutSeconds: 5 resources: limits: cpu: "2" memory: 512Mi requests: cpu: 100m memory: 64Mi status: {} --- apiVersion: v1 kind: Service metadata: name: demo-svc spec: selector: app: demo ports: - protocol: TCP port: 80 targetPort: 8080 --- apiVersion: extensions/v1beta1 kind: Ingress metadata: name: demo labels: app: demo spec: rules: - host: app.demo.example.com http: paths: - backend: serviceName: demo-svc servicePort: 80 --- -
デプロイメント ページで、ターゲットアプリケーション
demoをクリックし、アクセス方法 タブをクリックします。アクセス方法 タブで、外部エンドポイントを取得します。
-
ローカルの
hostsファイルに次のエントリを追加します。<External endpoint address> app.demo.example.com -
ブラウザのアドレスバーに app.demo.example.com と入力します。
Hello World at 2021-12-06 20:51:36ページが表示され、アプリケーションが正常にデプロイされたことを示します。
-
トリガーを作成します。
-
[Deployments] ページで、ターゲットアプリケーション
demoの名前をクリックします。 -
アプリケーション詳細ページで、トリガー タブをクリックし、トリガーの作成 をクリックします。
-
トリガーの作成 ダイアログボックスで、アクション を 再デプロイ に設定し、OK をクリックします。
トリガー タブで、トリガー URL を取得します。
-
-
パイプラインを作成します。
-
Jenkins にログインします。
-
左側メニューで、[New Item] をクリックします。
-
項目名を入力し、[Pipeline] を選択して、[OK] をクリックします。
-
[Build Triggers] タブをクリックし、[Generic Webhook Trigger] を選択します。
[汎用 Webhook トリガー] では、HTTP リクエストトリガー URL は
JENKINS_URL/generic-webhook-trigger/invoke形式です。このトピックでは、Generic Webhook tokenはhelloworld2021に設定されています。したがって、Webhook URL はJENKINS_URL/generic-webhook-trigger/invoke?token=helloworld2021です。 -
[Pipeline] タブをクリックし、実際の情報に基づいて次のテンプレートの汎用 Webhook トークンとトリガー URL を変更し、内容をテキストボックスに貼り付けてから [Save] をクリックします。
pipeline { agent any triggers { GenericTrigger( genericVariables: [ [key: 'InstanceId', value: '$.data.InstanceId'], [key: 'RepoNamespaceName', value: '$.data.RepoNamespaceName'], [key: 'RepoName', value: '$.data.RepoName'], [key: 'Tag', value: '$.data.Tag'] ], causeString: 'Triggered on $ref', token: 'helloworld2021', # 汎用 Webhook トークン。実際のトークンに置き換えてください。 tokenCredentialId: '', printContributedVariables: true, printPostContent: true, silentResponse: false, regexpFilterText: '$ref' ) } stages { stage('Some step') { steps { sh "echo 'will print post content'" sh "echo $InstanceId" sh "echo $RepoNamespaceName" sh "echo $RepoName" sh "echo $Tag" sh "echo 'redeploy to ACK or you can deoloy to other platforms use before message'" sh "curl 'https://cs.console.alibabacloud.com/hook/trigger?token=g****' # トリガー URL。実際の URL に置き換えてください。 sh "echo 'done'" } } } }
-
-
配信チェーンを作成します。詳細については、「配信チェーンの作成」をご参照ください。
-
イベントルールを作成します。
-
Enterprise Edition インスタンス管理ページの左側メニューで、 を選択します。
-
イベントルール タブで、ルールの作成 をクリックします。
-
イベントの範囲 ステップで、ルール名 を設定します。イベントタイプ を デリバリーチェーンを処理しました。 に設定し、成功 を選択し、適用範囲 を 名前空間 に設定し、名前空間として [ns] を選択して、次へ をクリックします。
-
インシデント通知 ステップで、通知方法 を [HTTP] に設定し、手順 3 で取得した Webhook URL を入力して、保存 をクリックします。
-
-
次のコマンドを実行して HelloController.java ファイルを変更し、コードを GitLab にコミットします。これにより、イメージビルドがトリガーされます。
vim java/com/example/demo/HelloController.java # 必要に応じて、ローカルマシンの HelloController.java ファイルを変更します。 git add . && git commit -m 'add update' && git push origin # ファイルを GitLab にコミットします。イメージをビルドすると、配信チェーンがトリガーされます。配信チェーンが完了すると、ACK の Deployment がトリガーされ、新しいイメージがプルされてアプリケーションが再デプロイされます。
-
次のコマンドを実行して、アプリケーションが再デプロイされたことを確認します。
curl app.demo.example.com変更された出力は、アプリケーションが正常に再デプロイされたことを示しています。