I recently faced a problem when trying to build a Docker image using the @pulumi/docker provider.
In this Blog Post I explain my journey on how I use the Pulumi Operator to build Docker Images and what are the problems I encountered.
If you simply want the solution, click here to scroll down
import * as docker from '@pulumi/docker'
import * as kubernetes from '@pulumi/kubernetes'
const image = new docker.Image("app", {
build: {
context: "../",
args: {
BUILDKIT_INLINE_CACHE: "1",
},
cacheFrom: {
images: ["ghcr.io/kerwanp/pulumi-preview-test:latest"]
}
},
imageName: "ghcr.io/kerwanp/pulumi-preview-test:latest",
})
const appLabels = { app: "preview-test" }
const deployment = new kubernetes.apps.v1.Deployment("app", {
spec: {
replicas: 1,
selector: { matchLabels: appLabels },
template: {
metadata: { labels: appLabels },
spec: {
imagePullSecrets: [
{
name: 'regcred'
}
],
containers: [
{
name: "app",
image: image.imageName,
imagePullPolicy: 'Always',
}
]
}
}
}
})
And then deploy it by creating a stack using the Pulumi Operator:
---
apiVersion: pulumi.com/v1
kind: Stack
metadata:
name: app-stack
namespace: pulumi
spec:
envRefs:
PULUMI_ACCESS_TOKEN:
type: Secret
secret:
name: pulumi-secret
key: pulumiAccessToken
gitAuth:
basicAuth:
password:
type: Secret
secret:
name: pulumi-secret
key: githubToken
userName:
type: Literal
literal:
value: kerwanp
stack: kerwanp/preview-test/test
projectRepo: https://github.com/kerwanp/pulumi-preview-test
repoDir: deploy
branch: "refs/heads/main"
destroyOnFinalize: true
The problem
When the Pulumi Operator tries to deploy the stack it throws the following error:
failed to run update: exit status 255code: 255
stdout: Updating (test)
View Live: https://app.pulumi.com/kerwanp/preview-test/test/updates/1
+ pulumi:pulumi:Stack preview-test-test creating (0s)
@ Updating......
+ pulumi:pulumi:Stack preview-test-test creating (2s) panic: runtime error: invalid memory address or nil pointer dereference
+ pulumi:pulumi:Stack preview-test-test creating (2s) [signal SIGSEGV: segmentation violation code=0x1 addr=0x29 pc=0x124befc]
+ pulumi:pulumi:Stack preview-test-test creating (2s) goroutine 27 [running]:
+ pulumi:pulumi:Stack preview-test-test creating (2s) github.com/pulumi/pulumi-docker/provider/v4.dockerHybridProvider.Configure({{}, {0x21b7ca0, 0x26157, 0x26157}, {0x17dd3fc, 0x6}, {0x17fe470, 0xc0000d1400}, {0x17fe3c0, 0xc0000b0690}}, ...)
+ pulumi:pulumi:Stack preview-test-test creating (2s) /home/runner/work/pulumi-docker/pulumi-docker/provider/hybrid.go:93 +0x17c
+ pulumi:pulumi:Stack preview-test-test creating (2s) github.com/pulumi/pulumi/sdk/v3/proto/go._ResourceProvider_Configure_Handler.func1({0x17f1f08, 0xc00042e6f0}, {0x14c4e00?, 0xc0007d7900})
+ pulumi:pulumi:Stack preview-test-test creating (2s) /home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.64.0/proto/go/provider_grpc.pb.go:462 +0x78
+ pulumi:pulumi:Stack preview-test-test creating (2s) github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc.OpenTracingServerInterceptor.func1({0x17f1f08, 0xc0006ffe90}, {0x14c4e00, 0xc0007d7900}, 0xc000136ac0, 0xc00043eca8)
+ pulumi:pulumi:Stack preview-test-test creating (2s) /home/runner/go/pkg/mod/github.com/grpc-ecosystem/grpc-opentracing@v0.0.0-20180507213350-8e809c8a8645/go/otgrpc/server.go:57 +0x3e8
+ pulumi:pulumi:Stack preview-test-test creating (2s) github.com/pulumi/pulumi/sdk/v3/proto/go._ResourceProvider_Configure_Handler({0x153bb40?, 0xc0007644b0}, {0x17f1f08, 0xc0006ffe90}, 0xc0005da8c0, 0xc000653960)
+ pulumi:pulumi:Stack preview-test-test creating (2s) /home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.64.0/proto/go/provider_grpc.pb.go:464 +0x138
+ pulumi:pulumi:Stack preview-test-test creating (2s) google.golang.org/grpc.(*Server).processUnaryRPC(0xc000234000, {0x17fa100, 0xc000702b60}, 0xc0003c5e60, 0xc00075cba0, 0x21f98e8, 0x0)
+ pulumi:pulumi:Stack preview-test-test creating (2s) /home/runner/go/pkg/mod/google.golang.org/grpc@v1.54.0/server.go:1345 +0xdf3
+ pulumi:pulumi:Stack preview-test-test creating (2s) google.golang.org/grpc.(*Server).handleStream(0xc000234000, {0x17fa100, 0xc000702b60}, 0xc0003c5e60, 0x0)
+ pulumi:pulumi:Stack preview-test-test creating (2s) /home/runner/go/pkg/mod/google.golang.org/grpc@v1.54.0/server.go:1722 +0xa36
+ pulumi:pulumi:Stack preview-test-test creating (2s) google.golang.org/grpc.(*Server).serveStreams.func1.2()
+ pulumi:pulumi:Stack preview-test-test creating (2s) /home/runner/go/pkg/mod/google.golang.org/grpc@v1.54.0/server.go:966 +0x98
+ pulumi:pulumi:Stack preview-test-test creating (2s) created by google.golang.org/grpc.(*Server).serveStreams.func1
+ pulumi:pulumi:Stack preview-test-test creating (2s) /home/runner/go/pkg/mod/google.golang.org/grpc@v1.54.0/server.go:964 +0x28a
docker:index:Image app error: error reading from server: EOF
docker:index:Image app **failed** 1 error
+ pulumi:pulumi:Stack preview-test-test created (2s) 19 messages
Diagnostics:
docker:index:Image (app):
error: error reading from server: EOF
pulumi:pulumi:Stack (preview-test-test):
panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x29 pc=0x124befc]
goroutine 27 [running]:
github.com/pulumi/pulumi-docker/provider/v4.dockerHybridProvider.Configure({{}, {0x21b7ca0, 0x26157, 0x26157}, {0x17dd3fc, 0x6}, {0x17fe470, 0xc0000d1400}, {0x17fe3c0, 0xc0000b0690}}, ...)
/home/runner/work/pulumi-docker/pulumi-docker/provider/hybrid.go:93 +0x17c
github.com/pulumi/pulumi/sdk/v3/proto/go._ResourceProvide
r_Configure_Handler.func1({0x17f1f08, 0xc00042e6f0}, {0x14c4e00?, 0xc0007d7900})
/home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.64.0/proto/go/provider_grpc.pb.go:462 +0x78
github.com/grpc-ecosystem/grpc-opentracing/go/otgrpc.OpenTracingServerInterceptor.func1({0x17f1f08, 0xc0006ffe90}, {0x14c4e00, 0xc0007d7900}, 0xc000136ac0, 0xc00043eca8)
/home/runner/go/pkg/mod/github.com/grpc-ecosystem/grpc-opentracing@v0.0.0-20180507213350-8e809c8a8645/go/otgrpc/server.go:57 +0x3e8
github.com/pulumi/pulumi/sdk/v3/proto/go._ResourceProvider_Configure_Handler({0x153bb40?, 0xc0007644b0}, {0x17f1f08, 0xc0006ffe90}, 0xc0005da8c0, 0xc000653960)
/home/runner/go/pkg/mod/github.com/pulumi/pulumi/sdk/v3@v3.64.0/proto/go/provider_grpc.pb.go:464 +0x138
google.golang.org/grpc.(*Server).processUnaryRPC(0xc000234000, {0x17fa100, 0xc000702b60}, 0xc0003c5e60, 0xc00075cba0, 0x21f98e8, 0x0)
/home/runner/go/pkg/mod/google.golang.org/grpc@v1.54.0/server.go:1345 +0xdf3
google.golang.org/grpc.(*Server).handleStream(0xc000234000, {0x17fa100, 0xc000702b60}, 0xc0003c5e60, 0x0)
/home/runner/go/pkg/mod/google.golang.org/grpc@v1.54.0/server.go:1722 +0xa36
google.golang.org/grpc.(*Server).serveStreams.func1.2()
/home/runner/go/pkg/mod/google.golang.org/grpc@v1.54.0/server.go:966 +0x98
created by google.golang.org/grpc.(*Server).serveStreams.func1
/home/runner/go/pkg/mod/google.golang.org/grpc@v1.54.0/server.go:964 +0x28a
Resources:
+ 1 created
Duration: 4s
stderr: warning: A new version of Pulumi is available. To upgrade from version '3.66.0' to '3.68.0', visit https://pulumi.com/docs/reference/install/ for manual instructions and release notes.
The Debuging Process
Replicating the issue
I opened a shell on the Pulumi Operator Pod to see if I can replicate the pulumi up
inside the container.
I found that the Operator cloned my repository inside /tmp/pulumi-working/<stack-name>
I jumped into it and simply ran pupumi up
.
Problem...
getcwd() failed: No such file or directory
This error usually appears when the folder you are in does not exist anymore, so I decided to duplicate it and re-run pulumi up
.
And voilà, error replicated!
Docker is simply not there..
If I try to build the Docker image by myself directly from the container I receive the following error:
Cannot connect to the Docker daemon at unix:///var/run/docker.sock. Is the docker daemon running?
It makes completely sense! If you do not have a Docker Daemon running, you cannot build a Docker image.
The solution
The idea is to add a docker:dind
container inside the Pulumi Operator pod to be used as a Docker Daemon.
Starting in
18.09+
the Docker Dind automatically generate TLS certificates. We have to mount a directory accross the containers to share thoses.
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: pulumi-operator-fc266171
namespace: pulumi
spec:
replicas: 1
selector:
matchLabels:
name: pulumi-kubernetes-operator
template:
spec:
volumes:
- name: dind-storage
emptyDir: {}
- name: docker-certs-client
emptyDir: {}
containers:
- name: dind
image: docker:24-dind
resources: {}
volumeMounts:
- name: dind-storage
mountPath: /var/lib/docker
- name: docker-certs-client
mountPath: /certs/client
# ^^^^^^
# Mount volume to share certificates to the operator
securityContext:
privileged: true
# ^^^^
# Must be defined to run Docker
- name: pulumi-kubernetes-operator
image: pulumi/pulumi-kubernetes-operator:v1.12.0
args:
- "--zap-level=error"
- "--zap-time-encoding=iso8601"
env:
- name: DOCKER_HOST
value: 'tcp://localhost:2376'
# ^^^^
# Define address for remote Docker Daemon
- name: DOCKER_CERT_PATH
value: '/certs'
# ^^^^
# Define custom certificate directory (mounted volume)
- name: DOCKER_TLS_VERIFY
value: 1
# ^^^^
# Force Docker client to use TLS
- name: WATCH_NAMESPACE
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
- name: POD_NAME
valueFrom:
fieldRef:
apiVersion: v1
fieldPath: metadata.name
- name: OPERATOR_NAME
value: pulumi-kubernetes-operator
- name: GRACEFUL_SHUTDOWN_TIMEOUT_DURATION
value: 5m
- name: MAX_CONCURRENT_RECONCILES
value: "10"
resources: {}
volumeMounts:
- name: docker-certs-client
mountPath: /certs
# ^^^^^^
# Mount volume to get Docker certificates
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
imagePullPolicy: Always
You can actually check that it works by running docker info
inside the Operator Pod.
Client:
Context: default
Debug Mode: false
Plugins:
buildx: Docker Buildx (Docker Inc.)
Version: v0.10.4
Path: /usr/libexec/docker/cli-plugins/docker-buildx
compose: Docker Compose (Docker Inc.)
Version: v2.17.3
Path: /usr/libexec/docker/cli-plugins/docker-compose
Server:
Containers: 0
Running: 0
Paused: 0
Stopped: 0
Images: 0
Server Version: 24.0.1
Storage Driver: overlay2
Backing Filesystem: extfs
Supports d_type: true
Using metacopy: false
Native Overlay Diff: true
userxattr: false
Logging Driver: json-file
Cgroup Driver: cgroupfs
Cgroup Version: 1
Plugins:
Volume: local
Network: bridge host ipvlan macvlan null overlay
Log: awslogs fluentd gcplogs gelf journald json-file local logentries splunk syslog
Swarm: inactive
Runtimes: io.containerd.runc.v2 runc
Default Runtime: runc
Init Binary: docker-init
containerd version: 1677a17964311325ed1c31e2c0a3589ce6d5c30d
runc version: v1.1.7-0-g860f061
init version: de40ad0
Security Options:
seccomp
Profile: builtin
Kernel Version: 5.15.90.1-microsoft-standard-WSL2
Operating System: Alpine Linux v3.18 (containerized)
OSType: linux
Architecture: x86_64
CPUs: 16
Total Memory: 31.32GiB
Name: pulumi-operator-anaxago-fc266171-6f896556dc-lxkvp
ID: 32511430-499e-4fa7-bbce-9b801ffb77ec
Docker Root Dir: /var/lib/docker
Debug Mode: false
Registry: https://index.docker.io/v1/
Experimental: false
Insecure Registries:
127.0.0.0/8
Live Restore Enabled: false
Product License: Community Engine
Tada! 🎉 You can now build images using Pulumi and the Pulumi Operator!
I hope that this Blog Post helped you! If you have any questions, feel free to use the comment section! 💬
Oh and if you want more content like this, follow me: