在Kubernetes上创建Docker私有仓库并与CircleCI进行集成
这篇文章是Kubernetes Advent Calendar第20天的文章。
总结
CircleCI には実はテストが通った後に Kubernetes にデプロイする機能が存在します。
ここでは、GCE に Kubernetes 環境を構築し、そこに Private Dokcer Repository を作成し、
CircleCI でテストが通ったアプリケーションを Kubernetes 上にデプロイする方法について説明します。
Kubernetes 環境を手に入れる
在这里我们会迅速地进行,如前所述我们会使用GCE。
这附近可以阅读之前的AdventCalendar和Qiita的文章。
事前に gcloud の components を update しておきましょう。(忘れててちゃんと動かなかった)
$ gcloud components update
Kubernetes を git から clone してきて、以下のコマンドを実行します。
$ make release
也许需要使用 boot2docker 或其他 Docker 环境。请做好准备。
次の手順では、minion から GCS を使うために、cluster/gce/config-default.sh を編集し、
minion の scopes を storage-full にしておきます。
--- a/cluster/gce/config-default.sh
+++ b/cluster/gce/config-default.sh
@@ -17,10 +17,10 @@
# TODO(jbeda): Provide a way to override project
# gcloud multiplexing for shared GCE/GKE tests.
GCLOUD=gcloud
ZONE=us-central1-b
MASTER_SIZE=n1-standard-1
MINION_SIZE=n1-standard-1
NUM_MINIONS=4
# TODO(dchen1107): Filed an internal issue to create an alias
# for containervm image, so that gcloud will expand this
# to the latest supported image.
@@ -34,7 +34,7 @@ MINION_TAG="${INSTANCE_PREFIX}-minion"
MINION_NAMES=($(eval echo ${INSTANCE_PREFIX}-minion-{1..${NUM_MINIONS}}))
CLUSTER_IP_RANGE="10.244.0.0/16"
MINION_IP_RANGES=($(eval echo "10.244.{1..${NUM_MINIONS}}.0/24"))
-MINION_SCOPES=("storage-ro" "compute-rw")
+MINION_SCOPES=("storage-full" "compute-rw")
# Increase the sleep interval value if concerned about API rate limits. 3, in seconds, is the default.
POLL_SLEEP_INTERVAL=3
PORTAL_NET="10.0.0.0/16"
(END)
上記の設定が完了したら、Kubernetes 環境を GCE 上に起動します。
cluster/kube-up.sh
kube-up.sh は 昔書いた時より色々進化してて感動しました。
やっていることや作られる VM/ストレージは同じですので、
内部で何が起きているか気になる方は昔書いた記事をご覧ください。
認証情報は標準出力でなく ~/.kunernetes-auth に出力されるようになった模様です。
Kubernetes 上の Private Docker Repository を手に入れる
google/docker-registry を使う
CircleCI との連携のために docker をがっつり使うので、
private docker repository を用意しましょう。
google/docker-registry を使います。
google/docker-registry は GCS 上に private docker repository を持つための物です。
せっかくなので、これも Kubernetes 上に乗せてしまうことにします。
事前に GCS に bucket を作っておきます。bucket の名前は後で設定ファイル内で使う事になるので記録しておきましょう。
$ gsutil mb -l ASIA gs://rrreeeyyy_docker_registry_bucket
为了在 Kubernetes 上部署,创建一个 google/docker-registry 的控制器。配置文件如下所示。
{
"id": "dockerRegistryController",
"kind": "ReplicationController",
"apiVersion": "v1beta1",
"desiredState": {
"replicas": 2,
"replicaSelector": {"name": "dockerregistry"},
"podTemplate": {
"desiredState": {
"manifest": {
"version": "v1beta1",
"id": "dockerregistry",
"containers": [{
"name": "dockerregistry",
"image": "google/docker-registry",
"cpu": 200,
"ports": [{"containerPort": 5000, "hostPort": 5000}],
"env": [{"name": "GCS_BUCKET", "value": "rrreeeyyy_docker_registry_bucket"}]
}]
}
},
"labels": {
"name": "dockerregistry",
}
}
},
"labels": {"name": "dockerregistry"}
}
重点是将先前创建的存储桶的名称注册到GCS_BUCKET中,步骤如下所示。
“env”: [{“name”: “GCS_BUCKET”, “value”: “rrreeeyyy_docker_registry_bucket”}]
“env”: [{“名称”: “GCS_BUCKET”, “值”: “rrreeeyyy_docker_registry_bucket”}]
用以下的命令创建控制器。
$ cluster/kubectl.sh create -f docker-registry-controller.json
接下来,我们将创建一个服务,以便无论放置在哪个小兵上都能够进行访问。
配置文件将如下所示。
{
"id": "dockerregistry",
"kind": "Service",
"apiVersion": "v1beta1",
"port": 5000,
"containerPort": 5000,
"labels": {
"name": "dockerregistry"
},
"selector": {
"name": "dockerregistry"
}
}
创建服务。
$ cluster/kubectl.sh create -f docker-registry-service.json
我会为GCE增加一条防火墙规则,以便允许对minion进行5000号开放。
$ gcloud compute firewall-rules create --allow=tcp:5000 --target-tags=kubernetes-minion kubernetes-minion-5000
当我们来到这一步,请确认Pod是否实际被创建了。
$ cluster/kubectl.sh get pods
NAME IMAGE(S) HOST LABELS STATUS
04f4c9f8-8771-11e4-b820-42010af08e38 google/docker-registry kubernetes-minion-1.c.xxxx-xxxx-123.internal/xxx.xxx.xxx.xxx name=dockerregistry Running
04f629c1-8771-11e4-b820-42010af08e38 google/docker-registry kubernetes-minion-2.c.xxxx-xxxx-123.internal/xxx.xxx.xxx.xxx name=dockerregistry Running
我们试着对其中一个 minion 的 :5000 进行 curl 请求。
$ curl xxx.xxx.xxx.xxx:5000
"docker-registry server (prod) (v0.8.1)"
如果显示如此,那么Docker注册表应该在Kubernetes上正常运行。
实际使用时可能需要添加访问限制和基本认证。
与CircleCI进行集成
现在我已经得到了私有的Docker仓库。
现在,让我们来试试将其与CircleCI连接起来吧。
让我们试试由CircleCI发布的用于测试的Rails应用程序。
(circleci/docker-hello-google)
当查看circle.yml文件时,内容如下:
machine:
services:
- docker
dependencies:
cache_directories:
- ~/kubernetes
pre:
- ./ensure-kubernetes-installed.sh
- docker build -t $EXTERNAL_REGISTRY_ENDPOINT/hello:$CIRCLE_SHA1 .
test:
post:
- docker run -d -p 3000:3000 -e "SECRET_KEY_BASE=abcd1234" $EXTERNAL_REGISTRY_ENDPOINT/hello:$CIRCLE_SHA1; sleep 10
- curl --retry 10 --retry-delay 5 -v http://localhost:3000
deployment:
prod:
branch: master
commands:
- docker login -e $DOCKER_EMAIL -u $DOCKER_USER -p $DOCKER_PASS $EXTERNAL_REGISTRY_ENDPOINT
- envsubst < .kubernetes_auth.template > ~/.kubernetes_auth
- ./deploy.sh
关键点是在dependencies中安装Kubernetes,以及在deployment中执行./deploy.sh。
确保-kubernetes-installed.sh 的内容如下。
#!/bin/bash
# Exit on any error
set -e
KUBERNETES_VERSION=41eb15bcff4f114e95788f1e3a5ad3645c4e53fd
# Exit if already installed
if [[ -d ~/kubernetes ]]; then
echo "Kubernetes already installed"
exit 0
else
echo "Installing Kubernetes..."
fi
# Clone repo
(cd ~ && git clone https://github.com/GoogleCloudPlatform/kubernetes.git)
(cd ~/kubernetes && git reset --hard $KUBERNETES_VERSION)
# Build go source
(cd ~/kubernetes && hack/build-go.sh)
看起来只是普通地安装了 Kubernetes。
由于进行了缓存,第二次以及之后的安装都会跳过,并显示为已安装 Kubernetes。
关于部署部分,deploy.sh 文件的内容如下:
#!/bin/bash
# Exit on any error
set -e
KUBE_CMD=${KUBERNETES_ROOT:-~/kubernetes}/cluster/kubecfg.sh
# Deploy image to private GCS-backed registry
docker push $EXTERNAL_REGISTRY_ENDPOINT/hello:$CIRCLE_SHA1
# Update Kubernetes replicationController
envsubst < kubernetes/rails-controller.json.template > rails-controller.json
$KUBE_CMD -c rails-controller.json \
update replicationControllers/railscontroller
# Roll over Kubernetes pods
$KUBE_CMD rollingupdate railscontroller
这里是在推送Docker镜像到$EXTERNAL_REGISTRY_ENDPOINT,并在注册控制器后进行滚动更新。
在 update-demo 中有对 rollingupdate 的解释,它是一种逐个更新 pod 的命令。
所以,我立刻分叉了这个存储库,并在CircleCI上进行了测试。
在进行CircleCI测试时,请务必设置以下环境变量,否则部署将会在中途停止,请注意。
-
- KUBE_MASTER_IP
-
- KUBERNETES_PASS
-
- KUBERNETES_USER
-
- DOCKER_USER
-
- DOCKER_EMAIL
-
- DOCKER_PASS
-
- RAILS_SECRET
-
- INTERNAL_REGISTRY_ENDPOINT
- EXTERNAL_REGISTRY_ENDPOINT
Kubernetes は、上記の環境変数がセットされていない場合には、プラットフォームに応じたコマンドで自動的に上記の値を検出しようとします。
例えば、KUBE_MASTER_IP がセットされていないような場合には、gcutil 等のコマンドを実行するようです。
CI 環境には gcutil のようなコマンドは入っていないため、そこで deployment が止まってしまいます。
此外,如果想在容器中使用最新的 Kubernetes,可能需要进行以下设置。
(通过更改ensure-kubernetes-installed.sh的哈希值,可以更改使用的 Kubernetes 版本)
- PROJECT
さて、正しく環境変数をセットしてリポジトリを push すると、CI の最後に以下のように deploy.sh を実行しています。
当 deploy.sh 被执行时,可以尝试使用手边的 kubectl.sh 来确认 pods 的状态。
➜ git:(master) ✗ ./cluster/kubectl.sh get pods
a59fdaf0-8792-11e4-b820-42010af08e38 xxx.xxx.xxx.xxx:5000/hello:latest kubernetes-minion-1.c.xxxxx-xxxxx-xxx.internal/xxx.xxx.xxx.xxx name=rails Running
a5a05c0a-8792-11e4-b820-42010af08e38 xxx.xxx.xxx.xxx:5000/hello:latest kubernetes-minion-2.c.xxxxx-xxxxx-xxx.internal/xxx.xxx.xxx.xxx name=rails Running
しばらくしてから minion の :3000 にアクセスすると、以下のように表示されます。
(GCE 側で :3000 を minion に対して許可する firewall-rule を追加しておきましょう)
感想
和几个月前(大约9月份)我接触时相比,功能已经相当丰富了。
现在,我们甚至可以像这次一样与CI环境互联,测试通过后直接部署到Kubernetes环境当中。
在Kubernetes上部署应用所面临的挑战是持久化存储。
目前,使用GCE时,容器可以挂载GCS。(参考:kubernetes/volumes)
或者,容器也可以挂载到minion的特定目录中。
因此,如果准备好小兵的分布式存储等,就可以将存储持久化。
という環境をしっかりと用意すれば、
例えば社内の Docker 用 PaaS 環境として Kubernetes はうってつけではないでしょうか。
まだ実サービスに投入するにはやや怖いですが、本記事のようにテストしたコンテナを適当に分散して、
再起動等の管理もよしなにやってくれるので、便利なように思えます。
引き続き目が離せないプロダクトの 1 つであると感じました。
整理
-
- GCE+Kubenetes で Docker Private Repository を作成
-
- Kubernetes + CircleCI でテストが通ったコンテナをデプロイ
- 社内 PaaS とかで使えそう。