garbagetown

個人の日記です

Docker と Redis と Spring

TL; DR

  • 職場のウインドーズでも spring-boot したい
    • spring-security したい
    • spring-session したい
  • ウインドーズに redis を入れる気はないので docker で入れる
    • docker-machine 便利

Docker Toolbox

下記 URL を参考に Docker Toolbox をインストールする。インストーラをダウンロードしてデフォルトのまま Enter を連打すればいい。

Docker

公式ドキュメントのメンテナンスが追い付いていないようで、書いてある通りにやってもうまくいかない、と言うかそもそも書いてある通りに実行できないので、勘と経験と度胸で進める。

まずは docker ホストを作る。ウインドーズはネイティブで docker ホストを実行できないので、Docker Toolbox に含まれる VirtualBox 上に docker ホストを立てる。この作業を簡単にしてくれるのが docker-machine コマンドで、例えば dev という名前の docker ホストは次のようにして作る。

c:\TEMP>docker-machine create --driver virtualbox dev
Running pre-create checks...
(dev) Default Boot2Docker ISO is out-of-date, downloading the latest release...
(dev) Latest release for github.com/boot2docker/boot2docker is v1.9.1
(dev) Downloading C:\Users\yu-umezawa\.docker\machine\cache\boot2docker.iso from https://github.com/boot2docker/boot2doc
ker/releases/download/v1.9.1/boot2docker.iso...
(dev) 0%....10%....20%....30%....40%....50%....60%....70%....80%....90%....100%
Creating machine...
(dev) Copying C:\Users\yu-umezawa\.docker\machine\cache\boot2docker.iso to C:\Users\yu-umezawa\.docker\machine\machines\
dev\boot2docker.iso...
(dev) Creating VirtualBox VM...
(dev) Creating SSH key...
(dev) Starting VM...
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 with boot2docker...
Copying certs to the local machine directory...
Copying certs to the remote machine...
Setting Docker configuration on the remote daemon...
Checking connection to Docker...
Docker is up and running!
To see how to connect Docker to this machine, run: docker-machine env dev

docker-machine ls で docker ホストを一覧できる。

c:\TEMP>docker-machine ls
NAME   ACTIVE   DRIVER       STATE     URL                         SWARM   DOCKER   ERRORS
dev    -        virtualbox   Running   tcp://192.168.99.100:2376           v1.9.1

作成した docker ホストに対して docker コマンドを実行するために、docker コマンドが参照する環境変数に docker ホストの情報を設定する。docker-machine env コマンドで設定すべき環境変数を確認できるので、出力された通りに設定する。

c:\TEMP>docker-machine env --shell cmd dev
SET DOCKER_TLS_VERIFY=1
SET DOCKER_HOST=tcp://192.168.99.100:2376
SET DOCKER_CERT_PATH=C:\Users\yu-umezawa\.docker\machine\machines\dev
SET DOCKER_MACHINE_NAME=dev
REM Run this command to configure your shell:
REM     FOR /f "tokens=*" %i IN ('docker-machine env --shell cmd dev') DO %i

c:\TEMP>SET DOCKER_TLS_VERIFY=1
c:\TEMP>SET DOCKER_HOST=tcp://192.168.99.100:2376
c:\TEMP>SET DOCKER_CERT_PATH=C:\Users\yu-umezawa\.docker\machine\machines\dev
c:\TEMP>SET DOCKER_MACHINE_NAME=dev

お約束の Hello World が正常に実行できることを確認する。

c:\TEMP>docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world

b901d36b6f2f: Pull complete
0a6ba66e537a: Pull complete
Digest: sha256:8be990ef2aeb16dbcb9271ddfe2610fa6658d13f6dfb8bc72074cc1ca36966a7
Status: Downloaded newer image for hello-world:latest

Hello from Docker.
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker Hub account:
 https://hub.docker.com

For more examples and ideas, visit:
 https://docs.docker.com/userguide/

Redis

docker の準備が整ったので、docker ホスト上で redis を実行するコンテナを起動する。指定しているオプションの詳細は下記の通り。

  • --name でコンテナに任意の名前を付ける
  • -d でデーモンとして起動する
  • -p で docker ホストの 6379 番ポートに対するアクセスを redis コンテナの同じく 6379 番ポートにフォワードする
c:\TEMP>docker run --name redis -d -p 6379:6379 redis
Unable to find image 'redis:latest' locally
latest: Pulling from library/redis

9ee13ca3b908: Pull complete
23cb15b0fcec: Pull complete
52a374d9c478: Pull complete
(snip)
Digest: sha256:7dfb86a89af97d915fbde39a91c87a0bf55d76f9fc979b89bfa05a5671f851c2
Status: Downloaded newer image for redis:latest
4101734092f09f3057433e02fdef26306c6ab501d602be2b7efc793f0ec48815

docker ps で docker コンテナを一覧できる。

c:\TEMP>docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
4101734092f0        redis               "/entrypoint.sh redis"   6 seconds ago       Up 5 seconds        0.0.0.0:6379->6379/tcp   redis

Spring

web, security, session, redis を追加した Spring Boot アプリケーションを作る。急にコマンドプロンプトではなく cygwin で作業しているが気にしない。

$ mkdir test && cd $_

$ curl https://start.spring.io/starter.tgz -d style=web -d style=security -d style=session -d style=redis -d name=test | tar -zxvf -
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100 49996  100 49936  100    60  38051     45  0:00:01  0:00:01 --:--:-- 42318
mvnw
.mvn/
.mvn/wrapper/
src/
src/main/
src/main/java/
src/main/java/com/
src/main/java/com/example/
src/main/resources/
src/main/resources/static/
src/main/resources/templates/
src/test/
src/test/java/
src/test/java/com/
src/test/java/com/example/
.mvn/wrapper/maven-wrapper.jar
.mvn/wrapper/maven-wrapper.properties
mvnw.cmd
pom.xml
src/main/java/com/example/TestApplication.java
src/main/resources/application.properties
src/test/java/com/example/TestApplicationTests.java

TestApplication にいろいろ追加してセッション情報を Redis で管理する Web アプリケーションにする。

package com.example;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.session.data.redis.config.annotation.web.http.EnableRedisHttpSession;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
@EnableRedisHttpSession
public class TestApplication extends WebSecurityConfigurerAdapter {

    @RequestMapping("/")
    public String index() {
        return "Hello, Spring Boot!";
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.httpBasic().and().authorizeRequests().anyRequest().authenticated();
    }

    public static void main(String[] args) {
        SpringApplication.run(TestApplication.class, args);
    }
}

application.properties にデフォルトパスワードと docker ホストのアドレスを設定する。

security.user.password=password
spring.redis.host=192.168.99.100

アプリケーションが正常に起動することを確認する。Redis に繋がらないと起動時点で例外が発生してコケる。何度もコケた。

$ mvn spring-boot:run
[INFO] Scanning for projects...
[INFO]
[INFO] ------------------------------------------------------------------------
[INFO] Building test 0.0.1-SNAPSHOT
[INFO] ------------------------------------------------------------------------
(snip)
[INFO] --- spring-boot-maven-plugin:1.3.1.RELEASE:run (default-cli) @ demo ---

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v1.3.1.RELEASE)
(snip)
2016-01-05 15:58:15.195  INFO 7440 --- [           main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http)
2016-01-05 15:58:15.201  INFO 7440 --- [           main] com.example.TestApplication              : Started TestApplication in 2.838 seconds (JVM running for 5.199)

localhost:8080 にアクセスすると spring-security の機能で Basic 認証ダイアログが表示されるので、ユーザ名に user を、パスワードに application.properties に指定した password を入力する。

これでセッション情報が Redis に書き込まれるので、docker exec コマンドで redis コンテナに入って確認する。また急にコマンドプロンプトで作業しているが気にしない。

c:\TEMP>docker exec -it redis bash
root@4101734092f0:/data# redis-cli

127.0.0.1:6379> keys *
1) "spring:session:expirations:1451979000000"
2) "spring:session:sessions:25c0619f-1dbf-422e-bd8e-85c9650f1073"
3) "spring:session:sessions:e27ff8dc-a3cf-4bc6-9093-b506600cc3bb"

セッション情報が二つあるのは、未認証状態で favicon を取得した時のものと、認証後にアクセスした時のものっぽい。ひとまずここまででやりたいことは大体できた。

2016/01/20 修正。セッション情報が二つあるのは、セッション偽装対策のために認証前後で JSESSIONID が変わるためだった。

おまけ

flushdb で redis 上のデータを削除することができる。

127.0.0.1:6379> flushdb
OK
127.0.0.1:6379> keys *
(empty list or set)