ry's Tech blog

Cloud Native技術などについて書いていきます。

Kubenews #30

2021-09-10

Managing Kubernetes seccomp profiles with security profiles operator (click here to source)

seccompプロファイルの作成と管理は煩雑であり、エラーが発生しやすくなるのを緩和する目的で作成された。

セキュリティプロファイルオペレータの機能

  • SeccompプロファイルCRD
  • seccompプロファイルをポッドにバインドするためのCRD
  • 実行中のポッドからseccompプロファイルを作成するためのCRD
  • Seccompプロファイルの同期とノードサポートの検証
  • メトリックス
  • ログエンリッチャー

インストール

cert-managerをインストールする必要があります。

$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v1.4.2/cert-manager.yaml

次にservice profile operatorのインストール

$ kubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/security-profiles-operator/master/deploy/operator.yaml

seccomp profileの作成

以下のプロファイルは、SCMP_ACT_LOGのデフォルトアクションのみを設定します。

profile1.yaml という名前でファイルを作成します。

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: SeccompProfile
metadata:
  namespace: default
  name: profile1
spec:
  defaultAction: SCMP_ACT_LOG

では、作成していきます。

$ kubectl apply -f profile1.yaml
seccompprofile.security-profiles-operator.x-k8s.io/profile1 created

$ kubectl get seccompprofile profile1 --output wide
NAME       STATUS      AGE     LOCALHOSTPROFILE
profile1   Installed   7m21s   operator/default/profile1.json

作成されたプロファイルは、

/var/lib/kubelet/seccomp/operator/<namespace>/<profile name>.json

に格納されます。

$ ssh k8s-pool1-18290975-vmss000000 sudo cat /var/lib/kubelet/seccomp/operator/default/profile1.json
{"defaultAction":"SCMP_ACT_LOG"}

seccomp profileの利用

作成したprofileをPodに適用してきます。

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  securityContext:
    seccompProfile:
      type: Localhost
      localhostProfile: operator/default/profile1.json
  containers:
    - name: nginx
      image: nginx

プロファイルの継承

1つのprofileをベースとして使用して新しいカスタムプロファイルを作成することもできます。

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: SeccompProfile
metadata:
  namespace: default
  name: inheritance-profile1
spec:
  defaultAction: SCMP_ACT_ERRNO
  baseProfileName: base-profile1
  syscalls:
    - action: SCMP_ACT_ALLOW
      names:
        - mkdir

profile bindings

PodのSpecを直接いじりたくない場合に、ProfileBindingが有用である。 今回は、nginx:1.21.1のimageを使用するPodに対して、最初に作成した profile1を適用させる例を紹介します。

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: ProfileBinding
metadata:
  name: nginx-binding
spec:
  profileRef:
    kind: SeccompProfile
    name: profile1
  image: nginx:1.21.1

適用します。

$ kubectl apply -f nginx-binding.yaml
profilebinding.security-profiles-operator.x-k8s.io/nginx-binding created

以下のprofileを指定していないPodを作成すると挙動が確認できます。

$ cat <<EOF kubectl create -f -
apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
    - name: nginx
      image: nginx:1.21.1
EOF
pod/nginx created

$ kubectl get pod nginx -o jsonpath='{.spec.containers[*].securityContext.seccompProfile}'
{"localhostProfile":"operator/default/profile1.json","type":"Localhost"}

profileの記録

以下のmanifestを適用した場合、app: nginx というラベルを持ったPodが作成された際に記録が開始されます。 そして、そのポッドが削除されると、seccompプロファイルリソース(ポッドごと)が作成され、使用できるようになります。

apiVersion: security-profiles-operator.x-k8s.io/v1alpha1
kind: ProfileRecording
metadata:
  name: recording
spec:
  kind: SeccompProfile
  recorder: hook
  podSelector:
    matchLabels:
      app: nginx

Shipwright - Building Container Images In Kubernetes (click here to source)

Shipwrightとという、Kubernetes上でコンテナイメージを作成するツールが紹介されています。

リソースとしては、

  • BuildStrategy or ClusterBuildStrategy
  • Build
  • BuildRun

BuildStrategy or ClusterBuildStrategy

BuildのManifestで指定する、コンテナイメージをビルドする際に使用するツールについてです。

現在は、以下の選択肢があるみたいです。

  • Available ClusterBuildStrategies
Name Supported platforms
buildah linux/amd64 only
BuildKit all
buildpacks-v3-heroku linux/amd64 only
buildpacks-v3 linux/amd64 only
kaniko all
ko all
source-to-image linux/amd64 only
  • Available BuildStrategies
Name Supported platforms
buildpacks-v3-heroku linux/amd64 only
buildpacks-v3 linux/amd64 only

Build

ここで、実際にどうビルドするのかを定義します。

  • spec.source.url : アプリケーションのソースコードのあるレポジトリを指定
  • spec.strategy : ビルドツールを指定
  • spec.output : 作成したimageをプッシュするregistryを指定
    • output.credentials.name : registryにアクセスする際に使用する既存のsecretを指定
apiVersion: shipwright.io/v1alpha1
kind: Build
metadata:
  name: s2i-nodejs-build
spec:
  source:
    url: https://github.com/shipwright-io/sample-nodejs
  strategy:
    name: kaniko
    kind: ClusterBuildStrategy
  output:
    image: us.icr.io/source-to-image-build/nodejs-ex
    credentials:
      name: icr-knbuild

例えば、

  • レポジトリのrootディレクトリにソースコードがない場合は、spec.source.contextDirにて指定
  • 独自のビルドツールを積んでいるimageを使う場合、spec.strategy.namesource-to-imageにし、spec.builder.imageにてそのimageを指定する

など、柔軟性がありそう。

apiVersion: shipwright.io/v1alpha1
kind: Build
metadata:
  name: s2i-nodejs-build
spec:
  source:
    url: https://github.com/shipwright-io/sample-nodejs
    contextDir: source-build/
  strategy:
    name: source-to-image
    kind: ClusterBuildStrategy
  builder:
    image: docker.io/centos/nodejs-10-centos7
  output:
    image: us.icr.io/source-to-image-build/nodejs-ex
    credentials:
      name: icr-knbuild

BuildRun

Kubernetes上でBuildを実行するリソース。

apiVersion: shipwright.io/v1alpha1
kind: BuildRun
metadata:
  name: buildpack-nodejs-buildrun-namespaced
spec:
  buildRef:
    name: s2i-nodejs-build
  serviceAccount:
    name: pipeline

Buildで定義したプッシュ先を上書きしたい場合、このリソースでも定義ができる。

apiVersion: shipwright.io/v1alpha1
kind: BuildRun
metadata:
  name: buildpack-nodejs-buildrun-namespaced
spec:
  buildRef:
    name: s2i-nodejs-build
  serviceAccount:
    name: pipeline
  output:
    image: us.icr.io/source-to-image-build/nodejs-ex
    credentials:
      name: icr-knbuild

Distributed tracing with Knative, OpenTelemetry and Jaeger (click here to source)

Knative、OpenTelemetry、Jaegerを使った分散トレーシングについて書かれています。

Setup

既にKnative Serving and Eventingがデプロイされてていることを前提としています。

まず、クラスターにOpenTelemetryオペレーターを追加します。 こちらは、cert-managerに依存しているため、そちらもデプロイします。

$ kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml 

$ kubectl apply -f https://github.com/open-telemetry/opentelemetry-operator/releases/latest/download/opentelemetry-operator.yaml

次に、イェーガーオペレーターをデプロイします。

$ kubectl create namespace observability
$ kubectl create -n observability \
    -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/crds/jaegertracing.io_jaegers_crd.yaml \
    -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/service_account.yaml \
    -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role.yaml \
    -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/role_binding.yaml \
    -f https://raw.githubusercontent.com/jaegertracing/jaeger-operator/master/deploy/operator.yaml

起動したら、次のコマンドを実行してJaegerインスタンスを作成できます。

$ kubectl apply -n observability -f - <<EOF
apiVersion: jaegertracing.io/v1
kind: Jaeger
metadata:
  name: simplest
EOF

次に、OpenTelemetryコレクターを作成します。

$ kubectl apply -f - <<EOF
apiVersion: opentelemetry.io/v1alpha1
kind: OpenTelemetryCollector
metadata:
  name: otel
  namespace: observability
spec:
  config: |
    receivers:
      zipkin:
    exporters:
      logging:
      jaeger:
        endpoint: "simplest-collector.observability:14250"
        insecure: true

    service:
      pipelines:
        traces:
          receivers: [zipkin]
          processors: []
          exporters: [logging, jaeger]
EOF

最後に、すべてのトレースがコレクターを指すようにEventing andServingを構成します。

$ for ns in knative-eventing knative-serving; do
  kubectl patch --namespace "$ns" configmap/config-tracing \
   --type merge \
   --patch '{"data":{"backend":"zipkin","zipkin-endpoint":"http://otel-collector.observability:9411/api/v2/spans", "debug": "true"}}'
done

Hello World

トレースインフラストラクチャがすべてデプロイおよび構成されたので、ハートビートイメージ をContainerSourceとして デプロイして、 すべてが正しく接続されていることをテストおよび確認できます。

$ kubectl apply -f - <<EOF
apiVersion: sources.knative.dev/v1
kind: ContainerSource
metadata:
  name: heartbeats
spec:
  template:
    spec:
      containers:
        - image: gcr.io/knative-nightly/knative.dev/eventing/cmd/heartbeats:latest
          name: heartbeats
          args:
            - --period=1
          env:
            - name: POD_NAME
              value: "heartbeats"
            - name: POD_NAMESPACE
              value: "default"
            - name: K_CONFIG_TRACING
              value: '{"backend":"zipkin","debug":"true","sample-rate":"1","zipkin-endpoint":"http://otel-collector.observability:9411/api/v2/spans"}'
  sink:
    uri: http://dev.null
EOF

今のところ、このコンテナは存在しないドメインhttp://dev.nullにハートビートを送信しているだけなので、このポッドのログを見ると、一連のDNS解決エラーが表示されますが、otel-collectorポッドのログを調べると、サービスからトレースを正常に受信していることがわかります。

ハートビートを受信するためにKnativeサービスを追加し、ハートビートサービスを更新します。

$ kubectl apply -f - <<EOF
apiVersion: serving.knative.dev/v1
kind: Service
metadata:
  name: event-display
spec:
  template:
    spec:
      containers:
        - image: gcr.io/knative-nightly/knative.dev/eventing/cmd/event_display:latest
          env:
            - name: K_CONFIG_TRACING
              value: '{"backend":"zipkin","debug":"true","zipkin-endpoint":"http://otel-collector.observability:9411/api/v2/spans"}'
EOF

$ kubectl apply -f - <<EOF
apiVersion: sources.knative.dev/v1
kind: ContainerSource
metadata:
  name: heartbeats
spec:
  template:
    spec:
      containers:
        - image: gcr.io/knative-nightly/knative.dev/eventing/cmd/heartbeats:latest
          name: heartbeats
          args:
            - --period=1
          env:
            - name: POD_NAME
              value: "heartbeats"
            - name: POD_NAMESPACE
              value: "default"
            - name: K_CONFIG_TRACING
              value: '{"backend":"zipkin","debug":"true","zipkin-endpoint":"http://otel-collector.observability:9411/api/v2/spans"}'
  sink:
    ref:
      apiVersion: serving.knative.dev/v1
      kind: Service
      name: event-display
EOF

これらのサービスがデプロイされたら、Jaegerダッシュボードでもう一度確認することで、おもしろいとレースが表示されます。

Getting fancy

Hello Worldより複雑な構成を紹介してくれているので、記事を読んでみてください。