-
Notifications
You must be signed in to change notification settings - Fork 2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Please add yarn
(awesome npm alternative)
#243
Comments
It may be a bit premature to decide that yet, but I was thinking about the same yesterday. Given the overwhelmingly positive response to the Yarn release (~9300 GitHub stars in just one day, ~1600 upvotes on HN) this might become a very common request. I've done a quick test, and adding Yarn to the |
Current official
Yep, Yarp adds 10% to size for
Agreed that needs to wait some time (a couple of hours 😂) till it became a new standart. List of most fat packages in
|
It's probably something we could add in the future if there was enough demand for it, but I'm pretty ambivalent about it at this point since it's so new. I'd prefer to keep the images fairly simple, especially the slim variant, since the main point is to use it as a base image and install dependencies etc on top of it. For installation, I would be more inclined to use the Yarn Debian package repository (under Linux): https://yarnpkg.com/en/docs/install That more closely follows the best practices for official images (pgp signed packages): https://github.com/docker-library/official-images#image-build |
Tried that yesterday. Currently doesn't work on our images, because their packages depend on an installed recent |
Ugh, ok. Maybe at some point they'll pgp sign the tarball. |
It looks like there may be soon a ~ 2 MB bundled release which is CLI-compatible: yarnpkg/yarn#888. If that works, I'm definitely +1 on including it. |
This would break the out-of-the-box experience for people that are just getting started. Without Node.js being installed, Most people are on Ubuntu 16.04 which has Node.js 4.2.6 in the repo, and the NodeSource repo can be added to get Node 6. |
The absence of a |
@Daniel15 a |
Are we talking about shipping with Yarn? Because that's not a thing in core yet and I'm not sure that is a good idea at this time. |
@Fishrock123 I don't think any of Docker WG members are suggesting we should add Yarn at this point, but that might change if Yarn becomes a popular request by the masses. |
Yes, that's my understanding too: this is just about adding yarn to the docker images. |
Any timeline for this? |
@andrewmclagan at them moment no, there is a similar discussion over at nodejs/node#9161. |
If you guys would tag your releases on here we could fork the repo and use our custom images (including yarn). But since you don't, it's pretty hard to establish a workflow based on your official image (which is sad). |
@jeremyzahner This is how you can use the official image with FROM node:6.9.1
RUN npm install -g yarn
... I agree that it would be more convenient to have it pre installed, but it is not a blocker from basing on the official image. |
@jeremyzahner I don't understand your "tag your releases" comment. Can you elaborate? That is, I don't understand how this fits with your desired workflow. |
The issue would be to run one-off commands like this since
|
This has been fixed, by the way. The Debian package should be suitable for you to use now 😄 The repo is GPG signed. |
* update Dockerfile - need the `apt-transport-https` until docker-library/buildpack-deps#55 is resolved - do not use `onbuild` and install yarn manually until nodejs/docker-node#243 is implemented - need `CMD` without `onbuild` * generate yarn.lock
Hey guys, would it be possible to prioritize this issue somehow? It is the most 👍-d in the whole project at the moment :–) I would love to use yarn in GitLab CI to test and build react apps. Today's lack of an official |
There is nothing preventing you from making your own image and publishing it to Docker Hub if you want to use FROM node:6-alpine
ENV YARN_VERSION 0.20.0
ADD https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v${YARN_VERSION}.tar.gz /opt/yarn.tar.gz
RUN yarnDirectory=/opt/yarn && \
mkdir -p "$yarnDirectory" && \
tar -xzf /opt/yarn.tar.gz -C "$yarnDirectory" && \
ln -s "$yarnDirectory/dist/bin/yarn" /usr/local/bin/ && \
rm /opt/yarn.tar.gz As it stands today |
@kachkaev We already did so: https://hub.docker.com/r/jshmrtn/node-yarn/ |
Good news! There is now an image by yarn developers: There are not that many tags at the moment (e.g. alpine version is missing), but this is still a great step forward! Related PR: yarnpkg/yarn#2721 Only after @Starefossen replied to my comment I realised that this issue is in |
The current node-yarn image is based off Debian Jessie, not Alpine. I can add an Alpine image if there's demand for it, but generally I haven't seen a lot of demand as most people are already using other Debian/Ubuntu packages as part of their build system anyways. |
My current solution for building docker image for production (good hint for starting your own). This solution is used a half year and rewritten several times. Run package.json{
...
"scripts": {
"build-docker": "BABEL_ENV=build ./node_modules/.bin/babel-node ./bin/run deploy/docker/buildAppImage",
},
... run.jsScript which runs other js files with some logging info import chalk from 'chalk';
function format(time) {
return time.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1');
}
async function run(fn, options) {
const start = new Date();
console.log(chalk.bold.yellow(`[${format(start)}] Starting '${fn.name}'...`));
let result;
let hasError = false;
try {
result = await fn(options);
} catch (err) {
console.log(chalk.bgRed.bold.white(`Failed '${fn.name}': ${err.message}`));
console.error(err.stack);
hasError = true;
}
const finish = new Date();
const time = finish.getTime() - start.getTime();
if (hasError) {
console.log(chalk.bgRed.bold.white(
`[${format(finish)}] Finished '${fn.name}' after ${time} ms`
));
} else {
console.log(chalk.bold.green(
`[${format(finish)}] Finished '${fn.name}' after ${time} ms`
));
}
return result;
}
if (process.mainModule.children.length === 0 && process.argv.length > 2) {
delete require.cache[__filename];
const module = require(`./${process.argv[2]}.js`).default;
run(module);
}
module.exports = run;
module.exports.default = run; config.jsimport path from 'path';
import cp from 'child_process';
export const regionECR = 'eu-west-1';
export const remoteRepo = `000000000000.dkr.ecr.${regionECR}.amazonaws.com`;
export const repositoryName = 'repo-kz';
export const rootDir = path.resolve(__dirname, '../../');
export const buildEnv = 'production';
export const webpackBuildDir = path.resolve(rootDir, `./build/${buildEnv}`);
export function isDockerImageExists(imageNameWithTag) {
const imageId = cp.execSync(`docker images -q ${imageNameWithTag}`).toString();
return imageId && imageId.length > 0;
}
export function getContainerInfo(imageNameWithTag) {
return cp.execSync(`docker images ${imageNameWithTag}`).toString();
} buildNodeImage.js/* eslint-disable no-use-before-define, camelcase, no-multi-str */
import cp from 'child_process';
import fsExtra from 'fs-extra';
import hashFiles from 'hash-files';
import chalk from 'chalk';
import {
repositoryName,
rootDir,
buildEnv,
isDockerImageExists,
getContainerInfo,
} from '../config';
const nodeVersion = '6.9.4';
const nodeSlimImage = `node:${nodeVersion}-slim`;
const nodeBuildImage = `node:${nodeVersion}-onbuild`;
const imageName = `${repositoryName}/node-modules`;
const ramDiskName = `docker_${new Date().toJSON().slice(0, 19).replace(/[^0-9]+/g, '')}`;
const dockerContextFolder = `/Volumes/${ramDiskName}`;
console.log(imageName);
export default function build_DockerContainer_With_NodeModules() {
const imageTag = calcPackageHash(buildEnv);
const imageNameWithTag = `${imageName}:${imageTag}`;
if (!isDockerImageExists(imageNameWithTag)) {
try {
console.log(`Mounting RAM disk ${ramDiskName}`);
mountRamDisk();
console.log(
chalk.magenta('Installing node_modules for debian '
+ `via Docker container (${nodeBuildImage} 650MB) to the RAM disk`)
);
npmInstallViaFatContainer(buildEnv);
console.log(chalk.magenta(`Creating slim docker image (from ${nodeSlimImage}): ${imageNameWithTag}...`));
createDockerContainer(imageNameWithTag);
} finally {
console.log(`Unmounting RAM disk ${ramDiskName}`);
unmountRamDisk();
}
console.log(chalk.magenta('Docker container with NODE_MODULES successfully builded.'));
} else {
console.log(chalk.magenta('Docker container with NODE_MODULES already exists.'));
}
console.log(chalk.magenta(getContainerInfo(imageNameWithTag)));
console.log(chalk.magenta('You can run container via command:'));
console.log(chalk.bold.magenta(`docker run -i -t --rm ${imageNameWithTag} /bin/bash`));
return {
name: imageName,
tag: imageTag,
nameWithTag: imageNameWithTag,
};
}
function createDockerContainer(imageNameWithTag) {
const dockerfile = [
`FROM ${nodeSlimImage}`, // 210MB container only with node
'WORKDIR /src',
'COPY . /src',
];
fsExtra.outputFileSync(`${dockerContextFolder}/Dockerfile`, dockerfile.join('\n'));
cp.execSync(`docker build \
-t ${imageNameWithTag} \
${dockerContextFolder}`,
{
cwd: dockerContextFolder,
stdio: [0, 1, 2],
}
);
}
function npmInstallViaFatContainer() {
fsExtra.emptyDirSync(dockerContextFolder);
fsExtra.copySync(`${rootDir}/package.json`, `${dockerContextFolder}/package.json`);
fsExtra.copySync(`${rootDir}/yarn.lock`, `${dockerContextFolder}/yarn.lock`);
const npmInstallCmd = (
'wget https://yarnpkg.com/install.sh && chmod +x install.sh && ./install.sh --nightly && rm -f install.sh '
+ '&& PATH=$PATH:~/.yarn/bin/ ' // add yarn to paths
+ '&& yarn add string-width --production ' // needed for build bcrypt module and runtime
+ '&& yarn install --production ' // install app modules
+ '&& rm -rf ~/.yarn ' // remove cached modules and yarn itself for reducing container size
);
// IMPORTANT: `node:onbuild` is 650MB container with build scripts
// needed for build `fsevent` and `crypto` bin-files for debian
cp.execSync(`docker run \
-t --rm -v ${dockerContextFolder}:/src \
${nodeBuildImage} \
/bin/bash -c "cd /src && ${npmInstallCmd}"`,
{
cwd: dockerContextFolder,
stdio: [0, 1, 2],
}
);
}
function calcPackageHash(env) {
const hash = hashFiles.sync({
files: [
`${rootDir}/package.json`,
`${rootDir}/yarn.lock`,
],
});
return `${env}-${hash}`;
}
function mountRamDisk() {
cp.execSync(`diskutil erasevolume hfsx ${ramDiskName} \`hdiutil attach -nomount ram://1200000\``,
{ stdio: [0, 1, 2] }
);
}
function unmountRamDisk() {
cp.execSync(`diskutil unmountDisk force ${ramDiskName}`,
{ stdio: [0, 1, 2] }
);
} buildAppImage.js/* eslint-disable no-use-before-define, camelcase */
import cp from 'child_process';
import del from 'del';
import fs from 'fs';
import fsExtra from 'fs-extra';
import chalk from 'chalk';
import run from '../../run';
import buildNodeImage from './buildNodeImage';
import {
repositoryName,
buildEnv,
webpackBuildDir,
isDockerImageExists,
getContainerInfo,
} from '../config';
const imageName = `${repositoryName}/${buildEnv}`;
export default async function build_DockerContainer_With_APP() {
let buildDirStat = fs.statSync(webpackBuildDir);
if (!buildDirStat) {
cp.execSync('yarn build', { cwd: webpackBuildDir, stdio: [0, 1, 2] });
}
buildDirStat = fs.statSync(webpackBuildDir);
if (!buildDirStat) {
console.log(chalk.magenta.bold('Firstly bundle app via command: `yarn build`'));
throw new Error(`Webpack folder with bundled code does not exist: ${webpackBuildDir}`);
}
const nodeImage = await run(buildNodeImage);
if (!nodeImage) {
throw new Error('Cannot build app container. NodeModules container not provided.');
}
const imageTagFallback = buildDirStat
.mtime
.toJSON()
.slice(0, 19)
.replace('T', '-')
.replace(/[^-0-9]+/g, '');
const revision = fs.readFileSync(`${webpackBuildDir}/revision.txt`, 'utf8').toString().trim();
const imageTag = revision || imageTagFallback;
const imageNameWithTag = `${imageName}:${imageTag}`;
console.log(`Creating app docker image: ${imageNameWithTag}...`);
if (!isDockerImageExists(imageNameWithTag)) {
console.log('Remove source map files for avoiding vulnerability of code');
del.sync([`${webpackBuildDir}/public/**/*.map`]);
createDockerfile(nodeImage.nameWithTag);
cp.execSync(`docker build \
-t ${imageNameWithTag} \
${webpackBuildDir}`,
{ cwd: webpackBuildDir, stdio: [0, 1, 2] });
console.log(chalk.magenta('Docker container successfully builded.'));
} else {
console.log(chalk.magenta('Docker container already exists.'));
}
console.log(chalk.magenta(getContainerInfo(imageNameWithTag)));
console.log(chalk.magenta('Command for enter to container:'));
console.log(chalk.bold.magenta(`docker run -i -t --rm ${imageNameWithTag} /bin/bash`));
console.log(chalk.magenta('Command to start server on port 5000:'));
console.log(chalk.bold.magenta(`docker run -i --rm -p 5000:5000 ${imageNameWithTag}`));
return {
name: imageName,
tag: imageTag,
nameWithTag: imageNameWithTag,
};
}
function createDockerfile(modulesImage) {
const dockerfile = [
`FROM ${modulesImage}`,
'WORKDIR /src',
'COPY . /src',
'EXPOSE 5000',
'CMD ["node", "/src/server.js"]',
];
const filename = `${webpackBuildDir}/Dockerfile`;
fsExtra.outputFileSync(filename, dockerfile.join('\n'));
return filename;
} |
That package doesn't really help, as that package will install its own version of Node, ignoring the already installed one. Nice to see some movement here, though! 😄 |
References yarnpkg/yarn#2728 References nodejs/docker-node#243
@pesho - |
New proposal PR created: #337 |
The latest version of yarn is now being added via #337 Here's the Docker Hub PR: docker-library/official-images#2703 |
Looks like yarn was added to all official node images – perfect! README on Docker Hub does not mention yarn so it may be confusing, but the recipes themselves do include it! 🎉 🎉 🎉 |
Yes, there is a pending PR to update documentation. |
@Starefossen This still seems to be missing from the docs. I was surprised when I looked at the environment variables for a recent node app to see Which PR is supposed to add the docs? |
Why |
Because of a miscommunication in when to update it. Next release will have it updated |
@Starefossen @SimenB Any idea why the readme on Docker Hub is still outdated*? *Notice no mention of yarn |
I don't know how that file is updated... |
It requires a separate PR to https://github.com/docker-library/docs They don't automatically pull in our README.md or docs unfortunately. |
This came up in nodejs/docker-node#243 (comment)
@chorrell Thanks, I submitted a PR in docker-library/docs#964 |
@romandragan FWIW 8.2.0 will have a updated Yarn installation. We'll update the others when a new version of node is released for them |
Will be cool if
yarn
was integrated to latest node@6 images.https://yarnpkg.com/
Yarn: A new package manager for JavaScript
The text was updated successfully, but these errors were encountered: