Rất lâu sau bài Những lý do để chọn GoLang là ngôn ngữ lập trình tiếp theo bạn nên tìm hiểu?, mình mới có cơ hội quay lại tiếp tục tìm hiểu về Golang, Golang là một ứng viên xuất sắc cho việc xây dựng và phát triển các ứng dụng Server-side, có thể handle một lượng lớn request. Mình sẽ chia sẻ từng bước để tiếp cận với Go nhé.
Cài đặt môi trường phát triển Go
Mình sẽ sử dụng môi trường Linux cho việc dev. Mình không ghét Windows nhưng mà cài đặt môi trường dev trên win rất tốn thời gian và phiền phức.
Nếu không có điều kiện sử dụng các môi trường Unix, các bạn có thể dùng Docker hoặc Vagrant, sẽ tiện hơn là cài trực tiếp trên môi trường Windows.
Cài đặt Go runtime
Đầu tiên hãy đảm bảo bạn bạn đã update và upgrade các bản sửa lỗi trên Ubuntu:
1 2 3 | ➜ ~ <span class="token function">sudo</span> <span class="token function">apt-get</span> update ➜ ~ <span class="token function">sudo</span> <span class="token function">apt-get</span> -y upgrade |
Sau đó, hãy download bản cài mới nhât của Go bằng command sau :
1 2 3 | ➜ ~ <span class="token function">curl</span> -O https://storage.googleapis.com/golang/go1.12.6.linux-amd64.tar.gz |
command này sẽ pull các package của Go về và lưu nó ở thư mục hiện tại của bạn, bạn có thể dùng pwd
để kiểm tra đường dẫn.
Sau đó, hãy giải nén package
1 2 3 | ➜ ~ <span class="token function">sudo</span> <span class="token function">tar</span> -C /usr/local -xzf go1.12.6.linux-amd64.tar.gz ➜ ~ <span class="token function">export</span> PATH<span class="token operator">=</span><span class="token variable">$PATH</span>:/usr/local/go/bin |
Kiểm tra thử version Go hiện tại
1 2 3 | ➜ ~ go version go version go1.12.6 linux/amd64 |
như vậy là bạn đã cài đặt thành công
Hello word!
Để chắc chắn Go đã được cài đặt chính xác, cùng tạo 1 workspace và viết 1 chương trình Go đơn giản thôi.
Đầu tiên hãy thêm giá trị GOPATH để Go biết có thể tìm thấy code của chúng ta ở đâu:
1 2 3 4 5 | ➜ ~ <span class="token function">sudo</span> <span class="token function">nano</span> ~/.profile <span class="token comment"># Thêm vào cuối file 2 dòng</span> <span class="token function">export</span> GOPATH<span class="token operator">=</span><span class="token variable">$HOME</span>/work <span class="token function">export</span> PATH<span class="token operator">=</span><span class="token variable">$PATH</span>:/usr/local/go/bin:<span class="token variable">$GOPATH</span>/bin |
$HOME/work
có thể chỏ đến bất kỳ thư mục nào, go get
sẽ lấy tất cả các file ở bên trong nó. Refresh lại để profile nhận config mới source ~/.profile
.
Đầu tiên hãy tạo thư mục work/src/hello
, và thêm 1 file hello.go
với nội dung
1 2 3 4 5 6 7 8 | <span class="token keyword">package</span> main <span class="token keyword">import</span> <span class="token string">"fmt"</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"hello, worldn"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Sau đó hãy build đoạn code vừa rồi với go
tool:
1 2 3 | cd ~/work/src/hello ➜ ~ go build |
Command trên sẽ build 1 executable file tên là hello trong thư mục đó. Chạy file đó ta sẽ có output:
1 2 3 | ➜ ./hello hello, world |
Điều khiển Docker từ Go lang =))
Có thể bạn sẽ thắc mắc tại sao trong bài viết Bắt đầu với ngôn ngữ lập trình Go lại nhắc tới Docker làm gì, chẳng có lý do sâu xa gì đâu. Đơn giản, Go được dùng để xây dựng nên Docker, và mình cũng đang cần điều khiển Docker bằng việc gọi qua Docker API từ Go, nên tiện chia sẻ luôn. Mọi người đều quen thuộc với các bài viết hướng dẫn tạo biến, rồi viết hàm con rồi mà =))
Tạo một Docker Container chạy Nginx
Đầu tiên chúng ta cần tạo 1 object cli
để tương tác với Docker Engine API:
1 2 3 4 5 6 | cli<span class="token punctuation">,</span> err <span class="token operator">:=</span> client<span class="token punctuation">.</span><span class="token function">NewEnvClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Unable to create docker client"</span><span class="token punctuation">)</span> <span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
NewEnvClient()
là cách đơn giản nhất để tạo ra 1 Docker client mà không cần truyền bất kỳ tham số nào cả.
Để tạo Docker container ta dùng func cli.ContainerCreate
, nó require một số tham số và sẽ return 1 ContainerCreateCreatedBody
struct gồm container ID.
Hàm ContainerCreate
được truyền những tham số sau :
1 2 3 4 5 6 | ContainerCreate <span class="token keyword">func</span><span class="token punctuation">(</span>ctx context<span class="token punctuation">.</span>Context<span class="token punctuation">,</span> config <span class="token operator">*</span>container<span class="token punctuation">.</span>Config<span class="token punctuation">,</span> hostConfig <span class="token operator">*</span>container<span class="token punctuation">.</span>HostConfig<span class="token punctuation">,</span> networkingConfig <span class="token operator">*</span>network<span class="token punctuation">.</span>NetworkingConfig<span class="token punctuation">,</span> containerName <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span>container<span class="token punctuation">.</span>ContainerCreateCreatedBody<span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> |
Bởi vì chúng ta chạy 1 Nginx container nên bạn cần expose cổng 80, ở đây mình expose cổng 80 của container đến cổng 8000 của máy mình. Cú pháp sẽ được khai báo trong thuộc tính PortBindings của struct hostConfig. Lưu ý là bạn cần import package nat
để map port ra máy host trước nhé.
1 2 3 4 5 6 7 8 9 | hostBinding <span class="token operator">:=</span> nat<span class="token punctuation">.</span>PortBinding<span class="token punctuation">{</span> HostIP<span class="token punctuation">:</span> <span class="token string">"0.0.0.0"</span><span class="token punctuation">,</span> HostPort<span class="token punctuation">:</span> <span class="token string">"8000"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> containerPort<span class="token punctuation">,</span> err <span class="token operator">:=</span> nat<span class="token punctuation">.</span><span class="token function">NewPort</span><span class="token punctuation">(</span><span class="token string">"tcp"</span><span class="token punctuation">,</span> <span class="token string">"80"</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span><span class="token punctuation">{</span> <span class="token function">panic</span><span class="token punctuation">(</span><span class="token string">"Unable to get the port"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
và tạo Container
1 2 3 4 5 6 7 8 9 10 | cont<span class="token punctuation">,</span> err <span class="token operator">:=</span> cli<span class="token punctuation">.</span><span class="token function">ContainerCreate</span><span class="token punctuation">(</span> context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&</span>container<span class="token punctuation">.</span>Config<span class="token punctuation">{</span> Image<span class="token punctuation">:</span> image<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token operator">&</span>container<span class="token punctuation">.</span>HostConfig <span class="token punctuation">{</span> PortBindings<span class="token punctuation">:</span> portBinding<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Sau khi tạo xong container, chúng ta lấy container ID được trả về từ ContainerCreateCreatedBody
và dùng nó để start container:
1 2 | cli<span class="token punctuation">.</span><span class="token function">ContainerStart</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> cont<span class="token punctuation">.</span>ID<span class="token punctuation">,</span> types<span class="token punctuation">.</span>ContainerStartOptions<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> |
Vậy là xong:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | <span class="token keyword">import</span> <span class="token punctuation">(</span> <span class="token string">"context"</span> <span class="token string">"fmt"</span> <span class="token string">"github.com/docker/docker/api/types"</span> <span class="token string">"github.com/docker/docker/api/types/container"</span> <span class="token string">"github.com/docker/docker/client"</span> <span class="token string">"github.com/docker/go-connections/nat"</span> <span class="token punctuation">)</span> <span class="token keyword">func</span> <span class="token function">CreateNewContainer</span><span class="token punctuation">(</span>image <span class="token builtin">string</span><span class="token punctuation">)</span> <span class="token punctuation">(</span><span class="token builtin">string</span><span class="token punctuation">,</span> <span class="token builtin">error</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> cli<span class="token punctuation">,</span> err <span class="token operator">:=</span> client<span class="token punctuation">.</span><span class="token function">NewEnvClient</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> fmt<span class="token punctuation">.</span><span class="token function">Println</span><span class="token punctuation">(</span><span class="token string">"Unable to create docker client"</span><span class="token punctuation">)</span> <span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">}</span> hostBinding <span class="token operator">:=</span> nat<span class="token punctuation">.</span>PortBinding<span class="token punctuation">{</span> HostIP<span class="token punctuation">:</span> <span class="token string">"0.0.0.0"</span><span class="token punctuation">,</span> HostPort<span class="token punctuation">:</span> <span class="token string">"8000"</span><span class="token punctuation">,</span> <span class="token punctuation">}</span> containerPort<span class="token punctuation">,</span> err <span class="token operator">:=</span> nat<span class="token punctuation">.</span><span class="token function">NewPort</span><span class="token punctuation">(</span><span class="token string">"tcp"</span><span class="token punctuation">,</span> <span class="token string">"80"</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">panic</span><span class="token punctuation">(</span><span class="token string">"Unable to get the port"</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> portBinding <span class="token operator">:=</span> nat<span class="token punctuation">.</span>PortMap<span class="token punctuation">{</span>containerPort<span class="token punctuation">:</span> <span class="token punctuation">[</span><span class="token punctuation">]</span>nat<span class="token punctuation">.</span>PortBinding<span class="token punctuation">{</span>hostBinding<span class="token punctuation">}</span><span class="token punctuation">}</span> cont<span class="token punctuation">,</span> err <span class="token operator">:=</span> cli<span class="token punctuation">.</span><span class="token function">ContainerCreate</span><span class="token punctuation">(</span> context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> <span class="token operator">&</span>container<span class="token punctuation">.</span>Config<span class="token punctuation">{</span> Image<span class="token punctuation">:</span> image<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token operator">&</span>container<span class="token punctuation">.</span>HostConfig<span class="token punctuation">{</span> PortBindings<span class="token punctuation">:</span> portBinding<span class="token punctuation">,</span> <span class="token punctuation">}</span><span class="token punctuation">,</span> <span class="token boolean">nil</span><span class="token punctuation">,</span> <span class="token string">""</span><span class="token punctuation">)</span> <span class="token keyword">if</span> err <span class="token operator">!=</span> <span class="token boolean">nil</span> <span class="token punctuation">{</span> <span class="token function">panic</span><span class="token punctuation">(</span>err<span class="token punctuation">)</span> <span class="token punctuation">}</span> cli<span class="token punctuation">.</span><span class="token function">ContainerStart</span><span class="token punctuation">(</span>context<span class="token punctuation">.</span><span class="token function">Background</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> cont<span class="token punctuation">.</span>ID<span class="token punctuation">,</span> types<span class="token punctuation">.</span>ContainerStartOptions<span class="token punctuation">{</span><span class="token punctuation">}</span><span class="token punctuation">)</span> fmt<span class="token punctuation">.</span><span class="token function">Printf</span><span class="token punctuation">(</span><span class="token string">"Container %s is started"</span><span class="token punctuation">,</span> cont<span class="token punctuation">.</span>ID<span class="token punctuation">)</span> <span class="token keyword">return</span> cont<span class="token punctuation">.</span>ID<span class="token punctuation">,</span> <span class="token boolean">nil</span> <span class="token punctuation">}</span> <span class="token keyword">func</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token punctuation">{</span> <span class="token function">CreateNewContainer</span><span class="token punctuation">(</span><span class="token string">'nginx:latest'</span><span class="token punctuation">)</span> <span class="token punctuation">}</span> |
Tiến hành build tương tự như chương trình Hello World!, truy cập vào địa chỉ localhost:8000
, bạn sẽ thấy message từ nginx chạy bên trong container.