APC 技術ブログ

株式会社エーピーコミュニケーションズの技術ブログです。

株式会社 エーピーコミュニケーションズの技術ブログです。

【OCI】OCI DevOpsを使用してKubernetesのCI/CDパイプラインを作ってみた

目次

はじめに

こんにちは、クラウド事業部の中本です。
今回は学習のため、OCI DevOpsを使用して、OKEで構築したKubernetesのCI/CDパイプラインを作ってみました。
OCI DevOpsを利用してみたい方、これからCI/CDを導入したい方等の参考になれば幸いです。
※導入手順がボリューミーなので、結果だけ見たい方はパイプラインを実行してみるまでスキップしてください!

OCI DevOpsとは

OCI上にCI/CD環境を構築するマネージドサービスです。
自前でCI/CD実行環境を用意せずとも、Oracleが管理する実行環境上でアプリのビルドやデプロイを自動化することができます。
例えばコードの変更をトリガーにしてDockerビルド、レジストリへのプッシュ、クラスタへのデプロイ…といった一連の流れをGUIベースで構築することができます。
もちろんOKEだけでなく、Oracle Functions やComputeインスタンス等で動くアプリのCI/CDパイプラインを作成することも可能です。

docs.oracle.com

前提環境

今回は、既にOKEクラスタ上でコンテナを稼働させている環境にOCI DevOpsを導入していきます。
そのため、まずは前提となる実行環境についてご紹介します。

  • Kubernetes実行環境:OKE
  • Image管理:OCIR(Oracleが提供するコンテナレジストリ)

各種資材

各ファイルは以下の通りです。

資材構成

OCI_DevOps_Test/
|-- app/
  └─ app.py
  └─ requirements.txt
|-- k8s/
  └─ deploy.yaml
|-- Dockerfile

※OCI DevOps導入時は、これに追加してビルド・パイプラインで必要になるbuild_spec.yamlというファイルを作成する必要があります。(後述)

app.py

PythonのFlaskを用いて、ポート5000で簡単なメッセージを返すWebアプリを使います。

from flask import Flask

app = Flask(__name__)

@app.route('/')
def flask_app():
    return '成功!'

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5000)

requirements.txt

flask

deploy.yaml

apiVersion: apps/v1
kind: Deployment
metadata:
  name: testapp-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: testapp
  template:
    metadata:
      labels:
        app: testapp
    spec:
      containers:
        - name: testapp
          image: ap-tokyo-1.ocir.io/<object-storage-namespace>/test-repository:test-app
          ports:
            - containerPort: 5000
---
apiVersion: v1
kind: Service
metadata:
  name: testapp-service
spec:
  type: LoadBalancer
  selector:
    app: testapp
  ports:
    - protocol: TCP
      port: 52000
      targetPort: 5000

Dockerfile

FROM python:3.11.11-slim

WORKDIR /app
COPY ./app /app

RUN pip install -r requirements.txt

EXPOSE 5000

CMD ["python", "app.py"]

このように、Pythonで作成されたアプリをDockerイメージ化し、deploy.yamlを使ってOKEにデプロイする形を取っており、
何かアプリのコードを変更した際は、Dockerビルド→OCIRへプッシュ→OKEクラスタへのデプロイという手作業で変更を反映させることとなります。
この流れをOCI DevOpsによって、コードのpushをトリガーに、クラスタへのデプロイまで自動で行われるように実装していきます。

OCI DevOps導入手順

それではOCI DevOpsを導入していきます。
以下のOracle公式のチュートリアルを参考にしました。
oracle-japan.github.io

今回作る環境は以下の通りです。

流れは以下のようになっています。
1. ユーザがOCI DevOpsのCode Repositoryに資材をpushする
2. ビルド・パイプラインでイメージがビルドされ、コンテナレジストリにpushされる
※ローカル(DevOps実行環境)ではtestapp_image:latestという名前でビルドされ、レジストリに登録される際はリポジトリのパスとビルドIDに基づく動的なタグ名に置き換わる形になります。
3. デプロイメント・パイプラインでOKEにデプロイされる。
4. コンテナレジストリにあるImageをpullし、コンテナが起動する。

目次

構築手順の目次はこちらの通りです。
※今回は上述の既存環境にOCI DevOpsを導入するため、OKEやコンテナレジストリ等の設定手順は省きます。

OCI Notifications セットアップ

パイプライン実施成否を通知するための、OCI Notificationsサービスの「トピック」と「サブスクリプション」の設定を行います。
OCI DevOpsのプロジェクトを作成する際に、ここで作成したトピックを紐づける形となります。

トピックの作成

メニューの「開発者サービス」>「通知」からトピック管理画面に入り、「トピックの作成」を押下してトピックを作成します。

サブスクリプションの作成

メニューの「サブスクリプション」から管理画面に入り、「サブスクリプション作成」を押下してサブスクリプションを作成します。
トピックは先ほど作成したものを選択し、電子メールには通知先のメールアドレスを入れます。

作成すると、メールが届くので、本文に従って「Confirm subscription」のリンクを押下し、以下の画面に遷移したら完了です。

動的グループとポリシーの設定

OCI DevOpsがOKEやOCIRなど、他のリソースを利用できるようにするための設定です。
OCI DevOpsのリソースを動的グループに所属させ、そのグループにOKE等の各リソースを管理するポリシーを割り当てます。
メニューの「アイデンティティとセキュリティ」>「アイデンティティ」から、以下のように作成していきます。

OCI DevOps構築

それではOCI DevOpsの構築に入っていきます。

プロジェクト作成

まずはOCI DevOps内のリソースをまとめる大枠となる、プロジェクトを作成します。
メニューから「開発者サービス」>「DevOps」>「プロジェクト」を選択し、「DevOpsプロジェクトの作成」ボタンを押下して必要項目を入れます。
プロジェクト名に任意の名前を入れ、トピックは先ほど作成したものを指定します。
「DevOpsプロジェクトの作成」を押下し、ステータスがアクティブになったら完了です。

コード・リポジトリの作成

続いて、資材管理を行うコード・リポジトリを作成していきます。
画像の赤枠部分です。

githubやGitLab等の外部リポジトリとも連携が可能ですが、Vaultのキー管理が必要になるため、今回はシンプルにDevOps内のリソースであるコード・リポジトリを使用しました。
外部リポジトリとの連携手順は以下をご参照ください。

docs.oracle.com

リポジトリ作成

メニューから「開発者サービス」>「DevOps」>「プロジェクト」>先ほど作成したプロジェクトを選択し、「リポジトリの作成」ボタンをクリックします。
任意のリポジトリ名を入れたら、「リポジトリの作成」を押下して作成完了です。

ビルド用ファイルの作成

ここでビルド時に必要となる、ビルド処理を定義するファイルを作成します。
このファイルをリポジトリにアップロードし、後ほど作成するビルド・パイプラインで指定することで、このファイルを用いてビルドが実行されることとなります。

build_spec.yaml
version: 0.1
component: build
timeoutInSeconds: 10000
runAs: root
shell: bash

env:
  exportedVariables:
    - BUILDRUN_HASH

steps:
  - type: Command
    name: "Export Variables"
    command: |
      export BUILDRUN_HASH=$(echo ${OCI_BUILD_RUN_ID} | rev | cut -c 1-7)
      echo "BUILDRUN_HASH: " ${BUILDRUN_HASH}

  - type: Command
    name: "Docker Build"
    command: |
      docker build -t testapp_image .

  - type: Command
    name: "Trivy Image Scan"
    timeoutInSeconds: 180
    command: |
      curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin v0.21.0
      trivy image testapp_image
    onFailure:
      - type: Command
        command: |
          echo "Trivy Scan Error"
        timeoutInSeconds: 40
        runAs: root

outputArtifacts:
  - name: testapp_image
    type: DOCKER_IMAGE
    location: testapp_image:latest
(補足)ファイルの解説

このファイルでは主に以下の内容を定義しています。

  1. Dockerイメージのビルドと、セキュリティスキャン処理
  2. 1でビルドされたイメージをtestapp_imageという名前の成果物として生成

●1について
stepsセクション内で定義しています。
まず "Docker Build"ステップで、Dockerfileを使ってdocker buildコマンドでイメージをビルドします。タグは未指定のため、testapp_image:latestというイメージが生成されます。
続いて"Trivy Image Scan"ステップでは、Trivyというコンテナイメージの脆弱性スキャンを行うツールをインストールし、ビルドしたイメージの脆弱性スキャンを実行します。

●2について
outputArtifactsセクションで定義しています。
ここでは、1でビルドしたDockerイメージtestapp_image:latesttestapp_imageという名前の成果物として登録します。
この成果物をOCIRと関連付ける※ことで、ローカルでビルドされたイメージがOCIRにap-tokyo-1.ocir.io/<object-storage-namespace>/test-repository:${BUILDRUN_HASH}という名前で登録されることになります。
アーティファクトの配信ステップ作成参照

コードのプッシュ

各種資材と、上記のbuild_spec.yamlをコード・リポジトリにプッシュします。

リポジトリの詳細画面から「クローン」ボタンをクリックするとcloneのコマンドを取得することができるので、通常のgit操作と同様にリポジトリをcloneし、資材を準備してpushします。Cloud Shellで実行していきます。

cloneやpushする際のUsernameとPasswordは以下を入れます。
Username:<テナンシ名>/<ユーザ名>
 ※テナンシ名とユーザ名を/で繋げた形
Password:認証トークン
認証トークンについては以下をご参照ください。

docs.oracle.com

プッシュすると、以下のように資材が反映されます。

※k8s/deploy.yamlは別途アーティファクト・レジストリに登録して使うため、ここではプッシュしなくても問題ないと思います。

アーティファクトの設定

Dockerイメージやkubernetesのマニフェストファイルをアーティファクトとして登録し、OCI DevOps内で使用できるように紐づけます。

アーティファクト・レジストリの設定

アーティファクト・レジストリを設定し、OKEクラスタへデプロイする際に使用するdeploy.yamlを登録します。
メニューの「開発者サービス」>「アーティファクト・レジストリ」に入り、「リポジトリの作成」を押下してリポジトリを作成します。 不変アーティファクトにすることで、意図しない上書きを防ぐことができるようですが、今回はチェックを外しておきます。
作成ボタンを押下し、作成完了です。

deploy.yamlの修正とアップロード

ここでdeploy.yamlを一部修正します。
コンテナレジストリから引っ張ってくるイメージのタグを${BUILDRUN_HASH}に修正します。

...
    spec:
      containers:
        - name: testapp
          image: ap-tokyo-1.ocir.io/<object-storage-namespace>/test-repository:${BUILDRUN_HASH}
          ports:
            - containerPort: 5000
...

続いて、deploy.yamlをリポジトリにアップロードします。
リポジトリの詳細画面から「アーティファクトのアップロード」ボタンを押下します。 画像の通りにアーティファクト・パスとバージョンを入力します。
アップロード方法はコンソールからドラッグ&ドロップでも可能のようですが、今回はCloud Shellで実施しました。
Cloud Shellを選択するとコマンドが表示されるので、deploy.yamlのパスを--content-bodyに指定し、Cloud Shellで実行します。

xxx@cloudshell:artifact-k8s (ap-tokyo-1)$ oci artifacts generic artifact upload-by-path \
>   --repository-id ocid1.artifactrepository.oc1.ap-tokyo-1.0.xxx\
>   --artifact-path deploy.yaml \
>   --artifact-version 1 \
>   --content-body ./deploy.yaml
{
  "data": {
    "artifact-path": "deploy.yaml",
    "compartment-id": "ocid1.tenancy.oc1..xxx",
    "defined-tags": {
      "Oracle-Tags": {
        "CreatedBy": "xxx/xxx",
        "CreatedOn": "2025-06-14T17:47:53.410Z"
      }
    },
    "display-name": "deploy.yaml:1",
    "freeform-tags": {},
    "id": "ocid1.genericartifact.oc1.ap-tokyo-1.0.xxx",
    "lifecycle-state": "AVAILABLE",
    "repository-id": "ocid1.artifactrepository.oc1.ap-tokyo-1.0.xxx",
    "sha256": "xxx",
    "size-in-bytes": 581,
    "time-created": "2025-06-14T17:47:53.578566+00:00",
    "version": "1"
  }
}

コンソールに戻り、「閉じる」ボタンを押下します。
アップロードされたことを確認します。

アーティファクトの追加

ここではOCIRとアーティファクト・リポジトリを「アーティファクト」としてOCI DevOpsのプロジェクトと紐づけることで、OCI DevOpsでそれらが使えるように設定します。

プロジェクトの詳細画面から、「最新アーティファクト」>「アーティファクトの追加」を押下します。

まずはOCIRを追加していきます。
コンテナ・レジストリのイメージへの完全修飾パスは以下の通り入力します。

ap-tokyo-1.ocir.io/<object-storage-namespace>/test-repository:${BUILDRUN_HASH}

「追加」ボタンを押下して完了です。

続いて、アーティファクト・リポジトリを追加します。
再度「アーティファクトの追加」を押下し、以下のように設定します。 「アーティファクト・レジストリ・リポジトリ」にはアーティファクト・レジストリの設定で作成したリポジトリを選択し、「アーティファクト」にはdeploy.yamlの修正とアップロードでアップロードしたdeploy.yamlを選択します。
「追加」ボタンを押下します。

以下のように登録されていることを確認し、アーティファクトの登録は完了です。

デプロイメント・パイプライン作成

アーティファクト・レジストリと連携して、OKEクラスタにアプリを自動デプロイするためのパイプラインを作成します。

プロジェクトの詳細画面から、「最新のデプロイメント・パイプライン」>「パイプラインの作成」を押下します。

作成すると、以下のようにステージを追加する画面に遷移するので、「ステージの追加」を押下します。 タイプを選択するメニューが出てくるので、「Kubernetesクラスタにマニフェストを適用」を選択し、「次」を押下します。 詳細を設定する画面に遷移するので、以下のように設定します。 「アーティファクトの選択」では、アーティファクトの追加で追加したアーティファクト・リポジトリを選択します。
「追加」ボタンを押下し、以下のように反映されていることを確認して登録完了です。

ビルド・パイプライン作成

コード・リポジトリからソースをダウンロードして、コンテナイメージビルド→イメージをOCIRに格納→デプロイメント・パイプライン連携という一連の流れをビルド・パイプラインとして作成します。

作成するステージは上記赤枠の通り、

  • マネージド・ビルドステップ(container-image-build)
  • アーティファクトの配信ステップ(container-image-ship)
  • デプロイメントのトリガーステップ(connect-deployment-pipeline)

と3つあります。

マネージド・ビルドステップ作成

まずはマネージド・ビルドステップを作成します。
このステップでは、コード・リポジトリからソースをダウンロードして、コンテナイメージをビルドします。

プロジェクトの詳細画面から、「最新のビルド・パイプライン」>「パイプラインの作成」を押下します。
作成後、デプロイメント・パイプラインの作成時と同様にステップを追加する画面に遷移するので、ステージの追加を押下し、「マネージドビルド」を選択します。
以下のように設定します。
「プライマリ・コード・リポジトリ」はリポジトリ作成で作成したリポジトリを選択します。
「追加」ボタンを押下します。

アーティファクトの配信ステップ作成

続いて、アーティファクトの配信ステップを作成します。
このステップでは、testapp_imageというコンテナイメージ(成果物)をOCIRと関連付け、OCIRへpushします。

ステージ管理画面に戻り、container-image-buildの下の+マークから「ステージの追加」を選択します。
「アーティファクトの配信」を選択し、以下のように設定します。

「アーティファクトの選択」では、アーティファクトの追加で追加したOCIRを選択します。
「ビルド構成/結果アーティファクト名」に「testapp_image」と入力します。
「追加」ボタンを押下します。

デプロイメントのトリガーステップ作成

最後に、デプロイメントのトリガーステップを作成します。
このステップは、ビルド・パイプラインの結果をデプロイメント・パイプラインへ連携させるステップです。

ステージ管理画面に戻り、container-image-shipの下の+マークから「ステージの追加」を選択します。
「デプロイメントのトリガー」を選択し、以下のように設定します。

「デプロイメント・パイプラインの選択」では、デプロイメント・パイプライン作成で作成したデプロイメント・パイプラインを選択します。
「追加」ボタンを押下します。

3つのステップが作成されていれば完了です。

トリガー作成

最後に、コード・リポジトリへの「git push」コマンド実行をトリガーにビルド・パイプラインが実行されるように、トリガーの設定を行います。

プロジェクトの詳細画面から、「最新のトリガー」>「トリガーの作成」を押下します。
以下のように設定します。
「ソース接続」には「OCIコード・リポジトリ」を選択し、コード・リポジトリの作成で作成したコード・リポジトリを選択します。
「アクション」にはビルド・パイプライン作成で作成したビルド・パイプラインを選択し、イベントは「プッシュ」を選択します。 プル・リクエストの作成や更新も選択できるので、レビューや承認を前提とした開発プロセスにもCI/CDパイプラインを組み込むことができそうです。

「作成」ボタンをクリックし、ステータスがアクティブになったら作成完了です。

以上で環境構築は完了です!

パイプラインを実行してみる

それでは実際にコードを変更してpushし、パイプラインが実行されるか確認してみます。

app.pyを以下のように書き換え、Cloud Shellでgit pushします。

...
@app.route('/')
def flask_app():
    return '成功!(v2)'
...

pushされると、「最新のビルド履歴」からステータスを確認することができます。
※画像は既に成功している状態ですが、パイプライン実行中は「実行中」のステータスとなります。

「実行」列のビルド履歴をクリックすると、以下のようにステップごとの成否やログをリアルタイムで観察することができます。

デプロイメント・パイプラインも同様に確認することができます。

OKEクラスタへのデプロイ状況も確認してみましょう。
※エビデンスの取得が後日になったためAGEは時間が経っていますが、以下のように確認できます。

xxx@cloudshell:~ (ap-tokyo-1)$ kubectl get pods
NAME                                  READY   STATUS    RESTARTS   AGE
testapp-deployment-5477cfb547-9gwzk   1/1     Running   0          4d6h

xxx@cloudshell:~ (ap-tokyo-1)$ kubectl get services
NAME              TYPE           CLUSTER-IP      EXTERNAL-IP     PORT(S)             AGE
kubernetes        ClusterIP      10.96.0.1       <none>          443/TCP,12250/TCP   11d
testapp-service   LoadBalancer   10.96.107.102   xx.xx.xx.xx   52000:32174/TCP     5d6h

最後に、アプリへの疎通確認を行います。
EXTERNAL-IPでポート52000で外部公開しているため、http://<EXTERNAL-IP>:52000にブラウザからアクセスします。

返ってきました!

通知については以下のように、 ビルドとデプロイそれぞれの開始・終了のタイミングで届きます。

CI/CDを導入した感想

私自身、Kubernetes環境の運用経験はあるもののCI/CDの経験が無かったため、今回導入してみることで、以下のようなCI/CDのメリットを実感することができました。

運用が手軽になる

前提環境でも述べた通り、パイプライン導入前は手作業で実施していたDockerビルド → OCIRへプッシュ → OKEクラスタへのデプロイという流れが自動化されたため、単純に開発やリリースにかかる時間を減らすことができます。
特にアプリ開発者からすれば、コードを修正してpushするだけで変更が自動反映されるため、インフラ操作に気を取られず、開発に集中できるのが大きなメリットかと思いました。
頻繁なリリースが求められるアジャイル開発や、ちょっとした変更を逐一試すような個人開発では特に効果的かと思います。

人的ミスを減らすことができる

今まで手作業で行っていた部分が自動化され、誰が実施しても毎回同じ手順・同じ流れで処理されるようになるため、人的ミスを減らすことができます。
また、今回はTrivyというツールを使い、ビルドしたコンテナイメージの脆弱性スキャンを行うようにしましたが、そういったテストのプロセスを組み込むことで、品質を維持しやすい環境が整えられると感じました。

おわりに

いかがでしたでしょうか。
今回はOCI DevOpsを使って、シンプルなCI/CDパイプラインを構築し、その流れやメリットを実際に体感することができました。
構成としてはシンプルなビルド・デプロイでしたが、今後はステージング環境での事前デプロイや、承認ステップの導入等、実運用に近い構成についても学んでいきたいです。
長くなりましたが、お読みいただきありがとうございました!

お知らせ

私達クラウド事業部はクラウド技術を活用したSI/SESのご支援をしております。

www.ap-com.co.jp

また、一緒に働いていただける仲間も募集中です!
今年もまだまだ組織規模拡大中なので、ご興味持っていただけましたらぜひお声がけください。

www.ap-com.co.jp