MacでネットワークのDNSサーバを変更する

新しいMacのセットアップや、ネットワークの追加した時にDNSサーバを変更する時があります.
その時のセットアップを楽にするためにスクリプトを書きました.

Gistにアップロードしてあるので、具体的なスクリプトをそちらを参照してください.

コマンド各種

dns-manager.sh [verbs] [optins] を意識して書いています.

Wi-Fiネットワークに設定されているDNSサーバを表示する

dns-manager.sh get -s Wi-Fi

Wi-FiネットワークのDNSサーバにCloudflareのPublic DNSを設定する

dns-manager.sh set -s Wi-Fi -d cloudflare

Wi-FiネットワークのDNSサーバにGoogleのPublic DNSを設定する

dns-manager.sh set -s Wi-Fi -d google

Wi-FiネットワークのDNSサーバに指定したIPアドレスを設定する

dns-manager.sh set -s Wi-Fi -d 192.168.1.1 192.168.1.2

Wi-FiネットワークのDNSサーバをクリアする

dns-manager.sh set -s Wi-Fi

conftestを使ってDeploymentにrequests,limitsが指定されているかをチェックする

最近は、プライベートでも仕事でもKubernetesマニフェストを書いていることが多くなってきました.
今回は「Deploymentのコンテナにrequests,limitsが指定されていること」をconftestというツールを使ってチェックする際のポリシーをメモしておきます.

# requestsが指定されていること
deny[msg] {
    input.kind = "Deployment"
    c := input.spec.template.spec.containers[_]
    not c.resources.requests

    msg = sprintf("%sコンテナにrequestsを指定してください", [c.name])
}

# limitsが指定されていること
deny[msg] {
    input.kind = "Deployment"
    c := input.spec.template.spec.containers[_]
    not c.resources.limits

    msg = sprintf("%sコンテナにlimitsを指定してください", [c.name])
}

OPAのIterationを参考に書いています.
OPAのIterationのArrayのサンプルでは、

# iterate over indices i
arr[i]

# iterate over values
val := arr[_]

# iterate over index/value pairs
val := arr[i]

と書かれており、今秋のようにDeployment内のコンテナにrequests,limitsが指定されていることを確認したければ、Iterationをポリシー内で使用してチェックすれば良いわけです.

おまけ

ちょっとしたおまけですが、どのコンテナがポリシー違反なのかを分かりやすくするためにsprintfを使ってコンテナ名を出力しています.
こういった細かい事でも出力するのと、しないのとで実際に利用するときのデバッグのしやすさが変わりますからね.

自宅k8s環境向けにownCloudのマニフェストを書く

週末に自宅で動かしているownCloudを、Kubernetesに乗せるためにマニフェストを定義していた.
私用用途なのでPV使うまでもないかなとhostPathを使ったり、ラベルの指定もまばらで規則性がないので気が向いた時に直しておきたい.

マニフェスト

ira-apps/owncloudで定義しているマニフェストkustomize buildの出力結果は以下のような形です.

Ingress

ファイルアップロードがされるWebアプリなので、とりあえずclient_max_body_sizeを20GBに指定しています.

---
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  annotations:
    kubernetes.io/ingress.class: nginx
    nginx.ingress.kubernetes.io/proxy-body-size: 20G
  labels:
    app.kubernetes.io/component: owncloud-server
    app.kubernetes.io/instance: owncloud
    app.kubernetes.io/managed-by: argocd
    app.kubernetes.io/name: owncloud-server
    app.kubernetes.io/part-of: owncloud
  name: owncloud-server
  namespace: owncloud
spec:
  rules:
  - host: owncloud.local
    http:
      paths:
      - backend:
          serviceName: owncloud-server
          servicePort: http

Service

WebアプリとDBのDeploymentを分けている都合上、Serviceもそれぞれ定義しています.

apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: server
    app.kubernetes.io/instance: owncloud
    app.kubernetes.io/managed-by: argocd
    app.kubernetes.io/name: owncloud-server
    app.kubernetes.io/part-of: owncloud
  name: owncloud-server
  namespace: owncloud
spec:
  ports:
  - name: http
    port: 80
    protocol: TCP
    targetPort: http
  selector:
    app.kubernetes.io/instance: owncloud
    app.kubernetes.io/managed-by: argocd
    app.kubernetes.io/name: owncloud-server
    app.kubernetes.io/part-of: owncloud
  type: ClusterIP

---
apiVersion: v1
kind: Service
metadata:
  labels:
    app.kubernetes.io/component: db
    app.kubernetes.io/instance: owncloud
    app.kubernetes.io/managed-by: argocd
    app.kubernetes.io/name: owncloud-db
    app.kubernetes.io/part-of: owncloud
  name: owncloud-db
  namespace: owncloud
spec:
  ports:
  - name: mysql
    port: 3306
    protocol: TCP
    targetPort: mysql
  selector:
    app.kubernetes.io/instance: owncloud
    app.kubernetes.io/managed-by: argocd
    app.kubernetes.io/name: owncloud-db
    app.kubernetes.io/part-of: owncloud
  type: ClusterIP

Secret

今回は載せませんが、SealedSecretを使って定義してます.
導入は多少面倒かもしれませんが、クラウドプロパイダを利用しない場合は手軽です.

Deployment

helm/chartsにあったownCloudのマニフェストを参考にしています.
PVCを利用してソフトウェアRAIDを組んでいるディスクに書き込みたかったんですが、サクッとKubernetsに移行しておきたかったので、hostPathで動かしています.

apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/component: server
    app.kubernetes.io/instance: owncloud
    app.kubernetes.io/managed-by: argocd
    app.kubernetes.io/name: owncloud-server
    app.kubernetes.io/part-of: owncloud
  name: server
  namespace: owncloud
spec:
  selector:
    matchLabels:
      app.kubernetes.io/instance: owncloud
      app.kubernetes.io/managed-by: argocd
      app.kubernetes.io/name: owncloud-server
      app.kubernetes.io/part-of: owncloud
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app.kubernetes.io/instance: owncloud
        app.kubernetes.io/managed-by: argocd
        app.kubernetes.io/name: owncloud-server
        app.kubernetes.io/part-of: owncloud
    spec:
      containers:
      - env:
        - name: OWNCLOUD_DOMAIN
          value: localhost
        - name: OWNCLOUD_DB_TYPE
          value: mysql
        - name: OWNCLOUD_MYSQL_UTF8MB4
          value: "true"
        - name: OWNCLOUD_DB_HOST
          value: owncloud-db
        - name: OWNCLOUD_DB_NAME
          value: owncloud
        - name: OWNCLOUD_DB_USERNAME
          value: owncloud
        - name: OWNCLOUD_DB_PASSWORD
          valueFrom:
            secretKeyRef:
              key: MYSQL_PASSWORD
              name: owncloud-db
        image: owncloud/server:10.3.2
        imagePullPolicy: Always
        livenessProbe:
          exec:
            command:
            - /usr/bin/healthcheck
          initialDelaySeconds: 5
          periodSeconds: 30
          timeoutSeconds: 10
        name: owncloud-server
        ports:
        - containerPort: 8080
          name: http
        readinessProbe:
          exec:
            command:
            - /usr/bin/healthcheck
          initialDelaySeconds: 120
          periodSeconds: 30
          timeoutSeconds: 10
        volumeMounts:
        - mountPath: /mnt/data
          name: owncloud-server
      volumes:
      - hostPath:
          path: /geminos/kubernetes/owncloud/server
        name: owncloud-server

---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app.kubernetes.io/component: db
    app.kubernetes.io/instance: owncloud
    app.kubernetes.io/managed-by: argocd
    app.kubernetes.io/name: owncloud-db
    app.kubernetes.io/part-of: owncloud
  name: db
  namespace: owncloud
spec:
  selector:
    matchLabels:
      app.kubernetes.io/instance: owncloud
      app.kubernetes.io/managed-by: argocd
      app.kubernetes.io/name: owncloud-db
      app.kubernetes.io/part-of: owncloud
  strategy:
    type: Recreate
  template:
    metadata:
      labels:
        app.kubernetes.io/instance: owncloud
        app.kubernetes.io/managed-by: argocd
        app.kubernetes.io/name: owncloud-db
        app.kubernetes.io/part-of: owncloud
    spec:
      containers:
      - args:
        - --default-authentication-plugin=mysql_native_password
        env:
        - name: MYSQL_DATABASE
          value: owncloud
        - name: MYSQL_USER
          value: owncloud
        - name: MYSQL_PASSWORD
          valueFrom:
            secretKeyRef:
              key: MYSQL_PASSWORD
              name: owncloud-db
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              key: MYSQL_ROOT_PASSWORD
              name: owncloud-db
        image: mysql:8
        livenessProbe:
          exec:
            command:
            - /usr/bin/mysqladmin
            - -u root
            - --password="${MYSQL_ROOT_PASSWORD}"
            - ping
          initialDelaySeconds: 5
          periodSeconds: 30
          timeoutSeconds: 10
        name: owncloud-db
        ports:
        - containerPort: 3306
          name: mysql
        readinessProbe:
          exec:
            command:
            - /usr/bin/mysqladmin
            - -u root
            - --password="${MYSQL_ROOT_PASSWORD}"
            - ping
          initialDelaySeconds: 5
          periodSeconds: 30
          timeoutSeconds: 10
        volumeMounts:
        - mountPath: /var/lib/mysql
          name: owncloud-db
        - mountPath: /var/lib/backup
          name: owncloud-db-backup
      volumes:
      - hostPath:
          path: /geminos/kubernetes/owncloud/db
        name: owncloud-db
      - hostPath:
          path: /geminos/kubernetes/owncloud/db-backup
        name: owncloud-db-backup

tmuxのステータスラインにk8sのvwntextとnamespaceを表示する

複数のk8sクラスタに接続する頻度が増えてきたので、現在どのコンテキストへ接続しているのかが分かりやすくtmuxのステータスラインに表示するようにしました.

f:id:corrupt952:20210127074407p:plain

この画像で言えば(kind-ira/owncloud)が、それぞれ現在選択するContextとNamespaceです.
実際の設定は、

set-option -g status-right "#[fg=colour33]#(/bin/bash ${HOME}/bin/tmux-kubectl) #[fg=green][#S:#I.#P]"

と定義し、スクリプトの実行結果をステータスラインに表示しています.
細かいケースを考慮できていないかもしれませんが、スクリプトは以下のように定義しています.

#!/usr/bin/env bash

if [[ -z "$(which kubectl 2>/dev/null)" ]]; then
    echo "kubectl doesn't exist"
    exit 1
fi

kubeconfig="$HOME/.kube/config"
if [[ -n "${KUBECONFIG}" ]]; then
    kubeconfig=${KUBECONFIG}
fi

context="$(kubectl config current-context 2>/dev/null)"
if [[ -z "${context}" ]]; then
    echo "current-context doesn't exist"
    exit 1
fi

namespace="$(kubectl config view -o "jsonpath={.contexts[?(@.name==\"${context}\")].context.namespace}" 2>/dev/null)"
[[ -z "${namespace}" ]] && namespace="default"

echo "(${context}/${namespace})"

今後、使っていく時に何か不都合があれば少しずつ改善していきますか.

Lambdaの関数名とランタイムの一覧を取得する

今朝、Lambdaの関数名とランタイムの一覧を取得するPythonスクリプト書いたので貼っておきます.
シェルの実装でも良かったが、これをベースに他のスクリプトを組む予定なのでboto3で実装してみました.

#!/usr/bin/env python3

import json
import boto3

def aws_region_names():
    ec2 = boto3.client('ec2')
    regions = ec2.describe_regions()['Regions']
    return sorted([regions[_]['RegionName'] for _ in range(len(regions))])

def lambda_functions(region_name):
    client = boto3.client('lambda', region_name=region_name)
    functions = client.list_functions()['Functions']
    return [{ 'Name': functions[_]['FunctionName'], 'Runtime': functions[_]['Runtime']} for _ in range(len(functions))]

if __name__ == '__main__':
    region_functions = {}
    for region_name in aws_region_names():
        region_functions[region_name] = lambda_functions(region_name)
    print(json.dumps(region_functions))

処理の流れは書くまでもないですが、

  • リージョン一覧取得
  • リージョンごとのLambda Function一覧取得

をしています.
処理自体は簡単だが、関数名とランタイムを抽出しているリスト内包表記は大分見づらいので改善の余地はありそうです.

某社SREとしての1年間

これはSRE Advent Calendar 20195日目の記事です.

一昨日、6Vメタモンを入手することができてホクホク顔の@corrupt952です.

技術的なことは他の方が書いてくれると思うので、某社SREとして過ごしてきた約1年間の話を簡潔に書きます.
SREに興味ある人や某社SREに興味ある人の参考になれば幸いです.

某社SREになった経緯

前職はソフトウェアエンジニアとしてSIerで働いていました.
私がSREになった理由は、「転職しました」に書いてある

自分に足りない知識を身につけ、自分の知識を元に挑戦したいという思いがありました.

を実現するための手段としてSREにチャレンジしてみたかったからでした.

SREやインフラに興味を持ったきっかけは @f440@yoppiの3人でISUCON5に出たのがきっかけでした.
あの時は結果として予選落ちだったのですが、自分にとってはアプリケーションを動かすために速く動かすために、アプリケーションコードだけでなくサービスアーキテクチャやインフラ側により目を向けていたので良いきっかけになりました.

そういった経緯もあって声をかけてもらった某社にSREとして入社することを決めました.

そもそもSREって何?

これに関してはネットに情報が溢れているので割愛しますが、各社によって実際の役割や責務が異なります.

某社におけるSREは以下の役割を持っています.

  • サービスの環境構築・運用
  • サービスの可用性・パフォーマンスの維持・向上
  • 障害対応
  • CI/CD整備
  • 開発環境の整備
  • パフォーマンスに懸念があるロジックのレビューや改善
  • セキュリティの担保
  • ログ収集・分析基盤の構築・運用
  • 中長期を見据えたアーキテクチャや基盤を先回りして検討や設計 

などがあります. ※1
これだけ見ると、インフラエンジニアと言われる職種に他の役割を足したように見えますよね.

プライベートでやってきたこと

プライベートではこれといって活動をしてはいません.
強いてやっていることは、

  • 読書
  • 情報収集

の2つです.

技術書や技術書に限らず、気になる本やいずれ必要になりそうな本を空き時間に消化しています.
これは持論なんですが、「本に答えを求めておらず、読んだ内容を元に自分なりに咀嚼し解釈、背景の想像・理解、現実への適用方法、代替方法の模索・検討、他の技術・概念との関係性を考えるために読む」というのを意識して本を読んでいます.

業務としてやってきたこと

参考にはなるかは分かりませんが、当たり障りのない範囲で業務としてやってきたことを書きます.

  • 組織内の開発環境整備
  • 新サービスの設計、構築、CI/CD整備
  • バックアップ設計、開発、構築
  • 監視基盤設計・見直し・実装
  • 不要なリソースの洗い出しや削除
  • コンテナ推進
  • Infrastructure as a Code推進
  • SRE Office Hoursの開催
  • 社内勉強会でのLT
  • 技術書の輪読

それぞれの業務について概要を書きたいですが、長くなるのでいくつかピックアップします.

コンテナが最適解だとは思いませんが、コンテナ技術を導入することで効率が良くなったり、楽ができると判断したため開発環境の整備やコンテナ推進を進めています.
The Twelve-Factor Appをベースにしたチェックリストを作りましたも推進に向けて、プライベートの時間で自分なりに解釈した結果を書いていました.
実際の業務ではこれを組織に落とし込んで、各サービスレベルで落とし込んで作業をしています.

某社のSREは役割が多岐に渡るため、組織が急激に成長するといつか破綻してしまうというリスクがありました.
そういったリスクに対して事前に対策を打つための1手段として、SREが持っている知識や経験則を共有、SREと一緒に悩む場として「SRE Office Hours」という活動を毎週金曜日に枠を設けて実施しています.
この活動はマイクロサービスについて何でも聞けるOffice Hoursに参加したよ! #メルカリな日々に影響を受けて企画し、今も開催し続けています. ※2
技術書の輪読もそういった活動の一環で、「仕組みはどうなっているのか」「弊社ではどうなのか」「これを改善していけるのではないか」といった疑問を持つきっかけだったり、議論をする場として活動しています.
社内勉強会のLT枠でも、SREが持っている知識を共有する場の1つとして利用させてもらっています.

最後に

技術的な話が皆無で申し訳ないですが、某社SREとして1年間やってきたことを書きました.
ぶっちゃけスキルの範囲が広いし、やることは尽きないし、障害対応もしなきゃないし大変な仕事ではあります.
裏を返せば「自分に足りない知識を身につけ、自分の知識を元に挑戦したい」を達成することができる仕事で面白いと個人的には思います.

興味があるかたは是非SREにチャレンジしてみてください.

次はfnaotoさんが6日目の記事を書くようですね. 楽しみです!

※1 全てをSREがやっているわけではなく他チームと協力してやっている業務もあります
※2 この活動の具体的な内容について気になる人は、12/11公開予定の某社アドベントカレンダーの記事を見てくれるとうれしいです

InkdropからBoostnoteに乗り換えた

1年半程度はInkdropでブログの下書き・メモ・議事録の下書きをしてきましたが、
Inkdropである必要がなくなってきたので一旦Inkdropを解約してBoostnoteに乗り換えました.

メモ書きツールに求めるモノ

khasegawa.hatenablog.com

以前書いた記事をベースにメモ書きツールに求めるモノがどう変化したのかをまとめてみます.

太字の項目が今回追加された求めるモノです.
欲張りですね.

複数のプラットフォーム間でデータを共有できること

マルチプラットフォームで動作することは大事ですが、データが共有されることも重要です.
同期速度が速いことが理想ですが、起動してネットワークに接続し、自動起動されるアプリケーションが起動し終わる頃に同期が終了していれば問題ありません.

キーバインドVimライクにできること

エンジニアとしてアルバイトをしている時から、開発するときにはVimもしくはNeovimを使っています.
そのため、メモツールでもできる限り同じ操作ができることが望ましいです.

何故、InkdropからBoostnoteへ変えたのか

まずは「何故、Inkdropをやめたのか」について書きます.
積極的な理由はなかったのですが、既に私が利用しているツールやサービスとOSSなツールで組み合わせて使える他の手段がないか?を模索していた結果、「一旦Inkdropを使うのをやめてみよう」と思うに至りました

次に「何故、Boostnoteを選んだのか」について書きます.
私のメモ書きツールに求めるモノを満たすのは、私が観測している範囲ではInkdropのみです.
では何故、Boostnoteにしたのか?という点が疑問になるはずですが、Boostnoteがどれだけ要求事項を満たしているかを書くにしてみましょう.

「複数のプラットフォーム間でデータを共有できること」以外は満たしていることになるため、何らかの形でデータを共有できれば全ての要求事項を満たすことができます.
Boostnoteでは、Cloud Syncing and Backupというページが公式から用意されており、DropboxGoogle Driveの共有フォルダを使うように書いてありました.
今回はそれに則ってGoogle Driveを利用して同期し、「複数のプラットフォーム間でデータを共有できること」を満たすようにしてみます.

Google Driveを使って同期する

Google Driveを手元のPCと同期させるには、バックアップと同期ドライブファイルストリームと2種類のやり方が存在します.
それぞれの違いについては、Googleドライブヘルプのバックアップと同期、ドライブ ファイル ストリームの違いを参照してください.

ここでは、今回の利用用途で関係ありそうな箇所を抜粋しました.

バックアップと同期 ドライブファイルストリーム
マイドライブ内のファイルを使用する
マイドライブ内の選択したフォルダのみを同期する
マイドライブの個別のファイルのみを同期する 不可
個人の Google アカウントで使用する 不可

これら2つの違いは、「個別ファイルの同期の可否」と「個人のGoogleアカウントでの利用可否」になりそうですね.
私の利用用途で考えると、Boostnote用のディレクトリのみを個別に同期したいですし、GSuiteを個人契約しているため、ドライブファイルストリームを使うのが良さそうです.

ドライブファイルストーリムを導入するを参考に導入し、Boostnote用のディレクトリ作成します.
後はそのディレクトリを同期すれば準備は完了です.

f:id:corrupt952:20210131110408j:plain

Boostnote with DFSの導入自体は非常に簡単でした.

Boostnoteに乗り換えて良かったこと・不満に感じていること

導入し実際に使ってみて以下の項目は良かったと感じました.

  • 1つのスニペットで複数のファイルを定義できる
  • Inkdropよりも軽快に感じる
  • GSuiteを契約していれば別途利用料金は発生しない

しかし、Boostnoteに乗り換えてみて良かったことばかりではありません.
以下の項目は導入してみて多少なりとも不満に感じている項目です.

最後に

久しぶりに自分のメモツールについて見直しました.
改めて振り返ると自分の求めているモノを言語化する必要性を感じます.

Boostnoteに乗り換えて良かったことが私が想定していたよりも少なかったですが、Inkdropの時と比べて不便というのは感じていません.
そのため、正直InkdropかBoostnoteかはたまた別のエディタなのかは、個人の求めるモノによって大きく変わりそうです.

また近いうちに変更しそうですが、今年度はBoostnoteでメモを書いてみようと思います.