Skip to content

Commit 478bc63

Browse files
authored
docs: Docker build and Hub publish guide (#396)
* docs: add Docker deployment intro * docs: add Docker "Getting started" guide * docs: add docker-publish.yml sample file and intro * docs: add explanations on docker-publish.yml sections and configuration * docs: add Dockerfile sample * docs: add Dockerfile best practices, add docker-compose.yml link * docs: rename to docker.md, add FINOS contact for DOCKER_PASSWORD setup * docs: add verification section * docs: add Tini explanation * docs: add Docker Build doc to sidebar
1 parent 997f74b commit 478bc63

2 files changed

Lines changed: 202 additions & 1 deletion

File tree

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
---
2+
id: docker
3+
title: Docker Build and Publish
4+
---
5+
6+
## Why Docker?
7+
8+
Many organizations require obtaining and deploying software packages from an image for ease of deployment. Furthermore, there can be strict requirements for those images to be signed or provide provenance attestations, as well as come from a trusted source such as Docker Hub or GitHub Container Registry.
9+
10+
[Publishing to Docker Hub](https://hub.docker.com/r/finos/) and verifying provenance allows FINOS projects to increase adoption by making deployments easy, consistent and trusted - something especially important for enterprise users.
11+
12+
## Getting started
13+
14+
In order to start publishing your image to Docker Hub, you'll first need to create a `Dockerfile` to define what the runtime environment should look like, install dependencies and build the project.
15+
16+
Then, you'll need a GitHub workflow `.github/workflows/docker-publish.yml` to check out the repository, and then build and publish the Docker image.
17+
18+
Optionally, a `docker-compose.yml` can be created for ease of local development and testing. This is not needed when publishing to Docker Hub, which only requires a Dockerfile.
19+
20+
### `Dockerfile`
21+
22+
Your Dockerfile will vary wildly depending on which dependencies you need to build the project, your project's runtime environment(s), etc. A guide on [how to write a basic Dockerfile](https://docs.docker.com/get-started/docker-concepts/building-images/writing-a-dockerfile/) is available in the Docker documentation.
23+
24+
Even if there isn't a one-size-fits-all solution, there are general best practices that are good to follow when writing a Dockerfile.
25+
26+
Here's a [sample Dockerfile from GitProxy](https://github.com/finos/git-proxy/blob/main/Dockerfile):
27+
28+
```Dockerfile
29+
FROM node:24@sha256:5a593d74b632d1c6f816457477b6819760e13624455d587eef0fa418c8d0777b AS builder
30+
31+
USER root
32+
33+
WORKDIR /out
34+
35+
COPY package*.json ./
36+
COPY tsconfig.json tsconfig.publish.json proxy.config.json config.schema.json test-e2e.proxy.config.json vite.config.ts index.html index.ts ./
37+
38+
RUN npm pkg delete scripts.prepare && npm ci --include=dev
39+
40+
COPY src/ /out/src/
41+
COPY public/ /out/public/
42+
43+
RUN npm run build-ui \
44+
&& npx tsc --project tsconfig.publish.json \
45+
&& cp config.schema.json dist/ \
46+
&& npm prune --omit=dev
47+
48+
FROM node:24@sha256:5a593d74b632d1c6f816457477b6819760e13624455d587eef0fa418c8d0777b AS production
49+
50+
COPY --from=builder /out/package*.json ./
51+
COPY --from=builder /out/node_modules/ /app/node_modules/
52+
COPY --from=builder /out/dist/ /app/dist/
53+
COPY --from=builder /out/build /app/dist/build/
54+
COPY proxy.config.json config.schema.json ./
55+
COPY docker-entrypoint.sh /docker-entrypoint.sh
56+
57+
USER root
58+
59+
RUN apt-get update && apt-get install -y \
60+
git tini \
61+
&& rm -rf /var/lib/apt/lists/*
62+
63+
RUN mkdir -p /app/.data /app/.tmp /app/.remote \
64+
&& chown -R 1000:1000 /app
65+
66+
USER 1000
67+
68+
WORKDIR /app
69+
70+
EXPOSE 8080 8000
71+
72+
ENTRYPOINT ["tini", "--", "/docker-entrypoint.sh"]
73+
CMD ["node", "--enable-source-maps", "dist/index.js"]
74+
```
75+
76+
This file is specific to GitProxy, but it showcases elements that are good to have in any `Dockerfile`:
77+
78+
- **Multi-stage builds**: We divide the work into a `builder` stage that compiles/installs everything, and a `production` stage that copies over the final artifacts. This keeps image sizes small and prevents shipping dev tooling into production
79+
- **Pinning images to SHA digests**: Notice that image versions include a SHA. This is needed because tags are mutable. Pinning to a specific SHA guarantees the environment is properly replicated
80+
- **Running as non-root**: Setting `USER 1000` allows minimizing privileges before initializing the app
81+
- **Tini entrypoint**: Sets the entrypoint to Tini, which allows reaping zombie processes and forwarding signals from Docker to the app
82+
83+
### `docker-publish.yml`
84+
85+
This file should be created in your `.github/workflows` directory to automate the build and publish process. In this file, we can detail *when* and *how* we want to publish to Docker Hub, for example:
86+
87+
- Publish a `my-project:main` tag every time something gets pushed to `main`
88+
- Publish a `my-project:latest` tag whenever a new version of our software is published
89+
- Publish a `my-project:X.Y` tag when publishing a specific version of our software
90+
91+
Here's an example of a [`docker-publish.yml` workflow from GitProxy](https://github.com/finos/git-proxy/blob/main/.github/workflows/docker-publish.yml):
92+
93+
```yml
94+
name: Build and Publish Docker Image
95+
96+
on:
97+
push:
98+
branches: [main]
99+
release:
100+
types: [published]
101+
102+
jobs:
103+
docker-build-publish:
104+
name: Build and Publish Docker Image
105+
runs-on: ubuntu-latest
106+
107+
steps:
108+
- name: Set up Docker Buildx
109+
uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4
110+
111+
- name: Checkout Repository
112+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
113+
114+
- name: Log in to Docker Hub
115+
if: github.repository_owner == 'finos'
116+
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
117+
with:
118+
username: finos
119+
password: ${{ secrets.DOCKER_PASSWORD }}
120+
121+
- name: Set Docker Image Tag
122+
id: tags
123+
run: |
124+
if [ "${{ github.event_name }}" = "release" ]; then
125+
echo "tags=${{ github.repository }}:${{ github.ref_name }},${{ github.repository }}:latest" >> $GITHUB_OUTPUT
126+
else
127+
echo "tags=${{ github.repository }}:main" >> $GITHUB_OUTPUT
128+
fi
129+
130+
- name: Build and Publish Docker Image
131+
if: github.repository_owner == 'finos'
132+
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7
133+
with:
134+
context: .
135+
file: Dockerfile
136+
push: true
137+
tags: ${{ steps.tags.outputs.tags }}
138+
provenance: true
139+
```
140+
141+
You can tweak when to run the workflow as follows:
142+
143+
```yml
144+
on:
145+
push:
146+
branches: [main] # Run when pushing to main
147+
release:
148+
types: [published] # Run when publishing a release (via GitHub)
149+
```
150+
151+
Note that the following section requires a `DOCKER_PASSWORD` repository secret to log into the `finos` Docker Hub account. Please contact [help@finos.org](mailto:help@finos.org) to set it up:
152+
153+
```yml
154+
- name: Log in to Docker Hub
155+
if: github.repository_owner == 'finos' # Only allow workflow to run from upstream repository
156+
uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4
157+
with:
158+
username: finos
159+
password: ${{ secrets.DOCKER_PASSWORD }}
160+
```
161+
162+
The following bit tags the image depending on whether the workflow got triggered on release or a regular push. Then, it automatically sets the tag name to the repository name and appends `:latest`, `:main` or `:X.Y` depending on what triggered the flow:
163+
164+
```yml
165+
- name: Set Docker Image Tag
166+
id: tags
167+
run: |
168+
if [ "${{ github.event_name }}" = "release" ]; then
169+
echo "tags=${{ github.repository }}:${{ github.ref_name }},${{ github.repository }}:latest" >> $GITHUB_OUTPUT
170+
else
171+
echo "tags=${{ github.repository }}:main" >> $GITHUB_OUTPUT
172+
fi
173+
```
174+
175+
Finally, the image gets published to Docker Hub using the tags determined earlier. The `provenance: true` flag includes a [provenance attestation](https://docs.docker.com/build/metadata/attestations/slsa-provenance/), often used for security and auditing purposes:
176+
177+
```yml
178+
- name: Build and Publish Docker Image
179+
if: github.repository_owner == 'finos'
180+
uses: docker/build-push-action@d08e5c354a6adb9ed34480a06d141179aa583294 # v7
181+
with:
182+
context: .
183+
file: Dockerfile
184+
push: true
185+
tags: ${{ steps.tags.outputs.tags }}
186+
provenance: true
187+
```
188+
189+
### docker-compose.yml
190+
191+
A `docker-compose.yml` can be optionally used for using images locally and testing. This isn't required for deploying to Docker Hub.
192+
193+
Here's an [example `docker-compose.yml` from GitProxy](https://github.com/finos/git-proxy/blob/main/docker-compose.yml) for reference.
194+
195+
## Verification
196+
197+
If everything is working as expected, you should find your published image in the [Docker Hub FINOS profile](https://hub.docker.com/r/finos/) after successfully running the `docker-publish.yml` workflow.
198+
199+
If it doesn't show up, there was likely an error in the workflow itself, or during the `Dockerfile` build process. For more details on why the flow failed, check out the **Actions** tab on your repository and look for the **Build and Publish Docker Image** action to see the workflow execution output along with the reason for failure. You may also want to verify that the `Dockerfile` build works locally before running it in your project's CI pipeline.

website/sidebars.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,9 @@ module.exports = {
8383
"development-infrastructure/continuous-integration/java",
8484
"development-infrastructure/continuous-integration/javascript",
8585
"development-infrastructure/continuous-integration/python",
86-
'development-infrastructure/continuous-delivery'
86+
"development-infrastructure/continuous-integration/docker",
87+
"development-infrastructure/continuous-delivery"
88+
8789
]
8890
},
8991
{

0 commit comments

Comments
 (0)