1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

GCP上の分析環境構築~Rstudio&Jupyterlab

Posted at

一、目的・背景

データサイエンティストに対して、重い学習ができ、モデル共有や再利用できるようクラウド上の分析環境を用意します。
データ基盤がGCP上で構築されデータウェアハウスがBigqueryに置いていて、社内の不特定多数人利用(部署内のデータサイエンティスト及びインターン生)するため、スケーラブル且つセキュリティ的なニーズに応じて、GCP上のGKEサービスを選定しました。
VertexAIを利用しない理由としては、①Rstudio向けてGPUが提供していない;②環境内部の細かく制御ができない;③コスパの考慮、

二、事前知識

1、GCPの基礎知識(APIを有効にする、権限付与、コマンド実行など)
2、ネットワークの色々(IP、domain、portなど)
3、kubectlの知識(Docker、yamlファイルなど)

三、アーキテクチャ&実装

phase1とphase2を分けて、phase1は基本利用できるサービスを提供することで、phase2はwebappデプロイし、ユーザが好きな時間帯でIDEの立ち上げを自動化にします。
構成図2.PNG

phase1

1、domainについて

GCPのIAPを利用するではドメイン名が必要ですので、今回はGCPのCloud Domainsからドメイン名を買いました。
https://cloud.google.com/blog/ja/products/networking/introducing-cloud-domains
CloudDNSのゾーンを作成してレジストラにレコードを登録します。

2、VPC作成&設定

※cloudShell上で実行&適当な権限を持ち&APIを有効にする
安全性を考慮した上、GKE限定公開クラスター作成しようと決まりました。
専用のVPCネットワークまず作成します。
https://cloud.google.com/kubernetes-engine/docs/how-to/private-clusters?hl=ja
①vpc&サブネットの作成
VPC

gcloud compute networks create gke-vpc \
    --subnet-mode=custom \
    --bgp-routing-mode=regional \
    --mtu=1460

Subnet追加
net3.PNG
※限定公開のGoogleアクセスを「オン」にする
net4.PNG
net1.PNG

②ファイアウォールの作成:
deny-all-up、deny-all-down
net2.PNG

3、静的IPアドレスの予約

GKEサービス用の外部静的IPアドレスを予約します。
外部静的IPアドレスの予約

gcloud compute addresses create <demo-static-ip> --global

4、GKEクラスターの作成

①クラスター作成
GPUも付け

gcloud container clusters create <your-private-cluster-name> \
    --machine-type=<your-machine-type> \
    --num-nodes=<例:3> \
    --disk-size=100 \
    --region=asia-northeast1 \
    --scopes=cloud-platform\
    --accelerator type=<TYPE>,count=<AMOUNT>\
    --subnetwork=<your-subnet-name>\
    --enable-master-authorized-networks \
    --enable-ip-alias \
    --enable-private-nodes \
    --enable-private-endpoint \
    --master-ipv4-cidr <例:172.16.0.32/28>

②Workload Identity pool作成
BigqueryやGCSなどのサービスを利用するために、workload identityの設定が必要です。
https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity?hl=ja
上ではノードプールを作成しましたので、ここではupdateだけ

gcloud container node-pools update <your-pool-name> --cluster=<your-private-cluster-name> --zone=asia-northeast1-c --workload-metadata=GKE_METADATA

③CloudRouter&CloudNATの作成
ほぼデフォルトの設定なので、割愛させていただきます。

5、踏み台VMの作成&IAPの設定&ファイアウォールルールの追加

GKEクラスターを弄るためには、踏み台サーバを接続してからします。
そのためここでは踏み台サーバの作成及び設定を行います。
①IAPのAPIを有効にする
②踏み台サーバVM「gke-api-server」作成
※scopes=https://www.googleapis.com/auth/cloud-platform、内部静的IPアドレスを使用、同じVPCネットワーク下に置いてあること
③IAP起動&ファイアウォールの追加
fireWALL.PNG
④VM起動&設定:https://cloud.google.com/kubernetes-engine/docs/tutorials/private-cluster-bastion
⑤IAMでVMのサービスアカウントにGKEへのアクセス権限を追加
⑥VMのipアドレスをGKEへのアクセス許可

gcloud container clusters update <your-private-cluster-name>\
 --enable-master-authorized-networks\
 --master-authorized-networks <vm内部ip>/32\
 --zone asia-northeast1-c

6、Workload Identity上の権限設定

①踏み台サーバからGKEクラスターに接続、kubectlコマンドを実行

gcloud container clusters get-credentials <your-private-cluster-name> --zone=asia-northeast1-c

②namespace作成

kubectl create namespace <your-namespace>

③Kubernetes Service Account作成

kubectl create serviceaccount <your-KubernetesServiceAccount> --namespace <your-namespace>

④google Service Accountの作成

gcloud iam service-accounts create <your-GoogleServiceAccount-name> --project=<your-project-id>

⑤google Service Accountの権限

gcloud projects add-iam-policy-binding dev-analysis-mountain\
 --member "serviceAccount:<your-GoogleServiceAccount>" --role "editor"

⑥GSAとKSAの権限(in cloud shell)

gcloud iam service-accounts add-iam-policy-binding <your-GoogleServiceAccount-name>\
 --role roles/iam.workloadIdentityUser\
 --member "serviceAccount:<your-project-id>.svc.id.goog[<your-namespace>/<your-KubernetesServiceAccount>]"

⑦GSAとKSAの権限(in VM)

kubectl annotate serviceaccount <your-KubernetesServiceAccount>\
 --namespace <your-namespace> iam.gke.io/gcp-service-account=<your-GoogleServiceAccount-name>

7、dockerファイル

①Rstudio

Dockerfile(from RockerProject: https://rocker-project.org/images/versioned/rstudio.html)

FROM rocker/r-ver:4.2.2

LABEL org.opencontainers.image.licenses="GPL-2.0-or-later" \
      org.opencontainers.image.source="https://github.com/rocker-org/rocker-versioned2" \
      org.opencontainers.image.vendor="Rocker Project" \
      org.opencontainers.image.authors="Carl Boettiger <cboettig@ropensci.org>"

ENV S6_VERSION=v2.1.0.2
ENV RSTUDIO_VERSION=2022.12.0+353
ENV DEFAULT_USER=rstudio
ENV PANDOC_VERSION=default
ENV QUARTO_VERSION=default

RUN /rocker_scripts/install_rstudio.sh
RUN /rocker_scripts/install_pandoc.sh
RUN /rocker_scripts/install_quarto.sh

RUN apt-get update -y && apt-get install -y vim
RUN apt-get install -y nginx
COPY ./nginx.conf /etc/nginx/
COPY ./init.sh /etc/
RUN /etc/init.d/nginx restart

# gcsfuse mount
RUN apt-get update -y && apt-get install -y gnupg2
# RUN echo "deb http://packages.cloud.google.com/apt gcsfuse-jessie main" | tee /etc/apt/sources.list.d/gcsfuse.list
# RUN wget https://packages.cloud.google.com/apt/doc/apt-key.gpg
# RUN sudo apt-key add apt-key.gpg
# RUN apt-get update -y && apt-get install -y gcsfuse

# packages install
# your packages

EXPOSE 8787
CMD ["bin/bash","/etc/init.sh"]

nginx.conf(root-pathをdomain.com/rstudio/の下に)

events {
        worker_connections 768;
}

http {

  map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
  }

        server {
                listen <port>;

                location /rstudio/ {
                rewrite ^/rstudio/(.*)$ /$1 break;
                proxy_pass http://localhost:8787;
                proxy_http_version 1.1;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection $connection_upgrade;
                proxy_read_timeout 20d;

                # Use preferably
                proxy_set_header X-RStudio-Request https://$host/$request_uri;
                proxy_set_header X-RStudio-Root-Path /rstudio;

        }
        #GCPのhealth checkを通るため
        location ~ /health {
                return 200;
        }
    }
}

init.sh

/etc/init.d/nginx restart
/init

②jupyterlab

Dockerfile

#GPUを利用するため下記のgoogle-docker-imageを使用
FROM gcr.io/deeplearning-platform-release/pytorch-gpu

# workdir set
ENV APP_HOME /app
WORKDIR $APP_HOME
COPY . ./

# gcsfuse mount
RUN apt-get update -y && apt-get install -y gnupg2
RUN echo "deb http://packages.cloud.google.com/apt gcsfuse-jessie main" | tee /etc/apt/sources.list.d/gcsfuse.list
RUN wget https://packages.cloud.google.com/apt/doc/apt-key.gpg
RUN sudo apt-key add apt-key.gpg
RUN apt-get update -y && apt-get install -y gcsfuse
RUN chmod 555 /app/gcsmount.sh

# JupyterLabをインストール
RUN apt-get install -y vim
RUN python -m pip install --upgrade pip 
RUN pip install jupyterlab 

# jupyterlab起動&baseurl設定&token設定
CMD jupyter-lab --allow-root --ip=0.0.0.0 --port=<your-port> --no-browser --LabApp.base_url=/jupyter --LabApp.token=<your-token>

③gcsmount.shの内容

GCSバケットの作成や権限設定を割愛させていただきます。

mkdir ./<your-folder>
gcsfuse -file-mode=777 -dir-mode=777 <your-bucket> ./<your-folder>

④Dockerfileのサミット

docker-image用のレジストリArtifact Registryを作成

gcloud artifacts repositories create <your-repo>\
 --project=<your-project-id>\
 --repository-format=docker\
 --location=asia-northeast1\
 --description="Docker repository"

CloudBuildでsubmit

gcloud builds submit --tag asia-northeast1-docker.pkg.dev/<your-project-id>/<your-repo>/<docker-image-name> .

8、yamlファイル

踏み台サーバから下記のyamlファイルをデプロイ

①deployment.yaml

Rstudio
parameterはこちらのサイドを参考:https://rocker-project.org/images/versioned/rstudio.html

apiVersion: apps/v1
kind: Deployment
metadata:
  name: rstudio-deployment
  namespace: <your-namespace>
spec:
  replicas: 1
  selector:
    matchLabels:
      app: rstudio
  template:
    metadata:
      labels:
        app: rstudio
    spec:
      serviceAccountName: <your-KubernetesServiceAccount>
      nodeSelector:
        iam.gke.io/gke-metadata-server-enabled: "true"
        cloud.google.com/gke-nodepool: <your-pool-name>
        cloud.google.com/gke-accelerator: nvidia-tesla-t4
      containers:
      - name: rstudio-app
        image: <your-docker-image>
        ports:
        - containerPort: <port番号>
        env:
          - name: PORT
            value: <port番号>
        securityContext:
            privileged: true

jupyterlab

apiVersion: apps/v1
kind: Deployment
metadata:
  name: jupyter-deployment
  namespace: <your-namespace>
spec:
  replicas: 1
  selector:
    matchLabels:
      app: jupyter
  template:
    metadata:
      labels:
        app: jupyter
    spec:
      serviceAccountName: <your-KubernetesServiceAccount>
      nodeSelector:
        iam.gke.io/gke-metadata-server-enabled: "true"
        cloud.google.com/gke-nodepool: <your-pool-name>
        cloud.google.com/gke-accelerator: nvidia-tesla-t4
      containers:
      - name: jupyter-app
        image: <your-docker-image>
        ports:
        - containerPort: <port番号>
        securityContext:
            privileged: true

②ManagedCertificate.yaml

apiVersion: networking.gke.io/v1 
kind: ManagedCertificate
metadata:
  name: <your-Certificate>
  namespace: <your-namespace>
spec:
  domains:
    - <your-domain>

③Oathu&IAPの設定

下記のサイドを参考しながら設定します。
https://cloud.google.com/iap/docs/enabling-kubernetes-howto?hl=ja

kubectl create secret generic <your-secret>\
 --from-literal=client_id=client_id_key \
 --from-literal=client_secret=client_secret_key

④BackendConfig.yaml

Rstudio

apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: config-rstudio
  namespace: <your-namespace>
spec:
  iap:
    enabled: true
    oauthclientCredentials:
      secretName: <your-secret>
  healthCheck:
    checkIntervalSec: 15
    timeoutSec: 15
    healthyThreshold: 1
    unhealthyThreshold: 2
    type: HTTP
    requestPath: /health
    port: <your-rstudio-port番号>

jupyter

apiVersion: cloud.google.com/v1
kind: BackendConfig
metadata:
  name: config-jupyter
  namespace: <your-namespace>
spec:
  iap:
    enabled: true
    oauthclientCredentials:
      secretName: <your-secret>
  healthCheck:
    checkIntervalSec: 15
    timeoutSec: 15
    healthyThreshold: 1
    unhealthyThreshold: 2
    type: HTTP
    requestPath: /jupyter/login?next=%2Flab%3F 
    port: <your-jupyter-port番号>

⑤service.yaml

Rstudio

apiVersion: "v1"
kind: "Service"
metadata:
  name: "rstudio-service"
  namespace: <your-namespace>
  annotations:
    beta.cloud.google.com/backend-config: '{"default": "config-rstudio"}'
spec:
  ports:
  - port: <your-rstudio-port番号>
    protocol: TCP
    targetPort: <your-rstudio-port番号>
  selector:
    app: rstudio
  type: ClusterIP

jupyter

apiVersion: "v1"
kind: "Service"
metadata:
  name: "jupyter-service"
  namespace: <your-namespace>
  annotations:
    beta.cloud.google.com/backend-config: '{"default": "config-jupyter"}'
spec:
  ports:
  - port: <your-jupyter-port番号>
    protocol: TCP
    targetPort: <your-jupyter-port番号>
  selector:
    app: jupyter
  type: ClusterIP

※typeはClusterIPでもNodePortIPでも大丈夫です。

⑥ingress.yaml

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: <your-ingress>
  namespace: <your-namespace>
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"
    kubernetes.io/ingress.global-static-ip-name: "<your-static-ip>"
    networking.gke.io/managed-certificates: "<your-Certificate>"
    kubernetes.io/ingress.allow-http: "false"
spec:
  defaultBackend:
    service:
      name: rstudio-service
      port:
        number: <your-rstudio-port番号>
  rules:
  - http:
      paths:
      - path: /jupyter
        pathType: Prefix
        backend:
          service:
            name: jupyter-service
            port:
              number: <your-jupyter-port番号>

phase2

元々はCloudRunを利用予定ですが、現在はGAE上にデプロイしています。
こちらの部分が自分の担当ではないで、同じGKEにデプロイした方が良いかもしれませんので、
一旦スキップさせていただきます。
また更新します。

四、最後

インフラの方では、terraformを使ってインフラを構築した方が良いと実感できました。
Rstudioの方については、healthcheckなど色々な問題が出てきました。
ユーザ要望によりアップデートする予定ですので、こちらの文章を何回も更新する可能性があります。

1
2
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?