建立MongoDB 虛擬環境

  • 使用 docker-compose.yaml
version: '3.3'
services:
  mongo:
    image: 'mongo:latest'
    container_name: 'mongo'
    ports:
      - '27100:27017'

啟動 docker

// -d 表示要demane執行
docker-compose up -d --build
// 或指定 docker-compose.yaml位置
docker-compose up -d --build -f ./docker-compose.yaml

查看執行結果

$ docker-compose ps
Name              Command             State            Ports
----------------------------------------------------------------------
mongo   docker-entrypoint.sh mongod   Up      0.0.0.0:27100->27017/tcp

結合Go當api

docker-compose.yaml

version: '3.3'
services:
  api:
    container_name: 'api'
    build: './api'
    ports:
      - '8080:8080'
    volumes:
      - './api:/go/src/app'
    depends_on:
      - 'mongo'
  mongo:
    image: 'mongo:latest'
    container_name: 'mongo'
    ports:
      - '27100:27017'

由於api 服務需透過Dockerfile被執行起來,因此,在api目錄下建立Dockerfile檔案:

FROM golang:1.8

RUN adduser --disabled-password --gecos '' api
USER api

WORKDIR /go/src/app
COPY . .

RUN go get github.com/pilu/fresh
RUN go-wrapper download
RUN go-wrapper install

CMD [ "fresh" ]
  • 在api目錄下建立 maon.go程式
package main

import (
    "encoding/json"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "time"
    "github.com/rs/cors"
    "github.com/gorilla/mux"
    "gopkg.in/mgo.v2"
)

type Post struct {
    Text      string    `json:"text" bson:"text"`
    CreatedAt time.Time `json:"createdAt" bson:"created_at"`
}

var posts *mgo.Collection

func main() {
    // Connect to mongo
    session, err := mgo.Dial("mongo:27017")
    if err != nil {
        log.Fatalln(err)
        log.Fatalln("mongo err")
        os.Exit(1)
    }
    defer session.Close()
    session.SetMode(mgo.Monotonic, true)
    // Get posts collection
    posts = session.DB("app").C("posts")

    // Set up routes
    r := mux.NewRouter()
    r.HandleFunc("/posts", createPost).Methods("POST")
    r.HandleFunc("/posts", readPosts).Methods("GET")
    http.ListenAndServe(":8080", cors.AllowAll().Handler(r))
    log.Println("Listening on port 8080...")
}

func createPost(w http.ResponseWriter, r *http.Request) {
    // Read body
    data, err := ioutil.ReadAll(r.Body)
    if err != nil {
        responseError(w, err.Error(), http.StatusBadRequest)
        return
    }
    // Read post
    post := &Post{}
    err = json.Unmarshal(data, post)
    if err != nil {
        responseError(w, err.Error(), http.StatusBadRequest)
        return
    }
    post.CreatedAt = time.Now().UTC()
    // Insert new post
    if err := posts.Insert(post); err != nil {
        responseError(w, err.Error(), http.StatusInternalServerError)
        return
    }
    responseJSON(w, post)
}

func readPosts(w http.ResponseWriter, r *http.Request) {
    result := []Post{}
    if err := posts.Find(nil).Sort("-created_at").All(&result); err != nil {
        responseError(w, err.Error(), http.StatusInternalServerError)
    } else {
        responseJSON(w, result)
    }
}

func responseError(w http.ResponseWriter, message string, code int) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(code)
    json.NewEncoder(w).Encode(map[string]string{"error": message})
}

func responseJSON(w http.ResponseWriter, data interface{}) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(data)
}

建立 container

docker-compose up -d --build

測試程式

curl localhost:8080/posts -d '{"text":"hello"}'    // POST
curl localhost:8080/posts  // GET

加入Nginx

在docker-compose.yaml中加入:

web:
  container_name: 'web'
  image: 'nginx:latest'
  ports:
    - '8081:80'
  volumes:
    - './web:/usr/share/nginx/html'
  depends_on:
    - 'api'

在 web 目錄下建立 Dockerfile

FROM nginx

RUN adduser --disabled-password --gecos '' web
USER web

COPY . /usr/share/nginx/html

在web目錄下建立 index.html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Dockerized Posts</title>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
  <h1>Dockerized Posts</h1>
  <form id="form">
    <input type="text" placeholder="New post..." id="post-input">
  </form>
  <div id="posts"></div>
  <script>
    $(document).ready(function() {
      $.get('http://localhost:8080/posts', function(posts) {
        $list = $('#posts');
        for (var i = 0; i < posts.length; i++) {
          $list.append('<p>' + posts[i].text + '</p>');
        }
      });
      $('#form').submit(function(event) {
        event.preventDefault();
        var text = $('#post-input').val();
        $.post('http://localhost:8080/posts', JSON.stringify({text: text}), function() {
          location.reload();
        });
      });
    });
  </script>
</body>
</html>

執行

docker-compose up -d --build

測試

如果api下有測試程式的話,ex: sanity_test.go

package main

import "testing"

func TestSanity(t *testing.T) {
    t.Log("Has sanity")
}

直接執行看看結果:

docker-compose run api go test -v
=== RUN   TestSanity
--- PASS: TestSanity (0.00s)
        sanity_test.go:6: Has sanity
PASS
ok      app     0.004s

完整的 docker-compose.yml

version: '3.3'
services:
  api:
    container_name: 'api'
    build: './api'
    ports:
      - '8080:8080'
    volumes:
      - './api:/go/src/app'
    depends_on:
      - 'mongo'
  web:
    container_name: 'web'
    image: 'nginx:latest'
    ports:
      - '8081:80'
    volumes:
      - './web:/usr/share/nginx/html'
    depends_on:
      - 'api'
  mongo:
    image: 'mongo:latest'
    container_name: 'mongo'
    ports:
      - '27100:27017'