- 投稿日:2019-05-07T23:47:52+09:00
SkaffoldのGetting StartedをMinikubeでサクッと試す
Cloud Native Online #01に登壇した際のスライドもご覧ください。
Skaffoldとは
Skaffoldは
継続的なKubernetesアプリケーション開発を促進するツールで、
ソースコードの変更をトリガとして以下を自動で行ってくれることで、我々開発者はアプリケーションの開発に注力できるようになります。
- コンテナイメージのビルド
- コンテナレジストリへのpush
- k8sクラスタへのデプロイ
kubectlの他、helmやkustomizeも記述できる画像はskaffold.dev/docsより引用
ソースコードの変更をトリガとしてk8sクラスタにデプロイされるまでの流れが示されています。
Forerver(Node)やRailsやDjangoがソースコードの変更を検出してサーバを再起動する機能のKubernetes版と考えて良さそうです。本エントリにはSkaffoldのGetting StartedをMinikubeで実施する場合に必要となる手順を記載します。
環境
Ubuntu 17.10でMinikube v1.0.0を使用しています。$ uname -a Linux mina 4.13.0-46-generic #51-Ubuntu SMP Tue Jun 12 12:36:29 UTC 2018 x86_64 x86_64 x86_64 GNU/Linux $ cat /etc/lsb-release DISTRIB_ID=Ubuntu DISTRIB_RELEASE=17.10 DISTRIB_CODENAME=artful DISTRIB_DESCRIPTION="Ubuntu 17.10" $ minikube version minikube version: v1.0.0 $ kubectl version Client Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.0", GitCommit:"641856db18352033a0d96dbc99153fa3b27298e5", GitTreeState:"clean", BuildDate:"2019-03-25T15:53:57Z", GoVersion:"go1.12.1", Compiler:"gc", Platform:"linux/amd64"} Server Version: version.Info{Major:"1", Minor:"14", GitVersion:"v1.14.0", GitCommit:"641856db18352033a0d96dbc99153fa3b27298e5", GitTreeState:"clean", BuildDate:"2019-03-25T15:45:25Z", GoVersion:"go1.12.1", Compiler:"gc", Platform:"linux/amd64"} $Minikubeの準備
MinikubeとSkaffoldを組み合わせて使用する場合、留意事項があります。
Skaffoldは、k8sクラスタがMinikubeであると認識した場合、
docker buildは行いますが、docker pushは行いません。これは、
docker buildコマンドが、MinikubeのVM内でのdocker buildとなるよう設定されていることが前提となっていると考えられます。これには、minikubeの
docker-envコマンドを活用できます。
このコマンドは以下のように、ローカルのdockerコマンドをMinikubeのVM内のdockerに接続するための環境変数設定を表示してくれます。$ minikube docker-env export DOCKER_TLS_VERIFY="1" export DOCKER_HOST="tcp://192.168.99.100:2376" export DOCKER_CERT_PATH="/home/loft/.minikube/certs" export DOCKER_API_VERSION="1.35" # Run this command to configure your shell: # eval $(minikube docker-env) $以下のようにevalで実行すると環境変数が設定されます。
$ eval $(minikube docker-env)以降、dockerコマンドはMinikubeのVM内のdockerに対する操作になることに留意する必要はありますが、
この機構によりコンテナイメージをわざわざレジストリにpushしなくても
minikubeにアプリケーションをデプロイできるので素早い開発サイクルを回せるメリットがあります。これを行なっていない場合、dockerイメージはMinikubeのVMの外のローカルでビルドされるため、当然PodのStatusは
ImagePullBackOffとなります。Skaffoldのインストール
例) Linuxの場合
$ curl -Lo skaffold https://storage.googleapis.com/skaffold/releases/latest/skaffold-linux-amd64 $ chmod +x skaffold $ sudo mv skaffold /usr/local/binSkaffold サンプルの入手
リポジトリをcloneし、
examples/getting-startedディレクトリに移動しましょう。
構成物については後述します。$ git clone https://github.com/GoogleContainerTools/skaffold $ cd examples/getting-started $ ls -Fla total 28 drwxrwxr-x 2 loft loft 4096 5月 7 21:00 ./ drwxrwxr-x 19 loft loft 4096 5月 7 21:00 ../ -rw-rw-r-- 1 loft loft 144 5月 7 21:00 Dockerfile -rw-rw-r-- 1 loft loft 153 5月 7 21:00 k8s-pod.yaml -rw-rw-r-- 1 loft loft 131 5月 7 21:00 main.go -rw-rw-r-- 1 loft loft 734 5月 7 21:00 README.adoc -rw-rw-r-- 1 loft loft 158 5月 7 21:00 skaffold.yaml $動作確認
ターミナルを2つ(
terminal-1とterminal-2)用意し、以下を実行します。terminal-1
skaffold devコマンドを実行します。
以下が行われる様子を確認できます。
- docker build ( MinikubeのVM内でbuild )
- docker push ( Minikubeの場合は行わない )
Found [minikube] context, using local docker daemon.より、Minikubeを認識していることが分かる- kubectl apply
- 標準出力ログの表示
- ソースコードの変更の監視
$ skaffold dev Generating tags... - gcr.io/k8s-skaffold/skaffold-example -> gcr.io/k8s-skaffold/skaffold-example:v0.28.0-89-gf25b09fa-dirty Tags generated in 7.774075ms Starting build... Found [minikube] context, using local docker daemon. Building [gcr.io/k8s-skaffold/skaffold-example]... Sending build context to Docker daemon 3.072kB Step 1/6 : FROM golang:1.10.1-alpine3.7 as builder ---> 52d894fca6d4 Step 2/6 : COPY main.go . ---> Using cache ---> 04e5f4a5d5e2 Step 3/6 : RUN go build -o /app main.go ---> Using cache ---> eb45bd0c0ce1 Step 4/6 : FROM alpine:3.7 ---> 6d1ef012b567 Step 5/6 : CMD ["./app"] ---> Using cache ---> da76805ab2e2 Step 6/6 : COPY --from=builder /app . ---> Using cache ---> 9c5ffabae1d9 Successfully built 9c5ffabae1d9 Successfully tagged gcr.io/k8s-skaffold/skaffold-example:v0.28.0-89-gf25b09fa-dirty Build complete in 72.162899ms Starting test... Test complete in 5.744µs Starting deploy... kubectl client version: 1.14 pod/getting-started created Deploy complete in 301.514962ms Watching for changes every 1s... [getting-started] Hello world! [getting-started] Hello world! [getting-started] Hello world! ( 略 : )terminal-2
アプリケーションのソースコードを書き換えてみましょう。
# 書き換え前 $ cat main.go ( 略 : ) fmt.Println("Hello world!") ( 略 : ) # 書き換え $ vim main.go # 書き換え後 $ cat main.go ( 略 : ) fmt.Println("Hello skaffold!") ( 略 : )terminal-1
再び
terminal-1を確認してみると、ソースコードの変更をトリガとして以下が行われる様子を確認できます。
- docker build ( MinikubeのVM内でbuild )
- docker push ( Minikubeの場合は行わない )
- kubectl apply
- 標準出力ログの表示
- ソースコードの変更の監視
手作業でイメージをビルドし、レジストリにpushし、クラスタにデプロイする手間が省かれていますね。
( 略 : ) [getting-started] Hello world! [getting-started] Hello world! [getting-started] Hello world! Generating tags... - gcr.io/k8s-skaffold/skaffold-example -> gcr.io/k8s-skaffold/skaffold-example:v0.28.0-89-gf25b09fa-dirty Tags generated in 21.094584ms Starting build... Found [minikube] context, using local docker daemon. Building [gcr.io/k8s-skaffold/skaffold-example]... Sending build context to Docker daemon 3.072kB Step 1/6 : FROM golang:1.10.1-alpine3.7 as builder ---> 52d894fca6d4 Step 2/6 : COPY main.go . ---> 4a61f315455a Step 3/6 : RUN go build -o /app main.go ---> Running in 30ee9d0e606f Removing intermediate container 30ee9d0e606f ---> 6a9d5e79631a Step 4/6 : FROM alpine:3.7 ---> 6d1ef012b567 Step 5/6 : CMD ["./app"] ---> Using cache ---> da76805ab2e2 Step 6/6 : COPY --from=builder /app . ---> 16dbf70b67d6 Successfully built 16dbf70b67d6 Successfully tagged gcr.io/k8s-skaffold/skaffold-example:v0.28.0-89-gf25b09fa-dirty Build complete in 1.75830858s Starting test... Test complete in 11.741µs Starting deploy... kubectl client version: 1.14 pod/getting-started configured Deploy complete in 296.352796ms Watching for changes every 1s... [getting-started] Hello skaffold! [getting-started] Hello skaffold! ( 略 : )構成物の確認
main.go
アプリケーションのソースコード。
1sec毎に標準出力にHello world!を表示する。$ cat main.go package main import ( "fmt" "time" ) func main() { for { fmt.Println("Hello world!") time.Sleep(time.Second * 1) } }Dockerfile
main.goをビルドしコンテナ起動時のコマンドとしている。
$ cat Dockerfile FROM golang:1.10.1-alpine3.7 as builder COPY main.go . RUN go build -o /app main.go FROM alpine:3.7 CMD ["./app"] COPY --from=builder /app . $k8s-pod.yaml
gcr.io/k8s-skaffold/skaffold-exampleというイメージを使用するPodの定義。$ cat k8s-pod.yaml apiVersion: v1 kind: Pod metadata: name: getting-started spec: containers: - name: getting-started image: gcr.io/k8s-skaffold/skaffold-example $skaffold.yaml
Skaffoldの設定ファイル。
build設定
- ビルドするイメージ名として
gcr.io/k8s-skaffold/skaffold-exampleを指定しているdeploy設定
kubectlコマンドを使用しているk8s-で始まるマニフェストファイル(yaml)を指定している$ cat skaffold.yaml apiVersion: skaffold/v1beta9 kind: Config build: artifacts: - image: gcr.io/k8s-skaffold/skaffold-example deploy: kubectl: manifests: - k8s-* $まとめ
いかがでしたか?
Skaffoldを使うと、クラスタにデプロイされるまでのルーチンが自動化されるので効率良く開発できそうですね。
特にMinikubeを開発環境として使用している場合は恩恵が大きそうです。今回はSkaffoldのさわりであるGetting Startedを試してみましたが、Tutorialsも用意されています。
本記事がSkaffold導入の参考になりましたら幸いです。
- 投稿日:2019-05-07T22:16:52+09:00
一足遅れて Kubernetes を学び始める - 08. discovery&LB その1 -
ストーリー
- 一足遅れて Kubernetes を学び始める - 01. 環境選択編 -
- 一足遅れて Kubernetes を学び始める - 02. Docker For Mac -
- 一足遅れて Kubernetes を学び始める - 03. Raspberry Pi -
- 一足遅れて Kubernetes を学び始める - 04. kubectl -
- 一足遅れて Kubernetes を学び始める - 05. workloads その1 -
- 一足遅れて Kubernetes を学び始める - 06. workloads その2 -
- 一足遅れて Kubernetes を学び始める - 07. workloads その3 -
- 一足遅れて Kubernetes を学び始める - 08. discovery&LB その1 -
前回
一足遅れて Kubernetes を学び始める - 07. workloads その3 -でようやくworkloadsが終了しました。今回は、discovery&LBを進めようと思います。
discovery&LB
Kubernetesには、下記のようにリソースの種類が存在します。
今回は、discovery&LBを学習します。
リソースの分類 内容 Workloadsリソース コンテナの実行に関するリソース Discovery&LBリソース コンテナを外部公開するようなエンドポイントを提供するリソース Config&Storageリソース 設定・機密情報・永続化ボリュームなどに関するリソース Clusterリソース セキュリティやクォータなどに関するリソース Metadataリソース リソースを操作する系統のリソース ※ KubernetesのWorkloadsリソース(その1)
discovery&LBをには、下記8つの種類があります。
- Service
- ClusterIP
- ExternalIP
- NodePort
- LoadBalancer
- Headless (None)
- ExternalName
- None-Selector
- Ingress
Serviceの概要について学びます。
Kubernetesとネットワーク
Kubernetesでは、Pod毎にIPアドレスが割り振られています。
そのため、異なるPod間で通信する際は、PodのIPアドレスが必要になります。逆に同一のPod内ならlocalhostで通信できます。説明するために、準備します。
sample-deployment.yamlapiVersion: apps/v1 kind: Deployment metadata: name: sample-deployment spec: replicas: 3 selector: matchLabels: app: sample-app template: metadata: labels: app: sample-app spec: containers: - name: nginx-container image: nginx:1.12 ports: - containerPort: 80 - name: redis-container image: redis:3.2pi@raspi001:~/tmp $ k apply -f sample-deployment.yaml pi@raspi001:~/tmp $ k get pods -l app=sample-app -o custom-columns="NAME:{metadata.name}, IP:{status.podIP},NODE:{spec.nodeName}" NAME IP NODE sample-deployment-9dc487867-h7lww 10.244.1.72 raspi002 sample-deployment-9dc487867-n8x5w 10.244.2.66 raspi003 sample-deployment-9dc487867-nxbxc 10.244.2.67 raspi003このような状況下で、
sample-deployment-9dc487867-n8x5w:redisを中心に見ていきます。※ nginxは80ポートで開放されています。
前準備
pi@raspi001:~/tmp $ k exec -it sample-deployment-9dc487867-n8x5w -c redis-container /bin/bash root@sample-deployment-9dc487867-n8x5w:/data# apt-get update && apt-get install curl -y root@sample-deployment-9dc487867-n8x5w:/data# exitcurlがないのでインストールします。
同一Node,同一Pod内のコンテナへ通信
pi@raspi001:~/tmp $ k exec -it sample-deployment-9dc487867-n8x5w -c redis-container /bin/bash root@sample-deployment-9dc487867-n8x5w:/data# curl localhost:80 <!DOCTYPE html> ...OK
同一Node,異なるPodのコンテナへ通信
pi@raspi001:~/tmp $ k exec -it sample-deployment-9dc487867-n8x5w -c redis-container /bin/bash root@sample-deployment-9dc487867-n8x5w:/data# curl 10.244.2.66:80 <!DOCTYPE html> ... root@sample-deployment-9dc487867-n8x5w:/data# curl 10.244.2.67:80 <!DOCTYPE html> ...OK
異なるNode,異なるPodのコンテナへ通信
pi@raspi001:~/tmp $ k exec -it sample-deployment-9dc487867-n8x5w -c redis-container /bin/bash root@sample-deployment-9dc487867-n8x5w:/data# curl 10.244.1.72:80 <!DOCTYPE html> ...OK
MasterNodeから各Podへ通信
pi@raspi001:~/tmp $ curl 10.244.1.72:80 <!DOCTYPE html> ... pi@raspi001:~/tmp $ curl 10.244.2.66:80 <!DOCTYPE html> ... pi@raspi001:~/tmp $ curl 10.244.2.67:80 <!DOCTYPE html> ...OK
ここから分かるように、Pod内部の通信、Pod間の通信、さらにNode間の通信までも、Kubernetesによってネットワークが構築されています。
Service
Serviceは、下記の2つの大きな機能が存在します。
- pod宛トラフィックのロードバランシング
- サービスディスカバリとクラスタ内DNS
pod宛トラフィックのロードバランシング
先程の例で、Pod間を通信することは可能です。しかし、podを作り直すたびにIPアドレスが変わってしまうため、
自作すると、少し大変です。そこで、Serviceの出番です。
サービスは、複数存在するPodに対して自動的にロードバランスしてくれるのと、合わせて外向けのIPアドレス(ExternalIP)や、内向けのIPアドレス(ClusterIP)も提供してくれます。さっそく、試してみます。
sample-clusterip.yamlapiVersion: v1 kind: Service metadata: name: sample-clusterip spec: type: ClusterIP ports: - name: "http-port" protocol: "TCP" port: 8080 targetPort: 80 selector: app: sample-appこれは、
app=sample-appにマッチするPodに対してロードバランスしてくれます。外から8080ポートで待ち受けて、80ポートでコンテナへ通信します。
spec.typeがClusterIPなので、内向けのIPアドレスが提供されています。pi@raspi001:~/tmp $ k apply -f sample-clusterip.yaml pi@raspi001:~/tmp $ k get service sample-clusterip NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE sample-clusterip ClusterIP 10.111.197.69 <none> 8080/TCP 30s pi@raspi001:~/tmp $ k describe service sample-clusterip Name: sample-clusterip ... Selector: app=sample-app Type: ClusterIP IP: 10.111.197.69 Port: http-port 8080/TCP TargetPort: 80/TCP Endpoints: 10.244.1.72:80,10.244.2.66:80,10.244.2.67:80 ...内向けに10.111.197.69のIPアドレスが振られました。また、ロードバランスする対象Podは、先にあげたPodのIPアドレスです。
Endpintsに:80とあるように、port毎にサービス(clusterIP)を作ることもできます。(serviceのspec.portsは配列指定)アクセスできるのか、試します。
せっかくなので、pod毎にindex.htmlの内容を変化させましょう。pi@raspi001:~/tmp $ for PODNAME in `k get pods -l app=sample-app -o jsonpath='{.items[*].metadata.name}'`; do > k exec -it ${PODNAME} -- cp /etc/hostname /usr/share/nginx/html/index.html; > done pi@raspi001:~/tmp $ curl 10.111.197.69:8080 sample-deployment-9dc487867-nxbxc pi@raspi001:~/tmp $ curl 10.111.197.69:8080 sample-deployment-9dc487867-n8x5w pi@raspi001:~/tmp $ curl 10.111.197.69:8080 sample-deployment-9dc487867-h7lww確かに、ロードバランシングによってpodに適度なランダム具合でアクセスできています。
もちろん、外からはアクセスできません。iMacへ移動
~ $ curl 10.111.197.69:8080 # 返答なしサービスディスカバリとクラスタ内DNS
サービスディスカバリとは、「問題においての解決策」を指しています。
Kubernetesにおける問題とは、動的にサービスが生成され続けていることによるサービスを特定することが難しくなる問題です。
そのサービスディスカバリが、Serviceにあります。
その方法について下記があります。
- 環境変数を利用したサービスディスカバリ
- PodにIPアドレスやport,protocolが設定されている。
- DNS Aレコードを利用したサービスディスカバリ
- Kubernetes内のクラスタ内DNSによって、ドメイン名によるアクセスができる。(ドメイン名の命名規則に従う)
- DNS SRVレコードを利用したサービスディスカバリ
- IPアドレスからドメイン名を取得する逆引きもできる。
dnsPolicyによる明示的な設定がない限り、Pod生成時にクラスタ内DNSへレコード追加されます。
クラスタ内DNSで名前解決できなかった場合は、クラスタ外DNSに問い合わせします。お片付け
pi@raspi001:~/tmp $ k delete -f sample-deployment.yaml -f sample-clusterip.yaml最後に
今回は、Serviceについての概要を学びました。Kubernetesの世界では、自動的にネットワーク構築されているため、特段意識することはありませんでした。
もう少し理解が進めれば、ネットワークがどのように構築されているのか、クラスタ内DNSがどのように動いているのか知りたいと思います。※ お絵かきしてアウトプットすると、理解が深まるのでおすすめです。
- 投稿日:2019-05-07T21:05:48+09:00
Rails+Docker+HerokuでCI/CD
CI/CD初心者が、CI/CDを構築した時のメモ。
初めて触ったので、誤っているところがあるかもしれません。
ご指摘いただければ、幸いです。
以下、参考にした記事等です。
https://qiita.com/kei_f_1996/items/934296e23b0d8d877ff1
https://qiita.com/Kesin11/items/47079bc7f659e71b694c
https://docs.docker.com/compose/rails/環境
OS:mac
Ruby:2.5.0
Rails:5.2.2
CicleCI
Heroku手順
Dockerfileの作成
docker-compose.ymlの作成
database.yml
CicleCI
HerokuDockerfileの作成
基本的にはdocker docsのQuickstartを参考に書きました。
DockerfileFROM ruby:2.5.0 RUN apt-get update -qq && apt-get install -y postgresql-client sudo RUN curl -sL https://deb.nodesource.com/setup_11.x | sudo -E bash - RUN sudo apt-get install -y nodejs RUN mkdir /myapp WORKDIR /myapp ADD Gemfile /myapp/Gemfile ADD Gemfile.lock /myapp/Gemfile.lock RUN bundle install COPY . /myapp CMD ["rails", "server", "-b", "0.0.0.0"]3行目で、nodejsのversionを指定しています。
server.pidファイルが存在する場合に、railsのサーバが再起動しなくなったので、Quickstartに書かれている、
# Add a script to be executed every time the container starts.
の部分を書いておくべきでした。docker-compose.ymlの作成
こちらもQuickstartを基に書いてます。
docker-compose.ymlversion: '3' services: db: image: postgres:9.6 ports: - '5432:5432' volumes: - postgresql-data:/var/lib/postgresql/data web: build: . command: bundle exec rails s -p 3000 -b '0.0.0.0' volumes: - .:/myapp ports: - "3000:3000" depends_on: - db - chrome chrome: image: selenium/standalone-chrome:3.141.59-dubnium ports: - 4444:4444 volumes: postgresql-data: driver: local・自動テスト時に、chromeでのテストがうまくできていなかったみたいなので、chrome部分を追加しました。
(対処として最適かどうかはわかりません)
・コンテナを潰してもDBのデータが消えないようにするために、volumesの部分を追加。database.yml
database.ymlに記述を追加。
これもQuickstartを参考にしました。database.ymldefault: &default adapter: postgresql encoding: unicode #追加 host: db username: postgres password: pool: 5あとはbuildしたりupしたりします。
$ docker-compose build $ docker-compose up $ docker-compose run web bin/rails db:createCicleCI
アプリケーションのルートディレクトリに、.cicleciフォルダを作ります。
.cicleci直下に以下のconfig.ymlを記述します。config.ymlversion: 2 jobs: build: machine: image: circleci/classic:edge steps: - checkout - run: name: docker-compose build command: docker-compose build - run: name: docker-compose up command: docker-compose up -d - run: name: sleep for waiting launch db command: sleep 1 - run: name: "before_test: setup db" command: docker-compose run web rails db:create db:migrate - run: name: test command: docker-compose run web bundle exec rspec - run: name: docker-compose down command: docker-compose down deploy: machine: image: circleci/classic:edge steps: - checkout - run: name: "build docker image" command: docker build --rm=false -t registry.heroku.com/${HEROKU_APP_NAME}/web . - run: name: setup heroku command command: bash .circleci/setup_heroku.sh - run: name: heroku maintenance on command: heroku maintenance:on --app ${HEROKU_APP_NAME} - run: # HEROKU_AUTH_TOKEN is generated by `heroku auth:token` name: "push container to registry.heroku.com" command: | docker login --username=_ --password=$HEROKU_AUTH_TOKEN registry.heroku.com docker push registry.heroku.com/${HEROKU_APP_NAME}/web bash .circleci/heroku-container-release.sh - run: name: heroku db migrate command: heroku run rails db:migrate --app ${HEROKU_APP_NAME} - run: name: heroku maintenance off command: heroku maintenance:off --app ${HEROKU_APP_NAME} workflows: version: 2 build_and_deploy: jobs: - build - deploy: requires: - build filters: branches: only: masterhttps://circleci.com/docs/2.0/configuration-reference/
machine: の部分でdocker-composeをそのまま使えるように設定しています。
CicleCIのEnvironmentValiablesに、
HEROKU_APP_NAME,HEROKU_AUTH_TOKEN,HEROKU_LOGIN
,HEROKU_API_KEYを設定しておきます。デプロイ時に実行するshellは以下の二つです。
setup_heroku.shwget https://cli-assets.heroku.com/branches/stable/heroku-linux-amd64.tar.gz sudo mkdir -p /usr/local/lib /usr/local/bin sudo tar -xvzf heroku-linux-amd64.tar.gz -C /usr/local/lib sudo ln -s /usr/local/lib/heroku/bin/heroku /usr/local/bin/heroku cat > ~/.netrc << EOF machine api.heroku.com login $HEROKU_LOGIN password $HEROKU_API_KEY EOF # machine git.heroku.com # login $HEROKU_LOGIN # password $HEROKU_API_KEY # Add heroku.com to the list of known hosts ssh-keyscan -H heroku.com >> ~/.ssh/known_hostheroku-container-release.sh#!/bin/bash imageId=$(docker inspect registry.heroku.com/${HEROKU_APP_NAME}/web:latest --format={{.Id}}) payload='{"updates":[{"type":"web","docker_image":"'"$imageId"'"}]}' curl -n -X PATCH https://api.heroku.com/apps/${HEROKU_APP_NAME}/formation \ -d "$payload" \ -H "Content-Type: application/json" \ -H "Accept: application/vnd.heroku+json; version=3.docker-releases" \ -H "Authorization: Bearer ${HEROKU_API_KEY}"Heroku
postgresのアドオンを入れ忘れないように気をつけて、herokuの設定も完了すれば、CICDできます。たぶん
- 投稿日:2019-05-07T19:23:29+09:00
Remix IDEをDockerで起動してローカルディレクトリと同期する
Dockerでremix-ideをローカルディレクトリと同期してみる
WindowsでRemix IDEをインストールしようとしたところ、色々エラーが発生して手間だったのでDockerで環境を作ってみました。
環境
- windows10
- Docker
やってみる
remix-ide githubを参考に実施していきます。
1.コンテナの作成
nodeの公式イメージでコンテナの作成をしていきます。
パラメータに、remix-ideで利用するポートと、ファイルを編集しやすいように共有ディレクトリを設定しました。$ docker run -it -d --name remix -p 8080:8080 -p 65520:65520 -v //E/work/share/Ethreum/remix-ide:/apl node:11.14.0 $ docker exec -it remix /bin/bash2.remix-ideのリポジトリを取得
remix-ideをインストールしていきます。
インストール方法はnodeモジュールとしての取得とリポジトリのクローンどちらでもできるようですが、今回はリポジトリのクローンで実施していきます。$ cd apl $ git clone https://github.com/ethereum/remix-ide.git $ cd remix-ide $ npm update $ npm install3.remix-ideのビルド
公式のドキュメントに記載されている通り「npm start」と、実行していきたいですが、
このままでは以下のエラーが発生し動かないのでbuildします。errGET http://localhost:8080/build/app.js net::ERR_ABORTED 404 (Not Found)ビルド。
$ npm run-script build4.ローカルディレクトリ同期のための設定変更
次にローカルディレクトリ用の設定変更します。
この時点で「npm start」をすればremixは動作すると思いますが、ローカルディレクトリとの共有を行おうとすると以下のエラーが発生します。errWebSocket connection to 'ws://localhost:65520/' failed: Connection closed before receiving a handshake responseremix起動時ログ[remixd ] [WARN] You may now only use IDE at http://127.0.0.1:8080 to connect to that instance [remixd ] [WARN] Any application that runs on your computer can potentially read from and write to all files in the directory. [remixd ] [WARN] Symbolinc links are not forwarded to Remix IDE [remixd ] [remixd ] [WARN] Symbolic link modification not allowed : ./contracts | /apl/remix-ide/contracts [remixd ] Sat Apr 27 2019 06:34:17 GMT+0000 (Coordinated Universal Time) Remixd is listening on 127.0.0.1:65520ローカルディレクトリ同期用のリスナーが(127.0.0.1:65520)で立ち上がっているため、接続に失敗しているのだと思うので、IPを変更していきます。
websocket.js内のIPを「127.0.0.1」から「0.0.0.0」に変更します。
変更することで、ディレクトリ共有のリスナーが「0.0.0.0:65520」に変更されます。$ sed -i s/127.0.0.1/0.0.0.0/g ./node_modules/remixd/src/websocket.js動作確認
準備完了です!起動していきます!
npm start > remix-ide@0.7.5 start /apl/remix-ide > npm-run-all -lpr serve watch onchange remixd [serve ] [serve ] > remix-ide@0.7.5 serve /apl/remix-ide [serve ] > execr --silent http-server . [serve ] [onchange] [onchange] > remix-ide@0.7.5 onchange /apl/remix-ide [onchange] > onchange build/app.js -- npm-run-all lint [onchange] [watch ] [watch ] > remix-ide@0.7.5 watch /apl/remix-ide [watch ] > watchify src/index.js -dv -p browserify-reload -o build/app.js --exclude solc [watch ] [remixd ] [remixd ] > remix-ide@0.7.5 remixd /apl/remix-ide [remixd ] > ./node_modules/remixd/bin/remixd -s ./contracts --remix-ide http://127.0.0.1:8080 [remixd ] [watch ] WS server listening on 39167 [remixd ] [WARN] You may now only use IDE at http://127.0.0.1:8080 to connect to that instance [remixd ] [WARN] Any application that runs on your computer can potentially read from and write to all files in the directory. [remixd ] [WARN] Symbolinc links are not forwarded to Remix IDE [remixd ] [remixd ] [WARN] Symbolic link modification not allowed : ./contracts | /apl/remix-ide/contracts [remixd ] Thu Apr 25 2019 11:46:17 GMT+0000 (Coordinated Universal Time) Remixd is listening on 0.0.0.0:65520 [remixd ] Thu Apr 25 2019 11:46:26 GMT+0000 (Coordinated Universal Time) Connection accepted. [remixd ] setup notifications for /apl/remix-ide/contracts [remixd ] Thu Apr 25 2019 11:46:32 GMT+0000 (Coordinated Universal Time) Remix 172.17.0.1 disconnected. [watch ] NOW ASKING FOR CLIENT TO RELOAD [watch ] 26856676 bytes written to build/app.js (20.88 seconds) at 11:46:35 AM [onchange] [onchange] > remix-ide@0.7.5 lint /apl/remix-ide [onchange] > standard | notify-error [onchange]ブラウザで「 http://127.0.0.1:8080 」を表示。
Remixの画面が表示されました!次にローカルディレクトリと共有してみます。
サイドバーに「lockalhost」ディレクトリが表示されました!
ディレクトリ共有出来ているみたいです。Remixのローカルディレクトリ同期先を変更したい場合は以下の「./contracts」のパスを変更すれば好きなディレクトリと同期できるようです。
package.json"remixd": "./node_modules/remixd/bin/remixd -s ./contracts --remix-ide http://127.0.0.1:8080",おまけ
Dockerfileも作ってみました。
/apl/remix-ide 配下でnpm startで動きます。DockerfileFROM node:11.14.0 SHELL ["/bin/bash", "-c"] RUN mkdir apl WORKDIR /apl # remix-ide clone RUN git clone https://github.com/ethereum/remix-ide.git WORKDIR /apl/remix-ide RUN npm update RUN npm install # ホストで利用できるようIP変更 RUN sed -i s/127.0.0.1/0.0.0.0/g ./node_modules/remixd/src/websocket.js # ビルド RUN npm run-script build参考
https://github.com/4c0n/remixd-docker/blob/master/Dockerfile
- 投稿日:2019-05-07T12:49:04+09:00
Docker Composeで起動したコンテナのログを、Amazon CloudWatch Logsに送る
TL;DR
- Docker Composeで起動したコンテナのログを、Amazon CloudWatch Logsに送るようにしたい
- 要するに、AWS環境で起動したコンテナのログを集約したい
- AWS上のコンテナサービスの話は、ここでは置いておく
- Dockerのlogging driverでAmazon CloudWatch Logs用のドライバがあるので、それを使用するように
docker-compose.ymlに設定するDockerのlogging driver
Dockerコンテナには、ログをどこに出力するかを設定できる、logging driverの仕組みがあります。
デフォルトは、
json-filelogging driverで、これはホスト側の/var/lib/docker/containers/[コンテナID]/[コンテナID]-json.logに出力されます。Dockerコンテナのログを、Amazon CloudWatch Logsに出力する
Dockerコンテナが使うlogging driverを、Amazon CloudWatch Logs logging driverに変更することで、DockerコンテナのログをAmazon CloudWatch Logsに送信することができます。
※なお、Amazon CloudWatch Logsにログ出力できるように、EC2へのIAMロールの付与は必要になりますAmazon CloudWatch Logs logging driver
あとは、これを
docker-compose.ymlに設定すればOKです。Compose file reference / logging
以下のように、
services内の各コンテナに対してlogging設定を書いていきます。指定するdriverは、awslogsです。services: xxx: ... logging: driver: awslogs options: awslogs-region: ap-northeast-1 awslogs-group: my-log-group少なくとも、リージョン(
awslogs-region)とロググループ(awslogs-group)は指定する必要があります。また、あらかじめロググループは作成しておく必要があります。
ログストリームは、デフォルトではコンテナのID(先頭12桁)が利用されます。
これを変更する場合は、
awslogs-streamやtagで指定して変更するとよいでしょう。以下では、
tagを使用して「イメージ名.コンテナ名.コンテナID(フル)」がログストリームとなるようにしています。logging: driver: awslogs options: awslogs-region: ap-northeast-1 awslogs-group: my-log-group tag: "{{.ImageName}}.{{.Name}}.{{.FullID}}"
- 投稿日:2019-05-07T07:26:06+09:00
RDBMSにGTFSデータを格納してみる(環境作成編)
GTFS Advent Calendar 2日目の記事です。
今日はGTFSデータをデータベース(RDBMS)に格納するための手順を紹介しようと思います。GTFSデータとRDBMS
GTFS(General Transit Feed Specification)は零細事業者の利用を視野に入れており、テキストエディタや表計算ソフトでの閲覧が容易なCSV形式を採用しています。具体的には経路情報(routes.txt)や便情報(trips.txt)のような情報の単位でCSVファイルが分かれています。
CSVファイル内のフィールドには他のCSVファイルのフィールドと関連付く項目があり、RDBMSにおける表結合の操作と相性が良さそうです。
というワケで、実際にGTFSデータをRDBMSに格納してSQLを介してGTFSデータを処理してみましょう。
RDBMS環境の準備
まずはRDBMS環境の準備です。Dockerを使用して手早く用意しましょう。
Dockerイメージ作成用のDockerfileは以下のリポジトリに用意してあります。$ git clone https://github.com/ValLaboratory/advcal.git $ cd advcal/2019/gw/docker_env/ $ docker-compose build gtfs_dbgit cloneしたのち
docker-compose buildを実行します。$ docker images REPOSITORY TAG IMAGE ID CREATED SIZE gtfs_db latest eab624c5d7f2 38 minutes ago 449MBDockerイメージが作成されるので、イメージからコンテナを起動します。
$ docker run --name mysql01 -e MYSQL_ROOT_PASSWORD=mysql -d -p 3306:3306 gtfs_db $ docker exec -ti mysql01 mysql -uroot -p上記の
docker execを実行すると、MySQLのコンソールにログインできます。
コンソール上から以下の手順を実行し、必要なデータベーススキーマとテーブルの作成、GTFSデータの投入を行います。
(必要なSQLファイルはDockerイメージ作成時にコンテナにコピーしています)mysql> -- データベーススキーマを作成する。 mysql> source /tmp/setup_gtfs_db.sql mysql> mysql> -- ER図作成用のデータベースを作成し、テーブルを作成する。 mysql> use gtfs_db_reference mysql> source /tmp/gtfs_reference.sql mysql> mysql> -- GTFSデータ格納用のデータベースを作成し、テーブルを作成する。 mysql> use gtfs_db mysql> source /tmp/gtfs.sql mysql> source /tmp/kitaena_gtfs_data.sqlこれでRDBMSの準備は完了です。ER図作成用のデータベースは後のAdvent Calendarで解説します。
まずはGTFSデータ格納用のデータベースを触ってみます。$ docker exec -ti mysql01 mysql -uroot -p Enter password: ...中略... mysql> mysql> -- GTFSデータ格納用のデータベースに切り替えます。 mysql> use gtfs_db ; mysql> mysql> -- 適当なテーブルの定義を表示させてみます。 mysql> desc feed_info ; +---------------------+-------------+------+-----+---------+-------+ | Field | Type | Null | Key | Default | Extra | +---------------------+-------------+------+-----+---------+-------+ | feed_publisher_name | text | NO | | NULL | | | feed_publisher_url | text | NO | | NULL | | | feed_lang | varchar(16) | NO | | NULL | | | feed_start_date | varchar(12) | YES | | NULL | | | feed_end_date | varchar(12) | YES | | NULL | | | feed_version | text | YES | | NULL | | +---------------------+-------------+------+-----+---------+-------+ 6 rows in set (0.00 sec) mysql> mysql> -- データを見てみる。 mysql> SELECT * FROM feed_info ; +-----------------------------+---------------------------+-----------+-----------------+---------------+-------------------------------------------------+ | feed_publisher_name | feed_publisher_url | feed_lang | feed_start_date | feed_end_date | feed_version | +-----------------------------+---------------------------+-----------+-----------------+---------------+-------------------------------------------------+ | 北恵那交通株式会社 | http://www.kitaena.co.jp/ | ja | 20190401 | 20200331 | 20190401_2019年04月01日(北恵那バス) | +-----------------------------+---------------------------+-----------+-----------------+---------------+-------------------------------------------------+ 1 row in set (0.00 sec)GTFSの各CSVファイル名をテーブル名、CSV内の各項目をテーブルのフィールドに対応させる形のテーブル定義となっています。そして、GTFSのデータとして北恵那交通株式会社のGTFSデータを格納しています。
これでGTFSを格納したDBが用意できました。
まとめ
CSVの形で提供されているGTFSデータをRDBMSに格納するための環境作成を行いました。
次回以降のAdvent Calendarでは、このDBを使ってGTFSデータの処理をいろいろ試してみようと思います。
- 投稿日:2019-05-07T00:23:19+09:00
Arduino(ESP32)でROSを動かす
はじめに
Arduino (ESP32-WROOM-32; ESP32) + ROSでchatterサンプルを動かすところまでを構築した。
その実績メモを残す。ググれば類似記事はたくさん出てくるので差別化のため本記事の特徴を書いておく。
- ROSのバージョンがMelodic Morenia
- ROS環境をDocker上で構築した
- ROSのArduino用シリアル通信ライブラリの抽出をビルドなしで行える
動作環境
- Windows 10 1809
- Docker Desktop Community 2.0.0.3
- Arduino IDE 1.8.9
手元に手頃なデスクトップPCがなかったのでOSはWindowsを使うことにし、Docker Desktopを使用してコンテナ内にROS環境を構築することにした。
Gazeboなどシミュレータ(GUI)を使う予定があるならこの方法は使えないことに注意。ESP32とWindowsはUSBで接続し、Arduinoプログラムのコーディング・ビルド・書き込みはホストOS(Windows)上にインストールしたArduino IDEで行う。
ESP32と通信するROSはDockerコンテナ上に構築する。
上述のとおり、ESP32とWindowsは物理的に接続されているが、ESP32とROSの間の通信はWiFiによる無線通信で行う。
セットアップ
Docker Desktopのインストールは省略する。
rosserial入りDockerイメージの作成ROSのイメージファイルは
ros:melodic-robotとして配布されているが、
今回やろうとしているArduiono(ESP32)との通信にはrosserialパッケージを別途インストールする必要がある。
ros:melodic-robotをrunしてからインストールしてもよいが、せっかくなのでDockerfileを作ってrosserialのインストールまでイメージ化しておく。
- 適当な空フォルダを用意する
- 以降では
C:\tmp\ros_envを用意したものとする- 本節下部に示すDockerfileを
C:\tmp\ros_env\Dockerfileとして置く- コマンドプロンプトやPowerShell上で
docker build -t local_ros/rosserial .\を実行してイメージを作成するDockerfile
FROM ros:melodic-robot # install packages RUN apt update && apt install -y \ ros-melodic-rosserial \ ros-melodic-rosserial-arduino \ && \ apt clean && \ rm -rf /var/lib/apt/lists/*Arduino IDEのインストール
(1)本体のインストール
公式ページからダウンロード・インストールする。
2019年5月現在なら「Windows Installer, for Windows XP and up」か「Windows app Requires Win 8.1 or 10」がいいと思う。
(2)ESP32用プラグインのインストール
こちらの記事を参考にした。
上記記事の「ESP32-WROOM-32」-「セットアップ」-「ソフトウェア」-「[Arduino core for the ESP3 のインストール (ライブラリマネージャ使用)]」を参照。
(3)
ros_libの抽出とインストールArduinoからROSを使うためには専用のC++ライブラリが必要になる(ライブラリとはいっても実体はヘッダファイルなのでビルドは不要)。
今回の構成ではROSをコンテナ上に構築したので、コンテナ上のファイルをホストにコピーする。通常はgitリポジトリをcloneしビルド・インストールしてパスを通したところでライブラリを抽出するスクリプトを実行するのが正規の手順であるが、いちいちビルドするのは面倒なので自前でコピーする。
cd C:\tmpでカレントディレクトリを移動するmkdir dockerで作業用フォルダを作成するdocker run -it --rm -v C:\tmp\docker:/root/host local_ros/rosserialを実行してDockerコンテナを起動するcp -R /opt/ros/melodic/share/rosserial_arduino/src/ros_lib ~/hostを実行してライブラリをコピーするexitを実行してコンテナを終了するC:\tmp\Docker\ros_libフォルダを<Arduinoのインストールフォルダ>\libraries\ros_libとして移動するビルド
Arduino IDEを開き、以下のプログラムをESP32に書き込む。
プログラムにはWiFiルータのSSIDおよびパスワード、接続先となるIPアドレスが必要になる。
接続先となるIPアドレスについてはホストであるWindowsのIPアドレスでよい。後述のとおりコンテナの起動時にポートフォワーディングオプションをつけるためである。// // このソースコードは以下の記事を参考にさせていただきました。 // https://gist.github.com/KobayashiRui/094ac01d9d3cd2445faa2a1ef103646f // #include <ros.h> #include <std_msgs/String.h> #include <WiFi.h> const char SSID[] = "<ここにWiFiルータのSSIDを書く>"; const char PASSWORD[] = "<ここにWiFiルータのパスワードを書く>"; IPAddress server(<ここにWindowsのIPアドレスを書く>); // e.g.: IPAddress server(192, 168, 1, 3); const uint16_t serverPort = 11411; WiFiClient client; class WiFiHardware { public: WiFiHardware() {}; void init() { client.connect(server, serverPort); } int read() { return client.read(); } void write(uint8_t* data, int length) { for(int i=0; i<length; i++) client.write(data[i]); } unsigned long time() { return millis(); } }; ros::NodeHandle_<WiFiHardware> nh; std_msgs::String str_msg; ros::Publisher chatter("chatter", &str_msg); char hello[13] = "hello world!"; void setup() { Serial.begin(115200); WiFi.begin(SSID,PASSWORD); Serial.print("WiFi connecting"); while (WiFi.status() != WL_CONNECTED) { Serial.print("."); delay(100); } Serial.println(" connected"); nh.initNode(); nh.advertise(chatter); delay(10); } void loop() { str_msg.data = hello; chatter.publish( &str_msg ); nh.spinOnce(); delay(1000); }実行
コンテナを起動してROSマスターを起動し、subscribeする。
前述のとおり接続先としてホストのIPアドレスを指定したため、コンテナの起動においてはポートフォワーディングオプションをつけていることに注意する。
docker run -it --rm -p 11411:11411 -v C:\tmp\docker:/root/host local_ros/rosserialを実行してコンテナを起動するroscore &を実行してROSマスターを起動するrosrun rosserial_python serial_node.py tcp &を実行してTCP/IPによるシリアル通信をsubscribeする- ESP32のUSBケーブルを差し直して電源を入れ直す
- subscribeに成功したメッセージが表示されたのを確認する
rostopic echo chatterを実行して"hello world!"を受信していることを確認する以上。




