Skip to main content

Golang / Go Programming Language: Installation (Linux, Windows), Hello World Example, Go Webserver Example Project, Multistage Dockerfile Container Build

1036 words·
Go Golang Docker Multistage Dockerfile Webserver
Table of Contents

Golang Installation
#

Windows
#

Install Golang (Winget)
#

# Install Golang with Winget
winget install GoLang.Go

Verify Installation
#

# Verify installation / check version
go version

Linux (Debian based)
#

Install Binary
#

# Change directory
cd /tmp

# Download package
wget https://go.dev/dl/go1.21.8.linux-amd64.tar.gz

# Install Go (Removes old Go packages)
sudo sh -c 'rm -rf /usr/local/go && tar -C /usr/local -xzf *.tar.gz'

Export Path Variable
#

Temporary:

# Add binary to the PATH environment variable
export PATH=$PATH:/usr/local/go/bin

Permanent:

# Append to .bashrc of current user
echo 'export PATH=$PATH:/usr/local/go/bin' >> ~/.bashrc

# Reload the .bashrc / apply the changes
source ~/.bashrc

Verify Installation
#

# Verify installation / check version
go version



Example: Hello World
#

This is the most basic version to run and compile some Go code.


Program Code
#

# Create a .go file
vi helloworld.go
// Declare the main package / required for any standalone executable Go program
package main

// Import the fmt package for formatted input/output functions
import "fmt"

// Define the main function, the entry point of the program
func main() {
  fmt.Println("Hi there!") // Print the message to the console
}

Run Application
#

# Run application
go run helloworld.go

# Shell output:
Hi there!

Compile & Run Application
#

# Compile Go file into an executable binary
go build helloworld.go
# Run the executable binary
./helloworld

# Shell output:
Hi there!



Example: Hello World Webserver Project
#

The goal of this code is to deploy a web server that is as simple as possible, just to get a feeling for the dependencies and how to containerize a Go application.


Project Directory
#

# Create a directory for the new project
mkdir some-go-project && cd some-go-project

Init Project
#

# Initialize the project
go mod init jueklu/some-go-project

# Shell output:
go: creating new go.mod: module jueklu/some-go-project

Verify go.mod
#

The go mod init command has created a go.mod file, where Go tracks the project dependencies.

# Cat the go.mod file
cat go.mod

# Shell output:
module jueklu/some-go-project

go 1.21.8

Program Code
#

Go Application:

# Create a .go file
vi helloworld.go
// Declare the main package / required for any standalone executable Go program
package main

import (
	"github.com/gin-gonic/contrib/static"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()

	// Serve static files from the "./views" directory
	r.Use(static.Serve("/", static.LocalFile("./views", true)))

	// Start the server on the default port (8080)
	r.Run()
}

Website:

# Create a directory for the website
mkdir views

# Create a index.html file
vi views/index.html
<!DOCTYPE html>
<html>

<head>
	<title>jklug.work</title>
</head>

<body>
	<h1>Hi there</h1>
</body>

</html>

Run Application / Add Dependencies
#

Run Go Application
#

# Run application
go run helloworld.go

# Shell output:
helloworld.go:5:2: no required module provides package github.com/gin-gonic/contrib/static; to add it:
        go get github.com/gin-gonic/contrib/static
helloworld.go:6:2: no required module provides package github.com/gin-gonic/gin; to add it:
        go get github.com/gin-gonic/gin

Add Dependencies
#

# Add the missing dependencies
go get github.com/gin-gonic/contrib/static
go get github.com/gin-gonic/gin

Verify go.mod
#

Verify the go.mod file again:

# Cat the go.mod file
cat go.mod

# Shell output:
module jueklu/some-go-project

go 1.21.8

require (
        github.com/bytedance/sonic v1.11.6 // indirect
        github.com/bytedance/sonic/loader v0.1.1 // indirect
        github.com/cloudwego/base64x v0.1.4 // indirect
        github.com/cloudwego/iasm v0.2.0 // indirect
        github.com/gabriel-vasile/mimetype v1.4.3 // indirect
        github.com/gin-contrib/sse v0.1.0 // indirect
        github.com/gin-gonic/contrib v0.0.0-20240508051311-c1c6bf0061b0 // indirect
        github.com/gin-gonic/gin v1.10.0 // indirect
        github.com/go-playground/locales v0.14.1 // indirect
        github.com/go-playground/universal-translator v0.18.1 // indirect
        github.com/go-playground/validator/v10 v10.20.0 // indirect
        github.com/goccy/go-json v0.10.2 // indirect
        github.com/json-iterator/go v1.1.12 // indirect
        github.com/klauspost/cpuid/v2 v2.2.7 // indirect
        github.com/leodido/go-urn v1.4.0 // indirect
        github.com/mattn/go-isatty v0.0.20 // indirect
        github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
        github.com/modern-go/reflect2 v1.0.2 // indirect
        github.com/pelletier/go-toml/v2 v2.2.2 // indirect
        github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
        github.com/ugorji/go/codec v1.2.12 // indirect
        golang.org/x/arch v0.8.0 // indirect
        golang.org/x/crypto v0.23.0 // indirect
        golang.org/x/net v0.25.0 // indirect
        golang.org/x/sys v0.20.0 // indirect
        golang.org/x/text v0.15.0 // indirect
        google.golang.org/protobuf v1.34.1 // indirect
        gopkg.in/yaml.v3 v3.0.1 // indirect
)

Run Go Application
#

Run the application again

# Run application
go run helloworld.go
# Verify the webserver (Open new shell)
curl localhost:8080

# Shell output:
<!DOCTYPE html>
<html>

<head>
        <title>jklug.work</title>
</head>

Stop the GO application: Strg + c


Verify go.sum File
#

A go.sum file should now be available:

# List GO project files
ls

# Shell output:
go.mod  go.sum  helloworld.go

Compile & Run Application
#

# Compile Go file into an executable binary
go build helloworld.go

Run the Binary:

# Run the executable binary
./helloworld

# Curl the application (In new shell)
curl localhost:8080



Multistage Docker Build
#

Create a Docker container of the previous “Hello World Webserver Project” example.

Dockerfile
#

# Create a Dockerfile
vi Dockerfile
### Stage 1: Compile Binary

# Start from Golang Alpine base image / Label image as "builder"
FROM golang:1.23.3-alpine AS builder

# Create build directory
RUN mkdir /build

# Add GO files
ADD go.mod go.sum helloworld.go /build/

# Define working directory
WORKDIR /build

# Compile Go file into an executable binary
RUN go build -o helloworld




### Stage 2: Final Container

# Start from Alpine base image
FROM alpine:latest

# Creates system user "appuser" / no PW
RUN adduser -S -D -H -h /app appuser

# Switch to "appuser"
USER appuser

# Copy "helloworld" binary from the "compile" stage to the "/app" directory in the container
COPY --from=builder /build/helloworld /app/

# Copy "views" directory from the build context (host) into "/app/views" in the container
COPY views/ /app/views

# Define working directory
WORKDIR /app

# Expose port 8080 for the application
EXPOSE 8080

# Execute the Go binary when the container starts
ENTRYPOINT ["./helloworld"]

Adduser:

  • -S Create system account meant for running services or daemons

  • D Disables password creation for the user, making it a “no-login” account

  • H Prevents the creation of a home directory for the user

  • h /app Specifies “/app” as the home directory for the user

  • appuser The name of the user being created


Build Container Image
#

# Build image named "helloworld" in from current directory
docker build -t helloworld .
# Verify the Docker image
docker images helloworld

# Shell output:
REPOSITORY   TAG       IMAGE ID       CREATED          SIZE
helloworld   latest    75ecb65223bc   19 seconds ago   19.1MB

Create & Verify Container
#

# Create a container from the "helloworld "image
docker run --rm -p 8080:8080 helloworld
# Verify the container (In new shell)
curl localhost:8080



Links #

# Download Golang package
https://go.dev/dl/

# Official Documentation
https://go.dev/doc/install

# gin-gonic / gin Web Framework
https://github.com/gin-gonic/gin