- virtualboxのインストール
- kubectlのインストール
- dockerのインストール(VirtualBox型Kubernetesでは不要)
- minikubeのインストール
- kubernetesの構築
- Istioの構築
- サンプルアプリケーション
Istioデモ環境構築のメモです。
minikubeでvirtualbox版Kubernetesを構築し、Istioをインストールします。
virtualboxのインストール
$ cat /etc/os-release NAME="Ubuntu" VERSION="21.04 (Hirsute Hippo)" #virtualboxのインストール $ sudo apt-get install virtualbox
kubectlのインストール
#curlのインストール $ sudo apt install curl #kubectlのダウンロード $ curl -LO "https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl" #実行権限付与 $ chmod +x ./kubectl #パスを通す $ sudo mv ./kubectl /usr/local/bin/kubectl kubectlの動作確認 $ kubectl version --client Client Version: version.Info{Major:"1", Minor:"22", GitVersion:"v1.22.0", GitCommit:"c2b5237ccd9c0f1d600d3072634ca66cefdf272f", GitTreeState:"clean", BuildDate:"2021-08-04T18:03:20Z", GoVersion:"go1.16.6", Compiler:"gc", Platform:"linux/amd64"}
dockerのインストール(VirtualBox型Kubernetesでは不要)
#パッケージ更新 $ sudo apt update #必要なパッケージをインストール $ sudo apt install apt-transport-https ca-certificates software-properties-common #dockerリポジトリの追加 $ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - $ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu focal stable" #パッケージ更新 $ sudo apt update #dockerのインストール $ sudo apt install docker-ce #dockerの動作確認 $ systemctl status docker ● docker.service - Docker Application Container Engine Loaded: loaded (/lib/systemd/system/docker.service; enabled; vendor preset: enabled) Active: active (running) since Mon 2021-08-09 14:18:39 JST; 3h 4min ago TriggeredBy: ● docker.socket Docs: https://docs.docker.com Main PID: 1436 (dockerd) Tasks: 12 Memory: 12.2M CGroup: /system.slice/docker.service └─1436 /usr/bin/dockerd -H fd:// --containerd=/run/containerd/containerd.sock $ docker version user@user-HP:~$ docker version Client: Docker Engine - Community Version: 20.10.8 API version: 1.41 Go version: go1.16.6 Git commit: 3967b7d Built: Fri Jul 30 19:54:27 2021 OS/Arch: linux/amd64 Context: default Experimental: true #sudo を省略するためにuserをDockerに追加 $ sudo usermod -aG docker user #ログアウト後に再起動 $ sudo systemctl restart docker
minikubeのインストール
#minikubeバイナリのダウンロード $ curl -Lo minikube https://storage.googleapis.com/minikube/releases/latest/minikube-linux-amd64 #実行権限付与 $ chmod +x minikube #パス設定 $ sudo mkdir -p /usr/local/bin/ $ sudo install minikube /usr/local/bin/
kubernetesの構築
virtualbox版
node数を3とすると、マスターノードが1つ、ワーカノードが2つ、合計3つのVMが起動します。
今回のIstio構築では、実機構成に近いvirtualboxパターンを利用します。
#kubernetesの構築 $ minikube start --vm-driver=virtualbox node 3
#ノード情報を確認 $ kubectl get nodes -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME minikube Ready control-plane,master 3h48m v1.21.2 192.168.99.100Buildroot 2020.02.12 4.19.182 docker://20.10.6 minikube-m02 Ready 3h21m v1.21.2 192.168.99.101 Buildroot 2020.02.12 4.19.182 docker://20.10.6 minikube-m03 Ready 3h v1.21.2 192.168.99.102 Buildroot 2020.02.12 4.19.182 docker://20.10.6
CONTAINER-RUNTIMEがdockerになっていますが、これはホストOSのDockerではなく、virtualboxのvm上で動作しているDockerになります。
docker版
minikube start --driver=docker
Istioの構築
以降、公式の手順でIstio環境を構築します。
Istio / Getting Started
Istioパッケージのダウンロード
$ curl -L https://istio.io/downloadIstio | sh - $ cd istio-1.10.3 #istioctlのパス設定 $ sudo mv bin/istioctl /usr/local/bin
サンプル設定の適用
$ istioctl install --set profile=demo -y #Envoyサイドカープロキシの自動挿入設定 $ kubectl label namespace default istio-injection=enabled
サンプルアプリケーション
$ kubectl apply -f samples/bookinfo/platform/kube/bookinfo.yaml
構成ファイルの中身を確認してみます。Details service、Ratings service、Reviews service、Productpage servicesの4つの(Kubernetes)Serviceが定義されています。
具体的には、kind: Deploymentがアプリケーションポッド(コンテナ)、kind: ServiceAccountがポッドに割り当てるサービスアカウント(AWS IAMのようなもの)、kind: Serviceがコンテナへのポートバインドを表しています。
特にこのService(ClusterIP)は、Kubernetes上でのみ通用する仮想IPアドレス(ポッドに割り当てられるClusterIP)の9080ポートとコンテナ本体の9080ポートを紐付けています。
これによって、Kubernetes内のポッドからClusterIP:9080にアクセスすればコンテナ本体の9080ポートにアクセスできることになります。ただし、外部との疎通はありません。
$ less samples/bookinfo/platform/kube/bookinfo.yaml ################################################################################################## # Details service ################################################################################################## apiVersion: v1 kind: Service metadata: name: details labels: app: details service: details spec: ports: - port: 9080 name: http selector: app: details apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-details labels: account: details apiVersion: apps/v1 kind: Deployment metadata: name: details-v1 labels: app: details version: v1 spec: replicas: 1 selector: matchLabels: app: details version: v1 template: metadata: labels: app: details version: v1 spec: serviceAccountName: bookinfo-details containers: - name: details image: docker.io/istio/examples-bookinfo-details-v1:1.16.2 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 securityContext: runAsUser: 1000 ################################################################################################## # Ratings service ################################################################################################## apiVersion: v1 kind: Service metadata: name: ratings labels: app: ratings service: ratings spec: ports: - port: 9080 name: http selector: app: ratings apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-ratings labels: account: ratings apiVersion: apps/v1 kind: Deployment metadata: name: ratings-v1 labels: app: ratings version: v1 spec: replicas: 1 selector: matchLabels: app: ratings version: v1 template: metadata: labels: app: ratings version: v1 spec: serviceAccountName: bookinfo-ratings containers: - name: ratings image: docker.io/istio/examples-bookinfo-ratings-v1:1.16.2 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 securityContext: runAsUser: 1000 ################################################################################################## # Reviews service ################################################################################################## apiVersion: v1 kind: Service metadata: name: reviews labels: app: reviews service: reviews spec: ports: - port: 9080 name: http selector: app: reviews apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-reviews labels: account: reviews apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v1 labels: app: reviews version: v1 spec: replicas: 1 selector: matchLabels: app: reviews version: v1 template: metadata: labels: app: reviews version: v1 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v1:1.16.2 imagePullPolicy: IfNotPresent env: - name: LOG_DIR value: "/tmp/logs" ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp - name: wlp-output mountPath: /opt/ibm/wlp/output securityContext: runAsUser: 1000 volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v2 labels: app: reviews version: v2 spec: replicas: 1 selector: matchLabels: app: reviews version: v2 template: metadata: labels: app: reviews version: v2 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v2:1.16.2 imagePullPolicy: IfNotPresent env: - name: LOG_DIR value: "/tmp/logs" ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp - name: wlp-output mountPath: /opt/ibm/wlp/output securityContext: runAsUser: 1000 volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} apiVersion: apps/v1 kind: Deployment metadata: name: reviews-v3 labels: app: reviews version: v3 spec: replicas: 1 selector: matchLabels: app: reviews version: v3 template: metadata: labels: app: reviews version: v3 spec: serviceAccountName: bookinfo-reviews containers: - name: reviews image: docker.io/istio/examples-bookinfo-reviews-v3:1.16.2 imagePullPolicy: IfNotPresent env: - name: LOG_DIR value: "/tmp/logs" ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp - name: wlp-output mountPath: /opt/ibm/wlp/output securityContext: runAsUser: 1000 volumes: - name: wlp-output emptyDir: {} - name: tmp emptyDir: {} ################################################################################################## # Productpage services ################################################################################################## apiVersion: v1 kind: Service metadata: name: productpage labels: app: productpage service: productpage spec: ports: - port: 9080 name: http selector: app: productpage apiVersion: v1 kind: ServiceAccount metadata: name: bookinfo-productpage labels: account: productpage apiVersion: apps/v1 kind: Deployment metadata: name: productpage-v1 labels: app: productpage version: v1 spec: replicas: 1 selector: matchLabels: app: productpage version: v1 template: metadata: labels: app: productpage version: v1 spec: serviceAccountName: bookinfo-productpage containers: - name: productpage image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2 imagePullPolicy: IfNotPresent ports: - containerPort: 9080 volumeMounts: - name: tmp mountPath: /tmp securityContext: runAsUser: 1000 volumes: - name: tmp emptyDir: {}
上記の構成ファイルから以下のアプリケーションがデプロイされます。
初期構成の確認
ここでkubernetesのnamespaceを確認すると、istio-systemというnamespaceが作成されています。
その他はkubernetesのデフォルトnamespaceになります。特に指定がない場合はアプリケーションポッドはdefault namespaceにデプロイされます。
ちなみにこのnamespaceはlinux kernelのnamespaceとは別物でkubernetes固有の論理単位になります。
$ kubectl get namespaces NAME STATUS AGE default Active 4h12m istio-system Active 131m kube-node-lease Active 4h12m kube-public Active 4h12m kube-system Active 4h12m
各namespaceにデプロイされたpodを確認します。
$ kubectl get pods -o wide -n istio-system NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES istio-egressgateway-5547fcc8fc-zpd72 1/1 Running 0 136m 10.244.2.4 minikube-m03istio-ingressgateway-8f568d595-s68gh 1/1 Running 0 136m 10.244.2.5 minikube-m03 istiod-568d797f55-6rvp5 1/1 Running 0 136m 10.244.1.3 minikube-m02
istio-system namespaceにはistio-egressgateway(出口)とistio-ingressgateway(入口)がポッドとしてデプロイされています。
Kubernetes cluster外と通信するには、これらのエンドポイントにアクセスすることになります。
#ポッド一覧 $ kubectl get pods -o wide -n default NAME READY STATUS RESTARTS AGE IP NODE NOMINATED NODE READINESS GATES details-v1-79f774bdb9-dxbmb 2/2 Running 0 130m 10.244.1.7 minikube-m02productpage-v1-6b746f74dc-mt5sn 2/2 Running 0 130m 10.244.1.9 minikube-m02 ratings-v1-b6994bb9-9gmdq 2/2 Running 0 130m 10.244.2.9 minikube-m03 reviews-v1-545db77b95-zhr6v 2/2 Running 0 130m 10.244.1.8 minikube-m02 reviews-v2-7bf8c9648f-4n9c6 2/2 Running 0 130m 10.244.0.3 minikube reviews-v3-84779c7bbc-8trsh 2/2 Running 0 130m 10.244.2.10 minikube-m03 #ポッドの詳細情報 $ kubectl describe pod productpage-v1-6b746f74dc-mt5sn Name: productpage-v1-6b746f74dc-mt5sn Namespace: default Priority: 0 Node: minikube-m02/192.168.99.101 Start Time: Mon, 09 Aug 2021 16:11:35 +0900 Labels: app=productpage ... Containers: productpage: Container ID: docker://25ae5f0dbd0984a2d8a675ffbe71592279ce936215b12c417aa06d543e201919 Image: docker.io/istio/examples-bookinfo-productpage-v1:1.16.2 Image ID: docker-pullable://istio/examples-bookinfo-productpage-v1@sha256:63ac3b4fb6c3ba395f5d044b0e10bae513afb34b9b7d862b3a7c3de7e0686667 Port: 9080/TCP Host Port: 0/TCP State: Running Started: Mon, 09 Aug 2021 16:11:36 +0900 Ready: True Restart Count: 0 Environment: Mounts: /tmp from tmp (rw) /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-f7hl9 (ro) istio-proxy: Container ID: docker://f48cac26a0bcce1c056044843c44389ec3cb7d55e477d7a39014b8c0746c9b61 Image: docker.io/istio/proxyv2:1.10.3 Image ID: docker-pullable://istio/proxyv2@sha256:a78b7a165744384d95f75d157c34e02d6b4355aaf8fe2a2c75914832bdf764e8 Port: 15090/TCP Host Port: 0/TCP Args: proxy sidecar --domain $(POD_NAMESPACE).svc.cluster.local --serviceCluster productpage.$(POD_NAMESPACE) --proxyLogLevel=warning --proxyComponentLogLevel=misc:error --log_output_level=default:info --concurrency 2 State: Running Started: Mon, 09 Aug 2021 16:11:37 +0900 ...
default namespaceには、アプリケーションを構成するDetails、Ratings、Reviews、Productpageのポッドが配置されています。
さらに、productpage-v1-6b746f74dc-mt5snポッドの構成情報を確認すると、productpageコンテナとistio-proxyコンテナの2つのコンテナが動作しています。
productpageコンテナがアプリケーション本体でistio-proxyコンテナはポッド間ルーティングを行うプロキシになります。つまり、Istioアプリケーションポッドの通信はすべてistio-proxyを経由します。
次にDetails(Pod)、Ratings(Pod)、Reviews(Pod)、Productpage(Pod)に割り当てられたserviceも確認します。
エンドポイントのClusterIPはVMのIPと異なることがわかります。これらを紐付けるのが後述するistio-egressgateway(出口)とistio-ingressgateway(入口)になります。
$ kubectl get services -n default NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE details ClusterIP 10.102.12.289080/TCP 3h7m kubernetes ClusterIP 10.96.0.1 443/TCP 5h14m productpage ClusterIP 10.103.127.69 9080/TCP 3h7m ratings ClusterIP 10.102.176.254 9080/TCP 3h7m reviews ClusterIP 10.109.191.110 9080/TCP 3h7m
#ノード情報を確認 $ kubectl get nodes -o wide NAME STATUS ROLES AGE VERSION INTERNAL-IP EXTERNAL-IP OS-IMAGE KERNEL-VERSION CONTAINER-RUNTIME minikube Ready control-plane,master 3h48m v1.21.2 192.168.99.100Buildroot 2020.02.12 4.19.182 docker://20.10.6 minikube-m02 Ready 3h21m v1.21.2 192.168.99.101 Buildroot 2020.02.12 4.19.182 docker://20.10.6 minikube-m03 Ready 3h v1.21.2 192.168.99.102 Buildroot 2020.02.12 4.19.182 docker://20.10.6
外部公開
デプロイしたアプリケーションService(Pod)をIstio gatewayに関連付けます。
これは
#紐付け $ kubectl apply -f samples/bookinfo/networking/bookinfo-gateway.yaml #構成ファイルの内容 $ less samples/bookinfo/networking/bookinfo-gateway.yaml apiVersion: networking.istio.io/v1alpha3 kind: Gateway metadata: name: bookinfo-gateway spec: selector: istio: ingressgateway # use istio default controller servers: - port: number: 80 name: http protocol: HTTP hosts: - "*" apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata: name: bookinfo spec: hosts: - "*" gateways: - bookinfo-gateway http: - match: - uri: exact: /productpage - uri: prefix: /static - uri: exact: /login - uri: exact: /logout - uri: prefix: /api/v1/products route: - destination: host: productpage port: number: 9080
istio-ingressgatewayの設定を確認すると、http2のポートが30800、httpsのポートが31633であることが分かります。
これは、node IP:30800にアクセスすると、(istio-ingressgatewayポッドの)コンテナの8080に転送されることを表します。
$ kubectl -n istio-system get service istio-ingressgateway -o json ... "spec": { "clusterIP": "10.106.165.119", "clusterIPs": [ "10.106.165.119" ], "externalTrafficPolicy": "Cluster", "ipFamilies": [ "IPv4" ], "ipFamilyPolicy": "SingleStack", "ports": [ { "name": "status-port", "nodePort": 31759, "port": 15021, "protocol": "TCP", "targetPort": 15021 }, { "name": "http2", "nodePort": 30800, "port": 80, "protocol": "TCP", "targetPort": 8080 }, { "name": "https", "nodePort": 31633, "port": 443, "protocol": "TCP", "targetPort": 8443 }, { "name": "tcp", "nodePort": 32007, "port": 31400, "protocol": "TCP", "targetPort": 31400 }, { "name": "tls", "nodePort": 30666, "port": 15443, "protocol": "TCP", "targetPort": 15443 } ], "selector": { "app": "istio-ingressgateway", "istio": "ingressgateway" }, "sessionAffinity": "None", "type": "LoadBalancer" ...
続いてistio-ingressgatewayポッドのコンテナを確認します。
確かに8080で待ち受けています。このコンテナがistio-ingressgatewayの本体(Envoy)です。
$ kubectl describe pod -n istio-system istio-ingressgateway-8f568d595-s68gh Name: istio-ingressgateway-8f568d595-s68gh Namespace: istio-system Priority: 0 Node: minikube-m03/192.168.99.102 Start Time: Mon, 09 Aug 2021 16:06:47 +0900 Labels: app=istio-ingressgateway ... Status: Running IP: 10.244.2.5 IPs: IP: 10.244.2.5 Controlled By: ReplicaSet/istio-ingressgateway-8f568d595 Containers: istio-proxy: Container ID: docker://1396e222cd2aa759fff144ef299f4a19910b47ea4891e8d22caee5ad9e973ee8 Image: docker.io/istio/proxyv2:1.10.3 Image ID: docker-pullable://istio/proxyv2@sha256:a78b7a165744384d95f75d157c34e02d6b4355aaf8fe2a2c75914832bdf764e8 Ports: 15021/TCP, 8080/TCP, 8443/TCP, 31400/TCP, 15443/TCP, 15090/TCP Host Ports: 0/TCP, 0/TCP, 0/TCP, 0/TCP, 0/TCP, 0/TCP ...
サイドカープロキシの設定にはlistener(受信),router(紐付け),cluster(送信),endpoint(ノード)があります。
まずistio-ingressgatewayの受信ポートを確認します。
8080で受信したトラフィックの送信先がRoute: http.80となっています。
補足ですがingressgatewaysの設定で8080が80にマップされています。
つまり、ingressgatewaysポッドが8080で受信したトラフィックは80に転送される形になります。
$ istioctl proxy-config listener istio-ingressgateway-8f568d595-s68gh -n istio-system ADDRESS PORT MATCH DESTINATION 0.0.0.0 8080 ALL Route: http.80 0.0.0.0 15021 ALL Inline Route: /healthz/ready* 0.0.0.0 15090 ALL Inline Route: /stats/prometheus*
ingressGateways: - name: istio-ingressgateway enabled: true k8s: resources: requests: cpu: 10m memory: 40Mi service: ports: ## You can add custom gateway ports in user values overrides, but it must include those ports since helm replaces. # Note that AWS ELB will by default perform health checks on the first port # on this list. Setting this to the health check port will ensure that health # checks always work. https://github.com/istio/istio/issues/12503 - port: 15021 targetPort: 15021 name: status-port - port: 80 targetPort: 8080 name: http2 - port: 443 targetPort: 8443 name: https - port: 31400 targetPort: 31400 name: tcp # This is the port where sni routing happens - port: 15443 targetPort: 15443 name: tls
次にrouteを確認するとhttp.80がVIRTUAL SERVICEに転送されています。
つまりVIRTUAL SERVICE(80→9080)によりproductpageサービスノードの9080に転送されます。
$ istioctl proxy-config route istio-ingressgateway-8f568d595-s68gh -n istio-system NAME DOMAINS MATCH VIRTUAL SERVICE http.80 * /productpage bookinfo.default http.80 * /static* bookinfo.default http.80 * /login bookinfo.default http.80 * /logout bookinfo.default http.80 * /api/v1/products* bookinfo.default * /stats/prometheus* * /healthz/ready*
VIRTUAL SERVICEによってproductpageサービスノードの9080にトラフィックは到達しますが、直接アプリケーションポッドには転送されません。
productpageポッドのサイドカープロキシの設定を確認すると、15001と15006の受信ポートがあります。
すべての送受信トラフィックはiptablesの設定で送信時はプロキシの15001、受信時はプロキシの15006にリダイレクションされます。
今回は15006 Trans: raw_buffer; Addr: *:9080に該当するため、 プロキシからproductpageの9080に転送されます。
これでやっとアプリケーションポッドにリクエストが到達します。
https://speakerdeck.com/110y/tour-of-istio?slide=39
$ istioctl proxy-config listener productpage-v1-6b746f74dc-mt5sn ... 0.0.0.0 15001 ALL PassthroughCluster 0.0.0.0 15001 Addr: *:15001 Non-HTTP/Non-TCP 0.0.0.0 15006 Addr: *:15006 Non-HTTP/Non-TCP 0.0.0.0 15006 Trans: tls; App: istio-http/1.0,istio-http/1.1,istio-h2; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4 0.0.0.0 15006 Trans: raw_buffer; App: HTTP; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4 0.0.0.0 15006 Trans: tls; App: TCP TLS; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4 0.0.0.0 15006 Trans: raw_buffer; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4 0.0.0.0 15006 Trans: tls; Addr: 0.0.0.0/0 InboundPassthroughClusterIpv4 0.0.0.0 15006 Trans: tls; App: istio,istio-peer-exchange,istio-http/1.0,istio-http/1.1,istio-h2; Addr: *:9080 Cluster: inbound|9080|| 0.0.0.0 15006 Trans: raw_buffer; Addr: *:9080 Cluster: inbound|9080|| ... $ istioctl proxy-config route productpage-v1-6b746f74dc-mt5sn NAME DOMAINS MATCH VIRTUAL SERVICE kube-dns.kube-system.svc.cluster.local:9153 kube-dns.kube-system /* 80 istio-egressgateway.istio-system /* 80 istio-ingressgateway.istio-system /* 9080 details /* 9080 productpage /* 9080 ratings /* 9080 reviews /* 15010 istiod.istio-system /* 15014 istiod.istio-system /* istio-ingressgateway.istio-system.svc.cluster.local:15021 istio-ingressgateway.istio-system /* * /stats/prometheus* InboundPassthroughClusterIpv4 * /* inbound|9080|| * /* inbound|9080|| * /* $ istioctl proxy-config cluster productpage-v1-6b746f74dc-k5lwk SERVICE FQDN PORT SUBSET DIRECTION TYPE DESTINATION RULE 9080 - inbound ORIGINAL_DST
これで透過型プロキシを経由したルーティングを確認しました。
ブラウザで192.168.99.100:30800(nodeport)にアクセスすると以下のようなサイトが表示されます。