å ¨å½ã® nginx è·äººã®ã¿ãªãã¾ãããã«ã¡ã¯ãé島(@nojima)ã§ãã
ç§ã®æå±ããYakumoããã¸ã§ã¯ãã§ã¯ãnginx ã Go 㨠Docker ã«ãã£ã¦ã¦ããããã¹ã1ãã¦ãã¾ãã æå ã§ç°¡åã«å®è¡ã§ãããã©ã³ãã¸ã®pushã®ãã³ã«CIã§ãã¹ããããã®ã§ãé常ã«ä¾¿å©ã§ãã ãã®è¨äºã§ã¯ããã®nginxã®ã¦ããããã¹ãã«ã¤ãã¦ç´¹ä»ãã¦ã¿ããã¨æãã¾ãã
èæ¯
nginx ã¯æ¥µãã¦æè»ãªãã¼ããã©ã³ãµã§ããããããã¯ã·ã§ã³ç°å¢ã§ã¯ãã®æè»ããçããã¦å¤å½©ãªå½¹å²ãæ ã£ã¦ãã¾ãã æã ã® nginx ã¯ãã¦ã¼ã¶ã¼ããã®ãªã¯ã¨ã¹ãã AP ãµã¼ãã¼ã«æ¯ãåããã¢ã¯ã»ã¹å¶éãè¡ãããªã¯ã¨ã¹ãããªãã¤ã¬ã¯ãããHTTPããããä»ä¸ãããåã£ãããã¦ãã¾ãã ããããnginx ã¯ä¾¿å©ãªåé¢ããã®è¨å®ã¯æ¥µãã¦è¤éã«ãªããèªè§£ãããå¤æ´ãããããã®ãé£ãããªã£ã¦ãã¾ãã ããã§ãnginx ãã¦ããããã¹ãããä»çµã¿ ã Go 㨠Docker ã§ä½ãã¾ããã
ã¦ããããã¹ããå°å ¥ããåã¯ãnginx ã®åä½ç¢ºèªã«ã¯ãã¼ã¿ã»ã³ã¿ã¼ã¸ã®ãããã¤ãå¿ è¦ã§ããã ãããã¤ã«ã¯çµæ§æéããããã®ã§ãåä½ç¢ºèªã¯ã¨ã¦ãé¢åã§ããã
ã¦ããããã¹ãã®å°å ¥ã«ããåä½ç¢ºèªã¯é常ã«å¹çåããã¾ããã ã¦ããããã¹ãã¯ä»ã®ãµã¼ãã¼ã«ä¾åãã¦ããªãã®ã§ããã¼ã«ã«ç°å¢ãCIç°å¢ã§ãå®è¡ã§ãã¾ãã ãã£ã¦ããã¼ã¿ã»ã³ã¿ã¼ã«ãããã¤ãããã¨ãªãæå ã§ãµã¯ãã¨åä½ã確èªã§ãã¾ããããããã¯ãã©ã³ãã CI ã§å¸¸ã«ãã¹ããã¦ãããã¨ãã§ãã¾ãã
ãã¹ãã¯ãã¹ã¦ Docker ã®å å´ã§è¡ãããã®ã§ããã¼ã«ã«ç°å¢ã«ç¹æ®ãªã»ããã¢ããããã¦ããå¿ è¦ã¯ããã¾ããã Docker ãã¤ã³ã¹ãã¼ã«ããã¦ããã°åãã¾ãã
ãµã³ãã«ã³ã¼ã
ãã®è¨äºã®ããã«èª¬æç¨ã®ãµã³ãã«ã³ã¼ããç¨æãã¾ããã
ãã¼ã«ã«ã« clone ãã¦ã㦠make test
ããã¨ãã¹ããå®è¡ã§ãã¾ãã
https://github.com/cybozu/SAMPLE-test-nginx-with-go-and-docker
ãã¹ã対象㮠nginx
ãµã³ãã«ã³ã¼ãã§ã¯ããã¹ã対象ã¨ãªã nginx ã¯ä»¥ä¸ã®ãããªè¨å®ã«ãªã£ã¦ãã¾ãï¼ä¸é¨æç²ï¼ã APãµã¼ãã¼ã«ãªãããããã¨ã³ããã¤ã³ããã常㫠400 ãè¿ãã¨ã³ããã¤ã³ããªã©ãããã¾ãã
server { listen 80; location / { proxy_pass http://${AP_SERVER_ADDR}; ... } location /secret/ { deny all; } ... }
注ç®ãã¦ã»ããã®ã¯ãproxy_pass
ã®é¨åã§ãã
APãµã¼ãã¼ã®ã¢ãã¬ã¹ãç´æ¥è¨å®ãã¡ã¤ã«ã«åãè¾¼ãã®ã§ã¯ãªããAP_SERVER_ADDR
ã¨ããç°å¢å¤æ°ã«åãåºãã¦ãã¾ãã
ããã¯ããã¹ãããéã«APãµã¼ãã¼ãã¢ãã¯ãµã¼ãã¼ã«ç½®ãæããå¿
è¦ãããããã§ãã
åãåºããç°å¢å¤æ°ã¯ãã³ã³ããèµ·åæã« envsubst ã§å ·ä½çãªå¤ã«å±éãã¾ãã ãµã³ãã«ã³ã¼ãã§ã¯ entrypoint ã¨ããã·ã§ã«ã¹ã¯ãªããããã®ä½æ¥ããã£ã¦ãã¾ãã
ãµã³ãã«ã³ã¼ãã§ã¯ç°å¢å¤æ°ã¯ AP_SERVER_ADDR
ã®ã¿ã§ãããããã¹ãç°å¢ã¨å®ç°å¢ã§å¤ãå¤ããè¨å®é
ç®ãããã°ãã¹ã¦ç°å¢å¤æ°ã«åãåºãã¦ããã¾ãã
ä¾ãã°ãresolver
ã使ã£ã¦ããå ´åããã¹ãç°å¢ã§ã¯ Docker ã® DNS ãµã¼ãã¼ã§ãã 127.0.0.11
ãæå®ããªãã¨ãããªãã®ã§ãç°å¢å¤æ°ã«åãåºãã¾ãã
ãã¹ãã®æ¦è¦³
ãã¹ãã³ã¼ãã®èª¬æã«å ¥ãåã«ããã¹ãã®æ¦è¦³ãå³ã使ã£ã¦èª¬æãã¾ãã ãã¼ã«ã«ç°å¢ã§ã CircleCI ç°å¢ã§ãå®è¡ã§ããããã«ããããã«ããã¹ãã¯ä»¥ä¸ã®ãããªæ§æã«ãªã£ã¦ãã¾ãã
太ãéæ ã§å²ãããé¨åã Docker ã³ã³ããã表ãã¦ãã¾ããnginx-tester 㨠nginx ã¨ããï¼ç¨®é¡ã®ã³ã³ãããããã¾ãã
nginx-tester
go test -v ./...
ãå®è¡ããã³ã³ããã§ãã- ãã¹ãã®å®è¡ã«ã¯ Go 㨠Docker ãå¿
è¦ãªã®ã§ãnginx-tester ã®ã¤ã¡ã¼ã¸ã«ã¯
circleci/golang:1.14
ã使ã£ã¦ãã¾ãã - nginx-tester ã¯å¿ è¦ã«å¿ã㦠nginx ã³ã³ãããèµ·åãã¾ãã
- ã³ã³ããã®ä¸ããå¥ã®ã³ã³ãããèµ·åããããã« Docker outside of Docker ã®æ§æãåã£ã¦ãã¾ããã¤ã¾ãããã¹ãã®
/var/run/docker.sock
ãã³ã³ããå ã«ãã¦ã³ããããã¨ã§ãã³ã³ãããããã¹ãã® docker ãæä½ã§ããããã«ãã¦ãã¾ãã
nginx-xxxxxx
- ãã¹ã対象ã¨ãªã nginx ãæ ¼ç´ãã¦ããã³ã³ããã§ãã
- ã³ã³ããåã®
-xxxxxx
ã®é¨åã¯å®éã«ã¯ã©ã³ãã ãªæååã§ããããã¯ãåæã«è¤æ°åèµ·åããã¨ãã«ååã被ãã®ãé²ãããã§ãã
nginx-tester 㨠nginx ãç¸äºã«éä¿¡ã§ããããã«ããããã«ãnginx ã³ã³ãã㨠nginx-tester ã¯åã docker network ã«æå±ãã¦ãã¾ãã ãã® docker network ã¯ãã¹ãã®èµ·ååã«ã·ã§ã«ã¹ã¯ãªããã§ä½æãã¦ããã¾ãã
AP ã®ã¢ãã¯ãµã¼ãã¼ã¯ç¬ç«ããã³ã³ããã§ã¯ãªããnginx-tester å ã® goroutine ã¨ãã¦èµ·åãã¾ããå³ã®ç ´ç·ã§å²ãããé¨åã goroutine ã表ãã¦ãã¾ãã
次㫠CircleCI ä¸ã§ã©ã®ããã«ãã¹ããå®è¡ãããã説æãã¾ãã
CircleCI ã§ã¯ãsetup_remote_docker
ã使ã£ã¦ãªã¢ã¼ãç°å¢ããããã¸ã§ã³ã§ãã¾ãã
ãã¹ãã¯ãã®ãªã¢ã¼ãç°å¢ã®ä¸ã§å®è¡ããã¾ãã
CircleCI ã§ãã®ãã¹ããèµ°ãããã«ããã£ã¦ã注æããªãã¨ãããªãã®ã¯æ¬¡ã®ï¼ç¹ã§ãã
- ãªã¢ã¼ãç°å¢ã¨ Primary Container (
config.yml
ã«æ¸ãããã¹ããããå®è¡ããã³ã³ãã)ã®éã§ã¯ãã»ãã¥ãªãã£ä¸ã®çç±ãããHTTP ã TCP ãªã©ã®éä¿¡ãè¡ãã¾ããããã£ã¦ããªã¢ã¼ãç°å¢ã¨ã®éä¿¡ã¯docker
ã³ãã³ããç¨ãããã®ã«éå®ããã¾ãã - ãªã¢ã¼ãç°å¢ã§ã¯ãPrimary Container ä¸ã®ãã¡ã¤ã«ã·ã¹ãã ãè¦ãã¾ãããã¤ã¾ãããªã¢ã¼ãç°å¢ããã¯ã½ã¼ã¹ã³ã¼ããè¦ããªãã¨ãããã¨ã§ãã
å®ã¯ãnginx-tester ããã¼ã«ã«ã§ç´æ¥å®è¡ããã«ããããã³ã³ããå ã§å®è¡ãã¦ããã®ã¯ã1 ã®åé¡ãåé¿ããããã§ããã
2 ã®åé¡ã«é¢ãã¦ã¯ CircleCI ã®å
¬å¼ããã¥ã¡ã³ãã«åé¿çãè¼ã£ã¦ãã¾ãã
以ä¸ã®æé ãè¡ããã¨ã§ãPrimary Container ãã nginx-tester
ã«ã½ã¼ã¹ã³ã¼ãã渡ããã¨ãã§ãã¾ãã
nginx-test-data
ã¨ããã空ã®ããªã¥ã¼ã ãæã¤ããã¼ã®ã³ã³ãããä½æãããdocker cp
ã§ã½ã¼ã¹ã³ã¼ããããã¼ã³ã³ããã«è»¢éãããdocker run
ã®--volumes-from
ãªãã·ã§ã³ã使ã£ã¦ãããã¼ã³ã³ããã®ããªã¥ã¼ã ããã¦ã³ãããã
ãã¦ããã¹ãã®æ¦è¦³ãããã£ãã¨ããã§å®éã®ãã¹ãã³ã¼ããè¦ã¦ããã¾ãããã ã¾ãã¯ããç°¡åãªãªããããªãã®å ´åããå§ãã¾ãã
ãã¹ãã³ã¼ã (ãªããããªã)
次ã®ãã¹ã㯠GET /secret/
㧠400 ãè¿ã£ã¦ãããã¨ã確èªãããã®ã§ãã
func TestSecretEndpoints(t *testing.T) { t.Parallel() nginx := StartNginx(t, NginxConfig{}) // â defer nginx.Close(t) nginx.Wait(t) resp, err := http.Get(nginx.URL() + "/secret/") // â¡ if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusForbidden { // ⢠t.Errorf("status code should be 400, but %d", resp.StatusCode) } }
â StartNginx()
㯠nginx ã³ã³ãããèµ·åããé¢æ°ã§ãã詳細ã¯å¾è¿°ãã¾ãã
次㫠nginx.Wait()
ã§èµ·åãå®äºããã¾ã§å¾
ã¡ã¾ãã
â¡ nginx ã«å¯¾ã㦠GET /secret/
ãè¡ãã¾ããhttp.Get()
ã¯åãªã Go ã®æ¨æºé¢æ°ã§ãã
⢠ã¬ã¹ãã³ã¹ã assert ãã¾ãããããæ®éã® Go ã®ãã¹ãã³ã¼ãã§ãã
åç¯ã§ã¯ Docker ã使ã£ã¦ãã¹ãããã¨èª¬æãã¾ããããDocker ã¯ãã®ãã¹ãã±ã¼ã¹ã®ã³ã¼ãããã¯å®å ¨ã«é è½ããã¦ãã¾ãã ããã¯ãDocker ã«ã¾ã¤ããè¤éãããã¹ãã±ã¼ã¹ããåé¢ãããã¨ã§ããã¹ãã±ã¼ã¹ãèªã¿ãããããããã§ã2ã
Docker ãå®éã«æä½ãã¦ããã®ã¯ StartNginx()
ã nginx.Close()
ãªã©ã®é¢æ°ã§ãã
ããã§ã¯ StartNginx()
ã®å®è£
ãè¦ã¦ããã¾ãããã
StartNginx()
StartNginx()
ã®èã¨ãªãé¨åã¯ä»¥ä¸ã®ã³ã¼ãã§ãããã®é¢æ°ã®ä¸»ãªä»äºã¯ docker ã³ãã³ããå©ããã¨ã§ãã
// docker ã³ãã³ããå©ã㦠sample-nginx:latest ãèµ·åããã args := []string{ "run", "--rm", "--name", name, "--net", network, "-e", fmt.Sprintf("AP_SERVER_ADDR=%s", config.APServerAddress), "sample-nginx:latest", } cmd := exec.Command("docker", args...) if err := cmd.Start(); err != nil { t.Fatal(err) }
ãã®ã³ã¼ãã§æ³¨ç®ãã¦ã»ããç¹ã¯ã-e
㧠AP_SERVER_ADDR
ã«å¤ã渡ãã¦ãããã¨ã§ãã
ããã«ãããä»»æã®APãµã¼ãã¼ãå·®ãè¾¼ã㧠nginx ãèµ·åã§ããããã§ãã
ããã§ã¯ãå®éã«APãµã¼ãã¼ãå·®ãè¾¼ããã¹ããè¦ã¦ããã¾ãããã
ãã¹ãã³ã¼ã (ãªããããã)
次ã®ãã¹ã㯠AP ãµã¼ãã¼ãã¢ãã¯ãã¦ãªãããã®æåã確èªãããã®ã§ãã
func TestReverseProxy(t *testing.T) { t.Parallel() ap := StartMockAP(t) // â defer ap.Close(t) nginx := StartNginx(t, NginxConfig{ // â¡ APServerAddress: ap.Address(), }) defer nginx.Close(t) nginx.Wait(t) resp, err := http.Get(nginx.URL() + "/") // ⢠if err != nil { t.Fatal(err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { t.Errorf("status code should be 200, but %d", resp.StatusCode) } body, err := ioutil.ReadAll(resp.Body) if err != nil { t.Fatal(err) } if string(body) != "I am AP server" { t.Errorf("unexpected response body: %s", string(body)) } }
â StartMockAP()
ã§ã¢ãã¯APãèµ·åãã¦ãã¾ãã
â¡ StartNginx()
㧠nginx ã³ã³ãããèµ·åãã¾ããããã§ã¢ãã¯APã®ã¢ãã¬ã¹ãå·®ãè¾¼ãã§ãããã¨ã«æ³¨ç®ãã¦ãã ããã
⢠AP 㨠nginx ãèµ·åã§ãããããã¨ã¯ããæ®éã®ãã¹ãã§ãããªã¯ã¨ã¹ããéããã¬ã¹ãã³ã¹ãæ®éã« assert ãã¾ãããã
MockAP ã Nginx ã«å·®ãè¾¼ãã§ããé¨å㯠OOP ã«ããã Dependency Injection ã«ç¸å½ãã¾ãã æ®éãDependecy Injection ã¯åãããã»ã¹å ã®ãªãã¸ã§ã¯ãã«å¯¾ãã¦è¡ãã®ã§ããããã®ä¾ã ã¨å¥ã®ããã»ã¹ã«å¯¾ãã¦ä¾åãªãã¸ã§ã¯ããå·®ãè¾¼ãã§ããå½¢ã«ãªã£ã¦ããã®ãé¢ç½ãã¨ããã§ãã
ããã§ã¯æå¾ã« StartMockAP()
ã®å®è£
ãè¦ã¦ã¿ã¾ãããã
StartMockAP()
StartMockAP()
ã¯ä»¥ä¸ã®ããã«ãªã£ã¦ãã¾ãï¼ä¸é¨èª¬æã«ä¸è¦ãªé¨åãçç¥ãã¦ãã¾ãï¼ã
ãã¼ããèªåçã«é¸ã¶ããã«ã¡ãã£ã¨ç¹æ®ãªãã¨ããã¦ãããã¨ãé¤ãã°ãåã« goroutine 㧠HTTP ãµã¼ãã¼ãç«ã¦ã¦ããã ãã§ãã
// 空ãã¦ãããã¼ããèªåçã«é¸ã¶ l, err := net.Listen("tcp", ":0") if err != nil { t.Fatal(err) } handler := func(w http.ResponseWriter, req *http.Request) { w.Write([]byte("I am AP server")) } ap := &MockAP{ host: host, port: l.Addr().(*net.TCPAddr).Port, server: &http.Server{ Handler: http.HandlerFunc(handler), }, } // å¥ã® goroutine ã§ãµã¼ãã¼ãèµ°ããã go func() { if err := ap.server.Serve(l); err != nil && err != http.ErrServerClosed { t.Log(err) } }()
ãªããæ¨æºã©ã¤ãã©ãªã® httptest
㧠StartMockAP
ã¨åããããªãã¨ãã§ãã¾ãããhttptest
ã¯ã¢ãã¬ã¹ã 127.0.0.1
ã«ãã¤ã³ããã¦ãã¾ãã®ã§ãä»åã®ã¦ã¼ã¹ã±ã¼ã¹ã§ã¯å©ç¨ã§ãã¾ããã
ä»åã®ãµã³ãã«ã³ã¼ãã§ã¯ä¸ç¨®é¡ã® MockAP ããå®è£ ããã¦ãã¾ããããç§éãå®éã«ä½¿ã£ã¦ãããã¹ãã§ã¯æ§ã 㪠MockAP ãå®è£ ããã¦ãã¾ãã ä¾ãã°ãã¬ã¹ãã³ã¹ãä¸åè¿ããªã MockAP ããä¸æ£ãª SSL 証ææ¸ãæ㤠MockAP ãªã©ãããã¾ãã ãããã® MockAP ã使ããã¨ã§ãæåã§èµ·ããã®ãé¢åãªã±ã¼ã¹ããã¹ããããã¨ãã§ãã¾ãã
ã¾ã¨ã
Go 㨠Docker ã使ã£ã¦ nginx ããã¹ãããä»çµã¿ãç´¹ä»ãã¾ããã ãã®ä»çµã¿ã¯ãæã ã®ãããã¯ã·ã§ã³ç°å¢ã«ããã nginx ãæ¯ãã¦ãã¾ãã
ãã®è¨äºãã¿ãªãã¾ã® nginx ã©ã¤ãã®ä¸å©ã¨ãªãã°å¹¸ãã§ãã
-
ããã§ã¯ nginx ãã²ã¨ã¤ã®ã¦ãããã¨ã¿ãªãã¦ãã¾ãï¼ã¦ããããã¹ãã«ããããã¦ãããããä½ãæããã¯å®ç¾©ã«ãã£ã¦ç°ãªã£ã¦ãããçµ±ä¸ããã¦ãã¾ããããã®è¨äºã§ã¯ nginx ãã²ã¨ã¤ã®ã¦ãããã¨ãªããããªå®ç¾©ãæ¡ç¨ããã¨è§£éãã¦ãã ããï¼ã↩
-
ãã¹ãã³ã¼ãã§ã¯é¢å¿ã®åé¢ãåä¸è²¬ä»»ã®ååãèãã«ããããã¨ãããããã¾ãããç§ã¯ãã¹ãã³ã¼ãã§ããããã®ååã¯éè¦ã ã¨æã£ã¦ãã¾ãã↩