Goならわかるシステムプログラミング:第3章(練習問題)

Q3.1 ファイルのコピー

package main

import (
    "io"
    "os"
)

func main() {
    file, err := os.Open("old.txt")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    newFile, err := os.Create("new.txt")
    if err != nil {
        panic(err)
    }
    defer newFile.Close()
    io.Copy(newFile, file)
}

テスト

package main

import (
    "bytes"
    "io/ioutil"
    "os"
    "testing"
)

func exists(filename string)bool {
    _, err := os.Stat(filename)
    return err == nil
}

func TestMainSuccess(t *testing.T){
    if exists("new.txt") {
        os.Remove("new.txt")
    }

    main()
    oldFile, err := os.Open("old.txt")
    if err != nil {
        t.Fatalf("failed to open old file: %#v", err)
    }
    newFile, err := os.Open("new.txt")
    if err != nil {
        t.Fatalf("failed to open new file: %#v", err)
    }
    oldBuffer, err := ioutil.ReadAll(oldFile)
    if err != nil {
        t.Fatalf("failed to read content: %#v", err)
    }
    newBuffer, err := ioutil.ReadAll(newFile)
    if err != nil {
        t.Fatalf("failed to read content: %#v", err)
    }
    if bytes.Compare(oldBuffer, newBuffer) != 0 {
        t.Fatalf("each contents of files are different.")
    }

    os.Remove("new.txt")
}

テストはもうちょっとスッキリかける気がする。

Q3.2 テスト用の適切なサイズのファイルを生成

package main

import (
    "crypto/rand"
    "io"
    "os"
)

func main() {
    file, err := os.Create("samplefile")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    r := io.LimitedReader{R: rand.Reader, N: 1024}
    io.Copy(file, &r)
}

io.Copyを使わないように」と問題には書いてあったが、LimitReaderを使って読み出しているので問題ない?

テスト

package main

import (
    "os"
    "testing"
)

func TestMainSuccess(t *testing.T) {
    main()
    info, err := os.Stat("samplefile")
    if err != nil {
        t.Fatalf("could not open file.")
    }
    if info.Size() != 1024 {
        t.Fatal("size was not 1024 byte.")
    }
    defer os.Remove("samplefile")
}

ファイルサイズはos.Stat()で取れる。

同じ振る舞いが想像以上に威圧的にとられてしまう懸念

エンジニアを育てる環境と、コミュニティのありかたについて - 滞舎路日記

自分自身は、率直に物事を指摘する、勢いのある若手のつもりでいても、在籍年数を重ねるうちに、同じ振る舞いが想像以上に威圧的にとられてしまうこともあるので、みなさん注意しましょう。

これがすごい金言で、ありがちな状況を的確に言語化していると思う。他人がそういうふうであることを見かけることもままあるし、自分が他人からそう見えていないかどうか不安だ。本人は悪意が無いかつ無自覚なので

DockerfileからビルドしたDockerイメージをDocker Hubにアップする

個人的によく使うDockerイメージをpublicに置いておきたかったので初めてやってみた。

今回実際に上げたのはこちら。 https://hub.docker.com/r/chokkoy/redis_trib/

Docker Hubのアカウントを取る

https://hub.docker.com/ から、画面に沿って行う。

Dockerfileを書いて、イメージをビルド

下記のような状態で、

~/Documents/docker/image ᐅ ls
Dockerfile
~/Documents/docker/image ᐅ cat Dockerfile
FROM ubuntu:16.04

RUN apt-get update
RUN apt-get install -y ruby vim wget redis-tools
RUN gem install redis
RUN wget http://download.redis.io/redis-stable/src/redis-trib.rb

下記のコマンドでビルドする

docker build -t chokkoy/redis_trib:1.0 ./

できている

~/Documents/docker/image ᐅ docker images | grep redis_trib                                                                                    
chokkoy/redis_trib                                            1.0                 8a146f5d717b        About an hour ago   245MB
  • docker loginでログインする

  • docker push chokkoy/redis_tribでイメージをPush

これでOK

不合理だからうまくいく:行動経済学で「人を動かす」

ブランド人になれ!会社の奴隷解放宣言

Docker for Macに同梱のKubernetesを使う(minikubeの代替?)

Docker for MacKubernetesが同梱されるようになったことはなんとなく知っていたが、どのように使うのかは分かっていなかったので試してみた。

現時点でのDocker for MacのバージョンはVersion 18.06.0-ce-mac70 (26399)

f:id:road288:20180810161026p:plain

Preferenceをみると、Kubernetesというタブができているので、それを開いてEnable Kubernetesにチェックを入れる。

f:id:road288:20180810161116p:plain

Defaultのオーケストレーションツールを選択する。SwarmはつかったことないのでKubernetesにする(デフォルトはSwarmになっていた)。

f:id:road288:20180810161212p:plain

画面に沿ってインストールする。インストールは数分かかった。 f:id:road288:20180810161220p:plain

インストールが終わると、docker for desktopというコンテキストが用意されていて、ターミナル上もそれに切り替わっている。

ᐅ kubectl config current-context                                                                                              
docker-for-desktop

defaultのネームスペースにはpodがないが、他のネームスペースにはpodがいくつか起動済みになっている。

~ ᐅ kubectl get pods

No resources found.
~ ᐅ kubectl get namespace

NAME          STATUS    AGE
default       Active    3m
docker        Active    3m
kube-public   Active    3m
kube-system   Active    3m
~ ᐅ kubectl get pods --namespace docker

NAME                           READY     STATUS    RESTARTS   AGE
compose-7447646cf5-bk579       1/1       Running   0          3m
compose-api-6fbc44c575-jg2dp   1/1       Running   0          3m
~ ᐅ kubectl get pods --namespace kube-public

No resources found.
~ ᐅ kubectl get pods --namespace kube-system

NAME                                         READY     STATUS    RESTARTS   AGE
etcd-docker-for-desktop                      1/1       Running   0          2m
kube-apiserver-docker-for-desktop            1/1       Running   0          3m
kube-controller-manager-docker-for-desktop   1/1       Running   0          2m
kube-dns-86f4d74b45-kddhh                    3/3       Running   0          3m
kube-proxy-nnsx8                             1/1       Running   0          3m
kube-scheduler-docker-for-desktop            1/1       Running   0          2m

nodeが1つなのは当然として、kubernetesというserviceがすでに存在している。このへんはkubernetesのデフォのものなのか、docker for macならではの設定なのかは調べてない。

~ ᐅ kubectl get nodes             
NAME                 STATUS    ROLES     AGE       VERSION
docker-for-desktop   Ready     master    16m       v1.10.3
~ ᐅ kubectl get services         
NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.96.0.1    <none>        443/TCP   16m
~ ᐅ kubectl describe service kubernetes 
Name:              kubernetes
Namespace:         default
Labels:            component=apiserver
                   provider=kubernetes
Annotations:       <none>
Selector:          <none>
Type:              ClusterIP
IP:                10.96.0.1
Port:              https  443/TCP
TargetPort:        6443/TCP
Endpoints:         192.168.65.3:6443
Session Affinity:  ClientIP
Events:            <none>

というか、get all --all-namespacesという便利なコマンドがあった。

~ ᐅ kubectl get all --all-namespaces   
NAMESPACE     NAME            DESIRED   CURRENT   READY     UP-TO-DATE   AVAILABLE   NODE SELECTOR   AGE
kube-system   ds/kube-proxy   1         1         1         1            1           <none>          4m

NAMESPACE     NAME                 DESIRED   CURRENT   UP-TO-DATE   AVAILABLE   AGE
docker        deploy/compose       1         1         1            1           4m
docker        deploy/compose-api   1         1         1            1           4m
kube-system   deploy/kube-dns      1         1         1            1           4m

NAMESPACE     NAME                        DESIRED   CURRENT   READY     AGE
docker        rs/compose-7447646cf5       1         1         1         4m
docker        rs/compose-api-6fbc44c575   1         1         1         4m
kube-system   rs/kube-dns-86f4d74b45      1         1         1         4m

NAMESPACE     NAME                                            READY     STATUS    RESTARTS   AGE
docker        po/compose-7447646cf5-fn2ft                     1/1       Running   0          4m
docker        po/compose-api-6fbc44c575-bcg82                 1/1       Running   0          4m
kube-system   po/etcd-docker-for-desktop                      1/1       Running   0          3m
kube-system   po/kube-apiserver-docker-for-desktop            1/1       Running   0          3m
kube-system   po/kube-controller-manager-docker-for-desktop   1/1       Running   0          3m
kube-system   po/kube-dns-86f4d74b45-ggqq4                    3/3       Running   0          4m
kube-system   po/kube-proxy-z9886                             1/1       Running   0          4m
kube-system   po/kube-scheduler-docker-for-desktop            1/1       Running   0          3m

NAMESPACE     NAME              TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)         AGE
default       svc/kubernetes    ClusterIP   10.96.0.1      <none>        443/TCP         4m
docker        svc/compose-api   ClusterIP   10.102.8.163   <none>        443/TCP         4m
kube-system   svc/kube-dns      ClusterIP   10.96.0.10     <none>        53/UDP,53/TCP   4m

このdocker for desktopをリセットしたいとき

docker for macのPreferenceにResetという項目があり、そこでKubernetesのリセットもできるようになっている。

docker composeの設定ファイルを使う

docker stackコマンドでdocker-compose.ymlもKubernetesへデプロイできると書いてあって試してみたが、docker composeのversion1や2には対応していない。version 3のサンプルを使ったら動作した。

ᐅ docker stack deploy --compose-file docker-compose.yml words                                                                        
unsupported Compose file version: 1.0

参考

[和訳] デスクトップ向けDockerでのDocker ComposeとKubernetes #docker #kubernetes #k8s - クリエーションライン株式会社

Compose file version 3 reference | Docker Documentation

GCPのロードバランサの仕組みの復習

あいまいな理解でなんとなく使っているので復習がてらメモ。

下記を参考にまとめる。

www.ianlewis.org

リンク先から引用するが、2枚目のダイアグラムの画像がすごくわかりやすい。

f:id:road288:20180717190925p:plain

  • まずはヘルスチェックを作る

    • ヘルスチェックは他のリソースに依存しない
    • バックエンドサービスにアタッチすると動作を始める。それまでは作っただけでは何もしない
    • Compute Engineリソースのカテゴリに存在する
  • バックエンドサービス

    • 実体はインスタンスグループへのリンクだが、ポート番号とロードバランシングモードを設定できる。そこで設定したポート番号はnamed portとして設定する
  • バックエンド

    • バックエンドサービスとは別。バックエンドサービスはいくつかのバックエンドを束ねる。バックエンドサービスにヘルスチェックがひもづき、バックエンドにインスタンスグループがひもづく。(上掲のダイアグラムを参照)
  • URLマップ(Url Maps)

    • ホスト(host rules)とURLのマッピング(path matchers)で構成。
    • デフォルトでつなぐバックエンドサービスを設定する。他にマッチするものが無ければこれが利用される。1つしか作らないのであればこれだけで十分。
  • ターゲットプロキシ

    • フォワーディングルールとURLマップの間に挟まるもの
    • ここでユーザーの接続はterminateする
    • 主にSSL証明書を設定する
  • フォワーディングルール

    • IPアドレスをロードバランサ・ターゲットプロキシとひもづける
    • 実際に課金されるのはここ
    • IPアドレスは事前に別で作っておく