勉強用としてはLoadBalancerの費用は高いため、LoadBalancerを使わずに外部にサービスを公開することを考えます。
一般的な構成
GKEのノードがPublicIPを持たない限定公開クラスタ、全てプリエンプティブルインスタンス
k8sのtype=LoadBalancerのService
GCPのCloud Load Balancing
外部からのアクセスはLoadBalancer->Serviceとなります。
案A
GKEのノードがPublicIPを持つクラスタ、1台だけ通常のインスタンスで残りはプリエンプティブルインスタンス
k8sのtype=NodePortのService
通常インスタンスにセットアップしたプロキシ
外部からのアクセスをk8s内の通常のインスタンスで実行しているプロキシで受け付けます。 プロキシからプリエンプティブルで実行しているNodePortのサービスにリクエストを流します。 SSL終端はプロキシで処理します。 Clouad NATを利用しません。 全てのノードがPublicIPを持つためその分の費用がかかります。 以前はこの方法で構築しました。
案B
GKEのノードがPublicIPを持たない限定公開クラスタ、全てプリエンプティブルインスタンス
k8sのtype=NodePortのService
GCPのVMインスタンスにセットアップしたプロキシ
外部からのアクセスをVMインスタンスにセットアップしたプロキシで受け付けます。 プロキシからプリエンプティブルで実行しているNodePortのサービスにリクエストを流します。 SSL終端はプロキシで処理します。 外部へのアクセスはCloud NATを利用します。 今回はこの方法でやります。
ポイントは、プロキシ->Serviceへの通信です。 プロキシとワーカーノードを同じネットワーク内に配置しても、ワーカーノードのIPアドレスがわからないためリクエストを渡すことができません。 ワーカーノードのIPアドレスを特定できないのであれば、プロキシから自身を除いたネットワーク内の全てのIPアドレスを対象としてしまいます。 当然、サーバーが存在しないためアクセスできないIPアドレスもあります。これに関してはヘルスチェックを実施することで生存しているIPアドレスだけを対象とします。 ワーカーノードは今のところ数台を想定しているため、割り当てるIPアドレスの範囲も狭めます。 プロキシにはtraefikを利用します。
Google Domainsでドメインを取得 ドメインを取得します。安いもので年額1,400円です。 今回はoct-26-1985.com
を取得しました。
IPアドレスを取得 1 2 gcloud compute addresses create traefik \ --region asia-northeast1
1 2 3 4 $ gcloud compute addresses list NAME ADDRESS/RANGE TYPE PURPOSE NETWORK REGION SUBNET STATUS traefik 34.146.91.23 EXTERNAL asia-northeast1 RESERVED
1 gcloud services enable dns.googleapis.com
1 EXTERNAL_IP=34.146.91.23
VPCネットワークの作成 1 2 gcloud compute networks create my-net \ --subnet-mode custom
サブネットの作成 1 2 3 4 5 6 gcloud compute networks subnets create my-subnet \ --network my-net \ --region asia-northeast1 \ --range 10.146.0.0/29 \ --secondary-range my-pods=10.4.0.0/14,my-services=10.0.32.0/20 \ --enable-private-ip-google-access
作成したネットワークは、ネットワーキング
-> VPC ネットワーク
-> VPC ネットワーク
で確認できます。
DNSレコードの作成 ゾーンを作成 https://cloud.google.com/dns/docs/zones
1 2 3 4 gcloud dns managed-zones create oct-26-1985 \ --description=oct-26-1985.com \ --dns-name=oct-26-1985.com \ --visibility=public
1 gcloud dns managed-zones list
traefik用のVMインスタンスの作成 traefik用のインスタンスを作成します。IPアドレスには10.146.0.2
を指定します。
ドキュメント:https://cloud.google.com/compute/docs/instances/create-start-instance?hl=ja#gcloud
1 2 3 4 gcloud compute instances create traefik \ --machine-type=e2-micro \ --network-interface=network-tier=PREMIUM,subnet=my-subnet,private-network-ip=10.146.0.2,address=$EXTERNAL_IP \ --tags=http-server,https-server
確認します。
1 2 3 $ gcloud compute instances list NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS traefik asia-northeast1-c e2-micro 10.146.0.2 34.146.91.23 RUNNING
SSH接続できるようにファイアウォールを設定します。ついでに80と443ポートも開けておきます。
1 2 3 $ gcloud compute firewall-rules create allow-ssh --network my-net --allow tcp:22 $ gcloud compute firewall-rules create http-server --network my-net --allow tcp:80 $ gcloud compute firewall-rules create https-server --network my-net --allow tcp:443
DNSレコードの作成 作成したVMインスタンスのPublic IPとDNSを関連付けます。
https://cloud.google.com/dns/docs/records#gcloud
1 gcloud dns record-sets transaction start --zone="oct-26-1985"
1 2 3 4 gcloud dns record-sets transaction add $EXTERNAL_IP --name="foo.oct-26-1985.com" \ --ttl="300" \ --type ="A" \ --zone="oct-26-1985"
1 gcloud dns record-sets transaction execute -z="oct-26-1985"
NameServerを確認します。
1 gcloud dns record-sets list -z="oct-26-1985"
ここで、NameServerをGoogleDomainsのCustom name serversに設定します。
traefik用のサービスアカウントを作成 LetsEncryptの証明書発行に必要です。
1 2 3 4 5 6 7 PROJECT_ID=$(gcloud config get-value project) PROJECT_NUMBER="$(gcloud projects describe ${PROJECT_ID} \ --format='get(projectNumber) ')" gcloud iam service-accounts create traefik-dns gcloud projects add-iam-policy-binding $PROJECT_ID \ --member serviceAccount:traefik-dns@$PROJECT_ID .iam.gserviceaccount.com \ --role roles/dns.admin
キーもファイルに出力します。
1 2 gcloud iam service-accounts keys create traefik-dns.json \ --iam-account traefik-dns@$PROJECT_NUMBER .iam.gserviceaccount.com
traefikをインストール まず、作成したVMにGCPのコンソールからログインします。 続いてtraefikをインストールします。
1 2 3 4 5 6 7 sudo apt install -y wget wget https://github.com/traefik/traefik/releases/download/v2.5.4/traefik_v2.5.4_linux_amd64.tar.gz tar xvzf traefik_v2.5.4_linux_amd64.tar.gz sudo mv ./traefik /usr/local/bin sudo chown root:root /usr/local/bin/traefik sudo chmod 755 /usr/local/bin/traefik sudo setcap 'cap_net_bind_service=+ep' /usr/local/bin/traefik
実行用のユーザーとグループを作成します。
1 2 3 4 5 6 sudo groupadd -g 500 traefik sudo useradd \ -g traefik --no-user-group \ --home-dir /var/www --no-create-home \ --shell /usr/sbin/nologin \ --system --uid 500 traefik
ディレクトリを作成します。
1 2 3 4 5 6 7 8 sudo mkdir /etc/traefik sudo mkdir /etc/traefik/acme sudo chown -R root:root /etc/traefik sudo chown -R traefik:traefik /etc/traefik/acme sudo mkdir /var/log/traefik sudo chown -R traefik:traefik /var/log/traefik sudo mkdir -p /usr/local/var/traefik sudo chown -R traefik:traefik /usr/local/var/traefik
設定ファイルを配置します。ファイルの内容は後述します。
1 2 sudo mv ./traefik.toml /etc/traefik/ sudo mv ./traefik.route.toml /etc/traefik/
OSのサービスとして登録します。ファイルの内容は後述します。
1 2 3 4 sudo mv ./traefik.service /etc/systemd/system/ sudo chown root:root /etc/systemd/system/traefik.service sudo chmod 644 /etc/systemd/system/traefik.service sudo systemctl daemon-reload
traefik-dns.json
を/etc/traefik/traefik-dns.json
に配置します。
traefikを実行します。
1 sudo systemctl start traefik.service
ログファイルは/var/log/traefik/traefik.log
です。エラーが出力されている確認します。
動作確認 この状態でhttp://foo.oct-26-1985.com
にアクセスするとhttps://foo.oct-26-1985.com
にリダイレクトします。 ただし、Proxyの先にサービスは存在しないのでGateway Timeout
、Service Unavailable
と表示されます。
今回はここまでです。 VMインスタンスとIPアドレスはお金かかるので気になるようなら一旦削除します。
traefik.toml 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 [global] checkNewVersion = false sendAnonymousUsage = false [entryPoints] [entryPoints.web] address = ":80" [entryPoints.websecure] address = ":443" [log] level = "ERROR" filePath = "/var/log/traefik/traefik.log" [accessLog] filePath = "/var/log/traefik/access.log" [api] dashboard = false [ping] [providers] [providers.file] watch = true filename = "/etc/traefik/traefik.route.toml" debugLogGeneratedTemplate = true [certificatesResolvers.le.acme] email = "***@gmail.com" storage = "/etc/traefik/acme/acme.json" [certificatesResolvers.le.acme.dnsChallenge] provider = "gcloud"
traefik.route.toml https://doc.traefik.io/traefik/v2.4/middlewares/redirectscheme/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 # Dynamic Configuration [http.routers] [http.routers.my-api] rule = "Host(`foo.oct-26-1985.com`)" entryPoints = ["web"] service = "my-service" middlewares = ["https-redirect"] [http.routers.my-api-ssl] rule = "Host(`foo.oct-26-1985.com`)" entryPoints = ["websecure"] service = "my-service" middlewares = [] [http.routers.my-api-ssl.tls] certResolver = "le" [[http.routers.my-api-ssl.tls.domains]] main = "foo.oct-26-1985.com" # sans = ["*.oct-26-1985.com"] [http.services] [http.services.my-service.loadBalancer] [[http.services.my-service.loadBalancer.servers]] url = "http://10.146.0.3/" [[http.services.my-service.loadBalancer.servers]] url = "http://10.146.0.4/" [[http.services.my-service.loadBalancer.servers]] url = "http://10.146.0.5/" [[http.services.my-service.loadBalancer.servers]] url = "http://10.146.0.6/" [[http.services.my-service.loadBalancer.servers]] url = "http://10.146.0.7/" [http.services.my-service.loadBalancer.healthCheck] path = "/healthz" [http.middlewares] [http.middlewares.https-redirect.redirectScheme] scheme = "https" permanent = true
traefik.service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 [Unit] Description=traefik proxy After=network-online.target Wants=network-online.target systemd-networkd-wait-online.service [Service] Restart=on-abnormal ; User and group the process will run as. User=traefik Group=traefik Environment=GOOGLE_APPLICATION_CREDENTIALS=/etc/traefik/traefik-dns.json ; WorkingDirectory=/usr/local/var/traefik ; Always set "-root" to something safe in case it gets forgotten in the traefikfile. ExecStart=/usr/local/bin/traefik --configfile=/etc/traefik/traefik.toml ; Limit the number of file descriptors; see `man systemd.exec` for more limit settings. LimitNOFILE=1048576 ; Use private /tmp and /var/tmp, which are discarded after traefik stops. PrivateTmp=true ; Use a minimal /dev (May bring additional security if switched to 'true', but it may not work on Raspberry Pi's or other devices, so it has been disabled in this dist.) PrivateDevices=false ; Hide /home, /root, and /run/user. Nobody will steal your SSH-keys. ProtectHome=true ; Make /usr, /boot, /etc and possibly some more folders read-only. ProtectSystem=full ; … except /etc/ssl/traefik, because we want Letsencrypt-certificates there. ; This merely retains r/w access rights, it does not add any new. Must still be writable on the host! ReadWriteDirectories=/etc/traefik/acme ; The following additional security directives only work with systemd v229 or later. ; They further restrict privileges that can be gained by traefik. Uncomment if you like. ; Note that you may have to add capabilities required by any plugins in use. CapabilityBoundingSet=CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_BIND_SERVICE NoNewPrivileges=true [Install] WantedBy=multi-user.target