概要
OptX は Newtroid 上で稼働するコンテナイメージです。 Newtroid では OptX を組み合わせてマイクロサービスアーキテクチャでシステムを構築、運用を行います。 このページでは OptX を開発し、Newtroid に登録し、実際に稼働させるまでの流れをチュートリアル形式で解説します。
まず初めに以下に CI/CD のアーキテクチャを記載します。
newtroid プラットフォームは一貫性のあるライフサイクルの開発環境を提供します。 開発した OptX は Github でソースコードを管理し、Jenkins によって OptX レジストリに登録されます。 登録された OptX は newtroid 上から起動できるようになります。
コンポーネント
API Server
- Github ... ソースコードのバージョン管理を行うクラウドサービス
- Jenkins ... ソースコードをビルドし、コンテナイメージにして OptX のレジストリにイメージを登録する CI/CD サービス。詳細はこちらをご覧ください。
- OptX レジストリ ... Newtroid で使用する OptX(コンテナイメージ)を登録・配布するサービス
OptX の開発の流れ
Step1: リポジトリの作成
Step2: OptX の開発
Step3: OptX の登録とリリース
- 3.1: OptX を新規作成する
- 3.2: Jenkins で Job を作成する
- 3.3: Job を実行する ここでは0から OptX を作成する流れを説明します。 OptX を開発する方法は、詳細な手順は順を追って次に解説していきます。 このチュートリアルでは例として、typescript のコンテナイメージを作成します。
まずは Github にリポジトリを作成します。Dockerfile を作成し、コンテナの開発環境を整えます。 VSCode で開発を行い、完成したらリポジトリを更新します。 リリースするには、newtroid にログインし、OptX を作成します。ソースコードに optx-conf.json を作成します。 ci フォルダに Jenkinsfile を作成します。 次に jenkins にログインし、ジョブを作成します。作成したら、パイプラインを実行します。 ビルドステータスが成功になっていれば登録完了となります。
画面からパイプラインを作成し、起動します。 ログビューアでログを見て完了です。
Step1:リポジトリの作成
ソースコードはゼロから作成しても構いませんがテンプレートを容易してますので、まずはそちらを使用することをおすすめします。 以下のコマンドで typescript のテンプレートをダウンロードできます。
git clone git@github.com:AllegroSmart-Inc/optx-sdk-ts.git my-first-optx
リモートリポジトリをあなたのリポジトリに設定してください
cd my-first-optx
rm -rf .git
git init
git remote add origin your-repogitory-url
git add .
git commit -m'init'
git push
Step2:OptX の開発
OptX として開発するのに特別なことはほとんど必要ありません。 通常、Docker イメージの開発と同様に行うことができます。
キーとなる構成ファイルを以下に示します。
- Dockerfile ... コンテナイメージのビルドに使用します。
- nJenkinsfile ... コンテナイメージのリリースに使用します。
- optx-conf.json ... newtroid 上で取り扱うためのファイルを設定します。
2.1 nJenkinsfile の編集
nJenkins ファイルは Jenkins のパイプラインスクリプト(groovy ベース)であり、OptX として newtroid 上に簡単にリリースできるようになっています。
ご自身の環境に合わせて以下の項目を修正してください。
変数名 | 説明 |
---|---|
NEWTROID_REGISTRY_URL | Docker イメージを管理する Docker Registry の URL |
NEWTROID_API_URL | OptX を登録する newtroid の URL |
DOCKER_FILE_PATH | ビルドする Dockerfile のパス。レポジトリのルートディレクトリからの相対パスを指定してください。 |
OPTX_CONF_PATH | 登録する OptX の optx-conf.json のパス。レポジトリのルートディレクトリからの相対パスを指定してください。 |
nJenkinsfile のテンプレート
以下のプログラムが nJenkins のテンプレートになります。
適宜、 NEWTROID_REGISTRY_URL
、NEWTROID_API_URL
、DOCKER_FILE_PATH
、OPTX_CONF_PATH
を変更してください。
import com.cloudbees.plugins.credentials.CredentialsProvider
import com.cloudbees.plugins.credentials.CredentialsMatchers
import com.cloudbees.plugins.credentials.common.StandardUsernameCredentials
import com.cloudbees.plugins.credentials.domains.DomainRequirement
import hudson.security.ACL
import jenkins.model.Jenkins
import org.jenkinsci.plugins.plaincredentials.StringCredentials
import java.util.regex.Matcher
import java.util.regex.Pattern
//newtroid.ioリリース用
private void loadVarsFromFile(String path) {
print(path)
def file = readFile(path)
.replaceAll("(?m)^\\s*\\r?\\n", "") // skip empty line
.replaceAll("(?m)^#[^\\n]*\\r?\\n", "") // skip commented lines
file.split('\n').each { envLine ->
def (key, value) = envLine.tokenize('=')
env."${key}" = "${value.trim().replaceAll('^\"|\"$', '')}"
}
}
private String parseJobName(String jobName) {
def pattern = /optx__r_(.*?)__o_(.*?)__c_(.*?)__t_(.*)/
def compiledPattern = Pattern.compile(pattern)
def matcher = compiledPattern.matcher(jobName)
print(matcher)
if (matcher.find()) {
// マッチングした場合、各グループから値を取得
def repositoryName = matcher.group(1)
def optxId = matcher.group(2)
def contractorNo = matcher.group(3)
def tag = matcher.group(4)
// 結果をマップとして返す
return [
REPOSITORY_NAME: repositoryName,
OPTX_ID: optxId,
CONTRACTOR: contractorNo,
TAG: tag
]
} else {
// マッチしない場合はnullまたはエラーを返す
print("pattern not match")
return null
}
}
private String findApiKey(String credentialsId) {
// Jenkinsインスタンスを取得
def jenkins = Jenkins.getInstance()
// 認証情報を検索
def lookupCredentials = CredentialsProvider.lookupCredentials(
StringCredentials.class,
jenkins,
ACL.SYSTEM,
(List<DomainRequirement>) null
)
// 指定されたIDに一致する認証情報を取得
def credentials = CredentialsMatchers.firstOrNull(
lookupCredentials,
CredentialsMatchers.withId(credentialsId)
)
if (credentials) {
// 秘密テキスト(Secret Text)を取得
def secretText = credentials.getSecret().getPlainText()
println "取得したSecret Text: $secretText"
return secretText
} else {
println "指定されたIDの認証情報が見つかりませんでした。"
return null
}
}
pipeline {
agent any
environment {
SSH_NAME='sakura-vps-002'
NEWTROID_REGISTRY_URL="registry.optxstore.io"
NEWTROID_API_URL="nroid.io"
// ####### #######
// ####### EDIT ENV PATH #######
// ####### #######
DOCKER_FILE_PATH="./Dockerfile"
OPTX_CONF_PATH="./optx-conf.json"
// #########################
// #########################
// build_timestamp = sh(script: 'date +%Y%m%d-%H%M%S', returnStdout: true).trim()
// commit_id = sh(returnStdout: true, script: 'git rev-parse HEAD')
COMMIT_ID_SHORT = sh(returnStdout: true, script: 'git rev-parse --short HEAD')
}
//pre build
stages {
stage('Pre Check'){
steps{
script{
def parsed = parseJobName("${JOB_NAME}")
print(parsed)
env.OPTX_ID = parsed.OPTX_ID
env.CONTRACTOR = parsed.CONTRACTOR
env.OPTX_ID = parsed.OPTX_ID
env.TAG = parsed.TAG
def credentialsId = "${CONTRACTOR}__APIKEY"
env.API_KEY = findApiKey(credentialsId)
}
echo "${OPTX_ID}"
echo "node -v"
}
}
//register call api via groovy
stage('Register OptX'){
steps{
echo 'creates .env file'
script{
def endpoint = "https://${NEWTROID_API_URL}/api/1/contractors/${CONTRACTOR}/optx/${OPTX_ID}/file";
def query1 = String.format("apikey=%s", URLEncoder.encode("${API_KEY}", "UTF-8"));
def query2 = String.format("upsert=%s", URLEncoder.encode("true", "UTF-8"));
def url = endpoint + "?" + query1 + "&"+ query2;
def post = new URL(url).openConnection();
post.setRequestMethod("POST");
post.setDoInput(true);
post.setDoOutput(true);
post.addRequestProperty("Content-Type", "application/json; charset=UTF-8");
File file = new File("${JENKINS_HOME}/workspace/${JOB_NAME}", "${OPTX_CONF_PATH}");
// def text = "{\"hoge\":\"hoge\"}";
String escaped = file.getText("UTF-8");
escaped = escaped.replace("\\", "\\\\");
escaped = escaped.replace("\"", "\\\"");
escaped = escaped.replace("\b", "\\b");
escaped = escaped.replace("\f", "\\f");
escaped = escaped.replace("\n", "\\n");
escaped = escaped.replace("\r", "\\r");
escaped = escaped.replace("\t", "\\t");
// TODO: escape other non-printing characters using uXXXX notation
def json =
"{" +
"\"name\": \"optx-conf.json\"," +
"\"text\":"+"\"" + escaped +"\""+
"}";
println(json);
post.connect();
PrintStream ps = new PrintStream(post.getOutputStream());
ps.print(json);
ps.close();
def res = post.getResponseCode();
}
}
}
//build
stage('Build BaseImange'){
steps{
echo "Building..."
script{
def str = "${OPTX_ID}"
env.OPTX_ID_LOWERCASE= str.toLowerCase();
env.DOCKER_FILE_PATH = "${DOCKER_FILE_PATH}"
}
sh "rsync -avzP ./ ${SSH_NAME}:/home/ubuntu/jenkins_deploy_workspace/${JOB_NAME}"
sh '''ls
ssh ${SSH_NAME} << EOF
cd /home/ubuntu/jenkins_deploy_workspace/${JOB_NAME}
docker build -t ${OPTX_ID_LOWERCASE} . -f ${DOCKER_FILE_PATH}
EOF'''
}
}
//update or create optx
stage('Register Image'){
steps{
echo "Register..."
sh "rsync -avzP ./ ${SSH_NAME}:/home/ubuntu/jenkins_deploy_workspace/${JOB_NAME}"
sh '''ls
ssh ${SSH_NAME} << EOF
cd /home/ubuntu/jenkins_deploy_workspace/${JOB_NAME}
docker tag ${OPTX_ID_LOWERCASE} ${NEWTROID_REGISTRY_URL}/${OPTX_ID_LOWERCASE}:${TAG}
docker tag ${OPTX_ID_LOWERCASE} ${NEWTROID_REGISTRY_URL}/${OPTX_ID_LOWERCASE}:latest
docker push ${NEWTROID_REGISTRY_URL}/${OPTX_ID_LOWERCASE}:${TAG}
docker push ${NEWTROID_REGISTRY_URL}/${OPTX_ID_LOWERCASE}:latest
EOF'''
}
}
}
}
2.2 optx-conf.json の作成
optx-conf.jsonは、環境変数、プロトコル、ポートの設定等、newtroid上でコンテナを起動するのに必要な設定情報を定義します。 optx-conf.jsonはjson形式で以下のパラメータを設定します。
{
version: string,
environments: string[]
io: {
input: {
name: string
portInside: number
type: `http` | `tcp`
}[],
output: {
name: string
type: `http` | `tcp`
}[]
}
}
parameter | description |
---|---|
version | コンフファイルの形式を指定します。 |
environments | newtroid で optx を起動する前に設定できる環境変数を定義します。開発に.env を使用している場合はその内容を記載することをお勧めします。 |
io | io には newtroid でコンテナ同士を繋ぐためのインターフェースの設定を記述します。 |
input | アクセスを許可するポートを定義します。アプリケーションでリッスンするポートに合わせて設定をしてください。 |
input.name | 端子名。役割がわかりやすいように名前をつけます |
input.portInside | アクセスを受け付けるポート番号 |
input.type | 通信プロトコル |
output | サービス検出を行う端子を設定します。この端子でスロット同士を接続すると接続したスロットのアクセス情報が環境変数として起動時に与えられます。 |
output.name | 端子名。スロットを起動すると環境変数に NEWTROID_[端子名]_URL という環境変数で接続したコンテナのホスト情報が与えられます。例えば name をapi と設定していた場合、NEWTROID_API_URL にホスト情報が与えられます。複数のスロットを接続していた場合、カンマ区切りで変数が与えられます。アプリケーションは動的に接続先を変更することができます。(サービス検出) |
output.type | 通信プロトコル |
optx-conf.json の例
{
"version": 1,
"environments": ["MESSAGE"],
"io": {
"inputs": [{ "name": "none", "portInside": 3000, "type": "http" }],
"outputs": [{ "name": "api", "type": "http" }]
}
}
Step3:OptX の登録とリリース
OptX をリリースするには OptX を newtroid 上で作成し、Jenkinsでジョブを作成し、作成したnJenkinsfileによってビルドを実行する必要があります。 Jenkins のジョブ名に newtroid 上で作成した OptX ID を設定してください。
3.1 OptX の新規作成
Newtroid Console上からOptXを新規作成します。Create New
ボタンをクリックしてOptXの名前を入力し、保存してください。
作成後、OptXにIDが割り当てらます。このIDはOptXをJenkinsでリリースする際に使用します。
3.2 Jenkins の設定
3.2.1 jenkins にログインします。
3.2.2 認証情報の設定
初回時及び認証情報を変更した場合に認証情報を設定する必要があります。
SCM の認証情報を設定する
Github等のSCMに認証が必要な場合に設定を行います。
ダッシュボード -> Jenkins の管理 -> Credentials -> System -> グローバルドメインの順に画面遷移します。
Add Credentials
をクリックしてSSH ユーザ名と秘密鍵
を選択します。以下のように項目を設定してください。
- スコープ: グローバル
- ID: Credentials で一意となるよう設定してください。
- 説明: 任意
- ユーザー名: SCM のユーザー名を設定してください。 秘密鍵
- 直接入力を選択し、秘密鍵をコピー&ペーストしてください。
設定が完了したら、Create
ボタンをクリックして設定を保存してください。
newtroid の認証情報を設定する
newtroid に OptX を登録するために、コントラクタと API キーを設定します。
ダッシュボード -> Jenkins の管理 -> Credentials -> System -> グローバルドメインの順に画面遷移します。
Add Credentials
をクリックしてSecret text
を選択します。以下のように項目を設定してください。([]は不要です。)
- スコープ: グローバル
- Secret: API キー
- ID:
[CONTRACTOR_NO]__APIKEY
のように設定してください。例. コントラクタ playground へ OptX を登録する場合、playground__APIKEY
- 説明: 任意
設定が完了したら、Create
ボタンをクリックして設定を保存してください。
3.2.2 新規ジョブの作成
ジョブ名の設定
ジョブ名でリポジトリ名、コントラクタ、OPTX ID、タグを設定します。 以下のようにジョブ名を設定してください。
optx__r_[REPOSITORY_NAME]__o_[OPTX_ID]__c_[CONTRACTOR_NO]__t_[TAG]
- REPOSITORY_NAME: SCM のリポジトリ名
- OPTX_ID: リリースする OptX ID
- CONTRACTOR_NO: コントラクタ
- TAG: Docker Registry に登録するタグに使用します。Docker Registry には、指定したタグと latest が登録されます。
例. optx__r_optx-sdk-ts__o_opt_1234567890AbCdEfGHIIJK__c_playground__t_v1.0.0
ジョブスケジュールの設定
ビルドトリガの SCM をポーリングをチェックし*/5 * * * *
と入力します。
これで 5 分に一度 Github のリポジトリの更新をチェックします。更新があった場合はジョブが実行されリリースが自動的に行われます。
SCM の設定
リポジトリの URL、ブランチ、jenkins ファイルのパスの設定をします。
以上の設定が完了したらジョブを保存します。
3.2.3 ジョブを実行します。
ビルド実行ボタンを押してジョブを実行します。すべてのステージをパスすれば newtroid に OptX が登録されます。
Trouble Shooting
初回の実行時において、Jenkinのセキュリティの制約上、groovyモジュールが使用できない場合があります。その場合、管理者がスクリプトを承認してください。
Jenkinsの管理 > In-process Script Approvalページに遷移し、対象のモジュールに対してApprove
ボタンをクリックし、モジュールを承認してください。
Step4:OptX の起動
- newtroid にログインし、Xervice画面を開きます。
- 画面右下の
+
ボタンをクリックし、新規パイプラインを作成します。 - パイプラインを開き、
+
ボタンで新規スロットを作成します。 - スロットをクリックし、画面右部のインスペクタ画面の
Select OptX
ボタンで OptX を選択します。
OptX の設定
※environments に指定した環境変数は newtroid のインスペクタメニューに表示され、 起動前に設定を行えるようになります。
※io の設定はスロットの端子として表示されます。
- インスペクタで OptX の設定をし、起動ボタンで起動します。