Mam pytanie odnośnie tego w jaki sposób najczęściej się pracuje z Dockerem. Postanowiłem sobie, że poznam to narzędzie i stworzę WebApke. Załóżmy że tworzę projekt w katalogu home/go/src/myproject
, pobieram z docker huba obraz (akurat z golangiem), tworzę dockefile, w którym kopiuję katalog projektu COPY home/go/src/myproject : go/src/myproject
następnie tworzę image i odpalam kontener. Wszystko fajnie, tylko teraz gdy chcę wprowadzić zmiany w kodzie, muszę zatrzymać działanie kontenera, usunąć go, wprowadzić zmiany, z obrazu stworzyć nowy kontener i uruchomić. Nie wydaje się to być wygodnym rozwiązaniem. W zwiazku z tym pomyśałem, aby zrobić volume mapping i kod przechowywać na hoście i tylko w parametrze wywołania kontenera mapować odpowiednie ścieżki, np tak: docker run --name myapp -p 8000:8000 -d -v home/go/src/myproject:go/src/myproject go-image
I chciałem właśnie się zapytać, czy faktycznie najczęściej tak się pracuje z tym narzędziem, przekazuje się link do repo na githubie oraz kontener (albo częściej zbiór kontenerów), jako "tylko" środowisko do pracy bez przetrzymywania w nim kodu aplikacji
Wszystko zależy od tego kto co jak ustawi. Sam docker jest dość niełatwym narzędziem w obsłudze, jeśli chodzi o mnogość opcji w CLI. Na potrzeby firmy, w której pracuję, gdzie wszyscy przed moim przyjściem siedzieli na Vagrancie, napisałem prosty bashowy skrypt, który robi interfejs nieco łatwiejszy, a na pewno bardziej podobny do Vagranta. Wykorzystujemy też docker-compose
i ogólnie polecam, bo zamiast docker run....
z volumami i networkiem robisz sobie elegancko docker-compose up
.
Co do istoty sprawy -właśnie po to masz volumy, by współdzielić kod. Sam robię tak, że kopiuję te pliki, które są np. tylko na potrzeby instalacji, a montuję te które faktycznie są wykorzystywane np. resources
kopiuję w pliku dockerfile, zaś app
jest montowane.
Zwykle dockera używa się do postawienia środowiska (bazy danych, kolejki i innych usług). Kodu raczej się nie wrzuca, jak już coś to bardziej postać wynikową typu war dla tomcata.
Dockera wykorzystuję do postgresa - to nigdy nie było tak wygodne.
Kodu jeśli nie muszę nie hostuje w ten sposób - po to masz własnego kompa i środowisko żeby szło bezproblemowo. Dopiero kiedy robisz release zastanowiłbym się czy warto
Można zrobić na przykład tak. Utwórzmy sobie katalog:
$ mkdir Golang
Wrzućmy do niego nasz kod w Go:
package main
import "fmt"
func main() {
fmt.Println("hello world")
}
Teraz odpalamy kontener z Go:
$ sudo docker run -t -v "$PWD/Golang:/go" --name playing-with-golang -d golang
Opcja -v
mapuje nasz katalog $PWD/Golang
, który przed chwilą stworzyliśmy na katalog /go
w kontenerze Użyłem $PWD
bo podaje się bezwzględne ścieżki. --name
przypisuje nazwę kontenerowi, -d
wybiera rodzaj kontenera, opcja -t
sprawia, że nie wyłącza się od razu po zakończeniu. docker ps
pokazuje, że kontener działa:
$ sudo docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
23996ddd9131 golang "bash" 6 minutes ago Up 6 minutes playing-with-golang
Można go zatrzymać (docker stop playing-with-golang
), wznowić (docker start playing-with-golang
) lub usunąć (docker rm playing-with-golang
, po zatrzymaniu) Teraz wchodzimy do kontenera, wywołując w nim Basha:
$ sudo docker exec -it playing-with-golang bash
root@23996ddd9131:/go# ls
test.go
root@23996ddd9131:/go# go run test.go
hello world
Jak widać, nasz plik jest obecny i można go uruchomić w środku kontenera, edytując go jednocześnie z poziomu normalnego systemu. Można też nie wchodzić do kontenera i odpalić od razu z powłoki
$ sudo docker exec -it playing-with-golang go run test.go
hello wordld
Opcją -p 8081:80
w dokcer run
dodatkowo mapujemy sobie lokalny port 80 na localhost:8081
, aby uruchomić webapkę. Skasujmy więc kontener i zamontujmy go na nowo z tą opcją:
$ sudo docker stop playing-with-golang
playing-with-golang
$ sudo docker rm playing-with-golang
playing-with-golang
$ sudo docker run -p 8081:80 -t -v "$PWD/Golang:/go" --name playing-with-golang -d golang
(można dodać port do działającego kontenera - https://stackoverflow.com/questions/19335444/how-do-i-assign-a-port-mapping-to-an-existing-docker-container - ale tak jest szybciej :P)
Wchodzimy w kontener i instalujemy framework, niech będzie Gorilla:
$ sudo docker exec -it playing-with-golang bash
root@d550a8c08247:/go# go get -u github.com/gorilla/mux
Wyłazimy (Crtl+D), wklejamy w plik kod wygrzebany stąd
package main
import (
"fmt"
"net/http"
"github.com/gorilla/mux"
)
func main() {
r := mux.NewRouter()
r.HandleFunc("/books/{title}/page/{page}", func(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
title := vars["title"]
page := vars["page"]
fmt.Fprintf(w, "You've requested the book: %s on page %s\n", title, page)
})
http.ListenAndServe(":80", r)
}
Odpalam w terminalu:
sudo docker exec -it playing-with-golang go run test.go
Włażę na http://localhost:8081/books/test/page/12 - i cóż - u mnie działa. Po modyfikacji kodu zamiast kasować cały kontener przerywam program odpalony powyżej z Crtl+D i restartuje.
Dalsza zabawa to ustawienie w kontenerze, aby przy starcie sam uruchamiał serwer + włożenie jakiegoś automatycznego reloadera, który bedzie wykrywał zmiany w plikach, wówczas sam docker start playing-with-golang
wystarczy. To pozostawiam jako ćwiczenie dla czytelnika :)
Nie wiem czy jest sens używania dockera w taki sposób, czyli defacto development w kontenerze. Jak już to ewentualnie docker z toolsetem go i np zamiast "go run/build/fmt/import " to używałbyś kontenera dockera, który ma te wszystkie toole, np do budowania apki. Wtedy nie syfisz se też na kompie przy okazji. Jeśli użyjesz w swojej apce bazy no to też odpalasz dockera z jakims mysqlem i podłączasz sie do niego, zamiast instalowania tego na kompie. To odnośnie używania dockera do developmentu :)