マルチホストでdocker−swarm (GCP)
マルチホストでdocker swarm を試す
参考サイト
kvs の作成
docker-machine
でswarm−kvsを作成する
$ docker-machine create \ --driver google \ --google-project myproject-xxxx \ --google-zone asia-east1-a \ --google-machine-type f1-micro \ swarm-kvs
instanceが作成されたのを確認。
$ docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM swarm-kvs - google Running tcp://130.211.246.63:2376
環境変数を整えて
$ eval "$(docker-machine env swarm-kvs)"
作成したswarm−kvs 上にconsul をサーバ・モードで実行する。
$ docker $(docker-machine config swarm-kvs) run -d \ -p "8500:8500" \ -h "consul" \ progrium/consul -server -bootstrap
swarm cluster を作成する
マルチホストで通信するためにはクラスタ設定が必要。
- swarm−master の作成
$ docker-machine create \ --driver google \ --google-project myproject-xxxx \ --google-zone asia-east1-a \ --google-machine-type f1-micro \ --swarm \ --swarm-master \ --swarm-discovery="consul://$(docker-machine ip swarm-kvs):8500" \ --engine-opt="cluster-store=consul://$(docker-machine ip swarm-kvs):8500" \ --engine-opt="cluster-advertise=eth0:2376" \ swarm-master
- swarm−agent の作成
$ docker-machine create \ --driver google \ --google-project myproject-xxxx \ --google-zone asia-east1-a \ --google-machine-type f1-micro \ --swarm \ --swarm-discovery="consul://$(docker-machine ip swarm-kvs):8500" \ --engine-opt="cluster-store=consul://$(docker-machine ip swarm-kvs):8500" \ --engine-opt="cluster-advertise=eth0:2376" \ swarm-agent
docker クライアントの環境変数の切り替え
--swarm
をつけるとswarmクラスタの設定になる。
$ eval $(docker-machine env --swarm swarm-master)
docker info
でクラスタの状態を確認できる。
$ docker info Containers: 3 Images: 2 Role: primary Strategy: spread Filters: health, port, dependency, affinity, constraint Nodes: 2 swarm-agent1: 104.155.201.164:2376 └ Status: Healthy └ Containers: 1 └ Reserved CPUs: 0 / 1 └ Reserved Memory: 0 B / 618.7 MiB └ Labels: executiondriver=native-0.2, kernelversion=3.19.0-28-generic, operatingsystem=Ubuntu 14.04.3 LTS, provider=google, storagedriver=aufs swarm-master: 104.199.156.16:2376 └ Status: Healthy └ Containers: 2 └ Reserved CPUs: 0 / 1 └ Reserved Memory: 0 B / 618.7 MiB └ Labels: executiondriver=native-0.2, kernelversion=3.19.0-28-generic, operatingsystem=Ubuntu 14.04.3 LTS, provider=google, storagedriver=aufs CPUs: 2 Total Memory: 1.208 GiB Name: bedd6437dbb4
compute engine のinstanceを確認。
$ gcloud compute instances list NAME ZONE MACHINE_TYPE PREEMPTIBLE INTERNAL_IP EXTERNAL_IP STATUS swarm-agent1 asia-east1-a f1-micro 10.240.0.4 104.155.201.164 RUNNING swarm-kvs asia-east1-a f1-micro 10.240.0.2 130.211.246.63 RUNNING swarm-master asia-east1-a f1-micro 10.240.0.3 104.199.156.16 RUNNING
$ docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM swarm-agent1 - google Running tcp://104.155.201.164:2376 swarm-master swarm-kvs - google Running tcp://130.211.246.63:2376 swarm-master * google Running tcp://104.199.156.16:2376 swarm-master (master)
オーバーレイ ネットワークの作成
- ネットワークの作成
$ docker network create --driver overlay my-network
9e37172e132c50e8bc89e06000cf2ef6774a0fb5305e87ce5c4599383f49096e
- 作成したネットワークの確認
$ docker network ls
NETWORK ID NAME DRIVER
bfa7f2d3ea25 swarm-master/none null
a656e617f5b0 swarm-master/host host
9e37172e132c my-network overlay
fd6eaa122101 swarm-master/bridge bridge
66f847f22da0 swarm-agent1/none null
1e48ec001aaa swarm-agent1/host host
0902af9fc7d3 swarm-agent1/bridge bridge
アプリケーションの起動(作成したネットワーク上での)
- DB(postgres) をswarm上に起動
$ docker build -t postgres ./postgres
$ docker run -d --net=my-network --name mydb postgres
- AP(spring-boot) をswarm上に起動 起動時の引数を以下のように設定 ↑で設定した名前(mydb)を使用できる。(自然な感じで接続できる)
FROM ubuntu # Setup RUN apt-get install -y software-properties-common debconf-utils RUN add-apt-repository -y ppa:webupd8team/java RUN apt-get update # Java8 RUN echo "oracle-java8-installer shared/accepted-oracle-license-v1-1 boolean true" | debconf-set-selections ENV DEBIAN_FRONTEND noninteractive RUN apt-get install -y oracle-java8-installer CMD ["java", "-version"] EXPOSE 8080 ADD ./my-application-0.1.0.jar /my-application-0.1.0.jar CMD ["java", "-jar", "/my-application-0.1.0.jar",\ "--spring.datasource.initialize=false",\ "--spring.datasource.driverClassName=org.postgresql.Driver",\ "--spring.datasource.url=jdbc:postgresql://mydb:5432/myschema",\ "--spring.datasource.username=scott",\ "--spring.datasource.password=tiger"]
$ docker build -t web ./web $ docker run --rm -p 80:8080 --net=my-network --name myapp web
ホスト間の接続の確認
docker ps
で別々のnodeで起動していることを確認
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES b033d2069995 web "java -jar /isolating" 9 seconds ago Up 6 seconds 104.199.156.16:80->8080/tcp swarm-master/myapp 87ec3da3b615 postgres "/usr/lib/postgresql/" 22 seconds ago Up 21 seconds 5432/tcp swarm-agent1/mydb
$ docker exec -it b033d2069995 bash root@b033d2069995:/# root@b033d2069995:/# cat /etc/hosts 10.0.0.3 b033d2069995 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 10.0.0.2 mydb 10.0.0.2 mydb.my-network root@b033d2069995:/# root@b033d2069995:/# ping mydb PING mydb (10.0.0.2) 56(84) bytes of data. 64 bytes from mydb (10.0.0.2): icmp_seq=1 ttl=64 time=1.35 ms 64 bytes from mydb (10.0.0.2): icmp_seq=2 ttl=64 time=0.631 ms 64 bytes from mydb (10.0.0.2): icmp_seq=3 ttl=64 time=0.680 ms 64 bytes from mydb (10.0.0.2): icmp_seq=4 ttl=64 time=0.590 ms 64 bytes from mydb (10.0.0.2): icmp_seq=5 ttl=64 time=0.589 ms 64 bytes from mydb (10.0.0.2): icmp_seq=6 ttl=64 time=0.620 ms ^C --- mydb ping statistics --- 6 packets transmitted, 6 received, 0% packet loss, time 4998ms
docker exec -it 87ec3da3b615 bash postgres@87ec3da3b615:/$ postgres@87ec3da3b615:/$ cat /etc/hosts 10.0.0.2 87ec3da3b615 127.0.0.1 localhost ::1 localhost ip6-localhost ip6-loopback fe00::0 ip6-localnet ff00::0 ip6-mcastprefix ff02::1 ip6-allnodes ff02::2 ip6-allrouters 10.0.0.3 myapp 10.0.0.3 myapp.my-network postgres@87ec3da3b615:/$ postgres@87ec3da3b615:/$ ping myapp PING myapp (10.0.0.3) 56(84) bytes of data. 64 bytes from myapp (10.0.0.3): icmp_seq=1 ttl=64 time=1.44 ms 64 bytes from myapp (10.0.0.3): icmp_seq=2 ttl=64 time=0.820 ms 64 bytes from myapp (10.0.0.3): icmp_seq=3 ttl=64 time=0.801 ms 64 bytes from myapp (10.0.0.3): icmp_seq=4 ttl=64 time=0.814 ms ^C --- myapp ping statistics --- 4 packets transmitted, 4 received, 0% packet loss, time 3000ms rtt min/avg/max/mdev = 0.801/0.970/1.446/0.275 ms
画面からもDBにアクセスするのを確認できた。*1 めでたし。めでたし。
途中、すんなり行かなかったところ。
compute engine のinstance を作成するときに、途中で止まってうまく作れない(Configuring swarm に失敗する)場合、 firewall−ruleでtcp:3386;tcp:2376を許可するルールを作成する必要がある。
Configuring swarm... WARNING >>> Error attempting heartbeat call to plugin server: unexpected EOF
と出力された場合、以下のコマンドでfirewall−ruleを作成する
$ gcloud compute firewall-rules create swarm-firewall \ --target-tags docker-machine --allow tcp:2376,tcp:3376 Created [https://www.googleapis.com/compute/v1/projects/myproject-xxxx/global/firewalls/swarm-firewall]. NAME NETWORK SRC_RANGES RULES SRC_TAGS TARGET_TAGS swarm-firewall default 0.0.0.0/0 tcp:2379,tcp:3389 docker-machine
firewall-rulesの確認
$ gcloud compute firewall-rules list NAME NETWORK SRC_RANGES RULES SRC_TAGS TARGET_TAGS default-allow-icmp default 0.0.0.0/0 icmp default-allow-internal default 10.240.0.0/16 tcp:0-65535,udp:0-65535,icmp default-ssh default 0.0.0.0/0 tcp:22 docker-machines default 0.0.0.0/0 tcp:2376,tcp:3376,tcp:8500,tcp:80 docker-machine
docker−machine create で失敗した時の出力
$ docker-machine create --driver google --google-project myproject-xxxx --google-zone asia-east1-a --google-machine-type f1-micro --swarm --swarm-master --swarm-discovery="consul://$(docker-machine ip swarm-kvs):8500" --engine-opt="cluster-store=consul://$(docker-machine ip swarm-kvs):8500" --engine-opt="cluster-advertise=eth0:2376" swarm-master Running pre-create checks... (swarm-master) OUT | Check that the project exists (swarm-master) OUT | Check if the instance already exists Creating machine... (swarm-master) OUT | Generating SSH Key (swarm-master) OUT | Creating host... (swarm-master) OUT | Creating instance. (swarm-master) OUT | Waiting for Instance... (swarm-master) OUT | Uploading SSH Key (swarm-master) OUT | Waiting for SSH Key Waiting for machine to be running, this may take a few minutes... Machine is running, waiting for SSH to be available... Detecting operating system of created instance... Detecting the provisioner... Provisioning created instance... Copying certs to the local machine directory... Copying certs to the remote machine... Setting Docker configuration on the remote daemon... Configuring swarm... WARNING >>> Error attempting heartbeat call to plugin server: unexpected EOF Error creating machine: Error running provisioning: connection is shut down
*1:screenshotは撮り忘れた。
docker swarm を(今さら)試す
docker swarm
- cruster-id 作成
$ docker run --rm swarm create 9b9bc80f30bfbf9d16142fcc031ad7a7
- master (swarm-master) 作成
$ docker-machine create \ --driver google \ --google-project myproject-1198 \ --google-zone asia-east1-a \ --google-machine-type f1-micro \ --swarm \ --swarm-master \ --swarm-discovery token://9b9bc80f30bfbf9d16142fcc031ad7a7 \ swarm-master
- node (swarm-agent) 作成
$ docker-machine create \ --driver google \ --google-project myproject-1198 \ --google-zone asia-east1-a \ --google-machine-type f1-micro \ --swarm \ --swarm-discovery token://9b9bc80f30bfbf9d16142fcc031ad7a7 \ swarm-agent
- firewall-rule作成
docker-machine createでinstanceを作成すると
docker-machine
tag が付与されるので、http通信できるようにfirewall-ruleを作成する。
$ gcloud compute firewall-rules create docker-http \ --source-ranges 0.0.0.0/0 \ --target-tags docker-machine \ --allow tcp:80
$ docker-machine ls NAME ACTIVE DRIVER STATE URL SWARM swarm-agent - google Running tcp://104.155.224.60:2376 swarm-master swarm-master * google Running tcp://104.155.223.102:2376 swarm-master (master)
- 環境変数の設定
$ docker-machine env --swarm swarm-master export DOCKER_TLS_VERIFY="1" export DOCKER_HOST="tcp://104.155.223.102:3376" export DOCKER_CERT_PATH="/Users/setoguchi/.docker/machine/machines/swarm-master" export DOCKER_MACHINE_NAME="swarm-master" # Run this command to configure your shell: # eval "$(docker-machine env --swarm swarm-master)"
$ eval "$(docker-machine env --swarm swarm-master)"
docker info
でclusterの状態を確認できる
$ docker info Containers: 6 Images: 7 Role: primary Strategy: spread Filters: health, port, dependency, affinity, constraint Nodes: 2 swarm-agent: 104.155.224.60:2376 └ Status: Healthy └ Containers: 2 └ Reserved CPUs: 0 / 1 └ Reserved Memory: 0 B / 618.7 MiB └ Labels: executiondriver=native-0.2, kernelversion=3.19.0-28-generic, operatingsystem=Ubuntu 14.04.3 LTS, provider=google, storagedriver=aufs swarm-master: 104.155.223.102:2376 └ Status: Healthy └ Containers: 4 └ Reserved CPUs: 0 / 1 └ Reserved Memory: 0 B / 618.7 MiB └ Labels: executiondriver=native-0.2, kernelversion=3.19.0-28-generic, operatingsystem=Ubuntu 14.04.3 LTS, provider=google, storagedriver=aufs CPUs: 2 Total Memory: 1.208 GiB Name: 16a45c9e82db
- databaseを作成
$ docker build -t postgres ./postgres
- databaseを起動
$ docker run --rm --name mydb postgres
- web app 作成
$ docker build -t web ./web
- web app 起動(コンテナリンクを使う)
$ docker run --rm -p 80:8080 --name myapp --link mydb:db web
$ docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 0c6fd7fe6288 web "java -jar /admission" 13 minutes ago Up 13 minutes 104.155.223.102:80->8080/tcp swarm-master/myapp 364cca700777 postgres "/usr/lib/postgresql/" 15 minutes ago Up 15 minutes 5432/tcp swarm-master/myapp/db,swarm-master/mydb
同じノードで動いてる。 コンテナリンクを使うとclusterを組んでいても同じノードで実行される。
linkをやめて再度トライ。
$ docker run --rm -p 80:8080 --name myapp web
$ docker run --rm --name mydb postgres
docker ps CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 890d7dbe56e5 postgres "/docker-entrypoint.s" 20 seconds ago Up 20 seconds 5432/tcp swarm-agent/mydb b412bd500dc6 web "java -jar /admission" 48 seconds ago Up 47 seconds 104.155.223.102:80->8080/tcp swarm-master/myapp
別のノードで動いている。 が、これだと、コンテナ間の通信はできない。
マルチホストのコンテナ間での通信は
link
ではなく network
を使えば可能。
Spring Cloud
Josh Longのセッションをなぞってみた。
Config-Server
プロパティをアプリケーションの外に設定する。 gitリポジトリー(GitHub,Gitbucket...)にyml、もしくは propertiesファイルで設定できる。
git リポジトリーの作成
Josh のリポジトリーをcloneして使う。
$ git clone git@github.com:joshlong/bootiful-microservices-config.git Cloning into 'bootiful-microservices-config'... remote: Counting objects: 158, done. remote: Compressing objects: 100% (9/9), done.
config-server を作成
http://start.spring.io/で、Config-Serverにチェックして、Download。
DemoApplication.java
に@EnableConfigServer
を追加
@EnableConfigServer @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
application.properties
にconfig−serverがlistenするportと変数を定義するgitリポジトリを定義する
server.port=8888 spring.cloud.config.server.git.uri=${CONFIG_REPO:${HOME}/bootiful/bootiful-microservices-config}
起動してConfig-Serverはできあがり。
http://localhost:8888/reservation-service/masterにアクセスすると、gitに登録してある情報を参照できる。
http://localhost:8888/${file_name}/${branch_name} で他のファイルやブランチにアクセスできる。
$ ls
application-cloud.properties eureka-service.properties reservation-client.properties
application.properties hystrix-dashboard.properties reservation-service.properties
auth-service.properties reservation-client-github.properties
今回は8つのファイルを登録しているので、下記のURLでアクセスできる。 http://localhost:8888/application-cloud/master http://localhost:8888/eureka-service/master http://localhost:8888/reservation-client/master http://localhost:8888/application/master http://localhost:8888/hystrix-dashboard/master http://localhost:8888/reservation-service/master http://localhost:8888/auth-service/master http://localhost:8888/reservation-client-github/master
Config-Client
Reservation-Service
をConfig-Serverにする。
pom.xml
に依存関係を定義
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency>
MessageRestController
を作成する。
gitにコミットした値をすぐにInjectionして反映させるため、
@RefreshScope
アノテーションをつける。
@RefreshScope @RestController class MessageRestController { @Value("${message}") private String message; @RequestMapping("/message") String msg() { return this.message; } }
applicationが起動する前に設定を定義するため
bootstrap.properties
にconfig-server の場所と、アプリケーションの名前を定義する。
spring.cloud.config.uri=${CONFIG_SERVER:http://localhost:8888} spring.application.name=reservation-service
config-server (gitリポジトリー)には下記のように登録されているので、portは8000で起動し、 messageに"konnichiwa JSUG!!!"がインジェクションされる。
{ name: "/Users/setoguchi/bootiful/bootiful-microservices-config/reservation-service.properties", source: { server.port: "8000", spring.cloud.stream.bindings.input: "reservations", message: "konnichiwa JSUG!!!" } }
Reservation-service (config-client)を起動してアクセス。
reservation-service.properties
のmessageを変更
$ vi reservation-service.properties server.port=8000 message = Hello Config-Server! $ git commit -a -m update\ message [master dfe712c] update message 1 file changed, 1 insertion(+), 1 deletion(-)
config-serverにアクセスすると、変更が反映されているのがわかる。
Reservation-serviceにアクセスすると
変わっていない!?
アプリケーションに以下のコマンドで変更を知らせる必要がある。
$ curl -d {} http://localhost:8000/refresh ["message"]
無事変更が反映された。
変更を通知するアプリケーションがたくさんある場合、
/bus/refresh
とすればすべてに通知されるらしいけど、まだ試していない。
Eureka
サービス名でURLを検索できるようにするサービス。
eureka-server を作成
http://start.spring.io/で、Eureka-server,Config-Clientにチェックして、Download。 Config-ClientをチェックするのはEureka-Serverのportをconfig−server経由で設定するため。
bootstrap.properties
にconfig-serverの場所と、application名を定義
spring.cloud.config.uri=${CONFIG_SERVER:http://localhost:8888} spring.application.name=eureka-service
eureka-serverを起動して、http://localhost:8761/ にアクセスすると何も登録されていないのがわかる。
eureka-server に登録する。
pom.xml
に依存関係を定義する。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency>
DemoApplication.java
に @EnableDiscoveryClient
をつける
@EnableDiscoveryClient @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); }
これで起動する。起動後、30秒してから http://localhost:8761 にアクセスすると、
reservation-service
という名前で登録されているのがわかる。
reservation-clientの作成
reservation-service
を利用するreservation-client
を作成する。
pom.xml
に依存関係を追加。
feign
,zuul
,hystrix
はあとから使うので、追記しておく。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-zuul</artifactId> </dependency>
DiscoveryClient
DemoApplication.java
にアノテーションと、reservation-service
にアクセスする処理を追加
@EnableDiscoveryClient @SpringBootApplication public class DemoApplication { @Bean CommandLineRunner dc(DiscoveryClient dc) { return args -> dc.getInstances("reservation-service") .forEach(si -> System.out.println(String.format("%s %s:%s", si.getServiceId(), si.getHost(), si.getPort()))); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
起動すると、DiscoveryClient
が取得したinstanceが表示される。
RestTemplate
@Bean CommandLineRunner rt(RestTemplate rt) { return args -> { ParameterizedTypeReference<List<Reservation>> ptr = new ParameterizedTypeReference<List<Reservation>>() {}; ResponseEntity<List<Reservation>> response = rt.exchange("http://reservation-service/reservations", HttpMethod.GET, null, ptr); response.getBody().forEach(System.out::println); }; }
http://localhost:8000/reservations にアクセスして取得できるjsonをRestTemplate
でreservation-service
という名前で解決して取得した値を表示する。
Feign
@EnableFeignClients
を付与する。
@FeignClient("reservation-service")
をつけることで、Reservation-client
が、reservation-service
の振りをする(feign)ことができるようになる。
/reservations にアクセスすると、reservatin-serviceの/reservations にdispach される。
@EnableFeignClients @EnableDiscoveryClient @SpringBootApplication public class DemoApplication { @Bean CommandLineRunner fc(ReservationClient rc) { return args -> rc.getReservations().forEach(System.out::println); } public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } } @FeignClient("reservation-service") interface ReservationClient { @RequestMapping(method = RequestMethod.GET, value="/reservations") Collection<Reservation> getReservations();
Hystrix
Circuit Breaker Pattern を実装している。 www.sawanoboly.net martinfowler.com
簡単に言うと、障害が発生したときにその障害を拡大させず、代替手段でサービスを提供し続けるようにする。
今回の例ではreservaation-serviceはeurekaで何台あってもロードバランスしてアクセスできるが、1台も動いていないときに例外*1が発生する。 そんなときに、代替メソッドを実行できるようになる。
DemoApplication.java
に @EnableCircuitBreaker
アノテーションをつける。
@EnableFeignClients @EnableCircuitBreaker @EnableDiscoveryClient @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
ReservationIntegration
を作成する。
@HystrixCommand(fallbackMethod = "reservationNamesFallback")
でメソッド実行失敗時の代替のメソッドを指定する。
fallbackMethod も もともとのメソッドもpublic
でないと正常に動かない。
@Component class ReservationIntegration { @Autowired ReservationClient rc; public Collection<String> reservationNamesFallback() { return Collections.emptyList(); } @HystrixCommand(fallbackMethod = "reservationNamesFallback") public Collection<String> reservationNames() { return rc.getReservations().stream() .map(Reservation::getReservationName) .collect(Collectors.toList()); } }
reservation-client を起動する。reservation-serviceが動いている時は正常に値を返却する。
reservation-serviceを停止してアクセスすると、
fallbackMethodの結果のemptyList()が表示される。
初回失敗時は少し時間がかかるが、2回目以降はすぐに結果が返却される。reservation-serviceの状態を記憶していて、すぐにfallbackMethodがよばれるからである。 (circuit が open 状態になる。)
reservation-serviceを再起動してしばらくすると、正常にもどる。 (circuit が close 状態にもどる。)
HystrixDashboard
http://start.spring.io/でhystrix-dashboard,eureka,config-clientにチェックをいれてダウンロード。
pom.xml
はこんな感じ。
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-config</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency>
DemoApplication.java
に@EnableHystrixDashboard
をつける。
@EnableHystrixDashboard @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
bootstrap.properties
を同じように作成。
spring.cloud.config.uri=${CONFIG_SERVER:http://localhost:8888} spring.application.name=hystrix-dashboard
Hystrix-dashboard アプリケーションを起動する。
http://localhost:9999/hystrix.stream にアクセスするとエンドレスで流れる。
http://localhost:9999/hystrix.stream をモニタリングできる。
連続してアクセスするとこんな感じ。
Zuul
バックエンドサービスをUIから呼び出す際のproxy。
reservation-client の application.yml
に設定
zuul: routes: reservation-service: /myservice/**
reservation-service
はeurekaに登録したapplication名で、/myservice/** というURL でアクセスしてきたリクエストをreservation-service
に転送する。
DemoApplication.java
に@EnableZuulProxy
アノテーションをつける。
@EnableFeignClients @EnableCircuitBreaker @EnableZuulProxy @EnableDiscoveryClient @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } }
起動する。
2015-09-24 10:27:18.741 INFO 7345 --- [ main] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/myservice/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController] 2015-09-24 10:27:18.741 INFO 7345 --- [ main] o.s.c.n.zuul.web.ZuulHandlerMapping : Mapped URL path [/reservation-service/**] onto handler of type [class org.springframework.cloud.netflix.zuul.web.ZuulController]
/myservice/** をマッピングしたログが出力される。
http://localhost:9999/myservice/reservationsにアクセスするとreservation-service(バックエンド)のproxyとして機能していることがわかる。
*1:cannot connect to reservation-service
Spring Boot
Josh のセッションをなぞる。
http://start.spring.io/でWeb,JPA,Thymeleaf,H2,Actuator,Remote ShellにチェックをいれてDownload
pom.xml
はこんな感じ。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-rest</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-remote-shell</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.h2database</groupId> <artifactId>h2</artifactId> <scope>runtime</scope> </dependency>
Reservation
クラスを追加
@Entity class Reservation { @Id @GeneratedValue private Long id; private String reservationName; @Override public String toString() { return "Reservation{" + "id=" + id + ", reservationName='" + reservationName + '\'' + '}'; } Reservation() { } public Reservation(String reservationName) { this.reservationName = reservationName; } public Long getId() { return id; } public String getReservationName() { return reservationName; } }
ReservationRepository
インターフェースを追加
@RepositoryRestResource interface ReservationRepository extends JpaRepository<Reservation, Long> { //select * from reservations where reservation_name = :rn Collection<Reservation> findByReservationName(@Param("rn") String rn); }
DemoApplication
クラスにCommandLineRunner
を追加。起動時に実行される。
@Bean CommandLineRunner runner(ReservationRepository repository) { return args -> { Arrays.asList("Josh,Julie,Michael,Peter".split(",")) .forEach(n -> repository.save(new Reservation(n))); repository.findByReservationName("Julie").forEach(System.out::println); repository.findAll().forEach(System.out::println); }; }
実行する。 H2に登録されていることがわかる。
RestController
の追加
@RestController class ReservationRestController { @Autowired private ReservationRepository reservationRepository; @RequestMapping("/reservations") Collection<Reservation> reservations() { return reservationRepository.findAll(); } }
実行する。
MvcController
の追加
@Controller class ReservationMvcController { @Autowired ReservationRepository reservationRepository; @RequestMapping("/reservations.php") String page(Model model) { model.addAttribute("reservations", reservationRepository.findAll()); return "reservations"; } }
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org" > <head> <meta http-equiv="content-type" content="text/html; charset=UTF-8"/> <title>Bootiful Reservations</title> </head> <body> <h1>Reservations</h1> <div th:each="r : ${reservations}" > <b th:text="${r.id}">ID</b> <span th:text="${r.reservationName}">ReservationName</span> </div> </body> </html>
実行する。
Actuator
http://localhost:8080/metrics にアクセスすると、何回アクセスがあったのか等の情報を得ることができる。
http://localhost:8080/traceにアクセス。
http://localhost:8080/mappings
jconsole
jconsole で見ることもできる
$ jconsole
"insecure connect"をクリックして
jmc
$ jmc
Remote Shell
コンソールにShellでアクセスする場合のpasswordが出力されている。
$ $ ssh -p 2000 user@127.0.0.1 Password authentication Password: . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.2.6.RELEASE) on phantom.local > help Try one of these commands with the -h or --help switch: NAME DESCRIPTION autoconfig Display auto configuration report from ApplicationContext beans Display beans in ApplicationContext cron manages the cron plugin dashboard a monitoring dashboard egrep search file(s) for lines that match a pattern endpoint Invoke actuator endpoints env display the term env filter a filter for a stream of map java various java language commands jmx Java Management Extensions jul java.util.logging commands jvm JVM informations less opposite of more mail interact with emails man format and display the on-line manual pages metrics Display metrics provided by Spring Boot shell shell related command sleep sleep for some time sort sort a map system vm system properties commands thread JVM thread commands help provides basic help repl list the repl or change the current repl >
> endpoint list requestMappingEndpoint environmentEndpoint healthEndpoint beansEndpoint infoEndpoint metricsEndpoint traceEndpoint dumpEndpoint autoConfigurationAuditEndpoint configurationPropertiesReportEndpoint > endpoint invoke healthEndpoint {status=UP, diskSpace={status=UP, free=39115100160, threshold=10485760}, db={status=UP, database=H2, hello=1}} >
> metrics
> dashboard
HealthIndicator
を追加
@Bean HealthIndicator gotoHealthIndecator() { return () -> Health.status("I <3 Chicago!").build(); }
application.properties
に設定を追加
server.port=8000 management.context-path=/admin management.port=9000
http://localhost:8000/reservations
http://localhost:9000/admin/health
make jar not war
$ mvn clean install $ java -jar demo-0.0.1-SNAPSHOT.jar $ export SERVER_PORT=8010 $ java -Dserver.port=8020 -jar demo-0.0.1.SNAPSHOT.jar
書籍の電子化(断捨離)
最近、本をカバンに入れて持ち歩くのが辛くなってきたので、
Kindleを買ったついでに電子化してみた。
気になったときにいつでも読めるようにするのが最終目標。
スキャン業者選び
スキャン業者を検索していると、どこも早くて1ヶ月〜長いところだと3ヶ月納品まで時間がかかることがわかる。
あと、多くの業者が350ページで1冊分として、何冊分になるか数えて、注文するスタイル。
数えるのがとってもメンドくさいので、何ページでも1冊150円の電天というところに注文。
電天 スキャン代行サービス
注文
オーダーサイトにしたがって入力し完了ボタンを押すだけ。今回は36冊注文した。
注文後、クレジットカードで決済し、ダンボールに本を詰めて指定された住所に送付する。
「本が届いた」というメールを受け取ってから1週間で「電子化が完了した」というメールを受け取った。
納品
サイトでは18営業日となっていたが、ずいぶん早かったなと思いつつダウンロードする。
CentOS7 minimal で Kubernetes ~part 5~
GoogleCloudPlatformでGuestBookを動かしてみる
(もうCentOSじゃありません。)
gcp に container cluseter を作成
$ gcloud beta container clusters create guestbook --num-nodes 3 Creating cluster guestbook...done. Created [https://container.googleapis.com/v1/projects/kube-1043/zones/asia-east1-b/clusters/guestbook]. kubeconfig entry generated for guestbook. NAME ZONE MASTER_VERSION MASTER_IP MACHINE_TYPE STATUS guestbook asia-east1-b 1.0.3 130.211.246.179 n1-standard-1 RUNNING
クラスタを確認する。
$ gcloud beta container clusters list
NAME ZONE MASTER_VERSION MASTER_IP MACHINE_TYPE STATUS
guestbook asia-east1-b 1.0.3 130.211.246.179 n1-standard-1 RUNNING
nodeの確認
$ kubectl get nodes NAME LABELS STATUS gke-guestbook-32b71c9c-node-2sd6 kubernetes.io/hostname=gke-guestbook-32b71c9c-node-2sd6 Ready gke-guestbook-32b71c9c-node-88j1 kubernetes.io/hostname=gke-guestbook-32b71c9c-node-88j1 Ready gke-guestbook-32b71c9c-node-kgjq kubernetes.io/hostname=gke-guestbook-32b71c9c-node-kgjq Ready
service はまだない。(kubernetesのサービスのみ)
$ kubectl get services NAME LABELS SELECTOR IP(S) PORT(S) kubernetes component=apiserver,provider=kubernetes <none> 10.215.240.1 443/TCP
redis のコントローラーを作成
$ kubectl create -f redis-master-controller.json
replicationcontrollers/redis-master
pod の確認
$ kubectl get -o wide pods NAME READY STATUS RESTARTS AGE NODE redis-master-x8dsk 1/1 Running 0 2m gke-guestbook-32b71c9c-node-2sd6
redis のサービス作成
$ kubectl create -f redis-master-service.json
services/redis-master
サービス確認
$ kubectl get service -l name=redis-master NAME LABELS SELECTOR IP(S) PORT(S) redis-master name=redis-master name=redis-master 10.215.243.107 6379/TCP
コントローラー作成
$ kubectl create -f redis-worker-controller.json
replicationcontrollers/redis-slave
サービス作成
$ kubectl create -f redis-worker-service.json
services/redis-slave
frontend のコントローラー作成
$ kubectl create -f frontend-controller.json
replicationcontrollers/frontend
サービス作成
$ kubectl create -f frontend-service.json
services/frontend
サービス確認
$ kubectl get services NAME LABELS SELECTOR IP(S) PORT(S) frontend name=frontend name=frontend 10.215.253.56 80/TCP kubernetes component=apiserver,provider=kubernetes <none> 10.215.240.1 443/TCP redis-master name=redis-master name=redis-master 10.215.243.107 6379/TCP redis-slave name=redis-slave name=redis-slave 10.215.248.252 6379/TCP
ロードバランサーのIPアドレス確認
$ kubectl describe services frontend | grep "LoadBalancer Ingress" LoadBalancer Ingress: 130.211.249.217
サービスを確認してもロードバランサーのIPアドレスが表示される
$ kubectl get services NAME LABELS SELECTOR IP(S) PORT(S) frontend name=frontend name=frontend 10.215.253.56 80/TCP 130.211.249.217 kubernetes component=apiserver,provider=kubernetes10.215.240.1 443/TCP redis-master name=redis-master name=redis-master 10.215.243.107 6379/TCP redis-slave name=redis-slave name=redis-slave 10.215.248.252 6379/TCP
ブラウザでアクセス
http://130.211.249.217
Kubernetes のweb ui にアクセスしてみる
ID/PWが必要なので確認する。
$ gcloud beta container clusters list --zone asia-east1-b NAME ZONE MASTER_VERSION MASTER_IP MACHINE_TYPE STATUS guestbook asia-east1-b 1.0.3 130.211.246.179 n1-standard-1 RUNNING $ $ gcloud beta container clusters describe guestbook clusterIpv4Cidr: 10.212.0.0/14 createTime: '2015-08-21T19:59:51+00:00' currentMasterVersion: 1.0.3 currentNodeVersion: 1.0.3 endpoint: 130.211.246.179 initialClusterVersion: 1.0.3 initialNodeCount: 3 instanceGroupUrls: - https://www.googleapis.com/replicapool/v1beta2/projects/kube-1043/zones/asia-east1-b/instanceGroupManagers/gke-guestbook-32b71c9c-group loggingService: logging.googleapis.com masterAuth: clientCertificate: LS0t…S0tLQo= clientKey: LS0tS…S0tLQo= clusterCaCertificate: LS0t…LS0K password: 'password' username: 'username' monitoringService: monitoring.googleapis.com name: guestbook network: default nodeConfig: diskSizeGb: 100 machineType: n1-standard-1 oauthScopes: - https://www.googleapis.com/auth/compute - https://www.googleapis.com/auth/devstorage.read_only - https://www.googleapis.com/auth/logging.write - https://www.googleapis.com/auth/monitoring nodeIpv4CidrSize: 24 selfLink: https://container.googleapis.com/v1/projects/162560360030/zones/asia-east1-b/clusters/guestbook servicesIpv4Cidr: 10.215.240.0/20 status: RUNNING zone: asia-east1-b
https://130.211.246.179/ui
にアクセスすると
https://130.211.246.179/api/v1/proxy/namespaces/kube-system/services/kube-ui/#/dashboard/
にリダイレクトされて
CentOS7 minimal で Kubernetes ~part 4~
GuestBookを試す。github.com
redis master を起動
redis-master-controller.yamlの作成
apiVersion: v1 kind: ReplicationController metadata: name: redis-master labels: name: redis-master spec: replicas: 1 selector: name: redis-master template: metadata: labels: name: redis-master spec: containers: - name: master image: redis ports: - containerPort: 6379
controller 起動
$ kubectl create -f redis-master-controller.yaml
replication controller の確認
$ kubectl get rc CONTROLLER CONTAINER(S) IMAGE(S) SELECTOR REPLICAS redis-master master redis name=redis-master 1
pod の確認
$ kubectl get pods NAME READY STATUS RESTARTS AGE redis-master-n2tok 1/1 Running 0 46m
redis slave の起動
read only のredisを作成する
redis-slave-controller.yaml の作成
apiVersion: v1 kind: ReplicationController metadata: name: redis-slave labels: name: redis-slave spec: replicas: 2 selector: name: redis-slave template: metadata: labels: name: redis-slave spec: containers: - name: worker image: kubernetes/redis-slave:v2 ports: - containerPort: 6379
controller 起動
$ kubectl create -f redis-slave-controller.yaml
redis master,slave のserviceを起動
redis-master-service.yaml の作成
apiVersion: v1 kind: Service metadata: name: redis-master labels: name: redis-master spec: ports: # the port that this service should serve on - port: 6379 targetPort: 6379 selector: name: redis-master
service起動
$ kubectl create -f redis-master-service.yaml
redis-slave-service.yaml の作成
apiVersion: v1 kind: Service metadata: name: redis-slave labels: name: redis-slave spec: ports: # the port that this service should serve on - port: 6379 selector: name: redis-slave
service起動
$ kubectl create -f redis-slave-service.yaml
Frontend の pod 作成
frontend-controller.yaml の作成
apiVersion: v1 kind: ReplicationController metadata: name: frontend labels: name: frontend spec: replicas: 3 selector: name: frontend template: metadata: labels: name: frontend spec: containers: - name: php-redis image: kubernetes/example-guestbook-php-redis:v2 ports: - containerPort: 80
$ kubectl create -f frontend-controller.yaml
frontend-service.yaml の作成
今回はLoadBalancerがないので、
外部ネットワークに公開するために
type: NodePort
を指定
Services in Kubernetesを参考に。
apiVersion: v1 kind: Service metadata: name: frontend labels: name: frontend spec: # if your cluster supports it, uncomment the following to automatically create # an external load-balanced IP for the frontend service. # type: LoadBalancer type: NodePort ports: # the port that this service should serve on - port: 80 targetport: 80 protocol: TCP selector: name: frontend
Frontend Service の起動
$ kubectl create -f frontend-service.yaml You have exposed your service on an external port on all nodes in your cluster. If you want to expose this service to the external internet, you may need to set up firewall rules for the service port(s) (tcp:30621) to serve traffic. See http://releases.k8s.io/HEAD/docs/user-guide/services-firewalls.md for more details. services/frontend