GeekFactory

int128.hatenablog.com

Go Modulesに移行した

GitHubで公開しているツールをGo Modulesに移行したので、やったことをメモしておきます。

ソースコード

これまではGOPATHの内側(e.g. ~/go/src/github.com/int128/example)に作業フォルダを置く必要がありましたが、Go Modulesに移行するとGOPATHの外側で作業できるようになります。

# フォルダをGOPATHの外側に移動
mv ~/go/src/github.com/int128/example ~/repo/example
cd ~/repo/example

# go.modの生成
go mod init

# 依存関係をgo.modに追加(go.sumも生成される)
go build -v

新しく生成されたgo.modとgo.sumをコミットしたら完了です。vendoringしていた場合は vendor を削除します。

詳しくは Modules · golang/go Wiki · GitHub を参照してください。

CircleCI

これまではGOPATHに合わせて作業フォルダを配置していましたが、Go Modulesに移行すると適当な場所でOKになります。config.ymlがだいぶ簡潔になります。

version: 2
jobs:
  build:
    docker:
      - image: circleci/golang:1.11.1
    steps:
      - checkout
      - run: go get golang.org/x/lint/golint
      - run: golint -set_exit_status ./...
      - run: go vet
      - run: go test -v ./...

Dockerfile

前項までと同様に、WORKDIRを自由に指定できるようになりました。

FROM golang:1.11.1-alpine AS builder
RUN apk update && apk add --no-cache git gcc musl-dev
WORKDIR /build
COPY . .
RUN go install -v

FROM alpine
RUN apk update && apk add --no-cache ca-certificates
USER daemon
COPY --from=builder /go/bin/example /
CMD ["/example"]

App Engine

app.yamlruntime をGo 1.11に変更します。

runtime: go111
handlers:
- url: /.*
  script: _go_app

他にも main が必須になるなどの変更があります。詳しくは Migrating your App Engine app from Go 1.9 to Go 1.11  |  App Engine standard environment for Go 1.11 docs  |  Google Cloud を参照してください。

二回目の育休日記(生後1ヶ月)

第二子が生まれてから1ヶ月が経ったので記録を残しておく。

産前産後

今回は里帰りではなく東京で出産することにしたので、生まれる前から休むことにした。男性の場合は産前産後休業がないため、子どもが生まれてからでないと育休を取得できない制約がある。それでは遅すぎるので、年休(有給)と育休(無給)を組み合わせることで、生まれる1週間ぐらい前から休みを取った。

夫婦ともに実家が遠いので、基本的に夫婦だけで乗り切る必要がある。実家から応援に来てもらうことも可能だが、家族の飛行機代や宿泊費、慣れない東京生活のサポートなどを考えると、夫婦だけで乗り切った方がよいと判断した。

妻の入院中はワンオペ生活であったが、何回かリハーサルを行うことでスムーズに移行できた。私の場合はもともと保育園の送り迎えをやっていたので混乱が少なかったが、そうでない場合は数ヶ月前から調整を始めた方がよいと思う。

生まれてからの事務処理は以下の通り。早めにやっておいた方がよい。

  • 出生届
  • 児童手当や医療費助成の申請
  • 健康保険証の申請
  • 扶養申請
  • 育休申請(氏名が決まった後の手続き)

生後1ヶ月

最近は以下のようなスケジュールで生活している。

  • 6:30 起床
  • 洗濯する ☆
  • 朝食を作る
  • 保育園の荷物をまとめる
  • 娘を起こす
  • 娘と一緒に朝食を食べる
  • 洗濯物を干す ☆
  • 出かける準備(着替え、歯磨き、顔洗い)
  • 8:40 娘を保育園に送る
  • 息子をお風呂に入れる
  • 昼食を作る(基本的に外部調達)
  • 掃除する ☆
  • 洗濯物を取り込む ☆
  • 15:40 保育園にお迎えに行く
  • 夕食を作る(基本的に外部調達)
  • ゴミを出す ☆
  • 娘をお風呂に入れる
  • 寝る準備(歯磨き、爪切り)
  • 明日の保育園の荷物をまとめる
  • 21:30 娘の寝かしつけ

上記に妻のタスクは入っていない。☆が付いているものは最初は私がやっていたが、最近は主に妻がやってくれている。息子の授乳が3時間おきにあるため、娘担当と息子担当で別々の部屋で寝る運用としている。これで生活時間に起因するトラブルはだいぶ軽減されていると思う。

食事は基本的に外部調達としている。朝食や昼食はスーパーで買った惣菜や弁当と簡単な自炊を組み合わせている。夕食はコープデリで弁当を注文している。買い物はネットスーパーを利用しているが、惣菜や弁当については店舗でないと買えないものがある。以下はネットスーパーで定期的に注文している。

  • お米
  • ミネラルウォーター、お茶
  • おむつ

掃除はルンバで自動化しているが、部屋が無人になる時間帯が少ないため、頻繁に掃除するのは難しい。現状では週1回程度になっている。ルンバが入れない領域や布団掃除機は手作業が必要になる。

娘にとっては赤ちゃんが突然現れることになるので、ストレスをためないようにケアが必要になる。親がイライラしてしまうので難しい。息子が家に帰ってきてから2〜3週間は非常に混乱していたように思う。娘と対話する時間を作ることが大事な気がする。あとは、保育園の往復だけではストレスをためてしまうので、保育園の帰りに寄り道するようにしている。土日は一緒にランチに行っている。

生活費は従前通り月末に精算して折半としている。詳しい運用は以下の記事を参照されたい。

int128.hatenablog.com

生後3ヶ月ぐらいまでは24時間体制になるので、しばらくこの運用を続ける予定である。

勉強

赤ちゃんはよく泣くので、隙間時間で作業するスタイルになる。作業に戻ったら何をしていたか忘れている。そのためバッチサイズを小さくして、手離れよく作業を進めていくことが望ましい。

最近は以下の開発をメインでやっている。

github.com

github.com

Goは実行やテストが速いので、隙間時間の作業に向いていると思う。

NGINX Ingressで複数ドメインを1つのALBに集約する

AWSKubernetesを利用する場合、Ingress Controllerの選択肢にはaws-alb-ingress-controllerkube-ingress-aws-controllernginx-ingressなどがあります。

aws-alb-ingress-controllerやkube-ingress-aws-controllerではKubernetesのレイヤでALBを管理できる反面、シンプルな構成しかサポートされていません。例えば、aws-alb-ingress-controllerでは複数のIngressを1つにALBに集約することができません。また、両者ともインターネットとVPC内部に同一のFQDNでサービスを提供する構成を実現できません*1

本稿では、nginx-ingressを利用して複数ドメインを1つのALBに集約する構成を説明します。ここでは簡単のためワイルドカードドメインで説明しますが、同じ方法で複数のドメインを集約することも可能です。

設計

具体的には、以下のようにKubernetesリソースとAWSリソースを構成します。

上記にInternal ALBやRoute53 Private Hosted Zoneを追加するとインターネットとVPC内部に同一のFQDNでサービスを提供する構成を実現できます。

kopsでKubernetesを管理する場合の構成を下図に示します。EKSの場合もおおよそ同じになるはずです。

f:id:int128:20180718102825p:plain

例えば、Route53やALBのワイルドカードドメイン*.dev.example.com とした場合、www.dev.example.com に対するトラフィックは以下の流れで転送されます。

  1. Route53が www.dev.example.com に対応するALBのAレコードを返す。
  2. ALB Listenerは *.dev.example.comSSL証明書を利用してネゴシエーションを行う。
  3. ALB Target GroupはKubernetes WorkerのNodePortにトラフィックを転送する。
  4. nginx-ingressはHostヘッダとパスでトラフィックの転送先を決定する。例の場合は www.dev.example.com に対応するServiceにトラフィックを転送する。
  5. アプリケーションのPodにトラフィックが到達する。

もし、Hostヘッダとパスに対応するServiceが見つからない場合、nginx-ingressはDefault Backendにトラフィックを転送します。

Helmfile, kops, Terraformによる構成管理

nginx-ingressHelm chartを利用すると簡単に導入できます。Helmの構成管理にHelmfileを利用する場合は以下のようになります。メモリサイズやタイムアウトなどはアプリケーションの特性に合わせて修正してください。

releases:
  - name: nginx-ingress
    namespace: kube-system
    chart: stable/nginx-ingress
    values:
      - rbac:
          create: true
        controller:
          replicaCount: 2
          resources:
            limits:
              memory: 128Mi
            requests:
              memory: 128Mi
          service:
            type: NodePort
            nodePorts:
              http: 30080
          config:
            proxy-read-timeout: "180"
            proxy-send-timeout: "180"
            # Large request header (e.g. OIDC proxy)
            proxy-buffer-size: "64k"
            # Large request body (e.g. file upload)
            proxy-body-size: "512m"
            server-tokens: "false"
        defaultBackend:
          replicaCount: 2
          resources:
            limits:
              memory: 16Mi
            requests:
              memory: 16Mi

kopsでKubernetesを管理する場合、kopsとTerraformが管理するリソースはそれぞれ下表のようになります。

  • kopsが管理するリソース
    • VPC, Subnet, Route Table, Internet Gateway
    • Auto Scaling Group (master), Security Group (API), Security Group (SSH)
    • Auto Scaling Group (node), Security Group (SSH)
  • Terraformが管理するリソース
    • ALB, Listener, Target Group, Security Group (HTTPS)
    • Route53 Hosted Zone, Route53 RecordSet
    • ACM Certificate

Terraformからkopsのリソースを参照するにはNameタグ、もしくはKubernetesクラスタのタグ(kubernetes.io/cluster/name=owned)を利用できます。例えば、Instance Group nodes-ap-northeast-1a に対応するAuto Scaling Groupを参照するには以下のようにNameタグを利用します。

# Auto Scaling Group for the Kubernetes nodes
data "aws_autoscaling_groups" "kops_nodes" {
  filter {
    name   = "key"
    values = ["Name"]
  }

  filter {
    name   = "value"
    values = ["${formatlist("nodes-%s.%s", data.aws_availability_zones.available.names, var.kubernetes_cluster_name)}"]
  }
}

Helmfile, kops, Terraformによる実装をGitHubint128/kops-terraform-starter に置いているので参考にしてください。

評価

この構成は小規模で中央集権的な管理を行うクラスタに向いています。多数のチームが1つのクラスタを共用する場合は、Kubernetesのレイヤで各チームが自律的にALBを管理する方がスムーズに運用できるでしょう。

この構成にはいくつかの課題があります。

まず、すべてのトラフィックがNodePortのNGINXを経由してアプリケーションのPodに到達するため、レイテンシが大きくなります。ALBからアプリケーションのNodePortに直接転送する方式(aws-alb-ingress-controller)の方がレイテンシは小さくなると考えられます。

また、高負荷やメモリリークなどでNGINXに障害が発生するとすべてのトラフィックに影響します。トラフィックが増えた場合やWebSocketを扱う場合の知見はPain(less) NGINX Ingressが非常に参考になります。

複数のInstance Groupや複数のALBを扱いたい場合はTerraformの記述が複雑になります。その場合はkube-ingress-aws-controllerを使うとよいでしょう。EC2とALBの対応を自動的に管理してくれます。また、ALBとSecurity Groupの対応はタグで管理できます。

See Also

*1:kube-ingress-aws-controllerでは同じルールのIngressをinternet-facing/internalに対して重複定義することで可能です。 https://github.com/zalando-incubator/kube-ingress-aws-controller/issues/114