一、目的・背景
データサイエンティストに対して、重い学習ができ、モデル共有や再利用できるようクラウド上の分析環境を用意します。
データ基盤がGCP上で構築されデータウェアハウスがBigqueryに置いていて、社内の不特定多数人利用(部署内のデータサイエンティスト及びインターン生)するため、スケーラブル且つセキュリティ的なニーズに応じて、GCP上のGKEサービスを選定しました。
VertexAIを利用しない理由としては、①Rstudio向けてGPUが提供していない;②環境内部の細かく制御ができない;③コスパの考慮、
二、事前知識
1、GCPの基礎知識(APIを有効にする、権限付与、コマンド実行など)
2、ネットワークの色々(IP、domain、portなど)
3、kubectlの知識(Docker、yamlファイルなど)
三、アーキテクチャ&実装
phase1とphase2を分けて、phase1は基本利用できるサービスを提供することで、phase2はwebappデプロイし、ユーザが好きな時間帯でIDEの立ち上げを自動化にします。
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追加
※限定公開のGoogleアクセスを「オン」にする
②ファイアウォールの作成:
deny-all-up、deny-all-down
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起動&ファイアウォールの追加
④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など色々な問題が出てきました。
ユーザ要望によりアップデートする予定ですので、こちらの文章を何回も更新する可能性があります。