このチュートリアルでは、ソースコードからイメージをビルドし、リポジトリにプッシュしてアプリケーションをデプロイする完全自動化ワークフローの作成方法を説明します。GitLab、Container Registry (ACR)、および Container Service for Kubernetes (ACK) と Jenkins を組み合わせることで、ソースコードがコミットされた際に自動的にイメージをビルドし、アプリケーションをデプロイし、DingTalk グループにイベント通知を送信できます。
前提条件
Git、GitLab、および Jenkins がインストール済みです。
説明GitLab には JDK 8 が必要です。一部のプラグインは JDK 11 では実行されません。
ACR Enterprise Edition インスタンスを作成済みで、インターネットアクセスが有効になっています。詳細については、「Enterprise Edition インスタンスの作成」および「インターネットアクセスの設定」をご参照ください。
ご利用の ACR インスタンスと同じリージョンに ACK クラスターを作成済みです。詳細については、「ACK マネージドクラスターの作成」をご参照ください。
DingTalk チャットボットを作成済みで、その Webhook URL およびシークレットトークンを記録済みです。詳細については、「ステップ 1:DingTalk チャットボットの作成」をご参照ください。
Container Registry (ACR) のデリバリーチェーン機能を使用するには、インスタンスをアドバンストエディションにスペックアップする必要があります。詳細については、「課金」をご参照ください。
Jenkins を使用したイメージの CI
GitLab にソースコードをコミットすると、Container Registry (ACR) が自動的にイメージをビルドします。その後、そのイメージに対してセキュリティスキャンを実行できます。スキャンが完了すると、イベント通知が 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 をクリックします。

ローカルコンピューター上で、次の内容を使用して 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/> <!-- lookup parent from repository --> </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 ページで、認証情報の ID が自動生成されます。この ID は後ほど使用するため、保存しておいてください。
パイプラインを作成します。
Jenkins ダッシュボードの左側ナビゲーションウィンドウで、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 コンソール にログインします。
上部ナビゲーションバーで、リージョンを選択します。
左側のナビゲーションウィンドウで、Instances をクリックします。
Instances ページで、管理対象の 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 をクリックします。リポジトリ ページで、左側ナビゲーションの タグ をクリックします。タグ ページにイメージが生成されていることを確認できます。
セキュリティスキャンを設定します。
セキュリティスキャン ページで、対象のイメージタグを見つけ、セキュリティスキャン 列の セキュリティスキャン をクリックします。
セキュリティスキャン ページで、スキャン をクリックします。
セキュリティスキャンが完了すると、DingTalk グループに通知が届きます。
Jenkins を使用したイメージの CD
GitLab へのソースコードのコミットにより、Container Registry (ACR) で自動的にイメージがビルドされます。ビルド完了によりデリバリーチェーンがトリガーされます。デリバリーチェーンが完了すると、HTTP リクエストが Jenkins に送信され、ACK デプロイメントが最新のイメージをプルしてアプリケーションを再デプロイします。
アプリケーションを作成します。
Container Service 管理コンソール にログインします。
左側ナビゲーションウィンドウで、クラスター をクリックします。
クラスターリスト ページで、送信先クラスターの名前をクリックするか、詳細 を 操作 列でクリックします。
クラスター管理ページの左側ナビゲーションウィンドウで、 を選択します。
デプロイメント ページで、名前空間 を選択し、YAML のリソースの作成 をクリックします。
YAML のリソースの作成 パネルで、サンプルテンプレート を カスタム に設定します。次のコードをテンプレートにコピーし、デプロイ をクリックします。
説明パスワード不要でのイメージプルを構成していない場合は、Container Registry (ACR) のインターネットアクセスを有効にし、イメージリポジトリを公開にする必要があります。詳細については、「インターネットアクセスの設定」をご参照ください。
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 を入力します。
上記のページが表示されれば、アプリケーションが正常にデプロイされています。
トリガーを作成します。
Deployments ページで、
demoアプリケーションをクリックします。アプリケーション詳細ページで、トリガー タブをクリックし、トリガーの作成 をクリックします。
トリガーの作成 ダイアログボックスで、アクション を 再デプロイ に設定し、OK をクリックします。
トリガー タブで、トリガー URL を取得します。
パイプラインを作成します。
Jenkins にログインします。
左側のナビゲーションウィンドウで、New Item をクリックします。
アイテム名を入力し、Pipeline を選択して、OK をクリックします。
Build Triggers タブをクリックし、Generic Webhook Trigger を選択します。
Generic Webhook Trigger セクションで、HTTP リクエストトリガー URL は
JENKINS_URL/generic-webhook-trigger/invokeです。本チュートリアルでは、Generic Webhook tokenはhelloworld2021に設定されています。したがって、Webhook URL はJENKINS_URL/generic-webhook-trigger/invoke?token=helloworld2021になります。Pipeline タブをクリックします。以下のテンプレートで、プレースホルダーの Generic 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', # Generic 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'" } } } }
デリバリーチェーンを作成します。詳細については、「デリバリーチェーンの作成」をご参照ください。
イベントルールを作成します。
ACR 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 デプロイメントがトリガーされ、新しいイメージをプルしてアプリケーションを再デプロイします。
次のコマンドを実行して、アプリケーションが再デプロイされたことを確認します。
curl app.demo.example.com出力が変化すれば、アプリケーションが正常に再デプロイされています。