Deisのpostgresをoff-clusterにする(Cloud SQLを利用)
やること
postgresをoff-clusterする。 で、そのpostgresをどこに立てるかというとGCEにたてるかGCPのmangaedなCloud SQL(RDSみたいなやつ)があるので、どっちにするかということで、今回はCloud SQLを使ってみた。
Cloud SQL PostgreSQL beta
Cloud SQLのポスグレはまだbeta版。なので、terraformにも来てない。けど、GCPは全体的に、公式docにGUIでconsoleでやるか、CLIでやるかっていうのが並列で揃ってて、とても親切。AWSみたいに、AWS Consoleで突然何かが色々できてしまうってことがない感じ。
作成は、rakeタスクにまとめるとこんな感じ。簡単
namespace :gcp do task :create_postgresql do sh %Q(gcloud beta sql instances create deis-cloudsql-postgres \ --database-version=POSTGRES_9_6 \ --activation-policy=ALWAYS \ --backup-start-time=16:00 \ --gce-zone=asia-northeast1-a \ --maintenance-window-day=SUN \ --maintenance-window-hour=17 \ --tier=db-n1-standard-1 \ --storage-type=SSD \ --storage-size=10GB \ --storage-auto-increase \ --region=asia-northeast1 ) end end
deis君からCloud SQLに接続するには
Cloud SQLに接続するには、何通りか方法があるけど、基本的にcloud sql proxyというのをたてて、それ経由でアクセスするっていうのがいい方法のようだ。 関係ないけど、Kubernetsとかにも言えるけどこういうのうまいなって思う。
cloud sql proxyを起動したマシン上で、localhostに対してDB接続しにいくと、つながるようになってる。
で、GKEの場合どうするかっていうと、sidecarパターンというやり方で、同一Pod内にcloud sql proxyのdocker image(imageが用意されてるところも憎い!)を起動させてそれをmountしてlocalhostをproxyとして接続できるようになる。うまい。
ただ、Deis君の場合は既にpodは固定されていて、ちょっと個別にpodを書き換えたりするのは面倒・・・っていうかやり方分からない。forkして、自分で追加したりしないといけないのかもしれない。なので、cloud sql proxyのパターンは使えない。もしかしたら、clusterたてるときに各nodeに初期処理入れてcloud sql proxyのinstallとかできるのかな。。
Cloud SQLは外部からアクセスする際に、IPアドレスを指定しておけば、そこからのアクセスは受け付けるようになる。なので、今回は、GKEのnodeのIPアドレスを指定してあげたら接続できた。 AWSだとNAT Gatewayがいたりするんだけど、GCPの場合はそういうのなさそう?やるなら、自分でNAT instance立てろ的な感じかもしれない。
nodeのIPアドレスたちは、kubectlコマンド使ってとれるようになってる。便利。この情報は、kubectl Cheat Sheetに書いてあった。
kubectl get nodes -o jsonpath='{.items[*].status.addresses[?(@.type=="ExternalIP")].address}'
deis用のユーザーとdatabaseの作成
GCPは、cloud shellっていうコンソールからssh接続できる風なやつが用意されてて、それ経由で作成してもOK。(AWSにも欲しい・・)
ただ、これもCLIが用意されてるのでそっちのがいい。ので、そうした。
task :create_database do sh %Q(gcloud beta sql users set-password postgres no-host \ --instance=deis-cloudsql-postgres --password=#{ENV["POSTGRES_PASSWORD"]} ) sh %Q(gcloud beta sql users create deis no-host \ --instance=deis-cloudsql-postgres --password=#{ENV["POSTGRES_DEIS_PASSWORD"]} ) sh %Q(gcloud beta sql databases create deis \ --instance=deis-cloudsql-postgres ) end
off-clusterにしてhelm install
さっき作ったdbの情報をvalues.yamlに書く。
cloud sqlのIPアドレスは、gcloudコマンドでとれる。結果はYAML形式でかえってくるので、rubyだといい感じに処理できる。それを--set database.postgres.host
で渡してあげればOK。passwordとかも、環境変数に入れておいてinstallをする。
namespace :helm do task :install do sh %Q(helm install deis/workflow --namespace deis -f values.yml \ --set gcs.key_json="$(cat gcloud-service-key.json | base64)" \ --set database.postgres.password=#{ENV["POSTGRES_DEIS_PASSWORD"]} \ --set database.postgres.host=#{sql_instance_address} ) end def sql_instance_address res = `gcloud sql instances describe deis-cloudsql-postgres` YAML.load(res)["ipAddresses"].first["ipAddress"] end end
動いているようだ
$ kd get pods NAME READY STATUS RESTARTS AGE deis-builder-1364651073-83r7x 1/1 Running 0 43m deis-controller-2711454920-g32nn 1/1 Running 0 43m deis-logger-176328999-06nwq 1/1 Running 2 43m deis-logger-fluentd-fbg4m 1/1 Running 0 43m deis-logger-fluentd-nt0tq 1/1 Running 0 43m deis-logger-fluentd-ttlwl 1/1 Running 0 43m deis-logger-fluentd-zc6hd 1/1 Running 0 43m deis-logger-redis-304849759-njc4w 1/1 Running 0 43m deis-monitor-grafana-432627134-b12zt 1/1 Running 0 43m deis-monitor-influxdb-2729788615-6pl28 1/1 Running 0 43m deis-monitor-telegraf-5rhmv 1/1 Running 1 43m deis-monitor-telegraf-ckfvc 1/1 Running 1 43m deis-monitor-telegraf-pdhj8 1/1 Running 1 43m deis-monitor-telegraf-q0d2h 1/1 Running 1 43m deis-nsqd-3597503299-sxcss 1/1 Running 0 43m deis-registry-169338602-4hzmk 1/1 Running 0 43m deis-registry-proxy-7h5f2 1/1 Running 0 43m deis-registry-proxy-k8vv4 1/1 Running 0 43m deis-registry-proxy-np9ks 1/1 Running 0 43m deis-registry-proxy-xcsx8 1/1 Running 0 43m deis-router-2126433040-7js0d 1/1 Running 0 43m deis-workflow-manager-2528409207-55vjg 1/1 Running 0 43m
うまくいってないと、STATUSがCrashになってたり、RESTARTSが増え続ける。
ちなみに、kd
はkubectl --namespace=deis
のalias。Tipsが公式に書いてあた
追記:これで解決
deisでminioを置き換えるだけをやってみる
公式ページを参考に
本当にここを変えただけ。bucketはGCSであらかじめ作っておく。権限とかもなんもしてない。
diff --git a/values.yml b/values.yml index c25cfb2..1d94aca 100644 --- a/values.yml +++ b/values.yml @@ -7,7 +7,7 @@ global: # - azure: Store persistent data in Azure's object storage # - gcs: Store persistent data in Google Cloud Storage # - minio: Store persistent data on in-cluster Minio server - storage: minio + storage: gcs # Set the location of Workflow's PostgreSQL database # @@ -75,9 +75,9 @@ gcs: # key_json is expanded into a JSON file on the remote server. It must be # well-formatted JSON data. key_json: <base64-encoded JSON data> - registry_bucket: "your-registry-bucket-name" - database_bucket: "your-database-bucket-name" - builder_bucket: "your-builder-bucket-name" + registry_bucket: "my-registry" + database_bucket: "my-database" + builder_bucket: "my-builder"
deisをinstallする
適当にRakeタスク作って、コマンド実行。--set
でsecrety keyを渡せるのいいね:100:
namespace :helm do task :install do sh %Q(helm install deis/workflow --namespace deis -f values.yml \ --set gcs.key_json="$(cat gcloud-service-key.json | base64)" ) end end
状態
確かにminioはいなくなった。
$ helm status nobby-gibbon LAST DEPLOYED: Tue Mar 28 20:41:00 2017 NAMESPACE: deis STATUS: DEPLOYED RESOURCES: ==> v1/ServiceAccount NAME SECRETS AGE deis-nsqd 1 9m deis-workflow-manager 1 9m deis-router 1 9m deis-registry 1 9m deis-database 1 9m deis-builder 1 9m deis-logger-fluentd 1 9m deis-controller 1 9m deis-logger 1 9m deis-monitor-telegraf 1 9m ==> v1/Service NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE deis-monitor-influxui 10.3.254.23 <none> 80/TCP 9m deis-logger-redis 10.3.249.48 <none> 6379/TCP 9m deis-registry 10.3.250.48 <none> 80/TCP 9m deis-database 10.3.242.10 <none> 5432/TCP 9m deis-monitor-influxapi 10.3.241.35 <none> 80/TCP 9m deis-workflow-manager 10.3.242.232 <none> 80/TCP 9m deis-builder 10.3.244.107 <none> 2222/TCP 9m deis-logger 10.3.253.37 <none> 80/TCP 9m deis-monitor-grafana 10.3.245.167 <none> 80/TCP 9m deis-router 10.3.252.162 35.190.228.247 80:32439/TCP,443:30793/TCP,2222:30898/TCP,9090:30627/TCP 9m deis-nsqd 10.3.246.21 <none> 4151/TCP,4150/TCP 9m deis-controller 10.3.241.43 <none> 80/TCP 9m ==> extensions/v1beta1/Deployment NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE deis-monitor-grafana 1 1 1 1 9m deis-controller 1 1 1 1 9m deis-logger 1 1 1 1 9m deis-nsqd 1 1 1 1 9m deis-database 1 1 1 1 9m deis-builder 1 1 1 1 9m deis-workflow-manager 1 1 1 1 9m deis-router 1 1 1 1 9m deis-registry 1 1 1 1 9m deis-logger-redis 1 1 1 1 9m deis-monitor-influxdb 1 1 1 1 9m ==> extensions/v1beta1/DaemonSet NAME DESIRED CURRENT READY NODE-SELECTOR AGE deis-monitor-telegraf 4 4 4 <none> 9m deis-registry-proxy 4 4 4 <none> 9m deis-logger-fluentd 4 4 4 <none> 9m ==> v1/Secret NAME TYPE DATA AGE deis-router-dhparam Opaque 1 9m objectstorage-keyfile Opaque 4 9m minio-user Opaque 2 9m ==> v1/ConfigMap NAME DATA AGE dockerbuilder-config 2 9m slugbuilder-config 2 9m slugrunner-config 1 9m
GCSのbucketの中になんか入ってる
databaseにだけ入ってた。これがなんか状態を保存してるやつなんだろうな。
deisのアンインストール
なんとなく分かったのでアンインストール。
$ helm list NAME REVISION UPDATED STATUS CHART NAMESPACE nobby-gibbon 1 Tue Mar 28 20:41:00 2017 DEPLOYED workflow-v2.12.0 deis $ helm delete nobby-gibbon
これだけだと、secret情報が消えなかったので次に再度helm installするときに、エラーになってしまう。こんな感じで残っちゃうようだ。
$ kubectl get secrets -n deis NAME TYPE DATA AGE builder-key-auth Opaque 1 18m builder-ssh-private-keys Opaque 3 18m database-creds Opaque 2 18m default-token-vdgrd kubernetes.io/service-account-token 3 19m deis-workflow-manager Opaque 1 17m deploy-hook-key Opaque 1 18m django-secret-key Opaque 1 18m logger-redis-creds Opaque 1 18m
なので、コマンドラインで一気に消す。今までのぼくなら1個ずつ手動入力してたけどlazy nightに参加したのでがんばって怠惰になるためにワンライナーでやる。
$ kubectl get secrets -n deis | awk '{print $1}' | tail -n +2 | xargs kubectl delete secret -n deis
追記
ただ、helm deleteだけだと、完全には消えなかったので、helm delete --purge
をつける必要があった。
Deisをproduction運用するための設定の仕方調査
背景
Deisはhelmコマンドでインストールすれば簡単に入るけど、そのままだとデータ保存先がephemeralなものになってしまうのでダメ。他にもproduction運用のためにした方が設定があるのでまとめる。
調べ方
- ほとんどdeisの公式ページに書いてあった
- installing-workflow
- production-deployments
- cloud sql postgres/connect-container-engine
変えなきゃいけなさそうなところ
- kubernetesのバージョン
- クラスターインスタンスの推奨スペック
- object storage はminioから
GCS
へ - postgres は
Cloud SQL for postgresql
orpostgresql on GCE
- docker registryは
GCR
へ - firewallの設定。そのままだと0.0.0.0/0からつながってしまう。
- deis registerできるのをadminだけにする。controllerの設定
- Grafanaのsign upをdisableにする。
- SSLの設定。そのままだとオレオレ認証のまま?
- monitorのinfluxDBの設定。そのままだとmetricsが永続化されない。
- DNS
- routerをscaleさせた方がいいとある -> 今回はいらないかも?
変え方
kubernetsの色んなリソースをまとめたものをChartという。まぁざっくりいうとyumとかhomebrew的なもの。 実はChartには海図という意味があり、kubernetesがギリシャ語で船長みたいな意味で、helmが舵という意味なので、kubernetesがchartをもとにhelmをとると考えればなんとなく意味が分かる。
Chartはある決まったstructureをしてるんだけど、それのうちパラメータを設定できるのがvalues.ymlというもの。CLI経由で--set
で一つ一つパラメータを渡すこともできるけどめんどいから基本やらない。ただ、--set
でoverrideするとか追加するとかはやりそうな気がする。
で、DeisもChartで管理されてるので、当然values.yml経由でパラメータを変更する。
values.ymlの持ってきかたは、helm inspect
サブコマンドで取得できる。valuesをつけなければ、values以外の基本的な情報を取得できたりする。ちなみに、当然だけど、deis/workflow以外のものの場合も同様で、deis/workflow
の部分をstable/mysql
とかにすれば、情報がとれる。
helm inspect values deis/workflow | sed -n '1!p' > values.yml
values.ymlの内容はこれ
global: # Set the storage backend # # Valid values are: # - s3: Store persistent data in AWS S3 (configure in S3 section) # - azure: Store persistent data in Azure's object storage # - gcs: Store persistent data in Google Cloud Storage # - minio: Store persistent data on in-cluster Minio server storage: minio # Set the location of Workflow's PostgreSQL database # # Valid values are: # - on-cluster: Run PostgreSQL within the Kubernetes cluster (credentials are generated # automatically; backups are sent to object storage # configured above) # - off-cluster: Run PostgreSQL outside the Kubernetes cluster (configure in database section) database_location: "on-cluster" # Set the location of Workflow's logger-specific Redis instance # # Valid values are: # - on-cluster: Run Redis within the Kubernetes cluster # - off-cluster: Run Redis outside the Kubernetes cluster (configure in loggerRedis section) logger_redis_location: "on-cluster" # Set the location of Workflow's influxdb cluster # # Valid values are: # - on-cluster: Run Influxdb within the Kubernetes cluster # - off-cluster: Influxdb is running outside of the cluster and credentials and connection information will be provided. influxdb_location: "on-cluster" # Set the location of Workflow's grafana instance # # Valid values are: # - on-cluster: Run Grafana within the Kubernetes cluster # - off-cluster: Grafana is running outside of the cluster grafana_location: "on-cluster" # Set the location of Workflow's Registry # # Valid values are: # - on-cluster: Run registry within the Kubernetes cluster # - off-cluster: Use registry outside the Kubernetes cluster (example: dockerhub,quay.io,self-hosted) # - ecr: Use Amazon's ECR # - gcr: Use Google's GCR registry_location: "on-cluster" # The host port to which registry proxy binds to host_port: 5555 # Prefix for the imagepull secret created when using private registry secret_prefix: "private-registry" s3: # Your AWS access key. Leave it empty if you want to use IAM credentials. accesskey: "" # Your AWS secret key. Leave it empty if you want to use IAM credentials. secretkey: "" # Any S3 region region: "us-west-1" # Your buckets. registry_bucket: "your-registry-bucket-name" database_bucket: "your-database-bucket-name" builder_bucket: "your-builder-bucket-name" azure: accountname: "YOUR ACCOUNT NAME" accountkey: "YOUR ACCOUNT KEY" registry_container: "your-registry-container-name" database_container: "your-database-container-name" builder_container: "your-builder-container-name" gcs: # key_json is expanded into a JSON file on the remote server. It must be # well-formatted JSON data. key_json: <base64-encoded JSON data> registry_bucket: "your-registry-bucket-name" database_bucket: "your-database-bucket-name" builder_bucket: "your-builder-bucket-name" swift: username: "Your OpenStack Swift Username" password: "Your OpenStack Swift Password" authurl: "Swift auth URL for obtaining an auth token" # Your OpenStack tenant name if you are using auth version 2 or 3. tenant: "" authversion: "Your OpenStack swift auth version" registry_container: "your-registry-container-name" database_container: "your-database-container-name" builder_container: "your-builder-container-name" # Set the default (global) way of how Application (your own) images are # pulled from within the Controller. # This can be configured per Application as well in the Controller. # # This affects pull apps and git push (slugrunner images) apps # # Values values are: # - Always # - IfNotPresent controller: app_pull_policy: "IfNotPresent" # Possible values are: # enabled - allows for open registration # disabled - turns off open registration # admin_only - allows for registration by an admin only. registration_mode: "admin_only" database: # The username and password to be used by the on-cluster database. # If left empty they will be generated using randAlphaNum username: "" password: "" # Configure the following ONLY if using an off-cluster PostgreSQL database postgres: name: "database name" username: "database username" password: "database password" host: "database host" port: "database port" redis: # Configure the following ONLY if using an off-cluster Redis instance for logger db: "0" host: "redis host" port: "redis port" password: "redis password" # "" == no password fluentd: syslog: # Configure the following ONLY if using Fluentd to send log messages to both # the Logger component and external syslog endpoint # external syslog endpoint url host: "" # external syslog endpoint port port: "" monitor: grafana: user: "admin" password: "admin" # Configure the following ONLY if you want persistence for on-cluster grafana # GCP PDs and EBS volumes are supported only persistence: enabled: false # Set to true to enable persistence size: 5Gi # PVC size influxdb: # Configure the following ONLY if using an off-cluster Influx database url: "my.influx.url" database: "kubernetes" user: "user" password: "password" # Configure the following ONLY if you want persistence for on-cluster influxdb # GCP PDs and EBS volumes are supported only persistence: enabled: false # Set to true to enable persistence size: 20Gi # PVC size registry-token-refresher: # Time in minutes after which the token should be refreshed. # Leave it empty to use the default provider time. token_refresh_time: "" off_cluster_registry: hostname: "" organization: "" username: "" password: "" ecr: # Your AWS access key. Leave it empty if you want to use IAM credentials. accesskey: "" # Your AWS secret key. Leave it empty if you want to use IAM credentials. secretkey: "" # Any S3 region region: "us-west-2" registryid: "" hostname: "" gcr: key_json: <base64-encoded JSON data> hostname: "" router: dhparam: "" # Any custom router annotations(https://github.com/deis/router#annotations) # which need to be applied can be specified as key-value pairs under "deployment_annotations" deployment_annotations: #<example-key>: <example-value> # Any custom annotations for k8s services like http://kubernetes.io/docs/user-guide/services/#ssl-support-on-aws # which need to be applied can be specified as key-value pairs under "service_annotations" service_annotations: #<example-key>: <example-value> # Enable to pin router pod hostPort when using vagrant host_port: enabled: false # Service type default to LoadBalancer # service_type: LoadBalancer workflow-manager: versions_api_url: https://versions.deis.com doctor_api_url: https://doctor.deis.com
このvalues.ymlの内容をproductionのための設定に変えていって、helm installとやる予定。
Terraform0.9.0からremote configがdeprecatedになる
remote config
がdeprecatedになる
terraform0.9.0からterraformコマンド実行するとdeprecated warningがでてきます。
💁 3行
remote config
コマンドがなくなってinit
コマンドが導入されるbackend
という概念が導入されterraform.tfstateファイルを保存する設定をtfファイル側に書くようになってlocalのtfstateは更新されなくなる- ざっくりmigrationする際は、tfstateは一応バックアップしといて、tfファイルにbackendの設定書いて
terraform init
すればOK
設定の書き方
https://www.terraform.io/docs/backends/types/index.html ここの左ペインにStandard Backendsに色々なタイプがあるのでそこを見る。
例: S3
terraform { backend "s3" { bucket = "mybucket" key = "path/to/my/key" region = "us-east-1" } }
例: local
terraform { backend "local" { path = "relative/path/to/terraform.tfstate" } }
そもそもなんでbackendというものを導入しようとしたのか、メリット的なものについて詳しく :pencil2:
該当のPR#11286に詳しく書いてある
remote config
をより一般的な考え方にするために、backend
という概念を導入した- 今ってterraform applyってやるタイミングでインフラ担当一人って感じだけど本当はいろんな箇所から複数同時に実行されることだってあるはず、将来はそうなるはず、そんな時のために、stateをlockしてくれる機能も自動的についてくる
- remote configはコマンドラインベースでオプションを渡す形で設定していたのでわかりにくい、それをファイルベースで明示的に書くことでより基本的な設定とした
init
コマンドに統合してmoduleのdownloadとかも一気にやってくれるようになる- 今まではremote configを見に行くコマンドと見に行かないコマンドがあったけど全部共通して
backend
を通してコマンドを実行するようになる - localにキャッシュされてたtfstateファイルにsecret情報が書かれてしまってたがそもそもlocalにキャッシュされてたtfstateファイルはもう更新されなくなるのでgitに含めなくてOK
- なので.gitignoreに
.terraform/
を追加しよう:exclamation: - localのtfstateのserialがremoteより高いとダメみたいなことが起きなくなる
Lineage
というUUID的なものが導入されてこの値が変わると別のinfraとみなされエラーが出るようになる。具体的なユースケースは間違って別環境のものでapplyしようとしたときとかだろうか 😐
terraform0.9.1からplanレベルでsecurity_groupのcidr_blocksのvalidateが入るようになった
0.9.1 changlog :tada:
https://github.com/hashicorp/terraform/blob/master/CHANGELOG.md#091-march-17-2017
provider/aws: Validate CIDR Blocks in SG and SG rule resources (#12765)
planで未然に防げる
つい間違ってcidr_blocksの方にsecurity_groupのidを指定しまったことがあったけどそれが防げるようになる💯
terraform consoleっていうREPLみたいなやつがあるの知らなかった
v0.8から導入された模様。俗に言うREPL。今のtfstateをもとにsyntaxを試すことができる。planやapplyなどを発行することはできないので安全。単にこういう時はterraformではどう書くんだろう??みたいなのを試したいときに便利。
例1: moduleのoutputを呼び出して要素を取得する
> "${module.base_network.private_subnet_001_ids}" [ subnet-xxxxxxxxx, subnet-yyyyyyyyy ] > "${module.base_network.private_subnet_001_ids[0]}" subnet-xxxxxxxxx > "${module.base_network.private_subnet_001_ids[1]}" subnet-yyyyyyyyy
例2: 組み込み関数が何を返すか調べる
> cidrsubnet("172.24.0.0/16", 10, 1) 172.24.0.64/26 > cidrsubnet("172.24.0.0/16", 10, 0) 172.24.0.0/26 > cidrsubnet("172.24.0.0/16", 26, 0) cidrsubnet: insufficient address space to extend prefix of 16 by 26 in: > cidrsubnet("172.24.0.0/16", 10, 4) 172.24.1.0/26 > cidrsubnet("172.24.0.0/16", 10, 5) 172.24.1.64/26 > cidrsubnet("172.24.0.0/16", 10, 6) 172.24.1.128/26 > cidrsubnet("172.24.0.0/16", 10, 8) 172.24.2.0/26
H/A とは
H/Aとかよく出てくるけどうまくググれなかったので。 多分 High Availability(高可用性)の略
自分で立てたdeisにアプリをdeployするまで
quickstartのConfigure DNSとregister an admin user and deploy your first app.をやっただけ。
ロードバランサーの確認
$ kubectl --namespace=deis describe svc deis-router | grep LoadBalancer Type: LoadBalancer LoadBalancer Ingress: 104.198.90.182
これがロードバランサーのIP。
でも、これがいつできたのがわからない :scream:
On Google Container Engine, Deis Workflow will automatically provision and attach a Google Cloud Loadbalancer to the router copmonent.
Deis Workflowは自動的にGCPのロードバランサーを作ってアタッチするっていってるけど、一体これはいつ行われたんだ・・ :thinking:
helm install
したときか?該当のコードをdeisのorganization配下でgcpとかload balancerとかgclbとかいろいろ検索してみたけど、わからず・・
kubernetesのserviceでtype:LoadBalancerとしてるから
そもそも、k8sにserviceというcomponentがあって、serviceは色んなタイプがあるけれどLoadBalancerを選ぶと、k8sがよしなにバックエンドのcloud providerを見てロードバランサーを作ってくれるってことですね。GKEの場合は、それは、Google cloud load balancerを使って実現すると。 なので、多分k8sのコードにそういうことが書いてあるはず(まだ見てないけど)。
deis/workflowがやるのはpodを作成するってことだけだと思いこんでしまっていて、podはcontainerを包含した単位なのに、なぜ上位のGCPの操作の仕方まで知っているんだろう?というところがつまづいてしまった原因でした:scream: そもそもk8sは何ができるのかを分かったようで分かってなかったってことですね。
ロードバランサーのIPをもとにnip.ioというサービスを利用したときのhostを確認する
$ host 104.198.90.182.nip.io 104.198.90.182.nip.io has address 104.198.90.182
これで deis.<GCPのロードバランサーのIPアドレス>.nip.io で手元でアクセスできるようになる。
$ curl http://deis.104.198.90.182.nip.io/v2/ && echo {"detail":"Authentication credentials were not provided."}
手元のdeisコマンドでユーザー登録する
$ deis register http://deis.104.198.90.182.nip.io username: admin password: password (confirm): email: xx@bb Registered admin Logged in as admin Configuration file written to /Users/mpon/.deis/client.json
既存のやつが上書きされちゃうっぽい。ってことは複数のdeis workflowに対して何かやりたいときは、deis registerをやり直さないといけないのか??ちょっとめんどいかもしれない :rolling_eyes:
$ deis register --help Registers a new user with a Deis controller. Usage: deis auth:register <controller> [options] Arguments: <controller> fully-qualified controller URI, e.g. 'http://deis.local3.deisapp.com/' Options: --username=<username> provide a username for the new account. --password=<password> provide a password for the new account. --email=<email> provide an email address. --login=true logs into the new account after registering. --ssl-verify=true enables/disables SSL certificate verification for API requests
deis registerのhelpを見てみても複数に切り替える的なやつはなさげだ。まぁいっか :dancer:
アプリケーションの器をdeis上に作る
$ deis create --no-remote Creating Application... done, created jangly-sailfish If you want to add a git remote for this app later, use `deis git:remote -a jangly-sailfish`
作ったとこにサンプルアプリケーションをpullする
$ deis pull deis/example-go -a jangly-sailfish Creating build... done
アクセスする
$ curl http://jangly-sailfish.104.198.90.182.nip.io Powered by Deis
コンフィグを上書きする
この間、勝手にローリングデプロイが行われるのでdown timeは0 💯
$ deis config:set POWERED_BY="Docker Images + Kubernetes" -a jangly-sailfish Creating config... done === jangly-sailfish Config POWERED_BY Docker Images + Kubernetes $ curl http://jangly-sailfish.104.198.90.182.nip.io Powered by Docker Images + Kubernetes
スケーリング
コマンドでさくっとできる。
$ deis scale cmd=2 -a jangly-sailfish Scaling processes... but first, coffee! done in 10s === jangly-sailfish Processes --- cmd: jangly-sailfish-cmd-2108971595-1vxqg up (v3) jangly-sailfish-cmd-2108971595-b7xhf up (v3)
Google Cloud Platformの環境をterraformで作る
そもそもGoogle Cloud Platformとは
Googleが出してるAWS的なやつ。GCPと略す。だいたいの比較表。
なんでAWS使わないの?
コンテナでぽんぽんアプリケーションをdeployしたいんだけどそれをAWSでやろうとすると、ECSというサービスを使うことになる。でも、このECSというのがdeployまわりの取り扱いがめんどくさくてちょっといけてない。 で、同じようなコンテナのスケジューリングとかやってくれるソフトウェアでkubernetesというのがあるんだけど、それをAWS上にたてようとするとちょいめんどい。GCPにはGKE(Google Container Engine)というkubernetesのマネージドサービスが最初から用意されてるのでこれを使いたいから。
GCPにおけるprojectという概念
GCPは一つのアカウントに対して、projectという概念を持ってる。たくさんprojectは作ることができる。すごくざっくりだけど、AWSでいうVPCぐらいの単位かも。
GCPにおける地域とゾーン
- AWSでいうregionはGCPだと地域で、アジアだと日本(asia-northeast1)と台湾(asia-east1)がある
- AWSでいうazはGCPだとゾーンと言って、日本には1a〜1cまである。
GCPのprovider
ここからterraformの話。とにもかくにもproviderを定義する。 GOOGLE CLOUD PROVIDER | terraform
provider "google" { credentials = "${file("../account.json")}" project = "elated-bebop-161001" region = "asia-northeast1" }
- account.jsonというのは、GCPのコンソールから認証情報(秘密鍵が書いてある)ファイルをjson形式で落としてきたもの。
- projectは上で言及してるもので全GCP上で一意になるもの。Project名ではなくIDが振られるのでそれを書く。
- regionは地域。ここでは日本を指定する。
terraformの実行🚀
terraformコマンドで直接実行してもいいけど、いろんなprojectを掛け持ちしてるとローカルのversionを揃えるのが大変なので、docker imagesで提供されているものを使う。 参考にしたサイト 実行するときにいろんなことをしたり、長いコマンドうつの面倒なのでRakefileを作ってそれで実行する。
Rakefileの中身とはまったポイント
- 🙅 terraformのdocを見ると
GOOGLE_CREDENTIALS
に認証情報のjsonの中身をつめて渡せばそれを使ってくれるとかいてあったが、これをdockerのコマンドに渡すようにするとstdoutに表示されてしまう - 😓 出力されないように環境変数にjsonの中身をいれようと試行錯誤したが、jsonの末尾の改行を取り除かないといけないのと、private keyが記述されてるのでそこに文字列として
\n
が入っててそれが環境変数として渡すと評価されてしまって改行扱いになってしまう
docker実行時に、GOOGLE_APPLICATION_CREDENTIALS
で認証情報のファイルの場所を渡してあげるようにする💯
@terraform_version = "0.8.8" @docker = %Q(docker run --rm -v ${PWD}:/work -w /work/terraform -e GOOGLE_APPLICATION_CREDENTIALS=/work/account.json hashicorp/terraform:#{@terraform_version}) namespace :tf do task :init do sh %Q(#{@docker} remote config \ -backend=gcs \ -backend-config="bucket=my-first-project-terraform-state-20170309" \ -backend-config="path=terraform.tfstate" \ -backend-config="project=elated-bebop-161001") end task :plan do sh %Q(#{@docker} plan) end end
- GCPの方の https://developers.google.com/identity/protocols/application-default-credentials に
GOOGLE_APPLICATION_CREDENTIALS
を使えって書いてあった。 - これをすれば、providerの方に
credentials = "${file("account.json")}"
って書かなくてOK
まとめ
俺はなぜあんな無駄な時間を・・ by 三井 寿
teeコマンドの使いどころ
標準入力を標準出力とファイルに出力する
というコマンドです。
なんか使いどころが分からないなーって思ってたけど、コマンドの結果をファイルに書き出してそれを別のコマンドに渡すってのをcircle ci上でやってて、あー、画面上でもこのファイルの中身見たいなーって思って、あっ!こういうときにteeを使いたくなるのか!!!ってなった。
terraformでif文的なものを使ったユースケース例
usecase
EC2インスタンス、ELB、security_groupあたりがまとまったmoduleがあるんだけど、特定の役割のインスタンスにだけiam_roleにSNSFullAccessをつけたい。
設定内容
variable "allow_sns_full_access" { default = false }
- 対象のリソースでtrueならcount = 1、falseならcount = 0にする。
resource "aws_iam_role_policy_attachment" "app_sns_full_access" { count = "${var.allow_sns_full_access ? 1 : 0}" role = "${aws_iam_role.app.name}" policy_arn = "arn:aws:iam::aws:policy/AmazonSNSFullAccess" }
- 利用したいやつだけ、trueにする
module "app" { source = "../modules/app" allow_sns_full_access = true }
結果
+ module.app.aws_iam_role_policy_attachment.app_sns_full_access
policy_arn: "arn:aws:iam::aws:policy/AmazonSNSFullAccess"
role: "app"
AWSのロードバランサーあたりのセキュリティーグループ制限がさくっと緩和できなかった
ロードバランサーあたりのセキュリティグループ 5
ご担当者様 平素よりお世話になっております。 AWSカスタマーサービスの藁品でございます。 折角ご申請いただきましたのに誠に申し訳ございませんが、「Classic Load Balancer」の 「ロードバランサーあたりのセキュリティグループの上限」を緩和することは致しかねます。 理由といたしましては、「ロードバランサーあたりのセキュリティグループ」は 「ネットワークインターフェースあたりのセキュリティグループ」に基づいている為でございます。 なお、下記サービスの上限値の変更を行うことで「ロードバランサーあたりのセキュリティグループの上限」の 緩和をすることも可能でございますが、この変更を行うことによりネットワークインターフェースを使用している 全リソースに影響を及ぼしますのでご留意くださいませ。 1) セキュリティグループあたりのインバウンド/アウトバウンドルール(デフォルト50)を減らす。 2) ネットワークインターフェースあたりのセキュリティグループを(デフォルト5)増やす。 引き続き緩和をご希望ということであれば、ケースにご返信いただくかたちでお知らせいただければ幸いです。 その他ご不明点、ご要望等ございましたら、ご遠慮なくお問い合わせくださいませ。 何卒よろしくお願いいたします。
terraformでsecurity groupをmodule化するときに気をつけること
IPアドレスを定義した共通のsecurity groupを作りたい
あるプロジェクトで外部のIPアドレスなどをまとめたものをsecurity groupとしてmodule化している。このアイディア自体はいいんだけど、環境ごとにこれを利用しようとした場合に失敗する。
例えば、こんな感じでmodulesに外部IPを書いたようなsecurity_groupをつくる。
terraform ├── development │ └── main.tf ├── staging │ └── main.tf ├── production │ └── main.tf └── modules └── external_ips_sg ├── security_group.tf └── outputs.tf
で、これらを各環境のmain.tfで利用すると、同一のsgを作ろうとするので、security groupの名前を変える必要がある。
module "external_ips_sg" { source = "../modules/external_ips_sg" }
ここで、security groupのnameを変数化しておかないと同じ名前のsecurity groupを作ろうとして失敗する。
解決策
当たり前の話だけど、変数でnameを変えられるようにしておく。
└── modules └── external_ips_sg ├── variables.tf ├── security_group.tf └── outputs.tf
variable "env" {} variable "vpc_id" {}
module "external_ips_sg" { source = "../modules/external_ips_sg" env = "dev" vpc_id = "vpc-xxxxxxxx" }
resource "aws_security_group" "a" { name = "external-ips-${var.env}" vpc_id = "${var.vpc_id}"
ちなみに、Security GroupのDescriptionはForce new Resource
なのであとから変更できないので、何も設定しない方がいいかも。ちょっと文言変えたいなーってときにもう変更できない。何も設定しなければ、 Mangaed By Terraform
のデフォルト文言が設定される。何か名前付けをしたい場合は、TagのNameを使おう。
あと、vpc_idを入れるのを忘れがち。vpc_idはなければ自動的にdefautl vpcが設定されるのでうまくいってしまう。
awsのsecurity groupにはルールの上限がある
IPアドレスをたくさん追加しようとしたら:scream:
各拠点のIPアドレスを追加してほしいという依頼で、terraform planで問題なかったので、applyしたらエラーが出た。
* aws_security_group.elb_app: Error authorizing security group ingress rules: RulesPerSecurityGroupLimitExceeded: The maximum number of rules per security group has been reached. status code: 400, request id: 88277351-1fc1-4e48-a005-3fe046a8145b
制限があるのを知らなかった。ドキュメントによると制限は50だった。
多分手動で追加してれば51個目でエラーになるから気づけるけど、terraformだとapplyしてはじめてエラーになる。なので、エラーになった瞬間、該当のsecurity groupの設定は空っぽになってしまう。
気をつけよう :cry:
とくにsecruty groupの変更は要注意
ingressとか書くときに、cidr_blocksと、security_groupsを指定できるけど、つい間違ってcidr_blocksの方にsecurity_groupのidを指定しまったことがあった。これも、planのときは問題ないけど、applyで指定方法が間違ってるってことでエラーになる。
ingress { ... cidr_blocks = [ "${aws_security_group.hoge.id}" ] }
まとめ
validateしたい。terraformにPRチャンスかもしれない。