とことんDevOps | 日本仮想化技術のDevOps技術情報メディア

DevOpsに関連する技術情報を幅広く提供していきます。

日本仮想化技術がお届けする「とことんDevOps」では、DevOpsに関する技術情報や、日々のDevOps業務の中での検証結果、TipsなどDevOpsのお役立ち情報をお届けします。
主なテーマ: DevOps、CI/CD、コンテナ開発、IaCなど
読者登録と各種SNSのフォローもよろしくお願いいたします。

オンプレミスでCircleCI Serverを動かそう(その2) 事前準備編

オンプレミスでCircleCI Serverを動かすシリーズの第2回です。前回は必要となるツールをインストールしました。今回はこれらのツールを使い、CircleCIサーバーを動かすためのEKSクラスター、アーティファクトを保存するS3バケット、アーティファクトをセキュアに保存するために必要な署名キーなどを作っていきます。

EKSクラスターの作成

eksctlコマンドでEKSクラスターを作成します。まず以下の内容でcircleci-eks.ymlというクラスターの設定をまとめたファイルを作成します。

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
  name: circleci-server
  region: ap-northeast-1
  version: '1.21'
iam:
  withOIDC: true
  serviceAccounts:
    - metadata:
        name: aws-load-balancer-controller
        namespace: kube-system
      wellKnownPolicies:
        awsLoadBalancerController: true
    - metadata:
        name: external-dns
        namespace: kube-system
      wellKnownPolicies:
        externalDNS: true
vpc:
  cidr: 10.21.0.0/16
  nat:
    gateway: Single
addons:
  - name: vpc-cni
  - name: coredns
  - name: kube-proxy
managedNodeGroups:
  - name: ng-1
    instanceType: t3.xlarge
    desiredCapacity: 3
    minSize: 1
    maxSize: 3
    volumeSize: 20
    privateNetworking: true
    iam:
      attachPolicyARNs:
        - arn:aws:iam::aws:policy/AmazonEC2ContainerRegistryReadOnly
        - arn:aws:iam::aws:policy/AmazonEKSWorkerNodePolicy
        - arn:aws:iam::aws:policy/AmazonEKS_CNI_Policy
        - arn:aws:iam::aws:policy/AmazonSSMManagedInstanceCore

上記ファイルの内容を具体的に解説すると

  • circleci-serverという名前でバージョン1.21のクラスターを作成する
  • vpc-cni/coredns/kube-proxyをクラスターアドオンとしてデプロイする
  • ノードグループにt3.xlargeのEC2インスタンスを3台起動する
  • AWS Load Balancer Controller/ExternalDNS用のサービスアカウントを作成する

となっています。公式ドキュメントではm5.xlargeを4台起動しているのですが、あくまでテストということで、少し安価な設定としています。またAWS Load Balancer ControllerやExternalDNSは必須ではないものの、一般的なEKSクラスターを動かす際にはあると便利ですので、使い回しができるよう設定に含めています。

設定ファイルが準備できたら、以下のコマンドでクラスターを作成します。この作業は完了まで30分程度の時間がかかります。

$ eksctl create cluster -f circleci-eks.yml

AWS Load Balancer Controllerのデプロイ

クラスターが完成したら、以下のコマンドでHelmのリポジトリを追加し、AWS Load Balancer Controllerをデプロイします。

$ helm repo add eks https://aws.github.io/eks-charts
$ helm repo update
$ helm install aws-load-balancer-controller eks/aws-load-balancer-controller \
       --namespace kube-system \
       --set clusterName=circleci-server \
       --set serviceAccount.create=false \
       --set serviceAccount.name=aws-load-balancer-controller \
       --set enableCertManager=false

ExternalDNSのデプロイ

前述のAWS L-B-Cと同様に、以下のコマンドでクラスターにExternalDNSをデプロイします。domainFiltersには使用するドメイン名(example.comなど)を、リスト形式で指定してください。txtOwnerIdは、追加したDNSレコードを他のExternalDNSに上書きされないよう、レコードのオーナーを識別するためのラベルです。ExternalDNSごとにユニークな文字列を指定してください。今回は競合するExternalDNSが存在しない前提ですので、circleci-serverのような文字列でよいでしょう。

$ helm repo add external-dns https://kubernetes-sigs.github.io/external-dns
$ helm repo update
$ helm install external-dns external-dns/external-dns \
       --namespace kube-system \
       --set serviceAccount.create=false \
       --set serviceAccount.name=external-dns \
       --set sources=\{service,ingress\} \
       --set domainFilters=\{(ドメイン名)\} \
       --set policy=sync \
       --set provider=aws \
       --set txtOwnerId=(任意の文字列)

S3バケットの作成

アーティファクトやテスト結果などを保存するS3バケットを作成します。ここではvtj-circleci-serverという名前のバケットを作成しています。

$ aws s3api create-bucket \
      --bucket vtj-circleci-server \
      --region ap-northeast-1 \
      --create-bucket-configuration LocationConstraint=ap-northeast-1

続いて、CircleCI ServerがS3への書き込みに利用するIAMユーザーを作成します。ここではcircleci-serverというユーザーを作成しています。

$ aws iam create-user --user-name circleci-server

このユーザーにアタッチするポリシードキュメントを作成します。以下の内容でpolicy.jsonというファイルを作成してください。なおResourceに指定しているバケット名(2箇所のvtj-circleci-serverの部分)は、先ほど作成したバケット名に書き換えてください。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutAnalyticsConfiguration",
        "s3:GetObjectVersionTagging",
        "s3:CreateBucket",
        "s3:GetObjectAcl",
        "s3:GetBucketObjectLockConfiguration",
        "s3:DeleteBucketWebsite",
        "s3:PutLifecycleConfiguration",
        "s3:GetObjectVersionAcl",
        "s3:PutObjectTagging",
        "s3:DeleteObject",
        "s3:DeleteObjectTagging",
        "s3:GetBucketPolicyStatus",
        "s3:GetObjectRetention",
        "s3:GetBucketWebsite",
        "s3:GetJobTagging",
        "s3:DeleteObjectVersionTagging",
        "s3:PutObjectLegalHold",
        "s3:GetObjectLegalHold",
        "s3:GetBucketNotification",
        "s3:PutBucketCORS",
        "s3:GetReplicationConfiguration",
        "s3:ListMultipartUploadParts",
        "s3:PutObject",
        "s3:GetObject",
        "s3:PutBucketNotification",
        "s3:DescribeJob",
        "s3:PutBucketLogging",
        "s3:GetAnalyticsConfiguration",
        "s3:PutBucketObjectLockConfiguration",
        "s3:GetObjectVersionForReplication",
        "s3:GetLifecycleConfiguration",
        "s3:GetInventoryConfiguration",
        "s3:GetBucketTagging",
        "s3:PutAccelerateConfiguration",
        "s3:DeleteObjectVersion",
        "s3:GetBucketLogging",
        "s3:ListBucketVersions",
        "s3:ReplicateTags",
        "s3:RestoreObject",
        "s3:ListBucket",
        "s3:GetAccelerateConfiguration",
        "s3:GetBucketPolicy",
        "s3:PutEncryptionConfiguration",
        "s3:GetEncryptionConfiguration",
        "s3:GetObjectVersionTorrent",
        "s3:AbortMultipartUpload",
        "s3:PutBucketTagging",
        "s3:GetBucketRequestPayment",
        "s3:GetAccessPointPolicyStatus",
        "s3:GetObjectTagging",
        "s3:GetMetricsConfiguration",
        "s3:PutBucketVersioning",
        "s3:GetBucketPublicAccessBlock",
        "s3:ListBucketMultipartUploads",
        "s3:PutMetricsConfiguration",
        "s3:PutObjectVersionTagging",
        "s3:GetBucketVersioning",
        "s3:GetBucketAcl",
        "s3:PutInventoryConfiguration",
        "s3:GetObjectTorrent",
        "s3:PutBucketWebsite",
        "s3:PutBucketRequestPayment",
        "s3:PutObjectRetention",
        "s3:GetBucketCORS",
        "s3:GetBucketLocation",
        "s3:GetAccessPointPolicy",
        "s3:GetObjectVersion",
        "s3:GetAccessPoint",
        "s3:GetAccountPublicAccessBlock",
        "s3:ListAllMyBuckets",
        "s3:ListAccessPoints",
        "s3:ListJobs"
      ],
      "Resource": [
        "arn:aws:s3:::vtj-circleci-server",
        "arn:aws:s3:::vtj-circleci-server/*"
      ]
    }
  ]
}

ポリシーをユーザーにアタッチします。

$ aws iam put-user-policy \
    --user-name circleci-server \
    --policy-name circleci-server \
    --policy-document file://policy.json

最後に、IAMユーザーのアクセスキーを作成します。以下のコマンドを実行し、出力されたアクセスキーとシークレットキーを控えておいてください。

$ aws iam create-access-key --user-name circleci-server
{
    "AccessKey": {
        "UserName": "circleci-server",
        "AccessKeyId": "アクセスキー",
        "Status": "Active",
        "SecretAccessKey": "シークレットキー",
        "CreateDate": "2022-04-18T08:38:48+00:00"
    }
}

署名キーの作成

CircleCIが生成するアーティファクトをセキュアに保存するためには、暗号化キーと署名キーが必要になります。これらのキーセットを生成しましょう。これらはCircleCIが提供しているserver-keysetsというDockerイメージを使うことで、簡単に生成することが可能です。

まず作業用のUbuntuマシンにDockerをインストールします。パッケージ名がdocker.ioである点に注意してください。dockerパッケージはまったく別のアプリケーションです。

$ sudo apt install docker.io

アーティファクト署名キー生成するには、以下のコマンドを実行します。

$ sudo docker run --rm circleci/server-keysets:latest generate signing -a stdout
{:meta {"name" "signing", "purpose" "SIGN_AND_VERIFY", "type" "HMAC_SHA1", "versions" [{"exportable" false, "status" "PRIMARY", "versionNumber" 1}], "encrypted" false}, :keys {1 {"hmacKeyString" "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "size" 256}}}

暗号化キー生成するには、以下のコマンドを実行します。

$ sudo docker run --rm circleci/server-keysets:latest generate encryption -a stdout
{:meta {"name" "encryption", "purpose" "DECRYPT_AND_ENCRYPT", "type" "AES", "versions" [{"exportable" false, "status" "PRIMARY", "versionNumber" 1}], "encrypted" false}, :keys {1 {"aesKeyString" "xxxxxxxxxxxxxxx", "hmacKey" {"hmacKeyString" "xxxxxxxxxxxxxxxxxxxxxxxxxxx", "size" 256}, "mode" "CBC", "size" 128}}}

出力された文字列を、安全な場所に控えておいてください。もしキーを失ってしまうと、暗号化されたアーティファクトを取り出せなくなる可能性があります。なお勘違いしやすいのですが、KeyStringに含まれる内容だけではなく、{:metaから最後の閉じ括弧までの全体が必要となります。注意してください。

GitHub OAuthアプリの作成

CircleCI ServerはVCSと連携して動作するのが前提です。そこでGitHubと連携するため、GitHub側にOAuthアプリを作成します。

GitHubにログインしたら、アプリを追加したいOrganizationないしは個人のSettingsを開きます。左ペインから「Developer settings」→「OAuth Apps」と辿り、Register an applicationをクリックしてください。

新規OAuthアプリの作成

新規OAuthアプリの設定画面が開きます。「アプリ名」「URL」「callback URL」を入力してください。アプリ名はわかりやすい名前なら何でも構いません。CircleCIなどとしておきましょう。URLはCircleCI Serverをホストする予定のFQDNとなります(例: https://circleci.example.com)https://から記述する必要がある点に注意してください。callback URLは、URLの末尾に/auth/githubを足したものになります。入力できたらRegister applicationをクリックしてください。

OAuthアプリの情報を入力

OAuthアプリの作成が完了すると、クライアントIDが発行されます。これを控えておいてください。そしてGenerate a new client secretをクリックして、クライアントシークレットを生成します。

発行されたクライアントID

クライアントシークレットが生成されたら、こちらも控えておきます。

生成されたクライアントシークレット

以上で必要な準備は完了です。次回はいよいよ、CircleCI Serverのコアサービスをインストールしていきます。