How can I install .apk file in alpine linux offline? - docker

My docker image based on alpine Linex can not get anything from network. So the command "apk add xxx" is valid. Now my idea is downloading the .apk file and coping it into the docker container. But how can I install the .apk file ?

Let's say you are trying to install glibc in Alpine
Download the packages into your current directory
wget "https://circle-artifacts.com/gh/andyshinn/alpine-pkg-glibc/6/artifacts/0/home/ubuntu/alpine-pkg-glibc/packages/x86_64/glibc-2.21-r2.apk"
wget "https://circle-artifacts.com/gh/andyshinn/alpine-pkg-glibc/6/artifacts/0/home/ubuntu/alpine-pkg-glibc/packages/x86_64/glibc-bin-2.21-r2.apk"
Then, use apk with --allow-untrusted flag
apk add --allow-untrusted glibc-2.21-r2.apk glibc-bin-2.21-r2.apk
And finish the installation (only needed in this example)
/usr/glibc/usr/bin/ldconfig /lib /usr/glibc/usr/lib

Next steps are fine for me:
Get an "online" Alpine machine and download packages. Example is with "zip" and "rsync" packages:
Update your system: sudo apk update
Download only this packages: apk fetch zip rsync
You will get this files (or maybe an actual version):
zip-3.0-r8.apk
rsync-3.1.3-r3.apk
Upload this files to the "offline" Alpine machine.
Install apk packages:
sudo apk add --allow-untrusted zip-3.0-r8.apk
sudo apk add --allow-untrusted rsync-3.1.3-r3.apk
More info: https://wiki.alpinelinux.org/wiki/Alpine_Linux_package_management

please note that the flag --recursive is necessary when you fetch your apk to download all the dependencies too, else you might get an error when you go offline for missing packages.
sudo apk update
sudo apk fetch --recursive packageName
transfer the files to the offline host
sudo apk add --allow-untrusted <dependency.apk>
sudo apk add --allow-untrusted <package.apk>

If it's possible to run Docker commands from a system that's connected to the public Internet, you can do this in a Docker-native way by splitting your image into two parts.
The first image only contains the apk commands, but no actual application code.
FROM alpine
RUN apk add ...
Build that image docker build -t me/alpine-base, connected to the network.
You now need to transfer that image into the isolated environment. If it's possible to connect some system to both networks, and run a Docker registry inside the environment, then you can use docker push to send the image to the isolated environment. Otherwise, this is one of the few cases where you need docker save: create a tar file of the image, move that file into the isolated environment (through a bastion host, on a USB key, ...), and docker load it on the target system.
Now you have that base image on the target system, so you can install the application on top of it without calling apk.
FROM me/alpine-base
WORKDIR /app
COPY . .
CMD ...
This approach will work for any sort of artifact. If you have something like an application's package.json/requirements.txt/Gemfile/go.mod that lists out all of the application's library dependencies, you can run the download-and-install step ahead of time like this, but you'll need to remember to repeat it and manually move the updated base image if these dependencies ever change.

Related

Automatic build of docker container (java backend, angular frontend)

I would like to say that this is my first container and actually my first JAVA app so maybe I will have basic questions so be lenient, please.
I wrote spring boot app and my colleague has written the frontend part for it in angular. What I would like to achieve is to have "one button/one command" in IntelliJ to create a container containing whole app backend and front end.
What I need to do is:
Clone FE from company repository (I am using ssh key now)
Clone BE from GitHub
Build FE
Copy built FE to static folder in java app
Build BE
Create a container running this app
My current solution is to create "builder" container and there build FE and BE and then copy it to "production" container like this:
#BUILDER
FROM alpine AS builder
WORKDIR /src
# add credentials on build
ARG SSH_PRIVATE_KEY
RUN mkdir /root/.ssh/ \
&& echo "${SSH_PRIVATE_KEY}" > /root/.ssh/id_rsa \
&& echo "github.com,140.82.121.3 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==" >> /root/.ssh/known_hosts \
&& chmod 600 /root/.ssh/id_rsa
# installing dependencies
RUN apk update && apk upgrade && apk add --update nodejs nodejs-npm \
&& npm install -g #angular/cli \
&& apk add openjdk11 \
&& apk add maven \
&& apk add --no-cache openssh \
&& apk add --no-cache git
#cloning repositories
RUN git clone git#code.siemens.com:apcprague/edge/metal-forming-fe.git
RUN git clone git#github.com:bzumik1/metalForming.git
# builds front end
WORKDIR /src/metal-forming-fe
RUN npm install && ng build
# builds whole java app with front end
WORKDIR /src/metalForming
RUN cp -a /src/metal-forming-fe/dist/metal-forming/. /src/metalForming/src/main/resources/static \
&& mvn install -DskipTests=true
#PRODUCTION CONTAINER
FROM adoptopenjdk/openjdk11:debian-slim
LABEL maintainer jakub.znamenacek#siemens.com
RUN mkdir app
RUN ["chmod", "+rwx", "/app"]
WORKDIR /app
COPY --from=builder /src/metalForming/target/metal_forming-0.0.1-SNAPSHOT.jar .
EXPOSE 4200
RUN java -version
CMD java -jar metal_forming-0.0.1-SNAPSHOT.jar
This works but I takes very long time so I guess this is not correct way how to do it. Could anyone point me in correct direction? I was thinking if there is a way how to make maven to all these steps for me but maybe this is totally off.
Also if you will find any problem in my Dockerfile please let me know as I said this is my first Dockerfile so I could overlook something.
EDITED:
BTW does anyone know how can I get rid of this part:
echo "github.com,140.82.121.3 ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAq2A7hRGmdnm9tUDbO9IDSwBK6TbQa+PXYPCPy6rbTrTtw7PHkccKrpp0yVhp5HdEIcKr6pLlVDBfOLX9QUsyCOV0wzfjIJNlGEYsdlLJizHhbn2mUjvSAHQqZETYP81eFzLQNnPHt4EVVUh7VfDESU84KezmD5QlWpXLmvU31/yMf+Se8xhHTvKSCZIFImWwoG6mbUoWf9nzpIoaSjB+weqqUUmpaaasXVal72J+UX2B+2RPW3RcT0eOzQgqlJL3RKrTJvdsjE3JEAvGq3lGHSZXy28G3skua2SmVi/w4yCE6gbODqnTWlg7+wC604ydGXA8VJiS5ap43JXiUFFAaQ==" >> /root/.ssh/known_hosts \
it adds GitHub to known_hosts (I also need to add a company repository there). It is because when I run git clone it will ask me if I trust this ... and I have to write yes but I don't know how to do it if it is automatically running in docker and I have no option to write there yes. I have tried yes | git clone ... but this is also not working
a few things:
1, if this runs "slow" on the machine than it will run slow inside a container too.
2, remove --no-cache,* you want to cache everything that is static, because next time when you build those commands will not run where there is no change. Once there is change in one command than that command will rerun instead using the builder cache and also all subsequent commands will have to rerun too.
*UPDATE: I have mistaken "apk update --no-cache" with "docker build --no-cache". I stated wrong that "apk add --no-cache" would mean that command is not cached, because this command is cached on docker builder level. However with this parameter you wouldn't need to delete in a later step the /var/cache/apk/ directory to make you image smaller, but that you wouldn't need to do here, because you are already using multi stage build, so this would not affect your final image size.
One more thing to clarify, all statements in Dockerfile are checked if they changed, if they did not than docker builder uses the cached layer for it and won't run that statement. Exception is ADD and COPY commands, here builder also checks the copied, added files if they changed with checksum. Also if a statement is changed or ADD-ed COPY-ed file(s) changed than that statement is re-run and all subsequent statements re-run too, so you want to put your source code copy statemant as much at the end as it is possible
If you want to disable this cache, do "docker build --no-cache ..." this way all the steps will be re-run that is in the Dockerfile.
3, specify WORKDIR at the top once, if you need to switch directory later use this:
RUN cd /someotherdir && mycommand
Also specifying a Subsequent WORKDIR will be relativ to the previous WORKDIR so it will mess up readibilty what is the (probably) sole purpose of WORKDIR statement.
4, Enable BuildKit:
Either declare environment variable
DOCKER_BUILDKIT=1
or add this to /etc/docker/daemon.json
{ "features": { "buildkit": true } }
BuildKit might not help in this case, but if you do more complex Dockerfiles with more stages Buildkit can run those parallel so overall build will be faster.
5, Do not skip tests with DskipTests=true :)
6, as stated in a comment, do not clone the repo inside the image build, you do not need to do that at all. Just put the Dockerfile in the / of the repo, and COPY the repo files with a Dockerfile command:
COPY . .
First dot is the source that is your current directory on your machine, second dot is the target, the working dir inside the image, /src with your Dockerfile. You build the image and publish it, push it to a docker registry so others can pull it and start using it. If you want more complex stuff building and publishing with a help of a server, look up CI/CD techniques.

How do I use apk in a docker image built from busybox:1.31.1-glibc

I am running a container built with FROM busybox:1.31.1-glibc as the base image, but now when I am in the container and I try to use apk to install packages it does not work. For example:
/ # apk add curl
/bin/sh: apk: not found
How do I go about adding packages to this running container if I want to?
The busybox image doesn't have a package manager. It's intended for final distribution of build artifacts like a binary, with a few shell utilities for convenience. Typical usage would be as as the final base image in a multi-stage build.
apk is the Alpine package manager. If you want to use it, you'll need to use alpine as your base image in your Dockerfile.

Create a new version of a container and submit do docker hub

I have a container and I want to make a change to it.
It is hosted at docker hub, and I would like to change one of its commands to install an additional app.
I can see the individual steps in docker hub, but not the dockerfile itself (not sure why).
This is the command I want to change:
/bin/sh -c apk --update add --no-cache openssh bash zip && ....
I want to pull the container, change that line to include an additional package in the add command of apk.
I have been reading about the docker commit --change command but I am unsure how to exactly pinpoint the command I want to change. Do I reference it numerically, saying I want to change line #3?
I will then tag a new version and push, which I know how to do, but I am finding it hard to understand how to change this without docker run -it [name] bash and then push this, as I already did that and it appended a new command after the CMD command, and broke the container...
What hosted on docker hub is actually the image of the container.
This image is hosted in layers. each docker file command represents a new layer. You can't actually change a single layer, You can however create a new docker file based on that image and add new layers.
FROM SOME_BASE_IMAGE
RUN apt-get update && \
apt-get install -y SOME_PACKAGE
(assuming the image has apt package manager)
You'll then have to build the new image with docker build -t IMAGE_REPO:IMAGE_TAG ...
If you don't have access to the Dockerfile, you can't change anything in that container, you can only append more stuff (layers).
This means that, you can't change /bin/sh -c apk --update add --no-cache openssh bash zip && .... for something else, but if your intention is to install / remove more packages, you can do the following:
FROM the_container_you_want_to_change
RUN ...

node:latest for alpine, apk not found because sbin is not on path

I am running a docker-compose file using node:latest. I noticed an issue with the timezone that I am trying to fix. Following an example I found online, I tried to install tzdata. This is not working as I keep getting apk not found errors. After finding this stackoverflow.com question, Docker Alpine /bin/sh apk not found, it seems to mirror my issue as I docker exec'ed into the container and found the apk command in the /sbin folder. I tried to do the following to make it work but I am still not able to access apk. From other articles I found, this seemed to be the way to resolve the issue but apk is still not found.
CMD export PATH=$PATH:$ADDITIONAL_PATH
RUN apk add --no-cache tzdata
ENV TZ=America/Chicago
node:latest is based on buildpack-deps, which is based on Debian. Debian does not use apk; it uses apt. You either want to use Debian's apt to install packages (apt-get install tzdata) or switch to node:alpine, which uses apk for package management.
You can use node:alpine which is based on alpine.
node:alpine
CMD export PATH=$PATH:$ADDITIONAL_PATH
RUN apk add --no-cache tzdata
ENV TZ=America/Chicago
node:-alpine
This image is based on the popular Alpine Linux project, available in
the alpine official image. Alpine Linux is much smaller than most
distribution base images (~5MB), and thus leads to much slimmer images
in general.

Docker image for built Golang binary

I have a Go application that I build into a binary and distribute as a Docker image.
Currently, I'm using ubuntu as my base image, but this causes an issue where if a user tries to use a Timezone other than UTC or their local timezone, they get an error stating:
pod error: panic: open /usr/local/go/lib/time/zoneinfo.zip: no such file or directory
This error is caused because the LoadLocation package in Go requires that file.
I can think of two ways to fix this issue:
Continue using the ubuntu base image, but in my Dockerfile add the commands: RUN apt-get install -y tzdata
Use one of Golang's base images, eg. golang:1.7.5-alpine.
What would be the recommended way? I'm not sure if I need to or should be using a Golang image since this is the container where the pre-built binary runs. My understanding is that Golang images are good for building the binary in the first place.
I prefer to use multi-stage build. On 1st step you use special golang building container for installing all dependencies and build an application. On 2nd stage I copy binary file to empty alpine container. This allows having all required tooling and minimal docker image simultaneously (in my case 6MB instead of 280MB).
Example of Dockerfile:
# build stage
FROM golang:1.8
ADD . /src
RUN set -x && \
cd /src && \
go get -t -v github.com/lisitsky/go-site-search-string && \
CGO_ENABLED=0 GOOS=linux go build -a -o goapp
# final stage
FROM alpine
WORKDIR /app
COPY --from=0 /src/goapp /app/
ENTRYPOINT /goapp
EXPOSE 8080
Since not all OS have localized timezone installed, this is what I did to make it work:
ADD https://github.com/golang/go/raw/master/lib/time/zoneinfo.zip /usr/local/go/lib/time/zoneinfo.zip
The full example of multi-step Docker image for adding timezone is here
This is more of a vote, but apt-get is what we (my company's tech group) do in situations like this. It gives us complete control over the hierarchy of images, but this is assuming you may have future images based on this one.
You can use the system's tzdata. Or you can copy $GOROOT/lib/time/zoneinfo.zip into your image, which is a trimmed version of the system one.

Resources