ローカル環境(kind)でingressを試す
はじめに
kind (kubernetes in docker)でIngressを作成したい場合、ingress controllerを使う必要があります。
しかし、ドキュメント通りに実行してもM1 macなどのApple シリコンでは動きません。
理由は hashicorp/http-echo が arm64に対応していないからです。
$ docker container run --rm hashicorp/http-echo:latest -text 'aaa' WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested runtime: failed to create new OS thread (have 2 already; errno=22) fatal error: newosproc runtime stack: runtime.throw(0x6955a0, 0x9) /usr/local/go/src/runtime/panic.go:596 +0x95 runtime.newosproc(0xc420022000, 0xc420032000) /usr/local/go/src/runtime/os_linux.go:163 +0x18c runtime.newm(0x6a4348, 0x0) /usr/local/go/src/runtime/proc.go:1628 +0x137 runtime.main.func1() /usr/local/go/src/runtime/proc.go:126 +0x36 runtime.systemstack(0x7cc200) /usr/local/go/src/runtime/asm_amd64.s:327 +0x79 runtime.mstart() /usr/local/go/src/runtime/proc.go:1132 goroutine 1 [running]: runtime.systemstack_switch() /usr/local/go/src/runtime/asm_amd64.s:281 fp=0xc42001e788 sp=0xc42001e780 runtime.main() /usr/local/go/src/runtime/proc.go:127 +0x6c fp=0xc42001e7e0 sp=0xc42001e788 runtime.goexit() /usr/local/go/src/runtime/asm_amd64.s:2197 +0x1 fp=0xc42001e7e8 sp=0xc42001e7e0
今回はM1 Macでも動くように、修正します
クラスターを作成する
kind-config.yaml
kind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 name: ingress-nginx nodes: - role: control-plane kubeadmConfigPatches: - | kind: InitConfiguration nodeRegistration: kubeletExtraArgs: node-labels: "ingress-ready=true" extraPortMappings: - containerPort: 80 hostPort: 80 protocol: TCP - containerPort: 443 hostPort: 443 protocol: TCP
$ kind create cluster --config kind-config.yaml $ kind get clusters ingress-nginx $ kubectl config current-context kind-ingress-nginx
ingress-nginxをapplyする
$ kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/main/deploy/static/provider/kind/deploy.yaml $ kubectl get pod -n ingress-nginx NAME READY STATUS RESTARTS AGE ingress-nginx-admission-create-8m6zg 0/1 Completed 0 35m ingress-nginx-admission-patch-rc9bm 0/1 Completed 1 35m ingress-nginx-controller-6bccc5966-249gx 1/1 Running 0 35m $ kubectl api-resources | grep ingress ingressclasses networking.k8s.io/v1 false IngressClass ingresses ing networking.k8s.io/v1 true Ingress $ kubectl get ingressclasses -n ingress-nginx NAME CONTROLLER PARAMETERS AGE nginx k8s.io/ingress-nginx <none> 37m $ kubectl get ingressclasses -n ingress-nginx nginx -o yaml apiVersion: networking.k8s.io/v1 kind: IngressClass metadata: annotations: kubectl.kubernetes.io/last-applied-configuration: | {"apiVersion":"networking.k8s.io/v1","kind":"IngressClass","metadata":{"annotations":{},"labels":{"app.kubernetes.io/component":"controller","app.kubernetes.io/instance":"ingress-nginx","app.kubernetes.io/name":"ingress-nginx","app.kubernetes.io/part-of":"ingress-nginx","app.kubernetes.io/version":"1.5.1"},"name":"nginx"},"spec":{"controller":"k8s.io/ingress-nginx"}} creationTimestamp: "2022-11-11T14:16:22Z" generation: 1 labels: app.kubernetes.io/component: controller app.kubernetes.io/instance: ingress-nginx app.kubernetes.io/name: ingress-nginx app.kubernetes.io/part-of: ingress-nginx app.kubernetes.io/version: 1.5.1 name: nginx resourceVersion: "1550" uid: 9ed127ea-e3e3-4051-910e-0486d1e9c53a spec: controller: k8s.io/ingress-nginx
http-echoのイメージを作成する
ドキュメントのUsing Ingress ではPodやServiceをapplyすることになるのですが、先ほどお伝えしたようにM1 macではエラーになるので、イメージをhttp-echoのコードから作成します。
Dockerfile
FROM golang:1.19.3-bullseye as build RUN git clone https://github.com/hashicorp/http-echo.git RUN cd http-echo && CGO_ENABLED=0 go build FROM gcr.io/distroless/static-debian11:debug COPY --from=build /go/http-echo/http-echo / ENTRYPOINT [ "/http-echo" ]
$ docker build . -t http-echo:0.0.1 $ docker image ls http-echo REPOSITORY TAG IMAGE ID CREATED SIZE http-echo 0.0.1 500c71506129 3 days ago 9.86MB
クラスターにイメージを入れる
自分でbuildしたイメージをPodに指定して使用するには、 kind load docker-image
コマンドを使ってクラスターにイメージを渡します
$ kind load docker-image http-echo:0.0.1 --name ingress-nginx $ docker exec -it ingress-nginx-control-plane crictl images | grep http-echo docker.io/library/http-echo 0.0.1 500c71506129b 11.4MB
Loading an Image Into Your Cluster
Ingressを作成する
Using Ingressからマニフェストをコピーしてimageを変更します
app.yaml
kind: Pod apiVersion: v1 metadata: name: foo-app labels: app: foo spec: containers: - name: foo-app # image: hashicorp/http-echo:0.2.3 ではなく http-echo:0.0.1に変更 image: http-echo:0.0.1 args: - "-text=foo" --- kind: Service apiVersion: v1 metadata: name: foo-service spec: selector: app: foo ports: # Default port used by the image - port: 5678 --- kind: Pod apiVersion: v1 metadata: name: bar-app labels: app: bar spec: containers: - name: bar-app # image: hashicorp/http-echo:0.2.3 ではなく http-echo:0.0.1に変更 image: http-echo:0.0.1 args: - "-text=bar" --- kind: Service apiVersion: v1 metadata: name: bar-service spec: selector: app: bar ports: # Default port used by the image - port: 5678 --- apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress spec: rules: - http: paths: - pathType: Prefix path: "/foo" backend: service: name: foo-service port: number: 5678 - pathType: Prefix path: "/bar" backend: service: name: bar-service port: number: 5678 ---
$ kubectl apply -f app.yaml $ kubectl get pod NAME READY STATUS RESTARTS AGE bar-app 1/1 Running 0 26s foo-app 1/1 Running 0 26s
動作確認
$ curl localhost/foo foo $ curl localhost/bar bar
M1 Macでも無事動くことが確認できました。
クラスター削除
$ kind delete cluster --name ingress-nginx
忙しい人向け: docker composeでnginxを立てる
ディレクトリ構造
$ tree . ├── README.md ├── conf.d │ └── default.conf └── docker-compose.yaml
docker-compose.yaml
version: '3.9' services: app: image: nginx:1.23-alpine ports: - "8080:80" volumes: - "./conf.d:/etc/nginx/conf.d"
conf.d/default.conf
server { listen 80; listen [::]:80; server_name localhost; #access_log /var/log/nginx/host.access.log main; location / { root /usr/share/nginx/html; index index.html index.htm; } #error_page 404 /404.html; # redirect server error pages to the static page /50x.html # error_page 500 502 503 504 /50x.html; location = /50x.html { root /usr/share/nginx/html; } # proxy the PHP scripts to Apache listening on 127.0.0.1:80 # #location ~ \.php$ { # proxy_pass http://127.0.0.1; #} # pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000 # #location ~ \.php$ { # root html; # fastcgi_pass 127.0.0.1:9000; # fastcgi_index index.php; # fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; # include fastcgi_params; #} # deny access to .htaccess files, if Apache's document root # concurs with nginx's one # #location ~ /\.ht { # deny all; #} }
実行
$ docker compose up
ブラウザで localhost:8080 にアクセス
終了
$ docker compose down
go言語のライブラリ、samber/loのGroupByが便利!
はじめに
go言語で実装をしていると、特定の条件に従ってスライスを複数のスライスに分割したい、というケースに遭遇します。
イメージとしてはこんな感じです。
sliceA, sliceB := SplitSlice(slice)
素直に実装するならば、関数内部で条件分岐して対応しているケースが多いのではないでしょうか?
func SplitSlice(s []int) ([]int, []int) { sliceA := make([]int,0) sliceB := make([]int,0) for _, x := range slice { if condA { sliceA = append(sliceA, x) } if condB { sliceB = append(sliceB, x) } } return sliceA, sliceB }
samber/lo
こういったケースは samber/lo のGroupByを使用すれば、簡単に実装できます。
以下はREADMEに書いているサンプルです。
func main() { list := []int{0, 1, 2, 3, 4, 5} result := lo.GroupBy(list, func(i int) int { return i % 3 }) for _, item := range result { fmt.Printf("%v\n", item) // [0 3] // [1 4] // [2 5] } }
https://github.com/samber/lo#groupby
正直これだけだと、すぐに理解するのは難しいと思います。もう少し実際にありそうなサンプルコードを作ってみました。
userにはそれぞれロールがあり、データはどこかのサーバからhttpを通して取得することを想定しています。
今回はそのロールごとにスライスを分割する例を表しています。
type Role string const ( Admin = Role("admin") Operator = Role("operator") Developer = Role("developer") ) type user struct { id string role Role } func GetUsersFromHogeAPI() []user { return []user{ { id: "1", role: Admin, }, { id: "2", role: Operator, }, { id: "3", role: Developer, }, { id: "4", role: Developer, }, } } func main() { users := GetUsersFromHogeAPI() // resultの型はmap[Role][]user result := lo.GroupBy(users, func(u user) Role { // mapのkeyは以下のreturnで決まる!! return u.role }) fmt.Printf("%+v\n", result[Admin]) // [{id:1 role:admin}] fmt.Printf("%+v\n", result[Operator]) // [{id:2 role:operator}] fmt.Printf("%+v\n", result[Developer]) // [{id:3 role:developer} {id:4 role:developer}] }
https://go.dev/play/p/sduv6XcX-8U
result変数は map[Role][]user
型となっており、keyにRoleを渡してあげるとuserのスライスが取得できます。
そのほかもこのライブラリにはたくさんの関数が用意されているので、mapやsliceをforで操作するときは使えそうな関数がないか探してみると実装が楽になるかもしれません。
最後に
このライブラリに対する感想は、実装で積極的に使ってもいいと思っています。
理由としては、
- Filter, Reduce, Mapと見るだけで、データに対してどんな操作をしているのかすぐにわかる
- 一つ一つの実装は数行なので、たとえメンテナンスされなくなったとしても自前で実装できる
です
実際 GruopByのソースコードを見てみると10行ぐらいであることがわかると思います。
func GroupBy[T any, U comparable](collection []T, iteratee func(item T) U) map[U][]T { result := map[U][]T{} for _, item := range collection { key := iteratee(item) result[key] = append(result[key], item) } return result }
個人的におすすめなので、実際に使ってみてはどうでしょうか?
Professional Cloud Architect 合格しました
はじめに
2021年9月に転職してから半年(2022年3月)が経過し、Professional Cloud Architectを受験しに行きました
Associateに合格したときは以下にあります ktbbrk.hatenablog.com
対策
主に試験対策として使った教材は以下の2つです。
問題集はテスト1つあたり2 ~ 3周して約80%以上にしてから望みました。
Google Cloud Certified Professional Cloud Architect - whizlabs
2022 Latest Google Cloud Architect Practice Question - udemy
試験前日
sentinelというアプリをインストールしました
試験当日
当日はレンタルスペースを試験開始時間30分前から3時間借りてオンラインで受けました
持っていったものは
です
開始前
Webassessorというサイトでアカウントを作成する必要があるのですが、名前を漢字で入力したので身分証明書として運転免許証を使いました
予約した時間10分前ぐらいからWebassessorのホーム画面にある登録済み試験
のボタンからいけるページのスケジュール済みの試験
に開始
ボタンがあるので、それをクリックして始めます
前回のAssociateの試験のときと違い、試験監督者とは日本語でチャットしました。
部屋をカメラで映す必要があるので、Mac Bookを手に持って部屋の周り、天井、床をそれぞれ5秒ずつ映すと"もう大丈夫"と言われました
そしてMac Bookと充電コードを映すように言われスマホで写真をとって見せ、最後にPCの全面を見せてくれと言われるのでそれもスマホで写真を撮って見せると確認が終わったらしく、試験がスタートしました
所感
実際の試験では問題集と同じ問題が出題されたのでUdemy等で対策は必要だと思いました。
しかし、実際に自分で手を動かしたほうが、理解が早くなるので実務で触れる機会があるなら積極的に触ったほうがいいと思っています
最後に
Associateに合格してからは実務でGCPを触ることはほとんどなかったですが、問題集をやれば意外に合格することがわかりました
golang genericsでswitch-caseをする
はじめに
Go言語では1.18でgenericsの機能が入ってくるので勉強していたのですが引数に型パラメータがあった場合どのようにswitchするのか気になったので備忘録を残します
引数の型が interface{}
のとき
proposalに書いてるswitch文を見てみるとcase
の部分で型パラメータが使われている点以外、引数は interface{}
になっており、普段go言語を書いている通りの記述になると思います
Generic types as type switch cases - Type Parameters Proposal
type Mystruct struct { fieldA string } func Switch[T any](v interface{}) (T, bool) { switch v := v.(type) { case T: return v, true default: var zero T return zero, false } } func main() { x, ok := Switch[string]("string") if ok { fmt.Println(x) // string } y, ok := Switch[Mystruct](Mystruct{fieldA: "a"}) if ok { fmt.Println(y) // {a} } }
https://gotipplay.golang.org/p/nQJNsMiwkzh
引数の型が型パラメータの場合
では、関数の引数の型が型パラメータだった場合にどのようにswitchを使うのかをprosalから探してみます
サンプルコードがありました。(※少しだけコードを変更しています)
Identifying the matched predeclared type - Type Parameters Proposal
type Float interface { ~float32 | ~float64 } func NewtonSqrt[T Float](v T) (int, T) { var iterations int switch (interface{})(v).(type) { case float32: iterations = 4 case float64: iterations = 5 default: // panic: unexpected type main.MyFloat panic(fmt.Sprintf("unexpected type %T", v)) } return iterations, v } type MyFloat float32 func main() { iterations, x := NewtonSqrt(MyFloat(64)) fmt.Println(iterations) fmt.Println(x) }
https://gotipplay.golang.org/p/te5gOiFyx2I
proposalを読んでみると以下のように書いてありました
The design doesn't provide any way to test the underlying type matched by a
~T
constraint element.
つまり、現時点 (2022/1/29)ではunderlying typeのswitch文は書けそうにありません。
しかし、あくまでunderlying typeのときの話なので、今度はMyFloat
ではなくfloat型を関数に渡してみた結果無事に結果が表示されました。
最後に
type switchに関するproposalが出ているので、将来的にやりたいことはできるのではと思っています
これからもproposalやリリース情報は追った方がよさそうです
Grafana 8のアラートはprovisioningできないのか?
はじめに
Grafanaでalertの設定 (Notification policies, Contact points)をしたいときダッシュボードのjsonファイルのように通知先の設定もyamlなどで定義したいと考えると思います
同じく悩んでいる人がいることが以下のリンクから確認できます
Provisioning contact points - Configuration - Grafana Labs Community Forums
※ この記事は 2022年1月25日に書いており、以下の画像のGrafanaはv8.3.3を使用しています。
※ちなみにこの時点の最新バージョンはv8.3.4です
結論
2022年1月現在、まだ宣言的に設定をすることができないようです。
currently, provisioning is not supported for Grafana 8 alerts
現在 issue #36153のコメントでは、
so perhaps 8.4/8.5 is a reasonable estimate at this time
とあり、マイルストーンにもタスクが追加されたので、8.4/.8.5らへんでリリースされるかもしれません
まとめ
将来的にリリースされることはほぼ確定だと思うので、リリースが楽しみですね
最後に
Grafana 8.2.0-beta2のリリースでは ngalert feature toggle
が非推奨になりました
そのためgithubのissueやblogなどでngalertが出てきても、それはおそらく古い記事なのでGrafanaのバージョンには注意しましょう
TweetDeckにリストからtweetを検索する方法
ケース
複数アカウントから特定のtweetのみ検索したい、でもリツイートは表示したくない
手順
まずはじめに検索の対象にしたいアカウントのリストを作成します。作成の仕方は以下のリンクを参考にしてください
作成が終わり、リストを表示するとlistの番号(白く塗りつぶしてある部分)がurlに書いてあると思います。この番号をコピーしておきます
次にTweetDeckに移行します
調べたい単語を入力してカラムを作成します
今回は docker
にします
tweet authors
でmembers of List...
を選択し、先ほどコピーしたlistの番号を直接指定します
そうするとリストに指定したアカウントから特定のtweeetを検索することができます
参考
【Twitter】リスト内のツイートを検索するには?手順を解説! | APPTOPI
【Twitter】リツイート・リプライを除外して純粋にフォロワーのコメントに絞る方法 - あなたのスイッチを押すブログ
The Most Comprehensive TweetDeck Research Guide In Existence (Probably) - bellingcat
How do I search within a Twitter list? - Web Applications Stack Exchange