k8s-deploy-helper 0.1.1 β 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +176 -1
- package/bin/cli.js +11 -4
- package/package.json +1 -1
- package/src/docker/buildPush.js +9 -7
- package/src/index.js +34 -13
- package/src/init/generateFiles.js +49 -0
- package/src/init/initProject.js +11 -0
- package/src/k8s/deployment.js +10 -6
- package/src/preflight/checkFiles.js +16 -0
package/README.md
CHANGED
|
@@ -1 +1,176 @@
|
|
|
1
|
-
|
|
1
|
+
# k8s-deploy-helper
|
|
2
|
+
|
|
3
|
+
A production-grade CLI tool that automates Docker image builds and Kubernetes deployments using a single command.
|
|
4
|
+
Designed for real-world CI/CD pipelines and local developer workflows.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## β¨ Overview
|
|
9
|
+
|
|
10
|
+
`k8s-deploy-helper` simplifies the process of:
|
|
11
|
+
- Building Docker images
|
|
12
|
+
- Pushing images to container registries
|
|
13
|
+
- Generating Kubernetes manifests
|
|
14
|
+
- Deploying applications to Kubernetes clusters
|
|
15
|
+
|
|
16
|
+
The tool is **configuration-driven**, **CI/CD-friendly**, and uses **native Docker and kubectl** instead of opaque SDK abstractions.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## π Features
|
|
21
|
+
|
|
22
|
+
- Docker image build automation
|
|
23
|
+
- Registry push support (Docker Hub, ECR, GHCR, etc.)
|
|
24
|
+
- Kubernetes Deployment & Service YAML generation
|
|
25
|
+
- Automatic `kubectl apply`
|
|
26
|
+
- Graceful handling of partial failures
|
|
27
|
+
- Idempotent deployments
|
|
28
|
+
- Works locally and in CI pipelines
|
|
29
|
+
|
|
30
|
+
---
|
|
31
|
+
|
|
32
|
+
## π¦ Installation
|
|
33
|
+
|
|
34
|
+
Install globally from npm:
|
|
35
|
+
npm install -g k8s-deploy-helper
|
|
36
|
+
|
|
37
|
+
Verify installation:
|
|
38
|
+
k8s-deploy --version
|
|
39
|
+
|
|
40
|
+
π Prerequisites
|
|
41
|
+
Ensure the following are installed and configured:
|
|
42
|
+
|
|
43
|
+
Node.js (v18+)
|
|
44
|
+
|
|
45
|
+
Docker (running)
|
|
46
|
+
|
|
47
|
+
kubectl
|
|
48
|
+
|
|
49
|
+
Kubernetes cluster:
|
|
50
|
+
|
|
51
|
+
Docker Desktop Kubernetes
|
|
52
|
+
|
|
53
|
+
Minikube
|
|
54
|
+
|
|
55
|
+
EKS / GKE / AKS
|
|
56
|
+
|
|
57
|
+
Container registry access (Docker Hub, ECR, GHCR, etc.)
|
|
58
|
+
|
|
59
|
+
π Configuration
|
|
60
|
+
Create a k8s.config.json file in your project root.
|
|
61
|
+
|
|
62
|
+
Example configuration:
|
|
63
|
+
json
|
|
64
|
+
Copy code
|
|
65
|
+
{
|
|
66
|
+
"appName": "my-app",
|
|
67
|
+
"namespace": "default",
|
|
68
|
+
|
|
69
|
+
"docker": {
|
|
70
|
+
"image": "username/my-app",
|
|
71
|
+
"tag": "latest",
|
|
72
|
+
"context": ".",
|
|
73
|
+
"dockerfile": "Dockerfile"
|
|
74
|
+
},
|
|
75
|
+
|
|
76
|
+
"kubernetes": {
|
|
77
|
+
"replicas": 2,
|
|
78
|
+
"containerPort": 3000,
|
|
79
|
+
"servicePort": 80,
|
|
80
|
+
"serviceType": "ClusterIP"
|
|
81
|
+
},
|
|
82
|
+
|
|
83
|
+
"resources": {
|
|
84
|
+
"limits": {
|
|
85
|
+
"cpu": "500m",
|
|
86
|
+
"memory": "512Mi"
|
|
87
|
+
},
|
|
88
|
+
"requests": {
|
|
89
|
+
"cpu": "250m",
|
|
90
|
+
"memory": "256Mi"
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
|
|
94
|
+
"env": {
|
|
95
|
+
"NODE_ENV": "production"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
βΆοΈ Usage
|
|
99
|
+
Deploy your application using a single command:
|
|
100
|
+
|
|
101
|
+
bash
|
|
102
|
+
Copy code
|
|
103
|
+
k8s-deploy deploy
|
|
104
|
+
What this command does:
|
|
105
|
+
Builds the Docker image
|
|
106
|
+
|
|
107
|
+
Pushes the image to the registry (if authorized)
|
|
108
|
+
|
|
109
|
+
Generates Kubernetes manifests:
|
|
110
|
+
|
|
111
|
+
deployment.yaml
|
|
112
|
+
|
|
113
|
+
service.yaml
|
|
114
|
+
|
|
115
|
+
Applies the resources to the Kubernetes cluster using kubectl
|
|
116
|
+
|
|
117
|
+
If the image push fails, deployment continues using the existing image tag.
|
|
118
|
+
|
|
119
|
+
π Generated Files
|
|
120
|
+
text
|
|
121
|
+
Copy code
|
|
122
|
+
deployment.yaml
|
|
123
|
+
service.yaml
|
|
124
|
+
These files can be reviewed, committed to version control, or applied independently.
|
|
125
|
+
|
|
126
|
+
π Authentication
|
|
127
|
+
This tool does not manage credentials.
|
|
128
|
+
|
|
129
|
+
Authentication must be handled externally:
|
|
130
|
+
|
|
131
|
+
docker login for container registries
|
|
132
|
+
|
|
133
|
+
kubectl config for Kubernetes clusters
|
|
134
|
+
|
|
135
|
+
This mirrors real-world CI/CD environments and avoids secret leakage.
|
|
136
|
+
|
|
137
|
+
π§ Design Principles
|
|
138
|
+
Explicit over implicit β no hidden magic
|
|
139
|
+
|
|
140
|
+
Fail loud, fail early β CI-friendly logs
|
|
141
|
+
|
|
142
|
+
Idempotent operations β safe re-runs
|
|
143
|
+
|
|
144
|
+
No vendor lock-in β relies on standard tools
|
|
145
|
+
|
|
146
|
+
π§ͺ Example Output
|
|
147
|
+
text
|
|
148
|
+
Copy code
|
|
149
|
+
π³ Building Docker image: username/my-app:latest
|
|
150
|
+
π€ Pushing Docker image: username/my-app:latest
|
|
151
|
+
β οΈ Image push failed β continuing deployment
|
|
152
|
+
|
|
153
|
+
βΈοΈ Deploying to Kubernetes namespace: default
|
|
154
|
+
π¦ Applying deployment.yaml
|
|
155
|
+
deployment.apps/my-app created
|
|
156
|
+
π¦ Applying service.yaml
|
|
157
|
+
service/my-app created
|
|
158
|
+
β
Kubernetes resources applied successfully
|
|
159
|
+
πΊ Roadmap
|
|
160
|
+
Planned enhancements:
|
|
161
|
+
|
|
162
|
+
--dry-run mode
|
|
163
|
+
|
|
164
|
+
Rollback support
|
|
165
|
+
|
|
166
|
+
Helm chart generation
|
|
167
|
+
|
|
168
|
+
Deployment health checks
|
|
169
|
+
|
|
170
|
+
GitHub Actions CI template
|
|
171
|
+
|
|
172
|
+
π License
|
|
173
|
+
MIT License
|
|
174
|
+
|
|
175
|
+
π¨βπ» Author
|
|
176
|
+
Lokesh Shimpi
|
package/bin/cli.js
CHANGED
|
@@ -1,18 +1,25 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import { runDeploy } from '../src/index.js';
|
|
4
|
+
import { initProject } from '../src/init/initProject.js';
|
|
4
5
|
|
|
5
6
|
const program = new Command();
|
|
6
7
|
|
|
7
8
|
program
|
|
8
9
|
.name('k8s-deploy')
|
|
9
10
|
.description('Build and deploy apps to Kubernetes')
|
|
10
|
-
.version('0.
|
|
11
|
+
.version('0.3.0');
|
|
12
|
+
|
|
13
|
+
program
|
|
14
|
+
.command('init')
|
|
15
|
+
.description('Initialize Dockerfile and k8s.config.json')
|
|
16
|
+
.action(initProject);
|
|
11
17
|
|
|
12
18
|
program
|
|
13
19
|
.command('deploy')
|
|
14
|
-
.description('Build
|
|
20
|
+
.description('Build and deploy app to Kubernetes')
|
|
15
21
|
.option('-c, --config <path>', 'Config file path', 'k8s.config.json')
|
|
22
|
+
.option('--local', 'Deploy using local Docker image (skip push)')
|
|
16
23
|
.action(runDeploy);
|
|
17
24
|
|
|
18
|
-
program.parse();
|
|
25
|
+
program.parse(process.argv);
|
package/package.json
CHANGED
package/src/docker/buildPush.js
CHANGED
|
@@ -1,18 +1,16 @@
|
|
|
1
1
|
import { execa } from 'execa';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
|
|
4
|
-
export async function buildAndPushImage(config) {
|
|
4
|
+
export async function buildAndPushImage(config, isLocal) {
|
|
5
5
|
const { image, tag, context, dockerfile } = config.docker;
|
|
6
|
-
|
|
6
|
+
const fullImageName = `${image}:${tag}`;
|
|
7
7
|
const dockerfilePath = dockerfile || 'Dockerfile';
|
|
8
8
|
|
|
9
9
|
if (!fs.existsSync(dockerfilePath)) {
|
|
10
|
-
throw new Error(`Dockerfile not found at
|
|
10
|
+
throw new Error(`Dockerfile not found at ${dockerfilePath}`);
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
console.log(`\nπ³ Building Docker image: ${fullImageName}`);
|
|
13
|
+
console.log(`π³ Building Docker image: ${fullImageName}`);
|
|
16
14
|
|
|
17
15
|
await execa(
|
|
18
16
|
'docker',
|
|
@@ -20,8 +18,12 @@ export async function buildAndPushImage(config) {
|
|
|
20
18
|
{ stdio: 'inherit' }
|
|
21
19
|
);
|
|
22
20
|
|
|
23
|
-
|
|
21
|
+
if (isLocal) {
|
|
22
|
+
console.log('π¦ Local mode enabled β skipping image push');
|
|
23
|
+
return fullImageName;
|
|
24
|
+
}
|
|
24
25
|
|
|
26
|
+
console.log(`π€ Pushing Docker image: ${fullImageName}`);
|
|
25
27
|
await execa('docker', ['push', fullImageName], { stdio: 'inherit' });
|
|
26
28
|
|
|
27
29
|
return fullImageName;
|
package/src/index.js
CHANGED
|
@@ -1,27 +1,48 @@
|
|
|
1
1
|
import { loadConfig } from './config/loadConfig.js';
|
|
2
2
|
import { validateConfig } from './config/validateConfig.js';
|
|
3
|
+
|
|
4
|
+
import { checkRequiredFiles } from './preflight/checkFiles.js';
|
|
5
|
+
import { checkEnvironment } from './preflight/checkEnvironment.js';
|
|
6
|
+
|
|
3
7
|
import { buildAndPushImage } from './docker/buildPush.js';
|
|
8
|
+
|
|
4
9
|
import { generateDeployment } from './k8s/deployment.js';
|
|
5
10
|
import { generateService } from './k8s/service.js';
|
|
6
11
|
import { applyKubernetes } from './k8s/apply.js';
|
|
7
12
|
|
|
8
13
|
export async function runDeploy(options) {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
try {
|
|
15
|
+
console.log('π Checking required files...');
|
|
16
|
+
checkRequiredFiles();
|
|
12
17
|
|
|
13
|
-
|
|
18
|
+
console.log('π Checking environment...');
|
|
19
|
+
await checkEnvironment();
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
const configPath = options.config || 'k8s.config.json';
|
|
22
|
+
const isLocal = options.local === true;
|
|
16
23
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
console.log('π Loading configuration...');
|
|
25
|
+
const config = loadConfig(configPath);
|
|
26
|
+
validateConfig(config);
|
|
27
|
+
|
|
28
|
+
console.log(`π Deploying application: ${config.appName}`);
|
|
29
|
+
if (isLocal) {
|
|
30
|
+
console.log('π¦ Local mode enabled (image push will be skipped)');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const image = await buildAndPushImage(config, isLocal);
|
|
22
34
|
|
|
23
|
-
|
|
24
|
-
|
|
35
|
+
console.log('π Generating Kubernetes manifests...');
|
|
36
|
+
generateDeployment(config, image, isLocal);
|
|
37
|
+
generateService(config);
|
|
25
38
|
|
|
26
|
-
|
|
39
|
+
console.log('βΈοΈ Applying resources to Kubernetes...');
|
|
40
|
+
await applyKubernetes(config);
|
|
41
|
+
|
|
42
|
+
console.log('\nβ
Deployment completed successfully');
|
|
43
|
+
} catch (err) {
|
|
44
|
+
console.error('\nβ Deployment failed');
|
|
45
|
+
console.error(err.message || err);
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
27
48
|
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
export function generateDockerfile() {
|
|
4
|
+
if (fs.existsSync('Dockerfile')) return;
|
|
5
|
+
|
|
6
|
+
const dockerfile = `
|
|
7
|
+
FROM node:18-alpine
|
|
8
|
+
WORKDIR /app
|
|
9
|
+
COPY server/package*.json ./
|
|
10
|
+
RUN npm install
|
|
11
|
+
COPY server .
|
|
12
|
+
EXPOSE 5000
|
|
13
|
+
CMD ["npm", "start"]
|
|
14
|
+
`.trim();
|
|
15
|
+
|
|
16
|
+
fs.writeFileSync('Dockerfile', dockerfile);
|
|
17
|
+
console.log('β
Dockerfile created');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function generateK8sConfig() {
|
|
21
|
+
if (fs.existsSync('k8s.config.json')) return;
|
|
22
|
+
|
|
23
|
+
const config = {
|
|
24
|
+
appName: 'my-app',
|
|
25
|
+
namespace: 'default',
|
|
26
|
+
docker: {
|
|
27
|
+
image: 'your-docker-username/my-app',
|
|
28
|
+
tag: 'latest',
|
|
29
|
+
context: '.',
|
|
30
|
+
dockerfile: 'Dockerfile'
|
|
31
|
+
},
|
|
32
|
+
kubernetes: {
|
|
33
|
+
replicas: 1,
|
|
34
|
+
containerPort: 5000,
|
|
35
|
+
servicePort: 80,
|
|
36
|
+
serviceType: 'ClusterIP'
|
|
37
|
+
},
|
|
38
|
+
env: {
|
|
39
|
+
NODE_ENV: 'production'
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
fs.writeFileSync(
|
|
44
|
+
'k8s.config.json',
|
|
45
|
+
JSON.stringify(config, null, 2)
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
console.log('β
k8s.config.json created');
|
|
49
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { generateDockerfile, generateK8sConfig } from './generateFiles.js';
|
|
2
|
+
|
|
3
|
+
export function initProject() {
|
|
4
|
+
console.log('π§ Initializing project for Kubernetes deployment...\n');
|
|
5
|
+
|
|
6
|
+
generateDockerfile();
|
|
7
|
+
generateK8sConfig();
|
|
8
|
+
|
|
9
|
+
console.log('\nπ Initialization complete');
|
|
10
|
+
console.log('π Update k8s.config.json with your Docker username');
|
|
11
|
+
}
|
package/src/k8s/deployment.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import yaml from 'js-yaml';
|
|
2
2
|
import fs from 'fs';
|
|
3
3
|
|
|
4
|
-
export function generateDeployment(config, image) {
|
|
4
|
+
export function generateDeployment(config, image, isLocal = false) {
|
|
5
5
|
const deployment = {
|
|
6
6
|
apiVersion: 'apps/v1',
|
|
7
7
|
kind: 'Deployment',
|
|
@@ -10,7 +10,7 @@ export function generateDeployment(config, image) {
|
|
|
10
10
|
namespace: config.namespace || 'default'
|
|
11
11
|
},
|
|
12
12
|
spec: {
|
|
13
|
-
replicas: config.kubernetes.replicas,
|
|
13
|
+
replicas: config.kubernetes.replicas || 1,
|
|
14
14
|
selector: {
|
|
15
15
|
matchLabels: {
|
|
16
16
|
app: config.appName
|
|
@@ -27,15 +27,19 @@ export function generateDeployment(config, image) {
|
|
|
27
27
|
{
|
|
28
28
|
name: config.appName,
|
|
29
29
|
image,
|
|
30
|
+
imagePullPolicy: isLocal ? 'IfNotPresent' : 'Always',
|
|
30
31
|
ports: [
|
|
31
32
|
{
|
|
32
33
|
containerPort: config.kubernetes.containerPort
|
|
33
34
|
}
|
|
34
35
|
],
|
|
35
36
|
env: Object.entries(config.env || {}).map(
|
|
36
|
-
([key, value]) => ({
|
|
37
|
+
([key, value]) => ({
|
|
38
|
+
name: key,
|
|
39
|
+
value: String(value)
|
|
40
|
+
})
|
|
37
41
|
),
|
|
38
|
-
resources: config.resources
|
|
42
|
+
resources: config.resources || undefined
|
|
39
43
|
}
|
|
40
44
|
]
|
|
41
45
|
}
|
|
@@ -43,8 +47,8 @@ export function generateDeployment(config, image) {
|
|
|
43
47
|
}
|
|
44
48
|
};
|
|
45
49
|
|
|
46
|
-
const yamlContent = yaml.dump(deployment);
|
|
50
|
+
const yamlContent = yaml.dump(deployment, { noRefs: true });
|
|
47
51
|
fs.writeFileSync('deployment.yaml', yamlContent);
|
|
48
52
|
|
|
49
|
-
|
|
53
|
+
console.log('π deployment.yaml generated');
|
|
50
54
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
export function checkRequiredFiles() {
|
|
4
|
+
const missing = [];
|
|
5
|
+
|
|
6
|
+
if (!fs.existsSync('Dockerfile')) missing.push('Dockerfile');
|
|
7
|
+
if (!fs.existsSync('k8s.config.json')) missing.push('k8s.config.json');
|
|
8
|
+
|
|
9
|
+
if (missing.length) {
|
|
10
|
+
console.error('\nβ Missing required files:');
|
|
11
|
+
missing.forEach(f => console.error(` - ${f}`));
|
|
12
|
+
|
|
13
|
+
console.error('\nπ Run: k8s-deploy init');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
}
|