CI/CDを使ってみよう

2020年9月11日(金)
山田 達也(やまだ たつや)

はじめに

前回は、作成したアプリケーションをマイクロサービスとして連携する方法を紹介しました。

今回は、作成したサービスをOpenShiftが提供するCI/CDツールを用いて、外部のイベントを契機にビルド・デプロイを動的に実行する方法を解説します。

CI/CDとは

昨今では、ビジネスの変化に対応するため、アプリケーションの改善およびリリースサイクルを継続的かつ迅速に回していくことが重要となっています。

それを実現していくために必要な手法としてCI/CDがあります。CI/CDは「継続的インテグレーション/継続的デリバリ(Continuous Integration/Continuous Delivery)」の略で、ソフトウェア開発におけるビルド、テスト、デプロイ等の作業を自動化し、アプリケーションのリリース頻度を高める手法です。これらの作業を自動化することで人に起因するミスを防げるため、品質担保しつつ、アプリケーションのライフサイクルを迅速に回し、ビジネスの変化への柔軟な対応が見込めます。

これらアプリケーションのライフサイクルに必要な作業を一連の定型作業としてまとめ、定義したものを「CI/CDパイプライン」と呼びます。

CI/CDパイプラインを作成してみよう

前回は、2つのサービスを手動でビルド・デプロイしましたが、今回はCI/CDパイプラインを用いて自動でビルド・デプロイしてみます。CI/CDパイプラインの作成および実行方法と、サービス(sample)のソースコードの改変をトリガにサービス(sample)のビルド・デプロイを動的に実施する仕組みを説明します。

なお、一般的にはテスト・検証のフェーズを含めてパイプラインを構築しますが、内容簡易化のため割愛しています。また、今回はテスト・検証環境を想定した内容としています。

今回のCI/CDパイプラインの全体概要は下図の通りです。

コンテナ管理ツールが提供する
CI/CDツール「Tekton」

コンテナ管理基盤のCI/CDツールとして注目を集めているのが「Tekton」です。

Tektonは、元々Knative(Serverless)のプロジェクトの一部として始まりました。現在は「Tekton Piplines」としてThe Linux Foundationの傘下のCDF(Continous Delivery Foundation)というCI/CDを標準化する組織で開発が行われているツールの1つです。

Tektonの特徴として、下記のような点がが挙げられます。

  • Kubernetes上でのCI/CDパイプラインを構築するためのフレームワークを提供
  • Kubernetes環境であればCI/CDが可能なため、他Kubernetes上への移植や再現性が高い
  • Tektonで利用するCRDはビルディングブロックとして組み合わせ可能なため、再利用性が高い
  • パイプライン起動時のみコンテナ生成するため、効率的にリソース活用が可能

今回は、コンテナ管理基盤としてRed Hat OpenShift Container Platform(以下、OpenShift v4.5)に同梱されているTektonベースのOpenShift Pipelineを使用します。

環境の準備

今回は、下記の環境でCI/CDパイプラインを構築していきます。

  • Red Hat OpenShift Container Platform v4.5(k8s 4.18.0.-193.13.2)
    ⇒ 作成したサービスおよびTekton(OpenShift Pipeline)が稼働するコンテナ管理基盤
  • GitHub Enterprise Server(v2.20.5)(以下GHES)
    ⇒ 作成したサービスのソースコードおよびTektonのCRDなどを格納するSCM(Source Code Management)として利用
    ※ GitHub.com、Gitlab等でも代替可能
  • 作業サーバ
    ⇒ oc(OpenShiftのCLI)やtkn(TektonのCLI)コマンドなどを実施するサーバ
    ※OpenShift及びGHESインストールについては割愛

Tekton(OpenShift Pipeline)のインストール

まず、Tekton(OpenShift Pipeline)をインストールします。Tekton(OpenShift Pipeline)はOpenShift Container Platform Operator Hubから簡単にインストールできます。

作業サーバからOpenShiftのWebコンソールにログインし、Administratorのパースペクティブから「Operators」→>「OperatorHub」を選択し、検索ボックスに「OpenShift Pipelines Operator」と入力すると、OpenShift Pipelines Operatorが表示されるのでクリックします(※OpenShiftドキュメントにも記載がありますが、Communnity版は選択しないでください)。

OpenShift Pipelines Operatorの画面が表示されるので、「Install」をクリックします。続いて表示される「Install Operator」画面で「Install」をクリックします。

インストール後、「Operators」→「Installed Operators」からインストールされた「OpenShift Pipelines Operator」を選択し、「CLI(tkn)」からTektonのCLIをダウンロードしてください。本環境ではLinux版をダウンロードして作業サーバでアーカイブを展開し、PATHのあるディレクトリに配置します。

$ tar zxvf 
$ sudo mv tkn /usr/bin/

以上で、Tekton(OpenShift Pipeline)のインストールは完了です。なお、インストール方法はOpenShiftドキュメントにも記載されているので活用ください。

Tekton概要

ここで、これから操作していくTektonについて説明します。Tektonでは、パイプラインの構築に必要なリソースをCRD(Custom Resource Definition)として作成し、これを元にパイプラインを構築および実行していきます。

作成するCRDの種別は下記の通りです。

CRD 説明
Task コードのコンパイル、イメージのビルドやデプロイ等を定義
Pipeline 1つまたは複数のTaskをまとめて定義
PipelineResource Task、Pipelineで利用する入力(ex: Gitリポジトリ)および出力(ex: ビルドされるコンテナイメージ)として利用するリソースを定義
PipelineRun 定義したPipelineを実行する際に利用
EventListener 動的にPipelineを実行するための、外部イベントを受け取るPodを定義
TriggerBinding TriggerTemplateで利用するパラメータを定義
TriggerTemplate EventListenerが受け取った外部イベントを契機に、実行するパイプラインを定義

Tektonおよび各CRDの詳細設定についてはTekton Documentationなどを参考にしてください。

それでは、実際にTask、Pipeline、PipelineResourceの3つを作成し、パイプラインを構築および実行(ビルド・デプロイ)していきます。Task、Pipeline、PipelineResourceの関係性は下記図の通りです。

今回利用するソースコードおよび内容については、第3回を参照ください。

Step1: Taskの作成

続いて、サービス(sample)とサービスを呼び出すための呼び出し元サービス(caller)をビルド・デプロイするTaskを作成していきます。今回は、これらサービスをmvnでビルドおよびデプロイするパイプラインを作成します。

サービス(sample)のTask

mvnコマンドを実行するためのPodの情報とそのPod内で実行する処理内容をspec.stepsで定義します。本Taskではビルドで利用するPodとして"maven:3.6.0-jdk-8-slim"を用いて、そのPod内の処理内容(コマンド)の"/usr/bin/mvn -f /workspace/source/pom.xml clean fabric8:deploy -Popenshift"を実行しています。Tektonでは、spec.inputs.resourcesで定義したリソース(ここではsampleのソースコードがあるgitレポジトリ)をmaven Pod内の/workspace/sourceというディレクトリにcloneし、pom.xmlを元にビルド・デプロイします。サービス(sample)のTaskのファイル名は「01_sample_task.yaml」として作成します。

apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: sample-build
spec:
  inputs:
    resources:
    - name: source
      type: git
  steps:
  - name: build
    image: maven:3.6.0-jdk-8-slim
    command:
    - /usr/bin/mvn
    args:
    - "-f"
    - "/workspace/source/pom.xml"
    - "clean"
    - "fabric8:deploy"
    - "-Popenshift"

呼び出し元サービス(caller)のTask

呼び出し元サービス(caller)には、サービス(sample)を呼び出す際に使用するライブラリ(sample-api-client)も必要です。本Taskではライブラリ(sample-api-client)をビルドし、ビルドしたライブラリを用いて呼び出し元サービス(caller)を同一のmaven Podでビルドしています。呼び出し元サービス(caller)のTaskのファイル名は「01_api_caller_task.yaml」として作成します。

apiVersion: tekton.dev/v1alpha1
kind: Task
metadata:
  name: sample-api-caller
spec:
  inputs:
    resources:
    - name: source
      type: git
  steps:
  - name: registry
    image: maven:3.6.0-jdk-8-slim
    command:
    - /usr/bin/mvn
    args:
    - "-f"
    - "/workspace/source/sample-api-client/pom.xml"
    - "install"
  - name: build
    image: maven:3.6.0-jdk-8-slim
    command:
    - /usr/bin/mvn
    args:
    - "-f"
    - "/workspace/source/caller/pom.xml"
    - "clean"
    - "fabric8:deploy"
    - "-Popenshift"

以上で、Taskの作成は終了です。

Step2: Pipelineの作成

次に、作成したTaskを実行していくためのPipelineを定義します。本記事では、サービス(sample)と呼び出し元サービス(caller)の2つのPipelineを定義します。

サービス(sample)のPipeline定義

Pipelineでは、spec.resourcesで利用するリソース(git、image)を定義し、関連するTask名(サービス(sample)のTask名)をspec.tasksで定義します。サービス(sample)のPipelineのファイル名は「02_sample_pipeline.yaml」として作成します。

apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
  name: maven-build-sample
spec:
  resources:
  - name: git-repo
    type: git
  - name: image
    type: image
  tasks:
  - name: sample-build
    taskRef:
      name: sample-build
    resources:
      inputs:
      - name: source
        resource: git-repo

呼び出し元サービス(caller)のPipeline定義

呼び出し元サービス(caller)の場合は、呼び出し元サービス(caller)のTask名をspec.taskで定義します。呼び出し元サービス(caller)のPipelineのファイル名は「02_api_caller_pipeline.yaml」として作成します。

apiVersion: tekton.dev/v1alpha1
kind: Pipeline
metadata:
  name: maven-build-api-and-caller
spec:
  resources:
  - name: git-repo
    type: git
  - name: image
    type: image
  tasks:
  - name: sample-api-caller
    taskRef:
      name: sample-api-caller
    resources:
      inputs:
      - name: source
        resource: git-repo

以上で、Pipelineの作成は終了です。

Step3: PipelineResourceの作成

次に、利用するリソースを定義していきます。本記事では、すでにGHES上にサービス(sample)と呼び出し元サービス(caller)およびライブラリ(sapmle-api-client)のソースコードをpushしているので、それらのURLを指定しています。そして、imageとしてs2iで作成されるイメージをそれぞれ指定します。

今回は「sample-cicd」というプロジェクトに作成します(※今回はSSLを無効にしてGHESのリソースをcloneしているため、sslVerifyをfalseとして設定しています)。PipelineResourceのファイル名は「03_resources.yaml 」として作成します。

---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: sample-repo
spec:
  type: git
  params:
  - name: url
    value: 
  - name: sslVerify
    value: "false"
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: sample-image
spec:
  type: image
  params:
  - name: url
    value: image-registry.openshift-image-registry.svc:5000/sample-cicd/sample:1.0.0
---
apiVersion: tekton.dev/v1alpha1
kind: PipelineResource
metadata:
  name: caller-repo
spec:
  type: git
  params:
  - name: url
    value: 
  - name: sslVerify
    value: "false"
---
kind: PipelineResource
metadata:
  name: caller-image
spec:
  type: image
  params:
  - name: url
  value: image-registry.openshift-image-registry.svc:5000/sample-cicd/caller:1.0.0

以上で、PipelineResourceの作成は終了です。

手動によるPipelineの実行

作成したCRDを組み合わせて、パイプラインが実行可能かを手動で確認していきます。作成したTask、Pipeline、PipelineResourceをocコマンド用いて作成していきます。

$ oc create -f 01_sample_task.yaml -n sample-cicd
$ oc create -f 01_api_caller_task.yaml -n sample-cicd
$ oc create -f 02_sample_pipeline.yaml -n sample-cicd
$ oc create -f 02_api_caller_pipeline.yaml -n sample-cicd
$ oc create -f 03_resources.yaml -n sample-cicd

作成した各CRDはtknコマンドで確認できます。

$ tkn task ls -n sample-cicd
NAME                DESCRIPTION   AGE
sample-api-caller                 36 seconds ago
sample-build                      36 seconds ago
$ tkn pipeline ls -n sample-cicd
NAME                         AGE              LAST RUN   STARTED   DURATION   STATUS
maven-build-api-and-caller   36 seconds ago    ---        ---       ---        ---
maven-build-sample           36 seconds ago    ---        ---       ---        ---
$ tkn resource ls -n sample-cicd
NAME           TYPE    DETAILS
caller-repo    git     url: 
sample-repo    git     url: 
caller-image   image   url: image-registry.openshift-image-registry.svc:5000/sample-cicd/caller:1.0.0
sample-image   image   url: image-registry.openshift-image-registry.svc:5000/sample-cicd/sample:1.0.0

パイプラインを実行していくには、Pipeline名とPipelineResourceで定義したgitの名前およびimage名を用いる必要があります。

$ tkn pipeline start < Pipeline名 > -r git-repo=< PipelineResourceで定義したgit名 > -r image=< PipelineResourceで定義したimage名 > -n <プロジェクト名>

実際の実行内容は、下記の通りです。

$ tkn pipeline start maven-build -r git-repo=sample-repo -r image=sample-image -n sample-cicd
$ tkn pipeline start maven-build-api-and-caller -r git-repo=caller-repo -r image=caller-iamge -n sample-cicd

Pipelineを実行すると下記のメッセージが出力され、パイプラインの実行ログを確認できます。

Pipelinerun started: maven-build-run-xxx

In order to track the pipelinerun progress run:
tkn pipelinerun logs maven-build-run-xxx -f -n sample-cicd

$ tkn pipelinerun logs maven-build-run-xxx -f -n sample-cicd

処理が成功していると、下記のようにPod等のリソースが作成されます。

$ oc get all -n sample-cicd
NAME                                                                  READY   STATUS      RESTARTS   AGE
pod/caller-1-deploy                                                   0/1     Completed   0          103s
pod/caller-1-qkvsq                                                    1/1     Running     0          101s
pod/caller-s2i-1-build                                                0/1     Completed   0          2m44s
pod/maven-build-api-and-caller-run-wnxfx-sample-api-caller-l6-2bkgx   0/3     Completed   0          6m5s
pod/maven-build-run-8lmtn-sample-build-deploy-l2bdv-pod-6xtgg         0/2     Completed   0          6m8s
pod/sample-1-deploy                                                   0/1     Completed   0          2m15s
pod/sample-1-v52fx                                                    1/1     Running     0          2m
pod/sample-s2i-1-build                                                0/1     Completed   0          3m12s

NAME                             DESIRED   CURRENT   READY   AGE
replicationcontroller/caller-1   1         1         1       103s
replicationcontroller/sample-1   1         1         1       2m15s

NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
service/caller   ClusterIP   172.30.93.36            8080/TCP   104s
service/sample   ClusterIP   172.30.194.53           8080/TCP   2m16s

NAME                                        REVISION   DESIRED   CURRENT   TRIGGERED BY
deploymentconfig.apps.openshift.io/caller   1          1         1         config,image(caller:1.0.0)
deploymentconfig.apps.openshift.io/sample   1          1         1         config,image(sample:1.0.0)

NAME                                        TYPE     FROM     LATEST
buildconfig.build.openshift.io/caller-s2i   Source   Binary   1
buildconfig.build.openshift.io/sample-s2i   Source   Binary   1

NAME                                    TYPE     FROM     STATUS     STARTED         DURATION
build.build.openshift.io/sample-s2i-1   Source   Binary   Complete   3 minutes ago   54s
build.build.openshift.io/caller-s2i-1   Source   Binary   Complete   2 minutes ago   59s

NAME                                    IMAGE REPOSITORY                                                         TAGS    UPDATED
imagestream.image.openshift.io/caller   image-registry.openshift-image-registry.svc:5000/sample-cicd/caller   1.0.0   About a minute ago
imagestream.image.openshift.io/sample   image-registry.openshift-image-registry.svc:5000/sample-cicd/sample   1.0.0   2 minutes ago

NAME                              HOST/PORT                                                        PATH   SERVICES   PORT   TERMINATION   WILDCARD
route.route.openshift.io/caller   caller-xxx          caller     8080                 None
route.route.openshift.io/sample   sample-xxx          sample     8080                 None

呼び出し元サービス(caller)のrouteに、curlコマンドでサービスを実行します。

$ curl http://caller-xxx/api/caller/apply -v -X POST -H "Content-
Type:application/json" -d "{\"user_name\":\"Taro\", \"store_id\":1111, \"store_name\":\"shop_A\"}"
…
{"user_id":1}

第3回と同様の結果が得られているので、パイプラインの作成は成功です。

ここまで、パイプラインの構築方法およびその実行方法を解説しました。パイプラインの実行により定型作業をまとめて実行できるようになりましたが、アプリケーションを更新する度にPipelineを手作業で実施するには手間が掛かります。

そこで、次はソースコード変更のpushを契機に、自動でサービス(sample)をGHESにローリングアップデートするEvent Triggerについて解説します。

Event Triggerの作成

外部イベントの発生と共にパイプラインを自動で実行するEvent Triggerを設定します。今回は、GHES上のサービス(sample)のレポジトリへソースコードをpushした場合に、自動でパイプラインが実行されるように設定します。

Event TriggerはTriggerTemplate、TriggerBinding、EventListenerの3つから構成されています。これら3つの関係性は下図の通りです。

Step4: TriggerTemplateの作成

TriggerTemplateは、外部イベントの取得時にどのようにPipeline実行するかを定義します。今回は受け取るパラメータを定義し、定義したパラメータを使用したPipelineResourceとPipelineRunを定義します。TriggerTemplateのファイル名は「04_template.yaml」として作成します。

apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerTemplate
metadata:
  name: sample
spec:
  params:
  - name: git-repo-url
    description: The git repository url
  - name: git-revision
    description: The git revision
    default: master
  - name: git-repo-name
    description: The name of the deployment to be created/patched

  resourcetemplates:
  - apiVersion: tekton.dev/v1alpha1
    kind: PipelineResource
    metadata:
      name: $(params.git-repo-name)-git-repo-$(uid)
    spec:
      type: git
      params:
      - name: revision
        value: $(params.git-revision)
      - name: url
        value: $(params.git-repo-url)
      - name: sslVerify
        value: "false"

  - apiVersion: tekton.dev/v1alpha1
    kind: PipelineResource
    metadata:
      name: $(params.git-repo-name)-image-$(uid)
    spec:
      type: image
      params:
      - name: url
        value: image-registry.openshift-image-registry.svc:5000/sample-cicd/$(params.git-repo-name):latest

  - apiVersion: tekton.dev/v1beta1
    kind: PipelineRun
    metadata:
      name: maven-build-sample-$(params.git-repo-name)-$(uid)
    spec:
      serviceAccountName: pipeline
      pipelineRef:
        name: maven-build-sample
      resources:
      - name: git-repo
        resourceRef:
          name: $(params.git-repo-name)-git-repo-$(uid)
      - name: image
        resourceRef:
          name: $(params.git-repo-name)-image-$(uid)
      params:
      - name: deployment-name
        value: $(params.git-repo-name)

Step5: TriggerBindingの作成

TriggerBindingは、TriggerTemplateのパラメータで利用する値を定義しています。今回のTriggerBindingでは、トリガーとなるイベントの値(GHESからのWebhook)と、TriggerTemplateで利用する名前を定義しています。TriggerBindingのファイル名は「05_binding.yaml」として作成します。

apiVersion: triggers.tekton.dev/v1alpha1
kind: TriggerBinding
metadata:
  name: sample
spec:
  params:
  - name: git-repo-url
    value: $(body.repository.url)
  - name: git-repo-name
    value: $(body.repository.name)
  - name: git-revision
    value: $(body.head_commit.id)

Step6: EventListenerの作成

EventListenerは、外部からのWebhookリクエストを受け取るPodとして稼働します。TriggerBindingとTriggerTemplateとを紐づけるように定義します。EventListenerのファイル名は「06_event_listener.yaml」として作成します。

apiVersion: triggers.tekton.dev/v1alpha1
kind: EventListener
metadata:
  name: sample
spec:
  serviceAccountName: pipeline
  triggers:
  - bindings:
    - name: sample
    template:
      name: sample

それでは、TriggerBinding、TriggerTemplateおよびEventListenerを作成していきます。EventListener作成後GHESから疎通可能にするため、作成されたServiceをRouteに紐づけします。

$ oc create -f 04_template.yaml -n sample-cicd
$ oc create -f 05_binding.yaml -n sample-cicd
$ oc create -f 06_event_listener.yaml -n sample-cicd
$ oc expose svc el-sample -n sample-cicd
$ oc get route -n sample-cicd
NAME                                 HOST/PORT                                                           PATH   SERVICES    PORT            TERMINATION   WILDCARD
route.route.openshift.io/caller      caller-xxx             caller      8080                          None
route.route.openshift.io/el-sample   el-sample-xxx          el-sample   http-listener                 None
route.route.openshift.io/sample      sample-xxx            sample      8080                          None

作成したRouteのURLをGHESのWebhookに設定します。今回は、サービス(sample)リポジトリがソースコードの変更(git push相当)を検知したら、サービス(sample)のパイプラインが動的に実行されるように設定します。

Webhookの設定は、GHES上のサービス(sample)のレポジトリで「Settings」→「Hooks」の「Add webhook」で作成します。「Payload URL」にEventListener作成時に構築したRouteを指定します。

以上で、Event Triggerの設定は完了です。

外部イベントを契機にした
自動Pipelineの実行

それでは、外部イベント契機で動的にビルド・デプロイするか確認していきます。今回のサービス(sample)は{user_id:1}を返すようにsrc\main\java\com\example\samplesystem\sample\service\SampleService.javaにてハードコードしています。そのため、返す値を下記の通り適当な数値に変更し「Commit changes」します。

Commit changesされたことを契機に、自動でパイプラインが実施されます。

$ tkn pipelinerun ls -n sample-cicd
NAME                                   STARTED         DURATION    STATUS
maven-build-sample-sample-4x88r        9 seconds ago   ---         Running
maven-build-api-and-caller-run-djr78   7 minutes ago   4 minutes   Succeeded
maven-build-sample-run-vbp45           8 minutes ago   4 minutes   Succeeded

ビルド・デプロイが完了すると、これまで"sample-1-v52fx"で稼働していたPodが"sample-3-wscsn"として稼働(ローリングアップデート)していることが分かります。

$ oc get pod -n sample-cicd
NAME                                                                  READY   STATUS      RESTARTS   AGE
pod/caller-1-deploy                                                   0/1     Completed   0          7m6s
pod/caller-1-qqx98                                                    1/1     Running     0          7m4s
pod/caller-s2i-1-build                                                0/1     Completed   0          8m
pod/el-sample-684bb5db4-vb654                                         1/1     Running     0          6m4s
pod/maven-build-api-and-caller-run-djr78-sample-api-caller-bu-s9hfv   0/3     Completed   0          11m
pod/maven-build-sample-run-vbp45-sample-build-k8tjq-pod-hp6j8         0/2     Completed   0          13m
pod/maven-build-sample-sample-4x88r-sample-build-pbwkp-pod-mv64m      0/2     Completed   0          4m30s
pod/sample-1-deploy                                                   0/1     Completed   0          8m58s
pod/sample-3-deploy                                                   0/1     Completed   0          32s
pod/sample-3-wscsn                                                    1/1     Running     0          28s
pod/sample-s2i-1-build                                                0/1     Completed   0          9m53s
pod/sample-s2i-2-build                                                0/1     Completed   0          93s

最後に、呼び出し元サービス(caller)のrouteに対して同様にcurlを実施します。

$ curl http://caller-xxx/api/caller/apply -v -X POST -H "Content-
Type:application/json" -d "{\"user_name\":\"Taro\", \"store_id\":1111, \"store_name\":\"shop_A\"}"
…
{"user_id":39}

{"user_id":39}と最新のソースコードで稼働していることがわかります。

おわりに

今回は、Tekton(OpenShift Pipeline)を用いて、第3回に紹介したサービスをビルド・デプロイするCI/CDパイプラインの作成およびソースコードpush契機でパイプラインを自動で実施する一例を紹介しました。

今回はマイクロサービスの開発部分を中心に説明してきましたが、サービスが増えていくにつれてサービス全体の処理の流れの把握やサービス間の認証・認可の実装等が難しくなります。これらを解決するためのアプローチの1つとしてサービスメッシュがあります。

次回は、サービスメッシュを使ったマイクロサービスの運用について解説します。

著者
山田 達也(やまだ たつや)
株式会社日立製作所 プラットフォームサービス部
株式会社日立製作所 プラットフォームサービス部所属。これまでにOpenStackの評価やテクニカルサポートに従事。近年ではOpenShiftのテクニカルサポートや関連OSSの評価にも従事。

連載バックナンバー

Think ITメルマガ会員登録受付中

Think ITでは、技術情報が詰まったメールマガジン「Think IT Weekly」の配信サービスを提供しています。メルマガ会員登録を済ませれば、メルマガだけでなく、さまざまな限定特典を入手できるようになります。

Think ITメルマガ会員のサービス内容を見る

他にもこの記事が読まれています