GeekFactory

int128.hatenablog.com

Kubernetes nodesをMulti-AZからSingle-AZに移行した

kopsで運用しているKubernetes nodesをMulti-AZからSingle-AZに移行したので作業メモを残しておきます。

us-west-2b や us-west-2c にあるNodesとPersistent Volumesを us-west-2a に移行します。Masterは us-west-2a のままにします。

kops 1.8.0, Kubernetes 1.8.4で確認しています。

Nodesの移行

kops

NodesのAvailability Zoneを us-west-2a,us-west-2b,us-west-2c から us-west-2a のみに変更します。

% kops get ig
Using cluster from kubectl context: kops.hidetake.org

NAME            ROLE    MACHINETYPE MIN MAX ZONES
master-us-west-2a   Master  t2.micro    1   1   us-west-2a
nodes           Node    t2.micro    2   2   us-west-2a,us-west-2b,us-west-2c

% kops edit ig nodes
Using cluster from kubectl context: kops.hidetake.org

% kops get ig
Using cluster from kubectl context: kops.hidetake.org

NAME            ROLE    MACHINETYPE MIN MAX ZONES
master-us-west-2a   Master  t2.micro    1   1   us-west-2a
nodes           Node    t2.micro    2   2   us-west-2a

Clusterのサブネットも us-west-2a,us-west-2b,us-west-2c から us-west-2a のみに変更します。

% kops edit cluster

変更内容を適用します。

% kops update cluster
Using cluster from kubectl context: kops.hidetake.org

I1228 10:58:37.403838   77384 executor.go:91] Tasks: 0 done / 73 total; 31 can run
I1228 10:58:38.933794   77384 executor.go:91] Tasks: 31 done / 73 total; 24 can run
I1228 10:58:40.071632   77384 executor.go:91] Tasks: 55 done / 73 total; 16 can run
I1228 10:58:41.554352   77384 executor.go:91] Tasks: 71 done / 73 total; 2 can run
I1228 10:58:41.711652   77384 executor.go:91] Tasks: 73 done / 73 total; 0 can run
Will modify resources:
  AutoscalingGroup/nodes.kops.hidetake.org
    Subnets                  [id:subnet-67dc353d, id:subnet-2daa9a4b, id:subnet-5210431a] -> [name:us-west-2a.kops.hidetake.org id:subnet-5210431a]

Must specify --yes to apply changes

% kops update cluster --yes
Using cluster from kubectl context: kops.hidetake.org

I1228 10:58:54.651294   77386 executor.go:91] Tasks: 0 done / 73 total; 31 can run
I1228 10:58:56.218052   77386 executor.go:91] Tasks: 31 done / 73 total; 24 can run
I1228 10:58:57.354869   77386 executor.go:91] Tasks: 55 done / 73 total; 16 can run
I1228 10:58:58.727423   77386 executor.go:91] Tasks: 71 done / 73 total; 2 can run
I1228 10:58:59.248501   77386 executor.go:91] Tasks: 73 done / 73 total; 0 can run
I1228 10:58:59.248636   77386 dns.go:153] Pre-creating DNS records
I1228 10:59:00.687645   77386 update_cluster.go:248] Exporting kubecfg for cluster
kops has set your kubectl context to kops.hidetake.org

Cluster changes have been applied to the cloud.


Changes may require instances to restart: kops rolling-update cluster

% kops rolling-update cluster
Using cluster from kubectl context: kops.hidetake.org

NAME            STATUS  NEEDUPDATE  READY   MIN MAX NODES
master-us-west-2a   Ready   0       1   1   1   1
nodes           Ready   0       4   2   2   4

No rolling-update required.

上記を実行するとAuto Scaling Groupの設定が変更されます。

AWS management console

EC2インスタンスの一覧を見ていると、Nodesが以下のように変化することが分かります。

  • us-west-2a で新しいnodeが起動する。
  • us-west-2a で新しいnodeが起動する。
  • us-west-2b にある既存のnodeがterminateする。
  • us-west-2c にある既存のnodeがterminateする。

すべて終わるまで10分ぐらいかかりました。

Kubernetes Dashboard

Podsが以下のように変化することが分かります。

  • us-west-2b や us-west-2c で実行されているPodsが終了する。
  • 上記のPodsが us-west-2a で実行される。

Persistent Volumesの移行

すべてのNodesが us-west-2a に移行したため、us-west-2b や us-west-2c に存在するPersistent Volumes(EBS)に依存するPodを実行しようとすると、以下のエラーが発生します。

No nodes are available that match all of the predicates: NoVolumeZoneConflict (3), PodToleratesNodeTaints (1).

動的に確保された(Dynamically provisioned)Persistent Volumesを移行するには細心の注意が必要です。移行に先立って、あらかじめ以下の関係を書き出しておきます。

  • Deployment
  • Persistent Volume Claim
  • Persistent Volume
  • EBS Volume

Persistent Volumesを別のAZに移行するには以下の手順が必要です。

  1. Persistent Volumeの persistentVolumeReclaimPolicyRetain に変更する。これはPersistent Volume Claimを削除した時にEBS Volumeも一緒に削除されるのを防ぐため。
  2. Persistent Volumeに依存するPersistent Volume ClaimとDeploymentを削除する。
  3. Persistent Volumeのステータスが Released になったことを確認する。
  4. EBS Volumeのステータスが available になったことを確認する。
  5. 旧AZのEBS VolumeからSnapshotを作成する。
  6. Snapshotから新AZでEBS Volumeを作成する。
  7. Persistent Volumeの以下の属性を変更する。この時点では新しいEBS Volumeはアタッチされない。
    • failure-domain.beta.kubernetes: 新しいAZ(例:us-west-2a
    • volume-id: 新しいEBS Volume(例:aws://us-west-2a/vol-0123456789
  8. Persistent Volume Claimを再作成する。Persistent Volumeを紐付けるため、以下の属性を指定しておく。
    • volumeName: Persistent Volumeの名前(例:pvc-1801704a-eb70-11e7-a258-060098a4bc1c)
  9. Persistent Volumeの以下の属性を変更する。逆方向の紐付けも直さないと正しくアタッチされない。
    • claimRef: uid: Persistent Volume Claimの uid
    • claimRef: resourceVersion: Persistent Volume Claimの resourceVersion
  10. Persistent Volumeのステータスが Bound になったことを確認する。
  11. EBS Volumeのステータスが in-use になったことを確認する。

順を追ってコマンドを説明します。

Persistent Volumeの紐付け解除とPersistent Volume Claimの削除

# Persistent Volumeの `persistentVolumeReclaimPolicy` を `Retain` に変更
kubectl edit pv

# Persistent Volumeに依存するPersistent Volume ClaimとDeploymentを削除
kubectl delete pvc/example deployment/example

# Persistent Volumeのステータスが Released になったことを確認
kubectl get pv

EBS VolumeのAZ移行

# 旧Volumeの確認(Nameの確認)
aws ec2 describe-volumes

# 旧AZのEBS VolumeからSnapshotを作成
aws ec2 create-snapshot --volume-id vol-0123456789 --description kops.example.com-dynamic-pvc-1801704a-eb70-11e7-a258-060098a4bc1c

タグは移行しなくても問題ないようですが、後から何のVolumeか分からなくなると辛いので、タグもそのまま移行することをおすすめします。

# 旧Volumeの確認(Tagsの確認)
aws ec2 describe-volumes

# 新Volumeの作成
aws ec2 create-volume --snapshot-id snap-0123456789 --availability-zone us-west-2a --volume-type gp2 --size 10 --tag-specifications '[{"ResourceType": "volume", "Tags": [上記でダンプした旧ボリュームのTags]}]'

Persistent Volumeの再紐付け

# Persistent Volumeの属性を変更
kubectl edit pv

# Persistent Volume Claimを再作成(volumeName指定)
vim example.yaml
kubectl apply -f example.yaml

# Persistent Volumeの属性を変更
kubectl edit pv

# Persistent Volumeのステータスが Bound になったことを確認
kubectl get pv

まとめ

  • NodesのAZ移行は kops で簡単にできる。
  • Persistent VolumesのAZ移行は辛い。特に、dynamically provisionedのものは関連を壊さないように細心の注意が必要になる。


入門 Kubernetes

入門 Kubernetes

コンテナ・ベース・オーケストレーション Docker/Kubernetesで作るクラウド時代のシステム基盤

コンテナ・ベース・オーケストレーション Docker/Kubernetesで作るクラウド時代のシステム基盤

インターネットとVPC内部から同一FQDNでアクセス可能な構成

AWSでALBを使う場合に、Security Groupでインターネットからアクセス可能なIPアドレスを制限しながら、VPC内部から同じFQDNでサービスにアクセスできるようにしたい。

TL;DR

Requirements:

  • インターネットから https://api.example.com でサービスにアクセスできること
  • EC2から https://api.example.com でサービスにアクセスできること
  • インターネット側はSecurity Groupでアクセスを制限する

Solution:

Internet
↓
Route53 Public Hosted Zone
↓
Internet Gateway
↓
Security Group (internet-facing)
↓
ALB (internet-facing)
↓
EC2
VPC ENI
↓
Route53 Private Hosted Zone
↓
Security Group (internal)
↓
ALB (internal)
↓
EC2

VPC内部からALBへの通信

EC2からinternet-facing ALBにリクエストを送ると、EC2に紐付いたPublic IPアドレスが送信元になります。ALBにアクセス可能なIPアドレスを制限する場合、内側からアクセス可能にするにはEC2のPublic IPアドレスも追加する必要があります。もちろん、EC2同士がPrivate IPアドレスで通信すれば問題ないのですが、Auto Scaling Groupを利用している場合はIPアドレスが変わってしまうため困難です。

EC2
↓
Security Group
↓
ALB (internet-facing)
↓
Auto Scaling Group
↓
EC2

下記のような場合、VPCの内側からALBへの通信が必要になります。

  • 開発ツールの設定にGitリポジトリのURLを書いており、外側からも内側からも同じURLでGitリポジトリにアクセスしたい。
  • OAuth2やOpenID Connectを使っており、ブラウザから見えるURLと同じFQDNで authorization code を取得できる必要がある。
  • VPC LambdaからALBにアクセスしたい。

もともとは内製のツールでAuto Scaling Group配下のEIPを固定し、Security GroupにEIPを入れる方式を採用していましたが、 id:mumoshu さんに別の解決法があるよと教えていただきました。

Private Hosted Zoneとinternal ALBによる解決法

VPC内部からALBへの通信を実現するため、別のルートを作る方法があります。具体的には、VPCの内側からサービスにアクセスするためのinternal ALBと、internal ALBを返すPrivate Hosted Zoneを作成します。

EC2
↓
Route53 Private Hosted Zone
↓
Security Group
↓
ALB (internal)
↓
Auto Scaling Group
↓
EC2

例えば、IPアドレス制限付きで https://api.example.com で公開しているサービスがあるとします。

VPCの内側から同じURLでサービスにアクセスできるようにするには下記を行います。

  • internal ALBを作成します。この時、EC2のSecurity Groupをソースに設定し、EC2からALBへの通信を許可しておきます。
  • Route53で example.com のPrivate Hosted Zoneを作成します。
  • Aレコードを作成して api.example.com をinternal ALBに割り当てます。

最終的な構成を下図に示します。

f:id:int128:20171225125725p:plain

まとめ

KeycloakのSAML SSOでGitLabやMattermostにログインする

KeycloakとGitLabでSAML SSOを設定する方法を説明します。ユーザ管理をKeycloakで行いながら、GitLabやMattermostにSSOできるのでとても便利です。

TL;DR

Keycloakの設定

Keycloakで新しいClientを追加します。認証連携したいユーザがいるRealmで作業してください。

  • Client ID: https://gitlab.example.com *1
  • Client Protocol: saml
  • Client Signature Required: OFF
  • Root URL: https://gitlab.example.com
  • Valid Redirect URIs: https://gitlab.example.com/*

それから属性のマッピングを設定します。

SAML属性名 Keycloakのユーザプロパティ
email email
first_name firstName
last_name lastName
username username

SAML OmniAuth Providerの説明 によると groups 属性が含まれていたらGitLabグループとして認識されるようですが、ユーザを自動的にグループに追加してくれるわけではないので、あまり役に立たなさそうです。

デフォルトではRealmに存在するすべてのユーザがGitLabでログインできます。

Clientを作成したら、IdPの証明書を取得しておきます。InstallationタブでSAML Metadata IDPSSODescriptorを選択するとXMLが表示されるので、<dsig:X509Certificate> 要素に入っているBASE64文字列をメモしておきます。

なお、Clientの設定にあたっては下記が参考になります。

qiita.com

GitLabの設定

/etc/gitlab/gitlab.rb でOmniAuthを設定します。DockerやKubernetesを利用している場合は GITLAB_OMNIBUS_CONFIG 環境変数で設定します。

gitlab_rails['omniauth_enabled'] = true
gitlab_rails['omniauth_allow_single_sign_on'] = ['saml']
gitlab_rails['omniauth_block_auto_created_users'] = false
gitlab_rails['omniauth_auto_link_saml_user'] = true
gitlab_rails['omniauth_providers'] = [
  {
    name: 'saml',
    label: 'EXAMPLE LOGIN',  # ログインボタンのラベル。日本語は利用できないようです
    args: {
      assertion_consumer_service_url: 'https://gitlab.example.com/users/auth/saml/callback',
      idp_cert: '-----BEGIN CERTIFICATE-----
MII****
-----END CERTIFICATE-----',  # Keycloakで取得した証明書
      idp_sso_target_url: 'https://keycloak.example.com/auth/realms/YOUR_REALM/protocol/saml',
      issuer: 'https://gitlab.example.com',
      name_identifier_format: 'urn:oasis:names:tc:SAML:2.0:nameid-format:persistent',
      attribute_statements: { username: ['username'] }  # GitLabのユーザ名で使う属性を指定 (optional)
    }
  }
]

上記の gitlab.example.comkeycloak.example.com は実際のドメイン名に置き換えてください。また、IdPの証明書はKeycloakで取得したものを入れます。

デフォルトではメールアドレスの @ より前がGitLabのユーザ名になりますが、 attribute_statements を設定すると任意のSAML属性をユーザ名に指定できます(Use different SAML claim to set username? (#12841) · Issues · GitLab.org / GitLab Community Edition · GitLab)。上記では username 属性をユーザ名に割り当てています。

設定が完了したらGitLabを再起動します。ログイン画面で EXAMPLE LOGIN というボタンが表示されたら成功です。

うまくいかない場合は、KeycloakやGitLabのログを確認しましょう。

docs.gitlab.com

Mattermostの設定

MattermostでGitLab SSOを設定します。これにより、Mattermost→GitLab→KeycloakのSSOを実現できます。

https://docs.mattermost.com/deployment/sso-gitlab.html

KeycloakのusernameがそのままGitLabやMattermostのユーザ名(@int128)になるので、ユーザ管理の設計はよく考えた方がよいと思います。もちろん、Keycloakのマッピングを変更すれば別の属性をユーザ名にできます。

Keycloakの設定を自動化する

(2018-06-22追記)Keycloakにクライアントを追加する作業をポチポチやっていると疲れるので、JSONでインポートできるようにしました。

まとめ

  • KeycloakでSAML Clientを作成する。
  • GitLabでOmniAuth SAMLを設定する。
  • MattermostでGitLab SSOを設定する。

残念ながら、GitLabやMattermostからのSLO(シングルログアウト)は対応していないようです(Explore SAML Single Sign Out (#17344) · Issues · GitLab.org / GitLab Community Edition · GitLab)。

*1:2018-06-22:追記しました