Hatena::ブログ(Diary)

私事ですが…… このページをアンテナに追加 RSSフィード

2016-06-07(Tue)

[]CircleCIとterraformとGitHubのプライベートリポジトリで3環境を構成する 14:05 CircleCIとterraformとGitHubのプライベートリポジトリで3環境を構成するを含むブックマーク CircleCIとterraformとGitHubのプライベートリポジトリで3環境を構成するのブックマークコメント


表題の件、結局以下のようにしてみた。当初は単一アクセスキー/シークレットキーを使ってクロスアカウントのAssumeRoleで切り分けようかと思ったものの、AssumeRoleの切り替えに一時セッショントークンの取得などが必要で余計にややこしくなることから、システムユーザについてはAWSアカウント別に相当するユーザを作ってしまうことにした(人間のユーザはAssumeRoleで運用している)。


prodは本番用AWSアカウントstgとdevは開発用AWSアカウント。各環境は別VPC。それぞれPowerUserポリシーのterraformerというIAMユーザを定義済み。

通常CircleCIでAWSと連携する場合は「AWS Permissions」にアクセスキーとシークレットキーを書くが、今回はそこには何も書かずに「Environment Variables」に「DEV_AWS_ACCESS_KEY_ID」「DEV_AWS_SECRET_ACCESS_KEY」のような感じでdev/stg/prodの分のアクセスキーを記述。


S3バケットとして対応するAWSアカウントに「company01-dev-terraform」「company01-stg-terraform」「company01-prod-terraform」というのを用意してそこで各環境のtfstateを管理(初回はバケットだけ作っておけば実際に何も入ってない状態でもremote pullとかは正しく動作する)。


git-flowとはちょっと違う運用で、developにpushすると開発環境のplan、release/developmentにpush(developをマージ)すると開発環境へのapply、release/stagingにpush(developをマージ)するとステージング環境へのapply、masterにpush(developをマージ)すると本番環境のplan、release/productionにpushする(masterをマージ)と本番環境へのapply。


GitHub上はOrganizationでプライベートリポジトリを作って運用しているが、当該リポジトリの[Settings]-[Branches]-[Protected branches]から、「master」「release/production」へのpush権限を別途作成したチーム「Administrators」に絞っている。


ちなみにアクセスキーを環境変数で与えている割に「provider "aws"」に「profile」を設定しているが、これは同じリソースを使って人間が手動で流すときに自身の「~/.aws/config」の設定で切り替えられるようにしたもの。CircleCI自体では使っていない。


  • ci/
    • terraform-build.sh
    • terraform-install.sh
    • terraform-validate.sh
  • env/
    • dev/
      • main.tf
    • prod/
      • main.tf
    • stg/
      • main.tf
  • module/
    • vpc/
      • main.tf
      • outputs.tf
      • variables.tf
    • web_cluster/
      • main.tf
      • outputs.tf
      • variables.tf
    • ...
  • .gitignore
  • circle.yml
  • README.md

ci/terraform-build.sh

#!/bin/bash

set -xe

TF_ENV_DIR=$1
TF_BUCKET=$2

ROOT_DIR=$(git rev-parse --show-toplevel)

cd "${ROOT_DIR}/${TF_ENV_DIR}"

terraform get -update
terraform remote config -backend=s3 -backend-config="bucket=${TF_BUCKET}" -backend-config="key=common-infra/terraform.tfstate" -backend-config="region=ap-northeast-1"
terraform apply
terraform remote push

ci/terraform-install.sh

#!/bin/bash

set -xe

cd ~/bin

TF_VERSION=0.6.16

if [ ! -e ~/bin/terraform ]; then
  wget https://releases.hashicorp.com/terraform/${TF_VERSION}/terraform_${TF_VERSION}_linux_amd64.zip
  unzip terraform_${TF_VERSION}_linux_amd64.zip
  rm terraform_${TF_VERSION}_linux_amd64.zip
fi

ci/terraform-validate.sh

#!/bin/bash

set -xe

TF_ENV_DIR=$1
TF_BUCKET=$2

ROOT_DIR=$(git rev-parse --show-toplevel)

cd "${ROOT_DIR}/${TF_ENV_DIR}"

terraform get -update
terraform remote config -backend=s3 -backend-config="bucket=${TF_BUCKET}" -backend-config="key=common-infra/terraform.tfstate" -backend-config="region=ap-northeast-1"
terraform plan

env/dev/main.tf

provider "aws" {
  profile = "closed-aws-account"
  region = "ap-northeast-1"
}

module "vpc_dev" {
  source = "../../module/vpc"
  env = "dev"
  service = "common"
  cidr = "10.x.x.x/x"
  public_subnets = "10.x.x.x/x,10.x.x.x/x"
  private_subnets = "10.x.x.x/x,10.x.x.x/x"
  azs = "ap-northeast-1a,ap-northeast-1c"
}

module "service01_dev" {
  source = "../../module/web_cluster"
  env = "dev"
  service = "service01"
  ...
}

module "service02_dev" {
  source = "../../module/web_cluster"
  env = "dev"
  service = "service02"
  ...
}

env/prod/main.tf

provider "aws" {
  profile = "open-aws-account"
  region = "ap-northeast-1"
}

module "vpc_prod" {
  source = "../../module/vpc"
  env = "prod"
  service = "common"
  cidr = "10.x.x.x/x"
  public_subnets = "10.x.x.x/x,10.x.x.x/x"
  private_subnets = "10.x.x.x/x,10.x.x.x/x"
  azs = "ap-northeast-1a,ap-northeast-1c"
}

module "service01_prod" {
  source = "../../module/web_cluster"
  env = "prod"
  service = "service01"
  ...
}

module "service02_prod" {
  source = "../../module/web_cluster"
  env = "prod"
  service = "service02"
  ...
}

env/stg/main.tf

provider "aws" {
  profile = "closed-aws-account"
  region = "ap-northeast-1"
}

module "vpc_stg" {
  source = "../../module/vpc"
  env = "stg"
  service = "common"
  cidr = "10.x.x.x/x"
  public_subnets = "10.x.x.x/x,10.x.x.x/x"
  private_subnets = "10.x.x.x/x,10.x.x.x/x"
  azs = "ap-northeast-1a,ap-northeast-1c"
}

module "service01_stg" {
  source = "../../module/web_cluster"
  env = "stg"
  service = "service01"
  ...
}

module "service02_stg" {
  source = "../../module/web_cluster"
  env = "stg"
  service = "service02"
  ...
}

.gitignore

.terraform
*.tfstate
*.tfstate.backup

circle.yml

general:
  branches:
    only:
      - release/development
      - release/staging
      - release/production
      - develop
      - master

machine:
  environment:
    PATH: "${HOME}/bin:${PATH}"

dependencies:
  cache_directories:
    - "~/bin"
  pre:
    - |
      mkdir -p ~/bin
      bash ./ci/terraform-install.sh

test:
  override:
    - |
      if [ "${CIRCLE_BRANCH}" = "release/development" ] || [ "${CIRCLE_BRANCH}" = "develop" ]; then
        AWS_ACCESS_KEY_ID=${DEV_AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY=${DEV_AWS_SECRET_ACCESS_KEY} AWS_DEFAULT_REGION=ap-northeast-1 bash ./ci/terraform-validate.sh env/dev company01-dev-terraform
      elif [ "${CIRCLE_BRANCH}" = "release/staging" ]; then
        AWS_ACCESS_KEY_ID=${STG_AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY=${STG_AWS_SECRET_ACCESS_KEY} AWS_DEFAULT_REGION=ap-northeast-1 bash ./ci/terraform-validate.sh env/stg company01-stg-terraform
      elif [ "${CIRCLE_BRANCH}" = "release/production" ] || [ "${CIRCLE_BRANCH}" = "master" ]; then
        AWS_ACCESS_KEY_ID=${PROD_AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY=${PROD_AWS_SECRET_ACCESS_KEY} AWS_DEFAULT_REGION=ap-northeast-1 bash ./ci/terraform-validate.sh env/prod company01-prod-terraform
      fi

deployment:
  development:
    branch: release/development
    commands:
      - AWS_ACCESS_KEY_ID=${DEV_AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY=${DEV_AWS_SECRET_ACCESS_KEY} AWS_DEFAULT_REGION=ap-northeast-1 bash ./ci/terraform-build.sh env/dev company01-dev-terraform
  staging:
    branch: release/staging
    commands:
      - AWS_ACCESS_KEY_ID=${STG_AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY=${STG_AWS_SECRET_ACCESS_KEY} AWS_DEFAULT_REGION=ap-northeast-1 bash ./ci/terraform-build.sh env/stg company01-stg-terraform
  production:
    branch: release/production
    commands:
      - AWS_ACCESS_KEY_ID=${PROD_AWS_ACCESS_KEY_ID} AWS_SECRET_ACCESS_KEY=${PROD_AWS_SECRET_ACCESS_KEY} AWS_DEFAULT_REGION=ap-northeast-1 bash ./ci/terraform-build.sh env/prod company01-prod-terraform
トラックバック - http://d.hatena.ne.jp/t_yamo/20160607

2016-06-04(Sat)

[]AWSアカウント間のIAMロールを使用したアクセスの委任をしているときのクレデンシャル設定 12:31 AWSアカウント間のIAMロールを使用したアクセスの委任をしているときのクレデンシャル設定を含むブックマーク AWSアカウント間のIAMロールを使用したアクセスの委任をしているときのクレデンシャル設定のブックマークコメント

AWS CLI上のお話

本番環境と開発環境でAWSアカウントを分けることがある。

で、以下のような感じでsts:AssumeRoleを使って開発環境のIAMユーザのまま本番環境のIAMロールで振る舞えるようにすることがある(あまりサクサク切り替えられるとAWSアカウントを分けた意味がないので実際には色々と調整)。

チュートリアル: AWS アカウント間の IAM ロールを使用したアクセスの委任

http://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/tutorial_cross-account-with-roles.html

このときに手元のPCからAWS CLIのコマンドを開発環境用と本番環境用で呼び分けたいと思ったのでそのときのメモ(defaultはまた別のアカウント用なので気にしない)。

まあ実際にはAWS CLIコマンドではなくterraformの適用先の切り替えをしたくてやっているのですが(resourceのproviderが変数を受け付けてくれないのでmoduleと併用だといろいろ厳しい)。

~/.aws/credentials

[default]
aws_access_key_id=xxx
aws_secret_access_key=xxx

[development]
aws_access_key_id=yyy
aws_secret_access_key=yyy

~/.aws/config

[default]
region=ap-northeast-1

[profile development]
region=ap-northeast-1

[profile production]
source_profile=development
role_arn=arn:aws:iam::xxx:role/role-xxx
region=ap-northeast-1

これで以下のように切り替えて使うことができる。めでたし。

aws ec2 describe-instances --profile development
aws ec2 describe-instances --profile production

terraform上のお話

なお、現時点のterraform(0.6.16)ではproviderを複数指定できるものの、resourceのproviderには変数が使えないため、provider汎用のmoduleを作って呼び元で指定しわけるということができない。

同じようなresourceでproviderをハードコードしたのを複数作らないとダメかなぁ。tjたんも「しかたないのでコピペしてる」って言ってるし。

https://github.com/hashicorp/terraform/issues/1819

追記01:

結局、ハードコードしてもproviderのaliasがmoduleに伝わらないことが分かったのでダミーのproviderをトップに置きつつ(「No valid credential sources found for AWS Provider.」って怒られるので)、実際にはaws_workaround.tfなるものを各moduleの中に入れた。うーん。

aws_workaround.tf

# TODO:
# 本当はprovider汎用のmoduleにproviderのaliasを渡してハンドリングしたいが
# terraform 0.6.16ではresourceのproviderに変数が使えず動的に変えられない。
# https://github.com/hashicorp/terraform/issues/1819
# かつ、そもそもmoduleにaliasしたproviderが伝達されない。
# https://github.com/hashicorp/terraform/issues/4789
# 仕方ないのでproviderを個々のmoduleで定義しているがいつ使えなくなるのか分からないので注意。

variable "profile" { }
variable "region" { }

provider "aws" {
  profile = "${var.profile}"
  region = "${var.region}"
}
追記02:

トップのが効いてダメそうだ。うーん。

とりあえず横断的にやるのは諦めて、

env/development/main.tf

env/production/main.tf

module/xxx/main.tf

module/yyy/main.tf

的にやって各envの中(provider定義こみ)でコマンド発行する方向にした。

ただ、今度はterraform remote configでS3にアクセスする際にassume roleが効かないというのが……。

https://github.com/hashicorp/terraform/issues/7014

以下の感じだとそもそもaws-sdk-go自体が対応していないのかな。

https://github.com/aws/aws-sdk-go/issues/472

トラックバック - http://d.hatena.ne.jp/t_yamo/20160604

2016-01-12(Tue)

[]gitをGitLabに移行したよ 13:45 gitをGitLabに移行したよを含むブックマーク gitをGitLabに移行したよのブックマークコメント

GUIなんかいらんわ」って思って私的リポジトリをずっとさくらVPSに入れたgitoliteで運用してたけど、業務ではGitLabずっと使ってるしいろいろ進化してインストールも楽になってるようだから最新版入れてみようかなということで入れてみた。


進化が激しくて巷のブログに「こうやってここを変更する」とか書いてあるのはほとんど陳腐化していて、結局公式ドキュメントに書いてあることを愚直に実行していくのが吉らしい。

ここにも流れを記すけれど、同様にすぐに陳腐化すると思われるので実際にインストールする際にはその時点での公式ドキュメントみるよろし。インストールコマンド実行後にどこを参照しなさいとかどこを変更しなさいとか教えてくれるのでちゃんとコマンド実行後の出力をみてれば大体わかるはず。

今回の条件

注記

  • nginxやpostgresqlなどのミドルウェアは「/opt/gitlab」や「/var/opt/gitlab」に入るのでファイル的には既に入っているものと競合しない。
  • 既存のものと競合するのは基本的にはポートのみ。
  • gitユーザはホームディレクトリが「/var/opt/gitlab」に変更される(GitLabで登録したデプロイキーなどはこの中の.ssh配下で扱われる)。既にgitユーザが存在する場合は「/home/git」の中身自体はそのまま残る。

資料

今回はCentOS6上にいれるので以下がベース。

https://about.gitlab.com/downloads/#centos6

HTTPSで使うので以下も参照。

https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/settings/nginx.md

既存のgitを移行する際には以下を参照。

https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/raketasks/import.md

バックアップのやり方は以下。

https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/raketasks/backup_restore.md

流れ

「/etc/sysconfig/iptables」を更新してポート「12345」をGitLabのWeb UI用に開けて「sudo service iptables restart」する。


公式ドキュメントに沿って以下を実行。ただしドキュメントには「sudo lokkit -s http -s ssh」とあるがiptablesは自分でいじりたかったので実行せず(ほとんどデフォルトで既に入っていたり設定されてたりするが、再度適用しても困るものではないので気にせず実行)。

sudo yum install curl openssh-server postfix cronie
sudo service postfix start
sudo chkconfig postfix on

公式ドキュメントに沿って以下を実行。

curl -sS https://packages.gitlab.com/install/repositories/gitlab/gitlab-ce/script.rpm.sh | sudo bash
sudo yum install gitlab-ce

sudo view /etc/gitlab/gitlab.rb」設定ファイルを開く。巷のサイトではymlを更新してたりするが現バージョンではreconfigureするときに使われるのはこちらのファイル。別のファイルをいじっても上書きされる可能性があるので注意。

自分の場合は以下を変えた。たぶんコメントアウトされてるのはデフォルト値なので同じ値ならわざわざコメントアウトを外さなくてよさそう。

SSL使う場合でもSSL用の設定は不要(後述の通りキーペアを所定のルールで置けば勝手に適用してくれる)。Web UIのポート設定も上記のようにexternal_urlに混ぜておくだけでOK。「○○port」などの設定はいじらなくてよい。


以下のようにオレオレSSL証明書を生成。公開鍵は自分の作業PCの「信頼されたルート証明書」に登録

sudo mkdir -p /etc/gitlab/ssl
sudo chmod 700 /etc/gitlab/ssl

openssl req -nodes -newkey rsa:4098 -keyout foo.bar.com.key -out foo.bar.com.csr -subj "/C=JP/ST=Tokyo/L=Shinjuku-ku/O=FooBar/OU=IT Department/CN=foo.bar.com"
openssl x509 -req -days 3650 -sha256 -in foo.bar.com.csr -signkey foo.bar.com.key -out foo.bar.com.crt

sudo cp foo.bar.com.key foo.bar.com.crt /etc/gitlab/ssl

※ポイントは「/etc/gitlab/ssl」に「【external_urlで設定したホスト名】.key」「【external_urlで設定したホスト名】.crt」というファイル名で格納すること。こうすると設定ファイルに明示しなくても自動的に読み込んでくれるようになる。


これまでの設定後に「sudo gitlab-ctl reconfigure」とするとchefで諸々導入された末にGitLabが起動する。

作業PCから「https://foo.bar.com:12345」にアクセスして初期パスワードログインするとパスワードの変更を求められるので適宜変更しておく。


バックアップroot権限で動くcronに以下を記すと2時に発動して「/var/opt/gitlab/backups」にはき出される。

ここではき出されたバックアップファイルは「/etc/gitlab/gitlab.rb」の「gitlab_rails['backup_keep_time']」に記した期間(秒)が過ぎると次のバックアップ処理時に削除される。

0 2 * * * /opt/gitlab/bin/gitlab-rake gitlab:backup:create CRON=1
トラックバック - http://d.hatena.ne.jp/t_yamo/20160112
カレンダー
2004 | 01 | 05 | 06 | 07 | 11 |
2005 | 04 | 05 | 09 | 10 | 11 | 12 |
2006 | 01 | 02 | 04 | 05 | 06 | 07 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 07 | 08 | 10 | 11 |
2008 | 02 | 04 | 06 | 09 | 10 | 11 | 12 |
2009 | 01 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 08 | 09 | 10 | 11 | 12 |
2011 | 02 | 05 | 06 | 07 | 09 | 10 | 12 |
2012 | 02 | 08 | 11 | 12 |
2013 | 04 | 05 | 08 | 09 | 12 |
2014 | 01 | 02 | 04 | 06 | 08 | 09 | 10 | 11 |
2015 | 01 | 03 | 05 | 07 | 08 | 09 | 11 | 12 |
2016 | 01 | 06 |