Kubernetesを学ばないとだんだん話についていけなくなってきたので止む無く勉強を始めた。
とりあえずKubernetes完全ガイドという、今のところ日本語だと一番良いと聞いたのでそいつで勉強。
ひとまずDockerの復習とKubernetesってなんぞやってとこまで。
動作環境
- Ubuntu 18.04.1 LTS
- docker version
1234567891011121314$ docker versionClient:Version: 18.09.1API version: 1.39Go version: go1.10.6Git commit: 4c52b90Server: Docker Engine - CommunityEngine:Version: 18.09.1API version: 1.39 (minimum version 1.12)Go version: go1.10.6Git commit: 4c52b90$
Dockerのおさらい
Dockerコンテナの設計のポイント
- 1コンテナにつき1プロセス
···仮想マシンのように1つのイメージの中に複数プロセスは非推奨。複数プロセス実行したいならイメージを分けるべし。 - Immutable Infrastructureなイメージにする
···コンテナ起動後に外部から実行バイナリやパッケージを取得・インストールしたりすると外部要因によってそのコンテナイメージの実行結果が変わってしまいます。可能な限り実行バイナリやリソースを埋め込み、コンテナイメージは普遍的な状態にする。 - 軽量なDockerイメージにする
···Dockerイメージは最初にリポジトリからpullする必要があるため、なるべく軽量な状態にすべき。方法としてyumやaptでパッケージインストールした後のキャッシュファイルの削除や、Alpineなどのベースイメージが軽量なものを利用するなどがある。 - 実行ユーザーをroot以外にする
···コンテナ内でプロセスを起動する実行ユーザーの権限を最小化すること。rootなど使うと大きなセキュリティリスクにつながるので注意が必要。
Dockerfileの書き方
大雑把な使用コマンド説明
- FROM
···ベースイメージを指定 - EXPOSE
···コンテナがListenするポートを指定 - COPY
···ビルドを行うマシン上のファイルをコンテナ(イメージ中)にコピーして配置 - RUN
···ビルド時にコンテナ上でコマンドを実行。yumやaptでパッケージインストールなどが一般的な使い方(後述するハンズオンではgoをコンパイルして実行バイナリを作成している) - ENTRYPOINTとCMD
···コンテナ起動時に実行するデフォルトコマンドを指定するために用いる。非常に簡単に説明すると、「$ENTRYPOINT $CMD」が実行されるようなイメージ。
ENTRYPOINTとCMDはコンテナ起動時に別途オプション指定することもできるので、実行の度に上書きすることもできる。一般的にはENTRYPOINTには基本的に書き換える必要のないコマンド、CMD部分にはデフォルト引数などを定義する。
例)
ENTRYPOINTに/bin/sleep
を指定し、CMDにはsleep秒数を指定する
ハンズオン
docker build
でDockerfileから独自のイメージを作成。
docker build
にDockerfileのパスと-t
オプションでイメージの名前とタグ(デフォルトはlatest)を指定できる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
$ cat -n Dockerfile 1 FROM alpine:3.7 2 3 CMD echo 'Hello alpine!' $ $ docker build ./ -t dockerfile_tri Sending build context to Docker daemon 4.096kB Step 1/2 : FROM alpine:3.7 3.7: Pulling from library/alpine [···中略···] Successfully built a8860019b346 Successfully tagged dockerfile_tri:latest $ $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE dockerfile_tri latest a8860019b346 6 seconds ago 4.21MB alpine 3.7 bc8fb6e6e49d 2 weeks ago 4.21MB $ |
docker run
でコンテナを作成して実行。
docker ps -a
は停止しているもの含めすべてのコンテナをリスト出力。(-a
オプション無しだと起動中のコンテナのみ)
1 2 3 4 5 6 |
$ docker run dockerfile_tri Hello alpine! $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES d72adf1238d0 dockerfile_tri "/bin/sh -c 'echo 'H…" 12 seconds ago Exited (0) 11 seconds ago elated_saha $ |
イメージの中に実行ソースを含める。
mian.goというソースをイメージの中にコピーしコンテナ化して実行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
$ cat -n Dockerfile 1 # Alpine 3.7ベースのgolang1.10.1のイメージをベース 2 FROM golang:1.10.1-alpine3.7 3 4 # 8080ポートを公開 5 EXPOSE 8080 6 7 # ビルドを行うマシン上のmain.goファイルをコンテナにコピー 8 COPY ./main.go ./ 9 10 # ビルド時にコンテナ内でコマンドを実行 11 RUN go build -o ./go-app ./main.go 12 13 # 実行ユーザーを指定 14 USER nobody 15 16 # コンテナ起動時に実行するコマンドを定義 17 ENTRYPOINT ["./go-app"] $ $ cat -n main.go 1 package main 2 3 import "fmt" 4 5 func main() { 6 fmt.Printf("Hello World\n") 7 } $ $ docker build ./ -t dockerfile_golang Sending build context to Docker daemon 4.096kB Step 1/6 : FROM golang:1.10.1-alpine3.7 [···中略···] Successfully built 065350c79354 Successfully tagged dockerfile_golang:latest $ $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE dockerfile_golang latest 065350c79354 52 seconds ago 378MB dockerfile_tri latest 71b3b19db5b7 9 minutes ago 4.21MB alpine 3.7 bc8fb6e6e49d 2 weeks ago 4.21MB golang 1.10.1-alpine3.7 52d894fca6d4 10 months ago 376MB $ $ docker run dockerfile_golang Hello World $ docker ps -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6738f7b9b0ff dockerfile_golang "./go-app" 12 seconds ago Exited (0) 10 seconds ago elastic_ride d72adf1238d0 dockerfile_tri "/bin/sh -c 'echo 'H…" 7 minutes ago Exited (0) 7 minutes ago elated_saha $ |
マルチステージビルド
先ほどのハンズオンの方法だとdockerfile_golangのサイズは378MBにもなってしまった。これはイメージファイル中にGoのビルドツールなども含まれているため。
Dockerのマルチステージビルドを使えば、複数のコンテナイメージを使って処理を行い、成果物だけを実行用のイメージにコピーできる。
先ほどの例でいえば、Golangのイメージはビルドに必要なツール用途でしか使われていないので、ビルドで作成したbinaryを実行用イメージにコピーして最終的なコンテナイメージにする。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
$ cat -n Dockerfile_multistage 1 # Stage1 2 # Alpine 3.7ベースのgolang1.10.1のイメージをベース 3 FROM golang:1.10.1-alpine3.7 as builder 4 # ビルドを行うマシン上のmain.goファイルをコンテナにコピー 5 COPY ./main.go ./ 6 # ビルド時にコンテナ内でコマンドを実行 7 RUN go build -o /go-app ./main.go 8 9 # Stage2 10 FROM alpine:3.7 11 EXPOSE 8080 12 13 # Stage1でビルドした成果物をコピー 14 COPY --from=builder /go-app . 15 16 # コンテナ起動時に実行するコマンドを定義 17 ENTRYPOINT ["./go-app"] $ $ docker build -t dockerfile_multistage -f ./Dockerfile_multistage . Sending build context to Docker daemon 5.12kB Step 1/7 : FROM golang:1.10.1-alpine3.7 as builder ---> 52d894fca6d4 Step 2/7 : COPY ./main.go ./ ---> Using cache ---> 44d7b9727cc6 Step 3/7 : RUN go build -o /go-app ./main.go ---> Running in 4851d4e5f798 Removing intermediate container 4851d4e5f798 ---> ffb605fe54c0 Step 4/7 : FROM alpine:3.7 ---> bc8fb6e6e49d Step 5/7 : EXPOSE 8080 ---> Using cache ---> e60a36d58ebf Step 6/7 : COPY --from=builder /go-app . ---> 36d914487a89 Step 7/7 : ENTRYPOINT ["./go-app"] ---> Running in 89f2fa54d058 Removing intermediate container 89f2fa54d058 ---> 2d93778cc857 Successfully built 2d93778cc857 Successfully tagged dockerfile_multistage:latest $ $ docker run dockerfile_multistage Hello World $ $ docker images REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> ffb605fe54c0 3 minutes ago 378MB dockerfile_multistage latest 2d93778cc857 3 minutes ago 6.23MB dockerfile_golang latest 065350c79354 8 days ago 378MB dockerfile_tri latest 71b3b19db5b7 8 days ago 4.21MB alpine 3.7 bc8fb6e6e49d 3 weeks ago 4.21MB golang 1.10.1-alpine3.7 52d894fca6d4 10 months ago 376MB |
-f
オプションで使用するDockerfileを指定することが可能。
dockerfile_multistageがdockerfile_golangよりもずいぶん小さくなったことがわかる。
※なぜかnone(dangling image)が出来上がってしまった。とりあえずdocker image prune
で削除したが、noneが出来上がって理由は後で調べる。
DockerレジストリへのPush
Dockerレジストリの有名どころ。
- DockerHub
- Google Container Registry(GCR)
- Amazon Elastic Container Registry(Amazon ECR)
- Azure Container Registry(ACR)
Dockerレジストリにプッシュするにはイメージ名を下記のフォーマットにする必要あり。
DockerHubの場合はホスト名は省略可能。ネームスペースはDockerHubのユーザー名。
または
[DockerHubのユーザー名]/sample-image:0.1
What is Kubernetes?と基本機能
Kubernetesとは
コンテナオーケストレーションエンジン。
Kubernetesはコンテナ化されたアプリケーションのデプロイ、スケーリングなどの管理を自動化するためのプラットフォーム。
Dockerは従来それ単体ではDockerがインストールされたホストを複数台協調して動作させたり、一元的に管理したりすることができなかったため、Docker単体では複数のホストで構成されるシステムを構築することは難しかった。
Kubernetes用語
- Kubernetes Node ··· 実際にコンテナが起動するノード
- Kubernetes Master ··· Nodeを管理するノード
- Pod ··· (本には記載なし)Podは複数のコンテナの集合体です。Kubernetesでコンテナを管理するための最小単位。コンテナを単独で管理するのではなく、グループとして管理することで、コンテナの使いにくさを解消することが可能(例えばNginxとNodeJSのコンテナをWeb/APサーバーPodとして管理する)
Kubernetesを使うと何ができるのか
- Infrastructure as Code
···YAML形式やJSON形式で記述したコード(マニフェスト)によりデプロイするコンテナや周辺リソースを管理可能 - スケーリング/オートスケーリング
···コンテナクラスタ(Kubernetesクラスタ)を形成して複数のノードを管理する。Kubernetes上にコンテナをデプロイする際に、同じイメージを元にした複製コンテナ(レプリカ)をデプロイすることで負荷分散や耐障害性の確保が可能。負荷に応じたオートスケーリングも可能。 - スケジューリング
···コンテナをどのノードにデプロイするか決める機能。ノードの性能差を意識してスケジューリングが可能(AffinityとAnti-Affinity)。例えば、「ディスクI/Oが多い」コンテナを「ディスクがSSD」のノードに配置といった感じ。また、クラウドのアベイラビリティゾーン(リージョン)を識別することもできるのでマルチリージョン化も可能になる。 - リソース管理
···(特別な指定がない限り)ノードのリソース使用状況にしたがってコンテナをスケジューリングする。ユーザーがどのコンテナを配置するか管理する必要がなくなる
※リソース使用状況だけでコンテナをスケジューリングするとオートスケールの時とかに苦労しそうな気がするが、その辺はまだ勉強中 - セルフヒーリング
···標準でコンテナのプロセス監視を行なっており、プロセス停止を検知すると自動的にコンテナ再デプロイする。更に、プロセス監視以外にもHTTP/TCPやシェルによるヘルスチェックの成否を条件に設定することもできる - サービスディスカバリとロードバランシング
···仮想マシンでロードバランサ経由でルーティングするように、Kubernetesもあらかじめ指定した条件に合致するコンテナ群に対してルーティングするロードバランシング機能(Service)を有している。スケール時のServiceへの自動追加/削除、コンテナ障害時のServiceからの切り離し、ローリングアップデート時の事前切り離しなどを自動的に行なってくれる - データの管理
···バックエンドのデータストアにetcdを採用しているため、コンテナやServiceに関するマニフェストも冗長化されて保存されている。また、コンテナが利用する設定ファイルや認証情報などのデータを保存する仕組みも用意されており、コンテナ共通設定やアプリケーションから利用されるPWなどの情報を安全かつ冗長化された状態で集中管理可能
※etcd···etcdはGo言語で記述された設定情報の共有とサービス検出のための分散KVS