11月です。寒いですね。ICPチームでgitlab-ci芸人をやっているchotaroです。
gitlab-ciと戦ったのも一年前。
性懲りもなく、ややこなれてきた人向けのgitlab-ciのTIPS記事です。
日本語での情報があまりない分野ですので、誰かの役に立てば幸いです。
(なおこの記事を書いている段階でブログ当番を2ヶ月ほど滞納しております。土下座。
主な元ネタはこちらのgitlab公式doc
本記事のモチベーション
以下をモチベーションとしています。
- 気軽にツール作ってdeployしたい。
- gitlab-ciのjobからgcrにimageをpushしたい
- ついでにそのままCloudRunにデプロイしたい
- でもdind(docker-in-docker)はセキュリティ的に使いたくない(重要)
jibなどもツールの選択肢にはなりうるのですが、CloudNative Buildpacksがdockerを必要とするため、dindを用いることが不適切なケースでは利用できません。
そこでここではgitlabの公式ドキュメントにも記載のある、kanikoを利用した実現方法を紹介していきます。
事前知識
kanikoとは
ものすごく端的に言えば、「dockerを使うことなく、Dockerfileからcontainer imageを作成し、リポジトリに pushするツール」です。
kaniko自体は、kubernates内でimageをbuildすることが主眼とされています。本記事ではあくまでも、container imageをdockerなしに作成するツールとして用います。
dockerfileの作成
kanikoはdockerを使いませんが、inputにDockerfileを必要とします。ProcfileなどではなくDockerfileなので、この点がややCloudNative Buildpacksなどと比べると開発者にとって少し敷居が高いかもしれません。
あくまでサンプルなので拘らず、GCPのdocにあるものを真似します。
Dockerfile
FROM node:15-slim WORKDIR /usr/src/app COPY package*.json ./ RUN npm install --only=production COPY . ./ CMD [ "node", "index.js" ]
index.js
const express = require('express'); const app = express(); app.get('/', (req, res) => { const name = process.env.NAME || 'World'; res.send(`Hello ${name}!`); }); const port = process.env.PORT || 8080; app.listen(port, () => { console.log(`helloworld: listening on port ${port}`); });
package.json
{ "name": "helloworld", "description": "Simple hello world sample in Node", "version": "1.0.0", "private": true, "main": "index.js", "scripts": { "start": "node index.js", "test": "mocha test/index.test.js --exit", "system-test": "NAME=Cloud test/runner.sh mocha test/system.test.js --timeout=30000", "lint": "eslint '**/*.js'", "fix": "eslint --fix '**/*.js'" }, "author": "Google LLC", "license": "Apache-2.0", "dependencies": { "express": "^4.17.1" }, "devDependencies": { "got": "^11.0.0", "mocha": "^8.0.0", "supertest": "^4.0.2" } }
起動して8080にアクセスするとHello World!
という表示だけ出ます。
kanikoを使ったbuild
kanikoは引数で三つ指定が必要です。
- docker-file
- build対象のDockerfileはどこに置いてあるか。
- gitlab-ciであれば
$CI_PROJECT_DIR
など。
- gitlab-ciであれば
- build対象のDockerfileはどこに置いてあるか。
- context
- どこのソースを使用するか。kubernatesでの利用が主眼なので、リモートリポジトリなども指定できます。
- gitlab-ciで特定のrepositoryでciする場合だと、素直に
$CI_PROJECT_DIR
などでOKです。
- gitlab-ciで特定のrepositoryでciする場合だと、素直に
- どこのソースを使用するか。kubernatesでの利用が主眼なので、リモートリポジトリなども指定できます。
- destination
- この設定値がpush先になります。
- 例えばgcrのフルパス。(ex: gcr.io/<プロジェクト名>/<image名>
- この設定値がpush先になります。
executorを対象にdocker runすればそのままdestinationに作成したimageがpushされる形です。
また、gcrにpushしたい場合GCPでの認証認可が必要になりますが、その他のGCPツール群と同じく$GOOGLE_APPLICATION_CREDENTIALS
の環境変数にcredentialファイルのpathを渡してあげればOKです。
gcr上のイメージをcloud run上へdeployする
pushしたdocker imageをcloud run上にdeployするには、gcloudのコマンドを使えばOKです。gcrへのpushで使ったservice accountにこの権限も付与しておくと管理すべき認証情報が一つのservice accountにまとまるのでエコです。
公式ドキュメントも参考に、コンテナイメージを使ってgcloud
コマンドでdeployしましょう
gcloud run deploy <サービス名> --image <imageのpush先> --platform managed --region <region名>
実際に試してみる
gitlab-ci.yml
このような形になりました(ExecutorにはDocker Machineを使用しています)
stages: - build - deploy before_script: - echo 'set up credentials' - echo $CREDENTIAL > $CI_PROJECT_DIR/key.json - export GOOGLE_APPLICATION_CREDENTIALS=$CI_PROJECT_DIR/key.json build: stage: build image: name: gcr.io/kaniko-project/executor:debug entrypoint: [""] script: - /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination $DESTINATION deploy: stage: deploy image: google/cloud-sdk:alpine script: - gcloud auth activate-service-account --key-file=key.json - gcloud config set project $GCP_PROJECT_NAME - echo y | gcloud auth configure-docker - gcloud info - echo y | gcloud run deploy kaniko-sample --image $DESTINATION --platform managed --region <region名>
ポイントは以下の点です。
- buildとdeployでjobを分ける
- 環境変数にcredentialなどの情報は外出しする(git管理下に鍵情報は置きたくないので
- before scriptで認証情報のセットアップを行う
buildとdeployでjobを分ける
kanikoのimageはdebugで利用します(gitlab公式docも参照ください
また、素のalpineなどにgcloudを入れるのは大変なので、gcloudのimageをそのまま使うためtaskを分けています。
環境変数にcredentialなどの情報は外出しする
gitlab-ciのvariablesに下記を設定します。
- GCP_PROJECT_NAME
- GCP上のプロジェクト名
- CREDENTIAL
- サービスアカウントの認証情報
- DESTINATION
- gcrの指定
before scriptで認証情報のセットアップを行う
- echo 'set up credentials' - echo $CREDENTIAL > $CI_PROJECT_DIR/key.json - export GOOGLE_APPLICATION_CREDENTIALS=$CI_PROJECT_DIR/key.json
ややトリッキーな書き方になってはいますが、variablesに設定した認証情報を書き出します。
- gcloud auth activate-service-account --key-file=key.json - gcloud config set project $GCP_PROJECT_NAME - echo y | gcloud auth configure-docker
gcloudでそのkeyをもとにサービスアカウントとしての認証設定を行います。これでgcloudのセットアップはOK
実行後の確認
gitlab-ciの実行結果を確認し、Service URL
にアクセスしてHello World!
を確認します。
Deploying container to Cloud Run service [<サービス名>] in project [<プロジェクト名>] region [<リージョン名>] Deploying... Creating Revision...................................................................................done Routing traffic......done Done. Service [<サービス名>] revision [<サービス名>-00004-but] has been deployed and is serving 100 percent of traffic. Service URL: https://<url>
trouble shooting系(ここでつまりました
- gcloud run の際にSERVICEがないと言われる
- ちゃんとhelpをよみましょう。
gcloud run deploy <SERVICE名称> <--で始まるその他オプション>
という構文です。
- gcloud run の際にregionが設定されてないと言われる
- ちゃんと(ry
- 設定しましょう。
--region
オプションです
- 403が出て表示できない
- これが出るのは主に、デプロイしたアプリが権限を必要とする状態になっており、かつアクセス者に権限がない場合です
- console上でアクセスする人(gcp上のメンバー)に権限を付与すればそのメンバーがアクセスできるようになります。
- あくまでサンプルやリクエストを複数受けても問題ない場合のみ使うべきTIPSではありますが、
--allow-unauthenticated
オプションをつけてdeployすることで誰でもアクセスできるようになります。
- あくまでサンプルやリクエストを複数受けても問題ない場合のみ使うべきTIPSではありますが、
まとめ
ざっくりとした解説にはなりますが、以上です。
趣味と実務の境目みたいなTIPSでしたが、こういう積み重ねが後々新しいことをやろうとした時に効いてくると信じています。
やってみた感想としては、Dockerfileを用意すればどんなアプリケーションでも原則このやり方でdeployできるはずなので、社内ツールのようなものにも活用していけたら気軽にいろいろ試していけそうかな、という感じです。
ICPとして今後本番利用したりしていくかは未知数ですが、jenkinsでの運用に限界があるのも事実なので、この辺りのアンテナも個人的には立てていきたいところだと思っているところです。
さて最後にはなりますがアスタミューゼでは引き続き好奇心豊富な(?)メンバーを募集しています。
業務やドメイン的には他の方の記事がどれもかなり興味深い内容にはなっておりますので、そちらも読んでいただきビビッときたらこちらよりご連絡いただければと思います。では!
参考にさせていただいたもの
google cloud runのdocument
- dockerfileのサンプルなど
- gcr上のimageをcloud runにdeployする方法
gitlab ci + kanikoについて
kanikoのオプションについて