Skip to content

Instantly share code, notes, and snippets.

@joseph0x45
Last active June 9, 2024 11:05
Show Gist options
  • Save joseph0x45/09507356e953fd3300e467ef5ea31be8 to your computer and use it in GitHub Desktop.
Save joseph0x45/09507356e953fd3300e467ef5ea31be8 to your computer and use it in GitHub Desktop.
How I develop my Go apps in containers using Neovim and docker compose

Hello reader. I have been trying to replicate the dev container experience that I had when I was still using vscode (I use neovim now btw) but it wasn't quite that, I tried some neovim plugins but didn't really like it. But I'm happy to tell you that I finally got something, just for Golang apps, but we are getting there. Let's quickly see how I achieved this.

Required dependencies

You will need docker and docker compose installed on your system. I use Manjaro so for me installing these two was as easy as running

pamac install docker docker-compose

Once you have these two installed you will need a text editor, I will be using neovim. What I mean by the "dev container experience" is to be able to have my code on my host, but run and debug it inside a container so that way my computer doesn't have any dependencies and I can easily reproduce the dev environment on another PC without having a "it worked on that other machine" moment. As exemple we will build a simple Go app that returns "Hello world" upon receiving a GET request at "/hello".

The actual stuff

After running go mod init app-name we create our main.go and write the following code inside

Screenshot_20231218_181539

So this is a simple Go server using the standard library. We want to run the app in a container so we will create a docker-compose.yml at the root of our directory with the following content

Screenshot_20231218_181706

Let's go through what the content of this file mean. In case you are not familiar with docker-compose I recommend you take a look at docker first and understand the concept of containerization and then read docker-compose docs. For the rest of the post I will assume you have a basic understanding of docker-compose. Nothing crazy here we just created a container based on Go's official image and we map the port 8080 of that container to the port 8080 on our host. The important part comes from the volumes section, we mount our current directory into the container so that we can have access to our files from inside it. If we run this using docker compose up

Screenshot_20231218_172412

We see that the container exists automatically, that's normal because the container doesn't have any process running. A way to solve that would be to give it a command to run.

Screenshot_20231218_181706

Setting the command as ["tail", "-f", "/dev/null"] means that the container will continuously run the tail command to follow the content of /dev/null, effectively doing nothing but keeping the container running indefinitely. Now if we run docker compose up again we get this

Screenshot_20231218_175534

Let's run it again with docker compose up -d.

Screenshot_20231218_175825

Now that our container is running in the background we can go inside it and run our app. We named our container server so we will run docker exec -it server bash.

Screenshot_20231218_180554

We are able to build and run our code from the container. And we can test this using curl

Screenshot_20231218_180819

Working with an LSP and packages

So this is really simple, but one limitation I found here is when we bring dependencies into the code, let's say we want to rewrite our server using Chi. Our code would now look like this

Screenshot_20231218_182937

Then we can go into our container and run it

Screenshot_20231218_183051

You can see that we first ran go get -u github.com/go-chi/chi/v5 to download chi and then we were able to build and run the code. All good. But if you take a look into our editor

Screenshot_20231218_183206

We see that our LSP is not happy, even tho we downloaded chi, our editor can not find it. And that is completely normal because the dependencies downloaded with go get are stored in the GOPATH in our container so our LSP doesn't have access to them. To solve that and still get LSP support and completions in our editor we can download chi on our machine and the errors will go away but the whole point of working in a dev container is to not have to install anything on our machine. The way I solved this issue was by using go mod vendor. With go mod vendor you can download all the dependencies required to build your app in a folder and you can even share that folder with your team mates or push it to source control. That way even if you need to work on the app but you have no access to internet, Go will take the dependencies from the vendor directory. You can read more about go vendor on Go's official documentation.

All we have to do is run go mod vendor inside our container

Screenshot_20231218_183853

And on the screenshot you can see that after we ran the command it created a new vendor directory inside our package root folder. And inside that folder you have a folder named github.com which contains the code for the chi library. There is also a modules.txt file that contains the list of all the dependencies. Now if we restart our LSP on our host machine we can see that the LSP will be happy now

Screenshot_20231218_184116

The LSP is configured to take dependencies from the vendor folder if present in your codebase. Now the dependencies are installed in our container, the vendor folder keeps a copy of these dependencies so that we can still have our LSP support without having to install the same packages on our host machine.

Conclusion

As a beginner Go developer and docker user myself I am so excited to learn more about these tools, Go is incredible because it has so much tools builtin already and docker is just awesome. I believe that with much more understanding of how volumes and LSPs work I can make this setup even better and why not turn it into a tool that could spin up automatically dev containers and connect to it from neovim, we'll see. Either way I will keep learning Go and Docker. Thanks for reading :) if you have any remarks please let me know in the comments. All the code showed in this post is available on this GitHub repo

Peace

@YvesleCurseur
Copy link

Insightful !

@HafizBkr
Copy link

Amazing

@woueziou
Copy link

woueziou commented Jun 9, 2024

👌🏿

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment