在 Linode 上開啟 K8s 群集、發布應用程式並設定 https
事前準備
- OS: Ubuntu 20.04
安裝 kubectl
~$ sudo snap install kubectl --classic
安裝 helm
~$ sudo snap install helm --classic
準備一個應用程式
如果已經有現成的應用程式可以使用,就可以直接略過這個段落
最簡單的 Express Hello
~$ mkdir simple-http
~$ cd simple-http
~$ npm init -y
~$ npm install express --save
~$ touch index.js
~$ touch Dockerfile
編輯 index.js
// index.js
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => {
res.send("Hello World!")
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
編輯 Dockerfile
FROM ubuntu:latest
RUN apt update
RUN apt install -y curl
RUN curl -sL https://deb.nodesource.com/setup_12.x | bash -
RUN apt install -y nodejs
COPY . app
WORKDIR app
RUN npm install
EXPOSE 3000
CMD ["node", "index.js"]
建構並推上 Dockerhub
~$ docker build -t floatflower/simple-http:latest .
~$ docker push floatflower/simple-http:latest
開啟 Linode Kubernetes
進到 Linode Kubernetes 的新增頁面中,輸入 Cluster label 以及將地點選在想要的地方,在這裡我選擇了 Japan 2, JP(選在哪裡都沒有關係),並且將 Kubernetes Version 選為1.17
。
接著選擇自己想要的 Node Pools 中 想要的 Instance 的配置,因為只是講解過程,因此就選 3 個Linode 2GB。
為了確保 availability,Pool 中最好最少有 3 個 node。
接下來就按下 Create 等待叢集創建完畢。
取得叢集配置文件
待叢集建立完畢之後,畫面上就會出現一個xxx-kubeconfig.yaml
的連結,按下下載,並將其複製到到.kube/config
文件中。
# .kube/config
apiVersion: v1
kind: Config
preferences: {}
clusters:
- cluster:
certificate-authority-data: ...
server: https://459cc53d-e195-439e-ba28-209a0a07ec29.ap-northeast-1.linodelke.net:443
name: lke10472
users:
- name: lke10472-admin
user:
as-user-extra: {}
token: ...
contexts:
- context:
cluster: lke10472
namespace: default
user: lke10472-admin
name: lke10472-ctx
current-context: lke10472-ctx
然後試著執行:
~$ kubectl get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.128.0.1 <none> 443/TCP 4m53s
到這裡叢集就開啟完畢並且可以開始我們的佈署工作了。
開始佈署
創建一個 Deployment
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
name: simple-http-deployment # Deployment 的名稱
labels:
app: simple-http
spec:
replicas: 3 # 建立三個 pods
selector:
matchLabels:
app: simple-http # 用於讓 deployment 找到對應的 pods
template:
metadata:
labels:
app: simple-http
spec:
containers:
- name: simple-http
image: floatflower/simple-http:latest # 要使用的 docker image
imagePullPolicy: Always # 每次佈署都從 docker registry 拉取最新的
ports:
- containerPort: 3000 # Docker image 暴露出來的 port
佈署
~$ kubectl apply -f deployment.yaml
deployment.apps/simple-http-deployment created
# 透過這個指令可以取得 deployment 的當前狀態
~$ kubectl get deployments
# 以下這是我們生成的 deployment 當前的狀態
NAME READY UP-TO-DATE AVAILABLE AGE
simple-http-deployment 3/3 3 3 2m9s
~$ kubectl get pods
# 因為將 replicas 設定為 3,所以這裡會有 3 個對應的 pods 產生
NAME READY STATUS RESTARTS AGE
simple-http-deployment-64bcf9bcbf-665hq 1/1 Running 0 2m31s
simple-http-deployment-64bcf9bcbf-htffg 1/1 Running 0 2m31s
simple-http-deployment-64bcf9bcbf-qjmd2 1/1 Running 0 2m31s
創建一個 Service
接著我們要創建一個以 ClusterIP 的 Service 並透過他暴露為內部服務:
# service.yaml
apiVersion: v1
kind: Service
metadata:
name: simple-http-service
spec:
ports:
- port: 3000
protocol: TCP
targetPort: 3000
selector:
# 我們要將擁有 app=simple-http label 的 pods 作為一個內部服務,
# 這個值是在我們剛才的 deployment.yaml 中的 template.metadata.labels 已經設定過了,
# 如果有更改,記得同時更改這裡。
app: simple-http
佈署服務
~$ kubectl apply -f service.yaml
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.128.0.1 <none> 443/TCP 25m
simple-http-service ClusterIP 10.128.200.114 <none> 3000/TCP 24s
這樣我們就完成了 Service 的建立。
建立 Nginx Ingress Controller
接著我們要透過 Nginx Ingress Controller 將我們的內部服務相網際網路暴露出去
安裝 Nginx Ingress Controller
~$ helm repo add stable https://kubernetes-charts.storage.googleapis.com/
"stable" has been added to your repositories
~$ helm install nginx-ingress stable/nginx-ingress --set controller.publishService.enabled=true
WARNING: This chart is deprecated
NAME: nginx-ingress
LAST DEPLOYED: Tue Sep 15 17:54:11 2020
NAMESPACE: default
STATUS: deployed
REVISION: 1
TEST SUITE: None
...
~$ kubectl get services -A -owide
NAMESPACE NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE SELECTOR
default kubernetes ClusterIP 10.128.0.1 <none> 443/TCP 30m <none>
# 會看到 nginx-ingress-controller 已經安裝完畢,其中 139.162.95.225 這個群集的對外 IP。
default nginx-ingress-controller LoadBalancer 10.128.232.172 139.162.95.225 80:31146/TCP,443:31639/TCP 2m54s app.kubernetes.io/component=controller,app=nginx-ingress,release=nginx-ingress
default nginx-ingress-default-backend ClusterIP 10.128.11.162 <none> 80/TCP 2m54s app.kubernetes.io/component=default-backend,app=nginx-ingress,release=nginx-ingress
default simple-http-service ClusterIP 10.128.200.114 <none> 3000/TCP 5m34s app=simple-http
kube-system kube-dns ClusterIP 10.128.0.10 <none> 53/UDP,53/TCP,9153/TCP 30m k8s-app=kube-dns
設定 DNS A Record
接著我們到我們的 DNS 服務商的頁面去,將我們的網域對應到指定的 IP,我將我的網域設定為simple-http.floatflower.me
。
配置 Ingress
# ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: simple-http.floatflower.me # IP 對應的網域
http:
paths:
- backend:
serviceName: simple-http-service # 要指向的 service,現在指向我們剛才創建的 simple-http-service
servicePort: 3000
佈署
~$ kubectl apply -f ingress.yaml
ingress.networking.k8s.io/my-ingress created
~$ kubectl get ingress
# 完成之後就可以看到指定的 host 以及對外的 IP 了。
NAME HOSTS ADDRESS PORTS AGE
my-ingress simple-http.floatflower.me 139.162.95.225 80 3m18s
接著訪問我們設定的網址,就會看到一個還沒有 https 的成果:
幫網站設定 https
接著最後一步就是將 https 設定起來,在這裡我將使用 sslforfree 來創建證書,但是不管你的證書在哪裡買的都沒有問題,設定方法都一樣,申請 https 證書的方法就不另外提了,總之我們會拿到三個檔案:certificate.crt
, ca_bundle.crt
以及private.key
,首先我們需要先把ca_bunde.crt
的內容複製下來然後貼到certificate.crt
的後面:
-----BEGIN CERTIFICATE-----
<CERTIFICATE_CONTENT>
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
<CA_BUNDLE_CONTENT>
-----END CERTIFICATE-----
接著我們要先將處理過後的certificate.crt
以及private.key
透過base64編碼:
~$ cat certificate.crt | base64
LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk....
~$ cat private.key | base64
LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS....
接著將這兩個經過 base64 編碼的檔案放到以下的配置文件中:
# secret.yaml
apiVersion: v1
kind: Secret
metadata:
name: simple-http-tls
data:
tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS...
tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktL...
type: kubernetes.io/tls
佈署 secret
~$ kubectl apply -f secret.yaml
NAME TYPE DATA AGE
default-token-2cnpn kubernetes.io/service-account-token 3 59m
nginx-ingress-backend-token-pxfkz kubernetes.io/service-account-token 3 31m
nginx-ingress-token-98925 kubernetes.io/service-account-token 3 31m
sh.helm.release.v1.nginx-ingress.v1 helm.sh/release.v1 1 31m
# 這裡就是剛才新增的憑證
simple-http-tls kubernetes.io/tls 2 66s
將 secret 加入 ingress.yaml 中
# ingress.yaml
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: my-ingress
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: simple-http.floatflower.me # IP 對應的網域
http:
paths:
- backend:
serviceName: simple-http-service # 要指向的 service
servicePort: 3000
tls:
- hosts:
- simple-http.floatflower.me
secretName: simple-http-tls # 剛才新增的 Secret 名稱
佈署
~$ kubectl apply -f ingress.yaml
ingress.networking.k8s.io/my-ingress configured
~$ kubectl get ingress
NAME HOSTS ADDRESS PORTS AGE
# 這時候就會發現 my-ingress 中心增了 443 port,他已經開始監聽 http 443 port 了。
my-ingress simple-http.floatflower.me 139.162.95.225 80, 443 26m
完成之後我們就可以去訪問 https 版本的網站了
做到這裡,就代表已經完成佈署以及設定 https 的步驟了。
學會之後,記得將 Linode Kubernetes 以及相關的 NodeBalancer 和 Linode 關閉喔,不然會不斷計費。