Skip to content

Commit 3f72e6a

Browse files
authored
dev: add docker developer container for easier installation and usage (#305)
* docker: add dev-container Dockerfile and scripts * dev-container: finalaize container structure and user prompts * docker: change port exposure in order to correctly ssh straight into container * add pykokkos into devcontainer * change EOF format * change EOF format * remove deprecated Dockerfile * update docker installation * add flexible NVIDIA docker container with custom kernel * add template for secrets * add default template proper handling * add pykokkos installation after base install * shellcheck warns fix * add workflow to check devcontainer linting errors, shell errors and build (build may fail) * fix shellcheck warns * fix hadolint docker container issues * fix shellcheck warns * add wget to install list * address all shellcheck warns
1 parent 189c54f commit 3f72e6a

File tree

10 files changed

+537
-51
lines changed

10 files changed

+537
-51
lines changed

.github/workflows/array_api.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
strategy:
1414
matrix:
1515
platform: [ubuntu-latest]
16-
python-version: ["3.13"]
16+
python-version: [3.11, 3.12, 3.13]
1717
runs-on: ${{ matrix.platform }}
1818
steps:
1919
- uses: actions/checkout@v3
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
name: Dev Container CI
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
paths:
7+
- "dev-container/**"
8+
- ".github/workflows/dev-container-ci.yml"
9+
pull_request:
10+
branches: [main, develop]
11+
paths:
12+
- "dev-container/**"
13+
- ".github/workflows/dev-container-ci.yml"
14+
15+
jobs:
16+
# Dockerfile linting
17+
dockerfile-lint:
18+
name: Lint Dockerfile
19+
runs-on: ubuntu-latest
20+
steps:
21+
- name: Checkout code
22+
uses: actions/checkout@v4
23+
24+
- name: Run hadolint
25+
uses: hadolint/[email protected]
26+
with:
27+
dockerfile: dev-container/Dockerfile
28+
failure-threshold: warning
29+
ignore: DL3002
30+
31+
# Shell scripts check
32+
shellcheck:
33+
name: ShellCheck Scripts
34+
runs-on: ubuntu-latest
35+
steps:
36+
- name: Checkout code
37+
uses: actions/checkout@v4
38+
39+
- name: Run ShellCheck on build-container.sh
40+
uses: ludeeus/action-shellcheck@master
41+
with:
42+
scandir: "./dev-container"
43+
severity: warning
44+
ignore_paths: secrets.yaml secrets-template.yaml
45+
46+
- name: Run ShellCheck on scripts directory
47+
uses: ludeeus/action-shellcheck@master
48+
env:
49+
SHELLCHECK_OPTS: -e SC1090
50+
with:
51+
scandir: "./dev-container/scripts"
52+
severity: warning
53+
54+
# Dockerfile building
55+
dockerfile-build-test:
56+
name: Test Dockerfile Build
57+
runs-on: ubuntu-latest
58+
steps:
59+
- name: Checkout code
60+
uses: actions/checkout@v4
61+
62+
- name: Set up Docker Buildx
63+
uses: docker/setup-buildx-action@v3
64+
65+
- name: Create dummy secrets for build test
66+
run: |
67+
mkdir -p dev-container/secrets
68+
echo "testuser" > dev-container/secrets/username
69+
echo "testpass" > dev-container/secrets/password
70+
echo "" > dev-container/secrets/ssh_key
71+
72+
- name: Build Docker image (test only)
73+
uses: docker/build-push-action@v5
74+
with:
75+
context: .
76+
file: ./dev-container/Dockerfile
77+
push: false
78+
tags: pykokkos-dev:test
79+
cache-from: type=gha
80+
cache-to: type=gha,mode=max
81+
secret-files: |
82+
username=dev-container/secrets/username
83+
password=dev-container/secrets/password
84+
ssh_key=dev-container/secrets/ssh_key
85+
86+
- name: Clean up secrets
87+
if: always()
88+
run: rm -rf dev-container/secrets

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,9 @@ tags
4646
# Persistent undo
4747
[._]*.un~
4848

49+
# Docker files and secrets
50+
dev-container/secrets.yaml
51+
4952
# Output files and directories
5053
build/
54+
dev-container/secrets.yaml

Dockerfile

Lines changed: 0 additions & 35 deletions
This file was deleted.

dev-container/Dockerfile

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
FROM nvidia/cuda:12.4.1-devel-ubuntu22.04
2+
3+
# Optional ARG for username (only set if secret is provided)
4+
ARG USERNAME=""
5+
ENV username="${USERNAME}"
6+
7+
# Optional ARG for SSH port
8+
ARG PORT="2222"
9+
ENV SSH_PORT="${PORT}"
10+
11+
# Dependencies installation
12+
RUN apt-get update && DEBIAN_FRONTEND=noninteractive apt-get install --no-install-recommends -y \
13+
build-essential=12.9ubuntu3 \
14+
micro=2.0.9-1ubuntu0.22.04.3 \
15+
nano=6.2-1ubuntu0.1 \
16+
git=1:2.34.1-1ubuntu1.15 \
17+
openssh-server=1:8.9p1-3ubuntu0.13 \
18+
wget=1.21.2-2ubuntu1.1 \
19+
&& rm -rf /var/lib/apt/lists/* \
20+
&& mkdir -p /var/run/sshd \
21+
&& ssh-keygen -A
22+
23+
# Set shell to bash with pipefail for safer pipe operations
24+
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
25+
26+
# Create user using Docker secrets for sensitive data
27+
RUN --mount=type=secret,id=username \
28+
--mount=type=secret,id=password \
29+
--mount=type=secret,id=ssh_key \
30+
if [ -f "/run/secrets/username" ] && [ -s "/run/secrets/username" ] && [ -f "/run/secrets/password" ] && [ -s "/run/secrets/password" ]; then \
31+
export NAME && \
32+
NAME="$(cat /run/secrets/username)" && \
33+
export PASSWORD && \
34+
PASSWORD="$(cat /run/secrets/password)" && \
35+
useradd -m -u 1000 -s /bin/bash "${NAME}" && \
36+
echo "${NAME}:${PASSWORD}" | chpasswd && \
37+
echo "root:${PASSWORD}" | chpasswd && \
38+
usermod -aG sudo "${NAME}" && \
39+
chown -R "${NAME}":"${NAME}" "/home/${NAME}" && \
40+
echo "${NAME} ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers && \
41+
echo "${NAME}" > /tmp/username; \
42+
else \
43+
echo "root" > /tmp/username; \
44+
fi
45+
46+
# Configure SSH permissions for devcontainer (only if custom user was created)
47+
RUN export USERNAME && \
48+
USERNAME="$(cat /tmp/username)" && \
49+
if [ "${USERNAME}" != "root" ]; then \
50+
mkdir -p /var/run/sshd && \
51+
sed -i 's/#PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config && \
52+
sed -i 's/#PasswordAuthentication yes/PasswordAuthentication yes/' /etc/ssh/sshd_config; \
53+
fi
54+
55+
# Conditionally add SSH public key if provided
56+
RUN --mount=type=secret,id=ssh_key \
57+
export USERNAME && \
58+
USERNAME="$(cat /tmp/username)" && \
59+
if [ -f /run/secrets/ssh_key ] && [ -s /run/secrets/ssh_key ] && [ "${USERNAME}" != "root" ]; then \
60+
export SSH_PUB_KEY && \
61+
SSH_PUB_KEY="$(cat /run/secrets/ssh_key)" && \
62+
mkdir -p "/home/${USERNAME}/.ssh/" && \
63+
echo "${SSH_PUB_KEY}" >> "/home/${USERNAME}/.ssh/authorized_keys" && \
64+
chmod 700 "/home/${USERNAME}/.ssh/" && \
65+
chown -R "${USERNAME}":"${USERNAME}" "/home/${USERNAME}/.ssh/" && \
66+
chmod 600 "/home/${USERNAME}/.ssh/authorized_keys"; \
67+
fi
68+
69+
# Set up custom entrypoint and init scripts
70+
COPY dev-container/scripts/custom-entrypoint.sh /opt/custom-entrypoint.sh
71+
COPY dev-container/scripts/init-pykokkos.sh /opt/init-pykokkos.sh
72+
RUN chmod +x /opt/custom-entrypoint.sh /opt/init-pykokkos.sh
73+
74+
# Set working directory based on USERNAME ARG
75+
# WORKDIR_PATH will be passed as build arg from build script
76+
ARG WORKDIR_PATH="/root"
77+
78+
# Set WORKDIR - use WORKDIR_PATH from build arg
79+
WORKDIR ${WORKDIR_PATH}
80+
81+
# Temporarily switch to user for conda and git operations
82+
USER ${USERNAME:-root}
83+
84+
# Set up and initialize conda environment
85+
# Install conda in the user's home directory
86+
RUN export USERNAME && \
87+
USERNAME="$(cat /tmp/username)" && \
88+
if [ "${USERNAME}" != "root" ]; then \
89+
export HOME_DIR="/home/${USERNAME}" && \
90+
wget -q https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh && \
91+
bash miniconda.sh -b -p "${HOME_DIR}/miniconda3" && \
92+
rm -f miniconda.sh && \
93+
echo "${HOME_DIR}/miniconda3/bin" > /tmp/conda_path; \
94+
else \
95+
export HOME_DIR="/root" && \
96+
wget -q https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh && \
97+
bash miniconda.sh -b -p "${HOME_DIR}/miniconda3" && \
98+
rm -f miniconda.sh && \
99+
echo "${HOME_DIR}/miniconda3/bin" > /tmp/conda_path; \
100+
fi
101+
102+
# Initialize user's conda environment, TOS agreements and ensure it's loaded in every shell
103+
RUN export CONDA_PATH && \
104+
CONDA_PATH="$(cat /tmp/conda_path)" && \
105+
"${CONDA_PATH}"/conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/main && \
106+
"${CONDA_PATH}"/conda tos accept --override-channels --channel https://repo.anaconda.com/pkgs/r && \
107+
"${CONDA_PATH}"/conda init
108+
109+
# Clone pykokkos repository (installation will happen at runtime)
110+
RUN git clone https://github.com/kokkos/pykokkos.git
111+
112+
# Add conda to PATH in .bashrc
113+
RUN export CONDA_PATH && \
114+
CONDA_PATH="$(cat /tmp/conda_path)" && \
115+
export HOME_DIR && \
116+
HOME_DIR="$(dirname "$(dirname "${CONDA_PATH}")")" && \
117+
if [ "${HOME_DIR}" != "/root" ]; then \
118+
echo "export PATH=\"${CONDA_PATH}:${HOME_DIR}/miniconda3/condabin:${HOME_DIR}/.local/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"" >> "${HOME_DIR}/.bashrc" && \
119+
echo "export condaPath=${CONDA_PATH}" >> "${HOME_DIR}/.bashrc"; \
120+
else \
121+
echo "export PATH=\"${CONDA_PATH}:${HOME_DIR}/miniconda3/condabin:${HOME_DIR}/.local/bin:/usr/local/nvidia/bin:/usr/local/cuda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin\"" >> "${HOME_DIR}/.bashrc" && \
122+
echo "export condaPath=${CONDA_PATH}" >> "${HOME_DIR}/.bashrc"; \
123+
fi
124+
125+
# Switch back to root for entrypoint execution (required for sshd and init scripts)
126+
USER root
127+
128+
# Expose port in order to be able to connect from IDEs
129+
EXPOSE ${SSH_PORT}
130+
131+
ENTRYPOINT ["/opt/custom-entrypoint.sh"]

0 commit comments

Comments
 (0)